Skip to content

Commit 72eeaa6

Browse files
committed
fix(ui): unify chat composer surfaces and compact tracks
Share composer radius, padding, border, context tint and shadow across both public editor shells. Use a 24px compact action track with 8px padding; remove the generic shell's extra border and inset. Match the context blur with reduced-transparency and forced-color fallbacks. Preserve editor and queue ownership, existing product controls, and footer measurement. Lab covers compact without context, expanded long input, queued attachments, busy and disabled slots. Legacy Appearance field-border and subtle-surface overrides retain precedence over newly introduced defaults. Validation: design-system:check; 89 focused Appearance/composer/input-layout/tail-reservation tests; check:web including all theme audits and TypeScript. Native expanded composer and context band inspected; full streaming, narrow-window and remote interaction matrix remains pending. No viewport compensation or backend protocol changes.
1 parent 83ead61 commit 72eeaa6

25 files changed

Lines changed: 158 additions & 68 deletions

‎design-system/apps/design-lab/src/preview/FlowChatPreviewRegistry.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,15 @@ type FlowChatPreviewDefinitionMap = {
133133
type PreviewProps = FlowChatPreviewRenderOptions;
134134

135135
function ChatComposerPreview({ interactive, state }: PreviewProps) {
136-
const [value, setValue] = useState("");
136+
const [value, setValue] = useState(state === "expanded" ? "Review the attached project notes and explain how the implementation should behave across narrow and wide windows." : "");
137137
const expanded = state === "expanded";
138138
const queued = state === "queued";
139139

140140
return (
141141
<div className="flow-chat-composer-preview">
142142
<ChatComposer
143143
busy={state === "busy"}
144-
contextBar={(
144+
contextBar={state === "default" ? undefined : (
145145
<div className="flow-chat-composer-preview__context">
146146
<span><Monitor aria-hidden="true" />This computer</span>
147147
<span>OpenBitFun</span>

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -485,12 +485,12 @@
485485
"chatComposer": {
486486
"$type": "dimension",
487487
"actionIconSize": { "$value": "14px" },
488-
"compactGap": { "$value": "9px" },
489-
"compactHeight": { "$value": "45px" },
490-
"compactPaddingBlock": { "$value": "9px" },
491-
"compactPaddingInline": { "$value": "9px" },
492-
"compactTrackHeight": { "$value": "25px" },
493-
"controlHeight": { "$value": "25px" }
488+
"compactGap": { "$value": "{control.composer.surfaceGap}" },
489+
"compactHeight": { "$value": "42px" },
490+
"compactPaddingBlock": { "$value": "{control.composer.surfacePadding}" },
491+
"compactPaddingInline": { "$value": "{control.composer.surfacePadding}" },
492+
"compactTrackHeight": { "$value": "24px" },
493+
"controlHeight": { "$value": "24px" }
494494
},
495495
"flowChat": {
496496
"$description": "Shared FlowChat rhythm for product and package-owned conversation surfaces.",
@@ -585,6 +585,7 @@
585585
"iconSizeMd": { "$value": "14px" }
586586
},
587587
"composer": {
588+
"contextBlur": { "$type": "dimension", "$value": "10px" },
588589
"$type": "dimension",
589590
"minBlockSize": { "$value": "120px" },
590591
"contextOffset": { "$value": "32px" },

‎design-system/packages/design-tokens/tests/contract.test.mjs‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,12 @@ test("AskUser geometry preserves the answered question reference contract", () =
175175
});
176176

177177
test("ChatComposer geometry preserves the scaled compact capsule contract", () => {
178-
assert.equal(tokens["control.chatComposer.compactGap"], "9px");
179-
assert.equal(tokens["control.chatComposer.compactHeight"], "45px");
180-
assert.equal(tokens["control.chatComposer.compactPaddingBlock"], "9px");
181-
assert.equal(tokens["control.chatComposer.compactPaddingInline"], "9px");
182-
assert.equal(tokens["control.chatComposer.compactTrackHeight"], "25px");
183-
assert.equal(tokens["control.chatComposer.controlHeight"], "25px");
178+
assert.equal(tokens["control.chatComposer.compactGap"], "12px");
179+
assert.equal(tokens["control.chatComposer.compactHeight"], "42px");
180+
assert.equal(tokens["control.chatComposer.compactPaddingBlock"], "8px");
181+
assert.equal(tokens["control.chatComposer.compactPaddingInline"], "8px");
182+
assert.equal(tokens["control.chatComposer.compactTrackHeight"], "24px");
183+
assert.equal(tokens["control.chatComposer.controlHeight"], "24px");
184184
});
185185

186186
test("FlowChat rhythm keeps compact rows line-like and Turn boundaries distinct", async () => {

‎design-system/packages/theme-openbitfun/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,5 @@ menu labels to secondary text (60%). Explicit imported action colors still win.
8181
Action cards own `color.actionCard.background`: the light entry surface is black at 3% opacity. `surface.subtle` is a transient navy tint and `field.groupBackground` belongs to form groups, so neither represents this persistent action surface. Other modes retain their neutral action surface. Product Appearance preserves explicit legacy neutral-surface overrides in root and chrome.
8282

8383
Compact indicators own `color.numberBadge.background` and `color.keyHint.content` so reference light values (8% fill and 60% text) can coexist with existing dark/contrast values and explicit legacy Appearance overrides. The light Switch off track and resting scrollbar thumb use 10% black; KeyHint and Launcher resting fills use 8%. Long status labels keep content colors; StatusPill emphasis is opt-in.
84+
85+
`color.composer.border` and `color.composer.contextBackground` are the shared editor-surface contract for Composer and ChatComposer. Reference light uses an 8% border and 3% context tint; persistent entry cards and form groups keep their separate owners. Legacy explicit field-border and subtle-surface overrides populate the new keys only when absent. Composer shadow uses a 12px CSS box-shadow blur (the Figma effect radius; its generated filter drop-shadow uses a 6px standard deviation).

‎design-system/packages/theme-openbitfun/src/dark.tokens.json‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
}
2020
},
2121
"color": {
22+
"composer": {
23+
"border": { "$type": "color", "$value": "{color.field.border}" },
24+
"contextBackground": { "$type": "color", "$value": "{color.surface.subtle}" }
25+
},
2226
"numberBadge": {
2327
"background": { "$type": "color", "$value": "{color.action.neutral.surface}" }
2428
},

‎design-system/packages/theme-openbitfun/src/high-contrast-dark.tokens.json‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
}
2020
},
2121
"color": {
22+
"composer": {
23+
"border": { "$type": "color", "$value": "{color.field.border}" },
24+
"contextBackground": { "$type": "color", "$value": "{color.surface.subtle}" }
25+
},
2226
"keyHint": {
2327
"content": { "$type": "color", "$value": "{color.content.muted}" }
2428
},

‎design-system/packages/theme-openbitfun/src/high-contrast-light.tokens.json‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
}
2020
},
2121
"color": {
22+
"composer": {
23+
"border": { "$type": "color", "$value": "{color.field.border}" },
24+
"contextBackground": { "$type": "color", "$value": "{color.surface.subtle}" }
25+
},
2226
"keyHint": {
2327
"content": { "$type": "color", "$value": "{color.content.muted}" }
2428
},

‎design-system/packages/theme-openbitfun/src/light.tokens.json‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
}
2020
},
2121
"color": {
22+
"composer": {
23+
"border": { "$type": "color", "$value": "{color.field.border}" },
24+
"contextBackground": { "$type": "color", "$value": "rgba(0, 0, 0, 0.03)" }
25+
},
2226
"numberBadge": {
2327
"background": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" }
2428
},
@@ -261,7 +265,7 @@
261265
"lg": { "$value": "0 8px 16px rgba(16, 26, 39, 0.09)" },
262266
"xl": { "$value": "0 12px 24px rgba(16, 26, 39, 0.11)" },
263267
"raised": { "$value": "{shadow.sm}" },
264-
"composer": { "$value": "0 2px 6px rgba(0, 0, 0, 0.08)" },
268+
"composer": { "$value": "0 2px 12px rgba(0, 0, 0, 0.08)" },
265269
"menu": { "$value": "0 4px 20px rgba(0, 0, 0, 0.12)" },
266270
"overlay": { "$value": "0 4px 20px rgba(0, 0, 0, 0.12)" },
267271
"accentGlow": {

‎design-system/packages/theme-openbitfun/tests/theme-contract.test.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ test("default modes preserve the built-in Appearance anchor values", () => {
328328
assert.equal(themes.light["color.identity.globalSearch.openFiles"], "#9e54ff");
329329
assert.equal(themes.light["color.status.warning.surface"], "rgba(255, 140, 0, 0.1)");
330330
assert.equal(themes.light["shadow.base"], "0 4px 8px rgba(16, 26, 39, 0.07)");
331-
assert.equal(themes.light["shadow.composer"], "0 2px 6px rgba(0, 0, 0, 0.08)");
331+
assert.equal(themes.light["shadow.composer"], "0 2px 12px rgba(0, 0, 0, 0.08)");
332332
assert.equal(themes.light["shadow.menu"], "0 4px 20px rgba(0, 0, 0, 0.12)");
333333
assert.equal(themes.light["shadow.overlay"], "0 4px 20px rgba(0, 0, 0, 0.12)");
334334
assert.equal(themes.light["opacity.disabled"], 0.55);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,3 +423,5 @@ Extra-large (`xl`) dialogs have an 800px maximum width and continue shrinking wi
423423
PageHeader `md` uses the settings title with a primary 15px description; `display` uses the welcome heading and medium 17px introduction with a 12px gap. ActionCard uses 12px padding, section-heading typography (15px semibold), and a primary 13px single-line action description. Its inset outline does not inflate the 62px medium minimum height; longer content keeps the independent sibling actions and OverflowText behavior.
424424

425425
KeyHint uses 10px text on a 10px line and 2px block padding. StatusPill uses a 14px line with 2px block padding; opt into `emphasis` for short mode labels such as Ask, while ordinary status descriptions retain readable content colors. LauncherButton owns a 72px minimum width, 40px height, 10px side padding, 4px gap, 12px icon and 11px monospace text. Product shells may retain a deliberate compact greeting that expands into this geometry.
426+
427+
Composer and ChatComposer share the 16px surface radius, 8px padding, 12px content gap, composer border, context tint, and composer shadow. Compact ChatComposer uses a 24px action track plus 8px padding on each side and two 1px borders (42px border-box height); expanded height follows editor content. The generic context shell has no duplicate outer border or surface inset. Queue and editor state remain owned by their existing slots and product adapters.

0 commit comments

Comments
 (0)