@xbrain-swe/nova-chat-widget 0.1.9 → 0.1.10
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 +229 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +229 -69
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1175,7 +1175,7 @@ function useXbNovaChat(options) {
|
|
|
1175
1175
|
next[next.length - 1] = { ...next[next.length - 1], id };
|
|
1176
1176
|
return next;
|
|
1177
1177
|
});
|
|
1178
|
-
async function setFeedback(messageId, rating) {
|
|
1178
|
+
async function setFeedback(messageId, rating, reason) {
|
|
1179
1179
|
const conversationId = onboarding?.conversation_id;
|
|
1180
1180
|
if (!conversationId || !messageId) return;
|
|
1181
1181
|
const prevRating = feedbackRef.current[messageId] ?? null;
|
|
@@ -1192,11 +1192,11 @@ function useXbNovaChat(options) {
|
|
|
1192
1192
|
{
|
|
1193
1193
|
method: "PUT",
|
|
1194
1194
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
1195
|
-
body: JSON.stringify({ rating, visitor_id: visitorId })
|
|
1195
|
+
body: JSON.stringify({ rating, visitor_id: visitorId, reason: rating === "down" ? reason ?? null : null })
|
|
1196
1196
|
}
|
|
1197
1197
|
);
|
|
1198
1198
|
if (!res.ok) throw new Error(`${res.status}`);
|
|
1199
|
-
options?.onFeedback?.({ conversationId, messageId, rating });
|
|
1199
|
+
options?.onFeedback?.({ conversationId, messageId, rating, reason });
|
|
1200
1200
|
} catch (e) {
|
|
1201
1201
|
console.warn("[xbnova] \uD53C\uB4DC\uBC31 \uC800\uC7A5 \uC2E4\uD328", e);
|
|
1202
1202
|
apply(prevRating);
|
|
@@ -1823,38 +1823,41 @@ function XbNovaChat({
|
|
|
1823
1823
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
|
|
1824
1824
|
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
1825
|
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
|
-
|
|
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
|
+
AnswerActionsFrame,
|
|
1828
|
+
{
|
|
1829
|
+
enabled: feedbackEnabled && !busy && !!message.id && message.parts.some((p) => p.kind === "text" && p.text.trim()),
|
|
1830
|
+
text: message.parts.filter((p) => p.kind === "text").map((p) => p.kind === "text" ? p.text : "").join("\n\n"),
|
|
1831
|
+
rating: message.id ? feedback[message.id] ?? null : null,
|
|
1832
|
+
isLast: i === messages.length - 1,
|
|
1833
|
+
onRate: (r, reason) => void setFeedback(message.id, r, reason),
|
|
1834
|
+
children: [
|
|
1835
|
+
message.parts.map((part, j) => {
|
|
1836
|
+
if (part.kind === "text") {
|
|
1837
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Bubble, { role: "assistant", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AssistantMarkdown, { text: part.text }) }, j);
|
|
1838
|
+
}
|
|
1839
|
+
if (clientActionHandlers.has(part.toolName)) {
|
|
1840
|
+
const result = clientResults[part.toolCallId];
|
|
1841
|
+
if (result) {
|
|
1842
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1843
|
+
ToolView,
|
|
1844
|
+
{
|
|
1845
|
+
part: { ...part, state: "output", view: result.view, output: result.output },
|
|
1846
|
+
viewRenderers,
|
|
1847
|
+
onVerdictAction
|
|
1848
|
+
},
|
|
1849
|
+
j
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1852
|
+
const spec = clientActionHandlers.get(part.toolName);
|
|
1853
|
+
return spec?.render ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { children: spec.render(part.input) }, j) : null;
|
|
1854
|
+
}
|
|
1855
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ToolView, { part, viewRenderers, onVerdictAction }, j);
|
|
1856
|
+
}),
|
|
1857
|
+
!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
|
|
1858
|
+
]
|
|
1859
|
+
}
|
|
1860
|
+
) }, i)),
|
|
1858
1861
|
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
1862
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: bottomRef })
|
|
1860
1863
|
] }),
|
|
@@ -1957,44 +1960,193 @@ function Avatar({
|
|
|
1957
1960
|
}
|
|
1958
1961
|
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
1962
|
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
|
-
|
|
1963
|
+
var COPY_PATH = "M9 9h11v11H9zM5 15H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1";
|
|
1964
|
+
var CHECK_PATH = "m5 12 5 5L20 7";
|
|
1965
|
+
var FEEDBACK_REASONS = [
|
|
1966
|
+
{ code: "inaccurate", label: "\uC815\uD655\uD558\uC9C0 \uC54A\uC544\uC694" },
|
|
1967
|
+
{ code: "irrelevant", label: "\uC9C8\uBB38\uACFC \uB2EC\uB77C\uC694" },
|
|
1968
|
+
{ code: "too_long", label: "\uB108\uBB34 \uAE38\uC5B4\uC694" },
|
|
1969
|
+
{ code: "unclear", label: "\uC774\uD574\uD558\uAE30 \uC5B4\uB824\uC6CC\uC694" }
|
|
1970
|
+
];
|
|
1971
|
+
var LONG_PRESS_MS = 420;
|
|
1972
|
+
function ActionIcon({ path, filled }) {
|
|
1973
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1974
|
+
"svg",
|
|
1975
|
+
{
|
|
1976
|
+
width: "16",
|
|
1977
|
+
height: "16",
|
|
1978
|
+
viewBox: "0 0 24 24",
|
|
1979
|
+
fill: filled ? "currentColor" : "none",
|
|
1980
|
+
stroke: "currentColor",
|
|
1981
|
+
strokeWidth: "2",
|
|
1982
|
+
strokeLinecap: "round",
|
|
1983
|
+
strokeLinejoin: "round",
|
|
1984
|
+
"aria-hidden": "true",
|
|
1985
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: path })
|
|
1986
|
+
}
|
|
1987
|
+
);
|
|
1988
|
+
}
|
|
1989
|
+
async function copyText(text) {
|
|
1990
|
+
try {
|
|
1991
|
+
await navigator.clipboard.writeText(text);
|
|
1992
|
+
return true;
|
|
1993
|
+
} catch {
|
|
1994
|
+
try {
|
|
1995
|
+
const ta = document.createElement("textarea");
|
|
1996
|
+
ta.value = text;
|
|
1997
|
+
ta.style.position = "fixed";
|
|
1998
|
+
ta.style.opacity = "0";
|
|
1999
|
+
document.body.appendChild(ta);
|
|
2000
|
+
ta.select();
|
|
2001
|
+
const ok = document.execCommand("copy");
|
|
2002
|
+
ta.remove();
|
|
2003
|
+
return ok;
|
|
2004
|
+
} catch {
|
|
2005
|
+
return false;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
function AnswerActions({
|
|
2010
|
+
text,
|
|
1961
2011
|
rating,
|
|
1962
|
-
|
|
2012
|
+
isLast,
|
|
2013
|
+
onRate,
|
|
2014
|
+
children
|
|
1963
2015
|
}) {
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2016
|
+
const rootRef = (0, import_react9.useRef)(null);
|
|
2017
|
+
const timerRef = (0, import_react9.useRef)(null);
|
|
2018
|
+
const [menuOpen, setMenuOpen] = (0, import_react9.useState)(false);
|
|
2019
|
+
const [reasonOpen, setReasonOpen] = (0, import_react9.useState)(false);
|
|
2020
|
+
const [copied, setCopied] = (0, import_react9.useState)(false);
|
|
2021
|
+
const [entered, setEntered] = (0, import_react9.useState)(false);
|
|
2022
|
+
const clearTimer = () => {
|
|
2023
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
2024
|
+
timerRef.current = null;
|
|
2025
|
+
};
|
|
2026
|
+
(0, import_react9.useEffect)(() => clearTimer, []);
|
|
2027
|
+
(0, import_react9.useEffect)(() => {
|
|
2028
|
+
if (!menuOpen) {
|
|
2029
|
+
setEntered(false);
|
|
2030
|
+
return;
|
|
2031
|
+
}
|
|
2032
|
+
const raf = requestAnimationFrame(() => setEntered(true));
|
|
2033
|
+
const onDown = (e) => {
|
|
2034
|
+
if (!e.composedPath().includes(rootRef.current)) setMenuOpen(false);
|
|
2035
|
+
};
|
|
2036
|
+
document.addEventListener("pointerdown", onDown, true);
|
|
2037
|
+
return () => {
|
|
2038
|
+
cancelAnimationFrame(raf);
|
|
2039
|
+
document.removeEventListener("pointerdown", onDown, true);
|
|
2040
|
+
};
|
|
2041
|
+
}, [menuOpen]);
|
|
2042
|
+
const startPress = (e) => {
|
|
2043
|
+
if (e.pointerType === "mouse" || isLast) return;
|
|
2044
|
+
clearTimer();
|
|
2045
|
+
timerRef.current = setTimeout(() => {
|
|
2046
|
+
setMenuOpen(true);
|
|
2047
|
+
if (typeof navigator.vibrate === "function") navigator.vibrate(10);
|
|
2048
|
+
}, LONG_PRESS_MS);
|
|
2049
|
+
};
|
|
2050
|
+
const handleCopy = async () => {
|
|
2051
|
+
if (await copyText(text)) {
|
|
2052
|
+
setCopied(true);
|
|
2053
|
+
setTimeout(() => setCopied(false), 1400);
|
|
2054
|
+
}
|
|
2055
|
+
};
|
|
2056
|
+
const handleRate = (kind) => {
|
|
2057
|
+
if (rating === kind) {
|
|
2058
|
+
onRate(null);
|
|
2059
|
+
setReasonOpen(false);
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
onRate(kind);
|
|
2063
|
+
setReasonOpen(kind === "down");
|
|
2064
|
+
if (kind === "up") setMenuOpen(false);
|
|
2065
|
+
};
|
|
2066
|
+
const barVisible = isLast || menuOpen;
|
|
2067
|
+
const floating = menuOpen && !isLast;
|
|
2068
|
+
const btn = (label, onClick, selected, color, icon) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2069
|
+
"button",
|
|
2070
|
+
{
|
|
2071
|
+
type: "button",
|
|
2072
|
+
"aria-label": label,
|
|
2073
|
+
"aria-pressed": selected,
|
|
2074
|
+
onClick,
|
|
2075
|
+
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"}`,
|
|
2076
|
+
style: color ? { color } : void 0,
|
|
2077
|
+
children: [
|
|
2078
|
+
icon,
|
|
2079
|
+
floating ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: label }) : null
|
|
2080
|
+
]
|
|
2081
|
+
}
|
|
2082
|
+
);
|
|
2083
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2084
|
+
"div",
|
|
2085
|
+
{
|
|
2086
|
+
ref: rootRef,
|
|
2087
|
+
className: "group space-y-1",
|
|
2088
|
+
onPointerDown: startPress,
|
|
2089
|
+
onPointerUp: clearTimer,
|
|
2090
|
+
onPointerCancel: clearTimer,
|
|
2091
|
+
onPointerLeave: clearTimer,
|
|
2092
|
+
onPointerMove: (e) => {
|
|
2093
|
+
if (Math.abs(e.movementX) + Math.abs(e.movementY) > 6) clearTimer();
|
|
2094
|
+
},
|
|
2095
|
+
onContextMenu: (e) => {
|
|
2096
|
+
if (!isLast && e.nativeEvent.pointerType !== "mouse") e.preventDefault();
|
|
2097
|
+
},
|
|
2098
|
+
children: [
|
|
2099
|
+
children,
|
|
2100
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2101
|
+
"div",
|
|
1978
2102
|
{
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2103
|
+
className: `flex pl-1 ${barVisible ? "" : "hidden md:group-hover:flex"}`,
|
|
2104
|
+
style: { transition: "opacity 140ms, transform 140ms", opacity: floating && !entered ? 0 : 1, transform: floating && !entered ? "scale(0.94)" : "none" },
|
|
2105
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2106
|
+
"div",
|
|
2107
|
+
{
|
|
2108
|
+
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"}`,
|
|
2109
|
+
children: [
|
|
2110
|
+
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 })),
|
|
2111
|
+
btn(
|
|
2112
|
+
"\uC88B\uC544\uC694",
|
|
2113
|
+
() => handleRate("up"),
|
|
2114
|
+
rating === "up",
|
|
2115
|
+
rating === "up" ? "var(--xbnova-accent-1, #4f46e5)" : void 0,
|
|
2116
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ActionIcon, { path: THUMB_UP_PATH, filled: rating === "up" })
|
|
2117
|
+
),
|
|
2118
|
+
btn(
|
|
2119
|
+
"\uBCC4\uB85C\uC608\uC694",
|
|
2120
|
+
() => handleRate("down"),
|
|
2121
|
+
rating === "down",
|
|
2122
|
+
rating === "down" ? "#dc2626" : void 0,
|
|
2123
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ActionIcon, { path: THUMB_DOWN_PATH, filled: rating === "down" })
|
|
2124
|
+
)
|
|
2125
|
+
]
|
|
2126
|
+
}
|
|
2127
|
+
)
|
|
1989
2128
|
}
|
|
1990
|
-
)
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2129
|
+
),
|
|
2130
|
+
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: [
|
|
2131
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[11px] text-neutral-400", children: "\uC5B4\uB5A4 \uC810\uC774 \uC544\uC26C\uC6E0\uB098\uC694?" }),
|
|
2132
|
+
FEEDBACK_REASONS.map((r) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2133
|
+
"button",
|
|
2134
|
+
{
|
|
2135
|
+
type: "button",
|
|
2136
|
+
onClick: () => {
|
|
2137
|
+
onRate("down", r.code);
|
|
2138
|
+
setReasonOpen(false);
|
|
2139
|
+
setMenuOpen(false);
|
|
2140
|
+
},
|
|
2141
|
+
className: "xbnova-chip rounded-full border px-2.5 py-1 text-[11px] font-semibold transition",
|
|
2142
|
+
children: r.label
|
|
2143
|
+
},
|
|
2144
|
+
r.code
|
|
2145
|
+
))
|
|
2146
|
+
] }) : null
|
|
2147
|
+
]
|
|
2148
|
+
}
|
|
2149
|
+
);
|
|
1998
2150
|
}
|
|
1999
2151
|
function Bubble({ role, children }) {
|
|
2000
2152
|
const mine = role === "user";
|
|
@@ -2009,6 +2161,14 @@ function Bubble({ role, children }) {
|
|
|
2009
2161
|
function AssistantMarkdown({ text }) {
|
|
2010
2162
|
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
2163
|
}
|
|
2164
|
+
function AnswerActionsFrame({
|
|
2165
|
+
enabled,
|
|
2166
|
+
children,
|
|
2167
|
+
...rest
|
|
2168
|
+
}) {
|
|
2169
|
+
if (!enabled) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "space-y-1", children });
|
|
2170
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AnswerActions, { ...rest, children });
|
|
2171
|
+
}
|
|
2012
2172
|
|
|
2013
2173
|
// src/facts.ts
|
|
2014
2174
|
var import_react10 = require("react");
|