@justanarthur/modo-ui 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSE.beui +21 -0
  3. package/LICENSE.fluid-functionalism +21 -0
  4. package/LICENSE.motion-primitives +21 -0
  5. package/LICENSE.sileo +21 -0
  6. package/README.md +608 -2
  7. package/dist/base.css +994 -0
  8. package/dist/blocks/sidebar-app/index.d.ts +34 -0
  9. package/dist/blocks/sidebar-app/index.js +31 -0
  10. package/dist/components/badge/index.d.ts +59 -0
  11. package/dist/components/badge/index.js +16 -0
  12. package/dist/components/button/index.d.ts +59 -0
  13. package/dist/components/button/index.js +14 -0
  14. package/dist/components/card/index.d.ts +141 -0
  15. package/dist/components/card/index.js +42 -0
  16. package/dist/components/inputs/checkbox-group/index.d.ts +50 -0
  17. package/dist/components/inputs/checkbox-group/index.js +21 -0
  18. package/dist/components/inputs/color-picker/index.d.ts +137 -0
  19. package/dist/components/inputs/color-picker/index.js +35 -0
  20. package/dist/components/inputs/combobox/index.d.ts +161 -0
  21. package/dist/components/inputs/combobox/index.js +38 -0
  22. package/dist/components/inputs/date-time/calendar/index.d.ts +45 -0
  23. package/dist/components/inputs/date-time/calendar/index.js +18 -0
  24. package/dist/components/inputs/date-time/date-picker/index.d.ts +82 -0
  25. package/dist/components/inputs/date-time/date-picker/index.js +32 -0
  26. package/dist/components/inputs/date-time/date-range-picker/index.d.ts +64 -0
  27. package/dist/components/inputs/date-time/date-range-picker/index.js +31 -0
  28. package/dist/components/inputs/date-time/duration-picker/index.d.ts +57 -0
  29. package/dist/components/inputs/date-time/duration-picker/index.js +31 -0
  30. package/dist/components/inputs/date-time/month-picker/index.d.ts +57 -0
  31. package/dist/components/inputs/date-time/month-picker/index.js +30 -0
  32. package/dist/components/inputs/date-time/time-picker/index.d.ts +59 -0
  33. package/dist/components/inputs/date-time/time-picker/index.js +31 -0
  34. package/dist/components/inputs/input/index.d.ts +84 -0
  35. package/dist/components/inputs/input/index.js +19 -0
  36. package/dist/components/inputs/input-copy/index.d.ts +44 -0
  37. package/dist/components/inputs/input-copy/index.js +16 -0
  38. package/dist/components/inputs/input-message/index.d.ts +125 -0
  39. package/dist/components/inputs/input-message/index.js +25 -0
  40. package/dist/components/inputs/radio-group/index.d.ts +58 -0
  41. package/dist/components/inputs/radio-group/index.js +20 -0
  42. package/dist/components/inputs/search/index.d.ts +83 -0
  43. package/dist/components/inputs/search/index.js +25 -0
  44. package/dist/components/inputs/select/index.d.ts +105 -0
  45. package/dist/components/inputs/select/index.js +34 -0
  46. package/dist/components/inputs/slider/index.d.ts +108 -0
  47. package/dist/components/inputs/slider/index.js +14 -0
  48. package/dist/components/inputs/switch/index.d.ts +43 -0
  49. package/dist/components/inputs/switch/index.js +14 -0
  50. package/dist/components/navigation/accordion/index.d.ts +117 -0
  51. package/dist/components/navigation/accordion/index.js +28 -0
  52. package/dist/components/navigation/sidebar/index.d.ts +388 -0
  53. package/dist/components/navigation/sidebar/index.js +99 -0
  54. package/dist/components/navigation/tabs/index.d.ts +70 -0
  55. package/dist/components/navigation/tabs/index.js +23 -0
  56. package/dist/components/navigation/tabs-subtle/index.d.ts +73 -0
  57. package/dist/components/navigation/tabs-subtle/index.js +21 -0
  58. package/dist/components/overlays/alert-dialog/index.d.ts +67 -0
  59. package/dist/components/overlays/alert-dialog/index.js +22 -0
  60. package/dist/components/overlays/command/index.d.ts +121 -0
  61. package/dist/components/overlays/command/index.js +36 -0
  62. package/dist/components/overlays/context-menu/index.d.ts +97 -0
  63. package/dist/components/overlays/context-menu/index.js +29 -0
  64. package/dist/components/overlays/dialog/index.d.ts +130 -0
  65. package/dist/components/overlays/dialog/index.js +37 -0
  66. package/dist/components/overlays/dropdown/index.d.ts +222 -0
  67. package/dist/components/overlays/dropdown/index.js +44 -0
  68. package/dist/components/overlays/popover/index.d.ts +87 -0
  69. package/dist/components/overlays/popover/index.js +26 -0
  70. package/dist/components/overlays/preview-card/index.d.ts +75 -0
  71. package/dist/components/overlays/preview-card/index.js +19 -0
  72. package/dist/components/overlays/sheet/index.d.ts +67 -0
  73. package/dist/components/overlays/sheet/index.js +24 -0
  74. package/dist/components/overlays/toast/index.d.ts +107 -0
  75. package/dist/components/overlays/toast/index.js +21 -0
  76. package/dist/components/overlays/tooltip/index.d.ts +75 -0
  77. package/dist/components/overlays/tooltip/index.js +17 -0
  78. package/dist/components/table/index.d.ts +44 -0
  79. package/dist/components/table/index.js +26 -0
  80. package/dist/components/thinking-indicator/index.d.ts +32 -0
  81. package/dist/components/thinking-indicator/index.js +13 -0
  82. package/dist/index.d.ts +3926 -0
  83. package/dist/index.js +511 -0
  84. package/dist/preset.d.ts +104 -0
  85. package/dist/preset.js +124 -0
  86. package/dist/primitives/code/index.d.ts +23 -0
  87. package/dist/primitives/code/index.js +11 -0
  88. package/dist/primitives/craft/index.d.ts +31 -0
  89. package/dist/primitives/craft/index.js +11 -0
  90. package/dist/primitives/fluid-hover/index.d.ts +233 -0
  91. package/dist/primitives/fluid-hover/index.js +25 -0
  92. package/dist/primitives/morph/index.d.ts +127 -0
  93. package/dist/primitives/morph/index.js +19 -0
  94. package/dist/primitives/motion/index.d.ts +115 -0
  95. package/dist/primitives/motion/index.js +19 -0
  96. package/dist/primitives/scroll-area/index.d.ts +41 -0
  97. package/dist/primitives/scroll-area/index.js +12 -0
  98. package/dist/primitives/sizes/index.d.ts +128 -0
  99. package/dist/primitives/sizes/index.js +21 -0
  100. package/dist/primitives/surface/index.d.ts +69 -0
  101. package/dist/primitives/surface/index.js +17 -0
  102. package/dist/shared/components-0ke6xada.js +123 -0
  103. package/dist/shared/components-0q0sqfh4.js +146 -0
  104. package/dist/shared/components-0sn7ypjw.js +61 -0
  105. package/dist/shared/components-1j9791sn.js +345 -0
  106. package/dist/shared/components-1rfwbyyc.js +361 -0
  107. package/dist/shared/components-1v7rxytx.js +30 -0
  108. package/dist/shared/components-1xekx2hx.js +215 -0
  109. package/dist/shared/components-39jq9va1.js +86 -0
  110. package/dist/shared/components-3gae8nta.js +719 -0
  111. package/dist/shared/components-3k8xpnpq.js +1759 -0
  112. package/dist/shared/components-3ky44908.js +9 -0
  113. package/dist/shared/components-3mkepka5.js +111 -0
  114. package/dist/shared/components-3wk0w46f.js +667 -0
  115. package/dist/shared/components-4cnjmgex.js +124 -0
  116. package/dist/shared/components-59efxjd9.js +298 -0
  117. package/dist/shared/components-5xc2j8jz.js +159 -0
  118. package/dist/shared/components-62ybc7t2.js +496 -0
  119. package/dist/shared/components-66mv71kx.js +262 -0
  120. package/dist/shared/components-7ans7ce5.js +120 -0
  121. package/dist/shared/components-7btqt08d.js +270 -0
  122. package/dist/shared/components-7r3etddy.js +171 -0
  123. package/dist/shared/components-7x9kawxv.js +152 -0
  124. package/dist/shared/components-8fpr3s4p.js +292 -0
  125. package/dist/shared/components-9jvzzvt1.js +198 -0
  126. package/dist/shared/components-apdwtweq.js +484 -0
  127. package/dist/shared/components-asr5pg50.js +191 -0
  128. package/dist/shared/components-b2wyq2cq.js +232 -0
  129. package/dist/shared/components-c6s6b43g.js +15 -0
  130. package/dist/shared/components-ca2c898n.js +1828 -0
  131. package/dist/shared/components-cbxjn9p8.js +141 -0
  132. package/dist/shared/components-chacnk61.js +216 -0
  133. package/dist/shared/components-cv16mryv.js +62 -0
  134. package/dist/shared/components-ez65vtbq.js +110 -0
  135. package/dist/shared/components-ezxhrmer.js +17 -0
  136. package/dist/shared/components-gc5vtq2f.js +149 -0
  137. package/dist/shared/components-gf5s6ap9.js +42 -0
  138. package/dist/shared/components-gvr9w41t.js +193 -0
  139. package/dist/shared/components-h2dpcwgc.js +137 -0
  140. package/dist/shared/components-hawvy2qz.js +105 -0
  141. package/dist/shared/components-hqeq3w9q.js +375 -0
  142. package/dist/shared/components-jjw69c4c.js +28 -0
  143. package/dist/shared/components-jwst3vtj.js +346 -0
  144. package/dist/shared/components-kvr7f9hy.js +45 -0
  145. package/dist/shared/components-men63rwj.js +9 -0
  146. package/dist/shared/components-mhd39z7x.js +22 -0
  147. package/dist/shared/components-n7mjr357.js +26 -0
  148. package/dist/shared/components-ng72k97e.js +128 -0
  149. package/dist/shared/components-nh8tma1n.js +986 -0
  150. package/dist/shared/components-njzgqdwy.js +445 -0
  151. package/dist/shared/components-ny774h8p.js +293 -0
  152. package/dist/shared/components-q4daerx0.js +58 -0
  153. package/dist/shared/components-q5xj5c16.js +79 -0
  154. package/dist/shared/components-rr7a6ja9.js +160 -0
  155. package/dist/shared/components-s2bz1xpk.js +63 -0
  156. package/dist/shared/components-tqvarbfv.js +288 -0
  157. package/dist/shared/components-v6xp325t.js +304 -0
  158. package/dist/shared/components-v7wtz8hk.js +144 -0
  159. package/dist/shared/components-vc7vq5nz.js +32 -0
  160. package/dist/shared/components-vfbhxf5r.js +58 -0
  161. package/dist/shared/components-vynjtrk6.js +19 -0
  162. package/dist/shared/components-wvp1m52g.js +1200 -0
  163. package/dist/shared/components-yh3kwdhr.js +981 -0
  164. package/dist/shared/components-ytb2tefr.js +291 -0
  165. package/dist/shared/components-zfnmn5d4.js +220 -0
  166. package/dist/shared/components-zrkzcayz.js +141 -0
  167. package/dist/shared/components-zx6b6yby.js +42 -0
  168. package/dist/styles.css +1949 -0
  169. package/dist/tailwind.css +219 -0
  170. package/dist/tokens.css +345 -0
  171. package/package.json +103 -4
@@ -0,0 +1,981 @@
1
+ import {
2
+ cn2
3
+ } from "./components-ezxhrmer.js";
4
+ import {
5
+ FluidHoverHighlight2
6
+ } from "./components-kvr7f9hy.js";
7
+ import {
8
+ useIcon
9
+ } from "./components-zrkzcayz.js";
10
+ import {
11
+ useShape
12
+ } from "./components-cv16mryv.js";
13
+ import {
14
+ useSize2
15
+ } from "./components-0sn7ypjw.js";
16
+ import {
17
+ surfaceClasses2,
18
+ SurfaceProvider2
19
+ } from "./components-zx6b6yby.js";
20
+ import {
21
+ useControllableState
22
+ } from "./components-n7mjr357.js";
23
+ import {
24
+ useFluidHover2,
25
+ useRegisterFluidHoverItem2
26
+ } from "./components-59efxjd9.js";
27
+ import {
28
+ SizeProvider2
29
+ } from "./components-jjw69c4c.js";
30
+ import {
31
+ Button2
32
+ } from "./components-b2wyq2cq.js";
33
+ import {
34
+ Tooltip2
35
+ } from "./components-ng72k97e.js";
36
+ import {
37
+ spring2
38
+ } from "./components-1v7rxytx.js";
39
+
40
+ // components/inputs/input-message/index.tsx
41
+ import { AnimatePresence, motion, Reorder, useReducedMotion } from "motion/react";
42
+ import {
43
+ forwardRef,
44
+ useCallback,
45
+ useEffect as useEffect2,
46
+ useId,
47
+ useLayoutEffect,
48
+ useMemo,
49
+ useRef,
50
+ useState as useState2
51
+ } from "react";
52
+
53
+ // components/inputs/input-message/file-thumbnail.tsx
54
+ import { useEffect, useState } from "react";
55
+ import { jsx, jsxs } from "react/jsx-runtime";
56
+ var pdfjsPromise = null;
57
+ async function loadPdfjs() {
58
+ if (!pdfjsPromise) {
59
+ pdfjsPromise = import("pdfjs-dist").then((mod) => {
60
+ if (!mod.GlobalWorkerOptions.workerSrc) {
61
+ mod.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${mod.version}/build/pdf.worker.min.mjs`;
62
+ }
63
+ return mod;
64
+ });
65
+ }
66
+ return pdfjsPromise;
67
+ }
68
+ async function renderPdfFirstPage(file, targetWidth) {
69
+ const pdfjs = await loadPdfjs();
70
+ const buffer = await file.arrayBuffer();
71
+ const pdf = await pdfjs.getDocument({ data: buffer }).promise;
72
+ const page = await pdf.getPage(1);
73
+ const baseViewport = page.getViewport({ scale: 1 });
74
+ const scale = targetWidth * 2 / baseViewport.width;
75
+ const viewport = page.getViewport({ scale });
76
+ const canvas = document.createElement("canvas");
77
+ canvas.width = viewport.width;
78
+ canvas.height = viewport.height;
79
+ await page.render({ canvas, viewport }).promise;
80
+ return canvas.toDataURL("image/png");
81
+ }
82
+ function FileThumbnail2({ file, size, className }) {
83
+ const shape = useShape();
84
+ const isImage = file.type.startsWith("image/");
85
+ const isPdf = file.type === "application/pdf";
86
+ const [imageUrl, setImageUrl] = useState(null);
87
+ useEffect(() => {
88
+ if (!isImage) {
89
+ setImageUrl(null);
90
+ return;
91
+ }
92
+ const url = URL.createObjectURL(file);
93
+ setImageUrl(url);
94
+ return () => URL.revokeObjectURL(url);
95
+ }, [isImage, file]);
96
+ const [pdfUrl, setPdfUrl] = useState(null);
97
+ const [pdfError, setPdfError] = useState(false);
98
+ useEffect(() => {
99
+ setPdfError(false);
100
+ if (!isPdf) {
101
+ setPdfUrl(null);
102
+ return;
103
+ }
104
+ let cancelled = false;
105
+ renderPdfFirstPage(file, size).then((url) => {
106
+ if (!cancelled)
107
+ setPdfUrl(url);
108
+ }).catch(() => {
109
+ if (!cancelled)
110
+ setPdfError(true);
111
+ });
112
+ return () => {
113
+ cancelled = true;
114
+ };
115
+ }, [file, isPdf, size]);
116
+ const previewUrl = imageUrl ?? pdfUrl;
117
+ const isPending = isImage && !imageUrl || isPdf && !pdfUrl && !pdfError;
118
+ return /* @__PURE__ */ jsx("div", {
119
+ className: cn2("relative shrink-0 overflow-hidden bg-accent border border-border", shape.bg, className),
120
+ style: { width: size, height: size },
121
+ children: previewUrl ? /* @__PURE__ */ jsx("img", {
122
+ src: previewUrl,
123
+ alt: file.name,
124
+ className: "absolute inset-0 w-full h-full object-cover"
125
+ }) : isPending ? /* @__PURE__ */ jsx("div", {
126
+ className: "absolute inset-0 flex items-center justify-center",
127
+ children: /* @__PURE__ */ jsx("div", {
128
+ className: "w-6 h-6 rounded-full border-2 border-border border-t-muted-foreground animate-spin",
129
+ "aria-label": "Loading preview",
130
+ role: "status"
131
+ })
132
+ }) : /* @__PURE__ */ jsx("div", {
133
+ className: "absolute inset-0 flex items-center justify-center text-muted-foreground",
134
+ role: "img",
135
+ "aria-label": file.name,
136
+ children: /* @__PURE__ */ jsxs("svg", {
137
+ width: Math.max(16, size * 0.35),
138
+ height: Math.max(16, size * 0.35),
139
+ viewBox: "0 0 24 24",
140
+ fill: "none",
141
+ stroke: "currentColor",
142
+ strokeWidth: "1.5",
143
+ strokeLinecap: "round",
144
+ strokeLinejoin: "round",
145
+ "aria-hidden": "true",
146
+ children: [
147
+ /* @__PURE__ */ jsx("path", {
148
+ d: "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"
149
+ }),
150
+ /* @__PURE__ */ jsx("path", {
151
+ d: "M14 3v5h5"
152
+ })
153
+ ]
154
+ })
155
+ })
156
+ });
157
+ }
158
+
159
+ // components/inputs/input-message/index.tsx
160
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
161
+ var useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect2;
162
+ function useRegionHeight() {
163
+ const roRef = useRef(null);
164
+ const [height, setHeight] = useState2(null);
165
+ const ref = useCallback((el) => {
166
+ roRef.current?.disconnect();
167
+ roRef.current = null;
168
+ if (!el)
169
+ return;
170
+ const sync = () => {
171
+ const next = el.parentElement?.scrollHeight ?? el.offsetHeight;
172
+ if (next > 0)
173
+ setHeight(next);
174
+ };
175
+ sync();
176
+ const ro = new ResizeObserver(sync);
177
+ ro.observe(el);
178
+ roRef.current = ro;
179
+ }, []);
180
+ return [ref, height];
181
+ }
182
+ function useIsTouch() {
183
+ const [isTouch, setIsTouch] = useState2(false);
184
+ useEffect2(() => {
185
+ const mq = window.matchMedia("(hover: none)");
186
+ const update = () => setIsTouch(mq.matches);
187
+ update();
188
+ mq.addEventListener("change", update);
189
+ return () => mq.removeEventListener("change", update);
190
+ }, []);
191
+ return isTouch;
192
+ }
193
+ var DEFAULT_ACCEPT = "image/png,image/jpeg,application/pdf";
194
+ var NO_FILES = [];
195
+ var NO_QUEUE = [];
196
+ var FilePreviewTile = forwardRef(function FilePreviewTile({ file, onRemove, size }, ref) {
197
+ const XIcon = useIcon("x");
198
+ return /* @__PURE__ */ jsxs2(motion.div, {
199
+ ref,
200
+ layout: true,
201
+ initial: { opacity: 0, scale: 0.9 },
202
+ animate: { opacity: 1, scale: 1 },
203
+ exit: { opacity: 0, scale: 0.9, transition: spring2.fast.exit },
204
+ transition: spring2.fast,
205
+ className: "relative shrink-0 cursor-default group/tile",
206
+ children: [
207
+ /* @__PURE__ */ jsx2(FileThumbnail2, {
208
+ file,
209
+ size
210
+ }),
211
+ /* @__PURE__ */ jsx2(Tooltip2, {
212
+ content: "Remove",
213
+ side: "top",
214
+ children: /* @__PURE__ */ jsx2("button", {
215
+ type: "button",
216
+ onClick: (e) => {
217
+ e.stopPropagation();
218
+ onRemove();
219
+ },
220
+ "aria-label": `Remove ${file.name}`,
221
+ className: "light absolute top-1 right-1 w-5 h-5 rounded-full bg-foreground text-background opacity-0 group-hover/tile:opacity-100 transition-opacity duration-fast flex items-center justify-center cursor-pointer outline-none focus-visible:opacity-100 focus-visible:ring-1 focus-visible:ring-focus-ring",
222
+ children: /* @__PURE__ */ jsx2(XIcon, {
223
+ size: 12,
224
+ strokeWidth: 2.5
225
+ })
226
+ })
227
+ })
228
+ ]
229
+ });
230
+ });
231
+ function QueuedRow({ item, index, total, reduceMotion, isTouch, onEdit, onRemove, onMove }) {
232
+ const XIcon = useIcon("x");
233
+ const ImageIcon = useIcon("image");
234
+ const compactStep = useSize2().variant === "compact";
235
+ const fileCount = item.files.length;
236
+ const label = item.text || `${fileCount} attachment${fileCount === 1 ? "" : "s"}`;
237
+ return /* @__PURE__ */ jsxs2(Reorder.Item, {
238
+ value: item,
239
+ layout: true,
240
+ initial: reduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.97 },
241
+ animate: { opacity: 1, scale: 1 },
242
+ exit: reduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.97, transition: spring2.fast.exit },
243
+ transition: spring2.fast,
244
+ "aria-label": `Queued message ${index + 1} of ${total}: ${label}`,
245
+ tabIndex: 0,
246
+ onDoubleClick: () => onEdit(item),
247
+ onKeyDown: (e) => {
248
+ if (e.key === "Enter" || e.key === "F2") {
249
+ e.preventDefault();
250
+ onEdit(item);
251
+ } else if (e.key === "Delete" || e.key === "Backspace") {
252
+ e.preventDefault();
253
+ onRemove(item);
254
+ } else if (e.altKey && (e.key === "ArrowUp" || e.key === "ArrowDown")) {
255
+ e.preventDefault();
256
+ onMove(item, e.key === "ArrowUp" ? -1 : 1);
257
+ }
258
+ },
259
+ className: cn2("group/qrow flex items-center gap-2 rounded-lg bg-muted", compactStep ? "h-7 px-2 text-body-compact" : "h-8 px-2.5 text-body", "text-foreground/85 select-none outline-none", "cursor-grab active:cursor-grabbing", "focus-visible:ring-1 focus-visible:ring-focus-ring", "weight-normal"),
260
+ children: [
261
+ fileCount > 0 && /* @__PURE__ */ jsxs2("span", {
262
+ className: "flex shrink-0 items-center gap-0.5 text-muted-foreground",
263
+ children: [
264
+ /* @__PURE__ */ jsx2(ImageIcon, {
265
+ size: 13
266
+ }),
267
+ item.text && /* @__PURE__ */ jsx2("span", {
268
+ className: "tabular-nums",
269
+ children: fileCount
270
+ })
271
+ ]
272
+ }),
273
+ /* @__PURE__ */ jsx2("span", {
274
+ className: "min-w-0 flex-1 truncate [text-box:trim-both_cap_alphabetic] py-1 -my-1",
275
+ children: label
276
+ }),
277
+ /* @__PURE__ */ jsx2(Tooltip2, {
278
+ content: "Remove",
279
+ side: "top",
280
+ children: /* @__PURE__ */ jsx2("button", {
281
+ type: "button",
282
+ onPointerDown: (e) => e.stopPropagation(),
283
+ onClick: (e) => {
284
+ e.stopPropagation();
285
+ onRemove(item);
286
+ },
287
+ "aria-label": `Remove queued message: ${label}`,
288
+ className: cn2("shrink-0 flex h-5 w-5 items-center justify-center rounded-full", "text-muted-foreground hover:text-foreground hover:bg-hover", isTouch ? "opacity-100" : "opacity-0 group-hover/qrow:opacity-100 focus-visible:opacity-100", "transition-opacity duration-fast cursor-pointer outline-none", "focus-visible:ring-1 focus-visible:ring-focus-ring"),
289
+ children: /* @__PURE__ */ jsx2(XIcon, {
290
+ size: 13,
291
+ strokeWidth: 2.5
292
+ })
293
+ })
294
+ })
295
+ ]
296
+ });
297
+ }
298
+ function SuggestionRow({ text, index, active, keyHint, optionId, registerItem, onSelect }) {
299
+ const EnterIcon = useIcon("corner-down-left");
300
+ const ArrowDownIcon = useIcon("arrow-down");
301
+ const compactStep = useSize2().variant === "compact";
302
+ const ref = useRef(null);
303
+ useRegisterFluidHoverItem2(registerItem, index, ref);
304
+ return /* @__PURE__ */ jsxs2("div", {
305
+ ref,
306
+ id: optionId,
307
+ role: "option",
308
+ "aria-selected": active,
309
+ onClick: onSelect,
310
+ className: cn2("relative flex cursor-pointer items-center gap-2", compactStep ? "h-7 px-2 text-subtitle-compact" : "h-8 px-2.5 text-subtitle", "text-muted-foreground transition-colors duration-fast", active && "text-foreground", "weight-normal"),
311
+ children: [
312
+ /* @__PURE__ */ jsx2("span", {
313
+ className: "min-w-0 flex-1 truncate [text-box:trim-both_cap_alphabetic] py-1 -my-1",
314
+ children: text
315
+ }),
316
+ !active && keyHint ? /* @__PURE__ */ jsx2(ArrowDownIcon, {
317
+ size: 13,
318
+ className: "shrink-0 text-muted-foreground/70 transition-opacity duration-fast"
319
+ }) : /* @__PURE__ */ jsx2(EnterIcon, {
320
+ size: 13,
321
+ className: cn2("shrink-0 transition-opacity duration-fast", active ? "opacity-100" : "opacity-0")
322
+ })
323
+ ]
324
+ });
325
+ }
326
+ var InputMessage2 = forwardRef(({
327
+ size,
328
+ value: valueProp,
329
+ defaultValue,
330
+ onValueChange,
331
+ onSend,
332
+ placeholder = "Ask me anything…",
333
+ leftSlot,
334
+ rightSlot,
335
+ disabled,
336
+ minRows = 1,
337
+ maxRows = 8,
338
+ clickToFocus = true,
339
+ sendLabel = "Send",
340
+ files: filesProp,
341
+ defaultFiles,
342
+ onFilesChange,
343
+ accept = DEFAULT_ACCEPT,
344
+ maxFiles,
345
+ filePreviewSize = 80,
346
+ textareaProps,
347
+ status: statusProp,
348
+ defaultStatus,
349
+ onStatusChange,
350
+ onStop,
351
+ queue: queueProp,
352
+ defaultQueue,
353
+ onQueueChange,
354
+ showQueue = true,
355
+ history = [],
356
+ placeholderSuggestion,
357
+ suggestions,
358
+ className,
359
+ style,
360
+ ...props
361
+ }, ref) => {
362
+ const shape = useShape();
363
+ const compactStep = useSize2(size).variant === "compact";
364
+ const ArrowUpIcon = useIcon("arrow-up");
365
+ const reduceMotion = useReducedMotion() ?? false;
366
+ const isTouch = useIsTouch();
367
+ const textareaRef = useRef(null);
368
+ const fileInputRef = useRef(null);
369
+ const [focusVisible, setFocusVisible] = useState2(false);
370
+ const [dragOver, setDragOver] = useState2(false);
371
+ const [hovered, setHovered] = useState2(false);
372
+ const {
373
+ onFocus: _textareaOnFocus,
374
+ onBlur: _textareaOnBlur,
375
+ "aria-describedby": textareaDescribedBy,
376
+ ...restTextareaProps
377
+ } = textareaProps ?? {};
378
+ const valueControlled = valueProp !== undefined;
379
+ const [value, setValue] = useControllableState(valueProp, defaultValue ?? "", onValueChange);
380
+ const filesControlled = filesProp !== undefined;
381
+ const supportsFiles = onFilesChange !== undefined || defaultFiles !== undefined;
382
+ const [filesArr, setFiles] = useControllableState(filesProp, defaultFiles ?? NO_FILES, onFilesChange);
383
+ const statusEnabled = statusProp !== undefined || defaultStatus !== undefined;
384
+ const [statusValue, setStatus] = useControllableState(statusProp, defaultStatus ?? "idle", onStatusChange);
385
+ const status = statusEnabled ? statusValue : undefined;
386
+ const streaming = status === "streaming";
387
+ const [queueArr, setQueue] = useControllableState(queueProp, defaultQueue ?? NO_QUEUE, onQueueChange);
388
+ const queueRef = useRef(queueArr);
389
+ queueRef.current = queueArr;
390
+ const supportsQueue = statusEnabled && (onQueueChange !== undefined || defaultQueue !== undefined);
391
+ const [liveMsg, setLiveMsg] = useState2("");
392
+ const [historyIndex, setHistoryIndex] = useState2(null);
393
+ const draftBeforeHistory = useRef("");
394
+ const suggestionsArr = useMemo(() => suggestions ?? [], [suggestions]);
395
+ const suggestionsOpen = suggestionsArr.length > 0 && value === "";
396
+ const suggestionListRef = useRef(null);
397
+ const [filesRegionRef, filesRegionHeight] = useRegionHeight();
398
+ const [queueRegionRef, queueRegionHeight] = useRegionHeight();
399
+ const [suggestionsRegionRef, suggestionsRegionHeight] = useRegionHeight();
400
+ const suggestionHover = useFluidHover2(suggestionListRef);
401
+ const {
402
+ activeIndex: activeSuggestion,
403
+ setActiveIndex: setActiveSuggestion,
404
+ handlers: suggestionHandlers,
405
+ registerItem: registerSuggestion,
406
+ remeasure
407
+ } = suggestionHover;
408
+ useEffect2(() => {
409
+ if (suggestionsOpen)
410
+ remeasure();
411
+ }, [suggestionsOpen, remeasure]);
412
+ const suggestionListId = useId();
413
+ const ghostHintId = useId();
414
+ const showGhost = !!placeholderSuggestion && value === "" && !(dragOver && supportsFiles);
415
+ useEffect2(() => {
416
+ if (!suggestionsOpen)
417
+ setActiveSuggestion(null);
418
+ }, [suggestionsOpen, setActiveSuggestion]);
419
+ const acceptSuggestion = useCallback((text) => {
420
+ setActiveSuggestion(null);
421
+ setHistoryIndex(null);
422
+ setValue(text);
423
+ requestAnimationFrame(() => {
424
+ const el = textareaRef.current;
425
+ if (!el)
426
+ return;
427
+ el.focus();
428
+ el.setSelectionRange(el.value.length, el.value.length);
429
+ });
430
+ }, [setValue, setActiveSuggestion]);
431
+ const lineHeightCache = useRef(null);
432
+ const resizeTextarea = useCallback(() => {
433
+ const el = textareaRef.current;
434
+ if (!el)
435
+ return;
436
+ el.style.height = "auto";
437
+ let cache = lineHeightCache.current;
438
+ if (!cache || cache.el !== el) {
439
+ const lineHeight = parseFloat(getComputedStyle(el).lineHeight);
440
+ cache = { el, value: Number.isNaN(lineHeight) ? 20 : lineHeight };
441
+ lineHeightCache.current = cache;
442
+ }
443
+ const min = cache.value * minRows;
444
+ const max = cache.value * maxRows;
445
+ const next = Math.min(Math.max(el.scrollHeight, min), max);
446
+ el.style.height = `${next}px`;
447
+ el.style.overflowY = el.scrollHeight > max ? "auto" : "hidden";
448
+ }, [minRows, maxRows]);
449
+ useIsoLayoutEffect(() => {
450
+ resizeTextarea();
451
+ }, [value, resizeTextarea]);
452
+ useEffect2(() => {
453
+ const el = textareaRef.current;
454
+ if (!el || typeof ResizeObserver === "undefined")
455
+ return;
456
+ let lastWidth = el.offsetWidth;
457
+ const ro = new ResizeObserver(() => {
458
+ const width = el.offsetWidth;
459
+ if (width === lastWidth)
460
+ return;
461
+ lastWidth = width;
462
+ resizeTextarea();
463
+ });
464
+ ro.observe(el);
465
+ return () => ro.disconnect();
466
+ }, [resizeTextarea]);
467
+ const trimmed = value.trim();
468
+ const canSend = !disabled && (trimmed.length > 0 || filesArr.length > 0);
469
+ const EDGE_DROP = "0 1px 1px -0.5px var(--shadow-color)";
470
+ const edgeShadow = dragOver ? `0 0 0 1px var(--brand), ${EDGE_DROP}` : focusVisible ? `0 0 0 1px color-mix(in oklab, var(--foreground) 20%, transparent), ${EDGE_DROP}` : hovered && clickToFocus && !disabled ? `0 0 0 1px var(--border), ${EDGE_DROP}` : undefined;
471
+ const handleSend = useCallback(() => {
472
+ if (!canSend)
473
+ return;
474
+ setHistoryIndex(null);
475
+ if (streaming && supportsQueue) {
476
+ const item = {
477
+ id: crypto.randomUUID(),
478
+ text: trimmed,
479
+ files: filesArr
480
+ };
481
+ setQueue([...queueRef.current, item]);
482
+ setValue("");
483
+ if (supportsFiles)
484
+ setFiles([]);
485
+ requestAnimationFrame(() => textareaRef.current?.focus());
486
+ return;
487
+ }
488
+ onSend?.(trimmed, filesArr);
489
+ if (!valueControlled)
490
+ setValue("");
491
+ if (supportsFiles && !filesControlled)
492
+ setFiles([]);
493
+ }, [
494
+ canSend,
495
+ streaming,
496
+ supportsQueue,
497
+ onSend,
498
+ trimmed,
499
+ filesArr,
500
+ setQueue,
501
+ setValue,
502
+ supportsFiles,
503
+ setFiles,
504
+ valueControlled,
505
+ filesControlled
506
+ ]);
507
+ const statusControlled = statusProp !== undefined;
508
+ const handleStop = useCallback(() => {
509
+ onStop?.();
510
+ if (!statusControlled)
511
+ setStatus("idle");
512
+ }, [onStop, statusControlled, setStatus]);
513
+ const prevStatusRef = useRef(status);
514
+ useEffect2(() => {
515
+ const prev = prevStatusRef.current;
516
+ prevStatusRef.current = status;
517
+ if (!supportsQueue)
518
+ return;
519
+ if (prev === "streaming" && status === "idle" && queueArr.length > 0) {
520
+ const [next, ...rest] = queueArr;
521
+ if (!next)
522
+ return;
523
+ setQueue(rest);
524
+ onSend?.(next.text, next.files, { queuedId: next.id });
525
+ setLiveMsg(`Message sent.${rest.length ? ` ${rest.length} still queued.` : ""}`);
526
+ }
527
+ }, [status, supportsQueue, queueArr, setQueue, onSend]);
528
+ const editQueued = useCallback((item) => {
529
+ if (!supportsQueue)
530
+ return;
531
+ setHistoryIndex(null);
532
+ setValue(item.text);
533
+ if (supportsFiles) {
534
+ setFiles(maxFiles != null ? item.files.slice(0, maxFiles) : item.files);
535
+ }
536
+ setQueue(queueRef.current.filter((q) => q.id !== item.id));
537
+ requestAnimationFrame(() => {
538
+ const el = textareaRef.current;
539
+ if (!el)
540
+ return;
541
+ el.focus();
542
+ el.setSelectionRange(el.value.length, el.value.length);
543
+ });
544
+ }, [supportsQueue, supportsFiles, setValue, setFiles, maxFiles, setQueue]);
545
+ const removeQueued = useCallback((item) => setQueue(queueRef.current.filter((q) => q.id !== item.id)), [setQueue]);
546
+ const moveQueued = useCallback((item, dir) => {
547
+ const cur = queueRef.current;
548
+ const i = cur.findIndex((q) => q.id === item.id);
549
+ const j = i + dir;
550
+ if (i < 0 || j < 0 || j >= cur.length)
551
+ return;
552
+ const next = [...cur];
553
+ const a = next[i];
554
+ const b = next[j];
555
+ if (!a || !b)
556
+ return;
557
+ next[i] = b;
558
+ next[j] = a;
559
+ setQueue(next);
560
+ }, [setQueue]);
561
+ const canStop = onStop !== undefined || !statusControlled;
562
+ const buttonMode = !streaming ? "send" : canSend && supportsQueue ? "queue" : canStop ? "stop" : "send";
563
+ const buttonLabel = buttonMode === "stop" ? "Stop" : buttonMode === "queue" ? "Queue message" : sendLabel;
564
+ const setCaretEnd = useCallback(() => {
565
+ requestAnimationFrame(() => {
566
+ const el = textareaRef.current;
567
+ if (el)
568
+ el.setSelectionRange(el.value.length, el.value.length);
569
+ });
570
+ }, []);
571
+ const handleKeyDown = useCallback((e) => {
572
+ if (e.nativeEvent.isComposing)
573
+ return;
574
+ if (suggestionsOpen && !e.shiftKey && !e.altKey && !e.metaKey && !e.ctrlKey) {
575
+ if (e.key === "ArrowDown") {
576
+ e.preventDefault();
577
+ setActiveSuggestion((prev) => prev == null ? 0 : Math.min(prev + 1, suggestionsArr.length - 1));
578
+ return;
579
+ }
580
+ if (activeSuggestion != null) {
581
+ if (e.key === "ArrowUp") {
582
+ e.preventDefault();
583
+ setActiveSuggestion(activeSuggestion === 0 ? null : activeSuggestion - 1);
584
+ return;
585
+ }
586
+ if (e.key === "Enter") {
587
+ e.preventDefault();
588
+ const picked = suggestionsArr[activeSuggestion];
589
+ if (picked !== undefined)
590
+ acceptSuggestion(picked);
591
+ return;
592
+ }
593
+ if (e.key === "Escape") {
594
+ e.preventDefault();
595
+ setActiveSuggestion(null);
596
+ return;
597
+ }
598
+ }
599
+ }
600
+ if (e.key === "Tab" && !e.shiftKey && placeholderSuggestion && value === "") {
601
+ e.preventDefault();
602
+ acceptSuggestion(placeholderSuggestion);
603
+ return;
604
+ }
605
+ if (history.length > 0 && (e.key === "ArrowUp" || e.key === "ArrowDown") && !e.shiftKey && !e.altKey && !e.metaKey && !e.ctrlKey) {
606
+ const el = e.currentTarget;
607
+ const caret = el.selectionStart ?? 0;
608
+ const end = el.selectionEnd ?? caret;
609
+ if (e.key === "ArrowUp" && !value.slice(0, caret).includes(`
610
+ `)) {
611
+ const start = historyIndex == null ? history.length : historyIndex;
612
+ if (start > 0) {
613
+ e.preventDefault();
614
+ if (historyIndex == null)
615
+ draftBeforeHistory.current = value;
616
+ const ni = start - 1;
617
+ setHistoryIndex(ni);
618
+ setValue(history[ni] ?? "");
619
+ setCaretEnd();
620
+ }
621
+ return;
622
+ }
623
+ if (e.key === "ArrowDown" && historyIndex != null && !value.slice(end).includes(`
624
+ `)) {
625
+ e.preventDefault();
626
+ const ni = historyIndex + 1;
627
+ if (ni >= history.length) {
628
+ setHistoryIndex(null);
629
+ setValue(draftBeforeHistory.current);
630
+ } else {
631
+ setHistoryIndex(ni);
632
+ setValue(history[ni] ?? "");
633
+ }
634
+ setCaretEnd();
635
+ return;
636
+ }
637
+ }
638
+ if (e.key === "Enter" && !e.shiftKey) {
639
+ e.preventDefault();
640
+ handleSend();
641
+ }
642
+ }, [
643
+ history,
644
+ value,
645
+ historyIndex,
646
+ setValue,
647
+ setCaretEnd,
648
+ handleSend,
649
+ suggestionsOpen,
650
+ suggestionsArr,
651
+ activeSuggestion,
652
+ setActiveSuggestion,
653
+ acceptSuggestion,
654
+ placeholderSuggestion
655
+ ]);
656
+ const handleContainerMouseDown = useCallback((e) => {
657
+ if (!clickToFocus || disabled)
658
+ return;
659
+ const target = e.target;
660
+ if (target === textareaRef.current)
661
+ return;
662
+ if (target.closest('button, a, input, select, textarea, [contenteditable], [role="button"], [data-im-queue]')) {
663
+ return;
664
+ }
665
+ e.preventDefault();
666
+ textareaRef.current?.focus();
667
+ }, [clickToFocus, disabled]);
668
+ const acceptTokens = useMemo(() => accept.split(",").map((s) => s.trim()).filter(Boolean), [accept]);
669
+ const matchesAccept = useCallback((file) => acceptTokens.some((token) => {
670
+ if (token.endsWith("/*"))
671
+ return file.type.startsWith(token.slice(0, -1));
672
+ if (token.startsWith("."))
673
+ return file.name.toLowerCase().endsWith(token.toLowerCase());
674
+ return file.type === token;
675
+ }), [acceptTokens]);
676
+ const addFiles = useCallback((incoming) => {
677
+ if (!supportsFiles)
678
+ return;
679
+ const fingerprint = (f) => `${f.name}-${f.size}-${f.lastModified}`;
680
+ const existing = new Set(filesArr.map(fingerprint));
681
+ const accepted = [];
682
+ for (const f of incoming) {
683
+ if (!matchesAccept(f))
684
+ continue;
685
+ const fp = fingerprint(f);
686
+ if (existing.has(fp))
687
+ continue;
688
+ existing.add(fp);
689
+ accepted.push(f);
690
+ }
691
+ if (!accepted.length)
692
+ return;
693
+ const next = [...filesArr, ...accepted];
694
+ setFiles(maxFiles != null ? next.slice(0, maxFiles) : next);
695
+ }, [supportsFiles, setFiles, filesArr, matchesAccept, maxFiles]);
696
+ const removeFile = useCallback((idx) => {
697
+ if (!supportsFiles)
698
+ return;
699
+ setFiles(filesArr.filter((_, i) => i !== idx));
700
+ }, [supportsFiles, setFiles, filesArr]);
701
+ const openFilePicker = useCallback((overrideAccept) => {
702
+ const el = fileInputRef.current;
703
+ if (!el)
704
+ return;
705
+ if (overrideAccept) {
706
+ el.accept = overrideAccept;
707
+ el.click();
708
+ queueMicrotask(() => {
709
+ if (fileInputRef.current)
710
+ fileInputRef.current.accept = accept;
711
+ });
712
+ return;
713
+ }
714
+ el.click();
715
+ }, [accept]);
716
+ const slotCtx = useMemo(() => ({ openFilePicker, files: filesArr }), [openFilePicker, filesArr]);
717
+ const leftContent = typeof leftSlot === "function" ? leftSlot(slotCtx) : leftSlot;
718
+ const rightContent = typeof rightSlot === "function" ? rightSlot(slotCtx) : rightSlot;
719
+ const handleDragOver = useCallback((e) => {
720
+ if (!supportsFiles || disabled)
721
+ return;
722
+ if (!Array.from(e.dataTransfer.types).includes("Files"))
723
+ return;
724
+ e.preventDefault();
725
+ e.dataTransfer.dropEffect = "copy";
726
+ setDragOver(true);
727
+ }, [supportsFiles, disabled]);
728
+ const handleDragLeave = useCallback((e) => {
729
+ const wrapper = e.currentTarget;
730
+ const next = e.relatedTarget;
731
+ if (next && wrapper.contains(next))
732
+ return;
733
+ setDragOver(false);
734
+ }, []);
735
+ const handleDrop = useCallback((e) => {
736
+ e.preventDefault();
737
+ setDragOver(false);
738
+ if (!supportsFiles || disabled)
739
+ return;
740
+ addFiles(Array.from(e.dataTransfer.files));
741
+ }, [supportsFiles, disabled, addFiles]);
742
+ const handleFileInputChange = useCallback((e) => {
743
+ if (!e.target.files)
744
+ return;
745
+ addFiles(Array.from(e.target.files));
746
+ e.target.value = "";
747
+ }, [addFiles]);
748
+ const composer = /* @__PURE__ */ jsx2("div", {
749
+ ref,
750
+ onMouseDown: handleContainerMouseDown,
751
+ onDragOver: handleDragOver,
752
+ onDragLeave: handleDragLeave,
753
+ onDrop: handleDrop,
754
+ className: cn2("flex flex-col gap-1 p-2 transition-[box-shadow,color] duration-fast", surfaceClasses2(2, 2), shape.container, clickToFocus && !disabled && "cursor-text", disabled && "opacity-50 pointer-events-none", className),
755
+ style: edgeShadow ? { boxShadow: edgeShadow, ...style } : style,
756
+ onMouseEnter: () => setHovered(true),
757
+ onMouseLeave: () => setHovered(false),
758
+ ...props,
759
+ children: /* @__PURE__ */ jsxs2(SurfaceProvider2, {
760
+ value: 2,
761
+ children: [
762
+ supportsFiles && /* @__PURE__ */ jsx2("input", {
763
+ ref: fileInputRef,
764
+ type: "file",
765
+ accept,
766
+ multiple: maxFiles == null || maxFiles > 1,
767
+ className: "hidden",
768
+ onChange: handleFileInputChange,
769
+ "aria-hidden": "true",
770
+ tabIndex: -1
771
+ }),
772
+ /* @__PURE__ */ jsx2(AnimatePresence, {
773
+ initial: false,
774
+ children: filesArr.length > 0 && /* @__PURE__ */ jsx2(motion.div, {
775
+ initial: { height: 0, opacity: 0 },
776
+ animate: { height: filesRegionHeight ?? 0, opacity: 1 },
777
+ exit: { height: 0, opacity: 0 },
778
+ transition: { ...spring2.moderate, bounce: 0 },
779
+ className: "overflow-hidden",
780
+ children: /* @__PURE__ */ jsx2("div", {
781
+ ref: filesRegionRef,
782
+ className: "flex flex-wrap gap-2 pb-1",
783
+ children: /* @__PURE__ */ jsx2(AnimatePresence, {
784
+ initial: false,
785
+ mode: "popLayout",
786
+ children: filesArr.map((file, i) => /* @__PURE__ */ jsx2(FilePreviewTile, {
787
+ file,
788
+ onRemove: () => removeFile(i),
789
+ size: filePreviewSize
790
+ }, `${file.name}-${file.size}-${file.lastModified}`))
791
+ })
792
+ })
793
+ }, "preview-row")
794
+ }),
795
+ supportsQueue && showQueue && /* @__PURE__ */ jsx2(AnimatePresence, {
796
+ initial: false,
797
+ children: queueArr.length > 0 && /* @__PURE__ */ jsx2(motion.div, {
798
+ initial: { height: 0, opacity: 0 },
799
+ animate: { height: queueRegionHeight ?? 0, opacity: 1 },
800
+ exit: { height: 0, opacity: 0 },
801
+ transition: { ...spring2.moderate, bounce: 0 },
802
+ className: "overflow-hidden",
803
+ children: /* @__PURE__ */ jsx2(Reorder.Group, {
804
+ ref: queueRegionRef,
805
+ axis: "y",
806
+ values: queueArr,
807
+ onReorder: (next) => setQueue(next),
808
+ "data-im-queue": true,
809
+ className: "flex flex-col gap-1 pb-1",
810
+ children: /* @__PURE__ */ jsx2(AnimatePresence, {
811
+ initial: false,
812
+ children: queueArr.map((item, i) => /* @__PURE__ */ jsx2(QueuedRow, {
813
+ item,
814
+ index: i,
815
+ total: queueArr.length,
816
+ reduceMotion,
817
+ isTouch,
818
+ onEdit: editQueued,
819
+ onRemove: removeQueued,
820
+ onMove: moveQueued
821
+ }, item.id))
822
+ })
823
+ })
824
+ }, "queue-row")
825
+ }),
826
+ /* @__PURE__ */ jsxs2("div", {
827
+ className: "relative",
828
+ children: [
829
+ /* @__PURE__ */ jsx2("textarea", {
830
+ ref: textareaRef,
831
+ value,
832
+ onChange: (e) => {
833
+ setHistoryIndex(null);
834
+ setActiveSuggestion(null);
835
+ setValue(e.target.value);
836
+ },
837
+ onKeyDown: handleKeyDown,
838
+ onFocus: (e) => {
839
+ if (e.target.matches(":focus-visible"))
840
+ setFocusVisible(true);
841
+ textareaProps?.onFocus?.(e);
842
+ },
843
+ onBlur: (e) => {
844
+ setFocusVisible(false);
845
+ setActiveSuggestion(null);
846
+ textareaProps?.onBlur?.(e);
847
+ },
848
+ placeholder: dragOver && supportsFiles ? "Drop files here to add to chat" : placeholderSuggestion ? undefined : placeholder,
849
+ disabled,
850
+ rows: minRows,
851
+ "aria-label": textareaProps?.["aria-label"] ?? "Message",
852
+ "aria-describedby": [showGhost ? ghostHintId : null, textareaDescribedBy].filter(Boolean).join(" ") || undefined,
853
+ "aria-activedescendant": activeSuggestion != null ? `${suggestionListId}-${activeSuggestion}` : undefined,
854
+ className: cn2("w-full resize-none rounded-none bg-transparent outline-none", "text-foreground placeholder:text-muted-foreground", compactStep ? "text-subtitle-compact leading-4.5 px-1.5 py-1.5" : "text-subtitle leading-5 px-2 py-2", "weight-normal"),
855
+ ...restTextareaProps
856
+ }),
857
+ showGhost && /* @__PURE__ */ jsx2("div", {
858
+ "aria-hidden": "true",
859
+ className: cn2("pointer-events-none absolute inset-0 overflow-hidden text-muted-foreground", compactStep ? "text-subtitle-compact leading-4.5 px-1.5 py-1.5" : "text-subtitle leading-5 px-2 py-2", "weight-normal"),
860
+ children: /* @__PURE__ */ jsxs2("span", {
861
+ className: "flex max-w-full items-center gap-1.5",
862
+ children: [
863
+ /* @__PURE__ */ jsx2("span", {
864
+ className: "min-w-0 truncate",
865
+ children: placeholderSuggestion
866
+ }),
867
+ /* @__PURE__ */ jsx2("kbd", {
868
+ className: cn2("inline-flex shrink-0 -translate-y-px items-center rounded-box border border-border bg-background px-1 font-sans text-muted-foreground", compactStep ? "h-4 text-micro-compact" : "h-[18px] text-micro"),
869
+ children: "Tab"
870
+ })
871
+ ]
872
+ })
873
+ }),
874
+ showGhost && /* @__PURE__ */ jsxs2("span", {
875
+ id: ghostHintId,
876
+ className: "sr-only",
877
+ children: [
878
+ "Suggested prompt: ",
879
+ placeholderSuggestion,
880
+ ". Press Tab to fill the composer with it."
881
+ ]
882
+ })
883
+ ]
884
+ }),
885
+ /* @__PURE__ */ jsxs2("div", {
886
+ className: cn2("flex items-center justify-between", compactStep ? "gap-1.5 [&_button]:h-6 [&_button.w-7]:w-6 [&_button]:text-caption-compact" : "gap-2"),
887
+ children: [
888
+ /* @__PURE__ */ jsx2("div", {
889
+ className: "flex items-center gap-1.5 min-w-0",
890
+ children: leftContent
891
+ }),
892
+ /* @__PURE__ */ jsxs2("div", {
893
+ className: "flex items-center gap-1.5 shrink-0",
894
+ children: [
895
+ rightContent,
896
+ /* @__PURE__ */ jsx2(Button2, {
897
+ type: "button",
898
+ variant: "primary",
899
+ size: "icon-sm",
900
+ onClick: buttonMode === "stop" ? handleStop : handleSend,
901
+ disabled: buttonMode === "stop" ? disabled : !canSend,
902
+ "aria-label": buttonLabel,
903
+ children: /* @__PURE__ */ jsx2(AnimatePresence, {
904
+ mode: "wait",
905
+ initial: false,
906
+ children: /* @__PURE__ */ jsx2(motion.span, {
907
+ initial: reduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.6 },
908
+ animate: { opacity: 1, scale: 1 },
909
+ exit: reduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.6, transition: spring2.fast.exit },
910
+ transition: spring2.fast,
911
+ className: "flex items-center justify-center leading-none",
912
+ children: buttonMode === "stop" ? /* @__PURE__ */ jsx2("span", {
913
+ className: "h-3 w-3 rounded-glyph bg-current"
914
+ }) : /* @__PURE__ */ jsx2(ArrowUpIcon, {
915
+ size: compactStep ? 15 : 19,
916
+ className: cn2("block", compactStep ? "!h-[15px] !w-[15px]" : "!h-[19px] !w-[19px]")
917
+ })
918
+ }, buttonMode === "stop" ? "stop" : "arrow")
919
+ })
920
+ })
921
+ ]
922
+ })
923
+ ]
924
+ }),
925
+ suggestionsArr.length > 0 && /* @__PURE__ */ jsx2(AnimatePresence, {
926
+ initial: false,
927
+ children: suggestionsOpen && /* @__PURE__ */ jsx2(motion.div, {
928
+ initial: { height: 0, opacity: 0 },
929
+ animate: { height: suggestionsRegionHeight ?? 0, opacity: 1 },
930
+ exit: { height: 0 },
931
+ transition: { ...spring2.moderate, bounce: 0 },
932
+ className: "-mx-2 -mt-1 overflow-hidden",
933
+ children: /* @__PURE__ */ jsxs2("div", {
934
+ ref: (el) => {
935
+ suggestionListRef.current = el;
936
+ suggestionsRegionRef(el);
937
+ },
938
+ role: "listbox",
939
+ id: suggestionListId,
940
+ "aria-label": "Suggested prompts",
941
+ onMouseEnter: suggestionHandlers.onMouseEnter,
942
+ onMouseMove: suggestionHandlers.onMouseMove,
943
+ onMouseLeave: suggestionHandlers.onMouseLeave,
944
+ onClick: suggestionHandlers.onClick,
945
+ className: "relative mt-2 flex flex-col border-t border-border/60 px-1.5 pt-1.5",
946
+ children: [
947
+ /* @__PURE__ */ jsx2(FluidHoverHighlight2, {
948
+ hover: suggestionHover,
949
+ className: shape.bg
950
+ }),
951
+ suggestionsArr.map((s, i) => /* @__PURE__ */ jsx2(SuggestionRow, {
952
+ text: s,
953
+ index: i,
954
+ active: i === activeSuggestion,
955
+ keyHint: i === 0 && activeSuggestion == null,
956
+ optionId: `${suggestionListId}-${i}`,
957
+ registerItem: registerSuggestion,
958
+ onSelect: () => acceptSuggestion(s)
959
+ }, `${s}-${i}`))
960
+ ]
961
+ })
962
+ }, "suggestions")
963
+ }),
964
+ /* @__PURE__ */ jsx2("span", {
965
+ className: "sr-only",
966
+ role: "status",
967
+ "aria-live": "polite",
968
+ children: liveMsg
969
+ })
970
+ ]
971
+ })
972
+ });
973
+ return size ? /* @__PURE__ */ jsx2(SizeProvider2, {
974
+ size,
975
+ children: composer
976
+ }) : composer;
977
+ });
978
+ InputMessage2.displayName = "InputMessage";
979
+ var input_message_default = InputMessage2;
980
+
981
+ export { FileThumbnail2, InputMessage2, input_message_default };