@elabs-ai/components-ai 4.2.0 → 5.0.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 (462) hide show
  1. package/README.md +1 -1
  2. package/dist/{_audio-player-media-chrome-T3XVXWRZ.js → _audio-player-media-chrome.js} +1 -1
  3. package/dist/_code-block-theme.js +12 -0
  4. package/dist/_code-block-theme.js.map +1 -0
  5. package/dist/{_flow-boundary-SHNWLQG5.js → _flow-boundary.js} +1 -1
  6. package/dist/_flow-boundary.js.map +1 -0
  7. package/dist/_flow-lazy.js +8 -0
  8. package/dist/_flow-lazy.js.map +1 -0
  9. package/dist/_lazy-boundary-conformance.js +2 -0
  10. package/dist/_lazy-boundary-conformance.js.map +1 -0
  11. package/dist/_lazy-cjk.js +13 -0
  12. package/dist/_lazy-cjk.js.map +1 -0
  13. package/dist/_lazy-engine-boundary.js +9 -0
  14. package/dist/_lazy-engine-boundary.js.map +1 -0
  15. package/dist/_lazy-math.js +13 -0
  16. package/dist/_lazy-math.js.map +1 -0
  17. package/dist/_lazy-mermaid.js +12 -0
  18. package/dist/_lazy-mermaid.js.map +1 -0
  19. package/dist/_mermaid-error-panel.js +9 -0
  20. package/dist/_mermaid-error-panel.js.map +1 -0
  21. package/dist/{_persona-rive-JEG44YHX.js → _persona-rive.js} +19 -24
  22. package/dist/_persona-rive.js.map +1 -0
  23. package/dist/_streamdown-i18n.js +21 -0
  24. package/dist/_streamdown-i18n.js.map +1 -0
  25. package/dist/_streamdown-safety.js +10 -0
  26. package/dist/_streamdown-safety.js.map +1 -0
  27. package/dist/_theme-scope-store.js +13 -0
  28. package/dist/_theme-scope-store.js.map +1 -0
  29. package/dist/a2ui/a2ui-surface.js +17 -0
  30. package/dist/a2ui/a2ui-surface.js.map +1 -0
  31. package/dist/a2ui/core/catalog.generated.js +10 -0
  32. package/dist/a2ui/core/catalog.generated.js.map +1 -0
  33. package/dist/a2ui/core/complete-json.js +10 -0
  34. package/dist/a2ui/core/complete-json.js.map +1 -0
  35. package/dist/a2ui/core/index.js +37 -0
  36. package/dist/a2ui/core/index.js.map +1 -0
  37. package/dist/a2ui/core/schema.js +11 -0
  38. package/dist/a2ui/core/schema.js.map +1 -0
  39. package/dist/a2ui/core/spec.js +10 -0
  40. package/dist/a2ui/core/spec.js.map +1 -0
  41. package/dist/a2ui/core/validate.js +16 -0
  42. package/dist/a2ui/core/validate.js.map +1 -0
  43. package/dist/a2ui/index.js +54 -0
  44. package/dist/a2ui/index.js.map +1 -0
  45. package/dist/a2ui/ui-catalog.js +18 -0
  46. package/dist/a2ui/ui-catalog.js.map +1 -0
  47. package/dist/agent-event.js +10 -0
  48. package/dist/agent-event.js.map +1 -0
  49. package/dist/agent-timeline.js +11 -0
  50. package/dist/agent-timeline.js.map +1 -0
  51. package/dist/agent.js +25 -0
  52. package/dist/agent.js.map +1 -0
  53. package/dist/artifact.js +23 -0
  54. package/dist/artifact.js.map +1 -0
  55. package/dist/asset-preview.js +22 -0
  56. package/dist/asset-preview.js.map +1 -0
  57. package/dist/attachments.js +33 -0
  58. package/dist/attachments.js.map +1 -0
  59. package/dist/audio-player.js +30 -0
  60. package/dist/audio-player.js.map +1 -0
  61. package/dist/audio-visualizer.js +9 -0
  62. package/dist/audio-visualizer.js.map +1 -0
  63. package/dist/canvas.js +10 -0
  64. package/dist/canvas.js.map +1 -0
  65. package/dist/chain-of-thought.js +22 -0
  66. package/dist/chain-of-thought.js.map +1 -0
  67. package/dist/chat-greeting.js +8 -0
  68. package/dist/chat-greeting.js.map +1 -0
  69. package/dist/chat-shell.js +9 -0
  70. package/dist/chat-shell.js.map +1 -0
  71. package/dist/checkpoint.js +13 -0
  72. package/dist/checkpoint.js.map +1 -0
  73. package/dist/chunk-2XOIG5TG.js +72 -0
  74. package/dist/chunk-2XOIG5TG.js.map +1 -0
  75. package/dist/chunk-34CQ2VHM.js +172 -0
  76. package/dist/chunk-34CQ2VHM.js.map +1 -0
  77. package/dist/chunk-3TDHPXZZ.js +60 -0
  78. package/dist/chunk-3TDHPXZZ.js.map +1 -0
  79. package/dist/chunk-3XNPMDZY.js +478 -0
  80. package/dist/chunk-3XNPMDZY.js.map +1 -0
  81. package/dist/chunk-3YPT3V7L.js +56 -0
  82. package/dist/chunk-3YPT3V7L.js.map +1 -0
  83. package/dist/chunk-475UEU2Q.js +87 -0
  84. package/dist/chunk-475UEU2Q.js.map +1 -0
  85. package/dist/chunk-4CBTII2I.js +95 -0
  86. package/dist/chunk-4CBTII2I.js.map +1 -0
  87. package/dist/chunk-4XRNIZMG.js +22 -0
  88. package/dist/chunk-4XRNIZMG.js.map +1 -0
  89. package/dist/chunk-5NNWUKK5.js +2 -0
  90. package/dist/chunk-5NNWUKK5.js.map +1 -0
  91. package/dist/chunk-5UOMU27Z.js +164 -0
  92. package/dist/chunk-5UOMU27Z.js.map +1 -0
  93. package/dist/chunk-5VGYZ62I.js +264 -0
  94. package/dist/chunk-5VGYZ62I.js.map +1 -0
  95. package/dist/chunk-6IQPH2OS.js +21 -0
  96. package/dist/chunk-6IQPH2OS.js.map +1 -0
  97. package/dist/chunk-6PB2MXCI.js +482 -0
  98. package/dist/chunk-6PB2MXCI.js.map +1 -0
  99. package/dist/chunk-7FD5CZEU.js +215 -0
  100. package/dist/chunk-7FD5CZEU.js.map +1 -0
  101. package/dist/chunk-7RL6KZDF.js +266 -0
  102. package/dist/chunk-7RL6KZDF.js.map +1 -0
  103. package/dist/chunk-ADOYAGGK.js +482 -0
  104. package/dist/chunk-ADOYAGGK.js.map +1 -0
  105. package/dist/chunk-AXMNJMMH.js +984 -0
  106. package/dist/chunk-AXMNJMMH.js.map +1 -0
  107. package/dist/chunk-BA3AEMOU.js +21 -0
  108. package/dist/chunk-BA3AEMOU.js.map +1 -0
  109. package/dist/chunk-BBHFOYML.js +336 -0
  110. package/dist/chunk-BBHFOYML.js.map +1 -0
  111. package/dist/chunk-BHUVISIY.js +221 -0
  112. package/dist/chunk-BHUVISIY.js.map +1 -0
  113. package/dist/chunk-BM2LWERA.js +100 -0
  114. package/dist/chunk-BM2LWERA.js.map +1 -0
  115. package/dist/chunk-CMOYXNPJ.js +211 -0
  116. package/dist/chunk-CMOYXNPJ.js.map +1 -0
  117. package/dist/chunk-CZP3RHC4.js +74 -0
  118. package/dist/chunk-CZP3RHC4.js.map +1 -0
  119. package/dist/chunk-DDG5JHQT.js +112 -0
  120. package/dist/chunk-DDG5JHQT.js.map +1 -0
  121. package/dist/chunk-DJODXO7I.js +38 -0
  122. package/dist/chunk-DJODXO7I.js.map +1 -0
  123. package/dist/chunk-DWJDJLPU.js +192 -0
  124. package/dist/chunk-DWJDJLPU.js.map +1 -0
  125. package/dist/chunk-DZW6LM22.js +45 -0
  126. package/dist/chunk-DZW6LM22.js.map +1 -0
  127. package/dist/chunk-ELGCZLZF.js +305 -0
  128. package/dist/chunk-ELGCZLZF.js.map +1 -0
  129. package/dist/chunk-EV3U6ZZB.js +209 -0
  130. package/dist/chunk-EV3U6ZZB.js.map +1 -0
  131. package/dist/chunk-EY7LVUKQ.js +87 -0
  132. package/dist/chunk-EY7LVUKQ.js.map +1 -0
  133. package/dist/chunk-F4C74F7M.js +230 -0
  134. package/dist/chunk-F4C74F7M.js.map +1 -0
  135. package/dist/chunk-FERYDFZA.js +108 -0
  136. package/dist/chunk-FERYDFZA.js.map +1 -0
  137. package/dist/chunk-FUIYG2DS.js +166 -0
  138. package/dist/chunk-FUIYG2DS.js.map +1 -0
  139. package/dist/chunk-GKYZT3LA.js +136 -0
  140. package/dist/chunk-GKYZT3LA.js.map +1 -0
  141. package/dist/chunk-H42LRUQ4.js +102 -0
  142. package/dist/chunk-H42LRUQ4.js.map +1 -0
  143. package/dist/chunk-HVXM2G5F.js +222 -0
  144. package/dist/chunk-HVXM2G5F.js.map +1 -0
  145. package/dist/chunk-I4RDAGVY.js +53 -0
  146. package/dist/chunk-I4RDAGVY.js.map +1 -0
  147. package/dist/chunk-IQJG4IDX.js +208 -0
  148. package/dist/chunk-IQJG4IDX.js.map +1 -0
  149. package/dist/chunk-IX2PXG4Y.js +144 -0
  150. package/dist/chunk-IX2PXG4Y.js.map +1 -0
  151. package/dist/chunk-IXICSOOP.js +287 -0
  152. package/dist/chunk-IXICSOOP.js.map +1 -0
  153. package/dist/chunk-JY5HSN4D.js +84 -0
  154. package/dist/chunk-JY5HSN4D.js.map +1 -0
  155. package/dist/chunk-LMNSBOBG.js +19 -0
  156. package/dist/chunk-LMNSBOBG.js.map +1 -0
  157. package/dist/chunk-MBI7D6IL.js +42 -0
  158. package/dist/chunk-MBI7D6IL.js.map +1 -0
  159. package/dist/chunk-MLFMYCPH.js +192 -0
  160. package/dist/chunk-MLFMYCPH.js.map +1 -0
  161. package/dist/chunk-MOK66734.js +33 -0
  162. package/dist/chunk-MOK66734.js.map +1 -0
  163. package/dist/chunk-MXTAWPYM.js +196 -0
  164. package/dist/chunk-MXTAWPYM.js.map +1 -0
  165. package/dist/chunk-NCL2UZKI.js +96 -0
  166. package/dist/chunk-NCL2UZKI.js.map +1 -0
  167. package/dist/chunk-NET4YLRY.js +107 -0
  168. package/dist/chunk-NET4YLRY.js.map +1 -0
  169. package/dist/chunk-NIG67CFM.js +329 -0
  170. package/dist/chunk-NIG67CFM.js.map +1 -0
  171. package/dist/chunk-NMVNNO3L.js +10 -0
  172. package/dist/chunk-NMVNNO3L.js.map +1 -0
  173. package/dist/chunk-NP7VID4K.js +2 -0
  174. package/dist/chunk-NP7VID4K.js.map +1 -0
  175. package/dist/chunk-NPUCMAG2.js +382 -0
  176. package/dist/chunk-NPUCMAG2.js.map +1 -0
  177. package/dist/chunk-NSCLRHES.js +102 -0
  178. package/dist/chunk-NSCLRHES.js.map +1 -0
  179. package/dist/chunk-OEUXHVFW.js +86 -0
  180. package/dist/chunk-OEUXHVFW.js.map +1 -0
  181. package/dist/chunk-ONBHFIMZ.js +137 -0
  182. package/dist/chunk-ONBHFIMZ.js.map +1 -0
  183. package/dist/chunk-OPH2HHL2.js +228 -0
  184. package/dist/chunk-OPH2HHL2.js.map +1 -0
  185. package/dist/chunk-OQERHIQP.js +287 -0
  186. package/dist/chunk-OQERHIQP.js.map +1 -0
  187. package/dist/chunk-P6YJAZFG.js +103 -0
  188. package/dist/chunk-P6YJAZFG.js.map +1 -0
  189. package/dist/chunk-PG7ERDOD.js +65 -0
  190. package/dist/chunk-PG7ERDOD.js.map +1 -0
  191. package/dist/chunk-PIB5JBMA.js +90 -0
  192. package/dist/chunk-PIB5JBMA.js.map +1 -0
  193. package/dist/chunk-PQ5BPIWQ.js +22 -0
  194. package/dist/chunk-PQ5BPIWQ.js.map +1 -0
  195. package/dist/chunk-PQBF6ZIQ.js +55 -0
  196. package/dist/chunk-PQBF6ZIQ.js.map +1 -0
  197. package/dist/chunk-PTWCTSZL.js +173 -0
  198. package/dist/chunk-PTWCTSZL.js.map +1 -0
  199. package/dist/chunk-QJUNPVOS.js +101 -0
  200. package/dist/chunk-QJUNPVOS.js.map +1 -0
  201. package/dist/chunk-QUDZOTRV.js +43 -0
  202. package/dist/chunk-QUDZOTRV.js.map +1 -0
  203. package/dist/chunk-RB7G65JC.js +213 -0
  204. package/dist/chunk-RB7G65JC.js.map +1 -0
  205. package/dist/chunk-RPHAOR7P.js +45 -0
  206. package/dist/chunk-RPHAOR7P.js.map +1 -0
  207. package/dist/chunk-RTCSH2AN.js +331 -0
  208. package/dist/chunk-RTCSH2AN.js.map +1 -0
  209. package/dist/chunk-S3ISPW3A.js +28 -0
  210. package/dist/chunk-S3ISPW3A.js.map +1 -0
  211. package/dist/chunk-SEVN656N.js +36 -0
  212. package/dist/chunk-SEVN656N.js.map +1 -0
  213. package/dist/chunk-SKVG672B.js +64 -0
  214. package/dist/chunk-SKVG672B.js.map +1 -0
  215. package/dist/chunk-SPQIU7GA.js +236 -0
  216. package/dist/chunk-SPQIU7GA.js.map +1 -0
  217. package/dist/chunk-SSGJ6MEM.js +1076 -0
  218. package/dist/chunk-SSGJ6MEM.js.map +1 -0
  219. package/dist/chunk-SSKZ2AED.js +118 -0
  220. package/dist/chunk-SSKZ2AED.js.map +1 -0
  221. package/dist/chunk-T56JFGXI.js +561 -0
  222. package/dist/chunk-T56JFGXI.js.map +1 -0
  223. package/dist/chunk-TCB6O74A.js +137 -0
  224. package/dist/chunk-TCB6O74A.js.map +1 -0
  225. package/dist/chunk-TTEYPO6X.js +57 -0
  226. package/dist/chunk-TTEYPO6X.js.map +1 -0
  227. package/dist/chunk-TWPHAJNM.js +462 -0
  228. package/dist/chunk-TWPHAJNM.js.map +1 -0
  229. package/dist/chunk-TXJJLP4O.js +336 -0
  230. package/dist/chunk-TXJJLP4O.js.map +1 -0
  231. package/dist/chunk-TZ6TB7VM.js +245 -0
  232. package/dist/chunk-TZ6TB7VM.js.map +1 -0
  233. package/dist/chunk-UBLKJJ7K.js +278 -0
  234. package/dist/chunk-UBLKJJ7K.js.map +1 -0
  235. package/dist/chunk-UCJ6IUFS.js +52 -0
  236. package/dist/chunk-UCJ6IUFS.js.map +1 -0
  237. package/dist/chunk-UCKFJ3QF.js +197 -0
  238. package/dist/chunk-UCKFJ3QF.js.map +1 -0
  239. package/dist/chunk-UPXSLILN.js +20 -0
  240. package/dist/chunk-UPXSLILN.js.map +1 -0
  241. package/dist/chunk-V4DORGWJ.js +92 -0
  242. package/dist/chunk-V4DORGWJ.js.map +1 -0
  243. package/dist/chunk-V6E5DYZD.js +80 -0
  244. package/dist/chunk-V6E5DYZD.js.map +1 -0
  245. package/dist/chunk-VLLYKF3P.js +96 -0
  246. package/dist/chunk-VLLYKF3P.js.map +1 -0
  247. package/dist/chunk-VLNBTOH6.js +43 -0
  248. package/dist/chunk-VLNBTOH6.js.map +1 -0
  249. package/dist/chunk-WMDSMSPL.js +303 -0
  250. package/dist/chunk-WMDSMSPL.js.map +1 -0
  251. package/dist/chunk-WU5BLGTF.js +149 -0
  252. package/dist/chunk-WU5BLGTF.js.map +1 -0
  253. package/dist/chunk-WYTZVSGK.js +245 -0
  254. package/dist/chunk-WYTZVSGK.js.map +1 -0
  255. package/dist/chunk-X47QMM6K.js +187 -0
  256. package/dist/chunk-X47QMM6K.js.map +1 -0
  257. package/dist/chunk-XNBKO32Y.js +174 -0
  258. package/dist/chunk-XNBKO32Y.js.map +1 -0
  259. package/dist/chunk-YHUSW7D2.js +40 -0
  260. package/dist/chunk-YHUSW7D2.js.map +1 -0
  261. package/dist/chunk-YSKD7UXX.js +126 -0
  262. package/dist/chunk-YSKD7UXX.js.map +1 -0
  263. package/dist/chunk-YYRDGWOU.js +72 -0
  264. package/dist/chunk-YYRDGWOU.js.map +1 -0
  265. package/dist/chunk-ZIGZMWM7.js +19 -0
  266. package/dist/chunk-ZIGZMWM7.js.map +1 -0
  267. package/dist/chunk-ZKRTUKNP.js +40 -0
  268. package/dist/chunk-ZKRTUKNP.js.map +1 -0
  269. package/dist/code-block.js +38 -0
  270. package/dist/code-block.js.map +1 -0
  271. package/dist/commit.js +51 -0
  272. package/dist/commit.js.map +1 -0
  273. package/dist/composer.js +15 -0
  274. package/dist/composer.js.map +1 -0
  275. package/dist/confirmation.js +53 -0
  276. package/dist/confirmation.js.map +1 -0
  277. package/dist/connection.js +8 -0
  278. package/dist/connection.js.map +1 -0
  279. package/dist/context-panel.js +25 -0
  280. package/dist/context-panel.js.map +1 -0
  281. package/dist/controls.js +10 -0
  282. package/dist/controls.js.map +1 -0
  283. package/dist/conversation.js +19 -0
  284. package/dist/conversation.js.map +1 -0
  285. package/dist/diff-view.js +15 -0
  286. package/dist/diff-view.js.map +1 -0
  287. package/dist/edge.js +10 -0
  288. package/dist/edge.js.map +1 -0
  289. package/dist/environment-variables.js +29 -0
  290. package/dist/environment-variables.js.map +1 -0
  291. package/dist/file-tree.js +23 -0
  292. package/dist/file-tree.js.map +1 -0
  293. package/dist/gallery.js +9 -0
  294. package/dist/gallery.js.map +1 -0
  295. package/dist/grouped-parts.js +21 -0
  296. package/dist/grouped-parts.js.map +1 -0
  297. package/dist/image.js +9 -0
  298. package/dist/image.js.map +1 -0
  299. package/dist/index.d.ts +366 -98
  300. package/dist/index.js +672 -13577
  301. package/dist/index.js.map +1 -1
  302. package/dist/inline-citation.js +39 -0
  303. package/dist/inline-citation.js.map +1 -0
  304. package/dist/jsx-preview.js +17 -0
  305. package/dist/jsx-preview.js.map +1 -0
  306. package/dist/markdown-view.js +17 -0
  307. package/dist/markdown-view.js.map +1 -0
  308. package/dist/message-compare.js +12 -0
  309. package/dist/message-compare.js.map +1 -0
  310. package/dist/message-edit.js +19 -0
  311. package/dist/message-edit.js.map +1 -0
  312. package/dist/message-feedback.js +11 -0
  313. package/dist/message-feedback.js.map +1 -0
  314. package/dist/message-form-spec.js +38 -0
  315. package/dist/message-form-spec.js.map +1 -0
  316. package/dist/message-form.js +26 -0
  317. package/dist/message-form.js.map +1 -0
  318. package/dist/message-table-spec.js +26 -0
  319. package/dist/message-table-spec.js.map +1 -0
  320. package/dist/message-table.js +12 -0
  321. package/dist/message-table.js.map +1 -0
  322. package/dist/message.js +51 -0
  323. package/dist/message.js.map +1 -0
  324. package/dist/mic-selector.js +27 -0
  325. package/dist/mic-selector.js.map +1 -0
  326. package/dist/model-provider-logo.js +13 -0
  327. package/dist/model-provider-logo.js.map +1 -0
  328. package/dist/motion-config.js +9 -0
  329. package/dist/motion-config.js.map +1 -0
  330. package/dist/node.js +22 -0
  331. package/dist/node.js.map +1 -0
  332. package/dist/open-in-chat.js +31 -0
  333. package/dist/open-in-chat.js.map +1 -0
  334. package/dist/package-info.js +25 -0
  335. package/dist/package-info.js.map +1 -0
  336. package/dist/panel.js +10 -0
  337. package/dist/panel.js.map +1 -0
  338. package/dist/part-groups.js +18 -0
  339. package/dist/part-groups.js.map +1 -0
  340. package/dist/permission-mode-select.js +9 -0
  341. package/dist/permission-mode-select.js.map +1 -0
  342. package/dist/persona-sources.js +8 -0
  343. package/dist/persona-sources.js.map +1 -0
  344. package/dist/persona.js +14 -0
  345. package/dist/persona.js.map +1 -0
  346. package/dist/plan.js +32 -0
  347. package/dist/plan.js.map +1 -0
  348. package/dist/prompt-input-effort.js +9 -0
  349. package/dist/prompt-input-effort.js.map +1 -0
  350. package/dist/prompt-input-mode.js +10 -0
  351. package/dist/prompt-input-mode.js.map +1 -0
  352. package/dist/prompt-input-slash.js +12 -0
  353. package/dist/prompt-input-slash.js.map +1 -0
  354. package/dist/prompt-input.js +89 -0
  355. package/dist/prompt-input.js.map +1 -0
  356. package/dist/queue.js +37 -0
  357. package/dist/queue.js.map +1 -0
  358. package/dist/reasoning.js +23 -0
  359. package/dist/reasoning.js.map +1 -0
  360. package/dist/sandbox.js +28 -0
  361. package/dist/sandbox.js.map +1 -0
  362. package/dist/schema-display.js +33 -0
  363. package/dist/schema-display.js.map +1 -0
  364. package/dist/selection-toolbar.js +11 -0
  365. package/dist/selection-toolbar.js.map +1 -0
  366. package/dist/session-header.js +9 -0
  367. package/dist/session-header.js.map +1 -0
  368. package/dist/session-status-bar.js +9 -0
  369. package/dist/session-status-bar.js.map +1 -0
  370. package/dist/shimmer.js +9 -0
  371. package/dist/shimmer.js.map +1 -0
  372. package/dist/snippet.js +17 -0
  373. package/dist/snippet.js.map +1 -0
  374. package/dist/sources.js +17 -0
  375. package/dist/sources.js.map +1 -0
  376. package/dist/speech-input.js +9 -0
  377. package/dist/speech-input.js.map +1 -0
  378. package/dist/stack-trace.js +27 -0
  379. package/dist/stack-trace.js.map +1 -0
  380. package/dist/suggestion.js +16 -0
  381. package/dist/suggestion.js.map +1 -0
  382. package/dist/task.js +18 -0
  383. package/dist/task.js.map +1 -0
  384. package/dist/test-results.js +43 -0
  385. package/dist/test-results.js.map +1 -0
  386. package/dist/token-usage.js +27 -0
  387. package/dist/token-usage.js.map +1 -0
  388. package/dist/tool-result-card.js +9 -0
  389. package/dist/tool-result-card.js.map +1 -0
  390. package/dist/tool.js +27 -0
  391. package/dist/tool.js.map +1 -0
  392. package/dist/toolbar.js +10 -0
  393. package/dist/toolbar.js.map +1 -0
  394. package/dist/transcription.js +11 -0
  395. package/dist/transcription.js.map +1 -0
  396. package/dist/turn-status.js +9 -0
  397. package/dist/turn-status.js.map +1 -0
  398. package/dist/use-audio-level.js +9 -0
  399. package/dist/use-audio-level.js.map +1 -0
  400. package/dist/voice-selector.js +47 -0
  401. package/dist/voice-selector.js.map +1 -0
  402. package/dist/web-preview.js +19 -0
  403. package/dist/web-preview.js.map +1 -0
  404. package/package.json +11 -9
  405. package/schemas/a2ui-surface.v1.schema.json +5425 -0
  406. package/src/_chat-shell-rail.tsx +3 -1
  407. package/src/_flow-boundary.tsx +2 -2
  408. package/src/_persona-rive.tsx +27 -41
  409. package/src/a2ui/a2ui-surface.stories.tsx +662 -0
  410. package/src/a2ui/a2ui-surface.test.tsx +201 -0
  411. package/src/a2ui/a2ui-surface.tsx +354 -0
  412. package/src/a2ui/catalog.source.json +772 -0
  413. package/src/a2ui/core/a2ui-core.test.ts +178 -0
  414. package/src/a2ui/core/catalog.generated.ts +1006 -0
  415. package/src/a2ui/core/complete-json.ts +81 -0
  416. package/src/a2ui/core/index.ts +10 -0
  417. package/src/a2ui/core/schema.ts +111 -0
  418. package/src/a2ui/core/spec.ts +148 -0
  419. package/src/a2ui/core/validate.ts +266 -0
  420. package/src/a2ui/index.ts +7 -0
  421. package/src/a2ui/ui-catalog.tsx +225 -0
  422. package/src/a2ui/wave1-chart-spec.test.ts +87 -0
  423. package/src/a2ui/wave2-chart-spec.test.ts +108 -0
  424. package/src/agentic-workspace.stories.tsx +6 -45
  425. package/src/chat-shell.test.tsx +40 -10
  426. package/src/chat-shell.tsx +90 -44
  427. package/src/chat.stories.tsx +47 -42
  428. package/src/composer.stories.tsx +4 -5
  429. package/src/composer.test.tsx +45 -0
  430. package/src/composer.tsx +104 -21
  431. package/src/context-panel.stories.tsx +1 -1
  432. package/src/context-panel.tsx +1 -1
  433. package/src/conversation.stories.tsx +1 -0
  434. package/src/conversation.test.tsx +16 -0
  435. package/src/conversation.tsx +16 -2
  436. package/src/diff-view.stories.tsx +79 -0
  437. package/src/diff-view.tsx +3 -2
  438. package/src/file-tree.tsx +24 -17
  439. package/src/gallery.stories.tsx +42 -0
  440. package/src/gallery.tsx +2 -2
  441. package/src/grouped-parts.stories.tsx +23 -0
  442. package/src/grouped-parts.tsx +1 -1
  443. package/src/index.ts +1 -0
  444. package/src/message-compare.stories.tsx +40 -0
  445. package/src/message-compare.tsx +2 -1
  446. package/src/message-form.tsx +1 -1
  447. package/src/message-table.stories.tsx +24 -0
  448. package/src/message-table.tsx +1 -1
  449. package/src/package-info.tsx +12 -9
  450. package/src/reasoning.stories.tsx +27 -0
  451. package/src/reasoning.tsx +1 -1
  452. package/src/session-header.stories.tsx +35 -1
  453. package/src/session-header.tsx +1 -1
  454. package/src/sources.stories.tsx +30 -0
  455. package/src/sources.tsx +4 -1
  456. package/src/templates-ai-assistant.stories.tsx +151 -87
  457. package/src/tool.stories.tsx +39 -0
  458. package/src/tool.tsx +3 -3
  459. package/src/toolbar.tsx +0 -10
  460. package/dist/_flow-boundary-SHNWLQG5.js.map +0 -1
  461. package/dist/_persona-rive-JEG44YHX.js.map +0 -1
  462. /package/dist/{_audio-player-media-chrome-T3XVXWRZ.js.map → _audio-player-media-chrome.js.map} +0 -0
@@ -0,0 +1,208 @@
1
+ "use client";
2
+
3
+ // src/audio-visualizer.tsx
4
+ import { useReducedMotion } from "@elabs-ai/components-tokens";
5
+ import { cn } from "@elabs-ai/components-ui/lib/cn";
6
+ import { useLocale } from "@elabs-ai/components-ui";
7
+ import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var AUDIO_VISUALIZER_STATE_KEYS = {
10
+ active: "ai.audioVisualizer.active",
11
+ idle: "ai.audioVisualizer.idle",
12
+ silent: "ai.audioVisualizer.silent"
13
+ };
14
+ var CANVAS_WIDTH = 320;
15
+ var CANVAS_HEIGHT = 64;
16
+ var DEFAULT_BAR_COUNT = 32;
17
+ var DEFAULT_SENSITIVITY = 1;
18
+ var DEFAULT_SILENCE_THRESHOLD = 0.04;
19
+ var SMOOTHING_FACTOR = 0.35;
20
+ var CONVERGENCE_EPSILON = 2e-3;
21
+ var HYSTERESIS_RATIO = 0.25;
22
+ var clamp01 = (value) => Math.min(1, Math.max(0, value));
23
+ function normalizeBarCount(value) {
24
+ if (!Number.isFinite(value)) return DEFAULT_BAR_COUNT;
25
+ return Math.max(2, Math.round(value));
26
+ }
27
+ function hasConverged(display, target) {
28
+ for (let i = 0; i < target.length; i += 1) {
29
+ if (Math.abs((display[i] ?? 0) - (target[i] ?? 0)) > CONVERGENCE_EPSILON) return false;
30
+ }
31
+ return true;
32
+ }
33
+ function computeLevelBucket(average, loading, silenceThreshold, previous) {
34
+ if (loading) return "idle";
35
+ const margin = silenceThreshold * HYSTERESIS_RATIO;
36
+ if (average >= silenceThreshold + margin) return "active";
37
+ if (average < silenceThreshold - margin) return "silent";
38
+ if (previous === "idle") return average >= silenceThreshold ? "active" : "silent";
39
+ return previous;
40
+ }
41
+ function resampleLevels(levels, count, sensitivity) {
42
+ if (levels.length === 0) {
43
+ return new Array(count).fill(0);
44
+ }
45
+ const out = new Array(count);
46
+ for (let i = 0; i < count; i += 1) {
47
+ const sourceIndex = Math.min(levels.length - 1, Math.floor(i / count * levels.length));
48
+ out[i] = clamp01((levels[sourceIndex] ?? 0) * sensitivity);
49
+ }
50
+ return out;
51
+ }
52
+ function drawBars(ctx, levels, color) {
53
+ const { width, height } = ctx.canvas;
54
+ ctx.clearRect(0, 0, width, height);
55
+ const n = levels.length;
56
+ if (n === 0) return;
57
+ const gap = 2;
58
+ const barWidth = Math.max(1, (width - gap * (n - 1)) / n);
59
+ ctx.fillStyle = color;
60
+ for (let i = 0; i < n; i += 1) {
61
+ const level = levels[i] ?? 0;
62
+ const barHeight = Math.max(2, level * height);
63
+ const x = i * (barWidth + gap);
64
+ ctx.fillRect(x, height - barHeight, barWidth, barHeight);
65
+ }
66
+ }
67
+ function drawWave(ctx, levels, color) {
68
+ const { width, height } = ctx.canvas;
69
+ ctx.clearRect(0, 0, width, height);
70
+ const n = levels.length;
71
+ if (n < 2) return;
72
+ const midY = height / 2;
73
+ ctx.fillStyle = color;
74
+ ctx.beginPath();
75
+ for (let i = 0; i < n; i += 1) {
76
+ const x = i / (n - 1) * width;
77
+ const y = midY - clamp01(levels[i] ?? 0) * midY;
78
+ if (i === 0) ctx.moveTo(x, y);
79
+ else ctx.lineTo(x, y);
80
+ }
81
+ for (let i = n - 1; i >= 0; i -= 1) {
82
+ const x = i / (n - 1) * width;
83
+ const y = midY + clamp01(levels[i] ?? 0) * midY;
84
+ ctx.lineTo(x, y);
85
+ }
86
+ ctx.closePath();
87
+ ctx.fill();
88
+ }
89
+ function resolveFillColor(canvas) {
90
+ const styles = getComputedStyle(canvas);
91
+ const token = styles.getPropertyValue("--color-primary").trim() || styles.getPropertyValue("--primary").trim();
92
+ return token || styles.color || "currentColor";
93
+ }
94
+ var AudioVisualizer = forwardRef(
95
+ function AudioVisualizer2({
96
+ className,
97
+ levels = [],
98
+ loading = false,
99
+ variant = "bars",
100
+ sensitivity = DEFAULT_SENSITIVITY,
101
+ barCount = DEFAULT_BAR_COUNT,
102
+ silenceThreshold = DEFAULT_SILENCE_THRESHOLD,
103
+ statusLabel,
104
+ ...props
105
+ }, ref) {
106
+ const { t } = useLocale();
107
+ const prefersReducedMotion = useReducedMotion();
108
+ const canvasRef = useRef(null);
109
+ const displayLevelsRef = useRef([]);
110
+ const rafRef = useRef(null);
111
+ const bucketRef = useRef("idle");
112
+ const [levelState, setLevelState] = useState("idle");
113
+ const [themeRevision, setThemeRevision] = useState(0);
114
+ const sampleCount = normalizeBarCount(barCount);
115
+ const targetLevels = useMemo(
116
+ () => resampleLevels(loading ? [] : levels, sampleCount, sensitivity),
117
+ [levels, loading, sampleCount, sensitivity]
118
+ );
119
+ useEffect(() => {
120
+ const canvas = canvasRef.current;
121
+ if (!canvas) return void 0;
122
+ const ctx = canvas.getContext("2d");
123
+ const average = targetLevels.reduce((sum, value) => sum + value, 0) / (targetLevels.length || 1);
124
+ const bucket = computeLevelBucket(average, loading, silenceThreshold, bucketRef.current);
125
+ if (bucket !== bucketRef.current) {
126
+ bucketRef.current = bucket;
127
+ setLevelState(bucket);
128
+ }
129
+ const paint = (paintLevels) => {
130
+ if (!ctx) return;
131
+ const color = resolveFillColor(canvas);
132
+ if (variant === "wave") drawWave(ctx, paintLevels, color);
133
+ else drawBars(ctx, paintLevels, color);
134
+ };
135
+ if (prefersReducedMotion || loading) {
136
+ displayLevelsRef.current = targetLevels;
137
+ paint(targetLevels);
138
+ return void 0;
139
+ }
140
+ if (displayLevelsRef.current.length !== targetLevels.length) {
141
+ displayLevelsRef.current = targetLevels.slice();
142
+ }
143
+ paint(displayLevelsRef.current);
144
+ const tick = () => {
145
+ const display = displayLevelsRef.current;
146
+ const next = display.map(
147
+ (value, i) => value + ((targetLevels[i] ?? 0) - value) * SMOOTHING_FACTOR
148
+ );
149
+ displayLevelsRef.current = next;
150
+ paint(next);
151
+ if (hasConverged(next, targetLevels)) return;
152
+ rafRef.current = requestAnimationFrame(tick);
153
+ };
154
+ rafRef.current = requestAnimationFrame(tick);
155
+ return () => {
156
+ if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
157
+ rafRef.current = null;
158
+ };
159
+ }, [targetLevels, variant, loading, prefersReducedMotion, silenceThreshold, themeRevision]);
160
+ useEffect(() => {
161
+ if (typeof document === "undefined") return void 0;
162
+ const observer = new MutationObserver(() => setThemeRevision((r) => r + 1));
163
+ observer.observe(document.documentElement, {
164
+ attributes: true,
165
+ attributeFilter: ["data-theme"]
166
+ });
167
+ return () => observer.disconnect();
168
+ }, []);
169
+ const label = statusLabel === null ? null : statusLabel ?? t(AUDIO_VISUALIZER_STATE_KEYS[levelState]);
170
+ return /* @__PURE__ */ jsxs(
171
+ "div",
172
+ {
173
+ className: cn("flex flex-col gap-1", className),
174
+ "data-slot": "audio-visualizer",
175
+ ref,
176
+ ...props,
177
+ children: [
178
+ /* @__PURE__ */ jsx(
179
+ "canvas",
180
+ {
181
+ "aria-hidden": "true",
182
+ className: "h-16 w-full rounded-md bg-muted",
183
+ "data-slot": "audio-visualizer-canvas",
184
+ height: CANVAS_HEIGHT,
185
+ ref: canvasRef,
186
+ width: CANVAS_WIDTH
187
+ }
188
+ ),
189
+ label !== null && /* @__PURE__ */ jsx(
190
+ "span",
191
+ {
192
+ className: "sr-only",
193
+ "data-slot": "audio-visualizer-status",
194
+ role: "status",
195
+ "aria-live": "polite",
196
+ children: label
197
+ }
198
+ )
199
+ ]
200
+ }
201
+ );
202
+ }
203
+ );
204
+
205
+ export {
206
+ AudioVisualizer
207
+ };
208
+ //# sourceMappingURL=chunk-IQJG4IDX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/audio-visualizer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useReducedMotion } from \"@elabs-ai/components-tokens\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { useLocale } from \"@elabs-ai/components-ui\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { forwardRef, useEffect, useMemo, useRef, useState } from \"react\";\n\n/**\n * brand-ui is a presentation layer (D5, docs/DECISIONS.md §D5): this component\n * never calls `getUserMedia`, never owns an `AudioContext`, and starts nothing\n * on mount. It draws whatever `levels` a parent — who already has a\n * `MediaStream`/`AnalyserNode` — passes it, frame by frame. The analyser\n * plumbing, when a consumer genuinely wants it, is the separate, opt-in\n * `useAudioLevel` export in `./use-audio-level` — never something this\n * component reaches for itself. See issue #21.\n */\n\nexport type AudioVisualizerVariant = \"bars\" | \"wave\";\nexport type AudioVisualizerLevelState = \"idle\" | \"silent\" | \"active\";\n\n/**\n * Localized status announced when the discretized level state changes, one\n * key per `AudioVisualizerLevelState`. Typed as a `Record` (not a bare\n * template string) so a state value added later without a matching key here\n * fails to typecheck — same convention as `Persona`'s `PERSONA_STATE_KEYS`.\n */\nconst AUDIO_VISUALIZER_STATE_KEYS: Record<AudioVisualizerLevelState, string> = {\n active: \"ai.audioVisualizer.active\",\n idle: \"ai.audioVisualizer.idle\",\n silent: \"ai.audioVisualizer.silent\",\n};\n\n// Fixed internal render resolution — the canvas is stretched to its CSS box\n// (see the className below), so this is a rendering-quality knob, not a\n// layout one. Picking a fixed size up front means the draw loop never has to\n// measure the DOM (no `getBoundingClientRect`/`offsetHeight` in the hot path).\nconst CANVAS_WIDTH = 320;\nconst CANVAS_HEIGHT = 64;\nconst DEFAULT_BAR_COUNT = 32;\nconst DEFAULT_SENSITIVITY = 1;\nconst DEFAULT_SILENCE_THRESHOLD = 0.04;\n/**\n * Fraction of the remaining distance to the target level closed per animation\n * frame — a fixed-cost lerp, never a growing history buffer, so the per-frame\n * cost stays O(barCount) no matter how long the session has run.\n */\nconst SMOOTHING_FACTOR = 0.35;\n/** Once every displayed level is within this of its target, the smoothing has\n * visually converged — stop scheduling more animation frames rather than\n * looping forever on a static input. */\nconst CONVERGENCE_EPSILON = 0.002;\n/**\n * Hysteresis margin (as a fraction of `silenceThreshold`) around the active/\n * silent boundary. A live signal hovering right at the threshold can cross it\n * on consecutive samples; without a dead zone, every such graze would flip\n * the announced `role=\"status\"` text, flooding assistive tech with\n * alternating \"active\"/\"no input\" announcements. A clear crossing (past the\n * margin on either side) still flips immediately — only a graze inside the\n * dead zone holds the previous state.\n */\nconst HYSTERESIS_RATIO = 0.25;\n\nconst clamp01 = (value: number) => Math.min(1, Math.max(0, value));\n\n/** A finite, rounded, >=2 bar count safe to use as an array length — an\n * unsanitized `barCount` (NaN, Infinity, a fractional value from a\n * misconfigured/computed prop) would otherwise throw `RangeError: Invalid\n * array length` the moment it reaches `new Array(count)`. */\nfunction normalizeBarCount(value: number): number {\n if (!Number.isFinite(value)) return DEFAULT_BAR_COUNT;\n return Math.max(2, Math.round(value));\n}\n\n/** True once every displayed level is within `CONVERGENCE_EPSILON` of its\n * target — i.e. the smoothing animation has nothing left to visibly close. */\nfunction hasConverged(display: number[], target: number[]): boolean {\n for (let i = 0; i < target.length; i += 1) {\n if (Math.abs((display[i] ?? 0) - (target[i] ?? 0)) > CONVERGENCE_EPSILON) return false;\n }\n return true;\n}\n\n/**\n * The discretized level bucket, with hysteresis around `silenceThreshold` —\n * see `HYSTERESIS_RATIO`. `previous` is only consulted inside the dead zone;\n * a clear reading on either side is decisive regardless of history.\n */\nfunction computeLevelBucket(\n average: number,\n loading: boolean,\n silenceThreshold: number,\n previous: AudioVisualizerLevelState,\n): AudioVisualizerLevelState {\n if (loading) return \"idle\";\n const margin = silenceThreshold * HYSTERESIS_RATIO;\n if (average >= silenceThreshold + margin) return \"active\";\n if (average < silenceThreshold - margin) return \"silent\";\n // Inside the dead zone: hold the previous bucket. `previous` starts as\n // \"idle\" (mount, or just-exited `loading`), which isn't itself a valid\n // active/silent reading to hold onto — fall back to the plain threshold\n // check for that one transition only.\n if (previous === \"idle\") return average >= silenceThreshold ? \"active\" : \"silent\";\n return previous;\n}\n\n/** Nearest-neighbor resample of an arbitrary-length sample array to `count`\n * entries, applying `sensitivity` and clamping to [0, 1]. Never allocates\n * more than `count` numbers, independent of `levels.length`. */\nfunction resampleLevels(levels: number[], count: number, sensitivity: number): number[] {\n if (levels.length === 0) {\n return new Array(count).fill(0);\n }\n const out = new Array<number>(count);\n for (let i = 0; i < count; i += 1) {\n const sourceIndex = Math.min(levels.length - 1, Math.floor((i / count) * levels.length));\n out[i] = clamp01((levels[sourceIndex] ?? 0) * sensitivity);\n }\n return out;\n}\n\nfunction drawBars(ctx: CanvasRenderingContext2D, levels: number[], color: string) {\n const { width, height } = ctx.canvas;\n ctx.clearRect(0, 0, width, height);\n const n = levels.length;\n if (n === 0) return;\n const gap = 2;\n const barWidth = Math.max(1, (width - gap * (n - 1)) / n);\n ctx.fillStyle = color;\n for (let i = 0; i < n; i += 1) {\n const level = levels[i] ?? 0;\n const barHeight = Math.max(2, level * height);\n const x = i * (barWidth + gap);\n ctx.fillRect(x, height - barHeight, barWidth, barHeight);\n }\n}\n\n/** Draws the CURRENT sample snapshot as a filled envelope (no history/scroll —\n * see the reduced-motion note on `AudioVisualizer` for why this shape never\n * accumulates state over time). */\nfunction drawWave(ctx: CanvasRenderingContext2D, levels: number[], color: string) {\n const { width, height } = ctx.canvas;\n ctx.clearRect(0, 0, width, height);\n const n = levels.length;\n if (n < 2) return;\n const midY = height / 2;\n ctx.fillStyle = color;\n ctx.beginPath();\n for (let i = 0; i < n; i += 1) {\n const x = (i / (n - 1)) * width;\n const y = midY - clamp01(levels[i] ?? 0) * midY;\n if (i === 0) ctx.moveTo(x, y);\n else ctx.lineTo(x, y);\n }\n for (let i = n - 1; i >= 0; i -= 1) {\n const x = (i / (n - 1)) * width;\n const y = midY + clamp01(levels[i] ?? 0) * midY;\n ctx.lineTo(x, y);\n }\n ctx.closePath();\n ctx.fill();\n}\n\n/** Resolves the bar/wave fill colour from tokens at draw time. `getComputedStyle`\n * resolves a CSS custom property's VALUE — unlike `getBoundingClientRect`/\n * `offsetHeight`, it never forces layout, so this is not a \"layout read\". */\nfunction resolveFillColor(canvas: HTMLCanvasElement): string {\n const styles = getComputedStyle(canvas);\n const token =\n styles.getPropertyValue(\"--color-primary\").trim() ||\n styles.getPropertyValue(\"--primary\").trim();\n return token || styles.color || \"currentColor\";\n}\n\nexport interface AudioVisualizerProps extends Omit<ComponentProps<\"div\">, \"children\"> {\n /**\n * Normalized 0–1 amplitude samples for the CURRENT frame — one per bar (or\n * per sample point for `variant=\"wave\"`). Update this from your own\n * analyser polling loop (or the opt-in `useAudioLevel` hook) on every frame\n * you want reflected; the component never touches the microphone itself.\n * Any length works — samples are resampled to `barCount`.\n */\n levels?: number[];\n /** No stream connected yet — renders the idle baseline instead of `levels`. */\n loading?: boolean;\n /** `\"bars\"` (default) — a bar chart. `\"wave\"` — a filled envelope through the\n * current samples. Both encode level as HEIGHT, never as hue alone. */\n variant?: AudioVisualizerVariant;\n /** Multiplies each level before clamping to [0, 1] — turn up for a quiet input. */\n sensitivity?: number;\n /** Number of bars/points drawn. */\n barCount?: number;\n /** Average level, 0–1, at/above which the status text reads \"active\". */\n silenceThreshold?: number;\n /**\n * Visually-hidden text announced when the discretized level state changes.\n * Defaults to a localized label (\"No input detected\" / \"Microphone\n * active\" / …). Pass `null` when the consuming surface already renders its\n * own live region, so assistive tech is not told twice.\n */\n statusLabel?: ReactNode | null;\n}\n\n/**\n * Live mic-level / waveform meter. Canvas-drawn (one element, no per-bar DOM,\n * so a `levels` prop updating every frame never costs React a 32-element\n * reconciliation) and driven entirely by the `levels` prop — see the module\n * doc comment for the presentation-layer boundary this enforces.\n *\n * The canvas is decorative (`aria-hidden`); the level itself is announced\n * through a throttled `role=\"status\"` text alternative — see `statusLabel`.\n *\n * Reduced motion: under `prefers-reduced-motion` (or while `loading`) the\n * component skips its internal smoothing animation entirely and paints the\n * raw `levels` directly on every prop change — a static bar chart of the\n * current levels, never a freeze mid-interpolation.\n */\nexport const AudioVisualizer = forwardRef<HTMLDivElement, AudioVisualizerProps>(\n function AudioVisualizer(\n {\n className,\n levels = [],\n loading = false,\n variant = \"bars\",\n sensitivity = DEFAULT_SENSITIVITY,\n barCount = DEFAULT_BAR_COUNT,\n silenceThreshold = DEFAULT_SILENCE_THRESHOLD,\n statusLabel,\n ...props\n },\n ref,\n ) {\n const { t } = useLocale();\n const prefersReducedMotion = useReducedMotion();\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const displayLevelsRef = useRef<number[]>([]);\n const rafRef = useRef<number | null>(null);\n const bucketRef = useRef<AudioVisualizerLevelState>(\"idle\");\n const [levelState, setLevelState] = useState<AudioVisualizerLevelState>(\"idle\");\n // Bumped whenever `data-theme` actually changes on the document — see the\n // MutationObserver effect below. `resolveFillColor` cannot read a token\n // value that isn't there yet: a theme-setting provider (e.g.\n // `ThemeProvider`) applies `data-theme` in its OWN mount effect, and React\n // fires a CHILD's effects before its parent's, so this component's paint\n // effect otherwise runs first and reads whatever the un-themed `:root`\n // fallback resolves to. Re-running the paint once the attribute lands\n // (rather than only on the next animation frame, which the `loading`/\n // reduced-motion path never schedules) is what fixes both the first paint\n // AND a theme change after mount.\n const [themeRevision, setThemeRevision] = useState(0);\n\n const sampleCount = normalizeBarCount(barCount);\n\n const targetLevels = useMemo(\n () => resampleLevels(loading ? [] : levels, sampleCount, sensitivity),\n [levels, loading, sampleCount, sensitivity],\n );\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return undefined;\n const ctx = canvas.getContext(\"2d\");\n\n // The announced status always reflects the CURRENT target level,\n // synchronously and independent of the smoothing animation below — a\n // screen-reader user is never a lerp-frame behind the real signal, even\n // though the visual bars/wave may still be easing toward it.\n const average =\n targetLevels.reduce((sum, value) => sum + value, 0) / (targetLevels.length || 1);\n const bucket = computeLevelBucket(average, loading, silenceThreshold, bucketRef.current);\n if (bucket !== bucketRef.current) {\n bucketRef.current = bucket;\n setLevelState(bucket);\n }\n\n const paint = (paintLevels: number[]) => {\n if (!ctx) return;\n const color = resolveFillColor(canvas);\n if (variant === \"wave\") drawWave(ctx, paintLevels, color);\n else drawBars(ctx, paintLevels, color);\n };\n\n if (prefersReducedMotion || loading) {\n displayLevelsRef.current = targetLevels;\n paint(targetLevels);\n return undefined;\n }\n\n // Seed (or reset, on a `barCount` change) the smoothing buffer and paint\n // this frame SYNCHRONOUSLY — the canvas and status text reflect the\n // current `levels` immediately on mount/update, rather than waiting for\n // the first `requestAnimationFrame` callback. The rAF loop below only\n // has to run to carry the smoothing motion forward from here.\n if (displayLevelsRef.current.length !== targetLevels.length) {\n displayLevelsRef.current = targetLevels.slice();\n }\n paint(displayLevelsRef.current);\n\n const tick = () => {\n const display = displayLevelsRef.current;\n const next = display.map(\n (value, i) => value + ((targetLevels[i] ?? 0) - value) * SMOOTHING_FACTOR,\n );\n displayLevelsRef.current = next;\n paint(next);\n // Stop rescheduling once the smoothing has visually converged — the\n // `targetLevels` dependency above restarts the loop the moment a real\n // prop change gives it somewhere new to go.\n if (hasConverged(next, targetLevels)) return;\n rafRef.current = requestAnimationFrame(tick);\n };\n\n rafRef.current = requestAnimationFrame(tick);\n return () => {\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n };\n // `themeRevision` is the trigger; `resolveFillColor` re-reads the token\n // live, so the value itself never needs to be a dependency.\n }, [targetLevels, variant, loading, prefersReducedMotion, silenceThreshold, themeRevision]);\n\n // Track `data-theme` (whatever sets it — `ThemeProvider`'s own mount\n // effect, a later `setTheme` call, or Storybook's theme decorator) and\n // re-run the paint effect above once it actually changes. Same fix as\n // `InteractiveTerminal`'s identical `data-theme` race.\n useEffect(() => {\n if (typeof document === \"undefined\") return undefined;\n const observer = new MutationObserver(() => setThemeRevision((r) => r + 1));\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-theme\"],\n });\n return () => observer.disconnect();\n }, []);\n\n const label =\n statusLabel === null ? null : (statusLabel ?? t(AUDIO_VISUALIZER_STATE_KEYS[levelState]));\n\n return (\n <div\n className={cn(\"flex flex-col gap-1\", className)}\n data-slot=\"audio-visualizer\"\n ref={ref}\n {...props}\n >\n <canvas\n aria-hidden=\"true\"\n className=\"h-16 w-full rounded-md bg-muted\"\n data-slot=\"audio-visualizer-canvas\"\n height={CANVAS_HEIGHT}\n ref={canvasRef}\n width={CANVAS_WIDTH}\n />\n {label !== null && (\n <span\n className=\"sr-only\"\n data-slot=\"audio-visualizer-status\"\n role=\"status\"\n aria-live=\"polite\"\n >\n {label}\n </span>\n )}\n </div>\n );\n },\n);\n"],"mappings":"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","names":["AudioVisualizer"]}
@@ -0,0 +1,144 @@
1
+ "use client";
2
+
3
+ // src/conversation.tsx
4
+ import { Button, downloadBlob, useLocale } from "@elabs-ai/components-ui";
5
+ import { cn } from "@elabs-ai/components-ui/lib/cn";
6
+ import { ArrowDownIcon, DownloadIcon } from "lucide-react";
7
+ import { useCallback } from "react";
8
+ import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ var Conversation = ({ className, isStreaming = false, ...props }) => /* @__PURE__ */ jsx(
11
+ StickToBottom,
12
+ {
13
+ className: cn("relative flex-1 overflow-y-hidden", className),
14
+ initial: "smooth",
15
+ resize: "smooth",
16
+ role: "log",
17
+ "aria-live": isStreaming ? "off" : "polite",
18
+ "aria-busy": isStreaming || void 0,
19
+ ...props
20
+ }
21
+ );
22
+ var ConversationContent = ({
23
+ className,
24
+ scrollClassName,
25
+ ...props
26
+ }) => /* @__PURE__ */ jsx(
27
+ StickToBottom.Content,
28
+ {
29
+ scrollClassName: cn("flex flex-col", scrollClassName),
30
+ className: cn(
31
+ "mx-auto flex w-full max-w-(--chat-column) flex-1 shrink-0 flex-col gap-8 p-4 pb-[calc(var(--chat-composer-inset,0px)+--spacing(4))]",
32
+ className
33
+ ),
34
+ ...props
35
+ }
36
+ );
37
+ var ConversationEmptyState = ({
38
+ className,
39
+ title = "No messages yet",
40
+ description = "Start a conversation to see messages here",
41
+ icon,
42
+ actions,
43
+ children,
44
+ ...props
45
+ }) => /* @__PURE__ */ jsx(
46
+ "div",
47
+ {
48
+ className: cn(
49
+ "flex size-full flex-col items-center justify-center gap-3 p-8 text-center",
50
+ className
51
+ ),
52
+ ...props,
53
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
54
+ icon && /* @__PURE__ */ jsx("div", { className: "text-muted-foreground", children: icon }),
55
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
56
+ /* @__PURE__ */ jsx("h3", { className: "font-medium text-body", children: title }),
57
+ description && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-body", children: description })
58
+ ] }),
59
+ actions ? /* @__PURE__ */ jsx("div", { className: "mt-1 flex items-center gap-2", children: actions }) : null
60
+ ] })
61
+ }
62
+ );
63
+ var ConversationScrollButton = ({
64
+ className,
65
+ ...props
66
+ }) => {
67
+ const { isAtBottom, scrollToBottom } = useStickToBottomContext();
68
+ const { t } = useLocale();
69
+ const handleScrollToBottom = useCallback(() => {
70
+ scrollToBottom();
71
+ }, [scrollToBottom]);
72
+ return !isAtBottom && /* @__PURE__ */ jsx(
73
+ Button,
74
+ {
75
+ className: cn(
76
+ // Was `dark:bg-background dark:hover:bg-muted` — a hardcoded-dark
77
+ // branch invisible to any other registered theme (styling-and-tokens.md).
78
+ // `bg-background` already matches the `outline` variant's own default;
79
+ // the quieter `hover:bg-muted` (in place of `outline`'s default
80
+ // `hover:bg-accent`) now applies in every theme, not only the two
81
+ // shipped ones.
82
+ // `--chat-composer-inset`: lift the button clear of a floating
83
+ // `ChatShell variant="bare"` composer (0 anywhere else).
84
+ "absolute bottom-[calc(var(--chat-composer-inset,0px)+--spacing(4))] left-[50%] z-30 translate-x-[-50%] rounded-full bg-background hover:bg-muted",
85
+ className
86
+ ),
87
+ "aria-label": t("ai.turnStatus.scrollToBottom"),
88
+ onClick: handleScrollToBottom,
89
+ size: "icon",
90
+ type: "button",
91
+ variant: "outline",
92
+ ...props,
93
+ children: /* @__PURE__ */ jsx(ArrowDownIcon, { className: "size-4" })
94
+ }
95
+ );
96
+ };
97
+ var getMessageText = (message) => message.parts.filter((part) => part.type === "text").map((part) => part.text).join("");
98
+ var defaultFormatMessage = (message) => {
99
+ const roleLabel = message.role.charAt(0).toUpperCase() + message.role.slice(1);
100
+ return `**${roleLabel}:** ${getMessageText(message)}`;
101
+ };
102
+ var messagesToMarkdown = (messages, formatMessage = defaultFormatMessage) => messages.map((msg, i) => formatMessage(msg, i)).join("\n\n");
103
+ var ConversationDownload = ({
104
+ messages,
105
+ filename = "conversation.md",
106
+ formatMessage = defaultFormatMessage,
107
+ className,
108
+ children,
109
+ ...props
110
+ }) => {
111
+ const { t } = useLocale();
112
+ const handleDownload = useCallback(() => {
113
+ const markdown = messagesToMarkdown(messages, formatMessage);
114
+ downloadBlob(new Blob([markdown], { type: "text/markdown" }), filename);
115
+ }, [messages, filename, formatMessage]);
116
+ return /* @__PURE__ */ jsx(
117
+ Button,
118
+ {
119
+ className: cn(
120
+ // See `ConversationScrollButton` — was a hardcoded-dark-only branch;
121
+ // `bg-background hover:bg-muted` now applies in every theme.
122
+ "absolute top-4 end-4 rounded-full bg-background hover:bg-muted",
123
+ className
124
+ ),
125
+ "aria-label": t("ai.conversation.download"),
126
+ onClick: handleDownload,
127
+ size: "icon",
128
+ type: "button",
129
+ variant: "outline",
130
+ ...props,
131
+ children: children ?? /* @__PURE__ */ jsx(DownloadIcon, { className: "size-4" })
132
+ }
133
+ );
134
+ };
135
+
136
+ export {
137
+ Conversation,
138
+ ConversationContent,
139
+ ConversationEmptyState,
140
+ ConversationScrollButton,
141
+ messagesToMarkdown,
142
+ ConversationDownload
143
+ };
144
+ //# sourceMappingURL=chunk-IX2PXG4Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/conversation.tsx"],"sourcesContent":["\"use client\";\n\nimport { Button, downloadBlob, useLocale } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport type { UIMessage } from \"ai\";\nimport { ArrowDownIcon, DownloadIcon } from \"lucide-react\";\nimport type { ComponentProps } from \"react\";\nimport { useCallback } from \"react\";\nimport { StickToBottom, useStickToBottomContext } from \"use-stick-to-bottom\";\n\nexport type ConversationProps = ComponentProps<typeof StickToBottom> & {\n /**\n * The assistant response is still arriving (loading-states.md\n * `isStreaming`). `role=\"log\"` otherwise announces every incoming token to\n * assistive tech as it streams in — deafening. While `true`, `aria-live`\n * is suppressed (`\"off\"`) and `aria-busy` is set; once streaming ends,\n * `aria-live` returns to `\"polite\"` so the settled content is what\n * actually gets announced.\n * @default false\n */\n isStreaming?: boolean;\n};\n\nexport const Conversation = ({ className, isStreaming = false, ...props }: ConversationProps) => (\n <StickToBottom\n className={cn(\"relative flex-1 overflow-y-hidden\", className)}\n initial=\"smooth\"\n resize=\"smooth\"\n role=\"log\"\n aria-live={isStreaming ? \"off\" : \"polite\"}\n aria-busy={isStreaming || undefined}\n {...props}\n />\n);\n\nexport type ConversationContentProps = ComponentProps<typeof StickToBottom.Content>;\n\nexport const ConversationContent = ({\n className,\n scrollClassName,\n ...props\n}: ConversationContentProps) => (\n <StickToBottom.Content\n // `use-stick-to-bottom` renders a scroll viewport (`scrollClassName`)\n // wrapping a content div (`className`, via `...props`). The viewport must\n // be a flex column so its height is a DEFINITE main size the content div\n // can consume via `flex-1` — a bare `height:100%` on the content div\n // can't resolve against a block-level parent whose own height is\n // `min-height`-only (the #72 empty-state-pinned-to-the-top bug).\n scrollClassName={cn(\"flex flex-col\", scrollClassName)}\n // Inside a `ChatShell variant=\"bare\"` the composer floats over the\n // transcript: the shell publishes `--chat-composer-inset` (its measured\n // height) so the last turn scrolls clear of it, and `--chat-column` so\n // turns share the composer's reading column. Outside such a shell both\n // variables are unset: the inset falls back to 0 and `max-width` to none.\n // `shrink-0`, never `min-h-0`: the content div must grow with its turns.\n // Clamped to the viewport height, the turns overflow it and its bottom\n // padding stays behind at the viewport's edge, so the last turn ends up\n // under the floating composer instead of above it.\n className={cn(\n \"mx-auto flex w-full max-w-(--chat-column) flex-1 shrink-0 flex-col gap-8 p-4 pb-[calc(var(--chat-composer-inset,0px)+--spacing(4))]\",\n className,\n )}\n {...props}\n />\n);\n\nexport type ConversationEmptyStateProps = ComponentProps<\"div\"> & {\n title?: string;\n description?: string;\n icon?: React.ReactNode;\n /** Primary / secondary action(s) below the description (e.g. suggested prompts). */\n actions?: React.ReactNode;\n};\n\nexport const ConversationEmptyState = ({\n className,\n title = \"No messages yet\",\n description = \"Start a conversation to see messages here\",\n icon,\n actions,\n children,\n ...props\n}: ConversationEmptyStateProps) => (\n <div\n className={cn(\n \"flex size-full flex-col items-center justify-center gap-3 p-8 text-center\",\n className,\n )}\n {...props}\n >\n {children ?? (\n <>\n {icon && <div className=\"text-muted-foreground\">{icon}</div>}\n <div className=\"space-y-1\">\n <h3 className=\"font-medium text-body\">{title}</h3>\n {description && <p className=\"text-muted-foreground text-body\">{description}</p>}\n </div>\n {actions ? <div className=\"mt-1 flex items-center gap-2\">{actions}</div> : null}\n </>\n )}\n </div>\n);\n\nexport type ConversationScrollButtonProps = ComponentProps<typeof Button>;\n\nexport const ConversationScrollButton = ({\n className,\n ...props\n}: ConversationScrollButtonProps) => {\n const { isAtBottom, scrollToBottom } = useStickToBottomContext();\n const { t } = useLocale();\n\n const handleScrollToBottom = useCallback(() => {\n scrollToBottom();\n }, [scrollToBottom]);\n\n return (\n !isAtBottom && (\n <Button\n className={cn(\n // Was `dark:bg-background dark:hover:bg-muted` — a hardcoded-dark\n // branch invisible to any other registered theme (styling-and-tokens.md).\n // `bg-background` already matches the `outline` variant's own default;\n // the quieter `hover:bg-muted` (in place of `outline`'s default\n // `hover:bg-accent`) now applies in every theme, not only the two\n // shipped ones.\n // `--chat-composer-inset`: lift the button clear of a floating\n // `ChatShell variant=\"bare\"` composer (0 anywhere else).\n \"absolute bottom-[calc(var(--chat-composer-inset,0px)+--spacing(4))] left-[50%] z-30 translate-x-[-50%] rounded-full bg-background hover:bg-muted\",\n className,\n )}\n aria-label={t(\"ai.turnStatus.scrollToBottom\")}\n onClick={handleScrollToBottom}\n size=\"icon\"\n type=\"button\"\n variant=\"outline\"\n {...props}\n >\n <ArrowDownIcon className=\"size-4\" />\n </Button>\n )\n );\n};\n\nconst getMessageText = (message: UIMessage): string =>\n message.parts\n .filter((part) => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n\nexport type ConversationDownloadProps = Omit<ComponentProps<typeof Button>, \"onClick\"> & {\n messages: UIMessage[];\n filename?: string;\n formatMessage?: (message: UIMessage, index: number) => string;\n};\n\nconst defaultFormatMessage = (message: UIMessage): string => {\n const roleLabel = message.role.charAt(0).toUpperCase() + message.role.slice(1);\n return `**${roleLabel}:** ${getMessageText(message)}`;\n};\n\nexport const messagesToMarkdown = (\n messages: UIMessage[],\n formatMessage: (message: UIMessage, index: number) => string = defaultFormatMessage,\n): string => messages.map((msg, i) => formatMessage(msg, i)).join(\"\\n\\n\");\n\nexport const ConversationDownload = ({\n messages,\n filename = \"conversation.md\",\n formatMessage = defaultFormatMessage,\n className,\n children,\n ...props\n}: ConversationDownloadProps) => {\n const { t } = useLocale();\n const handleDownload = useCallback(() => {\n const markdown = messagesToMarkdown(messages, formatMessage);\n downloadBlob(new Blob([markdown], { type: \"text/markdown\" }), filename);\n }, [messages, filename, formatMessage]);\n\n return (\n <Button\n className={cn(\n // See `ConversationScrollButton` — was a hardcoded-dark-only branch;\n // `bg-background hover:bg-muted` now applies in every theme.\n \"absolute top-4 end-4 rounded-full bg-background hover:bg-muted\",\n className,\n )}\n aria-label={t(\"ai.conversation.download\")}\n onClick={handleDownload}\n size=\"icon\"\n type=\"button\"\n variant=\"outline\"\n {...props}\n >\n {children ?? <DownloadIcon className=\"size-4\" />}\n </Button>\n );\n};\n"],"mappings":"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","names":[]}