@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.js
CHANGED
|
@@ -220,11 +220,13 @@ var import_lucide_react3 = require("lucide-react");
|
|
|
220
220
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
221
221
|
|
|
222
222
|
// src/ui/textarea.tsx
|
|
223
|
+
var React = __toESM(require("react"));
|
|
223
224
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
224
|
-
|
|
225
|
+
var Textarea = React.forwardRef(({ className, ...props }, ref) => {
|
|
225
226
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
226
227
|
"textarea",
|
|
227
228
|
{
|
|
229
|
+
ref,
|
|
228
230
|
"data-slot": "textarea",
|
|
229
231
|
className: cn(
|
|
230
232
|
"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",
|
|
@@ -233,7 +235,8 @@ function Textarea({ className, ...props }) {
|
|
|
233
235
|
...props
|
|
234
236
|
}
|
|
235
237
|
);
|
|
236
|
-
}
|
|
238
|
+
});
|
|
239
|
+
Textarea.displayName = "Textarea";
|
|
237
240
|
|
|
238
241
|
// src/components/prompt-input.tsx
|
|
239
242
|
var import_lucide_react4 = require("lucide-react");
|
|
@@ -747,6 +750,7 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
747
750
|
const [loading, setLoading] = (0, import_react3.useState)(false);
|
|
748
751
|
const debounceRef = (0, import_react3.useRef)(null);
|
|
749
752
|
const requestIdRef = (0, import_react3.useRef)(0);
|
|
753
|
+
const sessionValidatedRef = (0, import_react3.useRef)(false);
|
|
750
754
|
const pluginsByTrigger = (0, import_react3.useMemo)(() => {
|
|
751
755
|
const map = /* @__PURE__ */ new Map();
|
|
752
756
|
for (const p of plugins ?? []) {
|
|
@@ -763,9 +767,14 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
763
767
|
return value.slice(active.triggerIndex + 1, cursor);
|
|
764
768
|
}, [active, value, textareaRef]);
|
|
765
769
|
(0, import_react3.useEffect)(() => {
|
|
766
|
-
if (!active)
|
|
767
|
-
|
|
768
|
-
|
|
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;
|
|
769
778
|
if (value[active.triggerIndex] !== active.plugin.trigger) {
|
|
770
779
|
setActive(null);
|
|
771
780
|
return;
|
|
@@ -773,7 +782,7 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
773
782
|
if (/\s/.test(query)) {
|
|
774
783
|
setActive(null);
|
|
775
784
|
}
|
|
776
|
-
}, [active, value, query
|
|
785
|
+
}, [active, value, query]);
|
|
777
786
|
(0, import_react3.useEffect)(() => {
|
|
778
787
|
if (!active) {
|
|
779
788
|
setItems([]);
|
|
@@ -781,13 +790,20 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
781
790
|
return;
|
|
782
791
|
}
|
|
783
792
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
784
|
-
setLoading(true);
|
|
785
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);
|
|
786
802
|
debounceRef.current = setTimeout(async () => {
|
|
787
803
|
try {
|
|
788
|
-
const
|
|
804
|
+
const items2 = await result;
|
|
789
805
|
if (reqId !== requestIdRef.current) return;
|
|
790
|
-
setItems(
|
|
806
|
+
setItems(items2);
|
|
791
807
|
setHighlight(0);
|
|
792
808
|
} catch (err) {
|
|
793
809
|
console.error("[input-plugin] fetch failed:", err);
|
|
@@ -868,14 +884,8 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
868
884
|
},
|
|
869
885
|
[active, items, highlight, selectItem, close, pluginsByTrigger, value]
|
|
870
886
|
);
|
|
871
|
-
(0,
|
|
872
|
-
|
|
873
|
-
if (value[active.triggerIndex] !== active.plugin.trigger) {
|
|
874
|
-
close();
|
|
875
|
-
}
|
|
876
|
-
}, [value, active, close]);
|
|
877
|
-
const popover = active && (loading || items.length > 0 || active.plugin.emptyText) ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
878
|
-
PluginPopover,
|
|
887
|
+
const panel = active ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
888
|
+
PluginPanel,
|
|
879
889
|
{
|
|
880
890
|
plugin: active.plugin,
|
|
881
891
|
items,
|
|
@@ -885,43 +895,122 @@ function useInputPlugins({ textareaRef, value, setValue, plugins }) {
|
|
|
885
895
|
onSelect: selectItem
|
|
886
896
|
}
|
|
887
897
|
) : null;
|
|
888
|
-
return { onKeyDown,
|
|
898
|
+
return { onKeyDown, panel, isOpen: !!active };
|
|
889
899
|
}
|
|
890
|
-
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]);
|
|
891
915
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
892
916
|
"div",
|
|
893
917
|
{
|
|
894
918
|
role: "listbox",
|
|
895
|
-
className:
|
|
896
|
-
"absolute bottom-full left-0 right-0 mb-2 z-30",
|
|
897
|
-
"rounded-md border border-border bg-popover text-popover-foreground shadow-md",
|
|
898
|
-
"max-h-64 overflow-y-auto"
|
|
899
|
-
),
|
|
919
|
+
className: "rounded-t-xl bg-[hsl(var(--chat-background))] overflow-hidden mx-auto",
|
|
900
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
|
+
},
|
|
901
930
|
children: [
|
|
902
|
-
plugin.heading && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
903
|
-
|
|
904
|
-
!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" }),
|
|
905
|
-
items.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
906
|
-
"button",
|
|
931
|
+
plugin.heading && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
932
|
+
"div",
|
|
907
933
|
{
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
)
|
|
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
|
+
)
|
|
925
1014
|
]
|
|
926
1015
|
}
|
|
927
1016
|
);
|
|
@@ -2369,21 +2458,19 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
2369
2458
|
}
|
|
2370
2459
|
}
|
|
2371
2460
|
),
|
|
2461
|
+
inputPlugins.panel,
|
|
2372
2462
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(PromptInput, { onSubmit: handleSubmit, globalDrop: true, multiple: true, accept: "image/*", children: [
|
|
2373
2463
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(PromptInputBody, { children: [
|
|
2374
2464
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PromptInputAttachments, { children: (attachment) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PromptInputAttachment, { data: attachment }) }),
|
|
2375
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
),
|
|
2385
|
-
inputPlugins.popover
|
|
2386
|
-
] })
|
|
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
|
+
)
|
|
2387
2474
|
] }),
|
|
2388
2475
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(PromptInputToolbar, { children: [
|
|
2389
2476
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PromptInputTools, { children: config?.features?.fileUpload === true && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AttachButton, {}) }),
|
|
@@ -2936,9 +3023,9 @@ function useChatTheme() {
|
|
|
2936
3023
|
}
|
|
2937
3024
|
|
|
2938
3025
|
// src/ui/input.tsx
|
|
2939
|
-
var
|
|
3026
|
+
var React3 = __toESM(require("react"));
|
|
2940
3027
|
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
2941
|
-
var Input =
|
|
3028
|
+
var Input = React3.forwardRef(
|
|
2942
3029
|
({ className, type, ...props }, ref) => {
|
|
2943
3030
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2944
3031
|
"input",
|