@burtson-labs/ui 0.14.0 → 0.14.1

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 (239) hide show
  1. package/LICENSES/DEPENDENCIES.txt +1 -1
  2. package/README.md +15 -11
  3. package/dist/components/accordion.d.ts +3 -3
  4. package/dist/components/accordion.js +13 -10
  5. package/dist/components/accordion.js.map +1 -1
  6. package/dist/components/alert-dialog.d.ts +12 -10
  7. package/dist/components/alert-dialog.js +29 -20
  8. package/dist/components/alert-dialog.js.map +1 -1
  9. package/dist/components/alert.d.ts +5 -3
  10. package/dist/components/alert.js +11 -7
  11. package/dist/components/alert.js.map +1 -1
  12. package/dist/components/app-shell.d.ts +7 -6
  13. package/dist/components/app-shell.js +21 -14
  14. package/dist/components/app-shell.js.map +1 -1
  15. package/dist/components/attachment.d.ts +3 -3
  16. package/dist/components/attachment.js +10 -7
  17. package/dist/components/attachment.js.map +1 -1
  18. package/dist/components/audio-player.d.ts +2 -2
  19. package/dist/components/audio-player.js +8 -6
  20. package/dist/components/audio-player.js.map +1 -1
  21. package/dist/components/avatar.d.ts +3 -3
  22. package/dist/components/avatar.js +10 -7
  23. package/dist/components/avatar.js.map +1 -1
  24. package/dist/components/badge.d.ts +1 -1
  25. package/dist/components/badge.js +12 -10
  26. package/dist/components/badge.js.map +1 -1
  27. package/dist/components/breadcrumb.d.ts +8 -8
  28. package/dist/components/breadcrumb.js +24 -17
  29. package/dist/components/breadcrumb.js.map +1 -1
  30. package/dist/components/button.d.ts +8 -1
  31. package/dist/components/button.js +12 -3
  32. package/dist/components/button.js.map +1 -1
  33. package/dist/components/card.d.ts +8 -8
  34. package/dist/components/card.js +32 -23
  35. package/dist/components/card.js.map +1 -1
  36. package/dist/components/chat-history.d.ts +1 -1
  37. package/dist/components/chat-history.js +10 -9
  38. package/dist/components/chat-history.js.map +1 -1
  39. package/dist/components/chat-layout.d.ts +1 -1
  40. package/dist/components/chat-layout.js +7 -3
  41. package/dist/components/chat-layout.js.map +1 -1
  42. package/dist/components/checkbox-card.d.ts +2 -2
  43. package/dist/components/checkbox-card.js +9 -7
  44. package/dist/components/checkbox-card.js.map +1 -1
  45. package/dist/components/checkbox.d.ts +23 -2
  46. package/dist/components/checkbox.js +48 -6
  47. package/dist/components/checkbox.js.map +1 -1
  48. package/dist/components/collapsible.d.ts +2 -2
  49. package/dist/components/collapsible.js +7 -5
  50. package/dist/components/collapsible.js.map +1 -1
  51. package/dist/components/combobox.d.ts +14 -4
  52. package/dist/components/combobox.js +18 -8
  53. package/dist/components/combobox.js.map +1 -1
  54. package/dist/components/command.d.ts +68 -12
  55. package/dist/components/command.js +38 -25
  56. package/dist/components/command.js.map +1 -1
  57. package/dist/components/composer.d.ts +2 -2
  58. package/dist/components/composer.js +9 -7
  59. package/dist/components/composer.js.map +1 -1
  60. package/dist/components/connection-status.d.ts +3 -3
  61. package/dist/components/connection-status.js +18 -14
  62. package/dist/components/connection-status.js.map +1 -1
  63. package/dist/components/context-menu.d.ts +14 -13
  64. package/dist/components/context-menu.js +33 -23
  65. package/dist/components/context-menu.js.map +1 -1
  66. package/dist/components/conversation.d.ts +1 -1
  67. package/dist/components/conversation.js +6 -5
  68. package/dist/components/conversation.js.map +1 -1
  69. package/dist/components/copy-button.d.ts +1 -1
  70. package/dist/components/copy-button.js +3 -2
  71. package/dist/components/copy-button.js.map +1 -1
  72. package/dist/components/data-table.d.ts +7 -2
  73. package/dist/components/data-table.js +16 -42
  74. package/dist/components/data-table.js.map +1 -1
  75. package/dist/components/dialog.d.ts +18 -9
  76. package/dist/components/dialog.js +38 -22
  77. package/dist/components/dialog.js.map +1 -1
  78. package/dist/components/dropdown-menu.d.ts +14 -13
  79. package/dist/components/dropdown-menu.js +33 -23
  80. package/dist/components/dropdown-menu.js.map +1 -1
  81. package/dist/components/editor-tabs.d.ts +1 -1
  82. package/dist/components/editor-tabs.js +6 -5
  83. package/dist/components/editor-tabs.js.map +1 -1
  84. package/dist/components/empty-state.d.ts +5 -5
  85. package/dist/components/empty-state.js +16 -11
  86. package/dist/components/empty-state.js.map +1 -1
  87. package/dist/components/error-summary.d.ts +21 -0
  88. package/dist/components/error-summary.js +62 -0
  89. package/dist/components/error-summary.js.map +1 -0
  90. package/dist/components/field.d.ts +66 -7
  91. package/dist/components/field.js +151 -20
  92. package/dist/components/field.js.map +1 -1
  93. package/dist/components/form-actions.d.ts +18 -0
  94. package/dist/components/form-actions.js +30 -0
  95. package/dist/components/form-actions.js.map +1 -0
  96. package/dist/components/icon-button.d.ts +3 -1
  97. package/dist/components/icon-button.js +5 -3
  98. package/dist/components/icon-button.js.map +1 -1
  99. package/dist/components/input.d.ts +42 -7
  100. package/dist/components/input.js +53 -10
  101. package/dist/components/input.js.map +1 -1
  102. package/dist/components/kbd.d.ts +2 -2
  103. package/dist/components/kbd.js +7 -5
  104. package/dist/components/kbd.js.map +1 -1
  105. package/dist/components/label.d.ts +6 -1
  106. package/dist/components/label.js +10 -4
  107. package/dist/components/label.js.map +1 -1
  108. package/dist/components/markdown.d.ts +4 -3
  109. package/dist/components/markdown.js +7 -6
  110. package/dist/components/markdown.js.map +1 -1
  111. package/dist/components/menubar.d.ts +16 -14
  112. package/dist/components/menubar.js +39 -27
  113. package/dist/components/menubar.js.map +1 -1
  114. package/dist/components/message-actions.d.ts +3 -3
  115. package/dist/components/message-actions.js +15 -12
  116. package/dist/components/message-actions.js.map +1 -1
  117. package/dist/components/message.d.ts +1 -1
  118. package/dist/components/message.js +5 -3
  119. package/dist/components/message.js.map +1 -1
  120. package/dist/components/mobile-nav.d.ts +1 -1
  121. package/dist/components/mobile-nav.js +6 -5
  122. package/dist/components/mobile-nav.js.map +1 -1
  123. package/dist/components/native-select.d.ts +16 -0
  124. package/dist/components/native-select.js +33 -0
  125. package/dist/components/native-select.js.map +1 -0
  126. package/dist/components/navigation-menu.d.ts +12 -11
  127. package/dist/components/navigation-menu.js +32 -23
  128. package/dist/components/navigation-menu.js.map +1 -1
  129. package/dist/components/number-input.d.ts +23 -0
  130. package/dist/components/number-input.js +45 -0
  131. package/dist/components/number-input.js.map +1 -0
  132. package/dist/components/onboarding-checklist.d.ts +1 -1
  133. package/dist/components/onboarding-checklist.js +4 -3
  134. package/dist/components/onboarding-checklist.js.map +1 -1
  135. package/dist/components/page-header.d.ts +7 -6
  136. package/dist/components/page-header.js +22 -15
  137. package/dist/components/page-header.js.map +1 -1
  138. package/dist/components/pagination.d.ts +1 -1
  139. package/dist/components/pagination.js +3 -2
  140. package/dist/components/pagination.js.map +1 -1
  141. package/dist/components/popover.d.ts +13 -10
  142. package/dist/components/popover.js +23 -17
  143. package/dist/components/popover.js.map +1 -1
  144. package/dist/components/progress.d.ts +1 -1
  145. package/dist/components/progress.js +6 -5
  146. package/dist/components/progress.js.map +1 -1
  147. package/dist/components/radio-group.d.ts +22 -3
  148. package/dist/components/radio-group.js +51 -10
  149. package/dist/components/radio-group.js.map +1 -1
  150. package/dist/components/reasoning.d.ts +3 -3
  151. package/dist/components/reasoning.js +9 -7
  152. package/dist/components/reasoning.js.map +1 -1
  153. package/dist/components/scroll-area.d.ts +3 -2
  154. package/dist/components/scroll-area.js +10 -7
  155. package/dist/components/scroll-area.js.map +1 -1
  156. package/dist/components/secret-input.d.ts +3 -2
  157. package/dist/components/secret-input.js +6 -4
  158. package/dist/components/secret-input.js.map +1 -1
  159. package/dist/components/select.d.ts +11 -6
  160. package/dist/components/select.js +24 -18
  161. package/dist/components/select.js.map +1 -1
  162. package/dist/components/separator.d.ts +1 -1
  163. package/dist/components/separator.js +4 -3
  164. package/dist/components/separator.js.map +1 -1
  165. package/dist/components/sheet.d.ts +10 -8
  166. package/dist/components/sheet.js +31 -20
  167. package/dist/components/sheet.js.map +1 -1
  168. package/dist/components/skeleton.d.ts +2 -1
  169. package/dist/components/skeleton.js +6 -4
  170. package/dist/components/skeleton.js.map +1 -1
  171. package/dist/components/slider.d.ts +10 -6
  172. package/dist/components/slider.js +15 -7
  173. package/dist/components/slider.js.map +1 -1
  174. package/dist/components/source.d.ts +2 -2
  175. package/dist/components/source.js +9 -7
  176. package/dist/components/source.js.map +1 -1
  177. package/dist/components/spinner.d.ts +4 -2
  178. package/dist/components/spinner.js +4 -3
  179. package/dist/components/spinner.js.map +1 -1
  180. package/dist/components/stat-card.d.ts +27 -2
  181. package/dist/components/stat-card.js +69 -9
  182. package/dist/components/stat-card.js.map +1 -1
  183. package/dist/components/status.d.ts +7 -6
  184. package/dist/components/status.js +11 -5
  185. package/dist/components/status.js.map +1 -1
  186. package/dist/components/steps.d.ts +1 -1
  187. package/dist/components/steps.js +4 -3
  188. package/dist/components/steps.js.map +1 -1
  189. package/dist/components/switch.d.ts +46 -2
  190. package/dist/components/switch.js +85 -8
  191. package/dist/components/switch.js.map +1 -1
  192. package/dist/components/table.d.ts +29 -12
  193. package/dist/components/table.js +79 -34
  194. package/dist/components/table.js.map +1 -1
  195. package/dist/components/tabs.d.ts +9 -4
  196. package/dist/components/tabs.js +22 -13
  197. package/dist/components/tabs.js.map +1 -1
  198. package/dist/components/textarea.d.ts +6 -1
  199. package/dist/components/textarea.js +3 -3
  200. package/dist/components/textarea.js.map +1 -1
  201. package/dist/components/toast.d.ts +10 -6
  202. package/dist/components/toast.js +27 -18
  203. package/dist/components/toast.js.map +1 -1
  204. package/dist/components/toaster.d.ts +4 -2
  205. package/dist/components/toaster.js +6 -2
  206. package/dist/components/toaster.js.map +1 -1
  207. package/dist/components/tool-call.d.ts +2 -2
  208. package/dist/components/tool-call.js +10 -8
  209. package/dist/components/tool-call.js.map +1 -1
  210. package/dist/components/toolbar.d.ts +9 -4
  211. package/dist/components/toolbar.js +14 -9
  212. package/dist/components/toolbar.js.map +1 -1
  213. package/dist/components/tooltip.d.ts +3 -2
  214. package/dist/components/tooltip.js +7 -4
  215. package/dist/components/tooltip.js.map +1 -1
  216. package/dist/components/tour.js +2 -2
  217. package/dist/components/tour.js.map +1 -1
  218. package/dist/components/tree-view.d.ts +1 -1
  219. package/dist/components/tree-view.js +9 -5
  220. package/dist/components/tree-view.js.map +1 -1
  221. package/dist/components/voice-recorder.d.ts +1 -1
  222. package/dist/components/voice-recorder.js +4 -2
  223. package/dist/components/voice-recorder.js.map +1 -1
  224. package/dist/index.d.ts +6 -1
  225. package/dist/index.js +17 -12
  226. package/dist/lib/media.d.ts +18 -0
  227. package/dist/lib/media.js +34 -0
  228. package/dist/lib/media.js.map +1 -0
  229. package/dist/lib/utils.d.ts +32 -0
  230. package/dist/lib/utils.js +33 -1
  231. package/dist/lib/utils.js.map +1 -1
  232. package/dist/mui/index.js +20 -8
  233. package/dist/mui/index.js.map +1 -1
  234. package/dist/styles.css +1 -1
  235. package/dist/theme.css +36 -17
  236. package/dist/tokens.d.ts +78 -36
  237. package/dist/tokens.js +63 -32
  238. package/dist/tokens.js.map +1 -1
  239. package/package.json +5 -3
@@ -1,18 +1,19 @@
1
1
  "use client";
2
- import { cn } from "../lib/utils.js";
3
- import { Command as pkg } from "../primitives/vendor/cmdk/index.js";
2
+ import { cn, noOutlineClasses } from "../lib/utils.js";
4
3
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./dialog.js";
5
- import "react";
4
+ import { Command as pkg } from "../primitives/vendor/cmdk/index.js";
5
+ import * as React from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import Search from "@burtson-labs/icons/react/search";
8
8
  //#region src/components/command.tsx
9
- function Command({ className, ...props }) {
9
+ var Command = React.forwardRef(function Command({ className, ...props }, ref) {
10
10
  return /* @__PURE__ */ jsx(pkg, {
11
+ ref,
11
12
  "data-slot": "command",
12
13
  className: cn("flex h-full w-full flex-col overflow-hidden rounded-lg bg-surface-raised text-popover-foreground has-[[cmdk-input]:focus-visible]:border-ring", className),
13
14
  ...props
14
15
  });
15
- }
16
+ });
16
17
  /** A command palette in a dialog, the usual ⌘K surface. */
17
18
  function CommandDialog({ title = "Command palette", description = "Search for a page or an action", children, className, ...props }) {
18
19
  return /* @__PURE__ */ jsx(Dialog, {
@@ -30,62 +31,74 @@ function CommandDialog({ title = "Command palette", description = "Search for a
30
31
  })
31
32
  });
32
33
  }
33
- function CommandInput({ className, ...props }) {
34
+ /**
35
+ * The row is the field (icon + input, ruled off from the list); the input
36
+ * itself draws no border or outline, whatever the host page's CSS says.
37
+ */
38
+ var CommandInput = React.forwardRef(function CommandInput({ className, ...props }, ref) {
34
39
  return /* @__PURE__ */ jsxs("div", {
35
40
  "data-slot": "command-input-wrapper",
36
- className: "flex h-11 items-center gap-2 border-b px-3",
41
+ className: "flex items-center gap-2 border-b px-3",
37
42
  children: [/* @__PURE__ */ jsx(Search, {
38
- className: "size-4 shrink-0 opacity-50",
43
+ className: "size-4 shrink-0 text-muted-foreground",
39
44
  "aria-hidden": true
40
45
  }), /* @__PURE__ */ jsx(pkg.Input, {
46
+ ref,
41
47
  "data-slot": "command-input",
42
- className: cn("flex h-10 w-full rounded-none border-0 bg-transparent py-3 text-sm shadow-none outline-hidden! placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", className),
48
+ className: cn("flex h-11 w-full rounded-none border-0 bg-transparent py-3 text-base shadow-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm", noOutlineClasses, className),
43
49
  ...props
44
50
  })]
45
51
  });
46
- }
47
- function CommandList({ className, ...props }) {
52
+ });
53
+ var CommandList = React.forwardRef(function CommandList({ className, ...props }, ref) {
48
54
  return /* @__PURE__ */ jsx(pkg.List, {
55
+ ref,
49
56
  "data-slot": "command-list",
50
57
  className: cn("max-h-80 scroll-py-1 overflow-x-hidden overflow-y-auto p-1", className),
51
58
  ...props
52
59
  });
53
- }
54
- function CommandEmpty(props) {
60
+ });
61
+ var CommandEmpty = React.forwardRef(function CommandEmpty({ className, ...props }, ref) {
55
62
  return /* @__PURE__ */ jsx(pkg.Empty, {
63
+ ref,
56
64
  "data-slot": "command-empty",
57
- className: "py-6 text-center text-sm text-muted-foreground",
65
+ className: cn("py-6 text-center text-sm text-muted-foreground", className),
58
66
  ...props
59
67
  });
60
- }
61
- function CommandGroup({ className, ...props }) {
68
+ });
69
+ var CommandGroup = React.forwardRef(function CommandGroup({ className, ...props }, ref) {
62
70
  return /* @__PURE__ */ jsx(pkg.Group, {
71
+ ref,
63
72
  "data-slot": "command-group",
64
- className: cn("overflow-hidden py-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", className),
73
+ className: cn("overflow-hidden py-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-[11px] [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:tracking-[0.06em] [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:uppercase", className),
65
74
  ...props
66
75
  });
67
- }
68
- function CommandSeparator({ className, ...props }) {
76
+ });
77
+ var CommandSeparator = React.forwardRef(function CommandSeparator({ className, ...props }, ref) {
69
78
  return /* @__PURE__ */ jsx(pkg.Separator, {
79
+ ref,
70
80
  "data-slot": "command-separator",
71
81
  className: cn("-mx-1 h-px bg-border", className),
72
82
  ...props
73
83
  });
74
- }
75
- function CommandItem({ className, ...props }) {
84
+ });
85
+ /** A row: 32px (44px on touch screens), 8px radius, a fill when active. */
86
+ var CommandItem = React.forwardRef(function CommandItem({ className, ...props }, ref) {
76
87
  return /* @__PURE__ */ jsx(pkg.Item, {
88
+ ref,
77
89
  "data-slot": "command-item",
78
- className: cn("relative flex min-h-8 cursor-default items-center gap-2 rounded-sm px-2 text-[13px] outline-hidden! select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-secondary data-[selected=true]:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground", className),
90
+ className: cn("relative flex min-h-8 cursor-default items-center gap-2 rounded-sm px-2 text-[13px] select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-secondary data-[selected=true]:text-foreground pointer-coarse:min-h-11 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground", noOutlineClasses, className),
79
91
  ...props
80
92
  });
81
- }
82
- function CommandShortcut({ className, ...props }) {
93
+ });
94
+ var CommandShortcut = React.forwardRef(function CommandShortcut({ className, ...props }, ref) {
83
95
  return /* @__PURE__ */ jsx("span", {
96
+ ref,
84
97
  "data-slot": "command-shortcut",
85
98
  className: cn("ml-auto rounded-xs border border-border bg-surface-muted px-1.5 font-mono text-[11px] leading-5 text-muted-foreground", className),
86
99
  ...props
87
100
  });
88
- }
101
+ });
89
102
  //#endregion
90
103
  export { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut };
91
104
 
@@ -1 +1 @@
1
- {"version":3,"file":"command.js","names":["CommandPrimitive"],"sources":["../../src/components/command.tsx"],"sourcesContent":["import Search from '@burtson-labs/icons/react/search';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport { Command as CommandPrimitive } from '../primitives/vendor/cmdk';\n\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from './dialog';\n\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\n return (\n <CommandPrimitive\n data-slot=\"command\"\n className={cn(\n // Standalone with a border, the box is the field: its border takes the\n // ring colour while the search input has keyboard focus. In a popover or\n // dialog there is no border of its own, so the rule draws nothing.\n 'flex h-full w-full flex-col overflow-hidden rounded-lg bg-surface-raised text-popover-foreground has-[[cmdk-input]:focus-visible]:border-ring',\n className,\n )}\n {...props}\n />\n );\n}\n\n/** A command palette in a dialog, the usual ⌘K surface. */\nfunction CommandDialog({\n title = 'Command palette',\n description = 'Search for a page or an action',\n children,\n className,\n ...props\n}: React.ComponentProps<typeof Dialog> & {\n title?: string;\n description?: string;\n className?: string;\n}) {\n return (\n <Dialog {...props}>\n <DialogContent\n className={cn(\n 'top-[20%] translate-y-0 gap-0 overflow-hidden p-0 sm:max-w-xl sm:p-0',\n className,\n )}\n showCloseButton={false}\n >\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <Command className=\"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-2\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction CommandInput({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\n return (\n // The row is the field (icon + input, ruled off from the list); the input\n // itself draws no border or outline, whatever the host page's CSS says.\n <div data-slot=\"command-input-wrapper\" className=\"flex h-11 items-center gap-2 border-b px-3\">\n <Search className=\"size-4 shrink-0 opacity-50\" aria-hidden />\n <CommandPrimitive.Input\n data-slot=\"command-input\"\n className={cn(\n 'flex h-10 w-full rounded-none border-0 bg-transparent py-3 text-sm shadow-none outline-hidden! placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',\n className,\n )}\n {...props}\n />\n </div>\n );\n}\n\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\n return (\n <CommandPrimitive.List\n data-slot=\"command-list\"\n className={cn('max-h-80 scroll-py-1 overflow-x-hidden overflow-y-auto p-1', className)}\n {...props}\n />\n );\n}\n\nfunction CommandEmpty(props: React.ComponentProps<typeof CommandPrimitive.Empty>) {\n return (\n <CommandPrimitive.Empty\n data-slot=\"command-empty\"\n className=\"py-6 text-center text-sm text-muted-foreground\"\n {...props}\n />\n );\n}\n\nfunction CommandGroup({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\n return (\n <CommandPrimitive.Group\n data-slot=\"command-group\"\n className={cn(\n 'overflow-hidden py-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CommandSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Separator>) {\n return (\n <CommandPrimitive.Separator\n data-slot=\"command-separator\"\n className={cn('-mx-1 h-px bg-border', className)}\n {...props}\n />\n );\n}\n\nfunction CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\n return (\n <CommandPrimitive.Item\n data-slot=\"command-item\"\n className={cn(\n \"relative flex min-h-8 cursor-default items-center gap-2 rounded-sm px-2 text-[13px] outline-hidden! select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-secondary data-[selected=true]:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CommandShortcut({ className, ...props }: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(\n 'ml-auto rounded-xs border border-border bg-surface-muted px-1.5 font-mono text-[11px] leading-5 text-muted-foreground',\n className,\n )}\n {...props}\n />\n );\n}\n\nexport {\n Command,\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut,\n};\n"],"mappings":";;;;;;;;AAQA,SAAS,QAAQ,EAAE,WAAW,GAAG,SAAwD;CACvF,OACE,oBAACA,KAAD;EACE,aAAU;EACV,WAAW,GAIT,iJACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;;AAGA,SAAS,cAAc,EACrB,QAAQ,mBACR,cAAc,kCACd,UACA,WACA,GAAG,SAKF;CACD,OACE,oBAAC,QAAD;EAAQ,GAAI;EACV,UAAA,qBAAC,eAAD;GACE,WAAW,GACT,wEACA,SACF;GACA,iBAAiB;GALnB,UAAA,CAOE,qBAAC,cAAD;IAAc,WAAU;IAAxB,UAAA,CACE,oBAAC,aAAD,EAAA,UAAc,MAAmB,CAAA,GACjC,oBAAC,mBAAD,EAAA,UAAoB,YAA+B,CAAA,CACvC;GACd,CAAA,GAAA,oBAAC,SAAD;IAAS,WAAU;IAChB;GACM,CAAA,CACI;;CACT,CAAA;AAEZ;AAEA,SAAS,aAAa,EACpB,WACA,GAAG,SACmD;CACtD,OAGE,qBAAC,OAAD;EAAK,aAAU;EAAwB,WAAU;EAAjD,UAAA,CACE,oBAAC,QAAD;GAAQ,WAAU;GAA6B,eAAA;EAAa,CAAA,GAC5D,oBAACA,IAAiB,OAAlB;GACE,aAAU;GACV,WAAW,GACT,oLACA,SACF;GACA,GAAI;EACL,CAAA,CACE;;AAET;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,SAA6D;CAChG,OACE,oBAACA,IAAiB,MAAlB;EACE,aAAU;EACV,WAAW,GAAG,8DAA8D,SAAS;EACrF,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,aAAa,OAA4D;CAChF,OACE,oBAACA,IAAiB,OAAlB;EACE,aAAU;EACV,WAAU;EACV,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,aAAa,EACpB,WACA,GAAG,SACmD;CACtD,OACE,oBAACA,IAAiB,OAAlB;EACE,aAAU;EACV,WAAW,GACT,2NACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,iBAAiB,EACxB,WACA,GAAG,SACuD;CAC1D,OACE,oBAACA,IAAiB,WAAlB;EACE,aAAU;EACV,WAAW,GAAG,wBAAwB,SAAS;EAC/C,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,SAA6D;CAChG,OACE,oBAACA,IAAiB,MAAlB;EACE,aAAU;EACV,WAAW,GACT,yYACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAuC;CAC9E,OACE,oBAAC,QAAD;EACE,aAAU;EACV,WAAW,GACT,yHACA,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"command.js","names":["CommandPrimitive"],"sources":["../../src/components/command.tsx"],"sourcesContent":["import Search from '@burtson-labs/icons/react/search';\nimport * as React from 'react';\n\nimport { cn, noOutlineClasses } from '../lib/utils';\nimport { Command as CommandPrimitive } from '../primitives/vendor/cmdk';\n\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from './dialog';\n\nconst Command = React.forwardRef<HTMLDivElement, React.ComponentProps<typeof CommandPrimitive>>(\n function Command({ className, ...props }, ref) {\n return (\n <CommandPrimitive\n ref={ref}\n data-slot=\"command\"\n className={cn(\n // Standalone with a border, the box is the field: its border takes the\n // ring colour while the search input has keyboard focus. In a popover or\n // dialog there is no border of its own, so the rule draws nothing.\n 'flex h-full w-full flex-col overflow-hidden rounded-lg bg-surface-raised text-popover-foreground has-[[cmdk-input]:focus-visible]:border-ring',\n className,\n )}\n {...props}\n />\n );\n },\n);\n\nexport interface CommandDialogProps extends React.ComponentProps<typeof Dialog> {\n title?: string;\n description?: string;\n className?: string;\n}\n\n/** A command palette in a dialog, the usual ⌘K surface. */\nfunction CommandDialog({\n title = 'Command palette',\n description = 'Search for a page or an action',\n children,\n className,\n ...props\n}: CommandDialogProps) {\n return (\n <Dialog {...props}>\n <DialogContent\n className={cn(\n 'top-[20%] translate-y-0 gap-0 overflow-hidden p-0 sm:max-w-xl sm:p-0',\n className,\n )}\n showCloseButton={false}\n >\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <Command className=\"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-2\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\n/**\n * The row is the field (icon + input, ruled off from the list); the input\n * itself draws no border or outline, whatever the host page's CSS says.\n */\nconst CommandInput = React.forwardRef<\n HTMLInputElement,\n React.ComponentProps<typeof CommandPrimitive.Input>\n>(function CommandInput({ className, ...props }, ref) {\n return (\n <div data-slot=\"command-input-wrapper\" className=\"flex items-center gap-2 border-b px-3\">\n <Search className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden />\n <CommandPrimitive.Input\n ref={ref}\n data-slot=\"command-input\"\n className={cn(\n 'flex h-11 w-full rounded-none border-0 bg-transparent py-3 text-base shadow-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm',\n noOutlineClasses,\n className,\n )}\n {...props}\n />\n </div>\n );\n});\n\nconst CommandList = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof CommandPrimitive.List>\n>(function CommandList({ className, ...props }, ref) {\n return (\n <CommandPrimitive.List\n ref={ref}\n data-slot=\"command-list\"\n className={cn('max-h-80 scroll-py-1 overflow-x-hidden overflow-y-auto p-1', className)}\n {...props}\n />\n );\n});\n\nconst CommandEmpty = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof CommandPrimitive.Empty>\n>(function CommandEmpty({ className, ...props }, ref) {\n return (\n <CommandPrimitive.Empty\n ref={ref}\n data-slot=\"command-empty\"\n className={cn('py-6 text-center text-sm text-muted-foreground', className)}\n {...props}\n />\n );\n});\n\nconst CommandGroup = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof CommandPrimitive.Group>\n>(function CommandGroup({ className, ...props }, ref) {\n return (\n <CommandPrimitive.Group\n ref={ref}\n data-slot=\"command-group\"\n className={cn(\n 'overflow-hidden py-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-[11px] [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:tracking-[0.06em] [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:uppercase',\n className,\n )}\n {...props}\n />\n );\n});\n\nconst CommandSeparator = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof CommandPrimitive.Separator>\n>(function CommandSeparator({ className, ...props }, ref) {\n return (\n <CommandPrimitive.Separator\n ref={ref}\n data-slot=\"command-separator\"\n className={cn('-mx-1 h-px bg-border', className)}\n {...props}\n />\n );\n});\n\n/** A row: 32px (44px on touch screens), 8px radius, a fill when active. */\nconst CommandItem = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof CommandPrimitive.Item>\n>(function CommandItem({ className, ...props }, ref) {\n return (\n <CommandPrimitive.Item\n ref={ref}\n data-slot=\"command-item\"\n className={cn(\n \"relative flex min-h-8 cursor-default items-center gap-2 rounded-sm px-2 text-[13px] select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-secondary data-[selected=true]:text-foreground pointer-coarse:min-h-11 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n noOutlineClasses,\n className,\n )}\n {...props}\n />\n );\n});\n\nconst CommandShortcut = React.forwardRef<HTMLSpanElement, React.ComponentProps<'span'>>(\n function CommandShortcut({ className, ...props }, ref) {\n return (\n <span\n ref={ref}\n data-slot=\"command-shortcut\"\n className={cn(\n 'ml-auto rounded-xs border border-border bg-surface-muted px-1.5 font-mono text-[11px] leading-5 text-muted-foreground',\n className,\n )}\n {...props}\n />\n );\n },\n);\n\nexport {\n Command,\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut,\n};\n"],"mappings":";;;;;;;;AAQA,IAAM,UAAU,MAAM,WACpB,SAAS,QAAQ,EAAE,WAAW,GAAG,SAAS,KAAK;CAC7C,OACE,oBAACA,KAAD;EACO;EACL,aAAU;EACV,WAAW,GAIT,iJACA,SACF;EACA,GAAI;CACL,CAAA;AAEL,CACF;;AASA,SAAS,cAAc,EACrB,QAAQ,mBACR,cAAc,kCACd,UACA,WACA,GAAG,SACkB;CACrB,OACE,oBAAC,QAAD;EAAQ,GAAI;EACV,UAAA,qBAAC,eAAD;GACE,WAAW,GACT,wEACA,SACF;GACA,iBAAiB;GALnB,UAAA,CAOE,qBAAC,cAAD;IAAc,WAAU;IAAxB,UAAA,CACE,oBAAC,aAAD,EAAA,UAAc,MAAmB,CAAA,GACjC,oBAAC,mBAAD,EAAA,UAAoB,YAA+B,CAAA,CACvC;GACd,CAAA,GAAA,oBAAC,SAAD;IAAS,WAAU;IAChB;GACM,CAAA,CACI;;CACT,CAAA;AAEZ;;;;;AAMA,IAAM,eAAe,MAAM,WAGzB,SAAS,aAAa,EAAE,WAAW,GAAG,SAAS,KAAK;CACpD,OACE,qBAAC,OAAD;EAAK,aAAU;EAAwB,WAAU;EAAjD,UAAA,CACE,oBAAC,QAAD;GAAQ,WAAU;GAAwC,eAAA;EAAa,CAAA,GACvE,oBAACA,IAAiB,OAAlB;GACO;GACL,aAAU;GACV,WAAW,GACT,iLACA,kBACA,SACF;GACA,GAAI;EACL,CAAA,CACE;;AAET,CAAC;AAED,IAAM,cAAc,MAAM,WAGxB,SAAS,YAAY,EAAE,WAAW,GAAG,SAAS,KAAK;CACnD,OACE,oBAACA,IAAiB,MAAlB;EACO;EACL,aAAU;EACV,WAAW,GAAG,8DAA8D,SAAS;EACrF,GAAI;CACL,CAAA;AAEL,CAAC;AAED,IAAM,eAAe,MAAM,WAGzB,SAAS,aAAa,EAAE,WAAW,GAAG,SAAS,KAAK;CACpD,OACE,oBAACA,IAAiB,OAAlB;EACO;EACL,aAAU;EACV,WAAW,GAAG,kDAAkD,SAAS;EACzE,GAAI;CACL,CAAA;AAEL,CAAC;AAED,IAAM,eAAe,MAAM,WAGzB,SAAS,aAAa,EAAE,WAAW,GAAG,SAAS,KAAK;CACpD,OACE,oBAACA,IAAiB,OAAlB;EACO;EACL,aAAU;EACV,WAAW,GACT,+SACA,SACF;EACA,GAAI;CACL,CAAA;AAEL,CAAC;AAED,IAAM,mBAAmB,MAAM,WAG7B,SAAS,iBAAiB,EAAE,WAAW,GAAG,SAAS,KAAK;CACxD,OACE,oBAACA,IAAiB,WAAlB;EACO;EACL,aAAU;EACV,WAAW,GAAG,wBAAwB,SAAS;EAC/C,GAAI;CACL,CAAA;AAEL,CAAC;;AAGD,IAAM,cAAc,MAAM,WAGxB,SAAS,YAAY,EAAE,WAAW,GAAG,SAAS,KAAK;CACnD,OACE,oBAACA,IAAiB,MAAlB;EACO;EACL,aAAU;EACV,WAAW,GACT,iZACA,kBACA,SACF;EACA,GAAI;CACL,CAAA;AAEL,CAAC;AAED,IAAM,kBAAkB,MAAM,WAC5B,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAS,KAAK;CACrD,OACE,oBAAC,QAAD;EACO;EACL,aAAU;EACV,WAAW,GACT,yHACA,SACF;EACA,GAAI;CACL,CAAA;AAEL,CACF"}
@@ -47,11 +47,11 @@ export declare function acceptsFile(file: Pick<File, 'name' | 'type'>, accept?:
47
47
  * The message box: grows with its text, Enter sends, Shift+Enter adds a
48
48
  * line, and it never sends mid-composition (IME input).
49
49
  */
50
- declare function Composer({ onSubmit, onSubmitError, submitErrorText, value: controlled, onValueChange, streaming, onStop, disabled, placeholder, attachments, attachmentCount, canSubmit, actions, label, onAttach, accept, multiple, attachLabel, className, ...props }: ComposerProps): React.JSX.Element;
50
+ declare const Composer: React.ForwardRefExoticComponent<Omit<ComposerProps, "ref"> & React.RefAttributes<HTMLFormElement>>;
51
51
  export interface SuggestionsProps extends Omit<React.ComponentProps<'div'>, 'onSelect'> {
52
52
  items: string[];
53
53
  onSelect: (item: string) => void;
54
54
  }
55
55
  /** Starter prompts as chips. */
56
- declare function Suggestions({ items, onSelect, className, ...props }: SuggestionsProps): React.JSX.Element;
56
+ declare const Suggestions: React.ForwardRefExoticComponent<Omit<SuggestionsProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
57
57
  export { Composer, Suggestions };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { cn } from "../lib/utils.js";
2
+ import { cn, focusRingClasses, noOutlineClasses } from "../lib/utils.js";
3
3
  import { Button } from "./button.js";
4
4
  import * as React from "react";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -18,7 +18,7 @@ function acceptsFile(file, accept) {
18
18
  * The message box: grows with its text, Enter sends, Shift+Enter adds a
19
19
  * line, and it never sends mid-composition (IME input).
20
20
  */
21
- function Composer({ onSubmit, onSubmitError, submitErrorText = "Message could not be sent. Your draft is saved here. Try again.", value: controlled, onValueChange, streaming = false, onStop, disabled = false, placeholder = "Ask anything…", attachments, attachmentCount = 0, canSubmit = true, actions, label = "Message", onAttach, accept, multiple = true, attachLabel = "Attach files", className, ...props }) {
21
+ var Composer = React.forwardRef(function Composer({ onSubmit, onSubmitError, submitErrorText = "Message could not be sent. Your draft is saved here. Try again.", value: controlled, onValueChange, streaming = false, onStop, disabled = false, placeholder = "Ask anything…", attachments, attachmentCount = 0, canSubmit = true, actions, label = "Message", onAttach, accept, multiple = true, attachLabel = "Attach files", className, ...props }, ref) {
22
22
  const fileInput = React.useRef(null);
23
23
  const [dragging, setDragging] = React.useState(false);
24
24
  const dragDepth = React.useRef(0);
@@ -69,6 +69,7 @@ function Composer({ onSubmit, onSubmitError, submitErrorText = "Message could no
69
69
  }
70
70
  };
71
71
  return /* @__PURE__ */ jsxs("form", {
72
+ ref,
72
73
  "data-slot": "composer",
73
74
  "data-dragging": dragging || void 0,
74
75
  onSubmit: (e) => {
@@ -129,7 +130,7 @@ function Composer({ onSubmit, onSubmitError, submitErrorText = "Message could no
129
130
  send();
130
131
  }
131
132
  },
132
- className: "field-sizing-content max-h-48 min-h-9 w-full resize-none rounded-none border-0 bg-transparent px-2 py-1.5 text-base sm:text-sm leading-6 shadow-none outline-hidden! placeholder:text-muted-foreground disabled:cursor-not-allowed"
133
+ className: cn("field-sizing-content max-h-48 min-h-9 w-full resize-none rounded-none border-0 bg-transparent px-2 py-1.5 text-base sm:text-sm leading-6 shadow-none placeholder:text-muted-foreground disabled:cursor-not-allowed pointer-coarse:min-h-11 pointer-coarse:py-2.5", noOutlineClasses)
133
134
  }),
134
135
  failed && /* @__PURE__ */ jsx("p", {
135
136
  id: errorId,
@@ -193,10 +194,11 @@ function Composer({ onSubmit, onSubmitError, submitErrorText = "Message could no
193
194
  })
194
195
  ]
195
196
  });
196
- }
197
+ });
197
198
  /** Starter prompts as chips. */
198
- function Suggestions({ items, onSelect, className, ...props }) {
199
+ var Suggestions = React.forwardRef(function Suggestions({ items, onSelect, className, ...props }, ref) {
199
200
  return /* @__PURE__ */ jsx("div", {
201
+ ref,
200
202
  "data-slot": "suggestions",
201
203
  role: "group",
202
204
  "aria-label": "Suggestions",
@@ -205,11 +207,11 @@ function Suggestions({ items, onSelect, className, ...props }) {
205
207
  children: items.map((item) => /* @__PURE__ */ jsx("button", {
206
208
  type: "button",
207
209
  onClick: () => onSelect(item),
208
- className: "animate-in rounded-full border border-border-strong bg-surface px-3 py-1.5 text-[13px] text-foreground outline-none transition-colors hover:border-brand/40 hover:bg-brand-soft hover:text-brand-soft-foreground focus-visible:ring-[3px] focus-visible:ring-ring/20",
210
+ className: cn("animate-in rounded-full border border-border-strong bg-surface px-3 py-1.5 text-[13px] text-foreground transition-colors hover:border-brand/40 hover:bg-brand-soft hover:text-brand-soft-foreground pointer-coarse:min-h-11", focusRingClasses),
209
211
  children: item
210
212
  }, item))
211
213
  });
212
- }
214
+ });
213
215
  //#endregion
214
216
  export { Composer, Suggestions, acceptsFile };
215
217
 
@@ -1 +1 @@
1
- {"version":3,"file":"composer.js","names":["_Fragment"],"sources":["../../src/components/composer.tsx"],"sourcesContent":["import ArrowUp from '@burtson-labs/icons/react/arrow-up';\nimport Paperclip from '@burtson-labs/icons/react/paperclip';\nimport Square from '@burtson-labs/icons/react/square';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { Button } from './button';\n\nexport interface ComposerProps extends Omit<React.ComponentProps<'form'>, 'onSubmit' | 'onChange'> {\n /**\n * Clears after success. Reject a returned promise to retain the draft. The\n * text can be empty when `attachmentCount` allows an attachment-only send.\n */\n onSubmit: (text: string) => void | Promise<void>;\n onSubmitError?: (error: unknown) => void;\n submitErrorText?: string;\n /** Controlled value; leave out to let the composer keep its own. */\n value?: string;\n onValueChange?: (value: string) => void;\n /** While a reply streams, the send button becomes Stop. */\n streaming?: boolean;\n onStop?: () => void;\n disabled?: boolean;\n placeholder?: string;\n /** Attachment chips or a file button, shown under the text. */\n attachments?: React.ReactNode;\n /** Attachments ready to send. Above zero, an empty message can be sent. */\n attachmentCount?: number;\n /**\n * Extra gate on sending, e.g. false while an attachment is still parsing.\n * Unlike `disabled`, the person can keep typing.\n */\n canSubmit?: boolean;\n /** Extra controls left of the send button (model picker, tools). */\n actions?: React.ReactNode;\n label?: string;\n /**\n * Files the person picked, dropped on the composer or pasted. Setting it\n * shows a paperclip button. The composer only hands the files over: the\n * app uploads them and renders their state in `attachments` (an\n * AttachmentTray), with `attachmentCount` and `canSubmit` for sending.\n */\n onAttach?: (files: File[]) => void;\n /** File input `accept`, e.g. \"image/*,.pdf\". Dropped and pasted files are filtered too. */\n accept?: string;\n /** Allow several files at once. Default true. */\n multiple?: boolean;\n /** Accessible name of the attach button. */\n attachLabel?: string;\n}\n\n/** Does `file` match an `accept` list (\"image/*,.pdf,text/plain\")? Empty accepts all. */\nexport function acceptsFile(file: Pick<File, 'name' | 'type'>, accept?: string): boolean {\n if (!accept?.trim()) return true;\n const name = file.name.toLowerCase();\n const type = (file.type || '').toLowerCase();\n return accept\n .split(',')\n .map((a) => a.trim().toLowerCase())\n .filter(Boolean)\n .some((rule) =>\n rule.startsWith('.')\n ? name.endsWith(rule)\n : rule.endsWith('/*')\n ? type.startsWith(rule.slice(0, -1))\n : type === rule,\n );\n}\n\n/**\n * The message box: grows with its text, Enter sends, Shift+Enter adds a\n * line, and it never sends mid-composition (IME input).\n */\nfunction Composer({\n onSubmit,\n onSubmitError,\n submitErrorText = 'Message could not be sent. Your draft is saved here. Try again.',\n value: controlled,\n onValueChange,\n streaming = false,\n onStop,\n disabled = false,\n placeholder = 'Ask anything…',\n attachments,\n attachmentCount = 0,\n canSubmit = true,\n actions,\n label = 'Message',\n onAttach,\n accept,\n multiple = true,\n attachLabel = 'Attach files',\n className,\n ...props\n}: ComposerProps) {\n const fileInput = React.useRef<HTMLInputElement>(null);\n const [dragging, setDragging] = React.useState(false);\n const dragDepth = React.useRef(0);\n const attach = (list: FileList | File[] | null | undefined) => {\n if (!onAttach || !list) return;\n let files = Array.from(list).filter((f) => acceptsFile(f, accept));\n if (!multiple) files = files.slice(0, 1);\n if (files.length) onAttach(files);\n };\n const hasFiles = (e: React.DragEvent) =>\n Array.from(e.dataTransfer?.types ?? []).includes('Files');\n const [own, setOwn] = React.useState('');\n const value = controlled ?? own;\n const [pending, setPending] = React.useState(false);\n const [failed, setFailed] = React.useState(false);\n const sending = React.useRef(false);\n const latest = React.useRef(value);\n const mounted = React.useRef(true);\n const errorId = React.useId();\n React.useEffect(() => {\n latest.current = value;\n }, [value]);\n React.useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n const setValue = (v: string) => {\n if (controlled === undefined) setOwn(v);\n onValueChange?.(v);\n };\n const canSend =\n canSubmit &&\n !disabled &&\n !streaming &&\n !pending &&\n (value.trim().length > 0 || attachmentCount > 0);\n const send = async () => {\n if (!canSend || sending.current) return;\n const draft = value;\n sending.current = true;\n setFailed(false);\n setPending(true);\n try {\n await onSubmit(draft.trim());\n if (mounted.current && latest.current === draft) setValue('');\n } catch (error) {\n if (mounted.current) setFailed(true);\n onSubmitError?.(error);\n } finally {\n sending.current = false;\n if (mounted.current) setPending(false);\n }\n };\n\n return (\n <form\n data-slot=\"composer\"\n data-dragging={dragging || undefined}\n onSubmit={(e) => {\n e.preventDefault();\n void send();\n }}\n onDragEnter={(e) => {\n if (!onAttach || disabled || !hasFiles(e)) return;\n e.preventDefault();\n dragDepth.current += 1;\n setDragging(true);\n }}\n onDragOver={(e) => {\n if (!onAttach || disabled || !hasFiles(e)) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = 'copy';\n }}\n onDragLeave={() => {\n if (!onAttach) return;\n dragDepth.current = Math.max(0, dragDepth.current - 1);\n if (dragDepth.current === 0) setDragging(false);\n }}\n onDrop={(e) => {\n if (!onAttach || disabled) return;\n e.preventDefault();\n dragDepth.current = 0;\n setDragging(false);\n attach(e.dataTransfer?.files);\n }}\n className={cn(\n // The box is the field: same focus as Input (ring border + 1px inset ring).\n 'relative grid gap-2 rounded-lg border border-input bg-surface p-2 shadow-xs transition-[border-color,box-shadow] has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:inset-ring-1 has-[textarea:focus-visible]:inset-ring-ring dark:bg-surface-raised',\n 'data-[dragging]:border-brand data-[dragging]:inset-ring-1 data-[dragging]:inset-ring-brand',\n disabled && 'opacity-60',\n className,\n )}\n {...props}\n >\n {dragging && (\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-1 z-10 grid place-items-center rounded-md border border-dashed border-brand/50 bg-brand-soft/80 text-sm font-medium text-brand-soft-foreground\"\n >\n Drop files to attach\n </div>\n )}\n <textarea\n aria-label={label}\n rows={1}\n value={value}\n disabled={disabled || pending}\n aria-describedby={failed ? errorId : undefined}\n aria-invalid={failed || undefined}\n placeholder={placeholder}\n onChange={(e) => setValue(e.target.value)}\n onPaste={(e) => {\n if (!onAttach) return;\n const files = Array.from(e.clipboardData?.files ?? []);\n // Only take over the paste when it carries files; text pastes as text.\n if (files.length) {\n e.preventDefault();\n attach(files);\n }\n }}\n onKeyDown={(e) => {\n if (\n e.key === 'Enter' &&\n !e.shiftKey &&\n !e.nativeEvent.isComposing &&\n e.nativeEvent.keyCode !== 229\n ) {\n e.preventDefault();\n void send();\n }\n }}\n className=\"field-sizing-content max-h-48 min-h-9 w-full resize-none rounded-none border-0 bg-transparent px-2 py-1.5 text-base sm:text-sm leading-6 shadow-none outline-hidden! placeholder:text-muted-foreground disabled:cursor-not-allowed\"\n />\n {failed && (\n <p id={errorId} role=\"alert\" className=\"px-2 text-sm text-destructive\">\n {submitErrorText}\n </p>\n )}\n {attachments && <div className=\"flex flex-wrap gap-1.5 px-1\">{attachments}</div>}\n <div className=\"flex items-center gap-1\">\n {onAttach && (\n <>\n <input\n ref={fileInput}\n type=\"file\"\n hidden\n tabIndex={-1}\n accept={accept}\n multiple={multiple}\n data-slot=\"composer-file-input\"\n onChange={(e) => {\n attach(e.target.files);\n // Let the same file be picked again after a removal.\n e.target.value = '';\n }}\n />\n <Button\n type=\"button\"\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={attachLabel}\n title={attachLabel}\n disabled={disabled}\n onClick={() => fileInput.current?.click()}\n className=\"pointer-coarse:size-11\"\n >\n <Paperclip />\n </Button>\n </>\n )}\n {actions}\n <div className=\"ml-auto\">\n {streaming ? (\n <Button\n type=\"button\"\n size=\"icon-sm\"\n variant=\"secondary\"\n aria-label=\"Stop\"\n className=\"pointer-coarse:size-11\"\n onClick={onStop}\n disabled={!onStop || disabled}\n >\n <Square className=\"fill-current\" />\n </Button>\n ) : (\n <Button\n type=\"submit\"\n size=\"icon-sm\"\n aria-label=\"Send\"\n className=\"pointer-coarse:size-11\"\n loading={pending}\n disabled={!canSend}\n >\n <ArrowUp />\n </Button>\n )}\n </div>\n </div>\n </form>\n );\n}\n\nexport interface SuggestionsProps extends Omit<React.ComponentProps<'div'>, 'onSelect'> {\n items: string[];\n onSelect: (item: string) => void;\n}\n\n/** Starter prompts as chips. */\nfunction Suggestions({ items, onSelect, className, ...props }: SuggestionsProps) {\n return (\n <div\n data-slot=\"suggestions\"\n role=\"group\"\n aria-label=\"Suggestions\"\n className={cn('flex flex-wrap gap-2', className)}\n {...props}\n >\n {items.map((item) => (\n <button\n key={item}\n type=\"button\"\n onClick={() => onSelect(item)}\n className=\"animate-in rounded-full border border-border-strong bg-surface px-3 py-1.5 text-[13px] text-foreground outline-none transition-colors hover:border-brand/40 hover:bg-brand-soft hover:text-brand-soft-foreground focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n >\n {item}\n </button>\n ))}\n </div>\n );\n}\n\nexport { Composer, Suggestions };\n"],"mappings":"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"}
1
+ {"version":3,"file":"composer.js","names":["_Fragment"],"sources":["../../src/components/composer.tsx"],"sourcesContent":["import ArrowUp from '@burtson-labs/icons/react/arrow-up';\nimport Paperclip from '@burtson-labs/icons/react/paperclip';\nimport Square from '@burtson-labs/icons/react/square';\nimport * as React from 'react';\n\nimport { cn, focusRingClasses, noOutlineClasses } from '../lib/utils';\n\nimport { Button } from './button';\n\nexport interface ComposerProps extends Omit<React.ComponentProps<'form'>, 'onSubmit' | 'onChange'> {\n /**\n * Clears after success. Reject a returned promise to retain the draft. The\n * text can be empty when `attachmentCount` allows an attachment-only send.\n */\n onSubmit: (text: string) => void | Promise<void>;\n onSubmitError?: (error: unknown) => void;\n submitErrorText?: string;\n /** Controlled value; leave out to let the composer keep its own. */\n value?: string;\n onValueChange?: (value: string) => void;\n /** While a reply streams, the send button becomes Stop. */\n streaming?: boolean;\n onStop?: () => void;\n disabled?: boolean;\n placeholder?: string;\n /** Attachment chips or a file button, shown under the text. */\n attachments?: React.ReactNode;\n /** Attachments ready to send. Above zero, an empty message can be sent. */\n attachmentCount?: number;\n /**\n * Extra gate on sending, e.g. false while an attachment is still parsing.\n * Unlike `disabled`, the person can keep typing.\n */\n canSubmit?: boolean;\n /** Extra controls left of the send button (model picker, tools). */\n actions?: React.ReactNode;\n label?: string;\n /**\n * Files the person picked, dropped on the composer or pasted. Setting it\n * shows a paperclip button. The composer only hands the files over: the\n * app uploads them and renders their state in `attachments` (an\n * AttachmentTray), with `attachmentCount` and `canSubmit` for sending.\n */\n onAttach?: (files: File[]) => void;\n /** File input `accept`, e.g. \"image/*,.pdf\". Dropped and pasted files are filtered too. */\n accept?: string;\n /** Allow several files at once. Default true. */\n multiple?: boolean;\n /** Accessible name of the attach button. */\n attachLabel?: string;\n}\n\n/** Does `file` match an `accept` list (\"image/*,.pdf,text/plain\")? Empty accepts all. */\nexport function acceptsFile(file: Pick<File, 'name' | 'type'>, accept?: string): boolean {\n if (!accept?.trim()) return true;\n const name = file.name.toLowerCase();\n const type = (file.type || '').toLowerCase();\n return accept\n .split(',')\n .map((a) => a.trim().toLowerCase())\n .filter(Boolean)\n .some((rule) =>\n rule.startsWith('.')\n ? name.endsWith(rule)\n : rule.endsWith('/*')\n ? type.startsWith(rule.slice(0, -1))\n : type === rule,\n );\n}\n\n/**\n * The message box: grows with its text, Enter sends, Shift+Enter adds a\n * line, and it never sends mid-composition (IME input).\n */\nconst Composer = React.forwardRef<HTMLFormElement, ComposerProps>(function Composer(\n {\n onSubmit,\n onSubmitError,\n submitErrorText = 'Message could not be sent. Your draft is saved here. Try again.',\n value: controlled,\n onValueChange,\n streaming = false,\n onStop,\n disabled = false,\n placeholder = 'Ask anything…',\n attachments,\n attachmentCount = 0,\n canSubmit = true,\n actions,\n label = 'Message',\n onAttach,\n accept,\n multiple = true,\n attachLabel = 'Attach files',\n className,\n ...props\n },\n ref,\n) {\n const fileInput = React.useRef<HTMLInputElement>(null);\n const [dragging, setDragging] = React.useState(false);\n const dragDepth = React.useRef(0);\n const attach = (list: FileList | File[] | null | undefined) => {\n if (!onAttach || !list) return;\n let files = Array.from(list).filter((f) => acceptsFile(f, accept));\n if (!multiple) files = files.slice(0, 1);\n if (files.length) onAttach(files);\n };\n const hasFiles = (e: React.DragEvent) =>\n Array.from(e.dataTransfer?.types ?? []).includes('Files');\n const [own, setOwn] = React.useState('');\n const value = controlled ?? own;\n const [pending, setPending] = React.useState(false);\n const [failed, setFailed] = React.useState(false);\n const sending = React.useRef(false);\n const latest = React.useRef(value);\n const mounted = React.useRef(true);\n const errorId = React.useId();\n React.useEffect(() => {\n latest.current = value;\n }, [value]);\n React.useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n const setValue = (v: string) => {\n if (controlled === undefined) setOwn(v);\n onValueChange?.(v);\n };\n const canSend =\n canSubmit &&\n !disabled &&\n !streaming &&\n !pending &&\n (value.trim().length > 0 || attachmentCount > 0);\n const send = async () => {\n if (!canSend || sending.current) return;\n const draft = value;\n sending.current = true;\n setFailed(false);\n setPending(true);\n try {\n await onSubmit(draft.trim());\n if (mounted.current && latest.current === draft) setValue('');\n } catch (error) {\n if (mounted.current) setFailed(true);\n onSubmitError?.(error);\n } finally {\n sending.current = false;\n if (mounted.current) setPending(false);\n }\n };\n\n return (\n <form\n ref={ref}\n data-slot=\"composer\"\n data-dragging={dragging || undefined}\n onSubmit={(e) => {\n e.preventDefault();\n void send();\n }}\n onDragEnter={(e) => {\n if (!onAttach || disabled || !hasFiles(e)) return;\n e.preventDefault();\n dragDepth.current += 1;\n setDragging(true);\n }}\n onDragOver={(e) => {\n if (!onAttach || disabled || !hasFiles(e)) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = 'copy';\n }}\n onDragLeave={() => {\n if (!onAttach) return;\n dragDepth.current = Math.max(0, dragDepth.current - 1);\n if (dragDepth.current === 0) setDragging(false);\n }}\n onDrop={(e) => {\n if (!onAttach || disabled) return;\n e.preventDefault();\n dragDepth.current = 0;\n setDragging(false);\n attach(e.dataTransfer?.files);\n }}\n className={cn(\n // The box is the field: same focus as Input (ring border + 1px inset ring).\n 'relative grid gap-2 rounded-lg border border-input bg-surface p-2 shadow-xs transition-[border-color,box-shadow] has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:inset-ring-1 has-[textarea:focus-visible]:inset-ring-ring dark:bg-surface-raised',\n 'data-[dragging]:border-brand data-[dragging]:inset-ring-1 data-[dragging]:inset-ring-brand',\n disabled && 'opacity-60',\n className,\n )}\n {...props}\n >\n {dragging && (\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-1 z-10 grid place-items-center rounded-md border border-dashed border-brand/50 bg-brand-soft/80 text-sm font-medium text-brand-soft-foreground\"\n >\n Drop files to attach\n </div>\n )}\n <textarea\n aria-label={label}\n rows={1}\n value={value}\n disabled={disabled || pending}\n aria-describedby={failed ? errorId : undefined}\n aria-invalid={failed || undefined}\n placeholder={placeholder}\n onChange={(e) => setValue(e.target.value)}\n onPaste={(e) => {\n if (!onAttach) return;\n const files = Array.from(e.clipboardData?.files ?? []);\n // Only take over the paste when it carries files; text pastes as text.\n if (files.length) {\n e.preventDefault();\n attach(files);\n }\n }}\n onKeyDown={(e) => {\n if (\n e.key === 'Enter' &&\n !e.shiftKey &&\n !e.nativeEvent.isComposing &&\n e.nativeEvent.keyCode !== 229\n ) {\n e.preventDefault();\n void send();\n }\n }}\n className={cn(\n 'field-sizing-content max-h-48 min-h-9 w-full resize-none rounded-none border-0 bg-transparent px-2 py-1.5 text-base sm:text-sm leading-6 shadow-none placeholder:text-muted-foreground disabled:cursor-not-allowed pointer-coarse:min-h-11 pointer-coarse:py-2.5',\n noOutlineClasses,\n )}\n />\n {failed && (\n <p id={errorId} role=\"alert\" className=\"px-2 text-sm text-destructive\">\n {submitErrorText}\n </p>\n )}\n {attachments && <div className=\"flex flex-wrap gap-1.5 px-1\">{attachments}</div>}\n <div className=\"flex items-center gap-1\">\n {onAttach && (\n <>\n <input\n ref={fileInput}\n type=\"file\"\n hidden\n tabIndex={-1}\n accept={accept}\n multiple={multiple}\n data-slot=\"composer-file-input\"\n onChange={(e) => {\n attach(e.target.files);\n // Let the same file be picked again after a removal.\n e.target.value = '';\n }}\n />\n <Button\n type=\"button\"\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={attachLabel}\n title={attachLabel}\n disabled={disabled}\n onClick={() => fileInput.current?.click()}\n className=\"pointer-coarse:size-11\"\n >\n <Paperclip />\n </Button>\n </>\n )}\n {actions}\n <div className=\"ml-auto\">\n {streaming ? (\n <Button\n type=\"button\"\n size=\"icon-sm\"\n variant=\"secondary\"\n aria-label=\"Stop\"\n className=\"pointer-coarse:size-11\"\n onClick={onStop}\n disabled={!onStop || disabled}\n >\n <Square className=\"fill-current\" />\n </Button>\n ) : (\n <Button\n type=\"submit\"\n size=\"icon-sm\"\n aria-label=\"Send\"\n className=\"pointer-coarse:size-11\"\n loading={pending}\n disabled={!canSend}\n >\n <ArrowUp />\n </Button>\n )}\n </div>\n </div>\n </form>\n );\n});\n\nexport interface SuggestionsProps extends Omit<React.ComponentProps<'div'>, 'onSelect'> {\n items: string[];\n onSelect: (item: string) => void;\n}\n\n/** Starter prompts as chips. */\nconst Suggestions = React.forwardRef<HTMLDivElement, SuggestionsProps>(function Suggestions(\n { items, onSelect, className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n data-slot=\"suggestions\"\n role=\"group\"\n aria-label=\"Suggestions\"\n className={cn('flex flex-wrap gap-2', className)}\n {...props}\n >\n {items.map((item) => (\n <button\n key={item}\n type=\"button\"\n onClick={() => onSelect(item)}\n className={cn(\n 'animate-in rounded-full border border-border-strong bg-surface px-3 py-1.5 text-[13px] text-foreground transition-colors hover:border-brand/40 hover:bg-brand-soft hover:text-brand-soft-foreground pointer-coarse:min-h-11',\n focusRingClasses,\n )}\n >\n {item}\n </button>\n ))}\n </div>\n );\n});\n\nexport { Composer, Suggestions };\n"],"mappings":"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"}
@@ -15,7 +15,7 @@ export interface ConnectionStatusProps extends Omit<React.ComponentProps<'div'>,
15
15
  onRetry?: () => void;
16
16
  }
17
17
  /** A dot and a word for a live connection: model provider, gateway, socket. */
18
- declare function ConnectionStatus({ state, label, detail, onRetry, ...props }: ConnectionStatusProps): React.JSX.Element;
18
+ declare const ConnectionStatus: React.ForwardRefExoticComponent<Omit<ConnectionStatusProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
19
19
  export interface SyncStatusProps extends Omit<React.ComponentProps<'div'>, 'children'> {
20
20
  state: SyncState;
21
21
  /** When the last successful sync finished. */
@@ -29,7 +29,7 @@ export interface SyncStatusProps extends Omit<React.ComponentProps<'div'>, 'chil
29
29
  formatTime?: (date: Date) => string;
30
30
  }
31
31
  /** Whether local changes have reached the server. */
32
- declare function SyncStatus({ state, lastSynced, pendingCount, detail, onRetry, formatTime, ...props }: SyncStatusProps): React.JSX.Element;
32
+ declare const SyncStatus: React.ForwardRefExoticComponent<Omit<SyncStatusProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
33
33
  export interface ConnectionBannerProps extends Omit<React.ComponentProps<'div'>, 'children'> {
34
34
  state: ConnectionState;
35
35
  /** What still works, e.g. "Messages you send will go out when you reconnect." */
@@ -40,5 +40,5 @@ export interface ConnectionBannerProps extends Omit<React.ComponentProps<'div'>,
40
40
  * A full-width notice for connection trouble. It renders nothing while
41
41
  * connected, connecting or unknown, so it can stay mounted.
42
42
  */
43
- declare function ConnectionBanner({ state, description, onRetry, className, ...props }: ConnectionBannerProps): React.JSX.Element | null;
43
+ declare const ConnectionBanner: React.ForwardRefExoticComponent<Omit<ConnectionBannerProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
44
44
  export { ConnectionBanner, ConnectionStatus, SyncStatus };
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import { cn } from "../lib/utils.js";
2
+ import { cn, focusRingClasses } from "../lib/utils.js";
3
3
  import { Alert, AlertDescription, AlertTitle } from "./alert.js";
4
4
  import { Button } from "./button.js";
5
5
  import { Status } from "./status.js";
6
6
  import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip.js";
7
- import "react";
7
+ import * as React from "react";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  import CloudOff from "@burtson-labs/icons/react/cloud-off";
10
10
  import WifiOff from "@burtson-labs/icons/react/wifi-off";
@@ -30,7 +30,7 @@ var connection = {
30
30
  },
31
31
  error: {
32
32
  text: "Connection failed",
33
- tone: "danger"
33
+ tone: "destructive"
34
34
  },
35
35
  unknown: {
36
36
  text: "Status unknown",
@@ -57,7 +57,7 @@ var sync = {
57
57
  },
58
58
  error: {
59
59
  text: "Sync failed",
60
- tone: "danger"
60
+ tone: "destructive"
61
61
  },
62
62
  unknown: {
63
63
  text: "Sync status unknown",
@@ -71,8 +71,9 @@ function WithDetail({ detail, children }) {
71
71
  children
72
72
  }), /* @__PURE__ */ jsx(TooltipContent, { children: detail })] });
73
73
  }
74
- function StatusLine({ slot, state, text, tone, pulse, detail, onRetry, className, ...props }) {
74
+ var StatusLine = React.forwardRef(function StatusLine({ slot, state, text, tone, pulse, detail, onRetry, className, ...props }, ref) {
75
75
  return /* @__PURE__ */ jsxs("div", {
76
+ ref,
76
77
  "data-slot": slot,
77
78
  "data-state": state,
78
79
  role: "status",
@@ -84,7 +85,7 @@ function StatusLine({ slot, state, text, tone, pulse, detail, onRetry, className
84
85
  status: tone,
85
86
  pulse,
86
87
  tabIndex: detail ? 0 : void 0,
87
- className: "rounded-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20",
88
+ className: cn("rounded-sm", focusRingClasses, detail && "relative pointer-coarse:before:absolute pointer-coarse:before:inset-x-0 pointer-coarse:before:top-1/2 pointer-coarse:before:h-11 pointer-coarse:before:-translate-y-1/2 pointer-coarse:before:content-['']"),
88
89
  children: [text, detail && typeof detail === "string" && /* @__PURE__ */ jsxs("span", {
89
90
  className: "sr-only",
90
91
  children: [". ", detail]
@@ -99,11 +100,12 @@ function StatusLine({ slot, state, text, tone, pulse, detail, onRetry, className
99
100
  children: "Retry"
100
101
  })]
101
102
  });
102
- }
103
+ });
103
104
  /** A dot and a word for a live connection: model provider, gateway, socket. */
104
- function ConnectionStatus({ state, label, detail, onRetry, ...props }) {
105
+ var ConnectionStatus = React.forwardRef(function ConnectionStatus({ state, label, detail, onRetry, ...props }, ref) {
105
106
  const c = connection[state];
106
107
  return /* @__PURE__ */ jsx(StatusLine, {
108
+ ref,
107
109
  slot: "connection-status",
108
110
  state,
109
111
  text: label ?? c.text,
@@ -113,17 +115,18 @@ function ConnectionStatus({ state, label, detail, onRetry, ...props }) {
113
115
  onRetry: state === "offline" || state === "error" ? onRetry : void 0,
114
116
  ...props
115
117
  });
116
- }
118
+ });
117
119
  /** Whether local changes have reached the server. */
118
- function SyncStatus({ state, lastSynced, pendingCount, detail, onRetry, formatTime = (d) => d.toLocaleTimeString([], {
120
+ var SyncStatus = React.forwardRef(function SyncStatus({ state, lastSynced, pendingCount, detail, onRetry, formatTime = (d) => d.toLocaleTimeString([], {
119
121
  hour: "numeric",
120
122
  minute: "2-digit"
121
- }), ...props }) {
123
+ }), ...props }, ref) {
122
124
  const s = sync[state];
123
125
  let text = s.text;
124
126
  if (state === "synced" && lastSynced) text = `Synced ${formatTime(lastSynced)}`;
125
127
  if (state === "pending" && pendingCount) text = `${pendingCount} ${pendingCount === 1 ? "change" : "changes"} not synced yet`;
126
128
  return /* @__PURE__ */ jsx(StatusLine, {
129
+ ref,
127
130
  slot: "sync-status",
128
131
  state,
129
132
  text,
@@ -133,15 +136,16 @@ function SyncStatus({ state, lastSynced, pendingCount, detail, onRetry, formatTi
133
136
  onRetry: state === "error" ? onRetry : void 0,
134
137
  ...props
135
138
  });
136
- }
139
+ });
137
140
  /**
138
141
  * A full-width notice for connection trouble. It renders nothing while
139
142
  * connected, connecting or unknown, so it can stay mounted.
140
143
  */
141
- function ConnectionBanner({ state, description, onRetry, className, ...props }) {
144
+ var ConnectionBanner = React.forwardRef(function ConnectionBanner({ state, description, onRetry, className, ...props }, ref) {
142
145
  if (state !== "offline" && state !== "error" && state !== "reconnecting") return null;
143
146
  const title = state === "offline" ? "You are offline" : state === "reconnecting" ? "Reconnecting…" : "Could not connect";
144
147
  return /* @__PURE__ */ jsxs(Alert, {
148
+ ref,
145
149
  "data-slot": "connection-banner",
146
150
  "data-state": state,
147
151
  variant: state === "error" ? "destructive" : "warning",
@@ -161,7 +165,7 @@ function ConnectionBanner({ state, description, onRetry, className, ...props })
161
165
  })]
162
166
  })]
163
167
  });
164
- }
168
+ });
165
169
  //#endregion
166
170
  export { ConnectionBanner, ConnectionStatus, SyncStatus };
167
171
 
@@ -1 +1 @@
1
- {"version":3,"file":"connection-status.js","names":[],"sources":["../../src/components/connection-status.tsx"],"sourcesContent":["import CloudOff from '@burtson-labs/icons/react/cloud-off';\nimport WifiOff from '@burtson-labs/icons/react/wifi-off';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { Alert, AlertDescription, AlertTitle } from './alert';\nimport { Button } from './button';\nimport { Status } from './status';\nimport { Tooltip, TooltipContent, TooltipTrigger } from './tooltip';\n\ntype Tone = 'neutral' | 'brand' | 'success' | 'warning' | 'danger' | 'info';\n\n/**\n * The host reports the state; these components only present it. `unknown`\n * means nobody has checked yet and never reads as connected.\n */\nexport type ConnectionState =\n 'connected' | 'connecting' | 'reconnecting' | 'offline' | 'error' | 'unknown';\n\nconst connection: Record<ConnectionState, { text: string; tone: Tone; pulse?: boolean }> = {\n connected: { text: 'Connected', tone: 'success' },\n connecting: { text: 'Connecting', tone: 'info', pulse: true },\n reconnecting: { text: 'Reconnecting', tone: 'warning', pulse: true },\n offline: { text: 'Offline', tone: 'neutral' },\n error: { text: 'Connection failed', tone: 'danger' },\n unknown: { text: 'Status unknown', tone: 'neutral' },\n};\n\nexport type SyncState = 'synced' | 'syncing' | 'pending' | 'offline' | 'error' | 'unknown';\n\nconst sync: Record<SyncState, { text: string; tone: Tone; pulse?: boolean }> = {\n synced: { text: 'Synced', tone: 'success' },\n syncing: { text: 'Syncing', tone: 'info', pulse: true },\n pending: { text: 'Changes not synced yet', tone: 'warning' },\n offline: { text: 'Offline, saved on this device', tone: 'neutral' },\n error: { text: 'Sync failed', tone: 'danger' },\n unknown: { text: 'Sync status unknown', tone: 'neutral' },\n};\n\nfunction WithDetail({\n detail,\n children,\n}: {\n detail?: React.ReactNode;\n children: React.ReactElement;\n}) {\n if (!detail) return children;\n return (\n <Tooltip>\n <TooltipTrigger asChild>{children}</TooltipTrigger>\n <TooltipContent>{detail}</TooltipContent>\n </Tooltip>\n );\n}\n\nfunction StatusLine({\n slot,\n state,\n text,\n tone,\n pulse,\n detail,\n onRetry,\n className,\n ...props\n}: Omit<React.ComponentProps<'div'>, 'children'> & {\n slot: string;\n state: string;\n text: React.ReactNode;\n tone: Tone;\n pulse?: boolean;\n detail?: React.ReactNode;\n onRetry?: () => void;\n}) {\n return (\n <div\n data-slot={slot}\n data-state={state}\n role=\"status\"\n className={cn('inline-flex items-center gap-2', className)}\n {...props}\n >\n <WithDetail detail={detail}>\n <Status\n status={tone}\n pulse={pulse}\n tabIndex={detail ? 0 : undefined}\n className=\"rounded-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n >\n {text}\n {detail && typeof detail === 'string' && <span className=\"sr-only\">. {detail}</span>}\n </Status>\n </WithDetail>\n {onRetry && (\n <Button type=\"button\" variant=\"link\" size=\"xs\" className=\"h-auto px-0\" onClick={onRetry}>\n Retry\n </Button>\n )}\n </div>\n );\n}\n\nexport interface ConnectionStatusProps extends Omit<React.ComponentProps<'div'>, 'children'> {\n state: ConnectionState;\n /** Overrides the default text, e.g. \"Connected to Ollama\". */\n label?: React.ReactNode;\n /** More detail in a tooltip, e.g. the endpoint or last error. */\n detail?: React.ReactNode;\n /** Offered when offline or failed. */\n onRetry?: () => void;\n}\n\n/** A dot and a word for a live connection: model provider, gateway, socket. */\nfunction ConnectionStatus({ state, label, detail, onRetry, ...props }: ConnectionStatusProps) {\n const c = connection[state];\n return (\n <StatusLine\n slot=\"connection-status\"\n state={state}\n text={label ?? c.text}\n tone={c.tone}\n pulse={c.pulse}\n detail={detail}\n onRetry={state === 'offline' || state === 'error' ? onRetry : undefined}\n {...props}\n />\n );\n}\n\nexport interface SyncStatusProps extends Omit<React.ComponentProps<'div'>, 'children'> {\n state: SyncState;\n /** When the last successful sync finished. */\n lastSynced?: Date;\n /** Changes waiting to go up. */\n pendingCount?: number;\n detail?: React.ReactNode;\n /** Offered when sync failed. */\n onRetry?: () => void;\n /** Formats `lastSynced`; the default is the local time. */\n formatTime?: (date: Date) => string;\n}\n\n/** Whether local changes have reached the server. */\nfunction SyncStatus({\n state,\n lastSynced,\n pendingCount,\n detail,\n onRetry,\n formatTime = (d) => d.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }),\n ...props\n}: SyncStatusProps) {\n const s = sync[state];\n let text = s.text;\n if (state === 'synced' && lastSynced) text = `Synced ${formatTime(lastSynced)}`;\n if (state === 'pending' && pendingCount)\n text = `${pendingCount} ${pendingCount === 1 ? 'change' : 'changes'} not synced yet`;\n return (\n <StatusLine\n slot=\"sync-status\"\n state={state}\n text={text}\n tone={s.tone}\n pulse={s.pulse}\n detail={detail}\n onRetry={state === 'error' ? onRetry : undefined}\n {...props}\n />\n );\n}\n\nexport interface ConnectionBannerProps extends Omit<React.ComponentProps<'div'>, 'children'> {\n state: ConnectionState;\n /** What still works, e.g. \"Messages you send will go out when you reconnect.\" */\n description?: React.ReactNode;\n onRetry?: () => void;\n}\n\n/**\n * A full-width notice for connection trouble. It renders nothing while\n * connected, connecting or unknown, so it can stay mounted.\n */\nfunction ConnectionBanner({\n state,\n description,\n onRetry,\n className,\n ...props\n}: ConnectionBannerProps) {\n if (state !== 'offline' && state !== 'error' && state !== 'reconnecting') return null;\n const title =\n state === 'offline'\n ? 'You are offline'\n : state === 'reconnecting'\n ? 'Reconnecting…'\n : 'Could not connect';\n return (\n <Alert\n data-slot=\"connection-banner\"\n data-state={state}\n variant={state === 'error' ? 'destructive' : 'warning'}\n className={cn('items-center', className)}\n {...props}\n >\n {state === 'offline' ? <WifiOff aria-hidden /> : <CloudOff aria-hidden />}\n <div className=\"flex min-w-0 flex-wrap items-center justify-between gap-x-4 gap-y-2\">\n <div className=\"min-w-0\">\n <AlertTitle>{title}</AlertTitle>\n {description && <AlertDescription>{description}</AlertDescription>}\n </div>\n {onRetry && state !== 'reconnecting' && (\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={onRetry}>\n Retry\n </Button>\n )}\n </div>\n </Alert>\n );\n}\n\nexport { ConnectionBanner, ConnectionStatus, SyncStatus };\n"],"mappings":"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"}
1
+ {"version":3,"file":"connection-status.js","names":[],"sources":["../../src/components/connection-status.tsx"],"sourcesContent":["import CloudOff from '@burtson-labs/icons/react/cloud-off';\nimport WifiOff from '@burtson-labs/icons/react/wifi-off';\nimport * as React from 'react';\n\nimport { cn, focusRingClasses, touchTargetRowClasses } from '../lib/utils';\n\nimport { Alert, AlertDescription, AlertTitle } from './alert';\nimport { Button } from './button';\nimport { Status } from './status';\nimport { Tooltip, TooltipContent, TooltipTrigger } from './tooltip';\n\ntype Tone = 'neutral' | 'brand' | 'success' | 'warning' | 'destructive' | 'info';\n\n/**\n * The host reports the state; these components only present it. `unknown`\n * means nobody has checked yet and never reads as connected.\n */\nexport type ConnectionState =\n 'connected' | 'connecting' | 'reconnecting' | 'offline' | 'error' | 'unknown';\n\nconst connection: Record<ConnectionState, { text: string; tone: Tone; pulse?: boolean }> = {\n connected: { text: 'Connected', tone: 'success' },\n connecting: { text: 'Connecting', tone: 'info', pulse: true },\n reconnecting: { text: 'Reconnecting', tone: 'warning', pulse: true },\n offline: { text: 'Offline', tone: 'neutral' },\n error: { text: 'Connection failed', tone: 'destructive' },\n unknown: { text: 'Status unknown', tone: 'neutral' },\n};\n\nexport type SyncState = 'synced' | 'syncing' | 'pending' | 'offline' | 'error' | 'unknown';\n\nconst sync: Record<SyncState, { text: string; tone: Tone; pulse?: boolean }> = {\n synced: { text: 'Synced', tone: 'success' },\n syncing: { text: 'Syncing', tone: 'info', pulse: true },\n pending: { text: 'Changes not synced yet', tone: 'warning' },\n offline: { text: 'Offline, saved on this device', tone: 'neutral' },\n error: { text: 'Sync failed', tone: 'destructive' },\n unknown: { text: 'Sync status unknown', tone: 'neutral' },\n};\n\nfunction WithDetail({\n detail,\n children,\n}: {\n detail?: React.ReactNode;\n children: React.ReactElement;\n}) {\n if (!detail) return children;\n return (\n <Tooltip>\n <TooltipTrigger asChild>{children}</TooltipTrigger>\n <TooltipContent>{detail}</TooltipContent>\n </Tooltip>\n );\n}\n\ninterface StatusLineProps extends Omit<React.ComponentProps<'div'>, 'children'> {\n slot: string;\n state: string;\n text: React.ReactNode;\n tone: Tone;\n pulse?: boolean;\n detail?: React.ReactNode;\n onRetry?: () => void;\n}\n\nconst StatusLine = React.forwardRef<HTMLDivElement, StatusLineProps>(function StatusLine(\n { slot, state, text, tone, pulse, detail, onRetry, className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n data-slot={slot}\n data-state={state}\n role=\"status\"\n className={cn('inline-flex items-center gap-2', className)}\n {...props}\n >\n <WithDetail detail={detail}>\n <Status\n status={tone}\n pulse={pulse}\n tabIndex={detail ? 0 : undefined}\n className={cn('rounded-sm', focusRingClasses, detail && touchTargetRowClasses)}\n >\n {text}\n {detail && typeof detail === 'string' && <span className=\"sr-only\">. {detail}</span>}\n </Status>\n </WithDetail>\n {onRetry && (\n <Button type=\"button\" variant=\"link\" size=\"xs\" className=\"h-auto px-0\" onClick={onRetry}>\n Retry\n </Button>\n )}\n </div>\n );\n});\n\nexport interface ConnectionStatusProps extends Omit<React.ComponentProps<'div'>, 'children'> {\n state: ConnectionState;\n /** Overrides the default text, e.g. \"Connected to Ollama\". */\n label?: React.ReactNode;\n /** More detail in a tooltip, e.g. the endpoint or last error. */\n detail?: React.ReactNode;\n /** Offered when offline or failed. */\n onRetry?: () => void;\n}\n\n/** A dot and a word for a live connection: model provider, gateway, socket. */\nconst ConnectionStatus = React.forwardRef<HTMLDivElement, ConnectionStatusProps>(\n function ConnectionStatus({ state, label, detail, onRetry, ...props }, ref) {\n const c = connection[state];\n return (\n <StatusLine\n ref={ref}\n slot=\"connection-status\"\n state={state}\n text={label ?? c.text}\n tone={c.tone}\n pulse={c.pulse}\n detail={detail}\n onRetry={state === 'offline' || state === 'error' ? onRetry : undefined}\n {...props}\n />\n );\n },\n);\n\nexport interface SyncStatusProps extends Omit<React.ComponentProps<'div'>, 'children'> {\n state: SyncState;\n /** When the last successful sync finished. */\n lastSynced?: Date;\n /** Changes waiting to go up. */\n pendingCount?: number;\n detail?: React.ReactNode;\n /** Offered when sync failed. */\n onRetry?: () => void;\n /** Formats `lastSynced`; the default is the local time. */\n formatTime?: (date: Date) => string;\n}\n\n/** Whether local changes have reached the server. */\nconst SyncStatus = React.forwardRef<HTMLDivElement, SyncStatusProps>(function SyncStatus(\n {\n state,\n lastSynced,\n pendingCount,\n detail,\n onRetry,\n formatTime = (d) => d.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }),\n ...props\n },\n ref,\n) {\n const s = sync[state];\n let text = s.text;\n if (state === 'synced' && lastSynced) text = `Synced ${formatTime(lastSynced)}`;\n if (state === 'pending' && pendingCount)\n text = `${pendingCount} ${pendingCount === 1 ? 'change' : 'changes'} not synced yet`;\n return (\n <StatusLine\n ref={ref}\n slot=\"sync-status\"\n state={state}\n text={text}\n tone={s.tone}\n pulse={s.pulse}\n detail={detail}\n onRetry={state === 'error' ? onRetry : undefined}\n {...props}\n />\n );\n});\n\nexport interface ConnectionBannerProps extends Omit<React.ComponentProps<'div'>, 'children'> {\n state: ConnectionState;\n /** What still works, e.g. \"Messages you send will go out when you reconnect.\" */\n description?: React.ReactNode;\n onRetry?: () => void;\n}\n\n/**\n * A full-width notice for connection trouble. It renders nothing while\n * connected, connecting or unknown, so it can stay mounted.\n */\nconst ConnectionBanner = React.forwardRef<HTMLDivElement, ConnectionBannerProps>(\n function ConnectionBanner({ state, description, onRetry, className, ...props }, ref) {\n if (state !== 'offline' && state !== 'error' && state !== 'reconnecting') return null;\n const title =\n state === 'offline'\n ? 'You are offline'\n : state === 'reconnecting'\n ? 'Reconnecting…'\n : 'Could not connect';\n return (\n <Alert\n ref={ref}\n data-slot=\"connection-banner\"\n data-state={state}\n variant={state === 'error' ? 'destructive' : 'warning'}\n className={cn('items-center', className)}\n {...props}\n >\n {state === 'offline' ? <WifiOff aria-hidden /> : <CloudOff aria-hidden />}\n <div className=\"flex min-w-0 flex-wrap items-center justify-between gap-x-4 gap-y-2\">\n <div className=\"min-w-0\">\n <AlertTitle>{title}</AlertTitle>\n {description && <AlertDescription>{description}</AlertDescription>}\n </div>\n {onRetry && state !== 'reconnecting' && (\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={onRetry}>\n Retry\n </Button>\n )}\n </div>\n </Alert>\n );\n },\n);\n\nexport { ConnectionBanner, ConnectionStatus, SyncStatus };\n"],"mappings":"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"}
@@ -7,24 +7,25 @@ import * as ContextMenuPrimitive from '../primitives/vendor/radix/react-context-
7
7
  * DropdownMenu, so a command can appear in both.
8
8
  */
9
9
  declare function ContextMenu(props: React.ComponentProps<typeof ContextMenuPrimitive.Root>): React.JSX.Element;
10
- declare function ContextMenuTrigger(props: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>): React.JSX.Element;
10
+ declare const ContextMenuTrigger: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuTriggerProps & React.RefAttributes<HTMLSpanElement>, "ref"> & React.RefAttributes<HTMLSpanElement>>;
11
11
  declare function ContextMenuGroup(props: React.ComponentProps<typeof ContextMenuPrimitive.Group>): React.JSX.Element;
12
- declare function ContextMenuContent({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Content>): React.JSX.Element;
13
- declare function ContextMenuItem({ className, inset, variant, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
12
+ declare const ContextMenuContent: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
13
+ export interface ContextMenuItemProps extends React.ComponentProps<typeof ContextMenuPrimitive.Item> {
14
14
  inset?: boolean;
15
15
  variant?: 'default' | 'destructive';
16
- }): React.JSX.Element;
17
- declare function ContextMenuCheckboxItem({ className, children, checked, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>): React.JSX.Element;
16
+ }
17
+ declare const ContextMenuItem: React.ForwardRefExoticComponent<Omit<ContextMenuItemProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
18
+ declare const ContextMenuCheckboxItem: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuCheckboxItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
18
19
  declare function ContextMenuRadioGroup(props: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>): React.JSX.Element;
19
- declare function ContextMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>): React.JSX.Element;
20
- declare function ContextMenuLabel({ className, inset, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
20
+ declare const ContextMenuRadioItem: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuRadioItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
21
+ declare const ContextMenuLabel: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuLabelProps & React.RefAttributes<HTMLDivElement> & {
21
22
  inset?: boolean;
22
- }): React.JSX.Element;
23
- declare function ContextMenuSeparator({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Separator>): React.JSX.Element;
24
- declare function ContextMenuShortcut({ className, ...props }: React.ComponentProps<'span'>): React.JSX.Element;
23
+ }, "ref"> & React.RefAttributes<HTMLDivElement>>;
24
+ declare const ContextMenuSeparator: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
25
+ declare const ContextMenuShortcut: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & React.RefAttributes<HTMLSpanElement>>;
25
26
  declare function ContextMenuSub(props: React.ComponentProps<typeof ContextMenuPrimitive.Sub>): React.JSX.Element;
26
- declare function ContextMenuSubTrigger({ className, inset, children, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
27
+ declare const ContextMenuSubTrigger: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuSubTriggerProps & React.RefAttributes<HTMLDivElement> & {
27
28
  inset?: boolean;
28
- }): React.JSX.Element;
29
- declare function ContextMenuSubContent({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>): React.JSX.Element;
29
+ }, "ref"> & React.RefAttributes<HTMLDivElement>>;
30
+ declare const ContextMenuSubContent: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuSubContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
30
31
  export { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, };