@huskel/sdk 0.4.1 → 0.4.2
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.css +56 -0
- package/dist/index.css.map +1 -1
- package/dist/index.js +214 -104
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +180 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -557,6 +557,7 @@ function useChat() {
|
|
|
557
557
|
if (!query.trim() || loading) return;
|
|
558
558
|
(_a = abortRef.current) == null ? void 0 : _a.abort();
|
|
559
559
|
abortRef.current = new AbortController();
|
|
560
|
+
const signal = abortRef.current.signal;
|
|
560
561
|
const userMsg = { role: "user", content: query };
|
|
561
562
|
setMessages((prev) => [...prev, userMsg]);
|
|
562
563
|
setLoading(true);
|
|
@@ -564,20 +565,42 @@ function useChat() {
|
|
|
564
565
|
try {
|
|
565
566
|
const history = messages.map((m) => ({ role: m.role, content: m.content }));
|
|
566
567
|
const res = await client.api.chat(query, history);
|
|
567
|
-
|
|
568
|
-
|
|
568
|
+
if (signal.aborted) return;
|
|
569
|
+
const fullAnswer = res.answer || "";
|
|
570
|
+
const words = fullAnswer.split(/(\s+)/);
|
|
571
|
+
setMessages((prev) => [...prev, { role: "assistant", content: "" }]);
|
|
572
|
+
let currentContent = "";
|
|
573
|
+
for (const word of words) {
|
|
574
|
+
if (signal.aborted) return;
|
|
575
|
+
currentContent += word;
|
|
576
|
+
setMessages((prev) => {
|
|
577
|
+
const next = [...prev];
|
|
578
|
+
if (next.length > 0) {
|
|
579
|
+
next[next.length - 1] = { role: "assistant", content: currentContent };
|
|
580
|
+
}
|
|
581
|
+
return next;
|
|
582
|
+
});
|
|
583
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
584
|
+
}
|
|
585
|
+
if (signal.aborted) return;
|
|
569
586
|
setSources((_b = res.sources) != null ? _b : []);
|
|
570
587
|
} catch (e) {
|
|
588
|
+
if (signal.aborted) return;
|
|
571
589
|
setError((_c = e == null ? void 0 : e.message) != null ? _c : "Chat request failed");
|
|
572
590
|
setMessages((prev) => prev.slice(0, -1));
|
|
573
591
|
} finally {
|
|
574
|
-
|
|
592
|
+
if (!signal.aborted) {
|
|
593
|
+
setLoading(false);
|
|
594
|
+
}
|
|
575
595
|
}
|
|
576
596
|
}, [client, messages, loading]);
|
|
577
597
|
const reset = (0, import_react6.useCallback)(() => {
|
|
598
|
+
var _a;
|
|
599
|
+
(_a = abortRef.current) == null ? void 0 : _a.abort();
|
|
578
600
|
setMessages([]);
|
|
579
601
|
setSources([]);
|
|
580
602
|
setError(null);
|
|
603
|
+
setLoading(false);
|
|
581
604
|
}, []);
|
|
582
605
|
return { messages, sources, loading, error, send, reset };
|
|
583
606
|
}
|
|
@@ -866,19 +889,106 @@ function Sparkle({
|
|
|
866
889
|
|
|
867
890
|
// src/components/ChatWidget.tsx
|
|
868
891
|
var import_react9 = require("react");
|
|
892
|
+
|
|
893
|
+
// src/utils/markdown.tsx
|
|
869
894
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
870
|
-
var
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
895
|
+
var parseInline = (text, keyPrefix) => {
|
|
896
|
+
const tokenRegex = /(\[[^\]]+\]\([^)]+\)|\*\*[^*]+\*\*|`[^`]+`)/g;
|
|
897
|
+
const parts = text.split(tokenRegex);
|
|
898
|
+
return parts.map((part, index) => {
|
|
899
|
+
if (!part) return null;
|
|
900
|
+
const key = `${keyPrefix}-inline-${index}`;
|
|
901
|
+
if (part.startsWith("`") && part.endsWith("`")) {
|
|
902
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { className: "hsk-markdown-code", children: part.slice(1, -1) }, key);
|
|
903
|
+
}
|
|
904
|
+
if (part.startsWith("**") && part.endsWith("**")) {
|
|
905
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: parseInline(part.slice(2, -2), key) }, key);
|
|
906
|
+
}
|
|
907
|
+
const linkMatch = part.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
|
|
908
|
+
if (linkMatch) {
|
|
909
|
+
const url = linkMatch[2];
|
|
910
|
+
const isSafeUrl = /^(https?|mailto|tel):/i.test(url) || url.startsWith("/");
|
|
911
|
+
if (isSafeUrl) {
|
|
912
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: "hsk-markdown-link", children: parseInline(linkMatch[1], key) }, key);
|
|
913
|
+
}
|
|
914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: parseInline(linkMatch[1], key) }, key);
|
|
915
|
+
}
|
|
916
|
+
return part;
|
|
917
|
+
});
|
|
918
|
+
};
|
|
919
|
+
function renderMarkdown(content) {
|
|
920
|
+
const lines = content.split("\n");
|
|
921
|
+
const elements = [];
|
|
922
|
+
let i = 0;
|
|
923
|
+
while (i < lines.length) {
|
|
924
|
+
const line = lines[i];
|
|
925
|
+
const key = `md-line-${i}`;
|
|
926
|
+
if (!line.trim()) {
|
|
927
|
+
i++;
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
const headerMatch = line.match(/^(#{1,3})\s+(.*)/);
|
|
931
|
+
if (headerMatch) {
|
|
932
|
+
const level = headerMatch[1].length;
|
|
933
|
+
const Tag = `h${level + 3}`;
|
|
934
|
+
elements.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tag, { className: `hsk-markdown-h${level}`, children: parseInline(headerMatch[2], key) }, key));
|
|
935
|
+
i++;
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
if (line.match(/^[-*]\s+/)) {
|
|
939
|
+
const listItems = [];
|
|
940
|
+
while (i < lines.length && lines[i].match(/^[-*]\s+/)) {
|
|
941
|
+
const itemText = lines[i].replace(/^[-*]\s+/, "");
|
|
942
|
+
listItems.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: parseInline(itemText, `li-${i}`) }, `li-${i}`));
|
|
943
|
+
i++;
|
|
944
|
+
}
|
|
945
|
+
elements.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "hsk-markdown-list", children: listItems }, `ul-${key}`));
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
if (line.trim().startsWith("|")) {
|
|
949
|
+
const tableRows = [];
|
|
950
|
+
let isHeader = true;
|
|
951
|
+
while (i < lines.length && lines[i].trim().startsWith("|")) {
|
|
952
|
+
const rowLine = lines[i].trim();
|
|
953
|
+
if (rowLine.match(/^\|[-:| ]+\|$/)) {
|
|
954
|
+
i++;
|
|
955
|
+
isHeader = false;
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
const cells = rowLine.split("|").slice(1, -1).map((c) => c.trim());
|
|
959
|
+
const Tag = isHeader ? "th" : "td";
|
|
960
|
+
tableRows.push(
|
|
961
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tr", { children: cells.map((cell, cIdx) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tag, { children: parseInline(cell, `td-${i}-${cIdx}`) }, `td-${i}-${cIdx}`)) }, `tr-${i}`)
|
|
962
|
+
);
|
|
963
|
+
i++;
|
|
964
|
+
}
|
|
965
|
+
elements.push(
|
|
966
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-table-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("table", { className: "hsk-markdown-table", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: tableRows }) }) }, `table-wrapper-${key}`)
|
|
967
|
+
);
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
elements.push(
|
|
971
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "hsk-markdown-p", children: parseInline(line, key) }, key)
|
|
972
|
+
);
|
|
973
|
+
i++;
|
|
974
|
+
}
|
|
975
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: elements });
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// src/components/ChatWidget.tsx
|
|
979
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
980
|
+
var SparkleIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" }) });
|
|
981
|
+
var ArrowUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
982
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m5 12 7-7 7 7" }),
|
|
983
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 19V5" })
|
|
874
984
|
] });
|
|
875
985
|
function SourceCard({ source, defaultCurrency, onSelect }) {
|
|
876
986
|
var _a;
|
|
877
|
-
return /* @__PURE__ */ (0,
|
|
878
|
-
source.image && /* @__PURE__ */ (0,
|
|
879
|
-
/* @__PURE__ */ (0,
|
|
880
|
-
/* @__PURE__ */ (0,
|
|
881
|
-
source.price && /* @__PURE__ */ (0,
|
|
987
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-source-card", onClick: () => onSelect == null ? void 0 : onSelect(source), children: [
|
|
988
|
+
source.image && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: source.image, alt: source.name, className: "hsk-source-img" }),
|
|
989
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
990
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-source-name", children: source.name }),
|
|
991
|
+
source.price && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-source-price", children: [
|
|
882
992
|
(_a = source.currency) != null ? _a : defaultCurrency,
|
|
883
993
|
" ",
|
|
884
994
|
source.price
|
|
@@ -925,43 +1035,43 @@ function ChatWidget({
|
|
|
925
1035
|
t.style.height = Math.min(t.scrollHeight, 120) + "px";
|
|
926
1036
|
};
|
|
927
1037
|
const customStyles = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, (theme == null ? void 0 : theme.primaryColor) && { "--hsk-primary": theme.primaryColor }), (theme == null ? void 0 : theme.backgroundColor) && { "--hsk-bg": theme.backgroundColor }), (theme == null ? void 0 : theme.textColor) && { "--hsk-text": theme.textColor }), (theme == null ? void 0 : theme.fontFamily) && { "--hsk-font": theme.fontFamily });
|
|
928
|
-
return /* @__PURE__ */ (0,
|
|
1038
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
929
1039
|
"div",
|
|
930
1040
|
{
|
|
931
1041
|
className: `hsk-chat-widget ${classNames.root || ""} ${className || ""}`,
|
|
932
1042
|
style: customStyles,
|
|
933
1043
|
children: [
|
|
934
|
-
/* @__PURE__ */ (0,
|
|
935
|
-
/* @__PURE__ */ (0,
|
|
936
|
-
/* @__PURE__ */ (0,
|
|
937
|
-
/* @__PURE__ */ (0,
|
|
938
|
-
messages.length > 0 && /* @__PURE__ */ (0,
|
|
1044
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `hsk-chat-header ${classNames.header || ""}`, children: [
|
|
1045
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-chat-header-icon", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) }),
|
|
1046
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-chat-title", children: title }),
|
|
1047
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-chat-badge", children: "AI" }),
|
|
1048
|
+
messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "hsk-chat-reset", onClick: reset, style: { marginLeft: "auto" }, children: "Clear" })
|
|
939
1049
|
] }),
|
|
940
|
-
/* @__PURE__ */ (0,
|
|
941
|
-
messages.length === 0 ? /* @__PURE__ */ (0,
|
|
942
|
-
/* @__PURE__ */ (0,
|
|
943
|
-
/* @__PURE__ */ (0,
|
|
944
|
-
/* @__PURE__ */ (0,
|
|
945
|
-
] }) : messages.map((msg, idx) => /* @__PURE__ */ (0,
|
|
946
|
-
/* @__PURE__ */ (0,
|
|
947
|
-
/* @__PURE__ */ (0,
|
|
948
|
-
/* @__PURE__ */ (0,
|
|
1050
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-chat-messages", children: [
|
|
1051
|
+
messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-chat-empty", children: [
|
|
1052
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-chat-empty-icon", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) }),
|
|
1053
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: emptyStateText }),
|
|
1054
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-chat-empty-suggestions", children: emptyStateSuggestions })
|
|
1055
|
+
] }) : messages.map((msg, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
1056
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `hsk-msg-row ${msg.role}`, children: [
|
|
1057
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `hsk-msg-avatar ${msg.role === "assistant" ? "ai" : "user"}`, children: msg.role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) : "U" }),
|
|
1058
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `hsk-msg-bubble ${msg.role} ${classNames.messageBubble || ""}`, children: renderMarkdown(msg.content) })
|
|
949
1059
|
] }),
|
|
950
|
-
msg.role === "assistant" && idx === messages.length - 1 && sources.length > 0 && /* @__PURE__ */ (0,
|
|
1060
|
+
msg.role === "assistant" && idx === messages.length - 1 && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-sources-container", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-sources", children: sources.map((src, si) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SourceCard, { source: src, defaultCurrency, onSelect: onSelectSource }, si)) }) })
|
|
951
1061
|
] }, idx)),
|
|
952
|
-
loading && /* @__PURE__ */ (0,
|
|
953
|
-
/* @__PURE__ */ (0,
|
|
954
|
-
/* @__PURE__ */ (0,
|
|
955
|
-
/* @__PURE__ */ (0,
|
|
956
|
-
/* @__PURE__ */ (0,
|
|
957
|
-
/* @__PURE__ */ (0,
|
|
1062
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-msg-row", children: [
|
|
1063
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-msg-avatar ai", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) }),
|
|
1064
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-typing", children: [
|
|
1065
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-typing-dot" }),
|
|
1066
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-typing-dot" }),
|
|
1067
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-typing-dot" })
|
|
958
1068
|
] })
|
|
959
1069
|
] }),
|
|
960
|
-
error && /* @__PURE__ */ (0,
|
|
961
|
-
/* @__PURE__ */ (0,
|
|
1070
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-chat-error", children: error }),
|
|
1071
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: bottomRef })
|
|
962
1072
|
] }),
|
|
963
|
-
/* @__PURE__ */ (0,
|
|
964
|
-
/* @__PURE__ */ (0,
|
|
1073
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-chat-input-area", children: [
|
|
1074
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
965
1075
|
"textarea",
|
|
966
1076
|
{
|
|
967
1077
|
ref: textareaRef,
|
|
@@ -974,14 +1084,14 @@ function ChatWidget({
|
|
|
974
1084
|
disabled: loading
|
|
975
1085
|
}
|
|
976
1086
|
),
|
|
977
|
-
/* @__PURE__ */ (0,
|
|
1087
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
978
1088
|
"button",
|
|
979
1089
|
{
|
|
980
1090
|
className: "hsk-chat-send",
|
|
981
1091
|
onClick: handleSend,
|
|
982
1092
|
disabled: !input.trim() || loading,
|
|
983
1093
|
"aria-label": "Send message",
|
|
984
|
-
children: /* @__PURE__ */ (0,
|
|
1094
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArrowUpIcon, {})
|
|
985
1095
|
}
|
|
986
1096
|
)
|
|
987
1097
|
] })
|
|
@@ -993,17 +1103,17 @@ function ChatWidget({
|
|
|
993
1103
|
// src/components/AIChatButton.tsx
|
|
994
1104
|
var import_react10 = require("react");
|
|
995
1105
|
var import_react_dom2 = require("react-dom");
|
|
996
|
-
var
|
|
997
|
-
var SparkleIcon3 = ({ className }) => /* @__PURE__ */ (0,
|
|
998
|
-
var ArrowUpIcon2 = () => /* @__PURE__ */ (0,
|
|
999
|
-
/* @__PURE__ */ (0,
|
|
1000
|
-
/* @__PURE__ */ (0,
|
|
1106
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1107
|
+
var SparkleIcon3 = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" }) });
|
|
1108
|
+
var ArrowUpIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1109
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m5 12 7-7 7 7" }),
|
|
1110
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 19V5" })
|
|
1001
1111
|
] });
|
|
1002
|
-
var CloseIcon2 = () => /* @__PURE__ */ (0,
|
|
1003
|
-
/* @__PURE__ */ (0,
|
|
1004
|
-
/* @__PURE__ */ (0,
|
|
1112
|
+
var CloseIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1113
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1114
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
1005
1115
|
] });
|
|
1006
|
-
var ChevronRightIcon = () => /* @__PURE__ */ (0,
|
|
1116
|
+
var ChevronRightIcon = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m9 18 6-6-6-6" }) });
|
|
1007
1117
|
var DEFAULT_CHIPS = [
|
|
1008
1118
|
"Cheapest smartphone",
|
|
1009
1119
|
"Smart TV under KSh 20,000",
|
|
@@ -1035,20 +1145,20 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
|
1035
1145
|
var _a;
|
|
1036
1146
|
(_a = railRef.current) == null ? void 0 : _a.scrollBy({ left: 170, behavior: "smooth" });
|
|
1037
1147
|
};
|
|
1038
|
-
return /* @__PURE__ */ (0,
|
|
1039
|
-
/* @__PURE__ */ (0,
|
|
1148
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-sources-wrap", children: [
|
|
1149
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-sources", ref: railRef, children: sources.map((src, si) => {
|
|
1040
1150
|
var _a;
|
|
1041
|
-
return /* @__PURE__ */ (0,
|
|
1151
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1042
1152
|
"div",
|
|
1043
1153
|
{
|
|
1044
1154
|
className: "hsk-cb-source",
|
|
1045
1155
|
style: { animationDelay: `${si * 50}ms` },
|
|
1046
1156
|
onClick: () => onSelectSource == null ? void 0 : onSelectSource(src),
|
|
1047
1157
|
children: [
|
|
1048
|
-
src.image ? /* @__PURE__ */ (0,
|
|
1049
|
-
/* @__PURE__ */ (0,
|
|
1050
|
-
/* @__PURE__ */ (0,
|
|
1051
|
-
src.price && /* @__PURE__ */ (0,
|
|
1158
|
+
src.image ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-src-imgwrap", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: src.image, alt: src.name, loading: "lazy" }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-src-imgwrap-empty", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
|
|
1159
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-src-info", children: [
|
|
1160
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-src-name", children: src.name }),
|
|
1161
|
+
src.price && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-src-price", children: [
|
|
1052
1162
|
(_a = src.currency) != null ? _a : defaultCurrency,
|
|
1053
1163
|
" ",
|
|
1054
1164
|
parseFloat(src.price.replace(/[^0-9.]/g, "") || "0").toLocaleString()
|
|
@@ -1059,15 +1169,15 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
|
1059
1169
|
si
|
|
1060
1170
|
);
|
|
1061
1171
|
}) }),
|
|
1062
|
-
showNext && /* @__PURE__ */ (0,
|
|
1063
|
-
/* @__PURE__ */ (0,
|
|
1172
|
+
showNext && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1173
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1064
1174
|
"div",
|
|
1065
1175
|
{
|
|
1066
1176
|
className: "hsk-cb-sources-fade",
|
|
1067
1177
|
style: { background: "linear-gradient(to right, transparent, var(--hsk-fade-bg, #0e0e0f))" }
|
|
1068
1178
|
}
|
|
1069
1179
|
),
|
|
1070
|
-
/* @__PURE__ */ (0,
|
|
1180
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-sources-next", onClick: scrollNext, "aria-label": "See more", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronRightIcon, {}) })
|
|
1071
1181
|
] })
|
|
1072
1182
|
] });
|
|
1073
1183
|
}
|
|
@@ -1131,7 +1241,7 @@ function ChatModal({
|
|
|
1131
1241
|
};
|
|
1132
1242
|
const blurVal = typeof backdropBlur === "number" ? `${backdropBlur}px` : backdropBlur != null ? backdropBlur : "20px";
|
|
1133
1243
|
const customStyles = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, (theme == null ? void 0 : theme.primaryColor) && { "--hsk-primary": theme.primaryColor }), (theme == null ? void 0 : theme.backgroundColor) && { "--hsk-bg": theme.backgroundColor }), (theme == null ? void 0 : theme.textColor) && { "--hsk-text": theme.textColor }), (theme == null ? void 0 : theme.fontFamily) && { "--hsk-font": theme.fontFamily });
|
|
1134
|
-
return /* @__PURE__ */ (0,
|
|
1244
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1135
1245
|
"div",
|
|
1136
1246
|
{
|
|
1137
1247
|
className: `hsk-cb-overlay ${classNames.overlay || ""}`,
|
|
@@ -1140,26 +1250,26 @@ function ChatModal({
|
|
|
1140
1250
|
backdropFilter: `blur(${blurVal})`,
|
|
1141
1251
|
WebkitBackdropFilter: `blur(${blurVal})`
|
|
1142
1252
|
}, backdropColor ? { background: backdropColor } : {}), customStyles),
|
|
1143
|
-
children: /* @__PURE__ */ (0,
|
|
1144
|
-
/* @__PURE__ */ (0,
|
|
1145
|
-
/* @__PURE__ */ (0,
|
|
1146
|
-
/* @__PURE__ */ (0,
|
|
1147
|
-
/* @__PURE__ */ (0,
|
|
1148
|
-
/* @__PURE__ */ (0,
|
|
1149
|
-
/* @__PURE__ */ (0,
|
|
1253
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `hsk-cb-panel ${classNames.panel || ""}`, onClick: (e) => e.stopPropagation(), children: [
|
|
1254
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-topbar", children: [
|
|
1255
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-topbar-left", children: [
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "hsk-cb-topbar-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
|
|
1257
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1258
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-topbar-title", children: title }),
|
|
1259
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-topbar-sub", children: "Powered by Huskel AI \xB7 searches the whole catalogue" })
|
|
1150
1260
|
] })
|
|
1151
1261
|
] }),
|
|
1152
|
-
/* @__PURE__ */ (0,
|
|
1153
|
-
messages.length > 0 && /* @__PURE__ */ (0,
|
|
1154
|
-
/* @__PURE__ */ (0,
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-topbar-actions", children: [
|
|
1263
|
+
messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-topbar-btn", onClick: reset, children: "Clear chat" }),
|
|
1264
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CloseIcon2, {}) })
|
|
1155
1265
|
] })
|
|
1156
1266
|
] }),
|
|
1157
|
-
/* @__PURE__ */ (0,
|
|
1158
|
-
messages.length === 0 ? /* @__PURE__ */ (0,
|
|
1159
|
-
/* @__PURE__ */ (0,
|
|
1160
|
-
/* @__PURE__ */ (0,
|
|
1161
|
-
/* @__PURE__ */ (0,
|
|
1162
|
-
/* @__PURE__ */ (0,
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-msgs", children: [
|
|
1268
|
+
messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-empty", children: [
|
|
1269
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-empty-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-empty-title", children: "What can I help you find?" }),
|
|
1271
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-empty-sub", children: "Ask about products, budgets, gift ideas, specs \u2014 I'll search the entire catalogue for you." }),
|
|
1272
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-chips", children: chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1163
1273
|
"button",
|
|
1164
1274
|
{
|
|
1165
1275
|
className: "hsk-cb-chip",
|
|
@@ -1171,11 +1281,11 @@ function ChatModal({
|
|
|
1171
1281
|
] }) : messages.map((msg, idx) => {
|
|
1172
1282
|
const isLast = idx === messages.length - 1;
|
|
1173
1283
|
const isUser = msg.role === "user";
|
|
1174
|
-
return /* @__PURE__ */ (0,
|
|
1175
|
-
/* @__PURE__ */ (0,
|
|
1176
|
-
/* @__PURE__ */ (0,
|
|
1177
|
-
/* @__PURE__ */ (0,
|
|
1178
|
-
isLast && sources.length > 0 && /* @__PURE__ */ (0,
|
|
1284
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-msg-group", children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-user-msg", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-user-bubble", children: msg.content }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-ai-msg", children: [
|
|
1285
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
|
|
1286
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-ai-body", children: [
|
|
1287
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }),
|
|
1288
|
+
isLast && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1179
1289
|
SourcesCarousel,
|
|
1180
1290
|
{
|
|
1181
1291
|
sources,
|
|
@@ -1186,16 +1296,16 @@ function ChatModal({
|
|
|
1186
1296
|
] })
|
|
1187
1297
|
] }) }, idx);
|
|
1188
1298
|
}),
|
|
1189
|
-
selectedProduct && loading && /* @__PURE__ */ (0,
|
|
1299
|
+
selectedProduct && loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1190
1300
|
"div",
|
|
1191
1301
|
{
|
|
1192
1302
|
className: "hsk-cb-selected-product",
|
|
1193
1303
|
onClick: () => selectedProduct.url && window.open(selectedProduct.url, "_blank"),
|
|
1194
1304
|
children: [
|
|
1195
|
-
selectedProduct.image && /* @__PURE__ */ (0,
|
|
1196
|
-
/* @__PURE__ */ (0,
|
|
1197
|
-
/* @__PURE__ */ (0,
|
|
1198
|
-
selectedProduct.price && /* @__PURE__ */ (0,
|
|
1305
|
+
selectedProduct.image && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { className: "hsk-cb-selected-img", src: selectedProduct.image, alt: selectedProduct.name }),
|
|
1306
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-selected-info", children: [
|
|
1307
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-selected-name", children: selectedProduct.name }),
|
|
1308
|
+
selectedProduct.price && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-selected-price", children: [
|
|
1199
1309
|
(_a = selectedProduct.currency) != null ? _a : defaultCurrency,
|
|
1200
1310
|
" ",
|
|
1201
1311
|
parseFloat(((_b = selectedProduct.price) != null ? _b : "").replace(/[^0-9.]/g, "") || "0").toLocaleString()
|
|
@@ -1204,20 +1314,20 @@ function ChatModal({
|
|
|
1204
1314
|
]
|
|
1205
1315
|
}
|
|
1206
1316
|
),
|
|
1207
|
-
loading && /* @__PURE__ */ (0,
|
|
1208
|
-
/* @__PURE__ */ (0,
|
|
1209
|
-
/* @__PURE__ */ (0,
|
|
1210
|
-
/* @__PURE__ */ (0,
|
|
1211
|
-
/* @__PURE__ */ (0,
|
|
1212
|
-
/* @__PURE__ */ (0,
|
|
1317
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-typing-row", children: [
|
|
1318
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
|
|
1319
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-typing", children: [
|
|
1320
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" }),
|
|
1321
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" }),
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" })
|
|
1213
1323
|
] })
|
|
1214
1324
|
] }),
|
|
1215
|
-
error && /* @__PURE__ */ (0,
|
|
1216
|
-
/* @__PURE__ */ (0,
|
|
1325
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-error", children: error }),
|
|
1326
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: bottomRef, style: { height: 1 } })
|
|
1217
1327
|
] }),
|
|
1218
|
-
/* @__PURE__ */ (0,
|
|
1219
|
-
/* @__PURE__ */ (0,
|
|
1220
|
-
/* @__PURE__ */ (0,
|
|
1328
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-input-wrap", children: [
|
|
1329
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-input-box", children: [
|
|
1330
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1221
1331
|
"textarea",
|
|
1222
1332
|
{
|
|
1223
1333
|
ref: textareaRef,
|
|
@@ -1231,18 +1341,18 @@ function ChatModal({
|
|
|
1231
1341
|
autoFocus: true
|
|
1232
1342
|
}
|
|
1233
1343
|
),
|
|
1234
|
-
/* @__PURE__ */ (0,
|
|
1344
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1235
1345
|
"button",
|
|
1236
1346
|
{
|
|
1237
1347
|
className: `hsk-cb-send ${classNames.sendButton || ""}`,
|
|
1238
1348
|
onClick: () => handleSend(),
|
|
1239
1349
|
disabled: !input.trim() || loading,
|
|
1240
1350
|
"aria-label": "Send message",
|
|
1241
|
-
children: /* @__PURE__ */ (0,
|
|
1351
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArrowUpIcon2, {})
|
|
1242
1352
|
}
|
|
1243
1353
|
)
|
|
1244
1354
|
] }),
|
|
1245
|
-
/* @__PURE__ */ (0,
|
|
1355
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 searches the whole catalogue in real time" })
|
|
1246
1356
|
] })
|
|
1247
1357
|
] })
|
|
1248
1358
|
}
|
|
@@ -1267,8 +1377,8 @@ function AIChatButton({
|
|
|
1267
1377
|
setMounted(true);
|
|
1268
1378
|
}, []);
|
|
1269
1379
|
const customStyles = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, (theme == null ? void 0 : theme.primaryColor) && { "--hsk-primary": theme.primaryColor }), (theme == null ? void 0 : theme.backgroundColor) && { "--hsk-bg": theme.backgroundColor }), (theme == null ? void 0 : theme.textColor) && { "--hsk-text": theme.textColor }), (theme == null ? void 0 : theme.fontFamily) && { "--hsk-font": theme.fontFamily });
|
|
1270
|
-
return /* @__PURE__ */ (0,
|
|
1271
|
-
/* @__PURE__ */ (0,
|
|
1380
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1381
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1272
1382
|
"button",
|
|
1273
1383
|
{
|
|
1274
1384
|
className: `hsk-cb-btn ${classNames.button || ""} ${className || ""}`,
|
|
@@ -1276,13 +1386,13 @@ function AIChatButton({
|
|
|
1276
1386
|
style: customStyles,
|
|
1277
1387
|
"aria-label": "Open AI chat",
|
|
1278
1388
|
children: [
|
|
1279
|
-
/* @__PURE__ */ (0,
|
|
1389
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "hsk-cb-btn-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
|
|
1280
1390
|
label !== void 0 ? label : null
|
|
1281
1391
|
]
|
|
1282
1392
|
}
|
|
1283
1393
|
),
|
|
1284
1394
|
open && mounted && (0, import_react_dom2.createPortal)(
|
|
1285
|
-
/* @__PURE__ */ (0,
|
|
1395
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1286
1396
|
ChatModal,
|
|
1287
1397
|
{
|
|
1288
1398
|
title,
|