@nebutra/ui 0.2.1 → 0.2.3

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 (56) hide show
  1. package/LICENSE +21 -676
  2. package/README.md +81 -86
  3. package/dist/agent/index.d.ts +119 -0
  4. package/dist/agent/index.js +135 -0
  5. package/dist/agent/index.js.map +1 -0
  6. package/dist/canonical-Drca5UMj.d.ts +4309 -0
  7. package/dist/components/index.d.ts +51 -56
  8. package/dist/components/index.js +1696 -1203
  9. package/dist/components/index.js.map +1 -1
  10. package/dist/hooks/index.d.ts +62 -15
  11. package/dist/hooks/index.js +56 -34
  12. package/dist/hooks/index.js.map +1 -1
  13. package/dist/index-CKLIGfiU.d.ts +19 -0
  14. package/dist/index-Dg4jRJV2.d.ts +57 -0
  15. package/dist/layout/index.d.ts +7 -3
  16. package/dist/layout/index.js +525 -542
  17. package/dist/layout/index.js.map +1 -1
  18. package/dist/layouts/index.d.ts +80 -2
  19. package/dist/layouts/index.js +32 -19
  20. package/dist/layouts/index.js.map +1 -1
  21. package/dist/patterns/index.d.ts +131 -5
  22. package/dist/patterns/index.js +1594 -806
  23. package/dist/patterns/index.js.map +1 -1
  24. package/dist/primitives/canonical.d.ts +25 -0
  25. package/dist/primitives/canonical.js +12269 -0
  26. package/dist/primitives/canonical.js.map +1 -0
  27. package/dist/primitives/index.d.ts +694 -3544
  28. package/dist/primitives/index.js +8994 -6583
  29. package/dist/primitives/index.js.map +1 -1
  30. package/dist/shared/animation/css/index.d.ts +1 -0
  31. package/dist/shared/animation/css/index.js +14 -0
  32. package/dist/shared/animation/css/index.js.map +1 -0
  33. package/dist/shared/animation/index.d.ts +6 -0
  34. package/dist/shared/animation/index.js +272 -0
  35. package/dist/shared/animation/index.js.map +1 -0
  36. package/dist/shared/animation/motion/index.d.ts +4 -0
  37. package/dist/shared/animation/motion/index.js +205 -0
  38. package/dist/shared/animation/motion/index.js.map +1 -0
  39. package/dist/styles/brand-override.css +11 -11
  40. package/dist/tailwind.preset.d.ts +10 -10
  41. package/dist/tailwind.preset.js +7 -4
  42. package/dist/tailwind.preset.js.map +1 -1
  43. package/dist/theme/index.d.ts +2 -2
  44. package/dist/tokens-EAC6uPem.d.ts +302 -0
  45. package/dist/typography/fonts.css +51 -37
  46. package/dist/typography/index.d.ts +27 -27
  47. package/dist/typography/index.js +24 -4
  48. package/dist/typography/index.js.map +1 -1
  49. package/dist/utils/index.d.ts +66 -14
  50. package/dist/utils/index.js +53 -10
  51. package/dist/utils/index.js.map +1 -1
  52. package/package.json +56 -14
  53. package/src/styles/brand-override.css +61 -0
  54. package/src/styles/preset.css +29 -0
  55. package/src/styles/sources.css +37 -0
  56. package/src/typography/fonts.css +124 -0
@@ -1,43 +1,540 @@
1
1
  "use client";
2
2
  import { Button as Button$1 } from '@lobehub/ui';
3
3
  export { ActionIcon, ActionIconGroup, Alert, Avatar, Button, Checkbox, DraggablePanel, DraggablePanelBody, DraggablePanelContainer, DraggablePanelFooter, DraggablePanelHeader, Flexbox, Form, FormGroup, FormItem, Highlighter, Image, Input, List, Markdown, Menu, Modal, SearchBar, Segmented, Select, SideNav, Tag, TextArea, Tooltip } from '@lobehub/ui';
4
- export { Spotlight } from '@lobehub/ui/awesome';
5
4
  export { ChatInputArea, ChatItem, ChatList, MessageInput, MessageModal } from '@lobehub/ui/chat';
6
- import { Cross, Paperclip, Globe, Brain, FolderClosed, StopCircle, ArrowUp, Microphone, LoaderCircle, EyeOff, Eye, Bell, ChevronRight, CrossSmall, CheckCircle, PlayCircle, Users, MoreHorizontal, DoubleCheck, Check, FacePlus, PaperAirplane } from '@nebutra/icons';
7
- import { Square } from '@phosphor-icons/react/dist/ssr';
8
- import { motion, AnimatePresence, useReducedMotion, LazyMotion, domAnimation, m } from 'framer-motion';
5
+ export { default as Spotlight } from '@lobehub/ui/es/awesome/Spotlight/Spotlight';
6
+ import { motion, AnimatePresence, useReducedMotion, m, LazyMotion, domAnimation } from 'framer-motion';
7
+ export { AnimatePresence, LayoutGroup, LazyMotion, MotionConfig, animate, domAnimation, m, motion, useAnimationFrame, useInView, useMotionTemplate, useMotionValue, useReducedMotion, useScroll, useSpring, useTransform, useVelocity } from 'framer-motion';
8
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
+ import { Cross, Bell, ChevronRight, CrossSmall, CheckCircle, PlayCircle, Users, MoreHorizontal, DoubleCheck, Check, FacePlus, PaperAirplane, Paperclip, StopFill, StopCircle, ArrowUp, Microphone, LoaderCircle, EyeOff, Eye, Globe, Brain, FolderClosed } from '@nebutra/icons';
9
10
  import Image from 'next/image';
10
- import * as React6 from 'react';
11
- import React6__default, { useId, useState, useMemo, useCallback } from 'react';
12
- import { cva } from 'class-variance-authority';
11
+ import { Dialog as Dialog$1 } from '@base-ui/react/dialog';
12
+ import * as React5 from 'react';
13
+ import React5__default, { useEffectEvent, useId, useState } from 'react';
14
+ import { colors, emerge, flow, brandSpring } from '@nebutra/brand';
13
15
  import { clsx } from 'clsx';
14
16
  import { twMerge } from 'tailwind-merge';
15
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
16
- import { Dialog as Dialog$1 } from '@base-ui/react/dialog';
17
- import { colors, flow, emerge, brandSpring } from '@nebutra/brand';
17
+ import { cva } from 'class-variance-authority';
18
18
  import { Tooltip as Tooltip$1 } from '@base-ui/react/tooltip';
19
19
  import { useAsciiText, slant } from 'react-ascii-text';
20
- import { Handle, Position, ReactFlowProvider, ReactFlow, Background, Controls, Panel } from '@xyflow/react';
20
+ import { Popover as Popover$1 } from '@base-ui/react/popover';
21
+ import { ReactFlowProvider, ReactFlow, Background, Controls, Panel, Handle, Position } from '@xyflow/react';
21
22
  import '@xyflow/react/dist/style.css';
22
23
  import { wouldCreateCycle } from '@nebutra/graph-model';
23
24
  import { Input as Input$1 } from '@base-ui/react/input';
24
25
 
25
26
  // src/components/index.ts
27
+
28
+ // src/shared/animation/motion/tokens.ts
29
+ var animationDurations = {
30
+ instant: 0,
31
+ micro: 0.1,
32
+ fast: 0.16,
33
+ standard: 0.2,
34
+ reveal: 0.3,
35
+ page: 0.44
36
+ };
37
+ var animationEasings = {
38
+ linear: "linear",
39
+ enter: [0, 0, 0.2, 1],
40
+ exit: [0.4, 0, 1, 1],
41
+ standard: [0.4, 0, 0.2, 1],
42
+ emphasized: [0.16, 1, 0.3, 1]
43
+ };
44
+ var animationSprings = {
45
+ interactive: { type: "spring", stiffness: 380, damping: 34, mass: 0.8 },
46
+ layout: { type: "spring", stiffness: 480, damping: 42, mass: 1 },
47
+ panel: { type: "spring", stiffness: 320, damping: 36, mass: 1 }
48
+ };
49
+ var animationDistances = {
50
+ xs: 4,
51
+ sm: 8,
52
+ md: 16,
53
+ lg: 24,
54
+ panel: 32
55
+ };
56
+ var animationTransitions = {
57
+ instant: { duration: animationDurations.instant },
58
+ micro: { duration: animationDurations.micro, ease: animationEasings.enter },
59
+ standard: { duration: animationDurations.standard, ease: animationEasings.enter },
60
+ reveal: { duration: animationDurations.reveal, ease: animationEasings.emphasized },
61
+ page: { duration: animationDurations.page, ease: animationEasings.emphasized },
62
+ layout: animationSprings.layout,
63
+ panel: animationSprings.panel
64
+ };
65
+
66
+ // src/shared/animation/motion/variants.ts
67
+ var fadeIn = {
68
+ initial: { opacity: 0 },
69
+ animate: { opacity: 1, transition: animationTransitions.standard },
70
+ exit: { opacity: 0, transition: animationTransitions.micro }
71
+ };
72
+ var scaleIn = {
73
+ initial: { opacity: 0, scale: 0.96 },
74
+ animate: { opacity: 1, scale: 1, transition: animationTransitions.reveal },
75
+ exit: { opacity: 0, scale: 0.98, transition: animationTransitions.micro }
76
+ };
77
+ var slideIn = {
78
+ up: {
79
+ initial: { opacity: 0, y: animationDistances.md },
80
+ animate: { opacity: 1, y: 0, transition: animationTransitions.reveal },
81
+ exit: { opacity: 0, y: animationDistances.sm, transition: animationTransitions.micro }
82
+ },
83
+ down: {
84
+ initial: { opacity: 0, y: -animationDistances.md },
85
+ animate: { opacity: 1, y: 0, transition: animationTransitions.reveal },
86
+ exit: { opacity: 0, y: -animationDistances.sm, transition: animationTransitions.micro }
87
+ },
88
+ left: {
89
+ initial: { opacity: 0, x: animationDistances.md },
90
+ animate: { opacity: 1, x: 0, transition: animationTransitions.reveal },
91
+ exit: { opacity: 0, x: animationDistances.sm, transition: animationTransitions.micro }
92
+ },
93
+ right: {
94
+ initial: { opacity: 0, x: -animationDistances.md },
95
+ animate: { opacity: 1, x: 0, transition: animationTransitions.reveal },
96
+ exit: { opacity: 0, x: -animationDistances.sm, transition: animationTransitions.micro }
97
+ }
98
+ };
99
+ var animatedList = {
100
+ initial: {},
101
+ animate: { transition: { staggerChildren: 0.06, delayChildren: 0.02 } },
102
+ exit: { transition: { staggerChildren: 0.03, staggerDirection: -1 } }
103
+ };
104
+ var animatedListItem = {
105
+ initial: { opacity: 0, y: animationDistances.sm },
106
+ animate: { opacity: 1, y: 0, transition: animationTransitions.standard },
107
+ exit: { opacity: 0, y: animationDistances.xs, transition: animationTransitions.micro }
108
+ };
109
+ var modalSurface = {
110
+ initial: { opacity: 0, scale: 0.98, y: animationDistances.sm },
111
+ animate: { opacity: 1, scale: 1, y: 0, transition: animationTransitions.panel },
112
+ exit: {
113
+ opacity: 0,
114
+ scale: 0.98,
115
+ y: animationDistances.xs,
116
+ transition: animationTransitions.micro
117
+ }
118
+ };
119
+ var drawerSurface = {
120
+ left: {
121
+ initial: { opacity: 0, x: "-100%" },
122
+ animate: { opacity: 1, x: 0, transition: animationTransitions.panel },
123
+ exit: { opacity: 0, x: "-100%", transition: animationTransitions.micro }
124
+ },
125
+ right: {
126
+ initial: { opacity: 0, x: "100%" },
127
+ animate: { opacity: 1, x: 0, transition: animationTransitions.panel },
128
+ exit: { opacity: 0, x: "100%", transition: animationTransitions.micro }
129
+ },
130
+ top: {
131
+ initial: { opacity: 0, y: "-100%" },
132
+ animate: { opacity: 1, y: 0, transition: animationTransitions.panel },
133
+ exit: { opacity: 0, y: "-100%", transition: animationTransitions.micro }
134
+ },
135
+ bottom: {
136
+ initial: { opacity: 0, y: "100%" },
137
+ animate: { opacity: 1, y: 0, transition: animationTransitions.panel },
138
+ exit: { opacity: 0, y: "100%", transition: animationTransitions.micro }
139
+ }
140
+ };
141
+ var popoverSurface = {
142
+ initial: { opacity: 0, scale: 0.98, y: animationDistances.xs },
143
+ animate: { opacity: 1, scale: 1, y: 0, transition: animationTransitions.standard },
144
+ exit: {
145
+ opacity: 0,
146
+ scale: 0.98,
147
+ y: animationDistances.xs,
148
+ transition: animationTransitions.micro
149
+ }
150
+ };
151
+ var pageTransition = {
152
+ initial: { opacity: 0, y: animationDistances.sm },
153
+ animate: { opacity: 1, y: 0, transition: animationTransitions.page },
154
+ exit: { opacity: 0, y: -animationDistances.xs, transition: animationTransitions.standard }
155
+ };
156
+ var reducedMotionVariants = {
157
+ initial: { opacity: 0 },
158
+ animate: { opacity: 1, transition: animationTransitions.instant },
159
+ exit: { opacity: 0, transition: animationTransitions.instant }
160
+ };
161
+ function useAccessibleVariants(variants, allowTransform = false) {
162
+ const shouldReduceMotion = useReducedMotion();
163
+ return shouldReduceMotion && !allowTransform ? reducedMotionVariants : variants;
164
+ }
165
+ function FadeIn({ children, ...props }) {
166
+ const variants = useAccessibleVariants(fadeIn);
167
+ return /* @__PURE__ */ jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", variants, ...props, children });
168
+ }
169
+ function SlideIn({
170
+ children,
171
+ direction = "up",
172
+ ...props
173
+ }) {
174
+ const variants = useAccessibleVariants(slideIn[direction]);
175
+ return /* @__PURE__ */ jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", variants, ...props, children });
176
+ }
177
+ function ScaleIn({ children, ...props }) {
178
+ const variants = useAccessibleVariants(scaleIn);
179
+ return /* @__PURE__ */ jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", variants, ...props, children });
180
+ }
181
+ function AnimatedCard({ children, ...props }) {
182
+ return /* @__PURE__ */ jsx(ScaleIn, { whileHover: { y: -2 }, whileTap: { scale: 0.99 }, ...props, children });
183
+ }
184
+ function AnimatedList({ children, ...props }) {
185
+ const containerVariants2 = useAccessibleVariants(animatedList);
186
+ const itemVariants2 = useAccessibleVariants(animatedListItem);
187
+ return /* @__PURE__ */ jsx(
188
+ motion.div,
189
+ {
190
+ initial: "initial",
191
+ animate: "animate",
192
+ exit: "exit",
193
+ variants: containerVariants2,
194
+ ...props,
195
+ children: Array.isArray(children) ? children.map((child, index) => /* @__PURE__ */ jsx(motion.div, { variants: itemVariants2, children: child }, index)) : children
196
+ }
197
+ );
198
+ }
199
+ function AnimatedModal({ children, open, ...props }) {
200
+ const variants = useAccessibleVariants(modalSurface);
201
+ return /* @__PURE__ */ jsx(AnimatePresence, { children: open ? /* @__PURE__ */ jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", variants, ...props, children }) : null });
202
+ }
203
+ function AnimatedDrawer({
204
+ children,
205
+ open,
206
+ side = "right",
207
+ ...props
208
+ }) {
209
+ const variants = useAccessibleVariants(drawerSurface[side]);
210
+ return /* @__PURE__ */ jsx(AnimatePresence, { children: open ? /* @__PURE__ */ jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", variants, ...props, children }) : null });
211
+ }
212
+ function AnimatedPopover({
213
+ children,
214
+ open,
215
+ ...props
216
+ }) {
217
+ const variants = useAccessibleVariants(popoverSurface);
218
+ return /* @__PURE__ */ jsx(AnimatePresence, { children: open ? /* @__PURE__ */ jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", variants, ...props, children }) : null });
219
+ }
220
+ function PageTransition({ children, ...props }) {
221
+ const variants = useAccessibleVariants(pageTransition);
222
+ return /* @__PURE__ */ jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", variants, ...props, children });
223
+ }
224
+ ({
225
+ // Nebutra Blue (云毓蓝) — technology & trust — VI: #0033FE
226
+ blue50: colors.primary[50],
227
+ blue100: colors.primary[100],
228
+ blue200: colors.primary[200],
229
+ blue300: colors.primary[300],
230
+ blue400: colors.primary[400],
231
+ blue500: colors.primary[500],
232
+ // brand primary
233
+ blue600: colors.primary[600],
234
+ blue700: colors.primary[700],
235
+ blue800: colors.primary[800],
236
+ blue900: colors.primary[900],
237
+ blue950: colors.primary[950],
238
+ // Nebutra Cyan (云毓青) — data flow & intelligence — VI: #0BF1C3
239
+ cyan50: colors.accent[50],
240
+ cyan100: colors.accent[100],
241
+ cyan200: colors.accent[200],
242
+ cyan300: colors.accent[300],
243
+ cyan400: colors.accent[400],
244
+ cyan500: colors.accent[500],
245
+ // brand secondary
246
+ cyan600: colors.accent[600],
247
+ cyan700: colors.accent[700],
248
+ cyan800: colors.accent[800],
249
+ cyan900: colors.accent[900],
250
+ cyan950: colors.accent[950],
251
+ // Neutral (blue-undertone gray scale)
252
+ neutral50: colors.neutral[50],
253
+ neutral100: colors.neutral[100],
254
+ neutral200: colors.neutral[200],
255
+ neutral300: colors.neutral[300],
256
+ neutral400: colors.neutral[400],
257
+ neutral500: colors.neutral[500],
258
+ neutral600: colors.neutral[600],
259
+ neutral700: colors.neutral[700],
260
+ neutral800: colors.neutral[800],
261
+ neutral900: colors.neutral[900],
262
+ neutral950: colors.neutral[950],
263
+ white: colors.white,
264
+ black: colors.black
265
+ });
266
+ ({
267
+ /** Hero sections, primary CTA buttons, logo fills — VI signature gradient */
268
+ primary: colors.gradient.primary,
269
+ /** Hover states, secondary gradient elements */
270
+ reverse: colors.gradient.primaryReverse,
271
+ /** Vertical layout dividers, page section separators */
272
+ vertical: colors.gradient.primaryVertical,
273
+ /** Background halos, focus glow effects, radial emphasis */
274
+ radial: colors.gradient.primaryRadial});
275
+ var primitiveSpacing = {
276
+ 1: 4,
277
+ 2: 8,
278
+ 3: 12,
279
+ 4: 16,
280
+ 20: 80,
281
+ 24: 96
282
+ };
283
+ var primitiveSizing = {
284
+ xs: 20,
285
+ /** Geist button-specific size (24px). Larger than xs by design. */
286
+ tiny: 24,
287
+ sm: 32,
288
+ md: 40,
289
+ lg: 48};
290
+ var primitiveRadius = {
291
+ md: 6,
292
+ // Geist default — slightly more rounded than 4px
293
+ lg: 8};
294
+ var primitiveFontSize = {
295
+ xs: 12,
296
+ sm: 14,
297
+ base: 16};
298
+
299
+ // src/tokens/shadows.ts
300
+ var shadowClasses = {
301
+ md: "shadow-md",
302
+ xl: "shadow-xl",
303
+ "2xl": "shadow-2xl"};
304
+
305
+ // src/tokens/components/overlay.ts
306
+ var overlayFocusRingClassName = "outline-none";
307
+ var overlayZIndex = {
308
+ backdrop: 1040,
309
+ modal: 1050,
310
+ popover: 1060,
311
+ tooltip: 1070
312
+ };
313
+ var overlayClassNames = {
314
+ backdrop: "fixed inset-0 bg-black/40 backdrop-blur-sm transition-[opacity,display] duration-[var(--motion-duration-flow)] ease-[var(--ease-out)] data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none",
315
+ modalSurface: [
316
+ "fixed left-1/2 top-1/2 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4",
317
+ "border border-border/60 bg-background/95 p-6 text-foreground backdrop-blur-xl",
318
+ "rounded-[var(--radius-xl)]",
319
+ shadowClasses["2xl"],
320
+ "outline-none",
321
+ "transition-[opacity,transform,display] duration-[var(--motion-duration-flow)] ease-[var(--ease-out)]",
322
+ "data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0",
323
+ "motion-reduce:transition-none motion-reduce:data-starting-style:transform-none motion-reduce:data-ending-style:transform-none"
324
+ ].join(" "),
325
+ popoverSurface: [
326
+ "w-72 rounded-[var(--radius-lg)] border border-border/70 bg-popover/95 p-4 text-popover-foreground backdrop-blur-md",
327
+ shadowClasses.xl,
328
+ "outline-none transition-[opacity,transform,display] duration-[var(--motion-duration-flow)] ease-[var(--ease-out)]",
329
+ "data-[open]:opacity-100 data-[closed]:opacity-0 data-[closed]:scale-95 data-[open]:scale-100",
330
+ "motion-reduce:transition-none motion-reduce:data-[closed]:transform-none motion-reduce:data-[open]:transform-none"
331
+ ].join(" "),
332
+ tooltipSurface: [
333
+ "overflow-hidden rounded-[var(--radius-md)] border border-border/70 bg-popover/95 px-3 py-1.5 text-sm text-popover-foreground backdrop-blur-md",
334
+ shadowClasses.md,
335
+ "transition-[opacity,transform,display] duration-[var(--motion-duration-flow)] ease-[var(--ease-out)]",
336
+ "data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0",
337
+ "motion-reduce:transition-none motion-reduce:data-starting-style:transform-none motion-reduce:data-ending-style:transform-none"
338
+ ].join(" "),
339
+ closeButton: `absolute right-4 top-4 rounded-[var(--radius-sm)] opacity-70 ring-offset-background ${overlayFocusRingClassName} transition-opacity duration-[var(--motion-duration-micro)] ease-[var(--ease-out)] hover:opacity-100 disabled:pointer-events-none data-[open]:bg-accent data-[open]:text-muted-foreground motion-reduce:transition-none`
340
+ };
26
341
  function cn(...inputs) {
27
342
  return twMerge(clsx(inputs));
28
343
  }
29
- var Slot = React6.forwardRef(
344
+ var overlayPrimitiveClassNames = {
345
+ modalSurface: "rounded-[var(--radius-md)] border-border/70 bg-popover/95 text-popover-foreground shadow-xl",
346
+ popoverSurface: "rounded-[var(--radius-md)] border-border/70 bg-popover/95 shadow-md"};
347
+ var Dialog = Dialog$1.Root;
348
+ var DialogPortal = Dialog$1.Portal;
349
+ var DialogTrigger = ({
350
+ asChild,
351
+ children,
352
+ ref,
353
+ ...props
354
+ }) => {
355
+ if (asChild && React5.isValidElement(children)) {
356
+ return /* @__PURE__ */ jsx(
357
+ Dialog$1.Trigger,
358
+ {
359
+ ref,
360
+ ...props,
361
+ render: children
362
+ }
363
+ );
364
+ }
365
+ return /* @__PURE__ */ jsx(Dialog$1.Trigger, { ref, ...props, children });
366
+ };
367
+ DialogTrigger.displayName = "DialogTrigger";
368
+ var DialogClose = ({
369
+ asChild,
370
+ children,
371
+ ref,
372
+ ...props
373
+ }) => {
374
+ if (asChild && React5.isValidElement(children)) {
375
+ return /* @__PURE__ */ jsx(
376
+ Dialog$1.Close,
377
+ {
378
+ ref,
379
+ ...props,
380
+ render: children
381
+ }
382
+ );
383
+ }
384
+ return /* @__PURE__ */ jsx(Dialog$1.Close, { ref, ...props, children });
385
+ };
386
+ DialogClose.displayName = "DialogClose";
387
+ var DialogOverlay = ({
388
+ className,
389
+ style,
390
+ ref,
391
+ ...props
392
+ }) => /* @__PURE__ */ jsx(
393
+ Dialog$1.Backdrop,
394
+ {
395
+ ref,
396
+ className: cn(overlayClassNames.backdrop, className),
397
+ style: { zIndex: overlayZIndex.backdrop, ...style },
398
+ ...props
399
+ }
400
+ );
401
+ DialogOverlay.displayName = "DialogOverlay";
402
+ var DialogContent = ({
403
+ className,
404
+ children,
405
+ style,
406
+ ref,
407
+ ...props
408
+ }) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
409
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
410
+ /* @__PURE__ */ jsxs(
411
+ Dialog$1.Popup,
412
+ {
413
+ ref,
414
+ className: cn(
415
+ overlayClassNames.modalSurface,
416
+ overlayPrimitiveClassNames.modalSurface,
417
+ className
418
+ ),
419
+ style: { zIndex: overlayZIndex.modal, ...style },
420
+ ...props,
421
+ children: [
422
+ children,
423
+ /* @__PURE__ */ jsxs(DialogClose, { className: overlayClassNames.closeButton, children: [
424
+ /* @__PURE__ */ jsx(Cross, { className: "h-4 w-4" }),
425
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
426
+ ] })
427
+ ]
428
+ }
429
+ )
430
+ ] });
431
+ DialogContent.displayName = "DialogContent";
432
+ var DialogTitle = ({
433
+ className,
434
+ ref,
435
+ ...props
436
+ }) => /* @__PURE__ */ jsx(
437
+ Dialog$1.Title,
438
+ {
439
+ ref,
440
+ className: cn("text-lg font-semibold leading-none tracking-tight", className),
441
+ ...props
442
+ }
443
+ );
444
+ DialogTitle.displayName = "DialogTitle";
445
+ var isImageFile = (file) => file.type.startsWith("image/");
446
+ var getFilePreviewKey = (file) => `${file.name}-${file.size}-${file.lastModified}`;
447
+ var preventDragDefaults = (e) => {
448
+ e.preventDefault();
449
+ e.stopPropagation();
450
+ };
451
+ var ImageViewDialog = ({ imageUrl, onClose }) => {
452
+ const shouldReduceMotion = useReducedMotion();
453
+ if (!imageUrl) return null;
454
+ return /* @__PURE__ */ jsx(Dialog, { open: !!imageUrl, onOpenChange: onClose, children: /* @__PURE__ */ jsxs(DialogContent, { className: "p-0 border-none bg-transparent shadow-none max-w-[90vw] md:max-w-[800px]", children: [
455
+ /* @__PURE__ */ jsx(DialogTitle, { className: "sr-only", children: "Image Preview" }),
456
+ /* @__PURE__ */ jsx(
457
+ m.div,
458
+ {
459
+ initial: shouldReduceMotion ? { opacity: 1 } : { opacity: 0, scale: 0.95 },
460
+ animate: { opacity: 1, ...shouldReduceMotion ? {} : { scale: 1 } },
461
+ exit: shouldReduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.95 },
462
+ transition: shouldReduceMotion ? { duration: 0 } : { duration: 0.2, ease: "easeOut" },
463
+ className: "relative bg-background rounded-[var(--radius-2xl)] overflow-hidden shadow-2xl",
464
+ children: /* @__PURE__ */ jsx(
465
+ Image,
466
+ {
467
+ src: imageUrl,
468
+ alt: "Full preview",
469
+ className: "w-full max-h-[80vh] object-contain rounded-[var(--radius-2xl)]",
470
+ fill: true,
471
+ sizes: "(max-width: 768px) 90vw, 800px"
472
+ }
473
+ )
474
+ }
475
+ )
476
+ ] }) });
477
+ };
478
+ ImageViewDialog.displayName = "ImageViewDialog";
479
+ function PromptAttachmentPreviews({
480
+ files,
481
+ filePreviews,
482
+ isRecording,
483
+ onPreview,
484
+ onRemove
485
+ }) {
486
+ if (files.length === 0 || isRecording) return null;
487
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 p-0 pb-1 transition-[background-color,border-color,box-shadow,color,opacity,transform] duration-300 motion-reduce:transition-none", children: files.map((file) => {
488
+ const previewKey = getFilePreviewKey(file);
489
+ const preview = filePreviews[previewKey];
490
+ return /* @__PURE__ */ jsx("div", { className: "relative group h-16 w-16", children: isImageFile(file) && preview && /* @__PURE__ */ jsxs(Fragment, { children: [
491
+ /* @__PURE__ */ jsx(
492
+ "button",
493
+ {
494
+ type: "button",
495
+ "aria-label": `Preview ${file.name}`,
496
+ className: "relative h-16 w-16 rounded-[var(--radius-xl)] overflow-hidden cursor-pointer transition-[background-color,border-color,box-shadow,color,opacity,transform] duration-300 motion-reduce:transition-none",
497
+ onClick: () => onPreview(preview),
498
+ children: /* @__PURE__ */ jsx(
499
+ Image,
500
+ {
501
+ src: preview,
502
+ alt: file.name,
503
+ className: "h-full w-full object-cover",
504
+ fill: true,
505
+ sizes: "64px"
506
+ }
507
+ )
508
+ }
509
+ ),
510
+ /* @__PURE__ */ jsx(
511
+ "button",
512
+ {
513
+ type: "button",
514
+ "aria-label": `Remove ${file.name}`,
515
+ onClick: (event) => {
516
+ event.stopPropagation();
517
+ onRemove(file);
518
+ },
519
+ className: "absolute top-1 right-1 rounded-full bg-black/70 p-0.5 opacity-100 transition-opacity motion-reduce:transition-none",
520
+ children: /* @__PURE__ */ jsx(Cross, { className: "h-3 w-3 text-white" })
521
+ }
522
+ )
523
+ ] }) }, previewKey);
524
+ }) });
525
+ }
526
+ var Slot = React5.forwardRef(
30
527
  ({ children, className, style, ...props }, ref) => {
31
- const childArray = React6.Children.toArray(children);
528
+ const childArray = React5.Children.toArray(children);
32
529
  const child = childArray.length === 1 ? childArray[0] : null;
33
- if (child == null || !React6.isValidElement(child)) {
530
+ if (child == null || !React5.isValidElement(child)) {
34
531
  return /* @__PURE__ */ jsx("span", { ref, className, style, ...props, children });
35
532
  }
36
533
  const childProps = child.props;
37
534
  const mergedRef = (node) => {
38
535
  if (typeof ref === "function") ref(node);
39
536
  else if (ref) ref.current = node;
40
- const childRef = child.ref;
537
+ const childRef = childProps.ref;
41
538
  if (typeof childRef === "function") childRef(node);
42
539
  else if (childRef && "current" in childRef) {
43
540
  childRef.current = node;
@@ -51,7 +548,7 @@ var Slot = React6.forwardRef(
51
548
  className: mergedClass,
52
549
  style: { ...childProps.style, ...style }
53
550
  };
54
- return React6.cloneElement(
551
+ return React5.cloneElement(
55
552
  child,
56
553
  merged
57
554
  );
@@ -61,17 +558,18 @@ Slot.displayName = "Slot";
61
558
  var buttonVariants = cva(
62
559
  [
63
560
  "inline-flex items-center justify-center gap-2 whitespace-nowrap",
64
- "rounded-[var(--radius-md)] text-sm font-medium",
65
- "transition-colors duration-150 ease-out",
66
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
561
+ "rounded-[var(--btn-default-radius,var(--radius-md))]",
562
+ "text-[length:var(--control-font-size-md,0.875rem)] font-[number:var(--font-weight-medium,500)]",
563
+ "transition-colors duration-micro ease-out",
67
564
  "disabled:pointer-events-none disabled:opacity-50",
68
565
  "aria-busy:cursor-wait"
69
566
  ].join(" "),
70
567
  {
71
568
  variants: {
72
569
  variant: {
73
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
74
- ink: "bg-[var(--neutral-12)] text-[var(--neutral-1)] ring-1 ring-inset ring-white/[0.04] hover:bg-[var(--neutral-11)] hover:-translate-y-px active:translate-y-0 transition-[transform,background-color] duration-150",
570
+ // Recipe-driven via packages/design/tokens/recipe.css (.btn-brand-default)
571
+ default: "btn-brand-default",
572
+ ink: "bg-[var(--neutral-12)] text-[var(--neutral-1)] ring-1 ring-inset ring-[color:var(--neutral-1)]/5 hover:bg-[var(--neutral-11)] hover:-translate-y-px active:translate-y-0 transition-[transform,background-color] duration-micro",
75
573
  destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
76
574
  outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
77
575
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
@@ -81,34 +579,99 @@ var buttonVariants = cva(
81
579
  warning: "bg-warning text-warning-foreground hover:bg-warning/90"
82
580
  },
83
581
  size: {
84
- tiny: "h-6 rounded-[var(--radius-sm)] px-2 text-[11px]",
85
- // 24px
86
- sm: "h-8 rounded-[var(--radius-sm)] px-3 text-xs",
87
- // 32px
88
- default: "h-10 px-4 py-2",
89
- // 40px
90
- lg: "h-12 rounded-[var(--radius-lg)] px-5 text-base",
91
- // 48px
92
- icon: "h-10 w-10"
93
- // 40×40px (kept for backward compat)
582
+ tiny: "h-[var(--control-height-tiny,1.5rem)] rounded-[var(--radius-sm)] px-2 text-[length:var(--control-font-size-tiny,0.6875rem)]",
583
+ sm: "h-[var(--control-height-sm,2rem)] rounded-[var(--radius-sm)] px-3 text-[length:var(--control-font-size-sm,0.75rem)]",
584
+ default: "h-[var(--control-height-md,2.5rem)] px-[var(--btn-default-padding-x,1rem)] py-[var(--btn-default-padding-y,0.5rem)]",
585
+ lg: "h-[var(--control-height-lg,3rem)] rounded-[var(--radius-lg)] px-5 text-[length:var(--control-font-size-lg,1rem)]",
586
+ icon: "h-[var(--control-height-md,2.5rem)] w-[var(--control-height-md,2.5rem)]"
94
587
  },
95
588
  shape: {
96
589
  default: "",
97
- square: "",
98
- circle: ""
590
+ // Radius from --btn-default-radius / --radius-md — brand-switchable.
591
+ // Do not hardcode rounded-md here; a language must be able to retarget
592
+ // the corner treatment of every icon trigger at once.
593
+ square: "rounded-[var(--btn-default-radius,var(--radius-md))]",
594
+ // Pill radius token, not Tailwind rounded-full — same 9999px default,
595
+ // but a brand that ships a softer "circle" can override --radius-pill.
596
+ circle: "rounded-[var(--radius-pill,9999px)]",
597
+ pill: "rounded-[var(--radius-pill,9999px)]"
598
+ },
599
+ // Icon-only box size — orthogonal to `size` (which tunes text-button
600
+ // height/padding/font together) and to `shape` (which picks the
601
+ // corner treatment). Only takes effect combined with shape="square"
602
+ // or shape="circle" (see compoundVariants below); every other
603
+ // combination ignores it. 28/32/36 cover the icon-trigger sizes that
604
+ // actually occur in the app — 24/40/48 are already reachable via
605
+ // shape + size (tiny/default/lg). Heights read --control-height-icon-*
606
+ // so density-aware Brand Packages can scale them with the ladder.
607
+ iconSize: {
608
+ sm: "",
609
+ // 28px default
610
+ md: "",
611
+ // 32px default
612
+ lg: ""
613
+ // 36px default
99
614
  }
100
615
  },
101
616
  compoundVariants: [
102
- // shape="square" — width = height, no horizontal padding
103
- { shape: "square", size: "tiny", className: "w-6 px-0" },
104
- { shape: "square", size: "sm", className: "w-8 px-0" },
105
- { shape: "square", size: "default", className: "w-10 px-0" },
106
- { shape: "square", size: "lg", className: "w-12 px-0" },
107
- // shape="circle" — width = height + rounded-full
108
- { shape: "circle", size: "tiny", className: "w-6 px-0 rounded-full" },
109
- { shape: "circle", size: "sm", className: "w-8 px-0 rounded-full" },
110
- { shape: "circle", size: "default", className: "w-10 px-0 rounded-full" },
111
- { shape: "circle", size: "lg", className: "w-12 px-0 rounded-full" }
617
+ { shape: "square", size: "tiny", className: "w-[var(--control-height-tiny,1.5rem)] px-0" },
618
+ { shape: "square", size: "sm", className: "w-[var(--control-height-sm,2rem)] px-0" },
619
+ { shape: "square", size: "default", className: "w-[var(--control-height-md,2.5rem)] px-0" },
620
+ { shape: "square", size: "lg", className: "w-[var(--control-height-lg,3rem)] px-0" },
621
+ {
622
+ shape: "circle",
623
+ size: "tiny",
624
+ className: "w-[var(--control-height-tiny,1.5rem)] px-0"
625
+ },
626
+ {
627
+ shape: "circle",
628
+ size: "sm",
629
+ className: "w-[var(--control-height-sm,2rem)] px-0"
630
+ },
631
+ {
632
+ shape: "circle",
633
+ size: "default",
634
+ className: "w-[var(--control-height-md,2.5rem)] px-0"
635
+ },
636
+ {
637
+ shape: "circle",
638
+ size: "lg",
639
+ className: "w-[var(--control-height-lg,3rem)] px-0"
640
+ },
641
+ // iconSize wins over the size-driven box above — matched on shape +
642
+ // iconSize only, so it applies regardless of whatever `size` was
643
+ // passed (or left at its "default" fallback). Corner treatment stays
644
+ // on the shape variant (token-backed); only the box is set here.
645
+ {
646
+ shape: "square",
647
+ iconSize: "sm",
648
+ className: "h-[var(--control-height-icon-sm,1.75rem)] w-[var(--control-height-icon-sm,1.75rem)] px-0"
649
+ },
650
+ {
651
+ shape: "square",
652
+ iconSize: "md",
653
+ className: "h-[var(--control-height-icon-md,2rem)] w-[var(--control-height-icon-md,2rem)] px-0"
654
+ },
655
+ {
656
+ shape: "square",
657
+ iconSize: "lg",
658
+ className: "h-[var(--control-height-icon-lg,2.25rem)] w-[var(--control-height-icon-lg,2.25rem)] px-0"
659
+ },
660
+ {
661
+ shape: "circle",
662
+ iconSize: "sm",
663
+ className: "h-[var(--control-height-icon-sm,1.75rem)] w-[var(--control-height-icon-sm,1.75rem)] px-0"
664
+ },
665
+ {
666
+ shape: "circle",
667
+ iconSize: "md",
668
+ className: "h-[var(--control-height-icon-md,2rem)] w-[var(--control-height-icon-md,2rem)] px-0"
669
+ },
670
+ {
671
+ shape: "circle",
672
+ iconSize: "lg",
673
+ className: "h-[var(--control-height-icon-lg,2.25rem)] w-[var(--control-height-icon-lg,2.25rem)] px-0"
674
+ }
112
675
  ],
113
676
  defaultVariants: {
114
677
  variant: "default",
@@ -129,7 +692,10 @@ function Spinner({ className }) {
129
692
  }
130
693
  );
131
694
  }
132
- function getIconSizeClass(size) {
695
+ function getIconSizeClass(size, iconSize) {
696
+ if (iconSize === "sm") return "[&>svg]:size-3.5";
697
+ if (iconSize === "md") return "[&>svg]:size-3.5";
698
+ if (iconSize === "lg") return "[&>svg]:size-4";
133
699
  switch (size) {
134
700
  case "tiny":
135
701
  return "[&>svg]:size-3";
@@ -144,7 +710,10 @@ function getIconSizeClass(size) {
144
710
  return "[&>svg]:size-4";
145
711
  }
146
712
  }
147
- function getSpinnerSizeClass(size) {
713
+ function getSpinnerSizeClass(size, iconSize) {
714
+ if (iconSize === "sm") return "size-3.5";
715
+ if (iconSize === "md") return "size-3.5";
716
+ if (iconSize === "lg") return "size-4";
148
717
  switch (size) {
149
718
  case "tiny":
150
719
  return "size-3";
@@ -162,269 +731,64 @@ function getSpinnerSizeClass(size) {
162
731
  var SHADOW_CLASSES = {
163
732
  sm: "shadow-sm",
164
733
  md: "shadow-md",
165
- lg: "shadow-lg"
734
+ lg: "shadow-md"
166
735
  };
167
736
  function resolveShadowClass(shadow) {
168
737
  if (!shadow) return void 0;
169
738
  const level = shadow === true ? "md" : shadow;
170
739
  return SHADOW_CLASSES[level];
171
740
  }
172
- function ButtonContent({ loading, prefix, suffix, size, children }) {
173
- const iconSizeClass = getIconSizeClass(size);
741
+ function ButtonContent({ loading, prefix, suffix, size, iconSize, children }) {
742
+ const iconSizeClass = getIconSizeClass(size, iconSize);
174
743
  return /* @__PURE__ */ jsxs(Fragment, { children: [
175
- loading && /* @__PURE__ */ jsx(Spinner, { className: getSpinnerSizeClass(size) }),
744
+ loading && /* @__PURE__ */ jsx(Spinner, { className: getSpinnerSizeClass(size, iconSize) }),
176
745
  prefix != null && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("shrink-0", iconSizeClass), children: prefix }),
177
746
  children,
178
747
  suffix != null && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("shrink-0", iconSizeClass), children: suffix })
179
748
  ] });
180
749
  }
181
- var Button = React6.forwardRef(
182
- ({
183
- className,
184
- variant,
185
- size,
186
- shape,
187
- asChild = false,
188
- loading = false,
189
- disabled,
190
- prefix,
191
- suffix,
192
- shadow,
193
- children,
194
- ...props
195
- }, ref) => {
196
- const Comp = asChild ? Slot : "button";
197
- const isDisabled = disabled ?? loading;
198
- const shadowClass = resolveShadowClass(shadow);
199
- return /* @__PURE__ */ jsx(
200
- Comp,
201
- {
202
- ref,
203
- className: cn(buttonVariants({ variant, size, shape }), shadowClass, className),
204
- disabled: isDisabled,
205
- "aria-busy": loading || void 0,
206
- ...props,
207
- children: asChild ? children : /* @__PURE__ */ jsx(ButtonContent, { loading, prefix, suffix, size, children })
208
- }
209
- );
210
- }
211
- );
212
- Button.displayName = "Button";
213
- var ButtonLink = React6.forwardRef(
214
- ({
215
- className,
216
- variant,
217
- size,
218
- shape,
219
- prefix,
220
- suffix,
221
- shadow,
222
- loading = false,
223
- children,
224
- ...props
225
- }, ref) => {
226
- const shadowClass = resolveShadowClass(shadow);
227
- const loadingProps = loading ? {
228
- "aria-busy": "true",
229
- "aria-disabled": "true",
230
- tabIndex: -1
231
- } : {};
232
- return /* @__PURE__ */ jsx(
233
- "a",
234
- {
235
- ref,
236
- className: cn(
237
- buttonVariants({ variant, size, shape }),
238
- shadowClass,
239
- loading && "pointer-events-none opacity-50",
240
- className
241
- ),
242
- ...loadingProps,
243
- ...props,
244
- children: /* @__PURE__ */ jsx(ButtonContent, { loading, prefix, suffix, size, children })
245
- }
246
- );
247
- }
248
- );
249
- ButtonLink.displayName = "ButtonLink";
250
- var Dialog = Dialog$1.Root;
251
- var DialogPortal = Dialog$1.Portal;
252
- var DialogTrigger = React6.forwardRef(({ asChild, children, ...props }, ref) => {
253
- if (asChild && React6.isValidElement(children)) {
254
- return /* @__PURE__ */ jsx(
255
- Dialog$1.Trigger,
256
- {
257
- ref,
258
- ...props,
259
- render: children
260
- }
261
- );
262
- }
263
- return /* @__PURE__ */ jsx(Dialog$1.Trigger, { ref, ...props, children });
264
- });
265
- DialogTrigger.displayName = "DialogTrigger";
266
- var DialogClose = React6.forwardRef(({ asChild, children, ...props }, ref) => {
267
- if (asChild && React6.isValidElement(children)) {
268
- return /* @__PURE__ */ jsx(
269
- Dialog$1.Close,
270
- {
271
- ref,
272
- ...props,
273
- render: children
274
- }
275
- );
276
- }
277
- return /* @__PURE__ */ jsx(Dialog$1.Close, { ref, ...props, children });
278
- });
279
- DialogClose.displayName = "DialogClose";
280
- var DialogOverlay = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
281
- Dialog$1.Backdrop,
282
- {
283
- ref,
284
- className: cn(
285
- "fixed inset-0 z-50 bg-black/40 backdrop-blur-sm data-ending-style:animate-out data-starting-style:animate-in data-ending-style:fade-out-0 data-starting-style:fade-in-0 duration-300 transition-[opacity,display]",
286
- className
287
- ),
288
- ...props
289
- }
290
- ));
291
- DialogOverlay.displayName = "DialogOverlay";
292
- var DialogContent = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
293
- /* @__PURE__ */ jsx(DialogOverlay, {}),
294
- /* @__PURE__ */ jsxs(
295
- Dialog$1.Popup,
750
+ var Button = ({
751
+ className,
752
+ variant,
753
+ size,
754
+ shape,
755
+ iconSize,
756
+ asChild = false,
757
+ loading = false,
758
+ disabled,
759
+ prefix,
760
+ suffix,
761
+ shadow,
762
+ children,
763
+ ref,
764
+ ...props
765
+ }) => {
766
+ const Comp = asChild ? Slot : "button";
767
+ const isDisabled = Boolean(disabled || loading);
768
+ const shadowClass = resolveShadowClass(shadow);
769
+ return /* @__PURE__ */ jsx(
770
+ Comp,
296
771
  {
297
772
  ref,
298
- className: cn(
299
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border/50 bg-background/90 p-6 shadow-2xl backdrop-blur-xl duration-300 transition-[opacity,transform,display] data-starting-style:animate-in data-ending-style:animate-out data-ending-style:fade-out-0 data-starting-style:fade-in-0 data-ending-style:zoom-out-95 data-starting-style:zoom-in-95 sm:rounded-2xl",
300
- className
301
- ),
773
+ className: cn(buttonVariants({ variant, size, shape, iconSize }), shadowClass, className),
774
+ disabled: isDisabled,
775
+ "aria-busy": loading || void 0,
302
776
  ...props,
303
- children: [
304
- children,
305
- /* @__PURE__ */ jsxs(DialogClose, { className: "absolute right-4 top-4 rounded-[var(--radius-sm)] opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[open]:bg-accent data-[open]:text-muted-foreground", children: [
306
- /* @__PURE__ */ jsx(Cross, { className: "h-4 w-4" }),
307
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
308
- ] })
309
- ]
310
- }
311
- )
312
- ] }));
313
- DialogContent.displayName = "DialogContent";
314
- var DialogHeader = React6.forwardRef(
315
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
316
- "div",
317
- {
318
- ref,
319
- className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className),
320
- ...props
321
- }
322
- )
323
- );
324
- DialogHeader.displayName = "DialogHeader";
325
- var DialogFooter = React6.forwardRef(
326
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
327
- "div",
328
- {
329
- ref,
330
- className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className),
331
- ...props
777
+ children: asChild ? children : /* @__PURE__ */ jsx(
778
+ ButtonContent,
779
+ {
780
+ loading,
781
+ prefix,
782
+ suffix,
783
+ size,
784
+ iconSize,
785
+ children
786
+ }
787
+ )
332
788
  }
333
- )
334
- );
335
- DialogFooter.displayName = "DialogFooter";
336
- var DialogTitle = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
337
- Dialog$1.Title,
338
- {
339
- ref,
340
- className: cn("text-lg font-semibold leading-none tracking-tight", className),
341
- ...props
342
- }
343
- ));
344
- DialogTitle.displayName = "DialogTitle";
345
- var DialogDescription = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
346
- Dialog$1.Description,
347
- {
348
- ref,
349
- className: cn("text-sm text-muted-foreground", className),
350
- ...props
351
- }
352
- ));
353
- DialogDescription.displayName = "DialogDescription";
354
- ({
355
- // Nebutra Blue (云毓蓝) — technology & trust — VI: #0033FE
356
- blue50: colors.primary[50],
357
- blue100: colors.primary[100],
358
- blue200: colors.primary[200],
359
- blue300: colors.primary[300],
360
- blue400: colors.primary[400],
361
- blue500: colors.primary[500],
362
- // brand primary
363
- blue600: colors.primary[600],
364
- blue700: colors.primary[700],
365
- blue800: colors.primary[800],
366
- blue900: colors.primary[900],
367
- blue950: colors.primary[950],
368
- // Nebutra Cyan (云毓青) — data flow & intelligence — VI: #0BF1C3
369
- cyan50: colors.accent[50],
370
- cyan100: colors.accent[100],
371
- cyan200: colors.accent[200],
372
- cyan300: colors.accent[300],
373
- cyan400: colors.accent[400],
374
- cyan500: colors.accent[500],
375
- // brand secondary
376
- cyan600: colors.accent[600],
377
- cyan700: colors.accent[700],
378
- cyan800: colors.accent[800],
379
- cyan900: colors.accent[900],
380
- cyan950: colors.accent[950],
381
- // Neutral (blue-undertone gray scale)
382
- neutral50: colors.neutral[50],
383
- neutral100: colors.neutral[100],
384
- neutral200: colors.neutral[200],
385
- neutral300: colors.neutral[300],
386
- neutral400: colors.neutral[400],
387
- neutral500: colors.neutral[500],
388
- neutral600: colors.neutral[600],
389
- neutral700: colors.neutral[700],
390
- neutral800: colors.neutral[800],
391
- neutral900: colors.neutral[900],
392
- neutral950: colors.neutral[950],
393
- white: colors.white,
394
- black: colors.black
395
- });
396
- ({
397
- /** Hero sections, primary CTA buttons, logo fills — VI signature gradient */
398
- primary: colors.gradient.primary,
399
- /** Hover states, secondary gradient elements */
400
- reverse: colors.gradient.primaryReverse,
401
- /** Vertical layout dividers, page section separators */
402
- vertical: colors.gradient.primaryVertical,
403
- /** Background halos, focus glow effects, radial emphasis */
404
- radial: colors.gradient.primaryRadial});
405
- var primitiveSpacing = {
406
- 1: 4,
407
- 2: 8,
408
- 3: 12,
409
- 4: 16,
410
- 20: 80,
411
- 24: 96
789
+ );
412
790
  };
413
- var primitiveSizing = {
414
- xs: 20,
415
- /** Geist button-specific size (24px). Larger than xs by design. */
416
- tiny: 24,
417
- sm: 32,
418
- md: 40,
419
- lg: 48};
420
- var primitiveRadius = {
421
- md: 6,
422
- // Geist default — slightly more rounded than 4px
423
- lg: 8};
424
- var primitiveFontSize = {
425
- xs: 12,
426
- sm: 14,
427
- base: 16};
791
+ Button.displayName = "Button";
428
792
 
429
793
  // src/tokens/components/textarea.ts
430
794
  var textareaTokens = {
@@ -454,6 +818,20 @@ var textareaTokens = {
454
818
  labelSize: primitiveFontSize.sm,
455
819
  focusRingWidth: 3,
456
820
  fieldGap: primitiveSpacing[2] - primitiveSpacing[1] / 2};
821
+
822
+ // src/utils/primitive-props.ts
823
+ function asPlainStyle(style) {
824
+ if (style == null || typeof style === "function") return {};
825
+ return style;
826
+ }
827
+
828
+ // src/primitives/form-control.ts
829
+ var formControlFocusClassNames = {
830
+ input: "outline-none focus:border-ring focus:ring-[length:var(--input-focus-ring-width)] focus:ring-ring/30",
831
+ textarea: "outline-none focus:border-ring focus:ring-[length:var(--textarea-focus-ring-width)] focus:ring-ring/30"};
832
+ var formControlInvalidClassNames = {
833
+ input: "aria-invalid:border-destructive/60 aria-invalid:focus:border-destructive aria-invalid:focus:ring-destructive/20",
834
+ textarea: "aria-invalid:border-destructive/60 aria-invalid:focus:border-destructive aria-invalid:focus:ring-destructive/20"};
457
835
  var fieldMessageClassName = "text-xs text-muted-foreground";
458
836
  var fieldErrorClassName = "text-xs font-medium text-destructive";
459
837
  function getTextareaStyle(size, style) {
@@ -464,100 +842,99 @@ function getTextareaStyle(size, style) {
464
842
  "--textarea-padding-y": `${token.paddingY}px`,
465
843
  "--textarea-font-size": `${token.fontSize}px`,
466
844
  "--textarea-radius": `${token.radius}px`,
467
- "--textarea-focus-ring-width": `${textareaTokens.focusRingWidth}px`,
468
- borderRadius: "var(--textarea-radius)",
469
- outline: "none",
470
- ...style
471
- };
472
- }
473
- function getTextareaFieldStyle() {
474
- return {
475
- "--textarea-field-gap": `${textareaTokens.fieldGap}px`,
476
- "--textarea-label-size": `${textareaTokens.labelSize}px`
477
- };
478
- }
479
- function joinDescriptionIds(...ids) {
480
- return ids.filter(Boolean).join(" ") || void 0;
481
- }
482
- function isInvalid(error, ariaInvalid) {
483
- return error === true || typeof error === "string" || ariaInvalid === true || ariaInvalid === "true";
484
- }
485
- var Textarea = React6.forwardRef(
486
- ({
487
- className,
488
- style,
489
- size = "md",
490
- label,
491
- description,
492
- error,
493
- fieldClassName,
494
- id,
495
- onChange,
496
- onValueChange,
497
- "aria-describedby": ariaDescribedBy,
498
- "aria-invalid": ariaInvalid,
499
- ...props
500
- }, ref) => {
501
- const baseId = React6.useId();
502
- const textareaId = id ?? (label || description || typeof error === "string" ? baseId : void 0);
503
- const resolvedInvalid = isInvalid(error, ariaInvalid);
504
- const descriptionId = description && textareaId ? `${textareaId}-description` : void 0;
505
- const errorId = typeof error === "string" && textareaId ? `${textareaId}-error` : void 0;
506
- const describedBy = joinDescriptionIds(ariaDescribedBy, descriptionId, errorId);
507
- const handleChange = React6.useCallback(
508
- (event) => {
509
- onValueChange?.(event.currentTarget.value);
510
- onChange?.(event);
511
- },
512
- [onChange, onValueChange]
513
- );
514
- const control = /* @__PURE__ */ jsx(
515
- "textarea",
516
- {
517
- className: cn(
518
- "flex min-h-[var(--textarea-min-height)] w-full resize-y rounded-[var(--textarea-radius)] border border-input bg-background",
519
- "px-[var(--textarea-padding-x)] py-[var(--textarea-padding-y)] text-[length:var(--textarea-font-size)] text-foreground shadow-[var(--shadow-xs)]",
520
- "transition-[background-color,border-color,box-shadow,color] duration-micro ease-out placeholder:text-muted-foreground",
521
- "outline-none focus:border-ring focus:ring-[length:var(--textarea-focus-ring-width)] focus:ring-ring/30",
522
- "disabled:cursor-not-allowed disabled:opacity-50 read-only:cursor-default read-only:bg-muted/70",
523
- "aria-invalid:border-destructive/60 aria-invalid:focus:border-destructive aria-invalid:focus:ring-destructive/20",
524
- className
525
- ),
526
- ref,
527
- id: textareaId,
528
- "aria-invalid": resolvedInvalid || void 0,
529
- "aria-describedby": describedBy,
530
- style: getTextareaStyle(size, style),
531
- onChange: handleChange,
532
- ...props
533
- }
534
- );
535
- if (!label && !description && typeof error !== "string" && !fieldClassName) {
536
- return control;
845
+ "--textarea-focus-ring-width": `${textareaTokens.focusRingWidth}px`,
846
+ borderRadius: "var(--textarea-radius)",
847
+ outline: "none",
848
+ ...asPlainStyle(style)
849
+ };
850
+ }
851
+ function getTextareaFieldStyle() {
852
+ return {
853
+ "--textarea-field-gap": `${textareaTokens.fieldGap}px`,
854
+ "--textarea-label-size": `${textareaTokens.labelSize}px`
855
+ };
856
+ }
857
+ function joinDescriptionIds(...ids) {
858
+ return ids.filter(Boolean).join(" ") || void 0;
859
+ }
860
+ function isInvalid(error, ariaInvalid) {
861
+ return error === true || typeof error === "string" || ariaInvalid === true || ariaInvalid === "true";
862
+ }
863
+ var Textarea = ({
864
+ className,
865
+ style,
866
+ size = "md",
867
+ label,
868
+ description,
869
+ error,
870
+ fieldClassName,
871
+ id,
872
+ onChange,
873
+ onValueChange,
874
+ "aria-describedby": ariaDescribedBy,
875
+ "aria-invalid": ariaInvalid,
876
+ ref,
877
+ ...props
878
+ }) => {
879
+ const baseId = React5.useId();
880
+ const textareaId = id ?? (label || description || typeof error === "string" ? baseId : void 0);
881
+ const resolvedInvalid = isInvalid(error, ariaInvalid);
882
+ const descriptionId = description && textareaId ? `${textareaId}-description` : void 0;
883
+ const errorId = typeof error === "string" && textareaId ? `${textareaId}-error` : void 0;
884
+ const describedBy = joinDescriptionIds(ariaDescribedBy, descriptionId, errorId);
885
+ const handleChange = React5.useCallback(
886
+ (event) => {
887
+ onValueChange?.(event.currentTarget.value);
888
+ onChange?.(event);
889
+ },
890
+ [onChange, onValueChange]
891
+ );
892
+ const control = /* @__PURE__ */ jsx(
893
+ "textarea",
894
+ {
895
+ className: cn(
896
+ "flex min-h-[var(--textarea-min-height)] w-full resize-y rounded-[var(--textarea-radius)] border border-input bg-background",
897
+ "px-[var(--textarea-padding-x)] py-[var(--textarea-padding-y)] text-[length:var(--textarea-font-size)] text-foreground shadow-xs",
898
+ "transition-[background-color,border-color,box-shadow,color] duration-micro ease-out placeholder:text-muted-foreground",
899
+ formControlFocusClassNames.textarea,
900
+ "disabled:cursor-not-allowed disabled:opacity-50 read-only:cursor-default read-only:bg-muted/70",
901
+ formControlInvalidClassNames.textarea,
902
+ className
903
+ ),
904
+ ref,
905
+ id: textareaId,
906
+ "aria-invalid": resolvedInvalid || void 0,
907
+ "aria-describedby": describedBy,
908
+ style: getTextareaStyle(size, style),
909
+ onChange: handleChange,
910
+ ...props
537
911
  }
538
- return /* @__PURE__ */ jsxs(
539
- "div",
540
- {
541
- "data-slot": "textarea-field",
542
- className: cn("grid gap-[var(--textarea-field-gap)]", fieldClassName),
543
- style: getTextareaFieldStyle(),
544
- children: [
545
- label && /* @__PURE__ */ jsx(
546
- "label",
547
- {
548
- htmlFor: textareaId,
549
- className: "text-[length:var(--textarea-label-size)] font-medium text-foreground",
550
- children: label
551
- }
552
- ),
553
- control,
554
- description && !error && /* @__PURE__ */ jsx("p", { id: descriptionId, className: fieldMessageClassName, children: description }),
555
- typeof error === "string" && /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: fieldErrorClassName, children: error })
556
- ]
557
- }
558
- );
912
+ );
913
+ if (!label && !description && typeof error !== "string" && !fieldClassName) {
914
+ return control;
559
915
  }
560
- );
916
+ return /* @__PURE__ */ jsxs(
917
+ "div",
918
+ {
919
+ "data-slot": "textarea-field",
920
+ className: cn("grid gap-[var(--textarea-field-gap)]", fieldClassName),
921
+ style: getTextareaFieldStyle(),
922
+ children: [
923
+ label && /* @__PURE__ */ jsx(
924
+ "label",
925
+ {
926
+ htmlFor: textareaId,
927
+ className: "text-[length:var(--textarea-label-size)] font-medium text-foreground",
928
+ children: label
929
+ }
930
+ ),
931
+ control,
932
+ description && !error && /* @__PURE__ */ jsx("p", { id: descriptionId, className: fieldMessageClassName, children: description }),
933
+ typeof error === "string" && /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: fieldErrorClassName, children: error })
934
+ ]
935
+ }
936
+ );
937
+ };
561
938
  Textarea.displayName = "Textarea";
562
939
  var DEFAULT_TOOLTIP_DELAY_MS = 150;
563
940
  var TooltipProvider = ({
@@ -574,8 +951,13 @@ var Tooltip = ({
574
951
  const delay = delayDuration ?? DEFAULT_TOOLTIP_DELAY_MS;
575
952
  return /* @__PURE__ */ jsx(Tooltip$1.Provider, { delay, children: /* @__PURE__ */ jsx(Tooltip$1.Root, { ...props, children }) });
576
953
  };
577
- var TooltipTrigger = React6.forwardRef(({ asChild, children, ...props }, ref) => {
578
- if (asChild && React6.isValidElement(children)) {
954
+ var TooltipTrigger = ({
955
+ asChild,
956
+ children,
957
+ ref,
958
+ ...props
959
+ }) => {
960
+ if (asChild && React5.isValidElement(children)) {
579
961
  return /* @__PURE__ */ jsx(
580
962
  Tooltip$1.Trigger,
581
963
  {
@@ -586,183 +968,147 @@ var TooltipTrigger = React6.forwardRef(({ asChild, children, ...props }, ref) =>
586
968
  );
587
969
  }
588
970
  return /* @__PURE__ */ jsx(Tooltip$1.Trigger, { ref, ...props, children });
589
- });
971
+ };
590
972
  TooltipTrigger.displayName = "TooltipTrigger";
591
- var TooltipContent = React6.forwardRef(
592
- ({ className, side = "top", align = "center", sideOffset = 4, alignOffset = 0, ...props }, ref) => /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsx(
593
- Tooltip$1.Positioner,
594
- {
595
- side,
596
- align,
597
- sideOffset,
598
- alignOffset,
599
- className: "z-50",
600
- children: /* @__PURE__ */ jsx(
601
- Tooltip$1.Popup,
602
- {
603
- ref,
604
- className: cn(
605
- "z-50 overflow-hidden rounded-xl border bg-background/90 backdrop-blur-md px-3 py-1.5 text-sm text-popover-foreground shadow-xl transition-[opacity,transform,display] duration-200 data-starting-style:animate-in data-starting-style:fade-in-0 data-starting-style:zoom-in-95 data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
606
- className
607
- ),
608
- ...props
609
- }
610
- )
611
- }
612
- ) })
613
- );
973
+ var TooltipContent = ({
974
+ className,
975
+ style,
976
+ side = "top",
977
+ align = "center",
978
+ sideOffset = 4,
979
+ alignOffset = 0,
980
+ ref,
981
+ ...props
982
+ }) => /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsx(
983
+ Tooltip$1.Positioner,
984
+ {
985
+ side,
986
+ align,
987
+ sideOffset,
988
+ alignOffset,
989
+ style: { zIndex: overlayZIndex.tooltip },
990
+ children: /* @__PURE__ */ jsx(
991
+ Tooltip$1.Popup,
992
+ {
993
+ ref,
994
+ className: cn(overlayClassNames.tooltipSurface, className),
995
+ style: { zIndex: overlayZIndex.tooltip, ...style },
996
+ ...props
997
+ }
998
+ )
999
+ }
1000
+ ) });
614
1001
  TooltipContent.displayName = "TooltipContent";
615
- var cn2 = (...classes) => classes.filter(Boolean).join(" ");
1002
+ var MAX_VISUALIZER_BARS = 64;
1003
+ var VISUALIZER_BAR_STYLES = Array.from({ length: MAX_VISUALIZER_BARS }, (_, index) => ({
1004
+ id: `voice-bar-${index}`,
1005
+ height: `${15 + index * 37 % 86}%`,
1006
+ animationDelay: `${index * 0.05}s`,
1007
+ animationDuration: `${0.5 + index * 17 % 6 / 10}s`
1008
+ }));
1009
+ var formatRecordingTime = (seconds) => {
1010
+ const mins = Math.floor(seconds / 60);
1011
+ const secs = seconds % 60;
1012
+ return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
1013
+ };
616
1014
  var VoiceRecorder = ({
617
1015
  isRecording,
618
- onStartRecording,
619
- onStopRecording,
1016
+ time,
620
1017
  visualizerBars = 32
621
1018
  }) => {
622
- const [time, setTime] = React6__default.useState(0);
623
- const timerRef = React6__default.useRef(null);
624
- React6__default.useEffect(() => {
625
- if (isRecording) {
626
- onStartRecording();
627
- timerRef.current = setInterval(() => setTime((t) => t + 1), 1e3);
628
- } else {
629
- if (timerRef.current) {
630
- clearInterval(timerRef.current);
631
- timerRef.current = null;
632
- }
633
- onStopRecording(time);
634
- setTime(0);
635
- }
636
- return () => {
637
- if (timerRef.current) clearInterval(timerRef.current);
638
- };
639
- }, [isRecording, time, onStartRecording, onStopRecording]);
640
- const formatTime = (seconds) => {
641
- const mins = Math.floor(seconds / 60);
642
- const secs = seconds % 60;
643
- return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
644
- };
1019
+ const barStyles = VISUALIZER_BAR_STYLES.slice(
1020
+ 0,
1021
+ Math.min(visualizerBars, VISUALIZER_BAR_STYLES.length)
1022
+ );
645
1023
  return /* @__PURE__ */ jsxs(
646
1024
  "div",
647
1025
  {
648
- className: cn2(
649
- "flex flex-col items-center justify-center w-full transition-all duration-300 py-3",
1026
+ className: cn(
1027
+ "flex flex-col items-center justify-center w-full transition-[background-color,border-color,box-shadow,color,opacity,transform] duration-300 py-3",
650
1028
  isRecording ? "opacity-100" : "opacity-0 h-0"
651
1029
  ),
652
1030
  children: [
653
1031
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
654
1032
  /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-red-500 animate-pulse" }),
655
- /* @__PURE__ */ jsx("span", { className: "font-mono text-sm text-white/80", children: formatTime(time) })
1033
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-sm text-white/80", children: formatRecordingTime(time) })
656
1034
  ] }),
657
- /* @__PURE__ */ jsx("div", { className: "w-full h-10 flex items-center justify-center gap-0.5 px-4", children: [...Array(visualizerBars)].map((_, i) => /* @__PURE__ */ jsx(
1035
+ /* @__PURE__ */ jsx("div", { className: "w-full h-10 flex items-center justify-center gap-0.5 px-4", children: barStyles.map((bar) => /* @__PURE__ */ jsx(
658
1036
  "div",
659
1037
  {
660
1038
  className: "w-0.5 rounded-full bg-white/50 animate-pulse",
661
1039
  style: {
662
- height: `${Math.max(15, Math.random() * 100)}%`,
663
- animationDelay: `${i * 0.05}s`,
664
- animationDuration: `${0.5 + Math.random() * 0.5}s`
1040
+ height: bar.height,
1041
+ animationDelay: bar.animationDelay,
1042
+ animationDuration: bar.animationDuration
665
1043
  }
666
1044
  },
667
- i
1045
+ bar.id
668
1046
  )) })
669
1047
  ]
670
1048
  }
671
1049
  );
672
1050
  };
673
- var ImageViewDialog = ({ imageUrl, onClose }) => {
674
- if (!imageUrl) return null;
675
- return /* @__PURE__ */ jsx(Dialog, { open: !!imageUrl, onOpenChange: onClose, children: /* @__PURE__ */ jsxs(DialogContent, { className: "p-0 border-none bg-transparent shadow-none max-w-[90vw] md:max-w-[800px]", children: [
676
- /* @__PURE__ */ jsx(DialogTitle, { className: "sr-only", children: "Image Preview" }),
677
- /* @__PURE__ */ jsx(
678
- motion.div,
679
- {
680
- initial: { opacity: 0, scale: 0.95 },
681
- animate: { opacity: 1, scale: 1 },
682
- exit: { opacity: 0, scale: 0.95 },
683
- transition: { duration: 0.2, ease: "easeOut" },
684
- className: "relative bg-background rounded-2xl overflow-hidden shadow-2xl",
685
- children: /* @__PURE__ */ jsx(
686
- Image,
687
- {
688
- src: imageUrl,
689
- alt: "Full preview",
690
- className: "w-full max-h-[80vh] object-contain rounded-2xl",
691
- fill: true
692
- }
693
- )
694
- }
695
- )
696
- ] }) });
1051
+ VoiceRecorder.displayName = "VoiceRecorder";
1052
+ var noopSetPromptValue = (_value) => {
697
1053
  };
698
- var PromptInputContext = React6__default.createContext({
699
- isLoading: false,
700
- value: "",
701
- setValue: () => {
702
- },
703
- maxHeight: 240,
704
- onSubmit: void 0,
705
- disabled: false
706
- });
1054
+ var PromptInputIsLoadingContext = React5__default.createContext(false);
1055
+ var PromptInputValueContext = React5__default.createContext("");
1056
+ var PromptInputSetValueContext = React5__default.createContext(noopSetPromptValue);
1057
+ var PromptInputMaxHeightContext = React5__default.createContext(240);
1058
+ var PromptInputSubmitContext = React5__default.createContext(void 0);
1059
+ var PromptInputDisabledContext = React5__default.createContext(false);
707
1060
  function usePromptInput() {
708
- const context = React6__default.useContext(PromptInputContext);
709
- if (!context) throw new Error("usePromptInput must be used within a PromptInput");
710
- return context;
1061
+ return {
1062
+ isLoading: React5__default.use(PromptInputIsLoadingContext),
1063
+ value: React5__default.use(PromptInputValueContext),
1064
+ setValue: React5__default.use(PromptInputSetValueContext),
1065
+ maxHeight: React5__default.use(PromptInputMaxHeightContext),
1066
+ onSubmit: React5__default.use(PromptInputSubmitContext),
1067
+ disabled: React5__default.use(PromptInputDisabledContext)
1068
+ };
711
1069
  }
712
- var PromptInput = React6__default.forwardRef(
713
- ({
714
- className,
715
- isLoading = false,
716
- maxHeight = 240,
717
- value,
718
- onValueChange,
719
- onSubmit,
720
- children,
721
- disabled = false,
722
- onDragOver,
723
- onDragLeave,
724
- onDrop
725
- }, ref) => {
726
- const [internalValue, setInternalValue] = React6__default.useState(value || "");
727
- const handleChange = (newValue) => {
728
- setInternalValue(newValue);
729
- onValueChange?.(newValue);
730
- };
731
- return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(
732
- PromptInputContext.Provider,
733
- {
734
- value: {
735
- isLoading,
736
- value: value ?? internalValue,
737
- setValue: onValueChange ?? handleChange,
738
- maxHeight,
739
- onSubmit,
740
- disabled
741
- },
742
- children: /* @__PURE__ */ jsx(
743
- "div",
744
- {
745
- ref,
746
- className: cn2(
747
- "rounded-3xl border border-input bg-background p-2 shadow-lg transition-all duration-300",
748
- isLoading && "border-destructive/70",
749
- className
750
- ),
751
- onDragOver,
752
- onDragLeave,
753
- onDrop,
754
- children
755
- }
756
- )
757
- }
758
- ) });
759
- }
760
- );
1070
+ var PromptInput = ({
1071
+ className,
1072
+ isLoading = false,
1073
+ maxHeight = 240,
1074
+ value,
1075
+ onValueChange,
1076
+ onSubmit,
1077
+ children,
1078
+ disabled = false,
1079
+ onDragOver,
1080
+ onDragLeave,
1081
+ onDrop,
1082
+ ref
1083
+ }) => {
1084
+ const [internalValue, setInternalValue] = React5__default.useState(value || "");
1085
+ const handleChange = (newValue) => {
1086
+ setInternalValue(newValue);
1087
+ onValueChange?.(newValue);
1088
+ };
1089
+ const promptValue = value ?? internalValue;
1090
+ const setPromptValue = onValueChange ?? handleChange;
1091
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(PromptInputIsLoadingContext.Provider, { value: isLoading, children: /* @__PURE__ */ jsx(PromptInputValueContext.Provider, { value: promptValue, children: /* @__PURE__ */ jsx(PromptInputSetValueContext.Provider, { value: setPromptValue, children: /* @__PURE__ */ jsx(PromptInputMaxHeightContext.Provider, { value: maxHeight, children: /* @__PURE__ */ jsx(PromptInputSubmitContext.Provider, { value: onSubmit, children: /* @__PURE__ */ jsx(PromptInputDisabledContext.Provider, { value: disabled, children: /* @__PURE__ */ jsx(
1092
+ "div",
1093
+ {
1094
+ ref,
1095
+ className: cn(
1096
+ "rounded-[var(--radius-3xl)] border border-input bg-background p-2 shadow-lg transition-[background-color,border-color,box-shadow,color,opacity,transform] duration-300",
1097
+ isLoading && "border-destructive/70",
1098
+ className
1099
+ ),
1100
+ onDragOver,
1101
+ onDragLeave,
1102
+ onDrop,
1103
+ children
1104
+ }
1105
+ ) }) }) }) }) }) }) });
1106
+ };
761
1107
  PromptInput.displayName = "PromptInput";
762
1108
  var PromptInputTextarea = ({ className, onKeyDown, disableAutosize = false, placeholder, style, ...props }) => {
763
1109
  const { value, setValue, maxHeight, onSubmit, disabled } = usePromptInput();
764
- const textareaRef = React6__default.useRef(null);
765
- React6__default.useEffect(() => {
1110
+ const textareaRef = React5__default.useRef(null);
1111
+ React5__default.useEffect(() => {
766
1112
  if (disableAutosize || !textareaRef.current) return;
767
1113
  textareaRef.current.style.height = "auto";
768
1114
  textareaRef.current.style.height = typeof maxHeight === "number" ? `${Math.min(textareaRef.current.scrollHeight, maxHeight)}px` : `min(${textareaRef.current.scrollHeight}px, ${maxHeight})`;
@@ -783,7 +1129,7 @@ var PromptInputTextarea = ({ className, onKeyDown, disableAutosize = false, plac
783
1129
  onKeyDown: handleKeyDown,
784
1130
  rows: 1,
785
1131
  style: { "--textarea-min-height": "2rem", ...style },
786
- className: cn2("resize-none border-0 shadow-none focus-visible:ring-0", className),
1132
+ className: cn("resize-none border-0 shadow-none", className),
787
1133
  disabled,
788
1134
  placeholder,
789
1135
  ...props
@@ -794,7 +1140,7 @@ var PromptInputActions = ({
794
1140
  children,
795
1141
  className,
796
1142
  ...props
797
- }) => /* @__PURE__ */ jsx("div", { className: cn2("flex items-center gap-2", className), ...props, children });
1143
+ }) => /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-2", className), ...props, children });
798
1144
  var PromptInputAction = ({
799
1145
  tooltip,
800
1146
  children,
@@ -817,416 +1163,445 @@ var CustomDivider = () => /* @__PURE__ */ jsx("div", { className: "relative h-6
817
1163
  }
818
1164
  }
819
1165
  ) });
820
- var PromptInputBox = React6__default.forwardRef(
821
- (props, ref) => {
822
- const {
823
- onSend = () => {
824
- },
825
- isLoading = false,
826
- placeholder = "Type your message here...",
827
- className
828
- } = props;
829
- const [input, setInput] = React6__default.useState("");
830
- const [files, setFiles] = React6__default.useState([]);
831
- const [filePreviews, setFilePreviews] = React6__default.useState({});
832
- const [selectedImage, setSelectedImage] = React6__default.useState(null);
833
- const [isRecording, setIsRecording] = React6__default.useState(false);
834
- const [showSearch, setShowSearch] = React6__default.useState(false);
835
- const [showThink, setShowThink] = React6__default.useState(false);
836
- const [showCanvas, setShowCanvas] = React6__default.useState(false);
837
- const uploadInputRef = React6__default.useRef(null);
838
- const promptBoxRef = React6__default.useRef(null);
839
- const handleToggleChange = (value) => {
840
- if (value === "search") {
841
- setShowSearch((prev) => !prev);
842
- setShowThink(false);
843
- } else if (value === "think") {
844
- setShowThink((prev) => !prev);
845
- setShowSearch(false);
1166
+ var promptModeToneClassName = {
1167
+ search: {
1168
+ active: "bg-info/15 border-info text-info",
1169
+ icon: "text-info",
1170
+ label: "text-info"
1171
+ },
1172
+ think: {
1173
+ active: "bg-indigo-500/15 border-indigo-500 text-indigo-500",
1174
+ icon: "text-indigo-500",
1175
+ label: "text-indigo-500"
1176
+ },
1177
+ canvas: {
1178
+ active: "bg-warning/15 border-warning text-warning",
1179
+ icon: "text-warning",
1180
+ label: "text-warning"
1181
+ }
1182
+ };
1183
+ function PromptModeIcon({ tone, active }) {
1184
+ const className = cn("w-4 h-4", active ? promptModeToneClassName[tone].icon : "text-inherit");
1185
+ if (tone === "search") return /* @__PURE__ */ jsx(Globe, { className });
1186
+ if (tone === "think") return /* @__PURE__ */ jsx(Brain, { className });
1187
+ return /* @__PURE__ */ jsx(FolderClosed, { className });
1188
+ }
1189
+ function PromptModeButton({
1190
+ active,
1191
+ label,
1192
+ tone,
1193
+ onClick
1194
+ }) {
1195
+ const toneClassName = promptModeToneClassName[tone];
1196
+ const shouldReduceMotion = useReducedMotion();
1197
+ return /* @__PURE__ */ jsxs(
1198
+ "button",
1199
+ {
1200
+ type: "button",
1201
+ onClick,
1202
+ className: cn(
1203
+ "rounded-full transition-[background-color,border-color,box-shadow,color,opacity,transform] flex items-center gap-1 px-2 py-1 border h-8",
1204
+ active ? toneClassName.active : "bg-transparent border-transparent text-muted-foreground hover:text-foreground"
1205
+ ),
1206
+ children: [
1207
+ /* @__PURE__ */ jsx("div", { className: "w-5 h-5 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx(
1208
+ m.div,
1209
+ {
1210
+ animate: shouldReduceMotion ? { opacity: active ? 1 : 0.82 } : { rotate: active ? 360 : 0, scale: active ? 1.1 : 1 },
1211
+ whileHover: shouldReduceMotion ? { opacity: 1 } : {
1212
+ rotate: active ? 360 : 15,
1213
+ scale: 1.1,
1214
+ transition: { type: "spring", stiffness: 300, damping: 10 }
1215
+ },
1216
+ transition: shouldReduceMotion ? { duration: 0 } : { type: "spring", stiffness: 260, damping: 25 },
1217
+ children: /* @__PURE__ */ jsx(PromptModeIcon, { tone, active })
1218
+ }
1219
+ ) }),
1220
+ /* @__PURE__ */ jsx(AnimatePresence, { children: active && /* @__PURE__ */ jsx(
1221
+ m.span,
1222
+ {
1223
+ initial: shouldReduceMotion ? { opacity: 1 } : { width: 0, opacity: 0 },
1224
+ animate: { width: "auto", opacity: 1 },
1225
+ exit: shouldReduceMotion ? { opacity: 0 } : { width: 0, opacity: 0 },
1226
+ transition: shouldReduceMotion ? { duration: 0 } : { duration: 0.2 },
1227
+ className: cn(
1228
+ "text-xs overflow-hidden whitespace-nowrap flex-shrink-0",
1229
+ toneClassName.label
1230
+ ),
1231
+ children: label
1232
+ }
1233
+ ) })
1234
+ ]
1235
+ }
1236
+ );
1237
+ }
1238
+ function PromptModeActions({
1239
+ uploadInputRef,
1240
+ isRecording,
1241
+ modes,
1242
+ onToggleMode,
1243
+ onToggleCanvas,
1244
+ onProcessFile
1245
+ }) {
1246
+ return /* @__PURE__ */ jsxs(
1247
+ "div",
1248
+ {
1249
+ className: cn(
1250
+ "flex items-center gap-1 transition-opacity duration-300",
1251
+ isRecording ? "opacity-0 invisible h-0" : "opacity-100 visible"
1252
+ ),
1253
+ children: [
1254
+ /* @__PURE__ */ jsx(PromptInputAction, { tooltip: "Upload image", children: /* @__PURE__ */ jsx(
1255
+ "button",
1256
+ {
1257
+ type: "button",
1258
+ "aria-label": "Upload image",
1259
+ onClick: () => uploadInputRef.current?.click(),
1260
+ className: "flex h-8 w-8 text-muted-foreground cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-accent hover:text-foreground",
1261
+ disabled: isRecording,
1262
+ children: /* @__PURE__ */ jsx(Paperclip, { className: "h-5 w-5 transition-colors" })
1263
+ }
1264
+ ) }),
1265
+ /* @__PURE__ */ jsx(
1266
+ "input",
1267
+ {
1268
+ ref: uploadInputRef,
1269
+ type: "file",
1270
+ className: "hidden",
1271
+ onChange: (event) => {
1272
+ const file = event.target.files?.[0];
1273
+ if (file) onProcessFile(file);
1274
+ if (event.target) event.target.value = "";
1275
+ },
1276
+ accept: "image/*",
1277
+ "aria-label": "Upload image"
1278
+ }
1279
+ ),
1280
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
1281
+ /* @__PURE__ */ jsx(
1282
+ PromptModeButton,
1283
+ {
1284
+ active: modes.search,
1285
+ label: "Search",
1286
+ tone: "search",
1287
+ onClick: () => onToggleMode("search")
1288
+ }
1289
+ ),
1290
+ /* @__PURE__ */ jsx(CustomDivider, {}),
1291
+ /* @__PURE__ */ jsx(
1292
+ PromptModeButton,
1293
+ {
1294
+ active: modes.think,
1295
+ label: "Think",
1296
+ tone: "think",
1297
+ onClick: () => onToggleMode("think")
1298
+ }
1299
+ ),
1300
+ /* @__PURE__ */ jsx(CustomDivider, {}),
1301
+ /* @__PURE__ */ jsx(
1302
+ PromptModeButton,
1303
+ {
1304
+ active: modes.canvas,
1305
+ label: "Canvas",
1306
+ tone: "canvas",
1307
+ onClick: onToggleCanvas
1308
+ }
1309
+ )
1310
+ ] })
1311
+ ]
1312
+ }
1313
+ );
1314
+ }
1315
+ function PromptSubmitAction({
1316
+ isLoading,
1317
+ isRecording,
1318
+ hasContent,
1319
+ onActivate
1320
+ }) {
1321
+ const shouldReduceMotion = useReducedMotion();
1322
+ return /* @__PURE__ */ jsx(
1323
+ PromptInputAction,
1324
+ {
1325
+ tooltip: isLoading ? "Stop generation" : isRecording ? "Stop recording" : hasContent ? "Send message" : "Voice message",
1326
+ children: /* @__PURE__ */ jsx(
1327
+ Button,
1328
+ {
1329
+ variant: "default",
1330
+ size: "icon",
1331
+ className: cn(
1332
+ "h-8 w-8 rounded-full transition-[background-color,border-color,box-shadow,color,opacity,transform] duration-micro",
1333
+ isRecording ? "bg-transparent hover:bg-accent text-destructive hover:text-destructive/80" : hasContent ? "bg-primary hover:bg-primary/90 text-primary-foreground" : "bg-transparent hover:bg-accent text-muted-foreground hover:text-foreground"
1334
+ ),
1335
+ onClick: onActivate,
1336
+ disabled: isLoading && !hasContent,
1337
+ children: isLoading ? /* @__PURE__ */ jsx(
1338
+ StopFill,
1339
+ {
1340
+ className: cn(
1341
+ "h-4 w-4 fill-primary-foreground",
1342
+ shouldReduceMotion ? "opacity-100" : "animate-pulse"
1343
+ )
1344
+ }
1345
+ ) : isRecording ? /* @__PURE__ */ jsx(StopCircle, { className: "h-5 w-5 text-destructive" }) : hasContent ? /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4 text-primary-foreground" }) : /* @__PURE__ */ jsx(Microphone, { className: "h-5 w-5 text-primary-foreground transition-colors" })
1346
+ }
1347
+ )
1348
+ }
1349
+ );
1350
+ }
1351
+ var promptInputBoxInitialState = {
1352
+ input: "",
1353
+ files: [],
1354
+ filePreviews: {},
1355
+ selectedImage: null,
1356
+ isRecording: false,
1357
+ recordingTime: 0,
1358
+ showSearch: false,
1359
+ showThink: false,
1360
+ showCanvas: false
1361
+ };
1362
+ function promptInputBoxReducer(state, action) {
1363
+ switch (action.type) {
1364
+ case "set-input":
1365
+ return { ...state, input: action.value };
1366
+ case "set-active-file":
1367
+ return { ...state, files: [action.file], filePreviews: {} };
1368
+ case "set-file-preview":
1369
+ return { ...state, filePreviews: { [action.previewKey]: action.preview } };
1370
+ case "remove-file": {
1371
+ const nextPreviews = { ...state.filePreviews };
1372
+ delete nextPreviews[action.previewKey];
1373
+ return {
1374
+ ...state,
1375
+ filePreviews: nextPreviews,
1376
+ files: state.files.filter((file) => getFilePreviewKey(file) !== action.previewKey)
1377
+ };
1378
+ }
1379
+ case "set-selected-image":
1380
+ return { ...state, selectedImage: action.imageUrl };
1381
+ case "toggle-mode":
1382
+ if (action.mode === "search") {
1383
+ return { ...state, showSearch: !state.showSearch, showThink: false };
1384
+ }
1385
+ if (action.mode === "think") {
1386
+ return { ...state, showSearch: false, showThink: !state.showThink };
1387
+ }
1388
+ return { ...state, showCanvas: !state.showCanvas };
1389
+ case "start-recording":
1390
+ return { ...state, recordingTime: 0, isRecording: true };
1391
+ case "tick-recording":
1392
+ return { ...state, recordingTime: state.recordingTime + 1 };
1393
+ case "stop-recording":
1394
+ return { ...state, isRecording: false, recordingTime: 0 };
1395
+ case "reset-composer":
1396
+ return { ...state, input: "", files: [], filePreviews: {} };
1397
+ }
1398
+ }
1399
+ var PromptInputBox = (props) => {
1400
+ const {
1401
+ onSend = () => {
1402
+ },
1403
+ isLoading = false,
1404
+ placeholder = "Type your message here...",
1405
+ className,
1406
+ ref
1407
+ } = props;
1408
+ const [state, dispatch] = React5__default.useReducer(promptInputBoxReducer, promptInputBoxInitialState);
1409
+ const {
1410
+ input,
1411
+ files,
1412
+ filePreviews,
1413
+ selectedImage,
1414
+ isRecording,
1415
+ recordingTime,
1416
+ showSearch,
1417
+ showThink,
1418
+ showCanvas
1419
+ } = state;
1420
+ const uploadInputRef = React5__default.useRef(null);
1421
+ const promptBoxRef = React5__default.useRef(null);
1422
+ const handleToggleChange = (mode) => dispatch({ type: "toggle-mode", mode });
1423
+ const handleCanvasToggle = () => dispatch({ type: "toggle-mode", mode: "canvas" });
1424
+ const processFile = (file) => {
1425
+ if (!isImageFile(file)) {
1426
+ return;
1427
+ }
1428
+ if (file.size > 10 * 1024 * 1024) {
1429
+ return;
1430
+ }
1431
+ const previewKey = getFilePreviewKey(file);
1432
+ dispatch({ type: "set-active-file", file });
1433
+ const reader = new FileReader();
1434
+ reader.onload = (e) => {
1435
+ const preview = e.target?.result;
1436
+ if (typeof preview === "string") {
1437
+ dispatch({ type: "set-file-preview", previewKey, preview });
846
1438
  }
847
1439
  };
848
- const handleCanvasToggle = () => setShowCanvas((prev) => !prev);
849
- const isImageFile = React6__default.useCallback((file) => file.type.startsWith("image/"), []);
850
- const processFile = React6__default.useCallback(
851
- (file) => {
852
- if (!isImageFile(file)) {
853
- return;
854
- }
855
- if (file.size > 10 * 1024 * 1024) {
856
- return;
1440
+ reader.readAsDataURL(file);
1441
+ };
1442
+ const handleDrop = (e) => {
1443
+ preventDragDefaults(e);
1444
+ const droppedFiles = Array.from(e.dataTransfer.files);
1445
+ const firstFile = droppedFiles.find((file) => isImageFile(file));
1446
+ if (firstFile) processFile(firstFile);
1447
+ };
1448
+ const handleRemoveFile = (fileToRemove) => {
1449
+ const previewKey = getFilePreviewKey(fileToRemove);
1450
+ dispatch({ type: "remove-file", previewKey });
1451
+ };
1452
+ const openImageModal = (imageUrl) => dispatch({ type: "set-selected-image", imageUrl });
1453
+ const handlePasteEvent = useEffectEvent((e) => {
1454
+ const items = e.clipboardData?.items;
1455
+ if (!items) return;
1456
+ for (const item of Array.from(items)) {
1457
+ if (item.type.includes("image")) {
1458
+ const file = item.getAsFile();
1459
+ if (file) {
1460
+ e.preventDefault();
1461
+ processFile(file);
1462
+ break;
857
1463
  }
858
- setFiles([file]);
859
- const reader = new FileReader();
860
- reader.onload = (e) => setFilePreviews({ [file.name]: e.target?.result });
861
- reader.readAsDataURL(file);
862
- },
863
- [isImageFile]
864
- );
865
- const handleDragOver = React6__default.useCallback((e) => {
866
- e.preventDefault();
867
- e.stopPropagation();
868
- }, []);
869
- const handleDragLeave = React6__default.useCallback((e) => {
870
- e.preventDefault();
871
- e.stopPropagation();
872
- }, []);
873
- const handleDrop = React6__default.useCallback(
874
- (e) => {
875
- e.preventDefault();
876
- e.stopPropagation();
877
- const files2 = Array.from(e.dataTransfer.files);
878
- const imageFiles = files2.filter((file) => isImageFile(file));
879
- const firstFile = imageFiles[0];
880
- if (firstFile) processFile(firstFile);
881
- },
882
- [isImageFile, processFile]
883
- );
884
- const handleRemoveFile = (index) => {
885
- const fileToRemove = files[index];
886
- if (fileToRemove && filePreviews[fileToRemove.name]) {
887
- const newPreviews = { ...filePreviews };
888
- delete newPreviews[fileToRemove.name];
889
- setFilePreviews(newPreviews);
890
1464
  }
891
- setFiles([]);
892
- };
893
- const openImageModal = (imageUrl) => setSelectedImage(imageUrl);
894
- const handlePaste = React6__default.useCallback(
895
- (e) => {
896
- const items = e.clipboardData?.items;
897
- if (!items) return;
898
- for (let i = 0; i < items.length; i++) {
899
- const item = items[i];
900
- if (item && item.type.indexOf("image") !== -1) {
901
- const file = item.getAsFile();
902
- if (file) {
903
- e.preventDefault();
904
- processFile(file);
905
- break;
1465
+ }
1466
+ });
1467
+ React5__default.useEffect(() => {
1468
+ const handlePaste = (e) => handlePasteEvent(e);
1469
+ document.addEventListener("paste", handlePaste);
1470
+ return () => document.removeEventListener("paste", handlePaste);
1471
+ }, []);
1472
+ React5__default.useEffect(() => {
1473
+ if (!isRecording) return;
1474
+ const timer = window.setInterval(() => dispatch({ type: "tick-recording" }), 1e3);
1475
+ return () => window.clearInterval(timer);
1476
+ }, [isRecording]);
1477
+ const handleSubmit = () => {
1478
+ if (input.trim() || files.length > 0) {
1479
+ let messagePrefix = "";
1480
+ if (showSearch) messagePrefix = "[Search: ";
1481
+ else if (showThink) messagePrefix = "[Think: ";
1482
+ else if (showCanvas) messagePrefix = "[Canvas: ";
1483
+ const formattedInput = messagePrefix ? `${messagePrefix}${input}]` : input;
1484
+ onSend(formattedInput, files);
1485
+ dispatch({ type: "reset-composer" });
1486
+ }
1487
+ };
1488
+ const startRecording = () => dispatch({ type: "start-recording" });
1489
+ const stopRecording = () => {
1490
+ onSend(`[Voice message - ${recordingTime} seconds]`, []);
1491
+ dispatch({ type: "stop-recording" });
1492
+ };
1493
+ const hasContent = input.trim() !== "" || files.length > 0;
1494
+ return /* @__PURE__ */ jsxs(LazyMotion, { features: domAnimation, children: [
1495
+ /* @__PURE__ */ jsxs(
1496
+ PromptInput,
1497
+ {
1498
+ value: input,
1499
+ onValueChange: (value) => dispatch({ type: "set-input", value }),
1500
+ isLoading,
1501
+ onSubmit: handleSubmit,
1502
+ className: cn(
1503
+ "w-full bg-background border-input shadow-lg transition-[background-color,border-color,box-shadow,color,opacity,transform] duration-300 ease-in-out",
1504
+ isRecording && "border-destructive/70",
1505
+ className
1506
+ ),
1507
+ disabled: isLoading || isRecording,
1508
+ ref: ref || promptBoxRef,
1509
+ onDragOver: preventDragDefaults,
1510
+ onDragLeave: preventDragDefaults,
1511
+ onDrop: handleDrop,
1512
+ children: [
1513
+ /* @__PURE__ */ jsx(
1514
+ PromptAttachmentPreviews,
1515
+ {
1516
+ files,
1517
+ filePreviews,
1518
+ isRecording,
1519
+ onPreview: openImageModal,
1520
+ onRemove: handleRemoveFile
1521
+ }
1522
+ ),
1523
+ /* @__PURE__ */ jsx(
1524
+ "div",
1525
+ {
1526
+ className: cn(
1527
+ "transition-[background-color,border-color,box-shadow,color,opacity,transform] duration-300",
1528
+ isRecording ? "h-0 overflow-hidden opacity-0" : "opacity-100"
1529
+ ),
1530
+ children: /* @__PURE__ */ jsx(
1531
+ PromptInputTextarea,
1532
+ {
1533
+ placeholder: showSearch ? "Search the web..." : showThink ? "Think deeply..." : showCanvas ? "Create on canvas..." : placeholder,
1534
+ className: "text-base"
1535
+ }
1536
+ )
906
1537
  }
907
- }
908
- }
909
- },
910
- [processFile]
911
- );
912
- React6__default.useEffect(() => {
913
- document.addEventListener("paste", handlePaste);
914
- return () => document.removeEventListener("paste", handlePaste);
915
- }, [handlePaste]);
916
- const handleSubmit = () => {
917
- if (input.trim() || files.length > 0) {
918
- let messagePrefix = "";
919
- if (showSearch) messagePrefix = "[Search: ";
920
- else if (showThink) messagePrefix = "[Think: ";
921
- else if (showCanvas) messagePrefix = "[Canvas: ";
922
- const formattedInput = messagePrefix ? `${messagePrefix}${input}]` : input;
923
- onSend(formattedInput, files);
924
- setInput("");
925
- setFiles([]);
926
- setFilePreviews({});
927
- }
928
- };
929
- const handleStartRecording = () => {
930
- };
931
- const handleStopRecording = (duration) => {
932
- setIsRecording(false);
933
- onSend(`[Voice message - ${duration} seconds]`, []);
934
- };
935
- const hasContent = input.trim() !== "" || files.length > 0;
936
- return /* @__PURE__ */ jsxs(Fragment, { children: [
937
- /* @__PURE__ */ jsxs(
938
- PromptInput,
939
- {
940
- value: input,
941
- onValueChange: setInput,
942
- isLoading,
943
- onSubmit: handleSubmit,
944
- className: cn2(
945
- "w-full bg-background border-input shadow-lg transition-all duration-300 ease-in-out",
946
- isRecording && "border-destructive/70",
947
- className
948
1538
  ),
949
- disabled: isLoading || isRecording,
950
- ref: ref || promptBoxRef,
951
- onDragOver: handleDragOver,
952
- onDragLeave: handleDragLeave,
953
- onDrop: handleDrop,
954
- children: [
955
- files.length > 0 && !isRecording && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 p-0 pb-1 transition-all duration-300", children: files.map((file, index) => /* @__PURE__ */ jsx("div", { className: "relative group", children: file.type.startsWith("image/") && filePreviews[file.name] && // biome-ignore lint/a11y/useSemanticElements: ARIA pattern
956
- /* @__PURE__ */ jsxs(
957
- "div",
958
- {
959
- role: "button",
960
- tabIndex: 0,
961
- onKeyDown: (e) => {
962
- if (e.key === "Enter" || e.key === " ") {
963
- e.preventDefault();
964
- const preview = filePreviews[file.name];
965
- if (preview) openImageModal(preview);
966
- }
967
- },
968
- className: "w-16 h-16 rounded-xl overflow-hidden cursor-pointer transition-all duration-300",
969
- onClick: () => {
970
- const preview = filePreviews[file.name];
971
- if (preview) openImageModal(preview);
972
- },
973
- children: [
974
- /* @__PURE__ */ jsx(
975
- Image,
976
- {
977
- src: filePreviews[file.name] ?? "",
978
- alt: file.name,
979
- className: "h-full w-full object-cover",
980
- fill: true
981
- }
982
- ),
983
- /* @__PURE__ */ jsx(
984
- "button",
985
- {
986
- type: "button",
987
- onClick: (e) => {
988
- e.stopPropagation();
989
- handleRemoveFile(index);
990
- },
991
- className: "absolute top-1 right-1 rounded-full bg-black/70 p-0.5 opacity-100 transition-opacity",
992
- children: /* @__PURE__ */ jsx(Cross, { className: "h-3 w-3 text-white" })
993
- }
994
- )
995
- ]
996
- }
997
- ) }, index)) }),
1539
+ isRecording && /* @__PURE__ */ jsx(VoiceRecorder, { isRecording, time: recordingTime }),
1540
+ /* @__PURE__ */ jsxs(PromptInputActions, { className: "flex items-center justify-between gap-2 p-0 pt-2", children: [
998
1541
  /* @__PURE__ */ jsx(
999
- "div",
1542
+ PromptModeActions,
1000
1543
  {
1001
- className: cn2(
1002
- "transition-all duration-300",
1003
- isRecording ? "h-0 overflow-hidden opacity-0" : "opacity-100"
1004
- ),
1005
- children: /* @__PURE__ */ jsx(
1006
- PromptInputTextarea,
1007
- {
1008
- placeholder: showSearch ? "Search the web..." : showThink ? "Think deeply..." : showCanvas ? "Create on canvas..." : placeholder,
1009
- className: "text-base"
1010
- }
1011
- )
1544
+ uploadInputRef,
1545
+ isRecording,
1546
+ modes: { search: showSearch, think: showThink, canvas: showCanvas },
1547
+ onToggleMode: handleToggleChange,
1548
+ onToggleCanvas: handleCanvasToggle,
1549
+ onProcessFile: processFile
1012
1550
  }
1013
1551
  ),
1014
- isRecording && /* @__PURE__ */ jsx(
1015
- VoiceRecorder,
1552
+ /* @__PURE__ */ jsx(
1553
+ PromptSubmitAction,
1016
1554
  {
1555
+ isLoading,
1017
1556
  isRecording,
1018
- onStartRecording: handleStartRecording,
1019
- onStopRecording: handleStopRecording
1020
- }
1021
- ),
1022
- /* @__PURE__ */ jsxs(PromptInputActions, { className: "flex items-center justify-between gap-2 p-0 pt-2", children: [
1023
- /* @__PURE__ */ jsxs(
1024
- "div",
1025
- {
1026
- className: cn2(
1027
- "flex items-center gap-1 transition-opacity duration-300",
1028
- isRecording ? "opacity-0 invisible h-0" : "opacity-100 visible"
1029
- ),
1030
- children: [
1031
- /* @__PURE__ */ jsx(PromptInputAction, { tooltip: "Upload image", children: /* @__PURE__ */ jsxs(
1032
- "button",
1033
- {
1034
- type: "button",
1035
- onClick: () => uploadInputRef.current?.click(),
1036
- className: "flex h-8 w-8 text-muted-foreground cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-accent hover:text-foreground",
1037
- disabled: isRecording,
1038
- children: [
1039
- /* @__PURE__ */ jsx(Paperclip, { className: "h-5 w-5 transition-colors" }),
1040
- /* @__PURE__ */ jsx(
1041
- "input",
1042
- {
1043
- ref: uploadInputRef,
1044
- type: "file",
1045
- className: "hidden",
1046
- onChange: (e) => {
1047
- const file = e.target.files?.[0];
1048
- if (file) processFile(file);
1049
- if (e.target) e.target.value = "";
1050
- },
1051
- accept: "image/*"
1052
- }
1053
- )
1054
- ]
1055
- }
1056
- ) }),
1057
- /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
1058
- /* @__PURE__ */ jsxs(
1059
- "button",
1060
- {
1061
- type: "button",
1062
- onClick: () => handleToggleChange("search"),
1063
- className: cn2(
1064
- "rounded-full transition-all flex items-center gap-1 px-2 py-1 border h-8",
1065
- showSearch ? "bg-info/15 border-info text-info" : "bg-transparent border-transparent text-muted-foreground hover:text-foreground"
1066
- ),
1067
- children: [
1068
- /* @__PURE__ */ jsx("div", { className: "w-5 h-5 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx(
1069
- motion.div,
1070
- {
1071
- animate: { rotate: showSearch ? 360 : 0, scale: showSearch ? 1.1 : 1 },
1072
- whileHover: {
1073
- rotate: showSearch ? 360 : 15,
1074
- scale: 1.1,
1075
- transition: { type: "spring", stiffness: 300, damping: 10 }
1076
- },
1077
- transition: { type: "spring", stiffness: 260, damping: 25 },
1078
- children: /* @__PURE__ */ jsx(Globe, { className: cn2("w-4 h-4", showSearch ? "text-info" : "text-inherit") })
1079
- }
1080
- ) }),
1081
- /* @__PURE__ */ jsx(AnimatePresence, { children: showSearch && /* @__PURE__ */ jsx(
1082
- motion.span,
1083
- {
1084
- initial: { width: 0, opacity: 0 },
1085
- animate: { width: "auto", opacity: 1 },
1086
- exit: { width: 0, opacity: 0 },
1087
- transition: { duration: 0.2 },
1088
- className: "text-xs overflow-hidden whitespace-nowrap text-info flex-shrink-0",
1089
- children: "Search"
1090
- }
1091
- ) })
1092
- ]
1093
- }
1094
- ),
1095
- /* @__PURE__ */ jsx(CustomDivider, {}),
1096
- /* @__PURE__ */ jsxs(
1097
- "button",
1098
- {
1099
- type: "button",
1100
- onClick: () => handleToggleChange("think"),
1101
- className: cn2(
1102
- "rounded-full transition-all flex items-center gap-1 px-2 py-1 border h-8",
1103
- showThink ? "bg-indigo-500/15 border-indigo-500 text-indigo-500" : "bg-transparent border-transparent text-muted-foreground hover:text-foreground"
1104
- ),
1105
- children: [
1106
- /* @__PURE__ */ jsx("div", { className: "w-5 h-5 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx(
1107
- motion.div,
1108
- {
1109
- animate: { rotate: showThink ? 360 : 0, scale: showThink ? 1.1 : 1 },
1110
- whileHover: {
1111
- rotate: showThink ? 360 : 15,
1112
- scale: 1.1,
1113
- transition: { type: "spring", stiffness: 300, damping: 10 }
1114
- },
1115
- transition: { type: "spring", stiffness: 260, damping: 25 },
1116
- children: /* @__PURE__ */ jsx(
1117
- Brain,
1118
- {
1119
- className: cn2("w-4 h-4", showThink ? "text-indigo-500" : "text-inherit")
1120
- }
1121
- )
1122
- }
1123
- ) }),
1124
- /* @__PURE__ */ jsx(AnimatePresence, { children: showThink && /* @__PURE__ */ jsx(
1125
- motion.span,
1126
- {
1127
- initial: { width: 0, opacity: 0 },
1128
- animate: { width: "auto", opacity: 1 },
1129
- exit: { width: 0, opacity: 0 },
1130
- transition: { duration: 0.2 },
1131
- className: "text-xs overflow-hidden whitespace-nowrap text-indigo-500 flex-shrink-0",
1132
- children: "Think"
1133
- }
1134
- ) })
1135
- ]
1136
- }
1137
- ),
1138
- /* @__PURE__ */ jsx(CustomDivider, {}),
1139
- /* @__PURE__ */ jsxs(
1140
- "button",
1141
- {
1142
- type: "button",
1143
- onClick: handleCanvasToggle,
1144
- className: cn2(
1145
- "rounded-full transition-all flex items-center gap-1 px-2 py-1 border h-8",
1146
- showCanvas ? "bg-warning/15 border-warning text-warning" : "bg-transparent border-transparent text-muted-foreground hover:text-foreground"
1147
- ),
1148
- children: [
1149
- /* @__PURE__ */ jsx("div", { className: "w-5 h-5 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx(
1150
- motion.div,
1151
- {
1152
- animate: { rotate: showCanvas ? 360 : 0, scale: showCanvas ? 1.1 : 1 },
1153
- whileHover: {
1154
- rotate: showCanvas ? 360 : 15,
1155
- scale: 1.1,
1156
- transition: { type: "spring", stiffness: 300, damping: 10 }
1157
- },
1158
- transition: { type: "spring", stiffness: 260, damping: 25 },
1159
- children: /* @__PURE__ */ jsx(
1160
- FolderClosed,
1161
- {
1162
- className: cn2("w-4 h-4", showCanvas ? "text-warning" : "text-inherit")
1163
- }
1164
- )
1165
- }
1166
- ) }),
1167
- /* @__PURE__ */ jsx(AnimatePresence, { children: showCanvas && /* @__PURE__ */ jsx(
1168
- motion.span,
1169
- {
1170
- initial: { width: 0, opacity: 0 },
1171
- animate: { width: "auto", opacity: 1 },
1172
- exit: { width: 0, opacity: 0 },
1173
- transition: { duration: 0.2 },
1174
- className: "text-xs overflow-hidden whitespace-nowrap text-warning flex-shrink-0",
1175
- children: "Canvas"
1176
- }
1177
- ) })
1178
- ]
1179
- }
1180
- )
1181
- ] })
1182
- ]
1183
- }
1184
- ),
1185
- /* @__PURE__ */ jsx(
1186
- PromptInputAction,
1187
- {
1188
- tooltip: isLoading ? "Stop generation" : isRecording ? "Stop recording" : hasContent ? "Send message" : "Voice message",
1189
- children: /* @__PURE__ */ jsx(
1190
- Button,
1191
- {
1192
- variant: "default",
1193
- size: "icon",
1194
- className: cn2(
1195
- "h-8 w-8 rounded-full transition-all duration-200",
1196
- isRecording ? "bg-transparent hover:bg-accent text-destructive hover:text-destructive/80" : hasContent ? "bg-primary hover:bg-primary/90 text-primary-foreground" : "bg-transparent hover:bg-accent text-muted-foreground hover:text-foreground"
1197
- ),
1198
- onClick: () => {
1199
- if (isRecording) setIsRecording(false);
1200
- else if (hasContent) handleSubmit();
1201
- else setIsRecording(true);
1202
- },
1203
- disabled: isLoading && !hasContent,
1204
- children: isLoading ? /* @__PURE__ */ jsx(Square, { className: "h-4 w-4 fill-primary-foreground animate-pulse" }) : isRecording ? /* @__PURE__ */ jsx(StopCircle, { className: "h-5 w-5 text-destructive" }) : hasContent ? /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4 text-primary-foreground" }) : /* @__PURE__ */ jsx(Microphone, { className: "h-5 w-5 text-primary-foreground transition-colors" })
1205
- }
1206
- )
1557
+ hasContent,
1558
+ onActivate: () => {
1559
+ if (isRecording) stopRecording();
1560
+ else if (hasContent) handleSubmit();
1561
+ else startRecording();
1207
1562
  }
1208
- )
1209
- ] })
1210
- ]
1211
- }
1212
- ),
1213
- /* @__PURE__ */ jsx(ImageViewDialog, { imageUrl: selectedImage, onClose: () => setSelectedImage(null) })
1214
- ] });
1215
- }
1216
- );
1563
+ }
1564
+ )
1565
+ ] })
1566
+ ]
1567
+ }
1568
+ ),
1569
+ /* @__PURE__ */ jsx(
1570
+ ImageViewDialog,
1571
+ {
1572
+ imageUrl: selectedImage,
1573
+ onClose: () => dispatch({ type: "set-selected-image", imageUrl: null })
1574
+ }
1575
+ )
1576
+ ] });
1577
+ };
1217
1578
  PromptInputBox.displayName = "PromptInputBox";
1579
+ var emergeTransition = {
1580
+ duration: emerge.transition.duration,
1581
+ ease: [0.16, 1, 0.3, 1]
1582
+ };
1583
+ var flowTransition = {
1584
+ duration: flow.transition.duration,
1585
+ ease: [0, 0, 0.2, 1]
1586
+ };
1587
+ var defaultSpringTransition = {
1588
+ type: "spring",
1589
+ stiffness: brandSpring.default.stiffness,
1590
+ damping: brandSpring.default.damping,
1591
+ mass: brandSpring.default.mass
1592
+ };
1218
1593
  var PRESETS = {
1219
1594
  emerge: {
1220
1595
  initial: emerge.initial,
1221
1596
  animate: emerge.animate,
1222
1597
  exit: emerge.exit,
1223
- transition: emerge.transition
1598
+ transition: emergeTransition
1224
1599
  },
1225
1600
  flow: {
1226
1601
  initial: flow.initial,
1227
1602
  animate: flow.animate,
1228
1603
  exit: flow.exit,
1229
- transition: flow.transition
1604
+ transition: flowTransition
1230
1605
  },
1231
1606
  fade: {
1232
1607
  initial: { opacity: 0 },
@@ -1238,13 +1613,13 @@ var PRESETS = {
1238
1613
  initial: { opacity: 0, y: 16 },
1239
1614
  animate: { opacity: 1, y: 0 },
1240
1615
  exit: { opacity: 0, y: 16 },
1241
- transition: brandSpring
1616
+ transition: defaultSpringTransition
1242
1617
  },
1243
1618
  scale: {
1244
1619
  initial: { opacity: 0, scale: 0.95 },
1245
1620
  animate: { opacity: 1, scale: 1 },
1246
1621
  exit: { opacity: 0, scale: 0.95 },
1247
- transition: brandSpring
1622
+ transition: defaultSpringTransition
1248
1623
  }
1249
1624
  };
1250
1625
  var STAGGER = { fast: 0.05, normal: 0.1, slow: 0.2 };
@@ -1254,7 +1629,7 @@ function MotionDiv(props) {
1254
1629
  function AnimateIn(props) {
1255
1630
  const { children, preset = "emerge", delay = 0, duration, inView = false, className } = props;
1256
1631
  const shouldReduce = useReducedMotion();
1257
- const { initial, animate, exit, transition } = PRESETS[preset];
1632
+ const { initial, animate: animate2, exit, transition } = PRESETS[preset];
1258
1633
  const t = {
1259
1634
  ...transition,
1260
1635
  ...delay ? { delay } : {},
@@ -1267,7 +1642,7 @@ function AnimateIn(props) {
1267
1642
  {
1268
1643
  className,
1269
1644
  initial,
1270
- whileInView: animate,
1645
+ whileInView: animate2,
1271
1646
  exit,
1272
1647
  viewport: { once: true, margin: "-10%" },
1273
1648
  transition: t,
@@ -1275,7 +1650,7 @@ function AnimateIn(props) {
1275
1650
  }
1276
1651
  );
1277
1652
  }
1278
- return /* @__PURE__ */ jsx(MotionDiv, { className, initial, animate, exit, transition: t, children });
1653
+ return /* @__PURE__ */ jsx(MotionDiv, { className, initial, animate: animate2, exit, transition: t, children });
1279
1654
  }
1280
1655
  function AnimateInGroup(props) {
1281
1656
  const { children, stagger = "normal", inView = false, className } = props;
@@ -1313,115 +1688,192 @@ function AsciiText({ text, className = "", font = slant }) {
1313
1688
  }
1314
1689
  );
1315
1690
  }
1691
+ function Popover({ ...props }) {
1692
+ return /* @__PURE__ */ jsx(Popover$1.Root, { "data-slot": "popover", ...props });
1693
+ }
1694
+ function PopoverTrigger({ asChild, children, render, ...props }) {
1695
+ const renderElement = asChild && React5.isValidElement(children) ? children : render;
1696
+ return /* @__PURE__ */ jsx(
1697
+ Popover$1.Trigger,
1698
+ {
1699
+ "data-slot": "popover-trigger",
1700
+ render: renderElement,
1701
+ ...renderElement ? props : { ...props, children }
1702
+ }
1703
+ );
1704
+ }
1705
+ function PopoverContent({
1706
+ className,
1707
+ align = "center",
1708
+ sideOffset = 8,
1709
+ alignOffset = 0,
1710
+ side = "bottom",
1711
+ children,
1712
+ showArrow = false,
1713
+ style,
1714
+ ...props
1715
+ }) {
1716
+ return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
1717
+ Popover$1.Positioner,
1718
+ {
1719
+ "data-slot": "popover-positioner",
1720
+ sideOffset,
1721
+ align,
1722
+ alignOffset,
1723
+ side,
1724
+ children: /* @__PURE__ */ jsxs(
1725
+ Popover$1.Popup,
1726
+ {
1727
+ "data-slot": "popover-content",
1728
+ className: cn(
1729
+ overlayClassNames.popoverSurface,
1730
+ overlayPrimitiveClassNames.popoverSurface,
1731
+ className
1732
+ ),
1733
+ style: { zIndex: overlayZIndex.popover, ...style },
1734
+ ...props,
1735
+ children: [
1736
+ children,
1737
+ showArrow && /* @__PURE__ */ jsx(PopoverArrow, {})
1738
+ ]
1739
+ }
1740
+ )
1741
+ }
1742
+ ) });
1743
+ }
1744
+ function PopoverArrow({
1745
+ className,
1746
+ style,
1747
+ ...props
1748
+ }) {
1749
+ return /* @__PURE__ */ jsx(
1750
+ Popover$1.Arrow,
1751
+ {
1752
+ "data-slot": "popover-arrow",
1753
+ className: cn(
1754
+ "data-[side=bottom]:top-[-8px] data-[side=left]:right-[-13px] data-[side=left]:rotate-90 data-[side=right]:left-[-13px] data-[side=right]:-rotate-90 data-[side=top]:bottom-[-8px] data-[side=top]:rotate-180",
1755
+ className
1756
+ ),
1757
+ style: { zIndex: overlayZIndex.popover, ...style },
1758
+ ...props,
1759
+ children: /* @__PURE__ */ jsxs("svg", { "aria-hidden": "true", width: "20", height: "10", viewBox: "0 0 20 10", fill: "none", children: [
1760
+ /* @__PURE__ */ jsx(
1761
+ "path",
1762
+ {
1763
+ d: "M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V9H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z",
1764
+ className: "fill-popover"
1765
+ }
1766
+ ),
1767
+ /* @__PURE__ */ jsx(
1768
+ "path",
1769
+ {
1770
+ d: "M10.3333 3.34539L5.47654 7.71648C4.55842 8.54279 3.36693 9 2.13172 9H0V8H2.13172C3.11989 8 4.07308 7.63423 4.80758 6.97318L9.66437 2.60207C10.0447 2.25979 10.622 2.2598 11.0023 2.60207L15.8591 6.97318C16.5936 7.63423 17.5468 8 18.5349 8H20V9H18.5349C17.2998 9 16.1083 8.54278 15.1901 7.71648L10.3333 3.34539Z",
1771
+ className: "fill-border"
1772
+ }
1773
+ )
1774
+ ] })
1775
+ }
1776
+ );
1777
+ }
1316
1778
  var TAG_COLOR_MAP = {
1317
- feature: "bg-[var(--cyan-9)] text-white",
1779
+ feature: "bg-[hsl(var(--primary))] text-white",
1318
1780
  fix: "bg-[var(--status-success)] text-white",
1319
1781
  breaking: "bg-[var(--status-danger)] text-white",
1320
1782
  improvement: "bg-[var(--status-warning)] text-white",
1321
1783
  security: "bg-[var(--status-danger)] text-white",
1322
- experimental: "bg-[var(--neutral-8)] text-[var(--neutral-12)]"
1784
+ experimental: "bg-[var(--neutral-8)] text-foreground"
1785
+ };
1786
+ var getTagColor = (tag) => {
1787
+ if (!tag) return "bg-[var(--neutral-7)] text-foreground";
1788
+ return TAG_COLOR_MAP[tag.toLowerCase()] || TAG_COLOR_MAP.feature;
1789
+ };
1790
+ var getUnreadCount = (entries, lastSeenVersion) => {
1791
+ if (!lastSeenVersion) return entries.length;
1792
+ const lastSeenIndex = entries.findIndex((entry) => entry.version === lastSeenVersion);
1793
+ return lastSeenIndex === -1 ? entries.length : lastSeenIndex;
1323
1794
  };
1324
1795
  function ChangelogWidget({
1325
1796
  entries,
1326
1797
  changelogUrl = "/changelog",
1327
1798
  className
1328
1799
  }) {
1329
- const [isOpen, setIsOpen] = React6.useState(false);
1330
- const [lastSeenVersion, setLastSeenVersion] = React6.useState(null);
1331
- const dropdownRef = React6.useRef(null);
1332
- const unreadCount = React6.useMemo(() => {
1333
- if (!lastSeenVersion) return entries.length;
1334
- const lastSeenIndex = entries.findIndex((e) => e.version === lastSeenVersion);
1335
- return lastSeenIndex === -1 ? entries.length : lastSeenIndex;
1336
- }, [entries, lastSeenVersion]);
1800
+ const [isOpen, setIsOpen] = React5.useState(false);
1801
+ const [lastSeenVersion, setLastSeenVersion] = React5.useState(null);
1802
+ const unreadCount = getUnreadCount(entries, lastSeenVersion);
1337
1803
  const recentEntries = entries.slice(0, 5);
1338
- const handleClose = React6.useCallback(() => {
1339
- setIsOpen(false);
1340
- if (recentEntries.length > 0) {
1341
- setLastSeenVersion(recentEntries[0]?.version ?? null);
1342
- }
1343
- }, [recentEntries]);
1344
- React6.useEffect(() => {
1345
- const handleEscape = (e) => {
1346
- if (e.key === "Escape") handleClose();
1347
- };
1348
- const handleClickOutside = (e) => {
1349
- if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
1350
- handleClose();
1351
- }
1352
- };
1353
- if (isOpen) {
1354
- document.addEventListener("keydown", handleEscape);
1355
- document.addEventListener("mousedown", handleClickOutside);
1356
- }
1357
- return () => {
1358
- document.removeEventListener("keydown", handleEscape);
1359
- document.removeEventListener("mousedown", handleClickOutside);
1360
- };
1361
- }, [isOpen, handleClose]);
1362
- const getTagColor = (tag) => {
1363
- if (!tag) return "bg-[var(--neutral-7)] text-[var(--neutral-12)]";
1364
- return TAG_COLOR_MAP[tag.toLowerCase()] || TAG_COLOR_MAP.feature;
1804
+ const latestVersion = recentEntries[0]?.version ?? null;
1805
+ const handleOpenChange = (open) => {
1806
+ setIsOpen(open);
1807
+ if (!open && latestVersion) setLastSeenVersion(latestVersion);
1365
1808
  };
1366
- return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), ref: dropdownRef, children: [
1809
+ return /* @__PURE__ */ jsxs(Popover, { open: isOpen, onOpenChange: handleOpenChange, children: [
1367
1810
  /* @__PURE__ */ jsxs(
1368
- "button",
1811
+ PopoverTrigger,
1369
1812
  {
1370
- type: "button",
1371
1813
  "aria-label": "View changelog",
1372
- "aria-expanded": isOpen,
1373
- onClick: () => setIsOpen(!isOpen),
1374
- className: "relative inline-flex items-center justify-center rounded-lg p-2 transition-colors hover:bg-[var(--neutral-3)]",
1814
+ className: cn(
1815
+ "relative inline-flex items-center justify-center rounded-[var(--radius-lg)] p-2 transition-colors hover:bg-muted",
1816
+ className
1817
+ ),
1375
1818
  children: [
1376
- /* @__PURE__ */ jsx(Bell, { className: "h-5 w-5 text-[var(--neutral-11)]" }),
1819
+ /* @__PURE__ */ jsx(Bell, { className: "h-5 w-5 text-muted-foreground" }),
1377
1820
  unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-0 top-0 flex h-5 w-5 items-center justify-center rounded-full bg-[var(--status-danger)] text-xs font-bold text-white", children: unreadCount > 9 ? "9+" : unreadCount })
1378
1821
  ]
1379
1822
  }
1380
1823
  ),
1381
- isOpen && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 top-full z-50 mt-2 w-80 overflow-hidden rounded-lg border border-[var(--neutral-7)] bg-white shadow-lg transition-all duration-200", children: [
1382
- /* @__PURE__ */ jsx("div", { className: "bg-gradient-to-r from-[var(--blue-3)] to-[var(--cyan-3)] px-4 py-3", children: /* @__PURE__ */ jsx("h3", { className: "font-semibold text-[var(--neutral-12)]", children: "What's New" }) }),
1383
- /* @__PURE__ */ jsx("div", { className: "max-h-96 overflow-y-auto", children: recentEntries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-6 text-center text-sm text-[var(--neutral-11)]", children: "No updates available" }) : recentEntries.map((entry) => /* @__PURE__ */ jsxs(
1384
- "a",
1385
- {
1386
- href: entry.url,
1387
- className: "block border-b border-[var(--neutral-6)] px-4 py-3 transition-colors hover:bg-[var(--neutral-2)]",
1388
- children: [
1389
- /* @__PURE__ */ jsx("div", { className: "mb-2 flex items-center justify-between gap-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1390
- /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs font-bold text-[var(--blue-9)]", children: [
1391
- "v",
1392
- entry.version
1393
- ] }),
1394
- entry.tag && /* @__PURE__ */ jsx(
1395
- "span",
1396
- {
1397
- className: cn(
1398
- "rounded px-2 py-0.5 text-xs font-medium",
1399
- getTagColor(entry.tag)
1400
- ),
1401
- children: entry.tag
1402
- }
1403
- )
1404
- ] }) }),
1405
- /* @__PURE__ */ jsx("p", { className: "mb-1 text-sm font-medium text-[var(--neutral-12)]", children: entry.title }),
1406
- /* @__PURE__ */ jsx("p", { className: "mb-2 line-clamp-2 text-xs text-[var(--neutral-11)]", children: entry.excerpt }),
1407
- /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--neutral-10)]", children: entry.date })
1408
- ]
1409
- },
1410
- entry.version
1411
- )) }),
1412
- /* @__PURE__ */ jsxs(
1413
- "a",
1414
- {
1415
- href: changelogUrl,
1416
- onClick: handleClose,
1417
- className: "flex items-center justify-between border-t border-[var(--neutral-6)] bg-[var(--neutral-1)] px-4 py-3 text-sm font-medium text-[var(--blue-9)] transition-colors hover:bg-[var(--neutral-2)]",
1418
- children: [
1419
- /* @__PURE__ */ jsx("span", { children: "View all updates" }),
1420
- /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
1421
- ]
1422
- }
1423
- )
1424
- ] })
1824
+ /* @__PURE__ */ jsxs(
1825
+ PopoverContent,
1826
+ {
1827
+ align: "end",
1828
+ side: "bottom",
1829
+ sideOffset: 8,
1830
+ className: "w-80 overflow-hidden rounded-[var(--radius-lg)] border-border bg-white p-0 shadow-lg",
1831
+ children: [
1832
+ /* @__PURE__ */ jsx("div", { className: "bg-gradient-to-r from-[var(--blue-3)] to-[var(--cyan-3)] px-4 py-3", children: /* @__PURE__ */ jsx("h3", { className: "font-semibold text-foreground", children: "What's New" }) }),
1833
+ /* @__PURE__ */ jsx("div", { className: "max-h-96 overflow-y-auto", children: recentEntries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-6 text-center text-sm text-muted-foreground", children: "No updates available" }) : recentEntries.map((entry) => /* @__PURE__ */ jsxs(
1834
+ "a",
1835
+ {
1836
+ href: entry.url,
1837
+ className: "block border-b border-border px-4 py-3 transition-colors hover:bg-muted",
1838
+ children: [
1839
+ /* @__PURE__ */ jsx("div", { className: "mb-2 flex items-center justify-between gap-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1840
+ /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs font-bold text-primary", children: [
1841
+ "v",
1842
+ entry.version
1843
+ ] }),
1844
+ entry.tag && /* @__PURE__ */ jsx(
1845
+ "span",
1846
+ {
1847
+ className: cn(
1848
+ "rounded px-2 py-0.5 text-xs font-medium",
1849
+ getTagColor(entry.tag)
1850
+ ),
1851
+ children: entry.tag
1852
+ }
1853
+ )
1854
+ ] }) }),
1855
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-sm font-medium text-foreground", children: entry.title }),
1856
+ /* @__PURE__ */ jsx("p", { className: "mb-2 line-clamp-2 text-xs text-muted-foreground", children: entry.excerpt }),
1857
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: entry.date })
1858
+ ]
1859
+ },
1860
+ entry.version
1861
+ )) }),
1862
+ /* @__PURE__ */ jsxs(
1863
+ "a",
1864
+ {
1865
+ href: changelogUrl,
1866
+ onClick: () => handleOpenChange(false),
1867
+ className: "flex items-center justify-between border-t border-border bg-background px-4 py-3 text-sm font-medium text-primary transition-colors hover:bg-muted",
1868
+ children: [
1869
+ /* @__PURE__ */ jsx("span", { children: "View all updates" }),
1870
+ /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
1871
+ ]
1872
+ }
1873
+ )
1874
+ ]
1875
+ }
1876
+ )
1425
1877
  ] });
1426
1878
  }
1427
1879
  var GRAPH_NODE_FLOW_TYPE = "graph-node";
@@ -1496,12 +1948,28 @@ var XYFLOW_TOKEN_THEME = {
1496
1948
  ["--xy-controls-button-color"]: "var(--neutral-11)"
1497
1949
  };
1498
1950
  var nodeCardVariants = cva(
1499
- "min-w-[160px] rounded-lg border bg-neutral-2 px-3 py-2 text-neutral-12 shadow-sm transition-colors",
1951
+ "min-w-[160px] rounded-[var(--radius-lg)] border bg-neutral-2 px-3 py-2 text-neutral-12 shadow-sm transition-colors",
1500
1952
  {
1501
1953
  variants: { ready: { true: "border-success", false: "border-neutral-7" } },
1502
1954
  defaultVariants: { ready: false }
1503
1955
  }
1504
1956
  );
1957
+ function GraphNodeCard({ data }) {
1958
+ const view = data.view;
1959
+ return /* @__PURE__ */ jsxs("div", { className: nodeCardVariants({ ready: view.ready ?? false }), children: [
1960
+ /* @__PURE__ */ jsx(Handle, { type: "target", position: Position.Left }),
1961
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs font-medium text-neutral-11 uppercase tracking-wide", children: [
1962
+ view.icon,
1963
+ view.label
1964
+ ] }),
1965
+ view.subtitle ? /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-sm text-neutral-12", children: view.subtitle }) : null,
1966
+ /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right })
1967
+ ] });
1968
+ }
1969
+ var NODE_TYPES = {
1970
+ [GRAPH_NODE_FLOW_TYPE]: GraphNodeCard
1971
+ };
1972
+ var REACT_FLOW_PRO_OPTIONS = { hideAttribution: true };
1505
1973
  function NodeGraphCanvas({
1506
1974
  graph,
1507
1975
  onChange,
@@ -1513,82 +1981,63 @@ function NodeGraphCanvas({
1513
1981
  }) {
1514
1982
  const statusId = useId();
1515
1983
  const [rejection, setRejection] = useState(null);
1516
- const { nodes, edges } = useMemo(() => graphToFlow(graph, edgeIdentity), [graph, edgeIdentity]);
1517
- const nodeTypes = useMemo(
1518
- () => ({
1519
- [GRAPH_NODE_FLOW_TYPE]: ({ data }) => {
1520
- const view = renderNode(data.node);
1521
- return /* @__PURE__ */ jsxs("div", { className: nodeCardVariants({ ready: view.ready ?? false }), children: [
1522
- /* @__PURE__ */ jsx(Handle, { type: "target", position: Position.Left }),
1523
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs font-medium text-neutral-11 uppercase tracking-wide", children: [
1524
- view.icon,
1525
- view.label
1526
- ] }),
1527
- view.subtitle ? /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-sm text-neutral-12", children: view.subtitle }) : null,
1528
- /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right })
1529
- ] });
1530
- }
1531
- }),
1532
- [renderNode]
1533
- );
1534
- const onNodesChange = useCallback(
1535
- (changes) => {
1536
- if (readOnly) return;
1537
- const removed = changes.filter(
1538
- (c) => c.type === "remove"
1539
- );
1540
- if (removed.length > 0) {
1541
- let next = graph;
1542
- for (const r of removed) next = removeNode(next, r.id);
1543
- onChange(next);
1544
- return;
1545
- }
1546
- const positional = changes.some((c) => c.type === "position" && c.dragging === false);
1547
- if (positional) {
1548
- const moved = nodes.map((n) => {
1549
- const change = changes.find((c) => c.type === "position" && c.id === n.id);
1550
- if (change && change.type === "position" && change.position) {
1551
- return { ...n, position: change.position };
1552
- }
1553
- return n;
1554
- });
1555
- onChange(applyNodePositions(graph, moved));
1556
- }
1557
- },
1558
- [graph, nodes, onChange, readOnly]
1559
- );
1560
- const onEdgesChange = useCallback(
1561
- (changes) => {
1562
- if (readOnly) return;
1563
- const removed = changes.filter(
1564
- (c) => c.type === "remove"
1565
- );
1566
- if (removed.length === 0) return;
1984
+ const { nodes: graphNodes, edges } = graphToFlow(graph, edgeIdentity);
1985
+ const nodes = graphNodes.map((node) => ({
1986
+ ...node,
1987
+ data: {
1988
+ ...node.data,
1989
+ view: renderNode(node.data.node)
1990
+ }
1991
+ }));
1992
+ const onNodesChange = (changes) => {
1993
+ if (readOnly) return;
1994
+ const removed = changes.filter(
1995
+ (c) => c.type === "remove"
1996
+ );
1997
+ if (removed.length > 0) {
1567
1998
  let next = graph;
1568
- for (const r of removed) next = removeFlowEdge(next, r.id, edgeIdentity);
1999
+ for (const r of removed) next = removeNode(next, r.id);
1569
2000
  onChange(next);
1570
- },
1571
- [graph, edgeIdentity, onChange, readOnly]
1572
- );
1573
- const onConnect = useCallback(
1574
- (connection) => {
1575
- if (readOnly) return;
1576
- const result = tryAddEdge(graph, connection, { makeEdge, edgeIdentity });
1577
- if (result.ok) {
1578
- setRejection(null);
1579
- onChange(result.graph);
1580
- } else {
1581
- setRejection(result.reason);
1582
- }
1583
- },
1584
- [graph, edgeIdentity, makeEdge, onChange, readOnly]
1585
- );
2001
+ return;
2002
+ }
2003
+ const positional = changes.some((c) => c.type === "position" && c.dragging === false);
2004
+ if (positional) {
2005
+ const moved = nodes.map((n) => {
2006
+ const change = changes.find((c) => c.type === "position" && c.id === n.id);
2007
+ if (change && change.type === "position" && change.position) {
2008
+ return { ...n, position: change.position };
2009
+ }
2010
+ return n;
2011
+ });
2012
+ onChange(applyNodePositions(graph, moved));
2013
+ }
2014
+ };
2015
+ const onEdgesChange = (changes) => {
2016
+ if (readOnly) return;
2017
+ const removed = changes.filter(
2018
+ (c) => c.type === "remove"
2019
+ );
2020
+ if (removed.length === 0) return;
2021
+ let next = graph;
2022
+ for (const r of removed) next = removeFlowEdge(next, r.id, edgeIdentity);
2023
+ onChange(next);
2024
+ };
2025
+ const onConnect = (connection) => {
2026
+ if (readOnly) return;
2027
+ const result = tryAddEdge(graph, connection, { makeEdge, edgeIdentity });
2028
+ if (result.ok) {
2029
+ setRejection(null);
2030
+ onChange(result.graph);
2031
+ } else {
2032
+ setRejection(result.reason);
2033
+ }
2034
+ };
1586
2035
  return /* @__PURE__ */ jsx(AnimateIn, { preset: "emerge", children: /* @__PURE__ */ jsx(
1587
2036
  "div",
1588
2037
  {
1589
2038
  style: XYFLOW_TOKEN_THEME,
1590
2039
  className: cn(
1591
- "relative h-[480px] w-full overflow-hidden rounded-xl border border-neutral-7 bg-neutral-1",
2040
+ "relative h-[480px] w-full overflow-hidden rounded-[var(--radius-xl)] border border-neutral-7 bg-neutral-1",
1592
2041
  className
1593
2042
  ),
1594
2043
  children: /* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsxs(
@@ -1596,7 +2045,7 @@ function NodeGraphCanvas({
1596
2045
  {
1597
2046
  nodes,
1598
2047
  edges,
1599
- nodeTypes,
2048
+ nodeTypes: NODE_TYPES,
1600
2049
  onNodesChange,
1601
2050
  onEdgesChange,
1602
2051
  onConnect,
@@ -1604,7 +2053,7 @@ function NodeGraphCanvas({
1604
2053
  nodesConnectable: !readOnly,
1605
2054
  elementsSelectable: !readOnly,
1606
2055
  fitView: true,
1607
- proOptions: { hideAttribution: true },
2056
+ proOptions: REACT_FLOW_PRO_OPTIONS,
1608
2057
  children: [
1609
2058
  /* @__PURE__ */ jsx(Background, { color: "var(--neutral-6)" }),
1610
2059
  /* @__PURE__ */ jsx(Controls, { showInteractive: !readOnly }),
@@ -1614,7 +2063,7 @@ function NodeGraphCanvas({
1614
2063
  id: statusId,
1615
2064
  role: "alert",
1616
2065
  "aria-live": "assertive",
1617
- className: "flex items-center gap-2 rounded-md border border-destructive bg-destructive/10 px-3 py-2 text-sm text-destructive",
2066
+ className: "flex items-center gap-2 rounded-[var(--radius-md)] border border-destructive bg-destructive/10 px-3 py-2 text-sm text-destructive",
1618
2067
  children: [
1619
2068
  /* @__PURE__ */ jsx("span", { children: rejection }),
1620
2069
  /* @__PURE__ */ jsx(
@@ -1661,6 +2110,20 @@ var itemVariants = {
1661
2110
  }
1662
2111
  }
1663
2112
  };
2113
+ var reducedContainerVariants = {
2114
+ hidden: { opacity: 1 },
2115
+ visible: {
2116
+ opacity: 1,
2117
+ transition: { duration: 0 }
2118
+ }
2119
+ };
2120
+ var reducedItemVariants = {
2121
+ hidden: { opacity: 1 },
2122
+ visible: {
2123
+ opacity: 1,
2124
+ transition: { duration: 0 }
2125
+ }
2126
+ };
1664
2127
  function OnboardingChecklist({
1665
2128
  title,
1666
2129
  description,
@@ -1669,12 +2132,15 @@ function OnboardingChecklist({
1669
2132
  videoUrl,
1670
2133
  className
1671
2134
  }) {
2135
+ const shouldReduceMotion = useReducedMotion();
2136
+ const activeContainerVariants = shouldReduceMotion ? reducedContainerVariants : containerVariants;
2137
+ const activeItemVariants = shouldReduceMotion ? reducedItemVariants : itemVariants;
1672
2138
  return /* @__PURE__ */ jsx(
1673
2139
  motion.div,
1674
2140
  {
1675
2141
  initial: "hidden",
1676
2142
  animate: "visible",
1677
- variants: containerVariants,
2143
+ variants: activeContainerVariants,
1678
2144
  className: cn(
1679
2145
  "w-full max-w-4xl mx-auto bg-card text-card-foreground border rounded-[var(--radius-2xl)] shadow-sm p-8 overflow-hidden",
1680
2146
  className
@@ -1683,7 +2149,7 @@ function OnboardingChecklist({
1683
2149
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1684
2150
  /* @__PURE__ */ jsx("h2", { className: "text-2xl font-bold tracking-tight", children: title }),
1685
2151
  /* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: description }),
1686
- /* @__PURE__ */ jsx("ul", { className: "mt-6 grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-4", children: items.map((item) => /* @__PURE__ */ jsxs(motion.li, { variants: itemVariants, className: "flex flex-col", children: [
2152
+ /* @__PURE__ */ jsx("ul", { className: "mt-6 grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-4", children: items.map((item) => /* @__PURE__ */ jsxs(motion.li, { variants: activeItemVariants, className: "flex flex-col", children: [
1687
2153
  /* @__PURE__ */ jsxs("div", { className: "flex items-start", children: [
1688
2154
  /* @__PURE__ */ jsx(CheckCircle, { className: "h-5 w-5 text-green-500 mt-0.5 flex-shrink-0" }),
1689
2155
  /* @__PURE__ */ jsx("span", { className: "ml-3 text-sm font-medium", children: item.text })
@@ -1695,7 +2161,7 @@ function OnboardingChecklist({
1695
2161
  "a",
1696
2162
  {
1697
2163
  href: item.helperLink.href,
1698
- className: "text-primary underline-offset-4 hover:underline focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 rounded-[var(--radius-sm)]",
2164
+ className: "text-primary underline-offset-4 hover:underline rounded-[var(--radius-sm)]",
1699
2165
  children: item.helperLink.text
1700
2166
  }
1701
2167
  )
@@ -1705,25 +2171,26 @@ function OnboardingChecklist({
1705
2171
  /* @__PURE__ */ jsx(
1706
2172
  motion.div,
1707
2173
  {
1708
- variants: itemVariants,
2174
+ variants: activeItemVariants,
1709
2175
  className: "relative group rounded-[var(--radius-lg)] overflow-hidden cursor-pointer w-full aspect-video",
1710
2176
  children: /* @__PURE__ */ jsxs(Dialog, { children: [
1711
2177
  /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1712
2178
  "button",
1713
2179
  {
1714
2180
  type: "button",
1715
- className: "w-full h-full focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 rounded-[var(--radius-lg)]",
2181
+ "aria-label": "Play the video guide",
2182
+ className: "w-full h-full rounded-[var(--radius-lg)]",
1716
2183
  children: [
1717
2184
  /* @__PURE__ */ jsx(
1718
2185
  Image,
1719
2186
  {
1720
2187
  src: videoThumbnailUrl,
1721
2188
  alt: "Video guide thumbnail",
1722
- className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105",
2189
+ className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100",
1723
2190
  fill: true
1724
2191
  }
1725
2192
  ),
1726
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/30 flex items-center justify-center", children: /* @__PURE__ */ jsx(PlayCircle, { className: "h-16 w-16 text-white/80 transform transition-all duration-300 group-hover:scale-110 group-hover:text-white" }) })
2193
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/30 flex items-center justify-center", children: /* @__PURE__ */ jsx(PlayCircle, { className: "h-16 w-16 text-white/80 transform transition-[background-color,border-color,box-shadow,color,opacity,transform] duration-300 group-hover:scale-110 group-hover:text-white motion-reduce:transition-none motion-reduce:group-hover:scale-100" }) })
1727
2194
  ]
1728
2195
  }
1729
2196
  ) }),
@@ -1817,7 +2284,7 @@ function subscribePlatform() {
1817
2284
  return () => void 0;
1818
2285
  }
1819
2286
  function usePlatform() {
1820
- return React6.useSyncExternalStore(subscribePlatform, getClientPlatform, () => "apple");
2287
+ return React5.useSyncExternalStore(subscribePlatform, getClientPlatform, () => "apple");
1821
2288
  }
1822
2289
  function normalizeKey(children) {
1823
2290
  if (typeof children !== "string") {
@@ -1832,54 +2299,63 @@ function normalizeKey(children) {
1832
2299
  function getModifierSymbols(platform) {
1833
2300
  return platform === "apple" ? appleModifierSymbols : otherModifierSymbols;
1834
2301
  }
1835
- var Kbd = React6.forwardRef(
1836
- ({ meta, shift, alt, ctrl, small, children, className, "aria-label": ariaLabel, ...props }, ref) => {
1837
- const platform = usePlatform();
1838
- const symbols = getModifierSymbols(platform);
1839
- const activeModifiers = modifierOrder.filter((modifier) => {
1840
- if (modifier === "meta") return meta;
1841
- if (modifier === "shift") return shift;
1842
- if (modifier === "alt") return alt;
1843
- return ctrl;
1844
- });
1845
- const normalizedKey = normalizeKey(children);
1846
- const visualParts = [
1847
- ...activeModifiers.map((modifier) => symbols[modifier]),
1848
- normalizedKey
1849
- ].filter(Boolean);
1850
- const accessibleName = ariaLabel ?? [
1851
- ...activeModifiers.map((modifier) => modifierLabels[modifier]),
1852
- typeof normalizedKey === "string" ? normalizedKey : void 0
1853
- ].filter(Boolean).join(" ");
1854
- return /* @__PURE__ */ jsx(
1855
- "kbd",
1856
- {
1857
- ref,
1858
- "aria-label": accessibleName || void 0,
1859
- className: cn(
1860
- "inline-flex select-none items-center justify-center gap-0.5 rounded-[var(--radius-sm)] border border-border bg-muted font-mono font-medium leading-none text-muted-foreground tabular-nums",
1861
- small ? "min-h-4 min-w-4 px-1 py-0 text-xs" : "min-h-5 min-w-5 px-1.5 py-0.5 text-xs",
1862
- className
1863
- ),
1864
- ...props,
1865
- children: visualParts.join("")
1866
- }
1867
- );
1868
- }
1869
- );
2302
+ var Kbd = ({
2303
+ meta,
2304
+ shift,
2305
+ alt,
2306
+ ctrl,
2307
+ small,
2308
+ children,
2309
+ className,
2310
+ "aria-label": ariaLabel,
2311
+ ref,
2312
+ ...props
2313
+ }) => {
2314
+ const platform = usePlatform();
2315
+ const symbols = getModifierSymbols(platform);
2316
+ const activeModifiers = modifierOrder.filter((modifier) => {
2317
+ if (modifier === "meta") return meta;
2318
+ if (modifier === "shift") return shift;
2319
+ if (modifier === "alt") return alt;
2320
+ return ctrl;
2321
+ });
2322
+ const normalizedKey = normalizeKey(children);
2323
+ const visualParts = [
2324
+ ...activeModifiers.map((modifier) => symbols[modifier]),
2325
+ normalizedKey
2326
+ ].filter(Boolean);
2327
+ const accessibleName = ariaLabel ?? [
2328
+ ...activeModifiers.map((modifier) => modifierLabels[modifier]),
2329
+ typeof normalizedKey === "string" ? normalizedKey : void 0
2330
+ ].filter(Boolean).join(" ");
2331
+ return /* @__PURE__ */ jsx(
2332
+ "kbd",
2333
+ {
2334
+ ref,
2335
+ "aria-label": accessibleName || void 0,
2336
+ className: cn(
2337
+ "inline-flex select-none items-center justify-center gap-0.5 rounded-[var(--radius-sm)] border border-border bg-muted font-mono font-medium leading-none text-muted-foreground tabular-nums",
2338
+ small ? "min-h-4 min-w-4 px-1 py-0 text-xs" : "min-h-5 min-w-5 px-1.5 py-0.5 text-xs",
2339
+ className
2340
+ ),
2341
+ ...props,
2342
+ children: visualParts.join("")
2343
+ }
2344
+ );
2345
+ };
1870
2346
  Kbd.displayName = "Kbd";
1871
2347
  var inputVariants = cva(
1872
2348
  [
1873
- "flex h-[var(--input-height)] w-full rounded-[var(--input-radius)] border border-input bg-background",
1874
- "px-[var(--input-padding-x)] text-[length:var(--input-font-size)] text-foreground shadow-[var(--shadow-xs)]",
2349
+ "flex h-[var(--input-height)] w-full rounded-[var(--input-radius)]",
2350
+ "px-[var(--input-padding-x)] text-[length:var(--input-font-size)] text-foreground",
1875
2351
  "transition-[background-color,border-color,box-shadow,color] duration-micro ease-out",
1876
2352
  "placeholder:text-muted-foreground",
1877
- "outline-none focus:border-ring focus:ring-[length:var(--input-focus-ring-width)] focus:ring-ring/30",
2353
+ "outline-none",
1878
2354
  "disabled:cursor-not-allowed disabled:opacity-50",
1879
2355
  "read-only:bg-muted/70 read-only:cursor-default",
1880
2356
  "file:h-full file:border-0 file:border-e file:border-solid file:border-input file:bg-transparent",
1881
2357
  "file:me-[var(--input-padding-x)] file:pe-[var(--input-padding-x)] file:text-[length:var(--input-font-size)] file:font-medium file:text-foreground",
1882
- "aria-invalid:border-destructive/60 aria-invalid:focus:border-destructive aria-invalid:focus:ring-destructive/20",
2358
+ formControlInvalidClassNames.input,
1883
2359
  "[&[type=search]::-webkit-search-cancel-button]:appearance-none",
1884
2360
  "[&[type=search]::-webkit-search-decoration]:appearance-none",
1885
2361
  "[&[type=search]::-webkit-search-results-button]:appearance-none",
@@ -1897,11 +2373,31 @@ var inputVariants = cva(
1897
2373
  prefix: "pl-[var(--input-padding-left)]",
1898
2374
  suffix: "pr-[var(--input-padding-right)]",
1899
2375
  both: "pl-[var(--input-padding-left)] pr-[var(--input-padding-right)]"
2376
+ },
2377
+ /**
2378
+ * `bare` drops the field's own stroke, fill and lift so it can sit inside
2379
+ * a container that already provides them — a footer subscribe group, a
2380
+ * search well, a segmented composer. Those surfaces were each being built
2381
+ * by hand with a raw <input>, because overriding the bordered look from
2382
+ * outside is not reliable: `border-transparent` passed via className and
2383
+ * `border-input` from the base are both border-color utilities, so which
2384
+ * one wins depends on their order in the generated stylesheet rather than
2385
+ * on the order they were written. That is why the stroke, fill and shadow
2386
+ * live in this variant instead of the base — the two tones now never emit
2387
+ * a competing declaration.
2388
+ *
2389
+ * A bare field draws no focus ring of its own. The container owns it, so
2390
+ * the ring surrounds the whole group rather than a box inside it.
2391
+ */
2392
+ tone: {
2393
+ bordered: `border border-input bg-background shadow-xs ${formControlFocusClassNames.input}`,
2394
+ bare: "border-0 bg-transparent shadow-none"
1900
2395
  }
1901
2396
  },
1902
2397
  defaultVariants: {
1903
2398
  size: "md",
1904
- affix: "none"
2399
+ affix: "none",
2400
+ tone: "bordered"
1905
2401
  }
1906
2402
  }
1907
2403
  );
@@ -1924,7 +2420,7 @@ var inputControlButtonVariants = cva(
1924
2420
  "absolute right-[var(--input-control-inset)] top-1/2 flex size-[var(--input-control-size)] -translate-y-1/2 items-center justify-center",
1925
2421
  "rounded-[var(--radius-sm)] text-muted-foreground transition-colors duration-micro ease-out",
1926
2422
  "hover:bg-accent hover:text-foreground",
1927
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
2423
+ "focus-visible:outline-none",
1928
2424
  "disabled:pointer-events-none disabled:opacity-50",
1929
2425
  "[&_svg]:size-[var(--input-control-icon-size)]"
1930
2426
  ].join(" ")
@@ -1963,7 +2459,7 @@ function getInputStyle(size, style) {
1963
2459
  "--input-focus-ring-width": `${inputTokens.focusRingWidth}px`,
1964
2460
  borderRadius: "var(--input-radius)",
1965
2461
  outline: "none",
1966
- ...style
2462
+ ...asPlainStyle(style)
1967
2463
  };
1968
2464
  }
1969
2465
  function getAffixMode(prefix, hasRightAdornment) {
@@ -1981,11 +2477,15 @@ function isTextAffix(value) {
1981
2477
  function joinDescriptionIds2(...ids) {
1982
2478
  return ids.filter(Boolean).join(" ") || void 0;
1983
2479
  }
1984
- var Input = React6.forwardRef((props, forwardedRef) => {
2480
+ var Input = ({
2481
+ ref: forwardedRef,
2482
+ ...props
2483
+ }) => {
1985
2484
  const {
1986
2485
  className,
1987
2486
  type = "text",
1988
2487
  size = "md",
2488
+ tone = "bordered",
1989
2489
  prefix,
1990
2490
  suffix,
1991
2491
  clearable,
@@ -2014,16 +2514,16 @@ var Input = React6.forwardRef((props, forwardedRef) => {
2014
2514
  ...rest
2015
2515
  } = props;
2016
2516
  const isControlled = value !== void 0;
2017
- const baseId = React6.useId();
2018
- const inputRef = React6.useRef(null);
2517
+ const baseId = React5.useId();
2518
+ const inputRef = React5.useRef(null);
2019
2519
  const descriptionId = description && id ? `${id}-description` : void 0;
2020
2520
  const errorId = typeof error === "string" && id ? `${id}-error` : void 0;
2021
2521
  const prefixDescriptionId = isTextAffix(prefix) ? `${id ?? baseId}-prefix` : void 0;
2022
2522
  const suffixDescriptionId = isTextAffix(suffix) ? `${id ?? baseId}-suffix` : void 0;
2023
- const [internalValue, setInternalValue] = React6.useState(
2523
+ const [internalValue, setInternalValue] = React5.useState(
2024
2524
  typeof defaultValue === "string" || typeof defaultValue === "number" ? String(defaultValue) : ""
2025
2525
  );
2026
- const [passwordVisible, setPasswordVisible] = React6.useState(false);
2526
+ const [passwordVisible, setPasswordVisible] = React5.useState(false);
2027
2527
  const currentValue = isControlled ? String(value ?? "") : internalValue;
2028
2528
  const clearOnEscapeEnabled = clearOnEscape ?? type === "search";
2029
2529
  const canRevealPassword = Boolean(revealable && type === "password");
@@ -2043,7 +2543,7 @@ var Input = React6.forwardRef((props, forwardedRef) => {
2043
2543
  errorId
2044
2544
  );
2045
2545
  const valueProps = isControlled ? { value } : clearable || shortcut || clearOnEscapeEnabled ? { value: currentValue } : { defaultValue };
2046
- const setInputRef = React6.useCallback(
2546
+ const setInputRef = React5.useCallback(
2047
2547
  (node) => {
2048
2548
  const input = node instanceof HTMLInputElement ? node : null;
2049
2549
  inputRef.current = input;
@@ -2051,7 +2551,7 @@ var Input = React6.forwardRef((props, forwardedRef) => {
2051
2551
  },
2052
2552
  [forwardedRef]
2053
2553
  );
2054
- const emitNativeInput = React6.useCallback(
2554
+ const emitNativeInput = React5.useCallback(
2055
2555
  (nextValue) => {
2056
2556
  const input = inputRef.current;
2057
2557
  if (!input) {
@@ -2068,7 +2568,7 @@ var Input = React6.forwardRef((props, forwardedRef) => {
2068
2568
  },
2069
2569
  [isControlled, onValueChange]
2070
2570
  );
2071
- const syncInputValue = React6.useCallback(
2571
+ const syncInputValue = React5.useCallback(
2072
2572
  (event) => {
2073
2573
  if (!isControlled) setInternalValue(event.currentTarget.value);
2074
2574
  onValueChange?.(event.currentTarget.value);
@@ -2076,12 +2576,12 @@ var Input = React6.forwardRef((props, forwardedRef) => {
2076
2576
  },
2077
2577
  [isControlled, onChange, onValueChange]
2078
2578
  );
2079
- const handleClear = React6.useCallback(() => {
2579
+ const handleClear = React5.useCallback(() => {
2080
2580
  emitNativeInput("");
2081
2581
  onClear?.();
2082
2582
  inputRef.current?.focus();
2083
2583
  }, [emitNativeInput, onClear]);
2084
- const handleKeyDown = React6.useCallback(
2584
+ const handleKeyDown = React5.useCallback(
2085
2585
  (event) => {
2086
2586
  onKeyDown?.(event);
2087
2587
  if (event.defaultPrevented || event.key !== "Escape" || !clearOnEscapeEnabled || currentValue.length === 0 || disabled) {
@@ -2105,7 +2605,7 @@ var Input = React6.forwardRef((props, forwardedRef) => {
2105
2605
  "aria-describedby": describedBy,
2106
2606
  "aria-busy": loading || void 0,
2107
2607
  className: cn(
2108
- inputVariants({ size, affix }),
2608
+ inputVariants({ size, affix, tone }),
2109
2609
  type === "file" && "p-0 pr-[var(--input-padding-x)] text-muted-foreground",
2110
2610
  className
2111
2611
  ),
@@ -2210,7 +2710,7 @@ var Input = React6.forwardRef((props, forwardedRef) => {
2210
2710
  ]
2211
2711
  }
2212
2712
  );
2213
- });
2713
+ };
2214
2714
  Input.displayName = "Input";
2215
2715
  function Avatar({ src, alt, isOnline, size = "md" }) {
2216
2716
  const sizeClasses = size === "sm" ? "w-10 h-10" : "w-10 h-10";
@@ -2271,16 +2771,16 @@ function TeamChat({
2271
2771
  "div",
2272
2772
  {
2273
2773
  className: cn(
2274
- "w-full max-w-5xl mx-auto p-6 bg-white dark:bg-black rounded-[var(--radius-3xl)] shadow-lg flex flex-col h-[550px] border border-[var(--neutral-7)]",
2774
+ "w-full max-w-[var(--container-content)] mx-auto p-6 bg-white dark:bg-black rounded-[var(--radius-3xl)] shadow-lg flex flex-col h-[550px] border border-border",
2275
2775
  className
2276
2776
  ),
2277
2777
  children: [
2278
- /* @__PURE__ */ jsxs("header", { className: "flex justify-between items-center border-b border-[var(--neutral-7)] pb-3 mb-6", children: [
2778
+ /* @__PURE__ */ jsxs("header", { className: "flex justify-between items-center border-b border-border pb-3 mb-6", children: [
2279
2779
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
2280
- /* @__PURE__ */ jsx(Users, { className: "w-8 h-8 text-black dark:text-white" }),
2780
+ /* @__PURE__ */ jsx(Users, { className: "w-8 h-8 text-black" }),
2281
2781
  /* @__PURE__ */ jsxs("div", { children: [
2282
- /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold text-black dark:text-white", children: chatName }),
2283
- tagline && /* @__PURE__ */ jsx("p", { className: "italic text-sm text-[var(--neutral-10)]", children: tagline })
2782
+ /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold text-black", children: chatName }),
2783
+ tagline && /* @__PURE__ */ jsx("p", { className: "italic text-sm text-muted-foreground", children: tagline })
2284
2784
  ] })
2285
2785
  ] }),
2286
2786
  /* @__PURE__ */ jsx(
@@ -2289,13 +2789,13 @@ function TeamChat({
2289
2789
  type: "button",
2290
2790
  "aria-label": "More options",
2291
2791
  onClick: onMoreOptions,
2292
- className: "p-2 rounded-[var(--radius-lg)] hover:bg-[var(--neutral-3)] transition",
2293
- children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "w-6 h-6 text-[var(--neutral-10)]" })
2792
+ className: "p-2 rounded-[var(--radius-lg)] hover:bg-muted transition",
2793
+ children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "w-6 h-6 text-muted-foreground" })
2294
2794
  }
2295
2795
  )
2296
2796
  ] }),
2297
- /* @__PURE__ */ jsxs("main", { className: "flex flex-1 overflow-hidden rounded-[var(--radius-xl)] border border-[var(--neutral-7)]", children: [
2298
- /* @__PURE__ */ jsx("aside", { className: "w-56 bg-[var(--neutral-2)] border-r border-[var(--neutral-7)] p-4 overflow-y-auto", children: uniqueSenders.map((sender) => {
2797
+ /* @__PURE__ */ jsxs("main", { className: "flex flex-1 overflow-hidden rounded-[var(--radius-xl)] border border-border", children: [
2798
+ /* @__PURE__ */ jsx("aside", { className: "w-56 bg-muted border-r border-border p-4 overflow-y-auto", children: uniqueSenders.map((sender) => {
2299
2799
  const isSelected = selectedSender === sender.name;
2300
2800
  return /* @__PURE__ */ jsxs(
2301
2801
  "button",
@@ -2304,7 +2804,7 @@ function TeamChat({
2304
2804
  onClick: () => setSelectedSender(isSelected ? null : sender.name),
2305
2805
  className: cn(
2306
2806
  "flex items-center gap-3 w-full p-3 mb-3 rounded-[var(--radius-lg)] transition-colors",
2307
- isSelected ? "bg-black dark:bg-white text-white dark:text-black" : "hover:bg-[var(--neutral-4)] text-[var(--neutral-12)]"
2807
+ isSelected ? "bg-black text-white dark:text-black" : "hover:bg-muted text-foreground"
2308
2808
  ),
2309
2809
  children: [
2310
2810
  /* @__PURE__ */ jsx(Avatar, { src: sender.avatar, alt: sender.name, isOnline: sender.isOnline }),
@@ -2314,57 +2814,50 @@ function TeamChat({
2314
2814
  sender.name
2315
2815
  );
2316
2816
  }) }),
2317
- /* @__PURE__ */ jsx("section", { className: "flex-1 p-6 overflow-y-auto bg-white dark:bg-black", children: filteredMessages.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-center text-[var(--neutral-9)]", children: "No messages to display." }) : filteredMessages.map((message) => /* @__PURE__ */ jsxs(
2318
- "div",
2319
- {
2320
- className: "mb-6 last:mb-0 group border-b border-[var(--neutral-6)] pb-4",
2321
- children: [
2322
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 mb-2", children: [
2323
- /* @__PURE__ */ jsx(Avatar, { src: message.sender.avatar, alt: message.sender.name }),
2324
- /* @__PURE__ */ jsxs("div", { children: [
2325
- /* @__PURE__ */ jsx("p", { className: "font-semibold text-black dark:text-white", children: message.sender.name }),
2326
- /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--neutral-9)]", children: message.timestamp })
2327
- ] })
2328
- ] }),
2329
- /* @__PURE__ */ jsx("p", { className: "text-[var(--neutral-12)] text-lg mb-1", children: message.content }),
2330
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm text-[var(--neutral-10)]", children: [
2331
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2332
- message.status === "read" && /* @__PURE__ */ jsx(DoubleCheck, { className: "w-5 h-5 text-green-500" }),
2333
- message.status === "delivered" && /* @__PURE__ */ jsx(Check, { className: "w-5 h-5" }),
2334
- /* @__PURE__ */ jsx("span", { children: message.timestamp })
2335
- ] }),
2336
- /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: message.reactions?.map((reaction) => /* @__PURE__ */ jsxs(
2337
- "button",
2338
- {
2339
- type: "button",
2340
- onClick: () => onReactionClick?.(message.id, reaction.emoji),
2341
- className: cn(
2342
- "px-2 py-1 rounded-[var(--radius-md)] text-sm transition-colors",
2343
- reaction.reacted ? "bg-[var(--neutral-5)] text-black dark:text-white" : "bg-[var(--neutral-3)] text-[var(--neutral-11)]",
2344
- "hover:bg-[var(--neutral-4)]"
2345
- ),
2346
- children: [
2347
- reaction.emoji,
2348
- " ",
2349
- reaction.count
2350
- ]
2351
- },
2352
- reaction.emoji
2353
- )) })
2354
- ] })
2355
- ]
2356
- },
2357
- message.id
2358
- )) })
2817
+ /* @__PURE__ */ jsx("section", { className: "flex-1 p-6 overflow-y-auto bg-white dark:bg-black", children: filteredMessages.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-center text-muted-foreground", children: "No messages to display." }) : filteredMessages.map((message) => /* @__PURE__ */ jsxs("div", { className: "mb-6 last:mb-0 group border-b border-border pb-4", children: [
2818
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 mb-2", children: [
2819
+ /* @__PURE__ */ jsx(Avatar, { src: message.sender.avatar, alt: message.sender.name }),
2820
+ /* @__PURE__ */ jsxs("div", { children: [
2821
+ /* @__PURE__ */ jsx("p", { className: "font-semibold text-black", children: message.sender.name }),
2822
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: message.timestamp })
2823
+ ] })
2824
+ ] }),
2825
+ /* @__PURE__ */ jsx("p", { className: "text-foreground text-lg mb-1", children: message.content }),
2826
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm text-muted-foreground", children: [
2827
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2828
+ message.status === "read" && /* @__PURE__ */ jsx(DoubleCheck, { className: "w-5 h-5 text-green-500" }),
2829
+ message.status === "delivered" && /* @__PURE__ */ jsx(Check, { className: "w-5 h-5" }),
2830
+ /* @__PURE__ */ jsx("span", { children: message.timestamp })
2831
+ ] }),
2832
+ /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: message.reactions?.map((reaction) => /* @__PURE__ */ jsxs(
2833
+ "button",
2834
+ {
2835
+ type: "button",
2836
+ onClick: () => onReactionClick?.(message.id, reaction.emoji),
2837
+ className: cn(
2838
+ "px-2 py-1 rounded-[var(--radius-md)] text-sm transition-colors",
2839
+ reaction.reacted ? "bg-[var(--neutral-5)] text-black" : "bg-muted text-muted-foreground",
2840
+ "hover:bg-muted"
2841
+ ),
2842
+ children: [
2843
+ reaction.emoji,
2844
+ " ",
2845
+ reaction.count
2846
+ ]
2847
+ },
2848
+ reaction.emoji
2849
+ )) })
2850
+ ] })
2851
+ ] }, message.id)) })
2359
2852
  ] }),
2360
- /* @__PURE__ */ jsxs("footer", { className: "mt-6 flex items-center gap-4 border-t border-[var(--neutral-7)] pt-4", children: [
2853
+ /* @__PURE__ */ jsxs("footer", { className: "mt-6 flex items-center gap-4 border-t border-border pt-4", children: [
2361
2854
  /* @__PURE__ */ jsx(
2362
2855
  "button",
2363
2856
  {
2364
2857
  type: "button",
2365
2858
  "aria-label": "Add emoji",
2366
- className: "p-3 rounded-full bg-[var(--neutral-3)] hover:bg-[var(--neutral-4)] transition",
2367
- children: /* @__PURE__ */ jsx(FacePlus, { className: "w-6 h-6 text-[var(--neutral-10)]" })
2859
+ className: "p-3 rounded-full bg-muted hover:bg-muted transition",
2860
+ children: /* @__PURE__ */ jsx(FacePlus, { className: "w-6 h-6 text-muted-foreground" })
2368
2861
  }
2369
2862
  ),
2370
2863
  /* @__PURE__ */ jsx(
@@ -2383,7 +2876,7 @@ function TeamChat({
2383
2876
  type: "button",
2384
2877
  "aria-label": "Send message",
2385
2878
  onClick: handleSend,
2386
- className: "p-3 rounded-full bg-black dark:bg-white text-white dark:text-black hover:brightness-90 transition",
2879
+ className: "p-3 rounded-full bg-black text-white dark:text-black hover:brightness-90 transition",
2387
2880
  children: /* @__PURE__ */ jsx(PaperAirplane, { className: "w-6 h-6" })
2388
2881
  }
2389
2882
  )
@@ -2393,6 +2886,6 @@ function TeamChat({
2393
2886
  );
2394
2887
  }
2395
2888
 
2396
- export { AnimateIn, AnimateInGroup, AsciiText, ChangelogWidget, NodeGraphCanvas, OnboardingChecklist, PromptInputBox, TeamChat };
2889
+ export { AnimateIn, AnimateInGroup, AnimatedCard, AnimatedDrawer, AnimatedList, AnimatedModal, AnimatedPopover, AsciiText, ChangelogWidget, FadeIn, ImageViewDialog, MAX_VISUALIZER_BARS, NodeGraphCanvas, OnboardingChecklist, PageTransition, PromptAttachmentPreviews, PromptInputBox, ScaleIn, SlideIn, TeamChat, VISUALIZER_BAR_STYLES, VoiceRecorder, animatedList, animatedListItem, animationDistances, animationDurations, animationEasings, animationSprings, animationTransitions, drawerSurface, fadeIn, formatRecordingTime, getFilePreviewKey, isImageFile, modalSurface, pageTransition, popoverSurface, preventDragDefaults, reducedMotionVariants, scaleIn, slideIn };
2397
2890
  //# sourceMappingURL=index.js.map
2398
2891
  //# sourceMappingURL=index.js.map