From d20dce57bad899a19bbe9cdf0061bd73f25099da Mon Sep 17 00:00:00 2001 From: Nexus Dev Date: Wed, 1 Apr 2026 22:34:39 +0000 Subject: [PATCH] =?UTF-8?q?docs(24-02):=20complete=20UI=20components=20pla?= =?UTF-8?q?n=20=E2=80=94=20hooks,=20API=20methods,=20search/bookmark/branc?= =?UTF-8?q?h=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .planning/ROADMAP.md | 6 +- .planning/STATE.md | 16 ++- .../24-02-SUMMARY.md | 120 ++++++++++++++++++ 3 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 .planning/phases/24-search-history-branching/24-02-SUMMARY.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 848c084c..1b032ced 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -94,12 +94,12 @@ Plans: 2. User can bookmark any message and later filter or navigate to bookmarked messages 3. Editing a message that already has a response creates a new branch; both the original and the new branch are preserved and the user can switch between them 4. User can export any conversation as a Markdown file or as a JSON file containing all messages and metadata -**Plans:** 2/4 plans executed +**Plans:** 3/4 plans executed Plans: - [x] 24-00-PLAN.md — DB migrations (branch columns, tsvector+GIN, bookmarks table), shared types, Wave 0 test stubs - [x] 24-01-PLAN.md — Server: search, bookmark, branch, export service methods and Express routes -- [ ] 24-02-PLAN.md — UI: ChatSearchDialog, ChatMessageBookmark, ChatBookmarkList, ChatBranchSelector, API client, hooks +- [x] 24-02-PLAN.md — UI: ChatSearchDialog, ChatMessageBookmark, ChatBookmarkList, ChatBranchSelector, API client, hooks - [ ] 24-03-PLAN.md — Wiring: ChatPanel integration, CommandPalette search item, scroll-to-message, bookmark toggle, branch-on-edit **UI hint**: yes @@ -214,6 +214,6 @@ All 65 v1 requirements are mapped to exactly one phase. No orphans. | 21. Chat Foundation | v1.3 | 7/7 | Complete | 2026-04-01 | | 22. Agent Streaming | v1.3 | 6/6 | Complete | 2026-04-01 | | 23. Brainstormer Flow | v1.3 | 4/4 | Complete | 2026-04-01 | -| 24. Search, History & Branching | v1.3 | 2/4 | In Progress| | +| 24. Search, History & Branching | v1.3 | 3/4 | In Progress| | | 25. File System | v1.3 | 0/? | Not started | - | | 26. PWA & Performance | v1.3 | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index d496b8e2..27c9bd35 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v1.3 milestone_name: milestone status: executing -stopped_at: Completed 24-search-history-branching-24-01-PLAN.md -last_updated: "2026-04-01T22:33:10.664Z" +stopped_at: Completed 24-search-history-branching-24-02-PLAN.md +last_updated: "2026-04-01T22:34:30.283Z" last_activity: 2026-04-01 progress: total_phases: 6 completed_phases: 3 total_plans: 21 - completed_plans: 19 + completed_plans: 20 percent: 100 --- @@ -26,7 +26,7 @@ See: .planning/PROJECT.md (updated 2026-03-30) ## Current Position Phase: 24 (search-history-branching) — EXECUTING -Plan: 3 of 4 +Plan: 4 of 4 Status: Ready to execute Last activity: 2026-04-01 @@ -76,6 +76,7 @@ Progress: [██████████] 100% | Phase 23-brainstormer-flow P03 | 5 | 2 tasks | 4 files | | Phase 24-search-history-branching P00 | 5 | 2 tasks | 9 files | | Phase 24-search-history-branching P01 | 12 | 2 tasks | 2 files | +| Phase 24-search-history-branching P02 | 3min | 2 tasks | 7 files | ## Accumulated Context @@ -124,6 +125,9 @@ Recent decisions affecting current work: - [Phase 24-search-history-branching]: content_search tsvector column omitted from Drizzle schema — Postgres-generated stored column queried via raw sql`` only - [Phase 24-search-history-branching]: searchMessages returns empty items early when query.trim() is empty — avoids PostgreSQL error on blank tsquery - [Phase 24-search-history-branching]: exportConversation uses this.getConversation() to reuse notFound guard without duplicating query logic +- [Phase 24-search-history-branching]: Used HighlightedText React component instead of innerHTML for term highlighting — eliminates XSS surface +- [Phase 24-search-history-branching]: exportConversation returns URL string (not fetch call) since server sends file download via browser navigation +- [Phase 24-search-history-branching]: shouldFilter=false pattern on Command for server-side FTS — all future search dialogs should follow this ### Pending Todos @@ -136,6 +140,6 @@ None yet. ## Session Continuity -Last session: 2026-04-01T22:33:10.661Z -Stopped at: Completed 24-search-history-branching-24-01-PLAN.md +Last session: 2026-04-01T22:34:30.280Z +Stopped at: Completed 24-search-history-branching-24-02-PLAN.md Resume file: None diff --git a/.planning/phases/24-search-history-branching/24-02-SUMMARY.md b/.planning/phases/24-search-history-branching/24-02-SUMMARY.md new file mode 100644 index 00000000..9bba5024 --- /dev/null +++ b/.planning/phases/24-search-history-branching/24-02-SUMMARY.md @@ -0,0 +1,120 @@ +--- +phase: 24-search-history-branching +plan: 02 +subsystem: ui +tags: [react, tanstack-query, cmdk, lucide-react, typescript] + +# Dependency graph +requires: + - phase: 24-00 + provides: shared types (ChatMessageSearchResult, ChatBookmarkWithMessage, ChatBookmarkToggleResponse, ChatBookmarkListResponse, ChatConversation with branch fields) + +provides: + - chatApi.searchMessages — FTS endpoint client method + - chatApi.toggleBookmark — bookmark toggle client method + - chatApi.getBookmarks — bookmark list client method + - chatApi.branchConversation — branch creation client method + - chatApi.listBranches — branch list client method + - chatApi.exportConversation — export download URL helper + - useChatSearch hook — debounced FTS query hook with placeholderData + - useChatBookmarks hook — bookmark list query hook + - useToggleBookmark hook — bookmark mutation with cache invalidation + - ChatSearchDialog component — CommandDialog FTS overlay with term highlighting + - ChatMessageBookmark component — bookmark toggle icon button + - ChatBookmarkList component — filterable scrollable bookmark list + - ChatBranchSelector component — horizontal branch picker bar + +affects: [24-03] + +# Tech tracking +tech-stack: + added: [] + patterns: + - "useChatSearch: placeholderData=(prev)=>prev keeps previous results while new query loads" + - "ChatSearchDialog: shouldFilter=false on Command for server-side FTS (not cmdk client filtering)" + - "HighlightedText: React component splits text into segments to avoid unsafe innerHTML XSS risk" + - "useToggleBookmark: invalidates both [chat, bookmarks] and [chat, search] queries after toggle" + +key-files: + created: + - ui/src/hooks/useChatSearch.ts + - ui/src/hooks/useChatBookmarks.ts + - ui/src/components/ChatSearchDialog.tsx + - ui/src/components/ChatMessageBookmark.tsx + - ui/src/components/ChatBookmarkList.tsx + - ui/src/components/ChatBranchSelector.tsx + modified: + - ui/src/api/chat.ts + +key-decisions: + - "Used HighlightedText React component instead of innerHTML for term highlighting — eliminates XSS surface" + - "exportConversation returns URL string (not fetch call) since server sends file download via browser navigation" + - "ChatBranchSelector renders null when branches.length === 0 — no empty bar shown" + +patterns-established: + - "shouldFilter=false pattern: all server-side search dialogs set this to bypass cmdk client filtering" + - "Cache invalidation pattern: bookmark mutations invalidate both bookmarks and search query caches" + +requirements-completed: [CHAT-07, CHAT-13, CHAT-14, HIST-04, HIST-12, PERF-04] + +# Metrics +duration: 3min +completed: 2026-04-01 +--- + +# Phase 24 Plan 02: Search History Branching UI Summary + +**Six chatApi methods, two React Query hooks, and four components — search/bookmark/branch UI layer built independently from server routes, ready for wiring in Plan 03.** + +## Performance + +- **Duration:** ~3 min +- **Started:** 2026-04-01T22:29:56Z +- **Completed:** 2026-04-01T22:32:55Z +- **Tasks:** 2 completed +- **Files modified:** 7 + +## Accomplishments + +- Extended `chatApi` in `ui/src/api/chat.ts` with six new methods covering search, bookmarks, branches, and export +- Created `useChatSearch` and `useChatBookmarks`/`useToggleBookmark` TanStack Query hooks with proper cache invalidation +- Built four UI components: `ChatSearchDialog` (CommandDialog FTS overlay), `ChatMessageBookmark` (toggle icon button), `ChatBookmarkList` (scrollable list with skeletons/empty state), `ChatBranchSelector` (horizontal branch picker) +- UI build passes cleanly with no TypeScript errors + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: API client methods and React Query hooks** - `e1ab0ca0` (feat) +2. **Task 2: UI components** - `11145afe` (feat) + +**Plan metadata:** (pending — docs commit) + +## Files Created/Modified + +- `ui/src/api/chat.ts` — Added searchMessages, toggleBookmark, getBookmarks, branchConversation, listBranches, exportConversation methods; added shared type imports +- `ui/src/hooks/useChatSearch.ts` — TanStack Query hook for debounced FTS; enabled when query >= 2 chars; placeholderData keeps previous results during load; 30s staleTime +- `ui/src/hooks/useChatBookmarks.ts` — useChatBookmarks query hook + useToggleBookmark mutation; invalidates both bookmarks and search caches on success +- `ui/src/components/ChatSearchDialog.tsx` — CommandDialog-based FTS overlay; shouldFilter=false for server-side search; HighlightedText component splits text into marked segments without XSS risk +- `ui/src/components/ChatMessageBookmark.tsx` — Ghost icon button (h-6 w-6 / h-3.5 w-3.5) matching ChatMessageActions sizing; fill-current on bookmarked state; aria-label toggles +- `ui/src/components/ChatBookmarkList.tsx` — Scrollable list using useChatBookmarks; skeleton loading placeholders (matching ChatConversationList pattern); Bookmark icon empty state +- `ui/src/components/ChatBranchSelector.tsx` — Horizontal bar with GitBranch icon; "Original" button for parent conv; branch buttons with bg-accent for active; renders null when no branches + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 2 - Security] Replaced innerHTML approach with React HighlightedText component for term highlighting** +- **Found during:** Task 2 implementation +- **Issue:** Security hook flagged setting innerHTML for XSS risk when highlighting matched terms +- **Fix:** Extracted HighlightedText React component that splits text into plain/highlighted segments using regex, rendering mark elements directly without setting innerHTML +- **Files modified:** ui/src/components/ChatSearchDialog.tsx +- **Commit:** 11145afe + +## Known Stubs + +None. All components accept callback props (onNavigate, onToggle, onSelectBranch) — no routing or state is hardcoded inside. Data fetching is wired to real chatApi endpoints. Plan 03 will integrate these into ChatPanel. + +## Self-Check: PASSED + +All 7 files confirmed present on disk. Both task commits (e1ab0ca0, 11145afe) confirmed in git log.