@mordn/chat-widget 0.6.2 → 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 +140 -56
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +140 -56
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -750,6 +750,7 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
750
750
|
const [loading, setLoading] = (0, import_react3.useState)(false);
|
|
751
751
|
const debounceRef = (0, import_react3.useRef)(null);
|
|
752
752
|
const requestIdRef = (0, import_react3.useRef)(0);
|
|
753
|
+
const sessionValidatedRef = (0, import_react3.useRef)(false);
|
|
753
754
|
const pluginsByTrigger = (0, import_react3.useMemo)(() => {
|
|
754
755
|
const map = /* @__PURE__ */ new Map();
|
|
755
756
|
for (const p of plugins ?? []) {
|
|
@@ -766,9 +767,14 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
766
767
|
return value.slice(active.triggerIndex + 1, cursor);
|
|
767
768
|
}, [active, value, textareaRef]);
|
|
768
769
|
(0, import_react3.useEffect)(() => {
|
|
769
|
-
if (!active)
|
|
770
|
-
|
|
771
|
-
|
|
770
|
+
if (!active) {
|
|
771
|
+
sessionValidatedRef.current = false;
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
if (value[active.triggerIndex] === active.plugin.trigger) {
|
|
775
|
+
sessionValidatedRef.current = true;
|
|
776
|
+
}
|
|
777
|
+
if (!sessionValidatedRef.current) return;
|
|
772
778
|
if (value[active.triggerIndex] !== active.plugin.trigger) {
|
|
773
779
|
setActive(null);
|
|
774
780
|
return;
|
|
@@ -776,7 +782,7 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
776
782
|
if (/\s/.test(query)) {
|
|
777
783
|
setActive(null);
|
|
778
784
|
}
|
|
779
|
-
}, [active, value, query
|
|
785
|
+
}, [active, value, query]);
|
|
780
786
|
(0, import_react3.useEffect)(() => {
|
|
781
787
|
if (!active) {
|
|
782
788
|
setItems([]);
|
|
@@ -784,13 +790,20 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
784
790
|
return;
|
|
785
791
|
}
|
|
786
792
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
787
|
-
setLoading(true);
|
|
788
793
|
const reqId = ++requestIdRef.current;
|
|
794
|
+
const result = active.plugin.fetch(query);
|
|
795
|
+
if (Array.isArray(result)) {
|
|
796
|
+
setItems(result);
|
|
797
|
+
setHighlight(0);
|
|
798
|
+
setLoading(false);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
setLoading(true);
|
|
789
802
|
debounceRef.current = setTimeout(async () => {
|
|
790
803
|
try {
|
|
791
|
-
const
|
|
804
|
+
const items2 = await result;
|
|
792
805
|
if (reqId !== requestIdRef.current) return;
|
|
793
|
-
setItems(
|
|
806
|
+
setItems(items2);
|
|
794
807
|
setHighlight(0);
|
|
795
808
|
} catch (err) {
|
|
796
809
|
console.error("[input-plugin] fetch failed:", err);
|
|
@@ -871,14 +884,8 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
871
884
|
},
|
|
872
885
|
[active, items, highlight, selectItem, close, pluginsByTrigger, value]
|
|
873
886
|
);
|
|
874
|
-
(0,
|
|
875
|
-
|
|
876
|
-
if (value[active.triggerIndex] !== active.plugin.trigger) {
|
|
877
|
-
close();
|
|
878
|
-
}
|
|
879
|
-
}, [value, active, close]);
|
|
880
|
-
const popover = active && (loading || items.length > 0 || active.plugin.emptyText) ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
881
|
-
PluginPopover,
|
|
887
|
+
const panel = active ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
888
|
+
PluginPanel,
|
|
882
889
|
{
|
|
883
890
|
plugin: active.plugin,
|
|
884
891
|
items,
|
|
@@ -888,43 +895,122 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
888
895
|
onSelect: selectItem
|
|
889
896
|
}
|
|
890
897
|
) : null;
|
|
891
|
-
return { onKeyDown,
|
|
898
|
+
return { onKeyDown, panel, isOpen: !!active };
|
|
892
899
|
}
|
|
893
|
-
function
|
|
900
|
+
function PluginPanel({ plugin, items, loading, highlight, onHover, onSelect }) {
|
|
901
|
+
const viewportRef = (0, import_react3.useRef)(null);
|
|
902
|
+
const itemRefs = (0, import_react3.useRef)([]);
|
|
903
|
+
(0, import_react3.useEffect)(() => {
|
|
904
|
+
const btn = itemRefs.current[highlight];
|
|
905
|
+
const viewport = viewportRef.current;
|
|
906
|
+
if (!btn || !viewport) return;
|
|
907
|
+
const btnRect = btn.getBoundingClientRect();
|
|
908
|
+
const viewRect = viewport.getBoundingClientRect();
|
|
909
|
+
if (btnRect.top < viewRect.top) {
|
|
910
|
+
viewport.scrollTop -= viewRect.top - btnRect.top;
|
|
911
|
+
} else if (btnRect.bottom > viewRect.bottom) {
|
|
912
|
+
viewport.scrollTop += btnRect.bottom - viewRect.bottom;
|
|
913
|
+
}
|
|
914
|
+
}, [highlight]);
|
|
894
915
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
895
916
|
"div",
|
|
896
917
|
{
|
|
897
918
|
role: "listbox",
|
|
898
|
-
className:
|
|
899
|
-
"absolute bottom-full left-0 right-0 mb-2 z-30",
|
|
900
|
-
"rounded-md border border-border bg-popover text-popover-foreground shadow-md",
|
|
901
|
-
"max-h-64 overflow-y-auto"
|
|
902
|
-
),
|
|
919
|
+
className: "rounded-t-xl bg-[hsl(var(--chat-background))] overflow-hidden mx-auto",
|
|
903
920
|
onMouseDown: (e) => e.preventDefault(),
|
|
921
|
+
style: {
|
|
922
|
+
width: "96%",
|
|
923
|
+
borderTop: "1px solid var(--chat-divider)",
|
|
924
|
+
borderLeft: "1px solid var(--chat-divider)",
|
|
925
|
+
borderRight: "1px solid var(--chat-divider)",
|
|
926
|
+
// Pull down 1px so our bottom edge overlaps the form's top
|
|
927
|
+
// border, removing the visible seam between the two surfaces.
|
|
928
|
+
marginBottom: -1
|
|
929
|
+
},
|
|
904
930
|
children: [
|
|
905
|
-
plugin.heading && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
906
|
-
|
|
907
|
-
!loading && items.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "px-3 py-2 text-xs text-muted-foreground", children: plugin.emptyText ?? "No results" }),
|
|
908
|
-
items.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
909
|
-
"button",
|
|
931
|
+
plugin.heading && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
932
|
+
"div",
|
|
910
933
|
{
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
)
|
|
934
|
+
className: "px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wide",
|
|
935
|
+
style: {
|
|
936
|
+
color: "hsl(var(--chat-text)/0.5)",
|
|
937
|
+
borderBottom: "1px solid var(--chat-divider)"
|
|
938
|
+
},
|
|
939
|
+
children: plugin.heading
|
|
940
|
+
}
|
|
941
|
+
),
|
|
942
|
+
loading && items.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
943
|
+
"div",
|
|
944
|
+
{
|
|
945
|
+
className: "px-3 py-2 text-[13px]",
|
|
946
|
+
style: { color: "hsl(var(--chat-text)/0.5)" },
|
|
947
|
+
children: "Loading\u2026"
|
|
948
|
+
}
|
|
949
|
+
),
|
|
950
|
+
!loading && items.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
951
|
+
"div",
|
|
952
|
+
{
|
|
953
|
+
className: "px-3 py-2 text-[13px]",
|
|
954
|
+
style: { color: "hsl(var(--chat-text)/0.5)" },
|
|
955
|
+
children: plugin.emptyText ?? "No results"
|
|
956
|
+
}
|
|
957
|
+
),
|
|
958
|
+
items.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
959
|
+
"div",
|
|
960
|
+
{
|
|
961
|
+
ref: viewportRef,
|
|
962
|
+
className: "max-h-[200px] overflow-y-auto",
|
|
963
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "py-1", children: items.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
964
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
965
|
+
"button",
|
|
966
|
+
{
|
|
967
|
+
ref: (el) => {
|
|
968
|
+
itemRefs.current[idx] = el;
|
|
969
|
+
},
|
|
970
|
+
type: "button",
|
|
971
|
+
role: "option",
|
|
972
|
+
"aria-selected": idx === highlight,
|
|
973
|
+
onMouseEnter: () => onHover(idx),
|
|
974
|
+
onClick: () => onSelect(item),
|
|
975
|
+
className: cn(
|
|
976
|
+
"w-full text-left px-3 py-2",
|
|
977
|
+
"flex items-center justify-between gap-3",
|
|
978
|
+
"transition-colors duration-150 ease-out",
|
|
979
|
+
"cursor-pointer"
|
|
980
|
+
),
|
|
981
|
+
style: {
|
|
982
|
+
backgroundColor: idx === highlight ? "hsl(var(--chat-text)/0.06)" : "transparent"
|
|
983
|
+
},
|
|
984
|
+
children: [
|
|
985
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
986
|
+
"span",
|
|
987
|
+
{
|
|
988
|
+
className: "text-[13px] truncate",
|
|
989
|
+
style: { color: "hsl(var(--chat-text)/0.85)" },
|
|
990
|
+
children: item.label
|
|
991
|
+
}
|
|
992
|
+
),
|
|
993
|
+
item.sublabel && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
994
|
+
"span",
|
|
995
|
+
{
|
|
996
|
+
className: "text-[11px] flex-shrink-0",
|
|
997
|
+
style: { color: "hsl(var(--chat-text)/0.4)" },
|
|
998
|
+
children: item.sublabel
|
|
999
|
+
}
|
|
1000
|
+
)
|
|
1001
|
+
]
|
|
1002
|
+
}
|
|
1003
|
+
),
|
|
1004
|
+
idx < items.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1005
|
+
"div",
|
|
1006
|
+
{
|
|
1007
|
+
className: "h-px mx-3",
|
|
1008
|
+
style: { backgroundColor: "var(--chat-divider)" }
|
|
1009
|
+
}
|
|
1010
|
+
)
|
|
1011
|
+
] }, item.id)) })
|
|
1012
|
+
}
|
|
1013
|
+
)
|
|
928
1014
|
]
|
|
929
1015
|
}
|
|
930
1016
|
);
|
|
@@ -2372,21 +2458,19 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
2372
2458
|
}
|
|
2373
2459
|
}
|
|
2374
2460
|
),
|
|
2461
|
+
inputPlugins.panel,
|
|
2375
2462
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(PromptInput, { onSubmit: handleSubmit, globalDrop: true, multiple: true, accept: "image/*", children: [
|
|
2376
2463
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(PromptInputBody, { children: [
|
|
2377
2464
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PromptInputAttachments, { children: (attachment) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PromptInputAttachment, { data: attachment }) }),
|
|
2378
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
),
|
|
2388
|
-
inputPlugins.popover
|
|
2389
|
-
] })
|
|
2465
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2466
|
+
PromptInputTextarea,
|
|
2467
|
+
{
|
|
2468
|
+
ref: inputRef,
|
|
2469
|
+
onChange: (e) => setInput(e.target.value),
|
|
2470
|
+
onKeyDown: inputPlugins.onKeyDown,
|
|
2471
|
+
value: input
|
|
2472
|
+
}
|
|
2473
|
+
)
|
|
2390
2474
|
] }),
|
|
2391
2475
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(PromptInputToolbar, { children: [
|
|
2392
2476
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PromptInputTools, { children: config?.features?.fileUpload === true && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AttachButton, {}) }),
|