fix(desktop): tighten the gap between adjacent transcript scaffolding

The turn block gap is the space between the reply and the work around it, so
a back-to-back run of thinking headers and tool rows spent the full gap
between every line and read as a stack of cards rather than one column.
Adjacent scaffolding now ticks at a third of it.

The hook is the block list the rhythm rule already enumerates, minus prose,
rather than `data-conversation-scaffold`: that marker is absent on a
multi-call tool group — most of a real run — and present on rows nested
inside one, so it both missed the lines that needed tightening and moved
ones that didn't.

An open file edit is the exception. A diff is the deliverable, read like a
PR, so it keeps the full block gap on both sides while the scaffolding
around it stays tight. A streaming turn seals blocks into separate bubbles
where the flex gap would restore the full gap, so that seal is corrected to
match — and skipped when either side carries a diff.
This commit is contained in:
Brooklyn Nicholson 2026-07-27 23:27:08 -05:00
parent fab4c888ae
commit 2881243df3

View file

@ -406,6 +406,10 @@
/* Gap between top-level turn blocks (prose tools thinking) enough air
that scaffolding reads as separate from the reply, not crammed into it. */
--turn-block-gap: 0.75rem;
/* Between adjacent scaffolding, though: a back-to-back run of thinking
headers and tool rows is one sequence, not blocks to hold apart. Derived
so retuning the block gap carries it along. */
--scaffold-block-gap: calc(var(--turn-block-gap) / 3);
/* Tight gap between tool rows inside a single action group, so a back-to-back
run still reads as one cohesive sequence. */
--tool-row-gap: 0.375rem;
@ -1522,6 +1526,28 @@ text-* variant utilities. */ .btn-arc {
margin-top: var(--paragraph-gap);
}
/* And except scaffolding scaffolding: the full gap is the space between the
reply and the work around it, so a back-to-back run of thinking headers,
tool rows and status lines spent it between every line and read as stacked
cards. Same list as the rule above minus prose `data-conversation-scaffold`
is the wrong hook here: it's absent on a multi-call tool GROUP, which is most
of a real run, and present on rows nested inside one. */
[data-slot='aui_assistant-message-content']
> :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure'], [data-slot='aui_stream-stall'])
+ :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure'], [data-slot='aui_stream-stall']) {
margin-top: var(--scaffold-block-gap);
}
/* Except a file edit, which is the deliverable rather than another line in the
run a diff you read like a PR, so it keeps the full block gap on both
sides while the scaffolding around it stays tight. `data-file-edit` is only
on the row while it's open, so a collapsed edit rejoins the run. Margins on
the block itself, not adjacency pairs: it needs the clearance whatever sits
next to it, and adjacent margins collapse rather than stacking. */
[data-slot='aui_assistant-message-content'] > [data-tool-row][data-file-edit] {
margin-block: var(--turn-block-gap);
}
/* A streaming turn is sealed into several assistant bubbles as it goes
(`message.interim`) and rehydrates into fewer, so the same two blocks are
sometimes siblings inside one bubble and sometimes split across two. The
@ -1532,6 +1558,23 @@ text-* variant utilities. */ .btn-arc {
margin-top: calc(var(--turn-block-gap) - var(--conversation-turn-gap));
}
/* Unless the blocks either side of that seal are both scaffolding, in which
case the scaffold gap is the target and the flex gap already overshoots it.
Topping up to the full block gap here is what made a live run relax between
rows and then tighten once it rehydrated into one bubble. A bubble carrying
an open diff is excluded it keeps the full gap, same as in-flow. */
[data-slot='aui_turn-pair']
> [data-slot='aui_assistant-message-root']:has(
> [data-slot='aui_assistant-message-content']
> :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure'], [data-slot='aui_stream-stall']):last-child
):not(:has([data-file-edit]))
+ [data-slot='aui_assistant-message-root']:has(
> [data-slot='aui_assistant-message-content']
> :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure'], [data-slot='aui_stream-stall']):first-child
):not(:has([data-file-edit])) {
margin-top: calc(var(--scaffold-block-gap) - var(--conversation-turn-gap));
}
/* Message action bars — flat icon hits with default dim; only the hovered/focused control is full-strength. */
[data-slot='aui_msg-actions'] button {
border: 0;