diff --git a/apps/desktop/src/styles.css b/apps/desktop/src/styles.css index 980501ded4e4..e304eb9487e9 100644 --- a/apps/desktop/src/styles.css +++ b/apps/desktop/src/styles.css @@ -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;