@ctrl-ui/react 0.8.6 → 0.8.8

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 (376) hide show
  1. package/dist/action-bar.d.ts +15 -7
  2. package/dist/action-bar.js +39 -19
  3. package/dist/activity.js +7 -15
  4. package/dist/audio-recorder.d.ts +14 -4
  5. package/dist/audio-recorder.js +82 -29
  6. package/dist/audio-visualizer.js +1 -1
  7. package/dist/blocks/coding-agent.js +54 -57
  8. package/dist/blocks/design-canvas-data.d.ts +32 -0
  9. package/dist/blocks/design-canvas-data.js +100 -0
  10. package/dist/blocks/design-canvas-inspector.d.ts +8 -0
  11. package/dist/blocks/design-canvas-inspector.js +238 -0
  12. package/dist/blocks/design-canvas.d.ts +21 -0
  13. package/dist/blocks/design-canvas.js +452 -0
  14. package/dist/blocks/discussion.d.ts +28 -0
  15. package/dist/blocks/discussion.js +67 -0
  16. package/dist/blocks/file-explorer-data.d.ts +1 -0
  17. package/dist/blocks/file-explorer-data.js +4 -1
  18. package/dist/blocks/file-explorer.d.ts +9 -1
  19. package/dist/blocks/file-explorer.js +461 -192
  20. package/dist/blocks/settings.d.ts +2 -1
  21. package/dist/blocks/settings.js +101 -79
  22. package/dist/blocks/team-chat.d.ts +12 -0
  23. package/dist/blocks/team-chat.js +26 -0
  24. package/dist/blocks/theme-toggle.js +23 -31
  25. package/dist/chat-composer-attachment.d.ts +12 -3
  26. package/dist/chat-composer-attachment.js +61 -19
  27. package/dist/chat-composer-editor/extensions/mention.d.ts +5 -1
  28. package/dist/chat-composer-editor/extensions/mention.js +58 -27
  29. package/dist/chat-composer-editor/ghost.js +2 -4
  30. package/dist/chat-composer-editor/types.d.ts +5 -0
  31. package/dist/chat-composer-editor.d.ts +1 -1
  32. package/dist/chat-composer-editor.js +71 -28
  33. package/dist/chat-composer.d.ts +13 -32
  34. package/dist/chat-composer.js +54 -13
  35. package/dist/chat-layout.d.ts +19 -0
  36. package/dist/chat-layout.js +96 -32
  37. package/dist/chat-message/social.d.ts +26 -0
  38. package/dist/chat-message/social.js +122 -0
  39. package/dist/chat-message.d.ts +28 -29
  40. package/dist/chat-message.js +106 -25
  41. package/dist/context.d.ts +2 -2
  42. package/dist/context.js +38 -23
  43. package/dist/dynamic-notification-canvas-loop.d.ts +24 -0
  44. package/dist/dynamic-notification-canvas-loop.js +113 -0
  45. package/dist/dynamic-notification-glass.js +23 -114
  46. package/dist/dynamic-notification-liquid.js +14 -118
  47. package/dist/dynamic-notification.d.ts +16 -8
  48. package/dist/dynamic-notification.js +85 -22
  49. package/dist/email/email-brand.d.ts +7 -4
  50. package/dist/email/email-brand.js +9 -6
  51. package/dist/email/email-code.d.ts +3 -2
  52. package/dist/email/email-code.js +11 -9
  53. package/dist/email/email-footer.d.ts +5 -3
  54. package/dist/email/email-footer.js +11 -5
  55. package/dist/email/email.d.ts +31 -9
  56. package/dist/email/email.js +50 -26
  57. package/dist/email/templates.d.ts +10 -9
  58. package/dist/email/templates.js +102 -38
  59. package/dist/email/theme.js +1 -1
  60. package/dist/environment-variables.d.ts +4 -2
  61. package/dist/environment-variables.js +52 -32
  62. package/dist/filter-bar/context.d.ts +32 -0
  63. package/dist/filter-bar/context.js +11 -0
  64. package/dist/filter-bar/editor.d.ts +14 -0
  65. package/dist/filter-bar/editor.js +216 -0
  66. package/dist/filter-bar/model.d.ts +10 -0
  67. package/dist/filter-bar/model.js +57 -0
  68. package/dist/filter-bar/motion.d.ts +10 -0
  69. package/dist/filter-bar/motion.js +84 -0
  70. package/dist/filter-bar/parts.d.ts +19 -0
  71. package/dist/filter-bar/parts.js +244 -0
  72. package/dist/filter-bar/types.d.ts +54 -0
  73. package/dist/filter-bar/types.js +0 -0
  74. package/dist/filter-bar.d.ts +27 -0
  75. package/dist/filter-bar.js +310 -0
  76. package/dist/hooks/use-audio-recorder.js +12 -6
  77. package/dist/hooks/use-chat-composer.d.ts +21 -7
  78. package/dist/hooks/use-chat-composer.js +32 -5
  79. package/dist/hooks/use-chat-message.d.ts +28 -2
  80. package/dist/hooks/use-chat-message.js +41 -10
  81. package/dist/hooks/use-chat-thread-scroll.js +43 -7
  82. package/dist/hooks/use-color-area.d.ts +3 -3
  83. package/dist/hooks/use-color-area.js +13 -12
  84. package/dist/hooks/use-copy-to-clipboard.d.ts +2 -1
  85. package/dist/hooks/use-copy-to-clipboard.js +20 -20
  86. package/dist/hooks/use-dropzone.d.ts +7 -4
  87. package/dist/hooks/use-dropzone.js +34 -3
  88. package/dist/hooks/use-dynamic-notification.d.ts +6 -2
  89. package/dist/hooks/use-dynamic-notification.js +26 -10
  90. package/dist/hooks/use-mobile.d.ts +3 -2
  91. package/dist/hooks/use-mobile.js +8 -4
  92. package/dist/hooks/use-textarea-trigger-menu.js +2 -1
  93. package/dist/hooks/use-trigger-menu.d.ts +54 -0
  94. package/dist/hooks/use-trigger-menu.js +102 -47
  95. package/dist/hooks/use-user-ask.d.ts +2 -0
  96. package/dist/hooks/use-user-ask.js +35 -41
  97. package/dist/inline-attachment.d.ts +6 -2
  98. package/dist/inline-attachment.js +44 -29
  99. package/dist/inline-citation.d.ts +12 -5
  100. package/dist/inline-citation.js +25 -16
  101. package/dist/knob-contracts/audio-recorder-knobs.d.ts +1 -1
  102. package/dist/knob-contracts/audio-recorder-knobs.js +2 -1
  103. package/dist/knob-contracts/audio-visualizer-knobs.d.ts +1 -1
  104. package/dist/knob-contracts/audio-visualizer-knobs.js +2 -1
  105. package/dist/knob-contracts/avatar-knobs.d.ts +1 -1
  106. package/dist/knob-contracts/avatar-knobs.js +6 -1
  107. package/dist/knob-contracts/badge-knobs.d.ts +1 -1
  108. package/dist/knob-contracts/badge-knobs.js +2 -1
  109. package/dist/knob-contracts/breadcrumb-knobs.d.ts +4 -0
  110. package/dist/knob-contracts/breadcrumb-knobs.js +8 -0
  111. package/dist/knob-contracts/button-knobs.d.ts +1 -1
  112. package/dist/knob-contracts/button-knobs.js +1 -0
  113. package/dist/knob-contracts/chat-layout-knobs.d.ts +1 -1
  114. package/dist/knob-contracts/chat-layout-knobs.js +2 -1
  115. package/dist/knob-contracts/chat-message-knobs.d.ts +1 -1
  116. package/dist/knob-contracts/chat-message-knobs.js +8 -1
  117. package/dist/knob-contracts/choice-knobs.d.ts +1 -1
  118. package/dist/knob-contracts/choice-knobs.js +1 -0
  119. package/dist/knob-contracts/code-diff-knobs.d.ts +1 -1
  120. package/dist/knob-contracts/code-diff-knobs.js +2 -0
  121. package/dist/knob-contracts/code-knobs.d.ts +1 -1
  122. package/dist/knob-contracts/code-knobs.js +2 -1
  123. package/dist/knob-contracts/collapsible-knobs.d.ts +1 -1
  124. package/dist/knob-contracts/collapsible-knobs.js +1 -1
  125. package/dist/knob-contracts/dynamic-notification-knobs.d.ts +1 -1
  126. package/dist/knob-contracts/dynamic-notification-knobs.js +2 -0
  127. package/dist/knob-contracts/emoji-picker-knobs.d.ts +4 -0
  128. package/dist/knob-contracts/emoji-picker-knobs.js +10 -0
  129. package/dist/knob-contracts/field-knobs.d.ts +1 -1
  130. package/dist/knob-contracts/field-knobs.js +5 -1
  131. package/dist/knob-contracts/filter-bar-knobs.d.ts +4 -0
  132. package/dist/knob-contracts/filter-bar-knobs.js +15 -0
  133. package/dist/knob-contracts/infinite-canvas-knobs.d.ts +1 -1
  134. package/dist/knob-contracts/infinite-canvas-knobs.js +3 -0
  135. package/dist/knob-contracts/markdown-knobs.d.ts +1 -1
  136. package/dist/knob-contracts/markdown-knobs.js +3 -1
  137. package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
  138. package/dist/knob-contracts/page-layout-knobs.js +4 -1
  139. package/dist/knob-contracts/popup-knobs.d.ts +1 -1
  140. package/dist/knob-contracts/popup-knobs.js +28 -0
  141. package/dist/knob-contracts/range-knobs.d.ts +1 -1
  142. package/dist/knob-contracts/range-knobs.js +1 -0
  143. package/dist/knob-contracts/rich-tooltip-knobs.d.ts +1 -1
  144. package/dist/knob-contracts/rich-tooltip-knobs.js +2 -1
  145. package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
  146. package/dist/knob-contracts/sidebar-knobs.js +27 -2
  147. package/dist/knob-contracts/spinner-knobs.d.ts +1 -1
  148. package/dist/knob-contracts/spinner-knobs.js +1 -1
  149. package/dist/knob-contracts/stepper-knobs.d.ts +1 -1
  150. package/dist/knob-contracts/stepper-knobs.js +4 -1
  151. package/dist/knob-contracts/switch-knobs.d.ts +1 -1
  152. package/dist/knob-contracts/switch-knobs.js +5 -0
  153. package/dist/knob-contracts/table-of-contents-knobs.d.ts +1 -1
  154. package/dist/knob-contracts/table-of-contents-knobs.js +4 -5
  155. package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
  156. package/dist/knob-contracts/tabs-knobs.js +1 -0
  157. package/dist/knob-contracts/tree-knobs.d.ts +1 -1
  158. package/dist/knob-contracts/tree-knobs.js +2 -1
  159. package/dist/lib/cn.js +0 -4
  160. package/dist/lib/contrast.d.ts +1 -0
  161. package/dist/lib/contrast.js +17 -5
  162. package/dist/lib/dropzone-validation.js +41 -28
  163. package/dist/lib/env-file.d.ts +1 -1
  164. package/dist/lib/env-file.js +5 -5
  165. package/dist/lib/motion.d.ts +4 -0
  166. package/dist/lib/motion.js +15 -0
  167. package/dist/lib/trigger-detect.js +6 -8
  168. package/dist/markdown-block.js +13 -8
  169. package/dist/popup-parts.d.ts +8 -0
  170. package/dist/popup-parts.js +43 -0
  171. package/dist/scripts/contrast-eval.js +5 -4
  172. package/dist/source-badge.js +3 -1
  173. package/dist/styles/code.css +0 -2
  174. package/dist/styles/effects.css +6 -32
  175. package/dist/styles/index.css +14 -8
  176. package/dist/styles/recipes/accordion.css +9 -6
  177. package/dist/styles/recipes/action-bar.css +0 -15
  178. package/dist/styles/recipes/activity.css +5 -11
  179. package/dist/styles/recipes/alert.css +4 -4
  180. package/dist/styles/recipes/audio-recorder.css +15 -3
  181. package/dist/styles/recipes/audio-visualizer.css +13 -2
  182. package/dist/styles/recipes/avatar.css +78 -1
  183. package/dist/styles/recipes/badge.css +14 -4
  184. package/dist/styles/recipes/breadcrumb.css +77 -0
  185. package/dist/styles/recipes/button-group.css +6 -8
  186. package/dist/styles/recipes/button-states.css +66 -0
  187. package/dist/styles/recipes/button.css +26 -85
  188. package/dist/styles/recipes/calendar.css +17 -23
  189. package/dist/styles/recipes/card.css +2 -2
  190. package/dist/styles/recipes/chat-composer-attachment.css +9 -3
  191. package/dist/styles/recipes/chat-composer.css +21 -16
  192. package/dist/styles/recipes/chat-layout.css +64 -2
  193. package/dist/styles/recipes/chat-message-social.css +123 -0
  194. package/dist/styles/recipes/chat-message.css +157 -37
  195. package/dist/styles/recipes/choice.css +25 -21
  196. package/dist/styles/recipes/code-diff.css +51 -26
  197. package/dist/styles/recipes/code.css +44 -18
  198. package/dist/styles/recipes/collapsible.css +43 -1
  199. package/dist/styles/recipes/color-picker-details.css +3 -4
  200. package/dist/styles/recipes/color-picker-values.css +9 -8
  201. package/dist/styles/recipes/color-picker.css +23 -4
  202. package/dist/styles/recipes/context.css +3 -3
  203. package/dist/styles/recipes/dockable-panel.css +18 -7
  204. package/dist/styles/recipes/dropzone.css +7 -8
  205. package/dist/styles/recipes/dynamic-notification-input.css +8 -5
  206. package/dist/styles/recipes/dynamic-notification-motion.css +12 -21
  207. package/dist/styles/recipes/dynamic-notification.css +40 -26
  208. package/dist/styles/recipes/emoji-picker.css +132 -0
  209. package/dist/styles/recipes/empty.css +5 -4
  210. package/dist/styles/recipes/environment-variables.css +4 -15
  211. package/dist/styles/recipes/field-parts.css +89 -21
  212. package/dist/styles/recipes/field.css +83 -26
  213. package/dist/styles/recipes/filter-bar.css +161 -0
  214. package/dist/styles/recipes/gradient-editor.css +18 -7
  215. package/dist/styles/recipes/infinite-canvas.css +51 -4
  216. package/dist/styles/recipes/inline-attachment.css +9 -4
  217. package/dist/styles/recipes/inline-citation.css +10 -4
  218. package/dist/styles/recipes/item.css +23 -9
  219. package/dist/styles/recipes/markdown-block.css +5 -4
  220. package/dist/styles/recipes/markdown.css +68 -26
  221. package/dist/styles/recipes/morphing-panel.css +8 -8
  222. package/dist/styles/recipes/page-layout.css +36 -10
  223. package/dist/styles/recipes/pagination.css +5 -6
  224. package/dist/styles/recipes/phone-input.css +12 -8
  225. package/dist/styles/recipes/popup-controls.css +175 -26
  226. package/dist/styles/recipes/popup-drawer.css +35 -44
  227. package/dist/styles/recipes/popup-lists.css +139 -29
  228. package/dist/styles/recipes/popup-motion.css +4 -45
  229. package/dist/styles/recipes/popup-toast.css +55 -26
  230. package/dist/styles/recipes/popup.css +149 -153
  231. package/dist/styles/recipes/range.css +55 -19
  232. package/dist/styles/recipes/resizable.css +36 -7
  233. package/dist/styles/recipes/rich-tooltip.css +24 -34
  234. package/dist/styles/recipes/scroll-area.css +8 -1
  235. package/dist/styles/recipes/sidebar-menu.css +149 -34
  236. package/dist/styles/recipes/sidebar-parts.css +154 -0
  237. package/dist/styles/recipes/sidebar.css +187 -128
  238. package/dist/styles/recipes/source-badge.css +1 -1
  239. package/dist/styles/recipes/spinner.css +12 -1
  240. package/dist/styles/recipes/stepper.css +31 -13
  241. package/dist/styles/recipes/switch.css +68 -23
  242. package/dist/styles/recipes/table-of-contents.css +49 -41
  243. package/dist/styles/recipes/table.css +14 -10
  244. package/dist/styles/recipes/tabs.css +38 -12
  245. package/dist/styles/recipes/task-list.css +7 -14
  246. package/dist/styles/recipes/thread-rail.css +42 -19
  247. package/dist/styles/recipes/timeline.css +2 -0
  248. package/dist/styles/recipes/toolbar.css +27 -25
  249. package/dist/styles/recipes/track-highlight.css +12 -8
  250. package/dist/styles/recipes/transcript-divider.css +2 -2
  251. package/dist/styles/recipes/tree.css +31 -4
  252. package/dist/styles/recipes/user-ask-details.css +0 -1
  253. package/dist/styles/recipes/user-ask.css +4 -4
  254. package/dist/styles/theme.css +287 -107
  255. package/dist/task-list.d.ts +4 -1
  256. package/dist/task-list.js +24 -8
  257. package/dist/thread-rail.d.ts +1 -1
  258. package/dist/thread-rail.js +61 -19
  259. package/dist/transcript-divider.js +2 -1
  260. package/dist/ui/alert.d.ts +3 -1
  261. package/dist/ui/alert.js +14 -8
  262. package/dist/ui/autocomplete.js +7 -1
  263. package/dist/ui/avatar.d.ts +8 -0
  264. package/dist/ui/avatar.js +23 -3
  265. package/dist/ui/badge.js +1 -1
  266. package/dist/ui/breadcrumb.d.ts +27 -0
  267. package/dist/ui/breadcrumb.js +88 -0
  268. package/dist/ui/button.d.ts +2 -1
  269. package/dist/ui/button.js +1 -1
  270. package/dist/ui/calendar.js +2 -2
  271. package/dist/ui/card.d.ts +3 -1
  272. package/dist/ui/card.js +12 -7
  273. package/dist/ui/checkbox-group.js +1 -1
  274. package/dist/ui/checkbox.d.ts +2 -0
  275. package/dist/ui/code-diff.d.ts +1 -1
  276. package/dist/ui/code-diff.js +71 -27
  277. package/dist/ui/code.d.ts +5 -3
  278. package/dist/ui/code.js +27 -28
  279. package/dist/ui/color-picker.d.ts +5 -2
  280. package/dist/ui/color-picker.js +84 -36
  281. package/dist/ui/combobox-disabled-selection.d.ts +1 -2
  282. package/dist/ui/combobox-disabled-selection.js +7 -8
  283. package/dist/ui/combobox.d.ts +5 -23
  284. package/dist/ui/combobox.js +36 -16
  285. package/dist/ui/command.d.ts +8 -2
  286. package/dist/ui/command.js +39 -15
  287. package/dist/ui/context-menu.js +3 -0
  288. package/dist/ui/dialog.d.ts +2 -1
  289. package/dist/ui/dialog.js +3 -22
  290. package/dist/ui/dockable-panel-geometry.d.ts +1 -0
  291. package/dist/ui/dockable-panel-geometry.js +7 -1
  292. package/dist/ui/dockable-panel.d.ts +9 -6
  293. package/dist/ui/dockable-panel.js +81 -33
  294. package/dist/ui/drawer.d.ts +7 -4
  295. package/dist/ui/drawer.js +35 -22
  296. package/dist/ui/dropdown-menu.js +4 -3
  297. package/dist/ui/dropzone.d.ts +5 -3
  298. package/dist/ui/dropzone.js +78 -17
  299. package/dist/ui/emoji-picker.d.ts +18 -0
  300. package/dist/ui/emoji-picker.js +136 -0
  301. package/dist/ui/empty.d.ts +3 -1
  302. package/dist/ui/empty.js +12 -7
  303. package/dist/ui/field.js +1 -1
  304. package/dist/ui/gradient-editor.d.ts +1 -1
  305. package/dist/ui/gradient-editor.js +37 -21
  306. package/dist/ui/infinite-canvas.d.ts +13 -1
  307. package/dist/ui/infinite-canvas.js +139 -20
  308. package/dist/ui/input-group.d.ts +2 -4
  309. package/dist/ui/input-group.js +13 -14
  310. package/dist/ui/input-otp.d.ts +1 -1
  311. package/dist/ui/input-otp.js +32 -15
  312. package/dist/ui/input.js +11 -2
  313. package/dist/ui/item.d.ts +2 -1
  314. package/dist/ui/item.js +11 -7
  315. package/dist/ui/live-status.d.ts +7 -0
  316. package/dist/ui/live-status.js +17 -0
  317. package/dist/ui/markdown-elements.d.ts +6 -0
  318. package/dist/ui/markdown-elements.js +42 -9
  319. package/dist/ui/menubar.js +2 -2
  320. package/dist/ui/morphing-panel.js +2 -1
  321. package/dist/ui/native-select.d.ts +1 -1
  322. package/dist/ui/native-select.js +24 -11
  323. package/dist/ui/number-field.d.ts +1 -1
  324. package/dist/ui/number-field.js +13 -2
  325. package/dist/ui/page-layout.d.ts +2 -2
  326. package/dist/ui/page-layout.js +2 -4
  327. package/dist/ui/pagination.d.ts +7 -5
  328. package/dist/ui/pagination.js +19 -13
  329. package/dist/ui/phone-input.d.ts +1 -0
  330. package/dist/ui/phone-input.js +56 -58
  331. package/dist/ui/radio-group.d.ts +3 -0
  332. package/dist/ui/radio-group.js +11 -1
  333. package/dist/ui/resizable.d.ts +20 -2
  334. package/dist/ui/resizable.js +130 -8
  335. package/dist/ui/responsive-dialog.d.ts +1 -1
  336. package/dist/ui/responsive-dialog.js +9 -23
  337. package/dist/ui/rich-tooltip.d.ts +10 -4
  338. package/dist/ui/rich-tooltip.js +81 -33
  339. package/dist/ui/scroll-area.d.ts +4 -1
  340. package/dist/ui/scroll-area.js +44 -28
  341. package/dist/ui/select.d.ts +2 -1
  342. package/dist/ui/select.js +57 -32
  343. package/dist/ui/sidebar-menu.d.ts +5 -1
  344. package/dist/ui/sidebar-menu.js +31 -33
  345. package/dist/ui/sidebar-provider.d.ts +10 -1
  346. package/dist/ui/sidebar-provider.js +38 -13
  347. package/dist/ui/sidebar-resize-rail.js +7 -3
  348. package/dist/ui/sidebar.d.ts +11 -6
  349. package/dist/ui/sidebar.js +46 -23
  350. package/dist/ui/slider.js +31 -22
  351. package/dist/ui/spinner.d.ts +3 -2
  352. package/dist/ui/spinner.js +3 -2
  353. package/dist/ui/stepper.d.ts +8 -1
  354. package/dist/ui/stepper.js +74 -25
  355. package/dist/ui/switch.d.ts +1 -0
  356. package/dist/ui/table-of-contents.d.ts +5 -4
  357. package/dist/ui/table-of-contents.js +143 -52
  358. package/dist/ui/table.js +15 -9
  359. package/dist/ui/textarea.d.ts +1 -1
  360. package/dist/ui/textarea.js +20 -9
  361. package/dist/ui/timeline.d.ts +3 -1
  362. package/dist/ui/timeline.js +17 -9
  363. package/dist/ui/toast.d.ts +11 -5
  364. package/dist/ui/toast.js +100 -54
  365. package/dist/ui/toggle.d.ts +1 -1
  366. package/dist/ui/toggle.js +2 -1
  367. package/dist/ui/toolbar.d.ts +3 -2
  368. package/dist/ui/toolbar.js +4 -2
  369. package/dist/ui/tooltip.js +3 -20
  370. package/dist/ui/tree.js +120 -80
  371. package/dist/ui/trigger-menu.js +7 -2
  372. package/dist/user-ask.d.ts +10 -5
  373. package/dist/user-ask.js +31 -11
  374. package/package.json +5 -1
  375. package/dist/hooks/use-keyboard-navigation.d.ts +0 -3
  376. package/dist/hooks/use-keyboard-navigation.js +0 -22
@@ -20,7 +20,7 @@ function formatAudioInputDeviceLabel(device, fallback = "Microphone") {
20
20
  return `${label.replace(deviceIdRegex, "").trim() || fallback} (${id})`;
21
21
  }
22
22
  async function enumerateAudioInputDevices() {
23
- if (typeof navigator === "undefined" || !navigator.mediaDevices?.enumerateDevices) throw new Error("Microphone device selection is not available in this browser.");
23
+ if (typeof navigator === "undefined" || !navigator.mediaDevices?.enumerateDevices) throw unsupportedRecordingError();
24
24
  return (await navigator.mediaDevices.enumerateDevices()).filter((device) => device.kind === "audioinput");
25
25
  }
26
26
  function useAudioInputDevices() {
@@ -43,7 +43,7 @@ function useAudioInputDevices() {
43
43
  }
44
44
  async function requestPermission() {
45
45
  if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) {
46
- setError(/* @__PURE__ */ new Error("Microphone device selection is not available in this browser."));
46
+ setError(unsupportedRecordingError());
47
47
  return;
48
48
  }
49
49
  setLoading(true);
@@ -96,10 +96,16 @@ function browserEnvironment() {
96
96
  function asError(error) {
97
97
  return error instanceof Error ? error : new Error(String(error));
98
98
  }
99
+ function namedError(name, message) {
100
+ return Object.assign(new Error(message), { name });
101
+ }
102
+ function unsupportedRecordingError() {
103
+ return namedError("NotSupportedError", "This browser can't record audio. Try a current version of Chrome, Safari or Firefox.");
104
+ }
99
105
  function microphoneError(error) {
100
106
  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.");
107
+ if (nextError.name === "NotAllowedError" || nextError.name === "SecurityError") return namedError("NotAllowedError", "Microphone access is blocked. Allow it in your browser's site settings, then try again.");
108
+ if (nextError.name === "NotFoundError" || nextError.name === "OverconstrainedError") return namedError("NotFoundError", "Selected microphone isn't connected. Plug it in or choose another microphone.");
103
109
  return nextError;
104
110
  }
105
111
  function clampLevel(value) {
@@ -351,7 +357,7 @@ function createControllerForEnvironment(initialOptions, environment) {
351
357
  }
352
358
  function beginRecording(nextStream) {
353
359
  const MediaRecorderImpl = environment.MediaRecorder;
354
- if (!MediaRecorderImpl) throw new Error("Microphone recording is not available in this browser.");
360
+ if (!MediaRecorderImpl) throw unsupportedRecordingError();
355
361
  selectedMimeType = preferredAudioMimeType(MediaRecorderImpl) ?? "";
356
362
  const nextRecorder = new MediaRecorderImpl(nextStream, selectedMimeType ? { mimeType: selectedMimeType } : void 0);
357
363
  chunks = [];
@@ -388,7 +394,7 @@ function createControllerForEnvironment(initialOptions, environment) {
388
394
  if (!environment.mediaDevices?.getUserMedia || !environment.MediaRecorder) {
389
395
  setSnapshot({
390
396
  state: "error",
391
- error: /* @__PURE__ */ new Error("Microphone recording is not available in this browser.")
397
+ error: unsupportedRecordingError()
392
398
  });
393
399
  return false;
394
400
  }
@@ -1,6 +1,6 @@
1
1
  import { FormSubmitEvent } from "../control-props.js";
2
- import { ChatComposerKnobStyle } from "../knob-contracts/chat-composer-knobs.js";
3
2
  import { ChatDensity } from "./use-chat-message.js";
3
+ import { ChatComposerKnobStyle } from "../knob-contracts/chat-composer-knobs.js";
4
4
  import { CSSProperties, ComponentProps, ReactNode } from "react";
5
5
  //#region src/hooks/use-chat-composer.d.ts
6
6
  export type ChatComposerSubmitPayload = {
@@ -8,6 +8,7 @@ export type ChatComposerSubmitPayload = {
8
8
  clear: () => void;
9
9
  mentions?: MentionItem[];
10
10
  };
11
+ export type ChatComposerSubmitKey = "enter" | "mod-enter";
11
12
  export type ChatComposerProps = Omit<ComponentProps<"form">, "onSubmit" | "style"> & {
12
13
  children?: ReactNode;
13
14
  value?: string;
@@ -17,6 +18,9 @@ export type ChatComposerProps = Omit<ComponentProps<"form">, "onSubmit" | "style
17
18
  state?: "idle" | "submitting" | "disabled";
18
19
  density?: ChatDensity;
19
20
  disabled?: boolean;
21
+ /** Let an empty message send, for composers where an attachment or recording carries the turn. */
22
+ allowEmptySubmit?: boolean;
23
+ submitKey?: ChatComposerSubmitKey;
20
24
  style?: CSSProperties & ChatComposerKnobStyle;
21
25
  };
22
26
  export type MentionItem = {
@@ -27,23 +31,27 @@ export type MentionItem = {
27
31
  type ComposerKeyEvent = {
28
32
  key: string;
29
33
  shiftKey: boolean;
34
+ metaKey: boolean;
35
+ ctrlKey: boolean;
30
36
  defaultPrevented: boolean;
31
37
  nativeEvent: {
32
38
  isComposing: boolean;
39
+ keyCode: number;
33
40
  };
34
41
  preventDefault: () => void;
35
42
  };
36
- export 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
- }): {
43
+ type InsertText = (text: string) => void;
44
+ export type RegisterInsertionTarget = (insert: InsertText) => () => void;
45
+ export type ChatComposerController = {
40
46
  value: string;
41
- setValue: (nextValue: string) => void;
47
+ setValue: (value: string) => void;
42
48
  normalizedValue: string;
43
- state: "disabled" | "idle" | "submitting";
49
+ state: NonNullable<ChatComposerProps["state"]>;
44
50
  density: ChatDensity;
51
+ submitKey: ChatComposerSubmitKey;
45
52
  isCompact: boolean;
46
53
  isDisabled: boolean;
54
+ isLocked: boolean;
47
55
  canSubmit: boolean;
48
56
  rows: number;
49
57
  sendCount: number;
@@ -51,5 +59,11 @@ export declare function useChatComposer({ value, defaultValue, onValueChange, on
51
59
  submit: (extra?: Partial<ChatComposerSubmitPayload>) => void;
52
60
  handleSubmit: (event: FormSubmitEvent) => void;
53
61
  handleKeyDown: (event: ComposerKeyEvent) => void;
62
+ insertText: InsertText;
63
+ registerInsertionTarget: RegisterInsertionTarget;
54
64
  };
65
+ export declare function useChatComposer({ value, defaultValue, onValueChange, onSubmit, state, density, disabled, allowEmptySubmit, submitKey, trackSends }: Pick<ChatComposerProps, "value" | "defaultValue" | "onValueChange" | "onSubmit" | "state" | "density" | "disabled" | "allowEmptySubmit" | "submitKey"> & {
66
+ /** Count successful submits — only enabled when something reads counter (send-layer anchor), so idle apps pay no extra state update. */
67
+ trackSends?: boolean;
68
+ }): ChatComposerController;
55
69
  //#endregion
@@ -1,3 +1,4 @@
1
+ import { isComposingKey } from "./use-trigger-menu.js";
1
2
  import { useState } from "react";
2
3
  //#region src/hooks/use-chat-composer.ts
3
4
  function useControllableText({ value, defaultValue = "", onValueChange }) {
@@ -10,16 +11,30 @@ function useControllableText({ value, defaultValue = "", onValueChange }) {
10
11
  }
11
12
  return [currentValue, setValue];
12
13
  }
13
- function useChatComposer({ value, defaultValue, onValueChange, onSubmit, state = "idle", density = "comfortable", disabled = false, trackSends = false }) {
14
+ function useChatComposer({ value, defaultValue, onValueChange, onSubmit, state = "idle", density = "comfortable", disabled = false, allowEmptySubmit = false, submitKey = "enter", trackSends = false }) {
14
15
  const [inputValue, setInputValue] = useControllableText({
15
16
  value,
16
17
  defaultValue,
17
18
  onValueChange
18
19
  });
19
20
  const [sendCount, setSendCount] = useState(0);
21
+ const [insertion] = useState(() => {
22
+ let target = null;
23
+ const register = (insert) => {
24
+ target = insert;
25
+ return () => {
26
+ if (target === insert) target = null;
27
+ };
28
+ };
29
+ return {
30
+ register,
31
+ current: () => target
32
+ };
33
+ });
20
34
  const normalizedValue = inputValue.trim();
21
- const isDisabled = disabled || state === "disabled" || state === "submitting";
22
- const canSubmit = normalizedValue.length > 0 && !isDisabled;
35
+ const isDisabled = disabled || state === "disabled";
36
+ const isLocked = state === "submitting";
37
+ const canSubmit = (normalizedValue.length > 0 || allowEmptySubmit) && !isDisabled && !isLocked;
23
38
  const isCompact = density === "compact";
24
39
  function clear() {
25
40
  setInputValue("");
@@ -38,25 +53,37 @@ function useChatComposer({ value, defaultValue, onValueChange, onSubmit, state =
38
53
  submit();
39
54
  }
40
55
  function handleKeyDown(event) {
41
- if (event.defaultPrevented || event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing) return;
56
+ if (event.defaultPrevented || event.key !== "Enter" || event.shiftKey || isComposingKey(event.nativeEvent)) return;
57
+ const modifierHeld = event.metaKey || event.ctrlKey;
58
+ if (submitKey === "mod-enter" && !modifierHeld) return;
42
59
  event.preventDefault();
43
60
  submit();
44
61
  }
62
+ function insertText(text) {
63
+ if (isDisabled || isLocked) return;
64
+ const insertAtCaret = insertion.current();
65
+ if (insertAtCaret) insertAtCaret(text);
66
+ else setInputValue(`${inputValue}${text}`);
67
+ }
45
68
  return {
46
69
  value: inputValue,
47
70
  setValue: setInputValue,
48
71
  normalizedValue,
49
72
  state,
50
73
  density,
74
+ submitKey,
51
75
  isCompact,
52
76
  isDisabled,
77
+ isLocked,
53
78
  canSubmit,
54
79
  rows: isCompact ? 2 : 4,
55
80
  sendCount,
56
81
  clear,
57
82
  submit,
58
83
  handleSubmit,
59
- handleKeyDown
84
+ handleKeyDown,
85
+ insertText,
86
+ registerInsertionTarget: insertion.register
60
87
  };
61
88
  }
62
89
  //#endregion
@@ -1,13 +1,25 @@
1
1
  import { ChatMessageKnobStyle } from "../knob-contracts/chat-message-knobs.js";
2
2
  import { CSSProperties, ComponentProps } from "react";
3
3
  //#region src/hooks/use-chat-message.d.ts
4
- export type ChatRole = "user" | "assistant" | "system" | "tool";
4
+ export type ChatRole = "user" | "assistant" | "participant" | "system" | "tool";
5
5
  export type ChatDensity = "compact" | "comfortable";
6
6
  export type ChatState = "idle" | "streaming" | "pending" | "error";
7
+ export type ChatMessageLayout = "bubble" | "flat";
8
+ /** Transition announcements a message publishes to its thread. */
9
+ export type ChatMessageStatusLabels = {
10
+ pending: string;
11
+ replied: string;
12
+ error: string;
13
+ };
7
14
  export type ChatMessageProps = ComponentProps<"article"> & {
8
15
  from: ChatRole;
9
16
  state?: ChatState;
10
17
  density?: ChatDensity;
18
+ layout?: ChatMessageLayout;
19
+ continuation?: boolean;
20
+ /** Accessible name of the message, so the speaker isn't conveyed by alignment and colour alone. */
21
+ authorLabel?: string;
22
+ statusLabels?: Partial<ChatMessageStatusLabels>;
11
23
  } & {
12
24
  style?: CSSProperties & ChatMessageKnobStyle;
13
25
  };
@@ -15,14 +27,28 @@ export type ChatMessageContext = {
15
27
  from: ChatRole;
16
28
  state: ChatState;
17
29
  density: ChatDensity;
30
+ layout: ChatMessageLayout;
31
+ continuation: boolean;
18
32
  isUser: boolean;
19
33
  isAssistant: boolean;
34
+ isParticipant: boolean;
20
35
  isSystem: boolean;
21
36
  isTool: boolean;
22
37
  isCompact: boolean;
23
38
  isStreaming: boolean;
24
39
  isPending: boolean;
25
40
  isError: boolean;
41
+ isEndAligned: boolean;
42
+ isBubble: boolean;
26
43
  };
27
- export declare function useChatMessage({ from, state, density }: Pick<ChatMessageProps, "from" | "state" | "density">): ChatMessageContext;
44
+ export declare const chatAuthorLabels: Record<ChatRole, string>;
45
+ export declare const chatMessageStatusLabels: ChatMessageStatusLabels;
46
+ export declare const ChatThreadAnnounceContext: import("react").Context<(message: string) => void>;
47
+ /** Publishes a state transition (never streamed text) to the thread's one polite status region; a no-op outside `ChatThread`. */
48
+ export declare function useChatThreadAnnounce(): (message: string) => void;
49
+ export declare const ChatMessageStateProvider: import("react").Provider<ChatMessageContext | null>;
50
+ export declare function useChatMessageContext(): ChatMessageContext;
51
+ /** The announcement for a state change, or null when the change isn't worth announcing. */
52
+ export declare function chatMessageTransition(previous: ChatState, next: ChatState, labels: ChatMessageStatusLabels): string | null;
53
+ export declare function useChatMessage({ from, state, density, layout, continuation }: Pick<ChatMessageProps, "from" | "state" | "density" | "layout" | "continuation">): ChatMessageContext;
28
54
  //#endregion
@@ -1,25 +1,56 @@
1
+ import { createContext, useContext } from "react";
1
2
  //#region src/hooks/use-chat-message.ts
2
- function getChatMessageContext({ from, state, density }) {
3
+ const chatAuthorLabels = {
4
+ user: "You",
5
+ assistant: "Assistant",
6
+ participant: "Participant",
7
+ system: "System",
8
+ tool: "Tool"
9
+ };
10
+ const chatMessageStatusLabels = {
11
+ pending: "Assistant is replying",
12
+ replied: "Assistant replied",
13
+ error: "Reply failed"
14
+ };
15
+ const ChatThreadAnnounceContext = createContext(() => {});
16
+ /** Publishes a state transition (never streamed text) to the thread's one polite status region; a no-op outside `ChatThread`. */
17
+ function useChatThreadAnnounce() {
18
+ return useContext(ChatThreadAnnounceContext);
19
+ }
20
+ const ChatMessageStateContext = createContext(null);
21
+ const ChatMessageStateProvider = ChatMessageStateContext.Provider;
22
+ function useChatMessageContext() {
23
+ const context = useContext(ChatMessageStateContext);
24
+ if (!context) throw new Error("ChatMessage compound components must be rendered inside <ChatMessage>.");
25
+ return context;
26
+ }
27
+ /** The announcement for a state change, or null when the change isn't worth announcing. */
28
+ function chatMessageTransition(previous, next, labels) {
29
+ if (previous === next) return null;
30
+ if (next === "pending") return labels.pending;
31
+ if (next === "error") return labels.error;
32
+ if (next === "idle" && (previous === "pending" || previous === "streaming")) return labels.replied;
33
+ return null;
34
+ }
35
+ function useChatMessage({ from, state = "idle", density = "comfortable", layout = "bubble", continuation = false }) {
3
36
  return {
4
37
  from,
5
38
  state,
6
39
  density,
40
+ layout,
41
+ continuation,
7
42
  isUser: from === "user",
8
43
  isAssistant: from === "assistant",
44
+ isParticipant: from === "participant",
9
45
  isSystem: from === "system",
10
46
  isTool: from === "tool",
11
47
  isCompact: density === "compact",
12
48
  isStreaming: state === "streaming",
13
49
  isPending: state === "pending",
14
- isError: state === "error"
50
+ isError: state === "error",
51
+ isEndAligned: from === "user" && layout === "bubble",
52
+ isBubble: layout === "bubble" && (from === "user" || from === "participant")
15
53
  };
16
54
  }
17
- function useChatMessage({ from, state = "idle", density = "comfortable" }) {
18
- return getChatMessageContext({
19
- from,
20
- state,
21
- density
22
- });
23
- }
24
55
  //#endregion
25
- export { useChatMessage };
56
+ export { ChatMessageStateProvider, ChatThreadAnnounceContext, chatAuthorLabels, chatMessageStatusLabels, chatMessageTransition, useChatMessage, useChatMessageContext, useChatThreadAnnounce };
@@ -1,14 +1,29 @@
1
+ import { prefersReducedMotion } from "../lib/motion.js";
1
2
  import { useEffect, useRef, useState } from "react";
2
3
  //#region src/hooks/use-chat-thread-scroll.ts
3
4
  const BOTTOM_THRESHOLD = 24;
4
5
  const SETTLE_TIMEOUT = 700;
6
+ function firstVisibleChild(list, viewportTop) {
7
+ const children = list.children;
8
+ let low = 0;
9
+ let high = children.length - 1;
10
+ let found;
11
+ while (low <= high) {
12
+ const middle = low + high >> 1;
13
+ if (children[middle].getBoundingClientRect().bottom > viewportTop) {
14
+ found = children[middle];
15
+ high = middle - 1;
16
+ } else low = middle + 1;
17
+ }
18
+ return found;
19
+ }
5
20
  function useChatThreadScroll() {
6
21
  const viewportRef = useRef(null);
7
22
  const contentRef = useRef(null);
8
23
  const pinnedRef = useRef(true);
9
24
  const [atBottom, setAtBottom] = useState(true);
10
25
  const settlingRef = useRef(void 0);
11
- function scrollToBottom(behavior = "smooth") {
26
+ function scrollToBottom(behavior) {
12
27
  const viewport = viewportRef.current;
13
28
  if (!viewport) return;
14
29
  pinnedRef.current = true;
@@ -19,13 +34,29 @@ function useChatThreadScroll() {
19
34
  }, SETTLE_TIMEOUT);
20
35
  viewport.scrollTo({
21
36
  top: viewport.scrollHeight,
22
- behavior
37
+ behavior: behavior ?? (prefersReducedMotion(viewport) ? "auto" : "smooth")
23
38
  });
24
39
  }
25
40
  useEffect(() => {
26
41
  const viewport = viewportRef.current;
27
42
  const content = contentRef.current;
28
- if (!viewport || !content) return;
43
+ const messageList = content?.firstElementChild;
44
+ if (!viewport || !content || !messageList) return;
45
+ let anchor;
46
+ function rememberAnchor() {
47
+ if (!viewport || !messageList) return;
48
+ const viewportTop = viewport.getBoundingClientRect().top;
49
+ const element = firstVisibleChild(messageList, viewportTop);
50
+ anchor = element ? {
51
+ element,
52
+ offset: element.getBoundingClientRect().top - viewportTop
53
+ } : void 0;
54
+ }
55
+ function keepAnchorInPlace() {
56
+ if (!viewport || !anchor?.element.isConnected) return;
57
+ const drift = anchor.element.getBoundingClientRect().top - viewport.getBoundingClientRect().top - anchor.offset;
58
+ if (drift !== 0) viewport.scrollTop += drift;
59
+ }
29
60
  function settle() {
30
61
  clearTimeout(settlingRef.current);
31
62
  settlingRef.current = void 0;
@@ -37,21 +68,26 @@ function useChatThreadScroll() {
37
68
  pinnedRef.current = distance <= BOTTOM_THRESHOLD;
38
69
  setAtBottom(pinnedRef.current);
39
70
  }
71
+ function handleScroll() {
72
+ rememberAnchor();
73
+ readPosition();
74
+ }
40
75
  const observer = new ResizeObserver(() => {
41
76
  if (!viewport) return;
42
77
  if (pinnedRef.current) viewport.scrollTop = viewport.scrollHeight;
43
- else readPosition();
78
+ else keepAnchorInPlace();
79
+ handleScroll();
44
80
  });
45
81
  observer.observe(content);
46
82
  observer.observe(viewport);
47
- viewport.addEventListener("scroll", readPosition, { passive: true });
83
+ viewport.addEventListener("scroll", handleScroll, { passive: true });
48
84
  viewport.addEventListener("scrollend", settle);
49
85
  viewport.scrollTop = viewport.scrollHeight;
50
- readPosition();
86
+ handleScroll();
51
87
  return () => {
52
88
  observer.disconnect();
53
89
  clearTimeout(settlingRef.current);
54
- viewport.removeEventListener("scroll", readPosition);
90
+ viewport.removeEventListener("scroll", handleScroll);
55
91
  viewport.removeEventListener("scrollend", settle);
56
92
  };
57
93
  }, []);
@@ -8,10 +8,10 @@ export type ColorAreaRect = {
8
8
  width: number;
9
9
  height: number;
10
10
  };
11
- export type ColorAreaPointer = {
12
- areaRef: RefObject<HTMLDivElement | null>;
11
+ export type ColorAreaPointer<Element extends HTMLElement> = {
12
+ areaRef: RefObject<Element | null>;
13
13
  dragging: boolean;
14
14
  onPointerDown: (event: PointerEvent) => void;
15
15
  };
16
- export declare function useColorArea(onChange: (offset: ColorAreaOffset, rect: ColorAreaRect) => void): ColorAreaPointer;
16
+ export declare function useColorArea<Element extends HTMLElement = HTMLDivElement>(onChange: (offset: ColorAreaOffset, rect: ColorAreaRect) => void, targetRef?: RefObject<Element | null>): ColorAreaPointer<Element>;
17
17
  //#endregion
@@ -1,41 +1,42 @@
1
1
  "use client";
2
2
  import { useEffect, useEffectEvent, useRef, useState } from "react";
3
3
  //#region src/hooks/use-color-area.ts
4
- function useColorArea(onChange) {
5
- const areaRef = useRef(null);
4
+ function useColorArea(onChange, targetRef) {
5
+ const ownRef = useRef(null);
6
+ const areaRef = targetRef ?? ownRef;
6
7
  const [dragging, setDragging] = useState(false);
7
8
  const emitDragChange = useEffectEvent(onChange);
8
9
  useEffect(() => {
9
10
  if (!dragging) return;
10
- function emit(clientX, clientY) {
11
+ function onMove(event) {
11
12
  const el = areaRef.current;
12
13
  if (!el) return;
13
14
  const rect = el.getBoundingClientRect();
14
15
  emitDragChange({
15
- x: clientX - rect.left,
16
- y: clientY - rect.top
16
+ x: event.clientX - rect.left,
17
+ y: event.clientY - rect.top
17
18
  }, {
18
19
  width: rect.width,
19
20
  height: rect.height
20
21
  });
21
22
  }
22
- function onMove(event) {
23
- emit(event.clientX, event.clientY);
24
- }
25
- function onUp() {
23
+ function onEnd() {
26
24
  setDragging(false);
27
25
  }
28
26
  const previousSelect = document.body.style.userSelect;
29
27
  document.body.style.userSelect = "none";
30
28
  window.addEventListener("pointermove", onMove);
31
- window.addEventListener("pointerup", onUp);
29
+ window.addEventListener("pointerup", onEnd);
30
+ window.addEventListener("pointercancel", onEnd);
32
31
  return () => {
33
32
  window.removeEventListener("pointermove", onMove);
34
- window.removeEventListener("pointerup", onUp);
33
+ window.removeEventListener("pointerup", onEnd);
34
+ window.removeEventListener("pointercancel", onEnd);
35
35
  document.body.style.userSelect = previousSelect;
36
36
  };
37
- }, [dragging]);
37
+ }, [dragging, areaRef]);
38
38
  function onPointerDown(event) {
39
+ if (event.button !== 0) return;
39
40
  const el = areaRef.current;
40
41
  if (el) {
41
42
  const rect = el.getBoundingClientRect();
@@ -5,8 +5,9 @@ export type UseCopyToClipboardOptions = {
5
5
  onCopy?: (value: string) => void;
6
6
  onCopyError?: (error: unknown) => void;
7
7
  };
8
+ export type CopyStatus = "idle" | "copied" | "failed";
8
9
  export type UseCopyToClipboardResult = {
9
- isCopied: boolean;
10
+ status: CopyStatus;
10
11
  copyToClipboard: (value: string) => Promise<boolean>;
11
12
  resetCopied: () => void;
12
13
  };
@@ -17,15 +17,15 @@ function copyViaSelection(value) {
17
17
  textarea.setAttribute("readonly", "");
18
18
  textarea.setAttribute("aria-hidden", "true");
19
19
  textarea.style.position = "fixed";
20
- textarea.style.top = "0";
21
- textarea.style.left = "0";
20
+ textarea.style.insetBlockStart = "0";
21
+ textarea.style.insetInlineStart = "0";
22
22
  textarea.style.width = "1px";
23
23
  textarea.style.height = "1px";
24
24
  textarea.style.opacity = "0";
25
25
  textarea.style.pointerEvents = "none";
26
26
  document.body.appendChild(textarea);
27
27
  try {
28
- textarea.focus();
28
+ textarea.focus({ preventScroll: true });
29
29
  textarea.select();
30
30
  textarea.setSelectionRange(0, value.length);
31
31
  return document.execCommand("copy");
@@ -41,7 +41,7 @@ async function copyTextToClipboard(value) {
41
41
  return copyViaSelection(value);
42
42
  }
43
43
  function useCopyToClipboard({ text, copiedDuration = 1200, onCopy, onCopyError } = {}) {
44
- const [isCopied, setIsCopied] = useState(false);
44
+ const [status, setStatus] = useState("idle");
45
45
  const resetTimeout = useRef(null);
46
46
  useEffect(() => {
47
47
  const timeoutRef = resetTimeout;
@@ -52,38 +52,38 @@ function useCopyToClipboard({ text, copiedDuration = 1200, onCopy, onCopyError }
52
52
  function resetCopied() {
53
53
  if (resetTimeout.current) window.clearTimeout(resetTimeout.current);
54
54
  resetTimeout.current = null;
55
- setIsCopied(false);
55
+ setStatus("idle");
56
+ }
57
+ function settle(next) {
58
+ setStatus(next);
59
+ if (resetTimeout.current) window.clearTimeout(resetTimeout.current);
60
+ resetTimeout.current = next === "copied" ? window.setTimeout(() => setStatus("idle"), copiedDuration) : null;
61
+ return next === "copied";
56
62
  }
57
63
  async function copyToClipboard(value) {
58
64
  if (!await copyTextToClipboard(value)) {
59
65
  onCopyError?.(/* @__PURE__ */ new Error("Unable to copy text"));
60
- setIsCopied(false);
61
- return false;
66
+ return settle("failed");
62
67
  }
63
68
  try {
64
69
  onCopy?.(value);
65
70
  } catch (error) {
66
71
  onCopyError?.(error);
67
- setIsCopied(false);
68
- return false;
72
+ return settle("failed");
69
73
  }
70
- setIsCopied(true);
71
- if (resetTimeout.current) window.clearTimeout(resetTimeout.current);
72
- resetTimeout.current = window.setTimeout(() => setIsCopied(false), copiedDuration);
73
- return true;
74
+ return settle("copied");
74
75
  }
75
76
  function handleCopy() {
76
77
  return text !== void 0 ? copyToClipboard(text) : Promise.resolve(false);
77
78
  }
78
- return text === void 0 ? {
79
- isCopied,
79
+ const state = {
80
+ status,
80
81
  copyToClipboard,
81
82
  resetCopied
82
- } : {
83
- isCopied,
84
- copyToClipboard,
85
- handleCopy,
86
- resetCopied
83
+ };
84
+ return text === void 0 ? state : {
85
+ ...state,
86
+ handleCopy
87
87
  };
88
88
  }
89
89
  //#endregion
@@ -1,9 +1,9 @@
1
1
  import { DropzoneFileRejection, DropzonePolicy } from "../lib/dropzone-validation.js";
2
- import { ChangeEvent, ComponentProps, DragEvent, RefObject } from "react";
2
+ import { ChangeEvent, ClipboardEvent, ComponentProps, DragEvent, RefObject } from "react";
3
3
  //#region src/hooks/use-dropzone.d.ts
4
4
  export type DropzoneVisualState = "idle" | "accept" | "reject" | "unknown" | "processing";
5
- export type DropzoneValueChangeReason = "drop" | "input" | "remove" | "clear";
6
- export type DropzoneEvent = DragEvent<HTMLElement> | ChangeEvent<HTMLInputElement>;
5
+ export type DropzoneValueChangeReason = "drop" | "input" | "paste" | "remove" | "clear";
6
+ export type DropzoneEvent = DragEvent<HTMLElement> | ClipboardEvent<HTMLElement> | ChangeEvent<HTMLInputElement>;
7
7
  export type DropzoneGetFilesFromEvent = (event: DropzoneEvent, signal: AbortSignal) => readonly File[] | Promise<readonly File[]>;
8
8
  export type DropzoneValueChangeDetails = {
9
9
  reason: DropzoneValueChangeReason;
@@ -22,6 +22,7 @@ export type UseDropzoneOptions = {
22
22
  policy?: DropzonePolicy;
23
23
  disabled?: boolean;
24
24
  drag?: boolean;
25
+ paste?: boolean;
25
26
  preventDropOnDocument?: boolean;
26
27
  trackDocumentDrag?: boolean;
27
28
  getFilesFromEvent?: DropzoneGetFilesFromEvent;
@@ -35,6 +36,7 @@ export type UseDropzoneReturn = {
35
36
  multiple: boolean;
36
37
  disabled: boolean;
37
38
  dragEnabled: boolean;
39
+ pasteEnabled: boolean;
38
40
  visualState: DropzoneVisualState;
39
41
  isDragActive: boolean;
40
42
  isDragAccept: boolean;
@@ -48,11 +50,12 @@ export type UseDropzoneReturn = {
48
50
  open: () => void;
49
51
  removeFile: (file: File) => void;
50
52
  clearFiles: () => void;
53
+ removeRejection: (file: File) => void;
51
54
  clearRejections: () => void;
52
55
  reset: () => void;
53
56
  getRootProps: (props?: ComponentProps<"div">) => ComponentProps<"div">;
54
57
  getInputProps: (props?: ComponentProps<"input">) => ComponentProps<"input">;
55
58
  };
56
- export declare function useDropzone({ value: controlledValue, defaultValue, onValueChange, policy, disabled, drag, preventDropOnDocument, trackDocumentDrag, getFilesFromEvent, onDrop, onError }?: UseDropzoneOptions): UseDropzoneReturn;
59
+ export declare function useDropzone({ value: controlledValue, defaultValue, onValueChange, policy, disabled, drag, paste, preventDropOnDocument, trackDocumentDrag, getFilesFromEvent, onDrop, onError }?: UseDropzoneOptions): UseDropzoneReturn;
57
60
  export declare function defaultGetFilesFromEvent(event: DropzoneEvent): readonly File[];
58
61
  //#endregion