Skip to content

Commit 970cd19

Browse files
committed
fix(flow-chat): give transcript compositions consistent vertical rhythm
Use 8px item gaps, 4px inline spacing and 22px compact disclosure minimums. Move outer spacing from leaf views to model-round, retry, exploration and subagent compositions; retain continuous collapsed ambient runs across virtual rows. Remove adjacent negative margins and expanded Task margin stacking. Preserve the 24px runtime footer and existing measurement, collapse and viewport owners. Lab: real mixed tool sequence changes between compact runs and spaced expanded cards. Export wrappers retain their own spacing. Validation: design-system:check, check:web, ESLint, motion inventory (zero duplicate keyframes), and 40 tests across seven focused layout/measurement/collapse/rhythm/thinking/subagent files pass. Native Windows existing transcript: thinking expansion/collapse and switching away/back inspected; collapse preserved earlier content position. Live streaming, full mixed history, theme/density and remote scenarios remain manual pending. No remote runtime scenario was exercised; existing scrolling Known Gaps remain outside this change.
1 parent eb027f7 commit 970cd19

16 files changed

Lines changed: 119 additions & 66 deletions

File tree

‎design-system/apps/design-lab/src/preview/FlowChatToolGallery.css‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,16 @@
1717
}
1818

1919
.flow-chat-tool-sequence__rows {
20-
display: grid;
2120
min-inline-size: 0;
22-
gap: var(--openbitfun-control-flow-chat-flow-item-gap);
21+
}
22+
23+
.flow-chat-tool-sequence__rows > * + * {
24+
margin-block-start: var(--openbitfun-control-flow-chat-flow-item-gap);
25+
}
26+
27+
.flow-chat-tool-sequence__rows > [data-openbitfun-component='flow-chat-tool-card'][data-openbitfun-attention='ambient'][data-openbitfun-expanded-shell='false']
28+
+ [data-openbitfun-component='flow-chat-tool-card'][data-openbitfun-attention='ambient'][data-openbitfun-expanded-shell='false'] {
29+
margin-block-start: 0;
2330
}
2431

2532
.flow-chat-tool-gallery > .component-catalog-section-heading {

‎design-system/packages/design-tokens/src/system.tokens.json‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -497,19 +497,19 @@
497497
"$type": "dimension",
498498
"rowIconSize": { "$value": "{control.icon.sizeSm}" },
499499
"rowIconGap": { "$value": "{space.1}" },
500-
"flowItemGap": { "$value": "0.42rem" },
500+
"flowItemGap": { "$value": "{space.2}" },
501501
"turnGap": { "$value": "{space.4}" },
502-
"inlineGap": { "$value": "0.35rem" },
503-
"controlGap": { "$value": "0.5rem" },
504-
"controlPaddingBlock": { "$value": "0.35rem" },
502+
"inlineGap": { "$value": "{space.1}" },
503+
"controlGap": { "$value": "{space.2}" },
504+
"controlPaddingBlock": { "$value": "{space.1}" },
505505
"controlPaddingInline": { "$value": "0.75rem" },
506506
"contentPaddingInline": { "$value": "3rem" },
507507
"contentPaddingInlineMobile": { "$value": "1.5rem" },
508-
"cardGap": { "$value": "0.42rem" },
508+
"cardGap": { "$value": "{control.flowChat.flowItemGap}" },
509509
"cardRadius": { "$value": "{radius.base}" },
510510
"cardPaddingBlock": { "$value": "0.625rem" },
511511
"cardPaddingInline": { "$value": "0.75rem" },
512-
"cardExpandedPaddingBlock": { "$value": "0.5rem" },
512+
"cardExpandedPaddingBlock": { "$value": "{space.2}" },
513513
"cardExpandedPaddingInline": { "$value": "0.625rem" },
514514
"codeBlockPaddingBlock": { "$value": "0.55rem" },
515515
"codeBlockPaddingInline": { "$value": "0.75rem" }

‎design-system/packages/ui/README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,3 +448,9 @@ and disclosure glyphs occupy that same column. Expanded thinking prose remains
448448
aligned with the body edge; bordered tool detail retains its own content inset.
449449
The 12px/4px Figma inline trace is a smaller typography scene; the existing
450450
product 14px glyph size is retained when applying its gap to these summaries.
451+
452+
FlowChat vertical composition uses an 8px item/section gap and 4px inline gap.
453+
Collapsed ambient tool runs use their 22px minimum line boxes without extra
454+
spacing between adjacent rows; expanded/prominent cards retain the section gap.
455+
The enclosing composition owns those gaps, and card bodies own their internal
456+
padding. The Lab tool sequence demonstrates both arrangements with real cards.

‎src/apps/data-migrator/ui/generated/design-system.css‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,21 @@
6969
--openbitfun-control-composer-surface-padding: var(--openbitfun-space-2);
7070
--openbitfun-control-composer-surface-radius: var(--openbitfun-radius-xl);
7171
--openbitfun-control-composer-toolbar-gap: var(--openbitfun-space-2);
72-
--openbitfun-control-flow-chat-card-expanded-padding-block: 0.5rem;
72+
--openbitfun-control-flow-chat-card-expanded-padding-block: var(--openbitfun-space-2);
7373
--openbitfun-control-flow-chat-card-expanded-padding-inline: 0.625rem;
74-
--openbitfun-control-flow-chat-card-gap: 0.42rem;
74+
--openbitfun-control-flow-chat-card-gap: var(--openbitfun-control-flow-chat-flow-item-gap);
7575
--openbitfun-control-flow-chat-card-padding-block: 0.625rem;
7676
--openbitfun-control-flow-chat-card-padding-inline: 0.75rem;
7777
--openbitfun-control-flow-chat-card-radius: var(--openbitfun-radius-base);
7878
--openbitfun-control-flow-chat-code-block-padding-block: 0.55rem;
7979
--openbitfun-control-flow-chat-code-block-padding-inline: 0.75rem;
8080
--openbitfun-control-flow-chat-content-padding-inline: 3rem;
8181
--openbitfun-control-flow-chat-content-padding-inline-mobile: 1.5rem;
82-
--openbitfun-control-flow-chat-control-gap: 0.5rem;
83-
--openbitfun-control-flow-chat-control-padding-block: 0.35rem;
82+
--openbitfun-control-flow-chat-control-gap: var(--openbitfun-space-2);
83+
--openbitfun-control-flow-chat-control-padding-block: var(--openbitfun-space-1);
8484
--openbitfun-control-flow-chat-control-padding-inline: 0.75rem;
85-
--openbitfun-control-flow-chat-flow-item-gap: 0.42rem;
86-
--openbitfun-control-flow-chat-inline-gap: 0.35rem;
85+
--openbitfun-control-flow-chat-flow-item-gap: var(--openbitfun-space-2);
86+
--openbitfun-control-flow-chat-inline-gap: var(--openbitfun-space-1);
8787
--openbitfun-control-flow-chat-row-icon-gap: var(--openbitfun-space-1);
8888
--openbitfun-control-flow-chat-row-icon-size: var(--openbitfun-control-icon-size-sm);
8989
--openbitfun-control-flow-chat-turn-gap: var(--openbitfun-space-4);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Composition owns inter-item space. Leaf views own only their content padding.
2+
// A model round retains its trailing gap across virtual rows; enclosed content
3+
// drops it so the enclosing section does not count the last gap twice.
4+
@mixin apply($enclosed: false) {
5+
> :is(.flow-text-block, .flow-thinking-item, .flowchat-flow-item,
6+
.task-with-subagent-wrapper, .subagent-projection-text--truncated) {
7+
margin: 0 0 var(--openbitfun-control-flow-chat-flow-item-gap) 0;
8+
}
9+
10+
// Consecutive collapsed ambient tools form a continuous run of 22px lines.
11+
> .flowchat-flow-item:has(
12+
> .flow-tool-card-wrapper[data-openbitfun-attention='ambient']
13+
[data-openbitfun-component='flow-chat-tool-card'][data-openbitfun-part='root'][data-openbitfun-expanded-shell='false']
14+
):has(
15+
+ .flowchat-flow-item
16+
> .flow-tool-card-wrapper[data-openbitfun-attention='ambient']
17+
[data-openbitfun-component='flow-chat-tool-card'][data-openbitfun-part='root'][data-openbitfun-expanded-shell='false']
18+
) {
19+
margin-bottom: 0;
20+
}
21+
22+
@if $enclosed {
23+
> :is(.flow-text-block, .flow-thinking-item, .flowchat-flow-item,
24+
.task-with-subagent-wrapper, .subagent-projection-text--truncated):last-child {
25+
margin-bottom: 0;
26+
}
27+
}
28+
}

‎src/web-ui/src/flow_chat/components/FlowTextBlock.scss‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
@include markdownTypography.apply;
1111

1212
width: 100%;
13-
margin: 0 0 var(--openbitfun-control-flow-chat-flow-item-gap) 0;
13+
margin: 0;
1414
font-size: var(--openbitfun-type-flow-body-font-size);
1515
line-height: var(--openbitfun-type-flow-body-line-height);
1616
border: 0;

‎src/web-ui/src/flow_chat/components/FlowToolCard.scss‎

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,9 @@
11
.flowchat-flow-item {
2-
margin: 0 0 var(--openbitfun-control-flow-chat-flow-item-gap) 0;
2+
margin: 0;
33
font-size: var(--openbitfun-type-flow-body-font-size);
44
line-height: var(--openbitfun-type-flow-support-line-height);
55
}
66

7-
/* A run of collapsed ambient tools reads like consecutive transcript lines.
8-
* Keep the normal flow-item gap at either edge of the run, but do not insert a
9-
* card gap between each one-line row. Expanded ambient cards retain section
10-
* spacing because they are content surfaces rather than line items. */
11-
.flowchat-flow-item:has(
12-
> .flow-tool-card-wrapper[data-openbitfun-attention='ambient']
13-
[data-openbitfun-component='flow-chat-tool-card'][data-openbitfun-part='root'][data-openbitfun-expanded-shell='false']
14-
):has(
15-
+ .flowchat-flow-item
16-
> .flow-tool-card-wrapper[data-openbitfun-attention='ambient']
17-
[data-openbitfun-component='flow-chat-tool-card'][data-openbitfun-part='root'][data-openbitfun-expanded-shell='false']
18-
) {
19-
margin-bottom: 0;
20-
}
21-
227
.flow-tool-card-wrapper {
238
display: flex;
249
flex-direction: column;

‎src/web-ui/src/flow_chat/components/modern/ExploreRegion.scss‎

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@use '../../item-rhythm' as itemRhythm;
2+
13
/* Explore region styling for consecutive explore tool calls. */
24

35
.explore-region {
@@ -18,11 +20,12 @@
1820
// ==================== Collapsible (header + animated content) ====================
1921

2022
.explore-region--collapsible {
21-
margin: 0 0 0.3rem 0;
23+
margin: 0 0 var(--openbitfun-control-flow-chat-flow-item-gap) 0;
2224
font-size: var(--openbitfun-type-flow-control-font-size);
2325
line-height: var(--openbitfun-type-flow-support-line-height);
2426

2527
.explore-region__header {
28+
min-block-size: max(1lh, var(--openbitfun-control-tool-card-ambient-row-min-block-size));
2629
display: flex;
2730
align-items: center;
2831
gap: var(--openbitfun-control-flow-chat-row-icon-gap);
@@ -96,10 +99,11 @@
9699
}
97100

98101
.explore-region__content {
102+
@include itemRhythm.apply($enclosed: true);
99103
position: relative;
100104
// Keep expanded rows on the same leading edge as the collapsed group.
101105
// A nested inset makes auto-collapse read as a horizontal jump.
102-
padding: 0;
106+
padding: var(--openbitfun-space-2) 0 0;
103107
box-sizing: border-box;
104108
// Hide scrollbar track by default; only show when content actually overflows.
105109
overflow: hidden;
@@ -133,7 +137,7 @@
133137
content: '';
134138
position: absolute;
135139
// Account for collapsible header height.
136-
top: 28px;
140+
top: max(1lh, var(--openbitfun-control-tool-card-ambient-row-min-block-size));
137141
left: 0;
138142
right: 0;
139143
height: 16px;
@@ -175,7 +179,7 @@
175179
max-height: 400px;
176180
// Add bottom padding so the last line of text isn't obscured by the
177181
// gradient overlay or clipped at the container edge.
178-
padding-bottom: 8px;
182+
padding-bottom: var(--openbitfun-space-2);
179183
}
180184
}
181185

@@ -184,13 +188,6 @@
184188
overflow-y: auto;
185189
}
186190

187-
// ==================== Adjacent collapsed regions ====================
188-
189-
// Reduce spacing between adjacent collapsed regions.
190-
.explore-region--collapsed + .explore-region--collapsed {
191-
margin-top: -2px;
192-
}
193-
194191
// ==================== Cross-ModelRound collapse ====================
195192

196193
// Reduce spacing for explore-only model rounds.

‎src/web-ui/src/flow_chat/components/modern/ExportImageButton.scss‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,15 @@
143143
}
144144

145145
&__text-item {
146-
margin-bottom: 8px;
146+
margin-bottom: var(--openbitfun-control-flow-chat-flow-item-gap);
147147

148148
// Ensure FlowTextBlock styles render correctly.
149149
.flow-text-block {
150150
}
151151
}
152152

153153
&__thinking-item {
154-
margin-bottom: 8px;
154+
margin-bottom: var(--openbitfun-control-flow-chat-flow-item-gap);
155155
}
156156

157157
&__thinking-item--expanded {
@@ -177,7 +177,7 @@
177177
}
178178

179179
&__tool-item {
180-
margin-bottom: 8px;
180+
margin-bottom: var(--openbitfun-control-flow-chat-flow-item-gap);
181181

182182
// Hide tool card action buttons and expanded content.
183183
.tool-card__actions,

‎src/web-ui/src/flow_chat/components/modern/FLOWCHAT_VIRTUALIZATION.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,21 @@ internal whitespace; do not compensate for it with per-tool margins.
200200
Thinking/Explore labels use secondary content directly and their icons use the
201201
caption role, avoiding a second opacity multiplier. These layout rules do not
202202
change virtual-item identity, measurement ownership or viewport writes.
203+
204+
## Transcript vertical rhythm
205+
206+
The shared item gap is 8px and the inline gap is 4px. Thinking, Explore and
207+
retry disclosure headers share the ambient 22px minimum line box, growing with
208+
text. Consecutive collapsed ambient tools remain continuous lines with no added
209+
inter-item gap. The existing projection flag preserves that rule across virtual
210+
model-round boundaries; expanded and prominent cards keep the ordinary 8px gap.
211+
212+
The item-rhythm mixin belongs to ModelRoundItem, retry-attempt contents,
213+
Explore contents and the subagent projection. Leaves carry no outer margin.
214+
Enclosed contents remove their last gap; model rounds retain it until the virtual
215+
Turn boundary removes it. Expanded Task wrappers use the same parent-owned gap
216+
as other items; their body owns its internal padding. Export wrappers and the
217+
Lab sequence own their own gaps. Thinking/Explore content has an 8px top inset;
218+
bounded Explore retains 8px bottom padding for its scroll fade. There is no
219+
negative adjacent-region margin. The resident runtime slot stays 24px high and
220+
continues to participate in the existing footer/reservation contract.

0 commit comments

Comments
 (0)