feat: US-008 - Chat widget — panel UI with message display

This commit is contained in:
2026-02-15 18:18:51 +00:00
parent 7ee1a2d9de
commit 273c143d5e
3 changed files with 306 additions and 5 deletions
+1 -1
View File
@@ -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."
},
{
+30
View File
@@ -17,6 +17,8 @@
- `loadEmbeddings()` and `paletteMap` (Map<id, PaletteItem>) 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 `<style>` tag with `data-chat-panel` attribute handles responsive width/height (Tailwind can't express max-height conditionally based on viewport width easily)
- `textarea` with `rows={1}` and `maxHeight: 80px` gives auto-growing feel; `resize: none` prevents manual resize
- The `ChatMessage` interface (`{ role, content }`) is ready to be extended for US-009 Gemini integration — same shape as typical LLM message format
- `onFocus/onBlur` border color transitions on the textarea give a polished input interaction
---