@ctrl-ui/react 0.8.7 → 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 (351) hide show
  1. package/dist/action-bar.d.ts +15 -7
  2. package/dist/action-bar.js +40 -20
  3. package/dist/activity.js +8 -16
  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 +39 -39
  8. package/dist/blocks/design-canvas-inspector.js +2 -2
  9. package/dist/blocks/design-canvas.d.ts +5 -1
  10. package/dist/blocks/design-canvas.js +200 -94
  11. package/dist/blocks/discussion.d.ts +28 -0
  12. package/dist/blocks/discussion.js +67 -0
  13. package/dist/blocks/file-explorer-data.d.ts +1 -0
  14. package/dist/blocks/file-explorer-data.js +4 -1
  15. package/dist/blocks/file-explorer.d.ts +9 -1
  16. package/dist/blocks/file-explorer.js +461 -192
  17. package/dist/blocks/settings.d.ts +2 -1
  18. package/dist/blocks/settings.js +102 -80
  19. package/dist/blocks/team-chat.d.ts +12 -0
  20. package/dist/blocks/team-chat.js +26 -0
  21. package/dist/blocks/theme-toggle.js +23 -31
  22. package/dist/chat-composer-attachment.d.ts +4 -1
  23. package/dist/chat-composer-attachment.js +35 -10
  24. package/dist/chat-composer-editor/extensions/mention.d.ts +5 -1
  25. package/dist/chat-composer-editor/extensions/mention.js +58 -27
  26. package/dist/chat-composer-editor/ghost.js +2 -4
  27. package/dist/chat-composer-editor/types.d.ts +5 -0
  28. package/dist/chat-composer-editor.d.ts +1 -1
  29. package/dist/chat-composer-editor.js +57 -30
  30. package/dist/chat-composer.d.ts +13 -32
  31. package/dist/chat-composer.js +53 -13
  32. package/dist/chat-layout.d.ts +19 -0
  33. package/dist/chat-layout.js +91 -40
  34. package/dist/chat-message/social.d.ts +26 -0
  35. package/dist/chat-message/social.js +122 -0
  36. package/dist/chat-message.d.ts +28 -29
  37. package/dist/chat-message.js +106 -25
  38. package/dist/context.d.ts +2 -2
  39. package/dist/context.js +39 -24
  40. package/dist/dynamic-notification-canvas-loop.d.ts +24 -0
  41. package/dist/dynamic-notification-canvas-loop.js +113 -0
  42. package/dist/dynamic-notification-glass.js +23 -114
  43. package/dist/dynamic-notification-liquid.js +14 -118
  44. package/dist/dynamic-notification.d.ts +16 -8
  45. package/dist/dynamic-notification.js +86 -23
  46. package/dist/email/email-brand.d.ts +7 -4
  47. package/dist/email/email-brand.js +9 -6
  48. package/dist/email/email-code.d.ts +3 -2
  49. package/dist/email/email-code.js +11 -9
  50. package/dist/email/email-footer.d.ts +5 -3
  51. package/dist/email/email-footer.js +11 -5
  52. package/dist/email/email.d.ts +31 -9
  53. package/dist/email/email.js +50 -26
  54. package/dist/email/templates.d.ts +10 -9
  55. package/dist/email/templates.js +102 -38
  56. package/dist/email/theme.js +1 -1
  57. package/dist/environment-variables.d.ts +4 -2
  58. package/dist/environment-variables.js +52 -32
  59. package/dist/filter-bar/editor.js +20 -8
  60. package/dist/filter-bar/model.d.ts +1 -0
  61. package/dist/filter-bar/model.js +10 -1
  62. package/dist/filter-bar/motion.js +4 -4
  63. package/dist/filter-bar/parts.js +20 -9
  64. package/dist/filter-bar/types.d.ts +1 -0
  65. package/dist/filter-bar.js +20 -14
  66. package/dist/hooks/use-audio-recorder.js +12 -6
  67. package/dist/hooks/use-chat-composer.d.ts +19 -7
  68. package/dist/hooks/use-chat-composer.js +32 -5
  69. package/dist/hooks/use-chat-message.d.ts +28 -2
  70. package/dist/hooks/use-chat-message.js +41 -10
  71. package/dist/hooks/use-chat-thread-scroll.js +43 -7
  72. package/dist/hooks/use-color-area.d.ts +3 -3
  73. package/dist/hooks/use-color-area.js +13 -12
  74. package/dist/hooks/use-copy-to-clipboard.d.ts +2 -1
  75. package/dist/hooks/use-copy-to-clipboard.js +20 -20
  76. package/dist/hooks/use-dynamic-notification.d.ts +6 -2
  77. package/dist/hooks/use-dynamic-notification.js +26 -10
  78. package/dist/hooks/use-mobile.d.ts +3 -2
  79. package/dist/hooks/use-mobile.js +8 -4
  80. package/dist/hooks/use-textarea-trigger-menu.js +2 -1
  81. package/dist/hooks/use-trigger-menu.d.ts +54 -0
  82. package/dist/hooks/use-trigger-menu.js +102 -47
  83. package/dist/hooks/use-user-ask.d.ts +2 -0
  84. package/dist/hooks/use-user-ask.js +35 -41
  85. package/dist/inline-attachment.d.ts +6 -2
  86. package/dist/inline-attachment.js +36 -15
  87. package/dist/inline-citation.d.ts +12 -5
  88. package/dist/inline-citation.js +26 -17
  89. package/dist/knob-contracts/audio-recorder-knobs.d.ts +1 -1
  90. package/dist/knob-contracts/audio-recorder-knobs.js +2 -1
  91. package/dist/knob-contracts/audio-visualizer-knobs.d.ts +1 -1
  92. package/dist/knob-contracts/audio-visualizer-knobs.js +2 -1
  93. package/dist/knob-contracts/avatar-knobs.d.ts +1 -1
  94. package/dist/knob-contracts/avatar-knobs.js +6 -1
  95. package/dist/knob-contracts/badge-knobs.d.ts +1 -1
  96. package/dist/knob-contracts/badge-knobs.js +2 -1
  97. package/dist/knob-contracts/breadcrumb-knobs.d.ts +4 -0
  98. package/dist/knob-contracts/breadcrumb-knobs.js +8 -0
  99. package/dist/knob-contracts/button-knobs.d.ts +1 -1
  100. package/dist/knob-contracts/button-knobs.js +1 -0
  101. package/dist/knob-contracts/chat-message-knobs.d.ts +1 -1
  102. package/dist/knob-contracts/chat-message-knobs.js +8 -1
  103. package/dist/knob-contracts/code-diff-knobs.d.ts +1 -1
  104. package/dist/knob-contracts/code-diff-knobs.js +2 -0
  105. package/dist/knob-contracts/code-knobs.d.ts +1 -1
  106. package/dist/knob-contracts/code-knobs.js +2 -1
  107. package/dist/knob-contracts/dynamic-notification-knobs.d.ts +1 -1
  108. package/dist/knob-contracts/dynamic-notification-knobs.js +2 -0
  109. package/dist/knob-contracts/emoji-picker-knobs.d.ts +4 -0
  110. package/dist/knob-contracts/emoji-picker-knobs.js +10 -0
  111. package/dist/knob-contracts/field-knobs.d.ts +1 -1
  112. package/dist/knob-contracts/field-knobs.js +5 -1
  113. package/dist/knob-contracts/filter-bar-knobs.d.ts +1 -1
  114. package/dist/knob-contracts/filter-bar-knobs.js +1 -2
  115. package/dist/knob-contracts/markdown-knobs.d.ts +1 -1
  116. package/dist/knob-contracts/markdown-knobs.js +3 -1
  117. package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
  118. package/dist/knob-contracts/page-layout-knobs.js +2 -1
  119. package/dist/knob-contracts/popup-knobs.d.ts +1 -1
  120. package/dist/knob-contracts/popup-knobs.js +27 -0
  121. package/dist/knob-contracts/range-knobs.d.ts +1 -1
  122. package/dist/knob-contracts/range-knobs.js +1 -0
  123. package/dist/knob-contracts/rich-tooltip-knobs.d.ts +1 -1
  124. package/dist/knob-contracts/rich-tooltip-knobs.js +2 -1
  125. package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
  126. package/dist/knob-contracts/sidebar-knobs.js +27 -2
  127. package/dist/knob-contracts/spinner-knobs.d.ts +1 -1
  128. package/dist/knob-contracts/spinner-knobs.js +1 -1
  129. package/dist/knob-contracts/stepper-knobs.d.ts +1 -1
  130. package/dist/knob-contracts/stepper-knobs.js +4 -1
  131. package/dist/knob-contracts/switch-knobs.d.ts +1 -1
  132. package/dist/knob-contracts/switch-knobs.js +5 -0
  133. package/dist/knob-contracts/table-of-contents-knobs.d.ts +1 -1
  134. package/dist/knob-contracts/table-of-contents-knobs.js +2 -1
  135. package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
  136. package/dist/knob-contracts/tabs-knobs.js +1 -0
  137. package/dist/knob-contracts/tree-knobs.d.ts +1 -1
  138. package/dist/knob-contracts/tree-knobs.js +2 -1
  139. package/dist/lib/cn.js +0 -4
  140. package/dist/lib/contrast.d.ts +1 -0
  141. package/dist/lib/contrast.js +17 -5
  142. package/dist/lib/dropzone-validation.js +41 -28
  143. package/dist/lib/env-file.d.ts +1 -1
  144. package/dist/lib/env-file.js +5 -5
  145. package/dist/lib/motion.d.ts +4 -0
  146. package/dist/lib/motion.js +15 -0
  147. package/dist/lib/phone-input-format.js +1 -1
  148. package/dist/lib/trigger-detect.js +6 -8
  149. package/dist/markdown-block.js +2 -1
  150. package/dist/popup-parts.d.ts +8 -0
  151. package/dist/popup-parts.js +43 -0
  152. package/dist/source-badge.js +3 -1
  153. package/dist/styles/code.css +0 -2
  154. package/dist/styles/effects.css +6 -32
  155. package/dist/styles/index.css +10 -5
  156. package/dist/styles/recipes/accordion.css +9 -6
  157. package/dist/styles/recipes/action-bar.css +0 -15
  158. package/dist/styles/recipes/activity.css +5 -11
  159. package/dist/styles/recipes/alert.css +4 -4
  160. package/dist/styles/recipes/audio-recorder.css +15 -3
  161. package/dist/styles/recipes/audio-visualizer.css +13 -2
  162. package/dist/styles/recipes/avatar.css +78 -1
  163. package/dist/styles/recipes/badge.css +14 -4
  164. package/dist/styles/recipes/breadcrumb.css +77 -0
  165. package/dist/styles/recipes/button-group.css +5 -7
  166. package/dist/styles/recipes/button-states.css +66 -0
  167. package/dist/styles/recipes/button.css +24 -83
  168. package/dist/styles/recipes/calendar.css +17 -23
  169. package/dist/styles/recipes/card.css +2 -2
  170. package/dist/styles/recipes/chat-composer-attachment.css +9 -3
  171. package/dist/styles/recipes/chat-composer.css +7 -10
  172. package/dist/styles/recipes/chat-layout.css +35 -0
  173. package/dist/styles/recipes/chat-message-social.css +123 -0
  174. package/dist/styles/recipes/chat-message.css +157 -37
  175. package/dist/styles/recipes/choice.css +18 -21
  176. package/dist/styles/recipes/code-diff.css +46 -33
  177. package/dist/styles/recipes/code.css +36 -16
  178. package/dist/styles/recipes/collapsible.css +7 -2
  179. package/dist/styles/recipes/color-picker-details.css +3 -4
  180. package/dist/styles/recipes/color-picker-values.css +9 -8
  181. package/dist/styles/recipes/color-picker.css +11 -2
  182. package/dist/styles/recipes/context.css +3 -3
  183. package/dist/styles/recipes/dockable-panel.css +7 -4
  184. package/dist/styles/recipes/dropzone.css +7 -8
  185. package/dist/styles/recipes/dynamic-notification-input.css +8 -5
  186. package/dist/styles/recipes/dynamic-notification-motion.css +12 -21
  187. package/dist/styles/recipes/dynamic-notification.css +40 -26
  188. package/dist/styles/recipes/emoji-picker.css +132 -0
  189. package/dist/styles/recipes/empty.css +5 -4
  190. package/dist/styles/recipes/environment-variables.css +4 -15
  191. package/dist/styles/recipes/field-parts.css +30 -24
  192. package/dist/styles/recipes/field.css +61 -26
  193. package/dist/styles/recipes/filter-bar.css +18 -31
  194. package/dist/styles/recipes/gradient-editor.css +18 -7
  195. package/dist/styles/recipes/infinite-canvas.css +0 -1
  196. package/dist/styles/recipes/inline-attachment.css +3 -3
  197. package/dist/styles/recipes/inline-citation.css +10 -4
  198. package/dist/styles/recipes/item.css +23 -9
  199. package/dist/styles/recipes/markdown-block.css +5 -4
  200. package/dist/styles/recipes/markdown.css +68 -26
  201. package/dist/styles/recipes/morphing-panel.css +8 -8
  202. package/dist/styles/recipes/page-layout.css +20 -12
  203. package/dist/styles/recipes/pagination.css +4 -5
  204. package/dist/styles/recipes/phone-input.css +12 -8
  205. package/dist/styles/recipes/popup-controls.css +166 -33
  206. package/dist/styles/recipes/popup-drawer.css +3 -14
  207. package/dist/styles/recipes/popup-lists.css +128 -27
  208. package/dist/styles/recipes/popup-motion.css +4 -45
  209. package/dist/styles/recipes/popup-toast.css +55 -26
  210. package/dist/styles/recipes/popup.css +139 -141
  211. package/dist/styles/recipes/range.css +54 -18
  212. package/dist/styles/recipes/resizable.css +2 -7
  213. package/dist/styles/recipes/rich-tooltip.css +24 -34
  214. package/dist/styles/recipes/scroll-area.css +8 -1
  215. package/dist/styles/recipes/sidebar-menu.css +149 -34
  216. package/dist/styles/recipes/sidebar-parts.css +154 -0
  217. package/dist/styles/recipes/sidebar.css +187 -128
  218. package/dist/styles/recipes/source-badge.css +1 -1
  219. package/dist/styles/recipes/spinner.css +8 -1
  220. package/dist/styles/recipes/stepper.css +31 -13
  221. package/dist/styles/recipes/switch.css +68 -23
  222. package/dist/styles/recipes/table-of-contents.css +9 -1
  223. package/dist/styles/recipes/table.css +14 -10
  224. package/dist/styles/recipes/tabs.css +17 -12
  225. package/dist/styles/recipes/task-list.css +7 -14
  226. package/dist/styles/recipes/thread-rail.css +42 -19
  227. package/dist/styles/recipes/timeline.css +2 -0
  228. package/dist/styles/recipes/toolbar.css +27 -25
  229. package/dist/styles/recipes/track-highlight.css +10 -7
  230. package/dist/styles/recipes/tree.css +31 -4
  231. package/dist/styles/recipes/user-ask-details.css +0 -1
  232. package/dist/styles/recipes/user-ask.css +4 -4
  233. package/dist/styles/theme.css +37 -16
  234. package/dist/task-list.d.ts +4 -1
  235. package/dist/task-list.js +24 -8
  236. package/dist/thread-rail.d.ts +1 -1
  237. package/dist/thread-rail.js +61 -19
  238. package/dist/transcript-divider.js +2 -1
  239. package/dist/ui/alert-dialog.js +1 -1
  240. package/dist/ui/alert.d.ts +3 -1
  241. package/dist/ui/alert.js +14 -8
  242. package/dist/ui/autocomplete.js +4 -2
  243. package/dist/ui/avatar.d.ts +8 -0
  244. package/dist/ui/avatar.js +23 -3
  245. package/dist/ui/badge.js +1 -1
  246. package/dist/ui/breadcrumb.d.ts +27 -0
  247. package/dist/ui/breadcrumb.js +88 -0
  248. package/dist/ui/button.d.ts +2 -1
  249. package/dist/ui/button.js +3 -3
  250. package/dist/ui/calendar.js +2 -2
  251. package/dist/ui/card.d.ts +3 -1
  252. package/dist/ui/card.js +12 -7
  253. package/dist/ui/checkbox.d.ts +2 -0
  254. package/dist/ui/code-diff.d.ts +1 -1
  255. package/dist/ui/code-diff.js +72 -28
  256. package/dist/ui/code.d.ts +5 -3
  257. package/dist/ui/code.js +28 -29
  258. package/dist/ui/collapsible.js +1 -1
  259. package/dist/ui/color-picker.d.ts +5 -2
  260. package/dist/ui/color-picker.js +85 -37
  261. package/dist/ui/combobox-disabled-selection.d.ts +1 -2
  262. package/dist/ui/combobox-disabled-selection.js +7 -9
  263. package/dist/ui/combobox.js +14 -6
  264. package/dist/ui/command.d.ts +8 -2
  265. package/dist/ui/command.js +30 -14
  266. package/dist/ui/context-menu.js +4 -1
  267. package/dist/ui/dialog.d.ts +2 -1
  268. package/dist/ui/dialog.js +4 -23
  269. package/dist/ui/dockable-panel-geometry.d.ts +1 -0
  270. package/dist/ui/dockable-panel-geometry.js +7 -1
  271. package/dist/ui/dockable-panel.d.ts +9 -6
  272. package/dist/ui/dockable-panel.js +63 -23
  273. package/dist/ui/drawer.js +1 -1
  274. package/dist/ui/dropdown-menu.js +5 -4
  275. package/dist/ui/dropzone.d.ts +2 -2
  276. package/dist/ui/dropzone.js +75 -16
  277. package/dist/ui/emoji-picker.d.ts +18 -0
  278. package/dist/ui/emoji-picker.js +136 -0
  279. package/dist/ui/empty.d.ts +3 -1
  280. package/dist/ui/empty.js +12 -7
  281. package/dist/ui/gradient-editor.d.ts +1 -1
  282. package/dist/ui/gradient-editor.js +37 -21
  283. package/dist/ui/hover-card.js +1 -1
  284. package/dist/ui/infinite-canvas.d.ts +3 -2
  285. package/dist/ui/infinite-canvas.js +63 -19
  286. package/dist/ui/input-group.d.ts +0 -3
  287. package/dist/ui/input-group.js +4 -14
  288. package/dist/ui/input-otp.d.ts +1 -1
  289. package/dist/ui/input-otp.js +32 -15
  290. package/dist/ui/input.js +4 -4
  291. package/dist/ui/item.d.ts +2 -1
  292. package/dist/ui/item.js +12 -8
  293. package/dist/ui/live-status.d.ts +7 -0
  294. package/dist/ui/live-status.js +17 -0
  295. package/dist/ui/markdown-elements.d.ts +6 -0
  296. package/dist/ui/markdown-elements.js +31 -1
  297. package/dist/ui/menubar.js +3 -3
  298. package/dist/ui/morphing-panel.js +3 -2
  299. package/dist/ui/native-select.d.ts +1 -1
  300. package/dist/ui/native-select.js +24 -11
  301. package/dist/ui/navigation-menu.js +1 -1
  302. package/dist/ui/page-layout.d.ts +2 -2
  303. package/dist/ui/page-layout.js +2 -4
  304. package/dist/ui/pagination.d.ts +7 -5
  305. package/dist/ui/pagination.js +19 -13
  306. package/dist/ui/phone-input.d.ts +1 -0
  307. package/dist/ui/phone-input.js +58 -60
  308. package/dist/ui/popover.js +1 -1
  309. package/dist/ui/radio-group.d.ts +2 -0
  310. package/dist/ui/resizable.d.ts +3 -2
  311. package/dist/ui/resizable.js +5 -3
  312. package/dist/ui/responsive-dialog.d.ts +1 -1
  313. package/dist/ui/responsive-dialog.js +4 -21
  314. package/dist/ui/rich-tooltip.d.ts +10 -4
  315. package/dist/ui/rich-tooltip.js +82 -34
  316. package/dist/ui/scroll-area.d.ts +1 -1
  317. package/dist/ui/scroll-area.js +43 -28
  318. package/dist/ui/select.js +1 -1
  319. package/dist/ui/sheet.js +1 -1
  320. package/dist/ui/sidebar-menu.d.ts +5 -1
  321. package/dist/ui/sidebar-menu.js +32 -34
  322. package/dist/ui/sidebar-provider.d.ts +10 -1
  323. package/dist/ui/sidebar-provider.js +39 -14
  324. package/dist/ui/sidebar-resize-rail.js +8 -4
  325. package/dist/ui/sidebar.d.ts +11 -6
  326. package/dist/ui/sidebar.js +47 -24
  327. package/dist/ui/slider.js +31 -22
  328. package/dist/ui/spinner.d.ts +3 -2
  329. package/dist/ui/spinner.js +3 -2
  330. package/dist/ui/stepper.d.ts +8 -1
  331. package/dist/ui/stepper.js +74 -25
  332. package/dist/ui/switch.d.ts +1 -0
  333. package/dist/ui/table-of-contents.d.ts +2 -2
  334. package/dist/ui/table-of-contents.js +20 -8
  335. package/dist/ui/table.js +4 -2
  336. package/dist/ui/textarea.d.ts +1 -1
  337. package/dist/ui/textarea.js +20 -9
  338. package/dist/ui/timeline.d.ts +3 -1
  339. package/dist/ui/timeline.js +17 -9
  340. package/dist/ui/toast.d.ts +11 -5
  341. package/dist/ui/toast.js +101 -55
  342. package/dist/ui/toolbar.d.ts +3 -2
  343. package/dist/ui/toolbar.js +4 -2
  344. package/dist/ui/tooltip.js +4 -21
  345. package/dist/ui/tree.js +121 -81
  346. package/dist/ui/trigger-menu.js +2 -1
  347. package/dist/user-ask.d.ts +10 -5
  348. package/dist/user-ask.js +32 -12
  349. package/package.json +5 -1
  350. package/dist/hooks/use-keyboard-navigation.d.ts +0 -3
  351. package/dist/hooks/use-keyboard-navigation.js +0 -22
@@ -9,9 +9,10 @@ export type SettingsBlockProps = Omit<ComponentProps<"div">, "children"> & {
9
9
  onActivePageChange?: (pageId: string) => void;
10
10
  backAction?: ReactNode;
11
11
  searchPlaceholder?: string;
12
+ searchLabel?: string;
12
13
  layout?: "viewport" | "contained";
13
14
  };
14
- export declare function SettingsBlock({ pages, activePageId, defaultActivePageId, onActivePageChange, backAction, searchPlaceholder, layout, className, style, ...props }: SettingsBlockProps): import("react").JSX.Element;
15
+ export declare function SettingsBlock({ pages, activePageId, defaultActivePageId, onActivePageChange, backAction, searchPlaceholder, searchLabel, layout, className, style, ...props }: SettingsBlockProps): import("react").JSX.Element;
15
16
  export declare function SettingsBackAction({ children, ...props }: Omit<ComponentProps<typeof Button>, "children"> & {
16
17
  children?: ReactNode;
17
18
  }): import("react").JSX.Element;
@@ -1,19 +1,22 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { Button } from "../ui/button.js";
4
+ import { LiveStatus } from "../ui/live-status.js";
4
5
  import { ScrollArea } from "../ui/scroll-area.js";
5
- import { InputGroup, InputGroupAddon, InputGroupInput } from "../ui/input-group.js";
6
- import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "../ui/empty.js";
7
- import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet } from "../ui/field.js";
6
+ import { prefersReducedMotion } from "../lib/motion.js";
7
+ import { InputGroup, InputGroupAddon } from "../ui/input-group.js";
8
+ import { Input } from "../ui/input.js";
8
9
  import { SidebarProvider, useSidebar } from "../ui/sidebar-provider.js";
9
10
  import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "../ui/sidebar-menu.js";
10
11
  import { Sidebar, SidebarContent, SidebarHeader, SidebarInset, SidebarTrigger } from "../ui/sidebar.js";
12
+ import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "../ui/empty.js";
11
13
  import { searchSettings } from "./settings-data.js";
14
+ import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet } from "../ui/field.js";
12
15
  import { useEffect, useId, useState } from "react";
13
16
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
14
17
  import { ArrowLeftIcon, SearchIcon, XIcon } from "lucide-react";
15
18
  //#region src/blocks/settings.tsx
16
- function SettingsBlock({ pages, activePageId, defaultActivePageId, onActivePageChange, backAction, searchPlaceholder = "Search settings", layout = "viewport", className, style, ...props }) {
19
+ function SettingsBlock({ pages, activePageId, defaultActivePageId, onActivePageChange, backAction, searchPlaceholder = "Search settings", searchLabel = "Search settings", layout = "viewport", className, style, ...props }) {
17
20
  const [internalPageId, setInternalPageId] = useState(defaultActivePageId ?? pages[0]?.id);
18
21
  const [query, setQuery] = useState("");
19
22
  const [pendingTarget, setPendingTarget] = useState(null);
@@ -30,8 +33,8 @@ function SettingsBlock({ pages, activePageId, defaultActivePageId, onActivePageC
30
33
  ...style
31
34
  };
32
35
  return /* @__PURE__ */ jsx(SidebarProvider, {
33
- "data-settings-layout": layout,
34
- className: cn("overflow-hidden bg-background text-foreground", layout === "viewport" ? "h-svh min-h-svh" : "relative h-full min-h-0", className),
36
+ layout,
37
+ className: cn("bg-background text-foreground", layout === "viewport" && "h-svh overflow-hidden", className),
35
38
  style: providerStyle,
36
39
  ...props,
37
40
  children: /* @__PURE__ */ jsx(SettingsShell, {
@@ -41,7 +44,7 @@ function SettingsBlock({ pages, activePageId, defaultActivePageId, onActivePageC
41
44
  results,
42
45
  backAction,
43
46
  searchPlaceholder,
44
- contained: layout === "contained",
47
+ searchLabel,
45
48
  instanceId,
46
49
  pendingTarget,
47
50
  onPendingTargetChange: setPendingTarget,
@@ -50,7 +53,7 @@ function SettingsBlock({ pages, activePageId, defaultActivePageId, onActivePageC
50
53
  })
51
54
  });
52
55
  }
53
- function SettingsShell({ pages, currentPage, query, results, backAction, searchPlaceholder, contained, instanceId, pendingTarget, onPendingTargetChange, onPageChange, onQueryChange }) {
56
+ function SettingsShell({ pages, currentPage, query, results, backAction, searchPlaceholder, searchLabel, instanceId, pendingTarget, onPendingTargetChange, onPageChange, onQueryChange }) {
54
57
  const { setOpenMobile } = useSidebar();
55
58
  useEffect(() => {
56
59
  if (!pendingTarget || currentPage?.id !== pendingTarget.pageId) return;
@@ -59,7 +62,7 @@ function SettingsShell({ pages, currentPage, query, results, backAction, searchP
59
62
  const control = document.getElementById(pendingTarget.controlId);
60
63
  row?.scrollIntoView({
61
64
  block: "center",
62
- behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth"
65
+ behavior: prefersReducedMotion(row) ? "auto" : "smooth"
63
66
  });
64
67
  (control instanceof HTMLElement ? control : row)?.focus({ preventScroll: true });
65
68
  onPendingTargetChange(null);
@@ -83,62 +86,71 @@ function SettingsShell({ pages, currentPage, query, results, backAction, searchP
83
86
  });
84
87
  navigate(result.page.id);
85
88
  }
86
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Sidebar, {
87
- collapsible: "offcanvas",
88
- className: contained ? "absolute! inset-y-0! h-full" : void 0,
89
- children: [/* @__PURE__ */ jsxs(SidebarHeader, {
90
- className: "gap-3 border-b border-sidebar-border p-3",
91
- children: [backAction ? /* @__PURE__ */ jsx("div", {
92
- className: "min-w-0",
93
- children: backAction
94
- }) : null, /* @__PURE__ */ jsx(SettingsSearch, {
95
- value: query,
96
- placeholder: searchPlaceholder,
97
- onValueChange: onQueryChange
98
- })]
99
- }), /* @__PURE__ */ jsx(SidebarContent, { children: query.trim() ? /* @__PURE__ */ jsx(SettingsSearchNavigation, {
100
- results,
101
- onSelect: openResult
102
- }) : /* @__PURE__ */ jsx(SettingsPageNavigation, {
103
- pages,
104
- activePageId: currentPage?.id,
105
- onSelect: navigate
106
- }) })]
107
- }), /* @__PURE__ */ jsxs(SidebarInset, {
108
- className: "h-full min-h-0 min-w-0",
109
- children: [/* @__PURE__ */ jsxs("header", {
110
- className: "flex h-12 shrink-0 items-center gap-2 border-b px-3 md:hidden",
111
- children: [/* @__PURE__ */ jsx(SidebarTrigger, {}), /* @__PURE__ */ jsx("span", {
112
- className: "truncate text-label font-medium",
113
- children: currentPage?.title ?? "Settings"
89
+ const trimmedQuery = query.trim();
90
+ const resultCount = `${results.length} ${results.length === 1 ? "setting" : "settings"} found`;
91
+ const searchStatus = results.length > 0 ? resultCount : "No matching settings";
92
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
93
+ /* @__PURE__ */ jsx(LiveStatus, { message: trimmedQuery ? searchStatus : "" }),
94
+ /* @__PURE__ */ jsxs(Sidebar, {
95
+ collapsible: "offcanvas",
96
+ children: [/* @__PURE__ */ jsxs(SidebarHeader, {
97
+ className: "gap-3 border-b border-sidebar-border p-3",
98
+ children: [backAction ? /* @__PURE__ */ jsx("div", {
99
+ className: "min-w-0",
100
+ children: backAction
101
+ }) : null, /* @__PURE__ */ jsx(SettingsSearch, {
102
+ value: query,
103
+ label: searchLabel,
104
+ placeholder: searchPlaceholder,
105
+ onValueChange: onQueryChange
106
+ })]
107
+ }), /* @__PURE__ */ jsx(SidebarContent, { children: trimmedQuery ? /* @__PURE__ */ jsx(SettingsSearchNavigation, {
108
+ results,
109
+ onSelect: openResult,
110
+ onClear: () => onQueryChange("")
111
+ }) : /* @__PURE__ */ jsx(SettingsPageNavigation, {
112
+ pages,
113
+ activePageId: currentPage?.id,
114
+ onSelect: navigate
115
+ }) })]
116
+ }),
117
+ /* @__PURE__ */ jsxs(SidebarInset, {
118
+ className: "h-full min-h-0 min-w-0",
119
+ children: [/* @__PURE__ */ jsxs("header", {
120
+ className: "flex h-12 shrink-0 items-center gap-2 border-b px-3 lg:hidden",
121
+ children: [/* @__PURE__ */ jsx(SidebarTrigger, {}), /* @__PURE__ */ jsx("span", {
122
+ className: "truncate text-label font-medium",
123
+ title: currentPage?.title,
124
+ children: currentPage?.title ?? "Settings"
125
+ })]
126
+ }), /* @__PURE__ */ jsx(ScrollArea, {
127
+ className: "min-h-0 flex-1",
128
+ lockAxis: "x",
129
+ children: currentPage ? /* @__PURE__ */ jsx(SettingsPage, {
130
+ page: currentPage,
131
+ instanceId
132
+ }) : /* @__PURE__ */ jsx(SettingsEmpty, {
133
+ title: "No settings",
134
+ description: "Add a settings page to populate this space."
135
+ })
114
136
  })]
115
- }), /* @__PURE__ */ jsx(ScrollArea, {
116
- className: "min-h-0 flex-1",
117
- lockAxis: "x",
118
- children: currentPage ? /* @__PURE__ */ jsx(SettingsPage, {
119
- page: currentPage,
120
- instanceId
121
- }) : /* @__PURE__ */ jsx(SettingsEmpty, {
122
- title: "No settings",
123
- description: "Add a settings page to populate this space."
124
- })
125
- })]
126
- })] });
137
+ })
138
+ ] });
127
139
  }
128
- function SettingsSearch({ value, placeholder, onValueChange }) {
140
+ function SettingsSearch({ value, label, placeholder, onValueChange }) {
129
141
  return /* @__PURE__ */ jsxs(InputGroup, {
130
142
  size: "sm",
131
143
  children: [
132
144
  /* @__PURE__ */ jsx(InputGroupAddon, {
133
- className: "pl-2.5",
145
+ className: "ps-2.5",
134
146
  children: /* @__PURE__ */ jsx(SearchIcon, {
135
147
  className: "size-3.5",
136
148
  "aria-hidden": "true"
137
149
  })
138
150
  }),
139
- /* @__PURE__ */ jsx(InputGroupInput, {
151
+ /* @__PURE__ */ jsx(Input, {
140
152
  type: "search",
141
- "aria-label": placeholder,
153
+ "aria-label": label,
142
154
  placeholder,
143
155
  value,
144
156
  onChange: (event) => onValueChange(event.currentTarget.value)
@@ -148,9 +160,12 @@ function SettingsSearch({ value, placeholder, onValueChange }) {
148
160
  size: "xs",
149
161
  iconOnly: true,
150
162
  "aria-label": "Clear settings search",
151
- className: "mr-1",
163
+ className: "me-1",
152
164
  onClick: () => onValueChange(""),
153
- children: /* @__PURE__ */ jsx(XIcon, { className: "size-3.5" })
165
+ children: /* @__PURE__ */ jsx(XIcon, {
166
+ className: "size-3.5",
167
+ "aria-hidden": "true"
168
+ })
154
169
  }) : null
155
170
  ]
156
171
  });
@@ -163,10 +178,16 @@ function SettingsPageNavigation({ pages, activePageId, onSelect }) {
163
178
  children: [page.icon, /* @__PURE__ */ jsx("span", { children: page.title })]
164
179
  }) }, page.id)) })] });
165
180
  }
166
- function SettingsSearchNavigation({ results, onSelect }) {
181
+ function SettingsSearchNavigation({ results, onSelect, onClear }) {
167
182
  if (results.length === 0) return /* @__PURE__ */ jsx(SettingsEmpty, {
168
183
  title: "No matching settings",
169
- description: "Try another name or keyword."
184
+ description: "Try another name or keyword.",
185
+ action: /* @__PURE__ */ jsx(Button, {
186
+ variant: "surface",
187
+ size: "sm",
188
+ onClick: onClear,
189
+ children: "Clear search"
190
+ })
170
191
  });
171
192
  const groups = /* @__PURE__ */ new Map();
172
193
  for (const result of results) {
@@ -177,28 +198,22 @@ function SettingsSearchNavigation({ results, onSelect }) {
177
198
  group.results.push(result);
178
199
  groups.set(result.page.id, group);
179
200
  }
180
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
181
- className: "sr-only",
182
- role: "status",
183
- "aria-live": "polite",
184
- children: [
185
- results.length,
186
- " ",
187
- results.length === 1 ? "setting" : "settings",
188
- " found"
189
- ]
190
- }), [...groups.values()].map((group) => /* @__PURE__ */ jsxs(SidebarGroup, { children: [/* @__PURE__ */ jsxs(SidebarGroupLabel, { children: [group.page.icon, /* @__PURE__ */ jsx("span", { children: group.page.title })] }), /* @__PURE__ */ jsx(SidebarMenu, { children: group.results.map((result) => /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(SidebarMenuButton, {
201
+ return /* @__PURE__ */ jsx(Fragment$1, { children: [...groups.values()].map((group) => /* @__PURE__ */ jsxs(SidebarGroup, { children: [/* @__PURE__ */ jsxs(SidebarGroupLabel, { children: [group.page.icon, /* @__PURE__ */ jsx("span", { children: group.page.title })] }), /* @__PURE__ */ jsx(SidebarMenu, { children: group.results.map((result) => /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(SidebarMenuButton, {
191
202
  size: "sm",
192
- className: "h-auto min-h-[var(--control-h-sm)] py-1.5",
203
+ className: "h-auto min-h-(--control-h-sm) py-1.5",
193
204
  onClick: () => onSelect(result),
194
- children: /* @__PURE__ */ jsx("span", { children: result.setting.title })
195
- }) }, `${result.section.id}/${result.setting.id}`)) })] }, group.page.id))] });
205
+ children: /* @__PURE__ */ jsx("span", {
206
+ title: result.setting.title,
207
+ children: result.setting.title
208
+ })
209
+ }) }, `${result.section.id}/${result.setting.id}`)) })] }, group.page.id)) });
196
210
  }
197
211
  function SettingsPage({ page, instanceId }) {
212
+ const { layout } = useSidebar();
198
213
  return /* @__PURE__ */ jsxs("div", {
199
214
  className: "mx-auto flex w-full max-w-4xl flex-col gap-10 px-5 py-8 md:px-10 md:py-12",
200
- children: [/* @__PURE__ */ jsx("h1", {
201
- className: "text-heading font-semibold text-foreground text-balance",
215
+ children: [/* @__PURE__ */ jsx(layout === "contained" ? "h2" : "h1", {
216
+ className: "text-heading-2 text-foreground text-balance",
202
217
  children: page.title
203
218
  }), page.sections.map((section) => /* @__PURE__ */ jsxs(FieldSet, {
204
219
  className: "gap-3",
@@ -244,17 +259,20 @@ function SettingsRow({ pageId, sectionId, setting, instanceId, separated }) {
244
259
  })]
245
260
  })] });
246
261
  }
247
- function SettingsEmpty({ title, description }) {
248
- return /* @__PURE__ */ jsx(Empty, {
262
+ function SettingsEmpty({ title, description, action }) {
263
+ return /* @__PURE__ */ jsxs(Empty, {
249
264
  className: "min-h-40 gap-3 p-4",
250
- children: /* @__PURE__ */ jsxs(EmptyHeader, { children: [
265
+ children: [/* @__PURE__ */ jsxs(EmptyHeader, { children: [
251
266
  /* @__PURE__ */ jsx(EmptyMedia, {
252
267
  className: "mb-0 size-8",
253
- children: /* @__PURE__ */ jsx(SearchIcon, { className: "size-4" })
268
+ children: /* @__PURE__ */ jsx(SearchIcon, {
269
+ className: "size-4",
270
+ "aria-hidden": "true"
271
+ })
254
272
  }),
255
273
  /* @__PURE__ */ jsx(EmptyTitle, { children: title }),
256
274
  /* @__PURE__ */ jsx(EmptyDescription, { children: description })
257
- ] })
275
+ ] }), action ? /* @__PURE__ */ jsx(EmptyContent, { children: action }) : null]
258
276
  });
259
277
  }
260
278
  function settingDomIds(instanceId, pageId, sectionId, settingId) {
@@ -275,9 +293,13 @@ function SettingsBackAction({ children = "Back to app", ...props }) {
275
293
  return /* @__PURE__ */ jsxs(Button, {
276
294
  variant: "quiet",
277
295
  size: "sm",
278
- className: "-ml-1 justify-start gap-2",
296
+ className: "-ms-1 justify-start gap-2",
279
297
  ...props,
280
- children: [/* @__PURE__ */ jsx(ArrowLeftIcon, { className: "size-4" }), children]
298
+ children: [/* @__PURE__ */ jsx(ArrowLeftIcon, {
299
+ className: "size-4",
300
+ "data-icon-dir": "inline",
301
+ "aria-hidden": "true"
302
+ }), children]
281
303
  });
282
304
  }
283
305
  //#endregion
@@ -0,0 +1,12 @@
1
+ import { SidebarProvider } from "../ui/sidebar-provider.js";
2
+ import "../ui/sidebar.js";
3
+ import { ComponentProps, ReactNode } from "react";
4
+ //#region src/blocks/team-chat.d.ts
5
+ export type TeamChatBlockProps = Omit<ComponentProps<typeof SidebarProvider>, "children"> & {
6
+ sidebar: ReactNode;
7
+ children: ReactNode;
8
+ thread?: ReactNode;
9
+ threadLabel?: string;
10
+ };
11
+ export declare function TeamChatBlock({ sidebar, children, thread, threadLabel, className, ...props }: TeamChatBlockProps): import("react").JSX.Element;
12
+ //#endregion
@@ -0,0 +1,26 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { SidebarProvider } from "../ui/sidebar-provider.js";
4
+ import { SidebarInset } from "../ui/sidebar.js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/blocks/team-chat.tsx
7
+ function TeamChatBlock({ sidebar, children, thread, threadLabel = "Thread", className, ...props }) {
8
+ const threadIsOpen = thread !== void 0 && thread !== null;
9
+ return /* @__PURE__ */ jsxs(SidebarProvider, {
10
+ className: cn("bg-background text-foreground", className),
11
+ ...props,
12
+ children: [sidebar, /* @__PURE__ */ jsxs(SidebarInset, {
13
+ className: "@container/team-chat flex h-full min-h-0 min-w-0 flex-row",
14
+ children: [/* @__PURE__ */ jsx("div", {
15
+ className: cn("flex min-h-0 min-w-0 flex-1 flex-col", threadIsOpen && "@max-3xl/team-chat:hidden"),
16
+ children
17
+ }), threadIsOpen ? /* @__PURE__ */ jsx("aside", {
18
+ "aria-label": threadLabel,
19
+ className: "flex min-h-0 w-full min-w-0 flex-col border-border @3xl/team-chat:w-[min(26rem,45%)] @3xl/team-chat:shrink-0 @3xl/team-chat:border-s",
20
+ children: thread
21
+ }) : null]
22
+ })]
23
+ });
24
+ }
25
+ //#endregion
26
+ export { TeamChatBlock };
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { Button } from "../ui/button.js";
3
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
3
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
4
4
  import { Switch } from "../ui/switch.js";
5
5
  import { useId } from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
- import { CheckIcon, ChevronDownIcon, MonitorIcon, MoonIcon, SunIcon } from "lucide-react";
7
+ import { ChevronDownIcon, MonitorIcon, MoonIcon, SunIcon } from "lucide-react";
8
8
  //#region src/blocks/theme-toggle.tsx
9
9
  const systemThemeOption = {
10
10
  value: "system",
@@ -53,7 +53,7 @@ function ThemeSwitch({ value, onValueChange, onValue = "dark", offValue = "light
53
53
  checkedIcon: checkedIcon ?? defaultSwitchCheckedIcon,
54
54
  uncheckedIcon: uncheckedIcon ?? defaultSwitchUncheckedIcon,
55
55
  onCheckedChange: (checked) => onValueChange(checked ? onValue : offValue),
56
- "aria-label": ariaLabel ?? "Dark mode",
56
+ "aria-label": ariaLabel ?? `${currentThemeOption(onValue, defaultThemeOptions).label} mode`,
57
57
  ...props
58
58
  });
59
59
  }
@@ -61,10 +61,9 @@ function ThemeSegmentedSwitch({ value, onValueChange, options = defaultThemeOpti
61
61
  const generatedName = useId();
62
62
  const choices = controlOptions(options);
63
63
  const currentValue = effectiveThemeValue(value, choices);
64
- const activeIndex = Math.max(0, choices.findIndex((option) => option.value === currentValue));
65
64
  const indicatorStyle = {
66
- width: `calc((100% - var(--_theme-switch-padding) * 2) / ${choices.length})`,
67
- transform: `translateX(${activeIndex * 100}%)`
65
+ "--_theme-switch-index": Math.max(0, choices.findIndex((option) => option.value === currentValue)),
66
+ width: `calc((100% - var(--_theme-switch-padding) * 2) / ${choices.length})`
68
67
  };
69
68
  return /* @__PURE__ */ jsxs("div", {
70
69
  role: "radiogroup",
@@ -80,7 +79,7 @@ function ThemeSegmentedSwitch({ value, onValueChange, options = defaultThemeOpti
80
79
  "data-control-ui": "theme-toggle",
81
80
  "data-control-family": "theme-toggle",
82
81
  "data-slot": "indicator",
83
- className: "pointer-events-none absolute inset-y-(--_theme-switch-padding) left-(--_theme-switch-padding) rounded-(--_theme-switch-radius) bg-background shadow-(--shadow-sm) transition-transform duration-(--duration-base) ease-(--ease-standard)",
82
+ className: "pointer-events-none absolute inset-y-(--_theme-switch-padding) start-(--_theme-switch-padding) translate-x-[calc(var(--_theme-switch-index)*100%)] rounded-(--_theme-switch-radius) bg-background shadow-(--shadow-sm) transition-transform duration-(--duration-base) ease-(--ease-standard) rtl:translate-x-[calc(var(--_theme-switch-index)*-100%)]",
84
83
  style: indicatorStyle
85
84
  }), choices.map((option) => {
86
85
  const selected = option.value === currentValue;
@@ -92,7 +91,7 @@ function ThemeSegmentedSwitch({ value, onValueChange, options = defaultThemeOpti
92
91
  "data-control": "true",
93
92
  "data-selected": selected ? "true" : void 0,
94
93
  "data-disabled": option.disabled ? "true" : void 0,
95
- className: classes("relative z-[1] inline-flex h-(--control-h-xs) cursor-pointer items-center justify-center gap-1.5 rounded-(--_theme-switch-radius) px-1.5 text-label font-medium transition-colors duration-(--duration-fast) ease-(--ease-standard) hover:text-foreground data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-45 data-[selected=true]:text-foreground", showLabels ? "min-w-20" : "w-(--control-h-xs)"),
94
+ className: classes("relative z-[1] inline-flex h-(--control-h-xs) cursor-pointer items-center justify-center gap-1.5 rounded-(--_theme-switch-radius) px-1.5 text-label font-medium transition-colors duration-(--duration-fast) ease-(--ease-standard) not-data-[disabled=true]:hover:text-foreground data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-(--disabled-opacity) data-[selected=true]:text-foreground", showLabels ? "min-w-20" : "w-(--control-h-xs)"),
96
95
  children: [
97
96
  /* @__PURE__ */ jsx("input", {
98
97
  type: "radio",
@@ -140,7 +139,7 @@ function ThemeDropdown({ value, onValueChange, options = defaultThemeOptions, la
140
139
  const current = currentThemeOption(value, options);
141
140
  const CurrentIcon = current.icon;
142
141
  return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
143
- "aria-label": ariaLabel ?? label,
142
+ "aria-label": ariaLabel ?? `${label}: ${current.label}`,
144
143
  className: classes("min-w-36 gap-2", className),
145
144
  ...props,
146
145
  children: [
@@ -157,33 +156,26 @@ function ThemeDropdown({ value, onValueChange, options = defaultThemeOptions, la
157
156
  "aria-hidden": true
158
157
  })
159
158
  ]
160
- }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
159
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, {
161
160
  className: "min-w-40",
162
- children: [/* @__PURE__ */ jsx(DropdownMenuLabel, { children: label }), options.map((option) => {
163
- const selected = option.value === value;
164
- const Icon = option.icon;
165
- return /* @__PURE__ */ jsxs(DropdownMenuItem, {
166
- disabled: option.disabled,
167
- onClick: () => onValueChange(option.value),
168
- children: [
169
- /* @__PURE__ */ jsx(Icon, {
161
+ children: /* @__PURE__ */ jsxs(DropdownMenuRadioGroup, {
162
+ value,
163
+ onValueChange,
164
+ children: [/* @__PURE__ */ jsx(DropdownMenuLabel, { children: label }), options.map((option) => {
165
+ const Icon = option.icon;
166
+ return /* @__PURE__ */ jsxs(DropdownMenuRadioItem, {
167
+ value: option.value,
168
+ disabled: option.disabled,
169
+ children: [/* @__PURE__ */ jsx(Icon, {
170
170
  className: "size-3.5",
171
171
  "aria-hidden": true
172
- }),
173
- /* @__PURE__ */ jsx("span", {
172
+ }), /* @__PURE__ */ jsx("span", {
174
173
  className: "min-w-0 flex-1 truncate",
175
174
  children: option.label
176
- }),
177
- selected ? /* @__PURE__ */ jsx(CheckIcon, {
178
- className: "size-3.5",
179
- "aria-hidden": true
180
- }) : /* @__PURE__ */ jsx("span", {
181
- className: "size-3.5",
182
- "aria-hidden": true
183
- })
184
- ]
185
- }, option.value);
186
- })]
175
+ })]
176
+ }, option.value);
177
+ })]
178
+ })
187
179
  })] });
188
180
  }
189
181
  //#endregion
@@ -30,9 +30,12 @@ export type ChatComposerAttachmentProps = Omit<ComponentProps<"li">, "style"> &
30
30
  variant?: ChatComposerAttachmentVariant;
31
31
  onRemove?: () => void;
32
32
  removeLabel?: string;
33
+ errorLabel?: string;
34
+ uploadedLabel?: (name: string) => string;
35
+ uploadFailedLabel?: (name: string) => string;
33
36
  style?: CSSProperties & ChatComposerAttachmentKnobStyle;
34
37
  };
35
- export declare function ChatComposerAttachment({ file, name: nameInput, type: typeInput, description, status, progress: progressInput, previewUrl: previewUrlInput, kind, variant, onRemove, removeLabel, className, children, "aria-label": ariaLabel, ...props }: ChatComposerAttachmentProps): import("react").JSX.Element;
38
+ export declare function ChatComposerAttachment({ file, name: nameInput, type: typeInput, description, status, progress: progressInput, previewUrl: previewUrlInput, kind, variant, onRemove, removeLabel, errorLabel, uploadedLabel, uploadFailedLabel, className, children, "aria-label": ariaLabel, ...props }: ChatComposerAttachmentProps): import("react").JSX.Element;
36
39
  export type ChatComposerAttachmentPreviewProps = Omit<ComponentProps<"div">, "style"> & {
37
40
  style?: CSSProperties & ChatComposerAttachmentKnobStyle;
38
41
  };
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "./lib/cn.js";
3
3
  import { Button } from "./ui/button.js";
4
+ import { LiveStatus } from "./ui/live-status.js";
4
5
  import { ScrollArea } from "./ui/scroll-area.js";
5
6
  import { Spinner } from "./ui/spinner.js";
6
7
  import { createContext, useContext, useLayoutEffect, useState } from "react";
@@ -106,9 +107,27 @@ function defaultDescription(context) {
106
107
  const { progress } = context;
107
108
  if (context.description) return context.description;
108
109
  if (context.status === "uploading") return progress === void 0 ? "Uploading…" : `Uploading ${Math.round(progress)}%`;
109
- if (context.status === "error") return "Upload failed";
110
+ if (context.status === "error") return context.errorLabel;
110
111
  return fallbackLabel(context.kind, context.extension);
111
112
  }
113
+ function defaultUploadedLabel(name) {
114
+ return `${name} uploaded`;
115
+ }
116
+ function defaultUploadFailedLabel(name) {
117
+ return `${name} couldn't upload`;
118
+ }
119
+ function useUploadAnnouncement(status, name, uploadedLabel, uploadFailedLabel) {
120
+ const [previousStatus, setPreviousStatus] = useState(status);
121
+ const [announcement, setAnnouncement] = useState("");
122
+ if (status !== previousStatus) {
123
+ setPreviousStatus(status);
124
+ setAnnouncement({
125
+ uploaded: uploadedLabel,
126
+ error: uploadFailedLabel
127
+ }[status]?.(name) ?? "");
128
+ }
129
+ return announcement;
130
+ }
112
131
  function ChatComposerAttachments({ label = "Attachments", className, viewportClassName, children, ...props }) {
113
132
  return /* @__PURE__ */ jsx("div", {
114
133
  "data-control-ui": "chat-composer-attachments",
@@ -119,12 +138,12 @@ function ChatComposerAttachments({ label = "Attachments", className, viewportCla
119
138
  children: /* @__PURE__ */ jsx(ScrollArea, {
120
139
  "data-control-ui": "chat-composer-attachments",
121
140
  "data-slot": "scroll",
122
- "aria-label": label,
123
141
  lockAxis: "y",
124
142
  mask: true,
125
143
  className: "w-full",
126
144
  viewportClassName,
127
145
  children: /* @__PURE__ */ jsx("ul", {
146
+ "aria-label": label,
128
147
  "data-control-ui": "chat-composer-attachments",
129
148
  "data-control-family": "chat-composer-attachment",
130
149
  "data-slot": "list",
@@ -134,7 +153,7 @@ function ChatComposerAttachments({ label = "Attachments", className, viewportCla
134
153
  })
135
154
  });
136
155
  }
137
- function ChatComposerAttachment({ file, name: nameInput, type: typeInput, description, status = "idle", progress: progressInput, previewUrl: previewUrlInput, kind, variant = "auto", onRemove, removeLabel, className, children, "aria-label": ariaLabel, ...props }) {
156
+ function ChatComposerAttachment({ file, name: nameInput, type: typeInput, description, status = "idle", progress: progressInput, previewUrl: previewUrlInput, kind, variant = "auto", onRemove, removeLabel, errorLabel = "Couldn't upload. Remove and try again.", uploadedLabel = defaultUploadedLabel, uploadFailedLabel = defaultUploadFailedLabel, className, children, "aria-label": ariaLabel, ...props }) {
138
157
  const name = nameInput ?? file?.name ?? "";
139
158
  const type = typeInput ?? file?.type;
140
159
  const resolvedKind = kind ?? kindFromAttachment(type, name);
@@ -143,6 +162,7 @@ function ChatComposerAttachment({ file, name: nameInput, type: typeInput, descri
143
162
  const previewUrl = previewUrlInput ?? filePreviewUrl;
144
163
  const extension = extensionFromName(name);
145
164
  const progress = progressValue(progressInput);
165
+ const announcement = useUploadAnnouncement(status, name, uploadedLabel, uploadFailedLabel);
146
166
  return /* @__PURE__ */ jsx(ChatComposerAttachmentContext.Provider, {
147
167
  value: {
148
168
  name,
@@ -155,9 +175,10 @@ function ChatComposerAttachment({ file, name: nameInput, type: typeInput, descri
155
175
  variant: resolvedVariant,
156
176
  extension,
157
177
  onRemove,
158
- removeLabel
178
+ removeLabel,
179
+ errorLabel
159
180
  },
160
- children: /* @__PURE__ */ jsx("li", {
181
+ children: /* @__PURE__ */ jsxs("li", {
161
182
  "aria-label": ariaLabel ?? name,
162
183
  "data-control-ui": "chat-composer-attachment",
163
184
  "data-control-family": "chat-composer-attachment",
@@ -168,12 +189,12 @@ function ChatComposerAttachment({ file, name: nameInput, type: typeInput, descri
168
189
  "data-variant": resolvedVariant,
169
190
  className: cn("relative isolate flex shrink-0 items-center overflow-hidden", className),
170
191
  ...props,
171
- children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [
192
+ children: [children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [
172
193
  /* @__PURE__ */ jsx(ChatComposerAttachmentPreview, {}),
173
194
  resolvedVariant === "file" && /* @__PURE__ */ jsxs(ChatComposerAttachmentContent, { children: [/* @__PURE__ */ jsx(ChatComposerAttachmentTitle, {}), /* @__PURE__ */ jsx(ChatComposerAttachmentDescription, {})] }),
174
195
  /* @__PURE__ */ jsx(ChatComposerAttachmentRemove, {}),
175
196
  /* @__PURE__ */ jsx(ChatComposerAttachmentProgress, {})
176
- ] })
197
+ ] }), /* @__PURE__ */ jsx(LiveStatus, { message: announcement })]
177
198
  })
178
199
  });
179
200
  }
@@ -212,25 +233,29 @@ function ChatComposerAttachmentContent({ className, ...props }) {
212
233
  }
213
234
  function ChatComposerAttachmentTitle({ className, children, ...props }) {
214
235
  const { name } = useChatComposerAttachmentContext();
236
+ const content = children ?? name;
215
237
  return /* @__PURE__ */ jsx("div", {
216
238
  "data-control-ui": "chat-composer-attachment",
217
239
  "data-control-family": "chat-composer-attachment",
218
240
  "data-slot": "title",
241
+ title: typeof content === "string" ? content : void 0,
219
242
  className: cn("truncate", className),
220
243
  ...props,
221
- children: children ?? name
244
+ children: content
222
245
  });
223
246
  }
224
247
  function ChatComposerAttachmentDescription({ className, children, ...props }) {
225
248
  const context = useChatComposerAttachmentContext();
249
+ const content = children ?? defaultDescription(context);
226
250
  return /* @__PURE__ */ jsx("div", {
227
251
  "data-control-ui": "chat-composer-attachment",
228
252
  "data-control-family": "chat-composer-attachment",
229
253
  "data-slot": "description",
254
+ title: typeof content === "string" ? content : void 0,
230
255
  className: cn("truncate", className),
231
256
  "data-state": context.status,
232
257
  ...props,
233
- children: children ?? defaultDescription(context)
258
+ children: content
234
259
  });
235
260
  }
236
261
  function ChatComposerAttachmentRemove({ className, onClick, children, "aria-label": ariaLabel, ...props }) {
@@ -248,7 +273,7 @@ function ChatComposerAttachmentRemove({ className, onClick, children, "aria-labe
248
273
  "aria-label": ariaLabel ?? removeLabel ?? `Remove ${name}`,
249
274
  size: "xs",
250
275
  variant: "solid",
251
- className: cn("absolute right-1 top-1 z-10", className),
276
+ className: cn("absolute end-1 top-1 z-10", className),
252
277
  onClick: handleClick,
253
278
  ...props,
254
279
  children: children ?? /* @__PURE__ */ jsx(XIcon, {
@@ -8,6 +8,10 @@ export type MentionExtensionConfig<Item extends TriggerMenuItemData = TriggerMen
8
8
  triggers: readonly TriggerConfig<Item>[];
9
9
  side?: "top" | "bottom";
10
10
  align?: "start" | "center" | "end";
11
+ /** Shown and announced when a trigger's query matches nothing. */
12
+ emptyLabel?: string;
13
+ /** Announced while the menu is open, so opening it and narrowing it are heard from the editor. */
14
+ resultsLabel?: (count: number) => string;
11
15
  };
12
- export declare function mentionExtension<Item extends TriggerMenuItemData = TriggerMenuItemData>({ triggers, side, align }: MentionExtensionConfig<Item>): ChatComposerEditorExtension;
16
+ export declare function mentionExtension<Item extends TriggerMenuItemData = TriggerMenuItemData>({ triggers, side, align, emptyLabel, resultsLabel }: MentionExtensionConfig<Item>): ChatComposerEditorExtension;
13
17
  //#endregion