From 273c143d5e41bfab703c05837198bb00d3802aae Mon Sep 17 00:00:00 2001 From: Andy Charlwood Date: Sun, 15 Feb 2026 18:18:51 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20US-008=20-=20Chat=20widget=20=E2=80=94?= =?UTF-8?q?=20panel=20UI=20with=20message=20display?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ralph/prd.json | 2 +- Ralph/progress.txt | 30 ++++ src/components/ChatWidget.tsx | 279 +++++++++++++++++++++++++++++++++- 3 files changed, 306 insertions(+), 5 deletions(-) diff --git a/Ralph/prd.json b/Ralph/prd.json index 3befe83..bb6075c 100644 --- a/Ralph/prd.json +++ b/Ralph/prd.json @@ -152,7 +152,7 @@ "Verify in browser using dev-browser skill" ], "priority": 8, - "passes": false, + "passes": true, "notes": "Use the design system tokens: var(--surface) for panel bg, var(--border-light) for borders, var(--text-primary) for text, var(--accent) for user bubble bg at 10% opacity, font-ui for body text, font-geist for timestamps. The placeholder assistant response can be a static string like 'AI chat coming soon — this is a preview of the chat interface.' This lets us verify the full UI before wiring up Gemini." }, { diff --git a/Ralph/progress.txt b/Ralph/progress.txt index 01a5cc6..c6e590e 100644 --- a/Ralph/progress.txt +++ b/Ralph/progress.txt @@ -17,6 +17,8 @@ - `loadEmbeddings()` and `paletteMap` (Map) are precomputed via `useMemo` — no re-computation on each search - ChatWidget is mounted in DashboardLayout alongside CommandPalette and DetailPanel — z-index 90 (below command palette z-1000) - `prefersReducedMotion` pattern: read `window.matchMedia` at module level, use in framer-motion variants to skip animation +- ChatWidget stores messages as `Array<{ role: 'user' | 'assistant', content: string }>` — same shape as LLM message format, ready for Gemini integration +- ChatWidget `isOpen` state controls both panel visibility and button icon (MessageCircle ↔ X) — panel rendering handled by AnimatePresence --- @@ -135,3 +137,31 @@ - The `isOpen` state lives in ChatWidget — US-008 will add the panel UI inside the same component - Hover effects use `onMouseEnter/Leave` with direct style mutation (same pattern as other dashboard components) --- + +## 2026-02-15 - US-008 +- Built chat panel UI inside `ChatWidget.tsx` with header, message area, and input +- Panel opens above the floating button with scale+opacity entrance/exit animation via framer-motion `AnimatePresence` +- Messages stored as `Array<{ role: 'user' | 'assistant', content: string }>` in component state +- User messages right-aligned in teal-tinted bubbles (`var(--accent-light)` bg, `var(--accent-border)` border) +- Assistant messages left-aligned in light gray bubbles (`var(--bg-dashboard)` bg, `var(--border-light)` border) +- Message corner radii differ: user bubbles have small bottom-right radius, assistant bubbles small bottom-left (conversational feel) +- Input area: textarea with Enter to submit, Shift+Enter for newline. Send button enabled/disabled based on input content +- Empty state shows placeholder text when no messages yet +- Auto-scrolls to latest message via `useRef` + `scrollIntoView` +- Auto-focuses input when panel opens (200ms delay for animation) +- Responsive: on mobile (<640px), panel is full-width bottom sheet with rounded top corners; on desktop, 380px wide positioned above the button +- Panel entrance: scale(0.95)+opacity(0) → scale(1)+opacity(1), 200ms. Exit: reverse, 150ms +- Respects `prefers-reduced-motion` — skips all animation +- Close button in header triggers `setIsOpen(false)` (same as floating button toggle) +- Submitting appends both user message and placeholder assistant response to state +- Typecheck, lint (0 errors), and build all pass +- Browser verified: panel opens/closes correctly, messages display, input works, Enter submits, close button works +- Files changed: `src/components/ChatWidget.tsx` +- **Learnings for future iterations:** + - `AnimatePresence` with `key` prop on the panel div is needed for exit animations to work + - Panel uses `transformOrigin: 'bottom right'` for natural scale animation from the button corner + - CSS-in-JS ` +
+ {/* Header */} +
+ + Ask about Andy + + +
+ + {/* Messages area */} +
+ {messages.length === 0 && ( +
+ Ask me anything about Andy's experience, skills, or projects. +
+ )} + + {messages.map((msg, i) => ( +
+
+ {msg.content} +
+
+ ))} +
+
+ + {/* Input area */} +
+