@melony/react 0.1.54 → 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.
package/dist/index.js CHANGED
@@ -1,4043 +1,48 @@
1
- import * as React3 from 'react';
2
- import React3__default, { createContext, useContext, useRef, useEffect, useState, useCallback, useMemo } from 'react';
3
- import { filterEventsBySlots, convertEventsToMessages } from 'melony';
4
- import { NuqsAdapter } from 'nuqs/adapters/react';
5
- import { QueryClient, QueryClientProvider, useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
6
- import { Dialog as Dialog$1 } from '@base-ui/react/dialog';
7
- import { clsx } from 'clsx';
8
- import { twMerge } from 'tailwind-merge';
9
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
- import * as ICONS from '@tabler/icons-react';
11
- import { IconX, IconChevronLeft, IconChevronRight, IconLoader2, IconCheck, IconUpload, IconDotsVertical, IconUser, IconLogout, IconBrandGoogle, IconFileText, IconFile, IconPaperclip, IconChevronDown, IconArrowUp, IconHistory, IconPlus, IconArrowLeft, IconMessage, IconLayoutSidebarLeftExpand, IconLayoutSidebarLeftCollapse, IconLayoutSidebarRightExpand, IconLayoutSidebarRightCollapse, IconDeviceDesktop, IconMoon, IconSun, IconSelector, IconChevronUp } from '@tabler/icons-react';
12
- export * from '@tabler/icons-react';
13
- import { Separator as Separator$1 } from '@base-ui/react/separator';
14
- import { mergeProps } from '@base-ui/react/merge-props';
15
- import { useRender } from '@base-ui/react/use-render';
16
- import { cva } from 'class-variance-authority';
17
- import { Input as Input$1 } from '@base-ui/react/input';
18
- import { Select as Select$1 } from '@base-ui/react/select';
19
- import { createPortal } from 'react-dom';
20
- import { Button as Button$1 } from '@base-ui/react/button';
21
- import { Menu } from '@base-ui/react/menu';
22
- import { useQueryState, parseAsString } from 'nuqs';
23
- import { useHotkeys } from 'react-hotkeys-hook';
1
+ import { createContext, useState, useEffect, useMemo, useContext } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
24
3
 
25
4
  // src/providers/melony-provider.tsx
26
- function cn(...inputs) {
27
- return twMerge(clsx(inputs));
28
- }
29
- function Dialog({ ...props }) {
30
- return /* @__PURE__ */ jsx(Dialog$1.Root, { "data-slot": "dialog", ...props });
31
- }
32
- function DialogTrigger({ ...props }) {
33
- return /* @__PURE__ */ jsx(Dialog$1.Trigger, { "data-slot": "dialog-trigger", ...props });
34
- }
35
- function DialogPortal({ ...props }) {
36
- return /* @__PURE__ */ jsx(Dialog$1.Portal, { "data-slot": "dialog-portal", ...props });
37
- }
38
- function DialogOverlay({
39
- className,
40
- ...props
41
- }) {
42
- return /* @__PURE__ */ jsx(
43
- Dialog$1.Backdrop,
44
- {
45
- "data-slot": "dialog-overlay",
46
- className: cn(
47
- "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/80 duration-200 supports-backdrop-filter:backdrop-blur-sm fixed inset-0 isolate z-50",
48
- className
49
- ),
50
- ...props
51
- }
52
- );
53
- }
54
- function DialogContent({ className, ...props }) {
55
- return /* @__PURE__ */ jsxs(DialogPortal, { children: [
56
- /* @__PURE__ */ jsx(DialogOverlay, {}),
57
- /* @__PURE__ */ jsx(
58
- Dialog$1.Popup,
59
- {
60
- "data-slot": "dialog-content",
61
- className: cn(
62
- "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 sm:rounded-lg outline-none",
63
- className
64
- ),
65
- ...props
66
- }
67
- )
68
- ] });
69
- }
70
- function DialogClose({ className, ...props }) {
71
- return /* @__PURE__ */ jsx(
72
- Dialog$1.Close,
73
- {
74
- "data-slot": "dialog-close",
75
- className: cn(
76
- "absolute right-4 top-4 rounded-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-[state=open]:bg-accent data-[state=open]:text-muted-foreground",
77
- className
78
- ),
79
- ...props
80
- }
81
- );
82
- }
83
- function DialogHeader({ className, ...props }) {
84
- return /* @__PURE__ */ jsx(
85
- "div",
86
- {
87
- "data-slot": "dialog-header",
88
- className: cn(
89
- "flex flex-col space-y-1.5 text-center sm:text-left",
90
- className
91
- ),
92
- ...props
93
- }
94
- );
95
- }
96
- function DialogTitle({
97
- className,
98
- ...props
99
- }) {
100
- return /* @__PURE__ */ jsx(
101
- Dialog$1.Title,
102
- {
103
- "data-slot": "dialog-title",
104
- className: cn(
105
- "text-lg font-semibold leading-none tracking-tight",
106
- className
107
- ),
108
- ...props
109
- }
110
- );
111
- }
112
- function DialogDescription({
113
- className,
114
- ...props
115
- }) {
116
- return /* @__PURE__ */ jsx(
117
- Dialog$1.Description,
118
- {
119
- "data-slot": "dialog-description",
120
- className: cn("text-sm text-muted-foreground", className),
121
- ...props
122
- }
123
- );
124
- }
125
- function Card({
126
- className,
127
- size = "default",
128
- ...props
129
- }) {
130
- return /* @__PURE__ */ jsx(
131
- "div",
132
- {
133
- "data-slot": "card",
134
- "data-size": size,
135
- className: cn(
136
- "ring-foreground/10 bg-card text-card-foreground gap-6 overflow-hidden rounded-2xl py-6 text-sm ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col",
137
- className
138
- ),
139
- ...props
140
- }
141
- );
142
- }
143
- function CardHeader({ className, ...props }) {
144
- return /* @__PURE__ */ jsx(
145
- "div",
146
- {
147
- "data-slot": "card-header",
148
- className: cn(
149
- "gap-2 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
150
- className
151
- ),
152
- ...props
153
- }
154
- );
155
- }
156
- function CardTitle({ className, ...props }) {
157
- return /* @__PURE__ */ jsx(
158
- "div",
159
- {
160
- "data-slot": "card-title",
161
- className: cn("text-base font-medium", className),
162
- ...props
163
- }
164
- );
165
- }
166
- function CardDescription({ className, ...props }) {
167
- return /* @__PURE__ */ jsx(
168
- "div",
169
- {
170
- "data-slot": "card-description",
171
- className: cn("text-muted-foreground text-sm", className),
172
- ...props
173
- }
174
- );
175
- }
176
- function CardContent({ className, ...props }) {
177
- return /* @__PURE__ */ jsx(
178
- "div",
179
- {
180
- "data-slot": "card-content",
181
- className: cn("px-6 group-data-[size=sm]/card:px-4", className),
182
- ...props
183
- }
184
- );
185
- }
186
-
187
- // src/lib/theme-utils.ts
188
- var paddingMap = {
189
- none: "p-0",
190
- xs: "p-1",
191
- sm: "p-2",
192
- md: "p-4",
193
- lg: "p-6",
194
- xl: "p-8",
195
- xxl: "p-12"
196
- };
197
- var marginMap = {
198
- none: "m-0",
199
- xs: "m-1",
200
- sm: "m-2",
201
- md: "m-4",
202
- lg: "m-6",
203
- xl: "m-8",
204
- xxl: "m-12"
205
- };
206
- var gapMap = {
207
- none: "gap-0",
208
- xs: "gap-1",
209
- sm: "gap-2",
210
- md: "gap-4",
211
- lg: "gap-6",
212
- xl: "gap-8",
213
- xxl: "gap-12"
214
- };
215
- var colorBgMap = {
216
- primary: "bg-primary text-primary-foreground",
217
- secondary: "bg-secondary text-secondary-foreground",
218
- success: "bg-green-500 text-white",
219
- danger: "bg-destructive text-destructive-foreground",
220
- warning: "bg-yellow-500 text-white",
221
- info: "bg-blue-500 text-white",
222
- background: "bg-background text-foreground",
223
- foreground: "bg-foreground text-background",
224
- muted: "bg-muted text-muted-foreground",
225
- mutedForeground: "bg-muted-foreground text-muted",
226
- border: "bg-border",
227
- transparent: "bg-transparent"
228
- };
229
- var colorTextMap = {
230
- primary: "text-primary",
231
- secondary: "text-secondary",
232
- success: "text-green-600",
233
- danger: "text-destructive",
234
- warning: "text-yellow-600",
235
- info: "text-blue-600",
236
- background: "text-background",
237
- foreground: "text-foreground",
238
- muted: "text-muted-foreground",
239
- mutedForeground: "text-muted-foreground",
240
- border: "text-border",
241
- transparent: "text-transparent"
242
- };
243
- var colorBorderMap = {
244
- primary: "border-primary",
245
- secondary: "border-secondary",
246
- success: "border-green-500",
247
- danger: "border-destructive",
248
- warning: "border-yellow-500",
249
- info: "border-blue-500",
250
- background: "border-background",
251
- foreground: "border-foreground",
252
- muted: "border-muted",
253
- mutedForeground: "border-muted-foreground",
254
- border: "border-border",
255
- transparent: "border-transparent"
256
- };
257
- var widthMap = {
258
- auto: "w-auto",
259
- full: "w-full",
260
- min: "w-min",
261
- max: "w-max",
262
- "1/2": "w-1/2",
263
- "1/3": "w-1/3",
264
- "2/3": "w-2/3",
265
- "1/4": "w-1/4",
266
- "3/4": "w-3/4"
267
- };
268
- var shadowMap = {
269
- none: "shadow-none",
270
- sm: "shadow-sm",
271
- md: "shadow-md",
272
- lg: "shadow-lg",
273
- xl: "shadow-xl"
274
- };
275
- var radiusMap = {
276
- none: "rounded-none",
277
- sm: "rounded-sm",
278
- md: "rounded-md",
279
- lg: "rounded-lg",
280
- full: "rounded-full"
281
- };
282
- var alignMap = {
283
- start: "items-start",
284
- center: "items-center",
285
- end: "items-end",
286
- stretch: "items-stretch"
287
- };
288
- var justifyMap = {
289
- start: "justify-start",
290
- center: "justify-center",
291
- end: "justify-end",
292
- between: "justify-between",
293
- around: "justify-around"
294
- };
295
- var wrapMap = {
296
- nowrap: "flex-nowrap",
297
- wrap: "flex-wrap",
298
- "wrap-reverse": "flex-wrap-reverse"
299
- };
300
- var textSizeMap = {
301
- none: "text-[0]",
302
- xs: "text-xs",
303
- sm: "text-sm",
304
- md: "text-base",
305
- lg: "text-lg",
306
- xl: "text-xl",
307
- xxl: "text-2xl"
308
- };
309
- var textAlignMap = {
310
- start: "text-left",
311
- center: "text-center",
312
- end: "text-right",
313
- stretch: "text-justify"
314
- };
315
- var fontWeightMap = {
316
- normal: "font-normal",
317
- medium: "font-medium",
318
- semibold: "font-semibold",
319
- bold: "font-bold"
320
- };
321
- var Card2 = ({
322
- children,
323
- title,
324
- subtitle,
325
- background,
326
- padding = "md",
327
- radius = "md",
328
- shadow = "md"
329
- }) => {
330
- return /* @__PURE__ */ jsxs(
331
- Card,
332
- {
333
- className: cn(
334
- "min-w-96 relative",
335
- background && colorBgMap[background],
336
- radius && radiusMap[radius],
337
- shadow && shadowMap[shadow]
338
- ),
339
- children: [
340
- (title || subtitle) && /* @__PURE__ */ jsxs(CardHeader, { className: "pb-3", children: [
341
- title && /* @__PURE__ */ jsx(CardTitle, { className: "text-lg", children: title }),
342
- subtitle && /* @__PURE__ */ jsx(CardDescription, { children: subtitle })
343
- ] }),
344
- /* @__PURE__ */ jsx(CardContent, { className: cn("flex flex-col gap-4", paddingMap[padding]), children })
345
- ]
346
- }
347
- );
348
- };
349
- var Row = ({
350
- children,
351
- align = "start",
352
- justify = "start",
353
- wrap = "nowrap",
354
- gap = "none",
355
- padding = "none",
356
- width = "full"
357
- }) => {
358
- return /* @__PURE__ */ jsx(
359
- "div",
360
- {
361
- className: cn(
362
- "flex flex-row",
363
- alignMap[align],
364
- justifyMap[justify],
365
- wrapMap[wrap],
366
- gapMap[gap],
367
- paddingMap[padding],
368
- widthMap[width]
369
- ),
370
- children
371
- }
372
- );
373
- };
374
- var Col = ({
375
- children,
376
- align = "start",
377
- justify = "start",
378
- gap = "none",
379
- width = "auto",
380
- height = "auto",
381
- padding = "none",
382
- background,
383
- radius
384
- }) => {
385
- return /* @__PURE__ */ jsx(
386
- "div",
387
- {
388
- className: cn(
389
- "flex flex-col",
390
- alignMap[align],
391
- justifyMap[justify],
392
- gapMap[gap],
393
- paddingMap[padding],
394
- widthMap[width],
395
- height === "full" && "h-full",
396
- background && colorBgMap[background],
397
- radius && radiusMap[radius]
398
- ),
399
- children
400
- }
401
- );
402
- };
403
- var Box = ({
404
- children,
405
- padding = "none",
406
- margin = "none",
407
- background,
408
- border = false,
409
- borderColor = "border",
410
- radius = "none",
411
- width = "auto",
412
- height = "auto",
413
- shadow = "none",
414
- group = false
415
- }) => {
416
- return /* @__PURE__ */ jsx(
417
- "div",
418
- {
419
- className: cn(
420
- "relative",
421
- paddingMap[padding],
422
- marginMap[margin],
423
- background && colorBgMap[background],
424
- border && "border",
425
- border && colorBorderMap[borderColor],
426
- radiusMap[radius],
427
- widthMap[width],
428
- height === "full" && "h-full",
429
- shadowMap[shadow],
430
- group && "group"
431
- ),
432
- children
433
- }
434
- );
435
- };
436
- var Float = ({
437
- children,
438
- position = "top-right",
439
- offsetX = "none",
440
- offsetY = "none",
441
- showOnHover = false
442
- }) => {
443
- const positionClasses = {
444
- "top-left": "top-1 left-1",
445
- "top-right": "top-1 right-1",
446
- "top-center": "top-1 left-1/2 -translate-x-1/2",
447
- "bottom-left": "bottom-1 left-1",
448
- "bottom-right": "bottom-1 right-1",
449
- "bottom-center": "bottom-1 left-1/2 -translate-x-1/2",
450
- center: "top- 1 / 2 left - 1/2 -tra nslate-x-1/2 -translate-y-1/2",
451
- "left-center": "top-1/2 left-1 -translate-y-1/2",
452
- "right-center": "top-1/2 right-1 -translate-y-1/2"
453
- };
454
- const marginXClass = position.endsWith("left") ? marginMap[offsetX].replace("m-", "ml-") : marginMap[offsetX].replace("m-", "mr-");
455
- const marginYClass = position.startsWith("top") ? marginMap[offsetY].replace("m-", "mt-") : marginMap[offsetY].replace("m-", "mb-");
456
- return /* @__PURE__ */ jsx(
457
- "div",
458
- {
459
- className: cn(
460
- "absolute z-10",
461
- positionClasses[position],
462
- marginXClass,
463
- marginYClass,
464
- showOnHover && "opacity-0 group-hover:opacity-100 transition-opacity"
465
- ),
466
- children
467
- }
468
- );
469
- };
470
- var Spacer = ({
471
- size = "md",
472
- direction = "vertical"
473
- }) => {
474
- return /* @__PURE__ */ jsx(
475
- "div",
476
- {
477
- className: cn(
478
- direction === "vertical" ? "w-full" : "h-full",
479
- paddingMap[size]
480
- )
481
- }
482
- );
483
- };
484
- function Separator({
485
- className,
486
- orientation = "horizontal",
487
- ...props
488
- }) {
489
- return /* @__PURE__ */ jsx(
490
- Separator$1,
491
- {
492
- "data-slot": "separator",
493
- orientation,
494
- className: cn(
495
- "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch",
496
- className
497
- ),
498
- ...props
499
- }
500
- );
501
- }
502
- var Divider = ({
503
- orientation = "horizontal",
504
- color = "border",
505
- margin = "md"
506
- }) => {
507
- return /* @__PURE__ */ jsx(
508
- Separator,
509
- {
510
- orientation,
511
- className: cn(marginMap[margin], colorBgMap[color])
512
- }
513
- );
514
- };
515
- var List = ({ children, padding = "none", gap = "none", flex, overflow }) => {
516
- return /* @__PURE__ */ jsx(
517
- "div",
518
- {
519
- className: cn(
520
- "flex flex-col list-none m-0",
521
- paddingMap[padding],
522
- gapMap[gap]
523
- ),
524
- style: { flex, overflow },
525
- children
526
- }
527
- );
528
- };
529
- var useMelony = (options) => {
530
- const context = useContext(MelonyContext);
531
- if (context === void 0) {
532
- throw new Error("useMelony must be used within a MelonyClientProvider");
533
- }
534
- const { client, reset } = context;
535
- const { initialEvents } = options || {};
536
- const prevInitialEventsRef = useRef(void 0);
537
- useEffect(() => {
538
- if (initialEvents && initialEvents === client.getState().events) {
539
- return;
540
- }
541
- const currentSerialized = initialEvents ? JSON.stringify(initialEvents) : void 0;
542
- if (currentSerialized !== prevInitialEventsRef.current) {
543
- if (initialEvents) {
544
- reset(initialEvents);
545
- } else {
546
- reset([]);
547
- }
548
- prevInitialEventsRef.current = currentSerialized;
549
- }
550
- }, [client, initialEvents, reset]);
551
- return context;
552
- };
553
- var ListItem = ({
554
- children,
555
- onClickAction,
556
- gap = "sm",
557
- padding = "sm",
558
- background,
559
- radius = "md",
560
- align = "center"
561
- }) => {
562
- const { sendEvent } = useMelony();
563
- const isInteractive = !!onClickAction;
564
- const handleClick = () => {
565
- if (onClickAction) {
566
- sendEvent(onClickAction);
567
- }
568
- };
569
- return /* @__PURE__ */ jsx(
570
- "div",
571
- {
572
- onClick: isInteractive ? handleClick : void 0,
573
- className: cn(
574
- "flex flex-row transition-colors text-sm",
575
- paddingMap[padding],
576
- background ? colorBgMap[background] : isInteractive && "hover:bg-muted",
577
- radiusMap[radius],
578
- isInteractive ? "cursor-pointer" : "cursor-default"
579
- ),
580
- children: /* @__PURE__ */ jsx(Row, { align, gap, children })
581
- }
582
- );
583
- };
584
- var Image = ({
585
- src,
586
- alt,
587
- width = "auto",
588
- height,
589
- radius = "md",
590
- objectFit = "cover"
591
- }) => {
592
- const [hasError, setHasError] = useState(false);
593
- const [isLoading, setIsLoading] = useState(true);
594
- const [open, setOpen] = useState(false);
595
- const [currentIndex, setCurrentIndex] = useState(0);
596
- const [gallery, setGallery] = useState([]);
597
- const triggerRef = useRef(null);
598
- useEffect(() => {
599
- if (open && triggerRef.current) {
600
- let parent = triggerRef.current.parentElement;
601
- while (parent && parent.parentElement && parent.parentElement.children.length === 1) {
602
- parent = parent.parentElement;
603
- }
604
- const container = parent?.parentElement;
605
- if (container) {
606
- const foundImgs = Array.from(container.querySelectorAll("img")).map((img) => ({
607
- src: img.getAttribute("src") || "",
608
- alt: img.getAttribute("alt") || ""
609
- })).filter((v, i, a) => a.findIndex((t) => t.src === v.src) === i);
610
- setGallery(foundImgs);
611
- const idx = foundImgs.findIndex((img) => img.src === src);
612
- setCurrentIndex(idx >= 0 ? idx : 0);
613
- }
614
- }
615
- }, [open, src]);
616
- const navigate = (dir) => {
617
- setCurrentIndex((prev) => (prev + dir + gallery.length) % gallery.length);
618
- };
619
- const currentImage = gallery[currentIndex] || { src, alt };
620
- const hasMultiple = gallery.length > 1;
621
- const handleError = () => {
622
- setHasError(true);
623
- setIsLoading(false);
624
- };
625
- const handleLoad = () => {
626
- setIsLoading(false);
627
- };
628
- if (hasError) {
629
- return /* @__PURE__ */ jsx(
630
- "div",
631
- {
632
- className: cn(
633
- "flex items-center justify-center bg-muted text-muted-foreground",
634
- widthMap[width],
635
- radiusMap[radius]
636
- ),
637
- style: { height: height || "100px" },
638
- children: /* @__PURE__ */ jsx("span", { className: "text-[10px]", children: "Error" })
639
- }
640
- );
641
- }
642
- return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: setOpen, children: [
643
- /* @__PURE__ */ jsx(DialogTrigger, { children: /* @__PURE__ */ jsxs(
644
- "div",
645
- {
646
- ref: triggerRef,
647
- className: cn(
648
- "relative overflow-hidden cursor-pointer",
649
- widthMap[width],
650
- radiusMap[radius]
651
- ),
652
- style: { height },
653
- children: [
654
- /* @__PURE__ */ jsx(
655
- "img",
656
- {
657
- src,
658
- alt,
659
- onError: handleError,
660
- onLoad: handleLoad,
661
- className: cn(
662
- "block w-full transition-opacity duration-200 hover:opacity-90",
663
- isLoading ? "opacity-0" : "opacity-100",
664
- objectFit === "cover" ? "object-cover" : objectFit === "contain" ? "object-contain" : "object-fill",
665
- height ? "h-full" : "h-auto"
666
- )
667
- }
668
- ),
669
- isLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-muted animate-pulse" })
670
- ]
671
- }
672
- ) }),
673
- /* @__PURE__ */ jsx(
674
- DialogContent,
675
- {
676
- className: "max-w-[90vw] max-h-[90vh] p-0 bg-transparent border-none shadow-none outline-none",
677
- onClick: (e) => e.stopPropagation(),
678
- children: /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center group/lightbox", children: [
679
- /* @__PURE__ */ jsx(DialogClose, { className: "absolute -top-12 right-0 text-white hover:text-gray-300 transition-colors z-50 bg-black/50 rounded-full p-2", children: /* @__PURE__ */ jsx(IconX, { size: 20 }) }),
680
- hasMultiple && /* @__PURE__ */ jsxs(Fragment, { children: [
681
- /* @__PURE__ */ jsx(
682
- "button",
683
- {
684
- onClick: (e) => {
685
- e.stopPropagation();
686
- navigate(-1);
687
- },
688
- className: "absolute left-4 z-50 p-3 bg-black/40 hover:bg-black/60 text-white rounded-full transition-all opacity-0 group-hover/lightbox:opacity-100",
689
- children: /* @__PURE__ */ jsx(IconChevronLeft, { size: 28 })
690
- }
691
- ),
692
- /* @__PURE__ */ jsx(
693
- "button",
694
- {
695
- onClick: (e) => {
696
- e.stopPropagation();
697
- navigate(1);
698
- },
699
- className: "absolute right-4 z-50 p-3 bg-black/40 hover:bg-black/60 text-white rounded-full transition-all opacity-0 group-hover/lightbox:opacity-100",
700
- children: /* @__PURE__ */ jsx(IconChevronRight, { size: 28 })
701
- }
702
- )
703
- ] }),
704
- /* @__PURE__ */ jsx(
705
- "img",
706
- {
707
- src: currentImage.src,
708
- alt: currentImage.alt || alt || "Enlarged image",
709
- className: "max-w-full max-h-[85vh] object-contain rounded-lg shadow-2xl"
710
- }
711
- ),
712
- hasMultiple && /* @__PURE__ */ jsxs("div", { className: "absolute -bottom-10 left-1/2 -translate-x-1/2 text-white bg-black/50 px-3 py-1 rounded-full text-sm font-medium", children: [
713
- currentIndex + 1,
714
- " / ",
715
- gallery.length
716
- ] })
717
- ] })
718
- }
719
- )
720
- ] });
721
- };
722
- var Video = ({
723
- src,
724
- poster,
725
- autoPlay = false,
726
- controls = true,
727
- loop = false,
728
- muted = false,
729
- aspectRatio = "16/9",
730
- width = "full",
731
- height = "auto",
732
- radius = "lg"
733
- }) => {
734
- const aspectRatios = {
735
- "16/9": "aspect-video",
736
- "4/3": "aspect-[4/3]",
737
- "1/1": "aspect-square",
738
- "9/16": "aspect-[9/16]"
739
- };
740
- return /* @__PURE__ */ jsx(
741
- "div",
742
- {
743
- className: cn(
744
- "relative overflow-hidden bg-black shadow-sm",
745
- aspectRatios[aspectRatio] || "aspect-video",
746
- widthMap[width],
747
- radiusMap[radius]
748
- ),
749
- style: { height },
750
- children: /* @__PURE__ */ jsx(
751
- "video",
752
- {
753
- src,
754
- poster,
755
- autoPlay,
756
- controls,
757
- loop,
758
- muted,
759
- playsInline: true,
760
- className: "h-full w-full object-cover"
761
- }
762
- )
763
- }
764
- );
765
- };
766
- var Icon = ({
767
- name,
768
- size = "md",
769
- color = "foreground"
770
- }) => {
771
- const IconComponent = ICONS[name];
772
- if (!IconComponent) return null;
773
- const sizeMap = {
774
- sm: 16,
775
- md: 20,
776
- lg: 24
777
- };
778
- const resolvedSize = typeof size === "number" ? size : sizeMap[size] || 20;
779
- return /* @__PURE__ */ jsx(
780
- "div",
781
- {
782
- className: cn(
783
- "inline-flex items-center justify-center",
784
- colorTextMap[color]
785
- ),
786
- children: /* @__PURE__ */ jsx(IconComponent, { size: resolvedSize, strokeWidth: 1.5 })
787
- }
788
- );
789
- };
790
- var badgeVariants = cva(
791
- "h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-colors overflow-hidden group/badge",
792
- {
793
- variants: {
794
- variant: {
795
- default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
796
- secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
797
- destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
798
- outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground bg-input/30",
799
- ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
800
- link: "text-primary underline-offset-4 hover:underline"
801
- }
802
- },
803
- defaultVariants: {
804
- variant: "default"
805
- }
806
- }
807
- );
808
- function Badge({
809
- className,
810
- variant = "default",
811
- render,
812
- ...props
813
- }) {
814
- return useRender({
815
- defaultTagName: "span",
816
- props: mergeProps(
817
- {
818
- className: cn(badgeVariants({ className, variant }))
819
- },
820
- props
821
- ),
822
- render,
823
- state: {
824
- slot: "badge",
825
- variant
826
- }
827
- });
828
- }
829
- var Badge2 = ({
830
- label,
831
- variant = "primary",
832
- size = "md"
833
- }) => {
834
- const variantMap = {
835
- primary: "default",
836
- secondary: "secondary",
837
- danger: "destructive",
838
- success: "default",
839
- warning: "secondary",
840
- outline: "outline"
841
- };
842
- const sizeClasses = {
843
- sm: "text-[10px] px-1.5 py-0",
844
- md: "text-xs px-2.5 py-0.5",
845
- lg: "text-sm px-3 py-1"
846
- };
847
- return /* @__PURE__ */ jsx(
848
- Badge,
849
- {
850
- variant: variantMap[variant] || "default",
851
- className: sizeClasses[size],
852
- children: label
853
- }
854
- );
855
- };
856
- var Chart = ({
857
- data,
858
- chartType = "bar",
859
- title,
860
- height = 250,
861
- showValues = false,
862
- showGrid = false,
863
- showTooltips = true
864
- }) => {
865
- const [tooltip, setTooltip] = useState(null);
866
- if (!Array.isArray(data)) {
867
- return /* @__PURE__ */ jsx("div", { className: "p-4 text-destructive border border-destructive/20 rounded-md bg-destructive/5 text-sm", children: "Error: Chart data must be an array" });
868
- }
869
- const maxValue = Math.max(...data.map((d) => d.value), 1);
870
- const padding = { top: 40, right: 20, bottom: 40, left: 20 };
871
- const chartHeight = height;
872
- const chartWidth = 600;
873
- const defaultColors = [
874
- "hsl(var(--primary))",
875
- "hsl(var(--chart-1, 217 91% 60%))",
876
- "hsl(var(--chart-2, 142 71% 45%))",
877
- "hsl(var(--chart-3, 31 92% 55%))",
878
- "hsl(var(--chart-4, 346 84% 61%))",
879
- "hsl(var(--chart-5, 271 81% 56%))"
880
- ];
881
- const getColor = (index, color) => {
882
- if (color) return color;
883
- return defaultColors[index % defaultColors.length];
884
- };
885
- const renderGrid = () => {
886
- if (!showGrid) return null;
887
- return [0, 0.25, 0.5, 0.75, 1].map((fraction, i) => /* @__PURE__ */ jsx(
888
- "line",
889
- {
890
- x1: padding.left,
891
- y1: padding.top + chartHeight * (1 - fraction),
892
- x2: chartWidth - padding.right,
893
- y2: padding.top + chartHeight * (1 - fraction),
894
- stroke: "currentColor",
895
- className: "text-border",
896
- strokeDasharray: "4,4",
897
- strokeOpacity: 0.5
898
- },
899
- i
900
- ));
901
- };
902
- const renderTooltip = () => {
903
- if (!tooltip || !tooltip.visible) return null;
904
- return /* @__PURE__ */ jsxs("g", { className: "pointer-events-none", children: [
905
- /* @__PURE__ */ jsx(
906
- "rect",
907
- {
908
- x: tooltip.x - 40,
909
- y: tooltip.y - 45,
910
- width: 80,
911
- height: 40,
912
- fill: "hsl(var(--popover))",
913
- stroke: "hsl(var(--border))",
914
- strokeWidth: 1,
915
- rx: 6,
916
- className: "shadow-md"
917
- }
918
- ),
919
- /* @__PURE__ */ jsx(
920
- "text",
921
- {
922
- x: tooltip.x,
923
- y: tooltip.y - 28,
924
- textAnchor: "middle",
925
- className: "fill-popover-foreground text-[10px] font-semibold",
926
- children: tooltip.value
927
- }
928
- ),
929
- /* @__PURE__ */ jsx(
930
- "text",
931
- {
932
- x: tooltip.x,
933
- y: tooltip.y - 14,
934
- textAnchor: "middle",
935
- className: "fill-muted-foreground text-[9px]",
936
- children: tooltip.label
937
- }
938
- )
939
- ] });
940
- };
941
- const renderBarChart = () => {
942
- const totalBarSpace = chartWidth - padding.left - padding.right;
943
- const barSpacing = data.length > 1 ? totalBarSpace * 0.1 / data.length : 0;
944
- const actualBarWidth = (totalBarSpace - barSpacing * (data.length + 1)) / data.length;
945
- return /* @__PURE__ */ jsxs(
946
- "svg",
947
- {
948
- viewBox: `0 0 ${chartWidth} ${chartHeight + padding.bottom}`,
949
- className: "w-full h-auto overflow-visible",
950
- children: [
951
- renderGrid(),
952
- data.map((item, index) => {
953
- const barHeight = item.value / maxValue * chartHeight;
954
- const x = padding.left + barSpacing + index * (actualBarWidth + barSpacing);
955
- const y = padding.top + chartHeight - barHeight;
956
- return /* @__PURE__ */ jsxs("g", { children: [
957
- /* @__PURE__ */ jsx(
958
- "rect",
959
- {
960
- x,
961
- y,
962
- width: actualBarWidth,
963
- height: barHeight,
964
- fill: getColor(index, item.color),
965
- rx: 4,
966
- onMouseEnter: () => showTooltips && setTooltip({
967
- visible: true,
968
- x: x + actualBarWidth / 2,
969
- y: y - 5,
970
- label: item.label,
971
- value: item.value
972
- }),
973
- onMouseLeave: () => setTooltip({
974
- visible: false,
975
- x: 0,
976
- y: 0,
977
- label: "",
978
- value: 0
979
- }),
980
- className: "transition-all hover:opacity-80 cursor-pointer"
981
- }
982
- ),
983
- /* @__PURE__ */ jsx(
984
- "text",
985
- {
986
- x: x + actualBarWidth / 2,
987
- y: padding.top + chartHeight + 20,
988
- textAnchor: "middle",
989
- className: "fill-muted-foreground text-[10px]",
990
- children: item.label
991
- }
992
- )
993
- ] }, index);
994
- }),
995
- showTooltips && renderTooltip()
996
- ]
997
- }
998
- );
999
- };
1000
- const renderLineChart = () => {
1001
- const points = data.map((item, index) => ({
1002
- x: padding.left + index / Math.max(data.length - 1, 1) * (chartWidth - padding.left - padding.right),
1003
- y: padding.top + chartHeight - item.value / maxValue * chartHeight,
1004
- ...item
1005
- }));
1006
- const pathData = points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" ");
1007
- return /* @__PURE__ */ jsxs(
1008
- "svg",
1009
- {
1010
- viewBox: `0 0 ${chartWidth} ${chartHeight + padding.bottom}`,
1011
- className: "w-full h-auto overflow-visible",
1012
- children: [
1013
- renderGrid(),
1014
- /* @__PURE__ */ jsx(
1015
- "path",
1016
- {
1017
- d: pathData,
1018
- fill: "none",
1019
- stroke: getColor(0),
1020
- strokeWidth: 3,
1021
- className: "transition-all"
1022
- }
1023
- ),
1024
- points.map((point, index) => /* @__PURE__ */ jsxs("g", { children: [
1025
- /* @__PURE__ */ jsx(
1026
- "circle",
1027
- {
1028
- cx: point.x,
1029
- cy: point.y,
1030
- r: 5,
1031
- fill: getColor(index, point.color),
1032
- stroke: "hsl(var(--background))",
1033
- strokeWidth: 2,
1034
- onMouseEnter: () => showTooltips && setTooltip({
1035
- visible: true,
1036
- x: point.x,
1037
- y: point.y - 5,
1038
- label: point.label,
1039
- value: point.value
1040
- }),
1041
- onMouseLeave: () => setTooltip({ visible: false, x: 0, y: 0, label: "", value: 0 }),
1042
- className: "hover:r-6 transition-all cursor-pointer"
1043
- }
1044
- ),
1045
- /* @__PURE__ */ jsx(
1046
- "text",
1047
- {
1048
- x: point.x,
1049
- y: padding.top + chartHeight + 20,
1050
- textAnchor: "middle",
1051
- className: "fill-muted-foreground text-[10px]",
1052
- children: point.label
1053
- }
1054
- )
1055
- ] }, index)),
1056
- showTooltips && renderTooltip()
1057
- ]
1058
- }
1059
- );
1060
- };
1061
- const renderChart = () => {
1062
- switch (chartType) {
1063
- case "line":
1064
- return renderLineChart();
1065
- case "bar":
1066
- default:
1067
- return renderBarChart();
1068
- }
1069
- };
1070
- return /* @__PURE__ */ jsxs("div", { className: "py-4 w-full", children: [
1071
- title && /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold mb-4 text-center", children: title }),
1072
- renderChart()
1073
- ] });
1074
- };
1075
- var Text = ({
1076
- value,
1077
- size = "md",
1078
- weight = "normal",
1079
- align = "start",
1080
- color = "foreground"
1081
- }) => {
1082
- return /* @__PURE__ */ jsx(
1083
- "span",
1084
- {
1085
- className: cn(
1086
- textSizeMap[size],
1087
- fontWeightMap[weight],
1088
- textAlignMap[align],
1089
- colorTextMap[color]
1090
- ),
1091
- children: value
1092
- }
1093
- );
1094
- };
1095
- var Heading = ({
1096
- value,
1097
- level = 2,
1098
- color = "foreground",
1099
- align = "start"
1100
- }) => {
1101
- const Tag = `h${level}`;
1102
- const levelClasses = {
1103
- h1: "text-3xl font-bold tracking-tight",
1104
- h2: "text-2xl font-semibold tracking-tight",
1105
- h3: "text-xl font-semibold tracking-tight",
1106
- h4: "text-lg font-semibold tracking-tight",
1107
- h5: "text-base font-semibold",
1108
- h6: "text-sm font-semibold"
1109
- };
1110
- return /* @__PURE__ */ jsx(
1111
- Tag,
1112
- {
1113
- className: cn(
1114
- levelClasses[Tag] || levelClasses.h2,
1115
- colorTextMap[color],
1116
- textAlignMap[align]
1117
- ),
1118
- children: value
1119
- }
1120
- );
1121
- };
1122
- function Input({ className, type, ...props }) {
1123
- return /* @__PURE__ */ jsx(
1124
- Input$1,
1125
- {
1126
- type,
1127
- "data-slot": "input",
1128
- className: cn(
1129
- "bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 rounded-4xl border px-3 py-1 text-base transition-colors file:h-7 file:text-sm file:font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
1130
- className
1131
- ),
1132
- ...props
1133
- }
1134
- );
1135
- }
1136
- function Label({ className, ...props }) {
1137
- return /* @__PURE__ */ jsx(
1138
- "label",
1139
- {
1140
- "data-slot": "label",
1141
- className: cn(
1142
- "gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed",
1143
- className
1144
- ),
1145
- ...props
1146
- }
1147
- );
1148
- }
1149
- var fieldVariants = cva(
1150
- "data-[invalid=true]:text-destructive gap-3 group/field flex w-full",
1151
- {
1152
- variants: {
1153
- orientation: {
1154
- vertical: "flex-col [&>*]:w-full [&>.sr-only]:w-auto",
1155
- horizontal: "flex-row items-center [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
1156
- responsive: "flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px"
1157
- }
1158
- },
1159
- defaultVariants: {
1160
- orientation: "vertical"
1161
- }
1162
- }
1163
- );
1164
- function Field({
1165
- className,
1166
- orientation = "vertical",
1167
- ...props
1168
- }) {
1169
- return /* @__PURE__ */ jsx(
1170
- "div",
1171
- {
1172
- role: "group",
1173
- "data-slot": "field",
1174
- "data-orientation": orientation,
1175
- className: cn(fieldVariants({ orientation }), className),
1176
- ...props
1177
- }
1178
- );
1179
- }
1180
- function FieldTitle({ className, ...props }) {
1181
- return /* @__PURE__ */ jsx(
1182
- "div",
1183
- {
1184
- "data-slot": "field-label",
1185
- className: cn(
1186
- "gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center leading-snug",
1187
- className
1188
- ),
1189
- ...props
1190
- }
1191
- );
1192
- }
1193
- var Input2 = ({
1194
- inputType = "text",
1195
- placeholder,
1196
- defaultValue,
1197
- label,
1198
- name,
1199
- disabled,
1200
- required,
1201
- width = "full",
1202
- onChangeAction
1203
- }) => {
1204
- const { sendEvent } = useMelony();
1205
- const handleChange = (e) => {
1206
- if (onChangeAction) {
1207
- sendEvent({
1208
- ...onChangeAction,
1209
- data: {
1210
- name: name || "",
1211
- value: e.target.value
1212
- }
1213
- });
1214
- }
1215
- };
1216
- return /* @__PURE__ */ jsxs(Field, { className: cn(widthMap[width]), children: [
1217
- label && /* @__PURE__ */ jsxs(FieldTitle, { children: [
1218
- label,
1219
- required && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1", children: "*" })
1220
- ] }),
1221
- /* @__PURE__ */ jsx(
1222
- Input,
1223
- {
1224
- type: inputType,
1225
- name,
1226
- id: name,
1227
- placeholder,
1228
- defaultValue,
1229
- disabled,
1230
- onChange: handleChange,
1231
- required
1232
- }
1233
- )
1234
- ] });
1235
- };
1236
- function Textarea({ className, ...props }) {
1237
- return /* @__PURE__ */ jsx(
1238
- "textarea",
1239
- {
1240
- "data-slot": "textarea",
1241
- className: cn(
1242
- "border-input bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 resize-none rounded-xl border px-3 py-3 text-base transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50",
1243
- className
1244
- ),
1245
- ...props
1246
- }
1247
- );
1248
- }
1249
- var Textarea2 = ({
1250
- placeholder,
1251
- defaultValue,
1252
- label,
1253
- name,
1254
- disabled,
1255
- rows,
1256
- required,
1257
- width = "full",
1258
- onChangeAction
1259
- }) => {
1260
- const { sendEvent } = useMelony();
1261
- const handleChange = (e) => {
1262
- if (onChangeAction) {
1263
- sendEvent({
1264
- ...onChangeAction,
1265
- data: {
1266
- name: name || "",
1267
- value: e.target.value
1268
- }
1269
- });
1270
- }
1271
- };
1272
- return /* @__PURE__ */ jsxs(Field, { className: cn(widthMap[width]), children: [
1273
- label && /* @__PURE__ */ jsxs(FieldTitle, { children: [
1274
- label,
1275
- required && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1", children: "*" })
1276
- ] }),
1277
- /* @__PURE__ */ jsx(
1278
- Textarea,
1279
- {
1280
- name,
1281
- id: name,
1282
- placeholder,
1283
- defaultValue,
1284
- disabled,
1285
- rows,
1286
- onChange: handleChange,
1287
- required
1288
- }
1289
- )
1290
- ] });
1291
- };
1292
- var Select = Select$1.Root;
1293
- function SelectValue({ className, ...props }) {
1294
- return /* @__PURE__ */ jsx(
1295
- Select$1.Value,
1296
- {
1297
- "data-slot": "select-value",
1298
- className: cn("flex flex-1 text-left", className),
1299
- ...props
1300
- }
1301
- );
1302
- }
1303
- function SelectTrigger({
1304
- className,
1305
- size = "default",
1306
- children,
1307
- ...props
1308
- }) {
1309
- return /* @__PURE__ */ jsxs(
1310
- Select$1.Trigger,
1311
- {
1312
- "data-slot": "select-trigger",
1313
- "data-size": size,
1314
- className: cn(
1315
- "border-input data-[placeholder]:text-muted-foreground bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-4xl border px-3 py-2 text-sm transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:flex *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
1316
- className
1317
- ),
1318
- ...props,
1319
- children: [
1320
- children,
1321
- /* @__PURE__ */ jsx(
1322
- Select$1.Icon,
1323
- {
1324
- render: /* @__PURE__ */ jsx(IconSelector, { className: "text-muted-foreground size-4 pointer-events-none" })
1325
- }
1326
- )
1327
- ]
1328
- }
1329
- );
1330
- }
1331
- function SelectContent({
1332
- className,
1333
- children,
1334
- side = "bottom",
1335
- sideOffset = 4,
1336
- align = "center",
1337
- alignOffset = 0,
1338
- alignItemWithTrigger = true,
1339
- ...props
1340
- }) {
1341
- return /* @__PURE__ */ jsx(Select$1.Portal, { children: /* @__PURE__ */ jsx(
1342
- Select$1.Positioner,
1343
- {
1344
- side,
1345
- sideOffset,
1346
- align,
1347
- alignOffset,
1348
- alignItemWithTrigger,
1349
- className: "isolate z-50",
1350
- children: /* @__PURE__ */ jsxs(
1351
- Select$1.Popup,
1352
- {
1353
- "data-slot": "select-content",
1354
- className: cn(
1355
- "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-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 ring-foreground/5 min-w-36 rounded-2xl shadow-2xl ring-1 duration-100 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto",
1356
- className
1357
- ),
1358
- ...props,
1359
- children: [
1360
- /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
1361
- /* @__PURE__ */ jsx(Select$1.List, { children }),
1362
- /* @__PURE__ */ jsx(SelectScrollDownButton, {})
1363
- ]
1364
- }
1365
- )
1366
- }
1367
- ) });
1368
- }
1369
- function SelectItem({
1370
- className,
1371
- children,
1372
- ...props
1373
- }) {
1374
- return /* @__PURE__ */ jsxs(
1375
- Select$1.Item,
1376
- {
1377
- "data-slot": "select-item",
1378
- className: cn(
1379
- "focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
1380
- className
1381
- ),
1382
- ...props,
1383
- children: [
1384
- /* @__PURE__ */ jsx(Select$1.ItemText, { className: "flex flex-1 gap-2 shrink-0 whitespace-nowrap", children }),
1385
- /* @__PURE__ */ jsx(
1386
- Select$1.ItemIndicator,
1387
- {
1388
- render: /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center" }),
1389
- children: /* @__PURE__ */ jsx(IconCheck, { className: "pointer-events-none" })
1390
- }
1391
- )
1392
- ]
1393
- }
1394
- );
1395
- }
1396
- function SelectScrollUpButton({
1397
- className,
1398
- ...props
1399
- }) {
1400
- return /* @__PURE__ */ jsx(
1401
- Select$1.ScrollUpArrow,
1402
- {
1403
- "data-slot": "select-scroll-up-button",
1404
- className: cn(
1405
- "bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4 top-0 w-full",
1406
- className
1407
- ),
1408
- ...props,
1409
- children: /* @__PURE__ */ jsx(IconChevronUp, {})
1410
- }
1411
- );
1412
- }
1413
- function SelectScrollDownButton({
1414
- className,
1415
- ...props
1416
- }) {
1417
- return /* @__PURE__ */ jsx(
1418
- Select$1.ScrollDownArrow,
1419
- {
1420
- "data-slot": "select-scroll-down-button",
1421
- className: cn(
1422
- "bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4 bottom-0 w-full",
1423
- className
1424
- ),
1425
- ...props,
1426
- children: /* @__PURE__ */ jsx(IconChevronDown, {})
1427
- }
1428
- );
1429
- }
1430
- var Select2 = ({
1431
- options,
1432
- defaultValue,
1433
- label,
1434
- name,
1435
- disabled,
1436
- required,
1437
- width = "full",
1438
- placeholder,
1439
- onChangeAction
1440
- }) => {
1441
- const { sendEvent } = useMelony();
1442
- const handleValueChange = (val) => {
1443
- if (onChangeAction) {
1444
- sendEvent({
1445
- ...onChangeAction,
1446
- data: {
1447
- name: name || "",
1448
- value: val
1449
- }
1450
- });
1451
- }
1452
- };
1453
- return /* @__PURE__ */ jsxs(Field, { className: cn(widthMap[width]), children: [
1454
- label && /* @__PURE__ */ jsxs(FieldTitle, { children: [
1455
- label,
1456
- required && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1", children: "*" })
1457
- ] }),
1458
- /* @__PURE__ */ jsxs(
1459
- Select,
1460
- {
1461
- defaultValue,
1462
- disabled,
1463
- onValueChange: (value) => handleValueChange(value || ""),
1464
- required,
1465
- children: [
1466
- /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
1467
- /* @__PURE__ */ jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
1468
- ]
1469
- }
1470
- )
1471
- ] });
1472
- };
1473
- var Label2 = ({
1474
- value,
1475
- htmlFor,
1476
- required,
1477
- size = "md",
1478
- color = "foreground"
1479
- }) => {
1480
- return /* @__PURE__ */ jsxs(
1481
- Label,
1482
- {
1483
- htmlFor,
1484
- className: cn(
1485
- "flex items-center gap-1",
1486
- textSizeMap[size],
1487
- colorTextMap[color]
1488
- ),
1489
- children: [
1490
- value,
1491
- required && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: "*" })
1492
- ]
1493
- }
1494
- );
1495
- };
1496
- var Checkbox = ({
1497
- label,
1498
- name,
1499
- checked,
1500
- disabled,
1501
- onChangeAction
1502
- }) => {
1503
- const { sendEvent } = useMelony();
1504
- const handleChange = (e) => {
1505
- if (onChangeAction) {
1506
- sendEvent({
1507
- ...onChangeAction,
1508
- data: {
1509
- name: name || "",
1510
- checked: e.target.checked
1511
- }
1512
- });
1513
- }
1514
- };
1515
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1516
- /* @__PURE__ */ jsx(
1517
- "input",
1518
- {
1519
- type: "checkbox",
1520
- name,
1521
- id: name,
1522
- checked,
1523
- disabled,
1524
- onChange: handleChange,
1525
- className: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary disabled:cursor-not-allowed disabled:opacity-50"
1526
- }
1527
- ),
1528
- label && /* @__PURE__ */ jsx(
1529
- Label2,
1530
- {
1531
- htmlFor: name,
1532
- value: label,
1533
- color: disabled ? "muted" : "foreground",
1534
- size: "sm"
1535
- }
1536
- )
1537
- ] });
1538
- };
1539
- var Hidden = ({ name, value }) => {
1540
- return /* @__PURE__ */ jsx("input", { type: "hidden", name, value });
1541
- };
1542
- var PopoverContext = React3.createContext(
1543
- void 0
1544
- );
1545
- function usePopoverContext() {
1546
- const context = React3.useContext(PopoverContext);
1547
- if (!context) {
1548
- throw new Error("Popover components must be used within a Popover");
1549
- }
1550
- return context;
1551
- }
1552
- function Popover({
1553
- children,
1554
- defaultOpen = false,
1555
- open: controlledOpen,
1556
- onOpenChange
1557
- }) {
1558
- const [internalOpen, setInternalOpen] = React3.useState(defaultOpen);
1559
- const triggerRef = React3.useRef(null);
1560
- const open = controlledOpen ?? internalOpen;
1561
- const setOpen = React3.useCallback(
1562
- (newOpen) => {
1563
- if (controlledOpen === void 0) {
1564
- setInternalOpen(newOpen);
1565
- }
1566
- onOpenChange?.(newOpen);
1567
- },
1568
- [controlledOpen, onOpenChange]
1569
- );
1570
- const value = React3.useMemo(
1571
- () => ({
1572
- open,
1573
- setOpen,
1574
- triggerRef
1575
- }),
1576
- [open, setOpen]
1577
- );
1578
- return /* @__PURE__ */ jsx(PopoverContext.Provider, { value, children });
1579
- }
1580
- var PopoverTrigger = React3.forwardRef(
1581
- ({ asChild, className, children, ...props }, ref) => {
1582
- const { setOpen, triggerRef } = usePopoverContext();
1583
- const handleClick = (e) => {
1584
- setOpen(true);
1585
- props.onClick?.(e);
1586
- };
1587
- if (asChild && React3.isValidElement(children)) {
1588
- return React3.cloneElement(children, {
1589
- ref: (node) => {
1590
- triggerRef.current = node;
1591
- if (typeof children.ref === "function") {
1592
- children.ref(node);
1593
- } else if (children.ref) {
1594
- children.ref.current = node;
1595
- }
1596
- },
1597
- onClick: handleClick
1598
- });
1599
- }
1600
- return /* @__PURE__ */ jsx(
1601
- "button",
1602
- {
1603
- ref: (node) => {
1604
- triggerRef.current = node;
1605
- if (typeof ref === "function") {
1606
- ref(node);
1607
- } else if (ref) {
1608
- ref.current = node;
1609
- }
1610
- },
1611
- className,
1612
- onClick: handleClick,
1613
- ...props,
1614
- children
1615
- }
1616
- );
1617
- }
1618
- );
1619
- PopoverTrigger.displayName = "PopoverTrigger";
1620
- var PopoverContent = React3.forwardRef(
1621
- ({
1622
- className,
1623
- side = "bottom",
1624
- align = "start",
1625
- sideOffset = 4,
1626
- alignOffset = 0,
1627
- children,
1628
- ...props
1629
- }, ref) => {
1630
- const { open, setOpen, triggerRef } = usePopoverContext();
1631
- const [position, setPosition] = React3.useState({ top: 0, left: 0 });
1632
- const contentRef = React3.useRef(null);
1633
- React3.useEffect(() => {
1634
- if (!open || !triggerRef.current) return;
1635
- const updatePosition = () => {
1636
- if (!triggerRef.current || !contentRef.current) return;
1637
- const triggerRect = triggerRef.current.getBoundingClientRect();
1638
- const contentRect = contentRef.current.getBoundingClientRect();
1639
- const scrollX = window.scrollX;
1640
- const scrollY = window.scrollY;
1641
- let top = 0;
1642
- let left = 0;
1643
- switch (side) {
1644
- case "bottom":
1645
- top = triggerRect.bottom + sideOffset + scrollY;
1646
- break;
1647
- case "top":
1648
- top = triggerRect.top - contentRect.height - sideOffset + scrollY;
1649
- break;
1650
- case "right":
1651
- top = triggerRect.top + scrollY;
1652
- left = triggerRect.right + sideOffset + scrollX;
1653
- break;
1654
- case "left":
1655
- top = triggerRect.top + scrollY;
1656
- left = triggerRect.left - contentRect.width - sideOffset + scrollX;
1657
- break;
1658
- }
1659
- switch (align) {
1660
- case "start":
1661
- if (side === "top" || side === "bottom") {
1662
- left = triggerRect.left + scrollX + alignOffset;
1663
- } else {
1664
- top += alignOffset;
1665
- }
1666
- break;
1667
- case "center":
1668
- if (side === "top" || side === "bottom") {
1669
- left = triggerRect.left + triggerRect.width / 2 - contentRect.width / 2 + scrollX + alignOffset;
1670
- } else {
1671
- top += triggerRect.height / 2 - contentRect.height / 2 + alignOffset;
1672
- }
1673
- break;
1674
- case "end":
1675
- if (side === "top" || side === "bottom") {
1676
- left = triggerRect.left + triggerRect.width - contentRect.width + scrollX + alignOffset;
1677
- } else {
1678
- top += triggerRect.height - contentRect.height + alignOffset;
1679
- }
1680
- break;
1681
- }
1682
- setPosition({ top, left });
1683
- };
1684
- requestAnimationFrame(() => {
1685
- updatePosition();
1686
- });
1687
- window.addEventListener("resize", updatePosition);
1688
- window.addEventListener("scroll", updatePosition, true);
1689
- return () => {
1690
- window.removeEventListener("resize", updatePosition);
1691
- window.removeEventListener("scroll", updatePosition, true);
1692
- };
1693
- }, [open, side, align, sideOffset, alignOffset, triggerRef]);
1694
- React3.useEffect(() => {
1695
- if (!open) return;
1696
- const handleClickOutside = (event) => {
1697
- if (contentRef.current && !contentRef.current.contains(event.target) && triggerRef.current && !triggerRef.current.contains(event.target)) {
1698
- setOpen(false);
1699
- }
1700
- };
1701
- const handleEscape = (event) => {
1702
- if (event.key === "Escape") {
1703
- setOpen(false);
1704
- }
1705
- };
1706
- document.addEventListener("mousedown", handleClickOutside);
1707
- document.addEventListener("keydown", handleEscape);
1708
- return () => {
1709
- document.removeEventListener("mousedown", handleClickOutside);
1710
- document.removeEventListener("keydown", handleEscape);
1711
- };
1712
- }, [open, setOpen, triggerRef]);
1713
- if (!open) return null;
1714
- const content = /* @__PURE__ */ jsx(
1715
- "div",
1716
- {
1717
- ref: (node) => {
1718
- contentRef.current = node;
1719
- if (typeof ref === "function") {
1720
- ref(node);
1721
- } else if (ref) {
1722
- ref.current = node;
1723
- }
1724
- },
1725
- className: cn(
1726
- "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/5 rounded-2xl shadow-2xl ring-1 z-50 min-w-48 max-h-96 overflow-hidden",
1727
- className
1728
- ),
1729
- style: {
1730
- position: "absolute",
1731
- top: `${position.top}px`,
1732
- left: `${position.left}px`
1733
- },
1734
- ...props,
1735
- children
1736
- }
1737
- );
1738
- return createPortal(content, document.body);
1739
- }
1740
- );
1741
- PopoverContent.displayName = "PopoverContent";
1742
- var PRESET_COLORS = [
1743
- "#000000",
1744
- "#ffffff",
1745
- "#f44336",
1746
- "#e91e63",
1747
- "#9c27b0",
1748
- "#673ab7",
1749
- "#3f51b5",
1750
- "#2196f3",
1751
- "#03a9f4",
1752
- "#00bcd4",
1753
- "#009688",
1754
- "#4caf50",
1755
- "#8bc34a",
1756
- "#cddc39",
1757
- "#ffeb3b",
1758
- "#ffc107",
1759
- "#ff9800",
1760
- "#ff5722",
1761
- "#795548",
1762
- "#9e9e9e",
1763
- "#607d8b"
1764
- ];
1765
- var ColorPicker = ({
1766
- name,
1767
- label,
1768
- defaultValue = "#000000",
1769
- onChangeAction,
1770
- disabled
1771
- }) => {
1772
- const { sendEvent } = useMelony();
1773
- const [color, setColor] = useState(defaultValue);
1774
- const handleColorChange = (newColor) => {
1775
- setColor(newColor);
1776
- if (onChangeAction) {
1777
- sendEvent({
1778
- ...onChangeAction,
1779
- data: {
1780
- name: name || "",
1781
- value: newColor
1782
- }
1783
- });
1784
- }
1785
- };
1786
- return /* @__PURE__ */ jsxs(Field, { className: "w-full", children: [
1787
- label && /* @__PURE__ */ jsx(FieldTitle, { children: label }),
1788
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1789
- /* @__PURE__ */ jsxs(Popover, { children: [
1790
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
1791
- "button",
1792
- {
1793
- type: "button",
1794
- disabled,
1795
- className: cn(
1796
- "w-10 h-10 rounded-lg border border-border shadow-sm transition-transform hover:scale-105 active:scale-95 disabled:opacity-50 disabled:hover:scale-100",
1797
- "flex items-center justify-center p-1"
1798
- ),
1799
- children: /* @__PURE__ */ jsx(
1800
- "div",
1801
- {
1802
- className: "w-full h-full rounded-md",
1803
- style: { backgroundColor: color }
1804
- }
1805
- )
1806
- }
1807
- ) }),
1808
- /* @__PURE__ */ jsx(PopoverContent, { className: "p-3 w-64", side: "bottom", align: "start", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1809
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1", children: PRESET_COLORS.map((preset) => /* @__PURE__ */ jsx(
1810
- "button",
1811
- {
1812
- type: "button",
1813
- className: cn(
1814
- "w-6 h-6 rounded-md border border-border transition-transform hover:scale-110 active:scale-90",
1815
- color === preset && "ring-2 ring-primary ring-offset-1"
1816
- ),
1817
- style: { backgroundColor: preset },
1818
- onClick: () => handleColorChange(preset)
1819
- },
1820
- preset
1821
- )) }),
1822
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1823
- /* @__PURE__ */ jsx(
1824
- "input",
1825
- {
1826
- type: "color",
1827
- value: color,
1828
- onChange: (e) => handleColorChange(e.target.value),
1829
- className: "w-8 h-8 rounded border-none p-0 cursor-pointer"
1830
- }
1831
- ),
1832
- /* @__PURE__ */ jsx(
1833
- "input",
1834
- {
1835
- type: "text",
1836
- value: color,
1837
- onChange: (e) => handleColorChange(e.target.value),
1838
- className: "flex-1 h-8 px-2 text-xs font-mono border border-border rounded uppercase focus:outline-none focus:ring-1 focus:ring-primary"
1839
- }
1840
- )
1841
- ] })
1842
- ] }) })
1843
- ] }),
1844
- /* @__PURE__ */ jsx("span", { className: "text-sm font-mono uppercase text-muted-foreground", children: color })
1845
- ] }),
1846
- /* @__PURE__ */ jsx("input", { type: "hidden", name, value: color })
1847
- ] });
1848
- };
1849
- var RadioGroup = ({
1850
- name,
1851
- options,
1852
- defaultValue,
1853
- label,
1854
- disabled,
1855
- orientation = "vertical",
1856
- onChangeAction
1857
- }) => {
1858
- const { sendEvent } = useMelony();
1859
- const handleChange = (e) => {
1860
- if (onChangeAction) {
1861
- sendEvent({
1862
- ...onChangeAction,
1863
- data: {
1864
- name,
1865
- value: e.target.value
1866
- }
1867
- });
1868
- }
1869
- };
1870
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
1871
- label && /* @__PURE__ */ jsx(Label2, { value: label }),
1872
- /* @__PURE__ */ jsx(
1873
- "div",
1874
- {
1875
- className: cn(
1876
- "flex",
1877
- orientation === "horizontal" ? "flex-row gap-4" : "flex-col gap-2"
1878
- ),
1879
- children: options.map((option, index) => {
1880
- const radioId = `${name}-${index}`;
1881
- const isDisabled = disabled || option.disabled;
1882
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1883
- /* @__PURE__ */ jsx(
1884
- "input",
1885
- {
1886
- type: "radio",
1887
- name,
1888
- id: radioId,
1889
- value: option.value,
1890
- defaultChecked: defaultValue === option.value,
1891
- disabled: isDisabled,
1892
- onChange: handleChange,
1893
- className: "h-4 w-4 border-gray-300 text-primary focus:ring-primary disabled:cursor-not-allowed disabled:opacity-50"
1894
- }
1895
- ),
1896
- /* @__PURE__ */ jsx(
1897
- Label2,
1898
- {
1899
- htmlFor: radioId,
1900
- value: option.label,
1901
- size: "sm",
1902
- color: isDisabled ? "muted" : "foreground"
1903
- }
1904
- )
1905
- ] }, index);
1906
- })
1907
- }
1908
- )
1909
- ] });
1910
- };
1911
- var buttonVariants = cva(
1912
- "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-4xl border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
1913
- {
1914
- variants: {
1915
- variant: {
1916
- default: "bg-primary text-primary-foreground hover:bg-primary/80",
1917
- outline: "border-border bg-input/30 hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
1918
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
1919
- ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
1920
- destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
1921
- link: "text-primary underline-offset-4 hover:underline"
1922
- },
1923
- size: {
1924
- default: "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
1925
- xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
1926
- sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
1927
- lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
1928
- icon: "size-9",
1929
- "icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3",
1930
- "icon-sm": "size-8",
1931
- "icon-lg": "size-10"
1932
- }
1933
- },
1934
- defaultVariants: {
1935
- variant: "default",
1936
- size: "default"
1937
- }
1938
- }
1939
- );
1940
- function Button({
1941
- className,
1942
- variant = "default",
1943
- size = "default",
1944
- ...props
1945
- }) {
1946
- return /* @__PURE__ */ jsx(
1947
- Button$1,
1948
- {
1949
- "data-slot": "button",
1950
- className: cn(buttonVariants({ variant, size, className })),
1951
- ...props
1952
- }
1953
- );
1954
- }
1955
- var Button2 = ({
1956
- type = "button",
1957
- label,
1958
- variant = "primary",
1959
- size = "md",
1960
- disabled = false,
1961
- width,
1962
- onClickAction,
1963
- justify = "center"
1964
- }) => {
1965
- const { sendEvent } = useMelony();
1966
- const variantMap = {
1967
- primary: "default",
1968
- secondary: "secondary",
1969
- danger: "destructive",
1970
- success: "default",
1971
- // We might want a custom success style later
1972
- outline: "outline",
1973
- ghost: "ghost",
1974
- link: "link"
1975
- };
1976
- const widthMap2 = {
1977
- full: "w-full",
1978
- auto: "w-auto",
1979
- "1/2": "w-1/2",
1980
- "1/3": "w-1/3",
1981
- "2/3": "w-2/3",
1982
- "1/4": "w-1/4",
1983
- "3/4": "w-3/4"
1984
- };
1985
- return /* @__PURE__ */ jsx(
1986
- Button,
1987
- {
1988
- type,
1989
- variant: variantMap[variant] || "default",
1990
- size: size === "md" ? "default" : size,
1991
- disabled,
1992
- className: cn(width && widthMap2[width], justifyMap[justify]),
1993
- onClick: () => {
1994
- if (onClickAction) {
1995
- sendEvent(onClickAction);
1996
- }
1997
- },
1998
- children: label
1999
- }
2000
- );
2001
- };
2002
- var Upload = ({
2003
- label = "Upload",
2004
- multiple = false,
2005
- accept,
2006
- onUploadAction,
2007
- initialFiles,
2008
- mode = "append",
2009
- disabled
2010
- }) => {
2011
- const { sendEvent, events } = useMelony();
2012
- const fileInputRef = useRef(null);
2013
- const [isUploading, setIsUploading] = useState(false);
2014
- const [status, setStatus] = useState("idle");
2015
- const uploadedFilesEvents = events.filter(
2016
- (event) => event.type === "uploaded-files"
2017
- );
2018
- const displayEvents = mode === "replace" && uploadedFilesEvents.length > 0 ? [uploadedFilesEvents[uploadedFilesEvents.length - 1]] : uploadedFilesEvents;
2019
- const showInitialFiles = mode === "replace" ? displayEvents.length === 0 : true;
2020
- const handleFileChange = async (e) => {
2021
- const files = Array.from(e.target.files || []);
2022
- if (files.length === 0) return;
2023
- setIsUploading(true);
2024
- setStatus("idle");
2025
- try {
2026
- const filePromises = files.map((file) => {
2027
- return new Promise((resolve, reject) => {
2028
- const reader = new FileReader();
2029
- reader.onload = () => {
2030
- try {
2031
- const base64 = reader.result;
2032
- if (!base64) {
2033
- reject(new Error("FileReader returned empty result"));
2034
- return;
2035
- }
2036
- resolve({
2037
- name: file.name,
2038
- type: file.type,
2039
- size: file.size,
2040
- data: base64
2041
- });
2042
- } catch (error) {
2043
- reject(error);
2044
- }
2045
- };
2046
- reader.onerror = (error) => {
2047
- reject(new Error(`Failed to read file ${file.name}: ${error}`));
2048
- };
2049
- reader.readAsDataURL(file);
2050
- });
2051
- });
2052
- const convertedFiles = await Promise.all(filePromises);
2053
- if (onUploadAction) {
2054
- if (typeof onUploadAction === "function") {
2055
- await sendEvent(onUploadAction({ files: convertedFiles }));
2056
- } else {
2057
- await sendEvent({
2058
- ...onUploadAction,
2059
- data: {
2060
- ...onUploadAction.data,
2061
- files: convertedFiles
2062
- }
2063
- });
2064
- }
2065
- }
2066
- setStatus("success");
2067
- setTimeout(() => setStatus("idle"), 3e3);
2068
- } catch (error) {
2069
- console.error("Upload failed:", error);
2070
- setStatus("error");
2071
- setTimeout(() => setStatus("idle"), 3e3);
2072
- } finally {
2073
- setIsUploading(false);
2074
- if (fileInputRef.current) {
2075
- fileInputRef.current.value = "";
2076
- }
2077
- }
2078
- };
2079
- return /* @__PURE__ */ jsxs("div", { className: "relative inline-block", children: [
2080
- /* @__PURE__ */ jsx(
2081
- "input",
2082
- {
2083
- type: "file",
2084
- ref: fileInputRef,
2085
- onChange: handleFileChange,
2086
- multiple,
2087
- accept,
2088
- className: "hidden",
2089
- disabled: isUploading || disabled
2090
- }
2091
- ),
2092
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 mb-2 items-center", children: [
2093
- showInitialFiles && initialFiles?.map((file, index) => /* @__PURE__ */ jsx(
2094
- Image,
2095
- {
2096
- src: file.url,
2097
- alt: file.name,
2098
- width: "min",
2099
- radius: "md"
2100
- },
2101
- index
2102
- )),
2103
- displayEvents.map(
2104
- (event, index) => event.ui ? /* @__PURE__ */ jsx(UIRenderer, { node: event.ui }, index) : null
2105
- ),
2106
- /* @__PURE__ */ jsxs(
2107
- Button,
2108
- {
2109
- type: "button",
2110
- disabled: isUploading || disabled,
2111
- onClick: () => fileInputRef.current?.click(),
2112
- variant: "default",
2113
- children: [
2114
- isUploading ? /* @__PURE__ */ jsx(IconLoader2, { className: "h-4 w-4 animate-spin mr-2" }) : status === "success" ? /* @__PURE__ */ jsx(IconCheck, { className: "h-4 w-4 text-green-500 mr-2" }) : status === "error" ? /* @__PURE__ */ jsx(IconX, { className: "h-4 w-4 mr-2" }) : /* @__PURE__ */ jsx(IconUpload, { className: "h-4 w-4 mr-2" }),
2115
- status === "success" ? "Uploaded" : status === "error" ? "Failed" : label
2116
- ]
2117
- }
2118
- )
2119
- ] })
2120
- ] });
2121
- };
2122
- var Form = ({ children, onSubmitAction, gap = "md" }) => {
2123
- const { sendEvent } = useMelony();
2124
- const [isSubmitted, setIsSubmitted] = useState(false);
2125
- const handleSubmit = (e) => {
2126
- e.preventDefault();
2127
- if (isSubmitted) return;
2128
- const formData = new FormData(e.currentTarget);
2129
- const data = {};
2130
- formData.forEach((value, key) => {
2131
- data[key] = value;
2132
- });
2133
- if (onSubmitAction) {
2134
- setIsSubmitted(true);
2135
- if (typeof onSubmitAction === "object" && "type" in onSubmitAction) {
2136
- sendEvent({
2137
- ...onSubmitAction,
2138
- data: {
2139
- ...onSubmitAction?.data || {},
2140
- ...data
2141
- }
2142
- });
2143
- } else if (typeof onSubmitAction === "function") {
2144
- sendEvent(onSubmitAction(data));
2145
- }
2146
- }
2147
- };
2148
- return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, className: "w-full", children: /* @__PURE__ */ jsx("fieldset", { disabled: isSubmitted, className: "m-0 border-0 p-0", children: /* @__PURE__ */ jsx(
2149
- "div",
2150
- {
2151
- className: cn(
2152
- "flex flex-col transition-opacity",
2153
- gapMap[gap],
2154
- isSubmitted && "opacity-60 pointer-events-none"
2155
- ),
2156
- children
2157
- }
2158
- ) }) });
2159
- };
2160
- function DropdownMenu({ ...props }) {
2161
- return /* @__PURE__ */ jsx(Menu.Root, { "data-slot": "dropdown-menu", ...props });
2162
- }
2163
- function DropdownMenuTrigger({ ...props }) {
2164
- return /* @__PURE__ */ jsx(Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
2165
- }
2166
- function DropdownMenuContent({
2167
- align = "start",
2168
- alignOffset = 0,
2169
- side = "bottom",
2170
- sideOffset = 4,
2171
- className,
2172
- ...props
2173
- }) {
2174
- return /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(
2175
- Menu.Positioner,
2176
- {
2177
- className: "isolate z-50 outline-none",
2178
- align,
2179
- alignOffset,
2180
- side,
2181
- sideOffset,
2182
- children: /* @__PURE__ */ jsx(
2183
- Menu.Popup,
2184
- {
2185
- "data-slot": "dropdown-menu-content",
2186
- className: cn(
2187
- "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-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 ring-foreground/5 bg-popover text-popover-foreground min-w-48 rounded-2xl p-1 shadow-2xl ring-1 duration-100 z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none data-closed:overflow-hidden",
2188
- className
2189
- ),
2190
- ...props
2191
- }
2192
- )
2193
- }
2194
- ) });
2195
- }
2196
- function DropdownMenuGroup({ ...props }) {
2197
- return /* @__PURE__ */ jsx(Menu.Group, { "data-slot": "dropdown-menu-group", ...props });
2198
- }
2199
- function DropdownMenuLabel({
2200
- className,
2201
- inset,
2202
- ...props
2203
- }) {
2204
- return /* @__PURE__ */ jsx(
2205
- Menu.GroupLabel,
2206
- {
2207
- "data-slot": "dropdown-menu-label",
2208
- "data-inset": inset,
2209
- className: cn(
2210
- "text-muted-foreground px-3 py-2.5 text-xs data-[inset]:pl-8",
2211
- className
2212
- ),
2213
- ...props
2214
- }
2215
- );
2216
- }
2217
- function DropdownMenuItem({
2218
- className,
2219
- inset,
2220
- variant = "default",
2221
- ...props
2222
- }) {
2223
- return /* @__PURE__ */ jsx(
2224
- Menu.Item,
2225
- {
2226
- "data-slot": "dropdown-menu-item",
2227
- "data-inset": inset,
2228
- "data-variant": variant,
2229
- className: cn(
2230
- "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-xl px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
2231
- className
2232
- ),
2233
- ...props
2234
- }
2235
- );
2236
- }
2237
- function DropdownMenuCheckboxItem({
2238
- className,
2239
- children,
2240
- checked,
2241
- ...props
2242
- }) {
2243
- return /* @__PURE__ */ jsxs(
2244
- Menu.CheckboxItem,
2245
- {
2246
- "data-slot": "dropdown-menu-checkbox-item",
2247
- className: cn(
2248
- "focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
2249
- className
2250
- ),
2251
- checked,
2252
- ...props,
2253
- children: [
2254
- /* @__PURE__ */ jsx(
2255
- "span",
2256
- {
2257
- className: "pointer-events-none absolute right-2 flex items-center justify-center pointer-events-none",
2258
- "data-slot": "dropdown-menu-checkbox-item-indicator",
2259
- children: /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx(IconCheck, {}) })
2260
- }
2261
- ),
2262
- children
2263
- ]
2264
- }
2265
- );
2266
- }
2267
- function DropdownMenuSeparator({
2268
- className,
2269
- ...props
2270
- }) {
2271
- return /* @__PURE__ */ jsx(
2272
- Menu.Separator,
2273
- {
2274
- "data-slot": "dropdown-menu-separator",
2275
- className: cn("bg-border/50 -mx-1 my-1 h-px", className),
2276
- ...props
2277
- }
2278
- );
2279
- }
2280
- var Dropdown = ({ items = [], children }) => {
2281
- const { sendEvent } = useMelony();
2282
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
2283
- /* @__PURE__ */ jsx(
2284
- DropdownMenuTrigger,
2285
- {
2286
- render: (props) => /* @__PURE__ */ jsx(
2287
- Button,
2288
- {
2289
- variant: "outline",
2290
- size: "icon-sm",
2291
- ...props,
2292
- onClick: (e) => {
2293
- e.stopPropagation();
2294
- props.onClick?.(e);
2295
- },
2296
- children: children || /* @__PURE__ */ jsx(IconDotsVertical, { className: "size-3.5" })
2297
- }
2298
- )
2299
- }
2300
- ),
2301
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: cn("w-32"), children: items.map((item, i) => /* @__PURE__ */ jsxs(
2302
- DropdownMenuItem,
2303
- {
2304
- onClick: (e) => {
2305
- e.stopPropagation();
2306
- if (item.onClickAction) {
2307
- sendEvent(item.onClickAction);
2308
- }
2309
- },
2310
- children: [
2311
- item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm" }),
2312
- /* @__PURE__ */ jsx("span", { className: item.icon ? "ml-2" : "", children: item.label })
2313
- ]
2314
- },
2315
- `${item.label}-${i}`
2316
- )) })
2317
- ] });
2318
- };
2319
- function UIRenderer({ node }) {
2320
- const { type, props, children } = node;
2321
- const typeMap = {
2322
- card: Card2,
2323
- button: Button2,
2324
- row: Row,
2325
- col: Col,
2326
- text: Text,
2327
- heading: Heading,
2328
- badge: Badge2,
2329
- input: Input2,
2330
- hidden: Hidden,
2331
- textarea: Textarea2,
2332
- select: Select2,
2333
- checkbox: Checkbox,
2334
- radioGroup: RadioGroup,
2335
- colorPicker: ColorPicker,
2336
- spacer: Spacer,
2337
- divider: Divider,
2338
- box: Box,
2339
- float: Float,
2340
- image: Image,
2341
- video: Video,
2342
- icon: Icon,
2343
- list: List,
2344
- listItem: ListItem,
2345
- form: Form,
2346
- chart: Chart,
2347
- label: Label2,
2348
- upload: Upload,
2349
- dropdown: Dropdown
2350
- };
2351
- const Component = typeMap[type];
2352
- if (!Component) {
2353
- return /* @__PURE__ */ jsxs("div", { className: "text-destructive italic text-sm p-2 border border-dashed rounded border-destructive/50 bg-destructive/5", children: [
2354
- "[Unknown component: ",
2355
- type,
2356
- "]"
2357
- ] });
2358
- }
2359
- const renderedChildren = children?.map((child, i) => /* @__PURE__ */ jsx(UIRenderer, { node: child }, i));
2360
- return /* @__PURE__ */ jsx(Component, { ...props, children: renderedChildren });
2361
- }
2362
5
  var MelonyContext = createContext(
2363
6
  void 0
2364
7
  );
2365
- var defaultQueryClient = new QueryClient({
2366
- defaultOptions: {
2367
- queries: {
2368
- retry: false,
2369
- refetchOnWindowFocus: false
2370
- }
2371
- }
2372
- });
2373
- var MelonyContextProviderInner = ({
8
+ var MelonyProvider = ({
2374
9
  children,
2375
10
  client,
2376
- initialEvents,
2377
- setContextValue
11
+ initialEvents
2378
12
  }) => {
2379
13
  const [state, setState] = useState(client.getState());
2380
- const queryClient = useQueryClient();
2381
- const [dialog, setDialog] = useState();
2382
- const { data: config } = useQuery({
2383
- queryKey: ["melony-config", client.url],
2384
- queryFn: () => client.getConfig(),
2385
- staleTime: Infinity
2386
- });
2387
14
  useEffect(() => {
2388
- if (initialEvents && initialEvents.length > 0 && client.getState().events.length === 0) {
15
+ if (initialEvents?.length && client.getState().events.length === 0) {
2389
16
  client.reset(initialEvents);
2390
17
  }
2391
- }, [client, initialEvents]);
18
+ }, []);
2392
19
  useEffect(() => {
2393
- setState(client.getState());
2394
20
  const unsubscribe = client.subscribe(setState);
2395
- return () => {
2396
- unsubscribe();
2397
- };
21
+ return unsubscribe;
2398
22
  }, [client]);
2399
- const reset = useCallback(
2400
- (events) => client.reset(events),
2401
- [client]
2402
- );
2403
- const dispatchClientAction = useCallback(
2404
- async (event) => {
2405
- if (!event.type.startsWith("client:")) return false;
2406
- switch (event.type) {
2407
- case "client:navigate": {
2408
- const url = event.data?.url;
2409
- if (url) {
2410
- const isStreaming = client.getState().isLoading;
2411
- if (isStreaming) {
2412
- window.history.replaceState(null, "", url);
2413
- } else {
2414
- window.history.pushState(null, "", url);
2415
- }
2416
- window.dispatchEvent(new PopStateEvent("popstate"));
2417
- }
2418
- return true;
2419
- }
2420
- case "client:open-url": {
2421
- const { url, target = "_blank" } = event.data || {};
2422
- if (url) {
2423
- window.open(url, target);
2424
- }
2425
- return true;
2426
- }
2427
- case "client:copy": {
2428
- const { text } = event.data || {};
2429
- if (text) {
2430
- await navigator.clipboard.writeText(text);
2431
- }
2432
- return true;
2433
- }
2434
- case "client:reset": {
2435
- reset([]);
2436
- return true;
2437
- }
2438
- case "client:invalidate-query": {
2439
- const { queryKey } = event.data || {};
2440
- if (queryKey) {
2441
- await queryClient.invalidateQueries({ queryKey });
2442
- }
2443
- return true;
2444
- }
2445
- case "client:open-dialog": {
2446
- setDialog(event.data);
2447
- return true;
2448
- }
2449
- case "client:close-dialog": {
2450
- setDialog(null);
2451
- return true;
2452
- }
2453
- default:
2454
- return false;
2455
- }
2456
- },
2457
- [client, reset, queryClient]
2458
- );
2459
- const sendEvent = useCallback(
2460
- async (event) => {
2461
- const handled = await dispatchClientAction(event);
2462
- if (handled) return;
2463
- const generator = client.sendEvent(event);
2464
- for await (const incomingEvent of generator) {
2465
- await dispatchClientAction(incomingEvent);
2466
- }
2467
- },
2468
- [client, dispatchClientAction]
2469
- );
2470
- const value = useMemo(
23
+ const contextValue = useMemo(
2471
24
  () => ({
2472
25
  ...state,
2473
- messages: convertEventsToMessages(state.events),
2474
- sendEvent,
2475
- reset,
2476
- client,
2477
- config
2478
- }),
2479
- [state, sendEvent, reset, client, config]
2480
- );
2481
- useEffect(() => {
2482
- setContextValue(value);
2483
- }, [value, setContextValue]);
2484
- return /* @__PURE__ */ jsxs(NuqsAdapter, { children: [
2485
- children,
2486
- /* @__PURE__ */ jsx(
2487
- Dialog,
2488
- {
2489
- open: !!dialog,
2490
- onOpenChange: (open) => {
2491
- !open && setDialog(null);
2492
- },
2493
- children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md max-h-[90vh] overflow-y-auto", children: [
2494
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
2495
- /* @__PURE__ */ jsx(DialogTitle, { children: dialog?.title }),
2496
- dialog?.description && /* @__PURE__ */ jsx(DialogDescription, { children: dialog?.description })
2497
- ] }),
2498
- /* @__PURE__ */ jsxs(DialogClose, { children: [
2499
- /* @__PURE__ */ jsx(IconX, { className: "size-4" }),
2500
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
2501
- ] }),
2502
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: dialog?.ui && /* @__PURE__ */ jsx(UIRenderer, { node: dialog.ui }) })
2503
- ] })
2504
- }
2505
- )
2506
- ] });
2507
- };
2508
- var MelonyProvider = ({
2509
- children,
2510
- client,
2511
- initialEvents,
2512
- queryClient = defaultQueryClient
2513
- }) => {
2514
- const [contextValue, setContextValue] = useState(void 0);
2515
- return /* @__PURE__ */ jsx(MelonyContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(
2516
- MelonyContextProviderInner,
2517
- {
2518
- client,
2519
- initialEvents,
2520
- setContextValue,
2521
- children
2522
- }
2523
- ) }) });
2524
- };
2525
- var useAuth = () => {
2526
- const context = useContext(AuthContext);
2527
- if (context === void 0) {
2528
- throw new Error("useAuth must be used within an AuthProvider");
2529
- }
2530
- return context;
2531
- };
2532
- var AccountButton = ({
2533
- className,
2534
- variant = "outline",
2535
- size
2536
- }) => {
2537
- const { isLoading, isAuthenticated, user, login, logout } = useAuth();
2538
- const [open, setOpen] = React3.useState(false);
2539
- const [accountInfoOpen, setAccountInfoOpen] = React3.useState(false);
2540
- const [error, setError] = React3.useState(null);
2541
- const initials = React3.useMemo(() => {
2542
- const name = user?.displayName || user?.name;
2543
- if (!name) return "";
2544
- return name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
2545
- }, [user?.displayName, user?.name]);
2546
- const handleGoogleSignIn = async () => {
2547
- login();
2548
- };
2549
- if (isAuthenticated) {
2550
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2551
- /* @__PURE__ */ jsxs(DropdownMenu, { children: [
2552
- /* @__PURE__ */ jsx(
2553
- DropdownMenuTrigger,
2554
- {
2555
- render: (props) => /* @__PURE__ */ jsx(
2556
- Button,
2557
- {
2558
- variant,
2559
- size: "icon",
2560
- ...props,
2561
- className: cn("rounded-full", className),
2562
- children: user?.picture ? /* @__PURE__ */ jsx(
2563
- "img",
2564
- {
2565
- src: user.picture,
2566
- alt: user.displayName || user.name,
2567
- className: "size-7 rounded-full object-cover"
2568
- }
2569
- ) : /* @__PURE__ */ jsx("div", { className: "flex size-7 items-center justify-center rounded-full bg-muted text-xs font-bold", children: initials || /* @__PURE__ */ jsx(IconUser, { className: "size-4" }) })
2570
- }
2571
- )
2572
- }
2573
- ),
2574
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
2575
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 p-2", children: [
2576
- user?.picture ? /* @__PURE__ */ jsx(
2577
- "img",
2578
- {
2579
- src: user.picture,
2580
- alt: user.displayName || user.name,
2581
- className: "size-8 rounded-full object-cover"
2582
- }
2583
- ) : /* @__PURE__ */ jsx("div", { className: "flex size-8 items-center justify-center rounded-full bg-muted text-xs font-bold", children: initials || /* @__PURE__ */ jsx(IconUser, { className: "size-4" }) }),
2584
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-0.5 overflow-hidden", children: [
2585
- /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: user?.displayName || user?.name }),
2586
- /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user?.email })
2587
- ] })
2588
- ] }),
2589
- /* @__PURE__ */ jsx(Separator, { className: "my-1" }),
2590
- /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: () => setAccountInfoOpen(true), children: [
2591
- /* @__PURE__ */ jsx(IconUser, { className: "mr-2 size-4" }),
2592
- "Account Settings"
2593
- ] }),
2594
- /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: logout, className: "text-destructive", children: [
2595
- /* @__PURE__ */ jsx(IconLogout, { className: "mr-2 size-4" }),
2596
- "Logout"
2597
- ] })
2598
- ] })
2599
- ] }),
2600
- /* @__PURE__ */ jsx(Dialog, { open: accountInfoOpen, onOpenChange: setAccountInfoOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
2601
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
2602
- /* @__PURE__ */ jsx(DialogTitle, { children: "Account Information" }),
2603
- /* @__PURE__ */ jsx(DialogDescription, { children: "Your account details and settings." })
2604
- ] }),
2605
- /* @__PURE__ */ jsxs(DialogClose, { children: [
2606
- /* @__PURE__ */ jsx(IconX, { className: "size-4" }),
2607
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
2608
- ] }),
2609
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 py-4", children: [
2610
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
2611
- user?.picture ? /* @__PURE__ */ jsx(
2612
- "img",
2613
- {
2614
- src: user.picture,
2615
- alt: user.displayName || user.name,
2616
- className: "size-16 rounded-full object-cover"
2617
- }
2618
- ) : /* @__PURE__ */ jsx("div", { className: "flex size-16 items-center justify-center rounded-full bg-muted text-xl font-bold", children: initials || /* @__PURE__ */ jsx(IconUser, { className: "size-8 text-muted-foreground" }) }),
2619
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
2620
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: user?.displayName || user?.name }),
2621
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: user?.email })
2622
- ] })
2623
- ] }),
2624
- /* @__PURE__ */ jsx(Separator, {}),
2625
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
2626
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2627
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: "User ID" }),
2628
- /* @__PURE__ */ jsx("p", { className: "font-mono text-xs truncate", children: user?.uid || user?.id })
2629
- ] }),
2630
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2631
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: "Created At" }),
2632
- /* @__PURE__ */ jsx("p", { className: "text-xs", children: user?.createdAt || "N/A" })
2633
- ] })
2634
- ] })
2635
- ] })
2636
- ] }) })
2637
- ] });
2638
- }
2639
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2640
- /* @__PURE__ */ jsx(
2641
- Button,
2642
- {
2643
- variant,
2644
- size,
2645
- onClick: () => setOpen(true),
2646
- className,
2647
- children: "Sign in"
2648
- }
2649
- ),
2650
- /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
2651
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
2652
- /* @__PURE__ */ jsx(DialogTitle, { children: "Sign in to continue" }),
2653
- /* @__PURE__ */ jsx(DialogDescription, { children: "Choose your preferred sign-in method to access your account." })
2654
- ] }),
2655
- /* @__PURE__ */ jsxs(DialogClose, { children: [
2656
- /* @__PURE__ */ jsx(IconX, { className: "size-4" }),
2657
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
2658
- ] }),
2659
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
2660
- error && /* @__PURE__ */ jsx("div", { className: "rounded-lg bg-destructive/10 p-3 text-sm text-destructive", children: error }),
2661
- /* @__PURE__ */ jsxs(
2662
- Button,
2663
- {
2664
- onClick: handleGoogleSignIn,
2665
- disabled: isLoading,
2666
- variant: "outline",
2667
- className: "w-full",
2668
- size: "lg",
2669
- children: [
2670
- /* @__PURE__ */ jsx(IconBrandGoogle, { className: "mr-2 size-5" }),
2671
- isLoading ? "Signing in..." : "Continue with Google"
2672
- ]
2673
- }
2674
- )
2675
- ] })
2676
- ] }) })
2677
- ] });
2678
- };
2679
- function WelcomeScreen({
2680
- title = "Welcome to Melony",
2681
- description = "The most powerful AI agent framework for building modern applications. Connect your tools, build your brain, and ship faster.",
2682
- features = [
2683
- {
2684
- title: "Context Aware",
2685
- description: "Built-in state management for complex LLM flows."
2686
- },
2687
- {
2688
- title: "Extensible",
2689
- description: "Plugin architecture for easy integrations."
2690
- },
2691
- {
2692
- title: "Real-time",
2693
- description: "Streaming responses and live state updates."
2694
- },
2695
- {
2696
- title: "Tool-ready",
2697
- description: "Ready-to-use actions for common tasks."
2698
- }
2699
- ],
2700
- className,
2701
- onLoginClick,
2702
- termsUrl = "#",
2703
- privacyUrl = "#",
2704
- imageUrl,
2705
- imageAlt = "Product screenshot"
2706
- }) {
2707
- const { login, isLoading } = useAuth();
2708
- return /* @__PURE__ */ jsxs(
2709
- "div",
2710
- {
2711
- className: cn(
2712
- "flex min-h-[600px] h-full w-full flex-col md:flex-row bg-background overflow-hidden",
2713
- className
2714
- ),
2715
- children: [
2716
- /* @__PURE__ */ jsxs("div", { className: "flex w-8/12 flex-col bg-sidebar text-foreground relative overflow-hidden", children: [
2717
- /* @__PURE__ */ jsx("div", { className: "absolute -top-24 -left-24 size-96 bg-primary/5 rounded-full blur-3xl" }),
2718
- /* @__PURE__ */ jsx("div", { className: "absolute -bottom-24 -right-24 size-96 bg-primary/5 rounded-full blur-3xl" }),
2719
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto overflow-x-hidden relative z-10 flex flex-col p-8 md:p-12 lg:p-20", children: /* @__PURE__ */ jsxs("div", { className: "max-w-xl mx-auto w-full my-auto", children: [
2720
- /* @__PURE__ */ jsx("h1", { className: "mb-6 text-4xl font-bold tracking-tight md:text-5xl lg:text-6xl text-foreground", children: title }),
2721
- /* @__PURE__ */ jsx("p", { className: "mb-12 text-lg text-muted-foreground md:text-xl leading-relaxed", children: description }),
2722
- imageUrl && /* @__PURE__ */ jsxs("div", { className: "mb-12 relative group", children: [
2723
- /* @__PURE__ */ jsx("div", { className: "absolute -inset-1 bg-gradient-to-r from-primary/20 to-primary/10 rounded-xl blur opacity-25 group-hover:opacity-50 transition duration-1000 group-hover:duration-200" }),
2724
- /* @__PURE__ */ jsx(
2725
- "img",
2726
- {
2727
- src: imageUrl,
2728
- alt: imageAlt,
2729
- className: "relative rounded-xl border border-border/50 shadow-2xl transition-transform duration-500 hover:scale-[1.02] w-full"
2730
- }
2731
- )
2732
- ] }),
2733
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-x-8 gap-y-10 sm:grid-cols-2", children: features.map((feature, i) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2734
- /* @__PURE__ */ jsx("h3", { className: "font-bold text-lg text-foreground", children: feature.title }),
2735
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground leading-relaxed", children: feature.description })
2736
- ] }, i)) })
2737
- ] }) })
2738
- ] }),
2739
- /* @__PURE__ */ jsx("div", { className: "flex w-4/12 flex-col overflow-y-auto p-8 md:p-12 lg:p-20 bg-background transition-colors duration-300", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-sm space-y-8 my-auto mx-auto", children: [
2740
- /* @__PURE__ */ jsxs("div", { className: "space-y-3 text-center md:text-left", children: [
2741
- /* @__PURE__ */ jsx("h2", { className: "text-3xl font-bold tracking-tight text-foreground", children: "Get Started" }),
2742
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-lg", children: "Sign in to your account to continue" })
2743
- ] }),
2744
- /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx(AccountButton, {}) }),
2745
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground leading-relaxed text-center md:text-left", children: [
2746
- "By continuing, you agree to our ",
2747
- /* @__PURE__ */ jsx("br", { className: "hidden md:block" }),
2748
- /* @__PURE__ */ jsx(
2749
- "a",
2750
- {
2751
- href: termsUrl,
2752
- target: "_blank",
2753
- rel: "noopener noreferrer",
2754
- className: "underline underline-offset-4 hover:text-primary transition-colors font-medium",
2755
- children: "Terms of Service"
2756
- }
2757
- ),
2758
- " ",
2759
- "and",
2760
- " ",
2761
- /* @__PURE__ */ jsx(
2762
- "a",
2763
- {
2764
- href: privacyUrl,
2765
- target: "_blank",
2766
- rel: "noopener noreferrer",
2767
- className: "underline underline-offset-4 hover:text-primary transition-colors font-medium",
2768
- children: "Privacy Policy"
2769
- }
2770
- ),
2771
- "."
2772
- ] })
2773
- ] }) })
2774
- ]
2775
- }
2776
- );
2777
- }
2778
- var AuthContext = createContext(
2779
- void 0
2780
- );
2781
- var AuthProvider = ({
2782
- children,
2783
- service,
2784
- welcomeScreenProps
2785
- }) => {
2786
- const queryClient = useQueryClient();
2787
- const { data: user, isLoading } = useQuery({
2788
- queryKey: ["auth-user", service],
2789
- queryFn: () => service.getMe(),
2790
- retry: false
2791
- });
2792
- const logoutMutation = useMutation({
2793
- mutationFn: () => service.logout(),
2794
- onSuccess: () => {
2795
- queryClient.setQueryData(["auth-user", service], null);
2796
- }
2797
- });
2798
- const login = useCallback(() => {
2799
- service.login();
2800
- }, [service]);
2801
- const logout = useCallback(async () => {
2802
- try {
2803
- await logoutMutation.mutateAsync();
2804
- } catch (error) {
2805
- console.error("Failed to logout:", error);
2806
- }
2807
- }, [logoutMutation]);
2808
- if (isLoading) {
2809
- return /* @__PURE__ */ jsx(
2810
- "div",
2811
- {
2812
- style: {
2813
- height: "100vh",
2814
- width: "100vw",
2815
- display: "flex",
2816
- justifyContent: "center",
2817
- alignItems: "center",
2818
- fontSize: "0.875rem",
2819
- letterSpacing: "0.01em"
2820
- },
2821
- className: "text-muted-foreground animate-pulse",
2822
- children: "Loading..."
2823
- }
2824
- );
2825
- }
2826
- const value = {
2827
- user: user || null,
2828
- isAuthenticated: !!user,
2829
- isLoading,
2830
- login,
2831
- logout,
2832
- getToken: service.getToken
2833
- };
2834
- return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children: !value.isAuthenticated && welcomeScreenProps ? /* @__PURE__ */ jsx(WelcomeScreen, { ...welcomeScreenProps }) : children });
2835
- };
2836
- var ThreadContext = createContext(
2837
- void 0
2838
- );
2839
- var ThreadProvider = ({
2840
- children,
2841
- service,
2842
- initialThreadId: providedInitialThreadId
2843
- }) => {
2844
- const queryClient = useQueryClient();
2845
- const melonyContext = useContext(MelonyContext);
2846
- const [activeThreadId, setActiveThreadId] = useQueryState(
2847
- "threadId",
2848
- parseAsString
2849
- );
2850
- const prevActiveThreadIdRef = useRef(activeThreadId);
2851
- useEffect(() => {
2852
- prevActiveThreadIdRef.current = activeThreadId;
2853
- }, [activeThreadId]);
2854
- useEffect(() => {
2855
- if (!activeThreadId && providedInitialThreadId) {
2856
- setActiveThreadId(providedInitialThreadId);
2857
- }
2858
- }, [activeThreadId, providedInitialThreadId, setActiveThreadId]);
2859
- const {
2860
- data: threads = [],
2861
- isLoading,
2862
- isFetched: isFetchedThreads,
2863
- error: threadsError,
2864
- refetch: refreshThreads
2865
- } = useQuery({
2866
- queryKey: ["threads"],
2867
- queryFn: () => service.getThreads(),
2868
- staleTime: prevActiveThreadIdRef.current === null && activeThreadId !== null ? Infinity : 0
2869
- });
2870
- const isNewThread = useMemo(() => {
2871
- if (!activeThreadId || !isFetchedThreads) return false;
2872
- return !threads.some((t) => t.id === activeThreadId);
2873
- }, [activeThreadId, threads, isFetchedThreads]);
2874
- const { data: threadEvents = [], isLoading: isLoadingEvents } = useQuery({
2875
- queryKey: ["threads", activeThreadId, "events"],
2876
- queryFn: () => service.getEvents(activeThreadId),
2877
- enabled: !!activeThreadId,
2878
- initialData: isNewThread ? melonyContext?.events : void 0,
2879
- staleTime: isNewThread ? Infinity : 0
2880
- });
2881
- const createMutation = useMutation({
2882
- mutationFn: async () => {
2883
- return null;
2884
- },
2885
- onSuccess: async () => {
2886
- await setActiveThreadId(null);
2887
- }
2888
- });
2889
- const deleteMutation = useMutation({
2890
- mutationFn: (threadId) => service.deleteThread(threadId),
2891
- onSuccess: async (_, threadId) => {
2892
- await queryClient.invalidateQueries({ queryKey: ["threads"] });
2893
- if (activeThreadId === threadId) {
2894
- const remainingThreads = threads.filter((t) => t.id !== threadId);
2895
- const nextId = remainingThreads.length > 0 ? remainingThreads[0].id : null;
2896
- await setActiveThreadId(nextId);
2897
- }
2898
- }
2899
- });
2900
- const selectThread = useCallback(
2901
- (threadId) => {
2902
- setActiveThreadId(threadId);
2903
- },
2904
- [setActiveThreadId]
2905
- );
2906
- const createThread = useCallback(async () => {
2907
- return createMutation.mutateAsync();
2908
- }, [createMutation]);
2909
- const deleteThread = useCallback(
2910
- async (threadId) => {
2911
- return deleteMutation.mutateAsync(threadId);
2912
- },
2913
- [deleteMutation]
2914
- );
2915
- const value = useMemo(
2916
- () => ({
2917
- threads,
2918
- activeThreadId,
2919
- isLoading,
2920
- error: threadsError || null,
2921
- selectThread,
2922
- createThread,
2923
- deleteThread,
2924
- refreshThreads: async () => {
2925
- await refreshThreads();
26
+ sendEvent: async (event) => {
27
+ const generator = client.sendEvent(event);
28
+ for await (const _ of generator) {
29
+ }
2926
30
  },
2927
- threadEvents,
2928
- isLoadingEvents
31
+ reset: client.reset.bind(client),
32
+ client
2929
33
  }),
2930
- [
2931
- threads,
2932
- activeThreadId,
2933
- isLoading,
2934
- threadsError,
2935
- selectThread,
2936
- createThread,
2937
- deleteThread,
2938
- refreshThreads,
2939
- threadEvents,
2940
- isLoadingEvents
2941
- ]
34
+ [state, client]
2942
35
  );
2943
- return /* @__PURE__ */ jsx(ThreadContext.Provider, { value, children });
36
+ return /* @__PURE__ */ jsx(MelonyContext.Provider, { value: contextValue, children });
2944
37
  };
2945
- function useScreenSize(mobileBreakpoint = 768, tabletBreakpoint = 1024) {
2946
- const [screenSize, setScreenSize] = useState(() => {
2947
- if (typeof window === "undefined") {
2948
- return {
2949
- width: 1024,
2950
- height: 768,
2951
- isMobile: false,
2952
- isTablet: false,
2953
- isDesktop: true
2954
- };
2955
- }
2956
- const width = window.innerWidth;
2957
- return {
2958
- width,
2959
- height: window.innerHeight,
2960
- isMobile: width < mobileBreakpoint,
2961
- isTablet: width >= mobileBreakpoint && width < tabletBreakpoint,
2962
- isDesktop: width >= tabletBreakpoint
2963
- };
2964
- });
2965
- useEffect(() => {
2966
- if (typeof window === "undefined") return;
2967
- const updateScreenSize = () => {
2968
- const width = window.innerWidth;
2969
- const height = window.innerHeight;
2970
- setScreenSize({
2971
- width,
2972
- height,
2973
- isMobile: width < mobileBreakpoint,
2974
- isTablet: width >= mobileBreakpoint && width < tabletBreakpoint,
2975
- isDesktop: width >= tabletBreakpoint
2976
- });
2977
- };
2978
- updateScreenSize();
2979
- window.addEventListener("resize", updateScreenSize);
2980
- return () => {
2981
- window.removeEventListener("resize", updateScreenSize);
2982
- };
2983
- }, [mobileBreakpoint, tabletBreakpoint]);
2984
- return screenSize;
2985
- }
2986
- var SidebarContext = createContext(
2987
- void 0
2988
- );
2989
- function useSidebar() {
2990
- const context = useContext(SidebarContext);
2991
- if (context === void 0) {
2992
- throw new Error("useSidebar must be used within a SidebarProvider");
2993
- }
2994
- return context;
2995
- }
2996
- function SidebarProvider({
2997
- children,
2998
- defaultLeftCollapsed,
2999
- defaultRightCollapsed
3000
- }) {
3001
- const { isMobile, isTablet } = useScreenSize();
3002
- const isSmallScreen = isMobile || isTablet;
3003
- const [leftCollapsed, setLeftCollapsed] = useState(() => {
3004
- if (defaultLeftCollapsed !== void 0) return defaultLeftCollapsed;
3005
- if (typeof window !== "undefined") {
3006
- return window.innerWidth < 1024;
3007
- }
3008
- return false;
3009
- });
3010
- const [rightCollapsed, setRightCollapsed] = useState(() => {
3011
- if (defaultRightCollapsed !== void 0) return defaultRightCollapsed;
3012
- if (typeof window !== "undefined") {
3013
- return window.innerWidth < 1024;
3014
- }
3015
- return false;
3016
- });
3017
- useEffect(() => {
3018
- if (isSmallScreen) {
3019
- setLeftCollapsed(true);
3020
- setRightCollapsed(true);
3021
- }
3022
- }, [isSmallScreen]);
3023
- const handleLeftToggle = useCallback((collapsed) => {
3024
- setLeftCollapsed(collapsed);
3025
- }, []);
3026
- const handleRightToggle = useCallback((collapsed) => {
3027
- setRightCollapsed(collapsed);
3028
- }, []);
3029
- const contextValue = useMemo(
3030
- () => ({
3031
- leftCollapsed,
3032
- rightCollapsed,
3033
- setLeftCollapsed: handleLeftToggle,
3034
- setRightCollapsed: handleRightToggle,
3035
- leftCollapsible: true,
3036
- rightCollapsible: true
3037
- }),
3038
- [leftCollapsed, rightCollapsed, handleLeftToggle, handleRightToggle]
3039
- );
3040
- return /* @__PURE__ */ jsx(SidebarContext.Provider, { value: contextValue, children });
3041
- }
3042
- var ThemeContext = createContext(void 0);
3043
- function ThemeProvider({ children }) {
3044
- const [theme, setThemeState] = useState("system");
3045
- const [resolvedTheme, setResolvedTheme] = useState("light");
3046
- useEffect(() => {
3047
- if (typeof window !== "undefined") {
3048
- const stored = localStorage.getItem("theme");
3049
- if (stored) {
3050
- setThemeState(stored);
3051
- }
3052
- }
3053
- }, []);
3054
- useEffect(() => {
3055
- if (typeof window !== "undefined") {
3056
- if (theme === "system") {
3057
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
3058
- const updateResolvedTheme = () => {
3059
- setResolvedTheme(mediaQuery.matches ? "dark" : "light");
3060
- };
3061
- updateResolvedTheme();
3062
- mediaQuery.addEventListener("change", updateResolvedTheme);
3063
- return () => mediaQuery.removeEventListener("change", updateResolvedTheme);
3064
- } else {
3065
- setResolvedTheme(theme);
3066
- }
3067
- }
3068
- }, [theme]);
3069
- useEffect(() => {
3070
- if (typeof window !== "undefined") {
3071
- const root = document.documentElement;
3072
- if (resolvedTheme === "dark") {
3073
- root.classList.add("dark");
3074
- } else {
3075
- root.classList.remove("dark");
3076
- }
3077
- }
3078
- }, [resolvedTheme]);
3079
- const setTheme = (newTheme) => {
3080
- setThemeState(newTheme);
3081
- if (typeof window !== "undefined") {
3082
- localStorage.setItem("theme", newTheme);
3083
- }
3084
- };
3085
- return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: { theme, setTheme, resolvedTheme }, children });
3086
- }
3087
- function useTheme() {
3088
- const context = useContext(ThemeContext);
3089
- if (context === void 0) {
3090
- throw new Error("useTheme must be used within a ThemeProvider");
3091
- }
3092
- return context;
3093
- }
3094
- var useThreads = () => {
3095
- const context = useContext(ThreadContext);
38
+ var useMelony = () => {
39
+ const context = useContext(MelonyContext);
3096
40
  if (context === void 0) {
3097
- throw new Error("useThreads must be used within a ThreadProvider");
41
+ throw new Error("useMelony must be used within a MelonyProvider");
3098
42
  }
3099
43
  return context;
3100
44
  };
3101
- var useSurface = (options) => {
3102
- const { events } = useMelony();
3103
- const surfaceEvents = useMemo(() => {
3104
- const filtered = events.filter((event) => event.surface === options.name);
3105
- return filterEventsBySlots(filtered);
3106
- }, [events, options.name]);
3107
- return {
3108
- events: surfaceEvents
3109
- };
3110
- };
3111
- function Composer({
3112
- value,
3113
- onChange,
3114
- onSubmit,
3115
- placeholder = "Type a message...",
3116
- isLoading,
3117
- className,
3118
- options = [],
3119
- autoFocus = false,
3120
- defaultSelectedIds = [],
3121
- fileAttachments
3122
- }) {
3123
- const enabled = fileAttachments?.enabled || false;
3124
- const accept = fileAttachments?.accept;
3125
- const maxFiles = fileAttachments?.maxFiles ?? 10;
3126
- const maxFileSize = fileAttachments?.maxFileSize ?? 10 * 1024 * 1024;
3127
- const [selectedOptions, setSelectedOptions] = React3__default.useState(
3128
- () => new Set(defaultSelectedIds)
3129
- );
3130
- const [attachedFiles, setAttachedFiles] = React3__default.useState([]);
3131
- const [previews, setPreviews] = React3__default.useState([]);
3132
- const fileInputRef = React3__default.useRef(null);
3133
- React3__default.useEffect(() => {
3134
- const newPreviews = attachedFiles.map((file) => ({
3135
- name: file.name,
3136
- type: file.type,
3137
- size: file.size,
3138
- url: file.type.startsWith("image/") ? URL.createObjectURL(file) : ""
3139
- }));
3140
- setPreviews(newPreviews);
3141
- return () => {
3142
- newPreviews.forEach((p) => {
3143
- if (p.url) URL.revokeObjectURL(p.url);
3144
- });
3145
- };
3146
- }, [attachedFiles]);
3147
- const toggleOption = (id, groupOptions, type = "multiple") => {
3148
- const next = new Set(selectedOptions);
3149
- if (type === "single") {
3150
- const isAlreadySelected = next.has(id);
3151
- if (groupOptions) {
3152
- groupOptions.forEach((o) => next.delete(o.id));
3153
- }
3154
- if (!isAlreadySelected) {
3155
- next.add(id);
3156
- }
3157
- } else {
3158
- if (next.has(id)) {
3159
- next.delete(id);
3160
- } else {
3161
- next.add(id);
3162
- }
3163
- }
3164
- setSelectedOptions(next);
3165
- };
3166
- const handleFileSelect = (e) => {
3167
- const files = Array.from(e.target.files || []);
3168
- const validFiles = files.filter((file) => {
3169
- if (file.size > maxFileSize) {
3170
- console.warn(
3171
- `File ${file.name} exceeds maximum size of ${maxFileSize} bytes`
3172
- );
3173
- return false;
3174
- }
3175
- return true;
3176
- });
3177
- const remainingSlots = maxFiles - attachedFiles.length;
3178
- const filesToAdd = validFiles.slice(0, remainingSlots);
3179
- if (filesToAdd.length < validFiles.length) {
3180
- console.warn(
3181
- `Only ${filesToAdd.length} files can be added (max: ${maxFiles})`
3182
- );
3183
- }
3184
- setAttachedFiles((prev) => [...prev, ...filesToAdd]);
3185
- if (fileInputRef.current) {
3186
- fileInputRef.current.value = "";
3187
- }
3188
- };
3189
- const handleRemoveFile = (index) => {
3190
- setAttachedFiles((prev) => prev.filter((_, i) => i !== index));
3191
- };
3192
- const handleInternalSubmit = async () => {
3193
- const state = {};
3194
- options.forEach((group) => {
3195
- const selectedInGroup = group.options.filter(
3196
- (o) => selectedOptions.has(o.id)
3197
- );
3198
- if (selectedInGroup.length > 0) {
3199
- if (group.type === "single") {
3200
- state[group.id] = selectedInGroup[0].value;
3201
- } else {
3202
- state[group.id] = selectedInGroup.map((o) => ({
3203
- id: o.id,
3204
- value: o.value
3205
- }));
3206
- }
3207
- }
3208
- });
3209
- if (attachedFiles.length > 0) {
3210
- const filePromises = attachedFiles.map((file) => {
3211
- return new Promise((resolve, reject) => {
3212
- const reader = new FileReader();
3213
- reader.onload = () => {
3214
- try {
3215
- const base64 = reader.result;
3216
- if (!base64) {
3217
- reject(new Error("FileReader returned empty result"));
3218
- return;
3219
- }
3220
- resolve({
3221
- name: file.name,
3222
- type: file.type,
3223
- size: file.size,
3224
- data: base64
3225
- });
3226
- } catch (error) {
3227
- reject(error);
3228
- }
3229
- };
3230
- reader.onerror = (error) => {
3231
- reject(new Error(`Failed to read file ${file.name}: ${error}`));
3232
- };
3233
- reader.onabort = () => {
3234
- reject(new Error(`File read aborted for ${file.name}`));
3235
- };
3236
- reader.readAsDataURL(file);
3237
- });
3238
- });
3239
- try {
3240
- const convertedFiles = await Promise.all(filePromises);
3241
- if (convertedFiles.length > 0) {
3242
- state.files = convertedFiles;
3243
- }
3244
- } catch (error) {
3245
- console.error("Failed to convert files to base64:", error);
3246
- }
3247
- }
3248
- onSubmit(state);
3249
- setAttachedFiles([]);
3250
- };
3251
- const handleKeyDown = (e) => {
3252
- if (e.key === "Enter" && !e.shiftKey) {
3253
- e.preventDefault();
3254
- handleInternalSubmit().catch(console.error);
3255
- }
3256
- };
3257
- const handlePaste = (e) => {
3258
- if (!enabled) return;
3259
- const items = Array.from(e.clipboardData.items);
3260
- const files = items.map((item) => item.getAsFile()).filter((file) => file !== null);
3261
- if (files.length > 0) {
3262
- const remainingSlots = maxFiles - attachedFiles.length;
3263
- const validFiles = files.filter((f) => f.size <= maxFileSize);
3264
- const filesToAdd = validFiles.slice(0, remainingSlots);
3265
- if (filesToAdd.length > 0) {
3266
- setAttachedFiles((prev) => [...prev, ...filesToAdd]);
3267
- }
3268
- }
3269
- };
3270
- return /* @__PURE__ */ jsx("div", { className: cn("relative flex flex-col w-full", className), children: /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col w-full border-input border-[1.5px] rounded-3xl bg-background shadow-sm focus-within:border-ring transition-all p-2", children: [
3271
- previews.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-3 p-2 px-3 pb-3", children: previews.map((preview, index) => /* @__PURE__ */ jsxs(
3272
- "div",
3273
- {
3274
- className: "group relative flex flex-col items-center justify-center w-20 h-20 rounded-xl border bg-muted/30 overflow-hidden shadow-sm",
3275
- children: [
3276
- preview.type.startsWith("image/") ? /* @__PURE__ */ jsx(
3277
- "img",
3278
- {
3279
- src: preview.url,
3280
- alt: preview.name,
3281
- className: "w-full h-full object-cover"
3282
- }
3283
- ) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-2 text-center", children: [
3284
- preview.type.includes("text") || preview.type.includes("pdf") ? /* @__PURE__ */ jsx(IconFileText, { className: "h-8 w-8 text-muted-foreground" }) : /* @__PURE__ */ jsx(IconFile, { className: "h-8 w-8 text-muted-foreground" }),
3285
- /* @__PURE__ */ jsx("span", { className: "text-[9px] truncate w-full px-1 mt-1 text-muted-foreground", children: preview.name })
3286
- ] }),
3287
- /* @__PURE__ */ jsx(
3288
- "button",
3289
- {
3290
- type: "button",
3291
- onClick: () => handleRemoveFile(index),
3292
- className: "absolute top-1 right-1 p-1 rounded-full bg-foreground/10 hover:bg-foreground/20 backdrop-blur-md opacity-0 group-hover:opacity-100 transition-opacity",
3293
- children: /* @__PURE__ */ jsx(IconX, { className: "h-3 w-3" })
3294
- }
3295
- )
3296
- ]
3297
- },
3298
- index
3299
- )) }),
3300
- /* @__PURE__ */ jsx(
3301
- Textarea,
3302
- {
3303
- value,
3304
- onChange: (e) => onChange(e.target.value),
3305
- onKeyDown: handleKeyDown,
3306
- onPaste: handlePaste,
3307
- placeholder,
3308
- className: "min-h-[44px] max-h-[200px] border-none bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-3 py-2 text-[15px] resize-none",
3309
- autoFocus
3310
- }
3311
- ),
3312
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center px-1", children: [
3313
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
3314
- enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
3315
- /* @__PURE__ */ jsx(
3316
- "input",
3317
- {
3318
- ref: fileInputRef,
3319
- type: "file",
3320
- multiple: true,
3321
- accept,
3322
- onChange: handleFileSelect,
3323
- className: "hidden",
3324
- disabled: isLoading || attachedFiles.length >= maxFiles
3325
- }
3326
- ),
3327
- /* @__PURE__ */ jsxs(
3328
- Button,
3329
- {
3330
- type: "button",
3331
- variant: "ghost",
3332
- size: "sm",
3333
- onClick: () => fileInputRef.current?.click(),
3334
- disabled: isLoading || attachedFiles.length >= maxFiles,
3335
- className: "text-muted-foreground",
3336
- title: "Attach file",
3337
- children: [
3338
- /* @__PURE__ */ jsx(IconPaperclip, { className: "h-4 w-4" }),
3339
- attachedFiles.length > 0 && /* @__PURE__ */ jsx(Badge, { className: "ml-1 h-[18px] min-w-[18px] px-1.5 text-[10px]", children: attachedFiles.length })
3340
- ]
3341
- }
3342
- )
3343
- ] }),
3344
- options.map((group) => {
3345
- const selectedInGroup = group.options.filter(
3346
- (o) => selectedOptions.has(o.id)
3347
- );
3348
- const label = selectedInGroup.length === 0 ? group.label : selectedInGroup.length === 1 ? selectedInGroup[0].label : group.label;
3349
- const isSingle = group.type === "single";
3350
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
3351
- /* @__PURE__ */ jsx(
3352
- DropdownMenuTrigger,
3353
- {
3354
- render: (props) => /* @__PURE__ */ jsxs(
3355
- Button,
3356
- {
3357
- variant: "ghost",
3358
- size: "sm",
3359
- ...props,
3360
- className: cn(
3361
- "gap-2",
3362
- selectedInGroup.length > 0 ? "text-foreground bg-muted/50" : "text-muted-foreground"
3363
- ),
3364
- children: [
3365
- label,
3366
- selectedInGroup.length > 1 && /* @__PURE__ */ jsx(Badge, { className: "h-[18px] min-w-[18px] px-1.5 text-[10px]", children: selectedInGroup.length }),
3367
- /* @__PURE__ */ jsx(IconChevronDown, { className: "h-3 w-3 opacity-50" })
3368
- ]
3369
- }
3370
- )
3371
- }
3372
- ),
3373
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: "w-56", children: /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
3374
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: group.label }),
3375
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
3376
- group.options.map((option) => /* @__PURE__ */ jsx(
3377
- DropdownMenuCheckboxItem,
3378
- {
3379
- checked: selectedOptions.has(option.id),
3380
- onCheckedChange: () => toggleOption(
3381
- option.id,
3382
- group.options,
3383
- isSingle ? "single" : "multiple"
3384
- ),
3385
- onSelect: (e) => e.preventDefault(),
3386
- children: option.label
3387
- },
3388
- option.id
3389
- ))
3390
- ] }) })
3391
- ] }, group.id);
3392
- })
3393
- ] }),
3394
- /* @__PURE__ */ jsx(
3395
- Button,
3396
- {
3397
- type: "submit",
3398
- disabled: !value.trim() && attachedFiles.length === 0 && selectedOptions.size === 0 && !isLoading || isLoading,
3399
- size: "icon-lg",
3400
- onClick: () => handleInternalSubmit().catch(console.error),
3401
- children: isLoading ? /* @__PURE__ */ jsx(IconLoader2, { className: "h-5 w-5 animate-spin" }) : /* @__PURE__ */ jsx(IconArrowUp, { className: "h-5 w-5" })
3402
- }
3403
- )
3404
- ] })
3405
- ] }) });
3406
- }
3407
- function StarterPrompts({ prompts }) {
3408
- if (!prompts || prompts.length === 0) {
3409
- return null;
3410
- }
3411
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-4 animate-in fade-in slide-in-from-bottom-4 duration-500 mt-auto max-w-2xl", children: [
3412
- /* @__PURE__ */ jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold tracking-tight", children: "What can I help with today?" }) }),
3413
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1 w-full", children: prompts.map((item, index) => /* @__PURE__ */ jsx(
3414
- Button2,
3415
- {
3416
- label: item.label,
3417
- variant: "ghost",
3418
- size: "lg",
3419
- onClickAction: {
3420
- type: "text",
3421
- role: "user",
3422
- data: { content: item.prompt }
3423
- },
3424
- justify: "start"
3425
- },
3426
- index
3427
- )) })
3428
- ] });
3429
- }
3430
- function MessageContent({ events }) {
3431
- const displayEvents = useMemo(() => filterEventsBySlots(events), [events]);
3432
- return /* @__PURE__ */ jsx(Fragment, { children: displayEvents.map((displayEvent, index) => {
3433
- if (displayEvent.type === "text-delta") {
3434
- return /* @__PURE__ */ jsx("span", { children: displayEvent.data?.delta }, index);
3435
- }
3436
- if (displayEvent.type === "text") {
3437
- return /* @__PURE__ */ jsx("p", { children: displayEvent.data?.content || displayEvent.data?.text }, index);
3438
- }
3439
- if (displayEvent.ui) {
3440
- return /* @__PURE__ */ jsx(UIRenderer, { node: displayEvent.ui }, index);
3441
- }
3442
- return null;
3443
- }) });
3444
- }
3445
- function MessageBubble({ message }) {
3446
- const isUser = message.role === "user";
3447
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col", isUser ? "items-end" : "items-start"), children: /* @__PURE__ */ jsx(
3448
- "div",
3449
- {
3450
- className: cn(
3451
- "flex flex-col items-start max-w-[85%] rounded-2xl px-4 py-2 space-y-4 whitespace-pre-wrap",
3452
- isUser ? "bg-primary text-primary-foreground" : "px-0 py-0 text-foreground"
3453
- ),
3454
- children: /* @__PURE__ */ jsx(MessageContent, { events: message.content })
3455
- }
3456
- ) });
3457
- }
3458
- function LoadingIndicator({ status }) {
3459
- const [isExpanded, setIsExpanded] = useState(false);
3460
- const message = status?.message || "Processing...";
3461
- const details = status?.details;
3462
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
3463
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground group", children: [
3464
- /* @__PURE__ */ jsx(IconLoader2, { className: "size-3.5 animate-spin" }),
3465
- /* @__PURE__ */ jsx("div", { className: "animate-pulse", children: message }),
3466
- details && /* @__PURE__ */ jsx(
3467
- "button",
3468
- {
3469
- onClick: () => setIsExpanded(!isExpanded),
3470
- className: "p-0.5 hover:bg-muted rounded-sm transition-colors flex items-center justify-center",
3471
- title: isExpanded ? "Hide details" : "Show details",
3472
- children: isExpanded ? /* @__PURE__ */ jsx(IconChevronUp, { className: "size-3.5 opacity-50 group-hover:opacity-100" }) : /* @__PURE__ */ jsx(IconChevronDown, { className: "size-3.5 opacity-50 group-hover:opacity-100" })
3473
- }
3474
- )
3475
- ] }),
3476
- isExpanded && details && /* @__PURE__ */ jsx("div", { className: "text-[10px] leading-relaxed font-mono bg-muted/30 p-2.5 rounded border border-border/50 max-h-64 overflow-y-auto whitespace-pre-wrap text-muted-foreground shadow-sm", children: details })
3477
- ] });
3478
- }
3479
- function ErrorDisplay({ error }) {
3480
- return /* @__PURE__ */ jsx("div", { className: "text-destructive p-2 border border-destructive rounded-md bg-destructive/10", children: error.message });
3481
- }
3482
- function MessageList({
3483
- messages,
3484
- isLoading,
3485
- error,
3486
- loadingStatus
3487
- }) {
3488
- if (messages.length === 0) {
3489
- return null;
3490
- }
3491
- const isTextStreaming = useMemo(() => {
3492
- if (messages.length === 0 || !isLoading) return false;
3493
- const lastMessage = messages[messages.length - 1];
3494
- return lastMessage.content.some((event) => event.type === "text-delta");
3495
- }, [messages, isLoading]);
3496
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
3497
- messages.map((message, index) => /* @__PURE__ */ jsx(MessageBubble, { message }, index)),
3498
- isLoading && !isTextStreaming && /* @__PURE__ */ jsx(LoadingIndicator, { status: loadingStatus }),
3499
- error && /* @__PURE__ */ jsx(ErrorDisplay, { error })
3500
- ] });
3501
- }
3502
- function Thread({
3503
- placeholder = "Type a message...",
3504
- starterPrompts: localStarterPrompts,
3505
- options: localOptions,
3506
- autoFocus = false,
3507
- defaultSelectedIds
3508
- }) {
3509
- const { activeThreadId, threadEvents, isLoadingEvents } = useThreads();
3510
- const {
3511
- messages: initialMessages,
3512
- isLoading,
3513
- error,
3514
- sendEvent,
3515
- loadingStatus,
3516
- config
3517
- } = useMelony({
3518
- initialEvents: threadEvents
3519
- });
3520
- const messages = useMemo(() => {
3521
- return initialMessages.map((msg) => ({
3522
- ...msg,
3523
- content: msg.content.filter((event) => !event.surface)
3524
- })).filter(
3525
- (msg) => ["user", "assistant"].includes(msg.role) && msg.content.length > 0
3526
- );
3527
- }, [initialMessages]);
3528
- const starterPrompts = localStarterPrompts ?? config?.starterPrompts;
3529
- const options = localOptions ?? config?.options;
3530
- const fileAttachments = config?.fileAttachments;
3531
- const allDefaultSelectedIds = useMemo(() => {
3532
- const defaultSelectedIdsFromOptions = options?.flatMap((group) => group.defaultSelectedIds ?? []) ?? [];
3533
- return [
3534
- .../* @__PURE__ */ new Set([
3535
- ...defaultSelectedIdsFromOptions,
3536
- ...defaultSelectedIds ?? []
3537
- ])
3538
- ];
3539
- }, [options, defaultSelectedIds]);
3540
- const [input, setInput] = useState("");
3541
- const messagesEndRef = useRef(null);
3542
- useEffect(() => {
3543
- messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
3544
- }, [messages]);
3545
- const handleSubmit = async (state, overrideInput) => {
3546
- const text = (overrideInput ?? input).trim();
3547
- const hasFiles = state?.files && Array.isArray(state.files) && state.files.length > 0;
3548
- if (!text && !hasFiles || isLoading) return;
3549
- if (!overrideInput) setInput("");
3550
- await sendEvent({
3551
- type: "text",
3552
- role: "user",
3553
- data: { content: text || "" },
3554
- state: {
3555
- ...state,
3556
- threadId: activeThreadId ?? void 0
3557
- }
3558
- });
3559
- };
3560
- const showStarterPrompts = messages.length === 0 && starterPrompts && starterPrompts.length > 0 && !isLoadingEvents;
3561
- return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col h-full bg-background flex-1 overflow-hidden", children: [
3562
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 pb-36", children: [
3563
- /* @__PURE__ */ jsx(
3564
- "div",
3565
- {
3566
- className: cn(
3567
- "max-w-[48rem] mx-auto w-full p-4",
3568
- showStarterPrompts && "min-h-full flex flex-col"
3569
- ),
3570
- children: isLoadingEvents && messages.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-20", children: /* @__PURE__ */ jsx(LoadingIndicator, { status: { message: "Loading messages..." } }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3571
- showStarterPrompts && /* @__PURE__ */ jsx(StarterPrompts, { prompts: starterPrompts }),
3572
- /* @__PURE__ */ jsx(
3573
- MessageList,
3574
- {
3575
- messages,
3576
- isLoading,
3577
- error,
3578
- loadingStatus
3579
- }
3580
- )
3581
- ] })
3582
- }
3583
- ),
3584
- /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
3585
- ] }),
3586
- /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 p-4 w-full", children: /* @__PURE__ */ jsx("div", { className: "max-w-[48rem] mx-auto", children: /* @__PURE__ */ jsx(
3587
- Composer,
3588
- {
3589
- value: input,
3590
- onChange: setInput,
3591
- onSubmit: handleSubmit,
3592
- placeholder,
3593
- isLoading,
3594
- options,
3595
- autoFocus,
3596
- defaultSelectedIds: allDefaultSelectedIds,
3597
- fileAttachments
3598
- }
3599
- ) }) })
3600
- ] });
3601
- }
3602
- function ChatHeader({
3603
- leftContent,
3604
- rightContent,
3605
- className,
3606
- children
3607
- }) {
3608
- if (children) {
3609
- return /* @__PURE__ */ jsx(
3610
- "div",
3611
- {
3612
- className: cn(
3613
- "px-2 border-b border-border h-14 flex items-center shrink-0",
3614
- className
3615
- ),
3616
- children
3617
- }
3618
- );
3619
- }
3620
- return /* @__PURE__ */ jsxs(
3621
- "div",
3622
- {
3623
- className: cn(
3624
- "px-2 border-b border-border h-14 flex items-center justify-between shrink-0",
3625
- className
3626
- ),
3627
- children: [
3628
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: leftContent }),
3629
- rightContent && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 shrink-0 ml-2", children: rightContent })
3630
- ]
3631
- }
3632
- );
3633
- }
3634
- var ThreadList = ({
3635
- padding,
3636
- background,
3637
- gap,
3638
- radius = "md"
3639
- }) => {
3640
- const { threads, activeThreadId } = useThreads();
3641
- const sortedThreads = React3.useMemo(() => {
3642
- return [...threads].sort((a, b) => {
3643
- const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
3644
- const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
3645
- return dateB - dateA;
3646
- });
3647
- }, [threads]);
3648
- return /* @__PURE__ */ jsx(List, { padding, gap, flex: "1", overflow: "scroll", children: sortedThreads.map((thread) => {
3649
- const isActive = thread.id === activeThreadId;
3650
- return /* @__PURE__ */ jsx(
3651
- ListItem,
3652
- {
3653
- onClickAction: {
3654
- type: "client:navigate",
3655
- data: {
3656
- url: `?threadId=${thread.id}`
3657
- }
3658
- },
3659
- background: isActive ? "primary" : "transparent",
3660
- children: /* @__PURE__ */ jsxs(Box, { group: true, width: "full", children: [
3661
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0 pr-4", children: /* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: thread.title || `Thread ${thread.id.slice(0, 8)}` }) }),
3662
- /* @__PURE__ */ jsx(Float, { position: "right-center", showOnHover: true, children: /* @__PURE__ */ jsx(
3663
- Dropdown,
3664
- {
3665
- items: [
3666
- {
3667
- label: "Delete",
3668
- icon: "trash",
3669
- onClickAction: {
3670
- role: "system",
3671
- type: "delete-thread",
3672
- data: {
3673
- threadId: thread.id
3674
- }
3675
- }
3676
- }
3677
- ]
3678
- }
3679
- ) })
3680
- ] })
3681
- },
3682
- thread.id
3683
- );
3684
- }) });
3685
- };
3686
- function PopupChat({
3687
- title = "Chat",
3688
- placeholder = "Message the AI",
3689
- starterPrompts,
3690
- options,
3691
- defaultOpen = false,
3692
- headerProps,
3693
- defaultSelectedIds
3694
- }) {
3695
- const [isOpen, setIsOpen] = useState(defaultOpen);
3696
- const [view, setView] = useState("chat");
3697
- const { createThread } = useThreads();
3698
- const handleNewChat = async () => {
3699
- try {
3700
- await createThread();
3701
- setView("chat");
3702
- } catch (error) {
3703
- console.error("Failed to create new chat:", error);
3704
- }
3705
- };
3706
- return /* @__PURE__ */ jsxs("div", { className: "fixed bottom-6 right-6 z-50 flex flex-col items-end gap-4 font-sans", children: [
3707
- isOpen && /* @__PURE__ */ jsxs(Card, { className: "py-0 w-[440px] h-[640px] gap-0 flex flex-col overflow-hidden border bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60 shadow-2xl animate-in fade-in zoom-in-95 duration-200 origin-bottom-right", children: [
3708
- /* @__PURE__ */ jsx(
3709
- ChatHeader,
3710
- {
3711
- title: view === "history" ? "History" : title,
3712
- leftContent: view === "history" ? /* @__PURE__ */ jsx(
3713
- Button,
3714
- {
3715
- variant: "ghost",
3716
- size: "icon-xs",
3717
- onClick: () => setView("chat"),
3718
- className: "text-muted-foreground hover:text-foreground",
3719
- children: /* @__PURE__ */ jsx(IconArrowLeft, { className: "size-4" })
3720
- }
3721
- ) : void 0,
3722
- rightContent: /* @__PURE__ */ jsxs(Fragment, { children: [
3723
- view === "chat" && /* @__PURE__ */ jsx(
3724
- Button,
3725
- {
3726
- variant: "ghost",
3727
- size: "icon-xs",
3728
- onClick: () => setView("history"),
3729
- className: "text-muted-foreground hover:text-foreground",
3730
- title: "History",
3731
- children: /* @__PURE__ */ jsx(IconHistory, { className: "size-4" })
3732
- }
3733
- ),
3734
- /* @__PURE__ */ jsx(
3735
- Button,
3736
- {
3737
- variant: "ghost",
3738
- size: "icon-xs",
3739
- onClick: handleNewChat,
3740
- className: "text-muted-foreground hover:text-foreground",
3741
- title: "New Chat",
3742
- children: /* @__PURE__ */ jsx(IconPlus, { className: "size-4" })
3743
- }
3744
- ),
3745
- /* @__PURE__ */ jsx(
3746
- Button,
3747
- {
3748
- variant: "ghost",
3749
- size: "icon-xs",
3750
- onClick: () => setIsOpen(false),
3751
- className: "text-muted-foreground hover:text-foreground",
3752
- children: /* @__PURE__ */ jsx(IconX, { className: "size-4" })
3753
- }
3754
- )
3755
- ] }),
3756
- ...headerProps
3757
- }
3758
- ),
3759
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: view === "chat" ? /* @__PURE__ */ jsx(
3760
- Thread,
3761
- {
3762
- placeholder,
3763
- starterPrompts,
3764
- options,
3765
- defaultSelectedIds
3766
- }
3767
- ) : /* @__PURE__ */ jsx(
3768
- ThreadList,
3769
- {
3770
- padding: "md",
3771
- gap: "md",
3772
- background: "muted",
3773
- radius: "md"
3774
- }
3775
- ) })
3776
- ] }),
3777
- /* @__PURE__ */ jsx(
3778
- Button,
3779
- {
3780
- size: "icon-lg",
3781
- className: cn(
3782
- "h-14 w-14 rounded-full shadow-2xl transition-all hover:scale-105 active:scale-95",
3783
- isOpen ? "bg-muted text-muted-foreground hover:bg-muted/80" : "bg-primary text-primary-foreground"
3784
- ),
3785
- onClick: () => setIsOpen(!isOpen),
3786
- children: isOpen ? /* @__PURE__ */ jsx(IconX, { className: "size-6" }) : /* @__PURE__ */ jsx(IconMessage, { className: "size-6" })
3787
- }
3788
- )
3789
- ] });
3790
- }
3791
- function Sidebar({ side, children, width = "1/4" }) {
3792
- const { leftCollapsed, rightCollapsed } = useSidebar();
3793
- const collapsed = side === "left" ? leftCollapsed : rightCollapsed;
3794
- const widthClass = widthMap[width];
3795
- return /* @__PURE__ */ jsx(
3796
- "div",
3797
- {
3798
- className: cn(
3799
- "flex-shrink-0 border-border bg-background transition-all duration-300 ease-in-out overflow-hidden flex flex-col",
3800
- side === "left" ? "border-r" : "border-l",
3801
- collapsed ? "w-0 border-r-0 border-l-0 min-w-0" : "",
3802
- !collapsed && widthClass
3803
- ),
3804
- style: !collapsed && !widthClass ? { width } : void 0,
3805
- children: /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-h-0 flex flex-col", children })
3806
- }
3807
- );
3808
- }
3809
- function FullChat({
3810
- title = "Chat",
3811
- placeholder,
3812
- starterPrompts,
3813
- options,
3814
- className,
3815
- headerProps,
3816
- autoFocus = false,
3817
- defaultSelectedIds
3818
- }) {
3819
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full w-full bg-background", className), children: [
3820
- title && /* @__PURE__ */ jsx(ChatHeader, { title, ...headerProps }),
3821
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden flex relative", children: /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-w-0", children: /* @__PURE__ */ jsx(
3822
- Thread,
3823
- {
3824
- placeholder,
3825
- starterPrompts,
3826
- options,
3827
- autoFocus,
3828
- defaultSelectedIds
3829
- }
3830
- ) }) })
3831
- ] });
3832
- }
3833
- function Surface({ name, children }) {
3834
- const { events } = useSurface({ name });
3835
- return /* @__PURE__ */ jsx(Fragment, { children: children(events) });
3836
- }
3837
- function SidebarToggle({ side, className }) {
3838
- const {
3839
- leftCollapsed,
3840
- rightCollapsed,
3841
- setLeftCollapsed,
3842
- setRightCollapsed,
3843
- leftCollapsible,
3844
- rightCollapsible
3845
- } = useSidebar();
3846
- if (side === "left") {
3847
- if (!leftCollapsible) return null;
3848
- return /* @__PURE__ */ jsx(
3849
- Button,
3850
- {
3851
- variant: "ghost",
3852
- size: "icon",
3853
- onClick: () => setLeftCollapsed(!leftCollapsed),
3854
- "aria-label": leftCollapsed ? "Expand left sidebar" : "Collapse left sidebar",
3855
- className: cn("", className),
3856
- children: leftCollapsed ? /* @__PURE__ */ jsx(IconLayoutSidebarLeftExpand, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(IconLayoutSidebarLeftCollapse, { className: "h-4 w-4" })
3857
- }
3858
- );
3859
- }
3860
- if (side === "right") {
3861
- if (!rightCollapsible) return null;
3862
- return /* @__PURE__ */ jsx(
3863
- Button,
3864
- {
3865
- variant: "ghost",
3866
- size: "icon",
3867
- onClick: () => setRightCollapsed(!rightCollapsed),
3868
- "aria-label": rightCollapsed ? "Expand right sidebar" : "Collapse right sidebar",
3869
- className: cn("", className),
3870
- children: rightCollapsed ? /* @__PURE__ */ jsx(IconLayoutSidebarRightExpand, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(IconLayoutSidebarRightCollapse, { className: "h-4 w-4" })
3871
- }
3872
- );
3873
- }
3874
- return null;
3875
- }
3876
- var ThreadPopover = ({}) => {
3877
- const [isOpen, setIsOpen] = React3.useState(false);
3878
- useHotkeys(
3879
- "h",
3880
- (e) => {
3881
- e.preventDefault();
3882
- setIsOpen((prev) => !prev);
3883
- },
3884
- {
3885
- enableOnFormTags: false,
3886
- // Don't trigger when typing in form inputs
3887
- enableOnContentEditable: false
3888
- // Don't trigger in contenteditable elements
3889
- }
3890
- );
3891
- return /* @__PURE__ */ jsxs(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
3892
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx(IconHistory, { className: "size-4" }) }) }),
3893
- /* @__PURE__ */ jsx(
3894
- PopoverContent,
3895
- {
3896
- className: "w-80 p-0",
3897
- side: "bottom",
3898
- align: "start",
3899
- sideOffset: 8,
3900
- children: /* @__PURE__ */ jsx("div", { className: "flex flex-col h-[400px]", children: /* @__PURE__ */ jsx(ThreadList, {}) })
3901
- }
3902
- )
3903
- ] });
3904
- };
3905
- var CreateThreadButton = ({
3906
- className,
3907
- variant = "ghost",
3908
- size = "default",
3909
- onThreadCreated
3910
- }) => {
3911
- const { createThread } = useThreads();
3912
- const [isCreating, setIsCreating] = React3.useState(false);
3913
- const handleCreateThread = async () => {
3914
- if (isCreating) return;
3915
- try {
3916
- setIsCreating(true);
3917
- const threadId = await createThread();
3918
- if (threadId) {
3919
- onThreadCreated?.(threadId);
3920
- }
3921
- } catch (error) {
3922
- console.error("Failed to create thread:", error);
3923
- } finally {
3924
- setIsCreating(false);
3925
- }
3926
- };
3927
- useHotkeys(
3928
- "n",
3929
- (e) => {
3930
- e.preventDefault();
3931
- handleCreateThread();
3932
- },
3933
- {
3934
- enableOnFormTags: false,
3935
- // Don't trigger when typing in form inputs
3936
- enableOnContentEditable: false
3937
- // Don't trigger in contenteditable elements
3938
- }
3939
- );
3940
- return /* @__PURE__ */ jsxs(
3941
- Button,
3942
- {
3943
- variant,
3944
- size,
3945
- onClick: handleCreateThread,
3946
- disabled: isCreating,
3947
- className: cn(className),
3948
- children: [
3949
- /* @__PURE__ */ jsx(IconPlus, { className: "size-4" }),
3950
- "New chat"
3951
- ]
3952
- }
3953
- );
3954
- };
3955
- function ThemeToggle() {
3956
- const { theme, setTheme, resolvedTheme } = useTheme();
3957
- const cycleTheme = () => {
3958
- if (theme === "light") {
3959
- setTheme("dark");
3960
- } else if (theme === "dark") {
3961
- setTheme("system");
3962
- } else {
3963
- setTheme("light");
3964
- }
3965
- };
3966
- const getIcon = () => {
3967
- if (theme === "system") {
3968
- return /* @__PURE__ */ jsx(IconDeviceDesktop, { className: "h-4 w-4" });
3969
- }
3970
- return resolvedTheme === "dark" ? /* @__PURE__ */ jsx(IconMoon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(IconSun, { className: "h-4 w-4" });
3971
- };
3972
- const getLabel = () => {
3973
- if (theme === "system") {
3974
- return "System";
3975
- }
3976
- return resolvedTheme === "dark" ? "Dark" : "Light";
3977
- };
3978
- return /* @__PURE__ */ jsx(
3979
- Button,
3980
- {
3981
- variant: "ghost",
3982
- size: "icon",
3983
- onClick: cycleTheme,
3984
- "aria-label": `Toggle theme (current: ${getLabel()})`,
3985
- title: `Current: ${getLabel()}. Click to cycle: Light \u2192 Dark \u2192 System`,
3986
- children: getIcon()
3987
- }
3988
- );
3989
- }
3990
- var CreateThreadListItem = ({
3991
- padding = "sm",
3992
- background,
3993
- radius = "md"
3994
- }) => {
3995
- const { createThread } = useThreads();
3996
- const [isCreating, setIsCreating] = React3.useState(false);
3997
- const handleCreateThread = async () => {
3998
- if (isCreating) return;
3999
- try {
4000
- setIsCreating(true);
4001
- const threadId = await createThread();
4002
- } catch (error) {
4003
- console.error("Failed to create thread:", error);
4004
- } finally {
4005
- setIsCreating(false);
4006
- }
4007
- };
4008
- useHotkeys(
4009
- "n",
4010
- (e) => {
4011
- e.preventDefault();
4012
- handleCreateThread();
4013
- },
4014
- {
4015
- enableOnFormTags: false,
4016
- // Don't trigger when typing in form inputs
4017
- enableOnContentEditable: false
4018
- // Don't trigger in contenteditable elements
4019
- }
4020
- );
4021
- return /* @__PURE__ */ jsxs(
4022
- ListItem,
4023
- {
4024
- onClickAction: {
4025
- type: "client:navigate",
4026
- data: {
4027
- url: "?"
4028
- }
4029
- },
4030
- padding,
4031
- background,
4032
- radius,
4033
- children: [
4034
- /* @__PURE__ */ jsx(IconPlus, { className: "size-4" }),
4035
- "New chat"
4036
- ]
4037
- }
4038
- );
4039
- };
4040
45
 
4041
- export { AccountButton, AuthContext, AuthProvider, Badge2 as Badge, Box, Button2 as Button, Card2 as Card, Chart, ChatHeader, Checkbox, Col, ColorPicker, Composer, CreateThreadButton, CreateThreadListItem, Divider, Dropdown, Float, Form, FullChat, Heading, Hidden, Image, Input2 as Input, Label2 as Label, List, ListItem, MelonyContext, MelonyProvider, PopupChat, RadioGroup, Row, Select2 as Select, Sidebar, SidebarContext, SidebarProvider, SidebarToggle, Spacer, Surface, Text, Textarea2 as Textarea, ThemeProvider, ThemeToggle, Thread, ThreadContext, ThreadList, ThreadPopover, ThreadProvider, UIRenderer, Upload, WelcomeScreen, useAuth, useMelony, useScreenSize, useSidebar, useSurface, useTheme, useThreads };
46
+ export { MelonyContext, MelonyProvider, useMelony };
4042
47
  //# sourceMappingURL=index.js.map
4043
48
  //# sourceMappingURL=index.js.map