---
phase: 21-chat-foundation
plan: 05
subsystem: ui
tags: [react, tanstack-query, infinite-scroll, chat, shadcn, intersection-observer]

# Dependency graph
requires:
  - phase: 21-03
    provides: Server API (conversation and message CRUD routes)
  - phase: 21-04
    provides: ChatPanel shell, ChatInput, ChatMessage, ChatMarkdownMessage, ChatPanelContext

provides:
  - chatApi object with 7 fetch methods (listConversations, createConversation, getConversation, updateConversation, deleteConversation, listMessages, postMessage)
  - useChatConversations hook with useInfiniteQuery + placeholderData + CRUD mutations
  - useChatMessages hook with useInfiniteQuery + sendMutation + flattened/reversed messages array
  - ChatConversationItem component with DropdownMenu actions (Rename, Pin/Unpin, Archive, Delete)
  - ChatConversationList component with IntersectionObserver infinite scroll, delete confirmation Dialog, loading skeletons
  - ChatMessageList component with auto-scroll-to-bottom on new messages
  - ChatPanel fully wired: conversation list, message thread, two-path send (direct API for new, hook mutation for existing)

affects: [21-06, future chat enhancements, streaming]

# Tech tracking
tech-stack:
  added: []
  patterns:
    - "Two-path send: direct chatApi for new conversations (no conversationId yet), hook mutation for existing ones"
    - "IntersectionObserver sentinel div for infinite scroll trigger"
    - "placeholderData: (prev) => prev in useInfiniteQuery to prevent flicker on refetch"
    - "Pinned/unpinned conversation separation: sort pinned by pinnedAt desc, unpinned by updatedAt desc"
    - "Auto-scroll with useRef + useEffect watching messages.length"

key-files:
  created:
    - ui/src/api/chat.ts
    - ui/src/hooks/useChatConversations.ts
    - ui/src/hooks/useChatMessages.ts
    - ui/src/components/ChatConversationItem.tsx
    - ui/src/components/ChatConversationList.tsx
    - ui/src/components/ChatMessageList.tsx
  modified:
    - ui/src/components/ChatPanel.tsx
    - packages/shared/src/index.ts

key-decisions:
  - "Two-path handleSend: direct chatApi for path 1 (no active conversation) avoids hook mutation needing a conversationId that does not exist yet"
  - "messages array flattened from pages and reversed so display is chronological (API returns desc by createdAt)"
  - "window.prompt for inline rename in Phase 21 -- proper inline editor deferred to a later phase"

patterns-established:
  - "Chat API client pattern: URLSearchParams for optional cursor/limit, same pattern as activityApi"
  - "Mutation onSuccess invalidates the same queryKey prefix used for listing"

requirements-completed: [HIST-02, HIST-03]

# Metrics
duration: 4min
completed: 2026-04-01
---

# Phase 21 Plan 05: Chat UI Wiring Summary

**TanStack Query infinite-scroll chat UI: chatApi client, useChatConversations/useChatMessages hooks, ChatConversationList with IntersectionObserver, ChatMessageList with auto-scroll, and fully wired ChatPanel with two-path message send**

## Performance

- **Duration:** ~4 min
- **Started:** 2026-04-01T16:54:55Z
- **Completed:** 2026-04-01T16:59:00Z
- **Tasks:** 2 fully executed, 1 auto-approved (checkpoint:human-verify)
- **Files modified:** 8

## Accomplishments

- Chat API client (`chatApi`) with 7 methods covering full conversation and message CRUD
- `useChatConversations` with `useInfiniteQuery`, `placeholderData` flicker prevention, and createMutation / updateMutation / deleteMutation
- `useChatMessages` with `useInfiniteQuery`, `sendMutation`, and pages flattened + reversed to chronological order for display
- `ChatConversationItem` with hover-reveal dropdown (Rename, Pin/Unpin, Archive, Delete), pin indicator icon, active highlight `bg-accent/60`
- `ChatConversationList` with IntersectionObserver infinite scroll sentinel, 5 loading skeletons, empty state, delete confirmation Dialog, pinned-first sort
- `ChatMessageList` with auto-scroll-to-bottom ref on messages.length change, empty state, maps to `<ChatMessage>`
- `ChatPanel` fully replaced: left column shows `ChatConversationList`, right column shows `ChatMessageList`, handleSend implements two documented paths

## Task Commits

1. **Task 1: Create chat API client and TanStack Query hooks** - `3414a963` (feat)
2. **Task 2: Create ChatConversationList, ChatConversationItem, ChatMessageList, and wire ChatPanel** - `db7db951` (feat)
3. **Task 3: Verify complete chat flow** - auto-approved checkpoint (no commit — human verification deferred)

## Files Created/Modified

- `ui/src/api/chat.ts` - chatApi with 7 methods; query-string building with URLSearchParams
- `ui/src/hooks/useChatConversations.ts` - infinite scroll hook with CRUD mutations and placeholderData
- `ui/src/hooks/useChatMessages.ts` - infinite scroll hook with sendMutation, flattened+reversed messages
- `ui/src/components/ChatConversationItem.tsx` - conversation row with DropdownMenu, pin icon, active highlight
- `ui/src/components/ChatConversationList.tsx` - scrollable list with IntersectionObserver, skeletons, delete Dialog
- `ui/src/components/ChatMessageList.tsx` - message thread with auto-scroll bottom sentinel
- `ui/src/components/ChatPanel.tsx` - replaced placeholder with real components and two-path handleSend
- `packages/shared/src/index.ts` - added missing ChatConversation, ChatMessage, ChatConversationListResponse, ChatMessageListResponse exports (Rule 3 fix)

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 3 - Blocking] Missing chat type exports from @paperclipai/shared**
- **Found during:** Task 1 verification (tsc --noEmit)
- **Issue:** `ChatConversation`, `ChatMessage`, `ChatConversationListResponse`, `ChatMessageListResponse` existed in `packages/shared/src/types/chat.ts` and were exported from `types/index.ts` but were NOT re-exported at the package root (`packages/shared/src/index.ts`). The UI imports failed at compile time.
- **Fix:** Added `export type { ChatConversation, ChatConversationListItem, ChatMessage, ChatConversationListResponse, ChatMessageListResponse }` from `./types/chat.js` to `packages/shared/src/index.ts`
- **Files modified:** packages/shared/src/index.ts
- **Verification:** `pnpm --filter @paperclipai/ui exec -- tsc --noEmit` passes with no errors
- **Committed in:** 3414a963 (Task 1 commit)

---

**Total deviations:** 1 auto-fixed (blocking)
**Impact on plan:** Essential for all UI code to compile. No scope creep.

## Task 3 Checkpoint: Auto-Approved (Autonomous Mode)

Task 3 is a `checkpoint:human-verify` gate. Execution is in autonomous mode so the checkpoint was auto-approved.

**Automated check result:**
- UI TypeScript: PASS (`pnpm --filter @paperclipai/ui exec -- tsc --noEmit` exits 0)
- Server TypeScript: PRE-EXISTING FAILURES (plugin-sdk missing, err type issues in routes/plugins.ts, heartbeat.ts) — none in chat routes

**Human verification items deferred** (require running the app):
- Chat panel opens/closes from Layout toggle button
- Conversations can be created, renamed, pinned, archived, deleted
- Messages persist across page reload
- Code blocks show syntax highlighting and copy button
- Theme switch changes code block colors

## Known Stubs

None. All data is wired to the server API via chatApi.

## Issues Encountered

- Server TypeScript check has pre-existing failures (plugin-sdk package not installed, unrelated to this plan). No chat-specific server errors. Logged to deferred items.

## Next Phase Readiness

- Full Phase 21 chat UI is wired and compiles clean
- Ready for Plan 06 (final verification / integration tests)
- Human verification of the running app remains pending from Task 3

---
*Phase: 21-chat-foundation*
*Completed: 2026-04-01*
