@oxy.so/bloom 3.2.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/docs/adoption-matrix.mdx +3 -3
  2. package/docs/ai-chat.mdx +31 -3
  3. package/docs/composer-panel.mdx +41 -6
  4. package/lib/commonjs/ai-chat/AiChatContainer.js +122 -14
  5. package/lib/commonjs/ai-chat/AiChatContainer.js.map +1 -1
  6. package/lib/commonjs/ai-chat/AiChatShell.js +46 -6
  7. package/lib/commonjs/ai-chat/AiChatShell.js.map +1 -1
  8. package/lib/commonjs/ai-chat/context.js +5 -1
  9. package/lib/commonjs/ai-chat/context.js.map +1 -1
  10. package/lib/commonjs/ai-chat/index.js +7 -0
  11. package/lib/commonjs/ai-chat/index.js.map +1 -1
  12. package/lib/commonjs/ai-chat/index.web.js +7 -0
  13. package/lib/commonjs/ai-chat/index.web.js.map +1 -1
  14. package/lib/commonjs/composer-panel/ComposerControls.js +61 -0
  15. package/lib/commonjs/composer-panel/ComposerControls.js.map +1 -1
  16. package/lib/commonjs/composer-panel/ComposerPanelBase.js +12 -4
  17. package/lib/commonjs/composer-panel/ComposerPanelBase.js.map +1 -1
  18. package/lib/commonjs/composer-panel/ComposerPillBase.js +186 -85
  19. package/lib/commonjs/composer-panel/ComposerPillBase.js.map +1 -1
  20. package/lib/commonjs/composer-panel/EffortSlider.js +24 -15
  21. package/lib/commonjs/composer-panel/EffortSlider.js.map +1 -1
  22. package/lib/commonjs/composer-panel/ModelPickerBase.js +15 -4
  23. package/lib/commonjs/composer-panel/ModelPickerBase.js.map +1 -1
  24. package/lib/module/ai-chat/AiChatContainer.js +122 -14
  25. package/lib/module/ai-chat/AiChatContainer.js.map +1 -1
  26. package/lib/module/ai-chat/AiChatShell.js +46 -6
  27. package/lib/module/ai-chat/AiChatShell.js.map +1 -1
  28. package/lib/module/ai-chat/context.js +5 -1
  29. package/lib/module/ai-chat/context.js.map +1 -1
  30. package/lib/module/ai-chat/index.js +1 -0
  31. package/lib/module/ai-chat/index.js.map +1 -1
  32. package/lib/module/ai-chat/index.web.js +1 -0
  33. package/lib/module/ai-chat/index.web.js.map +1 -1
  34. package/lib/module/composer-panel/ComposerControls.js +60 -0
  35. package/lib/module/composer-panel/ComposerControls.js.map +1 -1
  36. package/lib/module/composer-panel/ComposerPanelBase.js +13 -5
  37. package/lib/module/composer-panel/ComposerPanelBase.js.map +1 -1
  38. package/lib/module/composer-panel/ComposerPillBase.js +187 -86
  39. package/lib/module/composer-panel/ComposerPillBase.js.map +1 -1
  40. package/lib/module/composer-panel/EffortSlider.js +24 -15
  41. package/lib/module/composer-panel/EffortSlider.js.map +1 -1
  42. package/lib/module/composer-panel/ModelPickerBase.js +15 -4
  43. package/lib/module/composer-panel/ModelPickerBase.js.map +1 -1
  44. package/lib/typescript/commonjs/ai-chat/AiChatContainer.d.ts +28 -5
  45. package/lib/typescript/commonjs/ai-chat/AiChatContainer.d.ts.map +1 -1
  46. package/lib/typescript/commonjs/ai-chat/AiChatShell.d.ts +11 -2
  47. package/lib/typescript/commonjs/ai-chat/AiChatShell.d.ts.map +1 -1
  48. package/lib/typescript/commonjs/ai-chat/context.d.ts +19 -1
  49. package/lib/typescript/commonjs/ai-chat/context.d.ts.map +1 -1
  50. package/lib/typescript/commonjs/ai-chat/index.d.ts +3 -1
  51. package/lib/typescript/commonjs/ai-chat/index.d.ts.map +1 -1
  52. package/lib/typescript/commonjs/ai-chat/index.web.d.ts +3 -1
  53. package/lib/typescript/commonjs/ai-chat/index.web.d.ts.map +1 -1
  54. package/lib/typescript/commonjs/ai-chat/types.d.ts +101 -3
  55. package/lib/typescript/commonjs/ai-chat/types.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/composer-panel/ComposerControls.d.ts +14 -0
  57. package/lib/typescript/commonjs/composer-panel/ComposerControls.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/composer-panel/ComposerPanelBase.d.ts +3 -2
  59. package/lib/typescript/commonjs/composer-panel/ComposerPanelBase.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/composer-panel/ComposerPillBase.d.ts +3 -2
  61. package/lib/typescript/commonjs/composer-panel/ComposerPillBase.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/composer-panel/EffortSlider.d.ts +10 -2
  63. package/lib/typescript/commonjs/composer-panel/EffortSlider.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/composer-panel/ModelPickerBase.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/composer-panel/types.d.ts +70 -20
  66. package/lib/typescript/commonjs/composer-panel/types.d.ts.map +1 -1
  67. package/lib/typescript/module/ai-chat/AiChatContainer.d.ts +28 -5
  68. package/lib/typescript/module/ai-chat/AiChatContainer.d.ts.map +1 -1
  69. package/lib/typescript/module/ai-chat/AiChatShell.d.ts +11 -2
  70. package/lib/typescript/module/ai-chat/AiChatShell.d.ts.map +1 -1
  71. package/lib/typescript/module/ai-chat/context.d.ts +19 -1
  72. package/lib/typescript/module/ai-chat/context.d.ts.map +1 -1
  73. package/lib/typescript/module/ai-chat/index.d.ts +3 -1
  74. package/lib/typescript/module/ai-chat/index.d.ts.map +1 -1
  75. package/lib/typescript/module/ai-chat/index.web.d.ts +3 -1
  76. package/lib/typescript/module/ai-chat/index.web.d.ts.map +1 -1
  77. package/lib/typescript/module/ai-chat/types.d.ts +101 -3
  78. package/lib/typescript/module/ai-chat/types.d.ts.map +1 -1
  79. package/lib/typescript/module/composer-panel/ComposerControls.d.ts +14 -0
  80. package/lib/typescript/module/composer-panel/ComposerControls.d.ts.map +1 -1
  81. package/lib/typescript/module/composer-panel/ComposerPanelBase.d.ts +3 -2
  82. package/lib/typescript/module/composer-panel/ComposerPanelBase.d.ts.map +1 -1
  83. package/lib/typescript/module/composer-panel/ComposerPillBase.d.ts +3 -2
  84. package/lib/typescript/module/composer-panel/ComposerPillBase.d.ts.map +1 -1
  85. package/lib/typescript/module/composer-panel/EffortSlider.d.ts +10 -2
  86. package/lib/typescript/module/composer-panel/EffortSlider.d.ts.map +1 -1
  87. package/lib/typescript/module/composer-panel/ModelPickerBase.d.ts.map +1 -1
  88. package/lib/typescript/module/composer-panel/types.d.ts +70 -20
  89. package/lib/typescript/module/composer-panel/types.d.ts.map +1 -1
  90. package/package.json +1 -1
  91. package/src/__tests__/support/adoption-matrix.ts +664 -0
  92. package/src/__tests__/support/collision-fixture-barrel.ts +20 -0
  93. package/src/__tests__/support/composite.ts +68 -0
  94. package/src/__tests__/support/constructed-style-sheets.ts +68 -0
  95. package/src/__tests__/support/press-host.ts +30 -0
  96. package/src/__tests__/support/rendered-style.ts +99 -0
  97. package/src/__tests__/support/unread-hook-fixture.ts +33 -0
  98. package/src/ai-chat/AiChatContainer.tsx +137 -16
  99. package/src/ai-chat/AiChatShell.tsx +59 -5
  100. package/src/ai-chat/context.ts +19 -1
  101. package/src/ai-chat/index.ts +3 -0
  102. package/src/ai-chat/index.web.ts +3 -0
  103. package/src/ai-chat/types.ts +104 -3
  104. package/src/composer-panel/ComposerControls.tsx +54 -0
  105. package/src/composer-panel/ComposerPanelBase.tsx +13 -5
  106. package/src/composer-panel/ComposerPillBase.tsx +178 -66
  107. package/src/composer-panel/EffortSlider.tsx +38 -18
  108. package/src/composer-panel/ModelPickerBase.tsx +23 -7
  109. package/src/composer-panel/types.ts +71 -20
  110. package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +7682 -0
  111. package/src/theme/__tests__/fixtures/color-engine-golden.json +1 -0
@@ -111,7 +111,7 @@ The classification is an EQUALITY in `src/__tests__/adoption-matrix.test.ts`, no
111
111
  | `agent-log` | `@oxy.so/bloom/agent-log` + barrel | universal | `agent-log.mdx` | yes | 1 | | | | |
112
112
  | `agent-progress` | `@oxy.so/bloom/agent-progress` + barrel | universal | `agent-progress.mdx` | yes | 1 | | | | reads |
113
113
  | `agent-thinking` | `@oxy.so/bloom/agent-thinking` + barrel | universal | `agent-thinking.mdx` | yes | 1 | | | | |
114
- | `ai-chat` | `@oxy.so/bloom/ai-chat` + barrel | web fork | `ai-chat.mdx` | yes | 1 | | | | reads |
114
+ | `ai-chat` | `@oxy.so/bloom/ai-chat` + barrel | web fork | `ai-chat.mdx` | yes | 2 | | | | reads |
115
115
  | `ai-profile-card` | `@oxy.so/bloom/ai-profile-card` + barrel | universal | `ai-profile-card.mdx` | yes | 1 | | | | reads |
116
116
  | `alert-dialog` | `@oxy.so/bloom/alert-dialog` + barrel | web fork | `alert-dialog.mdx` | yes | 1 | | | | |
117
117
  | `animated-check` | `@oxy.so/bloom/animated-check` + barrel | universal | `animated-check.mdx` | yes | 2 | | | | |
@@ -143,7 +143,7 @@ The classification is an EQUALITY in `src/__tests__/adoption-matrix.test.ts`, no
143
143
  | `code` | `@oxy.so/bloom/code` + barrel | universal | `code.mdx` | yes | 2 | | | | |
144
144
  | `command` | `@oxy.so/bloom/command` + barrel | web fork | `command.mdx` | yes | 1 | n/a¹ | | | reads |
145
145
  | `composer-loader` | `@oxy.so/bloom/composer-loader` + barrel | web fork | `composer-loader.mdx` | yes | 1 | | | | |
146
- | `composer-panel` | `@oxy.so/bloom/composer-panel` + barrel | web fork | `composer-panel.mdx` | yes | 1 | n/a¹ | | | reads |
146
+ | `composer-panel` | `@oxy.so/bloom/composer-panel` + barrel | web fork | `composer-panel.mdx` | yes | 2 | n/a¹ | | | reads |
147
147
  | `composition-bar` | `@oxy.so/bloom/composition-bar` + barrel | universal | `composition-bar.mdx` | yes | 1 | | | | |
148
148
  | `connection-dots` | `@oxy.so/bloom/connection-dots` + barrel | web fork | `connection-dots.mdx` | yes | 1 | | | | |
149
149
  | `connection-status` | `@oxy.so/bloom/connection-status` + barrel | web fork | `connection-status.mdx` | yes | 1 | | | | |
@@ -254,7 +254,7 @@ The classification is an EQUALITY in `src/__tests__/adoption-matrix.test.ts`, no
254
254
  | `tenancy` | `@oxy.so/bloom/tenancy` + barrel | universal | `tenancy.mdx` | yes | 3 | | n/a¹ | | |
255
255
  | `text-field` | `@oxy.so/bloom/text-field` + barrel | universal | `text-field.mdx` | yes | 10 | reads | reads | | |
256
256
  | `textarea` | `@oxy.so/bloom/textarea` + barrel | universal | `textarea.mdx` | yes | 4 | reads | reads | | n/a¹ |
257
- | `theme` | `@oxy.so/bloom/theme` + barrel | web fork | `theme.mdx` | yes | 242 | | | n/a¹ | reads |
257
+ | `theme` | `@oxy.so/bloom/theme` + barrel | web fork | `theme.mdx` | yes | 244 | | | n/a¹ | reads |
258
258
  | `theme-toggle` | `@oxy.so/bloom/theme-toggle` + barrel | universal | `theme-toggle.mdx` | yes | 1 | n/a¹ | | | |
259
259
  | `toast` | `@oxy.so/bloom/toast` + barrel | universal | `toast.mdx` | yes | 17 | | | | |
260
260
  | `tooltip` | `@oxy.so/bloom/tooltip` + barrel | web fork | `tooltip.mdx` | yes | 1 | | | | |
package/docs/ai-chat.mdx CHANGED
@@ -14,7 +14,7 @@ The pieces of the AI chat and AI image generation screens. Everything is data an
14
14
  | `AiChatMobileHeader` | The in-container header below `xl` (menu button, title, panel button), wired to the shell |
15
15
  | `AiChatResizeHandle` | The grip on the chat's right edge |
16
16
  | `AiChatContainer` | The chat card: breadcrumb header, thread, composer slot, `AgentThinking` while working |
17
- | `AiChatThread` | The bottom-anchored, self-following scroll of turns |
17
+ | `AiChatThread` | The bottom-anchored, self-following scroll of turns — with the follow, upward pagination and `ref` a host with history needs |
18
18
  | `AiChatUserMessage` / `AiChatAssistantMessage` | The turns, with the staggered blur-in |
19
19
  | `AiChatMessageLine`, `AiChatBulletList`, `AiChatBullet`, `AiChatStrong`, `AiChatLinkChip` | A reply's blocks and inline runs |
20
20
  | `AiChatFeedbackRow` | Like / dislike / copy with tooltips |
@@ -29,6 +29,7 @@ import { AiChatShell, AiChatContainer, AiChatMobileHeader, AiChatThread, AiChatU
29
29
 
30
30
  <AiChatShell
31
31
  sidebar={<Sidebar {...rail} />}
32
+ sidebarCollapsed={railOnly}
32
33
  mobileSidebar={<Sidebar {...rail} mobile flat />}
33
34
  panelLabel="Code"
34
35
  panel={(width) => <AiChatCodePanel width={width} code={file} language="tsx" />}>
@@ -37,7 +38,12 @@ import { AiChatShell, AiChatContainer, AiChatMobileHeader, AiChatThread, AiChatU
37
38
  title="coding scenario"
38
39
  header={<AiChatMobileHeader title="Agentic chat" />}
39
40
  working={working}
40
- composer={<ComposerLoader active={working}><ComposerPill surface={false} glass={working} /></ComposerLoader>}>
41
+ background={<YourOwnField />}
42
+ composer={
43
+ <ComposerLoader active={working}>
44
+ <ComposerPill surface={false} glass={working} busy={working} disabled={working} onStop={stop} />
45
+ </ComposerLoader>
46
+ }>
41
47
  <AiChatThread>
42
48
  <AiChatUserMessage>update our colour tokens for dark mode</AiChatUserMessage>
43
49
  <AiChatAssistantMessage>Done — the tokens are wired.</AiChatAssistantMessage>
@@ -55,12 +61,34 @@ Give the shell a sized parent — it fills it.
55
61
  - **below xl** the panel is a right drawer — min(410, 100% − 12) wide, background-full, p 12, shadow-sidebar, a 40-tall header with the panel name and a close button — over a 40% black backdrop, sliding 300ms. `panel` receives `'100%'` there.
56
62
  - **below lg** the `mobileSidebar` rests under the workspace (272 wide, py 12 / pl 6). Opening it slides the whole workspace 272 right and rounds it to 32 while the sidebar scales from 94% and fades in (325ms `cubic-bezier(0.42, 0, 0.58, 1)`); a 10% black / 5% white veil over the workspace closes it. Opening one drawer closes the other.
57
63
  - `navOpen` / `panelOpen` are controllable. Reduced motion snaps.
64
+ - **A collapsible rail** — `sidebarWidth` fixes the in-flow column's width, `sidebarCollapsed` narrows it to `collapsedSidebarWidth` (56: one 36px control and its gutters). Without either, the sidebar sizes itself exactly as before. The column snaps between the two widths; whatever animates inside it is the sidebar's own business.
65
+ - **`background` and `surface`** — `background` is any node, drawn above the shell's own background-full and below the sidebar, workspace and drawers; it is never interactive. The workspace paints background-full too, so pass `surface={false}` with it (that drops both) or the layer is covered exactly where the chat sits. When the layer belongs to the chat rather than the page, put it on `AiChatContainer` instead, which is the nearer slot and needs no opt-out.
66
+ - **What the shell publishes** — `useAiChatShell()` returns the shell's state to any descendant: `compact`, `navCollapsed`, `sidebarCollapsed`, `hasNav` / `hasPanel`, `openNav` / `closeNav` / `openPanel`, and **`navPresented`**.
67
+
68
+ `navPresented` is the one to guard a nav's accessibility with. Below `lg` a closed drawer stays **mounted** — it is translated offscreen, not removed — so everything inside `mobileSidebar` is still focusable and still read aloud unless its own tree says otherwise. The shell marks its own wrapper `aria-hidden` and `pointerEvents="none"`, which handles the pointer but not the tab order of a host's controls; read `navPresented` and skip them while it is `false`.
58
69
 
59
70
  ## Container and thread
60
71
 
61
72
  Radius 24 on background-secondary: the breadcrumb header (px 16 / pt 16, share and more glyphs 8 apart, deepening to black / white on hover), the thread, then the footer (px 10 / pt 12 / pb 10, gap 10) with `AgentThinking` (infinity) while `working` and the `composer`.
62
73
 
63
- The thread scrolls with a thin scrollbar, px 16 / pt 16, turns 12 apart, and is **bottom-anchored**: a short exchange sits just above the composer. It follows the newest content with a smooth scroll whenever it grows.
74
+ `project` is **optional**: a chat that belongs to nothing draws its own crumb alone, with no folder glyph and no empty crumb.
75
+
76
+ `background` is a layer filling the container, drawn **above its own surface and below every turn, header and composer**, clipped to the radius and never interactive — a host's wallpaper, gradient or animated field goes here rather than behind the container, and nothing has to reach into it. An opaque node needs nothing else; add `surface={false}` when the node is translucent and should show what the container is sitting on instead of background-secondary.
77
+
78
+ The thread scrolls with a thin scrollbar, px 16 / pt 16, turns 12 apart, and is **bottom-anchored**: a short exchange sits just above the composer. It follows the newest content with a smooth scroll whenever it grows — that is the default, and every prop below is off unless asked for.
79
+
80
+ | Prop | Type | Default | Description |
81
+ |---|---|---|---|
82
+ | `autoFollow` | `boolean` | `true` | Follow the newest turn as the content grows. |
83
+ | `followAnimated` | `boolean` | `true` | `false` for a follow that must not be seen travelling. |
84
+ | `followThreshold` | `number` | — | Follow only from within this many px of the bottom, measured **before** the growth. Omit and every growth follows. |
85
+ | `onStartReached` | `() => void` | — | The reader came near the **top**. A chat pages upward, so this — not `onEndReached` — is its pagination signal. Once per approach; it re-arms when the reader leaves the zone. |
86
+ | `onStartReachedThreshold` | `number` | `300` | How near the top, px. |
87
+ | `maintainStartPosition` | `boolean` | `false` | The growth that answers an `onStartReached` is added to the offset, so a page landing **above** the reader does not move the turn they are on. Inert without `onStartReached` — the thread only anchors growth it asked for. |
88
+ | `onScroll` / `scrollEventThrottle` | | `16` | The raw event. Nothing is attached at all unless something is listening. |
89
+ | `ref` | `AiChatThreadHandle` | — | `scrollToEnd({ animated })`, `scrollToOffset({ offset, animated })`, `getScrollView()`. |
90
+
91
+ **Not there:** virtualization and `scrollToIndex`. The thread is a `ScrollView`, so a long history is fully mounted, and a host jumping to a cursor measures that turn's row (`onLayout`) and calls `scrollToOffset`. `maintainStartPosition` is the web-safe half of React Native's `maintainVisibleContentPosition`, which react-native-web ignores.
64
92
 
65
93
  **Hazard (web):** react-native-web gives every view `min-height: 0`, so the usual `flexGrow: 1` + `justifyContent: 'flex-end'` content container stays the viewport's height and its children overflow it instead of scrolling — and `onContentSizeChange` never sees the growth. The thread floors its container at `minHeight: '100%'` on web instead.
66
94
 
@@ -34,6 +34,16 @@ import {
34
34
 
35
35
  The panel is UI and interaction state only. Sending, streaming, uploading and model routing belong to the consumer: `onSubmit` hands you the draft, `disabled` greys send while a turn is in flight, and each attachment's `progress` is yours to drive.
36
36
 
37
+ ## A turn in flight
38
+
39
+ `busy` puts the **stop** control where send was; `onStop` is what it calls. It is the one control `disabled` does not reach — `disabled` locks the composer for the whole turn, and cancelling that turn has to stay possible for the whole of it. Enter stops submitting while `busy` too.
40
+
41
+ ```tsx
42
+ <ComposerPill busy={streaming} disabled={streaming} onStop={abort} onSubmit={send} />
43
+ ```
44
+
45
+ Both composers take the pair. `busy` **without** `onStop` draws no stop at all: a labelled control wired to nothing is worse than no control. It is the same shape `AgentChatComposer` already uses.
46
+
37
47
  ## Exports
38
48
 
39
49
  | Export | What it is |
@@ -42,7 +52,7 @@ The panel is UI and interaction state only. Sending, streaming, uploading and mo
42
52
  | `ComposerAttachments` | `ComposerPanel` with a **simulated** sequential upload queue: files arriving with `progress: 0` land one after another. For demos and prototypes — real uploads pass their own `progress` to `ComposerPanel`. |
43
53
  | `ModelPicker` | The model picker on its own (provider rail, Quick Search, effort). |
44
54
  | `ComposerPanelStatusTab` | The status tab — branch, project folder, context meter. Pass it as `status`. |
45
- | `ComposerPill` | The AI chat's one-row pill composer: add menu, field, a simple Models + Effort menu, mic and send. |
55
+ | `ComposerPill` | The AI chat's pill composer: add menu, field, a simple Models + Effort menu, mic and send / stop. One line at rest, growing with the draft to `maxLines`. |
46
56
  | `ComposerStatusBar` | The line under the pill: branch, a Local Folders menu, agent mode and the context meter. |
47
57
  | `COMPOSER_PANEL_PERMISSIONS`, `COMPOSER_PANEL_ADD_MENU`, `MODEL_PICKER_EFFORT_LEVELS` | The default modes, add-menu rows and effort stops, for extending rather than retyping. |
48
58
 
@@ -60,6 +70,8 @@ import { ComposerPill, ComposerStatusBar } from '@oxy.so/bloom/composer-panel';
60
70
 
61
71
  - **Pill** — 52 tall, full radius, p 8, gap 10, background-primary + shadow-xs. The add disc is a step lighter than the panel's (neutral-100 / dark 700, hovered 200 / 600) and reuses the panel's `AddMenu`. The field is body-regular with an accent-500 caret; its placeholder shortens to `compactPlaceholder` ("Ask me") under 640 wide, and unfocused on web its last 32px fade into the pill.
62
72
  - **Model menu** — a 32-tall trigger (radius 12, py 6 / pr 4 / pl 8) with a turning 18px chevron, opening a 266px panel upward: "Models" radio rows (p 8, radius 10, 14px radio, primary-hover while selected), a full-bleed divider (7 above, 12 below), "Effort *value*" (the value blurs in on change), Faster / Smarter and the six-stop slider. Choosing a model closes it; the slider leaves it open. Omit `models` to hide the button.
73
+ - **Model identity** — `models` takes `{ id, name }` entries, the same contract as `ModelPicker`: the menu keys, matches and reports the **id** and only ever draws the `name`. A bare string is shorthand for `{ id: name, name }`, which is why the old `models={['Fable 5']}` form still behaves exactly as it did — but a routing identifier that is matched by display name is a bug waiting for two models to share a label, so pass ids where you have them.
74
+ - **No effort axis** — `effortLevels={[]}` drops the divider and the whole effort half of the panel. A model with no stops gets the model rows alone, not an empty chip over a slider that cannot commit.
63
75
  - **Mic and send** — the panel's own controls, 8 apart after a 6px lead.
64
76
  - **`surface={false}`** inside a `ComposerLoader`, which paints the pill surface behind its light.
65
77
  - **`glass`** — the add, model and mic controls drop their fill, border and shadow over 480ms onto frosted chips (web: 6px saturated backdrop blur, a 12% / dark 6% white tint, a 155° sheen, a 0.3px conic rim; native: the tint). Pair it with an active loader.
@@ -90,6 +102,7 @@ Every text run uses the type ramp (`body-medium`, `body-2-medium`, `body-regular
90
102
  - **Add** — the plus turns 45° while open. Groups of rows (`addMenu`), each with a 20px icon or 24px artwork and an inline muted description. `onAddMenuSelect(rowId)` fires and the menu closes. `addMenu={[]}` hides the button.
91
103
  - **Permissions** — a pill that paints only on hover, press or while open; the panel lists `permissions` as radios (`aria-checked`). `onLearnMore` shows "Learn more" in the header.
92
104
  - **Model picker** — the rail browses providers (tabs, `aria-selected`) and always reopens on the chosen model's provider, keeping its mark in view. "Quick Search" (or `/` and ⌘K on web) filters every lineup; Escape clears the query, then leaves search. The selected row carries the effort chip, which opens the effort panel. Pass `providers` to `ComposerPanel` to show it.
105
+ - **Effort is a separate axis, and an optional one.** `effortLevels={[]}` drops the chip entirely — models whose deployments cannot agree on a level have no effort to offer, and a chip over an uncommittable slider is a control wired to nothing. `effort={null}` is the other half: **no stop chosen**, the parameter left out, the model deciding. The chip then reads `labels.effortAuto` ("Auto"), the fill is empty, every tick is faded and the thumb is drawn hollow and dashed, so an absent choice never looks like the first stop. The picker reports only stops a reader actually chose — it has no way back to `null`, which stays the caller's to hold.
93
106
 
94
107
  All three panels open upward and hang from the trigger's left edge. On **web** they are `floating/FloatingPanel` on its menu surface — a 150ms fade + scale + blur, non-modal (except the model picker, which catches the outside press so a press inside its separately-portaled effort panel is not read as "outside"). On **native** each is a transparent `Modal` placed against the trigger's `measureInWindow` box (flipped when it does not fit, clamped 8px inside the window), not a bottom sheet: the effort panel opens from inside the picker, and nested `Modal`s stack where sheets do not.
95
108
 
@@ -126,7 +139,7 @@ interface ModelPickerProvider {
126
139
 
127
140
  ## State
128
141
 
129
- Everything is controllable and works uncontrolled: `value`/`defaultValue`, `permission`/`defaultPermission`, `model`/`defaultModel`, `effort`/`defaultEffort`, `listening`/`defaultListening`. Enter submits on web (Shift+Enter breaks the line; IME composition is respected); on native the send button submits. An uncontrolled draft clears after submit.
142
+ Everything is controllable and works uncontrolled: `value`/`defaultValue`, `permission`/`defaultPermission`, `model`/`defaultModel`, `effort`/`defaultEffort`, `listening`/`defaultListening`. Enter submits on web (Shift+Enter breaks the line; IME composition is respected) unless `busy`; on native the send button submits. An uncontrolled draft clears after submit.
130
143
 
131
144
  ## Colours
132
145
 
@@ -155,7 +168,7 @@ State colours 150ms; chevrons and the plus 200ms; mic ↔ equalizer crossfade 28
155
168
 
156
169
  ## Accessibility
157
170
 
158
- Every control is named: the triggers announce their current value ("Permission: Auto", "Models: GPT-5.6 Mini", "Effort: Medium") with `aria-expanded`; permission modes and model rows are radios (`aria-checked` + `accessibilityState`); rail marks are tabs; the mic is a toggle (`aria-pressed`); send carries `aria-disabled`; the effort slider is `adjustable` with flat `aria-value*`, `aria-valuetext` (the level name), increment/decrement actions, and arrow/Home/End keys on web.
171
+ Every control is named: the triggers announce their current value ("Permission: Auto", "Models: GPT-5.6 Mini", "Effort: Medium", "Effort: Auto" with no stop chosen) with `aria-expanded`; stop is named "Stop generating" and carries no `aria-disabled`; permission modes and model rows are radios (`aria-checked` + `accessibilityState`); rail marks are tabs; the mic is a toggle (`aria-pressed`); send carries `aria-disabled`; the effort slider is `adjustable` with flat `aria-value*`, `aria-valuetext` (the level name, or the "Auto" label with no stop chosen — where `aria-valuenow` is omitted rather than guessed), increment/decrement actions, and arrow/Home/End keys on web.
159
172
 
160
173
  ## Props
161
174
 
@@ -163,7 +176,8 @@ Every control is named: the triggers announce their current value ("Permission:
163
176
  |------|------|---------|-------------|
164
177
  | `value` / `defaultValue` / `onValueChange` | `string` | `''` | The draft. |
165
178
  | `onSubmit` | `(value) => void` | — | Send button and Enter (web). |
166
- | `disabled` | `boolean` | `false` | Greys send. |
179
+ | `onStop` / `busy` | `() => void` / `boolean` | — / `false` | Send becomes stop, outside `disabled`'s reach. No handler, no stop. |
180
+ | `disabled` | `boolean` | `false` | Greys send. Never the stop control. |
167
181
  | `placeholder` | `string` | `'Hi, what do you need today?'` | |
168
182
  | `permissions` | `ComposerPanelPermissionOption[]` | the default four | `[]` hides the picker. |
169
183
  | `permission` / `defaultPermission` / `onPermissionChange` | `string` | first mode | |
@@ -171,8 +185,9 @@ Every control is named: the triggers announce their current value ("Permission:
171
185
  | `addMenu` / `onAddMenuSelect` | `ComposerPanelAddMenuGroup[]` / `(id) => void` | Add + Plugins | `[]` hides the button. |
172
186
  | `providers` | `ModelPickerProvider[]` | — | Shows the model picker. |
173
187
  | `model` / `defaultModel` / `onModelChange` | `string` | first model | |
174
- | `effort` / `defaultEffort` / `onEffortChange` | `number` | `1` | Index into `effortLevels`. |
175
- | `effortLevels` | `string[]` | the default six | |
188
+ | `effort` / `defaultEffort` | `number \| null` | `1` | Index into `effortLevels`; `null` is "no stop chosen". |
189
+ | `onEffortChange` | `(effort: number) => void` | — | Only ever a stop a reader chose. |
190
+ | `effortLevels` | `string[]` | the default six | `[]` drops the effort axis. |
176
191
  | `listening` / `defaultListening` / `onListeningChange` | `boolean` | `false` | Voice input toggle. |
177
192
  | `attachments` / `onRemoveAttachment` | `ComposerPanelAttachment[]` / `(id) => void` | — | |
178
193
  | `status` | `ReactNode` | — | Usually `<ComposerPanelStatusTab />`. |
@@ -180,4 +195,24 @@ Every control is named: the triggers announce their current value ("Permission:
180
195
  | `labels`, `modelPickerLabels` | objects | English | Localisation. |
181
196
  | `style`, `testID` | | | |
182
197
 
198
+ `ComposerPill` takes the same draft, add-menu, voice and `onStop` / `busy` props, plus `models` (`string | { id, name }` entries), `model` / `defaultModel` / `onModelChange` (by **id**), the effort trio, `effortLevels`, `compactPlaceholder`, `surface`, `glass` and `maxLines`.
199
+
200
+ ### The pill grows
201
+
202
+ It is 52px at rest — the height of its controls, not of its field — and gains a
203
+ line's height for each line the draft wraps to, up to `maxLines` (default `8`),
204
+ after which the field scrolls inside it. Past one line the corners square off
205
+ from a full round to 26 and the controls sit with the LAST line, where the
206
+ caret is; centred, they would drift into the middle of the draft.
207
+
208
+ `maxLines={1}` keeps the fixed single-line box the pill had before it could
209
+ grow.
210
+
211
+ **Shift+Enter breaks the line; Enter sends.** Both composers in this family
212
+ answer Enter the same way now — `ComposerPill` used to read only the key, so
213
+ the most-used combination in a composer sent a half-written message on a field
214
+ that could not have held the line anyway. An input method's Enter
215
+ (`isComposing`) is left to the input method, and neither key submits while
216
+ `busy`.
217
+
183
218
  `ComposerAttachments` takes the same props plus `attachments` (required), `uploadDuration` (1100), `uploadGap` (240), `onAttachmentsChange`, `onUploadComplete`, `onAllUploaded`. `ModelPicker` takes `providers`, `value`/`defaultValue`/`onValueChange`, the effort trio, `effortLevels`, `labels`, `style`, `testID`.
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.AiChatContainer = AiChatContainer;
7
- exports.AiChatThread = AiChatThread;
7
+ exports.AiChatThread = void 0;
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
  var _reactNative = require("react-native");
10
10
  var _index = require("../agent-thinking/index.js");
@@ -26,6 +26,9 @@ const DEFAULT_LABELS = {
26
26
  * The AI chat's centre column: the centre column of the AI chat template.
27
27
  *
28
28
  * section fills its parent, radius 24, background-secondary, clipped
29
+ * layer the `background` slot, filling the section above its own paint
30
+ * and below everything else, clipped to the same radius;
31
+ * `surface={false}` drops that paint
29
32
  * header px 16 / pt 16, gap 8: the project › chat breadcrumb (flex 1) and
30
33
  * the 16px share / more glyphs, 8 apart
31
34
  * thread the `children` — usually an `AiChatThread`
@@ -33,6 +36,9 @@ const DEFAULT_LABELS = {
33
36
  * while `working`, then the `composer`
34
37
  *
35
38
  * `header` renders above all of it: the shell's `AiChatMobileHeader`.
39
+ *
40
+ * `project` is optional: without one the breadcrumb is the chat's own crumb
41
+ * alone, since a chat that belongs to nothing must not have to invent a folder.
36
42
  */
37
43
  function AiChatContainer({
38
44
  project,
@@ -46,6 +52,8 @@ function AiChatContainer({
46
52
  composer,
47
53
  working = false,
48
54
  workingLabel,
55
+ background,
56
+ surface = true,
49
57
  labels,
50
58
  style,
51
59
  testID
@@ -65,9 +73,19 @@ function AiChatContainer({
65
73
  flexDirection: 'column',
66
74
  overflow: 'hidden',
67
75
  borderRadius: _shared.CONTAINER_RADIUS,
68
- backgroundColor: palette.secondary
76
+ backgroundColor: surface ? palette.secondary : 'transparent'
69
77
  }, style],
70
- children: [header, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
78
+ children: [background ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
79
+ pointerEvents: "none",
80
+ style: {
81
+ position: 'absolute',
82
+ top: 0,
83
+ left: 0,
84
+ right: 0,
85
+ bottom: 0
86
+ },
87
+ children: background
88
+ }) : null, header, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
71
89
  style: {
72
90
  width: '100%',
73
91
  flexDirection: 'row',
@@ -85,7 +103,7 @@ function AiChatContainer({
85
103
  },
86
104
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_index2.Breadcrumb, {
87
105
  accessibilityLabel: l.breadcrumb,
88
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.BreadcrumbItem, {
106
+ children: [project === undefined ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.BreadcrumbItem, {
89
107
  icon: projectIcon,
90
108
  onPress: onProjectPress,
91
109
  children: project
@@ -138,29 +156,119 @@ function AiChatContainer({
138
156
  /**
139
157
  * The thread: the scrolling conversation, anchored to the bottom
140
158
  * so a short exchange sits just above the composer and grows upward — px 16 /
141
- * pt 16, turns 12 apart, a thin scrollbar. It follows the newest turn with a
142
- * smooth scroll whenever the content grows.
159
+ * pt 16, turns 12 apart, a thin scrollbar.
160
+ *
161
+ * It follows the newest turn with a smooth scroll whenever the content grows.
162
+ * That is the default and nothing else changes it; the timeline props are the
163
+ * ones a host with history needs and are each off unless asked for:
164
+ *
165
+ * autoFollow / followAnimated / followThreshold whether, how, and from how
166
+ * close to the bottom the follow happens
167
+ * onStartReached a chat pages UPWARD; this
168
+ * fires near the TOP, once per approach, re-arming when the reader leaves
169
+ * maintainStartPosition the growth that answers an
170
+ * `onStartReached` is added to the offset, so a page landing above the
171
+ * reader does not move the turn they are reading
172
+ * ref (`AiChatThreadHandle`) scrollToEnd /
173
+ * scrollToOffset / the ScrollView, for a cursor jump or a restore
174
+ *
175
+ * There is no virtualization and no `scrollToIndex`: a host jumping to a turn
176
+ * measures its row (`onLayout`) and calls `scrollToOffset`.
143
177
  */
144
- function AiChatThread({
178
+ const AiChatThread = exports.AiChatThread = /*#__PURE__*/(0, _react.forwardRef)(function AiChatThread({
145
179
  children,
180
+ autoFollow = true,
181
+ followAnimated = true,
182
+ followThreshold,
183
+ onStartReached,
184
+ onStartReachedThreshold = 300,
185
+ maintainStartPosition = false,
186
+ onScroll,
187
+ scrollEventThrottle = 16,
146
188
  style,
147
189
  testID
148
- }) {
190
+ }, ref) {
149
191
  const scrollRef = (0, _react.useRef)(null);
150
192
  const lastHeight = (0, _react.useRef)(0);
193
+ const offset = (0, _react.useRef)(0);
194
+ const viewport = (0, _react.useRef)(0);
195
+ /** An `onStartReached` is out and its page has not landed yet. */
196
+ const pageOut = (0, _react.useRef)(false);
197
+ /** The reader has left the top zone, so the next approach may fire again. */
198
+ const startArmed = (0, _react.useRef)(true);
199
+ (0, _react.useImperativeHandle)(ref, () => ({
200
+ scrollToEnd: options => scrollRef.current?.scrollToEnd({
201
+ animated: options?.animated ?? true
202
+ }),
203
+ scrollToOffset: ({
204
+ offset: y,
205
+ animated = false
206
+ }) => scrollRef.current?.scrollTo({
207
+ y,
208
+ animated
209
+ }),
210
+ getScrollView: () => scrollRef.current
211
+ }), []);
212
+ const listening = !!onScroll || !!onStartReached || followThreshold !== undefined || maintainStartPosition;
213
+ const handleScroll = (0, _react.useCallback)(event => {
214
+ const {
215
+ contentOffset,
216
+ layoutMeasurement
217
+ } = event.nativeEvent;
218
+ offset.current = contentOffset.y;
219
+ viewport.current = layoutMeasurement.height;
220
+ if (onStartReached) {
221
+ if (contentOffset.y <= onStartReachedThreshold) {
222
+ if (startArmed.current) {
223
+ startArmed.current = false;
224
+ pageOut.current = true;
225
+ onStartReached();
226
+ }
227
+ } else {
228
+ startArmed.current = true;
229
+ }
230
+ }
231
+ onScroll?.(event);
232
+ }, [onScroll, onStartReached, onStartReachedThreshold]);
233
+ const onLayout = (0, _react.useCallback)(event => {
234
+ viewport.current = event.nativeEvent.layout.height;
235
+ }, []);
151
236
  const onContentSizeChange = (0, _react.useCallback)((_width, height) => {
152
- const grew = height > lastHeight.current + 0.5;
153
- const first = lastHeight.current === 0;
237
+ const previous = lastHeight.current;
238
+ const grew = height > previous + 0.5;
239
+ const first = previous === 0;
154
240
  lastHeight.current = height;
155
- if (grew && !first) scrollRef.current?.scrollToEnd({
156
- animated: true
241
+ if (!grew || first) return;
242
+
243
+ // A page landing above the reader: keep the turn they are on where it is.
244
+ if (maintainStartPosition && pageOut.current) {
245
+ pageOut.current = false;
246
+ scrollRef.current?.scrollTo({
247
+ y: offset.current + (height - previous),
248
+ animated: false
249
+ });
250
+ return;
251
+ }
252
+ pageOut.current = false;
253
+ if (!autoFollow) return;
254
+ if (followThreshold !== undefined) {
255
+ // Measured against the height BEFORE the growth: was the reader near
256
+ // the bottom when this arrived?
257
+ const fromBottom = previous - offset.current - viewport.current;
258
+ if (fromBottom > followThreshold) return;
259
+ }
260
+ scrollRef.current?.scrollToEnd({
261
+ animated: followAnimated
157
262
  });
158
- }, []);
263
+ }, [autoFollow, followAnimated, followThreshold, maintainStartPosition]);
159
264
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
160
265
  ref: scrollRef,
161
266
  testID: testID,
162
267
  ...(0, _shared.dataHook)('bloomAiChatScroll', 'thin'),
163
268
  onContentSizeChange: onContentSizeChange,
269
+ onLayout: onLayout,
270
+ onScroll: listening ? handleScroll : undefined,
271
+ scrollEventThrottle: listening ? scrollEventThrottle : undefined,
164
272
  style: [{
165
273
  minHeight: 0,
166
274
  width: '100%',
@@ -185,5 +293,5 @@ function AiChatThread({
185
293
  },
186
294
  children: children
187
295
  });
188
- }
296
+ });
189
297
  //# sourceMappingURL=AiChatContainer.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_index","_index2","_RiFolderLine","_RiMoreFill","_RiShare2Line","_AiChatControls","_shared","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","DEFAULT_LABELS","breadcrumb","share","more","AiChatContainer","project","title","projectIcon","RiFolderLine","onProjectPress","onShare","onMore","header","children","composer","working","workingLabel","labels","style","testID","useAiChatWebCss","palette","useAiChatPalette","l","useMemo","jsxs","View","height","minWidth","flex","flexDirection","overflow","borderRadius","CONTAINER_RADIUS","backgroundColor","secondary","width","alignItems","justifyContent","gap","paddingLeft","paddingRight","paddingTop","jsx","Breadcrumb","accessibilityLabel","BreadcrumbItem","icon","onPress","current","flexShrink","GlyphAction","RiShare2Line","label","RiMoreFill","paddingBottom","AgentThinking","variant","AiChatThread","scrollRef","useRef","lastHeight","onContentSizeChange","useCallback","_width","grew","first","scrollToEnd","animated","ScrollView","ref","dataHook","minHeight","contentContainerStyle","IS_WEB","flexGrow"],"sourceRoot":"../../../src","sources":["ai-chat/AiChatContainer.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_index","_index2","_RiFolderLine","_RiMoreFill","_RiShare2Line","_AiChatControls","_shared","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","DEFAULT_LABELS","breadcrumb","share","more","AiChatContainer","project","title","projectIcon","RiFolderLine","onProjectPress","onShare","onMore","header","children","composer","working","workingLabel","background","surface","labels","style","testID","useAiChatWebCss","palette","useAiChatPalette","l","useMemo","jsxs","View","height","minWidth","flex","flexDirection","overflow","borderRadius","CONTAINER_RADIUS","backgroundColor","secondary","jsx","pointerEvents","position","top","left","right","bottom","width","alignItems","justifyContent","gap","paddingLeft","paddingRight","paddingTop","Breadcrumb","accessibilityLabel","undefined","BreadcrumbItem","icon","onPress","current","flexShrink","GlyphAction","RiShare2Line","label","RiMoreFill","paddingBottom","AgentThinking","variant","AiChatThread","exports","forwardRef","autoFollow","followAnimated","followThreshold","onStartReached","onStartReachedThreshold","maintainStartPosition","onScroll","scrollEventThrottle","ref","scrollRef","useRef","lastHeight","offset","viewport","pageOut","startArmed","useImperativeHandle","scrollToEnd","options","animated","scrollToOffset","y","scrollTo","getScrollView","listening","handleScroll","useCallback","event","contentOffset","layoutMeasurement","nativeEvent","onLayout","layout","onContentSizeChange","_width","previous","grew","first","fromBottom","ScrollView","dataHook","minHeight","contentContainerStyle","IS_WEB","flexGrow"],"sourceRoot":"../../../src","sources":["ai-chat/AiChatContainer.tsx"],"mappings":"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","ignoreList":[]}
@@ -24,6 +24,8 @@ const REVEAL_MS = 325;
24
24
  const REVEAL_OFFSET = 272;
25
25
  const DRAWER_EASE = _reactNativeReanimated.Easing.bezier(0.4, 0, 0.2, 1);
26
26
  const DRAWER_MS = 300;
27
+ /** An icon-only rail: one 36px control plus its 10px gutters. */
28
+ const COLLAPSED_SIDEBAR_WIDTH = 56;
27
29
  const DEFAULT_LABELS = {
28
30
  openNavigation: 'Open navigation',
29
31
  closeNavigation: 'Close navigation',
@@ -255,7 +257,9 @@ function AiChatMobileHeader({
255
257
  * The AI chat shell: the full AI chat
256
258
  * screen on background-full with a 12px frame.
257
259
  *
258
- * lg and up the floating `sidebar` in flow, 16 from the workspace
260
+ * lg and up the floating `sidebar` in flow, 16 from the workspace, at
261
+ * `sidebarWidth` (or its own width) — `sidebarCollapsed` narrows
262
+ * the column to `collapsedSidebarWidth` (56) for an icon rail
259
263
  * workspace the chat container flexing, then (xl and up) the `panel` 12 to
260
264
  * its right at `panelWidth`; the resize grip on the chat's right
261
265
  * edge trades width between them, clamped 320–560
@@ -270,9 +274,19 @@ function AiChatMobileHeader({
270
274
  * over the workspace closes it
271
275
  *
272
276
  * The chat container reads the drawer controls through `AiChatMobileHeader`.
277
+ * Everything the shell knows — including `navPresented`, which says whether the
278
+ * nav is on screen at all — is published to any descendant through
279
+ * `useAiChatShell()`.
280
+ *
281
+ * `background` is a layer drawn above the shell's own paint and below all of
282
+ * it; with `surface={false}` neither the root nor the workspace paints, so the
283
+ * layer reaches the chat.
273
284
  */
274
285
  function AiChatShell({
275
286
  sidebar,
287
+ sidebarWidth,
288
+ sidebarCollapsed = false,
289
+ collapsedSidebarWidth = COLLAPSED_SIDEBAR_WIDTH,
276
290
  mobileSidebar,
277
291
  children,
278
292
  panel,
@@ -285,6 +299,8 @@ function AiChatShell({
285
299
  onNavOpenChange,
286
300
  panelOpen: panelOpenProp,
287
301
  onPanelOpenChange,
302
+ background,
303
+ surface = true,
288
304
  labels,
289
305
  style,
290
306
  testID
@@ -375,10 +391,14 @@ function AiChatShell({
375
391
  navCollapsed: !navInFlow,
376
392
  hasNav: !!mobileSidebar,
377
393
  hasPanel: !!panel,
394
+ // In flow it is always on screen; as a drawer, only while it is open.
395
+ navPresented: navInFlow ? !!sidebar : navOpen,
396
+ sidebarCollapsed,
378
397
  openNav: () => {
379
398
  setPanelOpen(false);
380
399
  setNavOpen(true);
381
400
  },
401
+ closeNav: () => setNavOpen(false),
382
402
  openPanel: () => {
383
403
  setNavOpen(false);
384
404
  setPanelOpen(true);
@@ -389,7 +409,7 @@ function AiChatShell({
389
409
  openNavigation: l.openNavigation,
390
410
  openPanel: l.openPanel
391
411
  }
392
- }), [wide, navInFlow, mobileSidebar, panel, setPanelOpen, setNavOpen, panelLabel, panelIcon, l]);
412
+ }), [wide, navInFlow, navOpen, sidebar, sidebarCollapsed, mobileSidebar, panel, setPanelOpen, setNavOpen, panelLabel, panelIcon, l]);
393
413
  const rootStyle = {
394
414
  position: 'relative',
395
415
  width: '100%',
@@ -398,7 +418,7 @@ function AiChatShell({
398
418
  gap: 16,
399
419
  overflow: 'hidden',
400
420
  padding: 12,
401
- backgroundColor: palette.full
421
+ backgroundColor: surface ? palette.full : 'transparent'
402
422
  };
403
423
  const veil = palette.isDark ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.1)';
404
424
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_context.AiChatShellContext.Provider, {
@@ -408,7 +428,17 @@ function AiChatShell({
408
428
  testID: testID,
409
429
  onLayout: event => setShellWidth(event.nativeEvent.layout.width),
410
430
  style: [rootStyle, style],
411
- children: [!navInFlow && mobileSidebar ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
431
+ children: [background ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
432
+ pointerEvents: "none",
433
+ style: {
434
+ position: 'absolute',
435
+ top: 0,
436
+ left: 0,
437
+ right: 0,
438
+ bottom: 0
439
+ },
440
+ children: background
441
+ }) : null, !navInFlow && mobileSidebar ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
412
442
  "aria-hidden": !navOpen,
413
443
  pointerEvents: navOpen ? 'box-none' : 'none',
414
444
  style: {
@@ -435,7 +465,17 @@ function AiChatShell({
435
465
  style: {
436
466
  position: 'relative',
437
467
  zIndex: 10,
438
- height: '100%'
468
+ height: '100%',
469
+ // Untouched unless the host asked for a width: the sidebar has
470
+ // always sized itself.
471
+ ...(sidebarCollapsed ? {
472
+ width: collapsedSidebarWidth,
473
+ flexShrink: 0,
474
+ overflow: 'hidden'
475
+ } : sidebarWidth !== undefined ? {
476
+ width: sidebarWidth,
477
+ flexShrink: 0
478
+ } : null)
439
479
  },
440
480
  children: sidebar
441
481
  }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, {
@@ -447,7 +487,7 @@ function AiChatShell({
447
487
  flexDirection: 'column',
448
488
  gap: 8,
449
489
  overflow: 'hidden',
450
- backgroundColor: palette.full
490
+ backgroundColor: surface ? palette.full : 'transparent'
451
491
  }, navInFlow ? null : workspaceStyle],
452
492
  children: [!navInFlow ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
453
493
  pointerEvents: navOpen ? 'auto' : 'none',