Problem
MessageItem.svelte (459 lines) has grown into a monolithic component with significant code duplication:
-
Duplicated header pattern (8x) — Every message type block repeats the same header row structure:
<div class="flex justify-between items-center text-xs text-gh-text-secondary">
<span>/* type label */</span>
<div class="flex items-center gap-2">
<span>{formatDate(msg.timestamp)}</span>
{@render splitButton()}
{@render deleteButton()}
</div>
</div>
This pattern appears in: progress, turnDuration, stopHook, fileSnapshot, slashCommand, localCommand, toolUse, and standard message blocks.
-
file-history-snapshot renders raw list — When a snapshot has many tracked files (10+), the full <ul> list renders without truncation. Other content types use ExpandableContent for long content, but file snapshots do not.
-
Inline type assertions — Multiple as unknown as { ... } casts scattered in derived computations (snapshotData, commandData, agentName, customTitle, messageId).
Proposed Changes
Phase 1: Extract messageHeader snippet
- Create a
{#snippet messageHeader(label, icon, timestamp?)} that encapsulates the header row pattern
- Replace all 8 duplicated header blocks with
{@render messageHeader(...)} calls
- Reduces ~80 lines of duplication
Phase 2: Apply ExpandableContent to file-history-snapshot
- Wrap the file list in
ExpandableContent when snapshotData.files.length > 5
- Format: one file path per line, preserving click-to-open behavior for expanded state
- Collapsed state shows first 5 files + "Click to expand (N more files)"
Phase 3: Extract derived computations to utilities
- Move
snapshotData, commandData, toolUseData, thinkingBlocks parsing to $lib/utils/message.ts
- Replace inline
as unknown as casts with typed parser functions
- Improves testability — parsers can be unit tested independently
Out of Scope
Storybook
- Update
MessageItem.stories.svelte to cover:
- File snapshot with 1 file (no collapse)
- File snapshot with 10+ files (collapsed by default)
- Verify header consistency across all message types
Verification
pnpm -F web build passes
pnpm -F core test passes (if utils moved)
- Storybook visual check for all message types
- Playwright e2e: session viewer renders messages correctly
Problem
MessageItem.svelte(459 lines) has grown into a monolithic component with significant code duplication:Duplicated header pattern (8x) — Every message type block repeats the same header row structure:
This pattern appears in: progress, turnDuration, stopHook, fileSnapshot, slashCommand, localCommand, toolUse, and standard message blocks.
file-history-snapshot renders raw list — When a snapshot has many tracked files (10+), the full
<ul>list renders without truncation. Other content types useExpandableContentfor long content, but file snapshots do not.Inline type assertions — Multiple
as unknown as { ... }casts scattered in derived computations (snapshotData, commandData, agentName, customTitle, messageId).Proposed Changes
Phase 1: Extract
messageHeadersnippet{#snippet messageHeader(label, icon, timestamp?)}that encapsulates the header row pattern{@render messageHeader(...)}callsPhase 2: Apply ExpandableContent to file-history-snapshot
ExpandableContentwhensnapshotData.files.length > 5Phase 3: Extract derived computations to utilities
snapshotData,commandData,toolUseData,thinkingBlocksparsing to$lib/utils/message.tsas unknown ascasts with typed parser functionsOut of Scope
Storybook
MessageItem.stories.svelteto cover:Verification
pnpm -F web buildpassespnpm -F core testpasses (if utils moved)