@melony/react 0.1.22 → 0.1.24
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.cjs +210 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -6
- package/dist/index.d.ts +13 -6
- package/dist/index.js +211 -70
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React$1 from 'react';
|
|
2
2
|
import React__default, { ReactNode } from 'react';
|
|
3
3
|
import { ClientState, MelonyClient } from 'melony/client';
|
|
4
|
-
import { Role, Event, UINode } from 'melony';
|
|
4
|
+
import { Role, Event, Config, UINode } from 'melony';
|
|
5
5
|
import { QueryClient } from '@tanstack/react-query';
|
|
6
6
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
7
7
|
|
|
@@ -42,6 +42,7 @@ interface ComposerOptionGroup {
|
|
|
42
42
|
label: string;
|
|
43
43
|
options: ComposerOption[];
|
|
44
44
|
type?: "single" | "multiple";
|
|
45
|
+
defaultSelectedIds?: string[];
|
|
45
46
|
}
|
|
46
47
|
interface AuthService {
|
|
47
48
|
getMe: () => Promise<User | null>;
|
|
@@ -64,10 +65,7 @@ interface MelonyContextValue extends ClientState {
|
|
|
64
65
|
}) => Promise<void>;
|
|
65
66
|
reset: (events?: Event[]) => void;
|
|
66
67
|
client: MelonyClient;
|
|
67
|
-
config?:
|
|
68
|
-
starterPrompts: any[];
|
|
69
|
-
options: any[];
|
|
70
|
-
};
|
|
68
|
+
config?: Config;
|
|
71
69
|
}
|
|
72
70
|
declare const MelonyContext: React__default.Context<MelonyContextValue | undefined>;
|
|
73
71
|
interface MelonyClientProviderProps {
|
|
@@ -155,8 +153,17 @@ interface ComposerProps {
|
|
|
155
153
|
options?: ComposerOptionGroup[];
|
|
156
154
|
autoFocus?: boolean;
|
|
157
155
|
defaultSelectedIds?: string[];
|
|
156
|
+
fileAttachments?: {
|
|
157
|
+
enabled?: boolean;
|
|
158
|
+
accept?: string;
|
|
159
|
+
maxFiles?: number;
|
|
160
|
+
maxFileSize?: number;
|
|
161
|
+
};
|
|
162
|
+
accept?: string;
|
|
163
|
+
maxFiles?: number;
|
|
164
|
+
maxFileSize?: number;
|
|
158
165
|
}
|
|
159
|
-
declare function Composer({ value, onChange, onSubmit, placeholder, isLoading, className, options, autoFocus, defaultSelectedIds, }: ComposerProps): react_jsx_runtime.JSX.Element;
|
|
166
|
+
declare function Composer({ value, onChange, onSubmit, placeholder, isLoading, className, options, autoFocus, defaultSelectedIds, fileAttachments, accept: legacyAccept, maxFiles: legacyMaxFiles, maxFileSize: legacyMaxFileSize, }: ComposerProps): react_jsx_runtime.JSX.Element;
|
|
160
167
|
|
|
161
168
|
interface ChatHeaderProps {
|
|
162
169
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React$1 from 'react';
|
|
2
2
|
import React__default, { ReactNode } from 'react';
|
|
3
3
|
import { ClientState, MelonyClient } from 'melony/client';
|
|
4
|
-
import { Role, Event, UINode } from 'melony';
|
|
4
|
+
import { Role, Event, Config, UINode } from 'melony';
|
|
5
5
|
import { QueryClient } from '@tanstack/react-query';
|
|
6
6
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
7
7
|
|
|
@@ -42,6 +42,7 @@ interface ComposerOptionGroup {
|
|
|
42
42
|
label: string;
|
|
43
43
|
options: ComposerOption[];
|
|
44
44
|
type?: "single" | "multiple";
|
|
45
|
+
defaultSelectedIds?: string[];
|
|
45
46
|
}
|
|
46
47
|
interface AuthService {
|
|
47
48
|
getMe: () => Promise<User | null>;
|
|
@@ -64,10 +65,7 @@ interface MelonyContextValue extends ClientState {
|
|
|
64
65
|
}) => Promise<void>;
|
|
65
66
|
reset: (events?: Event[]) => void;
|
|
66
67
|
client: MelonyClient;
|
|
67
|
-
config?:
|
|
68
|
-
starterPrompts: any[];
|
|
69
|
-
options: any[];
|
|
70
|
-
};
|
|
68
|
+
config?: Config;
|
|
71
69
|
}
|
|
72
70
|
declare const MelonyContext: React__default.Context<MelonyContextValue | undefined>;
|
|
73
71
|
interface MelonyClientProviderProps {
|
|
@@ -155,8 +153,17 @@ interface ComposerProps {
|
|
|
155
153
|
options?: ComposerOptionGroup[];
|
|
156
154
|
autoFocus?: boolean;
|
|
157
155
|
defaultSelectedIds?: string[];
|
|
156
|
+
fileAttachments?: {
|
|
157
|
+
enabled?: boolean;
|
|
158
|
+
accept?: string;
|
|
159
|
+
maxFiles?: number;
|
|
160
|
+
maxFileSize?: number;
|
|
161
|
+
};
|
|
162
|
+
accept?: string;
|
|
163
|
+
maxFiles?: number;
|
|
164
|
+
maxFileSize?: number;
|
|
158
165
|
}
|
|
159
|
-
declare function Composer({ value, onChange, onSubmit, placeholder, isLoading, className, options, autoFocus, defaultSelectedIds, }: ComposerProps): react_jsx_runtime.JSX.Element;
|
|
166
|
+
declare function Composer({ value, onChange, onSubmit, placeholder, isLoading, className, options, autoFocus, defaultSelectedIds, fileAttachments, accept: legacyAccept, maxFiles: legacyMaxFiles, maxFileSize: legacyMaxFileSize, }: ComposerProps): react_jsx_runtime.JSX.Element;
|
|
160
167
|
|
|
161
168
|
interface ChatHeaderProps {
|
|
162
169
|
/**
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ 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
12
|
import * as ICONS from '@tabler/icons-react';
|
|
13
|
-
import { IconChevronDown, IconLoader2, IconArrowUp, IconPlus, IconMessage, IconTrash, IconHistory,
|
|
13
|
+
import { IconX, IconPaperclip, IconChevronDown, IconLoader2, IconArrowUp, IconPlus, IconMessage, IconTrash, IconHistory, IconArrowLeft, IconChevronLeft, IconChevronRight, IconUser, IconLogout, IconBrandGoogle, IconDeviceDesktop, IconMoon, IconSun, IconCheck, IconChevronUp, IconSelector } from '@tabler/icons-react';
|
|
14
14
|
import { Menu } from '@base-ui/react/menu';
|
|
15
15
|
import { Separator as Separator$1 } from '@base-ui/react/separator';
|
|
16
16
|
import { Dialog as Dialog$1 } from '@base-ui/react/dialog';
|
|
@@ -563,11 +563,22 @@ function Composer({
|
|
|
563
563
|
className,
|
|
564
564
|
options = [],
|
|
565
565
|
autoFocus = false,
|
|
566
|
-
defaultSelectedIds = []
|
|
566
|
+
defaultSelectedIds = [],
|
|
567
|
+
fileAttachments,
|
|
568
|
+
// Legacy props for backward compatibility
|
|
569
|
+
accept: legacyAccept,
|
|
570
|
+
maxFiles: legacyMaxFiles,
|
|
571
|
+
maxFileSize: legacyMaxFileSize
|
|
567
572
|
}) {
|
|
573
|
+
const enabled = fileAttachments?.enabled !== false;
|
|
574
|
+
const accept = fileAttachments?.accept ?? legacyAccept;
|
|
575
|
+
const maxFiles = fileAttachments?.maxFiles ?? legacyMaxFiles ?? 10;
|
|
576
|
+
const maxFileSize = fileAttachments?.maxFileSize ?? legacyMaxFileSize ?? 10 * 1024 * 1024;
|
|
568
577
|
const [selectedOptions, setSelectedOptions] = React10__default.useState(
|
|
569
578
|
() => new Set(defaultSelectedIds)
|
|
570
579
|
);
|
|
580
|
+
const [attachedFiles, setAttachedFiles] = React10__default.useState([]);
|
|
581
|
+
const fileInputRef = React10__default.useRef(null);
|
|
571
582
|
const toggleOption = (id, groupOptions, type = "multiple") => {
|
|
572
583
|
const next = new Set(selectedOptions);
|
|
573
584
|
if (type === "single") {
|
|
@@ -587,7 +598,34 @@ function Composer({
|
|
|
587
598
|
}
|
|
588
599
|
setSelectedOptions(next);
|
|
589
600
|
};
|
|
590
|
-
const
|
|
601
|
+
const handleFileSelect = (e) => {
|
|
602
|
+
const files = Array.from(e.target.files || []);
|
|
603
|
+
const validFiles = files.filter((file) => {
|
|
604
|
+
if (file.size > maxFileSize) {
|
|
605
|
+
console.warn(`File ${file.name} exceeds maximum size of ${maxFileSize} bytes`);
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
return true;
|
|
609
|
+
});
|
|
610
|
+
const remainingSlots = maxFiles - attachedFiles.length;
|
|
611
|
+
const filesToAdd = validFiles.slice(0, remainingSlots);
|
|
612
|
+
if (filesToAdd.length < validFiles.length) {
|
|
613
|
+
console.warn(`Only ${filesToAdd.length} files can be added (max: ${maxFiles})`);
|
|
614
|
+
}
|
|
615
|
+
setAttachedFiles((prev) => [...prev, ...filesToAdd]);
|
|
616
|
+
if (fileInputRef.current) {
|
|
617
|
+
fileInputRef.current.value = "";
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
const handleRemoveFile = (index) => {
|
|
621
|
+
setAttachedFiles((prev) => prev.filter((_, i) => i !== index));
|
|
622
|
+
};
|
|
623
|
+
const formatFileSize = (bytes) => {
|
|
624
|
+
if (bytes < 1024) return bytes + " B";
|
|
625
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
|
|
626
|
+
return (bytes / (1024 * 1024)).toFixed(1) + " MB";
|
|
627
|
+
};
|
|
628
|
+
const handleInternalSubmit = async () => {
|
|
591
629
|
const state = {};
|
|
592
630
|
options.forEach((group) => {
|
|
593
631
|
const selectedInGroup = group.options.filter(
|
|
@@ -604,85 +642,178 @@ function Composer({
|
|
|
604
642
|
}
|
|
605
643
|
}
|
|
606
644
|
});
|
|
645
|
+
if (attachedFiles.length > 0) {
|
|
646
|
+
const filePromises = attachedFiles.map((file) => {
|
|
647
|
+
return new Promise((resolve, reject) => {
|
|
648
|
+
const reader = new FileReader();
|
|
649
|
+
reader.onload = () => {
|
|
650
|
+
try {
|
|
651
|
+
const base64 = reader.result;
|
|
652
|
+
if (!base64) {
|
|
653
|
+
reject(new Error("FileReader returned empty result"));
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const base64Data = base64.includes(",") ? base64.split(",")[1] : base64;
|
|
657
|
+
resolve({
|
|
658
|
+
name: file.name,
|
|
659
|
+
type: file.type,
|
|
660
|
+
size: file.size,
|
|
661
|
+
data: base64Data
|
|
662
|
+
});
|
|
663
|
+
} catch (error) {
|
|
664
|
+
reject(error);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
reader.onerror = (error) => {
|
|
668
|
+
reject(new Error(`Failed to read file ${file.name}: ${error}`));
|
|
669
|
+
};
|
|
670
|
+
reader.onabort = () => {
|
|
671
|
+
reject(new Error(`File read aborted for ${file.name}`));
|
|
672
|
+
};
|
|
673
|
+
reader.readAsDataURL(file);
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
try {
|
|
677
|
+
const convertedFiles = await Promise.all(filePromises);
|
|
678
|
+
if (convertedFiles.length > 0) {
|
|
679
|
+
state.files = convertedFiles;
|
|
680
|
+
}
|
|
681
|
+
} catch (error) {
|
|
682
|
+
console.error("Failed to convert files to base64:", error);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
607
685
|
onSubmit(state);
|
|
686
|
+
setAttachedFiles([]);
|
|
608
687
|
};
|
|
609
688
|
const handleKeyDown = (e) => {
|
|
610
689
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
611
690
|
e.preventDefault();
|
|
612
|
-
handleInternalSubmit();
|
|
691
|
+
handleInternalSubmit().catch(console.error);
|
|
613
692
|
}
|
|
614
693
|
};
|
|
615
|
-
return /* @__PURE__ */
|
|
616
|
-
/* @__PURE__ */ jsx(
|
|
617
|
-
|
|
694
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("relative flex flex-col w-full", className), children: [
|
|
695
|
+
enabled && attachedFiles.length > 0 && /* @__PURE__ */ jsx("div", { className: "mb-2 flex flex-wrap gap-2", children: attachedFiles.map((file, index) => /* @__PURE__ */ jsxs(
|
|
696
|
+
"div",
|
|
618
697
|
{
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
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",
|
|
624
|
-
autoFocus
|
|
625
|
-
}
|
|
626
|
-
),
|
|
627
|
-
/* @__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: [
|
|
698
|
+
className: "flex items-center gap-2 px-3 py-1.5 bg-muted rounded-lg text-sm",
|
|
699
|
+
children: [
|
|
700
|
+
/* @__PURE__ */ jsx("span", { className: "truncate max-w-[200px]", title: file.name, children: file.name }),
|
|
701
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: formatFileSize(file.size) }),
|
|
635
702
|
/* @__PURE__ */ jsx(
|
|
636
|
-
|
|
703
|
+
"button",
|
|
637
704
|
{
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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
|
-
)
|
|
705
|
+
type: "button",
|
|
706
|
+
onClick: () => handleRemoveFile(index),
|
|
707
|
+
className: "ml-1 hover:bg-muted-foreground/20 rounded p-0.5 transition-colors",
|
|
708
|
+
"aria-label": "Remove file",
|
|
709
|
+
children: /* @__PURE__ */ jsx(IconX, { className: "h-3.5 w-3.5" })
|
|
652
710
|
}
|
|
653
|
-
)
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
{
|
|
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
|
-
}) }),
|
|
711
|
+
)
|
|
712
|
+
]
|
|
713
|
+
},
|
|
714
|
+
index
|
|
715
|
+
)) }),
|
|
716
|
+
/* @__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: [
|
|
674
717
|
/* @__PURE__ */ jsx(
|
|
675
|
-
|
|
718
|
+
Textarea,
|
|
676
719
|
{
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
720
|
+
value,
|
|
721
|
+
onChange: (e) => onChange(e.target.value),
|
|
722
|
+
onKeyDown: handleKeyDown,
|
|
723
|
+
placeholder,
|
|
724
|
+
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",
|
|
725
|
+
autoFocus
|
|
682
726
|
}
|
|
683
|
-
)
|
|
727
|
+
),
|
|
728
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center px-1", children: [
|
|
729
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
730
|
+
enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
731
|
+
/* @__PURE__ */ jsx(
|
|
732
|
+
"input",
|
|
733
|
+
{
|
|
734
|
+
ref: fileInputRef,
|
|
735
|
+
type: "file",
|
|
736
|
+
multiple: true,
|
|
737
|
+
accept,
|
|
738
|
+
onChange: handleFileSelect,
|
|
739
|
+
className: "hidden",
|
|
740
|
+
disabled: isLoading || attachedFiles.length >= maxFiles
|
|
741
|
+
}
|
|
742
|
+
),
|
|
743
|
+
/* @__PURE__ */ jsx(
|
|
744
|
+
Button,
|
|
745
|
+
{
|
|
746
|
+
type: "button",
|
|
747
|
+
variant: "ghost",
|
|
748
|
+
size: "sm",
|
|
749
|
+
onClick: () => fileInputRef.current?.click(),
|
|
750
|
+
disabled: isLoading || attachedFiles.length >= maxFiles,
|
|
751
|
+
className: "text-muted-foreground",
|
|
752
|
+
title: attachedFiles.length >= maxFiles ? `Maximum ${maxFiles} files allowed` : "Attach file",
|
|
753
|
+
children: /* @__PURE__ */ jsx(IconPaperclip, { className: "h-4 w-4" })
|
|
754
|
+
}
|
|
755
|
+
)
|
|
756
|
+
] }),
|
|
757
|
+
options.map((group) => {
|
|
758
|
+
const selectedInGroup = group.options.filter(
|
|
759
|
+
(o) => selectedOptions.has(o.id)
|
|
760
|
+
);
|
|
761
|
+
const label = selectedInGroup.length === 0 ? group.label : selectedInGroup.length === 1 ? selectedInGroup[0].label : `${group.label} (${selectedInGroup.length})`;
|
|
762
|
+
const isSingle = group.type === "single";
|
|
763
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
764
|
+
/* @__PURE__ */ jsx(
|
|
765
|
+
DropdownMenuTrigger,
|
|
766
|
+
{
|
|
767
|
+
render: /* @__PURE__ */ jsxs(
|
|
768
|
+
Button,
|
|
769
|
+
{
|
|
770
|
+
variant: "ghost",
|
|
771
|
+
size: "sm",
|
|
772
|
+
className: cn(
|
|
773
|
+
selectedInGroup.length > 0 ? "text-foreground bg-muted/50" : "text-muted-foreground"
|
|
774
|
+
),
|
|
775
|
+
children: [
|
|
776
|
+
label,
|
|
777
|
+
/* @__PURE__ */ jsx(IconChevronDown, { className: "h-3 w-3 opacity-50" })
|
|
778
|
+
]
|
|
779
|
+
}
|
|
780
|
+
)
|
|
781
|
+
}
|
|
782
|
+
),
|
|
783
|
+
/* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: "w-56", children: /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
|
|
784
|
+
/* @__PURE__ */ jsx(DropdownMenuLabel, { children: group.label }),
|
|
785
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
786
|
+
group.options.map((option) => /* @__PURE__ */ jsx(
|
|
787
|
+
DropdownMenuCheckboxItem,
|
|
788
|
+
{
|
|
789
|
+
checked: selectedOptions.has(option.id),
|
|
790
|
+
onCheckedChange: () => toggleOption(
|
|
791
|
+
option.id,
|
|
792
|
+
group.options,
|
|
793
|
+
isSingle ? "single" : "multiple"
|
|
794
|
+
),
|
|
795
|
+
onSelect: (e) => e.preventDefault(),
|
|
796
|
+
children: option.label
|
|
797
|
+
},
|
|
798
|
+
option.id
|
|
799
|
+
))
|
|
800
|
+
] }) })
|
|
801
|
+
] }, group.id);
|
|
802
|
+
})
|
|
803
|
+
] }),
|
|
804
|
+
/* @__PURE__ */ jsx(
|
|
805
|
+
Button,
|
|
806
|
+
{
|
|
807
|
+
type: "submit",
|
|
808
|
+
disabled: !value.trim() && attachedFiles.length === 0 && !isLoading || isLoading,
|
|
809
|
+
size: "icon-lg",
|
|
810
|
+
onClick: () => handleInternalSubmit().catch(console.error),
|
|
811
|
+
children: isLoading ? /* @__PURE__ */ jsx(IconLoader2, { className: "h-5 w-5 animate-spin" }) : /* @__PURE__ */ jsx(IconArrowUp, { className: "h-5 w-5" })
|
|
812
|
+
}
|
|
813
|
+
)
|
|
814
|
+
] })
|
|
684
815
|
] })
|
|
685
|
-
] })
|
|
816
|
+
] });
|
|
686
817
|
}
|
|
687
818
|
function Card({
|
|
688
819
|
className,
|
|
@@ -2341,6 +2472,14 @@ function Thread({
|
|
|
2341
2472
|
});
|
|
2342
2473
|
const starterPrompts = localStarterPrompts ?? config?.starterPrompts;
|
|
2343
2474
|
const options = localOptions ?? config?.options;
|
|
2475
|
+
const allDefaultSelectedIds = useMemo(() => {
|
|
2476
|
+
const defaultSelectedIdsFromOptions = options?.flatMap(
|
|
2477
|
+
(group) => group.defaultSelectedIds ?? []
|
|
2478
|
+
) ?? [];
|
|
2479
|
+
return [
|
|
2480
|
+
.../* @__PURE__ */ new Set([...defaultSelectedIdsFromOptions, ...defaultSelectedIds ?? []])
|
|
2481
|
+
];
|
|
2482
|
+
}, [options, defaultSelectedIds]);
|
|
2344
2483
|
const [input, setInput] = useState("");
|
|
2345
2484
|
const messagesEndRef = useRef(null);
|
|
2346
2485
|
useEffect(() => {
|
|
@@ -2348,13 +2487,14 @@ function Thread({
|
|
|
2348
2487
|
}, [messages]);
|
|
2349
2488
|
const handleSubmit = async (state, overrideInput) => {
|
|
2350
2489
|
const text = (overrideInput ?? input).trim();
|
|
2351
|
-
|
|
2490
|
+
const hasFiles = state?.files && Array.isArray(state.files) && state.files.length > 0;
|
|
2491
|
+
if (!text && !hasFiles || isLoading) return;
|
|
2352
2492
|
if (!overrideInput) setInput("");
|
|
2353
2493
|
await sendEvent(
|
|
2354
2494
|
{
|
|
2355
2495
|
type: "text",
|
|
2356
2496
|
role: "user",
|
|
2357
|
-
data: { content: text }
|
|
2497
|
+
data: { content: text || "" }
|
|
2358
2498
|
},
|
|
2359
2499
|
{ state: { ...state, threadId: activeThreadId ?? void 0 } }
|
|
2360
2500
|
);
|
|
@@ -2412,7 +2552,8 @@ function Thread({
|
|
|
2412
2552
|
isLoading,
|
|
2413
2553
|
options,
|
|
2414
2554
|
autoFocus,
|
|
2415
|
-
defaultSelectedIds
|
|
2555
|
+
defaultSelectedIds: allDefaultSelectedIds,
|
|
2556
|
+
fileAttachments: config?.fileAttachments
|
|
2416
2557
|
}
|
|
2417
2558
|
) }) })
|
|
2418
2559
|
]
|