|
| 1 | +# ChatSources Component Design |
| 2 | + |
| 3 | +**Issue:** #4 - Source and search results display component |
| 4 | +**Date:** 2026-03-24 |
| 5 | + |
| 6 | +## Overview |
| 7 | + |
| 8 | +A slide-out sidebar panel that displays source links associated with assistant messages. Sources are returned by the worker API, grouped by type, and triggered via an icon button on each message. |
| 9 | + |
| 10 | +## Types |
| 11 | + |
| 12 | +```typescript |
| 13 | +interface Source { |
| 14 | + url: string; |
| 15 | + title: string; |
| 16 | + type: "blog" | "page" | "external"; |
| 17 | +} |
| 18 | + |
| 19 | +// ChatResponse expands: |
| 20 | +interface ChatResponse { |
| 21 | + reply: string; |
| 22 | + sources?: Source[]; |
| 23 | +} |
| 24 | + |
| 25 | +// ChatMessage expands: |
| 26 | +interface ChatMessage { |
| 27 | + id: string; |
| 28 | + role: "user" | "assistant"; |
| 29 | + content: string; |
| 30 | + sources?: Source[]; |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +## Components |
| 35 | + |
| 36 | +### SourceIcon (new) |
| 37 | + |
| 38 | +Trigger button displayed on assistant messages that have sources. |
| 39 | + |
| 40 | +- Small link/document icon, bottom-left of assistant message bubble |
| 41 | +- Tiny badge circle showing source count, colored `claudius-primary` |
| 42 | +- Muted by default, more visible on hover |
| 43 | +- Tooltip: "View sources" |
| 44 | +- Clicking toggles the ChatSources sidebar for that message |
| 45 | + |
| 46 | +### ChatSources (new) |
| 47 | + |
| 48 | +Slide-out sidebar panel. |
| 49 | + |
| 50 | +- Slides from the left edge of ChatWindow, overlaying the message area |
| 51 | +- Width: ~280px |
| 52 | +- Background: `claudius-light` (dark mode aware) |
| 53 | +- Border-right: 2px `claudius-border` |
| 54 | +- Rounded corners on right side |
| 55 | +- Smooth slide transition (~200ms ease) |
| 56 | + |
| 57 | +**Header:** |
| 58 | +- "Sources" title |
| 59 | +- "{n} sources found" subtext |
| 60 | +- Close (X) button |
| 61 | + |
| 62 | +**Body:** |
| 63 | +- Grouped by type: blogs first, then pages, then external |
| 64 | +- Type group headers: small muted labels ("Blog", "Page", "External"), only shown if that type has entries |
| 65 | +- Source cards: rounded-[12px], 2px border, subtle fill |
| 66 | +- Each card shows title (truncated) and domain extracted from URL |
| 67 | +- Click opens link in new tab |
| 68 | +- Hover: slight background shift |
| 69 | + |
| 70 | +**No empty state needed** - icon only appears when sources exist. |
| 71 | + |
| 72 | +## Integration |
| 73 | + |
| 74 | +### Files to create |
| 75 | + |
| 76 | +- `widget/src/components/ChatSources.tsx` - Sidebar panel |
| 77 | +- `widget/src/components/SourceIcon.tsx` - Trigger button with badge |
| 78 | + |
| 79 | +### Files to modify |
| 80 | + |
| 81 | +- `widget/src/api/types.ts` - Add `Source` type, update `ChatResponse` |
| 82 | +- `widget/src/hooks/useChat.ts` - Pass sources through to `ChatMessage` |
| 83 | +- `widget/src/components/MessageBubble.tsx` - Render SourceIcon for assistant messages with sources |
| 84 | +- `widget/src/components/ChatWindow.tsx` - Manage sidebar state, render ChatSources overlay |
| 85 | +- `widget/src/index.ts` - Export new types |
| 86 | + |
| 87 | +### Not in scope (this PR) |
| 88 | + |
| 89 | +- Worker changes to return source data. Widget will be built and testable with mock data in dev mode. |
| 90 | + |
| 91 | +## Filtering |
| 92 | + |
| 93 | +The worker handles domain filtering server-side using the client's `allowedDomains` config. The widget trusts and renders whatever sources the API returns. |
| 94 | + |
| 95 | +## Source grouping order |
| 96 | + |
| 97 | +1. Blog posts (`type: "blog"`) |
| 98 | +2. Site pages (`type: "page"`) |
| 99 | +3. External links (`type: "external"`) |
0 commit comments