@artooi/agents-web-components 0.0.0-stage → 0.1.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 (543) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/LICENSE +21 -0
  3. package/README.md +4397 -2
  4. package/dist/agents-web-components.bundle.js +5708 -0
  5. package/dist/agents-web-components.bundle.js.map +7 -0
  6. package/dist/bundle.d.ts +3 -0
  7. package/dist/bundle.d.ts.map +1 -0
  8. package/dist/chat.d.ts +3 -0
  9. package/dist/chat.d.ts.map +1 -0
  10. package/dist/chat.js +16373 -0
  11. package/dist/chat.js.map +7 -0
  12. package/dist/chunk-ARNLFGCC.js +7715 -0
  13. package/dist/chunk-ARNLFGCC.js.map +7 -0
  14. package/dist/constants.d.ts +575 -0
  15. package/dist/constants.d.ts.map +1 -0
  16. package/dist/core/activity_registration.d.ts +19 -0
  17. package/dist/core/activity_registration.d.ts.map +1 -0
  18. package/dist/core/activity_registry.d.ts +46 -0
  19. package/dist/core/activity_registry.d.ts.map +1 -0
  20. package/dist/core/activity_renderer.d.ts +20 -0
  21. package/dist/core/activity_renderer.d.ts.map +1 -0
  22. package/dist/core/ag_ui_chat.d.ts +836 -0
  23. package/dist/core/ag_ui_chat.d.ts.map +1 -0
  24. package/dist/core/agui_client.d.ts +370 -0
  25. package/dist/core/agui_client.d.ts.map +1 -0
  26. package/dist/core/attachment.d.ts +42 -0
  27. package/dist/core/attachment.d.ts.map +1 -0
  28. package/dist/core/chat_registry.d.ts +23 -0
  29. package/dist/core/chat_registry.d.ts.map +1 -0
  30. package/dist/core/client_seed.d.ts +27 -0
  31. package/dist/core/client_seed.d.ts.map +1 -0
  32. package/dist/core/conversation_store.d.ts +195 -0
  33. package/dist/core/conversation_store.d.ts.map +1 -0
  34. package/dist/core/create_http_agent.d.ts +57 -0
  35. package/dist/core/create_http_agent.d.ts.map +1 -0
  36. package/dist/core/define_chat.d.ts +13 -0
  37. package/dist/core/define_chat.d.ts.map +1 -0
  38. package/dist/core/events/attachments_detail.d.ts +9 -0
  39. package/dist/core/events/attachments_detail.d.ts.map +1 -0
  40. package/dist/core/events/bar_height_detail.d.ts +6 -0
  41. package/dist/core/events/bar_height_detail.d.ts.map +1 -0
  42. package/dist/core/events/custom_agent_detail.d.ts +8 -0
  43. package/dist/core/events/custom_agent_detail.d.ts.map +1 -0
  44. package/dist/core/events/dock_width_detail.d.ts +8 -0
  45. package/dist/core/events/dock_width_detail.d.ts.map +1 -0
  46. package/dist/core/events/feedback_detail.d.ts +28 -0
  47. package/dist/core/events/feedback_detail.d.ts.map +1 -0
  48. package/dist/core/events/invalidate_detail.d.ts +16 -0
  49. package/dist/core/events/invalidate_detail.d.ts.map +1 -0
  50. package/dist/core/events/maximize_detail.d.ts +5 -0
  51. package/dist/core/events/maximize_detail.d.ts.map +1 -0
  52. package/dist/core/events/run_finished_detail.d.ts +36 -0
  53. package/dist/core/events/run_finished_detail.d.ts.map +1 -0
  54. package/dist/core/events/state_detail.d.ts +5 -0
  55. package/dist/core/events/state_detail.d.ts.map +1 -0
  56. package/dist/core/events/submit_detail.d.ts +8 -0
  57. package/dist/core/events/submit_detail.d.ts.map +1 -0
  58. package/dist/core/events/thread_detail.d.ts +27 -0
  59. package/dist/core/events/thread_detail.d.ts.map +1 -0
  60. package/dist/core/events/thread_title_detail.d.ts +8 -0
  61. package/dist/core/events/thread_title_detail.d.ts.map +1 -0
  62. package/dist/core/events/toggle_detail.d.ts +5 -0
  63. package/dist/core/events/toggle_detail.d.ts.map +1 -0
  64. package/dist/core/events/tool_run.d.ts +11 -0
  65. package/dist/core/events/tool_run.d.ts.map +1 -0
  66. package/dist/core/events/unread_detail.d.ts +11 -0
  67. package/dist/core/events/unread_detail.d.ts.map +1 -0
  68. package/dist/core/message_role.d.ts +4 -0
  69. package/dist/core/message_role.d.ts.map +1 -0
  70. package/dist/core/read_flag.d.ts +11 -0
  71. package/dist/core/read_flag.d.ts.map +1 -0
  72. package/dist/core/read_json_attribute.d.ts +15 -0
  73. package/dist/core/read_json_attribute.d.ts.map +1 -0
  74. package/dist/core/read_max_tool_rounds.d.ts +9 -0
  75. package/dist/core/read_max_tool_rounds.d.ts.map +1 -0
  76. package/dist/core/remote_conversation_store.d.ts +71 -0
  77. package/dist/core/remote_conversation_store.d.ts.map +1 -0
  78. package/dist/core/run_handlers.d.ts +85 -0
  79. package/dist/core/run_handlers.d.ts.map +1 -0
  80. package/dist/core/run_index.d.ts +58 -0
  81. package/dist/core/run_index.d.ts.map +1 -0
  82. package/dist/core/storage_scope.d.ts +118 -0
  83. package/dist/core/storage_scope.d.ts.map +1 -0
  84. package/dist/core/tool_outcome.d.ts +27 -0
  85. package/dist/core/tool_outcome.d.ts.map +1 -0
  86. package/dist/core/transcribe_audio.d.ts +32 -0
  87. package/dist/core/transcribe_audio.d.ts.map +1 -0
  88. package/dist/core/upload_attachment.d.ts +44 -0
  89. package/dist/core/upload_attachment.d.ts.map +1 -0
  90. package/dist/core/utils.d.ts +117 -0
  91. package/dist/core/utils.d.ts.map +1 -0
  92. package/dist/dom/animations.d.ts +122 -0
  93. package/dist/dom/animations.d.ts.map +1 -0
  94. package/dist/dom/dom_driver.d.ts +28 -0
  95. package/dist/dom/dom_driver.d.ts.map +1 -0
  96. package/dist/dom/highlight_overlay.d.ts +39 -0
  97. package/dist/dom/highlight_overlay.d.ts.map +1 -0
  98. package/dist/dom/native_setter.d.ts +5 -0
  99. package/dist/dom/native_setter.d.ts.map +1 -0
  100. package/dist/dom/ring_color.d.ts +16 -0
  101. package/dist/dom/ring_color.d.ts.map +1 -0
  102. package/dist/dom/utils.d.ts +3 -0
  103. package/dist/dom/utils.d.ts.map +1 -0
  104. package/dist/index.d.ts +62 -0
  105. package/dist/index.d.ts.map +1 -0
  106. package/dist/index.js +223 -0
  107. package/dist/index.js.map +7 -0
  108. package/dist/skills/fill_template.d.ts +16 -0
  109. package/dist/skills/fill_template.d.ts.map +1 -0
  110. package/dist/skills/parse_skills.d.ts +8 -0
  111. package/dist/skills/parse_skills.d.ts.map +1 -0
  112. package/dist/skills/skill.d.ts +33 -0
  113. package/dist/skills/skill.d.ts.map +1 -0
  114. package/dist/skills/skill_catalog.d.ts +69 -0
  115. package/dist/skills/skill_catalog.d.ts.map +1 -0
  116. package/dist/skills/skill_name_from.d.ts +12 -0
  117. package/dist/skills/skill_name_from.d.ts.map +1 -0
  118. package/dist/tools/chat_surface_tools.d.ts +77 -0
  119. package/dist/tools/chat_surface_tools.d.ts.map +1 -0
  120. package/dist/tools/client_tool_registry.d.ts +58 -0
  121. package/dist/tools/client_tool_registry.d.ts.map +1 -0
  122. package/dist/tools/is_destructive.d.ts +14 -0
  123. package/dist/tools/is_destructive.d.ts.map +1 -0
  124. package/dist/tools/is_navigates.d.ts +9 -0
  125. package/dist/tools/is_navigates.d.ts.map +1 -0
  126. package/dist/tools/page_action_tools.d.ts +30 -0
  127. package/dist/tools/page_action_tools.d.ts.map +1 -0
  128. package/dist/tools/page_map.d.ts +16 -0
  129. package/dist/tools/page_map.d.ts.map +1 -0
  130. package/dist/tools/page_state.d.ts +27 -0
  131. package/dist/tools/page_state.d.ts.map +1 -0
  132. package/dist/tools/parse_tool_catalog.d.ts +21 -0
  133. package/dist/tools/parse_tool_catalog.d.ts.map +1 -0
  134. package/dist/tools/route_map.d.ts +37 -0
  135. package/dist/tools/route_map.d.ts.map +1 -0
  136. package/dist/tools/tool_catalog.d.ts +86 -0
  137. package/dist/tools/tool_catalog.d.ts.map +1 -0
  138. package/dist/tools/tool_dispatch.d.ts +82 -0
  139. package/dist/tools/tool_dispatch.d.ts.map +1 -0
  140. package/dist/ui/charts/chart_block.d.ts +57 -0
  141. package/dist/ui/charts/chart_block.d.ts.map +1 -0
  142. package/dist/ui/charts/chart_spec_from.d.ts +12 -0
  143. package/dist/ui/charts/chart_spec_from.d.ts.map +1 -0
  144. package/dist/ui/charts/chart_styles.d.ts +2 -0
  145. package/dist/ui/charts/chart_styles.d.ts.map +1 -0
  146. package/dist/ui/charts/chart_tool.d.ts +15 -0
  147. package/dist/ui/charts/chart_tool.d.ts.map +1 -0
  148. package/dist/ui/composer/attachment_styles.d.ts +2 -0
  149. package/dist/ui/composer/attachment_styles.d.ts.map +1 -0
  150. package/dist/ui/composer/attachment_tray.d.ts +53 -0
  151. package/dist/ui/composer/attachment_tray.d.ts.map +1 -0
  152. package/dist/ui/composer/auto_grow.d.ts +10 -0
  153. package/dist/ui/composer/auto_grow.d.ts.map +1 -0
  154. package/dist/ui/composer/build_composer.d.ts +32 -0
  155. package/dist/ui/composer/build_composer.d.ts.map +1 -0
  156. package/dist/ui/composer/composer_attachments.d.ts +57 -0
  157. package/dist/ui/composer/composer_attachments.d.ts.map +1 -0
  158. package/dist/ui/composer/composer_styles.d.ts +2 -0
  159. package/dist/ui/composer/composer_styles.d.ts.map +1 -0
  160. package/dist/ui/composer/composer_voice.d.ts +48 -0
  161. package/dist/ui/composer/composer_voice.d.ts.map +1 -0
  162. package/dist/ui/composer/skill_styles.d.ts +2 -0
  163. package/dist/ui/composer/skill_styles.d.ts.map +1 -0
  164. package/dist/ui/composer/skills_menu.d.ts +34 -0
  165. package/dist/ui/composer/skills_menu.d.ts.map +1 -0
  166. package/dist/ui/composer/voice_input.d.ts +46 -0
  167. package/dist/ui/composer/voice_input.d.ts.map +1 -0
  168. package/dist/ui/excerpts/attach_copy_buttons.d.ts +13 -0
  169. package/dist/ui/excerpts/attach_copy_buttons.d.ts.map +1 -0
  170. package/dist/ui/excerpts/copy_payload.d.ts +30 -0
  171. package/dist/ui/excerpts/copy_payload.d.ts.map +1 -0
  172. package/dist/ui/excerpts/page_quote_offer.d.ts +33 -0
  173. package/dist/ui/excerpts/page_quote_offer.d.ts.map +1 -0
  174. package/dist/ui/excerpts/place_offer.d.ts +9 -0
  175. package/dist/ui/excerpts/place_offer.d.ts.map +1 -0
  176. package/dist/ui/excerpts/quote_selection.d.ts +66 -0
  177. package/dist/ui/excerpts/quote_selection.d.ts.map +1 -0
  178. package/dist/ui/excerpts/transcript_quote_offer.d.ts +57 -0
  179. package/dist/ui/excerpts/transcript_quote_offer.d.ts.map +1 -0
  180. package/dist/ui/fill_ui_string.d.ts +32 -0
  181. package/dist/ui/fill_ui_string.d.ts.map +1 -0
  182. package/dist/ui/history/checkpoint_menu.d.ts +41 -0
  183. package/dist/ui/history/checkpoint_menu.d.ts.map +1 -0
  184. package/dist/ui/history/conversation_history.d.ts +317 -0
  185. package/dist/ui/history/conversation_history.d.ts.map +1 -0
  186. package/dist/ui/history/history_styles.d.ts +2 -0
  187. package/dist/ui/history/history_styles.d.ts.map +1 -0
  188. package/dist/ui/history/relative_time.d.ts +23 -0
  189. package/dist/ui/history/relative_time.d.ts.map +1 -0
  190. package/dist/ui/history/thread_drawer.d.ts +64 -0
  191. package/dist/ui/history/thread_drawer.d.ts.map +1 -0
  192. package/dist/ui/history/utils.d.ts +2 -0
  193. package/dist/ui/history/utils.d.ts.map +1 -0
  194. package/dist/ui/interrupts/approval_card.d.ts +64 -0
  195. package/dist/ui/interrupts/approval_card.d.ts.map +1 -0
  196. package/dist/ui/interrupts/approval_styles.d.ts +2 -0
  197. package/dist/ui/interrupts/approval_styles.d.ts.map +1 -0
  198. package/dist/ui/interrupts/confirmation_card.d.ts +51 -0
  199. package/dist/ui/interrupts/confirmation_card.d.ts.map +1 -0
  200. package/dist/ui/interrupts/confirmation_styles.d.ts +2 -0
  201. package/dist/ui/interrupts/confirmation_styles.d.ts.map +1 -0
  202. package/dist/ui/interrupts/pending_decision.d.ts +22 -0
  203. package/dist/ui/interrupts/pending_decision.d.ts.map +1 -0
  204. package/dist/ui/interrupts/question_card.d.ts +49 -0
  205. package/dist/ui/interrupts/question_card.d.ts.map +1 -0
  206. package/dist/ui/interrupts/question_styles.d.ts +2 -0
  207. package/dist/ui/interrupts/question_styles.d.ts.map +1 -0
  208. package/dist/ui/place/place.d.ts +17 -0
  209. package/dist/ui/place/place.d.ts.map +1 -0
  210. package/dist/ui/place/place_chip.d.ts +26 -0
  211. package/dist/ui/place/place_chip.d.ts.map +1 -0
  212. package/dist/ui/place/place_from_route.d.ts +9 -0
  213. package/dist/ui/place/place_from_route.d.ts.map +1 -0
  214. package/dist/ui/place/place_styles.d.ts +2 -0
  215. package/dist/ui/place/place_styles.d.ts.map +1 -0
  216. package/dist/ui/placement/clamp_launcher.d.ts +20 -0
  217. package/dist/ui/placement/clamp_launcher.d.ts.map +1 -0
  218. package/dist/ui/placement/clamp_panel.d.ts +13 -0
  219. package/dist/ui/placement/clamp_panel.d.ts.map +1 -0
  220. package/dist/ui/placement/collapse_styles.d.ts +2 -0
  221. package/dist/ui/placement/collapse_styles.d.ts.map +1 -0
  222. package/dist/ui/placement/dock_styles.d.ts +2 -0
  223. package/dist/ui/placement/dock_styles.d.ts.map +1 -0
  224. package/dist/ui/placement/embedded_styles.d.ts +2 -0
  225. package/dist/ui/placement/embedded_styles.d.ts.map +1 -0
  226. package/dist/ui/placement/launcher_drag.d.ts +49 -0
  227. package/dist/ui/placement/launcher_drag.d.ts.map +1 -0
  228. package/dist/ui/placement/launcher_placement.d.ts +77 -0
  229. package/dist/ui/placement/launcher_placement.d.ts.map +1 -0
  230. package/dist/ui/placement/launcher_styles.d.ts +2 -0
  231. package/dist/ui/placement/launcher_styles.d.ts.map +1 -0
  232. package/dist/ui/placement/page_styles.d.ts +2 -0
  233. package/dist/ui/placement/page_styles.d.ts.map +1 -0
  234. package/dist/ui/placement/panel_drag.d.ts +40 -0
  235. package/dist/ui/placement/panel_drag.d.ts.map +1 -0
  236. package/dist/ui/placement/panel_placement.d.ts +173 -0
  237. package/dist/ui/placement/panel_placement.d.ts.map +1 -0
  238. package/dist/ui/placement/place_widget.d.ts +31 -0
  239. package/dist/ui/placement/place_widget.d.ts.map +1 -0
  240. package/dist/ui/placement/preset_styles.d.ts +2 -0
  241. package/dist/ui/placement/preset_styles.d.ts.map +1 -0
  242. package/dist/ui/placement/resize_handle.d.ts +78 -0
  243. package/dist/ui/placement/resize_handle.d.ts.map +1 -0
  244. package/dist/ui/placement/resize_styles.d.ts +2 -0
  245. package/dist/ui/placement/resize_styles.d.ts.map +1 -0
  246. package/dist/ui/placement/sidebar_styles.d.ts +2 -0
  247. package/dist/ui/placement/sidebar_styles.d.ts.map +1 -0
  248. package/dist/ui/placement/small_viewport_styles.d.ts +2 -0
  249. package/dist/ui/placement/small_viewport_styles.d.ts.map +1 -0
  250. package/dist/ui/placement/surface_for.d.ts +93 -0
  251. package/dist/ui/placement/surface_for.d.ts.map +1 -0
  252. package/dist/ui/progress/prettify_tool_name.d.ts +11 -0
  253. package/dist/ui/progress/prettify_tool_name.d.ts.map +1 -0
  254. package/dist/ui/progress/run_announcer.d.ts +41 -0
  255. package/dist/ui/progress/run_announcer.d.ts.map +1 -0
  256. package/dist/ui/progress/run_notice.d.ts +21 -0
  257. package/dist/ui/progress/run_notice.d.ts.map +1 -0
  258. package/dist/ui/progress/run_notice_styles.d.ts +2 -0
  259. package/dist/ui/progress/run_notice_styles.d.ts.map +1 -0
  260. package/dist/ui/progress/subagent_panel.d.ts +101 -0
  261. package/dist/ui/progress/subagent_panel.d.ts.map +1 -0
  262. package/dist/ui/progress/subagent_progress.d.ts +57 -0
  263. package/dist/ui/progress/subagent_progress.d.ts.map +1 -0
  264. package/dist/ui/progress/subagent_update.d.ts +28 -0
  265. package/dist/ui/progress/subagent_update.d.ts.map +1 -0
  266. package/dist/ui/progress/thoughts_block.d.ts +57 -0
  267. package/dist/ui/progress/thoughts_block.d.ts.map +1 -0
  268. package/dist/ui/progress/thoughts_styles.d.ts +2 -0
  269. package/dist/ui/progress/thoughts_styles.d.ts.map +1 -0
  270. package/dist/ui/progress/tool_call_card.d.ts +167 -0
  271. package/dist/ui/progress/tool_call_card.d.ts.map +1 -0
  272. package/dist/ui/progress/tool_group_styles.d.ts +2 -0
  273. package/dist/ui/progress/tool_group_styles.d.ts.map +1 -0
  274. package/dist/ui/progress/tool_step_group.d.ts +50 -0
  275. package/dist/ui/progress/tool_step_group.d.ts.map +1 -0
  276. package/dist/ui/progress/tool_step_styles.d.ts +2 -0
  277. package/dist/ui/progress/tool_step_styles.d.ts.map +1 -0
  278. package/dist/ui/shell/adopt_styles.d.ts +28 -0
  279. package/dist/ui/shell/adopt_styles.d.ts.map +1 -0
  280. package/dist/ui/shell/build_header.d.ts +45 -0
  281. package/dist/ui/shell/build_header.d.ts.map +1 -0
  282. package/dist/ui/shell/build_launcher.d.ts +33 -0
  283. package/dist/ui/shell/build_launcher.d.ts.map +1 -0
  284. package/dist/ui/shell/collapse_glyph.d.ts +14 -0
  285. package/dist/ui/shell/collapse_glyph.d.ts.map +1 -0
  286. package/dist/ui/shell/frame_styles.d.ts +2 -0
  287. package/dist/ui/shell/frame_styles.d.ts.map +1 -0
  288. package/dist/ui/shell/glyph_slot.d.ts +7 -0
  289. package/dist/ui/shell/glyph_slot.d.ts.map +1 -0
  290. package/dist/ui/shell/header_button.d.ts +11 -0
  291. package/dist/ui/shell/header_button.d.ts.map +1 -0
  292. package/dist/ui/shell/icon_element.d.ts +12 -0
  293. package/dist/ui/shell/icon_element.d.ts.map +1 -0
  294. package/dist/ui/shell/is_unread_badge_enabled.d.ts +10 -0
  295. package/dist/ui/shell/is_unread_badge_enabled.d.ts.map +1 -0
  296. package/dist/ui/shell/read_launcher_icon_url.d.ts +8 -0
  297. package/dist/ui/shell/read_launcher_icon_url.d.ts.map +1 -0
  298. package/dist/ui/shell/theme_styles.d.ts +2 -0
  299. package/dist/ui/shell/theme_styles.d.ts.map +1 -0
  300. package/dist/ui/shell/token_styles.d.ts +2 -0
  301. package/dist/ui/shell/token_styles.d.ts.map +1 -0
  302. package/dist/ui/styles.d.ts +2 -0
  303. package/dist/ui/styles.d.ts.map +1 -0
  304. package/dist/ui/themes/classic_styles.d.ts +2 -0
  305. package/dist/ui/themes/classic_styles.d.ts.map +1 -0
  306. package/dist/ui/themes/classic_tokens.d.ts +2 -0
  307. package/dist/ui/themes/classic_tokens.d.ts.map +1 -0
  308. package/dist/ui/themes/code_tokens.d.ts +2 -0
  309. package/dist/ui/themes/code_tokens.d.ts.map +1 -0
  310. package/dist/ui/themes/ledger_styles.d.ts +2 -0
  311. package/dist/ui/themes/ledger_styles.d.ts.map +1 -0
  312. package/dist/ui/themes/ledger_tokens.d.ts +2 -0
  313. package/dist/ui/themes/ledger_tokens.d.ts.map +1 -0
  314. package/dist/ui/themes/modern_styles.d.ts +2 -0
  315. package/dist/ui/themes/modern_styles.d.ts.map +1 -0
  316. package/dist/ui/themes/quiet_styles.d.ts +2 -0
  317. package/dist/ui/themes/quiet_styles.d.ts.map +1 -0
  318. package/dist/ui/themes/scheme_styles.d.ts +2 -0
  319. package/dist/ui/themes/scheme_styles.d.ts.map +1 -0
  320. package/dist/ui/themes/soft_styles.d.ts +2 -0
  321. package/dist/ui/themes/soft_styles.d.ts.map +1 -0
  322. package/dist/ui/themes/soft_tokens.d.ts +2 -0
  323. package/dist/ui/themes/soft_tokens.d.ts.map +1 -0
  324. package/dist/ui/themes/theme_hosts.d.ts +15 -0
  325. package/dist/ui/themes/theme_hosts.d.ts.map +1 -0
  326. package/dist/ui/transcript/answer_actions.d.ts +50 -0
  327. package/dist/ui/transcript/answer_actions.d.ts.map +1 -0
  328. package/dist/ui/transcript/answer_stream.d.ts +56 -0
  329. package/dist/ui/transcript/answer_stream.d.ts.map +1 -0
  330. package/dist/ui/transcript/attachment_chip_styles.d.ts +2 -0
  331. package/dist/ui/transcript/attachment_chip_styles.d.ts.map +1 -0
  332. package/dist/ui/transcript/attachment_chips.d.ts +21 -0
  333. package/dist/ui/transcript/attachment_chips.d.ts.map +1 -0
  334. package/dist/ui/transcript/build_transcript.d.ts +21 -0
  335. package/dist/ui/transcript/build_transcript.d.ts.map +1 -0
  336. package/dist/ui/transcript/greeting_styles.d.ts +2 -0
  337. package/dist/ui/transcript/greeting_styles.d.ts.map +1 -0
  338. package/dist/ui/transcript/groups_tool_steps.d.ts +13 -0
  339. package/dist/ui/transcript/groups_tool_steps.d.ts.map +1 -0
  340. package/dist/ui/transcript/message_action_styles.d.ts +2 -0
  341. package/dist/ui/transcript/message_action_styles.d.ts.map +1 -0
  342. package/dist/ui/transcript/message_actions.d.ts +74 -0
  343. package/dist/ui/transcript/message_actions.d.ts.map +1 -0
  344. package/dist/ui/transcript/message_styles.d.ts +2 -0
  345. package/dist/ui/transcript/message_styles.d.ts.map +1 -0
  346. package/dist/ui/transcript/render_markdown.d.ts +40 -0
  347. package/dist/ui/transcript/render_markdown.d.ts.map +1 -0
  348. package/dist/ui/transcript/render_or_warn.d.ts +16 -0
  349. package/dist/ui/transcript/render_or_warn.d.ts.map +1 -0
  350. package/dist/ui/transcript/reveal_words.d.ts +8 -0
  351. package/dist/ui/transcript/reveal_words.d.ts.map +1 -0
  352. package/dist/ui/transcript/starter_chips.d.ts +21 -0
  353. package/dist/ui/transcript/starter_chips.d.ts.map +1 -0
  354. package/dist/ui/transcript/stick_to_bottom.d.ts +55 -0
  355. package/dist/ui/transcript/stick_to_bottom.d.ts.map +1 -0
  356. package/dist/ui/transcript/suggestion_chips.d.ts +29 -0
  357. package/dist/ui/transcript/suggestion_chips.d.ts.map +1 -0
  358. package/dist/ui/transcript/transcript.d.ts +196 -0
  359. package/dist/ui/transcript/transcript.d.ts.map +1 -0
  360. package/dist/ui/transcript/transcript_styles.d.ts +2 -0
  361. package/dist/ui/transcript/transcript_styles.d.ts.map +1 -0
  362. package/dist/ui/ui_strings.d.ts +352 -0
  363. package/dist/ui/ui_strings.d.ts.map +1 -0
  364. package/dist/version.d.ts +2 -0
  365. package/dist/version.d.ts.map +1 -0
  366. package/package.json +86 -3
  367. package/src/bundle.ts +2 -0
  368. package/src/chat.ts +2 -0
  369. package/src/constants.ts +650 -0
  370. package/src/core/activity_registration.ts +19 -0
  371. package/src/core/activity_registry.ts +133 -0
  372. package/src/core/activity_renderer.ts +19 -0
  373. package/src/core/ag_ui_chat.ts +3966 -0
  374. package/src/core/agui_client.ts +1028 -0
  375. package/src/core/attachment.ts +62 -0
  376. package/src/core/chat_registry.ts +53 -0
  377. package/src/core/client_seed.ts +27 -0
  378. package/src/core/conversation_store.ts +478 -0
  379. package/src/core/create_http_agent.ts +97 -0
  380. package/src/core/define_chat.ts +25 -0
  381. package/src/core/events/attachments_detail.ts +9 -0
  382. package/src/core/events/bar_height_detail.ts +5 -0
  383. package/src/core/events/custom_agent_detail.ts +7 -0
  384. package/src/core/events/dock_width_detail.ts +7 -0
  385. package/src/core/events/feedback_detail.ts +27 -0
  386. package/src/core/events/invalidate_detail.ts +15 -0
  387. package/src/core/events/maximize_detail.ts +4 -0
  388. package/src/core/events/run_finished_detail.ts +36 -0
  389. package/src/core/events/state_detail.ts +4 -0
  390. package/src/core/events/submit_detail.ts +8 -0
  391. package/src/core/events/thread_detail.ts +26 -0
  392. package/src/core/events/thread_title_detail.ts +7 -0
  393. package/src/core/events/toggle_detail.ts +4 -0
  394. package/src/core/events/tool_run.ts +10 -0
  395. package/src/core/events/unread_detail.ts +10 -0
  396. package/src/core/message_role.ts +4 -0
  397. package/src/core/read_flag.ts +13 -0
  398. package/src/core/read_json_attribute.ts +30 -0
  399. package/src/core/read_max_tool_rounds.ts +13 -0
  400. package/src/core/remote_conversation_store.ts +279 -0
  401. package/src/core/run_handlers.ts +524 -0
  402. package/src/core/run_index.ts +114 -0
  403. package/src/core/storage_scope.ts +291 -0
  404. package/src/core/tool_outcome.ts +39 -0
  405. package/src/core/transcribe_audio.ts +76 -0
  406. package/src/core/upload_attachment.ts +130 -0
  407. package/src/core/utils.ts +230 -0
  408. package/src/dom/animations.ts +341 -0
  409. package/src/dom/dom_driver.ts +91 -0
  410. package/src/dom/highlight_overlay.ts +204 -0
  411. package/src/dom/native_setter.ts +40 -0
  412. package/src/dom/ring_color.ts +206 -0
  413. package/src/dom/utils.ts +5 -0
  414. package/src/index.ts +220 -0
  415. package/src/skills/fill_template.ts +31 -0
  416. package/src/skills/parse_skills.ts +31 -0
  417. package/src/skills/skill.ts +32 -0
  418. package/src/skills/skill_catalog.ts +172 -0
  419. package/src/skills/skill_name_from.ts +19 -0
  420. package/src/tools/chat_surface_tools.ts +183 -0
  421. package/src/tools/client_tool_registry.ts +80 -0
  422. package/src/tools/is_destructive.ts +17 -0
  423. package/src/tools/is_navigates.ts +12 -0
  424. package/src/tools/page_action_tools.ts +123 -0
  425. package/src/tools/page_map.ts +26 -0
  426. package/src/tools/page_state.ts +57 -0
  427. package/src/tools/parse_tool_catalog.ts +41 -0
  428. package/src/tools/route_map.ts +132 -0
  429. package/src/tools/tool_catalog.ts +334 -0
  430. package/src/tools/tool_dispatch.ts +453 -0
  431. package/src/ui/charts/chart_block.ts +519 -0
  432. package/src/ui/charts/chart_spec_from.ts +125 -0
  433. package/src/ui/charts/chart_styles.ts +63 -0
  434. package/src/ui/charts/chart_tool.ts +72 -0
  435. package/src/ui/composer/attachment_styles.ts +71 -0
  436. package/src/ui/composer/attachment_tray.ts +295 -0
  437. package/src/ui/composer/auto_grow.ts +12 -0
  438. package/src/ui/composer/build_composer.ts +114 -0
  439. package/src/ui/composer/composer_attachments.ts +324 -0
  440. package/src/ui/composer/composer_styles.ts +177 -0
  441. package/src/ui/composer/composer_voice.ts +93 -0
  442. package/src/ui/composer/skill_styles.ts +120 -0
  443. package/src/ui/composer/skills_menu.ts +188 -0
  444. package/src/ui/composer/voice_input.ts +282 -0
  445. package/src/ui/excerpts/attach_copy_buttons.ts +73 -0
  446. package/src/ui/excerpts/copy_payload.ts +156 -0
  447. package/src/ui/excerpts/page_quote_offer.ts +203 -0
  448. package/src/ui/excerpts/place_offer.ts +24 -0
  449. package/src/ui/excerpts/quote_selection.ts +345 -0
  450. package/src/ui/excerpts/transcript_quote_offer.ts +175 -0
  451. package/src/ui/fill_ui_string.ts +41 -0
  452. package/src/ui/history/checkpoint_menu.ts +300 -0
  453. package/src/ui/history/conversation_history.ts +1169 -0
  454. package/src/ui/history/history_styles.ts +492 -0
  455. package/src/ui/history/relative_time.ts +50 -0
  456. package/src/ui/history/thread_drawer.ts +471 -0
  457. package/src/ui/history/utils.ts +11 -0
  458. package/src/ui/interrupts/approval_card.ts +202 -0
  459. package/src/ui/interrupts/approval_styles.ts +52 -0
  460. package/src/ui/interrupts/confirmation_card.ts +138 -0
  461. package/src/ui/interrupts/confirmation_styles.ts +114 -0
  462. package/src/ui/interrupts/pending_decision.ts +33 -0
  463. package/src/ui/interrupts/question_card.ts +213 -0
  464. package/src/ui/interrupts/question_styles.ts +73 -0
  465. package/src/ui/place/place.ts +16 -0
  466. package/src/ui/place/place_chip.ts +146 -0
  467. package/src/ui/place/place_from_route.ts +27 -0
  468. package/src/ui/place/place_styles.ts +108 -0
  469. package/src/ui/placement/clamp_launcher.ts +38 -0
  470. package/src/ui/placement/clamp_panel.ts +30 -0
  471. package/src/ui/placement/collapse_styles.ts +472 -0
  472. package/src/ui/placement/dock_styles.ts +116 -0
  473. package/src/ui/placement/embedded_styles.ts +14 -0
  474. package/src/ui/placement/launcher_drag.ts +206 -0
  475. package/src/ui/placement/launcher_placement.ts +133 -0
  476. package/src/ui/placement/launcher_styles.ts +189 -0
  477. package/src/ui/placement/page_styles.ts +47 -0
  478. package/src/ui/placement/panel_drag.ts +138 -0
  479. package/src/ui/placement/panel_placement.ts +1975 -0
  480. package/src/ui/placement/place_widget.ts +64 -0
  481. package/src/ui/placement/preset_styles.ts +49 -0
  482. package/src/ui/placement/resize_handle.ts +229 -0
  483. package/src/ui/placement/resize_styles.ts +201 -0
  484. package/src/ui/placement/sidebar_styles.ts +113 -0
  485. package/src/ui/placement/small_viewport_styles.ts +70 -0
  486. package/src/ui/placement/surface_for.ts +183 -0
  487. package/src/ui/progress/prettify_tool_name.ts +16 -0
  488. package/src/ui/progress/run_announcer.ts +67 -0
  489. package/src/ui/progress/run_notice.ts +61 -0
  490. package/src/ui/progress/run_notice_styles.ts +52 -0
  491. package/src/ui/progress/subagent_panel.ts +227 -0
  492. package/src/ui/progress/subagent_progress.ts +200 -0
  493. package/src/ui/progress/subagent_update.ts +89 -0
  494. package/src/ui/progress/thoughts_block.ts +165 -0
  495. package/src/ui/progress/thoughts_styles.ts +74 -0
  496. package/src/ui/progress/tool_call_card.ts +429 -0
  497. package/src/ui/progress/tool_group_styles.ts +313 -0
  498. package/src/ui/progress/tool_step_group.ts +317 -0
  499. package/src/ui/progress/tool_step_styles.ts +486 -0
  500. package/src/ui/shell/adopt_styles.ts +34 -0
  501. package/src/ui/shell/build_header.ts +121 -0
  502. package/src/ui/shell/build_launcher.ts +79 -0
  503. package/src/ui/shell/collapse_glyph.ts +42 -0
  504. package/src/ui/shell/frame_styles.ts +147 -0
  505. package/src/ui/shell/glyph_slot.ts +12 -0
  506. package/src/ui/shell/header_button.ts +22 -0
  507. package/src/ui/shell/icon_element.ts +33 -0
  508. package/src/ui/shell/is_unread_badge_enabled.ts +11 -0
  509. package/src/ui/shell/read_launcher_icon_url.ts +9 -0
  510. package/src/ui/shell/theme_styles.ts +11 -0
  511. package/src/ui/shell/token_styles.ts +480 -0
  512. package/src/ui/styles.ts +110 -0
  513. package/src/ui/themes/classic_styles.ts +32 -0
  514. package/src/ui/themes/classic_tokens.ts +51 -0
  515. package/src/ui/themes/code_tokens.ts +21 -0
  516. package/src/ui/themes/ledger_styles.ts +169 -0
  517. package/src/ui/themes/ledger_tokens.ts +33 -0
  518. package/src/ui/themes/modern_styles.ts +80 -0
  519. package/src/ui/themes/quiet_styles.ts +118 -0
  520. package/src/ui/themes/scheme_styles.ts +26 -0
  521. package/src/ui/themes/soft_styles.ts +149 -0
  522. package/src/ui/themes/soft_tokens.ts +29 -0
  523. package/src/ui/themes/theme_hosts.ts +14 -0
  524. package/src/ui/transcript/answer_actions.ts +165 -0
  525. package/src/ui/transcript/answer_stream.ts +123 -0
  526. package/src/ui/transcript/attachment_chip_styles.ts +116 -0
  527. package/src/ui/transcript/attachment_chips.ts +82 -0
  528. package/src/ui/transcript/build_transcript.ts +67 -0
  529. package/src/ui/transcript/greeting_styles.ts +171 -0
  530. package/src/ui/transcript/groups_tool_steps.ts +16 -0
  531. package/src/ui/transcript/message_action_styles.ts +155 -0
  532. package/src/ui/transcript/message_actions.ts +234 -0
  533. package/src/ui/transcript/message_styles.ts +262 -0
  534. package/src/ui/transcript/render_markdown.ts +181 -0
  535. package/src/ui/transcript/render_or_warn.ts +22 -0
  536. package/src/ui/transcript/reveal_words.ts +41 -0
  537. package/src/ui/transcript/starter_chips.ts +42 -0
  538. package/src/ui/transcript/stick_to_bottom.ts +126 -0
  539. package/src/ui/transcript/suggestion_chips.ts +73 -0
  540. package/src/ui/transcript/transcript.ts +555 -0
  541. package/src/ui/transcript/transcript_styles.ts +153 -0
  542. package/src/ui/ui_strings.ts +550 -0
  543. package/src/version.ts +1 -0
@@ -0,0 +1,120 @@
1
+ // Skills and suggestions over the composer: the chip rows and the / palette.
2
+ export const SKILL_STYLES: string = `
3
+ /* Skills — chips row + the /-command palette, above the input. */
4
+ .skill-chips {
5
+ display: flex;
6
+ flex-wrap: wrap;
7
+ gap: 6px;
8
+ padding: 10px 12px;
9
+ }
10
+
11
+ /* The row is hidden whenever the host offers no skills, which is most elements
12
+ most of the time, and an author display beats the user-agent rule for the
13
+ hidden attribute. Without this it kept its padding: 20px of panel between
14
+ the transcript and the composer, under every placement, reading as the gap
15
+ under whatever the transcript ends with. */
16
+ .skill-chips[hidden] {
17
+ display: none;
18
+ }
19
+
20
+ .skill-chip {
21
+ border: 1px solid var(--_border);
22
+ border-radius: 999px;
23
+ padding: 4px 12px;
24
+ font: inherit;
25
+ font-size: 0.9em;
26
+ cursor: pointer;
27
+ background: var(--_assistant-bg);
28
+ color: var(--_fg);
29
+ }
30
+
31
+ .skill-chip:hover {
32
+ border-color: var(--_accent);
33
+ }
34
+
35
+ /* Follow-up suggestion chips. Deliberately the skill chips' shape rather than a
36
+ second chip vocabulary -- both are "a question you could ask", and the only
37
+ difference is who chose it. Inside the transcript, so they scroll with the
38
+ answer they follow instead of hovering above the composer. */
39
+ .suggestions {
40
+ display: flex;
41
+ flex-wrap: wrap;
42
+ gap: 6px;
43
+ align-self: stretch;
44
+ }
45
+
46
+ .suggestion-chip {
47
+ border: 1px solid var(--_border);
48
+ border-radius: 999px;
49
+ padding: 4px 12px;
50
+ font: inherit;
51
+ font-size: 0.9em;
52
+ text-align: left;
53
+ cursor: pointer;
54
+ background: var(--_assistant-bg);
55
+ color: var(--_fg);
56
+ }
57
+
58
+ .suggestion-chip:hover,
59
+ .suggestion-chip:focus-visible {
60
+ border-color: var(--_accent);
61
+ }
62
+
63
+ .skill-palette {
64
+ margin: 8px 12px 0;
65
+ display: flex;
66
+ flex-direction: column;
67
+ max-height: 220px;
68
+ overflow: auto;
69
+ /* Scrolling past the end of this must not scroll the page behind it. */
70
+ overscroll-behavior: contain;
71
+ background: var(--_bg);
72
+ border: 1px solid var(--_border);
73
+ border-radius: 8px;
74
+ box-shadow: 0 8px 24px rgba(20, 20, 50, 0.16);
75
+ }
76
+
77
+ /* Closed nearly all of the time and mounted on every element. The display
78
+ above beats the user-agent rule for the hidden attribute, so without this a
79
+ closed palette still painted its margin, border and shadow as a line over
80
+ the composer. */
81
+ .skill-palette[hidden] {
82
+ display: none;
83
+ }
84
+
85
+ .skill-item {
86
+ display: flex;
87
+ flex-direction: column;
88
+ gap: 2px;
89
+ align-items: flex-start;
90
+ padding: 8px 12px;
91
+ border: none;
92
+ background: none;
93
+ font: inherit;
94
+ text-align: left;
95
+ cursor: pointer;
96
+ color: var(--_fg);
97
+ }
98
+
99
+ .skill-item[aria-selected="true"] {
100
+ background: var(--_assistant-bg);
101
+ }
102
+
103
+ .skill-item-title {
104
+ font-weight: 600;
105
+ }
106
+
107
+ .skill-item-desc {
108
+ font-size: 0.85em;
109
+ color: var(--_muted);
110
+ }
111
+
112
+ /* The hint sits directly above the composer's top border, so a zero bottom
113
+ margin left the text touching the divider. */
114
+ .skill-hint {
115
+ margin: 8px 12px;
116
+ font-size: 0.85em;
117
+ line-height: 1.4;
118
+ color: var(--_danger);
119
+ }
120
+ `;
@@ -0,0 +1,188 @@
1
+ import type { Skill } from "../../skills/skill.js";
2
+
3
+ /**
4
+ * The two skill surfaces over a single catalog: a chips row (the `chip: true`
5
+ * subset) and a slash-command palette (all skills, filtered). Both opt-in.
6
+ * Pure DOM; the host appends {@link chips} and {@link palette}, feeds input via
7
+ * {@link onInput} / {@link onKeydown}, and acts on {@link onPick}.
8
+ */
9
+ export class SkillsMenu {
10
+ /** Chips row — append above the input. Hidden unless chips are enabled and present. */
11
+ readonly chips: HTMLDivElement;
12
+ /** The `/`-command dropdown — append in the input area. Hidden until opened. */
13
+ readonly palette: HTMLDivElement;
14
+
15
+ readonly #onPick: (skill: Skill) => void;
16
+ #skills: readonly Skill[] = [];
17
+ #chipsEnabled = false;
18
+ #slashEnabled = false;
19
+ #filtered: Skill[] = [];
20
+ #activeIndex = 0;
21
+
22
+ constructor(onPick: (skill: Skill) => void) {
23
+ this.#onPick = onPick;
24
+ this.chips = document.createElement("div");
25
+ this.chips.className = "skill-chips";
26
+ this.chips.setAttribute("part", "skill-chips");
27
+ this.chips.hidden = true;
28
+ this.palette = document.createElement("div");
29
+ this.palette.className = "skill-palette";
30
+ this.palette.setAttribute("part", "skill-palette");
31
+ this.palette.setAttribute("role", "listbox");
32
+ this.palette.hidden = true;
33
+ }
34
+
35
+ /** Replace the catalog (both surfaces re-derive from it). */
36
+ setSkills(skills: readonly Skill[]): void {
37
+ this.#skills = skills;
38
+ this.#renderChips();
39
+ }
40
+
41
+ enableChips(enabled: boolean): void {
42
+ this.#chipsEnabled = enabled;
43
+ this.#renderChips();
44
+ }
45
+
46
+ enableSlash(enabled: boolean): void {
47
+ this.#slashEnabled = enabled;
48
+ }
49
+
50
+ /** Whether the palette is currently open. */
51
+ isOpen(): boolean {
52
+ return !this.palette.hidden;
53
+ }
54
+
55
+ /**
56
+ * React to an input value change: open + filter the palette when slash mode
57
+ * is on and the value starts with `/`, otherwise close it.
58
+ */
59
+ onInput(value: string): void {
60
+ if (this.#slashEnabled && value.startsWith("/")) {
61
+ this.#open(value.slice(1));
62
+ } else {
63
+ this.close();
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Handle a keydown while the palette is open. Returns ``true`` when consumed
69
+ * (the host should `preventDefault` and skip its own handling).
70
+ */
71
+ onKeydown(event: KeyboardEvent): boolean {
72
+ if (!this.isOpen()) {
73
+ return false;
74
+ }
75
+ if (event.key === "ArrowDown") {
76
+ this.#move(1);
77
+ return true;
78
+ }
79
+ if (event.key === "ArrowUp") {
80
+ this.#move(-1);
81
+ return true;
82
+ }
83
+ if (event.key === "Escape") {
84
+ this.close();
85
+ return true;
86
+ }
87
+ if (event.key === "Enter") {
88
+ // `slice(i, i+1)` yields a 0-or-1 element array; forEach gives a
89
+ // definite Skill (no indexed-access `| undefined`).
90
+ this.#filtered.slice(this.#activeIndex, this.#activeIndex + 1).forEach((skill) => {
91
+ this.#pick(skill);
92
+ });
93
+ return true;
94
+ }
95
+ return false;
96
+ }
97
+
98
+ /** Close the palette. */
99
+ close(): void {
100
+ this.palette.hidden = true;
101
+ this.palette.replaceChildren();
102
+ }
103
+
104
+ #open(query: string): void {
105
+ const needle = query.trim().toLowerCase();
106
+ const matches = this.#skills.filter(
107
+ (skill) =>
108
+ skill.name.toLowerCase().includes(needle) || skill.title.toLowerCase().includes(needle),
109
+ );
110
+ if (matches.length === 0) {
111
+ this.close();
112
+ return;
113
+ }
114
+ this.#filtered = matches;
115
+ this.#activeIndex = 0;
116
+ this.#renderPalette();
117
+ this.palette.hidden = false;
118
+ }
119
+
120
+ #move(delta: number): void {
121
+ const count = this.#filtered.length;
122
+ this.#activeIndex = (this.#activeIndex + delta + count) % count;
123
+ this.#renderPalette();
124
+ }
125
+
126
+ #pick(skill: Skill): void {
127
+ this.close();
128
+ this.#onPick(skill);
129
+ }
130
+
131
+ #renderChips(): void {
132
+ this.chips.replaceChildren();
133
+ const chipSkills = this.#chipsEnabled
134
+ ? this.#skills.filter((skill) => skill.chip === true)
135
+ : [];
136
+ this.chips.hidden = chipSkills.length === 0;
137
+ for (const skill of chipSkills) {
138
+ const button = document.createElement("button");
139
+ button.type = "button";
140
+ button.className = "skill-chip";
141
+ button.setAttribute("part", "skill-chip");
142
+ button.textContent = skill.title;
143
+ // The chip stays a label — it is a one-click affordance, not something to
144
+ // memorise — but naming the command it stands for makes the two
145
+ // discoverable as the same thing.
146
+ button.title = `/${skill.name}`;
147
+ button.addEventListener("click", () => this.#pick(skill));
148
+ this.chips.appendChild(button);
149
+ }
150
+ }
151
+
152
+ #renderPalette(): void {
153
+ this.palette.replaceChildren();
154
+ this.#filtered.forEach((skill, index) => {
155
+ const item = document.createElement("button");
156
+ item.type = "button";
157
+ item.className = "skill-item";
158
+ item.setAttribute("part", "skill-item");
159
+ item.setAttribute("role", "option");
160
+ item.setAttribute("aria-selected", index === this.#activeIndex ? "true" : "false");
161
+
162
+ const title = document.createElement("span");
163
+ title.className = "skill-item-title";
164
+ title.setAttribute("part", "skill-item-title");
165
+
166
+ // The token leads, because it is the thing the user has to type. A row
167
+ // showing only the label taught the label and not the command, so the
168
+ // palette could not be used to learn its own vocabulary.
169
+ const token = document.createElement("code");
170
+ token.className = "skill-item-token";
171
+ token.setAttribute("part", "skill-item-token");
172
+ token.textContent = `/${skill.name}`;
173
+ title.append(token, document.createTextNode(` ${skill.title}`));
174
+ item.appendChild(title);
175
+
176
+ if (skill.description !== undefined) {
177
+ const desc = document.createElement("span");
178
+ desc.className = "skill-item-desc";
179
+ desc.setAttribute("part", "skill-item-desc");
180
+ desc.textContent = skill.description;
181
+ item.appendChild(desc);
182
+ }
183
+
184
+ item.addEventListener("click", () => this.#pick(skill));
185
+ this.palette.appendChild(item);
186
+ });
187
+ }
188
+ }
@@ -0,0 +1,282 @@
1
+ import { ICON_VOICE } from "../../constants.js";
2
+ import type { TranscribeHandler } from "../../core/transcribe_audio.js";
3
+ import { fillUiString } from "../fill_ui_string.js";
4
+ import { DEFAULT_UI_STRINGS, type UiStrings } from "../ui_strings.js";
5
+
6
+ /** Lifecycle of the mic button, reflected on its `data-state` for CSS. */
7
+ type VoiceState = "idle" | "recording" | "transcribing";
8
+
9
+ /**
10
+ * How long one recording may run before it stops itself.
11
+ *
12
+ * Two minutes is a ceiling, not a target: dictating into a chat composer is a
13
+ * sentence or two, and two minutes of speech is roughly 350 words — longer than
14
+ * any message this control is for. What it bounds is the case that has no end
15
+ * at all, a mic left live in a forgotten tab: about a megabyte of accumulated
16
+ * Opus, a recording indicator the user is no longer watching, and eventually a
17
+ * multipart upload no client-side check sizes.
18
+ *
19
+ * Hitting it stops and transcribes; the audio is never discarded, because a cap
20
+ * the user was not told about must not cost them the words they already spoke.
21
+ */
22
+ const MAX_RECORDING_MS = 120_000;
23
+
24
+ /** Construction options for {@link VoiceInput}. */
25
+ export interface VoiceInputOptions {
26
+ /** Turns a recorded clip into text (the built-in or a custom transport). */
27
+ readonly transcribe: TranscribeHandler;
28
+ /** Called with the transcript when a recording transcribes successfully. */
29
+ readonly onText: (text: string) => void;
30
+ /** UI string table (labels/tooltips). */
31
+ readonly strings?: UiStrings;
32
+ }
33
+
34
+ /**
35
+ * The composer's voice-input control: a mic button that records via
36
+ * `MediaRecorder`, then POSTs the clip through a {@link TranscribeHandler} and
37
+ * hands the transcript back via `onText`.
38
+ *
39
+ * Click to start recording, which prompts for mic permission, and again to stop
40
+ * and transcribe. The button reflects its `idle` / `recording` / `transcribing`
41
+ * state on `data-state` for theming and is exposed as `part="voice-button"`; a
42
+ * capture or transcription failure returns it to idle and surfaces the message
43
+ * on its tooltip.
44
+ *
45
+ * Pure DOM. The host mounts {@link element} in the input row.
46
+ */
47
+ export class VoiceInput {
48
+ /** The mic button; mount this in the composer. */
49
+ readonly element: HTMLButtonElement;
50
+
51
+ readonly #transcribe: TranscribeHandler;
52
+ readonly #onText: (text: string) => void;
53
+ readonly #strings: UiStrings;
54
+ #state: VoiceState = "idle";
55
+ #recorder: MediaRecorder | null = null;
56
+ #stream: MediaStream | null = null;
57
+ #chunks: Blob[] = [];
58
+ #capTimer: ReturnType<typeof setTimeout> | null = null;
59
+ #hitCap = false;
60
+ #disposed = false;
61
+ /** Identifies the newest start; an older one that settles is stale. */
62
+ #startToken: object = {};
63
+
64
+ constructor(options: VoiceInputOptions) {
65
+ this.#transcribe = options.transcribe;
66
+ this.#onText = options.onText;
67
+ this.#strings = options.strings ?? DEFAULT_UI_STRINGS;
68
+
69
+ this.element = document.createElement("button");
70
+ this.element.type = "button";
71
+ this.element.className = "voice-btn";
72
+ this.element.setAttribute("part", "voice-button");
73
+ // The mic glyph, in a slot so a host can project its own mark. Author-written
74
+ // constant markup, never user or server data.
75
+ const glyph = document.createElement("slot");
76
+ glyph.name = "icon-voice";
77
+ glyph.innerHTML = ICON_VOICE;
78
+ this.element.append(glyph);
79
+ this.#setState("idle");
80
+ this.element.addEventListener("click", () => {
81
+ void this.toggle();
82
+ });
83
+ }
84
+
85
+ /** Start recording when idle, stop (and transcribe) when recording. */
86
+ async toggle(): Promise<void> {
87
+ if (this.#state === "recording") {
88
+ this.#stop();
89
+ return;
90
+ }
91
+ if (this.#state === "transcribing") {
92
+ return;
93
+ }
94
+ await this.#start();
95
+ }
96
+
97
+ async #start(): Promise<void> {
98
+ // The button reads idle for the whole of the permission prompt, so a second
99
+ // click during it is a second start. The latest click wins: this token is
100
+ // replaced by every start, and whichever one settles while it is not the
101
+ // newest is stale. It is a token and not an in-flight flag on purpose:
102
+ // getUserMedia may never settle (a dismissed prompt), and a flag waiting
103
+ // for it would leave the button dead until reload.
104
+ const token = {};
105
+ this.#startToken = token;
106
+ let stream: MediaStream | null;
107
+ try {
108
+ stream = await navigator.mediaDevices.getUserMedia({ audio: true });
109
+ } catch {
110
+ stream = null;
111
+ }
112
+ if (token !== this.#startToken || this.#disposed) {
113
+ // Superseded by a later click, or the prompt outlived the control: the
114
+ // element left, or user-key changed, while it was open. Whatever this
115
+ // call was granted is released and nothing shared is touched: #stream,
116
+ // #recorder and the cap timer belong to the newer start, and recording
117
+ // now on a disposed control would light the browser's indicator for a
118
+ // button nobody can see or stop (dispose has already run, so nothing
119
+ // else would ever release these tracks). A refusal is dropped the same
120
+ // way, so a click nobody is waiting on draws no error over the newer one.
121
+ for (const track of stream?.getTracks() ?? []) {
122
+ track.stop();
123
+ }
124
+ return;
125
+ }
126
+ if (stream === null) {
127
+ this.#fail(this.#strings.transcriptionFailed);
128
+ return;
129
+ }
130
+ this.#stream = stream;
131
+ this.#chunks = [];
132
+ this.#hitCap = false;
133
+ let recorder: MediaRecorder;
134
+ try {
135
+ recorder = new MediaRecorder(stream);
136
+ recorder.addEventListener("dataavailable", (event) => {
137
+ this.#chunks.push(event.data);
138
+ });
139
+ recorder.addEventListener("stop", () => {
140
+ void this.#finish(recorder.mimeType);
141
+ });
142
+ this.#recorder = recorder;
143
+ recorder.start();
144
+ } catch {
145
+ // A granted stream the browser will not record: one whose tracks have
146
+ // already ended (Chromium throws NotSupportedError from start), a device
147
+ // that went away between the grant and the start, or no MediaRecorder at
148
+ // all. It is a refused mic by another route, so it ends the same way: the
149
+ // tracks are released (the stream is already stored, and nothing else
150
+ // would stop it), the button shows the failure, and nothing reaches the
151
+ // click handler, which discards the promise. A recorder whose start threw
152
+ // can still report "recording" and still holds its listeners, but #fail
153
+ // clears #recorder, so neither #stop nor dispose can reach it and its stop
154
+ // listener never runs #finish.
155
+ this.#fail(this.#strings.transcriptionFailed);
156
+ return;
157
+ }
158
+ // Nothing else ends a recording: `MediaRecorder` runs until it is told to
159
+ // stop, so without this the only exits are a second click and `dispose()`.
160
+ this.#capTimer = setTimeout(() => {
161
+ this.#hitCap = true;
162
+ this.#stop();
163
+ }, MAX_RECORDING_MS);
164
+ this.#setState("recording");
165
+ }
166
+
167
+ #stop(): void {
168
+ this.#clearCap();
169
+ // ``stop`` flushes a final ``dataavailable`` then fires ``stop`` → #finish.
170
+ this.#recorder?.stop();
171
+ }
172
+
173
+ /** Drop the cap timer; recording is over, by whichever route. */
174
+ #clearCap(): void {
175
+ if (this.#capTimer !== null) {
176
+ clearTimeout(this.#capTimer);
177
+ this.#capTimer = null;
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Tear the control down, for a host element removed mid-recording or handed
183
+ * to the next principal. Stops any live `MediaRecorder`, releases the mic
184
+ * tracks so the browser's recording indicator clears, and suppresses the
185
+ * pending transcription — a disconnected control must not fire `onText` into
186
+ * a detached element.
187
+ *
188
+ * Disposal is final: nothing un-disposes an instance, and the host's next
189
+ * wiring builds a new one. So the disposed flag alone tells a continuation
190
+ * that outlived the control from a live one, and a clip already posted when this runs is
191
+ * dropped whenever its answer arrives.
192
+ */
193
+ dispose(): void {
194
+ this.#disposed = true;
195
+ this.#clearCap();
196
+ if (this.#recorder !== null && this.#recorder.state !== "inactive") {
197
+ this.#recorder.stop();
198
+ }
199
+ this.#recorder = null;
200
+ this.#releaseStream();
201
+ }
202
+
203
+ async #finish(mimeType: string): Promise<void> {
204
+ if (this.#disposed) {
205
+ return;
206
+ }
207
+ this.#releaseStream();
208
+ this.#setState("transcribing");
209
+ const audio = new Blob(this.#chunks, { type: mimeType || "audio/webm" });
210
+ try {
211
+ const text = await this.#transcribe(audio);
212
+ if (this.#disposed) {
213
+ // Disposed while the clip was in flight. The check above ran before the
214
+ // await and cannot see this: on a change of user-key the host disposes
215
+ // and wires a fresh mic over the same composer, so delivering now
216
+ // would put the previous principal's words in the next one's box.
217
+ // The button is left as it was, too: it is no longer this control's
218
+ // to change.
219
+ return;
220
+ }
221
+ this.#setState("idle");
222
+ if (this.#hitCap) {
223
+ // #setState has just reset the tooltip to the idle label, so this goes
224
+ // after it. It says why the mic went quiet on its own — the transcript
225
+ // below is the proof nothing was thrown away.
226
+ this.element.title = fillUiString(this.#strings.recordingLimit, {
227
+ n: MAX_RECORDING_MS / 60_000,
228
+ });
229
+ }
230
+ if (text !== "") {
231
+ this.#onText(text);
232
+ }
233
+ } catch (error) {
234
+ this.#fail(error instanceof Error ? error.message : this.#strings.transcriptionFailed);
235
+ } finally {
236
+ this.#recorder = null;
237
+ }
238
+ }
239
+
240
+ /** Stop the mic tracks so the browser's recording indicator clears. */
241
+ #releaseStream(): void {
242
+ for (const track of this.#stream?.getTracks() ?? []) {
243
+ track.stop();
244
+ }
245
+ this.#stream = null;
246
+ }
247
+
248
+ #fail(message: string): void {
249
+ // A transcription that fails can settle after dispose. (A refused prompt
250
+ // cannot reach here that way: #start drops a stale or disposed refusal
251
+ // before it calls this.) A disposed control has nothing to release and no
252
+ // tooltip to draw.
253
+ if (this.#disposed) {
254
+ return;
255
+ }
256
+ this.#releaseStream();
257
+ this.#recorder = null;
258
+ this.#setState("idle");
259
+ this.element.title = message;
260
+ }
261
+
262
+ #setState(state: VoiceState): void {
263
+ this.#state = state;
264
+ this.element.dataset["state"] = state;
265
+ const label = this.#labelFor(state);
266
+ this.element.title = label;
267
+ this.element.setAttribute("aria-label", label);
268
+ this.element.setAttribute("aria-pressed", String(state === "recording"));
269
+ // The control is inert while a clip transcribes (no second recording yet).
270
+ this.element.disabled = state === "transcribing";
271
+ }
272
+
273
+ #labelFor(state: VoiceState): string {
274
+ if (state === "recording") {
275
+ return this.#strings.stopRecording;
276
+ }
277
+ if (state === "transcribing") {
278
+ return this.#strings.transcribing;
279
+ }
280
+ return this.#strings.recordVoice;
281
+ }
282
+ }
@@ -0,0 +1,73 @@
1
+ import type { UiStrings } from "../ui_strings.js";
2
+
3
+ /** How long the button shows its "copied" confirmation before reverting. */
4
+ const CONFIRM_MS = 1500;
5
+
6
+ /**
7
+ * Give every fenced code block in `root` a copy button.
8
+ *
9
+ * Call only on finished bubbles: the streaming bubble reassigns its `innerHTML`
10
+ * on every delta, so a button attached mid-stream is discarded and rebuilt for
11
+ * each one.
12
+ *
13
+ * Idempotent — a bubble already processed is skipped, so a re-render or a second
14
+ * call cannot stack buttons.
15
+ */
16
+ export function attachCopyButtons(root: ParentNode, strings: UiStrings): void {
17
+ for (const pre of Array.from(root.querySelectorAll("pre"))) {
18
+ const code = pre.querySelector("code");
19
+ if (code === null || pre.querySelector(".code-copy") !== null) {
20
+ continue;
21
+ }
22
+ pre.classList.add("has-copy");
23
+ pre.append(button(code, strings));
24
+ }
25
+ }
26
+
27
+ function button(code: Element, strings: UiStrings): HTMLButtonElement {
28
+ // Never null: `textContent` is nullable only on documents and doctypes,
29
+ // and this is always the `code` element `querySelector` just returned.
30
+ const text = code.textContent as string;
31
+ const copy = document.createElement("button");
32
+ copy.type = "button";
33
+ copy.className = "code-copy";
34
+ copy.setAttribute("part", "code-copy");
35
+ copy.textContent = strings.copyCode;
36
+ copy.title = strings.copyCode;
37
+ copy.setAttribute("aria-label", strings.copyCode);
38
+ copy.addEventListener("click", () => {
39
+ void writeText(text).then((ok) => {
40
+ // Report the real outcome: a button that always claims success leaves the
41
+ // reader pasting stale clipboard content.
42
+ confirm(copy, ok ? strings.copied : strings.copyFailed, strings);
43
+ });
44
+ });
45
+ return copy;
46
+ }
47
+
48
+ /**
49
+ * Copy `text`, reporting whether it landed. The Clipboard API needs a secure
50
+ * context and a user gesture and is absent in some embeddings, so failure is an
51
+ * ordinary outcome rather than an error to throw at the host page.
52
+ */
53
+ async function writeText(text: string): Promise<boolean> {
54
+ const clipboard = navigator.clipboard;
55
+ if (clipboard === undefined) {
56
+ return false;
57
+ }
58
+ try {
59
+ await clipboard.writeText(text);
60
+ return true;
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
65
+
66
+ function confirm(copy: HTMLButtonElement, message: string, strings: UiStrings): void {
67
+ copy.textContent = message;
68
+ copy.dataset["state"] = message === strings.copied ? "copied" : "failed";
69
+ setTimeout(() => {
70
+ copy.textContent = strings.copyCode;
71
+ delete copy.dataset["state"];
72
+ }, CONFIRM_MS);
73
+ }