@nebutra/ui 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/components/index.d.ts +380 -0
  2. package/dist/components/index.js +2398 -0
  3. package/dist/components/index.js.map +1 -0
  4. package/dist/hooks/index.d.ts +106 -0
  5. package/dist/hooks/index.js +182 -0
  6. package/dist/hooks/index.js.map +1 -0
  7. package/dist/icons/index.d.ts +3 -0
  8. package/dist/icons/index.js +5 -0
  9. package/dist/icons/index.js.map +1 -0
  10. package/dist/index.d.ts +5 -0
  11. package/dist/index.js +31 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/layout/index.d.ts +296 -0
  14. package/dist/layout/index.js +1303 -0
  15. package/dist/layout/index.js.map +1 -0
  16. package/dist/layouts/index.d.ts +161 -0
  17. package/dist/layouts/index.js +611 -0
  18. package/dist/layouts/index.js.map +1 -0
  19. package/dist/patterns/index.d.ts +363 -0
  20. package/dist/patterns/index.js +3004 -0
  21. package/dist/patterns/index.js.map +1 -0
  22. package/dist/primitives/index.d.ts +9588 -0
  23. package/dist/primitives/index.js +26779 -0
  24. package/dist/primitives/index.js.map +1 -0
  25. package/dist/styles/brand-override.css +61 -0
  26. package/dist/tailwind.preset.d.ts +251 -0
  27. package/dist/tailwind.preset.js +273 -0
  28. package/dist/tailwind.preset.js.map +1 -0
  29. package/dist/theme/index.d.ts +150 -0
  30. package/dist/theme/index.js +28 -0
  31. package/dist/theme/index.js.map +1 -0
  32. package/dist/typography/fonts.css +110 -0
  33. package/dist/typography/index.d.ts +425 -0
  34. package/dist/typography/index.js +374 -0
  35. package/dist/typography/index.js.map +1 -0
  36. package/dist/utils/index.d.ts +61 -0
  37. package/dist/utils/index.js +55 -0
  38. package/dist/utils/index.js.map +1 -0
  39. package/package.json +16 -6
@@ -0,0 +1,2398 @@
1
+ "use client";
2
+ import { Button as Button$1 } from '@lobehub/ui';
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
+ 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';
9
+ 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';
13
+ import { clsx } from 'clsx';
14
+ 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';
18
+ import { Tooltip as Tooltip$1 } from '@base-ui/react/tooltip';
19
+ import { useAsciiText, slant } from 'react-ascii-text';
20
+ import { Handle, Position, ReactFlowProvider, ReactFlow, Background, Controls, Panel } from '@xyflow/react';
21
+ import '@xyflow/react/dist/style.css';
22
+ import { wouldCreateCycle } from '@nebutra/graph-model';
23
+ import { Input as Input$1 } from '@base-ui/react/input';
24
+
25
+ // src/components/index.ts
26
+ function cn(...inputs) {
27
+ return twMerge(clsx(inputs));
28
+ }
29
+ var Slot = React6.forwardRef(
30
+ ({ children, className, style, ...props }, ref) => {
31
+ const childArray = React6.Children.toArray(children);
32
+ const child = childArray.length === 1 ? childArray[0] : null;
33
+ if (child == null || !React6.isValidElement(child)) {
34
+ return /* @__PURE__ */ jsx("span", { ref, className, style, ...props, children });
35
+ }
36
+ const childProps = child.props;
37
+ const mergedRef = (node) => {
38
+ if (typeof ref === "function") ref(node);
39
+ else if (ref) ref.current = node;
40
+ const childRef = child.ref;
41
+ if (typeof childRef === "function") childRef(node);
42
+ else if (childRef && "current" in childRef) {
43
+ childRef.current = node;
44
+ }
45
+ };
46
+ const mergedClass = [className, childProps.className].filter(Boolean).join(" ");
47
+ const merged = {
48
+ ...childProps,
49
+ ...props,
50
+ ref: mergedRef,
51
+ className: mergedClass,
52
+ style: { ...childProps.style, ...style }
53
+ };
54
+ return React6.cloneElement(
55
+ child,
56
+ merged
57
+ );
58
+ }
59
+ );
60
+ Slot.displayName = "Slot";
61
+ var buttonVariants = cva(
62
+ [
63
+ "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",
67
+ "disabled:pointer-events-none disabled:opacity-50",
68
+ "aria-busy:cursor-wait"
69
+ ].join(" "),
70
+ {
71
+ variants: {
72
+ 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",
75
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
76
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
77
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
78
+ ghost: "hover:bg-accent hover:text-accent-foreground",
79
+ link: "text-primary underline-offset-4 hover:underline",
80
+ tertiary: "border border-transparent bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground hover:border-input",
81
+ warning: "bg-warning text-warning-foreground hover:bg-warning/90"
82
+ },
83
+ 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)
94
+ },
95
+ shape: {
96
+ default: "",
97
+ square: "",
98
+ circle: ""
99
+ }
100
+ },
101
+ 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" }
112
+ ],
113
+ defaultVariants: {
114
+ variant: "default",
115
+ size: "default",
116
+ shape: "default"
117
+ }
118
+ }
119
+ );
120
+ function Spinner({ className }) {
121
+ return /* @__PURE__ */ jsx(
122
+ "span",
123
+ {
124
+ "aria-hidden": "true",
125
+ className: cn(
126
+ "block rounded-full border-2 border-current border-t-transparent animate-[spinner_0.6s_linear_infinite]",
127
+ className
128
+ )
129
+ }
130
+ );
131
+ }
132
+ function getIconSizeClass(size) {
133
+ switch (size) {
134
+ case "tiny":
135
+ return "[&>svg]:size-3";
136
+ // 12px
137
+ case "sm":
138
+ return "[&>svg]:size-3.5";
139
+ // 14px
140
+ case "lg":
141
+ return "[&>svg]:size-4.5";
142
+ // 18px
143
+ default:
144
+ return "[&>svg]:size-4";
145
+ }
146
+ }
147
+ function getSpinnerSizeClass(size) {
148
+ switch (size) {
149
+ case "tiny":
150
+ return "size-3";
151
+ // 12px
152
+ case "sm":
153
+ return "size-3.5";
154
+ // 14px
155
+ case "lg":
156
+ return "size-4.5";
157
+ // 18px
158
+ default:
159
+ return "size-4";
160
+ }
161
+ }
162
+ var SHADOW_CLASSES = {
163
+ sm: "shadow-sm",
164
+ md: "shadow-md",
165
+ lg: "shadow-lg"
166
+ };
167
+ function resolveShadowClass(shadow) {
168
+ if (!shadow) return void 0;
169
+ const level = shadow === true ? "md" : shadow;
170
+ return SHADOW_CLASSES[level];
171
+ }
172
+ function ButtonContent({ loading, prefix, suffix, size, children }) {
173
+ const iconSizeClass = getIconSizeClass(size);
174
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
175
+ loading && /* @__PURE__ */ jsx(Spinner, { className: getSpinnerSizeClass(size) }),
176
+ prefix != null && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("shrink-0", iconSizeClass), children: prefix }),
177
+ children,
178
+ suffix != null && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("shrink-0", iconSizeClass), children: suffix })
179
+ ] });
180
+ }
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,
296
+ {
297
+ 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
+ ),
302
+ ...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
332
+ }
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
412
+ };
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};
428
+
429
+ // src/tokens/components/textarea.ts
430
+ var textareaTokens = {
431
+ sizes: {
432
+ sm: {
433
+ minHeight: primitiveSpacing[20],
434
+ paddingX: primitiveSpacing[2],
435
+ paddingY: primitiveSpacing[2],
436
+ fontSize: primitiveFontSize.xs,
437
+ radius: primitiveRadius.md
438
+ },
439
+ md: {
440
+ minHeight: primitiveSpacing[24],
441
+ paddingX: primitiveSpacing[3],
442
+ paddingY: primitiveSpacing[2],
443
+ fontSize: primitiveFontSize.sm,
444
+ radius: primitiveRadius.md
445
+ },
446
+ lg: {
447
+ minHeight: primitiveSpacing[24],
448
+ paddingX: primitiveSpacing[4],
449
+ paddingY: primitiveSpacing[3],
450
+ fontSize: primitiveFontSize.base,
451
+ radius: primitiveRadius.lg
452
+ }
453
+ },
454
+ labelSize: primitiveFontSize.sm,
455
+ focusRingWidth: 3,
456
+ fieldGap: primitiveSpacing[2] - primitiveSpacing[1] / 2};
457
+ var fieldMessageClassName = "text-xs text-muted-foreground";
458
+ var fieldErrorClassName = "text-xs font-medium text-destructive";
459
+ function getTextareaStyle(size, style) {
460
+ const token = textareaTokens.sizes[size];
461
+ return {
462
+ "--textarea-min-height": `${token.minHeight}px`,
463
+ "--textarea-padding-x": `${token.paddingX}px`,
464
+ "--textarea-padding-y": `${token.paddingY}px`,
465
+ "--textarea-font-size": `${token.fontSize}px`,
466
+ "--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;
537
+ }
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
+ );
559
+ }
560
+ );
561
+ Textarea.displayName = "Textarea";
562
+ var DEFAULT_TOOLTIP_DELAY_MS = 150;
563
+ var TooltipProvider = ({
564
+ children,
565
+ delayDuration = DEFAULT_TOOLTIP_DELAY_MS
566
+ }) => {
567
+ return /* @__PURE__ */ jsx(Tooltip$1.Provider, { delay: delayDuration, children });
568
+ };
569
+ var Tooltip = ({
570
+ delayDuration,
571
+ children,
572
+ ...props
573
+ }) => {
574
+ const delay = delayDuration ?? DEFAULT_TOOLTIP_DELAY_MS;
575
+ return /* @__PURE__ */ jsx(Tooltip$1.Provider, { delay, children: /* @__PURE__ */ jsx(Tooltip$1.Root, { ...props, children }) });
576
+ };
577
+ var TooltipTrigger = React6.forwardRef(({ asChild, children, ...props }, ref) => {
578
+ if (asChild && React6.isValidElement(children)) {
579
+ return /* @__PURE__ */ jsx(
580
+ Tooltip$1.Trigger,
581
+ {
582
+ ref,
583
+ ...props,
584
+ render: children
585
+ }
586
+ );
587
+ }
588
+ return /* @__PURE__ */ jsx(Tooltip$1.Trigger, { ref, ...props, children });
589
+ });
590
+ 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
+ );
614
+ TooltipContent.displayName = "TooltipContent";
615
+ var cn2 = (...classes) => classes.filter(Boolean).join(" ");
616
+ var VoiceRecorder = ({
617
+ isRecording,
618
+ onStartRecording,
619
+ onStopRecording,
620
+ visualizerBars = 32
621
+ }) => {
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
+ };
645
+ return /* @__PURE__ */ jsxs(
646
+ "div",
647
+ {
648
+ className: cn2(
649
+ "flex flex-col items-center justify-center w-full transition-all duration-300 py-3",
650
+ isRecording ? "opacity-100" : "opacity-0 h-0"
651
+ ),
652
+ children: [
653
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
654
+ /* @__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) })
656
+ ] }),
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(
658
+ "div",
659
+ {
660
+ className: "w-0.5 rounded-full bg-white/50 animate-pulse",
661
+ style: {
662
+ height: `${Math.max(15, Math.random() * 100)}%`,
663
+ animationDelay: `${i * 0.05}s`,
664
+ animationDuration: `${0.5 + Math.random() * 0.5}s`
665
+ }
666
+ },
667
+ i
668
+ )) })
669
+ ]
670
+ }
671
+ );
672
+ };
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
+ ] }) });
697
+ };
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
+ });
707
+ 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;
711
+ }
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
+ );
761
+ PromptInput.displayName = "PromptInput";
762
+ var PromptInputTextarea = ({ className, onKeyDown, disableAutosize = false, placeholder, style, ...props }) => {
763
+ const { value, setValue, maxHeight, onSubmit, disabled } = usePromptInput();
764
+ const textareaRef = React6__default.useRef(null);
765
+ React6__default.useEffect(() => {
766
+ if (disableAutosize || !textareaRef.current) return;
767
+ textareaRef.current.style.height = "auto";
768
+ textareaRef.current.style.height = typeof maxHeight === "number" ? `${Math.min(textareaRef.current.scrollHeight, maxHeight)}px` : `min(${textareaRef.current.scrollHeight}px, ${maxHeight})`;
769
+ }, [maxHeight, disableAutosize]);
770
+ const handleKeyDown = (e) => {
771
+ if (e.key === "Enter" && !e.shiftKey) {
772
+ e.preventDefault();
773
+ onSubmit?.();
774
+ }
775
+ onKeyDown?.(e);
776
+ };
777
+ return /* @__PURE__ */ jsx(
778
+ Textarea,
779
+ {
780
+ ref: textareaRef,
781
+ value,
782
+ onChange: (e) => setValue(e.target.value),
783
+ onKeyDown: handleKeyDown,
784
+ rows: 1,
785
+ style: { "--textarea-min-height": "2rem", ...style },
786
+ className: cn2("resize-none border-0 shadow-none focus-visible:ring-0", className),
787
+ disabled,
788
+ placeholder,
789
+ ...props
790
+ }
791
+ );
792
+ };
793
+ var PromptInputActions = ({
794
+ children,
795
+ className,
796
+ ...props
797
+ }) => /* @__PURE__ */ jsx("div", { className: cn2("flex items-center gap-2", className), ...props, children });
798
+ var PromptInputAction = ({
799
+ tooltip,
800
+ children,
801
+ className,
802
+ side = "top",
803
+ ...props
804
+ }) => {
805
+ const { disabled } = usePromptInput();
806
+ return /* @__PURE__ */ jsxs(Tooltip, { ...props, children: [
807
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, disabled, children }),
808
+ /* @__PURE__ */ jsx(TooltipContent, { side, className: className || "", children: tooltip })
809
+ ] });
810
+ };
811
+ var CustomDivider = () => /* @__PURE__ */ jsx("div", { className: "relative h-6 w-[1.5px] mx-1", children: /* @__PURE__ */ jsx(
812
+ "div",
813
+ {
814
+ className: "absolute inset-0 bg-gradient-to-t from-transparent via-cyan/40 to-transparent rounded-full",
815
+ style: {
816
+ clipPath: "polygon(0% 0%, 100% 0%, 100% 40%, 140% 50%, 100% 60%, 100% 100%, 0% 100%, 0% 60%, -40% 50%, 0% 40%)"
817
+ }
818
+ }
819
+ ) });
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);
846
+ }
847
+ };
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;
857
+ }
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
+ }
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;
906
+ }
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
+ ),
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)) }),
998
+ /* @__PURE__ */ jsx(
999
+ "div",
1000
+ {
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
+ )
1012
+ }
1013
+ ),
1014
+ isRecording && /* @__PURE__ */ jsx(
1015
+ VoiceRecorder,
1016
+ {
1017
+ 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
+ )
1207
+ }
1208
+ )
1209
+ ] })
1210
+ ]
1211
+ }
1212
+ ),
1213
+ /* @__PURE__ */ jsx(ImageViewDialog, { imageUrl: selectedImage, onClose: () => setSelectedImage(null) })
1214
+ ] });
1215
+ }
1216
+ );
1217
+ PromptInputBox.displayName = "PromptInputBox";
1218
+ var PRESETS = {
1219
+ emerge: {
1220
+ initial: emerge.initial,
1221
+ animate: emerge.animate,
1222
+ exit: emerge.exit,
1223
+ transition: emerge.transition
1224
+ },
1225
+ flow: {
1226
+ initial: flow.initial,
1227
+ animate: flow.animate,
1228
+ exit: flow.exit,
1229
+ transition: flow.transition
1230
+ },
1231
+ fade: {
1232
+ initial: { opacity: 0 },
1233
+ animate: { opacity: 1 },
1234
+ exit: { opacity: 0 },
1235
+ transition: { duration: 0.3 }
1236
+ },
1237
+ fadeUp: {
1238
+ initial: { opacity: 0, y: 16 },
1239
+ animate: { opacity: 1, y: 0 },
1240
+ exit: { opacity: 0, y: 16 },
1241
+ transition: brandSpring
1242
+ },
1243
+ scale: {
1244
+ initial: { opacity: 0, scale: 0.95 },
1245
+ animate: { opacity: 1, scale: 1 },
1246
+ exit: { opacity: 0, scale: 0.95 },
1247
+ transition: brandSpring
1248
+ }
1249
+ };
1250
+ var STAGGER = { fast: 0.05, normal: 0.1, slow: 0.2 };
1251
+ function MotionDiv(props) {
1252
+ return /* @__PURE__ */ jsx(LazyMotion, { features: domAnimation, children: /* @__PURE__ */ jsx(m.div, { ...props }) });
1253
+ }
1254
+ function AnimateIn(props) {
1255
+ const { children, preset = "emerge", delay = 0, duration, inView = false, className } = props;
1256
+ const shouldReduce = useReducedMotion();
1257
+ const { initial, animate, exit, transition } = PRESETS[preset];
1258
+ const t = {
1259
+ ...transition,
1260
+ ...delay ? { delay } : {},
1261
+ ...duration ? { duration } : {}
1262
+ };
1263
+ if (shouldReduce) return /* @__PURE__ */ jsx("div", { className, children });
1264
+ if (inView) {
1265
+ return /* @__PURE__ */ jsx(
1266
+ MotionDiv,
1267
+ {
1268
+ className,
1269
+ initial,
1270
+ whileInView: animate,
1271
+ exit,
1272
+ viewport: { once: true, margin: "-10%" },
1273
+ transition: t,
1274
+ children
1275
+ }
1276
+ );
1277
+ }
1278
+ return /* @__PURE__ */ jsx(MotionDiv, { className, initial, animate, exit, transition: t, children });
1279
+ }
1280
+ function AnimateInGroup(props) {
1281
+ const { children, stagger = "normal", inView = false, className } = props;
1282
+ const shouldReduce = useReducedMotion();
1283
+ const variants = {
1284
+ initial: {},
1285
+ animate: { transition: { staggerChildren: STAGGER[stagger] } }
1286
+ };
1287
+ if (shouldReduce) return /* @__PURE__ */ jsx("div", { className, children });
1288
+ if (inView) {
1289
+ return /* @__PURE__ */ jsx(
1290
+ MotionDiv,
1291
+ {
1292
+ className,
1293
+ initial: "initial",
1294
+ whileInView: "animate",
1295
+ viewport: { once: true, margin: "-10%" },
1296
+ variants,
1297
+ children
1298
+ }
1299
+ );
1300
+ }
1301
+ return /* @__PURE__ */ jsx(MotionDiv, { className, initial: "initial", animate: "animate", variants, children });
1302
+ }
1303
+ function AsciiText({ text, className = "", font = slant }) {
1304
+ const asciiTextRef = useAsciiText({
1305
+ font,
1306
+ text
1307
+ });
1308
+ return /* @__PURE__ */ jsx(
1309
+ "pre",
1310
+ {
1311
+ ref: asciiTextRef,
1312
+ className: `font-mono leading-none m-0 p-0 ${className}`
1313
+ }
1314
+ );
1315
+ }
1316
+ var TAG_COLOR_MAP = {
1317
+ feature: "bg-[var(--cyan-9)] text-white",
1318
+ fix: "bg-[var(--status-success)] text-white",
1319
+ breaking: "bg-[var(--status-danger)] text-white",
1320
+ improvement: "bg-[var(--status-warning)] text-white",
1321
+ security: "bg-[var(--status-danger)] text-white",
1322
+ experimental: "bg-[var(--neutral-8)] text-[var(--neutral-12)]"
1323
+ };
1324
+ function ChangelogWidget({
1325
+ entries,
1326
+ changelogUrl = "/changelog",
1327
+ className
1328
+ }) {
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]);
1337
+ 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;
1365
+ };
1366
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), ref: dropdownRef, children: [
1367
+ /* @__PURE__ */ jsxs(
1368
+ "button",
1369
+ {
1370
+ type: "button",
1371
+ "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)]",
1375
+ children: [
1376
+ /* @__PURE__ */ jsx(Bell, { className: "h-5 w-5 text-[var(--neutral-11)]" }),
1377
+ 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
+ ]
1379
+ }
1380
+ ),
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
+ ] })
1425
+ ] });
1426
+ }
1427
+ var GRAPH_NODE_FLOW_TYPE = "graph-node";
1428
+ function graphToFlow(graph, edgeIdentity) {
1429
+ const nodes = graph.nodes.map((n) => ({
1430
+ id: n.id,
1431
+ type: GRAPH_NODE_FLOW_TYPE,
1432
+ position: { x: n.x, y: n.y },
1433
+ data: { node: n }
1434
+ }));
1435
+ const edges = graph.edges.map((e) => ({
1436
+ id: edgeIdentity(e),
1437
+ source: e.from,
1438
+ target: e.to,
1439
+ data: { edge: e }
1440
+ }));
1441
+ return { nodes, edges };
1442
+ }
1443
+ function applyNodePositions(graph, flowNodes) {
1444
+ const posById = new Map(flowNodes.map((n) => [n.id, n.position]));
1445
+ const nodes = graph.nodes.map((n) => {
1446
+ const pos = posById.get(n.id);
1447
+ if (!pos || pos.x === n.x && pos.y === n.y) return n;
1448
+ return { ...n, x: pos.x, y: pos.y };
1449
+ });
1450
+ return { ...graph, nodes };
1451
+ }
1452
+ function tryAddEdge(graph, connection, opts) {
1453
+ if (!connection.source || !connection.target) {
1454
+ return { ok: false, reason: "Connection needs both a source and a target node." };
1455
+ }
1456
+ if (connection.source === connection.target) {
1457
+ return { ok: false, reason: "A node cannot connect to itself." };
1458
+ }
1459
+ const candidate = opts.makeEdge(connection.source, connection.target, connection.targetHandle);
1460
+ if (!candidate) {
1461
+ return { ok: false, reason: "That connection is not allowed between these nodes." };
1462
+ }
1463
+ const candidateId = opts.edgeIdentity(candidate);
1464
+ if (graph.edges.some((e) => opts.edgeIdentity(e) === candidateId)) {
1465
+ return { ok: false, reason: "That connection already exists between these nodes." };
1466
+ }
1467
+ if (wouldCreateCycle(graph.edges, candidate.from, candidate.to)) {
1468
+ return {
1469
+ ok: false,
1470
+ reason: "That connection would create a cycle. The graph must stay acyclic (DAG)."
1471
+ };
1472
+ }
1473
+ return { ok: true, graph: { ...graph, edges: [...graph.edges, candidate] } };
1474
+ }
1475
+ function removeNode(graph, nodeId) {
1476
+ return {
1477
+ ...graph,
1478
+ nodes: graph.nodes.filter((n) => n.id !== nodeId),
1479
+ edges: graph.edges.filter((e) => e.from !== nodeId && e.to !== nodeId)
1480
+ };
1481
+ }
1482
+ function removeFlowEdge(graph, flowEdgeId, edgeIdentity) {
1483
+ return {
1484
+ ...graph,
1485
+ edges: graph.edges.filter((e) => edgeIdentity(e) !== flowEdgeId)
1486
+ };
1487
+ }
1488
+ var XYFLOW_TOKEN_THEME = {
1489
+ ["--xy-background-pattern-color"]: "var(--neutral-6)",
1490
+ ["--xy-edge-stroke"]: "var(--neutral-8)",
1491
+ ["--xy-connectionline-stroke"]: "var(--neutral-8)",
1492
+ ["--xy-handle-background-color"]: "var(--neutral-9)",
1493
+ ["--xy-handle-border-color"]: "var(--neutral-7)",
1494
+ ["--xy-controls-button-background-color"]: "var(--neutral-2)",
1495
+ ["--xy-controls-button-border-color"]: "var(--neutral-7)",
1496
+ ["--xy-controls-button-color"]: "var(--neutral-11)"
1497
+ };
1498
+ var nodeCardVariants = cva(
1499
+ "min-w-[160px] rounded-lg border bg-neutral-2 px-3 py-2 text-neutral-12 shadow-sm transition-colors",
1500
+ {
1501
+ variants: { ready: { true: "border-success", false: "border-neutral-7" } },
1502
+ defaultVariants: { ready: false }
1503
+ }
1504
+ );
1505
+ function NodeGraphCanvas({
1506
+ graph,
1507
+ onChange,
1508
+ edgeIdentity,
1509
+ makeEdge,
1510
+ renderNode,
1511
+ readOnly = false,
1512
+ className
1513
+ }) {
1514
+ const statusId = useId();
1515
+ 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;
1567
+ let next = graph;
1568
+ for (const r of removed) next = removeFlowEdge(next, r.id, edgeIdentity);
1569
+ 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
+ );
1586
+ return /* @__PURE__ */ jsx(AnimateIn, { preset: "emerge", children: /* @__PURE__ */ jsx(
1587
+ "div",
1588
+ {
1589
+ style: XYFLOW_TOKEN_THEME,
1590
+ className: cn(
1591
+ "relative h-[480px] w-full overflow-hidden rounded-xl border border-neutral-7 bg-neutral-1",
1592
+ className
1593
+ ),
1594
+ children: /* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsxs(
1595
+ ReactFlow,
1596
+ {
1597
+ nodes,
1598
+ edges,
1599
+ nodeTypes,
1600
+ onNodesChange,
1601
+ onEdgesChange,
1602
+ onConnect,
1603
+ nodesDraggable: !readOnly,
1604
+ nodesConnectable: !readOnly,
1605
+ elementsSelectable: !readOnly,
1606
+ fitView: true,
1607
+ proOptions: { hideAttribution: true },
1608
+ children: [
1609
+ /* @__PURE__ */ jsx(Background, { color: "var(--neutral-6)" }),
1610
+ /* @__PURE__ */ jsx(Controls, { showInteractive: !readOnly }),
1611
+ rejection ? /* @__PURE__ */ jsx(Panel, { position: "top-center", children: /* @__PURE__ */ jsxs(
1612
+ "div",
1613
+ {
1614
+ id: statusId,
1615
+ role: "alert",
1616
+ "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",
1618
+ children: [
1619
+ /* @__PURE__ */ jsx("span", { children: rejection }),
1620
+ /* @__PURE__ */ jsx(
1621
+ Button$1,
1622
+ {
1623
+ size: "small",
1624
+ type: "text",
1625
+ icon: /* @__PURE__ */ jsx(CrossSmall, { size: 14 }),
1626
+ "aria-label": "Dismiss connection error",
1627
+ onClick: () => setRejection(null),
1628
+ children: "Dismiss"
1629
+ }
1630
+ )
1631
+ ]
1632
+ }
1633
+ ) }) : null
1634
+ ]
1635
+ }
1636
+ ) })
1637
+ }
1638
+ ) });
1639
+ }
1640
+ var containerVariants = {
1641
+ hidden: { opacity: 0, y: 20 },
1642
+ visible: {
1643
+ opacity: 1,
1644
+ y: 0,
1645
+ transition: {
1646
+ duration: 0.5,
1647
+ ease: "easeOut",
1648
+ when: "beforeChildren",
1649
+ staggerChildren: 0.1
1650
+ }
1651
+ }
1652
+ };
1653
+ var itemVariants = {
1654
+ hidden: { opacity: 0, x: -20 },
1655
+ visible: {
1656
+ opacity: 1,
1657
+ x: 0,
1658
+ transition: {
1659
+ duration: 0.4,
1660
+ ease: "easeOut"
1661
+ }
1662
+ }
1663
+ };
1664
+ function OnboardingChecklist({
1665
+ title,
1666
+ description,
1667
+ items,
1668
+ videoThumbnailUrl,
1669
+ videoUrl,
1670
+ className
1671
+ }) {
1672
+ return /* @__PURE__ */ jsx(
1673
+ motion.div,
1674
+ {
1675
+ initial: "hidden",
1676
+ animate: "visible",
1677
+ variants: containerVariants,
1678
+ className: cn(
1679
+ "w-full max-w-4xl mx-auto bg-card text-card-foreground border rounded-[var(--radius-2xl)] shadow-sm p-8 overflow-hidden",
1680
+ className
1681
+ ),
1682
+ children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-8 items-center", children: [
1683
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1684
+ /* @__PURE__ */ jsx("h2", { className: "text-2xl font-bold tracking-tight", children: title }),
1685
+ /* @__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: [
1687
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start", children: [
1688
+ /* @__PURE__ */ jsx(CheckCircle, { className: "h-5 w-5 text-green-500 mt-0.5 flex-shrink-0" }),
1689
+ /* @__PURE__ */ jsx("span", { className: "ml-3 text-sm font-medium", children: item.text })
1690
+ ] }),
1691
+ item.helperText && item.helperLink && /* @__PURE__ */ jsxs("div", { className: "ml-8 mt-1 text-xs text-muted-foreground", children: [
1692
+ item.helperText,
1693
+ " ",
1694
+ /* @__PURE__ */ jsx(
1695
+ "a",
1696
+ {
1697
+ 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)]",
1699
+ children: item.helperLink.text
1700
+ }
1701
+ )
1702
+ ] })
1703
+ ] }, item.id)) })
1704
+ ] }),
1705
+ /* @__PURE__ */ jsx(
1706
+ motion.div,
1707
+ {
1708
+ variants: itemVariants,
1709
+ className: "relative group rounded-[var(--radius-lg)] overflow-hidden cursor-pointer w-full aspect-video",
1710
+ children: /* @__PURE__ */ jsxs(Dialog, { children: [
1711
+ /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1712
+ "button",
1713
+ {
1714
+ 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)]",
1716
+ children: [
1717
+ /* @__PURE__ */ jsx(
1718
+ Image,
1719
+ {
1720
+ src: videoThumbnailUrl,
1721
+ alt: "Video guide thumbnail",
1722
+ className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105",
1723
+ fill: true
1724
+ }
1725
+ ),
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" }) })
1727
+ ]
1728
+ }
1729
+ ) }),
1730
+ /* @__PURE__ */ jsx(DialogContent, { className: "max-w-3xl p-0 border-0", children: /* @__PURE__ */ jsx("div", { className: "aspect-video", children: /* @__PURE__ */ jsx(
1731
+ "iframe",
1732
+ {
1733
+ src: videoUrl,
1734
+ title: "Onboarding Video Guide",
1735
+ frameBorder: "0",
1736
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
1737
+ allowFullScreen: true,
1738
+ className: "w-full h-full rounded-[var(--radius-lg)]"
1739
+ }
1740
+ ) }) })
1741
+ ] })
1742
+ }
1743
+ )
1744
+ ] })
1745
+ }
1746
+ );
1747
+ }
1748
+
1749
+ // src/tokens/components/input.ts
1750
+ var inputTokens = {
1751
+ sizes: {
1752
+ sm: {
1753
+ height: primitiveSizing.sm,
1754
+ paddingX: primitiveSpacing[2],
1755
+ fontSize: primitiveFontSize.xs,
1756
+ radius: primitiveRadius.md,
1757
+ affixInset: primitiveSpacing[2],
1758
+ affixWidth: primitiveSizing.sm,
1759
+ controlInset: primitiveSpacing[2] - primitiveSpacing[1] / 2,
1760
+ controlSize: primitiveSizing.xs,
1761
+ controlIconSize: primitiveSizing.xs - primitiveSpacing[2],
1762
+ iconSize: primitiveFontSize.sm
1763
+ },
1764
+ md: {
1765
+ height: primitiveSizing.md,
1766
+ paddingX: primitiveSpacing[3],
1767
+ fontSize: primitiveFontSize.sm,
1768
+ radius: primitiveRadius.md,
1769
+ affixInset: primitiveSpacing[3],
1770
+ affixWidth: primitiveSizing.sm + primitiveSpacing[1],
1771
+ controlInset: primitiveSpacing[2],
1772
+ controlSize: primitiveSizing.tiny,
1773
+ controlIconSize: primitiveSizing.tiny - primitiveSpacing[2],
1774
+ iconSize: primitiveFontSize.base
1775
+ },
1776
+ lg: {
1777
+ height: primitiveSizing.lg,
1778
+ paddingX: primitiveSpacing[4],
1779
+ fontSize: primitiveFontSize.base,
1780
+ radius: primitiveRadius.lg,
1781
+ affixInset: primitiveSpacing[4],
1782
+ affixWidth: primitiveSizing.md,
1783
+ controlInset: primitiveSpacing[3],
1784
+ controlSize: primitiveSizing.tiny,
1785
+ controlIconSize: primitiveSizing.tiny - primitiveSpacing[2],
1786
+ iconSize: primitiveFontSize.base
1787
+ }
1788
+ },
1789
+ focusRingWidth: 3,
1790
+ fieldGap: primitiveSpacing[2] - primitiveSpacing[1] / 2};
1791
+ var modifierOrder = ["meta", "shift", "alt", "ctrl"];
1792
+ var appleModifierSymbols = {
1793
+ meta: "\u2318",
1794
+ shift: "\u21E7",
1795
+ alt: "\u2325",
1796
+ ctrl: "\u2303"
1797
+ };
1798
+ var otherModifierSymbols = {
1799
+ meta: "Ctrl",
1800
+ shift: "Shift",
1801
+ alt: "Alt",
1802
+ ctrl: "Ctrl"
1803
+ };
1804
+ var modifierLabels = {
1805
+ meta: "Command or Control",
1806
+ shift: "Shift",
1807
+ alt: "Option or Alt",
1808
+ ctrl: "Control"
1809
+ };
1810
+ function getClientPlatform() {
1811
+ const platform = window.navigator.platform.toLowerCase();
1812
+ const userAgent = window.navigator.userAgent.toLowerCase();
1813
+ const isApple = /mac|iphone|ipad|ipod/.test(platform) || /mac|iphone|ipad|ipod/.test(userAgent);
1814
+ return isApple ? "apple" : "other";
1815
+ }
1816
+ function subscribePlatform() {
1817
+ return () => void 0;
1818
+ }
1819
+ function usePlatform() {
1820
+ return React6.useSyncExternalStore(subscribePlatform, getClientPlatform, () => "apple");
1821
+ }
1822
+ function normalizeKey(children) {
1823
+ if (typeof children !== "string") {
1824
+ return children;
1825
+ }
1826
+ const trimmed = children.trim();
1827
+ if (/^[a-z]$/.test(trimmed)) {
1828
+ return trimmed.toUpperCase();
1829
+ }
1830
+ return trimmed;
1831
+ }
1832
+ function getModifierSymbols(platform) {
1833
+ return platform === "apple" ? appleModifierSymbols : otherModifierSymbols;
1834
+ }
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
+ );
1870
+ Kbd.displayName = "Kbd";
1871
+ var inputVariants = cva(
1872
+ [
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)]",
1875
+ "transition-[background-color,border-color,box-shadow,color] duration-micro ease-out",
1876
+ "placeholder:text-muted-foreground",
1877
+ "outline-none focus:border-ring focus:ring-[length:var(--input-focus-ring-width)] focus:ring-ring/30",
1878
+ "disabled:cursor-not-allowed disabled:opacity-50",
1879
+ "read-only:bg-muted/70 read-only:cursor-default",
1880
+ "file:h-full file:border-0 file:border-e file:border-solid file:border-input file:bg-transparent",
1881
+ "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",
1883
+ "[&[type=search]::-webkit-search-cancel-button]:appearance-none",
1884
+ "[&[type=search]::-webkit-search-decoration]:appearance-none",
1885
+ "[&[type=search]::-webkit-search-results-button]:appearance-none",
1886
+ "[&[type=search]::-webkit-search-results-decoration]:appearance-none"
1887
+ ].join(" "),
1888
+ {
1889
+ variants: {
1890
+ size: {
1891
+ sm: "",
1892
+ md: "",
1893
+ lg: ""
1894
+ },
1895
+ affix: {
1896
+ none: "",
1897
+ prefix: "pl-[var(--input-padding-left)]",
1898
+ suffix: "pr-[var(--input-padding-right)]",
1899
+ both: "pl-[var(--input-padding-left)] pr-[var(--input-padding-right)]"
1900
+ }
1901
+ },
1902
+ defaultVariants: {
1903
+ size: "md",
1904
+ affix: "none"
1905
+ }
1906
+ }
1907
+ );
1908
+ var affixVariants = cva(
1909
+ [
1910
+ "pointer-events-none absolute top-1/2 flex -translate-y-1/2 items-center text-muted-foreground",
1911
+ "[&_svg:not([class*=size-])]:size-[var(--input-icon-size)]"
1912
+ ].join(" "),
1913
+ {
1914
+ variants: {
1915
+ side: {
1916
+ prefix: "left-[var(--input-affix-inset)]",
1917
+ suffix: "right-[var(--input-affix-inset)]"
1918
+ }
1919
+ }
1920
+ }
1921
+ );
1922
+ var inputControlButtonVariants = cva(
1923
+ [
1924
+ "absolute right-[var(--input-control-inset)] top-1/2 flex size-[var(--input-control-size)] -translate-y-1/2 items-center justify-center",
1925
+ "rounded-[var(--radius-sm)] text-muted-foreground transition-colors duration-micro ease-out",
1926
+ "hover:bg-accent hover:text-foreground",
1927
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
1928
+ "disabled:pointer-events-none disabled:opacity-50",
1929
+ "[&_svg]:size-[var(--input-control-icon-size)]"
1930
+ ].join(" ")
1931
+ );
1932
+ var fieldMessageVariants = cva("text-xs", {
1933
+ variants: {
1934
+ tone: {
1935
+ description: "text-muted-foreground",
1936
+ error: "font-medium text-destructive"
1937
+ }
1938
+ }
1939
+ });
1940
+ function assignRef(ref, value) {
1941
+ if (typeof ref === "function") {
1942
+ ref(value);
1943
+ return;
1944
+ }
1945
+ if (ref) {
1946
+ ref.current = value;
1947
+ }
1948
+ }
1949
+ function getInputStyle(size, style) {
1950
+ const token = inputTokens.sizes[size];
1951
+ return {
1952
+ "--input-height": `${token.height}px`,
1953
+ "--input-padding-x": `${token.paddingX}px`,
1954
+ "--input-padding-left": `${token.affixWidth}px`,
1955
+ "--input-padding-right": `${token.affixWidth}px`,
1956
+ "--input-font-size": `${token.fontSize}px`,
1957
+ "--input-radius": `${token.radius}px`,
1958
+ "--input-affix-inset": `${token.affixInset}px`,
1959
+ "--input-control-inset": `${token.controlInset}px`,
1960
+ "--input-control-size": `${token.controlSize}px`,
1961
+ "--input-control-icon-size": `${token.controlIconSize}px`,
1962
+ "--input-icon-size": `${token.iconSize}px`,
1963
+ "--input-focus-ring-width": `${inputTokens.focusRingWidth}px`,
1964
+ borderRadius: "var(--input-radius)",
1965
+ outline: "none",
1966
+ ...style
1967
+ };
1968
+ }
1969
+ function getAffixMode(prefix, hasRightAdornment) {
1970
+ if (prefix != null && hasRightAdornment) return "both";
1971
+ if (prefix != null) return "prefix";
1972
+ if (hasRightAdornment) return "suffix";
1973
+ return "none";
1974
+ }
1975
+ function isInvalid2(error, ariaInvalid) {
1976
+ return error === true || typeof error === "string" || ariaInvalid === true || ariaInvalid === "true";
1977
+ }
1978
+ function isTextAffix(value) {
1979
+ return typeof value === "string" && value.trim().length > 0;
1980
+ }
1981
+ function joinDescriptionIds2(...ids) {
1982
+ return ids.filter(Boolean).join(" ") || void 0;
1983
+ }
1984
+ var Input = React6.forwardRef((props, forwardedRef) => {
1985
+ const {
1986
+ className,
1987
+ type = "text",
1988
+ size = "md",
1989
+ prefix,
1990
+ suffix,
1991
+ clearable,
1992
+ onClear,
1993
+ onValueChange,
1994
+ shortcut,
1995
+ loading,
1996
+ revealable,
1997
+ revealLabel = "Show password",
1998
+ hideLabel = "Hide password",
1999
+ clearOnEscape,
2000
+ wrapperClassName,
2001
+ fieldClassName,
2002
+ label,
2003
+ description,
2004
+ error,
2005
+ value,
2006
+ defaultValue,
2007
+ onChange,
2008
+ onKeyDown,
2009
+ disabled,
2010
+ style,
2011
+ "aria-describedby": ariaDescribedBy,
2012
+ "aria-invalid": ariaInvalid,
2013
+ id,
2014
+ ...rest
2015
+ } = props;
2016
+ const isControlled = value !== void 0;
2017
+ const baseId = React6.useId();
2018
+ const inputRef = React6.useRef(null);
2019
+ const descriptionId = description && id ? `${id}-description` : void 0;
2020
+ const errorId = typeof error === "string" && id ? `${id}-error` : void 0;
2021
+ const prefixDescriptionId = isTextAffix(prefix) ? `${id ?? baseId}-prefix` : void 0;
2022
+ const suffixDescriptionId = isTextAffix(suffix) ? `${id ?? baseId}-suffix` : void 0;
2023
+ const [internalValue, setInternalValue] = React6.useState(
2024
+ typeof defaultValue === "string" || typeof defaultValue === "number" ? String(defaultValue) : ""
2025
+ );
2026
+ const [passwordVisible, setPasswordVisible] = React6.useState(false);
2027
+ const currentValue = isControlled ? String(value ?? "") : internalValue;
2028
+ const clearOnEscapeEnabled = clearOnEscape ?? type === "search";
2029
+ const canRevealPassword = Boolean(revealable && type === "password");
2030
+ const showClear = Boolean(clearable && !disabled && currentValue.length > 0 && !loading);
2031
+ const showShortcut = Boolean(shortcut && !loading && !showClear);
2032
+ const showSpinner = Boolean(loading);
2033
+ const showPasswordToggle = Boolean(canRevealPassword && !loading && !showClear && !showShortcut);
2034
+ const hasRightAdornment = showClear || showShortcut || showSpinner || showPasswordToggle || suffix != null;
2035
+ const hasControlWrapper = prefix != null || hasRightAdornment;
2036
+ const affix = getAffixMode(prefix, hasRightAdornment);
2037
+ const resolvedInvalid = isInvalid2(error, ariaInvalid);
2038
+ const describedBy = joinDescriptionIds2(
2039
+ ariaDescribedBy,
2040
+ prefixDescriptionId,
2041
+ suffixDescriptionId,
2042
+ descriptionId,
2043
+ errorId
2044
+ );
2045
+ const valueProps = isControlled ? { value } : clearable || shortcut || clearOnEscapeEnabled ? { value: currentValue } : { defaultValue };
2046
+ const setInputRef = React6.useCallback(
2047
+ (node) => {
2048
+ const input = node instanceof HTMLInputElement ? node : null;
2049
+ inputRef.current = input;
2050
+ assignRef(forwardedRef, input);
2051
+ },
2052
+ [forwardedRef]
2053
+ );
2054
+ const emitNativeInput = React6.useCallback(
2055
+ (nextValue) => {
2056
+ const input = inputRef.current;
2057
+ if (!input) {
2058
+ if (!isControlled) setInternalValue(nextValue);
2059
+ onValueChange?.(nextValue);
2060
+ return;
2061
+ }
2062
+ const valueSetter = Object.getOwnPropertyDescriptor(
2063
+ window.HTMLInputElement.prototype,
2064
+ "value"
2065
+ )?.set;
2066
+ valueSetter?.call(input, nextValue);
2067
+ input.dispatchEvent(new Event("input", { bubbles: true }));
2068
+ },
2069
+ [isControlled, onValueChange]
2070
+ );
2071
+ const syncInputValue = React6.useCallback(
2072
+ (event) => {
2073
+ if (!isControlled) setInternalValue(event.currentTarget.value);
2074
+ onValueChange?.(event.currentTarget.value);
2075
+ onChange?.(event);
2076
+ },
2077
+ [isControlled, onChange, onValueChange]
2078
+ );
2079
+ const handleClear = React6.useCallback(() => {
2080
+ emitNativeInput("");
2081
+ onClear?.();
2082
+ inputRef.current?.focus();
2083
+ }, [emitNativeInput, onClear]);
2084
+ const handleKeyDown = React6.useCallback(
2085
+ (event) => {
2086
+ onKeyDown?.(event);
2087
+ if (event.defaultPrevented || event.key !== "Escape" || !clearOnEscapeEnabled || currentValue.length === 0 || disabled) {
2088
+ return;
2089
+ }
2090
+ event.preventDefault();
2091
+ event.stopPropagation();
2092
+ handleClear();
2093
+ },
2094
+ [clearOnEscapeEnabled, currentValue.length, disabled, handleClear, onKeyDown]
2095
+ );
2096
+ const control = /* @__PURE__ */ jsx(
2097
+ Input$1,
2098
+ {
2099
+ ref: setInputRef,
2100
+ "data-slot": "input",
2101
+ type: canRevealPassword && passwordVisible ? "text" : type,
2102
+ id,
2103
+ disabled,
2104
+ "aria-invalid": resolvedInvalid || void 0,
2105
+ "aria-describedby": describedBy,
2106
+ "aria-busy": loading || void 0,
2107
+ className: cn(
2108
+ inputVariants({ size, affix }),
2109
+ type === "file" && "p-0 pr-[var(--input-padding-x)] text-muted-foreground",
2110
+ className
2111
+ ),
2112
+ style: getInputStyle(size, style),
2113
+ onChange: syncInputValue,
2114
+ onKeyDown: handleKeyDown,
2115
+ ...rest,
2116
+ ...valueProps
2117
+ }
2118
+ );
2119
+ const inputControl = hasControlWrapper ? /* @__PURE__ */ jsxs(
2120
+ "div",
2121
+ {
2122
+ "data-slot": "input-wrapper",
2123
+ className: cn("relative flex items-center", wrapperClassName),
2124
+ style: getInputStyle(size, void 0),
2125
+ children: [
2126
+ prefix != null && /* @__PURE__ */ jsx(
2127
+ "span",
2128
+ {
2129
+ id: prefixDescriptionId,
2130
+ "aria-hidden": !isTextAffix(prefix),
2131
+ className: affixVariants({ side: "prefix" }),
2132
+ children: prefix
2133
+ }
2134
+ ),
2135
+ control,
2136
+ showSpinner && /* @__PURE__ */ jsx(
2137
+ "span",
2138
+ {
2139
+ "aria-hidden": "true",
2140
+ className: "pointer-events-none absolute right-[var(--input-control-inset)] top-1/2 flex size-[var(--input-control-size)] -translate-y-1/2 items-center justify-center text-muted-foreground",
2141
+ style: getInputStyle(size, void 0),
2142
+ children: /* @__PURE__ */ jsx(LoaderCircle, { className: "size-[var(--input-control-icon-size)] animate-spin" })
2143
+ }
2144
+ ),
2145
+ showClear && /* @__PURE__ */ jsx(
2146
+ "button",
2147
+ {
2148
+ type: "button",
2149
+ "aria-label": "Clear input",
2150
+ disabled,
2151
+ className: inputControlButtonVariants(),
2152
+ style: getInputStyle(size, void 0),
2153
+ onClick: handleClear,
2154
+ children: /* @__PURE__ */ jsx(Cross, { "aria-hidden": "true" })
2155
+ }
2156
+ ),
2157
+ showShortcut && /* @__PURE__ */ jsx(
2158
+ "span",
2159
+ {
2160
+ "aria-hidden": "true",
2161
+ className: "pointer-events-none absolute right-[var(--input-control-inset)] top-1/2 flex -translate-y-1/2 items-center",
2162
+ style: getInputStyle(size, void 0),
2163
+ children: /* @__PURE__ */ jsx(Kbd, { small: true, children: currentValue.length > 0 ? "Esc" : shortcut })
2164
+ }
2165
+ ),
2166
+ showPasswordToggle && /* @__PURE__ */ jsx(
2167
+ "button",
2168
+ {
2169
+ type: "button",
2170
+ "aria-label": passwordVisible ? hideLabel : revealLabel,
2171
+ "aria-pressed": passwordVisible,
2172
+ disabled,
2173
+ className: inputControlButtonVariants(),
2174
+ style: getInputStyle(size, void 0),
2175
+ onClick: () => {
2176
+ setPasswordVisible((visible) => !visible);
2177
+ inputRef.current?.focus();
2178
+ },
2179
+ children: passwordVisible ? /* @__PURE__ */ jsx(EyeOff, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Eye, { "aria-hidden": "true" })
2180
+ }
2181
+ ),
2182
+ suffix != null && !showClear && !showShortcut && !showSpinner && !showPasswordToggle && /* @__PURE__ */ jsx(
2183
+ "span",
2184
+ {
2185
+ id: suffixDescriptionId,
2186
+ "aria-hidden": !isTextAffix(suffix),
2187
+ className: affixVariants({ side: "suffix" }),
2188
+ children: suffix
2189
+ }
2190
+ )
2191
+ ]
2192
+ }
2193
+ ) : control;
2194
+ if (!label && !description && typeof error !== "string") {
2195
+ return inputControl;
2196
+ }
2197
+ return /* @__PURE__ */ jsxs(
2198
+ "div",
2199
+ {
2200
+ "data-slot": "input-field",
2201
+ className: cn("grid gap-[var(--input-field-gap)]", fieldClassName),
2202
+ style: {
2203
+ "--input-field-gap": `${inputTokens.fieldGap}px`
2204
+ },
2205
+ children: [
2206
+ label && /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm font-medium text-foreground", children: label }),
2207
+ inputControl,
2208
+ description && !error && /* @__PURE__ */ jsx("p", { id: descriptionId, className: fieldMessageVariants({ tone: "description" }), children: description }),
2209
+ typeof error === "string" && /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: fieldMessageVariants({ tone: "error" }), children: error })
2210
+ ]
2211
+ }
2212
+ );
2213
+ });
2214
+ Input.displayName = "Input";
2215
+ function Avatar({ src, alt, isOnline, size = "md" }) {
2216
+ const sizeClasses = size === "sm" ? "w-10 h-10" : "w-10 h-10";
2217
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2218
+ /* @__PURE__ */ jsx(
2219
+ Image,
2220
+ {
2221
+ src,
2222
+ alt,
2223
+ className: cn(
2224
+ sizeClasses,
2225
+ "rounded-full ring-1 ring-gray-400 dark:ring-gray-600 object-cover"
2226
+ ),
2227
+ width: 400,
2228
+ height: 400
2229
+ }
2230
+ ),
2231
+ isOnline !== void 0 && /* @__PURE__ */ jsx(
2232
+ "span",
2233
+ {
2234
+ className: cn(
2235
+ "absolute bottom-0 right-0 w-3 h-3 rounded-full ring-2 ring-white dark:ring-black",
2236
+ isOnline ? "bg-green-500" : "bg-[var(--neutral-8)]"
2237
+ )
2238
+ }
2239
+ )
2240
+ ] });
2241
+ }
2242
+ function TeamChat({
2243
+ chatName,
2244
+ tagline = "Collaborate creatively, deliver clearly.",
2245
+ messages,
2246
+ inputPlaceholder = "Write your message...",
2247
+ onSendMessage,
2248
+ onReactionClick,
2249
+ onMoreOptions,
2250
+ className
2251
+ }) {
2252
+ const [selectedSender, setSelectedSender] = useState(null);
2253
+ const [inputValue, setInputValue] = useState("");
2254
+ const uniqueSenders = Array.from(
2255
+ new Map(messages.map((m2) => [m2.sender.name, m2.sender])).values()
2256
+ );
2257
+ const filteredMessages = selectedSender ? messages.filter((m2) => m2.sender.name === selectedSender) : messages;
2258
+ const handleSend = () => {
2259
+ if (inputValue.trim() && onSendMessage) {
2260
+ onSendMessage(inputValue.trim());
2261
+ setInputValue("");
2262
+ }
2263
+ };
2264
+ const handleKeyDown = (e) => {
2265
+ if (e.key === "Enter" && !e.shiftKey) {
2266
+ e.preventDefault();
2267
+ handleSend();
2268
+ }
2269
+ };
2270
+ return /* @__PURE__ */ jsxs(
2271
+ "div",
2272
+ {
2273
+ 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)]",
2275
+ className
2276
+ ),
2277
+ children: [
2278
+ /* @__PURE__ */ jsxs("header", { className: "flex justify-between items-center border-b border-[var(--neutral-7)] pb-3 mb-6", children: [
2279
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
2280
+ /* @__PURE__ */ jsx(Users, { className: "w-8 h-8 text-black dark:text-white" }),
2281
+ /* @__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 })
2284
+ ] })
2285
+ ] }),
2286
+ /* @__PURE__ */ jsx(
2287
+ "button",
2288
+ {
2289
+ type: "button",
2290
+ "aria-label": "More options",
2291
+ 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)]" })
2294
+ }
2295
+ )
2296
+ ] }),
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) => {
2299
+ const isSelected = selectedSender === sender.name;
2300
+ return /* @__PURE__ */ jsxs(
2301
+ "button",
2302
+ {
2303
+ type: "button",
2304
+ onClick: () => setSelectedSender(isSelected ? null : sender.name),
2305
+ className: cn(
2306
+ "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)]"
2308
+ ),
2309
+ children: [
2310
+ /* @__PURE__ */ jsx(Avatar, { src: sender.avatar, alt: sender.name, isOnline: sender.isOnline }),
2311
+ /* @__PURE__ */ jsx("span", { className: "text-left font-medium truncate", children: sender.name })
2312
+ ]
2313
+ },
2314
+ sender.name
2315
+ );
2316
+ }) }),
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
+ )) })
2359
+ ] }),
2360
+ /* @__PURE__ */ jsxs("footer", { className: "mt-6 flex items-center gap-4 border-t border-[var(--neutral-7)] pt-4", children: [
2361
+ /* @__PURE__ */ jsx(
2362
+ "button",
2363
+ {
2364
+ type: "button",
2365
+ "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)]" })
2368
+ }
2369
+ ),
2370
+ /* @__PURE__ */ jsx(
2371
+ Input,
2372
+ {
2373
+ type: "text",
2374
+ value: inputValue,
2375
+ onChange: (e) => setInputValue(e.target.value),
2376
+ onKeyDown: handleKeyDown,
2377
+ placeholder: inputPlaceholder
2378
+ }
2379
+ ),
2380
+ /* @__PURE__ */ jsx(
2381
+ "button",
2382
+ {
2383
+ type: "button",
2384
+ "aria-label": "Send message",
2385
+ onClick: handleSend,
2386
+ className: "p-3 rounded-full bg-black dark:bg-white text-white dark:text-black hover:brightness-90 transition",
2387
+ children: /* @__PURE__ */ jsx(PaperAirplane, { className: "w-6 h-6" })
2388
+ }
2389
+ )
2390
+ ] })
2391
+ ]
2392
+ }
2393
+ );
2394
+ }
2395
+
2396
+ export { AnimateIn, AnimateInGroup, AsciiText, ChangelogWidget, NodeGraphCanvas, OnboardingChecklist, PromptInputBox, TeamChat };
2397
+ //# sourceMappingURL=index.js.map
2398
+ //# sourceMappingURL=index.js.map