Private
Public Access
Renders message content with markdown-to-jsx: bold/italic/strikethrough, inline code, fenced code blocks, blockquotes, lists (incl. nested and task lists), tables, footnotes, headings, and highlight (==text==). Raw HTML in message content is parsed to escaped literal text rather than rendered (disableParsingRawHTML), which is the XSS mitigation for this being user-generated content -- verified against both <img onerror> and <script> probes. Markdown image embeds degrade to a link instead of an <img>, since the app already has a first-class image upload and a second silent remote-image-embed path would duplicate it and leak the viewer's IP to arbitrary URLs. The inline message-edit control is upgraded from a single-line <input> to a <textarea> so multi-line markdown can actually be edited without losing newlines, mirroring the Composer's Enter-sends/Shift+Enter- newlines convention. Since CommonMark treats a single newline as a soft break (collapses to a space) rather than a visible line break, added a small code-fence- aware preprocessor that converts single newlines to hard breaks -- without it, existing multi-line messages sent via the Composer's Shift+Enter would silently collapse onto one line. Also fixes heading levels rendering at an identical capped size (should still step down by level, just capped lower than default), and adds CSS for markdown constructs the library already parsed but hadn't been styled for the dark theme: table borders, highlight/mark color, task-list checkbox accent, and footnote divider.
KeepItTalking frontend (Phase 1)
React + Vite PWA. Login, room list, and chat views wired to the backend's
REST API and /ws/chat WebSocket endpoint. See ../README.md
and ../backend/README.md for full local setup.
Dev
npm install
npm run dev
The dev server proxies /api and /ws to http://localhost:8000 (see
vite.config.ts), so the backend must be running for anything beyond the
login page to work.
Build
npm run build
Generates the PWA manifest and service worker via vite-plugin-pwa into dist/.
Layout
src/
main.tsx, App.tsx routes: /login, /rooms, /rooms/:roomId
api/ fetch wrappers (client, auth, rooms)
ws/useChatSocket.ts WebSocket hook (join/send/receive)
context/AuthContext.tsx current-user state, hydrated via GET /api/auth/me
components/ ProtectedRoute, RoomListItem, MessageList, MessageInput
pages/ LoginPage, RoomListPage, ChatRoomPage