style: quiet transparent stream activity rows

This commit is contained in:
Mohamed Habib
2026-06-12 12:52:54 +03:00
parent 43bb015135
commit 62743be299
2 changed files with 104 additions and 100 deletions

View File

@@ -3137,14 +3137,14 @@ body.resizing .sidebar{transition:none!important;}
.tool-card-result pre{font-size:var(--message-code-font-size);color:var(--muted);font-family:'SF Mono',ui-monospace,monospace;white-space:pre-wrap;word-break:break-word;max-height:240px;overflow-y:auto;margin:0;line-height:1.55;}
/* ── Transparent Stream event rows ── */
/* Box rhythm: each event is a compact rounded card with a thin, transparent
border, a 2px type rail, and tight spacing. Keep the newest activity easy
to scan without letting the execution trace overpower the final answer. */
/* Inline rhythm: transparent stream events are transcript metadata inside an
assistant turn, not first-class cards. Keep them inspectable but visually
below assistant prose unless hovered, focused, or expanded. */
.transparent-event-controls{
display:flex;
align-items:center;
gap:6px;
margin:1px 0 3px var(--msg-rail);
margin:0 0 2px var(--msg-rail);
max-width:var(--msg-max);
color:var(--muted);
font-size:11px;
@@ -3163,28 +3163,33 @@ body.resizing .sidebar{transition:none!important;}
.transparent-event-action:hover,
.transparent-event-control:hover{color:var(--text);opacity:1;text-decoration-color:currentColor;}
.transparent-event-row{
margin:1px 0 1px var(--msg-rail);
margin:0 0 1px var(--msg-rail);
max-width:var(--msg-max);
width:calc(100% - var(--msg-rail));
border:1px solid color-mix(in srgb,var(--accent) 12%,var(--border-subtle));
border-left:2px solid color-mix(in srgb,var(--accent) 42%,transparent);
border-radius:12px;
background:linear-gradient(90deg,color-mix(in srgb,var(--accent-bg) 22%,transparent),color-mix(in srgb,var(--surface) 88%,transparent) 34%);
border:1px solid transparent;
border-left:1px solid transparent;
border-radius:6px;
background:transparent;
overflow:hidden;
box-shadow:0 1px 0 rgba(0,0,0,.03);
box-shadow:none;
animation:transparent-event-enter .18s ease-out both;
transition:border-color .22s ease,box-shadow .22s ease,opacity .22s ease;
transition:background .18s ease,border-color .18s ease,opacity .22s ease;
}
.transparent-event-row[data-event-type="thinking"]{
background:linear-gradient(90deg,color-mix(in srgb,var(--accent-bg-strong) 30%,transparent),color-mix(in srgb,var(--accent-bg) 28%,color-mix(in srgb,var(--surface) 88%,transparent)) 42%);
border-color:color-mix(in srgb,var(--accent) 18%,var(--border-subtle));
border-left-color:color-mix(in srgb,var(--accent) 58%,transparent);
background:transparent;
border-color:transparent;
border-left-color:transparent;
}
.transparent-event-row[data-expanded="1"]{
border-color:color-mix(in srgb,var(--accent) 28%,var(--border-subtle));
box-shadow:0 4px 14px rgba(0,0,0,.06);
border-color:var(--border-subtle);
background:color-mix(in srgb,var(--surface-subtle) 62%,transparent);
box-shadow:none;
}
.transparent-event-row:hover,
.transparent-event-row:focus-within{
border-color:var(--border-subtle);
background:color-mix(in srgb,var(--surface-subtle) 48%,transparent);
}
.transparent-event-row:hover{border-color:color-mix(in srgb,var(--accent) 38%,var(--border-subtle));}
.transparent-event-row .tool-card{
border:0;
border-radius:0;
@@ -3197,8 +3202,8 @@ body.resizing .sidebar{transition:none!important;}
flex-direction:row;
align-items:center;
gap:5px;
min-height:20px;
padding:2px 4px 2px 8px;
min-height:19px;
padding:1px 4px 1px 7px;
margin-left:0;
border-radius:0;
color:var(--muted);
@@ -3213,15 +3218,15 @@ body.resizing .sidebar{transition:none!important;}
justify-content:center;
width:14px;height:14px;
flex-shrink:0;
opacity:.85;
opacity:.7;
}
.transparent-event-row .tool-card-icon{order:1;}
.transparent-event-row .tool-card-icon svg,
.transparent-event-row .thinking-card-icon svg{width:11px;height:11px;}
.transparent-event-row .tool-card-name{
font-weight:600;
color:var(--text);
opacity:.78;
color:var(--muted);
opacity:.86;
font-size:11.5px;
order:2;
flex-shrink:0;
@@ -3246,8 +3251,8 @@ body.resizing .sidebar{transition:none!important;}
.transparent-event-row .thinking-card-label{
display:inline;
flex:0 0 auto;
color:var(--accent);
opacity:.86;
color:var(--muted);
opacity:.88;
font-size:11.5px;
font-weight:600;
overflow:hidden;
@@ -3269,19 +3274,19 @@ body.resizing .sidebar{transition:none!important;}
flex-shrink:0;
order:4;
color:var(--muted);
opacity:.7;
opacity:.64;
font-size:10px;
font-weight:500;
letter-spacing:.02em;
padding:1px 5px;
border-radius:6px;
background:color-mix(in srgb,var(--accent) 8%,transparent);
background:transparent;
}
.transparent-event-copy{
border:0;
background:transparent;
color:var(--muted);
opacity:.4;
opacity:.28;
padding:1px 2px;
cursor:pointer;
flex-shrink:0;
@@ -3292,7 +3297,8 @@ body.resizing .sidebar{transition:none!important;}
border-radius:4px;
transition:opacity .18s ease,color .18s ease;
}
.transparent-event-copy:hover{opacity:1;color:var(--text);background:transparent;}
.transparent-event-copy:hover,
.transparent-event-copy:focus-visible{opacity:1;color:var(--text);background:transparent;}
.transparent-event-row .tool-card-toggle,
.transparent-event-row .thinking-card-toggle{
order:10;
@@ -3301,18 +3307,24 @@ body.resizing .sidebar{transition:none!important;}
align-items:center;
justify-content:center;
width:14px;height:14px;
opacity:.55;
opacity:.34;
transition:opacity .18s ease,transform .22s ease;
}
.transparent-event-row:hover .transparent-event-copy,
.transparent-event-row:focus-within .transparent-event-copy,
.transparent-event-row:hover .tool-card-toggle,
.transparent-event-row:focus-within .tool-card-toggle,
.transparent-event-row:hover .thinking-card-toggle,
.transparent-event-row:focus-within .thinking-card-toggle{opacity:.76;}
.transparent-event-row .tool-card:hover .tool-card-toggle,
.transparent-event-row .thinking-card:hover .thinking-card-toggle{opacity:1;}
.transparent-event-row .tool-card.open .tool-card-toggle,
.transparent-event-row .thinking-card.open .thinking-card-toggle{transform:rotate(90deg);opacity:.9;}
.transparent-event-progress{
position:relative;
height:3px;
background:linear-gradient(180deg,color-mix(in srgb,var(--border-subtle) 100%,transparent) 0,color-mix(in srgb,var(--border-subtle) 40%,transparent) 100%);
border-radius:0 0 12px 12px;
height:1px;
background:transparent;
border-radius:0 0 6px 6px;
overflow:hidden;
margin:0 -1px -1px -1px;
}
@@ -3320,30 +3332,23 @@ body.resizing .sidebar{transition:none!important;}
content:"";
position:absolute;left:0;top:0;bottom:0;
width:0%;
background:linear-gradient(90deg,color-mix(in srgb,var(--accent) 70%,transparent),color-mix(in srgb,var(--accent) 95%,transparent) 50%,color-mix(in srgb,var(--accent) 70%,transparent) 100%);
box-shadow:0 0 4px color-mix(in srgb,var(--accent) 60%,transparent);
background:color-mix(in srgb,var(--muted) 42%,transparent);
box-shadow:none;
opacity:0;
border-radius:inherit;
transition:width .35s cubic-bezier(.4,0,.2,1);
}
.transparent-event-progress[data-progress-running="1"]::before{animation:transparent-progress-shimmer 1.6s ease-in-out infinite;}
.transparent-event-progress[data-progress-running="1"]::before{opacity:.55;animation:transparent-progress-shimmer 1.6s ease-in-out infinite;}
.transparent-event-progress[data-progress-percent]::before{width:var(--transparent-progress-percent,0%);}
.transparent-event-progress::after{
content:"";
position:absolute;left:0;right:0;top:0;height:1px;
background:linear-gradient(90deg,transparent 0,color-mix(in srgb,var(--surface) 70%,transparent) 20%,color-mix(in srgb,var(--surface) 90%,transparent) 50%,color-mix(in srgb,var(--surface) 70%,transparent) 80%,transparent 100%);
pointer-events:none;
content:none;
}
@keyframes transparent-progress-shimmer{
0%{left:0;right:100%;}
100%{left:100%;right:0;}
}
.transparent-event-row + .transparent-event-row::before{
content:"";
display:block;
height:1px;
margin:0 12px;
background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--accent) 18%,transparent) 20%,color-mix(in srgb,var(--accent) 28%,transparent) 50%,color-mix(in srgb,var(--accent) 18%,transparent) 80%,transparent);
border-radius:1px;
content:none;
}
.transparent-detail-modes{
display:flex;
@@ -3373,17 +3378,17 @@ body.resizing .sidebar{transition:none!important;}
opacity:1;
transform:translateY(0);
border-top-color:var(--border-subtle);
padding:2px 0 4px;
padding:1px 0 3px;
overflow:auto;
}
.transparent-event-row .thinking-card.open .thinking-card-body{
border-top-color:transparent;
padding:0 0 4px;
padding:0 0 3px;
}
.transparent-event-row .tool-card-detail > *,
.transparent-event-row .thinking-card-body > *{min-height:0;}
.transparent-event-row .tool-card-args,
.transparent-event-row .tool-card-result{padding:6px 8px;}
.transparent-event-row .tool-card-result{padding:5px 7px;}
.transparent-event-row .tool-card-detail[data-transparent-detail-mode="output"] .tool-card-args{display:none;}
.transparent-event-row .tool-card-args{margin-bottom:0;}
.transparent-event-row .tool-arg-pair{display:flex;flex-direction:column;gap:1px;padding:3px 0;border-bottom:1px solid color-mix(in srgb,var(--border-subtle) 72%,transparent);}
@@ -3393,8 +3398,8 @@ body.resizing .sidebar{transition:none!important;}
.transparent-event-row .tool-card-result pre{
background:var(--code-bg);
border:1px solid var(--border-subtle);
border-radius:8px;
padding:6px 8px;
border-radius:6px;
padding:5px 7px;
color:var(--muted);
line-height:1.5;
}

View File

@@ -236,35 +236,36 @@ def test_transparent_existing_copy_buttons_are_rebound_after_cache_restore():
assert "const fallbackName=row.getAttribute('data-event-name')||row.getAttribute('data-tool-name')||'tool';" in UI_JS
def test_transparent_event_row_pre_3820_visual_rhythm():
"""Restore the pre-#3820 visual rhythm: collapsed rows are flat
list-items (no border) so the stack reads as a clean list, and tool
rows are distinguished from thinking rows by a warm low-opacity
secondary tint. When the user clicks a row, it expands into a small
card with a smooth height animation.
def test_transparent_event_row_quiet_metadata_visual_rhythm():
"""Transparent stream rows are quiet transcript metadata, not highlighted
cards. Collapsed rows are transparent by default, with only neutral
hover/focus/expanded treatment. Thinking rows share the same quiet family
instead of using a special accent banner.
Body itself stays flush with the card's border (no inner chrome), but
contains a bordered code block for output. Status is rendered as a
small text badge only when the tool isn't Completed.
"""
# Per-event card (when expanded): thin border, surface bg, radius, soft shadow.
assert "border:1px solid var(--border-subtle)" in STYLE_CSS
# Softened edges: 12px (was 10px, was 6px originally).
assert "border-radius:12px" in STYLE_CSS
assert "background:var(--surface)" in STYLE_CSS
# Collapsed rows are inline metadata: no gradient, no shadow, no accent rail.
assert "border:1px solid transparent" in STYLE_CSS
assert "border-left:1px solid transparent" in STYLE_CSS
assert "border-radius:6px" in STYLE_CSS
assert "background:transparent" in STYLE_CSS
assert "box-shadow:none" in STYLE_CSS
# Header: single flex line, low height, hover subtle.
# Compact rhythm: 20px header (was 22px) for tighter box size.
assert "min-height:20px" in STYLE_CSS
# Compact rhythm: 19px header for tighter inline trace rows.
assert "min-height:19px" in STYLE_CSS
assert "display:flex" in STYLE_CSS
# Thinking row uses the warm low-opacity secondary tint (per-theme) PLUS
# a thin transparent border so the box reads as a unit even when collapsed.
assert "background:linear-gradient(90deg,color-mix(in srgb,var(--accent-bg-strong)" in STYLE_CSS
# All transparent event boxes carry a very-low-opacity border so each box
# is visually anchored. The thinking row's border is slightly stronger.
assert "color-mix(in srgb,var(--accent) 58%,transparent)" in STYLE_CSS
assert "color-mix(in srgb,var(--accent) 38%,var(--border-subtle))" in STYLE_CSS
# Thinking rows no longer get a special accent gradient or bright border.
thinking_start = STYLE_CSS.index('.transparent-event-row[data-event-type="thinking"]')
thinking_end = STYLE_CSS.index('.transparent-event-row[data-expanded="1"]', thinking_start)
thinking_block = STYLE_CSS[thinking_start:thinking_end]
assert "background:transparent;" in thinking_block
assert "border-color:transparent;" in thinking_block
assert "border-left-color:transparent;" in thinking_block
assert "var(--accent)" not in thinking_block
# Expanded body uses visible max-height / opacity animation for smooth height.
assert "max-height:0" in STYLE_CSS
@@ -281,7 +282,7 @@ def test_transparent_event_row_pre_3820_visual_rhythm():
assert "border-radius:0;" in STYLE_CSS
assert "margin-top:0;" in STYLE_CSS
assert "padding:0 8px 6px 27px;" in STYLE_CSS
assert ".transparent-event-row .thinking-card.open .thinking-card-body{\n border-top-color:transparent;\n padding:0 0 4px;\n}" in STYLE_CSS
assert ".transparent-event-row .thinking-card.open .thinking-card-body{\n border-top-color:transparent;\n padding:0 0 3px;\n}" in STYLE_CSS
# Tabs: text-link style, font-weight indicates active, no pill background.
assert ".transparent-detail-mode.active{color:var(--text);opacity:1;font-weight:600;}" in STYLE_CSS
@@ -299,7 +300,7 @@ def test_transparent_event_row_pre_3820_visual_rhythm():
assert ".transparent-event-row .tool-card:not(.open) .tool-card-preview" not in STYLE_CSS
assert "display:inline" in STYLE_CSS
assert "flex-direction:row;" in STYLE_CSS
assert ".transparent-event-row .thinking-card-label{\n display:inline;\n flex:0 0 auto;\n color:var(--accent);" in STYLE_CSS
assert ".transparent-event-row .thinking-card-label{\n display:inline;\n flex:0 0 auto;\n color:var(--muted);" in STYLE_CSS
assert ".transparent-event-thinking-preview{\n display:inline;\n flex:1 1 auto;\n min-width:0;" in STYLE_CSS
thinking_label_block = STYLE_CSS[
STYLE_CSS.index(".transparent-event-row .thinking-card-label{"):
@@ -495,26 +496,27 @@ def test_transparent_turn_wiring_runs_after_per_turn_duration_block():
)
def test_three_d_progress_bar_attached_to_each_event_row():
"""Each transparent event card has a 3D progress bar at its bottom
edge. The bar shimmers while the tool is running and fills to 100%
when it completes. Drawn as a 1px highlight + 1px shadow for the
3D ridge effect, and uses a step-break pseudo-element between
consecutive rows so the stream reads as discrete steps."""
def test_muted_progress_bar_attached_to_each_event_row():
"""Each transparent event row keeps a very quiet progress affordance.
The strip is neutral, 1px high, and only becomes visible while running so
completed internal traces do not compete with assistant prose."""
assert "function _attachProgressBar" in UI_JS
# The decorator wires the progress bar at the end of the function.
assert "_attachProgressBar(row, opts)" in UI_JS
# CSS: bar has a 3D ridge (::after highlight) and a shimmer keyframe.
# CSS: bar is present and can shimmer while running, but has no accent glow.
assert ".transparent-event-progress" in STYLE_CSS
assert "transparent-progress-shimmer" in STYLE_CSS
assert "@keyframes transparent-progress-shimmer" in STYLE_CSS
# The bar lives at the bottom of the card with a 12px radius to match
# the card's bottom-left and bottom-right corners.
assert "border-radius:0 0 12px 12px" in STYLE_CSS
# The 3D ridge highlight.
assert "transparent-event-progress::after" in STYLE_CSS
# Step break: between two consecutive event rows, a soft gradient line.
assert "height:1px" in STYLE_CSS
assert "border-radius:0 0 6px 6px" in STYLE_CSS
assert "background:color-mix(in srgb,var(--muted) 42%,transparent)" in STYLE_CSS
assert "box-shadow:none" in STYLE_CSS
assert "opacity:0" in STYLE_CSS
assert '.transparent-event-progress[data-progress-running="1"]::before{opacity:.55;' in STYLE_CSS
# No 3D ridge or accent separator between adjacent rows.
assert ".transparent-event-progress::after{\n content:none;\n}" in STYLE_CSS
assert ".transparent-event-row + .transparent-event-row::before" in STYLE_CSS
assert ".transparent-event-row + .transparent-event-row::before{\n content:none;\n}" in STYLE_CSS
def test_copy_button_position_is_stable_and_dedup_handles_legacy_template():
@@ -527,8 +529,8 @@ def test_copy_button_position_is_stable_and_dedup_handles_legacy_template():
# Flex order pinning.
assert "order:9" in STYLE_CSS
assert "order:10" in STYLE_CSS
assert ".transparent-event-copy{\n border:0;\n background:transparent;\n color:var(--muted);\n opacity:.4;" in STYLE_CSS
assert ".transparent-event-copy:hover{opacity:1;color:var(--text);background:transparent;}" in STYLE_CSS
assert ".transparent-event-copy{\n border:0;\n background:transparent;\n color:var(--muted);\n opacity:.28;" in STYLE_CSS
assert ".transparent-event-copy:hover,\n.transparent-event-copy:focus-visible{opacity:1;color:var(--text);background:transparent;}" in STYLE_CSS
# Dedup: _attachCopyButton checks for both .transparent-event-copy
# and .thinking-copy-btn.
assert "'.transparent-event-copy,.thinking-copy-btn'" in UI_JS
@@ -538,19 +540,16 @@ def test_copy_button_position_is_stable_and_dedup_handles_legacy_template():
assert "return bindCopyButton(existing);" in UI_JS
def test_increased_transparency_and_softened_edges():
"""Background opacity is slightly lower (22% accent for tool, 30% for
thinking vs 28%/38% before) and the border is softer (12% accent
mix). Edges are softer: 12px border-radius, lower box-shadow
opacity, and the hover/expanded shadows are gentler."""
# Softer background.
assert "color-mix(in srgb,var(--accent-bg) 22%,transparent)" in STYLE_CSS
# Softer border.
assert "color-mix(in srgb,var(--accent) 12%,var(--border-subtle))" in STYLE_CSS
# Softer shadow.
assert "rgba(0,0,0,.03)" in STYLE_CSS
# 12px edges.
assert "border-radius:12px" in STYLE_CSS
def test_transparent_rows_remove_card_chrome_by_default():
"""Default transparent rows avoid card/banner styling. Neutral border and
low-tint background are reserved for hover, focus, or expanded states."""
assert "border:1px solid transparent" in STYLE_CSS
assert "border-left:1px solid transparent" in STYLE_CSS
assert "border-color:var(--border-subtle)" in STYLE_CSS
assert "background:color-mix(in srgb,var(--surface-subtle) 62%,transparent)" in STYLE_CSS
assert "background:color-mix(in srgb,var(--surface-subtle) 48%,transparent)" in STYLE_CSS
assert "box-shadow:none" in STYLE_CSS
assert "border-radius:6px" in STYLE_CSS
def test_smaller_tool_icons_and_reduced_font_sizes():