Add a user guide, reachable in-app from Help in the account menu

USER_GUIDE.md at the repo root is the single source of truth --
frontend/public/USER_GUIDE.md symlinks to it so the same file is both
readable directly in the repo and served by the app, rendered on a new
/help page reusing the existing markdown renderer. Scoped to regular
member features (messaging, rooms, attachments, notifications,
profile); room admin/site admin features are intentionally left out.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 13:58:05 -06:00
co-authored by Claude Sonnet 5
parent 344eb7ebf1
commit aadf620014
6 changed files with 270 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
../../USER_GUIDE.md
+9
View File
@@ -11,6 +11,7 @@ import { ForgotPasswordPage } from './pages/ForgotPasswordPage'
import { ResetPasswordPage } from './pages/ResetPasswordPage'
import { ChatShellPage } from './pages/ChatShellPage'
import { AdminPage } from './pages/AdminPage'
import { HelpPage } from './pages/HelpPage'
function AppRoutes() {
const routes = (
@@ -43,6 +44,14 @@ function AppRoutes() {
</AdminRoute>
}
/>
<Route
path="/help"
element={
<ProtectedRoute>
<HelpPage />
</ProtectedRoute>
}
/>
<Route path="*" element={<Navigate to="/rooms" replace />} />
</Routes>
)
+10
View File
@@ -122,6 +122,16 @@ export function TopBar() {
>
Profile settings
</button>
<button
type="button"
role="menuitem"
onClick={() => {
setMenuOpen(false)
navigate('/help')
}}
>
Help
</button>
<button
type="button"
role="menuitem"
+36
View File
@@ -0,0 +1,36 @@
.help-page {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
background: var(--ds-void);
}
.help-body {
flex: 1;
overflow-y: auto;
padding: var(--sp-6) var(--sp-8);
max-width: 760px;
width: 100%;
margin: 0 auto;
}
.help-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: var(--sp-6);
}
.help-header h1 {
font-size: 1.3rem;
margin: 0;
}
.help-loading {
color: var(--ds-muted);
}
.help-content a[href^='#'] {
word-break: normal;
}
+55
View File
@@ -0,0 +1,55 @@
import { useEffect, useState } from 'react'
import Markdown from 'markdown-to-jsx'
import { Link } from 'react-router-dom'
import { MARKDOWN_OPTIONS } from '../components/MessageContent'
import { TopBar } from '../components/TopBar'
import './HelpPage.css'
// Single source of truth is the repo-root USER_GUIDE.md -- frontend/public/
// symlinks to it, so this fetches the same file a developer sees when
// browsing the repo, rather than duplicating its content into the bundle.
const GUIDE_URL = '/USER_GUIDE.md'
export function HelpPage() {
const [content, setContent] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
let cancelled = false
fetch(GUIDE_URL)
.then((res) => {
if (!res.ok) throw new Error(`Failed to load guide (${res.status})`)
return res.text()
})
.then((text) => {
if (!cancelled) setContent(text)
})
.catch((err) => {
if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load guide')
})
return () => {
cancelled = true
}
}, [])
return (
<div className="help-page">
<TopBar />
<div className="help-body">
<div className="help-header">
<h1>Help</h1>
<Link to="/rooms" className="btn-secondary">
Back to chat
</Link>
</div>
{error && <p className="admin-error">{error}</p>}
{!error && content === null && <p className="help-loading">Loading</p>}
{!error && content !== null && (
<div className="message-text help-content">
<Markdown options={MARKDOWN_OPTIONS}>{content}</Markdown>
</div>
)}
</div>
</div>
)
}