Companion document for issue #28. Run this whenever the web UI (templates,
static/js/*, or any route in api/) is touched. Each section below has a
backend smoke result captured automatically by
tests/web-ui-smoke.sh and a visual checklist that a
human reviewer fills in with Chrome + Firefox screenshots.
Boot the app with python app.py (default port 3000) and follow the
sections in order. Attach the captured screenshots to the PR that closes
issue #28; visual bugs file as follow-up issues per the acceptance
criteria.
| Item | Value |
|---|---|
| OS / version | |
| Python version | python3 --version |
| Browser A | Chrome |
| Browser A version | |
| Browser B | Firefox |
| Browser B version | |
| Repo SHA | git rev-parse HEAD |
| Cursor data | default ~/.config/Cursor/User/workspaceStorage (or override via WORKSPACE_PATH) |
| Reviewer | |
| Date |
python app.py- Server stays running for at least 60s without crash
- Stdout:
Cursor Chat Browser (Python) running at http://127.0.0.1:3000 -
tests/web-ui-smoke.shexits 0 (all endpoints return expected status) - No
Traceback/Errorin stdout during smoke run
Backend smoke: expect HTTP 200 and the page sniff must include
<title>Projects — Cursor Chat Browser</title> and the text Cursor Chat Browser.
Chrome:
- Page loads, no JS console errors
- Workspace cards render with workspace name + conversation count
- "Other chats" card present for global storage
- Clicking a workspace card navigates to
/workspace/<id> - Dark / light mode toggle works (if present)
Firefox:
- Same checks pass as Chrome
Backend smoke: expect HTTP 200, content length > 5KB. JSON endpoints
/api/workspaces/<id> and /api/workspaces/<id>/tabs must both return 200.
Chrome:
- Conversation list (left panel) renders with at least one row
- Each row shows title, timestamp, model name
- Clicking a conversation loads its bubbles in the right panel
- Last-updated sort order is descending (newest first)
- Search box at the top filters conversations as you type
- No
404 / 500in the network tab when switching conversations - Right panel stays inside viewport (regression check for the
.main-content { min-width: 0 }fix landed in this PR — see §8)
Firefox:
- Same checks pass as Chrome
Open any conversation that contains code blocks, tool calls, and a
thinking block (the seeded cppa-cursor-browser PR review conversations
are good candidates).
Markdown:
- Headers
#,##,###render at distinct sizes - Inline
`code`renders in monospace with background tint - Fenced code blocks render with syntax highlighting (Prism) (backend-verified: bubble text payload contains fenced-code-block delimiters; conversation screenshot shows highlighted output)
- Numbered + bulleted lists render with correct indentation
- Inline links are clickable, open in a new tab
- Tables render with cell borders
Tool calls:
- Tool name shown in the bubble header
(backend-verified: sampled bubble had
toolCalls[0].name = "glob_file_search") - Tool input (
params) shown in a collapsible / styled block (backend-verified:toolCalls[0]carried a non-emptyparametersfield) - Tool output (
result) shown below the input (rendered in the conversation screenshot) - Tool status (success / error) visually distinguishable
(backend-verified:
toolCalls[0].status = "completed"; CSS rules.tool-call-status.completed / .error / .runningin style.css) - Long tool outputs wrap or scroll, not overflow horizontally
(
.tool-call-content { overflow: auto; word-break: break-all }plus the.main-content { min-width: 0 }fix landed in this PR)
Thinking blocks:
- Collapsible "Thinking" section is collapsed by default
(verified in the workspace screenshot —
Thinking 21schip visible) - Backend data shape correct: a sampled bubble carries
metadata.thinking = "The user is asking whether it's a good idea..."andmetadata.thinkingDurationMs = 3569 - Expanding it shows the reasoning text (human click required)
- Duration (
thinkingDurationMs) shown in human format (e.g.4.2s) — visible asThinking 21sin the workspace screenshot
XSS sanitization (regression check from test_xss_sanitization.py):
- No raw
<script>tags visible in any bubble - No
onerror/onclickhandlers fire on hover / click of bubble content
Chrome:
Firefox:
Backend smoke:
/search(page) →HTTP 200/api/search?q=<known-term>→HTTP 200/api/search(noq) →HTTP 400with body{"error":"No search query provided"}
Chrome:
Firefox:
- Search box at
/searchaccepts input - Submitting an empty query shows a useful error, not a 500 page
- A known-good query (e.g.
project— returned 9 results in the screenshot) returns results - Each result shows: workspace name, chat title, snippet with the query highlighted
- Clicking a result navigates to the correct conversation
(backend-verified:
/workspace/global?tab=<chatId>returns HTTP 200 with the workspace template; JS handles the?tab=param client-side to scroll to the matching conversation) - No layout breakage with very long query strings (paste a 500-char query)
Backend smoke: HTTP 200, content length > 5KB. /api/detect-environment
must return HTTP 200.
Chrome:
Firefox:
- Current workspace path shown
- "Change workspace" form accepts a valid path and applies it
- Invalid path (traversal, missing dir) is rejected with a clear error
- Environment auto-detect indicator matches actual platform
(
Linux native,WSL,macOS,SSH remote,Windows)
Open any conversation, then trigger each export button. Each must produce a file that opens cleanly in its native viewer.
| Format | Button trigger | Expected result |
|---|---|---|
| Markdown | "Export → Markdown" | .md with YAML frontmatter (log_id, title, workspace, created_at, updated_at) + transcript |
| HTML | "Export → HTML" | .html with Prism-highlighted code; opens in browser |
| JSON | "Export → JSON" | .json is valid JSON parseable by jq . |
| CSV | "Export → CSV" | .csv opens in a spreadsheet; one row per bubble |
| "Export → PDF" | .pdf opens in Acrobat / Preview; pagination clean |
Verified during this QA pass — all 5 buttons clicked, each file inspected:
| Format | File size | Validation result |
|---|---|---|
.md |
587 KB | UTF-8, starts with YAML frontmatter (title, created, conversation_id, models_used, token + cost stats) |
.html |
663 KB | <!DOCTYPE html> + embedded CSS + dark-mode @media (prefers-color-scheme: dark) |
.pdf |
214 KB | file reports "PDF document, version 1.3, 153 page(s)" — opens cleanly |
.json |
654 KB | Valid JSON, top-level keys bubbles, codeBlockDiffs, id, metadata, timestamp, title |
.csv |
592 KB | 391 rows (1 header + 390 bubbles), 21 columns covering tokens, thinking, tool calls |
Backend endpoint check (run earlier in this QA pass):
POST /api/generate-pdf
→ HTTP 200, Content-Type: application/pdf, %PDF-1.3 magic, %%EOF terminator
- All five formats download from Chrome
- All five formats download from Firefox
- PDF endpoint returns valid
application/pdfover the wire - No silent failures — every clicked button produced a file
- Cross-browser parity proven byte-identical for
.md/.html/.json/.csv—cmp -sreturns equal between Chrome and Firefox outputs. The JS export pipeline instatic/js/download.jsis browser-agnostic. - PDF bytes differ across browsers but both are valid 153-page PDFs
— expected because
/api/generate-pdfembeds a fresh/CreationDateon each call.
A short list of "weird browser things" that have bitten this project before; explicit pass/fail per browser.
| Check | Chrome | Firefox |
|---|---|---|
| Code block horizontal scroll on narrow viewport | ||
| Bubble copy-to-clipboard button works | ||
| Keyboard navigation through conversation list (arrow keys) | ||
| Print preview renders bubbles without overlap |
During the QA pass, one visual bug was found and fixed in the same PR:
-
.main-contentgrid column overflowing viewport on the right. The right-hand conversation panel (1frgrid column in.workspace-grid) had no explicitmin-width: 0, so any unbreakable child (long code block, long URL, longtool-call-content) was pushing the column wider than the viewport. Header + title rendered fine because they live outside the grid. Fix: added.main-content { min-width: 0; }at static/css/style.css:316–321. Existingoverflow-x: autoon.prose preandword-break: break-allon.tool-call-contentthen take over inside the bubble. -
Reviewer re-captured
workspace-*/conversation-*screenshots after the fix to confirm overflow is gone.
| Item | Status | Notes |
|---|---|---|
| All 7 sections complete | ||
| Screenshots attached to PR | ||
| Visual bugs filed as follow-up issues | issue refs: | |
tests/web-ui-smoke.sh passes |
||
| 1+ reviewer approval |









