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:
2026-01-30 09:26:09 -05:00
parent 50b29ff0a2
commit 1dc178f59f
2 changed files with 231 additions and 27 deletions

View File

@@ -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;