@melony/react 0.1.12 → 0.1.15

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,25 +1,27 @@
1
- import * as React9 from 'react';
2
- import { createContext, useState, useEffect, useCallback, useMemo, useContext, useRef } from 'react';
1
+ import * as React11 from 'react';
2
+ import React11__default, { createContext, useState, useEffect, useCallback, useMemo, useContext, useRef } from 'react';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { generateId } from 'melony/client';
3
5
  import { clsx } from 'clsx';
4
6
  import { twMerge } from 'tailwind-merge';
5
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
- import { generateId } from '@melony/core/client';
7
- import { Separator as Separator$1 } from '@base-ui/react/separator';
7
+ import { Button as Button$1 } from '@base-ui/react/button';
8
+ import { cva } from 'class-variance-authority';
8
9
  import * as ICONS from '@tabler/icons-react';
9
- import { IconArrowUp, IconPlus, IconMessage, IconTrash, IconArrowLeft, IconHistory, IconX, IconUser, IconLogout, IconBrandGoogle, IconSelector, IconCheck, IconChevronUp, IconChevronDown } from '@tabler/icons-react';
10
+ import { IconChevronDown, IconLoader2, IconArrowUp, IconPlus, IconMessage, IconTrash, IconHistory, IconX, IconArrowLeft, IconChevronLeft, IconChevronRight, IconUser, IconLogout, IconBrandGoogle, IconDeviceDesktop, IconMoon, IconSun, IconCheck, IconSelector, IconChevronUp } from '@tabler/icons-react';
11
+ import { Menu } from '@base-ui/react/menu';
12
+ import { Separator as Separator$1 } from '@base-ui/react/separator';
13
+ import { Dialog as Dialog$1 } from '@base-ui/react/dialog';
10
14
  import { mergeProps } from '@base-ui/react/merge-props';
11
15
  import { useRender } from '@base-ui/react/use-render';
12
- import { cva } from 'class-variance-authority';
13
16
  import { Input as Input$1 } from '@base-ui/react/input';
14
17
  import { Select as Select$1 } from '@base-ui/react/select';
15
- import { Button as Button$1 } from '@base-ui/react/button';
18
+ import { createPortal } from 'react-dom';
19
+ import { useHotkeys } from 'react-hotkeys-hook';
16
20
  import { AlertDialog as AlertDialog$1 } from '@base-ui/react/alert-dialog';
17
- import { Menu } from '@base-ui/react/menu';
18
21
 
19
22
  // src/providers/melony-provider.tsx
20
- function cn(...inputs) {
21
- return twMerge(clsx(inputs));
22
- }
23
+
24
+ // src/lib/group-events-to-messages.ts
23
25
  function groupEventsToMessages(events) {
24
26
  if (events.length === 0) return [];
25
27
  const messages = [];
@@ -282,6 +284,58 @@ var ThreadProvider = ({
282
284
  );
283
285
  return /* @__PURE__ */ jsx(ThreadContext.Provider, { value, children });
284
286
  };
287
+ var ThemeContext = createContext(void 0);
288
+ function ThemeProvider({ children }) {
289
+ const [theme, setThemeState] = useState("system");
290
+ const [resolvedTheme, setResolvedTheme] = useState("light");
291
+ useEffect(() => {
292
+ if (typeof window !== "undefined") {
293
+ const stored = localStorage.getItem("theme");
294
+ if (stored) {
295
+ setThemeState(stored);
296
+ }
297
+ }
298
+ }, []);
299
+ useEffect(() => {
300
+ if (typeof window !== "undefined") {
301
+ if (theme === "system") {
302
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
303
+ const updateResolvedTheme = () => {
304
+ setResolvedTheme(mediaQuery.matches ? "dark" : "light");
305
+ };
306
+ updateResolvedTheme();
307
+ mediaQuery.addEventListener("change", updateResolvedTheme);
308
+ return () => mediaQuery.removeEventListener("change", updateResolvedTheme);
309
+ } else {
310
+ setResolvedTheme(theme);
311
+ }
312
+ }
313
+ }, [theme]);
314
+ useEffect(() => {
315
+ if (typeof window !== "undefined") {
316
+ const root = document.documentElement;
317
+ if (resolvedTheme === "dark") {
318
+ root.classList.add("dark");
319
+ } else {
320
+ root.classList.remove("dark");
321
+ }
322
+ }
323
+ }, [resolvedTheme]);
324
+ const setTheme = (newTheme) => {
325
+ setThemeState(newTheme);
326
+ if (typeof window !== "undefined") {
327
+ localStorage.setItem("theme", newTheme);
328
+ }
329
+ };
330
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: { theme, setTheme, resolvedTheme }, children });
331
+ }
332
+ function useTheme() {
333
+ const context = useContext(ThemeContext);
334
+ if (context === void 0) {
335
+ throw new Error("useTheme must be used within a ThemeProvider");
336
+ }
337
+ return context;
338
+ }
285
339
  var useMelony = (options) => {
286
340
  const context = useContext(MelonyContext);
287
341
  if (context === void 0) {
@@ -310,6 +364,313 @@ var useThreads = () => {
310
364
  }
311
365
  return context;
312
366
  };
367
+ function cn(...inputs) {
368
+ return twMerge(clsx(inputs));
369
+ }
370
+ var buttonVariants = cva(
371
+ "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",
372
+ {
373
+ variants: {
374
+ variant: {
375
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
376
+ outline: "border-border bg-input/30 hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
377
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
378
+ ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
379
+ 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",
380
+ link: "text-primary underline-offset-4 hover:underline"
381
+ },
382
+ size: {
383
+ default: "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
384
+ 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",
385
+ sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
386
+ lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
387
+ icon: "size-9",
388
+ "icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3",
389
+ "icon-sm": "size-8",
390
+ "icon-lg": "size-10"
391
+ }
392
+ },
393
+ defaultVariants: {
394
+ variant: "default",
395
+ size: "default"
396
+ }
397
+ }
398
+ );
399
+ function Button({
400
+ className,
401
+ variant = "default",
402
+ size = "default",
403
+ ...props
404
+ }) {
405
+ return /* @__PURE__ */ jsx(
406
+ Button$1,
407
+ {
408
+ "data-slot": "button",
409
+ className: cn(buttonVariants({ variant, size, className })),
410
+ ...props
411
+ }
412
+ );
413
+ }
414
+ function Textarea({ className, ...props }) {
415
+ return /* @__PURE__ */ jsx(
416
+ "textarea",
417
+ {
418
+ "data-slot": "textarea",
419
+ className: cn(
420
+ "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",
421
+ className
422
+ ),
423
+ ...props
424
+ }
425
+ );
426
+ }
427
+ function DropdownMenu({ ...props }) {
428
+ return /* @__PURE__ */ jsx(Menu.Root, { "data-slot": "dropdown-menu", ...props });
429
+ }
430
+ function DropdownMenuTrigger({ ...props }) {
431
+ return /* @__PURE__ */ jsx(Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
432
+ }
433
+ function DropdownMenuContent({
434
+ align = "start",
435
+ alignOffset = 0,
436
+ side = "bottom",
437
+ sideOffset = 4,
438
+ className,
439
+ ...props
440
+ }) {
441
+ return /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(
442
+ Menu.Positioner,
443
+ {
444
+ className: "isolate z-50 outline-none",
445
+ align,
446
+ alignOffset,
447
+ side,
448
+ sideOffset,
449
+ children: /* @__PURE__ */ jsx(
450
+ Menu.Popup,
451
+ {
452
+ "data-slot": "dropdown-menu-content",
453
+ 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),
454
+ ...props
455
+ }
456
+ )
457
+ }
458
+ ) });
459
+ }
460
+ function DropdownMenuGroup({ ...props }) {
461
+ return /* @__PURE__ */ jsx(Menu.Group, { "data-slot": "dropdown-menu-group", ...props });
462
+ }
463
+ function DropdownMenuLabel({
464
+ className,
465
+ inset,
466
+ ...props
467
+ }) {
468
+ return /* @__PURE__ */ jsx(
469
+ Menu.GroupLabel,
470
+ {
471
+ "data-slot": "dropdown-menu-label",
472
+ "data-inset": inset,
473
+ className: cn("text-muted-foreground px-3 py-2.5 text-xs data-[inset]:pl-8", className),
474
+ ...props
475
+ }
476
+ );
477
+ }
478
+ function DropdownMenuItem({
479
+ className,
480
+ inset,
481
+ variant = "default",
482
+ ...props
483
+ }) {
484
+ return /* @__PURE__ */ jsx(
485
+ Menu.Item,
486
+ {
487
+ "data-slot": "dropdown-menu-item",
488
+ "data-inset": inset,
489
+ "data-variant": variant,
490
+ className: cn(
491
+ "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",
492
+ className
493
+ ),
494
+ ...props
495
+ }
496
+ );
497
+ }
498
+ function DropdownMenuCheckboxItem({
499
+ className,
500
+ children,
501
+ checked,
502
+ ...props
503
+ }) {
504
+ return /* @__PURE__ */ jsxs(
505
+ Menu.CheckboxItem,
506
+ {
507
+ "data-slot": "dropdown-menu-checkbox-item",
508
+ className: cn(
509
+ "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",
510
+ className
511
+ ),
512
+ checked,
513
+ ...props,
514
+ children: [
515
+ /* @__PURE__ */ jsx(
516
+ "span",
517
+ {
518
+ className: "pointer-events-none absolute right-2 flex items-center justify-center pointer-events-none",
519
+ "data-slot": "dropdown-menu-checkbox-item-indicator",
520
+ children: /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx(
521
+ IconCheck,
522
+ {}
523
+ ) })
524
+ }
525
+ ),
526
+ children
527
+ ]
528
+ }
529
+ );
530
+ }
531
+ function DropdownMenuSeparator({
532
+ className,
533
+ ...props
534
+ }) {
535
+ return /* @__PURE__ */ jsx(
536
+ Menu.Separator,
537
+ {
538
+ "data-slot": "dropdown-menu-separator",
539
+ className: cn("bg-border/50 -mx-1 my-1 h-px", className),
540
+ ...props
541
+ }
542
+ );
543
+ }
544
+ function Composer({
545
+ value,
546
+ onChange,
547
+ onSubmit,
548
+ placeholder = "Type a message...",
549
+ isLoading,
550
+ className,
551
+ options = [],
552
+ autoFocus = false,
553
+ defaultSelectedIds = []
554
+ }) {
555
+ const [selectedOptions, setSelectedOptions] = React11__default.useState(
556
+ () => new Set(defaultSelectedIds)
557
+ );
558
+ const toggleOption = (id, groupOptions, type = "multiple") => {
559
+ const next = new Set(selectedOptions);
560
+ if (type === "single") {
561
+ const isAlreadySelected = next.has(id);
562
+ if (groupOptions) {
563
+ groupOptions.forEach((o) => next.delete(o.id));
564
+ }
565
+ if (!isAlreadySelected) {
566
+ next.add(id);
567
+ }
568
+ } else {
569
+ if (next.has(id)) {
570
+ next.delete(id);
571
+ } else {
572
+ next.add(id);
573
+ }
574
+ }
575
+ setSelectedOptions(next);
576
+ };
577
+ const handleInternalSubmit = () => {
578
+ const state = {};
579
+ options.forEach((group) => {
580
+ const selectedInGroup = group.options.filter(
581
+ (o) => selectedOptions.has(o.id)
582
+ );
583
+ if (selectedInGroup.length > 0) {
584
+ if (group.type === "single") {
585
+ state[group.id] = selectedInGroup[0].value;
586
+ } else {
587
+ state[group.id] = selectedInGroup.map((o) => ({
588
+ id: o.id,
589
+ value: o.value
590
+ }));
591
+ }
592
+ }
593
+ });
594
+ onSubmit(state);
595
+ };
596
+ const handleKeyDown = (e) => {
597
+ if (e.key === "Enter" && !e.shiftKey) {
598
+ e.preventDefault();
599
+ handleInternalSubmit();
600
+ }
601
+ };
602
+ 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: [
603
+ /* @__PURE__ */ jsx(
604
+ Textarea,
605
+ {
606
+ value,
607
+ onChange: (e) => onChange(e.target.value),
608
+ onKeyDown: handleKeyDown,
609
+ placeholder,
610
+ 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",
611
+ autoFocus
612
+ }
613
+ ),
614
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center px-1", children: [
615
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: options.map((group) => {
616
+ const selectedInGroup = group.options.filter(
617
+ (o) => selectedOptions.has(o.id)
618
+ );
619
+ const label = selectedInGroup.length === 0 ? group.label : selectedInGroup.length === 1 ? selectedInGroup[0].label : `${group.label} (${selectedInGroup.length})`;
620
+ const isSingle = group.type === "single";
621
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
622
+ /* @__PURE__ */ jsx(
623
+ DropdownMenuTrigger,
624
+ {
625
+ render: /* @__PURE__ */ jsxs(
626
+ Button,
627
+ {
628
+ variant: "ghost",
629
+ size: "sm",
630
+ className: cn(
631
+ selectedInGroup.length > 0 ? "text-foreground bg-muted/50" : "text-muted-foreground"
632
+ ),
633
+ children: [
634
+ label,
635
+ /* @__PURE__ */ jsx(IconChevronDown, { className: "h-3 w-3 opacity-50" })
636
+ ]
637
+ }
638
+ )
639
+ }
640
+ ),
641
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: "w-56", children: /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
642
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: group.label }),
643
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
644
+ group.options.map((option) => /* @__PURE__ */ jsx(
645
+ DropdownMenuCheckboxItem,
646
+ {
647
+ checked: selectedOptions.has(option.id),
648
+ onCheckedChange: () => toggleOption(
649
+ option.id,
650
+ group.options,
651
+ isSingle ? "single" : "multiple"
652
+ ),
653
+ onSelect: (e) => e.preventDefault(),
654
+ children: option.label
655
+ },
656
+ option.id
657
+ ))
658
+ ] }) })
659
+ ] }, group.id);
660
+ }) }),
661
+ /* @__PURE__ */ jsx(
662
+ Button,
663
+ {
664
+ type: "submit",
665
+ disabled: !value.trim() && !isLoading || isLoading,
666
+ size: "icon-lg",
667
+ onClick: handleInternalSubmit,
668
+ children: isLoading ? /* @__PURE__ */ jsx(IconLoader2, { className: "h-5 w-5 animate-spin" }) : /* @__PURE__ */ jsx(IconArrowUp, { className: "h-5 w-5" })
669
+ }
670
+ )
671
+ ] })
672
+ ] }) });
673
+ }
313
674
  function Card({
314
675
  className,
315
676
  size = "default",
@@ -378,7 +739,7 @@ var Card2 = ({
378
739
  return /* @__PURE__ */ jsxs(
379
740
  Card,
380
741
  {
381
- className: cn("w-full max-w-2xl shadow-sm", className),
742
+ className: cn("min-w-96", className),
382
743
  style,
383
744
  children: [
384
745
  (title || subtitle) && /* @__PURE__ */ jsxs(CardHeader, { className: "pb-3", children: [
@@ -693,26 +1054,132 @@ var ListItem = ({
693
1054
  }
694
1055
  );
695
1056
  };
696
- var Image = ({
697
- src,
698
- alt,
699
- size = "sm",
1057
+ function Dialog({ ...props }) {
1058
+ return /* @__PURE__ */ jsx(Dialog$1.Root, { "data-slot": "dialog", ...props });
1059
+ }
1060
+ function DialogTrigger({ ...props }) {
1061
+ return /* @__PURE__ */ jsx(Dialog$1.Trigger, { "data-slot": "dialog-trigger", ...props });
1062
+ }
1063
+ function DialogPortal({ ...props }) {
1064
+ return /* @__PURE__ */ jsx(Dialog$1.Portal, { "data-slot": "dialog-portal", ...props });
1065
+ }
1066
+ function DialogOverlay({
700
1067
  className,
701
- style
702
- }) => {
703
- const [hasError, setHasError] = useState(false);
704
- const [isLoading, setIsLoading] = useState(true);
705
- const sizes = {
706
- sm: "h-11 w-11",
707
- md: "h-22 w-22",
708
- lg: "h-44 w-44"
709
- };
710
- const handleError = () => {
711
- setHasError(true);
712
- setIsLoading(false);
713
- };
714
- const handleLoad = () => {
715
- setIsLoading(false);
1068
+ ...props
1069
+ }) {
1070
+ return /* @__PURE__ */ jsx(
1071
+ Dialog$1.Backdrop,
1072
+ {
1073
+ "data-slot": "dialog-overlay",
1074
+ className: cn(
1075
+ "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",
1076
+ className
1077
+ ),
1078
+ ...props
1079
+ }
1080
+ );
1081
+ }
1082
+ function DialogContent({
1083
+ className,
1084
+ ...props
1085
+ }) {
1086
+ return /* @__PURE__ */ jsxs(DialogPortal, { children: [
1087
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
1088
+ /* @__PURE__ */ jsx(
1089
+ Dialog$1.Popup,
1090
+ {
1091
+ "data-slot": "dialog-content",
1092
+ className: cn(
1093
+ "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",
1094
+ className
1095
+ ),
1096
+ ...props
1097
+ }
1098
+ )
1099
+ ] });
1100
+ }
1101
+ function DialogClose({
1102
+ className,
1103
+ ...props
1104
+ }) {
1105
+ return /* @__PURE__ */ jsx(
1106
+ Dialog$1.Close,
1107
+ {
1108
+ "data-slot": "dialog-close",
1109
+ className: cn(
1110
+ "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",
1111
+ className
1112
+ ),
1113
+ ...props
1114
+ }
1115
+ );
1116
+ }
1117
+ function DialogHeader({
1118
+ className,
1119
+ ...props
1120
+ }) {
1121
+ return /* @__PURE__ */ jsx(
1122
+ "div",
1123
+ {
1124
+ "data-slot": "dialog-header",
1125
+ className: cn(
1126
+ "flex flex-col space-y-1.5 text-center sm:text-left",
1127
+ className
1128
+ ),
1129
+ ...props
1130
+ }
1131
+ );
1132
+ }
1133
+ function DialogTitle({
1134
+ className,
1135
+ ...props
1136
+ }) {
1137
+ return /* @__PURE__ */ jsx(
1138
+ Dialog$1.Title,
1139
+ {
1140
+ "data-slot": "dialog-title",
1141
+ className: cn(
1142
+ "text-lg font-semibold leading-none tracking-tight",
1143
+ className
1144
+ ),
1145
+ ...props
1146
+ }
1147
+ );
1148
+ }
1149
+ function DialogDescription({
1150
+ className,
1151
+ ...props
1152
+ }) {
1153
+ return /* @__PURE__ */ jsx(
1154
+ Dialog$1.Description,
1155
+ {
1156
+ "data-slot": "dialog-description",
1157
+ className: cn("text-sm text-muted-foreground", className),
1158
+ ...props
1159
+ }
1160
+ );
1161
+ }
1162
+ var Image = ({
1163
+ src,
1164
+ alt,
1165
+ size = "sm",
1166
+ className,
1167
+ style
1168
+ }) => {
1169
+ const [hasError, setHasError] = useState(false);
1170
+ const [isLoading, setIsLoading] = useState(true);
1171
+ const [open, setOpen] = useState(false);
1172
+ const sizes = {
1173
+ sm: "h-11",
1174
+ md: "h-22",
1175
+ lg: "h-44"
1176
+ };
1177
+ const handleError = () => {
1178
+ setHasError(true);
1179
+ setIsLoading(false);
1180
+ };
1181
+ const handleLoad = () => {
1182
+ setIsLoading(false);
716
1183
  };
717
1184
  if (hasError) {
718
1185
  return /* @__PURE__ */ jsx(
@@ -728,22 +1195,67 @@ var Image = ({
728
1195
  }
729
1196
  );
730
1197
  }
731
- return /* @__PURE__ */ jsxs("div", { className: cn("relative overflow-hidden rounded-md border", className), style, children: [
1198
+ return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: setOpen, children: [
1199
+ /* @__PURE__ */ jsx(DialogTrigger, { children: /* @__PURE__ */ jsxs(
1200
+ "div",
1201
+ {
1202
+ className: cn("relative overflow-hidden rounded-md border cursor-pointer", className),
1203
+ style,
1204
+ children: [
1205
+ /* @__PURE__ */ jsx(
1206
+ "img",
1207
+ {
1208
+ src,
1209
+ alt,
1210
+ onError: handleError,
1211
+ onLoad: handleLoad,
1212
+ className: cn(
1213
+ "block h-auto w-full transition-opacity duration-200 hover:opacity-90",
1214
+ isLoading ? "opacity-0" : "opacity-100",
1215
+ sizes[size]
1216
+ )
1217
+ }
1218
+ ),
1219
+ isLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-muted animate-pulse" })
1220
+ ]
1221
+ }
1222
+ ) }),
732
1223
  /* @__PURE__ */ jsx(
733
- "img",
1224
+ DialogContent,
734
1225
  {
735
- src,
736
- alt,
737
- onError: handleError,
738
- onLoad: handleLoad,
739
- className: cn(
740
- "block h-auto w-full transition-opacity duration-200",
741
- isLoading ? "opacity-0" : "opacity-100",
742
- sizes[size]
743
- )
1226
+ className: "max-w-[90vw] max-h-[90vh] p-0 bg-transparent border-none shadow-none",
1227
+ onClick: (e) => e.stopPropagation(),
1228
+ children: /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
1229
+ /* @__PURE__ */ jsx(DialogClose, { className: "absolute -top-10 right-0 text-white hover:text-gray-300 transition-colors z-10 bg-black/50 rounded-full p-2", children: /* @__PURE__ */ jsx(
1230
+ "svg",
1231
+ {
1232
+ xmlns: "http://www.w3.org/2000/svg",
1233
+ className: "h-5 w-5",
1234
+ fill: "none",
1235
+ viewBox: "0 0 24 24",
1236
+ stroke: "currentColor",
1237
+ children: /* @__PURE__ */ jsx(
1238
+ "path",
1239
+ {
1240
+ strokeLinecap: "round",
1241
+ strokeLinejoin: "round",
1242
+ strokeWidth: 2,
1243
+ d: "M6 18L18 6M6 6l12 12"
1244
+ }
1245
+ )
1246
+ }
1247
+ ) }),
1248
+ /* @__PURE__ */ jsx(
1249
+ "img",
1250
+ {
1251
+ src,
1252
+ alt: alt || "Enlarged image",
1253
+ className: "max-w-full max-h-[90vh] object-contain rounded-lg"
1254
+ }
1255
+ )
1256
+ ] })
744
1257
  }
745
- ),
746
- isLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-muted animate-pulse" })
1258
+ )
747
1259
  ] });
748
1260
  };
749
1261
  var Icon = ({
@@ -1199,19 +1711,6 @@ var Input2 = ({
1199
1711
  )
1200
1712
  ] });
1201
1713
  };
1202
- function Textarea({ className, ...props }) {
1203
- return /* @__PURE__ */ jsx(
1204
- "textarea",
1205
- {
1206
- "data-slot": "textarea",
1207
- className: cn(
1208
- "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",
1209
- className
1210
- ),
1211
- ...props
1212
- }
1213
- );
1214
- }
1215
1714
  var Textarea2 = ({
1216
1715
  placeholder,
1217
1716
  defaultValue,
@@ -1583,50 +2082,6 @@ var RadioGroup = ({
1583
2082
  )
1584
2083
  ] });
1585
2084
  };
1586
- var buttonVariants = cva(
1587
- "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",
1588
- {
1589
- variants: {
1590
- variant: {
1591
- default: "bg-primary text-primary-foreground hover:bg-primary/80",
1592
- outline: "border-border bg-input/30 hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
1593
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
1594
- ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
1595
- 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",
1596
- link: "text-primary underline-offset-4 hover:underline"
1597
- },
1598
- size: {
1599
- default: "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
1600
- 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",
1601
- sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
1602
- lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
1603
- icon: "size-9",
1604
- "icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3",
1605
- "icon-sm": "size-8",
1606
- "icon-lg": "size-10"
1607
- }
1608
- },
1609
- defaultVariants: {
1610
- variant: "default",
1611
- size: "default"
1612
- }
1613
- }
1614
- );
1615
- function Button({
1616
- className,
1617
- variant = "default",
1618
- size = "default",
1619
- ...props
1620
- }) {
1621
- return /* @__PURE__ */ jsx(
1622
- Button$1,
1623
- {
1624
- "data-slot": "button",
1625
- className: cn(buttonVariants({ variant, size, className })),
1626
- ...props
1627
- }
1628
- );
1629
- }
1630
2085
  var Button2 = ({
1631
2086
  label,
1632
2087
  variant = "primary",
@@ -1643,6 +2098,8 @@ var Button2 = ({
1643
2098
  secondary: "secondary",
1644
2099
  danger: "destructive",
1645
2100
  outline: "outline",
2101
+ ghost: "ghost",
2102
+ link: "link",
1646
2103
  success: "default"
1647
2104
  // Success doesn't have a direct shadcn mapping in base variant, default is usually primary
1648
2105
  };
@@ -1711,6 +2168,32 @@ var Form = ({ children, onSubmitAction, className, style }) => {
1711
2168
  }
1712
2169
  );
1713
2170
  };
2171
+ function StarterPrompts({
2172
+ prompts,
2173
+ onPromptClick
2174
+ }) {
2175
+ if (!prompts || prompts.length === 0) {
2176
+ return null;
2177
+ }
2178
+ 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: [
2179
+ /* @__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?" }) }),
2180
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 w-full", children: prompts.map((item, index) => /* @__PURE__ */ jsx(
2181
+ Button2,
2182
+ {
2183
+ label: item.label,
2184
+ variant: "ghost",
2185
+ size: "lg",
2186
+ onClickAction: {
2187
+ type: "text",
2188
+ role: "user",
2189
+ data: { content: item.prompt }
2190
+ },
2191
+ className: "w-full justify-start"
2192
+ },
2193
+ index
2194
+ )) })
2195
+ ] });
2196
+ }
1714
2197
  function UIRenderer({ node }) {
1715
2198
  const { type, props, children } = node;
1716
2199
  const typeMap = {
@@ -1749,69 +2232,108 @@ function UIRenderer({ node }) {
1749
2232
  const componentProps = { ...props };
1750
2233
  return /* @__PURE__ */ jsx(Component, { ...componentProps, children: renderedChildren });
1751
2234
  }
1752
- function Composer({
1753
- value,
1754
- onChange,
1755
- onSubmit,
1756
- placeholder = "Type a message...",
1757
- isLoading,
1758
- className
1759
- }) {
1760
- const handleKeyDown = (e) => {
1761
- if (e.key === "Enter" && !e.shiftKey) {
1762
- e.preventDefault();
1763
- onSubmit();
2235
+ function MessageContent({ events }) {
2236
+ return /* @__PURE__ */ jsx(Fragment, { children: events.map((event, index) => {
2237
+ if (event.type === "text-delta") {
2238
+ return /* @__PURE__ */ jsx("span", { children: event.data?.delta }, index);
1764
2239
  }
1765
- };
1766
- 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: [
1767
- /* @__PURE__ */ jsx(
1768
- Textarea,
1769
- {
1770
- value,
1771
- onChange: (e) => onChange(e.target.value),
1772
- onKeyDown: handleKeyDown,
1773
- placeholder,
1774
- 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"
1775
- }
1776
- ),
1777
- /* @__PURE__ */ jsx("div", { className: "flex justify-end items-center px-2 pb-0.5", children: /* @__PURE__ */ jsx(
1778
- Button,
1779
- {
1780
- type: "submit",
1781
- disabled: !value.trim() && !isLoading || isLoading,
1782
- size: "icon",
1783
- onClick: () => onSubmit(),
1784
- className: cn(
1785
- "h-8 w-8 rounded-full transition-all shrink-0",
1786
- value.trim() ? "bg-foreground text-background hover:bg-foreground/90" : "bg-muted-foreground/20 text-muted-foreground/40"
1787
- ),
1788
- children: /* @__PURE__ */ jsx(IconArrowUp, { className: "h-5 w-5" })
1789
- }
1790
- ) })
1791
- ] }) });
2240
+ if (event.type === "text") {
2241
+ return /* @__PURE__ */ jsx("p", { children: event.data?.content || event.data?.text }, index);
2242
+ }
2243
+ if (event.ui) {
2244
+ return /* @__PURE__ */ jsx(UIRenderer, { node: event.ui }, index);
2245
+ }
2246
+ return null;
2247
+ }) });
2248
+ }
2249
+ function MessageBubble({ message }) {
2250
+ const isUser = message.role === "user";
2251
+ return /* @__PURE__ */ jsx(
2252
+ "div",
2253
+ {
2254
+ className: cn(
2255
+ "flex flex-col",
2256
+ isUser ? "items-end" : "items-start"
2257
+ ),
2258
+ children: /* @__PURE__ */ jsx(
2259
+ "div",
2260
+ {
2261
+ className: cn(
2262
+ "flex flex-col items-start max-w-[85%] rounded-2xl px-4 py-2 space-y-4 whitespace-pre-wrap",
2263
+ isUser ? "bg-primary text-primary-foreground" : "px-0 py-0 text-foreground"
2264
+ ),
2265
+ children: /* @__PURE__ */ jsx(MessageContent, { events: message.content })
2266
+ }
2267
+ )
2268
+ }
2269
+ );
2270
+ }
2271
+ function LoadingIndicator({ status }) {
2272
+ const [isExpanded, setIsExpanded] = useState(false);
2273
+ const message = status?.message || "Processing...";
2274
+ const details = status?.details;
2275
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
2276
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground group", children: [
2277
+ /* @__PURE__ */ jsx(IconLoader2, { className: "size-3.5 animate-spin" }),
2278
+ /* @__PURE__ */ jsx("div", { className: "animate-pulse", children: message }),
2279
+ details && /* @__PURE__ */ jsx(
2280
+ "button",
2281
+ {
2282
+ onClick: () => setIsExpanded(!isExpanded),
2283
+ className: "p-0.5 hover:bg-muted rounded-sm transition-colors flex items-center justify-center",
2284
+ title: isExpanded ? "Hide details" : "Show details",
2285
+ 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" })
2286
+ }
2287
+ )
2288
+ ] }),
2289
+ 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 })
2290
+ ] });
2291
+ }
2292
+ function ErrorDisplay({ error }) {
2293
+ return /* @__PURE__ */ jsx("div", { className: "text-destructive p-2 border border-destructive rounded-md bg-destructive/10", children: error.message });
2294
+ }
2295
+ function MessageList({ messages, isLoading, error, loadingStatus }) {
2296
+ if (messages.length === 0) {
2297
+ return null;
2298
+ }
2299
+ const isTextStreaming = useMemo(() => {
2300
+ if (messages.length === 0 || !isLoading) return false;
2301
+ const lastMessage = messages[messages.length - 1];
2302
+ return lastMessage.content.some((event) => event.type === "text-delta");
2303
+ }, [messages, isLoading]);
2304
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2305
+ messages.map((message, index) => /* @__PURE__ */ jsx(MessageBubble, { message }, index)),
2306
+ isLoading && !isTextStreaming && /* @__PURE__ */ jsx(LoadingIndicator, { status: loadingStatus }),
2307
+ error && /* @__PURE__ */ jsx(ErrorDisplay, { error })
2308
+ ] });
1792
2309
  }
1793
2310
  function Thread({
1794
2311
  className,
1795
2312
  placeholder = "Type a message...",
1796
2313
  starterPrompts,
1797
- onStarterPromptClick
2314
+ onStarterPromptClick,
2315
+ options,
2316
+ autoFocus = false,
2317
+ defaultSelectedIds
1798
2318
  }) {
1799
- const { messages, isLoading, error, sendEvent } = useMelony();
2319
+ const { messages, isLoading, error, sendEvent, loadingStatus } = useMelony();
1800
2320
  const [input, setInput] = useState("");
1801
2321
  const messagesEndRef = useRef(null);
1802
2322
  useEffect(() => {
1803
2323
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
1804
2324
  }, [messages]);
1805
- const handleSubmit = async (e, overrideInput) => {
1806
- e?.preventDefault();
2325
+ const handleSubmit = async (state, overrideInput) => {
1807
2326
  const text = (overrideInput ?? input).trim();
1808
2327
  if (!text || isLoading) return;
1809
2328
  if (!overrideInput) setInput("");
1810
- await sendEvent({
1811
- type: "text",
1812
- role: "user",
1813
- data: { content: text }
1814
- });
2329
+ await sendEvent(
2330
+ {
2331
+ type: "text",
2332
+ role: "user",
2333
+ data: { content: text }
2334
+ },
2335
+ { state }
2336
+ );
1815
2337
  };
1816
2338
  const handleStarterPromptClick = (prompt) => {
1817
2339
  if (onStarterPromptClick) {
@@ -1820,66 +2342,73 @@ function Thread({
1820
2342
  handleSubmit(void 0, prompt);
1821
2343
  }
1822
2344
  };
1823
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full bg-background", className), children: [
1824
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 space-y-6", children: [
1825
- /* @__PURE__ */ jsxs("div", { className: "max-w-4xl mx-auto w-full", children: [
1826
- messages.length === 0 && starterPrompts && starterPrompts.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center min-h-[300px] space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500", children: [
1827
- /* @__PURE__ */ jsx("div", { className: "text-center space-y-2", children: /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold tracking-tight", children: "What can I help with today?" }) }),
1828
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 w-full max-w-2xl px-4", children: starterPrompts.map((item, i) => /* @__PURE__ */ jsxs(
1829
- "button",
2345
+ const showStarterPrompts = messages.length === 0 && starterPrompts && starterPrompts.length > 0;
2346
+ return /* @__PURE__ */ jsxs(
2347
+ "div",
2348
+ {
2349
+ className: cn("relative flex flex-col h-full bg-background", className),
2350
+ children: [
2351
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 pb-36", children: [
2352
+ /* @__PURE__ */ jsxs(
2353
+ "div",
1830
2354
  {
1831
- onClick: () => handleStarterPromptClick(item.prompt),
1832
- className: "flex items-center gap-3 p-4 rounded-xl border bg-card hover:bg-muted/50 transition-all text-left group",
2355
+ className: cn(
2356
+ "max-w-4xl mx-auto w-full p-4",
2357
+ showStarterPrompts && "min-h-full flex flex-col"
2358
+ ),
1833
2359
  children: [
1834
- item.icon && /* @__PURE__ */ jsx("div", { className: "p-2 rounded-lg bg-muted group-hover:bg-background transition-colors", children: item.icon }),
1835
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: item.label })
2360
+ showStarterPrompts && /* @__PURE__ */ jsx(
2361
+ StarterPrompts,
2362
+ {
2363
+ prompts: starterPrompts,
2364
+ onPromptClick: handleStarterPromptClick
2365
+ }
2366
+ ),
2367
+ /* @__PURE__ */ jsx(
2368
+ MessageList,
2369
+ {
2370
+ messages,
2371
+ isLoading,
2372
+ error,
2373
+ loadingStatus
2374
+ }
2375
+ )
1836
2376
  ]
1837
- },
1838
- i
1839
- )) })
2377
+ }
2378
+ ),
2379
+ /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
1840
2380
  ] }),
1841
- messages.map((message, i) => /* @__PURE__ */ jsx(
1842
- "div",
2381
+ /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 p-4 w-full", children: /* @__PURE__ */ jsx("div", { className: "max-w-4xl mx-auto", children: /* @__PURE__ */ jsx(
2382
+ Composer,
1843
2383
  {
1844
- className: cn(
1845
- "flex flex-col",
1846
- message.role === "user" ? "items-end" : "items-start"
1847
- ),
1848
- children: /* @__PURE__ */ jsx(
1849
- "div",
1850
- {
1851
- className: cn(
1852
- "max-w-[85%] rounded-2xl px-4 py-2 space-y-2",
1853
- message.role === "user" ? "bg-primary text-primary-foreground" : "px-0 py-0 text-foreground"
1854
- ),
1855
- children: message.content.map((event, j) => {
1856
- if (event.type === "text-delta")
1857
- return /* @__PURE__ */ jsx("span", { children: event.data?.delta }, j);
1858
- if (event.type === "text")
1859
- return /* @__PURE__ */ jsx("p", { children: event.data?.content || event.data?.text }, j);
1860
- if (event.ui) return /* @__PURE__ */ jsx(UIRenderer, { node: event.ui }, j);
1861
- return null;
1862
- })
1863
- }
1864
- )
1865
- },
1866
- i
1867
- )),
1868
- isLoading && /* @__PURE__ */ jsx("div", { className: "text-muted-foreground animate-pulse", children: "Thinking..." }),
1869
- error && /* @__PURE__ */ jsx("div", { className: "text-destructive p-2 border border-destructive rounded-md bg-destructive/10", children: error.message })
1870
- ] }),
1871
- /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
1872
- ] }),
1873
- /* @__PURE__ */ jsx("div", { className: "p-4 border-t w-full", children: /* @__PURE__ */ jsx("div", { className: "max-w-4xl mx-auto", children: /* @__PURE__ */ jsx(
1874
- Composer,
1875
- {
1876
- value: input,
1877
- onChange: setInput,
1878
- onSubmit: handleSubmit,
1879
- placeholder,
1880
- isLoading
1881
- }
1882
- ) }) })
2384
+ value: input,
2385
+ onChange: setInput,
2386
+ onSubmit: handleSubmit,
2387
+ placeholder,
2388
+ isLoading,
2389
+ options,
2390
+ autoFocus,
2391
+ defaultSelectedIds
2392
+ }
2393
+ ) }) })
2394
+ ]
2395
+ }
2396
+ );
2397
+ }
2398
+ function ChatHeader({
2399
+ title,
2400
+ leftContent,
2401
+ rightContent,
2402
+ className,
2403
+ titleClassName,
2404
+ children
2405
+ }) {
2406
+ if (children) {
2407
+ return /* @__PURE__ */ jsx("div", { className: cn("p-4 border-b border-border h-14 flex items-center shrink-0", className), children });
2408
+ }
2409
+ return /* @__PURE__ */ jsxs("div", { className: cn("p-4 border-b border-border h-14 flex items-center justify-between shrink-0", className), children: [
2410
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: leftContent }),
2411
+ rightContent && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 shrink-0 ml-2", children: rightContent })
1883
2412
  ] });
1884
2413
  }
1885
2414
  var ThreadList = ({
@@ -1887,13 +2416,7 @@ var ThreadList = ({
1887
2416
  emptyState,
1888
2417
  onThreadSelect
1889
2418
  }) => {
1890
- const {
1891
- threads,
1892
- activeThreadId,
1893
- selectThread,
1894
- createThread,
1895
- deleteThread
1896
- } = useThreads();
2419
+ const { threads, activeThreadId, selectThread, createThread, deleteThread } = useThreads();
1897
2420
  const handleThreadClick = (threadId) => {
1898
2421
  if (threadId !== activeThreadId) {
1899
2422
  selectThread(threadId);
@@ -1931,10 +2454,10 @@ var ThreadList = ({
1931
2454
  return d.toLocaleDateString();
1932
2455
  };
1933
2456
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full", className), children: [
1934
- /* @__PURE__ */ jsx("div", { className: "p-2 border-b", children: /* @__PURE__ */ jsxs(
2457
+ /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsxs(
1935
2458
  Button,
1936
2459
  {
1937
- variant: "outline",
2460
+ variant: "ghost",
1938
2461
  size: "sm",
1939
2462
  onClick: handleNewThread,
1940
2463
  className: "w-full justify-start",
@@ -1955,31 +2478,13 @@ var ThreadList = ({
1955
2478
  {
1956
2479
  onClick: () => handleThreadClick(thread.id),
1957
2480
  className: cn(
1958
- "group relative flex items-center gap-3 p-3 rounded-lg cursor-pointer transition-colors",
1959
- isActive ? "bg-primary text-primary-foreground" : "hover:bg-muted"
2481
+ "group relative flex items-center gap-3 px-3 py-1.5 rounded-lg cursor-pointer transition-colors",
2482
+ isActive ? "bg-muted" : "hover:bg-muted"
1960
2483
  ),
1961
2484
  children: [
1962
2485
  /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
1963
- /* @__PURE__ */ jsx(
1964
- "p",
1965
- {
1966
- className: cn(
1967
- "text-sm font-medium truncate",
1968
- isActive && "text-primary-foreground"
1969
- ),
1970
- children: thread.title || `Thread ${thread.id.slice(0, 8)}`
1971
- }
1972
- ),
1973
- thread.updatedAt && /* @__PURE__ */ jsx(
1974
- "span",
1975
- {
1976
- className: cn(
1977
- "text-xs shrink-0",
1978
- isActive ? "text-primary-foreground/70" : "text-muted-foreground"
1979
- ),
1980
- children: formatDate(thread.updatedAt)
1981
- }
1982
- )
2486
+ /* @__PURE__ */ jsx("p", { className: cn("text-sm font-medium truncate"), children: thread.title || `Thread ${thread.id.slice(0, 8)}` }),
2487
+ thread.updatedAt && /* @__PURE__ */ jsx("span", { className: cn("text-xs shrink-0"), children: formatDate(thread.updatedAt) })
1983
2488
  ] }) }),
1984
2489
  /* @__PURE__ */ jsx(
1985
2490
  Button,
@@ -2005,7 +2510,10 @@ function ChatPopup({
2005
2510
  title = "Chat",
2006
2511
  placeholder = "Message the AI",
2007
2512
  starterPrompts,
2008
- defaultOpen = false
2513
+ options,
2514
+ defaultOpen = false,
2515
+ headerProps,
2516
+ defaultSelectedIds
2009
2517
  }) {
2010
2518
  const [isOpen, setIsOpen] = useState(defaultOpen);
2011
2519
  const [view, setView] = useState("chat");
@@ -2019,10 +2527,12 @@ function ChatPopup({
2019
2527
  }
2020
2528
  };
2021
2529
  return /* @__PURE__ */ jsxs("div", { className: "fixed bottom-6 right-6 z-50 flex flex-col items-end gap-4 font-sans", children: [
2022
- isOpen && /* @__PURE__ */ jsxs(Card, { className: "py-0 w-[440px] h-[640px] 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: [
2023
- /* @__PURE__ */ jsxs(CardHeader, { className: "p-4 border-b flex flex-row items-center justify-between space-y-0 h-14 shrink-0", children: [
2024
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2025
- view === "history" && /* @__PURE__ */ jsx(
2530
+ 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: [
2531
+ /* @__PURE__ */ jsx(
2532
+ ChatHeader,
2533
+ {
2534
+ title: view === "history" ? "History" : title,
2535
+ leftContent: view === "history" ? /* @__PURE__ */ jsx(
2026
2536
  Button,
2027
2537
  {
2028
2538
  variant: "ghost",
@@ -2031,50 +2541,52 @@ function ChatPopup({
2031
2541
  className: "text-muted-foreground hover:text-foreground",
2032
2542
  children: /* @__PURE__ */ jsx(IconArrowLeft, { className: "size-4" })
2033
2543
  }
2034
- ),
2035
- /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-semibold", children: view === "history" ? "History" : title })
2036
- ] }),
2037
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2038
- view === "chat" && /* @__PURE__ */ jsx(
2039
- Button,
2040
- {
2041
- variant: "ghost",
2042
- size: "icon-xs",
2043
- onClick: () => setView("history"),
2044
- className: "text-muted-foreground hover:text-foreground",
2045
- title: "History",
2046
- children: /* @__PURE__ */ jsx(IconHistory, { className: "size-4" })
2047
- }
2048
- ),
2049
- /* @__PURE__ */ jsx(
2050
- Button,
2051
- {
2052
- variant: "ghost",
2053
- size: "icon-xs",
2054
- onClick: handleNewChat,
2055
- className: "text-muted-foreground hover:text-foreground",
2056
- title: "New Chat",
2057
- children: /* @__PURE__ */ jsx(IconPlus, { className: "size-4" })
2058
- }
2059
- ),
2060
- /* @__PURE__ */ jsx(
2061
- Button,
2062
- {
2063
- variant: "ghost",
2064
- size: "icon-xs",
2065
- onClick: () => setIsOpen(false),
2066
- className: "text-muted-foreground hover:text-foreground",
2067
- children: /* @__PURE__ */ jsx(IconX, { className: "size-4" })
2068
- }
2069
- )
2070
- ] })
2071
- ] }),
2544
+ ) : void 0,
2545
+ rightContent: /* @__PURE__ */ jsxs(Fragment, { children: [
2546
+ view === "chat" && /* @__PURE__ */ jsx(
2547
+ Button,
2548
+ {
2549
+ variant: "ghost",
2550
+ size: "icon-xs",
2551
+ onClick: () => setView("history"),
2552
+ className: "text-muted-foreground hover:text-foreground",
2553
+ title: "History",
2554
+ children: /* @__PURE__ */ jsx(IconHistory, { className: "size-4" })
2555
+ }
2556
+ ),
2557
+ /* @__PURE__ */ jsx(
2558
+ Button,
2559
+ {
2560
+ variant: "ghost",
2561
+ size: "icon-xs",
2562
+ onClick: handleNewChat,
2563
+ className: "text-muted-foreground hover:text-foreground",
2564
+ title: "New Chat",
2565
+ children: /* @__PURE__ */ jsx(IconPlus, { className: "size-4" })
2566
+ }
2567
+ ),
2568
+ /* @__PURE__ */ jsx(
2569
+ Button,
2570
+ {
2571
+ variant: "ghost",
2572
+ size: "icon-xs",
2573
+ onClick: () => setIsOpen(false),
2574
+ className: "text-muted-foreground hover:text-foreground",
2575
+ children: /* @__PURE__ */ jsx(IconX, { className: "size-4" })
2576
+ }
2577
+ )
2578
+ ] }),
2579
+ ...headerProps
2580
+ }
2581
+ ),
2072
2582
  /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: view === "chat" ? /* @__PURE__ */ jsx(
2073
2583
  Thread,
2074
2584
  {
2075
2585
  placeholder,
2076
2586
  starterPrompts,
2077
- className: "h-full"
2587
+ options,
2588
+ className: "h-full",
2589
+ defaultSelectedIds
2078
2590
  }
2079
2591
  ) : /* @__PURE__ */ jsx(
2080
2592
  ThreadList,
@@ -2102,16 +2614,21 @@ function ChatSidebar({
2102
2614
  title = "Chat",
2103
2615
  placeholder = "Message the AI",
2104
2616
  starterPrompts,
2105
- className
2617
+ options,
2618
+ className,
2619
+ headerProps,
2620
+ defaultSelectedIds
2106
2621
  }) {
2107
2622
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full border-r bg-background w-80", className), children: [
2108
- /* @__PURE__ */ jsx("div", { className: "p-4 border-b h-14 flex items-center shrink-0", children: /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: title }) }),
2623
+ /* @__PURE__ */ jsx(ChatHeader, { title, ...headerProps }),
2109
2624
  /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(
2110
2625
  Thread,
2111
2626
  {
2112
2627
  placeholder,
2113
2628
  starterPrompts,
2114
- className: "h-full"
2629
+ options,
2630
+ className: "h-full",
2631
+ defaultSelectedIds
2115
2632
  }
2116
2633
  ) })
2117
2634
  ] });
@@ -2120,13 +2637,437 @@ function ChatFull({
2120
2637
  title = "Chat",
2121
2638
  placeholder = "Message the AI",
2122
2639
  starterPrompts,
2123
- className
2640
+ options,
2641
+ className,
2642
+ headerProps,
2643
+ leftSidebar,
2644
+ rightSidebar,
2645
+ leftSidebarClassName,
2646
+ rightSidebarClassName,
2647
+ leftSidebarCollapsible = false,
2648
+ rightSidebarCollapsible = false,
2649
+ defaultLeftSidebarCollapsed = false,
2650
+ defaultRightSidebarCollapsed = false,
2651
+ leftSidebarCollapsed: controlledLeftCollapsed,
2652
+ rightSidebarCollapsed: controlledRightCollapsed,
2653
+ onLeftSidebarCollapseChange,
2654
+ onRightSidebarCollapseChange,
2655
+ autoFocus = false,
2656
+ defaultSelectedIds
2124
2657
  }) {
2658
+ const [internalLeftCollapsed, setInternalLeftCollapsed] = useState(
2659
+ defaultLeftSidebarCollapsed
2660
+ );
2661
+ const [internalRightCollapsed, setInternalRightCollapsed] = useState(
2662
+ defaultRightSidebarCollapsed
2663
+ );
2664
+ const leftCollapsed = controlledLeftCollapsed !== void 0 ? controlledLeftCollapsed : internalLeftCollapsed;
2665
+ const rightCollapsed = controlledRightCollapsed !== void 0 ? controlledRightCollapsed : internalRightCollapsed;
2666
+ const handleLeftToggle = () => {
2667
+ const newCollapsed = !leftCollapsed;
2668
+ if (controlledLeftCollapsed === void 0) {
2669
+ setInternalLeftCollapsed(newCollapsed);
2670
+ }
2671
+ onLeftSidebarCollapseChange?.(newCollapsed);
2672
+ };
2673
+ const handleRightToggle = () => {
2674
+ const newCollapsed = !rightCollapsed;
2675
+ if (controlledRightCollapsed === void 0) {
2676
+ setInternalRightCollapsed(newCollapsed);
2677
+ }
2678
+ onRightSidebarCollapseChange?.(newCollapsed);
2679
+ };
2125
2680
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full w-full bg-background", className), children: [
2126
- title && /* @__PURE__ */ jsx("div", { className: "p-4 border-b h-14 flex items-center shrink-0", children: /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: title }) }),
2127
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(Thread, { placeholder, starterPrompts }) })
2681
+ title && /* @__PURE__ */ jsx(ChatHeader, { title, ...headerProps }),
2682
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden flex relative", children: [
2683
+ leftSidebar && /* @__PURE__ */ jsxs(Fragment, { children: [
2684
+ /* @__PURE__ */ jsx(
2685
+ "div",
2686
+ {
2687
+ className: cn(
2688
+ "flex-shrink-0 border-r border-border bg-background transition-all duration-300 ease-in-out overflow-hidden flex flex-col",
2689
+ leftCollapsed && leftSidebarCollapsible ? "w-0 border-r-0 min-w-0" : "",
2690
+ !leftCollapsed && leftSidebarClassName
2691
+ ),
2692
+ children: !leftCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
2693
+ leftSidebarCollapsible && /* @__PURE__ */ jsx("div", { className: "flex justify-end p-2 border-b border-border shrink-0", children: /* @__PURE__ */ jsx(
2694
+ Button,
2695
+ {
2696
+ variant: "ghost",
2697
+ size: "icon-sm",
2698
+ onClick: handleLeftToggle,
2699
+ "aria-label": "Collapse left sidebar",
2700
+ className: "h-8 w-8",
2701
+ children: /* @__PURE__ */ jsx(IconChevronLeft, { className: "h-4 w-4" })
2702
+ }
2703
+ ) }),
2704
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-h-0", children: leftSidebar })
2705
+ ] })
2706
+ }
2707
+ ),
2708
+ leftSidebarCollapsible && leftCollapsed && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 border-r border-border bg-background flex items-center justify-center w-10", children: /* @__PURE__ */ jsx(
2709
+ Button,
2710
+ {
2711
+ variant: "ghost",
2712
+ size: "icon-sm",
2713
+ onClick: handleLeftToggle,
2714
+ "aria-label": "Expand left sidebar",
2715
+ className: "h-8 w-8",
2716
+ children: /* @__PURE__ */ jsx(IconChevronRight, { className: "h-4 w-4" })
2717
+ }
2718
+ ) })
2719
+ ] }),
2720
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-w-0", children: /* @__PURE__ */ jsx(
2721
+ Thread,
2722
+ {
2723
+ placeholder,
2724
+ starterPrompts,
2725
+ options,
2726
+ autoFocus,
2727
+ defaultSelectedIds
2728
+ }
2729
+ ) }),
2730
+ rightSidebar && /* @__PURE__ */ jsxs(Fragment, { children: [
2731
+ rightSidebarCollapsible && rightCollapsed && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 border-l border-border bg-background flex items-center justify-center w-10", children: /* @__PURE__ */ jsx(
2732
+ Button,
2733
+ {
2734
+ variant: "ghost",
2735
+ size: "icon-sm",
2736
+ onClick: handleRightToggle,
2737
+ "aria-label": "Expand right sidebar",
2738
+ className: "h-8 w-8",
2739
+ children: /* @__PURE__ */ jsx(IconChevronLeft, { className: "h-4 w-4" })
2740
+ }
2741
+ ) }),
2742
+ /* @__PURE__ */ jsx(
2743
+ "div",
2744
+ {
2745
+ className: cn(
2746
+ "flex-shrink-0 border-l border-border bg-background transition-all duration-300 ease-in-out overflow-hidden flex flex-col",
2747
+ rightCollapsed && rightSidebarCollapsible ? "w-0 border-l-0 min-w-0" : "",
2748
+ !rightCollapsed && rightSidebarClassName
2749
+ ),
2750
+ children: !rightCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
2751
+ rightSidebarCollapsible && /* @__PURE__ */ jsx("div", { className: "flex justify-start p-2 border-b border-border shrink-0", children: /* @__PURE__ */ jsx(
2752
+ Button,
2753
+ {
2754
+ variant: "ghost",
2755
+ size: "icon-sm",
2756
+ onClick: handleRightToggle,
2757
+ "aria-label": "Collapse right sidebar",
2758
+ className: "h-8 w-8",
2759
+ children: /* @__PURE__ */ jsx(IconChevronRight, { className: "h-4 w-4" })
2760
+ }
2761
+ ) }),
2762
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-h-0", children: rightSidebar })
2763
+ ] })
2764
+ }
2765
+ )
2766
+ ] })
2767
+ ] })
2128
2768
  ] });
2129
2769
  }
2770
+ var PopoverContext = React11.createContext(
2771
+ void 0
2772
+ );
2773
+ function usePopoverContext() {
2774
+ const context = React11.useContext(PopoverContext);
2775
+ if (!context) {
2776
+ throw new Error("Popover components must be used within a Popover");
2777
+ }
2778
+ return context;
2779
+ }
2780
+ function Popover({
2781
+ children,
2782
+ defaultOpen = false,
2783
+ open: controlledOpen,
2784
+ onOpenChange
2785
+ }) {
2786
+ const [internalOpen, setInternalOpen] = React11.useState(defaultOpen);
2787
+ const triggerRef = React11.useRef(null);
2788
+ const open = controlledOpen ?? internalOpen;
2789
+ const setOpen = React11.useCallback(
2790
+ (newOpen) => {
2791
+ if (controlledOpen === void 0) {
2792
+ setInternalOpen(newOpen);
2793
+ }
2794
+ onOpenChange?.(newOpen);
2795
+ },
2796
+ [controlledOpen, onOpenChange]
2797
+ );
2798
+ const value = React11.useMemo(
2799
+ () => ({
2800
+ open,
2801
+ setOpen,
2802
+ triggerRef
2803
+ }),
2804
+ [open, setOpen]
2805
+ );
2806
+ return /* @__PURE__ */ jsx(PopoverContext.Provider, { value, children });
2807
+ }
2808
+ var PopoverTrigger = React11.forwardRef(
2809
+ ({ asChild, className, children, ...props }, ref) => {
2810
+ const { setOpen, triggerRef } = usePopoverContext();
2811
+ const handleClick = (e) => {
2812
+ setOpen(true);
2813
+ props.onClick?.(e);
2814
+ };
2815
+ if (asChild && React11.isValidElement(children)) {
2816
+ return React11.cloneElement(children, {
2817
+ ref: (node) => {
2818
+ triggerRef.current = node;
2819
+ if (typeof children.ref === "function") {
2820
+ children.ref(node);
2821
+ } else if (children.ref) {
2822
+ children.ref.current = node;
2823
+ }
2824
+ },
2825
+ onClick: handleClick
2826
+ });
2827
+ }
2828
+ return /* @__PURE__ */ jsx(
2829
+ "button",
2830
+ {
2831
+ ref: (node) => {
2832
+ triggerRef.current = node;
2833
+ if (typeof ref === "function") {
2834
+ ref(node);
2835
+ } else if (ref) {
2836
+ ref.current = node;
2837
+ }
2838
+ },
2839
+ className,
2840
+ onClick: handleClick,
2841
+ ...props,
2842
+ children
2843
+ }
2844
+ );
2845
+ }
2846
+ );
2847
+ PopoverTrigger.displayName = "PopoverTrigger";
2848
+ var PopoverContent = React11.forwardRef(
2849
+ ({
2850
+ className,
2851
+ side = "bottom",
2852
+ align = "start",
2853
+ sideOffset = 4,
2854
+ alignOffset = 0,
2855
+ children,
2856
+ ...props
2857
+ }, ref) => {
2858
+ const { open, setOpen, triggerRef } = usePopoverContext();
2859
+ const [position, setPosition] = React11.useState({ top: 0, left: 0 });
2860
+ const contentRef = React11.useRef(null);
2861
+ React11.useEffect(() => {
2862
+ if (!open || !triggerRef.current) return;
2863
+ const updatePosition = () => {
2864
+ if (!triggerRef.current || !contentRef.current) return;
2865
+ const triggerRect = triggerRef.current.getBoundingClientRect();
2866
+ const contentRect = contentRef.current.getBoundingClientRect();
2867
+ const scrollX = window.scrollX;
2868
+ const scrollY = window.scrollY;
2869
+ let top = 0;
2870
+ let left = 0;
2871
+ switch (side) {
2872
+ case "bottom":
2873
+ top = triggerRect.bottom + sideOffset + scrollY;
2874
+ break;
2875
+ case "top":
2876
+ top = triggerRect.top - contentRect.height - sideOffset + scrollY;
2877
+ break;
2878
+ case "right":
2879
+ top = triggerRect.top + scrollY;
2880
+ left = triggerRect.right + sideOffset + scrollX;
2881
+ break;
2882
+ case "left":
2883
+ top = triggerRect.top + scrollY;
2884
+ left = triggerRect.left - contentRect.width - sideOffset + scrollX;
2885
+ break;
2886
+ }
2887
+ switch (align) {
2888
+ case "start":
2889
+ if (side === "top" || side === "bottom") {
2890
+ left = triggerRect.left + scrollX + alignOffset;
2891
+ } else {
2892
+ top += alignOffset;
2893
+ }
2894
+ break;
2895
+ case "center":
2896
+ if (side === "top" || side === "bottom") {
2897
+ left = triggerRect.left + triggerRect.width / 2 - contentRect.width / 2 + scrollX + alignOffset;
2898
+ } else {
2899
+ top += triggerRect.height / 2 - contentRect.height / 2 + alignOffset;
2900
+ }
2901
+ break;
2902
+ case "end":
2903
+ if (side === "top" || side === "bottom") {
2904
+ left = triggerRect.left + triggerRect.width - contentRect.width + scrollX + alignOffset;
2905
+ } else {
2906
+ top += triggerRect.height - contentRect.height + alignOffset;
2907
+ }
2908
+ break;
2909
+ }
2910
+ setPosition({ top, left });
2911
+ };
2912
+ requestAnimationFrame(() => {
2913
+ updatePosition();
2914
+ });
2915
+ window.addEventListener("resize", updatePosition);
2916
+ window.addEventListener("scroll", updatePosition, true);
2917
+ return () => {
2918
+ window.removeEventListener("resize", updatePosition);
2919
+ window.removeEventListener("scroll", updatePosition, true);
2920
+ };
2921
+ }, [open, side, align, sideOffset, alignOffset, triggerRef]);
2922
+ React11.useEffect(() => {
2923
+ if (!open) return;
2924
+ const handleClickOutside = (event) => {
2925
+ if (contentRef.current && !contentRef.current.contains(event.target) && triggerRef.current && !triggerRef.current.contains(event.target)) {
2926
+ setOpen(false);
2927
+ }
2928
+ };
2929
+ const handleEscape = (event) => {
2930
+ if (event.key === "Escape") {
2931
+ setOpen(false);
2932
+ }
2933
+ };
2934
+ document.addEventListener("mousedown", handleClickOutside);
2935
+ document.addEventListener("keydown", handleEscape);
2936
+ return () => {
2937
+ document.removeEventListener("mousedown", handleClickOutside);
2938
+ document.removeEventListener("keydown", handleEscape);
2939
+ };
2940
+ }, [open, setOpen, triggerRef]);
2941
+ if (!open) return null;
2942
+ const content = /* @__PURE__ */ jsx(
2943
+ "div",
2944
+ {
2945
+ ref: (node) => {
2946
+ contentRef.current = node;
2947
+ if (typeof ref === "function") {
2948
+ ref(node);
2949
+ } else if (ref) {
2950
+ ref.current = node;
2951
+ }
2952
+ },
2953
+ className: cn(
2954
+ "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",
2955
+ className
2956
+ ),
2957
+ style: {
2958
+ position: "absolute",
2959
+ top: `${position.top}px`,
2960
+ left: `${position.left}px`
2961
+ },
2962
+ ...props,
2963
+ children
2964
+ }
2965
+ );
2966
+ return createPortal(content, document.body);
2967
+ }
2968
+ );
2969
+ PopoverContent.displayName = "PopoverContent";
2970
+ var ThreadPopover = ({
2971
+ className,
2972
+ buttonClassName,
2973
+ buttonVariant = "ghost",
2974
+ buttonSize = "icon",
2975
+ emptyState,
2976
+ onThreadSelect
2977
+ }) => {
2978
+ const [isOpen, setIsOpen] = React11.useState(false);
2979
+ useHotkeys(
2980
+ "h",
2981
+ (e) => {
2982
+ e.preventDefault();
2983
+ setIsOpen((prev) => !prev);
2984
+ },
2985
+ {
2986
+ enableOnFormTags: false,
2987
+ // Don't trigger when typing in form inputs
2988
+ enableOnContentEditable: false
2989
+ // Don't trigger in contenteditable elements
2990
+ }
2991
+ );
2992
+ const handleThreadSelect = (threadId) => {
2993
+ setIsOpen(false);
2994
+ onThreadSelect?.(threadId);
2995
+ };
2996
+ return /* @__PURE__ */ jsxs(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
2997
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
2998
+ Button,
2999
+ {
3000
+ variant: buttonVariant,
3001
+ size: buttonSize,
3002
+ className: cn(buttonClassName),
3003
+ children: /* @__PURE__ */ jsx(IconHistory, { className: "size-4" })
3004
+ }
3005
+ ) }),
3006
+ /* @__PURE__ */ jsx(
3007
+ PopoverContent,
3008
+ {
3009
+ className: cn("w-80 p-0", className),
3010
+ side: "bottom",
3011
+ align: "start",
3012
+ sideOffset: 8,
3013
+ children: /* @__PURE__ */ jsx("div", { className: "flex flex-col h-[400px]", children: /* @__PURE__ */ jsx(
3014
+ ThreadList,
3015
+ {
3016
+ emptyState,
3017
+ onThreadSelect: handleThreadSelect,
3018
+ className: "h-full"
3019
+ }
3020
+ ) })
3021
+ }
3022
+ )
3023
+ ] });
3024
+ };
3025
+ var CreateThreadButton = ({
3026
+ className,
3027
+ variant = "ghost",
3028
+ size = "icon",
3029
+ children,
3030
+ onThreadCreated
3031
+ }) => {
3032
+ const { createThread } = useThreads();
3033
+ const [isCreating, setIsCreating] = React11.useState(false);
3034
+ const handleCreateThread = async () => {
3035
+ if (isCreating) return;
3036
+ try {
3037
+ setIsCreating(true);
3038
+ const threadId = await createThread();
3039
+ onThreadCreated?.(threadId);
3040
+ } catch (error) {
3041
+ console.error("Failed to create thread:", error);
3042
+ } finally {
3043
+ setIsCreating(false);
3044
+ }
3045
+ };
3046
+ useHotkeys(
3047
+ "n",
3048
+ (e) => {
3049
+ e.preventDefault();
3050
+ handleCreateThread();
3051
+ },
3052
+ {
3053
+ enableOnFormTags: false,
3054
+ // Don't trigger when typing in form inputs
3055
+ enableOnContentEditable: false
3056
+ // Don't trigger in contenteditable elements
3057
+ }
3058
+ );
3059
+ return /* @__PURE__ */ jsx(
3060
+ Button,
3061
+ {
3062
+ variant,
3063
+ size,
3064
+ onClick: handleCreateThread,
3065
+ disabled: isCreating,
3066
+ className: cn(className),
3067
+ children: /* @__PURE__ */ jsx(IconPlus, { className: "size-4" })
3068
+ }
3069
+ );
3070
+ };
2130
3071
  function AlertDialog({ ...props }) {
2131
3072
  return /* @__PURE__ */ jsx(AlertDialog$1.Root, { "data-slot": "alert-dialog", ...props });
2132
3073
  }
@@ -2209,100 +3150,111 @@ function AlertDialogDescription({
2209
3150
  }
2210
3151
  );
2211
3152
  }
2212
- function DropdownMenu({ ...props }) {
2213
- return /* @__PURE__ */ jsx(Menu.Root, { "data-slot": "dropdown-menu", ...props });
2214
- }
2215
- function DropdownMenuTrigger({ ...props }) {
2216
- return /* @__PURE__ */ jsx(Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
2217
- }
2218
- function DropdownMenuContent({
2219
- align = "start",
2220
- alignOffset = 0,
2221
- side = "bottom",
2222
- sideOffset = 4,
2223
- className,
2224
- ...props
2225
- }) {
2226
- return /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(
2227
- Menu.Positioner,
2228
- {
2229
- className: "isolate z-50 outline-none",
2230
- align,
2231
- alignOffset,
2232
- side,
2233
- sideOffset,
2234
- children: /* @__PURE__ */ jsx(
2235
- Menu.Popup,
2236
- {
2237
- "data-slot": "dropdown-menu-content",
2238
- 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),
2239
- ...props
2240
- }
2241
- )
2242
- }
2243
- ) });
2244
- }
2245
- function DropdownMenuItem({
2246
- className,
2247
- inset,
2248
- variant = "default",
2249
- ...props
2250
- }) {
2251
- return /* @__PURE__ */ jsx(
2252
- Menu.Item,
2253
- {
2254
- "data-slot": "dropdown-menu-item",
2255
- "data-inset": inset,
2256
- "data-variant": variant,
2257
- className: cn(
2258
- "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",
2259
- className
2260
- ),
2261
- ...props
2262
- }
2263
- );
2264
- }
2265
3153
  var AccountDialog = ({
2266
3154
  className,
2267
- variant,
3155
+ variant = "outline",
2268
3156
  size
2269
3157
  }) => {
2270
- const { isLoading, isAuthenticated, login, logout } = useAuth();
2271
- const [open, setOpen] = React9.useState(false);
2272
- const [error, setError] = React9.useState(null);
3158
+ const { isLoading, isAuthenticated, user, login, logout } = useAuth();
3159
+ const [open, setOpen] = React11.useState(false);
3160
+ const [accountInfoOpen, setAccountInfoOpen] = React11.useState(false);
3161
+ const [error, setError] = React11.useState(null);
3162
+ const initials = React11.useMemo(() => {
3163
+ const name = user?.displayName || user?.name;
3164
+ if (!name) return "";
3165
+ return name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
3166
+ }, [user?.displayName, user?.name]);
2273
3167
  const handleGoogleSignIn = async () => {
2274
3168
  login();
2275
3169
  };
2276
3170
  if (isAuthenticated) {
2277
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
2278
- /* @__PURE__ */ jsx(
2279
- DropdownMenuTrigger,
2280
- {
2281
- render: (props) => /* @__PURE__ */ jsxs(
2282
- Button,
2283
- {
2284
- variant,
2285
- size,
2286
- ...props,
2287
- className,
2288
- children: [
2289
- /* @__PURE__ */ jsx(IconUser, { className: "mr-2 size-4" }),
2290
- "Account"
2291
- ]
2292
- }
2293
- )
2294
- }
2295
- ),
2296
- /* @__PURE__ */ jsxs(DropdownMenuContent, { children: [
2297
- /* @__PURE__ */ jsxs(DropdownMenuItem, { children: [
2298
- /* @__PURE__ */ jsx(IconUser, { className: "mr-2 size-4" }),
2299
- "Account"
3171
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3172
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
3173
+ /* @__PURE__ */ jsx(
3174
+ DropdownMenuTrigger,
3175
+ {
3176
+ render: (props) => /* @__PURE__ */ jsx(
3177
+ Button,
3178
+ {
3179
+ variant,
3180
+ size: "icon",
3181
+ ...props,
3182
+ className: cn("rounded-full", className),
3183
+ children: user?.picture ? /* @__PURE__ */ jsx(
3184
+ "img",
3185
+ {
3186
+ src: user.picture,
3187
+ alt: user.displayName || user.name,
3188
+ className: "size-7 rounded-full object-cover"
3189
+ }
3190
+ ) : /* @__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" }) })
3191
+ }
3192
+ )
3193
+ }
3194
+ ),
3195
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
3196
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 p-2", children: [
3197
+ user?.picture ? /* @__PURE__ */ jsx(
3198
+ "img",
3199
+ {
3200
+ src: user.picture,
3201
+ alt: user.displayName || user.name,
3202
+ className: "size-8 rounded-full object-cover"
3203
+ }
3204
+ ) : /* @__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" }) }),
3205
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-0.5 overflow-hidden", children: [
3206
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: user?.displayName || user?.name }),
3207
+ /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user?.email })
3208
+ ] })
3209
+ ] }),
3210
+ /* @__PURE__ */ jsx(Separator, { className: "my-1" }),
3211
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: () => setAccountInfoOpen(true), children: [
3212
+ /* @__PURE__ */ jsx(IconUser, { className: "mr-2 size-4" }),
3213
+ "Account Settings"
3214
+ ] }),
3215
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: logout, className: "text-destructive", children: [
3216
+ /* @__PURE__ */ jsx(IconLogout, { className: "mr-2 size-4" }),
3217
+ "Logout"
3218
+ ] })
3219
+ ] })
3220
+ ] }),
3221
+ /* @__PURE__ */ jsx(Dialog, { open: accountInfoOpen, onOpenChange: setAccountInfoOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
3222
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
3223
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Account Information" }),
3224
+ /* @__PURE__ */ jsx(DialogDescription, { children: "Your account details and settings." })
3225
+ ] }),
3226
+ /* @__PURE__ */ jsxs(DialogClose, { children: [
3227
+ /* @__PURE__ */ jsx(IconX, { className: "size-4" }),
3228
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
2300
3229
  ] }),
2301
- /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: logout, children: [
2302
- /* @__PURE__ */ jsx(IconLogout, { className: "mr-2 size-4" }),
2303
- "Logout"
3230
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 py-4", children: [
3231
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
3232
+ user?.picture ? /* @__PURE__ */ jsx(
3233
+ "img",
3234
+ {
3235
+ src: user.picture,
3236
+ alt: user.displayName || user.name,
3237
+ className: "size-16 rounded-full object-cover"
3238
+ }
3239
+ ) : /* @__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" }) }),
3240
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
3241
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: user?.displayName || user?.name }),
3242
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: user?.email })
3243
+ ] })
3244
+ ] }),
3245
+ /* @__PURE__ */ jsx(Separator, {}),
3246
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
3247
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
3248
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: "User ID" }),
3249
+ /* @__PURE__ */ jsx("p", { className: "font-mono text-xs truncate", children: user?.uid || user?.id })
3250
+ ] }),
3251
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
3252
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: "Created At" }),
3253
+ /* @__PURE__ */ jsx("p", { className: "text-xs", children: user?.createdAt || "N/A" })
3254
+ ] })
3255
+ ] })
2304
3256
  ] })
2305
- ] })
3257
+ ] }) })
2306
3258
  ] });
2307
3259
  }
2308
3260
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -2344,7 +3296,42 @@ var AccountDialog = ({
2344
3296
  ] }) })
2345
3297
  ] });
2346
3298
  };
3299
+ function ThemeToggle() {
3300
+ const { theme, setTheme, resolvedTheme } = useTheme();
3301
+ const cycleTheme = () => {
3302
+ if (theme === "light") {
3303
+ setTheme("dark");
3304
+ } else if (theme === "dark") {
3305
+ setTheme("system");
3306
+ } else {
3307
+ setTheme("light");
3308
+ }
3309
+ };
3310
+ const getIcon = () => {
3311
+ if (theme === "system") {
3312
+ return /* @__PURE__ */ jsx(IconDeviceDesktop, { className: "h-4 w-4" });
3313
+ }
3314
+ return resolvedTheme === "dark" ? /* @__PURE__ */ jsx(IconMoon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(IconSun, { className: "h-4 w-4" });
3315
+ };
3316
+ const getLabel = () => {
3317
+ if (theme === "system") {
3318
+ return "System";
3319
+ }
3320
+ return resolvedTheme === "dark" ? "Dark" : "Light";
3321
+ };
3322
+ return /* @__PURE__ */ jsx(
3323
+ Button,
3324
+ {
3325
+ variant: "ghost",
3326
+ size: "icon",
3327
+ onClick: cycleTheme,
3328
+ "aria-label": `Toggle theme (current: ${getLabel()})`,
3329
+ title: `Current: ${getLabel()}. Click to cycle: Light \u2192 Dark \u2192 System`,
3330
+ children: getIcon()
3331
+ }
3332
+ );
3333
+ }
2347
3334
 
2348
- export { AccountDialog, AuthContext, AuthProvider, ChatFull, ChatPopup, ChatSidebar, Composer, MelonyClientProvider, MelonyContext, Thread, ThreadContext, ThreadList, ThreadProvider, UIRenderer, useAuth, useMelony, useThreads };
3335
+ export { AccountDialog, AuthContext, AuthProvider, ChatFull, ChatHeader, ChatPopup, ChatSidebar, Composer, CreateThreadButton, MelonyClientProvider, MelonyContext, ThemeProvider, ThemeToggle, Thread, ThreadContext, ThreadList, ThreadPopover, ThreadProvider, UIRenderer, groupEventsToMessages, useAuth, useMelony, useTheme, useThreads };
2349
3336
  //# sourceMappingURL=index.js.map
2350
3337
  //# sourceMappingURL=index.js.map