@melony/react 0.1.23 → 0.1.25

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,5 +1,5 @@
1
- import * as React10 from 'react';
2
- import React10__default, { createContext, useState, useCallback, useEffect, useMemo, useContext, useRef } from 'react';
1
+ import * as React11 from 'react';
2
+ import React11__default, { createContext, useState, useCallback, useEffect, useMemo, useContext, useRef } from 'react';
3
3
  import { NuqsAdapter } from 'nuqs/adapters/react';
4
4
  import { QueryClient, QueryClientProvider, useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -9,13 +9,13 @@ import { clsx } from 'clsx';
9
9
  import { twMerge } from 'tailwind-merge';
10
10
  import { Button as Button$1 } from '@base-ui/react/button';
11
11
  import { cva } from 'class-variance-authority';
12
+ import { mergeProps } from '@base-ui/react/merge-props';
13
+ import { useRender } from '@base-ui/react/use-render';
12
14
  import * as ICONS from '@tabler/icons-react';
13
- import { IconChevronDown, IconLoader2, IconArrowUp, IconPlus, IconMessage, IconTrash, IconHistory, IconX, IconArrowLeft, IconChevronLeft, IconChevronRight, IconUser, IconLogout, IconBrandGoogle, IconDeviceDesktop, IconMoon, IconSun, IconCheck, IconChevronUp, IconSelector } from '@tabler/icons-react';
15
+ import { IconPaperclip, IconX, IconPlus, IconChevronDown, IconLoader2, IconArrowUp, IconMessage, IconTrash, IconHistory, IconArrowLeft, IconLayoutSidebarLeftExpand, IconLayoutSidebarLeftCollapse, IconLayoutSidebarRightExpand, IconLayoutSidebarRightCollapse, IconUser, IconLogout, IconBrandGoogle, IconDeviceDesktop, IconMoon, IconSun, IconCheck, IconSelector, IconChevronUp } from '@tabler/icons-react';
14
16
  import { Menu } from '@base-ui/react/menu';
15
17
  import { Separator as Separator$1 } from '@base-ui/react/separator';
16
18
  import { Dialog as Dialog$1 } from '@base-ui/react/dialog';
17
- import { mergeProps } from '@base-ui/react/merge-props';
18
- import { useRender } from '@base-ui/react/use-render';
19
19
  import { Input as Input$1 } from '@base-ui/react/input';
20
20
  import { Select as Select$1 } from '@base-ui/react/select';
21
21
  import { createPortal } from 'react-dom';
@@ -63,14 +63,12 @@ var MelonyContextProviderInner = ({
63
63
  children,
64
64
  client,
65
65
  initialEvents,
66
- configApi,
67
66
  setContextValue
68
67
  }) => {
69
68
  const [state, setState] = useState(client.getState());
70
69
  const { data: config } = useQuery({
71
- queryKey: ["melony-config", configApi],
72
- queryFn: () => client.getConfig(configApi),
73
- enabled: !!configApi,
70
+ queryKey: ["melony-config", client.url],
71
+ queryFn: () => client.getConfig(),
74
72
  staleTime: Infinity
75
73
  });
76
74
  useEffect(() => {
@@ -85,17 +83,56 @@ var MelonyContextProviderInner = ({
85
83
  unsubscribe();
86
84
  };
87
85
  }, [client]);
86
+ const reset = useCallback(
87
+ (events) => client.reset(events),
88
+ [client]
89
+ );
90
+ const dispatchClientAction = useCallback(
91
+ async (event) => {
92
+ if (!event.type.startsWith("client:")) return false;
93
+ switch (event.type) {
94
+ case "client:navigate": {
95
+ const url = event.data?.url;
96
+ if (url) {
97
+ window.history.pushState(null, "", url);
98
+ window.dispatchEvent(new PopStateEvent("popstate"));
99
+ }
100
+ return true;
101
+ }
102
+ case "client:open-url": {
103
+ const { url, target = "_blank" } = event.data || {};
104
+ if (url) {
105
+ window.open(url, target);
106
+ }
107
+ return true;
108
+ }
109
+ case "client:copy": {
110
+ const { text } = event.data || {};
111
+ if (text) {
112
+ await navigator.clipboard.writeText(text);
113
+ }
114
+ return true;
115
+ }
116
+ case "client:reset": {
117
+ reset([]);
118
+ return true;
119
+ }
120
+ default:
121
+ return false;
122
+ }
123
+ },
124
+ [client, reset]
125
+ );
88
126
  const sendEvent = useCallback(
89
127
  async (event, options) => {
128
+ const handled = await dispatchClientAction(event);
129
+ if (handled) return;
90
130
  const generator = client.sendEvent(event, options);
91
- for await (const _ of generator) {
131
+ for await (const incomingEvent of generator) {
132
+ await dispatchClientAction(incomingEvent);
92
133
  }
93
134
  },
94
- [client]
95
- );
96
- const reset = useCallback(
97
- (events) => client.reset(events),
98
- [client]
135
+ [client, dispatchClientAction]
99
136
  );
100
137
  const value = useMemo(
101
138
  () => ({
@@ -117,8 +154,7 @@ var MelonyClientProvider = ({
117
154
  children,
118
155
  client,
119
156
  initialEvents,
120
- queryClient = defaultQueryClient,
121
- configApi
157
+ queryClient = defaultQueryClient
122
158
  }) => {
123
159
  const [contextValue, setContextValue] = useState(void 0);
124
160
  return /* @__PURE__ */ jsx(MelonyContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(
@@ -126,7 +162,6 @@ var MelonyClientProvider = ({
126
162
  {
127
163
  client,
128
164
  initialEvents,
129
- configApi,
130
165
  setContextValue,
131
166
  children
132
167
  }
@@ -377,6 +412,47 @@ var useThreads = () => {
377
412
  }
378
413
  return context;
379
414
  };
415
+ function useScreenSize(mobileBreakpoint = 768, tabletBreakpoint = 1024) {
416
+ const [screenSize, setScreenSize] = useState(() => {
417
+ if (typeof window === "undefined") {
418
+ return {
419
+ width: 1024,
420
+ height: 768,
421
+ isMobile: false,
422
+ isTablet: false,
423
+ isDesktop: true
424
+ };
425
+ }
426
+ const width = window.innerWidth;
427
+ return {
428
+ width,
429
+ height: window.innerHeight,
430
+ isMobile: width < mobileBreakpoint,
431
+ isTablet: width >= mobileBreakpoint && width < tabletBreakpoint,
432
+ isDesktop: width >= tabletBreakpoint
433
+ };
434
+ });
435
+ useEffect(() => {
436
+ if (typeof window === "undefined") return;
437
+ const updateScreenSize = () => {
438
+ const width = window.innerWidth;
439
+ const height = window.innerHeight;
440
+ setScreenSize({
441
+ width,
442
+ height,
443
+ isMobile: width < mobileBreakpoint,
444
+ isTablet: width >= mobileBreakpoint && width < tabletBreakpoint,
445
+ isDesktop: width >= tabletBreakpoint
446
+ });
447
+ };
448
+ updateScreenSize();
449
+ window.addEventListener("resize", updateScreenSize);
450
+ return () => {
451
+ window.removeEventListener("resize", updateScreenSize);
452
+ };
453
+ }, [mobileBreakpoint, tabletBreakpoint]);
454
+ return screenSize;
455
+ }
380
456
  function cn(...inputs) {
381
457
  return twMerge(clsx(inputs));
382
458
  }
@@ -437,6 +513,45 @@ function Textarea({ className, ...props }) {
437
513
  }
438
514
  );
439
515
  }
516
+ var badgeVariants = cva(
517
+ "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",
518
+ {
519
+ variants: {
520
+ variant: {
521
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
522
+ secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
523
+ 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",
524
+ outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground bg-input/30",
525
+ ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
526
+ link: "text-primary underline-offset-4 hover:underline"
527
+ }
528
+ },
529
+ defaultVariants: {
530
+ variant: "default"
531
+ }
532
+ }
533
+ );
534
+ function Badge({
535
+ className,
536
+ variant = "default",
537
+ render,
538
+ ...props
539
+ }) {
540
+ return useRender({
541
+ defaultTagName: "span",
542
+ props: mergeProps(
543
+ {
544
+ className: cn(badgeVariants({ className, variant }))
545
+ },
546
+ props
547
+ ),
548
+ render,
549
+ state: {
550
+ slot: "badge",
551
+ variant
552
+ }
553
+ });
554
+ }
440
555
  function DropdownMenu({ ...props }) {
441
556
  return /* @__PURE__ */ jsx(Menu.Root, { "data-slot": "dropdown-menu", ...props });
442
557
  }
@@ -563,11 +678,18 @@ function Composer({
563
678
  className,
564
679
  options = [],
565
680
  autoFocus = false,
566
- defaultSelectedIds = []
681
+ defaultSelectedIds = [],
682
+ fileAttachments
567
683
  }) {
568
- const [selectedOptions, setSelectedOptions] = React10__default.useState(
684
+ const enabled = fileAttachments?.enabled || false;
685
+ const accept = fileAttachments?.accept;
686
+ const maxFiles = fileAttachments?.maxFiles ?? 10;
687
+ const maxFileSize = fileAttachments?.maxFileSize ?? 10 * 1024 * 1024;
688
+ const [selectedOptions, setSelectedOptions] = React11__default.useState(
569
689
  () => new Set(defaultSelectedIds)
570
690
  );
691
+ const [attachedFiles, setAttachedFiles] = React11__default.useState([]);
692
+ const fileInputRef = React11__default.useRef(null);
571
693
  const toggleOption = (id, groupOptions, type = "multiple") => {
572
694
  const next = new Set(selectedOptions);
573
695
  if (type === "single") {
@@ -587,7 +709,38 @@ function Composer({
587
709
  }
588
710
  setSelectedOptions(next);
589
711
  };
590
- const handleInternalSubmit = () => {
712
+ const handleFileSelect = (e) => {
713
+ const files = Array.from(e.target.files || []);
714
+ const validFiles = files.filter((file) => {
715
+ if (file.size > maxFileSize) {
716
+ console.warn(
717
+ `File ${file.name} exceeds maximum size of ${maxFileSize} bytes`
718
+ );
719
+ return false;
720
+ }
721
+ return true;
722
+ });
723
+ const remainingSlots = maxFiles - attachedFiles.length;
724
+ const filesToAdd = validFiles.slice(0, remainingSlots);
725
+ if (filesToAdd.length < validFiles.length) {
726
+ console.warn(
727
+ `Only ${filesToAdd.length} files can be added (max: ${maxFiles})`
728
+ );
729
+ }
730
+ setAttachedFiles((prev) => [...prev, ...filesToAdd]);
731
+ if (fileInputRef.current) {
732
+ fileInputRef.current.value = "";
733
+ }
734
+ };
735
+ const handleRemoveFile = (index) => {
736
+ setAttachedFiles((prev) => prev.filter((_, i) => i !== index));
737
+ };
738
+ const formatFileSize = (bytes) => {
739
+ if (bytes < 1024) return bytes + " B";
740
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
741
+ return (bytes / (1024 * 1024)).toFixed(1) + " MB";
742
+ };
743
+ const handleInternalSubmit = async () => {
591
744
  const state = {};
592
745
  options.forEach((group) => {
593
746
  const selectedInGroup = group.options.filter(
@@ -604,12 +757,52 @@ function Composer({
604
757
  }
605
758
  }
606
759
  });
760
+ if (attachedFiles.length > 0) {
761
+ const filePromises = attachedFiles.map((file) => {
762
+ return new Promise((resolve, reject) => {
763
+ const reader = new FileReader();
764
+ reader.onload = () => {
765
+ try {
766
+ const base64 = reader.result;
767
+ if (!base64) {
768
+ reject(new Error("FileReader returned empty result"));
769
+ return;
770
+ }
771
+ resolve({
772
+ name: file.name,
773
+ type: file.type,
774
+ size: file.size,
775
+ data: base64
776
+ });
777
+ } catch (error) {
778
+ reject(error);
779
+ }
780
+ };
781
+ reader.onerror = (error) => {
782
+ reject(new Error(`Failed to read file ${file.name}: ${error}`));
783
+ };
784
+ reader.onabort = () => {
785
+ reject(new Error(`File read aborted for ${file.name}`));
786
+ };
787
+ reader.readAsDataURL(file);
788
+ });
789
+ });
790
+ try {
791
+ const convertedFiles = await Promise.all(filePromises);
792
+ if (convertedFiles.length > 0) {
793
+ state.files = convertedFiles;
794
+ }
795
+ } catch (error) {
796
+ console.error("Failed to convert files to base64:", error);
797
+ }
798
+ }
607
799
  onSubmit(state);
800
+ setAttachedFiles([]);
608
801
  };
609
802
  const handleKeyDown = (e) => {
610
803
  if (e.key === "Enter" && !e.shiftKey) {
611
804
  e.preventDefault();
612
- handleInternalSubmit();
805
+ handleInternalSubmit().catch(console.error);
613
806
  }
614
807
  };
615
808
  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: [
@@ -625,59 +818,171 @@ function Composer({
625
818
  }
626
819
  ),
627
820
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center px-1", children: [
628
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: options.map((group) => {
629
- const selectedInGroup = group.options.filter(
630
- (o) => selectedOptions.has(o.id)
631
- );
632
- const label = selectedInGroup.length === 0 ? group.label : selectedInGroup.length === 1 ? selectedInGroup[0].label : `${group.label} (${selectedInGroup.length})`;
633
- const isSingle = group.type === "single";
634
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
821
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
822
+ enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
635
823
  /* @__PURE__ */ jsx(
636
- DropdownMenuTrigger,
824
+ "input",
637
825
  {
638
- render: /* @__PURE__ */ jsxs(
639
- Button,
640
- {
641
- variant: "ghost",
642
- size: "sm",
643
- className: cn(
644
- selectedInGroup.length > 0 ? "text-foreground bg-muted/50" : "text-muted-foreground"
645
- ),
646
- children: [
647
- label,
648
- /* @__PURE__ */ jsx(IconChevronDown, { className: "h-3 w-3 opacity-50" })
649
- ]
650
- }
651
- )
826
+ ref: fileInputRef,
827
+ type: "file",
828
+ multiple: true,
829
+ accept,
830
+ onChange: handleFileSelect,
831
+ className: "hidden",
832
+ disabled: isLoading || attachedFiles.length >= maxFiles
652
833
  }
653
834
  ),
654
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: "w-56", children: /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
655
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: group.label }),
656
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
657
- group.options.map((option) => /* @__PURE__ */ jsx(
658
- DropdownMenuCheckboxItem,
835
+ attachedFiles.length === 0 ? /* @__PURE__ */ jsx(
836
+ Button,
837
+ {
838
+ type: "button",
839
+ variant: "ghost",
840
+ size: "sm",
841
+ onClick: () => fileInputRef.current?.click(),
842
+ disabled: isLoading,
843
+ className: "text-muted-foreground",
844
+ title: "Attach file",
845
+ children: /* @__PURE__ */ jsx(IconPaperclip, { className: "h-4 w-4" })
846
+ }
847
+ ) : /* @__PURE__ */ jsxs(DropdownMenu, { children: [
848
+ /* @__PURE__ */ jsx(
849
+ DropdownMenuTrigger,
659
850
  {
660
- checked: selectedOptions.has(option.id),
661
- onCheckedChange: () => toggleOption(
662
- option.id,
663
- group.options,
664
- isSingle ? "single" : "multiple"
665
- ),
666
- onSelect: (e) => e.preventDefault(),
667
- children: option.label
668
- },
669
- option.id
670
- ))
671
- ] }) })
672
- ] }, group.id);
673
- }) }),
851
+ render: /* @__PURE__ */ jsxs(
852
+ Button,
853
+ {
854
+ type: "button",
855
+ variant: "ghost",
856
+ size: "sm",
857
+ className: "text-muted-foreground gap-2",
858
+ title: `${attachedFiles.length} files attached`,
859
+ children: [
860
+ /* @__PURE__ */ jsx(IconPaperclip, { className: "h-4 w-4" }),
861
+ /* @__PURE__ */ jsx(Badge, { className: "h-[18px] min-w-[18px] px-1.5 text-[10px]", children: attachedFiles.length })
862
+ ]
863
+ }
864
+ )
865
+ }
866
+ ),
867
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "start", className: "w-64", children: [
868
+ /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
869
+ /* @__PURE__ */ jsxs(DropdownMenuLabel, { children: [
870
+ "Attached Files (",
871
+ attachedFiles.length,
872
+ "/",
873
+ maxFiles,
874
+ ")"
875
+ ] }),
876
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
877
+ attachedFiles.map((file, index) => /* @__PURE__ */ jsxs(
878
+ DropdownMenuItem,
879
+ {
880
+ className: "flex items-center justify-between group",
881
+ onSelect: (e) => e.preventDefault(),
882
+ children: [
883
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
884
+ /* @__PURE__ */ jsx(
885
+ "span",
886
+ {
887
+ className: "truncate text-sm",
888
+ title: file.name,
889
+ children: file.name
890
+ }
891
+ ),
892
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: formatFileSize(file.size) })
893
+ ] }),
894
+ /* @__PURE__ */ jsx(
895
+ Button,
896
+ {
897
+ type: "button",
898
+ variant: "ghost",
899
+ size: "icon",
900
+ className: "h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity",
901
+ onClick: () => handleRemoveFile(index),
902
+ children: /* @__PURE__ */ jsx(IconX, { className: "h-3 w-3" })
903
+ }
904
+ )
905
+ ]
906
+ },
907
+ index
908
+ ))
909
+ ] }),
910
+ attachedFiles.length < maxFiles && /* @__PURE__ */ jsxs(Fragment, { children: [
911
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
912
+ /* @__PURE__ */ jsxs(
913
+ DropdownMenuItem,
914
+ {
915
+ onSelect: (e) => {
916
+ e.preventDefault();
917
+ fileInputRef.current?.click();
918
+ },
919
+ className: "text-primary focus:text-primary",
920
+ children: [
921
+ /* @__PURE__ */ jsx(IconPlus, { className: "mr-2 h-4 w-4" }),
922
+ /* @__PURE__ */ jsx("span", { children: "Add more files" })
923
+ ]
924
+ }
925
+ )
926
+ ] })
927
+ ] })
928
+ ] })
929
+ ] }),
930
+ options.map((group) => {
931
+ const selectedInGroup = group.options.filter(
932
+ (o) => selectedOptions.has(o.id)
933
+ );
934
+ const label = selectedInGroup.length === 0 ? group.label : selectedInGroup.length === 1 ? selectedInGroup[0].label : group.label;
935
+ const isSingle = group.type === "single";
936
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
937
+ /* @__PURE__ */ jsx(
938
+ DropdownMenuTrigger,
939
+ {
940
+ render: /* @__PURE__ */ jsxs(
941
+ Button,
942
+ {
943
+ variant: "ghost",
944
+ size: "sm",
945
+ className: cn(
946
+ "gap-2",
947
+ selectedInGroup.length > 0 ? "text-foreground bg-muted/50" : "text-muted-foreground"
948
+ ),
949
+ children: [
950
+ label,
951
+ selectedInGroup.length > 1 && /* @__PURE__ */ jsx(Badge, { className: "h-[18px] min-w-[18px] px-1.5 text-[10px]", children: selectedInGroup.length }),
952
+ /* @__PURE__ */ jsx(IconChevronDown, { className: "h-3 w-3 opacity-50" })
953
+ ]
954
+ }
955
+ )
956
+ }
957
+ ),
958
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: "w-56", children: /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
959
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: group.label }),
960
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
961
+ group.options.map((option) => /* @__PURE__ */ jsx(
962
+ DropdownMenuCheckboxItem,
963
+ {
964
+ checked: selectedOptions.has(option.id),
965
+ onCheckedChange: () => toggleOption(
966
+ option.id,
967
+ group.options,
968
+ isSingle ? "single" : "multiple"
969
+ ),
970
+ onSelect: (e) => e.preventDefault(),
971
+ children: option.label
972
+ },
973
+ option.id
974
+ ))
975
+ ] }) })
976
+ ] }, group.id);
977
+ })
978
+ ] }),
674
979
  /* @__PURE__ */ jsx(
675
980
  Button,
676
981
  {
677
982
  type: "submit",
678
- disabled: !value.trim() && !isLoading || isLoading,
983
+ disabled: !value.trim() && attachedFiles.length === 0 && !isLoading || isLoading,
679
984
  size: "icon-lg",
680
- onClick: handleInternalSubmit,
985
+ onClick: () => handleInternalSubmit().catch(console.error),
681
986
  children: isLoading ? /* @__PURE__ */ jsx(IconLoader2, { className: "h-5 w-5 animate-spin" }) : /* @__PURE__ */ jsx(IconArrowUp, { className: "h-5 w-5" })
682
987
  }
683
988
  )
@@ -814,8 +1119,6 @@ var Col = ({
814
1119
  gap = "sm",
815
1120
  align = "start",
816
1121
  justify = "start",
817
- wrap = "nowrap",
818
- flex = 1,
819
1122
  width,
820
1123
  height,
821
1124
  padding,
@@ -865,13 +1168,11 @@ var Col = ({
865
1168
  gapClasses[gap] || "gap-2",
866
1169
  alignClasses[align] || "items-start",
867
1170
  justifyClasses[justify] || "justify-start",
868
- wrap === "wrap" ? "flex-wrap" : "flex-nowrap",
869
1171
  overflow && overflowClasses[overflow],
870
1172
  position && positionClasses[position],
871
1173
  className
872
1174
  ),
873
1175
  style: {
874
- flex,
875
1176
  width,
876
1177
  height,
877
1178
  padding,
@@ -1305,45 +1606,6 @@ var Icon = ({
1305
1606
  }
1306
1607
  );
1307
1608
  };
1308
- var badgeVariants = cva(
1309
- "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",
1310
- {
1311
- variants: {
1312
- variant: {
1313
- default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
1314
- secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
1315
- 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",
1316
- outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground bg-input/30",
1317
- ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
1318
- link: "text-primary underline-offset-4 hover:underline"
1319
- }
1320
- },
1321
- defaultVariants: {
1322
- variant: "default"
1323
- }
1324
- }
1325
- );
1326
- function Badge({
1327
- className,
1328
- variant = "default",
1329
- render,
1330
- ...props
1331
- }) {
1332
- return useRender({
1333
- defaultTagName: "span",
1334
- props: mergeProps(
1335
- {
1336
- className: cn(badgeVariants({ className, variant }))
1337
- },
1338
- props
1339
- ),
1340
- render,
1341
- state: {
1342
- slot: "badge",
1343
- variant
1344
- }
1345
- });
1346
- }
1347
1609
  var Badge2 = ({
1348
1610
  label,
1349
1611
  variant = "primary",
@@ -1932,9 +2194,9 @@ var Select2 = ({
1932
2194
  defaultValue,
1933
2195
  value,
1934
2196
  disabled,
1935
- onValueChange: handleValueChange,
2197
+ onValueChange: (value2) => handleValueChange(value2 || ""),
1936
2198
  children: [
1937
- /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: placeholder || "Select an option" }) }),
2199
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
1938
2200
  /* @__PURE__ */ jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
1939
2201
  ]
1940
2202
  }
@@ -1964,7 +2226,6 @@ var Label2 = ({
1964
2226
  var Checkbox = ({
1965
2227
  label,
1966
2228
  name,
1967
- value = "on",
1968
2229
  checked,
1969
2230
  defaultChecked,
1970
2231
  disabled,
@@ -1979,46 +2240,37 @@ var Checkbox = ({
1979
2240
  ...onChangeAction,
1980
2241
  data: {
1981
2242
  name: name || "",
1982
- value,
1983
2243
  checked: e.target.checked
1984
2244
  }
1985
2245
  });
1986
2246
  }
1987
2247
  };
1988
- return /* @__PURE__ */ jsxs(
1989
- "div",
1990
- {
1991
- className: cn("flex items-center gap-2", className),
1992
- style,
1993
- children: [
1994
- /* @__PURE__ */ jsx(
1995
- "input",
1996
- {
1997
- type: "checkbox",
1998
- name,
1999
- id: name,
2000
- value,
2001
- checked,
2002
- defaultChecked,
2003
- disabled,
2004
- onChange: handleChange,
2005
- className: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary disabled:cursor-not-allowed disabled:opacity-50"
2006
- }
2007
- ),
2008
- label && /* @__PURE__ */ jsx(
2009
- Label2,
2010
- {
2011
- htmlFor: name,
2012
- value: label,
2013
- className: cn(
2014
- "cursor-pointer select-none text-sm font-medium leading-none",
2015
- disabled && "cursor-not-allowed opacity-50"
2016
- )
2017
- }
2248
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", className), style, children: [
2249
+ /* @__PURE__ */ jsx(
2250
+ "input",
2251
+ {
2252
+ type: "checkbox",
2253
+ name,
2254
+ id: name,
2255
+ checked,
2256
+ defaultChecked,
2257
+ disabled,
2258
+ onChange: handleChange,
2259
+ className: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary disabled:cursor-not-allowed disabled:opacity-50"
2260
+ }
2261
+ ),
2262
+ label && /* @__PURE__ */ jsx(
2263
+ Label2,
2264
+ {
2265
+ htmlFor: name,
2266
+ value: label,
2267
+ className: cn(
2268
+ "cursor-pointer select-none text-sm font-medium leading-none",
2269
+ disabled && "cursor-not-allowed opacity-50"
2018
2270
  )
2019
- ]
2020
- }
2021
- );
2271
+ }
2272
+ )
2273
+ ] });
2022
2274
  };
2023
2275
  var RadioGroup = ({
2024
2276
  name,
@@ -2341,12 +2593,14 @@ function Thread({
2341
2593
  });
2342
2594
  const starterPrompts = localStarterPrompts ?? config?.starterPrompts;
2343
2595
  const options = localOptions ?? config?.options;
2596
+ const fileAttachments = config?.fileAttachments;
2344
2597
  const allDefaultSelectedIds = useMemo(() => {
2345
- const defaultSelectedIdsFromOptions = options?.flatMap(
2346
- (group) => group.defaultSelectedIds ?? []
2347
- ) ?? [];
2598
+ const defaultSelectedIdsFromOptions = options?.flatMap((group) => group.defaultSelectedIds ?? []) ?? [];
2348
2599
  return [
2349
- .../* @__PURE__ */ new Set([...defaultSelectedIdsFromOptions, ...defaultSelectedIds ?? []])
2600
+ .../* @__PURE__ */ new Set([
2601
+ ...defaultSelectedIdsFromOptions,
2602
+ ...defaultSelectedIds ?? []
2603
+ ])
2350
2604
  ];
2351
2605
  }, [options, defaultSelectedIds]);
2352
2606
  const [input, setInput] = useState("");
@@ -2356,13 +2610,14 @@ function Thread({
2356
2610
  }, [messages]);
2357
2611
  const handleSubmit = async (state, overrideInput) => {
2358
2612
  const text = (overrideInput ?? input).trim();
2359
- if (!text || isLoading) return;
2613
+ const hasFiles = state?.files && Array.isArray(state.files) && state.files.length > 0;
2614
+ if (!text && !hasFiles || isLoading) return;
2360
2615
  if (!overrideInput) setInput("");
2361
2616
  await sendEvent(
2362
2617
  {
2363
2618
  type: "text",
2364
2619
  role: "user",
2365
- data: { content: text }
2620
+ data: { content: text || "" }
2366
2621
  },
2367
2622
  { state: { ...state, threadId: activeThreadId ?? void 0 } }
2368
2623
  );
@@ -2420,7 +2675,8 @@ function Thread({
2420
2675
  isLoading,
2421
2676
  options,
2422
2677
  autoFocus,
2423
- defaultSelectedIds: allDefaultSelectedIds
2678
+ defaultSelectedIds: allDefaultSelectedIds,
2679
+ fileAttachments
2424
2680
  }
2425
2681
  ) }) })
2426
2682
  ]
@@ -2428,20 +2684,36 @@ function Thread({
2428
2684
  );
2429
2685
  }
2430
2686
  function ChatHeader({
2431
- title,
2432
2687
  leftContent,
2433
2688
  rightContent,
2434
2689
  className,
2435
- titleClassName,
2436
2690
  children
2437
2691
  }) {
2438
2692
  if (children) {
2439
- return /* @__PURE__ */ jsx("div", { className: cn("p-4 border-b border-border h-14 flex items-center shrink-0", className), children });
2693
+ return /* @__PURE__ */ jsx(
2694
+ "div",
2695
+ {
2696
+ className: cn(
2697
+ "px-2 border-b border-border h-14 flex items-center shrink-0",
2698
+ className
2699
+ ),
2700
+ children
2701
+ }
2702
+ );
2440
2703
  }
2441
- return /* @__PURE__ */ jsxs("div", { className: cn("p-4 border-b border-border h-14 flex items-center justify-between shrink-0", className), children: [
2442
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: leftContent }),
2443
- rightContent && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 shrink-0 ml-2", children: rightContent })
2444
- ] });
2704
+ return /* @__PURE__ */ jsxs(
2705
+ "div",
2706
+ {
2707
+ className: cn(
2708
+ "px-2 border-b border-border h-14 flex items-center justify-between shrink-0",
2709
+ className
2710
+ ),
2711
+ children: [
2712
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: leftContent }),
2713
+ rightContent && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 shrink-0 ml-2", children: rightContent })
2714
+ ]
2715
+ }
2716
+ );
2445
2717
  }
2446
2718
  var ThreadList = ({
2447
2719
  className,
@@ -2492,58 +2764,43 @@ var ThreadList = ({
2492
2764
  if (diffDays < 7) return `${diffDays}d ago`;
2493
2765
  return d.toLocaleDateString();
2494
2766
  };
2495
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full", className), children: [
2496
- /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsxs(
2497
- Button,
2767
+ return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col h-full", className), children: /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: isLoading && threads.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx(IconLoader2, { className: "size-5 animate-spin text-muted-foreground" }) }) : threads.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center text-muted-foreground", children: emptyState || /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2768
+ /* @__PURE__ */ jsx(IconMessage, { className: "size-8 mx-auto opacity-50" }),
2769
+ /* @__PURE__ */ jsx("p", { className: "text-sm", children: "No threads yet" }),
2770
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: handleNewThread, children: "Start a conversation" })
2771
+ ] }) }) : /* @__PURE__ */ jsx("div", { className: "p-2 space-y-1", children: threads.map((thread) => {
2772
+ const isActive = thread.id === activeThreadId;
2773
+ return /* @__PURE__ */ jsxs(
2774
+ "div",
2498
2775
  {
2499
- variant: "ghost",
2500
- size: "sm",
2501
- onClick: handleNewThread,
2502
- className: "w-full justify-start",
2776
+ onClick: () => handleThreadClick(thread.id),
2777
+ className: cn(
2778
+ "group relative flex items-center gap-3 px-3 py-1.5 rounded-lg cursor-pointer transition-colors",
2779
+ isActive ? "bg-muted" : "hover:bg-muted"
2780
+ ),
2503
2781
  children: [
2504
- /* @__PURE__ */ jsx(IconPlus, { className: "mr-2 size-4" }),
2505
- "New Thread"
2782
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2783
+ /* @__PURE__ */ jsx("p", { className: cn("text-sm font-medium truncate"), children: thread.title || `Thread ${thread.id.slice(0, 8)}` }),
2784
+ thread.updatedAt && /* @__PURE__ */ jsx("span", { className: cn("text-xs shrink-0"), children: formatDate(thread.updatedAt) })
2785
+ ] }) }),
2786
+ /* @__PURE__ */ jsx(
2787
+ Button,
2788
+ {
2789
+ variant: "ghost",
2790
+ size: "icon-xs",
2791
+ onClick: (e) => handleDelete(e, thread.id),
2792
+ className: cn(
2793
+ "opacity-0 group-hover:opacity-100 transition-opacity shrink-0",
2794
+ isActive && "hover:bg-primary-foreground/20"
2795
+ ),
2796
+ children: /* @__PURE__ */ jsx(IconTrash, { className: "size-3" })
2797
+ }
2798
+ )
2506
2799
  ]
2507
- }
2508
- ) }),
2509
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: isLoading && threads.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx(IconLoader2, { className: "size-5 animate-spin text-muted-foreground" }) }) : threads.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center text-muted-foreground", children: emptyState || /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2510
- /* @__PURE__ */ jsx(IconMessage, { className: "size-8 mx-auto opacity-50" }),
2511
- /* @__PURE__ */ jsx("p", { className: "text-sm", children: "No threads yet" }),
2512
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: handleNewThread, children: "Start a conversation" })
2513
- ] }) }) : /* @__PURE__ */ jsx("div", { className: "p-2 space-y-1", children: threads.map((thread) => {
2514
- const isActive = thread.id === activeThreadId;
2515
- return /* @__PURE__ */ jsxs(
2516
- "div",
2517
- {
2518
- onClick: () => handleThreadClick(thread.id),
2519
- className: cn(
2520
- "group relative flex items-center gap-3 px-3 py-1.5 rounded-lg cursor-pointer transition-colors",
2521
- isActive ? "bg-muted" : "hover:bg-muted"
2522
- ),
2523
- children: [
2524
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2525
- /* @__PURE__ */ jsx("p", { className: cn("text-sm font-medium truncate"), children: thread.title || `Thread ${thread.id.slice(0, 8)}` }),
2526
- thread.updatedAt && /* @__PURE__ */ jsx("span", { className: cn("text-xs shrink-0"), children: formatDate(thread.updatedAt) })
2527
- ] }) }),
2528
- /* @__PURE__ */ jsx(
2529
- Button,
2530
- {
2531
- variant: "ghost",
2532
- size: "icon-xs",
2533
- onClick: (e) => handleDelete(e, thread.id),
2534
- className: cn(
2535
- "opacity-0 group-hover:opacity-100 transition-opacity shrink-0",
2536
- isActive && "hover:bg-primary-foreground/20"
2537
- ),
2538
- children: /* @__PURE__ */ jsx(IconTrash, { className: "size-3" })
2539
- }
2540
- )
2541
- ]
2542
- },
2543
- thread.id
2544
- );
2545
- }) }) })
2546
- ] });
2800
+ },
2801
+ thread.id
2802
+ );
2803
+ }) }) }) });
2547
2804
  };
2548
2805
  function ChatPopup({
2549
2806
  title = "Chat",
@@ -2672,6 +2929,16 @@ function ChatSidebar({
2672
2929
  ) })
2673
2930
  ] });
2674
2931
  }
2932
+ var ChatSidebarContext = createContext(
2933
+ void 0
2934
+ );
2935
+ function useChatSidebar() {
2936
+ const context = useContext(ChatSidebarContext);
2937
+ if (context === void 0) {
2938
+ throw new Error("useChatSidebar must be used within a ChatSidebarProvider");
2939
+ }
2940
+ return context;
2941
+ }
2675
2942
  function ChatFull({
2676
2943
  title = "Chat",
2677
2944
  placeholder = "Message the AI",
@@ -2683,134 +2950,134 @@ function ChatFull({
2683
2950
  rightSidebar,
2684
2951
  leftSidebarClassName,
2685
2952
  rightSidebarClassName,
2686
- leftSidebarCollapsible = false,
2687
- rightSidebarCollapsible = false,
2688
- defaultLeftSidebarCollapsed = false,
2689
- defaultRightSidebarCollapsed = false,
2690
- leftSidebarCollapsed: controlledLeftCollapsed,
2691
- rightSidebarCollapsed: controlledRightCollapsed,
2692
- onLeftSidebarCollapseChange,
2693
- onRightSidebarCollapseChange,
2694
2953
  autoFocus = false,
2695
2954
  defaultSelectedIds
2696
2955
  }) {
2697
- const [internalLeftCollapsed, setInternalLeftCollapsed] = useState(
2698
- defaultLeftSidebarCollapsed
2699
- );
2700
- const [internalRightCollapsed, setInternalRightCollapsed] = useState(
2701
- defaultRightSidebarCollapsed
2702
- );
2703
- const leftCollapsed = controlledLeftCollapsed !== void 0 ? controlledLeftCollapsed : internalLeftCollapsed;
2704
- const rightCollapsed = controlledRightCollapsed !== void 0 ? controlledRightCollapsed : internalRightCollapsed;
2705
- const handleLeftToggle = () => {
2706
- const newCollapsed = !leftCollapsed;
2707
- if (controlledLeftCollapsed === void 0) {
2708
- setInternalLeftCollapsed(newCollapsed);
2956
+ const { isMobile, isTablet } = useScreenSize();
2957
+ const isSmallScreen = isMobile || isTablet;
2958
+ const [internalLeftCollapsed, setInternalLeftCollapsed] = useState(() => {
2959
+ if (typeof window !== "undefined") {
2960
+ return window.innerWidth < 1024;
2709
2961
  }
2710
- onLeftSidebarCollapseChange?.(newCollapsed);
2711
- };
2712
- const handleRightToggle = () => {
2713
- const newCollapsed = !rightCollapsed;
2714
- if (controlledRightCollapsed === void 0) {
2715
- setInternalRightCollapsed(newCollapsed);
2962
+ return false;
2963
+ });
2964
+ const [internalRightCollapsed, setInternalRightCollapsed] = useState(() => {
2965
+ if (typeof window !== "undefined") {
2966
+ return window.innerWidth < 1024;
2716
2967
  }
2717
- onRightSidebarCollapseChange?.(newCollapsed);
2718
- };
2719
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full w-full bg-background", className), children: [
2720
- title && /* @__PURE__ */ jsx(ChatHeader, { title, ...headerProps }),
2721
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden flex relative", children: [
2722
- leftSidebar && /* @__PURE__ */ jsxs(Fragment, { children: [
2723
- /* @__PURE__ */ jsx(
2724
- "div",
2725
- {
2726
- className: cn(
2727
- "flex-shrink-0 border-r border-border bg-background transition-all duration-300 ease-in-out overflow-hidden flex flex-col",
2728
- leftCollapsed && leftSidebarCollapsible ? "w-0 border-r-0 min-w-0" : "",
2729
- !leftCollapsed && leftSidebarClassName
2730
- ),
2731
- children: !leftCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
2732
- leftSidebarCollapsible && /* @__PURE__ */ jsx("div", { className: "flex justify-end p-2 border-b border-border shrink-0", children: /* @__PURE__ */ jsx(
2733
- Button,
2734
- {
2735
- variant: "ghost",
2736
- size: "icon-sm",
2737
- onClick: handleLeftToggle,
2738
- "aria-label": "Collapse left sidebar",
2739
- className: "h-8 w-8",
2740
- children: /* @__PURE__ */ jsx(IconChevronLeft, { className: "h-4 w-4" })
2741
- }
2742
- ) }),
2743
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-h-0", children: leftSidebar })
2744
- ] })
2745
- }
2746
- ),
2747
- 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(
2748
- Button,
2749
- {
2750
- variant: "ghost",
2751
- size: "icon-sm",
2752
- onClick: handleLeftToggle,
2753
- "aria-label": "Expand left sidebar",
2754
- className: "h-8 w-8",
2755
- children: /* @__PURE__ */ jsx(IconChevronRight, { className: "h-4 w-4" })
2756
- }
2757
- ) })
2758
- ] }),
2759
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-w-0", children: /* @__PURE__ */ jsx(
2760
- Thread,
2761
- {
2762
- placeholder,
2763
- starterPrompts,
2764
- options,
2765
- autoFocus,
2766
- defaultSelectedIds
2767
- }
2768
- ) }),
2769
- rightSidebar && /* @__PURE__ */ jsxs(Fragment, { children: [
2770
- 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(
2771
- Button,
2772
- {
2773
- variant: "ghost",
2774
- size: "icon-sm",
2775
- onClick: handleRightToggle,
2776
- "aria-label": "Expand right sidebar",
2777
- className: "h-8 w-8",
2778
- children: /* @__PURE__ */ jsx(IconChevronLeft, { className: "h-4 w-4" })
2779
- }
2780
- ) }),
2781
- /* @__PURE__ */ jsx(
2782
- "div",
2783
- {
2784
- className: cn(
2785
- "flex-shrink-0 border-l border-border bg-background transition-all duration-300 ease-in-out overflow-hidden flex flex-col",
2786
- rightCollapsed && rightSidebarCollapsible ? "w-0 border-l-0 min-w-0" : "",
2787
- !rightCollapsed && rightSidebarClassName
2788
- ),
2789
- children: !rightCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
2790
- rightSidebarCollapsible && /* @__PURE__ */ jsx("div", { className: "flex justify-start p-2 border-b border-border shrink-0", children: /* @__PURE__ */ jsx(
2791
- Button,
2792
- {
2793
- variant: "ghost",
2794
- size: "icon-sm",
2795
- onClick: handleRightToggle,
2796
- "aria-label": "Collapse right sidebar",
2797
- className: "h-8 w-8",
2798
- children: /* @__PURE__ */ jsx(IconChevronRight, { className: "h-4 w-4" })
2799
- }
2800
- ) }),
2801
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-h-0", children: rightSidebar })
2802
- ] })
2803
- }
2804
- )
2805
- ] })
2806
- ] })
2807
- ] });
2968
+ return false;
2969
+ });
2970
+ useEffect(() => {
2971
+ if (isSmallScreen) {
2972
+ setInternalLeftCollapsed(true);
2973
+ setInternalRightCollapsed(true);
2974
+ }
2975
+ }, [isSmallScreen]);
2976
+ const leftCollapsed = internalLeftCollapsed;
2977
+ const rightCollapsed = internalRightCollapsed;
2978
+ const handleLeftToggle = useCallback((collapsed) => {
2979
+ setInternalLeftCollapsed(collapsed);
2980
+ }, []);
2981
+ const handleRightToggle = useCallback((collapsed) => {
2982
+ setInternalRightCollapsed(collapsed);
2983
+ }, []);
2984
+ const contextValue = useMemo(
2985
+ () => ({
2986
+ leftCollapsed,
2987
+ rightCollapsed,
2988
+ setLeftCollapsed: handleLeftToggle,
2989
+ setRightCollapsed: handleRightToggle,
2990
+ leftCollapsible: true,
2991
+ rightCollapsible: true
2992
+ }),
2993
+ [leftCollapsed, rightCollapsed, handleLeftToggle, handleRightToggle]
2994
+ );
2995
+ return /* @__PURE__ */ jsx(ChatSidebarContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs(
2996
+ "div",
2997
+ {
2998
+ className: cn("flex flex-col h-full w-full bg-background", className),
2999
+ children: [
3000
+ title && /* @__PURE__ */ jsx(ChatHeader, { title, ...headerProps }),
3001
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden flex relative", children: [
3002
+ leftSidebar && /* @__PURE__ */ jsx(
3003
+ "div",
3004
+ {
3005
+ className: cn(
3006
+ "flex-shrink-0 border-r border-border bg-background transition-all duration-300 ease-in-out overflow-hidden flex flex-col",
3007
+ leftCollapsed ? "w-0 border-r-0 min-w-0" : "",
3008
+ !leftCollapsed && leftSidebarClassName
3009
+ ),
3010
+ children: /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-h-0", children: leftSidebar })
3011
+ }
3012
+ ),
3013
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-w-0", children: /* @__PURE__ */ jsx(
3014
+ Thread,
3015
+ {
3016
+ placeholder,
3017
+ starterPrompts,
3018
+ options,
3019
+ autoFocus,
3020
+ defaultSelectedIds
3021
+ }
3022
+ ) }),
3023
+ rightSidebar && /* @__PURE__ */ jsx(
3024
+ "div",
3025
+ {
3026
+ className: cn(
3027
+ "flex-shrink-0 border-l border-border bg-background transition-all duration-300 ease-in-out overflow-hidden flex flex-col",
3028
+ rightCollapsed ? "w-0 border-l-0 min-w-0" : "",
3029
+ !rightCollapsed && rightSidebarClassName
3030
+ ),
3031
+ children: /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-h-0", children: rightSidebar })
3032
+ }
3033
+ )
3034
+ ] })
3035
+ ]
3036
+ }
3037
+ ) });
3038
+ }
3039
+ function SidebarToggle({ side, className }) {
3040
+ const {
3041
+ leftCollapsed,
3042
+ rightCollapsed,
3043
+ setLeftCollapsed,
3044
+ setRightCollapsed,
3045
+ leftCollapsible,
3046
+ rightCollapsible
3047
+ } = useChatSidebar();
3048
+ if (side === "left") {
3049
+ if (!leftCollapsible) return null;
3050
+ return /* @__PURE__ */ jsx(
3051
+ Button,
3052
+ {
3053
+ variant: "ghost",
3054
+ onClick: () => setLeftCollapsed(!leftCollapsed),
3055
+ "aria-label": leftCollapsed ? "Expand left sidebar" : "Collapse left sidebar",
3056
+ className: cn("", className),
3057
+ children: leftCollapsed ? /* @__PURE__ */ jsx(IconLayoutSidebarLeftExpand, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(IconLayoutSidebarLeftCollapse, { className: "h-4 w-4" })
3058
+ }
3059
+ );
3060
+ }
3061
+ if (side === "right") {
3062
+ if (!rightCollapsible) return null;
3063
+ return /* @__PURE__ */ jsx(
3064
+ Button,
3065
+ {
3066
+ variant: "ghost",
3067
+ onClick: () => setRightCollapsed(!rightCollapsed),
3068
+ "aria-label": rightCollapsed ? "Expand right sidebar" : "Collapse right sidebar",
3069
+ className: cn("", className),
3070
+ children: rightCollapsed ? /* @__PURE__ */ jsx(IconLayoutSidebarRightExpand, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(IconLayoutSidebarRightCollapse, { className: "h-4 w-4" })
3071
+ }
3072
+ );
3073
+ }
3074
+ return null;
2808
3075
  }
2809
- var PopoverContext = React10.createContext(
3076
+ var PopoverContext = React11.createContext(
2810
3077
  void 0
2811
3078
  );
2812
3079
  function usePopoverContext() {
2813
- const context = React10.useContext(PopoverContext);
3080
+ const context = React11.useContext(PopoverContext);
2814
3081
  if (!context) {
2815
3082
  throw new Error("Popover components must be used within a Popover");
2816
3083
  }
@@ -2822,10 +3089,10 @@ function Popover({
2822
3089
  open: controlledOpen,
2823
3090
  onOpenChange
2824
3091
  }) {
2825
- const [internalOpen, setInternalOpen] = React10.useState(defaultOpen);
2826
- const triggerRef = React10.useRef(null);
3092
+ const [internalOpen, setInternalOpen] = React11.useState(defaultOpen);
3093
+ const triggerRef = React11.useRef(null);
2827
3094
  const open = controlledOpen ?? internalOpen;
2828
- const setOpen = React10.useCallback(
3095
+ const setOpen = React11.useCallback(
2829
3096
  (newOpen) => {
2830
3097
  if (controlledOpen === void 0) {
2831
3098
  setInternalOpen(newOpen);
@@ -2834,7 +3101,7 @@ function Popover({
2834
3101
  },
2835
3102
  [controlledOpen, onOpenChange]
2836
3103
  );
2837
- const value = React10.useMemo(
3104
+ const value = React11.useMemo(
2838
3105
  () => ({
2839
3106
  open,
2840
3107
  setOpen,
@@ -2844,15 +3111,15 @@ function Popover({
2844
3111
  );
2845
3112
  return /* @__PURE__ */ jsx(PopoverContext.Provider, { value, children });
2846
3113
  }
2847
- var PopoverTrigger = React10.forwardRef(
3114
+ var PopoverTrigger = React11.forwardRef(
2848
3115
  ({ asChild, className, children, ...props }, ref) => {
2849
3116
  const { setOpen, triggerRef } = usePopoverContext();
2850
3117
  const handleClick = (e) => {
2851
3118
  setOpen(true);
2852
3119
  props.onClick?.(e);
2853
3120
  };
2854
- if (asChild && React10.isValidElement(children)) {
2855
- return React10.cloneElement(children, {
3121
+ if (asChild && React11.isValidElement(children)) {
3122
+ return React11.cloneElement(children, {
2856
3123
  ref: (node) => {
2857
3124
  triggerRef.current = node;
2858
3125
  if (typeof children.ref === "function") {
@@ -2884,7 +3151,7 @@ var PopoverTrigger = React10.forwardRef(
2884
3151
  }
2885
3152
  );
2886
3153
  PopoverTrigger.displayName = "PopoverTrigger";
2887
- var PopoverContent = React10.forwardRef(
3154
+ var PopoverContent = React11.forwardRef(
2888
3155
  ({
2889
3156
  className,
2890
3157
  side = "bottom",
@@ -2895,9 +3162,9 @@ var PopoverContent = React10.forwardRef(
2895
3162
  ...props
2896
3163
  }, ref) => {
2897
3164
  const { open, setOpen, triggerRef } = usePopoverContext();
2898
- const [position, setPosition] = React10.useState({ top: 0, left: 0 });
2899
- const contentRef = React10.useRef(null);
2900
- React10.useEffect(() => {
3165
+ const [position, setPosition] = React11.useState({ top: 0, left: 0 });
3166
+ const contentRef = React11.useRef(null);
3167
+ React11.useEffect(() => {
2901
3168
  if (!open || !triggerRef.current) return;
2902
3169
  const updatePosition = () => {
2903
3170
  if (!triggerRef.current || !contentRef.current) return;
@@ -2958,7 +3225,7 @@ var PopoverContent = React10.forwardRef(
2958
3225
  window.removeEventListener("scroll", updatePosition, true);
2959
3226
  };
2960
3227
  }, [open, side, align, sideOffset, alignOffset, triggerRef]);
2961
- React10.useEffect(() => {
3228
+ React11.useEffect(() => {
2962
3229
  if (!open) return;
2963
3230
  const handleClickOutside = (event) => {
2964
3231
  if (contentRef.current && !contentRef.current.contains(event.target) && triggerRef.current && !triggerRef.current.contains(event.target)) {
@@ -3014,7 +3281,7 @@ var ThreadPopover = ({
3014
3281
  emptyState,
3015
3282
  onThreadSelect
3016
3283
  }) => {
3017
- const [isOpen, setIsOpen] = React10.useState(false);
3284
+ const [isOpen, setIsOpen] = React11.useState(false);
3018
3285
  useHotkeys(
3019
3286
  "h",
3020
3287
  (e) => {
@@ -3069,7 +3336,7 @@ var CreateThreadButton = ({
3069
3336
  onThreadCreated
3070
3337
  }) => {
3071
3338
  const { createThread } = useThreads();
3072
- const [isCreating, setIsCreating] = React10.useState(false);
3339
+ const [isCreating, setIsCreating] = React11.useState(false);
3073
3340
  const handleCreateThread = async () => {
3074
3341
  if (isCreating) return;
3075
3342
  try {
@@ -3195,10 +3462,10 @@ var AccountDialog = ({
3195
3462
  size
3196
3463
  }) => {
3197
3464
  const { isLoading, isAuthenticated, user, login, logout } = useAuth();
3198
- const [open, setOpen] = React10.useState(false);
3199
- const [accountInfoOpen, setAccountInfoOpen] = React10.useState(false);
3200
- const [error, setError] = React10.useState(null);
3201
- const initials = React10.useMemo(() => {
3465
+ const [open, setOpen] = React11.useState(false);
3466
+ const [accountInfoOpen, setAccountInfoOpen] = React11.useState(false);
3467
+ const [error, setError] = React11.useState(null);
3468
+ const initials = React11.useMemo(() => {
3202
3469
  const name = user?.displayName || user?.name;
3203
3470
  if (!name) return "";
3204
3471
  return name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
@@ -3371,6 +3638,6 @@ function ThemeToggle() {
3371
3638
  );
3372
3639
  }
3373
3640
 
3374
- 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 };
3641
+ export { AccountDialog, AuthContext, AuthProvider, Badge2 as Badge, Box, Button2 as Button, Card2 as Card, Chart, ChatFull, ChatHeader, ChatPopup, ChatSidebar, ChatSidebarContext, Checkbox, Col, Composer, CreateThreadButton, Divider, Form, Heading, Image, Input2 as Input, Label2 as Label, List, ListItem, MelonyClientProvider, MelonyContext, RadioGroup, Row, Select2 as Select, SidebarToggle, Spacer, Text, Textarea2 as Textarea, ThemeProvider, ThemeToggle, Thread, ThreadContext, ThreadList, ThreadPopover, ThreadProvider, UIRenderer, groupEventsToMessages, useAuth, useChatSidebar, useMelony, useScreenSize, useTheme, useThreads };
3375
3642
  //# sourceMappingURL=index.js.map
3376
3643
  //# sourceMappingURL=index.js.map