feat: implement full context-scoped AI chat UI in AIChatPanel
- Added AIChatPanel component with context header, user and AI message handling. - Integrated streaming responses via IPC and error handling for chat mutations. - Enhanced user experience with input handling and auto-scrolling features. - Updated AppShell to derive AI chat context from the current route. - Introduced ScrollArea component for better scrolling behavior in various dialogs. - Added support for Tailwind typography and improved global styles. - Updated project and task dialogs to utilize ScrollArea for better UX.
This commit is contained in:
26
progress.txt
26
progress.txt
@@ -416,3 +416,29 @@
|
||||
- The graph is compiled once via `buildGraph()` singleton — no per-request overhead for graph construction
|
||||
- Architecture: agent logic (LangGraph) is now fully decoupled from the LLM provider. Adding a new provider only requires a new factory function in `llm.ts`
|
||||
---
|
||||
|
||||
## 2026-02-23 - US-020
|
||||
- What was implemented:
|
||||
- Full context-scoped AI chat UI in `AIChatPanel` component, replacing the "coming soon" placeholder
|
||||
- Two-mode layout: empty state (centered input) and chat state (messages + pinned bottom input)
|
||||
- Context header: `Badge` (variant=outline) showing "Chatting about: [Project Name]" or "Global workspace"
|
||||
- Context derived in AppShell from `currentPath` + `searchObj['projectId']`; project name fetched via `trpc.projects.get` query
|
||||
- User messages: right-aligned `Card` components
|
||||
- AI messages: left-aligned plain text (no Card) with `Sparkles` icon + bold "Adiuva" header line
|
||||
- Streaming: subscribes to `window.electronAI.onStreamChunk` IPC channel before firing `trpc.ai.chat.mutate()`; tokens accumulate in `streamingContent` state via `useRef` pattern
|
||||
- Loading indicator: `Skeleton` lines (w-48 + w-32) shown below Adiuva header while waiting for first token
|
||||
- Error handling: mutation errors and `{ error }` responses display in `Card` with `border-destructive` styling
|
||||
- Session-only history: `useEffect` on `curtainOpen` prop clears all messages, input, and streaming state when curtain closes
|
||||
- Scroll behavior: after user sends, scrolls user message to top of visible area; does NOT auto-scroll during AI streaming
|
||||
- Input: `Textarea` matching Figma (white bg, border #d4d4d4, shadow-lg, min-h 109px, "Ask me anything..."), Send `Button` (default variant, Send icon + label) absolute bottom-right
|
||||
- Enter sends, Shift+Enter for newline
|
||||
- Extracted `ChatInput` sub-component for reuse between empty and chat states
|
||||
- Typecheck passes (zero errors), no new lint errors introduced
|
||||
- Files changed: `src/renderer/components/ai/AIChatPanel.tsx`, `src/renderer/components/layout/AppShell.tsx`, `prd.json`, `progress.txt`
|
||||
- **Learnings for future iterations:**
|
||||
- `window.electronAI.onStreamChunk` returns an unsubscribe function — subscribe before firing the mutation, unsubscribe in error/completion handlers
|
||||
- Use `useRef` for accumulating streaming content (`streamingContentRef.current += token`) to avoid stale closure issues in the stream callback, then sync to state with `setStreamingContent(streamingContentRef.current)`
|
||||
- `trpc.ai.chat.mutate()` returns `{ response, error? }` — the `error` field is `string | undefined`, so must narrow before passing to typed state (assign to a `const` first)
|
||||
- `trpc.projects.get` query with `enabled: !!projectId` + `id: projectId ?? ''` avoids both the non-null assertion lint warning and unnecessary queries
|
||||
- For scroll-to-user-message UX: track the last user message with a ref and use `scrollIntoView({ behavior: 'smooth', block: 'start' })` — do NOT auto-scroll on AI streaming to let the user read from the top
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user