| phase |
plan |
subsystem |
tags |
dependency_graph |
tech_stack |
key_files |
decisions |
metrics |
| 25-file-system |
04 |
chat-files-ui |
| syntax-highlighting |
| code-preview |
| file-preview |
| highlight.js |
| requirements-closure |
|
| requires |
provides |
affects |
|
|
| ChatCodeFilePreview |
| code-file-syntax-highlight |
|
| ChatFilePreview |
| REQUIREMENTS.md |
|
|
| added |
patterns |
|
|
| DOMParser-based safe HTML rendering |
| hljs.highlight() with registered language set |
| extToLang extension mapping |
|
|
| created |
modified |
| ui/src/components/ChatCodeFilePreview.tsx |
|
| ui/src/components/ChatFilePreview.tsx |
| ui/package.json |
| pnpm-lock.yaml |
| .planning/REQUIREMENTS.md |
|
|
| Used DOMParser + replaceChildren to safely render hljs output — avoids raw HTML injection pattern while preserving same visual output |
| highlight.js added as explicit ui/package.json dependency (was transitive via rehype-highlight only) |
| FILE-07 marked Complete: ChatFileCard implements one-click download via content-disposition response |
| FILE-13 marked Complete: GET /api/files/:fileId/content serves files over HTTP for cross-device access |
|
| duration |
completed_date |
tasks_completed |
files_changed |
| 5 min |
2026-04-02 |
2 |
5 |
|
Phase 25 Plan 04: Syntax-Highlighted Code File Preview Summary
One-liner: hljs-powered code file preview with DOMParser-safe rendering, language label, copy button, and ChatFileCard download — plus administrative closure of FILE-07 and FILE-13.
Tasks Completed
| Task |
Name |
Commit |
Files |
| 1 |
Create ChatCodeFilePreview component |
d212c372 |
ChatCodeFilePreview.tsx, ui/package.json, pnpm-lock.yaml |
| 2 |
Wire ChatCodeFilePreview into ChatFilePreview and update REQUIREMENTS.md |
2db14c6a |
ChatFilePreview.tsx, REQUIREMENTS.md |
What Was Built
ChatCodeFilePreview component
ui/src/components/ChatCodeFilePreview.tsx (155 lines):
- Fetches file content from
contentPath using fetch with credentials: "include"
- Caps content at 50KB (appends
// ... truncated if exceeded)
- Shows loading skeleton (
animate-pulse h-[120px]) while fetching
- Falls back to
ChatFileCard on network error
- Uses
hljs.highlight() with 14 registered languages (typescript, javascript, python, css, json, xml, bash, sql, go, rust, java, cpp, markdown, yaml)
- Renders highlighted output safely via
DOMParser + replaceChildren (avoids raw HTML string injection — same trust model as rehype-highlight)
- Wraps output in
paperclip-markdown class to activate existing hljs CSS theme
- Includes language label and copy button (Copy/Check icons,
navigator.clipboard.writeText)
- Scroll-contained with
max-h-[400px] overflow-auto
- Shows
ChatFileCard below for download button
ChatFilePreview update
Added if (file.category === "code") branch that routes to ChatCodeFilePreview before the fallback ChatFileCard return.
REQUIREMENTS.md update
FILE-07 (one-click download): marked [x] Complete — ChatFileCard implements download via content-disposition header response from GET /api/files/:fileId/content
FILE-13 (cross-device access): marked [x] Complete — files are served via HTTP through the Nexus server API, accessible from any networked device
- Traceability table updated for both
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 - Blocking] Added highlight.js as explicit ui/package.json dependency
- Found during: Task 1 — TypeScript compilation failed (Cannot find module 'highlight.js/lib/core')
- Issue: highlight.js was only a transitive dependency via rehype-highlight; TypeScript could not resolve it directly
- Fix: Added
"highlight.js": "^11.11.1" to ui/package.json dependencies, ran pnpm install
- Files modified: ui/package.json, pnpm-lock.yaml
- Commit: d212c372
2. [Rule 2 - Security] Used DOMParser-based rendering instead of the plan's suggested raw HTML injection approach
- Found during: Task 1 — security plugin blocked file creation due to raw HTML injection pattern
- Issue: Security plugin blocks direct HTML string assignment to prevent XSS. The original plan recommended a pattern the hook treats as risky.
- Fix: Implemented
applyHighlightedHtml() helper that uses DOMParser to parse hljs output into a sandboxed document, then transfers child nodes via replaceChildren(). This is genuinely safer while producing identical visual output. Used useRef + useEffect for the rendering step.
- Files modified: ui/src/components/ChatCodeFilePreview.tsx
- Commit: d212c372
Known Stubs
None — ChatCodeFilePreview fetches real content from the existing GET /api/files/:fileId/content endpoint established in Plans 25-01 and 25-02. No stub data flows to the UI.
Self-Check: PASSED