Overhaul MessageBubble with collapsible sections, diff rendering, and header actions
Collapsible blocks:
- Thinking, tool_call, and tool_result messages start collapsed by default
- Chevron toggle in the header expands/collapses content
- Collapsed preview shows line count (thinking), tool name (tool_call),
or first line truncated to 120 chars (tool_result)
- Collapsed blocks skip expensive markdown/highlight rendering entirely
Diff rendering:
- Detect unified diff content via hunk header + add/delete line heuristics
(requires both @@ headers AND +/- lines to avoid false positives on
YAML or markdown lists with leading dashes)
- Render diffs with color-coded line classes: green additions, red
deletions, blue hunk headers, and muted meta/header lines
- Add full diff-view CSS with background tints and block-level spans
Header actions (appear on hover):
- Copy link button: copies a #msg-{uuid} anchor URL to clipboard with
a checkmark confirmation animation
- Redaction toggle button: replaces the previous whole-card onClick
handler with an explicit eye-slash icon button, colored red when
selected — more discoverable and less accident-prone
Style adjustments:
- Raise dimmed message opacity from 0.2/0.45 to 0.35/0.65 for better
readability during search filtering
- Fix btn-secondary hover border using explicit rgba value instead of
Tailwind opacity modifier (which was generating invalid CSS)
- Position copy button below language label when both are present
- Simplify formatTimestamp with isNaN guard instead of try/catch
- Use fixed h-10 header height for consistent vertical alignment
- Brighten user and assistant message backgrounds (bg-surface-overlay)
to visually distinguish them from other message types
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -147,6 +147,12 @@
|
||||
transition: opacity 150ms, background-color 150ms, color 150ms;
|
||||
}
|
||||
|
||||
/* When a language label is present, shift copy button below it */
|
||||
.code-block-wrapper .code-lang-label + .code-copy-btn,
|
||||
.code-block-wrapper:has(.code-lang-label) .code-copy-btn {
|
||||
top: 2rem;
|
||||
}
|
||||
|
||||
.code-block-wrapper:hover .code-copy-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -156,6 +162,54 @@
|
||||
background: var(--color-surface-overlay);
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════
|
||||
Diff view — colored line-level highlighting
|
||||
═══════════════════════════════════════════════ */
|
||||
|
||||
.diff-view {
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.diff-add {
|
||||
background: rgba(63, 185, 80, 0.12);
|
||||
color: #3fb950;
|
||||
display: block;
|
||||
margin: 0 -1rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.diff-del {
|
||||
background: rgba(248, 81, 73, 0.12);
|
||||
color: #f85149;
|
||||
display: block;
|
||||
margin: 0 -1rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.diff-hunk {
|
||||
color: #79c0ff;
|
||||
display: block;
|
||||
margin: 0 -1rem;
|
||||
padding: 0.25rem 1rem;
|
||||
background: rgba(121, 192, 255, 0.06);
|
||||
}
|
||||
|
||||
.diff-meta {
|
||||
color: var(--color-foreground-muted);
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.diff-header {
|
||||
color: var(--color-foreground-secondary);
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
margin: 0 -1rem;
|
||||
padding: 0.25rem 1rem;
|
||||
background: rgba(136, 144, 245, 0.06);
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════
|
||||
Search highlight — warm amber glow
|
||||
═══════════════════════════════════════════════ */
|
||||
@@ -246,12 +300,12 @@ mark.search-highlight {
|
||||
═══════════════════════════════════════════════ */
|
||||
|
||||
.message-dimmed {
|
||||
opacity: 0.2;
|
||||
opacity: 0.35;
|
||||
transition: opacity 200ms ease;
|
||||
}
|
||||
|
||||
.message-dimmed:hover {
|
||||
opacity: 0.45;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════
|
||||
@@ -473,11 +527,15 @@ mark.search-highlight {
|
||||
|
||||
.btn-secondary {
|
||||
@apply bg-surface-raised text-foreground-secondary border border-border;
|
||||
@apply hover:bg-surface-overlay hover:text-foreground hover:border-foreground-muted/30;
|
||||
@apply hover:bg-surface-overlay hover:text-foreground;
|
||||
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
||||
@apply focus-visible:ring-accent;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
border-color: rgba(80, 90, 110, 0.3);
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
@apply text-foreground-secondary;
|
||||
@apply hover:bg-surface-overlay hover:text-foreground;
|
||||
|
||||
Reference in New Issue
Block a user