@mordn/chat-widget 0.4.1 → 0.5.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.d.mts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +75 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +75 -41
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -490,7 +490,11 @@ var PromptInput = ({
|
|
|
490
490
|
"form",
|
|
491
491
|
{
|
|
492
492
|
className: cn(
|
|
493
|
-
|
|
493
|
+
// The form border + horizontal divider colour both come from
|
|
494
|
+
// the styles.src.css `.chat-widget-container form ...` rules so
|
|
495
|
+
// there's a single token (--chat-divider) controlling both.
|
|
496
|
+
// No utility classes for border colour here.
|
|
497
|
+
"w-full overflow-hidden rounded-xl bg-background transition-colors",
|
|
494
498
|
"[&:focus-within]:shadow-none [&:focus]:shadow-none shadow-none",
|
|
495
499
|
className
|
|
496
500
|
),
|
|
@@ -1174,7 +1178,7 @@ var CodeBlock = ({
|
|
|
1174
1178
|
"div",
|
|
1175
1179
|
{
|
|
1176
1180
|
className: cn(
|
|
1177
|
-
"relative w-full overflow-hidden rounded-lg bg-[hsl(var(--chat-text)/0.03)] border border-[
|
|
1181
|
+
"relative w-full overflow-hidden rounded-lg bg-[hsl(var(--chat-text)/0.03)] border border-[var(--chat-divider)]",
|
|
1178
1182
|
className
|
|
1179
1183
|
),
|
|
1180
1184
|
...props,
|
|
@@ -1241,7 +1245,7 @@ import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
|
1241
1245
|
var Tool = ({ className, ...props }) => /* @__PURE__ */ jsx17(
|
|
1242
1246
|
Collapsible,
|
|
1243
1247
|
{
|
|
1244
|
-
className: cn("not-prose w-full rounded-md border border-[
|
|
1248
|
+
className: cn("not-prose w-full rounded-md border border-[var(--chat-divider)]", className),
|
|
1245
1249
|
...props
|
|
1246
1250
|
}
|
|
1247
1251
|
);
|
|
@@ -1267,6 +1271,7 @@ var ToolHeader = ({
|
|
|
1267
1271
|
className,
|
|
1268
1272
|
title,
|
|
1269
1273
|
type,
|
|
1274
|
+
toolName,
|
|
1270
1275
|
state,
|
|
1271
1276
|
...props
|
|
1272
1277
|
}) => /* @__PURE__ */ jsxs10(
|
|
@@ -1280,7 +1285,7 @@ var ToolHeader = ({
|
|
|
1280
1285
|
children: [
|
|
1281
1286
|
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
|
|
1282
1287
|
/* @__PURE__ */ jsx17(WrenchIcon, { className: "size-4 text-muted-foreground" }),
|
|
1283
|
-
/* @__PURE__ */ jsx17("span", { className: "font-medium text-sm", children: title ?? type.split("-").slice(1).join("-") }),
|
|
1288
|
+
/* @__PURE__ */ jsx17("span", { className: "font-medium text-sm", children: title ?? toolName ?? type.split("-").slice(1).join("-") }),
|
|
1284
1289
|
getStatusBadge(state)
|
|
1285
1290
|
] }),
|
|
1286
1291
|
/* @__PURE__ */ jsx17(ChevronDownIcon4, { className: "size-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" })
|
|
@@ -1359,7 +1364,10 @@ function StarterMessages({
|
|
|
1359
1364
|
onClick: () => onPromptSelect(prompt)
|
|
1360
1365
|
}
|
|
1361
1366
|
),
|
|
1362
|
-
index < prompts.length - 1 &&
|
|
1367
|
+
index < prompts.length - 1 && // 1px-tall element used as a divider — same --chat-divider token
|
|
1368
|
+
// every other separator in the widget uses, so consumers only
|
|
1369
|
+
// need to override one variable to recolour all of them.
|
|
1370
|
+
/* @__PURE__ */ jsx18("div", { className: "h-px mx-3", style: { backgroundColor: "var(--chat-divider)" } })
|
|
1363
1371
|
] }, index))
|
|
1364
1372
|
}
|
|
1365
1373
|
);
|
|
@@ -1432,6 +1440,7 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
1432
1440
|
const [activeTabId, setActiveTabId] = useState4("");
|
|
1433
1441
|
const [initialTabCreated, setInitialTabCreated] = useState4(false);
|
|
1434
1442
|
const [isInitializing, setIsInitializing] = useState4(true);
|
|
1443
|
+
const [isLoadingMessages, setIsLoadingMessages] = useState4(false);
|
|
1435
1444
|
const lastSyncedTabId = useRef3("");
|
|
1436
1445
|
const hasInitialized = useRef3(false);
|
|
1437
1446
|
const { messages, sendMessage, status, setMessages } = useChat({
|
|
@@ -1611,7 +1620,12 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
1611
1620
|
}))
|
|
1612
1621
|
);
|
|
1613
1622
|
setActiveTabId(tabId);
|
|
1614
|
-
|
|
1623
|
+
setIsLoadingMessages(true);
|
|
1624
|
+
try {
|
|
1625
|
+
await loadConversation(tabId);
|
|
1626
|
+
} finally {
|
|
1627
|
+
setIsLoadingMessages(false);
|
|
1628
|
+
}
|
|
1615
1629
|
};
|
|
1616
1630
|
const closeTab = (tabId) => {
|
|
1617
1631
|
if (tabs.length <= 1) return;
|
|
@@ -1679,31 +1693,28 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
1679
1693
|
useEffect4(() => {
|
|
1680
1694
|
if (hasInitialized.current) return;
|
|
1681
1695
|
const loadInitialTabs = () => {
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
title: "New Chat",
|
|
1696
|
-
isActive: true
|
|
1697
|
-
};
|
|
1698
|
-
setTabs([currentTab]);
|
|
1699
|
-
setActiveTabId(initialTabId);
|
|
1700
|
-
setInitialTabCreated(true);
|
|
1701
|
-
}
|
|
1702
|
-
} finally {
|
|
1696
|
+
const savedTabs = localStorage.getItem(storageKey("tabs"));
|
|
1697
|
+
const savedActiveTabId = localStorage.getItem(storageKey("active-tab-id"));
|
|
1698
|
+
if (savedTabs && savedTabs !== "[]") {
|
|
1699
|
+
const parsedTabs = JSON.parse(savedTabs);
|
|
1700
|
+
setTabs(parsedTabs);
|
|
1701
|
+
const activeId = savedActiveTabId || parsedTabs[0]?.id;
|
|
1702
|
+
setActiveTabId(activeId);
|
|
1703
|
+
setInitialTabCreated(true);
|
|
1704
|
+
} else if (!initialTabCreated && tabs.length === 0) {
|
|
1705
|
+
const initialTabId = `chat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
1706
|
+
setTabs([{ id: initialTabId, title: "New Chat", isActive: true }]);
|
|
1707
|
+
setActiveTabId(initialTabId);
|
|
1708
|
+
setInitialTabCreated(true);
|
|
1703
1709
|
setIsInitializing(false);
|
|
1704
1710
|
}
|
|
1705
1711
|
};
|
|
1706
|
-
|
|
1712
|
+
try {
|
|
1713
|
+
loadInitialTabs();
|
|
1714
|
+
} catch (err) {
|
|
1715
|
+
console.error("[chat-widget] init failed, falling back to clean start:", err);
|
|
1716
|
+
setIsInitializing(false);
|
|
1717
|
+
}
|
|
1707
1718
|
hasInitialized.current = true;
|
|
1708
1719
|
}, []);
|
|
1709
1720
|
const hasLoadedInitialMessages = useRef3(false);
|
|
@@ -1711,10 +1722,15 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
1711
1722
|
if (hasLoadedInitialMessages.current) return;
|
|
1712
1723
|
if (!config?.userId) return;
|
|
1713
1724
|
if (!activeTabId) return;
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1725
|
+
(async () => {
|
|
1726
|
+
try {
|
|
1727
|
+
await loadConversation(activeTabId);
|
|
1728
|
+
} finally {
|
|
1729
|
+
hasLoadedInitialMessages.current = true;
|
|
1730
|
+
setIsInitializing(false);
|
|
1731
|
+
}
|
|
1732
|
+
})();
|
|
1733
|
+
}, [config?.userId, activeTabId]);
|
|
1718
1734
|
useEffect4(() => {
|
|
1719
1735
|
if (isInitializing) return;
|
|
1720
1736
|
if (activeTabId && tabs.length > 0 && activeTabId !== lastSyncedTabId.current) {
|
|
@@ -1834,7 +1850,14 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
1834
1850
|
if (part.type.startsWith("tool-") || part.type === "dynamic-tool") {
|
|
1835
1851
|
const toolPart = part;
|
|
1836
1852
|
return /* @__PURE__ */ jsxs12(Tool, { children: [
|
|
1837
|
-
/* @__PURE__ */ jsx20(
|
|
1853
|
+
/* @__PURE__ */ jsx20(
|
|
1854
|
+
ToolHeader,
|
|
1855
|
+
{
|
|
1856
|
+
type: part.type,
|
|
1857
|
+
toolName: toolPart.toolName,
|
|
1858
|
+
state: toolPart.state
|
|
1859
|
+
}
|
|
1860
|
+
),
|
|
1838
1861
|
/* @__PURE__ */ jsxs12(ToolContent, { children: [
|
|
1839
1862
|
/* @__PURE__ */ jsx20(ToolInput, { input: toolPart.input }),
|
|
1840
1863
|
/* @__PURE__ */ jsx20(ToolOutput, { output: toolPart.output, errorText: toolPart.errorText })
|
|
@@ -1900,7 +1923,7 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
1900
1923
|
),
|
|
1901
1924
|
children: [
|
|
1902
1925
|
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2 px-3 py-2 border-b backdrop-blur-sm relative z-20", style: {
|
|
1903
|
-
borderColor: "var(--chat-
|
|
1926
|
+
borderColor: "var(--chat-divider)",
|
|
1904
1927
|
backgroundColor: "var(--chat-header-bg)"
|
|
1905
1928
|
}, children: [
|
|
1906
1929
|
/* @__PURE__ */ jsx20("div", { className: "flex items-center gap-1 flex-1 min-w-0 overflow-x-auto scrollbar-hide py-0.5 scroll-smooth", children: tabs.map((tab, index) => /* @__PURE__ */ jsxs12(
|
|
@@ -2002,10 +2025,10 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
2002
2025
|
),
|
|
2003
2026
|
showHistory && /* @__PURE__ */ jsxs12("div", { className: "absolute right-0 top-full mt-1.5 w-72 rounded-xl shadow-[0_4px_24px_rgba(0,0,0,0.08)] dark:shadow-[0_4px_24px_rgba(0,0,0,0.3)] z-50 animate-in fade-in slide-in-from-top-1 duration-150 overflow-hidden", style: {
|
|
2004
2027
|
backgroundColor: "hsl(var(--chat-background))",
|
|
2005
|
-
border: `1px solid ${"var(--chat-
|
|
2028
|
+
border: `1px solid ${"var(--chat-divider)"}`
|
|
2006
2029
|
}, children: [
|
|
2007
2030
|
/* @__PURE__ */ jsx20("div", { className: "p-2.5 border-b", style: {
|
|
2008
|
-
borderColor: "var(--chat-
|
|
2031
|
+
borderColor: "var(--chat-divider)",
|
|
2009
2032
|
backgroundColor: "var(--chat-overlay)"
|
|
2010
2033
|
}, children: /* @__PURE__ */ jsx20("div", { className: "relative", children: /* @__PURE__ */ jsx20(
|
|
2011
2034
|
"input",
|
|
@@ -2017,7 +2040,7 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
2017
2040
|
className: "w-full h-7 px-2.5 text-[13px] rounded-lg focus:outline-none transition-all",
|
|
2018
2041
|
style: {
|
|
2019
2042
|
backgroundColor: "hsl(var(--chat-surface-deep))",
|
|
2020
|
-
border: `1px solid ${"var(--chat-
|
|
2043
|
+
border: `1px solid ${"var(--chat-divider)"}`,
|
|
2021
2044
|
color: "hsl(var(--chat-text))"
|
|
2022
2045
|
}
|
|
2023
2046
|
}
|
|
@@ -2114,7 +2137,7 @@ function ChatInterface({ id, initialMessages, config, onClose, headerActions } =
|
|
|
2114
2137
|
] }),
|
|
2115
2138
|
/* @__PURE__ */ jsxs12("div", { className: "px-5 pb-5", children: [
|
|
2116
2139
|
uploadError && /* @__PURE__ */ jsx20("div", { className: "mb-3 px-4 py-3 bg-red-50 dark:bg-red-900/20 border border-red-200/60 dark:border-red-800/60 rounded-2xl text-sm text-red-700 dark:text-red-400 shadow-sm", children: uploadError }),
|
|
2117
|
-
isInitializing ? /* @__PURE__ */ jsx20("div", { className: "flex items-center justify-center py-8", role: "status", "aria-label": "Loading conversation", children: /* @__PURE__ */ jsx20("div", { className: "h-4 w-4 rounded-full border-2 border-current border-t-transparent animate-spin", style: { color: "hsl(var(--chat-text-muted))" } }) }) : messages.length === 0 && status !== "submitted" && config?.starterPrompts && /* @__PURE__ */ jsx20(
|
|
2140
|
+
isInitializing || isLoadingMessages ? /* @__PURE__ */ jsx20("div", { className: "flex items-center justify-center py-8", role: "status", "aria-label": "Loading conversation", children: /* @__PURE__ */ jsx20("div", { className: "h-4 w-4 rounded-full border-2 border-current border-t-transparent animate-spin", style: { color: "hsl(var(--chat-text-muted))" } }) }) : messages.length === 0 && status !== "submitted" && config?.starterPrompts && /* @__PURE__ */ jsx20(
|
|
2118
2141
|
StarterMessages,
|
|
2119
2142
|
{
|
|
2120
2143
|
prompts: config.starterPrompts,
|
|
@@ -2198,15 +2221,26 @@ function ChatWidget({
|
|
|
2198
2221
|
display,
|
|
2199
2222
|
starterPrompts,
|
|
2200
2223
|
onClose,
|
|
2201
|
-
headerActions
|
|
2224
|
+
headerActions,
|
|
2225
|
+
open,
|
|
2226
|
+
onOpenChange
|
|
2202
2227
|
}) {
|
|
2203
2228
|
const layout = display?.layout || "popup";
|
|
2204
|
-
const
|
|
2229
|
+
const isControlled = open !== void 0;
|
|
2230
|
+
const showToggleButton = !isControlled && display?.showToggleButton !== false;
|
|
2205
2231
|
const resizable = layout === "popup" && display?.resizable !== false;
|
|
2206
2232
|
const size = display?.size || "default";
|
|
2207
|
-
const [
|
|
2233
|
+
const [internalIsOpen, setInternalIsOpen] = useState5(
|
|
2208
2234
|
layout !== "popup" ? true : display?.defaultOpen || false
|
|
2209
2235
|
);
|
|
2236
|
+
const isOpen = isControlled ? open : internalIsOpen;
|
|
2237
|
+
const setIsOpen = (next) => {
|
|
2238
|
+
if (isControlled) {
|
|
2239
|
+
onOpenChange?.(next);
|
|
2240
|
+
} else {
|
|
2241
|
+
setInternalIsOpen(next);
|
|
2242
|
+
}
|
|
2243
|
+
};
|
|
2210
2244
|
const [isResizing, setIsResizing] = useState5(false);
|
|
2211
2245
|
const containerRef = useRef4(null);
|
|
2212
2246
|
const customStyles = useMemo3(() => {
|