` with `p-4 gap-4 flex flex-col`
+ - User messages: right-aligned (`ml-auto`), `bg-secondary text-secondary-foreground`, `max-w-[75%]`, `px-4 py-2`, plain text (no markdown)
+ - Assistant messages: left-aligned, no background, `max-w-[85%]`, rendered via `
`
+ - Timestamps: `text-xs text-muted-foreground`, visible on hover (`opacity-0 group-hover:opacity-100 transition-opacity`)
+ - Auto-scroll to bottom when new messages arrive: `useEffect` with `scrollIntoView({ behavior: "smooth" })` on a bottom sentinel ref
+ - Loading: single `
` block
+ - Empty (no messages yet): light prompt text "Send a message to start the conversation."
+
+ 3. Create `ui/src/components/ChatPanel.tsx`:
+ The main right-side drawer shell that composes ChatConversationList, ChatMessageList, and ChatInput.
+
+ Implementation details:
+ - Uses `useChatPanel()` for open/close state and activeConversationId
+ - Uses `useCompany()` for selectedCompanyId
+ - Uses `useCreateConversation(companyId)` for creating new conversations
+ - Uses `useSendMessage(activeConversationId)` for sending messages
+ - Outer div: `role="complementary" aria-label="Chat"`, width transition `transition-[width] duration-100 ease-out`, width: `chatOpen ? 380 : 0`, `overflow-hidden`, `border-l border-border`, `flex-shrink-0`
+ - Internal layout when open: flex column, full height
+ - Top: header bar (48px, `border-b border-border`, "Chat" heading, plus button, close button)
+ - Middle: split horizontally — left side is `ChatConversationList` (240px wide, `bg-sidebar`, `border-r border-border`), right side is `ChatMessageList` (flex-1)
+ - When no activeConversationId: show conversation list full-width and empty state
+ - When activeConversationId set: show conversation list (240px) + message area
+ - Bottom: `ChatInput` with `onSend` that calls `sendMessage.mutateAsync(content)`, `onClose` that calls `setChatOpen(false)`, `isSubmitting` bound to `sendMessage.isPending`
+ - On "New conversation": call `createConversation.mutateAsync()`, set activeConversationId to the returned id, focus the ChatInput textarea
+ - Focus management: when panel opens, focus ChatInput. When new conversation created, focus ChatInput.
+
+ 4. Modify `ui/src/components/Layout.tsx`:
+ - Add import: `import { ChatPanel } from "./ChatPanel";`
+ - Add import: `import { useChatPanel } from "../context/ChatPanelContext";`
+ - Add import: `import { MessageSquare } from "lucide-react";`
+ - In the `Layout()` function body, add: `const { chatOpen, toggleChat, setChatOpen } = useChatPanel();`
+ - Add effect: when `chatOpen` becomes true, call `setPanelVisible(false)` to close PropertiesPanel. This prevents both panels from competing for space.
+ - In the flex row at line 416 (``), AFTER `
` (line 434), add `
`.
+ - Add a chat toggle button in the top-right area of the layout (near the theme toggle button, around line 290-310). Use: `
{chatOpen ? "Close chat" : "Open chat"}`
+
+
+ cd /Volumes/UsbNvme/repos/nexus && grep -c "ChatPanel" ui/src/components/Layout.tsx && grep -c "role=\"complementary\"" ui/src/components/ChatPanel.tsx && grep -c "IntersectionObserver" ui/src/components/ChatConversationList.tsx && grep -c "role=\"log\"" ui/src/components/ChatMessageList.tsx
+
+
+ - ui/src/components/ChatPanel.tsx contains `role="complementary"` and `aria-label="Chat"`
+ - ui/src/components/ChatPanel.tsx contains `transition-[width] duration-100 ease-out`
+ - ui/src/components/ChatPanel.tsx contains `chatOpen ? 380 : 0`
+ - ui/src/components/ChatConversationList.tsx contains ``
+ - ui/src/components/Layout.tsx contains `useChatPanel`
+ - ui/src/components/Layout.tsx contains `MessageSquare`
+ - ui/src/components/Layout.tsx contains `setPanelVisible(false)` when chat opens
+
+
Chat panel is visible in Layout, conversation list shows with infinite scroll, messages render with markdown, input sends messages. Opening chat closes PropertiesPanel.
+
+
+
+
+
+- App compiles without errors: `cd /Volumes/UsbNvme/repos/nexus && pnpm --filter @paperclipai/ui build` succeeds
+- ChatPanel renders in Layout with width transition
+- ChatConversationList uses IntersectionObserver for infinite scroll
+- ChatMessageList renders messages with ChatMarkdownMessage
+- Full test suite still passes: `pnpm test:run`
+
+
+
+- User can open/close chat panel via MessageSquare button in Layout
+- User can create a new conversation via the plus button
+- User can send a message and see it in the message list
+- Conversation list shows sorted by most recent with infinite scroll
+- Pin/archive/delete/rename work from dropdown menu
+- Opening chat closes PropertiesPanel
+- Panel state persists in localStorage
+
+
+
diff --git a/.planning/phases/21-chat-foundation/21-04-PLAN.md b/.planning/phases/21-chat-foundation/21-04-PLAN.md
new file mode 100644
index 00000000..e1567dff
--- /dev/null
+++ b/.planning/phases/21-chat-foundation/21-04-PLAN.md
@@ -0,0 +1,132 @@
+---
+phase: 21-chat-foundation
+plan: 04
+type: execute
+wave: 3
+depends_on: ["21-03"]
+files_modified: []
+autonomous: false
+requirements:
+ - CHAT-02
+ - CHAT-03
+ - CHAT-04
+ - CHAT-05
+ - CHAT-06
+ - INPUT-01
+ - INPUT-07
+ - HIST-01
+ - HIST-02
+ - HIST-03
+ - HIST-05
+ - HIST-06
+ - THEME-01
+ - THEME-02
+
+must_haves:
+ truths:
+ - "All phase 21 success criteria verified visually by user"
+ artifacts: []
+ key_links: []
+---
+
+
+Visual and functional verification checkpoint for the complete Phase 21 chat foundation.
+
+Purpose: Confirm the chat interface works end-to-end with correct theme integration, markdown rendering, and persistence before marking the phase complete.
+Output: User approval or list of issues to fix.
+
+
+
+@$HOME/.claude/get-shit-done/workflows/execute-plan.md
+@$HOME/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/phases/21-chat-foundation/21-03-SUMMARY.md
+
+
+
+
+
+ Task 1: Run full test suite and generate migration
+
+
+ server/src/__tests__/chat-service.test.ts,
+ server/src/__tests__/chat-routes.test.ts
+
+
+ 1. Run `pnpm db:generate` to generate the migration SQL if not already done. Verify generated SQL file exists under `packages/db/src/migrations/` and contains `CREATE TABLE "chat_conversations"` and `CREATE TABLE "chat_messages"` with `ON DELETE CASCADE`.
+
+ 2. Run the full test suite: `pnpm test:run`
+ All tests must pass, including the new chat-service and chat-routes tests.
+
+ 3. Run `pnpm --filter @paperclipai/ui build` to verify the UI compiles cleanly with all new components.
+
+ 4. If the dev server is not running, start it: `pnpm dev`
+
+
+ cd /Volumes/UsbNvme/repos/nexus && pnpm test:run && pnpm --filter @paperclipai/ui build
+
+
+ - `pnpm test:run` exits 0
+ - `pnpm --filter @paperclipai/ui build` exits 0
+ - Migration SQL file exists in packages/db/src/migrations/ containing "chat_conversations" and "chat_messages"
+
+ Full test suite green, UI builds clean, migration SQL generated.
+
+
+
+ Task 2: Visual and functional verification of chat foundation
+
+
+ Present the running application to the user for visual verification of all Phase 21 requirements. Ensure the dev server is running at http://localhost:5173 and the database migration has been applied.
+
+ User provides "approved" signal
+ User confirms chat foundation works correctly.
+ Complete chat foundation: right-side chat panel with conversation sidebar, markdown message rendering with syntax-highlighted code blocks and copy buttons, auto-resize input with keyboard shortcuts, theme-aware styling across all three themes, and persistent storage in PostgreSQL.
+
+ 1. Open the app at http://localhost:5173
+ 2. Click the chat icon (MessageSquare) in the top-right area of the layout — the chat panel should slide open from the right (380px wide)
+ 3. Verify the PropertiesPanel (if visible) closes when chat opens
+ 4. Click "New conversation" (Plus icon) — a conversation should appear in the sidebar list
+ 5. Type "Hello, this is a test message" and press Enter — the message should appear right-aligned in the message area
+ 6. Type a message with markdown: "Here is some **bold** and a code block:\n```typescript\nconst x: number = 42;\nconsole.log(x);\n```" and press Enter
+ 7. Verify the code block has:
+ - Syntax highlighting (colored keywords)
+ - A "typescript" language label in the top-left
+ - A copy button in the top-right (click it — should show checkmark for 2 seconds)
+ 8. Switch themes: click the theme toggle button. For each theme (Catppuccin Mocha, Tokyo Night, Catppuccin Latte):
+ - Verify the chat panel background matches the theme
+ - Verify code block highlighting colors change with the theme
+ - Verify text is readable and contrast is good
+ 9. Test keyboard shortcuts:
+ - Shift+Enter in the input should create a newline (not send)
+ - Enter should send the message
+ - Escape with text in input should clear the input
+ - Escape with empty input should close the chat panel
+ 10. Test conversation management: hover over a conversation in the sidebar, click the "..." menu:
+ - Pin the conversation (should show pin icon)
+ - Rename the conversation (should allow inline editing)
+ - Archive the conversation (should disappear from list)
+ - Create another conversation and delete it (should show "Delete this conversation?" confirmation)
+ 11. Reload the page — conversations and messages should persist
+ 12. Verify auto-resize: type multiple lines in the input — it should grow up to about 6 lines then scroll internally
+
+ Type "approved" or describe any issues found
+
+
+
+
+
+Full visual and functional verification by user covering all 14 phase requirements.
+
+
+
+User approves the chat foundation as working correctly across all themes with proper markdown rendering, keyboard shortcuts, conversation CRUD, and persistence.
+
+
+