@lostgradient/cinder 0.8.0 → 0.9.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 (271) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +2 -2
  3. package/components.json +59 -15
  4. package/dist/components/_internal/command-list-context.d.ts +3 -3
  5. package/dist/components/_internal/create-command-list-state.svelte.d.ts +11 -11
  6. package/dist/components/access-gate/index.js +201 -126
  7. package/dist/components/approval-card/approval-card-actions.svelte.d.ts +16 -0
  8. package/dist/components/approval-card/approval-card-state.d.ts +65 -0
  9. package/dist/components/approval-card/approval-card.css +269 -92
  10. package/dist/components/approval-card/approval-card.schema.js +10 -31
  11. package/dist/components/approval-card/approval-card.svelte.d.ts +3 -3
  12. package/dist/components/approval-card/approval-card.types.d.ts +14 -28
  13. package/dist/components/approval-card/index.d.ts +1 -1
  14. package/dist/components/approval-card/index.js +4268 -2884
  15. package/dist/components/card/card.css +5 -0
  16. package/dist/components/card/card.schema.js +2 -2
  17. package/dist/components/card/card.types.d.ts +2 -2
  18. package/dist/components/card/index.js +2 -1
  19. package/dist/components/chat/chat.schema.js +44 -2
  20. package/dist/components/chat/chat.svelte.d.ts +4 -2
  21. package/dist/components/chat/chat.types.d.ts +34 -3
  22. package/dist/components/chat/container/chat.svelte.d.ts +4 -2
  23. package/dist/components/chat/index.d.ts +3 -3
  24. package/dist/components/chat/index.js +2485 -6279
  25. package/dist/components/chat/input/chat-attachment-serialization.d.ts +9 -0
  26. package/dist/components/chat/input/chat-input.svelte.d.ts +22 -1
  27. package/dist/components/chat/input/index.d.ts +1 -0
  28. package/dist/components/chat-composer-popover/chat-composer-popover-filter.d.ts +11 -0
  29. package/dist/components/chat-composer-popover/chat-composer-popover.css +7 -0
  30. package/dist/components/chat-composer-popover/chat-composer-popover.css.d.ts +1 -0
  31. package/dist/components/chat-composer-popover/chat-composer-popover.schema.d.ts +3 -0
  32. package/dist/components/chat-composer-popover/chat-composer-popover.schema.js +150 -0
  33. package/dist/components/chat-composer-popover/chat-composer-popover.svelte.d.ts +46 -0
  34. package/dist/components/chat-composer-popover/chat-composer-popover.types.d.ts +77 -0
  35. package/dist/components/chat-composer-popover/chat-composer-popover.variables.d.ts +2 -0
  36. package/dist/components/chat-composer-popover/chat-composer-popover.variables.js +61 -0
  37. package/dist/components/chat-composer-popover/index.d.ts +6 -0
  38. package/dist/components/chat-composer-popover/index.js +3285 -0
  39. package/dist/components/chat-conversation-header/index.js +1627 -5600
  40. package/dist/components/chat-conversation-list/index.js +15 -3988
  41. package/dist/components/code-block/code-block.schema.js +5 -1
  42. package/dist/components/code-block/code-block.types.d.ts +6 -0
  43. package/dist/components/code-block/index.js +4 -4
  44. package/dist/components/command-item/index.js +7 -6
  45. package/dist/components/command-menu/command-menu.schema.js +5 -1
  46. package/dist/components/command-menu/command-menu.types.d.ts +2 -0
  47. package/dist/components/command-menu/index.js +48 -19
  48. package/dist/components/command-palette/index.js +40 -14
  49. package/dist/components/event-stream-viewer/index.js +44 -44
  50. package/dist/components/experimental/json-viewer/index.js +44 -44
  51. package/dist/components/json-schema-editor/index.js +5 -2
  52. package/dist/components/json-viewer/index.js +44 -44
  53. package/dist/components/json-viewer/json-viewer.css +10 -6
  54. package/dist/components/payload-inspector/index.d.ts +1 -1
  55. package/dist/components/payload-inspector/index.js +306 -1422
  56. package/dist/components/payload-inspector/payload-inspector.css +15 -75
  57. package/dist/components/payload-inspector/payload-inspector.schema.js +5 -41
  58. package/dist/components/payload-inspector/payload-inspector.svelte.d.ts +5 -5
  59. package/dist/components/payload-inspector/payload-inspector.types.d.ts +10 -56
  60. package/dist/components/tab/index.js +5 -2
  61. package/dist/components/tab/tab.schema.js +7 -3
  62. package/dist/components/tab/tab.svelte.d.ts +2 -1
  63. package/dist/components/tab/tab.types.d.ts +8 -2
  64. package/dist/components/tabs/index.js +5 -2
  65. package/dist/components/team-section/index.js +2 -1
  66. package/dist/components/testimonial-section/index.js +2 -1
  67. package/dist/index.d.ts +4 -2
  68. package/dist/index.js +23711 -27432
  69. package/dist/server/components/access-gate/index.js +2 -1
  70. package/dist/server/components/accordion/index.js +2 -2
  71. package/dist/server/components/alert-dialog/index.js +2 -2
  72. package/dist/server/components/approval-card/index.js +15 -19
  73. package/dist/server/components/autocomplete/index.js +7 -7
  74. package/dist/server/components/avatar-group/index.js +5 -5
  75. package/dist/server/components/button-group/index.js +3 -5
  76. package/dist/server/components/card/index.js +2 -2
  77. package/dist/server/components/chat/index.js +14 -11
  78. package/dist/server/components/chat-composer-popover/index.js +26 -0
  79. package/dist/server/components/chat-conversation-header/index.js +9 -10
  80. package/dist/server/components/chat-conversation-list/index.js +4 -5
  81. package/dist/server/components/checkbox/index.js +3 -3
  82. package/dist/server/components/choice-grid/index.js +2 -2
  83. package/dist/server/components/code-block/index.js +2 -2
  84. package/dist/server/components/color-field/index.js +3 -3
  85. package/dist/server/components/combobox/index.js +6 -6
  86. package/dist/server/components/command-item/index.js +5 -3
  87. package/dist/server/components/command-menu/index.js +6 -4
  88. package/dist/server/components/command-palette/index.js +2 -2
  89. package/dist/server/components/confirm-dialog/index.js +2 -2
  90. package/dist/server/components/context-menu/index.js +5 -5
  91. package/dist/server/components/data-table/index.js +5 -5
  92. package/dist/server/components/date-picker/index.js +6 -6
  93. package/dist/server/components/date-range-field/index.js +7 -7
  94. package/dist/server/components/description-list/index.js +3 -5
  95. package/dist/server/components/diff-viewer/index.js +7 -7
  96. package/dist/server/components/drawer/index.js +3 -3
  97. package/dist/server/components/dropdown/index.js +4 -4
  98. package/dist/server/components/event-stream-viewer/index.js +2 -2
  99. package/dist/server/components/experimental/json-viewer/index.js +2 -2
  100. package/dist/server/components/faceted-filter-bar/index.js +1 -1
  101. package/dist/server/components/hover-card/index.js +1 -1
  102. package/dist/server/components/input/index.js +3 -3
  103. package/dist/server/components/json-schema-editor/index.js +22 -22
  104. package/dist/server/components/json-viewer/index.js +2 -2
  105. package/dist/server/components/markdown-editor/index.js +13 -13
  106. package/dist/server/components/modal/index.js +1 -1
  107. package/dist/server/components/multi-select/index.js +1 -1
  108. package/dist/server/components/newsletter-section/index.js +3 -3
  109. package/dist/server/components/payload-inspector/index.js +4 -16
  110. package/dist/server/components/popover/index.js +5 -5
  111. package/dist/server/components/review-editor/index.js +25 -25
  112. package/dist/server/components/run-step-timeline/index.js +2 -2
  113. package/dist/server/components/schema-form/index.js +4 -4
  114. package/dist/server/components/secret-value-field/index.js +3 -5
  115. package/dist/server/components/segment/index.js +3 -3
  116. package/dist/server/components/segmented-control/index.js +4 -4
  117. package/dist/server/components/selection-popover/index.js +4 -4
  118. package/dist/server/components/sheet/index.js +2 -2
  119. package/dist/server/components/sidebar/index.js +3 -3
  120. package/dist/server/components/tab/index.js +2 -2
  121. package/dist/server/components/table/index.js +3 -3
  122. package/dist/server/components/tabs/index.js +4 -4
  123. package/dist/server/components/tag-input/index.js +3 -3
  124. package/dist/server/components/team-section/index.js +8 -8
  125. package/dist/server/components/testimonial-section/index.js +2 -2
  126. package/dist/server/components/toggle/index.js +2 -2
  127. package/dist/server/components/tooltip/index.js +1 -1
  128. package/dist/server/index.js +159 -148
  129. package/dist/server/{index.server-cbtg8rq5.js → index.server-0atyznp7.js} +5 -5
  130. package/dist/server/{index.server-20y4mdhm.js → index.server-0chhpkg7.js} +40 -14
  131. package/dist/server/{index.server-ecm1vs3g.js → index.server-1nq7z1qw.js} +1 -1
  132. package/dist/server/{index.server-jync915v.js → index.server-2dga5yzr.js} +25 -25
  133. package/dist/server/index.server-2hqrv7ca.js +9 -0
  134. package/dist/server/{index.server-69epaape.js → index.server-2qj6s2mh.js} +1 -1
  135. package/dist/server/{index.server-pt8b65mt.js → index.server-38aywdk1.js} +1 -1
  136. package/dist/server/{index.server-rermas0k.js → index.server-47v2fhp4.js} +4 -4
  137. package/dist/server/{index.server-9j5sym59.js → index.server-4x91gzy1.js} +2 -2
  138. package/dist/server/{index.server-q48q15g7.js → index.server-4y2jtvv1.js} +4 -2
  139. package/dist/server/index.server-67qgdypc.js +9 -0
  140. package/dist/server/{index.server-9jete4xx.js → index.server-6sh11ap6.js} +4 -4
  141. package/dist/server/{index.server-4ztq565m.js → index.server-85p4ydph.js} +1 -1
  142. package/dist/server/{index.server-4nk7gy0q.js → index.server-894t97kv.js} +1 -1
  143. package/dist/server/{index.server-1vscnped.js → index.server-8dt36sdm.js} +2 -1
  144. package/dist/server/{index.server-bfsbp9dz.js → index.server-8xvc73zh.js} +4 -4
  145. package/dist/server/{index.server-cvnw3hw9.js → index.server-9kz37z89.js} +1 -1
  146. package/dist/server/{index.server-kc1k02tk.js → index.server-9ykk1kbg.js} +1 -1
  147. package/dist/server/{index.server-g789yep6.js → index.server-aqethy0w.js} +2 -2
  148. package/dist/server/{index.server-wc9aggfz.js → index.server-bmxbsk4y.js} +7 -7
  149. package/dist/server/{index.server-j3ddzcaa.js → index.server-c6pe4kaf.js} +6 -6
  150. package/dist/server/{index.server-q89dz8h1.js → index.server-d3pqthtp.js} +4 -3
  151. package/dist/server/{index.server-qfa2rxzx.js → index.server-dg83ke3k.js} +3 -5
  152. package/dist/server/{index.server-jk4rx4aa.js → index.server-dmmvwy6p.js} +1 -1
  153. package/dist/server/{index.server-hcfnae5y.js → index.server-dwwqssey.js} +4 -4
  154. package/dist/server/{index.server-e8x1hgw6.js → index.server-dz1r9xw5.js} +4 -4
  155. package/dist/server/{index.server-wm7mk5kw.js → index.server-ez5hp0ya.js} +1 -1
  156. package/dist/server/{index.server-ejyf7e48.js → index.server-fy068yne.js} +1 -1
  157. package/dist/server/{index.server-wc8dbm5e.js → index.server-g95bk8rc.js} +4 -2
  158. package/dist/server/index.server-gkazv439.js +576 -0
  159. package/dist/server/{index.server-dp9j2kkr.js → index.server-h0cvwvhy.js} +1 -1
  160. package/dist/server/{index.server-pacrqvyz.js → index.server-h90pjrez.js} +1 -1
  161. package/dist/server/{index.server-p8d04em5.js → index.server-hcsd5gdb.js} +6 -2
  162. package/dist/server/{index.server-n39drzes.js → index.server-hpeavvnq.js} +1 -1
  163. package/dist/server/{index.server-1p3patnj.js → index.server-hx91ng7z.js} +1 -1
  164. package/dist/server/{index.server-jtj3r53y.js → index.server-jgb8yh5k.js} +1 -1
  165. package/dist/server/{index.server-w1g5q9j9.js → index.server-jtgw9d23.js} +4 -2
  166. package/dist/server/{index.server-mehav5vs.js → index.server-jxg42jpz.js} +4 -4
  167. package/dist/server/{index.server-hd8gpehd.js → index.server-jxseyx33.js} +1 -1
  168. package/dist/server/{index.server-kg2sepsr.js → index.server-krqvnt6p.js} +14 -15
  169. package/dist/server/{index.server-0e1m3agz.js → index.server-ks49bprh.js} +1 -1
  170. package/dist/server/{index.server-f0hma3jp.js → index.server-kt0ncrqs.js} +1 -1
  171. package/dist/server/index.server-m0ezemav.js +170 -0
  172. package/dist/server/index.server-m191rm44.js +79 -0
  173. package/dist/server/{index.server-f23vtaw4.js → index.server-marjz8ps.js} +6 -6
  174. package/dist/server/index.server-mqt1ngqw.js +250 -0
  175. package/dist/server/{index.server-s9khh4ry.js → index.server-mzzb7dnm.js} +3 -5
  176. package/dist/server/{index.server-z721a5gp.js → index.server-n82rcv0m.js} +4 -4
  177. package/dist/server/{index.server-rhhmdxtz.js → index.server-np3c7kx0.js} +4 -4
  178. package/dist/server/{index.server-hpcs7tmh.js → index.server-p56xxef1.js} +1 -1
  179. package/dist/server/{index.server-w83kmcrx.js → index.server-pag862vz.js} +10 -10
  180. package/dist/server/{index.server-gxf0d1p5.js → index.server-q7w4jw89.js} +2 -2
  181. package/dist/server/{index.server-v4zabrsx.js → index.server-qh9qzd5t.js} +163 -25
  182. package/dist/server/{index.server-pxg74pyp.js → index.server-qk1h0n02.js} +4 -4
  183. package/dist/server/{index.server-sh2rxejg.js → index.server-qke3ekjt.js} +1 -1
  184. package/dist/server/{index.server-73x2d3j3.js → index.server-rbpvcrr3.js} +1 -1
  185. package/dist/server/{index.server-8cxs4kya.js → index.server-rga8sc7j.js} +41 -44
  186. package/dist/server/{index.server-e3fnv1g6.js → index.server-sedjxt36.js} +5 -5
  187. package/dist/server/{index.server-pen9pxt6.js → index.server-sf3nk3j2.js} +2 -2
  188. package/dist/server/{index.server-7fp0epev.js → index.server-veastatv.js} +4 -4
  189. package/dist/server/{index.server-jeqg2d22.js → index.server-xeqtpv61.js} +1 -1
  190. package/dist/server/{index.server-9s4na701.js → index.server-xjnge1yw.js} +4 -4
  191. package/dist/server/index.server-xtjjmsyq.js +315 -0
  192. package/dist/server/{index.server-hhxjaeen.js → index.server-xz99aed4.js} +4 -4
  193. package/dist/server/{index.server-vne4rzfa.js → index.server-y4n8vc08.js} +1 -1
  194. package/dist/server/{index.server-5d01cn4f.js → index.server-y83kqayt.js} +4 -4
  195. package/dist/server/{index.server-1nh09ck7.js → index.server-yhzydqxe.js} +5 -5
  196. package/dist/server/{index.server-atjc1qcq.js → index.server-yz4k36cw.js} +1 -1
  197. package/dist/server/{index.server-w6k62afr.js → index.server-ze1ymmbw.js} +2 -2
  198. package/dist/server/{index.server-914v805w.js → index.server-zgd1g8mk.js} +1 -1
  199. package/dist/server/{index.server-aeexcxdp.js → index.server-zh3r8vfd.js} +1 -1
  200. package/dist/server/{index.server-10jb2jzm.js → index.server-zsjf8enc.js} +4 -4
  201. package/package.json +40 -6
  202. package/src/components/_internal/command-list-context.ts +3 -3
  203. package/src/components/_internal/create-command-list-state.svelte.ts +39 -13
  204. package/src/components/approval-card/approval-card-actions.svelte +168 -0
  205. package/src/components/approval-card/approval-card-state.ts +152 -1
  206. package/src/components/approval-card/approval-card.css +269 -92
  207. package/src/components/approval-card/approval-card.examples.json +3 -3
  208. package/src/components/approval-card/approval-card.schema.json +9 -30
  209. package/src/components/approval-card/approval-card.schema.ts +13 -31
  210. package/src/components/approval-card/approval-card.svelte +176 -470
  211. package/src/components/approval-card/approval-card.types.ts +15 -28
  212. package/src/components/approval-card/index.ts +1 -0
  213. package/src/components/card/card.css +5 -0
  214. package/src/components/card/card.schema.json +1 -1
  215. package/src/components/card/card.schema.ts +1 -1
  216. package/src/components/card/card.svelte +1 -0
  217. package/src/components/card/card.types.ts +2 -2
  218. package/src/components/chat/chat.schema.json +36 -1
  219. package/src/components/chat/chat.schema.ts +44 -1
  220. package/src/components/chat/chat.svelte +12 -0
  221. package/src/components/chat/chat.types.ts +35 -3
  222. package/src/components/chat/container/chat.svelte +73 -3
  223. package/src/components/chat/index.ts +15 -3
  224. package/src/components/chat/input/chat-attachment-serialization.ts +63 -0
  225. package/src/components/chat/input/chat-input.svelte +22 -5
  226. package/src/components/chat/input/index.ts +5 -0
  227. package/src/components/chat-composer-popover/chat-composer-popover-filter.ts +68 -0
  228. package/src/components/chat-composer-popover/chat-composer-popover.css +7 -0
  229. package/src/components/chat-composer-popover/chat-composer-popover.examples.json +13 -0
  230. package/src/components/chat-composer-popover/chat-composer-popover.schema.json +90 -0
  231. package/src/components/chat-composer-popover/chat-composer-popover.schema.ts +95 -0
  232. package/src/components/chat-composer-popover/chat-composer-popover.svelte +234 -0
  233. package/src/components/chat-composer-popover/chat-composer-popover.types.ts +88 -0
  234. package/src/components/chat-composer-popover/chat-composer-popover.variables.json +1 -0
  235. package/src/components/chat-composer-popover/chat-composer-popover.variables.ts +3 -0
  236. package/src/components/chat-composer-popover/index.ts +19 -0
  237. package/src/components/code-block/code-block.schema.json +4 -0
  238. package/src/components/code-block/code-block.schema.ts +5 -0
  239. package/src/components/code-block/code-block.svelte +3 -3
  240. package/src/components/code-block/code-block.types.ts +6 -0
  241. package/src/components/command-item/command-item.svelte +7 -6
  242. package/src/components/command-menu/command-menu.schema.json +4 -0
  243. package/src/components/command-menu/command-menu.schema.ts +4 -0
  244. package/src/components/command-menu/command-menu.svelte +16 -5
  245. package/src/components/command-menu/command-menu.types.ts +2 -0
  246. package/src/components/json-viewer/_json-viewer-node.svelte +15 -1
  247. package/src/components/json-viewer/json-viewer.css +10 -6
  248. package/src/components/payload-inspector/index.ts +0 -2
  249. package/src/components/payload-inspector/payload-inspector.css +15 -75
  250. package/src/components/payload-inspector/payload-inspector.examples.json +5 -5
  251. package/src/components/payload-inspector/payload-inspector.schema.json +3 -31
  252. package/src/components/payload-inspector/payload-inspector.schema.ts +3 -36
  253. package/src/components/payload-inspector/payload-inspector.svelte +46 -239
  254. package/src/components/payload-inspector/payload-inspector.types.ts +10 -58
  255. package/src/components/tab/tab.schema.json +6 -2
  256. package/src/components/tab/tab.schema.ts +8 -2
  257. package/src/components/tab/tab.svelte +9 -5
  258. package/src/components/tab/tab.types.ts +8 -2
  259. package/src/index.ts +18 -5
  260. package/src/styles/components.css +1 -0
  261. package/dist/server/index-49we2xfs.js +0 -344
  262. package/dist/server/index-8fhvxk30.js +0 -265
  263. package/dist/server/index-x2ysg8ds.js +0 -388
  264. package/dist/server/index.server-20j0jh30.js +0 -9
  265. package/dist/server/index.server-dc35kh7d.js +0 -1835
  266. package/dist/server/index.server-kd4b0342.js +0 -607
  267. package/dist/server/index.server-n5cdmsz4.js +0 -30
  268. package/dist/server/index.server-rjap4k7y.js +0 -1389
  269. package/dist/server/index.server-vt6sn9y4.js +0 -447
  270. package/dist/server/index.server-w63n618v.js +0 -9
  271. package/dist/server/index.server-y2pa1k46.js +0 -9
@@ -2,6 +2,9 @@ import type { HTMLAttributes } from 'svelte/elements';
2
2
 
3
3
  export type ApprovalToolRisk = 'low' | 'medium' | 'high';
4
4
 
5
+ /** Heading level rendered for the card title. Section headings render one level deeper. */
6
+ export type ApprovalCardHeadingLevel = 2 | 3 | 4 | 5 | 6;
7
+
5
8
  /** @schemaObject */
6
9
  export type ApprovalTool = {
7
10
  /** Human-readable tool name requesting approval. */
@@ -28,7 +31,7 @@ export type ApprovalCommandOperation = {
28
31
  kind: 'command';
29
32
  /** Shell command for command approvals. */
30
33
  command: string;
31
- /** File paths that the operation may read or write. */
34
+ /** File paths that the operation may read or write. Duplicate paths are collapsed for display. */
32
35
  filesTouched?: string[];
33
36
  /**
34
37
  * JSON-like argument preview shown to the approver.
@@ -42,7 +45,7 @@ export type ApprovalCommandOperation = {
42
45
  export type ApprovalFileWriteOperation = {
43
46
  /** Operation family being approved. */
44
47
  kind: 'file-write';
45
- /** File paths that the operation may read or write. File-write approvals require at least one path. */
48
+ /** File paths that the operation may read or write. File-write approvals require at least one path. Duplicate paths are collapsed for display. */
46
49
  filesTouched: string[];
47
50
  /**
48
51
  * JSON-like argument preview shown to the approver.
@@ -56,7 +59,7 @@ export type ApprovalFileWriteOperation = {
56
59
  export type ApprovalPatchOperation = {
57
60
  /** Operation family being approved. */
58
61
  kind: 'patch';
59
- /** File paths that the operation may read or write. */
62
+ /** File paths that the operation may read or write. Duplicate paths are collapsed for display. */
60
63
  filesTouched?: string[];
61
64
  /**
62
65
  * JSON-like argument preview shown to the approver.
@@ -72,7 +75,7 @@ export type ApprovalPatchOperation = {
72
75
  export type ApprovalOtherOperation = {
73
76
  /** Operation family being approved. */
74
77
  kind: 'other';
75
- /** File paths that the operation may read or write. */
78
+ /** File paths that the operation may read or write. Duplicate paths are collapsed for display. */
76
79
  filesTouched?: string[];
77
80
  /**
78
81
  * JSON-like argument preview shown to the approver.
@@ -117,18 +120,8 @@ export type ApprovalResolution = {
117
120
  };
118
121
 
119
122
  export type ApprovalCardCallbacks = {
120
- /** Called for approve, approve-with-edits, deny, and cancel with the complete resolution payload. */
123
+ /** Called for approve, approve-with-edits, deny, and dismiss with the complete resolution payload. */
121
124
  onresolve?: (resolution: ApprovalResolution) => void;
122
- /** Called when the approver accepts the operation as presented. */
123
- onapprove?: () => void;
124
- /** Called with parsed JSON arguments when the approver accepts edited arguments. */
125
- onapprovewithedits?: (editedArgs: unknown) => void;
126
- /** Called when the approver denies the operation. */
127
- ondeny?: () => void;
128
- /** Called when the approver asks the host application to remember the decision. */
129
- onremember?: () => void;
130
- /** Called when the approver cancels the approval prompt. */
131
- oncancel?: () => void;
132
125
  };
133
126
 
134
127
  /** Props for the ApprovalCard component. */
@@ -154,6 +147,8 @@ export type ApprovalCardProps = Omit<HTMLAttributes<HTMLElement>, 'class' | 'chi
154
147
  state: ApprovalState;
155
148
  /** Whether approving with edited JSON arguments is available. Default `false`. */
156
149
  editableArgs?: boolean;
150
+ /** Heading level for the card title; section headings render one level deeper. Default `3`. */
151
+ headingLevel?: ApprovalCardHeadingLevel;
157
152
  /** Additional CSS classes applied to the root element. */
158
153
  class?: string;
159
154
  };
@@ -161,8 +156,8 @@ export type ApprovalCardProps = Omit<HTMLAttributes<HTMLElement>, 'class' | 'chi
161
156
  /**
162
157
  * Cinder-specific schema surface for ApprovalCard.
163
158
  *
164
- * Callback props are documented but marked unsupported because functions cannot
165
- * be represented as JSON Schema controls.
159
+ * The resolution callback is documented but marked unsupported because
160
+ * functions cannot be represented as JSON Schema controls.
166
161
  */
167
162
  export type ApprovalCardSchemaProps = {
168
163
  /** Tool requesting approval. */
@@ -185,18 +180,10 @@ export type ApprovalCardSchemaProps = {
185
180
  state: ApprovalState;
186
181
  /** Whether approving with edited JSON arguments is available. Default `false`. */
187
182
  editableArgs?: boolean;
188
- /** Called when the approver accepts the operation as presented. */
189
- onapprove?: () => void;
190
- /** Called for approve, approve-with-edits, deny, and cancel with the complete resolution payload. */
183
+ /** Heading level for the card title; section headings render one level deeper. Default `3`. */
184
+ headingLevel?: ApprovalCardHeadingLevel;
185
+ /** Called for approve, approve-with-edits, deny, and dismiss with the complete resolution payload. */
191
186
  onresolve?: (resolution: ApprovalResolution) => void;
192
- /** Called with parsed JSON arguments when the approver accepts edited arguments. */
193
- onapprovewithedits?: (editedArgs: unknown) => void;
194
- /** Called when the approver denies the operation. */
195
- ondeny?: () => void;
196
- /** Called when the approver asks the host application to remember the decision. */
197
- onremember?: () => void;
198
- /** Called when the approver cancels the approval prompt. */
199
- oncancel?: () => void;
200
187
  /** Additional CSS classes applied to the root element. */
201
188
  class?: string;
202
189
  };
@@ -4,6 +4,7 @@ import ApprovalCard from './approval-card.svelte';
4
4
  export default ApprovalCard;
5
5
  export type {
6
6
  ApprovalCardCallbacks,
7
+ ApprovalCardHeadingLevel,
7
8
  ApprovalCardProps,
8
9
  ApprovalCardSchemaProps,
9
10
  ApprovalOperation,
@@ -95,6 +95,11 @@
95
95
  padding: 0;
96
96
  }
97
97
 
98
+ .cinder-card[data-cinder-padding='none'] > .cinder-card__header,
99
+ .cinder-card[data-cinder-padding='none'] > .cinder-card__footer {
100
+ padding: 0;
101
+ }
102
+
98
103
  .cinder-card[data-cinder-variant='well'] > .cinder-card__body {
99
104
  background: var(--cinder-surface-inset);
100
105
  }
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "padding": {
30
30
  "enum": ["none", "default"],
31
- "description": "Body padding. `none` removes all padding for flush or full-bleed content (e.g. an image)."
31
+ "description": "Region padding. `none` removes body, header, and footer padding for flush or full-bleed content (e.g. an image)."
32
32
  },
33
33
  "headingLevel": {
34
34
  "enum": [2, 3, 4, 5, 6],
@@ -32,7 +32,7 @@ const schema = {
32
32
  padding: {
33
33
  enum: ['none', 'default'],
34
34
  description:
35
- 'Body padding. `none` removes all padding for flush or full-bleed content (e.g. an image).',
35
+ 'Region padding. `none` removes body, header, and footer padding for flush or full-bleed content (e.g. an image).',
36
36
  },
37
37
  headingLevel: {
38
38
  enum: [2, 3, 4, 5, 6],
@@ -37,6 +37,7 @@ const labelAttributes = $derived(hasGeneratedHeader
37
37
  data-cinder-variant={variant}
38
38
  data-cinder-tone={tone}
39
39
  data-cinder-edge-to-edge-mobile={edgeToEdgeOnMobile ? '' : undefined}
40
+ data-cinder-padding={padding}
40
41
  {...labelAttributes}
41
42
  >
42
43
  {#if header}
@@ -3,7 +3,7 @@ import type { HTMLAttributes } from 'svelte/elements';
3
3
  export type CardVariant = 'card' | 'well';
4
4
  export type CardTone = 'default' | 'muted';
5
5
  export type CardSurfaceTone = 'default' | 'danger';
6
- /** Controls body padding. `none` removes all padding for flush/full-bleed content. Future values like `compact` may be added. */
6
+ /** Controls region padding. `none` removes body, header, and footer padding for flush/full-bleed content. Future values like `compact` may be added. */
7
7
  export type CardPadding = 'default' | 'none';
8
8
  /** Heading level for the generated card title, so the document outline stays correct. */
9
9
  export type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;
@@ -19,7 +19,7 @@ type CardBase = HTMLAttributes<HTMLDivElement> & {
19
19
  footerTone?: CardTone;
20
20
  /** Remove side borders/radius and bleed to the viewport edge on narrow screens. */
21
21
  edgeToEdgeOnMobile?: boolean;
22
- /** Body padding. `none` removes all padding for flush or full-bleed content (e.g. an image). */
22
+ /** Region padding. `none` removes body, header, and footer padding for flush or full-bleed content (e.g. an image). */
23
23
  padding?: CardPadding;
24
24
  };
25
25
  /** Basic card with no generated header. */
@@ -50,6 +50,26 @@
50
50
  "type": "string",
51
51
  "description": "Optional status label displayed in the typing indicator while `streaming` is `true` and no streaming content has arrived yet (e.g. `\"Thinking…\"` or `\"Analyzing file…\"`). When omitted, three animated dots are shown."
52
52
  },
53
+ "composerRole": {
54
+ "type": "string",
55
+ "description": "Explicit role for the composer textarea, for overlay patterns such as ARIA comboboxes."
56
+ },
57
+ "composerAriaExpanded": {
58
+ "enum": [false, true, "true", "false"],
59
+ "description": "`aria-expanded` passed to the composer textarea for overlays such as slash-command menus."
60
+ },
61
+ "composerAriaControls": {
62
+ "type": "string",
63
+ "description": "`aria-controls` passed to the composer textarea for overlays such as slash-command menus."
64
+ },
65
+ "composerAriaActiveDescendant": {
66
+ "type": "string",
67
+ "description": "`aria-activedescendant` passed to the composer textarea for overlays such as slash-command menus."
68
+ },
69
+ "composerAriaAutocomplete": {
70
+ "enum": ["none", "inline", "list", "both"],
71
+ "description": "`aria-autocomplete` passed to the composer textarea for overlays such as slash-command menus."
72
+ },
53
73
  "virtualized": {
54
74
  "type": "boolean",
55
75
  "description": "Use the virtualized message render path for long transcripts. The complete `ConversationHistory` remains unchanged; only the DOM window is reduced. Default `false`."
@@ -164,10 +184,25 @@
164
184
  "name": "onattachmentremove",
165
185
  "reason": "function-or-snippet"
166
186
  },
187
+ {
188
+ "name": "oncomposerblur",
189
+ "reason": "function-or-snippet",
190
+ "description": "Called when focus leaves the composer textarea. Overlay primitives can use\nthis to dismiss without preventing native focus movement."
191
+ },
167
192
  {
168
193
  "name": "oncomposerinput",
169
194
  "reason": "function-or-snippet",
170
- "description": "Called with the composer's current plain-text value on every composer\ninput event. Lets a consumer build slash-command, mention, or autocomplete\nUX without reaching into `.chat-input-editor` DOM directly."
195
+ "description": "Called with the composer's current plain-text value on every composer\ninput event. The optional event exposes the textarea for composer-bound\noverlays without reaching into `.chat-input-editor` DOM directly."
196
+ },
197
+ {
198
+ "name": "oncomposerkeydown",
199
+ "reason": "function-or-snippet",
200
+ "description": "Called before Chat's internal composer key handling when a keydown\noriginates from the composer textarea. Call `event.preventDefault()` to\nlet an overlay consume Arrow keys, Enter, or Escape before Enter-to-send.\nChat does not call this hook during IME composition, so Enter can still\nconfirm the active candidate instead of sending."
201
+ },
202
+ {
203
+ "name": "oncomposerselectionchange",
204
+ "reason": "function-or-snippet",
205
+ "description": "Called after pointer or selection activity may have moved the composer\ncaret without changing text. Overlay primitives can resync their active\ntoken from the textarea selection."
171
206
  },
172
207
  {
173
208
  "name": "ondeny",
@@ -63,6 +63,31 @@ const schema = {
63
63
  description:
64
64
  'Optional status label displayed in the typing indicator while `streaming` is `true` and no streaming content has arrived yet (e.g. `"Thinking…"` or `"Analyzing file…"`). When omitted, three animated dots are shown.',
65
65
  },
66
+ composerRole: {
67
+ type: 'string',
68
+ description:
69
+ 'Explicit role for the composer textarea, for overlay patterns such as ARIA comboboxes.',
70
+ },
71
+ composerAriaExpanded: {
72
+ enum: [false, true, 'true', 'false'],
73
+ description:
74
+ '`aria-expanded` passed to the composer textarea for overlays such as slash-command menus.',
75
+ },
76
+ composerAriaControls: {
77
+ type: 'string',
78
+ description:
79
+ '`aria-controls` passed to the composer textarea for overlays such as slash-command menus.',
80
+ },
81
+ composerAriaActiveDescendant: {
82
+ type: 'string',
83
+ description:
84
+ '`aria-activedescendant` passed to the composer textarea for overlays such as slash-command menus.',
85
+ },
86
+ composerAriaAutocomplete: {
87
+ enum: ['none', 'inline', 'list', 'both'],
88
+ description:
89
+ '`aria-autocomplete` passed to the composer textarea for overlays such as slash-command menus.',
90
+ },
66
91
  virtualized: {
67
92
  type: 'boolean',
68
93
  description:
@@ -191,11 +216,29 @@ const schema = {
191
216
  name: 'onattachmentremove',
192
217
  reason: 'function-or-snippet',
193
218
  },
219
+ {
220
+ name: 'oncomposerblur',
221
+ reason: 'function-or-snippet',
222
+ description:
223
+ 'Called when focus leaves the composer textarea. Overlay primitives can use\nthis to dismiss without preventing native focus movement.',
224
+ },
194
225
  {
195
226
  name: 'oncomposerinput',
196
227
  reason: 'function-or-snippet',
197
228
  description:
198
- "Called with the composer's current plain-text value on every composer\ninput event. Lets a consumer build slash-command, mention, or autocomplete\nUX without reaching into `.chat-input-editor` DOM directly.",
229
+ "Called with the composer's current plain-text value on every composer\ninput event. The optional event exposes the textarea for composer-bound\noverlays without reaching into `.chat-input-editor` DOM directly.",
230
+ },
231
+ {
232
+ name: 'oncomposerkeydown',
233
+ reason: 'function-or-snippet',
234
+ description:
235
+ "Called before Chat's internal composer key handling when a keydown\noriginates from the composer textarea. Call `event.preventDefault()` to\nlet an overlay consume Arrow keys, Enter, or Escape before Enter-to-send.\nChat does not call this hook during IME composition, so Enter can still\nconfirm the active candidate instead of sending.",
236
+ },
237
+ {
238
+ name: 'oncomposerselectionchange',
239
+ reason: 'function-or-snippet',
240
+ description:
241
+ 'Called after pointer or selection activity may have moved the composer\ncaret without changing text. Overlay primitives can resync their active\ntoken from the textarea selection.',
199
242
  },
200
243
  {
201
244
  name: 'ondeny',
@@ -30,6 +30,14 @@ export function pushToken(token) {
30
30
  export function endStreaming() {
31
31
  impl?.endStreaming();
32
32
  }
33
+ /**
34
+ * Announce consumer-rendered status text through Chat's existing live regions.
35
+ * No-op until mounted. Built-in tool-approval announcements take precedence
36
+ * over consumer assertive announcements to avoid duplicate urgent output.
37
+ */
38
+ export function announce(message, level = 'polite') {
39
+ impl?.announce(message, level);
40
+ }
33
41
  /** Scroll the message viewport to the bottom. No-op until mounted. */
34
42
  export function scrollToBottom() {
35
43
  impl?.scrollToBottom();
@@ -50,6 +58,10 @@ export function clearInput() {
50
58
  export function getComposerValue() {
51
59
  return impl?.getComposerValue() ?? '';
52
60
  }
61
+ /** Read the composer textarea element. Returns null until mounted. */
62
+ export function getEditorElement() {
63
+ return impl?.getEditorElement() ?? null;
64
+ }
53
65
  </script>
54
66
 
55
67
  <ChatImplementation bind:this={impl} class={mergedClassName} {...rest} />
@@ -49,6 +49,9 @@ export type ReadReceipt = {
49
49
  readBy?: string[];
50
50
  };
51
51
 
52
+ /** Live-region channel for imperative Chat announcements. */
53
+ export type ChatAnnounceLevel = 'polite' | 'assertive';
54
+
52
55
  /**
53
56
  * Full-row override snippet. Inversion of control: receives the message AND a
54
57
  * `renderDefault` snippet that renders the built-in row, so a consumer can wrap
@@ -127,6 +130,16 @@ export type ChatProps = Omit<HTMLAttributes<HTMLElement>, 'class' | 'onsubmit'>
127
130
  * rather than as five separate boolean props.
128
131
  */
129
132
  capabilities?: ChatCapabilities;
133
+ /** Explicit role for the composer textarea, for overlay patterns such as ARIA comboboxes. */
134
+ composerRole?: string | undefined;
135
+ /** `aria-expanded` passed to the composer textarea for overlays such as slash-command menus. */
136
+ composerAriaExpanded?: boolean | 'true' | 'false' | undefined;
137
+ /** `aria-controls` passed to the composer textarea for overlays such as slash-command menus. */
138
+ composerAriaControls?: string | undefined;
139
+ /** `aria-activedescendant` passed to the composer textarea for overlays such as slash-command menus. */
140
+ composerAriaActiveDescendant?: string | undefined;
141
+ /** `aria-autocomplete` passed to the composer textarea for overlays such as slash-command menus. */
142
+ composerAriaAutocomplete?: 'none' | 'inline' | 'list' | 'both' | undefined;
130
143
  /** Use the virtualized message render path for long transcripts. The complete `ConversationHistory` remains unchanged; only the DOM window is reduced. Default `false`. */
131
144
  virtualized?: boolean;
132
145
  /** Estimated row height in pixels for virtualized message rows. Default `88`. */
@@ -245,8 +258,27 @@ export type ChatProps = Omit<HTMLAttributes<HTMLElement>, 'class' | 'onsubmit'>
245
258
  onattachmentfailure?: (file: File, error: string) => void;
246
259
  /**
247
260
  * Called with the composer's current plain-text value on every composer
248
- * input event. Lets a consumer build slash-command, mention, or autocomplete
249
- * UX without reaching into `.chat-input-editor` DOM directly.
261
+ * input event. The optional event exposes the textarea for composer-bound
262
+ * overlays without reaching into `.chat-input-editor` DOM directly.
263
+ */
264
+ oncomposerinput?: (value: string, event?: Event) => void;
265
+ /**
266
+ * Called before Chat's internal composer key handling when a keydown
267
+ * originates from the composer textarea. Call `event.preventDefault()` to
268
+ * let an overlay consume Arrow keys, Enter, or Escape before Enter-to-send.
269
+ * Chat does not call this hook during IME composition, so Enter can still
270
+ * confirm the active candidate instead of sending.
271
+ */
272
+ oncomposerkeydown?: (event: KeyboardEvent) => void;
273
+ /**
274
+ * Called after pointer or selection activity may have moved the composer
275
+ * caret without changing text. Overlay primitives can resync their active
276
+ * token from the textarea selection.
277
+ */
278
+ oncomposerselectionchange?: (event: Event) => void;
279
+ /**
280
+ * Called when focus leaves the composer textarea. Overlay primitives can use
281
+ * this to dismiss without preventing native focus movement.
250
282
  */
251
- oncomposerinput?: (value: string) => void;
283
+ oncomposerblur?: (event: FocusEvent) => void;
252
284
  };
@@ -26,7 +26,8 @@ import { useChatReadReceipts } from './use-chat-read-receipts.svelte.ts';
26
26
  import ChatParticipantTyping from './chat-participant-typing.svelte';
27
27
  import ChatReadReceipt from '../message/chat-read-receipt.svelte';
28
28
  const noopAttachment = () => { };
29
- let { id, conversation, atBottom = $bindable(true), unreadCount = $bindable(0), newMessageIndicatorVisible = $bindable(false), class: className, surfaceMode = 'default', density = 'comfortable', variant = 'bubble', bottomThreshold = DEFAULT_SCROLL_CONFIGURATION.bottomThreshold, jumpThreshold = DEFAULT_SCROLL_CONFIGURATION.jumpThreshold, streaming = false, streamingStatus, capabilities, virtualized = false, virtualizationEstimatedRowHeight = 88, virtualizationOverscan = 3, virtualizationInitialHeight = 640, moreHistoryAvailable = true, loadEarlierLabel = 'Load earlier messages', loadingEarlierLabel = 'Loading earlier messages', header, empty, emptyPrompts, messageActions, messageStatus, row, messagePart, viewportAttachment, typingParticipants, readReceipts, adapter, onadaptererror, onpushmessage, ontypingchange, onreadreceipt, onsubmit, onretry, onedit, onapprove, ondeny, messageReasoning, messageSteps, messageSuggestions, onsuggestionselect, onloadhistory, onstopgenerating, onjumptolatest, onscrollstatechange, onunreadindicatorchange, onexpandedchange, onattachmentadd, onattachmentremove, onattachmentfailure, oncomposerinput, ...rest } = $props();
29
+ const CONSUMER_ANNOUNCEMENT_CLEAR_DELAY_MS = 1000;
30
+ let { id, conversation, atBottom = $bindable(true), unreadCount = $bindable(0), newMessageIndicatorVisible = $bindable(false), class: className, surfaceMode = 'default', density = 'comfortable', variant = 'bubble', bottomThreshold = DEFAULT_SCROLL_CONFIGURATION.bottomThreshold, jumpThreshold = DEFAULT_SCROLL_CONFIGURATION.jumpThreshold, streaming = false, streamingStatus, capabilities, virtualized = false, virtualizationEstimatedRowHeight = 88, virtualizationOverscan = 3, virtualizationInitialHeight = 640, moreHistoryAvailable = true, loadEarlierLabel = 'Load earlier messages', loadingEarlierLabel = 'Loading earlier messages', header, empty, emptyPrompts, messageActions, messageStatus, row, messagePart, viewportAttachment, typingParticipants, readReceipts, adapter, onadaptererror, onpushmessage, ontypingchange, onreadreceipt, onsubmit, onretry, onedit, onapprove, ondeny, messageReasoning, messageSteps, messageSuggestions, onsuggestionselect, onloadhistory, onstopgenerating, onjumptolatest, onscrollstatechange, onunreadindicatorchange, onexpandedchange, onattachmentadd, onattachmentremove, onattachmentfailure, oncomposerinput, oncomposerkeydown, oncomposerselectionchange, oncomposerblur, composerRole, composerAriaExpanded, composerAriaControls, composerAriaActiveDescendant, composerAriaAutocomplete, ...rest } = $props();
30
31
  // ==========================================================================
31
32
  // Refs and Internal State
32
33
  // ==========================================================================
@@ -84,10 +85,15 @@ $effect(() => {
84
85
  toolCallState.reset();
85
86
  typingIndicatorState.reset();
86
87
  readReceiptsState.reset();
88
+ clearConsumerAnnouncements();
87
89
  });
88
90
  let isLoadingHistory = $state(false);
89
91
  let adapterHasMoreHistory = $state(undefined);
90
92
  let historyAnnouncement = $state('');
93
+ let consumerPoliteAnnouncement = $state('');
94
+ let consumerAssertiveAnnouncement = $state('');
95
+ let consumerPoliteAnnouncementTimeout;
96
+ let consumerAssertiveAnnouncementTimeout;
91
97
  let pendingHistoryScroll = $state(null);
92
98
  let deferredAdapterHasMoreHistory = null;
93
99
  let historyAnchorMessageId = $state(null);
@@ -325,6 +331,13 @@ const toolApprovalAssertiveMessage = $derived.by(() => {
325
331
  }
326
332
  return '';
327
333
  });
334
+ const assertiveAnnouncement = $derived(toolApprovalAssertiveMessage || consumerAssertiveAnnouncement);
335
+ const politeAnnouncement = $derived(consumerPoliteAnnouncement || historyAnnouncement || unreadState.announcerMessage);
336
+ $effect(() => {
337
+ if (toolApprovalAssertiveMessage && consumerAssertiveAnnouncement) {
338
+ clearConsumerAssertiveAnnouncement();
339
+ }
340
+ });
328
341
  // ==========================================================================
329
342
  // Scroll Anchoring via $effect.pre
330
343
  // ==========================================================================
@@ -516,6 +529,7 @@ $effect(() => {
516
529
  cancelAnimationFrame(streamingScrollRaf);
517
530
  streamingScrollRaf = undefined;
518
531
  }
532
+ clearConsumerAnnouncements();
519
533
  };
520
534
  });
521
535
  // ==========================================================================
@@ -588,6 +602,50 @@ const historyAnchorScrollAttachment = $derived(historyAnchorMessageId === null |
588
602
  // ==========================================================================
589
603
  // Actions
590
604
  // ==========================================================================
605
+ export function announce(message, level = 'polite') {
606
+ const trimmedMessage = message.trim();
607
+ if (!trimmedMessage)
608
+ return;
609
+ if (level === 'assertive') {
610
+ if (toolApprovalAssertiveMessage)
611
+ return;
612
+ setConsumerAssertiveAnnouncement(trimmedMessage);
613
+ return;
614
+ }
615
+ setConsumerPoliteAnnouncement(trimmedMessage);
616
+ }
617
+ function setConsumerPoliteAnnouncement(message) {
618
+ clearTimeout(consumerPoliteAnnouncementTimeout);
619
+ consumerPoliteAnnouncement = message;
620
+ consumerPoliteAnnouncementTimeout = setTimeout(() => {
621
+ if (consumerPoliteAnnouncement === message)
622
+ consumerPoliteAnnouncement = '';
623
+ consumerPoliteAnnouncementTimeout = undefined;
624
+ }, CONSUMER_ANNOUNCEMENT_CLEAR_DELAY_MS);
625
+ }
626
+ function setConsumerAssertiveAnnouncement(message) {
627
+ clearTimeout(consumerAssertiveAnnouncementTimeout);
628
+ consumerAssertiveAnnouncement = message;
629
+ consumerAssertiveAnnouncementTimeout = setTimeout(() => {
630
+ if (consumerAssertiveAnnouncement === message)
631
+ consumerAssertiveAnnouncement = '';
632
+ consumerAssertiveAnnouncementTimeout = undefined;
633
+ }, CONSUMER_ANNOUNCEMENT_CLEAR_DELAY_MS);
634
+ }
635
+ function clearConsumerPoliteAnnouncement() {
636
+ clearTimeout(consumerPoliteAnnouncementTimeout);
637
+ consumerPoliteAnnouncementTimeout = undefined;
638
+ consumerPoliteAnnouncement = '';
639
+ }
640
+ function clearConsumerAssertiveAnnouncement() {
641
+ clearTimeout(consumerAssertiveAnnouncementTimeout);
642
+ consumerAssertiveAnnouncementTimeout = undefined;
643
+ consumerAssertiveAnnouncement = '';
644
+ }
645
+ function clearConsumerAnnouncements() {
646
+ clearConsumerPoliteAnnouncement();
647
+ clearConsumerAssertiveAnnouncement();
648
+ }
591
649
  function handleJumpToLatest() {
592
650
  if (isVirtualized) {
593
651
  chatVirtualizer.scrollToIndex(Math.max(0, renderRows.length - 1), {
@@ -1069,6 +1127,10 @@ export function clearInput() {
1069
1127
  export function getComposerValue() {
1070
1128
  return inputRef?.getValue() ?? '';
1071
1129
  }
1130
+ /** Read the composer textarea element. Returns null until mounted. */
1131
+ export function getEditorElement() {
1132
+ return inputRef?.getEditorElement() ?? null;
1133
+ }
1072
1134
  /**
1073
1135
  * Begin streaming content for a specific message.
1074
1136
  * The message should already exist in the conversation.
@@ -1414,6 +1476,14 @@ function virtualRowAttachment(row) {
1414
1476
  {allowAttachments}
1415
1477
  onstop={streaming ? handleStopGenerating : undefined}
1416
1478
  {oncomposerinput}
1479
+ {oncomposerkeydown}
1480
+ {oncomposerselectionchange}
1481
+ {oncomposerblur}
1482
+ {composerRole}
1483
+ {composerAriaExpanded}
1484
+ {composerAriaControls}
1485
+ {composerAriaActiveDescendant}
1486
+ {composerAriaAutocomplete}
1417
1487
  {onattachmentadd}
1418
1488
  {onattachmentremove}
1419
1489
  {onattachmentfailure}
@@ -1424,8 +1494,8 @@ function virtualRowAttachment(row) {
1424
1494
  <ChatStatusAnnouncer
1425
1495
  {statusId}
1426
1496
  messageCount={messages.length}
1427
- announcerMessage={historyAnnouncement || unreadState.announcerMessage}
1428
- assertiveMessage={toolApprovalAssertiveMessage}
1497
+ announcerMessage={politeAnnouncement}
1498
+ assertiveMessage={assertiveAnnouncement}
1429
1499
  />
1430
1500
 
1431
1501
  <!-- Typing-participant live region: outside role="log" to avoid double announcement.
@@ -13,7 +13,13 @@ import './chat.css';
13
13
  import Chat from './chat.svelte';
14
14
 
15
15
  export default Chat;
16
- export type { ChatCapabilities, ChatProps, ReadReceipt, TypingParticipant } from './chat.types.ts';
16
+ export type {
17
+ ChatAnnounceLevel,
18
+ ChatCapabilities,
19
+ ChatProps,
20
+ ReadReceipt,
21
+ TypingParticipant,
22
+ } from './chat.types.ts';
17
23
  export { Chat };
18
24
 
19
25
  // Adapter seam — the optional event/transport boundary around <Chat>. Type-only.
@@ -80,8 +86,14 @@ export {
80
86
  } from './utilities';
81
87
 
82
88
  // Input
83
- export { ChatAttachmentPreview, ChatInput, deriveAttachmentKind } from './input';
84
- export type { AttachmentKind, ChatAttachment } from './input';
89
+ export {
90
+ ChatAttachmentPreview,
91
+ ChatInput,
92
+ deriveAttachmentKind,
93
+ serializeChatAttachment,
94
+ serializeChatAttachments,
95
+ } from './input';
96
+ export type { AttachmentKind, ChatAttachment, SerializedChatAttachment } from './input';
85
97
 
86
98
  // Message
87
99
  export {
@@ -0,0 +1,63 @@
1
+ import type { ChatAttachment } from './chat-attachment.ts';
2
+
3
+ const BASE64_CHUNK_SIZE = 0x8000;
4
+
5
+ type Base64Runtime = typeof globalThis & {
6
+ btoa?: (data: string) => string;
7
+ Buffer?: {
8
+ from: (data: Uint8Array) => { toString: (encoding: 'base64') => string };
9
+ };
10
+ };
11
+
12
+ export interface SerializedChatAttachment {
13
+ name: string;
14
+ mimeType: string;
15
+ kind: ChatAttachment['kind'];
16
+ content: string;
17
+ }
18
+
19
+ function bytesToBase64(bytes: Uint8Array): string {
20
+ const runtime = globalThis as Base64Runtime;
21
+
22
+ if (typeof runtime.btoa === 'function') {
23
+ let binary = '';
24
+
25
+ for (let start = 0; start < bytes.length; start += BASE64_CHUNK_SIZE) {
26
+ const chunk = bytes.subarray(start, start + BASE64_CHUNK_SIZE);
27
+ binary += String.fromCharCode(...chunk);
28
+ }
29
+
30
+ return runtime.btoa(binary);
31
+ }
32
+
33
+ if (typeof runtime.Buffer?.from === 'function') {
34
+ return runtime.Buffer.from(bytes).toString('base64');
35
+ }
36
+
37
+ throw new Error('serializeChatAttachment requires btoa or Buffer for base64 encoding.');
38
+ }
39
+
40
+ export async function serializeChatAttachment(
41
+ attachment: ChatAttachment,
42
+ ): Promise<SerializedChatAttachment> {
43
+ const bytes = new Uint8Array(await attachment.file.arrayBuffer());
44
+
45
+ return {
46
+ name: attachment.file.name,
47
+ mimeType: attachment.file.type,
48
+ kind: attachment.kind,
49
+ content: bytesToBase64(bytes),
50
+ };
51
+ }
52
+
53
+ export async function serializeChatAttachments(
54
+ attachments: readonly ChatAttachment[],
55
+ ): Promise<SerializedChatAttachment[]> {
56
+ const serializedAttachments: SerializedChatAttachment[] = [];
57
+
58
+ for (const attachment of attachments) {
59
+ serializedAttachments.push(await serializeChatAttachment(attachment));
60
+ }
61
+
62
+ return serializedAttachments;
63
+ }