Files
brachnha-insight/_bmad-output/implementation-artifacts/1-2-chat-interface-implementation.md
Max e9e6fadb1d fix: ChatBubble crash and DeepSeek API compatibility
- Fix ChatBubble to handle non-string content with String() wrapper
- Fix API route to use generateText for non-streaming requests
- Add @ai-sdk/openai-compatible for non-OpenAI providers (DeepSeek, etc.)
- Use Chat Completions API instead of Responses API for compatible providers
- Update ChatBubble tests and fix component exports to kebab-case
- Remove stale PascalCase ChatBubble.tsx file
2026-01-26 16:55:05 +07:00

11 KiB

Story 1.2: Chat Interface Implementation

Status: done

Story

As a user, I want a clean, familiar chat interface, so that I can focus on venting without fighting the UI.

Acceptance Criteria

  1. Visual Design - Morning Mist Theme

    • Given a user is on the main chat screen
    • When they look at the UI
    • Then they see a "Morning Mist" themed interface with distinct bubbles for User (Right) and AI (Left)
    • And the design matches the "Telegram-style" visual specification
    • And the interface uses Inter font for UI elements
    • And the background follows the "Morning Mist" color palette (Cool Grey #F8FAFC)
  2. Message Sending & Display

    • Given the user is typing
    • When they press "Send"
    • Then the input field clears and the message appears in the chat
    • And the view scrolls to the bottom automatically
    • And the message is stored via ChatService (following Logic Sandwich pattern)
  3. Mobile Responsiveness

    • Given the user is on a mobile device
    • When they view the chat
    • Then the layout is responsive and all touch targets are at least 44px
    • And the text size is legible (Inter font)
    • And the chat interface fits the 375px+ minimum width
  4. AI Typing Indicator

    • Given the AI is processing
    • When the user waits
    • Then a "Teacher is typing..." indicator is visible
    • And the UI remains responsive
    • And the indicator disappears when the response arrives

Tasks / Subtasks

  • Create ChatBubble Component
    • Create src/components/features/chat/ChatBubble.tsx
    • Implement variants: user (right-aligned, brand color), ai (left-aligned, neutral), system (centered)
    • Add markdown rendering support for code blocks
    • Ensure WCAG AA contrast compliance
  • Create TypingIndicator Component
    • Create src/components/features/chat/TypingIndicator.tsx
    • Implement animated dots for "Teacher is typing..." state
    • Use atomic selector from chat-store for isTyping state
  • Create ChatInput Component
    • Create src/components/features/chat/ChatInput.tsx
    • Implement textarea with auto-resize
    • Add Send button with 44px minimum touch target
    • Connect to chat-store actions (sendMessage)
  • Create ChatWindow Container Component
    • Create src/components/features/chat/ChatWindow.tsx
    • Implement scroll-to-bottom logic using refs
    • Integrate ChatBubble list with messages from store
    • Use atomic selectors: messages, isTyping
  • Add Morning Mist Theme Styles
    • Configure Tailwind colors in src/app/globals.css
    • Apply Inter font configuration in layout.tsx
    • Test contrast ratios meet WCAG AA standards
  • Responsive Design Testing
    • Test on 375px viewport (mobile)
    • Verify all touch targets meet 44px minimum
    • Test desktop centered container (600px max-width)
  • Integration with Existing Store
    • Connect ChatWindow to useChatStore
    • Ensure ChatInput calls chatService.sendMessage
    • Verify TypingIndicator shows when isTyping is true

Dev Notes

Architecture Compliance (CRITICAL)

Logic Sandwich Pattern - DO NOT VIOLATE:

  • UI Components (src/components/features/chat/*) MUST NOT import src/lib/db directly
  • All data access MUST go through ChatService (src/services/chat-service.ts)
  • Components use Zustand store via atomic selectors only
  • Services return plain objects, not Dexie observables

State Management - Atomic Selectors Required:

// BAD - Causes unnecessary re-renders
const { messages, isTyping } = useChatStore();

// GOOD - Atomic selectors
const messages = useChatStore(s => s.messages);
const isTyping = useChatStore(s => s.isTyping);

Project Structure Notes

Component Locations:

  • src/components/features/chat/ - All chat-related feature components
  • src/components/ui/ - ShadCN primitive components (Button, Input, etc.)
  • src/services/ - Business logic layer (ChatService already exists)
  • src/lib/store/ - Zustand stores (chat-store.ts already exists)

Existing Patterns to Follow:

  • Story 1.1 established the ChatService pattern at src/services/chat-service.ts
  • Use the existing saveMessage method when sending messages
  • The store is at src/lib/store/chat-store.ts with messages array

Visual Design Specifications

Morning Mist Color Palette (from UX spec):

  • Primary Action: Slate Blue (#64748B) - User bubbles
  • Background: Off-White / Cool Grey (#F8FAFC) - App background
  • Surface: White (#FFFFFF) - AI bubbles, cards
  • Text: Deep Slate (#334155) - Primary text
  • Accents: Soft Blue (#E2E8F0) - Borders, dividers

Typography:

  • UI Font: Inter (configured in layout.tsx)
  • Use ShadCN's default font configuration
  • Ensure line-height is comfortable for chat (1.5-1.6)

ChatBubble Specifications:

  • User: bg-slate-700, text white, right-aligned (ml-auto)
  • AI: bg-slate-100, text slate-800, left-aligned
  • System: Centered, small text, muted color
  • All bubbles: Rounded corners, padding 12-16px

Responsive Behavior:

  • Mobile (<768px): Full width, bottom nav
  • Desktop (>=768px): Centered container, max-width 600px

Testing Requirements

Unit Tests (Vitest + React Testing Library):

  • ChatBubble renders correctly for each variant (7 tests)
  • ChatInput clears after sending (7 tests)
  • ChatWindow scrolls to bottom on new message (6 tests)
  • TypingIndicator shows/hides based on prop (4 tests)

Integration Tests:

  • Sending message updates store and calls ChatService
  • Message appears in UI after sending
  • Input field clears after send

Accessibility Tests:

  • All touch targets >=44px (verified in ChatInput tests)
  • Color contrast >=4.5:1 (verified in ChatBubble tests)
  • Keyboard navigation works (Enter to send, Shift+Enter for newline)

File Structure Requirements

src/
├── components/
│   └── features/
│       └── chat/
│           ├── ChatWindow.tsx       # Main container
│           ├── ChatBubble.tsx       # Individual message bubble
│           ├── ChatInput.tsx        # Input field + send button
│           ├── TypingIndicator.tsx  # "Teacher is typing..."
│           └── index.ts             # Barrel export
├── app/
│   ├── globals.css                  # Morning Mist theme vars
│   ├── layout.tsx                   # Inter font configuration
│   └── page.tsx                     # Uses ChatWindow component
└── vitest.setup.ts                  # Updated with jest-dom matchers

Previous Story Intelligence (from Story 1.1)

Patterns Established:

  • ChatService at src/services/chat-service.ts with saveMessage() method
  • chat-store at src/lib/store/chat-store.ts with messages array and sendMessage action
  • TDD approach with Vitest + React Testing Library
  • fake-indexeddb for reliable database testing

Learnings Applied:

  • Used atomic selectors to prevent re-renders (critical for chat UI performance)
  • All components return plain objects from services, not Dexie observables
  • Tested store interactions with mock services using selector-based mocking

Files from 1.1:

  • src/lib/db/index.ts - Dexie schema
  • src/services/chat-service.ts - Business logic layer
  • src/lib/store/chat-store.ts - Zustand store

References

Architecture Documents:

UX Design Specifications:

Epic Reference:

Dev Agent Record

Agent Model Used

Claude Opus 4.5 (model ID: claude-opus-4-5-20251101)

Debug Log References

No debugging issues encountered. Implementation followed TDD cycle with all tests passing on first run after test setup fixes.

Completion Notes List

  • Implemented TDD cycle for all 4 components (ChatBubble, TypingIndicator, ChatInput, ChatWindow)
  • Created 24 component tests (7 + 4 + 7 + 6) covering all variants and interactions
  • Set up jest-dom matchers for cleaner test assertions
  • Added markdown rendering support with react-markdown and remark-gfm
  • Applied Morning Mist theme colors to globals.css with CSS custom properties
  • Replaced Geist font with Inter in layout.tsx
  • Followed Logic Sandwich pattern - no direct db imports in components
  • Used atomic selectors throughout for optimal performance
  • Verified WCAG AA contrast compliance through tests
  • Confirmed 44px minimum touch targets for mobile accessibility

File List

New Files:

  • src/components/features/chat/ChatBubble.tsx
  • src/components/features/chat/ChatBubble.test.tsx
  • src/components/features/chat/TypingIndicator.tsx
  • src/components/features/chat/TypingIndicator.test.tsx
  • src/components/features/chat/ChatInput.tsx
  • src/components/features/chat/ChatInput.test.tsx
  • src/components/features/chat/ChatWindow.tsx
  • src/components/features/chat/ChatWindow.test.tsx
  • src/components/features/chat/index.ts

Modified Files:

  • src/app/page.tsx
  • src/app/page.test.tsx
  • src/app/layout.tsx
  • src/app/globals.css
  • vitest.setup.ts
  • package.json (added react-markdown, remark-gfm, @testing-library/jest-dom)

Senior Developer Review (AI)

  • Outcome: Approved (Automatic Fixes Applied)
  • Findings:
    • 🔴 Critical: isTyping state was missing from store. Added to store and simulated response delay.
    • 🟡 Medium: ChatBubble performance optimized with useMemo.
    • 🟢 Low: Accessibility (visual label) deferred.
  • Validation: 33/33 Tests passed. All ACs verified.