@mordn/chat-widget 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +147 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +149 -62
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -171,11 +171,13 @@ import { CheckIcon as CheckIcon2, ChevronDownIcon, ChevronUpIcon } from "lucide-
|
|
|
171
171
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
172
172
|
|
|
173
173
|
// src/ui/textarea.tsx
|
|
174
|
+
import * as React from "react";
|
|
174
175
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
175
|
-
|
|
176
|
+
var Textarea = React.forwardRef(({ className, ...props }, ref) => {
|
|
176
177
|
return /* @__PURE__ */ jsx7(
|
|
177
178
|
"textarea",
|
|
178
179
|
{
|
|
180
|
+
ref,
|
|
179
181
|
"data-slot": "textarea",
|
|
180
182
|
className: cn(
|
|
181
183
|
"border-input placeholder:text-muted-foreground focus-visible:border-ring aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-[14px] transition-colors outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
@@ -184,7 +186,8 @@ function Textarea({ className, ...props }) {
|
|
|
184
186
|
...props
|
|
185
187
|
}
|
|
186
188
|
);
|
|
187
|
-
}
|
|
189
|
+
});
|
|
190
|
+
Textarea.displayName = "Textarea";
|
|
188
191
|
|
|
189
192
|
// src/components/prompt-input.tsx
|
|
190
193
|
import {
|
|
@@ -197,7 +200,7 @@ import {
|
|
|
197
200
|
XIcon
|
|
198
201
|
} from "lucide-react";
|
|
199
202
|
import { nanoid } from "nanoid";
|
|
200
|
-
import
|
|
203
|
+
import React2, {
|
|
201
204
|
Children,
|
|
202
205
|
createContext,
|
|
203
206
|
Fragment as Fragment2,
|
|
@@ -509,7 +512,7 @@ var PromptInputBody = ({
|
|
|
509
512
|
className,
|
|
510
513
|
...props
|
|
511
514
|
}) => /* @__PURE__ */ jsx8("div", { className: cn(className, "flex flex-col"), ...props });
|
|
512
|
-
var PromptInputTextarea =
|
|
515
|
+
var PromptInputTextarea = React2.forwardRef(({
|
|
513
516
|
onChange,
|
|
514
517
|
onKeyDown: externalOnKeyDown,
|
|
515
518
|
className,
|
|
@@ -717,6 +720,7 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
717
720
|
const [loading, setLoading] = useState2(false);
|
|
718
721
|
const debounceRef = useRef2(null);
|
|
719
722
|
const requestIdRef = useRef2(0);
|
|
723
|
+
const sessionValidatedRef = useRef2(false);
|
|
720
724
|
const pluginsByTrigger = useMemo2(() => {
|
|
721
725
|
const map = /* @__PURE__ */ new Map();
|
|
722
726
|
for (const p of plugins ?? []) {
|
|
@@ -733,9 +737,14 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
733
737
|
return value.slice(active.triggerIndex + 1, cursor);
|
|
734
738
|
}, [active, value, textareaRef]);
|
|
735
739
|
useEffect2(() => {
|
|
736
|
-
if (!active)
|
|
737
|
-
|
|
738
|
-
|
|
740
|
+
if (!active) {
|
|
741
|
+
sessionValidatedRef.current = false;
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
if (value[active.triggerIndex] === active.plugin.trigger) {
|
|
745
|
+
sessionValidatedRef.current = true;
|
|
746
|
+
}
|
|
747
|
+
if (!sessionValidatedRef.current) return;
|
|
739
748
|
if (value[active.triggerIndex] !== active.plugin.trigger) {
|
|
740
749
|
setActive(null);
|
|
741
750
|
return;
|
|
@@ -743,7 +752,7 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
743
752
|
if (/\s/.test(query)) {
|
|
744
753
|
setActive(null);
|
|
745
754
|
}
|
|
746
|
-
}, [active, value, query
|
|
755
|
+
}, [active, value, query]);
|
|
747
756
|
useEffect2(() => {
|
|
748
757
|
if (!active) {
|
|
749
758
|
setItems([]);
|
|
@@ -751,13 +760,20 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
751
760
|
return;
|
|
752
761
|
}
|
|
753
762
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
754
|
-
setLoading(true);
|
|
755
763
|
const reqId = ++requestIdRef.current;
|
|
764
|
+
const result = active.plugin.fetch(query);
|
|
765
|
+
if (Array.isArray(result)) {
|
|
766
|
+
setItems(result);
|
|
767
|
+
setHighlight(0);
|
|
768
|
+
setLoading(false);
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
setLoading(true);
|
|
756
772
|
debounceRef.current = setTimeout(async () => {
|
|
757
773
|
try {
|
|
758
|
-
const
|
|
774
|
+
const items2 = await result;
|
|
759
775
|
if (reqId !== requestIdRef.current) return;
|
|
760
|
-
setItems(
|
|
776
|
+
setItems(items2);
|
|
761
777
|
setHighlight(0);
|
|
762
778
|
} catch (err) {
|
|
763
779
|
console.error("[input-plugin] fetch failed:", err);
|
|
@@ -838,14 +854,8 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
838
854
|
},
|
|
839
855
|
[active, items, highlight, selectItem, close, pluginsByTrigger, value]
|
|
840
856
|
);
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
if (value[active.triggerIndex] !== active.plugin.trigger) {
|
|
844
|
-
close();
|
|
845
|
-
}
|
|
846
|
-
}, [value, active, close]);
|
|
847
|
-
const popover = active && (loading || items.length > 0 || active.plugin.emptyText) ? /* @__PURE__ */ jsx10(
|
|
848
|
-
PluginPopover,
|
|
857
|
+
const panel = active ? /* @__PURE__ */ jsx10(
|
|
858
|
+
PluginPanel,
|
|
849
859
|
{
|
|
850
860
|
plugin: active.plugin,
|
|
851
861
|
items,
|
|
@@ -855,43 +865,122 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
855
865
|
onSelect: selectItem
|
|
856
866
|
}
|
|
857
867
|
) : null;
|
|
858
|
-
return { onKeyDown,
|
|
868
|
+
return { onKeyDown, panel, isOpen: !!active };
|
|
859
869
|
}
|
|
860
|
-
function
|
|
870
|
+
function PluginPanel({ plugin, items, loading, highlight, onHover, onSelect }) {
|
|
871
|
+
const viewportRef = useRef2(null);
|
|
872
|
+
const itemRefs = useRef2([]);
|
|
873
|
+
useEffect2(() => {
|
|
874
|
+
const btn = itemRefs.current[highlight];
|
|
875
|
+
const viewport = viewportRef.current;
|
|
876
|
+
if (!btn || !viewport) return;
|
|
877
|
+
const btnRect = btn.getBoundingClientRect();
|
|
878
|
+
const viewRect = viewport.getBoundingClientRect();
|
|
879
|
+
if (btnRect.top < viewRect.top) {
|
|
880
|
+
viewport.scrollTop -= viewRect.top - btnRect.top;
|
|
881
|
+
} else if (btnRect.bottom > viewRect.bottom) {
|
|
882
|
+
viewport.scrollTop += btnRect.bottom - viewRect.bottom;
|
|
883
|
+
}
|
|
884
|
+
}, [highlight]);
|
|
861
885
|
return /* @__PURE__ */ jsxs6(
|
|
862
886
|
"div",
|
|
863
887
|
{
|
|
864
888
|
role: "listbox",
|
|
865
|
-
className:
|
|
866
|
-
"absolute bottom-full left-0 right-0 mb-2 z-30",
|
|
867
|
-
"rounded-md border border-border bg-popover text-popover-foreground shadow-md",
|
|
868
|
-
"max-h-64 overflow-y-auto"
|
|
869
|
-
),
|
|
889
|
+
className: "rounded-t-xl bg-[hsl(var(--chat-background))] overflow-hidden mx-auto",
|
|
870
890
|
onMouseDown: (e) => e.preventDefault(),
|
|
891
|
+
style: {
|
|
892
|
+
width: "96%",
|
|
893
|
+
borderTop: "1px solid var(--chat-divider)",
|
|
894
|
+
borderLeft: "1px solid var(--chat-divider)",
|
|
895
|
+
borderRight: "1px solid var(--chat-divider)",
|
|
896
|
+
// Pull down 1px so our bottom edge overlaps the form's top
|
|
897
|
+
// border, removing the visible seam between the two surfaces.
|
|
898
|
+
marginBottom: -1
|
|
899
|
+
},
|
|
871
900
|
children: [
|
|
872
|
-
plugin.heading && /* @__PURE__ */ jsx10(
|
|
873
|
-
|
|
874
|
-
!loading && items.length === 0 && /* @__PURE__ */ jsx10("div", { className: "px-3 py-2 text-xs text-muted-foreground", children: plugin.emptyText ?? "No results" }),
|
|
875
|
-
items.map((item, idx) => /* @__PURE__ */ jsxs6(
|
|
876
|
-
"button",
|
|
901
|
+
plugin.heading && /* @__PURE__ */ jsx10(
|
|
902
|
+
"div",
|
|
877
903
|
{
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
|
|
904
|
+
className: "px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wide",
|
|
905
|
+
style: {
|
|
906
|
+
color: "hsl(var(--chat-text)/0.5)",
|
|
907
|
+
borderBottom: "1px solid var(--chat-divider)"
|
|
908
|
+
},
|
|
909
|
+
children: plugin.heading
|
|
910
|
+
}
|
|
911
|
+
),
|
|
912
|
+
loading && items.length === 0 && /* @__PURE__ */ jsx10(
|
|
913
|
+
"div",
|
|
914
|
+
{
|
|
915
|
+
className: "px-3 py-2 text-[13px]",
|
|
916
|
+
style: { color: "hsl(var(--chat-text)/0.5)" },
|
|
917
|
+
children: "Loading\u2026"
|
|
918
|
+
}
|
|
919
|
+
),
|
|
920
|
+
!loading && items.length === 0 && /* @__PURE__ */ jsx10(
|
|
921
|
+
"div",
|
|
922
|
+
{
|
|
923
|
+
className: "px-3 py-2 text-[13px]",
|
|
924
|
+
style: { color: "hsl(var(--chat-text)/0.5)" },
|
|
925
|
+
children: plugin.emptyText ?? "No results"
|
|
926
|
+
}
|
|
927
|
+
),
|
|
928
|
+
items.length > 0 && /* @__PURE__ */ jsx10(
|
|
929
|
+
"div",
|
|
930
|
+
{
|
|
931
|
+
ref: viewportRef,
|
|
932
|
+
className: "max-h-[200px] overflow-y-auto",
|
|
933
|
+
children: /* @__PURE__ */ jsx10("div", { className: "py-1", children: items.map((item, idx) => /* @__PURE__ */ jsxs6("div", { children: [
|
|
934
|
+
/* @__PURE__ */ jsxs6(
|
|
935
|
+
"button",
|
|
936
|
+
{
|
|
937
|
+
ref: (el) => {
|
|
938
|
+
itemRefs.current[idx] = el;
|
|
939
|
+
},
|
|
940
|
+
type: "button",
|
|
941
|
+
role: "option",
|
|
942
|
+
"aria-selected": idx === highlight,
|
|
943
|
+
onMouseEnter: () => onHover(idx),
|
|
944
|
+
onClick: () => onSelect(item),
|
|
945
|
+
className: cn(
|
|
946
|
+
"w-full text-left px-3 py-2",
|
|
947
|
+
"flex items-center justify-between gap-3",
|
|
948
|
+
"transition-colors duration-150 ease-out",
|
|
949
|
+
"cursor-pointer"
|
|
950
|
+
),
|
|
951
|
+
style: {
|
|
952
|
+
backgroundColor: idx === highlight ? "hsl(var(--chat-text)/0.06)" : "transparent"
|
|
953
|
+
},
|
|
954
|
+
children: [
|
|
955
|
+
/* @__PURE__ */ jsx10(
|
|
956
|
+
"span",
|
|
957
|
+
{
|
|
958
|
+
className: "text-[13px] truncate",
|
|
959
|
+
style: { color: "hsl(var(--chat-text)/0.85)" },
|
|
960
|
+
children: item.label
|
|
961
|
+
}
|
|
962
|
+
),
|
|
963
|
+
item.sublabel && /* @__PURE__ */ jsx10(
|
|
964
|
+
"span",
|
|
965
|
+
{
|
|
966
|
+
className: "text-[11px] flex-shrink-0",
|
|
967
|
+
style: { color: "hsl(var(--chat-text)/0.4)" },
|
|
968
|
+
children: item.sublabel
|
|
969
|
+
}
|
|
970
|
+
)
|
|
971
|
+
]
|
|
972
|
+
}
|
|
973
|
+
),
|
|
974
|
+
idx < items.length - 1 && /* @__PURE__ */ jsx10(
|
|
975
|
+
"div",
|
|
976
|
+
{
|
|
977
|
+
className: "h-px mx-3",
|
|
978
|
+
style: { backgroundColor: "var(--chat-divider)" }
|
|
979
|
+
}
|
|
980
|
+
)
|
|
981
|
+
] }, item.id)) })
|
|
982
|
+
}
|
|
983
|
+
)
|
|
895
984
|
]
|
|
896
985
|
}
|
|
897
986
|
);
|
|
@@ -2349,21 +2438,19 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
2349
2438
|
}
|
|
2350
2439
|
}
|
|
2351
2440
|
),
|
|
2441
|
+
inputPlugins.panel,
|
|
2352
2442
|
/* @__PURE__ */ jsxs13(PromptInput, { onSubmit: handleSubmit, globalDrop: true, multiple: true, accept: "image/*", children: [
|
|
2353
2443
|
/* @__PURE__ */ jsxs13(PromptInputBody, { children: [
|
|
2354
2444
|
/* @__PURE__ */ jsx21(PromptInputAttachments, { children: (attachment) => /* @__PURE__ */ jsx21(PromptInputAttachment, { data: attachment }) }),
|
|
2355
|
-
/* @__PURE__ */
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
),
|
|
2365
|
-
inputPlugins.popover
|
|
2366
|
-
] })
|
|
2445
|
+
/* @__PURE__ */ jsx21(
|
|
2446
|
+
PromptInputTextarea,
|
|
2447
|
+
{
|
|
2448
|
+
ref: inputRef,
|
|
2449
|
+
onChange: (e) => setInput(e.target.value),
|
|
2450
|
+
onKeyDown: inputPlugins.onKeyDown,
|
|
2451
|
+
value: input
|
|
2452
|
+
}
|
|
2453
|
+
)
|
|
2367
2454
|
] }),
|
|
2368
2455
|
/* @__PURE__ */ jsxs13(PromptInputToolbar, { children: [
|
|
2369
2456
|
/* @__PURE__ */ jsx21(PromptInputTools, { children: config?.features?.fileUpload === true && /* @__PURE__ */ jsx21(AttachButton, {}) }),
|
|
@@ -2916,9 +3003,9 @@ function useChatTheme() {
|
|
|
2916
3003
|
}
|
|
2917
3004
|
|
|
2918
3005
|
// src/ui/input.tsx
|
|
2919
|
-
import * as
|
|
3006
|
+
import * as React3 from "react";
|
|
2920
3007
|
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
2921
|
-
var Input =
|
|
3008
|
+
var Input = React3.forwardRef(
|
|
2922
3009
|
({ className, type, ...props }, ref) => {
|
|
2923
3010
|
return /* @__PURE__ */ jsx23(
|
|
2924
3011
|
"input",
|