@genesislcap/ai-assistant 15.19.0 → 15.19.1

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 (288) hide show
  1. package/package.json +17 -18
  2. package/api-extractor.json +0 -11
  3. package/dist/dts/components/chat-driver/chat-driver.compact.test.d.ts +0 -2
  4. package/dist/dts/components/chat-driver/chat-driver.compact.test.d.ts.map +0 -1
  5. package/dist/dts/components/chat-driver/chat-driver.invocation-scope.test.d.ts +0 -2
  6. package/dist/dts/components/chat-driver/chat-driver.invocation-scope.test.d.ts.map +0 -1
  7. package/dist/dts/components/chat-driver/chat-driver.test.d.ts +0 -2
  8. package/dist/dts/components/chat-driver/chat-driver.test.d.ts.map +0 -1
  9. package/dist/dts/components/chat-driver/chat-driver.thinking-policy.test.d.ts +0 -2
  10. package/dist/dts/components/chat-driver/chat-driver.thinking-policy.test.d.ts.map +0 -1
  11. package/dist/dts/components/chat-driver/chat-driver.trace-capture.test.d.ts +0 -2
  12. package/dist/dts/components/chat-driver/chat-driver.trace-capture.test.d.ts.map +0 -1
  13. package/dist/dts/components/chat-driver/chat-driver.turn-usage.test.d.ts +0 -2
  14. package/dist/dts/components/chat-driver/chat-driver.turn-usage.test.d.ts.map +0 -1
  15. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.test.d.ts +0 -2
  16. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.test.d.ts.map +0 -1
  17. package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts +0 -2
  18. package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts.map +0 -1
  19. package/dist/dts/components/orchestrating-driver/orchestrating-driver.cost.test.d.ts +0 -2
  20. package/dist/dts/components/orchestrating-driver/orchestrating-driver.cost.test.d.ts.map +0 -1
  21. package/dist/dts/components/orchestrating-driver/orchestrating-driver.pin.test.d.ts +0 -2
  22. package/dist/dts/components/orchestrating-driver/orchestrating-driver.pin.test.d.ts.map +0 -1
  23. package/dist/dts/components/settings-modal/settings-modal.styles.test.d.ts +0 -2
  24. package/dist/dts/components/settings-modal/settings-modal.styles.test.d.ts.map +0 -1
  25. package/dist/dts/components/settings-modal/settings-modal.template.test.d.ts +0 -2
  26. package/dist/dts/components/settings-modal/settings-modal.template.test.d.ts.map +0 -1
  27. package/dist/dts/config/define-stateful-agent.test.d.ts +0 -2
  28. package/dist/dts/config/define-stateful-agent.test.d.ts.map +0 -1
  29. package/dist/dts/config/validate-providers.test.d.ts +0 -2
  30. package/dist/dts/config/validate-providers.test.d.ts.map +0 -1
  31. package/dist/dts/main/blocked-state.test.d.ts +0 -2
  32. package/dist/dts/main/blocked-state.test.d.ts.map +0 -1
  33. package/dist/dts/main/budget-meter.test.d.ts +0 -2
  34. package/dist/dts/main/budget-meter.test.d.ts.map +0 -1
  35. package/dist/dts/main/cost-session-banking.test.d.ts +0 -2
  36. package/dist/dts/main/cost-session-banking.test.d.ts.map +0 -1
  37. package/dist/dts/main/file-attachments.test.d.ts +0 -2
  38. package/dist/dts/main/file-attachments.test.d.ts.map +0 -1
  39. package/dist/dts/main/interaction-cost.test.d.ts +0 -2
  40. package/dist/dts/main/interaction-cost.test.d.ts.map +0 -1
  41. package/dist/dts/main/main.styles.test.d.ts +0 -2
  42. package/dist/dts/main/main.styles.test.d.ts.map +0 -1
  43. package/dist/dts/main/persistence-broken-sources.test.d.ts +0 -2
  44. package/dist/dts/main/persistence-broken-sources.test.d.ts.map +0 -1
  45. package/dist/dts/main/popout-interaction-gate.test.d.ts +0 -2
  46. package/dist/dts/main/popout-interaction-gate.test.d.ts.map +0 -1
  47. package/dist/dts/state/ai-assistant-slice.test.d.ts +0 -2
  48. package/dist/dts/state/ai-assistant-slice.test.d.ts.map +0 -1
  49. package/dist/dts/state/debug-event-log.test.d.ts +0 -2
  50. package/dist/dts/state/debug-event-log.test.d.ts.map +0 -1
  51. package/dist/dts/state/driver-registry.test.d.ts +0 -2
  52. package/dist/dts/state/driver-registry.test.d.ts.map +0 -1
  53. package/dist/dts/state/interaction-context.test.d.ts +0 -2
  54. package/dist/dts/state/interaction-context.test.d.ts.map +0 -1
  55. package/dist/dts/state/persistence/diagnostics-cursors.test.d.ts +0 -2
  56. package/dist/dts/state/persistence/diagnostics-cursors.test.d.ts.map +0 -1
  57. package/dist/dts/state/persistence/diagnostics.test.d.ts +0 -2
  58. package/dist/dts/state/persistence/diagnostics.test.d.ts.map +0 -1
  59. package/dist/dts/state/persistence/session-persistence-provider.test.d.ts +0 -2
  60. package/dist/dts/state/persistence/session-persistence-provider.test.d.ts.map +0 -1
  61. package/dist/dts/state/persistence/session-persistence.integration.test.d.ts +0 -2
  62. package/dist/dts/state/persistence/session-persistence.integration.test.d.ts.map +0 -1
  63. package/dist/dts/state/persistence/session-persister.test.d.ts +0 -2
  64. package/dist/dts/state/persistence/session-persister.test.d.ts.map +0 -1
  65. package/dist/dts/state/persistence/session-snapshot.test.d.ts +0 -2
  66. package/dist/dts/state/persistence/session-snapshot.test.d.ts.map +0 -1
  67. package/dist/dts/state/persistence/stateful-restore.e2e.test.d.ts +0 -2
  68. package/dist/dts/state/persistence/stateful-restore.e2e.test.d.ts.map +0 -1
  69. package/dist/dts/utils/animation-exclusivity.test.d.ts +0 -2
  70. package/dist/dts/utils/animation-exclusivity.test.d.ts.map +0 -1
  71. package/dist/dts/utils/collect-session-models.test.d.ts +0 -2
  72. package/dist/dts/utils/collect-session-models.test.d.ts.map +0 -1
  73. package/dist/dts/utils/condense-history.test.d.ts +0 -2
  74. package/dist/dts/utils/condense-history.test.d.ts.map +0 -1
  75. package/dist/dts/utils/cost-session-history.test.d.ts +0 -2
  76. package/dist/dts/utils/cost-session-history.test.d.ts.map +0 -1
  77. package/dist/dts/utils/derive-cost-session-title.test.d.ts +0 -2
  78. package/dist/dts/utils/derive-cost-session-title.test.d.ts.map +0 -1
  79. package/dist/dts/utils/flatten-sub-agent-messages.test.d.ts +0 -2
  80. package/dist/dts/utils/flatten-sub-agent-messages.test.d.ts.map +0 -1
  81. package/dist/dts/utils/history-transform.test.d.ts +0 -2
  82. package/dist/dts/utils/history-transform.test.d.ts.map +0 -1
  83. package/dist/dts/utils/message-partition.test.d.ts +0 -2
  84. package/dist/dts/utils/message-partition.test.d.ts.map +0 -1
  85. package/dist/dts/utils/resolve-cost-history-config.test.d.ts +0 -2
  86. package/dist/dts/utils/resolve-cost-history-config.test.d.ts.map +0 -1
  87. package/dist/dts/utils/resolve-preference-baseline.test.d.ts +0 -2
  88. package/dist/dts/utils/resolve-preference-baseline.test.d.ts.map +0 -1
  89. package/dist/dts/utils/strip-agent-handlers.test.d.ts +0 -2
  90. package/dist/dts/utils/strip-agent-handlers.test.d.ts.map +0 -1
  91. package/dist/dts/utils/sum-costs.test.d.ts +0 -2
  92. package/dist/dts/utils/sum-costs.test.d.ts.map +0 -1
  93. package/dist/dts/utils/sum-tokens.test.d.ts +0 -2
  94. package/dist/dts/utils/sum-tokens.test.d.ts.map +0 -1
  95. package/dist/dts/utils/sum-usage.test.d.ts +0 -2
  96. package/dist/dts/utils/sum-usage.test.d.ts.map +0 -1
  97. package/dist/dts/utils/usage-rows.test.d.ts +0 -2
  98. package/dist/dts/utils/usage-rows.test.d.ts.map +0 -1
  99. package/dist/esm/components/chat-driver/chat-driver.compact.test.js +0 -93
  100. package/dist/esm/components/chat-driver/chat-driver.invocation-scope.test.js +0 -238
  101. package/dist/esm/components/chat-driver/chat-driver.test.js +0 -2770
  102. package/dist/esm/components/chat-driver/chat-driver.thinking-policy.test.js +0 -137
  103. package/dist/esm/components/chat-driver/chat-driver.trace-capture.test.js +0 -200
  104. package/dist/esm/components/chat-driver/chat-driver.turn-usage.test.js +0 -268
  105. package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.test.js +0 -104
  106. package/dist/esm/components/orchestrating-driver/orchestrating-driver.budget.test.js +0 -312
  107. package/dist/esm/components/orchestrating-driver/orchestrating-driver.cost.test.js +0 -76
  108. package/dist/esm/components/orchestrating-driver/orchestrating-driver.pin.test.js +0 -61
  109. package/dist/esm/components/settings-modal/settings-modal.styles.test.js +0 -80
  110. package/dist/esm/components/settings-modal/settings-modal.template.test.js +0 -91
  111. package/dist/esm/config/define-stateful-agent.test.js +0 -53
  112. package/dist/esm/config/validate-providers.test.js +0 -100
  113. package/dist/esm/main/blocked-state.test.js +0 -1065
  114. package/dist/esm/main/budget-meter.test.js +0 -359
  115. package/dist/esm/main/cost-session-banking.test.js +0 -538
  116. package/dist/esm/main/file-attachments.test.js +0 -164
  117. package/dist/esm/main/interaction-cost.test.js +0 -107
  118. package/dist/esm/main/main.styles.test.js +0 -161
  119. package/dist/esm/main/persistence-broken-sources.test.js +0 -180
  120. package/dist/esm/main/popout-interaction-gate.test.js +0 -54
  121. package/dist/esm/state/ai-assistant-slice.test.js +0 -229
  122. package/dist/esm/state/debug-event-log.test.js +0 -117
  123. package/dist/esm/state/driver-registry.test.js +0 -55
  124. package/dist/esm/state/interaction-context.test.js +0 -78
  125. package/dist/esm/state/persistence/diagnostics-cursors.test.js +0 -30
  126. package/dist/esm/state/persistence/diagnostics.test.js +0 -280
  127. package/dist/esm/state/persistence/session-persistence-provider.test.js +0 -110
  128. package/dist/esm/state/persistence/session-persistence.integration.test.js +0 -113
  129. package/dist/esm/state/persistence/session-persister.test.js +0 -613
  130. package/dist/esm/state/persistence/session-snapshot.test.js +0 -256
  131. package/dist/esm/state/persistence/stateful-restore.e2e.test.js +0 -158
  132. package/dist/esm/utils/animation-exclusivity.test.js +0 -52
  133. package/dist/esm/utils/collect-session-models.test.js +0 -53
  134. package/dist/esm/utils/condense-history.test.js +0 -547
  135. package/dist/esm/utils/cost-session-history.test.js +0 -174
  136. package/dist/esm/utils/derive-cost-session-title.test.js +0 -37
  137. package/dist/esm/utils/flatten-sub-agent-messages.test.js +0 -139
  138. package/dist/esm/utils/history-transform.test.js +0 -158
  139. package/dist/esm/utils/message-partition.test.js +0 -90
  140. package/dist/esm/utils/resolve-cost-history-config.test.js +0 -32
  141. package/dist/esm/utils/resolve-preference-baseline.test.js +0 -69
  142. package/dist/esm/utils/strip-agent-handlers.test.js +0 -81
  143. package/dist/esm/utils/sum-costs.test.js +0 -114
  144. package/dist/esm/utils/sum-tokens.test.js +0 -51
  145. package/dist/esm/utils/sum-usage.test.js +0 -164
  146. package/dist/esm/utils/usage-rows.test.js +0 -189
  147. package/dist/tsconfig.tsbuildinfo +0 -1
  148. package/license.txt +0 -46
  149. package/sandbox/tsconfig.json +0 -8
  150. package/src/channel/ai-activity-bus.ts +0 -209
  151. package/src/channel/ai-activity-channel.ts +0 -146
  152. package/src/chat-driver-node.ts +0 -159
  153. package/src/components/activity-halo/activity-halo.ts +0 -106
  154. package/src/components/agent-picker/agent-picker.constants.ts +0 -2
  155. package/src/components/agent-picker/agent-picker.styles.ts +0 -24
  156. package/src/components/agent-picker/agent-picker.template.ts +0 -54
  157. package/src/components/agent-picker/agent-picker.ts +0 -99
  158. package/src/components/agent-picker/index.ts +0 -1
  159. package/src/components/ai-driver/ai-driver.ts +0 -166
  160. package/src/components/ai-driver/index.ts +0 -1
  161. package/src/components/chat-bubble/chat-bubble.styles.ts +0 -194
  162. package/src/components/chat-bubble/chat-bubble.template.ts +0 -67
  163. package/src/components/chat-bubble/chat-bubble.ts +0 -436
  164. package/src/components/chat-bubble/index.ts +0 -1
  165. package/src/components/chat-driver/align-event-globals.ts +0 -23
  166. package/src/components/chat-driver/chat-driver.compact.test.ts +0 -131
  167. package/src/components/chat-driver/chat-driver.invocation-scope.test.ts +0 -285
  168. package/src/components/chat-driver/chat-driver.test.ts +0 -3679
  169. package/src/components/chat-driver/chat-driver.thinking-policy.test.ts +0 -185
  170. package/src/components/chat-driver/chat-driver.trace-capture.test.ts +0 -251
  171. package/src/components/chat-driver/chat-driver.ts +0 -3925
  172. package/src/components/chat-driver/chat-driver.turn-usage.test.ts +0 -362
  173. package/src/components/chat-driver/index.ts +0 -1
  174. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts +0 -7
  175. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts +0 -6
  176. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts +0 -144
  177. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +0 -243
  178. package/src/components/chat-interaction-wrapper/index.ts +0 -3
  179. package/src/components/chat-markdown/chat-markdown.ts +0 -89
  180. package/src/components/chat-markdown/index.ts +0 -1
  181. package/src/components/flowing-waves-indicator.ts +0 -286
  182. package/src/components/halo-overlay.ts +0 -184
  183. package/src/components/orchestrating-driver/index.ts +0 -1
  184. package/src/components/orchestrating-driver/orchestrating-driver.budget.test.ts +0 -438
  185. package/src/components/orchestrating-driver/orchestrating-driver.cost.test.ts +0 -107
  186. package/src/components/orchestrating-driver/orchestrating-driver.pin.test.ts +0 -96
  187. package/src/components/orchestrating-driver/orchestrating-driver.ts +0 -813
  188. package/src/components/plasma-orb-indicator.ts +0 -281
  189. package/src/components/popout-manager/index.ts +0 -1
  190. package/src/components/popout-manager/popout-manager.ts +0 -212
  191. package/src/components/settings-modal/index.ts +0 -2
  192. package/src/components/settings-modal/settings-modal.styles.test.ts +0 -94
  193. package/src/components/settings-modal/settings-modal.styles.ts +0 -853
  194. package/src/components/settings-modal/settings-modal.template.test.ts +0 -124
  195. package/src/components/settings-modal/settings-modal.template.ts +0 -943
  196. package/src/components/waves-indicator.ts +0 -241
  197. package/src/config/config.ts +0 -608
  198. package/src/config/define-stateful-agent.test.ts +0 -64
  199. package/src/config/define-stateful-agent.ts +0 -642
  200. package/src/config/fallback-agents.ts +0 -29
  201. package/src/config/index.ts +0 -2
  202. package/src/config/validate-providers.test.ts +0 -148
  203. package/src/config/validate-providers.ts +0 -58
  204. package/src/index.ts +0 -29
  205. package/src/main/blocked-state.test.ts +0 -1444
  206. package/src/main/budget-meter.test.ts +0 -477
  207. package/src/main/cost-session-banking.test.ts +0 -679
  208. package/src/main/file-attachments.test.ts +0 -215
  209. package/src/main/index.ts +0 -3
  210. package/src/main/interaction-cost.test.ts +0 -140
  211. package/src/main/main.styles.test.ts +0 -216
  212. package/src/main/main.styles.ts +0 -995
  213. package/src/main/main.template.ts +0 -1119
  214. package/src/main/main.ts +0 -5432
  215. package/src/main/main.types.ts +0 -225
  216. package/src/main/persistence-broken-sources.test.ts +0 -219
  217. package/src/main/popout-interaction-gate.test.ts +0 -87
  218. package/src/provider/ai-provider-switcher.ts +0 -40
  219. package/src/provider/assistant-app-settings.ts +0 -84
  220. package/src/state/ai-assistant-slice.test.ts +0 -336
  221. package/src/state/ai-assistant-slice.ts +0 -604
  222. package/src/state/debug-event-log.test.ts +0 -154
  223. package/src/state/debug-event-log.ts +0 -358
  224. package/src/state/driver-registry.test.ts +0 -70
  225. package/src/state/driver-registry.ts +0 -79
  226. package/src/state/interaction-context.test.ts +0 -90
  227. package/src/state/interaction-context.ts +0 -101
  228. package/src/state/persistence/build-timeline-entries.ts +0 -66
  229. package/src/state/persistence/diagnostics-cursors.test.ts +0 -53
  230. package/src/state/persistence/diagnostics-cursors.ts +0 -80
  231. package/src/state/persistence/diagnostics.test.ts +0 -346
  232. package/src/state/persistence/diagnostics.ts +0 -220
  233. package/src/state/persistence/index.ts +0 -10
  234. package/src/state/persistence/persister-registry.ts +0 -55
  235. package/src/state/persistence/session-persistence-provider.test.ts +0 -136
  236. package/src/state/persistence/session-persistence-provider.ts +0 -226
  237. package/src/state/persistence/session-persistence.integration.test.ts +0 -141
  238. package/src/state/persistence/session-persister.test.ts +0 -787
  239. package/src/state/persistence/session-persister.ts +0 -917
  240. package/src/state/persistence/session-snapshot.test.ts +0 -352
  241. package/src/state/persistence/session-snapshot.ts +0 -344
  242. package/src/state/persistence/stateful-restore.e2e.test.ts +0 -229
  243. package/src/state/session-store.ts +0 -56
  244. package/src/styles/ai-colours.ts +0 -30
  245. package/src/styles/menu.ts +0 -187
  246. package/src/styles/settings-section.ts +0 -40
  247. package/src/suggestions/chat-suggestions.ts +0 -186
  248. package/src/tags/index.ts +0 -1
  249. package/src/types/ai-chat-widget.ts +0 -67
  250. package/src/types/interaction-context.ts +0 -82
  251. package/src/utils/animated-panel-toggle.ts +0 -62
  252. package/src/utils/animation-exclusivity.test.ts +0 -72
  253. package/src/utils/animation-exclusivity.ts +0 -40
  254. package/src/utils/banked-usage-baselines.ts +0 -90
  255. package/src/utils/collect-session-models.test.ts +0 -82
  256. package/src/utils/collect-session-models.ts +0 -65
  257. package/src/utils/condense-history.test.ts +0 -746
  258. package/src/utils/condense-history.ts +0 -448
  259. package/src/utils/cost-session-history.test.ts +0 -226
  260. package/src/utils/cost-session-history.ts +0 -218
  261. package/src/utils/derive-cost-session-title.test.ts +0 -63
  262. package/src/utils/derive-cost-session-title.ts +0 -76
  263. package/src/utils/flatten-sub-agent-messages.test.ts +0 -163
  264. package/src/utils/flatten-sub-agent-messages.ts +0 -88
  265. package/src/utils/format-usd.ts +0 -26
  266. package/src/utils/history-transform.test.ts +0 -228
  267. package/src/utils/history-transform.ts +0 -163
  268. package/src/utils/index.ts +0 -3
  269. package/src/utils/logger.ts +0 -3
  270. package/src/utils/message-partition.test.ts +0 -129
  271. package/src/utils/message-partition.ts +0 -115
  272. package/src/utils/resolve-cost-history-config.test.ts +0 -38
  273. package/src/utils/resolve-cost-history-config.ts +0 -38
  274. package/src/utils/resolve-preference-baseline.test.ts +0 -84
  275. package/src/utils/resolve-preference-baseline.ts +0 -80
  276. package/src/utils/strip-agent-handlers.test.ts +0 -99
  277. package/src/utils/strip-agent-handlers.ts +0 -53
  278. package/src/utils/sum-costs.test.ts +0 -136
  279. package/src/utils/sum-costs.ts +0 -18
  280. package/src/utils/sum-tokens.test.ts +0 -93
  281. package/src/utils/sum-tokens.ts +0 -68
  282. package/src/utils/sum-usage.test.ts +0 -191
  283. package/src/utils/sum-usage.ts +0 -208
  284. package/src/utils/tool-fold.ts +0 -181
  285. package/src/utils/usage-rows.test.ts +0 -237
  286. package/src/utils/usage-rows.ts +0 -187
  287. package/src/utils/with-timeout.ts +0 -24
  288. package/tsconfig.json +0 -9
@@ -1,995 +0,0 @@
1
- import { css } from '@genesislcap/web-core';
2
- import { settingsModalStyles } from '../components/settings-modal/settings-modal.styles';
3
- import {
4
- AI_COLOUR_AMBER,
5
- AI_COLOUR_CYAN,
6
- AI_COLOUR_PINK,
7
- AI_COLOUR_VIOLET,
8
- } from '../styles/ai-colours';
9
- import { menuItemStyles } from '../styles/menu';
10
-
11
- const baseStyles = css`
12
- :host {
13
- /* Shared max width for the conversation column and the composer, so they stay
14
- aligned. Override on the host to change both at once. NOTE: the @container
15
- threshold that floats the composer can't read this (container queries can't
16
- use custom properties), so if you override this below the 1500px default the
17
- float still triggers at 1500px. */
18
- --ai-conversation-max-width: 1500px;
19
-
20
- /* INTERNAL — not a supported override point, despite sitting beside one.
21
- Deliberately NOT --ai-*-namespaced and absent from docs/styling.md: the
22
- documented banner surface is ::part(blocked-banner), and this token is
23
- plumbing, not a knob. It is the banner's block margin AND the exact
24
- amount the resize handle pulls itself up by when it follows the banner —
25
- one name, on the host only because a sibling cannot read a custom
26
- property declared on the banner (they inherit downward), so the
27
- cancellation is structural: change the gap here and both rules move
28
- together, instead of two magnitudes ~80 lines apart that only prose kept
29
- in sync. A host that overrides it anyway gets a consistent gap, but that
30
- is a side effect, not a promise. */
31
- --blocked-banner-gap: calc(var(--design-unit) * 1px);
32
-
33
- display: flex;
34
- flex-direction: column;
35
- height: 100%;
36
- width: 100%;
37
- font-family: var(--body-font);
38
- color: var(--neutral-foreground-rest);
39
- }
40
-
41
- .chat-wrapper {
42
- container-type: inline-size;
43
- position: relative;
44
- display: flex;
45
- flex-direction: column;
46
- height: 100%;
47
- overflow: hidden;
48
- background-color: var(--neutral-layer-1);
49
- border: 1px solid var(--neutral-stroke-rest);
50
- border-radius: var(--control-corner-radius) * 1px;
51
- }
52
-
53
- .chat-header {
54
- display: flex;
55
- justify-content: space-between;
56
- align-items: center;
57
- padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
58
- border-bottom: 1px solid var(--neutral-stroke-rest);
59
- background-color: var(--neutral-layer-2);
60
- }
61
-
62
- .chat-header.draggable {
63
- cursor: move;
64
- user-select: none;
65
- }
66
-
67
- .chat-title {
68
- font-weight: bold;
69
- font-size: 1.2em;
70
- }
71
-
72
- .header-actions {
73
- display: flex;
74
- align-items: center;
75
- gap: 2px;
76
- margin-left: auto;
77
- }
78
-
79
- .settings-button,
80
- .popout-button {
81
- font-size: 1.2em;
82
- }
83
-
84
- ::slotted([slot='header-actions-extra']) {
85
- font-size: 1.2em;
86
- }
87
-
88
- .splash-wrapper {
89
- position: absolute;
90
- inset: 0;
91
- z-index: 10;
92
- overflow: hidden;
93
- pointer-events: none;
94
- }
95
-
96
- ::slotted([slot='splash']) {
97
- display: block;
98
- width: 100%;
99
- height: 100%;
100
- }
101
-
102
- .messages {
103
- flex: 1;
104
-
105
- /* Allow the scroll area to shrink below its content height so a grown
106
- composer (drag-to-resize) takes space from here instead of overflowing
107
- the column and clipping the input row's buttons. */
108
- min-height: 0;
109
- overflow-y: auto;
110
- padding: 10px;
111
- }
112
-
113
- /* Caps and centres the conversation column inside the (full-width) scroll
114
- container, so the scrollbar stays at the panel edge while the messages sit
115
- in a readable-width column. Exposed as part="messages-content" so a host
116
- can widen it (::part(messages-content) { max-width: none }) or neutralise
117
- the wrapper entirely (::part(messages-content) { display: contents }). */
118
- .messages-content {
119
- display: flex;
120
- flex-direction: column;
121
- gap: calc(var(--design-unit) * 2.5px);
122
- width: 100%;
123
- max-width: var(--ai-conversation-max-width);
124
- margin-inline: auto;
125
- }
126
-
127
- @keyframes slide-in-right {
128
- from {
129
- opacity: 0%;
130
- transform: translateX(16px);
131
- }
132
-
133
- to {
134
- opacity: 100%;
135
- transform: translateX(0);
136
- }
137
- }
138
-
139
- @keyframes slide-in-left {
140
- from {
141
- opacity: 0%;
142
- transform: translateX(-16px);
143
- }
144
-
145
- to {
146
- opacity: 100%;
147
- transform: translateX(0);
148
- }
149
- }
150
-
151
- @keyframes thinking-ring {
152
- 0% {
153
- transform: scale(1);
154
- opacity: 60%;
155
- }
156
-
157
- 100% {
158
- transform: scale(1.8);
159
- opacity: 0%;
160
- }
161
- }
162
-
163
- .agent-switch-indicator {
164
- display: flex;
165
- align-items: center;
166
- gap: 8px;
167
- margin: 8px 0;
168
- color: var(--neutral-foreground-hint, var(--neutral-foreground-rest));
169
- opacity: 60%;
170
- font-size: 0.75em;
171
- }
172
-
173
- .agent-switch-indicator::before,
174
- .agent-switch-indicator::after {
175
- content: '';
176
- flex: 1;
177
- height: 1px;
178
- background: currentColor;
179
- }
180
-
181
- .agent-switch-label {
182
- white-space: nowrap;
183
- }
184
-
185
- .message-row {
186
- display: flex;
187
- align-items: flex-start;
188
- gap: 8px;
189
- }
190
-
191
- .message-row.user {
192
- flex-direction: row-reverse;
193
- animation: slide-in-right 0.25s ease-out;
194
- }
195
-
196
- .message-row.ai,
197
- .message-row.ai-thinking,
198
- .message-row.ai-function {
199
- animation: slide-in-left 0.25s ease-out;
200
- }
201
-
202
- .message-row.interaction {
203
- flex-direction: column;
204
- animation: slide-in-left 0.25s ease-out;
205
- }
206
-
207
- /* A 'bubble' interaction reads like a normal assistant message: avatar to the
208
- left, bubble to the right — not the full-width column the other interaction
209
- presentations use. */
210
- .message-row.interaction.present-bubble {
211
- flex-direction: row;
212
- }
213
-
214
- .avatar {
215
- position: relative;
216
- width: 32px;
217
- height: 32px;
218
- border-radius: 50%;
219
- background-color: var(--neutral-layer-3);
220
- border: 1px solid var(--neutral-stroke-rest);
221
- display: flex;
222
- align-items: center;
223
- justify-content: center;
224
- flex-shrink: 0;
225
- font-size: 1em;
226
- overflow: hidden;
227
- }
228
-
229
- .avatar.ai-thinking {
230
- border-color: var(--ai-thinking-color, #7c3aed);
231
- overflow: visible;
232
- }
233
-
234
- .avatar.ai-thinking::before,
235
- .avatar.ai-thinking::after {
236
- content: '';
237
- position: absolute;
238
- inset: 0;
239
- border-radius: 50%;
240
- border: 2px solid var(--ai-thinking-color, #7c3aed);
241
- animation: thinking-ring 1.5s ease-out infinite;
242
- }
243
-
244
- .avatar.ai-thinking::after {
245
- animation-delay: 0.75s;
246
- }
247
-
248
- .avatar-img {
249
- width: 100%;
250
- height: 100%;
251
- object-fit: cover;
252
- border-radius: 50%;
253
- }
254
-
255
- .avatar-icon {
256
- display: flex;
257
- align-items: center;
258
- justify-content: center;
259
- width: 100%;
260
- height: 100%;
261
- }
262
-
263
- .avatar-icon svg {
264
- /* !important so a consumer-supplied SVG's own inline width/height/style
265
- cannot break out of the avatar bounds. */
266
- width: 84% !important;
267
- height: 84% !important;
268
- }
269
-
270
- .message {
271
- padding: calc(var(--design-unit) * 2.5px) calc(var(--design-unit) * 3.5px);
272
- max-width: 80%;
273
- min-width: 70px;
274
- font-family: var(--body-font);
275
- font-size: 1.1em;
276
-
277
- /* Long unbroken strings (URLs, file paths, tokens, base64) wrap instead of
278
- overflowing the bubble. overflow-wrap: anywhere also lets the bubble shrink
279
- below such a string's width in the flex row, preventing horizontal overflow. */
280
- overflow-wrap: anywhere;
281
- word-break: break-word;
282
- }
283
-
284
- .message.user {
285
- background: linear-gradient(
286
- 135deg,
287
- var(--accent-fill-rest) 0%,
288
- color-mix(in srgb, var(--accent-fill-rest) 70%, black) 100%
289
- );
290
-
291
- /* The on-accent foreground token that pairs with the --accent-fill-rest background
292
- above. It must be the design system's real token name: color is inherited, so an
293
- undefined custom property with no fallback is invalid at computed-value time and
294
- silently leaves the text at the inherited neutral foreground — which reads as
295
- white-ish (fine) on a dark app and near-black (unreadable) on a light one. */
296
- color: var(--foreground-on-accent-rest);
297
-
298
- /* Squared "tail" at top-right — the corner nearest the user avatar. */
299
- border-radius: 18px 4px 18px 18px;
300
- box-shadow: 0 2px 8px color-mix(in srgb, var(--accent-fill-rest) 35%, transparent);
301
- }
302
-
303
- .message.ai {
304
- background: linear-gradient(135deg, var(--neutral-layer-3) 0%, var(--neutral-layer-4) 100%);
305
- color: var(--neutral-foreground-rest);
306
-
307
- /* Squared "tail" at top-left — the corner nearest the assistant avatar. */
308
- border-radius: 4px 18px 18px;
309
- border: calc(var(--stroke-width) * 1px) solid var(--neutral-stroke-rest);
310
- box-shadow: var(--ai-message-shadow, 0 2px 8px rgb(0 0 0 / 15%));
311
- }
312
-
313
- .message.ai-thinking {
314
- background: linear-gradient(
315
- 135deg,
316
- color-mix(in srgb, var(--ai-thinking-color, #7c3aed) 12%, transparent) 0%,
317
- color-mix(in srgb, var(--ai-thinking-color, #7c3aed) 18%, transparent) 100%
318
- );
319
- color: var(--neutral-foreground-rest);
320
- border-radius: 4px 18px 18px;
321
- border-left: 2px solid var(--ai-thinking-color, #7c3aed);
322
- font-style: italic;
323
- font-size: 0.95em;
324
- }
325
-
326
- /* Narration shares the Thinking bubble but is the assistant's commentary (labelled "Working"),
327
- not its inner monologue — keep it upright so it reads as action, distinct from italic reasoning. */
328
- .message.ai-thinking.narration {
329
- font-style: normal;
330
- }
331
-
332
- .message.ai-function {
333
- background: linear-gradient(135deg, rgb(15 32 39 / 90%) 0%, rgb(30 58 58 / 80%) 100%);
334
- color: var(--ai-function-color, #86efac);
335
- border-radius: 4px 18px 18px;
336
- font-size: 1em;
337
- border: 1px solid color-mix(in srgb, var(--ai-function-color, #86efac) 20%, transparent);
338
- }
339
-
340
- /* 'label' (default) and 'bare' interactions render the widget chrome-free and
341
- full-width — the widget owns its body. */
342
- .message-row.interaction.present-label .message,
343
- .message-row.interaction.present-bare .message {
344
- background: none;
345
- border: none;
346
- padding: 0;
347
- max-width: 100%;
348
- width: 100%;
349
- }
350
-
351
- /* 'bubble' interactions reuse the standard assistant-message skin so the
352
- widget sits inside a normal chat bubble. Padding, max-width and min-width
353
- come from the base .message rule. */
354
- .message-row.interaction.present-bubble .message {
355
- background: linear-gradient(135deg, var(--neutral-layer-3) 0%, var(--neutral-layer-4) 100%);
356
- color: var(--neutral-foreground-rest);
357
- border-radius: 4px 18px 18px;
358
- border: calc(var(--stroke-width) * 1px) solid var(--neutral-stroke-rest);
359
- box-shadow: var(--ai-message-shadow, 0 2px 8px rgb(0 0 0 / 15%));
360
- }
361
-
362
- .sender {
363
- font-weight: bold;
364
- font-size: 0.9em;
365
- margin-bottom: 4px;
366
- }
367
-
368
- .payload {
369
- font-family: monospace;
370
- white-space: pre-wrap;
371
- margin: 0;
372
- font-size: 0.95em;
373
- }
374
-
375
- .payload.unknown-tool {
376
- color: var(--error-color, #f87171);
377
- border-left: 2px solid color-mix(in srgb, var(--error-color, #f87171) 40%, transparent);
378
- padding-left: 8px;
379
- }
380
-
381
- .live-sub-agent-trace {
382
- animation: slide-in-left 0.25s ease-out;
383
- border-left: 2px solid var(--neutral-stroke-rest);
384
- padding: 4px 8px;
385
- margin: 4px 0;
386
- opacity: 80%;
387
- }
388
-
389
- .live-sub-agent-name {
390
- font-family: monospace;
391
- font-size: 0.8em;
392
- opacity: 70%;
393
- font-style: italic;
394
- display: block;
395
- margin-bottom: 2px;
396
- }
397
-
398
- .sub-agent-trace {
399
- margin-top: 6px;
400
- border-left: 2px solid var(--neutral-stroke-rest);
401
- padding-left: 8px;
402
- }
403
-
404
- .sub-agent-trace-summary {
405
- font-family: monospace;
406
- font-size: 0.85em;
407
- opacity: 70%;
408
- cursor: pointer;
409
- user-select: none;
410
- padding: 2px 0;
411
- }
412
-
413
- .sub-agent-trace-summary:hover {
414
- opacity: 100%;
415
- }
416
-
417
- .sub-agent-message {
418
- font-family: monospace;
419
- font-size: 0.85em;
420
- margin-top: 4px;
421
- }
422
-
423
- .sub-agent-assistant {
424
- opacity: 85%;
425
- white-space: pre-wrap;
426
- }
427
-
428
- .sub-agent-tool-call {
429
- opacity: 60%;
430
- }
431
-
432
- .sub-agent-tool-name::before {
433
- content: '⚙ ';
434
- }
435
-
436
- .sub-agent-tool-result {
437
- opacity: 50%;
438
- border-left: 1px solid var(--neutral-stroke-rest);
439
- padding-left: 6px;
440
- white-space: pre-wrap;
441
- }
442
-
443
- /* The composer is one bordered box: textarea on top, controls on a bar beneath
444
- it, both inside the same surface. It is position:relative so it is the
445
- containing block for the two menus, which anchor to its top edge. */
446
- .composer {
447
- position: relative;
448
- display: flex;
449
- flex-direction: column;
450
-
451
- /* Matched by the bar's own padding-top below, so the divider between the two
452
- sits with equal air above and below it. */
453
- gap: calc(var(--design-unit) * 1.5px);
454
- padding: calc(var(--design-unit) * 1.5px);
455
-
456
- /* Flush with the panel edges by default, so only the top edge is drawn — side
457
- and bottom borders on a full-bleed strip are either invisible or noise, and
458
- a radius on square-cut corners reads as a mistake. The transparent border
459
- is still declared on all four sides so the box metrics are identical in
460
- both modes and crossing the floating threshold cannot reflow the composer.
461
- The container query below promotes it to a full, rounded card. */
462
- border: 1px solid transparent;
463
- border-top-color: var(--neutral-stroke-rest);
464
- background-color: var(--neutral-layer-2);
465
- }
466
-
467
- .composer-bar {
468
- display: flex;
469
- align-items: center;
470
- gap: calc(var(--design-unit) * 2px);
471
-
472
- /* Pulled out to the composer's padding edges so the divider below spans the
473
- full box rather than floating inset from it, with the same amount added
474
- back as padding so nothing on the bar actually moves. The inline padding is
475
- therefore the composer's own padding PLUS the 8px the textarea's control
476
- insets its text by — which is what keeps the leading pill aligned with the
477
- placeholder and gives Send the same inset on the right. The block-start
478
- padding is the divider's breathing room and pairs with the composer's gap. */
479
- margin-inline: calc(var(--design-unit) * -1.5px);
480
- padding: calc(var(--design-unit) * 1.5px)
481
- calc(var(--design-unit) * 1.5px + var(--design-unit) * 2px) 0;
482
-
483
- /* Off by default, but always PRESENT: painted in the composer's own surface
484
- colour so it is invisible while still occupying its 1px, which means an app
485
- switching it on gets a line and not a one-pixel reflow of the whole bar.
486
- Hosts that want it set --ai-composer-divider to a colour — see
487
- docs/styling.md. */
488
- border-top: 1px solid var(--ai-composer-divider, var(--neutral-layer-2));
489
- }
490
-
491
- .composer-bar-start,
492
- .composer-bar-end {
493
- display: flex;
494
- align-items: center;
495
- gap: calc(var(--design-unit) * 1.5px);
496
- min-width: 0;
497
- }
498
-
499
- .composer-bar-start {
500
- flex: 1 1 auto;
501
- }
502
-
503
- .composer-bar-end {
504
- flex: 0 0 auto;
505
- margin-inline-start: auto;
506
- }
507
-
508
- /* Mirror the messages column: cap the composer (input row + its buttons, resize
509
- handle, attachment chips, agent picker panel, suggestions) at the same max width
510
- and centre it, so it does not span full width while the messages sit in a
511
- narrower column. These are flex items in the column layout, so margin-inline:auto
512
- would disable their default stretch — width:100% keeps them filling up to the
513
- cap, and border-box keeps padding/border from overflowing when full-width.
514
- Overridable via ::part(input-row) etc. */
515
- .composer,
516
- .composer-resize-handle,
517
- .attachment-chips,
518
- .blocked-banner,
519
- chat-suggestions {
520
- box-sizing: border-box;
521
- width: 100%;
522
- max-width: var(--ai-conversation-max-width);
523
- margin-inline: auto;
524
- }
525
-
526
- /* When the assistant is wider than that cap the composer is genuinely constrained,
527
- so lift the input row — and the panels that sit above it — into matching
528
- floating, rounded, outlined cards with a gap to the bottom. Below the cap they
529
- stay full-width and flush (the default border-top). The query resolves against
530
- the assistant's own inline-size container (.chat-wrapper). The threshold is a
531
- literal because a container query can't read a custom property — keep it equal
532
- to the --ai-conversation-max-width default. */
533
- @container (width > 1500px) {
534
- .composer,
535
- chat-suggestions {
536
- border: 1px solid var(--neutral-stroke-rest);
537
- border-radius: 12px;
538
- }
539
-
540
- /* The banner joins the same floating-card family, but keeps its own
541
- warning-toned border — overriding it with the neutral stroke would erase
542
- the one colour signal it carries. Only the radius needs to match. */
543
- .blocked-banner.is-visible {
544
- border-radius: 12px;
545
- }
546
-
547
- .composer {
548
- margin-bottom: 12px;
549
- }
550
-
551
- /* Genuinely floating now, so the whole ring is drawn and the focus tint
552
- follows it round rather than lighting only the top edge. */
553
- .composer:focus-within {
554
- border-color: var(--focus-stroke-outer);
555
- }
556
-
557
- /* chat-suggestions paints its background on an inner element, so clip it to the
558
- rounded corners — otherwise that square inner background bleeds past the radius
559
- and the corners read as square with the border hidden behind them. The smaller
560
- end margin keeps it a distinct card above the composer without a large void. */
561
- chat-suggestions {
562
- overflow: hidden;
563
- margin-bottom: 8px;
564
- }
565
- }
566
-
567
- /* Focus lives on the textarea inside, which is visually flush with the box —
568
- so lift the ring to the box itself and let the inner control stay quiet.
569
- Driven by :focus-within rather than a class: no state to keep in sync.
570
- Declared here, below the container query, because that block also styles a
571
- bare .composer and a lower-specificity rule may not follow a higher one. */
572
- .composer:focus-within {
573
- border-top-color: var(--focus-stroke-outer);
574
- }
575
-
576
- /* ── Composer control pills ───────────────────────────────────────────────
577
- One shape for every icon control on the bar: a round, quiet button that
578
- tints on hover. The design-system button paints its surface on :host and
579
- pads an inner 'control' part, so the circle is set here and the padding
580
- cleared there — otherwise the control's default 16px inline padding forces
581
- the host wider than it is tall and the circle becomes a lozenge. */
582
- .composer-pill {
583
- --pill-size: calc(var(--design-unit) * 7px);
584
-
585
- height: var(--pill-size);
586
- min-width: var(--pill-size);
587
- border-radius: 999px;
588
- color: var(--neutral-foreground-hint);
589
- background: var(--neutral-fill-stealth-rest);
590
- }
591
-
592
- .composer-pill:hover {
593
- color: var(--neutral-foreground-rest);
594
- background: var(--neutral-fill-stealth-hover);
595
- }
596
-
597
- .composer-pill[disabled] {
598
- opacity: 45%;
599
- }
600
-
601
- .composer-pill::part(control) {
602
- min-width: 0;
603
- padding: 0;
604
- justify-content: center;
605
- }
606
-
607
- /* The agent pill is the one that carries a label, so it is the one that grows.
608
- A pill-height radius rather than a circle, and room for the text. */
609
- .composer-pill-labelled {
610
- max-width: 45%;
611
- }
612
-
613
- .composer-pill-labelled::part(control) {
614
- gap: calc(var(--design-unit) * 1px);
615
- padding: 0 calc(var(--design-unit) * 2px) 0 calc(var(--design-unit) * 1px);
616
- }
617
-
618
- /* The glyph keeps the circular footprint the unlabelled pills have, so the
619
- three read as one family with the agent one simply extended by its label. */
620
- .pill-glyph {
621
- display: inline-flex;
622
- align-items: center;
623
- justify-content: center;
624
- flex: 0 0 auto;
625
- width: calc(var(--design-unit) * 5px);
626
- height: calc(var(--design-unit) * 5px);
627
- border-radius: 50%;
628
- font-size: 0.8em;
629
- }
630
-
631
- .pill-label {
632
- overflow: hidden;
633
- font-size: 0.75em;
634
- font-weight: 600;
635
- letter-spacing: 0.02em;
636
- text-overflow: ellipsis;
637
- white-space: nowrap;
638
- }
639
-
640
- /* Send keeps the design system's own accent button. The shell around it is
641
- inert by default and exists only as a styling hook — see the note in
642
- main.template.ts and ::part(send-shell) in docs/styling.md. */
643
- .send-shell {
644
- display: inline-flex;
645
- border-radius: calc(var(--control-corner-radius) * 1px);
646
- }
647
-
648
- .send-button {
649
- height: calc(var(--design-unit) * 7px);
650
- }
651
-
652
- /* The "stopping" state shows a spinner — rotate it (the icon set's glyph is
653
- static). Transform-only, so it never affects layout/width. */
654
- @keyframes ai-stop-spin {
655
- to {
656
- transform: rotate(360deg);
657
- }
658
- }
659
-
660
- .stop-icon-spinning {
661
- /* inline-block so the icon host is transformable — an inline element
662
- ignores the transform, which is why the spinner appeared frozen. */
663
- display: inline-block;
664
- animation: ai-stop-spin 0.8s linear infinite;
665
- }
666
-
667
- /* ── Composer menus ───────────────────────────────────────────────────────
668
- The agent picker and the session menu are the same panel with different
669
- contents. Absolutely positioned against .composer, so opening one neither
670
- pushes the transcript up nor covers a half-typed message, and edge-aligned
671
- to the button that opened it. */
672
- .composer-menu {
673
- position: absolute;
674
- z-index: 2;
675
- box-sizing: border-box;
676
- min-width: calc(var(--design-unit) * 55px);
677
-
678
- /* Never wider than the composer, so a long agent name cannot push the menu
679
- past the panel edge at narrow widths. */
680
- max-width: 100%;
681
-
682
- /* Both the ceiling and the measured headroom apply: whichever is smaller
683
- wins, so a tall assistant gets a comfortable menu and a short one gets a
684
- scrolling menu rather than one clipped by the wrapper's overflow. */
685
- max-height: min(320px, var(--composer-menu-max-height, 320px));
686
- overflow-y: auto;
687
- padding: calc(var(--design-unit) * 1px);
688
- border: 1px solid var(--neutral-stroke-rest);
689
- border-radius: calc(var(--control-corner-radius) * 2px);
690
- background-color: var(--neutral-layer-3);
691
- box-shadow: 0 8px 24px rgb(0 0 0 / 28%);
692
-
693
- /* All three offsets are measured and written by positionComposerMenu() when
694
- the menu opens — CSS cannot read the trigger's offset from here. The menu
695
- sits directly on top of the button that opened it, in both axes, and grows
696
- out of that button's centre. Anchoring to the button rather than to the
697
- composer's top edge means it can overlap the last line or two of the
698
- textarea; that is the deliberate trade for a menu that reads as belonging
699
- to the control, and it is transient. The fallbacks only apply for the one
700
- frame before the measurement lands. */
701
- left: var(--composer-menu-left, 0);
702
- bottom: calc(var(--composer-menu-bottom, 100%) + var(--design-unit) * 2px);
703
- transform-origin: var(--composer-menu-origin, 0) bottom;
704
- animation: composer-menu-in 0.14s ease-out;
705
- }
706
-
707
- .composer-menu.closing {
708
- animation: composer-menu-out 0.14s ease-in forwards;
709
- }
710
-
711
- @keyframes composer-menu-in {
712
- from {
713
- opacity: 0%;
714
- transform: scale(0.96) translateY(4px);
715
- }
716
-
717
- to {
718
- opacity: 100%;
719
- transform: scale(1) translateY(0);
720
- }
721
- }
722
-
723
- @keyframes composer-menu-out {
724
- from {
725
- opacity: 100%;
726
- transform: scale(1) translateY(0);
727
- }
728
-
729
- to {
730
- opacity: 0%;
731
- transform: scale(0.96) translateY(4px);
732
- }
733
- }
734
-
735
- /* The session menu renders its rows directly (the picker is a child element
736
- that brings its own copy of the shared row styles). */
737
- .session-menu-panel {
738
- display: flex;
739
- flex-direction: column;
740
- gap: 1px;
741
- }
742
-
743
- /* Shown in place of the transcript while a manual compaction runs (GENC-1351). */
744
- .compacting-indicator {
745
- display: flex;
746
- flex-direction: column;
747
- align-items: center;
748
- justify-content: center;
749
- gap: calc(var(--design-unit) * 2px);
750
- padding: calc(var(--design-unit) * 6px) 0;
751
- color: var(--neutral-foreground-hint, var(--neutral-foreground-rest));
752
- }
753
-
754
- .compacting-caption {
755
- font-size: 0.9em;
756
- }
757
-
758
- /* Backend-blocked banner (GENC-1464) — sits between the transcript and the
759
- disabled composer. Flat and quiet rather than an error toast: the condition
760
- is standing, not a momentary failure, so it should read as part of the
761
- chrome the user is now working within. */
762
-
763
- /* The container is ALWAYS in the DOM so the live region is registered before the
764
- text arrives (see main.template.ts) — so the bare selector carries only layout,
765
- and an unblocked banner is an empty, zero-height, invisible box. Everything
766
- that would make it visible hangs off .is-visible. */
767
- .blocked-banner {
768
- display: flex;
769
- align-items: center;
770
- gap: calc(var(--design-unit) * 2px);
771
- }
772
-
773
- .blocked-banner.is-visible {
774
- /* Block axis only. The inline-axis margin is owned by the composer width group
775
- above (margin-inline: auto), which centres the banner on the same column as
776
- the input row — an inline margin here would fight it. */
777
- margin-block: var(--blocked-banner-gap);
778
- padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
779
- border: calc(var(--stroke-width) * 1px) solid var(--warning-color);
780
- border-radius: calc(var(--control-corner-radius) * 1px);
781
- background: var(--neutral-layer-2);
782
- color: var(--neutral-foreground-rest);
783
- }
784
-
785
- /* Partial exhaustion: at least one vendor is walled but the composer is still
786
- live, so the banner is advice rather than a wall. Softened to match — same
787
- layout, same part, lower alarm. */
788
- .blocked-banner.is-partial {
789
- border-color: var(--neutral-stroke-rest);
790
- }
791
-
792
- .blocked-banner-text {
793
- font-size: 0.9em;
794
- line-height: 1.4;
795
- }
796
-
797
- .chat-input {
798
- flex: 1;
799
- resize: none;
800
- align-self: stretch;
801
-
802
- /* Drag-to-resize. --composer-height (set inline by the handle) pins an
803
- explicit height once dragged; unset, height is auto and capped at 150px
804
- (the original auto-grow behaviour). The host is sized — not the inner
805
- control — because the design system already stretches its control to the
806
- host height, and that rule out-specifies anything we could set via
807
- ::part(control). Sizing the host (not just its max-height) is what stops
808
- a downward drag from squishing the textarea below its natural height. */
809
- height: var(--composer-height);
810
- max-height: var(--composer-height, 150px);
811
- }
812
-
813
- /* The box around the textarea is .composer's job now, so strip the control's
814
- own frame — two nested borders is what made the old row look like a field
815
- inside a field. */
816
- .chat-input::part(control) {
817
- border: none;
818
- background: transparent;
819
- }
820
-
821
- .chat-input:focus-within::part(control) {
822
- outline: none;
823
- box-shadow: none;
824
- }
825
-
826
- /* Full-width grab bar above the input row; drag up/down to resize the composer.
827
- Sits above the row so it never overlaps the send/stop controls. */
828
- .composer-resize-handle {
829
- height: 7px;
830
- flex-shrink: 0;
831
- cursor: ns-resize;
832
- touch-action: none;
833
- user-select: none;
834
- display: flex;
835
- align-items: center;
836
- justify-content: center;
837
- }
838
-
839
- .composer-resize-handle::after {
840
- content: '';
841
- width: 32px;
842
- height: 3px;
843
- border-radius: 999px;
844
- background-color: var(--neutral-stroke-rest);
845
- opacity: 0%;
846
- transition: opacity 0.15s ease;
847
- }
848
-
849
- .composer-resize-handle:hover::after {
850
- opacity: 70%;
851
- }
852
-
853
- /* With the banner visible, its block-end margin and the handle's height used
854
- to STACK into an ~11px void between the exhausted message and the composer
855
- — and the hover pill centres in the handle's own 7px slice, i.e. below the
856
- middle of the gap the user actually sees. Pull the handle up over the
857
- banner's end margin instead: the handle's height becomes the whole gap and
858
- the pill lands dead-centre of it. FAST's when-anchors are comment nodes,
859
- so the adjacency holds whenever the handle renders; without a handle
860
- (resize disabled) the banner keeps its normal margin before the input row. */
861
- .blocked-banner.is-visible + .composer-resize-handle {
862
- margin-block-start: calc(var(--blocked-banner-gap) * -1);
863
- }
864
-
865
- .file-input {
866
- display: none;
867
- }
868
-
869
- .thinking-dots {
870
- display: flex;
871
- align-items: center;
872
- align-self: center;
873
- gap: 5px;
874
- padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
875
- }
876
-
877
- .dot {
878
- width: 9px;
879
- height: 9px;
880
- border-radius: 50%;
881
- background-image: linear-gradient(145deg, rgb(255 255 255 / 40%) 0%, rgb(0 0 0 / 0%) 100%);
882
- animation: dot-bounce 1.5s linear infinite;
883
- }
884
-
885
- .dot-1 {
886
- background-color: ${AI_COLOUR_AMBER};
887
- animation-delay: 0s;
888
- }
889
-
890
- .dot-2 {
891
- background-color: ${AI_COLOUR_PINK};
892
- animation-delay: 0.1s;
893
- }
894
-
895
- .dot-3 {
896
- background-color: ${AI_COLOUR_CYAN};
897
- animation-delay: 0.2s;
898
- }
899
-
900
- .dot-4 {
901
- background-color: ${AI_COLOUR_VIOLET};
902
- animation-delay: 0.3s;
903
- }
904
-
905
- @keyframes dot-bounce {
906
- 0%,
907
- 50%,
908
- 100% {
909
- transform: scale(1);
910
- filter: blur(0);
911
- }
912
-
913
- 25% {
914
- transform: scale(0.6);
915
- filter: blur(2px);
916
- }
917
-
918
- 75% {
919
- transform: scale(1.4);
920
- filter: blur(2px);
921
- }
922
- }
923
-
924
- .thinking-waves,
925
- .thinking-flowing-waves,
926
- .thinking-plasma {
927
- display: flex;
928
- flex-direction: column;
929
- align-items: center;
930
- align-self: center;
931
- gap: 6px;
932
- padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
933
- }
934
-
935
- /* The flowing-waves host is the avatar's height (32px), so drop the top padding
936
- the shared rule adds — that keeps the wave line level with the avatar instead
937
- of dropping it toward the row's vertical centre. */
938
- .thinking-flowing-waves {
939
- padding-top: 0;
940
- }
941
-
942
- .thinking-caption {
943
- font-size: var(--type-ramp-minus-1-font-size, 12px);
944
- line-height: var(--type-ramp-minus-1-line-height, 16px);
945
- color: var(--neutral-foreground-rest);
946
- opacity: 70%;
947
- }
948
-
949
- .attachment-chips {
950
- display: flex;
951
- flex-wrap: wrap;
952
- gap: 8px;
953
- padding: 0 calc(var(--design-unit) * 3px) calc(var(--design-unit) * 2px);
954
- }
955
-
956
- .attachment-chip {
957
- display: flex;
958
- align-items: center;
959
- gap: 6px;
960
- padding: 4px 8px;
961
- background-color: var(--neutral-layer-4);
962
- border-radius: calc(var(--control-corner-radius) * 1px);
963
- font-size: 0.9em;
964
- border: 1px solid var(--neutral-stroke-rest);
965
- }
966
-
967
- .attachment-chip-error {
968
- background-color: var(--error-fill-rest);
969
- border-color: var(--error-stroke-rest);
970
- }
971
-
972
- .chip-remove {
973
- background: none;
974
- border: none;
975
- color: var(--neutral-foreground-rest);
976
- cursor: pointer;
977
- font-size: 1.2em;
978
- padding: 0 0 0 4px;
979
- opacity: 70%;
980
- }
981
-
982
- .chip-remove:hover {
983
- opacity: 100%;
984
- }
985
- `;
986
-
987
- export const styles = css`
988
- ${baseStyles}
989
- ${
990
- /* The session menu's rows. Same partial the agent picker imports into its own
991
- shadow root — the one thing that keeps the two menus looking alike. */ ''
992
- }
993
- ${menuItemStyles}
994
- ${settingsModalStyles}
995
- `;