@xbrain-swe/nova-chat-widget 0.1.9 → 0.1.11
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 +264 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +264 -74
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
XbNovaTrack: () => XbNovaTrack,
|
|
39
39
|
trackXbNovaEvent: () => trackXbNovaEvent,
|
|
40
40
|
useXbNovaChat: () => useXbNovaChat,
|
|
41
|
+
useXbNovaContext: () => useXbNovaContext,
|
|
41
42
|
useXbNovaFact: () => useXbNovaFact,
|
|
42
43
|
useXbNovaFacts: () => useXbNovaFacts,
|
|
43
44
|
useXbNovaSettings: () => useXbNovaSettings,
|
|
@@ -86,6 +87,17 @@ function resolveVisitorId() {
|
|
|
86
87
|
function resolveFactValue(value) {
|
|
87
88
|
return typeof value === "function" ? value() : value;
|
|
88
89
|
}
|
|
90
|
+
function collectContextEntries(registry) {
|
|
91
|
+
const out = [];
|
|
92
|
+
for (const source of registry.values()) {
|
|
93
|
+
const entries = typeof source === "function" ? source() : source;
|
|
94
|
+
if (!Array.isArray(entries)) continue;
|
|
95
|
+
for (const e of entries) {
|
|
96
|
+
if (e && typeof e.title === "string" && typeof e.description === "string") out.push(e);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
89
101
|
function InternalProvider({
|
|
90
102
|
auth,
|
|
91
103
|
clientActionHandlers,
|
|
@@ -94,6 +106,7 @@ function InternalProvider({
|
|
|
94
106
|
}) {
|
|
95
107
|
const trackRegistry = (0, import_react.useRef)(/* @__PURE__ */ new Map()).current;
|
|
96
108
|
const factsRegistry = (0, import_react.useRef)(/* @__PURE__ */ new Map()).current;
|
|
109
|
+
const contextRegistry = (0, import_react.useRef)(/* @__PURE__ */ new Map()).current;
|
|
97
110
|
const emptyHandlers = (0, import_react.useRef)(/* @__PURE__ */ new Map()).current;
|
|
98
111
|
const visitorIdRef = (0, import_react.useRef)(null);
|
|
99
112
|
if (visitorIdRef.current === null) {
|
|
@@ -104,11 +117,12 @@ function InternalProvider({
|
|
|
104
117
|
auth,
|
|
105
118
|
trackRegistry,
|
|
106
119
|
factsRegistry,
|
|
120
|
+
contextRegistry,
|
|
107
121
|
clientActionHandlers: clientActionHandlers ?? emptyHandlers,
|
|
108
122
|
visitorId: visitorIdRef.current,
|
|
109
123
|
theme
|
|
110
124
|
}),
|
|
111
|
-
[auth, trackRegistry, factsRegistry, clientActionHandlers, emptyHandlers, theme]
|
|
125
|
+
[auth, trackRegistry, factsRegistry, contextRegistry, clientActionHandlers, emptyHandlers, theme]
|
|
112
126
|
);
|
|
113
127
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InternalContext.Provider, { value, children });
|
|
114
128
|
}
|
|
@@ -1136,7 +1150,7 @@ async function resumeToolResult(apiBaseUrl, apiKey, body, handlers) {
|
|
|
1136
1150
|
// src/use-chat.ts
|
|
1137
1151
|
var DEFAULT_CLIENT_ACTION_TIMEOUT_MS = 15e3;
|
|
1138
1152
|
function useXbNovaChat(options) {
|
|
1139
|
-
const { auth, trackRegistry, clientActionHandlers, visitorId } = useXbNovaInternal();
|
|
1153
|
+
const { auth, trackRegistry, contextRegistry, clientActionHandlers, visitorId } = useXbNovaInternal();
|
|
1140
1154
|
const botId = options?.botId ?? auth.botId;
|
|
1141
1155
|
const nudgeNodeId = options?.nudgeNodeId;
|
|
1142
1156
|
const { apiKey, apiBaseUrl } = auth;
|
|
@@ -1175,7 +1189,7 @@ function useXbNovaChat(options) {
|
|
|
1175
1189
|
next[next.length - 1] = { ...next[next.length - 1], id };
|
|
1176
1190
|
return next;
|
|
1177
1191
|
});
|
|
1178
|
-
async function setFeedback(messageId, rating) {
|
|
1192
|
+
async function setFeedback(messageId, rating, reason) {
|
|
1179
1193
|
const conversationId = onboarding?.conversation_id;
|
|
1180
1194
|
if (!conversationId || !messageId) return;
|
|
1181
1195
|
const prevRating = feedbackRef.current[messageId] ?? null;
|
|
@@ -1192,11 +1206,11 @@ function useXbNovaChat(options) {
|
|
|
1192
1206
|
{
|
|
1193
1207
|
method: "PUT",
|
|
1194
1208
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
1195
|
-
body: JSON.stringify({ rating, visitor_id: visitorId })
|
|
1209
|
+
body: JSON.stringify({ rating, visitor_id: visitorId, reason: rating === "down" ? reason ?? null : null })
|
|
1196
1210
|
}
|
|
1197
1211
|
);
|
|
1198
1212
|
if (!res.ok) throw new Error(`${res.status}`);
|
|
1199
|
-
options?.onFeedback?.({ conversationId, messageId, rating });
|
|
1213
|
+
options?.onFeedback?.({ conversationId, messageId, rating, reason });
|
|
1200
1214
|
} catch (e) {
|
|
1201
1215
|
console.warn("[xbnova] \uD53C\uB4DC\uBC31 \uC800\uC7A5 \uC2E4\uD328", e);
|
|
1202
1216
|
apply(prevRating);
|
|
@@ -1288,7 +1302,9 @@ function useXbNovaChat(options) {
|
|
|
1288
1302
|
return next;
|
|
1289
1303
|
});
|
|
1290
1304
|
const focused = getFocusedTrackEntry(trackRegistry);
|
|
1291
|
-
const
|
|
1305
|
+
const notes = collectContextEntries(contextRegistry);
|
|
1306
|
+
const screen = focused ? { region: focused.region, data: resolveFactValue(focused.data) } : {};
|
|
1307
|
+
const context = focused || notes.length ? { ...screen, ...notes.length ? { notes } : {} } : void 0;
|
|
1292
1308
|
try {
|
|
1293
1309
|
await streamChat(
|
|
1294
1310
|
apiBaseUrl,
|
|
@@ -1298,7 +1314,8 @@ function useXbNovaChat(options) {
|
|
|
1298
1314
|
message: trimmed,
|
|
1299
1315
|
conversation_id: onboarding?.conversation_id ?? null,
|
|
1300
1316
|
context,
|
|
1301
|
-
nudge_node_id: nudgeNodeId ?? null
|
|
1317
|
+
nudge_node_id: nudgeNodeId ?? null,
|
|
1318
|
+
opening_message: messages.length === 0 ? (options?.openingMessage ?? onboarding?.greeting) || null : null
|
|
1302
1319
|
},
|
|
1303
1320
|
{ onPart: replaceLast, onMessageId: setLastAssistantId, onError: setError }
|
|
1304
1321
|
);
|
|
@@ -1597,6 +1614,7 @@ function XbNovaChat({
|
|
|
1597
1614
|
onFeedback,
|
|
1598
1615
|
botId: activeBotId ?? botId,
|
|
1599
1616
|
nudgeNodeId: activeNudgeNodeId,
|
|
1617
|
+
openingMessage: openingLine ?? void 0,
|
|
1600
1618
|
onDismissSheet: () => setOpen(false)
|
|
1601
1619
|
});
|
|
1602
1620
|
const effectiveBotId = activeBotId ?? botId;
|
|
@@ -1823,38 +1841,41 @@ function XbNovaChat({
|
|
|
1823
1841
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
|
|
1824
1842
|
showDisclosure ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rounded-lg bg-neutral-50 px-3 py-2 text-[11px] text-neutral-400 dark:bg-neutral-800", children: "\uC9C0\uAE08 \uBCF4\uACE0 \uACC4\uC2E0 \uD654\uBA74\uACFC \uB9E4\uBB3C \uC815\uBCF4\uB97C \uAE30\uC900\uC73C\uB85C \uC548\uB0B4\uD574 \uB4DC\uB824\uC694." }) : null,
|
|
1825
1843
|
firstBubble ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Bubble, { role: "assistant", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AssistantMarkdown, { text: firstBubble }) }) : null,
|
|
1826
|
-
messages.map((message, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { children: message.role === "user" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Bubble, { role: "user", children: message.parts.map((p) => p.kind === "text" ? p.text : "").join("") }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1844
|
+
messages.map((message, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { children: message.role === "user" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Bubble, { role: "user", children: message.parts.map((p) => p.kind === "text" ? p.text : "").join("") }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1845
|
+
AnswerActionsFrame,
|
|
1846
|
+
{
|
|
1847
|
+
enabled: feedbackEnabled && !busy && !!message.id && message.parts.some((p) => p.kind === "text" && p.text.trim()),
|
|
1848
|
+
text: message.parts.filter((p) => p.kind === "text").map((p) => p.kind === "text" ? p.text : "").join("\n\n"),
|
|
1849
|
+
rating: message.id ? feedback[message.id] ?? null : null,
|
|
1850
|
+
isLast: i === messages.length - 1,
|
|
1851
|
+
onRate: (r, reason) => void setFeedback(message.id, r, reason),
|
|
1852
|
+
children: [
|
|
1853
|
+
message.parts.map((part, j) => {
|
|
1854
|
+
if (part.kind === "text") {
|
|
1855
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Bubble, { role: "assistant", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AssistantMarkdown, { text: part.text }) }, j);
|
|
1856
|
+
}
|
|
1857
|
+
if (clientActionHandlers.has(part.toolName)) {
|
|
1858
|
+
const result = clientResults[part.toolCallId];
|
|
1859
|
+
if (result) {
|
|
1860
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1861
|
+
ToolView,
|
|
1862
|
+
{
|
|
1863
|
+
part: { ...part, state: "output", view: result.view, output: result.output },
|
|
1864
|
+
viewRenderers,
|
|
1865
|
+
onVerdictAction
|
|
1866
|
+
},
|
|
1867
|
+
j
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
const spec = clientActionHandlers.get(part.toolName);
|
|
1871
|
+
return spec?.render ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { children: spec.render(part.input) }, j) : null;
|
|
1872
|
+
}
|
|
1873
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ToolView, { part, viewRenderers, onVerdictAction }, j);
|
|
1874
|
+
}),
|
|
1875
|
+
!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
|
|
1876
|
+
]
|
|
1877
|
+
}
|
|
1878
|
+
) }, i)),
|
|
1858
1879
|
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,
|
|
1859
1880
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: bottomRef })
|
|
1860
1881
|
] }),
|
|
@@ -1957,44 +1978,193 @@ function Avatar({
|
|
|
1957
1978
|
}
|
|
1958
1979
|
var THUMB_UP_PATH = "M7 10v12M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z";
|
|
1959
1980
|
var THUMB_DOWN_PATH = "M17 14V2M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z";
|
|
1960
|
-
|
|
1981
|
+
var COPY_PATH = "M9 9h11v11H9zM5 15H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1";
|
|
1982
|
+
var CHECK_PATH = "m5 12 5 5L20 7";
|
|
1983
|
+
var FEEDBACK_REASONS = [
|
|
1984
|
+
{ code: "inaccurate", label: "\uC815\uD655\uD558\uC9C0 \uC54A\uC544\uC694" },
|
|
1985
|
+
{ code: "irrelevant", label: "\uC9C8\uBB38\uACFC \uB2EC\uB77C\uC694" },
|
|
1986
|
+
{ code: "too_long", label: "\uB108\uBB34 \uAE38\uC5B4\uC694" },
|
|
1987
|
+
{ code: "unclear", label: "\uC774\uD574\uD558\uAE30 \uC5B4\uB824\uC6CC\uC694" }
|
|
1988
|
+
];
|
|
1989
|
+
var LONG_PRESS_MS = 420;
|
|
1990
|
+
function ActionIcon({ path, filled }) {
|
|
1991
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1992
|
+
"svg",
|
|
1993
|
+
{
|
|
1994
|
+
width: "16",
|
|
1995
|
+
height: "16",
|
|
1996
|
+
viewBox: "0 0 24 24",
|
|
1997
|
+
fill: filled ? "currentColor" : "none",
|
|
1998
|
+
stroke: "currentColor",
|
|
1999
|
+
strokeWidth: "2",
|
|
2000
|
+
strokeLinecap: "round",
|
|
2001
|
+
strokeLinejoin: "round",
|
|
2002
|
+
"aria-hidden": "true",
|
|
2003
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: path })
|
|
2004
|
+
}
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
async function copyText(text) {
|
|
2008
|
+
try {
|
|
2009
|
+
await navigator.clipboard.writeText(text);
|
|
2010
|
+
return true;
|
|
2011
|
+
} catch {
|
|
2012
|
+
try {
|
|
2013
|
+
const ta = document.createElement("textarea");
|
|
2014
|
+
ta.value = text;
|
|
2015
|
+
ta.style.position = "fixed";
|
|
2016
|
+
ta.style.opacity = "0";
|
|
2017
|
+
document.body.appendChild(ta);
|
|
2018
|
+
ta.select();
|
|
2019
|
+
const ok = document.execCommand("copy");
|
|
2020
|
+
ta.remove();
|
|
2021
|
+
return ok;
|
|
2022
|
+
} catch {
|
|
2023
|
+
return false;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
function AnswerActions({
|
|
2028
|
+
text,
|
|
1961
2029
|
rating,
|
|
1962
|
-
|
|
2030
|
+
isLast,
|
|
2031
|
+
onRate,
|
|
2032
|
+
children
|
|
1963
2033
|
}) {
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2034
|
+
const rootRef = (0, import_react9.useRef)(null);
|
|
2035
|
+
const timerRef = (0, import_react9.useRef)(null);
|
|
2036
|
+
const [menuOpen, setMenuOpen] = (0, import_react9.useState)(false);
|
|
2037
|
+
const [reasonOpen, setReasonOpen] = (0, import_react9.useState)(false);
|
|
2038
|
+
const [copied, setCopied] = (0, import_react9.useState)(false);
|
|
2039
|
+
const [entered, setEntered] = (0, import_react9.useState)(false);
|
|
2040
|
+
const clearTimer = () => {
|
|
2041
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
2042
|
+
timerRef.current = null;
|
|
2043
|
+
};
|
|
2044
|
+
(0, import_react9.useEffect)(() => clearTimer, []);
|
|
2045
|
+
(0, import_react9.useEffect)(() => {
|
|
2046
|
+
if (!menuOpen) {
|
|
2047
|
+
setEntered(false);
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
const raf = requestAnimationFrame(() => setEntered(true));
|
|
2051
|
+
const onDown = (e) => {
|
|
2052
|
+
if (!e.composedPath().includes(rootRef.current)) setMenuOpen(false);
|
|
2053
|
+
};
|
|
2054
|
+
document.addEventListener("pointerdown", onDown, true);
|
|
2055
|
+
return () => {
|
|
2056
|
+
cancelAnimationFrame(raf);
|
|
2057
|
+
document.removeEventListener("pointerdown", onDown, true);
|
|
2058
|
+
};
|
|
2059
|
+
}, [menuOpen]);
|
|
2060
|
+
const startPress = (e) => {
|
|
2061
|
+
if (e.pointerType === "mouse" || isLast) return;
|
|
2062
|
+
clearTimer();
|
|
2063
|
+
timerRef.current = setTimeout(() => {
|
|
2064
|
+
setMenuOpen(true);
|
|
2065
|
+
if (typeof navigator.vibrate === "function") navigator.vibrate(10);
|
|
2066
|
+
}, LONG_PRESS_MS);
|
|
2067
|
+
};
|
|
2068
|
+
const handleCopy = async () => {
|
|
2069
|
+
if (await copyText(text)) {
|
|
2070
|
+
setCopied(true);
|
|
2071
|
+
setTimeout(() => setCopied(false), 1400);
|
|
2072
|
+
}
|
|
2073
|
+
};
|
|
2074
|
+
const handleRate = (kind) => {
|
|
2075
|
+
if (rating === kind) {
|
|
2076
|
+
onRate(null);
|
|
2077
|
+
setReasonOpen(false);
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
onRate(kind);
|
|
2081
|
+
setReasonOpen(kind === "down");
|
|
2082
|
+
if (kind === "up") setMenuOpen(false);
|
|
2083
|
+
};
|
|
2084
|
+
const barVisible = isLast || menuOpen;
|
|
2085
|
+
const floating = menuOpen && !isLast;
|
|
2086
|
+
const btn = (label, onClick, selected, color, icon) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2087
|
+
"button",
|
|
2088
|
+
{
|
|
2089
|
+
type: "button",
|
|
2090
|
+
"aria-label": label,
|
|
2091
|
+
"aria-pressed": selected,
|
|
2092
|
+
onClick,
|
|
2093
|
+
className: `flex items-center justify-center gap-1 rounded-full text-neutral-400 transition-colors hover:text-neutral-600 dark:text-neutral-500 dark:hover:text-neutral-300 ${floating ? "h-9 px-3 text-xs font-semibold" : "h-7 w-7"}`,
|
|
2094
|
+
style: color ? { color } : void 0,
|
|
2095
|
+
children: [
|
|
2096
|
+
icon,
|
|
2097
|
+
floating ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: label }) : null
|
|
2098
|
+
]
|
|
2099
|
+
}
|
|
2100
|
+
);
|
|
2101
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2102
|
+
"div",
|
|
2103
|
+
{
|
|
2104
|
+
ref: rootRef,
|
|
2105
|
+
className: "group space-y-1",
|
|
2106
|
+
onPointerDown: startPress,
|
|
2107
|
+
onPointerUp: clearTimer,
|
|
2108
|
+
onPointerCancel: clearTimer,
|
|
2109
|
+
onPointerLeave: clearTimer,
|
|
2110
|
+
onPointerMove: (e) => {
|
|
2111
|
+
if (Math.abs(e.movementX) + Math.abs(e.movementY) > 6) clearTimer();
|
|
2112
|
+
},
|
|
2113
|
+
onContextMenu: (e) => {
|
|
2114
|
+
if (!isLast && e.nativeEvent.pointerType !== "mouse") e.preventDefault();
|
|
2115
|
+
},
|
|
2116
|
+
children: [
|
|
2117
|
+
children,
|
|
2118
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2119
|
+
"div",
|
|
1978
2120
|
{
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2121
|
+
className: `flex pl-1 ${barVisible ? "" : "hidden md:group-hover:flex"}`,
|
|
2122
|
+
style: { transition: "opacity 140ms, transform 140ms", opacity: floating && !entered ? 0 : 1, transform: floating && !entered ? "scale(0.94)" : "none" },
|
|
2123
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2124
|
+
"div",
|
|
2125
|
+
{
|
|
2126
|
+
className: `flex items-center ${floating ? "gap-0.5 rounded-full border border-neutral-200 bg-white p-1 shadow-lg dark:border-neutral-700 dark:bg-neutral-800" : "gap-0.5"}`,
|
|
2127
|
+
children: [
|
|
2128
|
+
btn(copied ? "\uBCF5\uC0AC\uB428" : "\uBCF5\uC0AC", handleCopy, false, copied ? "var(--xbnova-accent-1, #4f46e5)" : void 0, /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ActionIcon, { path: copied ? CHECK_PATH : COPY_PATH })),
|
|
2129
|
+
btn(
|
|
2130
|
+
"\uC88B\uC544\uC694",
|
|
2131
|
+
() => handleRate("up"),
|
|
2132
|
+
rating === "up",
|
|
2133
|
+
rating === "up" ? "var(--xbnova-accent-1, #4f46e5)" : void 0,
|
|
2134
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ActionIcon, { path: THUMB_UP_PATH, filled: rating === "up" })
|
|
2135
|
+
),
|
|
2136
|
+
btn(
|
|
2137
|
+
"\uBCC4\uB85C\uC608\uC694",
|
|
2138
|
+
() => handleRate("down"),
|
|
2139
|
+
rating === "down",
|
|
2140
|
+
rating === "down" ? "#dc2626" : void 0,
|
|
2141
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ActionIcon, { path: THUMB_DOWN_PATH, filled: rating === "down" })
|
|
2142
|
+
)
|
|
2143
|
+
]
|
|
2144
|
+
}
|
|
2145
|
+
)
|
|
1989
2146
|
}
|
|
1990
|
-
)
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2147
|
+
),
|
|
2148
|
+
rating === "down" && reasonOpen ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5 pl-1 pt-0.5", children: [
|
|
2149
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-neutral-400", children: "\uC5B4\uB5A4 \uC810\uC774 \uC544\uC26C\uC6E0\uB098\uC694?" }),
|
|
2150
|
+
FEEDBACK_REASONS.map((r) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2151
|
+
"button",
|
|
2152
|
+
{
|
|
2153
|
+
type: "button",
|
|
2154
|
+
onClick: () => {
|
|
2155
|
+
onRate("down", r.code);
|
|
2156
|
+
setReasonOpen(false);
|
|
2157
|
+
setMenuOpen(false);
|
|
2158
|
+
},
|
|
2159
|
+
className: "xbnova-chip rounded-full border px-2.5 py-1 text-[11px] font-semibold transition",
|
|
2160
|
+
children: r.label
|
|
2161
|
+
},
|
|
2162
|
+
r.code
|
|
2163
|
+
))
|
|
2164
|
+
] }) : null
|
|
2165
|
+
]
|
|
2166
|
+
}
|
|
2167
|
+
);
|
|
1998
2168
|
}
|
|
1999
2169
|
function Bubble({ role, children }) {
|
|
2000
2170
|
const mine = role === "user";
|
|
@@ -2009,6 +2179,14 @@ function Bubble({ role, children }) {
|
|
|
2009
2179
|
function AssistantMarkdown({ text }) {
|
|
2010
2180
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "space-y-1.5 text-sm leading-relaxed [&_ol]:list-decimal [&_ul]:list-disc [&_ol]:pl-5 [&_ul]:pl-5 [&_li]:pl-0.5 [&_strong]:font-semibold [&_a]:underline [&_code]:rounded [&_code]:bg-black/10 [&_code]:px-1 [&_code]:py-0.5 [&_code]:text-[0.85em] [&_p]:mb-1.5 last:[&_p]:mb-0", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default, import_remark_breaks.default], children: text }) });
|
|
2011
2181
|
}
|
|
2182
|
+
function AnswerActionsFrame({
|
|
2183
|
+
enabled,
|
|
2184
|
+
children,
|
|
2185
|
+
...rest
|
|
2186
|
+
}) {
|
|
2187
|
+
if (!enabled) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "space-y-1", children });
|
|
2188
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AnswerActions, { ...rest, children });
|
|
2189
|
+
}
|
|
2012
2190
|
|
|
2013
2191
|
// src/facts.ts
|
|
2014
2192
|
var import_react10 = require("react");
|
|
@@ -2040,6 +2218,18 @@ function useXbNovaFacts(values) {
|
|
|
2040
2218
|
};
|
|
2041
2219
|
}, [factsRegistry]);
|
|
2042
2220
|
}
|
|
2221
|
+
function useXbNovaContext(entries) {
|
|
2222
|
+
const { contextRegistry } = useXbNovaInternal();
|
|
2223
|
+
const id = (0, import_react10.useId)();
|
|
2224
|
+
(0, import_react10.useEffect)(() => {
|
|
2225
|
+
contextRegistry.set(id, entries);
|
|
2226
|
+
});
|
|
2227
|
+
(0, import_react10.useEffect)(() => {
|
|
2228
|
+
return () => {
|
|
2229
|
+
contextRegistry.delete(id);
|
|
2230
|
+
};
|
|
2231
|
+
}, [id, contextRegistry]);
|
|
2232
|
+
}
|
|
2043
2233
|
function useXbNovaVisitor() {
|
|
2044
2234
|
const { visitorId } = useXbNovaInternal();
|
|
2045
2235
|
return visitorId;
|
|
@@ -2061,6 +2251,7 @@ function XbNovaFacts({ values }) {
|
|
|
2061
2251
|
XbNovaTrack,
|
|
2062
2252
|
trackXbNovaEvent,
|
|
2063
2253
|
useXbNovaChat,
|
|
2254
|
+
useXbNovaContext,
|
|
2064
2255
|
useXbNovaFact,
|
|
2065
2256
|
useXbNovaFacts,
|
|
2066
2257
|
useXbNovaSettings,
|