From ec345f60679ada27e46b1b9441fba494ecf64832 Mon Sep 17 00:00:00 2001 From: Nexus Dev Date: Wed, 1 Apr 2026 16:42:34 +0000 Subject: [PATCH] =?UTF-8?q?docs(21-02):=20complete=20markdown=20renderer?= =?UTF-8?q?=20plan=20=E2=80=94=20ChatMarkdownMessage,=20ChatCodeBlock,=20h?= =?UTF-8?q?ljs=20theme=20CSS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .planning/REQUIREMENTS.md | 4 +- .planning/ROADMAP.md | 4 +- .planning/STATE.md | 11 ++- .../21-chat-foundation/21-02-SUMMARY.md | 99 +++++++++++++++++++ 4 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 .planning/phases/21-chat-foundation/21-02-SUMMARY.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 36405eb3..0701ec3e 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -70,7 +70,7 @@ ### Theme Integration (3) - [ ] **THEME-01** — Chat interface respects the Nexus theme system (Catppuccin Mocha, Tokyo Night, Catppuccin Latte) -- [ ] **THEME-02** — Code blocks use theme-appropriate syntax highlighting colors +- [x] **THEME-02** — Code blocks use theme-appropriate syntax highlighting colors - [ ] **THEME-03** — Agent avatars/colors are visually distinguishable in all three themes ### Performance (5) @@ -161,7 +161,7 @@ The following are explicitly deferred: | PWA-07 | Phase 26 | Pending | | PWA-08 | Phase 26 | Pending | | THEME-01 | Phase 21 | Pending | -| THEME-02 | Phase 21 | Pending | +| THEME-02 | Phase 21 | Complete | | THEME-03 | Phase 22 | Pending | | PERF-01 | Phase 26 | Pending | | PERF-02 | Phase 22 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 41778625..09c889cf 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -31,12 +31,12 @@ 3. Agent messages render with full markdown: code blocks with syntax highlighting and a copy button, tables, lists, headings, links, and inline images 4. Conversations and all messages are stored in libSQL and survive a server restart 5. The chat interface applies Catppuccin Mocha, Tokyo Night, and Catppuccin Latte themes correctly; code block highlighting matches the active theme -**Plans:** 2/6 plans executed +**Plans:** 3/6 plans executed Plans: - [x] 21-00-PLAN.md — Wave 0 test stubs (chat-service, chat-routes, ChatMarkdownMessage, ChatInput) - [x] 21-01-PLAN.md — DB schema (chat_conversations + chat_messages) and shared types/validators -- [ ] 21-02-PLAN.md — Markdown renderer with rehype-highlight, code block copy button, theme CSS +- [x] 21-02-PLAN.md — Markdown renderer with rehype-highlight, code block copy button, theme CSS - [ ] 21-03-PLAN.md — Server chat service and REST API routes (CRUD + pagination) - [ ] 21-04-PLAN.md — ChatPanel shell, ChatPanelContext, ChatInput, Layout integration - [ ] 21-05-PLAN.md — Full UI wiring: API client, conversation list, message thread, infinite scroll diff --git a/.planning/STATE.md b/.planning/STATE.md index 1a66a702..452d86c9 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,8 +3,8 @@ gsd_state_version: 1.0 milestone: v1.3 milestone_name: milestone status: executing -stopped_at: Completed 21-chat-foundation-21-00-PLAN.md -last_updated: "2026-04-01T16:39:05.467Z" +stopped_at: Completed 21-chat-foundation-21-02-PLAN.md +last_updated: "2026-04-01T16:42:00.000Z" last_activity: 2026-04-01 progress: total_phases: 6 @@ -59,6 +59,7 @@ Progress: [░░░░░░░░░░] 0% *Updated after each plan completion* | Phase 01-foundation P01 | 2 | 2 tasks | 7 files | +| Phase 21-chat-foundation P02 | ~15min | 2 tasks | 5 files | | Phase 21-chat-foundation P01 | 2 | 2 tasks | 8 files | | Phase 21-chat-foundation P00 | 2 | 2 tasks | 4 files | @@ -81,6 +82,8 @@ Recent decisions affecting current work: - [Phase 21-chat-foundation]: Used object-syntax (table) => ({}) for Drizzle index callbacks to match existing codebase convention in documents.ts, agents.ts - [Phase 21-chat-foundation]: Use it.todo() (not it.skip()) for Wave 0 test scaffolding — vitest marks todos semantically, no false positives - [Phase 21-chat-foundation]: Minimal imports in test stubs — no service mocks until Plans 01-05 wire up implementations +- [Phase 21-02]: Use ExtraProps from react-markdown for ChatCodeBlock type signature to satisfy ComponentType constraint +- [Phase 21-02]: Add hljs CSS as plain rules (not @import) scoped to .dark, .theme-tokyo-night, :root selectors ### Pending Todos @@ -93,6 +96,6 @@ None yet. ## Session Continuity -Last session: 2026-04-01T16:39:05.464Z -Stopped at: Completed 21-chat-foundation-21-00-PLAN.md +Last session: 2026-04-01T16:42:00.000Z +Stopped at: Completed 21-chat-foundation-21-02-PLAN.md Resume file: None diff --git a/.planning/phases/21-chat-foundation/21-02-SUMMARY.md b/.planning/phases/21-chat-foundation/21-02-SUMMARY.md new file mode 100644 index 00000000..c34b7856 --- /dev/null +++ b/.planning/phases/21-chat-foundation/21-02-SUMMARY.md @@ -0,0 +1,99 @@ +--- +phase: 21-chat-foundation +plan: "02" +subsystem: ui +tags: [chat, markdown, syntax-highlighting, rehype-highlight, components] +dependency_graph: + requires: ["21-00"] + provides: [ChatMarkdownMessage, ChatCodeBlock] + affects: [chat-message-list] +tech_stack: + added: [rehype-highlight@7.0.2] + patterns: [TDD, ExtraProps-typed react-markdown components] +key_files: + created: + - ui/src/components/ChatMarkdownMessage.tsx + - ui/src/components/ChatCodeBlock.tsx + - ui/src/components/ChatMarkdownMessage.test.tsx + modified: + - ui/package.json + - ui/src/index.css +decisions: + - Use ExtraProps from react-markdown for ChatCodeBlock type signature to satisfy ComponentType constraint + - Add hljs CSS as plain rules (not @import) scoped to .dark, .theme-tokyo-night, :root selectors +metrics: + duration: ~15 minutes + completed_date: "2026-04-01" + tasks: 2 + files: 5 +--- + +# Phase 21 Plan 02: Chat Markdown Renderer with Syntax Highlighting Summary + +**One-liner:** rehype-highlight markdown renderer with theme-aware hljs CSS overrides and ChatCodeBlock copy button using navigator.clipboard.writeText. + +## Completed Tasks + +| Task | Name | Commit | Files | +|------|------|--------|-------| +| 1 | Install rehype-highlight and add hljs theme CSS overrides | 3e2bc1ae | ui/package.json, ui/src/index.css | +| 2 (RED) | Add failing tests for ChatMarkdownMessage | 732032a6 | ui/src/components/ChatMarkdownMessage.test.tsx | +| 2 (GREEN) | Create ChatCodeBlock and ChatMarkdownMessage components | 576e302a | ui/src/components/ChatCodeBlock.tsx, ui/src/components/ChatMarkdownMessage.tsx | + +## What Was Built + +### ChatMarkdownMessage + +Markdown renderer component wrapping react-markdown with: +- `rehypePlugins={[rehypeHighlight]}` — applies hljs token classes to code blocks +- `remarkPlugins={[remarkGfm]}` — GitHub Flavored Markdown +- `components={{ pre: ChatCodeBlock }}` — custom code block with toolbar +- `paperclip-markdown` class — picks up existing prose styles from index.css + +### ChatCodeBlock + +Pre-element override for react-markdown that provides: +- **Language label** extracted from `language-xxx` className on the child `` element +- **Copy button** using `navigator.clipboard.writeText` (Copy/Check icon toggle with 1500ms success state) +- **Plain pre fallback** when no code child present (e.g. plain preformatted text) +- Typed using `HTMLAttributes & ExtraProps` to satisfy react-markdown's `ComponentType` constraint + +### Highlight.js Theme CSS (index.css) + +Added 60 lines of CSS overrides covering 15 token types across three themes: +- `.dark .hljs*` — Catppuccin Mocha palette (#cba6f7 keywords, #a6e3a1 strings, etc.) +- `.theme-tokyo-night .hljs*` — Tokyo Night palette (#bb9af7 keywords, #9ece6a strings, etc.) +- `:root .hljs*` — Catppuccin Latte palette (#8839ef keywords, #40a02b strings, etc.) + +No external CSS file imports — all overrides are scoped custom properties, avoiding FOUC and theme conflicts. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Fixed ChatCodeBlock TypeScript type signature** +- **Found during:** Task 2 — TypeScript type check after implementation +- **Issue:** `[key: string]: unknown` index signature incompatible with `ClassAttributes & HTMLAttributes & ExtraProps` from react-markdown +- **Fix:** Changed component props type to `HTMLAttributes & ExtraProps` (importing `ExtraProps` from `react-markdown`) +- **Files modified:** `ui/src/components/ChatCodeBlock.tsx` +- **Commit:** 576e302a (included in same commit) + +## Verification Results + +- `pnpm --filter @paperclipai/ui exec -- tsc --noEmit` — PASS (0 errors) +- `pnpm vitest run ui/src/components/ChatMarkdownMessage.test.tsx` — PASS (4/4 tests) +- `rehype-highlight` present in ui/package.json — PASS +- `.dark .hljs-keyword { color: #cba6f7; }` in index.css — PASS +- `.theme-tokyo-night .hljs-keyword { color: #bb9af7; }` in index.css — PASS +- `:root .hljs-keyword { color: #8839ef; }` in index.css — PASS +- `navigator.clipboard.writeText` in ChatCodeBlock — PASS +- `aria-label="Copy code"` / `"Copied!"` in ChatCodeBlock — PASS +- Language extraction from `language-xxx` pattern — PASS + +## Self-Check: PASSED + +All files created and commits verified. + +## Known Stubs + +None — both components are fully wired. ChatMarkdownMessage uses rehype-highlight for real syntax highlighting; ChatCodeBlock calls navigator.clipboard.writeText for real copy functionality.