@xbrain-swe/nova-chat-widget 0.1.4 → 0.1.7
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 +87 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -31
- package/dist/index.d.ts +57 -31
- package/dist/index.js +87 -14
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -934,6 +934,7 @@ function XbNovaProvider({
|
|
|
934
934
|
{
|
|
935
935
|
handler: spec.handler,
|
|
936
936
|
awaitResult: spec.awaitResult ?? false,
|
|
937
|
+
timeoutMs: spec.timeoutMs,
|
|
937
938
|
dismissSheet: spec.dismissSheet ?? false,
|
|
938
939
|
render: spec.render
|
|
939
940
|
}
|
|
@@ -1130,6 +1131,7 @@ async function resumeToolResult(apiBaseUrl, apiKey, body, handlers) {
|
|
|
1130
1131
|
}
|
|
1131
1132
|
|
|
1132
1133
|
// src/use-chat.ts
|
|
1134
|
+
var DEFAULT_CLIENT_ACTION_TIMEOUT_MS = 15e3;
|
|
1133
1135
|
function useXbNovaChat(options) {
|
|
1134
1136
|
const { auth, trackRegistry, clientActionHandlers } = useXbNovaInternal();
|
|
1135
1137
|
const botId = options?.botId ?? auth.botId;
|
|
@@ -1139,6 +1141,7 @@ function useXbNovaChat(options) {
|
|
|
1139
1141
|
const [messages, setMessages] = (0, import_react8.useState)([]);
|
|
1140
1142
|
const [busy, setBusy] = (0, import_react8.useState)(false);
|
|
1141
1143
|
const [error, setError] = (0, import_react8.useState)(null);
|
|
1144
|
+
const [clientResults, setClientResults] = (0, import_react8.useState)({});
|
|
1142
1145
|
const dispatchedClientActionsRef = (0, import_react8.useRef)(/* @__PURE__ */ new Set());
|
|
1143
1146
|
const nudgeNodeIdRef = (0, import_react8.useRef)(nudgeNodeId);
|
|
1144
1147
|
nudgeNodeIdRef.current = nudgeNodeId;
|
|
@@ -1146,6 +1149,7 @@ function useXbNovaChat(options) {
|
|
|
1146
1149
|
setOnboarding(null);
|
|
1147
1150
|
setMessages([]);
|
|
1148
1151
|
setError(null);
|
|
1152
|
+
setClientResults({});
|
|
1149
1153
|
dispatchedClientActionsRef.current = /* @__PURE__ */ new Set();
|
|
1150
1154
|
fetch(`${apiBaseUrl}/v1/conversations`, {
|
|
1151
1155
|
method: "POST",
|
|
@@ -1162,7 +1166,20 @@ function useXbNovaChat(options) {
|
|
|
1162
1166
|
if (spec.dismissSheet) options?.onDismissSheet?.();
|
|
1163
1167
|
let result;
|
|
1164
1168
|
try {
|
|
1165
|
-
|
|
1169
|
+
if (!spec.awaitResult) {
|
|
1170
|
+
await spec.handler(part.input);
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
const timeoutMs = spec.timeoutMs ?? DEFAULT_CLIENT_ACTION_TIMEOUT_MS;
|
|
1174
|
+
let timer;
|
|
1175
|
+
const timeout = new Promise((resolve) => {
|
|
1176
|
+
timer = setTimeout(() => resolve("\uC624\uB958: \uC751\uB2F5 \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."), timeoutMs);
|
|
1177
|
+
});
|
|
1178
|
+
try {
|
|
1179
|
+
result = await Promise.race([Promise.resolve(spec.handler(part.input)), timeout]);
|
|
1180
|
+
} finally {
|
|
1181
|
+
clearTimeout(timer);
|
|
1182
|
+
}
|
|
1166
1183
|
} catch (e) {
|
|
1167
1184
|
if (!spec.awaitResult) return;
|
|
1168
1185
|
result = e instanceof Error ? `\uC624\uB958: ${e.message}` : "\uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
|
|
@@ -1170,7 +1187,15 @@ function useXbNovaChat(options) {
|
|
|
1170
1187
|
if (!spec.awaitResult) return;
|
|
1171
1188
|
const conversationId = onboarding?.conversation_id;
|
|
1172
1189
|
if (!conversationId) return;
|
|
1173
|
-
const
|
|
1190
|
+
const structured = result && typeof result === "object" ? result : null;
|
|
1191
|
+
if (structured?.view) {
|
|
1192
|
+
setClientResults((prev) => ({
|
|
1193
|
+
...prev,
|
|
1194
|
+
[part.toolCallId]: { view: structured.view, output: structured.output }
|
|
1195
|
+
}));
|
|
1196
|
+
}
|
|
1197
|
+
const rawText = structured ? structured.text : result;
|
|
1198
|
+
const resultText = typeof rawText === "string" && rawText.trim() ? rawText : "\uD578\uB4E4\uB7EC\uAC00 \uACB0\uACFC\uB97C \uBC18\uD658\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.";
|
|
1174
1199
|
setBusy(true);
|
|
1175
1200
|
try {
|
|
1176
1201
|
await resumeToolResult(
|
|
@@ -1239,7 +1264,7 @@ function useXbNovaChat(options) {
|
|
|
1239
1264
|
setBusy(false);
|
|
1240
1265
|
}
|
|
1241
1266
|
}
|
|
1242
|
-
return { onboarding, messages, busy, error, send };
|
|
1267
|
+
return { onboarding, messages, busy, error, send, clientResults };
|
|
1243
1268
|
}
|
|
1244
1269
|
|
|
1245
1270
|
// src/views.tsx
|
|
@@ -1362,9 +1387,10 @@ function ToolView({
|
|
|
1362
1387
|
|
|
1363
1388
|
// src/chat.tsx
|
|
1364
1389
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1365
|
-
function hasVisiblePart(parts, clientActionHandlers) {
|
|
1390
|
+
function hasVisiblePart(parts, clientActionHandlers, clientResults) {
|
|
1366
1391
|
return parts.some((part) => {
|
|
1367
1392
|
if (part.kind === "text") return true;
|
|
1393
|
+
if (clientResults[part.toolCallId]) return true;
|
|
1368
1394
|
const spec = clientActionHandlers.get(part.toolName);
|
|
1369
1395
|
return spec ? Boolean(spec.render) : true;
|
|
1370
1396
|
});
|
|
@@ -1497,6 +1523,7 @@ function XbNovaChat({
|
|
|
1497
1523
|
backdropStyle,
|
|
1498
1524
|
defaultOpen = false,
|
|
1499
1525
|
idleText = "AI\uC5D0\uAC8C \uC774 \uCC28 \uBB3C\uC5B4\uBCF4\uAE30",
|
|
1526
|
+
avatarSize = 28,
|
|
1500
1527
|
onVerdictAction
|
|
1501
1528
|
}) {
|
|
1502
1529
|
const { auth, trackRegistry, factsRegistry, visitorId, clientActionHandlers, theme } = useXbNovaInternal();
|
|
@@ -1504,6 +1531,7 @@ function XbNovaChat({
|
|
|
1504
1531
|
"--xbnova-accent-1": theme.accentFrom,
|
|
1505
1532
|
"--xbnova-accent-2": theme.accentTo
|
|
1506
1533
|
};
|
|
1534
|
+
const headerAvatarSize = Math.min(36, Math.max(24, Math.round(avatarSize * 0.8)));
|
|
1507
1535
|
const avatarGradientStyle = {
|
|
1508
1536
|
background: "linear-gradient(135deg, var(--xbnova-accent-1), var(--xbnova-accent-2))"
|
|
1509
1537
|
};
|
|
@@ -1519,7 +1547,7 @@ function XbNovaChat({
|
|
|
1519
1547
|
const [input, setInput] = (0, import_react9.useState)("");
|
|
1520
1548
|
const bottomRef = (0, import_react9.useRef)(null);
|
|
1521
1549
|
const typedHintText = useTypewriter(hint?.text ?? "", hint?.triggerNodeId ?? "");
|
|
1522
|
-
const { onboarding, messages, busy, error, send } = useXbNovaChat({
|
|
1550
|
+
const { onboarding, messages, busy, error, send, clientResults } = useXbNovaChat({
|
|
1523
1551
|
botId: activeBotId ?? botId,
|
|
1524
1552
|
nudgeNodeId: activeNudgeNodeId,
|
|
1525
1553
|
onDismissSheet: () => setOpen(false)
|
|
@@ -1649,11 +1677,12 @@ function XbNovaChat({
|
|
|
1649
1677
|
className: "flex shrink-0 items-center justify-center",
|
|
1650
1678
|
style: { width: collapsedSize, height: collapsedSize },
|
|
1651
1679
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1652
|
-
|
|
1680
|
+
Avatar,
|
|
1653
1681
|
{
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1682
|
+
branding: onboarding?.branding,
|
|
1683
|
+
size: avatarSize,
|
|
1684
|
+
className: "shadow-md",
|
|
1685
|
+
gradient: avatarGradientStyle
|
|
1657
1686
|
}
|
|
1658
1687
|
)
|
|
1659
1688
|
}
|
|
@@ -1697,11 +1726,12 @@ function XbNovaChat({
|
|
|
1697
1726
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex h-full w-full flex-col rounded-t-[20px] bg-white shadow-2xl sm:rounded-2xl sm:border sm:border-neutral-200 dark:bg-neutral-900 sm:dark:border-neutral-700", children: [
|
|
1698
1727
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("header", { className: "flex items-center gap-2 border-b border-neutral-200 px-3.5 py-2.5 dark:border-neutral-700", children: [
|
|
1699
1728
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1700
|
-
|
|
1729
|
+
Avatar,
|
|
1701
1730
|
{
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1731
|
+
branding: onboarding?.branding,
|
|
1732
|
+
size: headerAvatarSize,
|
|
1733
|
+
className: "shrink-0",
|
|
1734
|
+
gradient: avatarGradientStyle
|
|
1705
1735
|
}
|
|
1706
1736
|
),
|
|
1707
1737
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
@@ -1728,12 +1758,24 @@ function XbNovaChat({
|
|
|
1728
1758
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Bubble, { role: "assistant", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AssistantMarkdown, { text: part.text }) }, j);
|
|
1729
1759
|
}
|
|
1730
1760
|
if (clientActionHandlers.has(part.toolName)) {
|
|
1761
|
+
const result = clientResults[part.toolCallId];
|
|
1762
|
+
if (result) {
|
|
1763
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1764
|
+
ToolView,
|
|
1765
|
+
{
|
|
1766
|
+
part: { ...part, state: "output", view: result.view, output: result.output },
|
|
1767
|
+
viewRenderers,
|
|
1768
|
+
onVerdictAction
|
|
1769
|
+
},
|
|
1770
|
+
j
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1731
1773
|
const spec = clientActionHandlers.get(part.toolName);
|
|
1732
1774
|
return spec?.render ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { children: spec.render(part.input) }, j) : null;
|
|
1733
1775
|
}
|
|
1734
1776
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ToolView, { part, viewRenderers, onVerdictAction }, j);
|
|
1735
1777
|
}),
|
|
1736
|
-
!hasVisiblePart(message.parts, clientActionHandlers) && busy ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Bubble, { role: "assistant", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-neutral-400", children: "\uC0DD\uAC01\uD558\uB294 \uC911\u2026" }) }) : null
|
|
1778
|
+
!hasVisiblePart(message.parts, clientActionHandlers, clientResults) && busy ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Bubble, { role: "assistant", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-neutral-400", children: "\uC0DD\uAC01\uD558\uB294 \uC911\u2026" }) }) : null
|
|
1737
1779
|
] }) }, i)),
|
|
1738
1780
|
error ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rounded-lg bg-red-50 px-3 py-2 text-xs text-red-700 dark:bg-red-950 dark:text-red-300", children: error }) : null,
|
|
1739
1781
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: bottomRef })
|
|
@@ -1804,6 +1846,37 @@ function XbNovaChat({
|
|
|
1804
1846
|
)
|
|
1805
1847
|
] });
|
|
1806
1848
|
}
|
|
1849
|
+
function Avatar({
|
|
1850
|
+
branding,
|
|
1851
|
+
size,
|
|
1852
|
+
className = "",
|
|
1853
|
+
gradient
|
|
1854
|
+
}) {
|
|
1855
|
+
const box = { width: size, height: size };
|
|
1856
|
+
const logoUrl = branding?.logo_url?.trim() ?? "";
|
|
1857
|
+
const [failedUrl, setFailedUrl] = (0, import_react9.useState)("");
|
|
1858
|
+
if (logoUrl && failedUrl !== logoUrl) {
|
|
1859
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1860
|
+
"img",
|
|
1861
|
+
{
|
|
1862
|
+
src: logoUrl,
|
|
1863
|
+
alt: "",
|
|
1864
|
+
onError: () => setFailedUrl(logoUrl),
|
|
1865
|
+
style: box,
|
|
1866
|
+
className: `${className} flex-none rounded-full bg-white object-cover`
|
|
1867
|
+
}
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
const text = branding?.avatar_text?.trim() || "AI";
|
|
1871
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1872
|
+
"span",
|
|
1873
|
+
{
|
|
1874
|
+
className: `${className} flex flex-none items-center justify-center rounded-full font-bold text-white`,
|
|
1875
|
+
style: { ...gradient, ...box, fontSize: Math.max(10, Math.round(size * 0.4)) },
|
|
1876
|
+
children: text
|
|
1877
|
+
}
|
|
1878
|
+
);
|
|
1879
|
+
}
|
|
1807
1880
|
function Bubble({ role, children }) {
|
|
1808
1881
|
const mine = role === "user";
|
|
1809
1882
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: mine ? "flex justify-end" : "flex justify-start", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|