Compare commits

..

14 Commits
v1.0 ... master

Author SHA1 Message Date
2ad360e43c
[arch] update dep
Signed-off-by: szdytom <szdytom@qq.com>
2025-09-14 18:10:22 +08:00
05c9127f8b
[arch] update dependence
Signed-off-by: szdytom <szdytom@qq.com>
2025-09-14 17:22:06 +08:00
55c7402868 [fix] typo 2022-09-22 05:48:37 +00:00
a33cbdd1b7 [fix] typo 2022-09-22 05:48:05 +00:00
e033f8133d [fix] upload files
Co-authored-by: szdytom <szdytom@163.com>
2022-09-21 16:43:39 +08:00
4c5dd23c88 [feat] upload files
Co-authored-by: szdytom < szdytom@163.com >
2022-09-21 15:40:34 +08:00
3b6aceb370 [fix] https 2022-09-21 14:12:33 +08:00
f1645ecaaa [fix] katex 2022-09-21 14:11:17 +08:00
1178533e03 [fix] hljs 2022-09-21 14:04:12 +08:00
f7ddf898c6 [arch] use parcel
Signed-off-by: gzezFISHER <fisher@mail.soj.ac.cn>
2022-09-20 17:17:48 +08:00
f1996f903b [feature] md bilibili extension
Signed-off-by: zhangtianli2006 <zhangtianli2006@163.com>
2021-08-26 21:07:21 +08:00
87246f41be [feature] md lichess extension
Signed-off-by: zhangtianli2006 <zhangtianli2006@163.com>
2021-08-26 20:51:03 +08:00
e8ecf69a00 [feature] add favicon
Signed-off-by: zhangtianli2006 <zhangtianli2006@163.com>
2021-08-25 20:42:53 +08:00
580118bac2 [feature] use url query to set username
Signed-off-by: zhangtianli2006 <zhangtianli2006@163.com>
2021-08-25 20:25:55 +08:00
9 changed files with 146 additions and 1645 deletions

6
.gitignore vendored
View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -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
View 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"
}
}

View File

@ -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 `![](%asset${id})`;
}
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`);
}); });

View File

@ -1,4 +1,4 @@
@import 'proton-color.css'; @import './proton-color.css';
body { body {
font-family: sans-serif; font-family: sans-serif;

View File

@ -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, () => {