Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
2ad360e43c | |||
05c9127f8b | |||
55c7402868 | |||
a33cbdd1b7 | |||
e033f8133d | |||
4c5dd23c88 | |||
3b6aceb370 | |||
f1645ecaaa | |||
1178533e03 | |||
f7ddf898c6 | |||
f1996f903b | |||
87246f41be | |||
e8ecf69a00 | |||
580118bac2 |
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,3 +1,7 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
config.json
|
config.json
|
||||||
|
dist/
|
||||||
|
.parcel-cache/
|
||||||
|
.cache/
|
||||||
|
package-lock.json
|
||||||
|
.vscode/
|
||||||
|
1601
package-lock.json
generated
1601
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@ -1,21 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "wschat-ng",
|
"name": "wschat-ng",
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "ts-node src/index.ts"
|
"start": "npx ts-node src/index.ts"
|
||||||
},
|
},
|
||||||
"author": "szdytom <szdytom@163.com>",
|
"author": "szdytom <szdytom@qq.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"dependencies": {
|
||||||
"@types/express": "^4.17.11",
|
"@types/express": "^5.0.3",
|
||||||
"@types/socket.io": "^2.1.13",
|
"@types/socket.io": "^3.0.1",
|
||||||
"ts-node": "^9.1.1",
|
"express": "^5.1.0",
|
||||||
"typescript": "^4.2.3"
|
"socket.io": "^4.8.1",
|
||||||
},
|
"ts-node": "^10.9.2"
|
||||||
"dependencies": {
|
}
|
||||||
"express": "^4.17.1",
|
|
||||||
"socket.io": "^4.0.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
BIN
public/favicon.png
Normal file
BIN
public/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>WebSocket Chat Room</title>
|
<title>WebSocket Chat Room</title>
|
||||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
<link rel="icon" href="favicon.png">
|
||||||
|
<link rel="stylesheet" href="style.css"/>
|
||||||
|
<script type="module" src="script.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -11,13 +13,14 @@
|
|||||||
|
|
||||||
<textarea id="send" placeholder="Say something or run a command..." spellcheck="false"></textarea>
|
<textarea id="send" placeholder="Say something or run a command..." spellcheck="false"></textarea>
|
||||||
<div class="top-box">
|
<div class="top-box">
|
||||||
<button class="send-button" onclick="clear_message()">Clear</button>
|
<button class="send-button" id="clear-button">Clear</button>
|
||||||
<p class="note"><input id="scroll-option" type="checkbox" checked /> Enable auto scroll</p>
|
<p class="note"><input id="scroll-option" type="checkbox" checked /> Enable auto scroll</p>
|
||||||
<p class="note">Version: SOCKET.IO-2.2</p>
|
<p class="note">Version: SOCKET.IO-2.2</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom-box">
|
<div class="bottom-box">
|
||||||
<span class="note">Press Ctrl + Enter to send </span>
|
<span class="note">Press Ctrl + Enter to send </span>
|
||||||
<button class="send-button" onclick="send()">Send</button>
|
<button class="send-button" id="uploader">Upload</button>
|
||||||
|
<button class="send-button" id="send-button">Send</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="prompt-background" hidden></div>
|
<div id="prompt-background" hidden></div>
|
||||||
@ -29,14 +32,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/es5-shim@4.5.15/es5-shim.min.js"></script>
|
<input type="file" id="real-uploader" hidden/>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@4.0.0/dist/socket.io.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dompurify@2.2.7/dist/purify.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
|
|
||||||
<script src="./script.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/remarkable@2.0.1/dist/remarkable.js"></script>
|
|
||||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.7.1/build/styles/tomorrow.min.css">
|
|
||||||
<script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.7.1/build/highlight.min.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
24
public/package.json
Normal file
24
public/package.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "public",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"scripts": {
|
||||||
|
"build": "npx parcel src/index.html"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"buffer": "^6.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dompurify": "^3.2.6",
|
||||||
|
"highlightjs": "^9.16.2",
|
||||||
|
"jquery": "^3.6.1",
|
||||||
|
"katex": "^0.16.2",
|
||||||
|
"parcel": "^2.15.4",
|
||||||
|
"remarkable": "^2.0.1",
|
||||||
|
"remarkable-katex": "^1.2.1",
|
||||||
|
"socket.io-client": "^4.5.2"
|
||||||
|
}
|
||||||
|
}
|
103
public/script.js
103
public/script.js
@ -1,12 +1,23 @@
|
|||||||
|
import io from 'socket.io-client';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import {Remarkable} from 'remarkable';
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
import rkatex from 'remarkable-katex';
|
||||||
|
import hljs from 'highlightjs';
|
||||||
|
|
||||||
|
import 'highlightjs/styles/tomorrow.css';
|
||||||
|
import 'katex/dist/katex.min.css'
|
||||||
|
|
||||||
let username;
|
let username;
|
||||||
let is_reconnection = false;
|
let is_reconnection = false;
|
||||||
let last_command = null;
|
let last_command = null;
|
||||||
const server = `ws://${location.host}`;
|
|
||||||
|
|
||||||
let ws;
|
let ws;
|
||||||
let md;
|
let md;
|
||||||
let unread_message = 0;
|
let unread_message = 0;
|
||||||
|
|
||||||
|
let assets = [];
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
init().then(x => ws = x).catch(err => console.log(err))
|
init().then(x => ws = x).catch(err => console.log(err))
|
||||||
});
|
});
|
||||||
@ -23,6 +34,11 @@ function format_time(time) {
|
|||||||
return `${time.getMonth() + 1}-${time.getDate()} ${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}`;
|
return `${time.getMonth() + 1}-${time.getDate()} ${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function render_download(data) {
|
||||||
|
const reg = /\{\%download\s+([^\s]+)\s+([^\}]+)\}/g;
|
||||||
|
return data.replaceAll(reg, '<a href="$2" download="$1">Download $1</a>');
|
||||||
|
}
|
||||||
|
|
||||||
function write_message(data) {
|
function write_message(data) {
|
||||||
const message = data.msg;
|
const message = data.msg;
|
||||||
|
|
||||||
@ -44,6 +60,8 @@ function write_message(data) {
|
|||||||
}
|
}
|
||||||
rendered_message = DOMPurify.sanitize(rendered_message);
|
rendered_message = DOMPurify.sanitize(rendered_message);
|
||||||
|
|
||||||
|
rendered_message = render_download(rendered_message);
|
||||||
|
|
||||||
$('#message').append(`
|
$('#message').append(`
|
||||||
<div class="msg">
|
<div class="msg">
|
||||||
<span class="msg-from ${message_class} ${(data.is_private ? 'msg-private' : '')}">
|
<span class="msg-from ${message_class} ${(data.is_private ? 'msg-private' : '')}">
|
||||||
@ -76,18 +94,18 @@ async function init() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
clear_message();
|
clear_message();
|
||||||
|
|
||||||
write_message({
|
write_message({
|
||||||
type: 'system-message',
|
type: 'system-message',
|
||||||
msg: 'Notification permission are use to get you informated. Please allow it.',
|
msg: 'Notification permission is used to get you informated. Please allow it.',
|
||||||
is_private: true,
|
is_private: true,
|
||||||
plain: true,
|
plain: true,
|
||||||
});
|
});
|
||||||
await Notification.requestPermission();
|
await Notification.requestPermission();
|
||||||
|
|
||||||
await login_name();
|
await login_name();
|
||||||
|
|
||||||
md = new remarkable.Remarkable({
|
md = new Remarkable({
|
||||||
highlight: function (str, lang) {
|
highlight: function (str, lang) {
|
||||||
if (lang && hljs.getLanguage(lang)) {
|
if (lang && hljs.getLanguage(lang)) {
|
||||||
try {
|
try {
|
||||||
@ -102,10 +120,15 @@ async function init() {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
md.use(rkatex);
|
||||||
md.inline.ruler.enable(['mark', 'sup', 'sub']);
|
md.inline.ruler.enable(['mark', 'sup', 'sub']);
|
||||||
|
|
||||||
|
md.inline.validateLink = function() {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
const ws = new io(server);
|
const ws = new io();
|
||||||
|
|
||||||
ws.on('connect', () => {
|
ws.on('connect', () => {
|
||||||
if (is_reconnection) {
|
if (is_reconnection) {
|
||||||
@ -162,7 +185,7 @@ async function init() {
|
|||||||
ws.on('command-block-reply', data => {
|
ws.on('command-block-reply', data => {
|
||||||
write_message({
|
write_message({
|
||||||
type: 'command-block',
|
type: 'command-block',
|
||||||
msg: `>> ${last_command}\n<- ${data}`,
|
msg: `>> ${last_command}<br/><- ${data}`,
|
||||||
is_private: true,
|
is_private: true,
|
||||||
plain: true,
|
plain: true,
|
||||||
});
|
});
|
||||||
@ -177,6 +200,23 @@ async function init() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#clear-button').click(function() {
|
||||||
|
clear_message();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#send-button').click(async function() {
|
||||||
|
await send();
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#uploader').click(function() {
|
||||||
|
$('#real-uploader').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#real-uploader').change(async function() {
|
||||||
|
const content = await upload(this.files[0]);
|
||||||
|
$('#send').val($('#send').val() + content);
|
||||||
|
});
|
||||||
|
|
||||||
$('#send').focus();
|
$('#send').focus();
|
||||||
|
|
||||||
return ws;
|
return ws;
|
||||||
@ -204,8 +244,44 @@ function open_prompt(title) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_url_query(query_name) {
|
||||||
|
var reg = new RegExp("(^|&)" + query_name + "=([^&]*)(&|$)", "i");
|
||||||
|
var r = window.location.search.substr(1).match(reg);
|
||||||
|
var context = "";
|
||||||
|
if (r != null) {
|
||||||
|
context = r[2];
|
||||||
|
}
|
||||||
|
reg = null;
|
||||||
|
r = null;
|
||||||
|
return context == null || context == "" || context == "undefined" ? "" : context;
|
||||||
|
}
|
||||||
|
|
||||||
async function login_name() {
|
async function login_name() {
|
||||||
username = await open_prompt('[Login] Input your name');
|
username = get_url_query("name");
|
||||||
|
|
||||||
|
if (username === undefined || username.length == 0) {
|
||||||
|
username = await open_prompt('[Login] Input your name');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function encode_file(file) {
|
||||||
|
return new Promise((resolve,) => {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = (e) => {
|
||||||
|
resolve(e.target.result);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function upload(file) {
|
||||||
|
const dataurl = await encode_file(file);
|
||||||
|
const id = assets.length;
|
||||||
|
assets.push(dataurl);
|
||||||
|
if (file.type.startsWith('image/')) {
|
||||||
|
return ``;
|
||||||
|
}
|
||||||
|
return `{%download ${file.name} %asset${id}}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function send() {
|
async function send() {
|
||||||
@ -225,6 +301,11 @@ async function send() {
|
|||||||
last_command = data;
|
last_command = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let i in assets) {
|
||||||
|
data = data.replaceAll(`%asset${i}`, assets[i]);
|
||||||
|
}
|
||||||
|
assets = [];
|
||||||
|
|
||||||
if (data.startsWith('/su')) {
|
if (data.startsWith('/su')) {
|
||||||
// a administrator login command.
|
// a administrator login command.
|
||||||
// now ask for passcode
|
// now ask for passcode
|
||||||
@ -259,7 +340,7 @@ document.addEventListener('keydown', async function (key_event) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("visibilitychange", function() {
|
document.addEventListener("visibilitychange", function () {
|
||||||
if (document.visibilityState === 'visible') { unread_message = 0; }
|
if (document.visibilityState === 'visible') { unread_message = 0; }
|
||||||
$('title').text(`WebSocket Chat Room`);
|
$('title').text(`WebSocket Chat Room`);
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import 'proton-color.css';
|
@import './proton-color.css';
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
@ -44,7 +44,7 @@ io.on('connection', socket => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use('/', Express.static(path.join(__dirname, '../public')));
|
app.use('/', Express.static(path.join(__dirname, '../public/dist')));
|
||||||
|
|
||||||
const server_port = parseInt(process.env.PORT) | 4412;
|
const server_port = parseInt(process.env.PORT) | 4412;
|
||||||
http.listen(server_port, () => {
|
http.listen(server_port, () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user