---
phase: 25-file-system
plan: "04"
subsystem: chat-files-ui
tags: [syntax-highlighting, code-preview, file-preview, highlight.js, requirements-closure]
dependency_graph:
  requires: ["25-03"]
  provides: ["ChatCodeFilePreview", "code-file-syntax-highlight"]
  affects: ["ChatFilePreview", "REQUIREMENTS.md"]
tech_stack:
  added: ["highlight.js@11.11.1"]
  patterns: ["DOMParser-based safe HTML rendering", "hljs.highlight() with registered language set", "extToLang extension mapping"]
key_files:
  created:
    - ui/src/components/ChatCodeFilePreview.tsx
  modified:
    - ui/src/components/ChatFilePreview.tsx
    - ui/package.json
    - pnpm-lock.yaml
    - .planning/REQUIREMENTS.md
decisions:
  - "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"
metrics:
  duration: "5 min"
  completed_date: "2026-04-02"
  tasks_completed: 2
  files_changed: 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
