diff --git a/chat.py b/chat.py index 7e97370..eec502f 100644 --- a/chat.py +++ b/chat.py @@ -6,6 +6,10 @@ Minimal multi-user chat. No dependencies beyond stdlib. Client: python3 chat.py [port] The server also exposes a web UI at /chat (HTTP on the same port). + +CLI client commands: + /file Upload a file to the chat + /quit Disconnect """ import asyncio @@ -21,14 +25,16 @@ import html import urllib.parse PORT = 9999 -VERSION = "5" +VERSION = "6" TUNNEL_TARGET = ("185.208.174.152", 22) +MAX_UPLOAD = 10 * 1024 * 1024 # 10 MB # ── Server ────────────────────────────────────────────────────────────── clients: dict[asyncio.StreamWriter, str] = {} # TCP clients sse_queues: list[asyncio.Queue] = [] # web clients history: list[dict] = [] +uploaded_files: dict[str, bytes] = {} # file_id -> raw bytes async def broadcast(msg: dict): @@ -70,24 +76,45 @@ CHAT_HTML = r""" body { background: #1a1a2e; color: #e0e0e0; font-family: 'Courier New', monospace; display: flex; flex-direction: column; height: 100vh; } #messages { flex: 1; overflow-y: auto; padding: 12px; } - .msg { padding: 3px 0; } + .msg { padding: 3px 0; white-space: pre-wrap; word-wrap: break-word; } + .msg code { background: #2a2a4a; padding: 1px 5px; border-radius: 3px; color: #f8c555; } + .msg pre { background: #12122a; border: 1px solid #333; border-radius: 4px; + padding: 8px; margin: 4px 0; overflow-x: auto; } + .msg pre code { background: none; padding: 0; color: #e0e0e0; } + .msg strong { color: #fff; } + .msg em { color: #ccc; } + .msg a.auto-link { color: #67c7eb; } .ts { color: #666; } .sys { color: #5e9ca0; font-style: italic; } - #bottom { display: flex; padding: 8px; gap: 8px; border-top: 1px solid #333; background: #16213e; } + .file-link { display: inline-block; background: #0f3460; border: 1px solid #444; + padding: 4px 10px; border-radius: 4px; margin: 2px 0; color: #67c7eb; + text-decoration: none; } + .file-link:hover { background: #1a4a80; } + #bottom { display: flex; padding: 8px; gap: 8px; border-top: 1px solid #333; + background: #16213e; align-items: flex-end; } #name { width: 100px; padding: 8px; background: #0f3460; border: 1px solid #444; - color: #e0e0e0; border-radius: 4px; } + color: #e0e0e0; border-radius: 4px; align-self: flex-end; } #input { flex: 1; padding: 8px; background: #0f3460; border: 1px solid #444; - color: #e0e0e0; border-radius: 4px; } + color: #e0e0e0; border-radius: 4px; resize: none; min-height: 38px; + max-height: 200px; font-family: inherit; font-size: inherit; line-height: 1.4; } #send { padding: 8px 16px; background: #e94560; border: none; color: #fff; - border-radius: 4px; cursor: pointer; } + border-radius: 4px; cursor: pointer; align-self: flex-end; } #send:hover { background: #c73e54; } + #file-btn { padding: 8px 10px; background: #0f3460; border: 1px solid #444; color: #e0e0e0; + border-radius: 4px; cursor: pointer; align-self: flex-end; font-size: 1.1em; } + #file-btn:hover { background: #1a4a80; } + #file-input { display: none; } + .hint { color: #555; font-size: 0.75em; padding: 2px 12px; }
+
Shift+Enter for newline · Enter to send
- + +