@ctrl-ui/react 0.8.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 (500) hide show
  1. package/README.md +70 -0
  2. package/dist/action-bar.d.ts +44 -0
  3. package/dist/action-bar.js +89 -0
  4. package/dist/activity.d.ts +53 -0
  5. package/dist/activity.js +188 -0
  6. package/dist/audio-recorder.d.ts +32 -0
  7. package/dist/audio-recorder.js +235 -0
  8. package/dist/audio-visualizer.d.ts +12 -0
  9. package/dist/audio-visualizer.js +58 -0
  10. package/dist/blocks/chat.d.ts +10 -0
  11. package/dist/blocks/chat.js +12 -0
  12. package/dist/blocks/coding-agent.d.ts +61 -0
  13. package/dist/blocks/coding-agent.js +255 -0
  14. package/dist/blocks/file-explorer-data.d.ts +57 -0
  15. package/dist/blocks/file-explorer-data.js +74 -0
  16. package/dist/blocks/file-explorer.d.ts +24 -0
  17. package/dist/blocks/file-explorer.js +427 -0
  18. package/dist/blocks/settings-data.d.ts +36 -0
  19. package/dist/blocks/settings-data.js +66 -0
  20. package/dist/blocks/settings.d.ts +19 -0
  21. package/dist/blocks/settings.js +282 -0
  22. package/dist/blocks/theme-toggle.d.ts +43 -0
  23. package/dist/blocks/theme-toggle.js +180 -0
  24. package/dist/chat-composer-attachment.d.ts +55 -0
  25. package/dist/chat-composer-attachment.js +267 -0
  26. package/dist/chat-composer-editor/extensions/mention.d.ts +14 -0
  27. package/dist/chat-composer-editor/extensions/mention.js +278 -0
  28. package/dist/chat-composer-editor/ghost.d.ts +5 -0
  29. package/dist/chat-composer-editor/ghost.js +55 -0
  30. package/dist/chat-composer-editor/schema.d.ts +6 -0
  31. package/dist/chat-composer-editor/schema.js +25 -0
  32. package/dist/chat-composer-editor/serialize.d.ts +6 -0
  33. package/dist/chat-composer-editor/serialize.js +16 -0
  34. package/dist/chat-composer-editor/types.d.ts +29 -0
  35. package/dist/chat-composer-editor/types.js +0 -0
  36. package/dist/chat-composer-editor.d.ts +9 -0
  37. package/dist/chat-composer-editor.js +154 -0
  38. package/dist/chat-composer.d.ts +60 -0
  39. package/dist/chat-composer.js +134 -0
  40. package/dist/chat-layout.d.ts +27 -0
  41. package/dist/chat-layout.js +114 -0
  42. package/dist/chat-message.d.ts +31 -0
  43. package/dist/chat-message.js +95 -0
  44. package/dist/code-block-editor.d.ts +59 -0
  45. package/dist/code-block-editor.js +250 -0
  46. package/dist/context-model.d.ts +33 -0
  47. package/dist/context-model.js +52 -0
  48. package/dist/context.d.ts +47 -0
  49. package/dist/context.js +335 -0
  50. package/dist/control-props.d.ts +28 -0
  51. package/dist/control-props.js +4 -0
  52. package/dist/control-variants.d.ts +8 -0
  53. package/dist/control-variants.js +14 -0
  54. package/dist/dynamic-notification-glass.d.ts +10 -0
  55. package/dist/dynamic-notification-glass.js +311 -0
  56. package/dist/dynamic-notification-liquid.d.ts +24 -0
  57. package/dist/dynamic-notification-liquid.js +682 -0
  58. package/dist/dynamic-notification-siri-wave.d.ts +4 -0
  59. package/dist/dynamic-notification-siri-wave.js +82 -0
  60. package/dist/dynamic-notification.d.ts +74 -0
  61. package/dist/dynamic-notification.js +342 -0
  62. package/dist/environment-variables.d.ts +124 -0
  63. package/dist/environment-variables.js +492 -0
  64. package/dist/extensions/create-track-highlight.d.ts +12 -0
  65. package/dist/extensions/create-track-highlight.js +147 -0
  66. package/dist/extensions/track-highlight.d.ts +22 -0
  67. package/dist/extensions/track-highlight.js +52 -0
  68. package/dist/hooks/use-audio-recorder.d.ts +107 -0
  69. package/dist/hooks/use-audio-recorder.js +577 -0
  70. package/dist/hooks/use-chat-composer.d.ts +56 -0
  71. package/dist/hooks/use-chat-composer.js +63 -0
  72. package/dist/hooks/use-chat-message.d.ts +28 -0
  73. package/dist/hooks/use-chat-message.js +24 -0
  74. package/dist/hooks/use-color-area.d.ts +18 -0
  75. package/dist/hooks/use-color-area.js +60 -0
  76. package/dist/hooks/use-copy-to-clipboard.d.ts +22 -0
  77. package/dist/hooks/use-copy-to-clipboard.js +90 -0
  78. package/dist/hooks/use-dropzone.d.ts +59 -0
  79. package/dist/hooks/use-dropzone.js +372 -0
  80. package/dist/hooks/use-dynamic-notification.d.ts +37 -0
  81. package/dist/hooks/use-dynamic-notification.js +87 -0
  82. package/dist/hooks/use-environment-variables.d.ts +51 -0
  83. package/dist/hooks/use-environment-variables.js +188 -0
  84. package/dist/hooks/use-mobile.d.ts +5 -0
  85. package/dist/hooks/use-mobile.js +29 -0
  86. package/dist/hooks/use-textarea-trigger-menu.d.ts +8 -0
  87. package/dist/hooks/use-textarea-trigger-menu.js +69 -0
  88. package/dist/hooks/use-trigger-menu.d.ts +49 -0
  89. package/dist/hooks/use-trigger-menu.js +102 -0
  90. package/dist/hooks/use-user-ask.d.ts +48 -0
  91. package/dist/hooks/use-user-ask.js +167 -0
  92. package/dist/inline-attachment.d.ts +37 -0
  93. package/dist/inline-attachment.js +192 -0
  94. package/dist/inline-citation.d.ts +56 -0
  95. package/dist/inline-citation.js +319 -0
  96. package/dist/knob-contracts/accordion-knobs.d.ts +5 -0
  97. package/dist/knob-contracts/accordion-knobs.js +9 -0
  98. package/dist/knob-contracts/action-bar-knobs.d.ts +5 -0
  99. package/dist/knob-contracts/action-bar-knobs.js +4 -0
  100. package/dist/knob-contracts/activity-knobs.d.ts +5 -0
  101. package/dist/knob-contracts/activity-knobs.js +9 -0
  102. package/dist/knob-contracts/alert-knobs.d.ts +5 -0
  103. package/dist/knob-contracts/alert-knobs.js +9 -0
  104. package/dist/knob-contracts/audio-recorder-knobs.d.ts +5 -0
  105. package/dist/knob-contracts/audio-recorder-knobs.js +9 -0
  106. package/dist/knob-contracts/audio-visualizer-knobs.d.ts +5 -0
  107. package/dist/knob-contracts/audio-visualizer-knobs.js +9 -0
  108. package/dist/knob-contracts/avatar-knobs.d.ts +5 -0
  109. package/dist/knob-contracts/avatar-knobs.js +9 -0
  110. package/dist/knob-contracts/badge-knobs.d.ts +5 -0
  111. package/dist/knob-contracts/badge-knobs.js +9 -0
  112. package/dist/knob-contracts/button-group-knobs.d.ts +5 -0
  113. package/dist/knob-contracts/button-group-knobs.js +9 -0
  114. package/dist/knob-contracts/button-knobs.d.ts +5 -0
  115. package/dist/knob-contracts/button-knobs.js +25 -0
  116. package/dist/knob-contracts/calendar-knobs.d.ts +5 -0
  117. package/dist/knob-contracts/calendar-knobs.js +10 -0
  118. package/dist/knob-contracts/card-knobs.d.ts +5 -0
  119. package/dist/knob-contracts/card-knobs.js +11 -0
  120. package/dist/knob-contracts/chat-composer-attachment-knobs.d.ts +5 -0
  121. package/dist/knob-contracts/chat-composer-attachment-knobs.js +11 -0
  122. package/dist/knob-contracts/chat-composer-knobs.d.ts +5 -0
  123. package/dist/knob-contracts/chat-composer-knobs.js +18 -0
  124. package/dist/knob-contracts/chat-layout-knobs.d.ts +5 -0
  125. package/dist/knob-contracts/chat-layout-knobs.js +11 -0
  126. package/dist/knob-contracts/chat-message-knobs.d.ts +5 -0
  127. package/dist/knob-contracts/chat-message-knobs.js +17 -0
  128. package/dist/knob-contracts/choice-knobs.d.ts +5 -0
  129. package/dist/knob-contracts/choice-knobs.js +9 -0
  130. package/dist/knob-contracts/code-block-editor-knobs.d.ts +5 -0
  131. package/dist/knob-contracts/code-block-editor-knobs.js +11 -0
  132. package/dist/knob-contracts/code-diff-knobs.d.ts +5 -0
  133. package/dist/knob-contracts/code-diff-knobs.js +18 -0
  134. package/dist/knob-contracts/code-knobs.d.ts +5 -0
  135. package/dist/knob-contracts/code-knobs.js +11 -0
  136. package/dist/knob-contracts/collapsible-knobs.d.ts +5 -0
  137. package/dist/knob-contracts/collapsible-knobs.js +4 -0
  138. package/dist/knob-contracts/color-picker-knobs.d.ts +5 -0
  139. package/dist/knob-contracts/color-picker-knobs.js +12 -0
  140. package/dist/knob-contracts/context-knobs.d.ts +5 -0
  141. package/dist/knob-contracts/context-knobs.js +13 -0
  142. package/dist/knob-contracts/dockable-panel-knobs.d.ts +5 -0
  143. package/dist/knob-contracts/dockable-panel-knobs.js +12 -0
  144. package/dist/knob-contracts/dropzone-knobs.d.ts +5 -0
  145. package/dist/knob-contracts/dropzone-knobs.js +11 -0
  146. package/dist/knob-contracts/dynamic-notification-knobs.d.ts +5 -0
  147. package/dist/knob-contracts/dynamic-notification-knobs.js +18 -0
  148. package/dist/knob-contracts/empty-knobs.d.ts +5 -0
  149. package/dist/knob-contracts/empty-knobs.js +12 -0
  150. package/dist/knob-contracts/environment-variables-knobs.d.ts +5 -0
  151. package/dist/knob-contracts/environment-variables-knobs.js +11 -0
  152. package/dist/knob-contracts/field-knobs.d.ts +5 -0
  153. package/dist/knob-contracts/field-knobs.js +16 -0
  154. package/dist/knob-contracts/gradient-editor-knobs.d.ts +5 -0
  155. package/dist/knob-contracts/gradient-editor-knobs.js +13 -0
  156. package/dist/knob-contracts/infinite-canvas-knobs.d.ts +5 -0
  157. package/dist/knob-contracts/infinite-canvas-knobs.js +13 -0
  158. package/dist/knob-contracts/inline-attachment-knobs.d.ts +5 -0
  159. package/dist/knob-contracts/inline-attachment-knobs.js +14 -0
  160. package/dist/knob-contracts/inline-citation-knobs.d.ts +5 -0
  161. package/dist/knob-contracts/inline-citation-knobs.js +14 -0
  162. package/dist/knob-contracts/item-knobs.d.ts +5 -0
  163. package/dist/knob-contracts/item-knobs.js +10 -0
  164. package/dist/knob-contracts/kbd-knobs.d.ts +5 -0
  165. package/dist/knob-contracts/kbd-knobs.js +10 -0
  166. package/dist/knob-contracts/markdown-block-knobs.d.ts +5 -0
  167. package/dist/knob-contracts/markdown-block-knobs.js +11 -0
  168. package/dist/knob-contracts/markdown-knobs.d.ts +5 -0
  169. package/dist/knob-contracts/markdown-knobs.js +14 -0
  170. package/dist/knob-contracts/morphing-panel-knobs.d.ts +5 -0
  171. package/dist/knob-contracts/morphing-panel-knobs.js +11 -0
  172. package/dist/knob-contracts/pagination-knobs.d.ts +5 -0
  173. package/dist/knob-contracts/pagination-knobs.js +10 -0
  174. package/dist/knob-contracts/phone-input-knobs.d.ts +5 -0
  175. package/dist/knob-contracts/phone-input-knobs.js +10 -0
  176. package/dist/knob-contracts/popup-knobs.d.ts +5 -0
  177. package/dist/knob-contracts/popup-knobs.js +21 -0
  178. package/dist/knob-contracts/range-knobs.d.ts +5 -0
  179. package/dist/knob-contracts/range-knobs.js +13 -0
  180. package/dist/knob-contracts/resizable-knobs.d.ts +5 -0
  181. package/dist/knob-contracts/resizable-knobs.js +14 -0
  182. package/dist/knob-contracts/rich-tooltip-knobs.d.ts +5 -0
  183. package/dist/knob-contracts/rich-tooltip-knobs.js +13 -0
  184. package/dist/knob-contracts/scroll-area-knobs.d.ts +5 -0
  185. package/dist/knob-contracts/scroll-area-knobs.js +8 -0
  186. package/dist/knob-contracts/separator-knobs.d.ts +5 -0
  187. package/dist/knob-contracts/separator-knobs.js +4 -0
  188. package/dist/knob-contracts/sidebar-knobs.d.ts +5 -0
  189. package/dist/knob-contracts/sidebar-knobs.js +26 -0
  190. package/dist/knob-contracts/skeleton-knobs.d.ts +5 -0
  191. package/dist/knob-contracts/skeleton-knobs.js +4 -0
  192. package/dist/knob-contracts/source-badge-knobs.d.ts +5 -0
  193. package/dist/knob-contracts/source-badge-knobs.js +11 -0
  194. package/dist/knob-contracts/spinner-knobs.d.ts +5 -0
  195. package/dist/knob-contracts/spinner-knobs.js +4 -0
  196. package/dist/knob-contracts/stepper-knobs.d.ts +5 -0
  197. package/dist/knob-contracts/stepper-knobs.js +11 -0
  198. package/dist/knob-contracts/switch-knobs.d.ts +5 -0
  199. package/dist/knob-contracts/switch-knobs.js +12 -0
  200. package/dist/knob-contracts/table-knobs.d.ts +5 -0
  201. package/dist/knob-contracts/table-knobs.js +12 -0
  202. package/dist/knob-contracts/table-of-contents-knobs.d.ts +5 -0
  203. package/dist/knob-contracts/table-of-contents-knobs.js +8 -0
  204. package/dist/knob-contracts/tabs-knobs.d.ts +5 -0
  205. package/dist/knob-contracts/tabs-knobs.js +16 -0
  206. package/dist/knob-contracts/task-list-knobs.d.ts +5 -0
  207. package/dist/knob-contracts/task-list-knobs.js +14 -0
  208. package/dist/knob-contracts/thread-rail-knobs.d.ts +5 -0
  209. package/dist/knob-contracts/thread-rail-knobs.js +14 -0
  210. package/dist/knob-contracts/timeline-knobs.d.ts +5 -0
  211. package/dist/knob-contracts/timeline-knobs.js +12 -0
  212. package/dist/knob-contracts/toolbar-knobs.d.ts +5 -0
  213. package/dist/knob-contracts/toolbar-knobs.js +20 -0
  214. package/dist/knob-contracts/track-highlight-knobs.d.ts +5 -0
  215. package/dist/knob-contracts/track-highlight-knobs.js +11 -0
  216. package/dist/knob-contracts/transcript-divider-knobs.d.ts +5 -0
  217. package/dist/knob-contracts/transcript-divider-knobs.js +10 -0
  218. package/dist/knob-contracts/tree-knobs.d.ts +5 -0
  219. package/dist/knob-contracts/tree-knobs.js +13 -0
  220. package/dist/knob-contracts/user-ask-knobs.d.ts +5 -0
  221. package/dist/knob-contracts/user-ask-knobs.js +17 -0
  222. package/dist/lib/cn.d.ts +5 -0
  223. package/dist/lib/cn.js +34 -0
  224. package/dist/lib/code-block-shiki.d.ts +9 -0
  225. package/dist/lib/code-block-shiki.js +6 -0
  226. package/dist/lib/code-tokens.d.ts +28 -0
  227. package/dist/lib/code-tokens.js +160 -0
  228. package/dist/lib/color.d.ts +72 -0
  229. package/dist/lib/color.js +490 -0
  230. package/dist/lib/contrast.d.ts +30 -0
  231. package/dist/lib/contrast.js +64 -0
  232. package/dist/lib/diff.d.ts +50 -0
  233. package/dist/lib/diff.js +315 -0
  234. package/dist/lib/dropzone-validation.d.ts +66 -0
  235. package/dist/lib/dropzone-validation.js +228 -0
  236. package/dist/lib/env-file.d.ts +28 -0
  237. package/dist/lib/env-file.js +218 -0
  238. package/dist/lib/format-audio-recorder-duration.d.ts +4 -0
  239. package/dist/lib/format-audio-recorder-duration.js +7 -0
  240. package/dist/lib/liquid-glass-optics.d.ts +4 -0
  241. package/dist/lib/liquid-glass-optics.js +96 -0
  242. package/dist/lib/phone-input-format.d.ts +6 -0
  243. package/dist/lib/phone-input-format.js +26 -0
  244. package/dist/lib/phone-number.d.ts +18 -0
  245. package/dist/lib/phone-number.js +28 -0
  246. package/dist/lib/trigger-detect.d.ts +15 -0
  247. package/dist/lib/trigger-detect.js +77 -0
  248. package/dist/markdown-block.d.ts +25 -0
  249. package/dist/markdown-block.js +77 -0
  250. package/dist/skin.config.d.ts +5 -0
  251. package/dist/skin.config.js +4 -0
  252. package/dist/skin.d.ts +69 -0
  253. package/dist/skin.js +62 -0
  254. package/dist/source-badge.d.ts +23 -0
  255. package/dist/source-badge.js +72 -0
  256. package/dist/styles/chat-composer-editor.css +44 -0
  257. package/dist/styles/code.css +57 -0
  258. package/dist/styles/effects.css +126 -0
  259. package/dist/styles/index.css +81 -0
  260. package/dist/styles/recipes/accordion.css +87 -0
  261. package/dist/styles/recipes/action-bar.css +19 -0
  262. package/dist/styles/recipes/activity.css +129 -0
  263. package/dist/styles/recipes/alert.css +75 -0
  264. package/dist/styles/recipes/audio-recorder.css +102 -0
  265. package/dist/styles/recipes/audio-visualizer.css +90 -0
  266. package/dist/styles/recipes/avatar.css +52 -0
  267. package/dist/styles/recipes/badge.css +110 -0
  268. package/dist/styles/recipes/button-group.css +64 -0
  269. package/dist/styles/recipes/button.css +305 -0
  270. package/dist/styles/recipes/calendar.css +152 -0
  271. package/dist/styles/recipes/card.css +93 -0
  272. package/dist/styles/recipes/chat-composer-attachment.css +177 -0
  273. package/dist/styles/recipes/chat-composer.css +195 -0
  274. package/dist/styles/recipes/chat-layout.css +108 -0
  275. package/dist/styles/recipes/chat-message.css +160 -0
  276. package/dist/styles/recipes/choice.css +108 -0
  277. package/dist/styles/recipes/code-block-editor.css +136 -0
  278. package/dist/styles/recipes/code-diff.css +256 -0
  279. package/dist/styles/recipes/code.css +136 -0
  280. package/dist/styles/recipes/collapsible.css +27 -0
  281. package/dist/styles/recipes/color-picker-details.css +61 -0
  282. package/dist/styles/recipes/color-picker-values.css +117 -0
  283. package/dist/styles/recipes/color-picker.css +158 -0
  284. package/dist/styles/recipes/context.css +237 -0
  285. package/dist/styles/recipes/dockable-panel.css +106 -0
  286. package/dist/styles/recipes/dropzone.css +149 -0
  287. package/dist/styles/recipes/dynamic-notification-input.css +29 -0
  288. package/dist/styles/recipes/dynamic-notification-motion.css +298 -0
  289. package/dist/styles/recipes/dynamic-notification.css +241 -0
  290. package/dist/styles/recipes/empty.css +96 -0
  291. package/dist/styles/recipes/environment-variables.css +173 -0
  292. package/dist/styles/recipes/field-parts.css +148 -0
  293. package/dist/styles/recipes/field.css +234 -0
  294. package/dist/styles/recipes/gradient-editor.css +109 -0
  295. package/dist/styles/recipes/infinite-canvas.css +78 -0
  296. package/dist/styles/recipes/inline-attachment.css +182 -0
  297. package/dist/styles/recipes/inline-citation.css +174 -0
  298. package/dist/styles/recipes/item.css +107 -0
  299. package/dist/styles/recipes/kbd.css +55 -0
  300. package/dist/styles/recipes/markdown-block.css +75 -0
  301. package/dist/styles/recipes/markdown.css +204 -0
  302. package/dist/styles/recipes/morphing-panel.css +123 -0
  303. package/dist/styles/recipes/pagination.css +86 -0
  304. package/dist/styles/recipes/phone-input.css +105 -0
  305. package/dist/styles/recipes/popup-controls.css +59 -0
  306. package/dist/styles/recipes/popup-drawer.css +162 -0
  307. package/dist/styles/recipes/popup-lists.css +198 -0
  308. package/dist/styles/recipes/popup-motion.css +94 -0
  309. package/dist/styles/recipes/popup-toast.css +126 -0
  310. package/dist/styles/recipes/popup.css +396 -0
  311. package/dist/styles/recipes/range.css +259 -0
  312. package/dist/styles/recipes/resizable.css +170 -0
  313. package/dist/styles/recipes/rich-tooltip.css +272 -0
  314. package/dist/styles/recipes/scroll-area.css +59 -0
  315. package/dist/styles/recipes/separator.css +12 -0
  316. package/dist/styles/recipes/sidebar.css +345 -0
  317. package/dist/styles/recipes/skeleton.css +23 -0
  318. package/dist/styles/recipes/source-badge.css +77 -0
  319. package/dist/styles/recipes/spinner.css +33 -0
  320. package/dist/styles/recipes/stepper.css +178 -0
  321. package/dist/styles/recipes/switch.css +149 -0
  322. package/dist/styles/recipes/table-of-contents.css +107 -0
  323. package/dist/styles/recipes/table.css +124 -0
  324. package/dist/styles/recipes/tabs.css +235 -0
  325. package/dist/styles/recipes/task-list.css +146 -0
  326. package/dist/styles/recipes/thread-rail.css +247 -0
  327. package/dist/styles/recipes/timeline.css +107 -0
  328. package/dist/styles/recipes/toolbar.css +260 -0
  329. package/dist/styles/recipes/track-highlight.css +73 -0
  330. package/dist/styles/recipes/transcript-divider.css +64 -0
  331. package/dist/styles/recipes/tree.css +137 -0
  332. package/dist/styles/recipes/user-ask-details.css +45 -0
  333. package/dist/styles/recipes/user-ask.css +204 -0
  334. package/dist/styles/skin-theme.css +137 -0
  335. package/dist/styles/skin.css +143 -0
  336. package/dist/styles/theme.css +628 -0
  337. package/dist/styles/type-scale.css +45 -0
  338. package/dist/surface-variants.d.ts +6 -0
  339. package/dist/surface-variants.js +16 -0
  340. package/dist/task-list.d.ts +34 -0
  341. package/dist/task-list.js +177 -0
  342. package/dist/thread-rail.d.ts +52 -0
  343. package/dist/thread-rail.js +190 -0
  344. package/dist/transcript-divider.d.ts +15 -0
  345. package/dist/transcript-divider.js +25 -0
  346. package/dist/ui/accordion.d.ts +31 -0
  347. package/dist/ui/accordion.js +60 -0
  348. package/dist/ui/alert-dialog.d.ts +36 -0
  349. package/dist/ui/alert-dialog.js +100 -0
  350. package/dist/ui/alert.d.ts +21 -0
  351. package/dist/ui/alert.js +36 -0
  352. package/dist/ui/aspect-ratio.d.ts +8 -0
  353. package/dist/ui/aspect-ratio.js +17 -0
  354. package/dist/ui/autocomplete.d.ts +70 -0
  355. package/dist/ui/autocomplete.js +150 -0
  356. package/dist/ui/avatar.d.ts +24 -0
  357. package/dist/ui/avatar.js +44 -0
  358. package/dist/ui/badge.d.ts +20 -0
  359. package/dist/ui/badge.js +36 -0
  360. package/dist/ui/button-group.d.ts +24 -0
  361. package/dist/ui/button-group.js +45 -0
  362. package/dist/ui/button.d.ts +36 -0
  363. package/dist/ui/button.js +104 -0
  364. package/dist/ui/calendar.d.ts +11 -0
  365. package/dist/ui/calendar.js +160 -0
  366. package/dist/ui/card.d.ts +36 -0
  367. package/dist/ui/card.js +70 -0
  368. package/dist/ui/checkbox-group.d.ts +11 -0
  369. package/dist/ui/checkbox-group.js +16 -0
  370. package/dist/ui/checkbox.d.ts +23 -0
  371. package/dist/ui/checkbox.js +58 -0
  372. package/dist/ui/code-diff.d.ts +28 -0
  373. package/dist/ui/code-diff.js +561 -0
  374. package/dist/ui/code.d.ts +62 -0
  375. package/dist/ui/code.js +292 -0
  376. package/dist/ui/collapsible.d.ts +31 -0
  377. package/dist/ui/collapsible.js +72 -0
  378. package/dist/ui/color-picker.d.ts +137 -0
  379. package/dist/ui/color-picker.js +797 -0
  380. package/dist/ui/combobox-disabled-selection.d.ts +5 -0
  381. package/dist/ui/combobox-disabled-selection.js +11 -0
  382. package/dist/ui/combobox.d.ts +70 -0
  383. package/dist/ui/combobox.js +194 -0
  384. package/dist/ui/command.d.ts +44 -0
  385. package/dist/ui/command.js +166 -0
  386. package/dist/ui/context-menu.d.ts +86 -0
  387. package/dist/ui/context-menu.js +202 -0
  388. package/dist/ui/dialog.d.ts +38 -0
  389. package/dist/ui/dialog.js +124 -0
  390. package/dist/ui/dockable-panel-geometry.d.ts +18 -0
  391. package/dist/ui/dockable-panel-geometry.js +17 -0
  392. package/dist/ui/dockable-panel.d.ts +58 -0
  393. package/dist/ui/dockable-panel.js +366 -0
  394. package/dist/ui/drawer.d.ts +42 -0
  395. package/dist/ui/drawer.js +182 -0
  396. package/dist/ui/dropdown-menu.d.ts +46 -0
  397. package/dist/ui/dropdown-menu.js +89 -0
  398. package/dist/ui/dropzone.d.ts +66 -0
  399. package/dist/ui/dropzone.js +311 -0
  400. package/dist/ui/empty.d.ts +28 -0
  401. package/dist/ui/empty.js +59 -0
  402. package/dist/ui/field.d.ts +64 -0
  403. package/dist/ui/field.js +166 -0
  404. package/dist/ui/form.d.ts +10 -0
  405. package/dist/ui/form.js +17 -0
  406. package/dist/ui/gradient-editor.d.ts +47 -0
  407. package/dist/ui/gradient-editor.js +301 -0
  408. package/dist/ui/hover-card.d.ts +25 -0
  409. package/dist/ui/hover-card.js +46 -0
  410. package/dist/ui/infinite-canvas.d.ts +40 -0
  411. package/dist/ui/infinite-canvas.js +278 -0
  412. package/dist/ui/input-group.d.ts +21 -0
  413. package/dist/ui/input-group.js +44 -0
  414. package/dist/ui/input-otp.d.ts +38 -0
  415. package/dist/ui/input-otp.js +45 -0
  416. package/dist/ui/input.d.ts +12 -0
  417. package/dist/ui/input.js +18 -0
  418. package/dist/ui/item.d.ts +46 -0
  419. package/dist/ui/item.js +110 -0
  420. package/dist/ui/kbd.d.ts +12 -0
  421. package/dist/ui/kbd.js +23 -0
  422. package/dist/ui/markdown-elements.d.ts +52 -0
  423. package/dist/ui/markdown-elements.js +211 -0
  424. package/dist/ui/markdown.d.ts +14 -0
  425. package/dist/ui/markdown.js +31 -0
  426. package/dist/ui/menubar.d.ts +74 -0
  427. package/dist/ui/menubar.js +162 -0
  428. package/dist/ui/meter.d.ts +34 -0
  429. package/dist/ui/meter.js +57 -0
  430. package/dist/ui/morphing-panel.d.ts +24 -0
  431. package/dist/ui/morphing-panel.js +87 -0
  432. package/dist/ui/native-select.d.ts +12 -0
  433. package/dist/ui/native-select.js +41 -0
  434. package/dist/ui/navigation-menu.d.ts +51 -0
  435. package/dist/ui/navigation-menu.js +135 -0
  436. package/dist/ui/number-field.d.ts +72 -0
  437. package/dist/ui/number-field.js +124 -0
  438. package/dist/ui/pagination.d.ts +26 -0
  439. package/dist/ui/pagination.js +77 -0
  440. package/dist/ui/phone-input.d.ts +30 -0
  441. package/dist/ui/phone-input.js +263 -0
  442. package/dist/ui/popover.d.ts +34 -0
  443. package/dist/ui/popover.js +87 -0
  444. package/dist/ui/progress.d.ts +34 -0
  445. package/dist/ui/progress.js +57 -0
  446. package/dist/ui/radio-group.d.ts +29 -0
  447. package/dist/ui/radio-group.js +37 -0
  448. package/dist/ui/resizable.d.ts +56 -0
  449. package/dist/ui/resizable.js +82 -0
  450. package/dist/ui/responsive-dialog.d.ts +18 -0
  451. package/dist/ui/responsive-dialog.js +121 -0
  452. package/dist/ui/rich-tooltip-tour.d.ts +12 -0
  453. package/dist/ui/rich-tooltip-tour.js +20 -0
  454. package/dist/ui/rich-tooltip.d.ts +83 -0
  455. package/dist/ui/rich-tooltip.js +331 -0
  456. package/dist/ui/scroll-area.d.ts +25 -0
  457. package/dist/ui/scroll-area.js +93 -0
  458. package/dist/ui/select.d.ts +44 -0
  459. package/dist/ui/select.js +124 -0
  460. package/dist/ui/separator.d.ts +9 -0
  461. package/dist/ui/separator.js +16 -0
  462. package/dist/ui/sheet.d.ts +25 -0
  463. package/dist/ui/sheet.js +76 -0
  464. package/dist/ui/sidebar.d.ts +88 -0
  465. package/dist/ui/sidebar.js +362 -0
  466. package/dist/ui/skeleton.d.ts +8 -0
  467. package/dist/ui/skeleton.js +13 -0
  468. package/dist/ui/slider.d.ts +24 -0
  469. package/dist/ui/slider.js +114 -0
  470. package/dist/ui/spinner.d.ts +12 -0
  471. package/dist/ui/spinner.js +26 -0
  472. package/dist/ui/stepper.d.ts +58 -0
  473. package/dist/ui/stepper.js +232 -0
  474. package/dist/ui/switch.d.ts +26 -0
  475. package/dist/ui/switch.js +71 -0
  476. package/dist/ui/table-of-contents.d.ts +22 -0
  477. package/dist/ui/table-of-contents.js +181 -0
  478. package/dist/ui/table.d.ts +37 -0
  479. package/dist/ui/table.js +83 -0
  480. package/dist/ui/tabs.d.ts +36 -0
  481. package/dist/ui/tabs.js +127 -0
  482. package/dist/ui/textarea.d.ts +9 -0
  483. package/dist/ui/textarea.js +16 -0
  484. package/dist/ui/timeline.d.ts +40 -0
  485. package/dist/ui/timeline.js +80 -0
  486. package/dist/ui/toast.d.ts +28 -0
  487. package/dist/ui/toast.js +132 -0
  488. package/dist/ui/toggle.d.ts +20 -0
  489. package/dist/ui/toggle.js +71 -0
  490. package/dist/ui/toolbar.d.ts +40 -0
  491. package/dist/ui/toolbar.js +72 -0
  492. package/dist/ui/tooltip.d.ts +27 -0
  493. package/dist/ui/tooltip.js +93 -0
  494. package/dist/ui/tree.d.ts +56 -0
  495. package/dist/ui/tree.js +438 -0
  496. package/dist/ui/trigger-menu.d.ts +46 -0
  497. package/dist/ui/trigger-menu.js +116 -0
  498. package/dist/user-ask.d.ts +50 -0
  499. package/dist/user-ask.js +398 -0
  500. package/package.json +115 -0
@@ -0,0 +1,577 @@
1
+ import { useEffect, useEffectEvent, useRef, useState, useSyncExternalStore } from "react";
2
+ //#region src/hooks/use-audio-recorder.ts
3
+ const DEFAULT_LEVEL_COUNT = 28;
4
+ const DEFAULT_BAR_DURATION_MS = 80;
5
+ const MIN_BAR_DURATION_MS = 50;
6
+ const MIME_TYPE_CANDIDATES = ["audio/webm;codecs=opus", "audio/mp4"];
7
+ const CALM_LEVEL = .1;
8
+ const LEVEL_COMPRESSION = 2.2;
9
+ const PEAK_BLEND = .35;
10
+ function preferredAudioMimeType(MediaRecorderImpl) {
11
+ if (!MediaRecorderImpl?.isTypeSupported) return void 0;
12
+ return MIME_TYPE_CANDIDATES.find((mimeType) => MediaRecorderImpl.isTypeSupported?.(mimeType));
13
+ }
14
+ const deviceIdRegex = /\(([\da-fA-F]{4}:[\da-fA-F]{4})\)$/;
15
+ function formatAudioInputDeviceLabel(device, fallback = "Microphone") {
16
+ const label = device.label.trim();
17
+ const matches = label.match(deviceIdRegex);
18
+ if (!matches) return label || fallback;
19
+ const [, id] = matches;
20
+ return `${label.replace(deviceIdRegex, "").trim() || fallback} (${id})`;
21
+ }
22
+ async function enumerateAudioInputDevices() {
23
+ if (typeof navigator === "undefined" || !navigator.mediaDevices?.enumerateDevices) throw new Error("Microphone device selection is not available in this browser.");
24
+ return (await navigator.mediaDevices.enumerateDevices()).filter((device) => device.kind === "audioinput");
25
+ }
26
+ function useAudioInputDevices() {
27
+ const [devices, setDevices] = useState([]);
28
+ const [loading, setLoading] = useState(false);
29
+ const [error, setError] = useState(null);
30
+ const [hasPermission, setHasPermission] = useState(false);
31
+ async function refresh() {
32
+ setLoading(true);
33
+ setError(null);
34
+ try {
35
+ const nextDevices = await enumerateAudioInputDevices();
36
+ setDevices(nextDevices);
37
+ if (nextDevices.some((device) => device.label)) setHasPermission(true);
38
+ } catch (nextError) {
39
+ setError(asError(nextError));
40
+ } finally {
41
+ setLoading(false);
42
+ }
43
+ }
44
+ async function requestPermission() {
45
+ if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) {
46
+ setError(/* @__PURE__ */ new Error("Microphone device selection is not available in this browser."));
47
+ return;
48
+ }
49
+ setLoading(true);
50
+ setError(null);
51
+ try {
52
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
53
+ for (const track of stream.getTracks()) track.stop();
54
+ setHasPermission(true);
55
+ setDevices(await enumerateAudioInputDevices());
56
+ } catch (nextError) {
57
+ setError(microphoneError(nextError));
58
+ } finally {
59
+ setLoading(false);
60
+ }
61
+ }
62
+ const refreshFromEffect = useEffectEvent(refresh);
63
+ useEffect(() => {
64
+ queueMicrotask(() => void refreshFromEffect());
65
+ }, []);
66
+ useEffect(() => {
67
+ if (typeof navigator === "undefined" || !navigator.mediaDevices?.addEventListener) return;
68
+ const handleDeviceChange = () => {
69
+ refreshFromEffect();
70
+ };
71
+ navigator.mediaDevices.addEventListener("devicechange", handleDeviceChange);
72
+ return () => navigator.mediaDevices.removeEventListener("devicechange", handleDeviceChange);
73
+ }, []);
74
+ return {
75
+ devices,
76
+ loading,
77
+ error,
78
+ hasPermission,
79
+ refresh,
80
+ requestPermission
81
+ };
82
+ }
83
+ function browserEnvironment() {
84
+ const scope = globalThis;
85
+ return {
86
+ mediaDevices: typeof navigator !== "undefined" ? navigator.mediaDevices : void 0,
87
+ MediaRecorder: typeof MediaRecorder !== "undefined" ? MediaRecorder : void 0,
88
+ AudioContext: typeof AudioContext !== "undefined" ? AudioContext : scope.webkitAudioContext,
89
+ requestAnimationFrame: typeof requestAnimationFrame !== "undefined" ? (callback) => requestAnimationFrame(callback) : void 0,
90
+ cancelAnimationFrame: typeof cancelAnimationFrame !== "undefined" ? (handle) => cancelAnimationFrame(handle) : void 0,
91
+ createObjectURL: typeof URL !== "undefined" ? URL.createObjectURL?.bind(URL) : void 0,
92
+ revokeObjectURL: typeof URL !== "undefined" ? URL.revokeObjectURL?.bind(URL) : void 0,
93
+ now: typeof performance !== "undefined" ? () => performance.now() : () => Date.now()
94
+ };
95
+ }
96
+ function asError(error) {
97
+ return error instanceof Error ? error : new Error(String(error));
98
+ }
99
+ function microphoneError(error) {
100
+ const nextError = asError(error);
101
+ if (nextError.name === "NotAllowedError" || nextError.name === "SecurityError") return /* @__PURE__ */ new Error("Microphone permission was denied.");
102
+ if (nextError.name === "NotFoundError" || nextError.name === "OverconstrainedError") return /* @__PURE__ */ new Error("Selected microphone is not available.");
103
+ return nextError;
104
+ }
105
+ function clampLevel(value) {
106
+ return Math.min(1, Math.max(.06, value));
107
+ }
108
+ function rmsFromSamples(samples, start, end) {
109
+ let total = 0;
110
+ let count = 0;
111
+ for (const sampleValue of samples.subarray(start, end)) {
112
+ const sample = (sampleValue - 128) / 128;
113
+ total += sample * sample;
114
+ count += 1;
115
+ }
116
+ return count === 0 ? 0 : Math.sqrt(total / count);
117
+ }
118
+ function nextAudioLevel(samples, calibration) {
119
+ const wholeSignal = rmsFromSamples(samples, 0, samples.length);
120
+ calibration.noiseFloor = wholeSignal < calibration.noiseFloor * 1.6 ? calibration.noiseFloor * .96 + wholeSignal * .04 : calibration.noiseFloor;
121
+ const signal = Math.max(0, wholeSignal - calibration.noiseFloor);
122
+ const targetGain = Math.min(5, Math.max(1.8, .36 / Math.max(signal, .08)));
123
+ calibration.gain = calibration.gain * .88 + targetGain * .12;
124
+ const compressedSignal = 1 - Math.exp(-signal * calibration.gain * LEVEL_COMPRESSION);
125
+ const target = clampLevel(CALM_LEVEL + compressedSignal * .9);
126
+ const smoothing = target > calibration.smoothedLevel ? .5 : .14;
127
+ calibration.smoothedLevel = clampLevel(calibration.smoothedLevel * (1 - smoothing) + target * smoothing);
128
+ return calibration.smoothedLevel;
129
+ }
130
+ function recordingStateCanCancel(state) {
131
+ return state === "requesting" || state === "recording" || state === "recorded" || state === "error";
132
+ }
133
+ function audioTrackConstraints(deviceId) {
134
+ const constraints = {
135
+ echoCancellation: true,
136
+ noiseSuppression: true,
137
+ autoGainControl: true
138
+ };
139
+ if (deviceId) constraints.deviceId = { exact: deviceId };
140
+ return constraints;
141
+ }
142
+ function stopMediaStream(mediaStream) {
143
+ for (const track of mediaStream.getTracks()) track.stop();
144
+ }
145
+ function createAudioRecorderController(initialOptions = {}, environment) {
146
+ return environment ? createControllerForEnvironment(initialOptions, environment) : createControllerForEnvironment(initialOptions, browserEnvironment());
147
+ }
148
+ function createControllerForEnvironment(initialOptions, environment) {
149
+ let options = { ...initialOptions };
150
+ let snapshot = {
151
+ state: "idle",
152
+ durationMs: 0,
153
+ levels: [],
154
+ error: null
155
+ };
156
+ const listeners = /* @__PURE__ */ new Set();
157
+ let stream = null;
158
+ let recorder = null;
159
+ let chunks = [];
160
+ let audioContext = null;
161
+ let audioSource = null;
162
+ let analyser = null;
163
+ let audioSamples = null;
164
+ let animationFrame = null;
165
+ let startedAt = 0;
166
+ let selectedMimeType = "";
167
+ let activeRecording;
168
+ let stopPromise = null;
169
+ let resolveStop = null;
170
+ let discardOnStop = false;
171
+ let requestId = 0;
172
+ let destroyed = false;
173
+ let bucketStartedAt = 0;
174
+ let bucketLevelTotal = 0;
175
+ let bucketPeakLevel = 0;
176
+ let bucketSampleCount = 0;
177
+ const calibration = {
178
+ noiseFloor: .018,
179
+ gain: 4,
180
+ smoothedLevel: CALM_LEVEL
181
+ };
182
+ function emit() {
183
+ for (const listener of listeners) listener();
184
+ }
185
+ function setSnapshot(next) {
186
+ snapshot = {
187
+ ...snapshot,
188
+ ...next
189
+ };
190
+ emit();
191
+ }
192
+ function now() {
193
+ return environment.now?.() ?? Date.now();
194
+ }
195
+ function resolvedBarCount() {
196
+ return Math.max(1, Math.floor(options.barCount ?? DEFAULT_LEVEL_COUNT));
197
+ }
198
+ function resolvedBarDurationMs() {
199
+ return Math.max(MIN_BAR_DURATION_MS, Math.floor(options.barDurationMs ?? DEFAULT_BAR_DURATION_MS));
200
+ }
201
+ function resetWaveform(startTime = now()) {
202
+ calibration.noiseFloor = .018;
203
+ calibration.gain = 4;
204
+ calibration.smoothedLevel = CALM_LEVEL;
205
+ bucketStartedAt = startTime;
206
+ bucketLevelTotal = 0;
207
+ bucketPeakLevel = 0;
208
+ bucketSampleCount = 0;
209
+ return [];
210
+ }
211
+ function clearActiveRecording() {
212
+ if (activeRecording?.url) environment.revokeObjectURL?.(activeRecording.url);
213
+ activeRecording = void 0;
214
+ }
215
+ function stopLiveInput() {
216
+ if (animationFrame !== null) {
217
+ environment.cancelAnimationFrame?.(animationFrame);
218
+ animationFrame = null;
219
+ }
220
+ try {
221
+ audioSource?.disconnect();
222
+ } catch {}
223
+ try {
224
+ audioContext?.close();
225
+ } catch {}
226
+ for (const track of stream?.getTracks() ?? []) track.stop();
227
+ stream = null;
228
+ audioContext = null;
229
+ audioSource = null;
230
+ analyser = null;
231
+ audioSamples = null;
232
+ }
233
+ function releaseRecorder() {
234
+ recorder = null;
235
+ chunks = [];
236
+ selectedMimeType = "";
237
+ startedAt = 0;
238
+ }
239
+ function resetToIdle() {
240
+ setSnapshot({
241
+ state: "idle",
242
+ durationMs: 0,
243
+ levels: resetWaveform(),
244
+ error: null,
245
+ recording: void 0
246
+ });
247
+ }
248
+ function finishStop(recording) {
249
+ const done = resolveStop;
250
+ resolveStop = null;
251
+ stopPromise = null;
252
+ done?.(recording);
253
+ }
254
+ function finalizeRecording() {
255
+ const discard = discardOnStop;
256
+ discardOnStop = false;
257
+ stopLiveInput();
258
+ if (discard) {
259
+ releaseRecorder();
260
+ resetToIdle();
261
+ finishStop(null);
262
+ return;
263
+ }
264
+ const stoppedAt = now();
265
+ const durationMs = Math.max(snapshot.durationMs, stoppedAt - startedAt);
266
+ const mimeType = recorder?.mimeType || selectedMimeType || "audio/webm";
267
+ const blob = new Blob(chunks, { type: mimeType });
268
+ clearActiveRecording();
269
+ activeRecording = {
270
+ blob,
271
+ url: environment.createObjectURL?.(blob) ?? "",
272
+ durationMs,
273
+ mimeType: blob.type || mimeType
274
+ };
275
+ releaseRecorder();
276
+ setSnapshot({
277
+ state: "recorded",
278
+ durationMs,
279
+ recording: activeRecording,
280
+ error: null
281
+ });
282
+ finishStop(activeRecording);
283
+ }
284
+ function scheduleFrame() {
285
+ if (!environment.requestAnimationFrame || animationFrame !== null || snapshot.state !== "recording") return;
286
+ animationFrame = environment.requestAnimationFrame(readFrame);
287
+ }
288
+ function readAudioLevel() {
289
+ if (!analyser || !audioSamples) return CALM_LEVEL;
290
+ analyser.getByteTimeDomainData(audioSamples);
291
+ return nextAudioLevel(audioSamples, calibration);
292
+ }
293
+ function collectAudioLevel() {
294
+ const level = readAudioLevel();
295
+ bucketLevelTotal += level;
296
+ bucketPeakLevel = Math.max(bucketPeakLevel, level);
297
+ bucketSampleCount += 1;
298
+ }
299
+ function flushLevelBucket(timestamp, force = false) {
300
+ if (bucketSampleCount === 0) return snapshot.levels;
301
+ if (!force && timestamp - bucketStartedAt < resolvedBarDurationMs()) return snapshot.levels;
302
+ const averageLevel = bucketLevelTotal / bucketSampleCount;
303
+ const level = clampLevel(averageLevel + (bucketPeakLevel - averageLevel) * PEAK_BLEND);
304
+ const levels = [...snapshot.levels, level].slice(-resolvedBarCount());
305
+ bucketStartedAt = timestamp;
306
+ bucketLevelTotal = 0;
307
+ bucketPeakLevel = 0;
308
+ bucketSampleCount = 0;
309
+ return levels;
310
+ }
311
+ function readFrame() {
312
+ animationFrame = null;
313
+ if (snapshot.state !== "recording") return;
314
+ const timestamp = now();
315
+ collectAudioLevel();
316
+ const levels = flushLevelBucket(timestamp);
317
+ const durationMs = Math.max(0, timestamp - startedAt);
318
+ setSnapshot({
319
+ durationMs,
320
+ levels
321
+ });
322
+ if (options.maxDurationMs && durationMs >= options.maxDurationMs) {
323
+ stop();
324
+ return;
325
+ }
326
+ scheduleFrame();
327
+ }
328
+ function attachAnalyser(nextStream) {
329
+ if (!environment.AudioContext) return;
330
+ try {
331
+ audioContext = new environment.AudioContext();
332
+ (audioContext.resume?.())?.catch(() => {});
333
+ audioSource = audioContext.createMediaStreamSource(nextStream);
334
+ analyser = audioContext.createAnalyser();
335
+ analyser.fftSize = 1024;
336
+ analyser.smoothingTimeConstant = .18;
337
+ audioSamples = new Uint8Array(analyser.fftSize);
338
+ audioSource.connect(analyser);
339
+ } catch {
340
+ audioContext = null;
341
+ audioSource = null;
342
+ analyser = null;
343
+ audioSamples = null;
344
+ }
345
+ }
346
+ function discardStaleStream(nextStream, currentRequest) {
347
+ if (!destroyed && currentRequest === requestId) return false;
348
+ stopMediaStream(nextStream);
349
+ if (currentRequest === requestId) resetToIdle();
350
+ return true;
351
+ }
352
+ function beginRecording(nextStream) {
353
+ const MediaRecorderImpl = environment.MediaRecorder;
354
+ if (!MediaRecorderImpl) throw new Error("Microphone recording is not available in this browser.");
355
+ selectedMimeType = preferredAudioMimeType(MediaRecorderImpl) ?? "";
356
+ const nextRecorder = new MediaRecorderImpl(nextStream, selectedMimeType ? { mimeType: selectedMimeType } : void 0);
357
+ chunks = [];
358
+ nextRecorder.ondataavailable = (event) => {
359
+ if (event.data.size > 0) chunks.push(event.data);
360
+ };
361
+ nextRecorder.onstop = finalizeRecording;
362
+ stream = nextStream;
363
+ recorder = nextRecorder;
364
+ attachAnalyser(nextStream);
365
+ startedAt = now();
366
+ resetWaveform(startedAt);
367
+ nextRecorder.start(Math.min(1e3, Math.max(250, resolvedBarDurationMs())));
368
+ setSnapshot({
369
+ state: "recording",
370
+ durationMs: 0,
371
+ levels: [],
372
+ error: null
373
+ });
374
+ scheduleFrame();
375
+ }
376
+ async function start() {
377
+ if (options.disabled || snapshot.state === "requesting" || snapshot.state === "recording") return false;
378
+ clearActiveRecording();
379
+ requestId += 1;
380
+ const currentRequest = requestId;
381
+ setSnapshot({
382
+ state: "requesting",
383
+ durationMs: 0,
384
+ levels: resetWaveform(),
385
+ error: null,
386
+ recording: void 0
387
+ });
388
+ if (!environment.mediaDevices?.getUserMedia || !environment.MediaRecorder) {
389
+ setSnapshot({
390
+ state: "error",
391
+ error: /* @__PURE__ */ new Error("Microphone recording is not available in this browser.")
392
+ });
393
+ return false;
394
+ }
395
+ try {
396
+ const nextStream = await environment.mediaDevices.getUserMedia({ audio: audioTrackConstraints(options.deviceId) });
397
+ if (discardStaleStream(nextStream, currentRequest)) return false;
398
+ beginRecording(nextStream);
399
+ return true;
400
+ } catch (error) {
401
+ stopLiveInput();
402
+ releaseRecorder();
403
+ setSnapshot({
404
+ state: "error",
405
+ error: microphoneError(error),
406
+ recording: void 0
407
+ });
408
+ return false;
409
+ }
410
+ }
411
+ function stop() {
412
+ if (snapshot.state === "recorded") return Promise.resolve(snapshot.recording ?? null);
413
+ if (!recorder || recorder.state === "inactive") return Promise.resolve(null);
414
+ if (stopPromise) return stopPromise;
415
+ if (snapshot.state === "recording") {
416
+ const stoppedAt = now();
417
+ collectAudioLevel();
418
+ setSnapshot({
419
+ durationMs: Math.max(snapshot.durationMs, stoppedAt - startedAt),
420
+ levels: flushLevelBucket(stoppedAt, true)
421
+ });
422
+ }
423
+ discardOnStop = false;
424
+ stopPromise = new Promise((resolve) => {
425
+ resolveStop = resolve;
426
+ recorder?.stop();
427
+ });
428
+ return stopPromise;
429
+ }
430
+ function cancelInternal(notify) {
431
+ const shouldNotify = notify && recordingStateCanCancel(snapshot.state);
432
+ requestId += 1;
433
+ clearActiveRecording();
434
+ if (recorder && recorder.state !== "inactive") {
435
+ discardOnStop = true;
436
+ if (!stopPromise) stopPromise = new Promise((resolve) => {
437
+ resolveStop = resolve;
438
+ });
439
+ stopLiveInput();
440
+ recorder.stop();
441
+ } else {
442
+ stopLiveInput();
443
+ releaseRecorder();
444
+ resetToIdle();
445
+ }
446
+ if (shouldNotify) options.onCancel?.();
447
+ }
448
+ function cancel() {
449
+ cancelInternal(true);
450
+ }
451
+ async function submit() {
452
+ if (snapshot.state === "recording") {
453
+ const recording = await stop();
454
+ return recording ? submitRecording(recording) : null;
455
+ }
456
+ const recording = snapshot.recording;
457
+ if (snapshot.state !== "recorded" || !recording) return null;
458
+ return submitRecording(recording);
459
+ }
460
+ async function submitRecording(recording) {
461
+ setSnapshot({
462
+ state: "submitting",
463
+ error: null
464
+ });
465
+ try {
466
+ await options.onRecordingComplete?.(recording);
467
+ setSnapshot({
468
+ state: "idle",
469
+ durationMs: 0,
470
+ levels: resetWaveform(),
471
+ error: null,
472
+ recording: void 0
473
+ });
474
+ return recording;
475
+ } catch (error) {
476
+ setSnapshot({
477
+ state: "recorded",
478
+ error: asError(error),
479
+ recording
480
+ });
481
+ return null;
482
+ }
483
+ }
484
+ function destroy() {
485
+ destroyed = true;
486
+ cancelInternal(false);
487
+ clearActiveRecording();
488
+ listeners.clear();
489
+ }
490
+ function setOptions(nextOptions) {
491
+ const nextBarCount = nextOptions.barCount ?? DEFAULT_LEVEL_COUNT;
492
+ const currentBarCount = options.barCount ?? DEFAULT_LEVEL_COUNT;
493
+ options = { ...nextOptions };
494
+ if (nextBarCount !== currentBarCount && snapshot.state === "idle") setSnapshot({ levels: resetWaveform() });
495
+ if (options.disabled && (snapshot.state === "requesting" || snapshot.state === "recording")) cancelInternal(false);
496
+ }
497
+ function getResult() {
498
+ const isDisabled = Boolean(options.disabled);
499
+ return {
500
+ ...snapshot,
501
+ isDisabled,
502
+ canStart: !isDisabled && (snapshot.state === "idle" || snapshot.state === "error"),
503
+ canCancel: recordingStateCanCancel(snapshot.state),
504
+ canSubmit: snapshot.state === "recording" || snapshot.state === "recorded",
505
+ start,
506
+ stop,
507
+ cancel,
508
+ submit
509
+ };
510
+ }
511
+ return {
512
+ getSnapshot: () => snapshot,
513
+ getResult,
514
+ setOptions,
515
+ subscribe(listener) {
516
+ listeners.add(listener);
517
+ return () => {
518
+ listeners.delete(listener);
519
+ };
520
+ },
521
+ isDestroyed: () => destroyed,
522
+ start,
523
+ stop,
524
+ cancel,
525
+ submit,
526
+ destroy
527
+ };
528
+ }
529
+ function useAudioRecorder(options = {}) {
530
+ const [controller] = useState(() => createAudioRecorderController(options));
531
+ const snapshot = useSyncExternalStore(controller.subscribe, controller.getSnapshot, controller.getSnapshot);
532
+ const activeMount = useRef(null);
533
+ const { onRecordingComplete, onCancel, maxDurationMs, disabled, barCount, barDurationMs, deviceId } = options;
534
+ useEffect(() => {
535
+ const mount = Symbol("audio-recorder-mount");
536
+ activeMount.current = mount;
537
+ return () => {
538
+ queueMicrotask(() => {
539
+ if (activeMount.current === mount) controller.destroy();
540
+ });
541
+ };
542
+ }, [controller]);
543
+ useEffect(() => {
544
+ controller.setOptions({
545
+ onRecordingComplete,
546
+ onCancel,
547
+ maxDurationMs,
548
+ disabled,
549
+ barCount,
550
+ barDurationMs,
551
+ deviceId
552
+ });
553
+ }, [
554
+ controller,
555
+ onRecordingComplete,
556
+ onCancel,
557
+ maxDurationMs,
558
+ disabled,
559
+ barCount,
560
+ barDurationMs,
561
+ deviceId
562
+ ]);
563
+ const isDisabled = Boolean(disabled);
564
+ return {
565
+ ...snapshot,
566
+ isDisabled,
567
+ canStart: !isDisabled && (snapshot.state === "idle" || snapshot.state === "error"),
568
+ canCancel: recordingStateCanCancel(snapshot.state),
569
+ canSubmit: snapshot.state === "recording" || snapshot.state === "recorded",
570
+ start: controller.start,
571
+ stop: controller.stop,
572
+ cancel: controller.cancel,
573
+ submit: controller.submit
574
+ };
575
+ }
576
+ //#endregion
577
+ export { createAudioRecorderController, formatAudioInputDeviceLabel, preferredAudioMimeType, useAudioInputDevices, useAudioRecorder };
@@ -0,0 +1,56 @@
1
+ import { FormSubmitEvent } from "../control-props.js";
2
+ import { ChatComposerKnobStyle } from "../knob-contracts/chat-composer-knobs.js";
3
+ import { ChatDensity } from "./use-chat-message.js";
4
+ import { CSSProperties, ComponentProps, ReactNode } from "react";
5
+ //#region src/hooks/use-chat-composer.d.ts
6
+ type ChatComposerSubmitPayload = {
7
+ value: string;
8
+ clear: () => void;
9
+ mentions?: MentionItem[];
10
+ };
11
+ type ChatComposerProps = Omit<ComponentProps<"form">, "onSubmit" | "style"> & {
12
+ children?: ReactNode;
13
+ value?: string;
14
+ defaultValue?: string;
15
+ onValueChange?: (value: string) => void;
16
+ onSubmit?: (payload: ChatComposerSubmitPayload) => void | Promise<void>;
17
+ state?: "idle" | "submitting" | "disabled";
18
+ density?: ChatDensity;
19
+ disabled?: boolean;
20
+ style?: CSSProperties & ChatComposerKnobStyle;
21
+ };
22
+ type MentionItem = {
23
+ id: string;
24
+ label: string;
25
+ kind: string;
26
+ };
27
+ type ComposerKeyEvent = {
28
+ key: string;
29
+ shiftKey: boolean;
30
+ defaultPrevented: boolean;
31
+ nativeEvent: {
32
+ isComposing: boolean;
33
+ };
34
+ preventDefault: () => void;
35
+ };
36
+ declare function useChatComposer({ value, defaultValue, onValueChange, onSubmit, state, density, disabled, trackSends }: Pick<ChatComposerProps, "value" | "defaultValue" | "onValueChange" | "onSubmit" | "state" | "density" | "disabled"> & {
37
+ /** Count successful submits — only enabled when something reads counter (send-layer anchor), so idle apps pay no extra state update. */
38
+ trackSends?: boolean;
39
+ }): {
40
+ value: string;
41
+ setValue: (nextValue: string) => void;
42
+ normalizedValue: string;
43
+ state: "disabled" | "idle" | "submitting";
44
+ density: ChatDensity;
45
+ isCompact: boolean;
46
+ isDisabled: boolean;
47
+ canSubmit: boolean;
48
+ rows: number;
49
+ sendCount: number;
50
+ clear: () => void;
51
+ submit: (extra?: Partial<ChatComposerSubmitPayload>) => void;
52
+ handleSubmit: (event: FormSubmitEvent) => void;
53
+ handleKeyDown: (event: ComposerKeyEvent) => void;
54
+ };
55
+ //#endregion
56
+ export { ChatComposerProps, ChatComposerSubmitPayload, MentionItem, useChatComposer };
@@ -0,0 +1,63 @@
1
+ import { useState } from "react";
2
+ //#region src/hooks/use-chat-composer.ts
3
+ function useControllableText({ value, defaultValue = "", onValueChange }) {
4
+ const [internalValue, setInternalValue] = useState(defaultValue);
5
+ const isControlled = value !== void 0;
6
+ const currentValue = isControlled ? value : internalValue;
7
+ function setValue(nextValue) {
8
+ if (!isControlled) setInternalValue(nextValue);
9
+ onValueChange?.(nextValue);
10
+ }
11
+ return [currentValue, setValue];
12
+ }
13
+ function useChatComposer({ value, defaultValue, onValueChange, onSubmit, state = "idle", density = "comfortable", disabled = false, trackSends = false }) {
14
+ const [inputValue, setInputValue] = useControllableText({
15
+ value,
16
+ defaultValue,
17
+ onValueChange
18
+ });
19
+ const [sendCount, setSendCount] = useState(0);
20
+ const normalizedValue = inputValue.trim();
21
+ const isDisabled = disabled || state === "disabled" || state === "submitting";
22
+ const canSubmit = normalizedValue.length > 0 && !isDisabled;
23
+ const isCompact = density === "compact";
24
+ function clear() {
25
+ setInputValue("");
26
+ }
27
+ function submit(extra) {
28
+ if (!canSubmit) return;
29
+ if (trackSends) setSendCount((count) => count + 1);
30
+ Promise.resolve(onSubmit?.({
31
+ value: normalizedValue,
32
+ clear,
33
+ ...extra
34
+ })).catch(reportError);
35
+ }
36
+ function handleSubmit(event) {
37
+ event.preventDefault();
38
+ submit();
39
+ }
40
+ function handleKeyDown(event) {
41
+ if (event.defaultPrevented || event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing) return;
42
+ event.preventDefault();
43
+ submit();
44
+ }
45
+ return {
46
+ value: inputValue,
47
+ setValue: setInputValue,
48
+ normalizedValue,
49
+ state,
50
+ density,
51
+ isCompact,
52
+ isDisabled,
53
+ canSubmit,
54
+ rows: isCompact ? 2 : 4,
55
+ sendCount,
56
+ clear,
57
+ submit,
58
+ handleSubmit,
59
+ handleKeyDown
60
+ };
61
+ }
62
+ //#endregion
63
+ export { useChatComposer };