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