@huskel/sdk 0.4.1 → 0.4.3
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 +1060 -155
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +64 -27
- package/dist/index.d.ts +64 -27
- package/dist/index.js +912 -247
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +870 -208
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -39,6 +39,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
39
39
|
var index_exports = {};
|
|
40
40
|
__export(index_exports, {
|
|
41
41
|
AIChatButton: () => AIChatButton,
|
|
42
|
+
CartBadge: () => CartBadge,
|
|
43
|
+
CartDrawer: () => CartDrawer,
|
|
42
44
|
ChatWidget: () => ChatWidget,
|
|
43
45
|
HuskelAPI: () => HuskelAPI,
|
|
44
46
|
HuskelClient: () => HuskelClient,
|
|
@@ -47,6 +49,7 @@ __export(index_exports, {
|
|
|
47
49
|
Sparkle: () => Sparkle,
|
|
48
50
|
getHuskelClient: () => getHuskelClient,
|
|
49
51
|
initHuskel: () => initHuskel,
|
|
52
|
+
useCart: () => useCart,
|
|
50
53
|
useChat: () => useChat,
|
|
51
54
|
useHuskel: () => useHuskel,
|
|
52
55
|
useIngest: () => useIngest,
|
|
@@ -99,7 +102,15 @@ var HuskelAPI = class {
|
|
|
99
102
|
});
|
|
100
103
|
if (!res.ok) {
|
|
101
104
|
const text = await res.text();
|
|
102
|
-
|
|
105
|
+
let message = text;
|
|
106
|
+
try {
|
|
107
|
+
const parsed = JSON.parse(text);
|
|
108
|
+
if (parsed && typeof parsed.error === "string") {
|
|
109
|
+
message = parsed.error;
|
|
110
|
+
}
|
|
111
|
+
} catch (e) {
|
|
112
|
+
}
|
|
113
|
+
const err = { status: res.status, message };
|
|
103
114
|
if (res.status >= 400 && res.status < 500) {
|
|
104
115
|
log("error", `${path} failed [${res.status}]`, text);
|
|
105
116
|
throw err;
|
|
@@ -150,6 +161,52 @@ var HuskelAPI = class {
|
|
|
150
161
|
log("info", "chat query", query);
|
|
151
162
|
return this.post("/chat", { query, siteId: this.siteId, history });
|
|
152
163
|
}
|
|
164
|
+
// --- Cart System ---
|
|
165
|
+
buildHeaders() {
|
|
166
|
+
var _a, _b;
|
|
167
|
+
const headers = {
|
|
168
|
+
"Content-Type": "application/json",
|
|
169
|
+
"X-Huskel-Token": this.apiToken,
|
|
170
|
+
"X-Huskel-Site": this.siteId
|
|
171
|
+
};
|
|
172
|
+
const shopperId = (_a = this.getShopperId) == null ? void 0 : _a.call(this);
|
|
173
|
+
if (shopperId) headers["X-Huskel-Shopper-Id"] = shopperId;
|
|
174
|
+
const sessionId = (_b = this.getSessionId) == null ? void 0 : _b.call(this);
|
|
175
|
+
if (sessionId) headers["X-Huskel-Session-Id"] = sessionId;
|
|
176
|
+
return headers;
|
|
177
|
+
}
|
|
178
|
+
async getCart() {
|
|
179
|
+
const res = await fetch(`${this.apiUrl}/cart?siteId=${this.siteId}`, {
|
|
180
|
+
headers: this.buildHeaders()
|
|
181
|
+
});
|
|
182
|
+
if (!res.ok) throw new Error("Failed to fetch cart");
|
|
183
|
+
return res.json();
|
|
184
|
+
}
|
|
185
|
+
async clearCart() {
|
|
186
|
+
const res = await fetch(`${this.apiUrl}/cart?siteId=${this.siteId}`, {
|
|
187
|
+
method: "DELETE",
|
|
188
|
+
headers: this.buildHeaders()
|
|
189
|
+
});
|
|
190
|
+
if (!res.ok) throw new Error("Failed to clear cart");
|
|
191
|
+
return res.json();
|
|
192
|
+
}
|
|
193
|
+
async checkoutCart() {
|
|
194
|
+
const res = await fetch(`${this.apiUrl}/cart/checkout`, {
|
|
195
|
+
method: "POST",
|
|
196
|
+
headers: this.buildHeaders(),
|
|
197
|
+
body: JSON.stringify({ siteId: this.siteId })
|
|
198
|
+
});
|
|
199
|
+
if (!res.ok) throw new Error("Failed to checkout cart");
|
|
200
|
+
return res.json();
|
|
201
|
+
}
|
|
202
|
+
async getCheckoutConfig() {
|
|
203
|
+
const res = await fetch(`${this.apiUrl}/checkout/config?site_id=${this.siteId}`, {
|
|
204
|
+
method: "GET",
|
|
205
|
+
headers: this.buildHeaders()
|
|
206
|
+
});
|
|
207
|
+
if (!res.ok) throw new Error("Failed to fetch checkout config");
|
|
208
|
+
return res.json();
|
|
209
|
+
}
|
|
153
210
|
};
|
|
154
211
|
|
|
155
212
|
// src/client.ts
|
|
@@ -257,13 +314,14 @@ var _HuskelClient = class _HuskelClient {
|
|
|
257
314
|
if (!apiUrl) console.error('[Huskel] Missing apiUrl. Set it via <HuskelProvider apiUrl="..."> or NEXT_PUBLIC_HUSKEL_API_URL.');
|
|
258
315
|
if (!apiToken) console.error('[Huskel] Missing apiToken. Set it via <HuskelProvider apiToken="..."> or NEXT_PUBLIC_HUSKEL_API_TOKEN.');
|
|
259
316
|
this.shopperId = config.shopperId;
|
|
317
|
+
this.onCheckout = config.onCheckout;
|
|
260
318
|
this.initSession();
|
|
261
319
|
this.loadIngestedCache();
|
|
262
320
|
this.api = new HuskelAPI(
|
|
263
321
|
apiUrl,
|
|
264
322
|
siteId,
|
|
265
323
|
apiToken,
|
|
266
|
-
() => this.
|
|
324
|
+
() => this.getShopperId(),
|
|
267
325
|
() => this.sessionId
|
|
268
326
|
);
|
|
269
327
|
instance = this;
|
|
@@ -304,7 +362,7 @@ var _HuskelClient = class _HuskelClient {
|
|
|
304
362
|
this.shopperId = id;
|
|
305
363
|
}
|
|
306
364
|
getShopperId() {
|
|
307
|
-
return this.shopperId;
|
|
365
|
+
return this.shopperId || "guest_" + this.sessionId;
|
|
308
366
|
}
|
|
309
367
|
getSessionId() {
|
|
310
368
|
return this.sessionId;
|
|
@@ -477,7 +535,15 @@ function useSearch() {
|
|
|
477
535
|
}
|
|
478
536
|
} catch (e) {
|
|
479
537
|
if (gen === genRef.current) {
|
|
480
|
-
|
|
538
|
+
let msg = (_b = e == null ? void 0 : e.message) != null ? _b : "Search failed";
|
|
539
|
+
try {
|
|
540
|
+
const parsed = JSON.parse(msg);
|
|
541
|
+
if (parsed && parsed.error) {
|
|
542
|
+
msg = parsed.error;
|
|
543
|
+
}
|
|
544
|
+
} catch (e2) {
|
|
545
|
+
}
|
|
546
|
+
setError(msg);
|
|
481
547
|
}
|
|
482
548
|
} finally {
|
|
483
549
|
if (gen === genRef.current) setLoading(false);
|
|
@@ -552,38 +618,127 @@ function useChat() {
|
|
|
552
618
|
const [loading, setLoading] = (0, import_react6.useState)(false);
|
|
553
619
|
const [error, setError] = (0, import_react6.useState)(null);
|
|
554
620
|
const abortRef = (0, import_react6.useRef)(null);
|
|
555
|
-
const send = (0, import_react6.useCallback)(async (query) => {
|
|
556
|
-
var _a, _b, _c;
|
|
621
|
+
const send = (0, import_react6.useCallback)(async (query, displayQuery) => {
|
|
622
|
+
var _a, _b, _c, _d, _e;
|
|
557
623
|
if (!query.trim() || loading) return;
|
|
558
624
|
(_a = abortRef.current) == null ? void 0 : _a.abort();
|
|
559
625
|
abortRef.current = new AbortController();
|
|
560
|
-
const
|
|
626
|
+
const signal = abortRef.current.signal;
|
|
627
|
+
const userMsg = { role: "user", content: displayQuery != null ? displayQuery : query };
|
|
561
628
|
setMessages((prev) => [...prev, userMsg]);
|
|
562
629
|
setLoading(true);
|
|
563
630
|
setError(null);
|
|
564
631
|
try {
|
|
565
632
|
const history = messages.map((m) => ({ role: m.role, content: m.content }));
|
|
566
633
|
const res = await client.api.chat(query, history);
|
|
567
|
-
|
|
568
|
-
|
|
634
|
+
if (signal.aborted) return;
|
|
635
|
+
const fullAnswer = res.answer || "";
|
|
636
|
+
const words = fullAnswer.split(/(\s+)/);
|
|
637
|
+
setMessages((prev) => [...prev, { role: "assistant", content: "" }]);
|
|
638
|
+
let currentContent = "";
|
|
639
|
+
for (const word of words) {
|
|
640
|
+
if (signal.aborted) return;
|
|
641
|
+
currentContent += word;
|
|
642
|
+
setMessages((prev) => {
|
|
643
|
+
const next = [...prev];
|
|
644
|
+
if (next.length > 0) {
|
|
645
|
+
next[next.length - 1] = { role: "assistant", content: currentContent };
|
|
646
|
+
}
|
|
647
|
+
return next;
|
|
648
|
+
});
|
|
649
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
650
|
+
}
|
|
651
|
+
if (signal.aborted) return;
|
|
569
652
|
setSources((_b = res.sources) != null ? _b : []);
|
|
653
|
+
if (((_c = res.action) == null ? void 0 : _c.type) === "add_to_cart" || res.checkout) {
|
|
654
|
+
if (typeof window !== "undefined") {
|
|
655
|
+
window.dispatchEvent(new CustomEvent("huskel:cart_updated", { detail: res.checkout }));
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (((_d = res.action) == null ? void 0 : _d.type) === "checkout") {
|
|
659
|
+
if (typeof window !== "undefined") {
|
|
660
|
+
window.dispatchEvent(new CustomEvent("huskel:trigger_checkout", { detail: res.checkout }));
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
if (res.checkout && client.onCheckout) {
|
|
664
|
+
client.onCheckout(res.checkout);
|
|
665
|
+
}
|
|
570
666
|
} catch (e) {
|
|
571
|
-
|
|
667
|
+
if (signal.aborted) return;
|
|
668
|
+
let msg = (_e = e == null ? void 0 : e.message) != null ? _e : "Chat request failed";
|
|
669
|
+
try {
|
|
670
|
+
const parsed = JSON.parse(msg);
|
|
671
|
+
if (parsed && parsed.error) {
|
|
672
|
+
msg = parsed.error;
|
|
673
|
+
}
|
|
674
|
+
} catch (e2) {
|
|
675
|
+
}
|
|
676
|
+
setError(msg);
|
|
572
677
|
setMessages((prev) => prev.slice(0, -1));
|
|
573
678
|
} finally {
|
|
574
|
-
|
|
679
|
+
if (!signal.aborted) {
|
|
680
|
+
setLoading(false);
|
|
681
|
+
}
|
|
575
682
|
}
|
|
576
683
|
}, [client, messages, loading]);
|
|
577
684
|
const reset = (0, import_react6.useCallback)(() => {
|
|
685
|
+
var _a;
|
|
686
|
+
(_a = abortRef.current) == null ? void 0 : _a.abort();
|
|
578
687
|
setMessages([]);
|
|
579
688
|
setSources([]);
|
|
580
689
|
setError(null);
|
|
690
|
+
setLoading(false);
|
|
581
691
|
}, []);
|
|
582
692
|
return { messages, sources, loading, error, send, reset };
|
|
583
693
|
}
|
|
584
694
|
|
|
585
|
-
// src/
|
|
695
|
+
// src/hooks/useCart.ts
|
|
586
696
|
var import_react7 = require("react");
|
|
697
|
+
function useCart() {
|
|
698
|
+
const client = useHuskelContext();
|
|
699
|
+
const [cart, setCart] = (0, import_react7.useState)(null);
|
|
700
|
+
const [loading, setLoading] = (0, import_react7.useState)(false);
|
|
701
|
+
const shopperId = client.getShopperId();
|
|
702
|
+
const fetchCart = (0, import_react7.useCallback)(async () => {
|
|
703
|
+
if (!shopperId) return;
|
|
704
|
+
setLoading(true);
|
|
705
|
+
try {
|
|
706
|
+
const res = await client.api.getCart();
|
|
707
|
+
setCart(res);
|
|
708
|
+
} catch (e) {
|
|
709
|
+
console.error("[Huskel] Failed to fetch cart", e);
|
|
710
|
+
} finally {
|
|
711
|
+
setLoading(false);
|
|
712
|
+
}
|
|
713
|
+
}, [client, shopperId]);
|
|
714
|
+
(0, import_react7.useEffect)(() => {
|
|
715
|
+
fetchCart();
|
|
716
|
+
const handleCartUpdate = (e) => {
|
|
717
|
+
if (e.detail) {
|
|
718
|
+
setCart(e.detail);
|
|
719
|
+
} else {
|
|
720
|
+
fetchCart();
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
if (typeof window !== "undefined") {
|
|
724
|
+
window.addEventListener("huskel:cart_updated", handleCartUpdate);
|
|
725
|
+
return () => window.removeEventListener("huskel:cart_updated", handleCartUpdate);
|
|
726
|
+
}
|
|
727
|
+
}, [fetchCart, shopperId]);
|
|
728
|
+
return { cart, loading, fetchCart };
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/components/SearchBar.tsx
|
|
732
|
+
var import_react8 = require("react");
|
|
733
|
+
|
|
734
|
+
// src/utils/cn.ts
|
|
735
|
+
var import_clsx = require("clsx");
|
|
736
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
737
|
+
function cn(...inputs) {
|
|
738
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// src/components/SearchBar.tsx
|
|
587
742
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
588
743
|
var SearchIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
589
744
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8.5", cy: "8.5", r: "5.5" }),
|
|
@@ -601,12 +756,12 @@ function SearchBar({
|
|
|
601
756
|
theme,
|
|
602
757
|
classNames = {}
|
|
603
758
|
}) {
|
|
604
|
-
const [query, setQuery] = (0,
|
|
605
|
-
const [open, setOpen] = (0,
|
|
759
|
+
const [query, setQuery] = (0, import_react8.useState)("");
|
|
760
|
+
const [open, setOpen] = (0, import_react8.useState)(false);
|
|
606
761
|
const { results, loading, search, clear } = useSearch();
|
|
607
|
-
const timer = (0,
|
|
608
|
-
const wrap = (0,
|
|
609
|
-
(0,
|
|
762
|
+
const timer = (0, import_react8.useRef)();
|
|
763
|
+
const wrap = (0, import_react8.useRef)(null);
|
|
764
|
+
(0, import_react8.useEffect)(() => {
|
|
610
765
|
clearTimeout(timer.current);
|
|
611
766
|
if (!query.trim()) {
|
|
612
767
|
clear();
|
|
@@ -619,7 +774,7 @@ function SearchBar({
|
|
|
619
774
|
}, debounceMs);
|
|
620
775
|
return () => clearTimeout(timer.current);
|
|
621
776
|
}, [query]);
|
|
622
|
-
(0,
|
|
777
|
+
(0, import_react8.useEffect)(() => {
|
|
623
778
|
const h = (e) => {
|
|
624
779
|
if (wrap.current && !wrap.current.contains(e.target)) setOpen(false);
|
|
625
780
|
};
|
|
@@ -632,13 +787,13 @@ function SearchBar({
|
|
|
632
787
|
onSelect == null ? void 0 : onSelect(r);
|
|
633
788
|
};
|
|
634
789
|
const showDrop = open && query.trim().length > 0;
|
|
635
|
-
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 });
|
|
636
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className:
|
|
790
|
+
const customStyles = __spreadValues(__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 }), (theme == null ? void 0 : theme.borderRadius) && { "--hsk-border-radius": theme.borderRadius });
|
|
791
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: cn("hsk-sb-wrap", classNames.root, className), ref: wrap, style: customStyles, children: [
|
|
637
792
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "hsk-sb-icon", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SearchIcon, {}) }),
|
|
638
793
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
639
794
|
"input",
|
|
640
795
|
{
|
|
641
|
-
className:
|
|
796
|
+
className: cn("hsk-sb-input", classNames.input, inputClassName),
|
|
642
797
|
type: "text",
|
|
643
798
|
value: query,
|
|
644
799
|
placeholder,
|
|
@@ -648,7 +803,7 @@ function SearchBar({
|
|
|
648
803
|
spellCheck: false
|
|
649
804
|
}
|
|
650
805
|
),
|
|
651
|
-
showDrop && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className:
|
|
806
|
+
showDrop && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: cn("hsk-sb-drop", classNames.dropdown, dropdownClassName), style: { position: "absolute" }, children: [
|
|
652
807
|
loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-loading-bar" }),
|
|
653
808
|
results.length === 0 && !loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-empty", children: [
|
|
654
809
|
"No results for \u201C",
|
|
@@ -669,7 +824,7 @@ function SearchBar({
|
|
|
669
824
|
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
670
825
|
"div",
|
|
671
826
|
{
|
|
672
|
-
className:
|
|
827
|
+
className: cn("hsk-sb-row hsk-sb-fade", classNames.row),
|
|
673
828
|
style: { animationDelay: `${i * 18}ms` },
|
|
674
829
|
onClick: () => handleSelect(r),
|
|
675
830
|
children: [
|
|
@@ -688,14 +843,124 @@ function SearchBar({
|
|
|
688
843
|
}
|
|
689
844
|
|
|
690
845
|
// src/components/Sparkle.tsx
|
|
691
|
-
var
|
|
846
|
+
var import_react9 = require("react");
|
|
692
847
|
var import_react_dom = require("react-dom");
|
|
848
|
+
|
|
849
|
+
// src/utils/markdown.tsx
|
|
693
850
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
694
|
-
var
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
851
|
+
var parseInline = (text, keyPrefix) => {
|
|
852
|
+
const tokenRegex = /(\[[^\]]+\]\([^)]+\)|\*\*[^*]+\*\*|`[^`]+`)/g;
|
|
853
|
+
const parts = text.split(tokenRegex);
|
|
854
|
+
return parts.map((part, index) => {
|
|
855
|
+
if (!part) return null;
|
|
856
|
+
const key = `${keyPrefix}-inline-${index}`;
|
|
857
|
+
if (part.startsWith("`") && part.endsWith("`")) {
|
|
858
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { className: "hsk-markdown-code", children: part.slice(1, -1) }, key);
|
|
859
|
+
}
|
|
860
|
+
if (part.startsWith("**") && part.endsWith("**")) {
|
|
861
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: parseInline(part.slice(2, -2), key) }, key);
|
|
862
|
+
}
|
|
863
|
+
const linkMatch = part.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
|
|
864
|
+
if (linkMatch) {
|
|
865
|
+
const url = linkMatch[2];
|
|
866
|
+
const isSafeUrl = /^(https?|mailto|tel):/i.test(url) || url.startsWith("/");
|
|
867
|
+
if (isSafeUrl) {
|
|
868
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: "hsk-markdown-link", children: parseInline(linkMatch[1], key) }, key);
|
|
869
|
+
}
|
|
870
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: parseInline(linkMatch[1], key) }, key);
|
|
871
|
+
}
|
|
872
|
+
return part;
|
|
873
|
+
});
|
|
874
|
+
};
|
|
875
|
+
function renderMarkdown(content) {
|
|
876
|
+
const lines = content.split("\n");
|
|
877
|
+
const elements = [];
|
|
878
|
+
let i = 0;
|
|
879
|
+
while (i < lines.length) {
|
|
880
|
+
const line = lines[i];
|
|
881
|
+
const key = `md-line-${i}`;
|
|
882
|
+
if (!line.trim()) {
|
|
883
|
+
i++;
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
const headerMatch = line.match(/^(#{1,3})\s+(.*)/);
|
|
887
|
+
if (headerMatch) {
|
|
888
|
+
const level = headerMatch[1].length;
|
|
889
|
+
const Tag = `h${level + 3}`;
|
|
890
|
+
elements.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Tag, { className: `hsk-markdown-h${level}`, children: parseInline(headerMatch[2], key) }, key));
|
|
891
|
+
i++;
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
894
|
+
if (line.match(/^[-*]\s+/)) {
|
|
895
|
+
const listItems = [];
|
|
896
|
+
while (i < lines.length && lines[i].match(/^[-*]\s+/)) {
|
|
897
|
+
const itemText = lines[i].replace(/^[-*]\s+/, "");
|
|
898
|
+
listItems.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: parseInline(itemText, `li-${i}`) }, `li-${i}`));
|
|
899
|
+
i++;
|
|
900
|
+
}
|
|
901
|
+
elements.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "hsk-markdown-list", children: listItems }, `ul-${key}`));
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
if (line.trim().startsWith("|")) {
|
|
905
|
+
const tableRows = [];
|
|
906
|
+
let isHeader = true;
|
|
907
|
+
while (i < lines.length && lines[i].trim().startsWith("|")) {
|
|
908
|
+
const rowLine = lines[i].trim();
|
|
909
|
+
if (rowLine.match(/^\|[-:| ]+\|$/)) {
|
|
910
|
+
i++;
|
|
911
|
+
isHeader = false;
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
const cells = rowLine.split("|").slice(1, -1).map((c) => c.trim());
|
|
915
|
+
const Tag = isHeader ? "th" : "td";
|
|
916
|
+
tableRows.push(
|
|
917
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tr", { children: cells.map((cell, cIdx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Tag, { children: parseInline(cell, `td-${i}-${cIdx}`) }, `td-${i}-${cIdx}`)) }, `tr-${i}`)
|
|
918
|
+
);
|
|
919
|
+
i++;
|
|
920
|
+
}
|
|
921
|
+
elements.push(
|
|
922
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-table-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("table", { className: "hsk-markdown-table", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: tableRows }) }) }, `table-wrapper-${key}`)
|
|
923
|
+
);
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
elements.push(
|
|
927
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "hsk-markdown-p", children: parseInline(line, key) }, key)
|
|
928
|
+
);
|
|
929
|
+
i++;
|
|
930
|
+
}
|
|
931
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: elements });
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// src/components/Sparkle.tsx
|
|
935
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
936
|
+
var SparkleIcon = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime4.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_runtime4.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" }) });
|
|
937
|
+
var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
938
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
939
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
698
940
|
] });
|
|
941
|
+
var ArrowUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
942
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m5 12 7-7 7 7" }),
|
|
943
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 19V5" })
|
|
944
|
+
] });
|
|
945
|
+
var getFriendlyError = (err) => {
|
|
946
|
+
let str = "";
|
|
947
|
+
if (typeof err === "string") str = err;
|
|
948
|
+
else if (err && typeof err === "object" && err.message) str = err.message;
|
|
949
|
+
else try {
|
|
950
|
+
str = JSON.stringify(err);
|
|
951
|
+
} catch (e) {
|
|
952
|
+
str = String(err);
|
|
953
|
+
}
|
|
954
|
+
if (str.toLowerCase().includes("token limit")) {
|
|
955
|
+
return "You've reached your usage limit. Please update your billing limits in your dashboard to continue.";
|
|
956
|
+
}
|
|
957
|
+
try {
|
|
958
|
+
const parsed = JSON.parse(str);
|
|
959
|
+
return parsed.error || parsed.message || str;
|
|
960
|
+
} catch (e) {
|
|
961
|
+
return str;
|
|
962
|
+
}
|
|
963
|
+
};
|
|
699
964
|
function SparkleModal({
|
|
700
965
|
productName,
|
|
701
966
|
limit,
|
|
@@ -705,26 +970,42 @@ function SparkleModal({
|
|
|
705
970
|
onNavigate,
|
|
706
971
|
onResult,
|
|
707
972
|
theme,
|
|
708
|
-
classNames = {}
|
|
973
|
+
classNames = {},
|
|
974
|
+
product: initialProduct
|
|
709
975
|
}) {
|
|
710
|
-
|
|
711
|
-
const
|
|
712
|
-
(0,
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
976
|
+
var _a, _b, _c;
|
|
977
|
+
const client = useHuskelContext();
|
|
978
|
+
const [fetchedProduct, setFetchedProduct] = (0, import_react9.useState)(null);
|
|
979
|
+
const displayProduct = initialProduct || fetchedProduct;
|
|
980
|
+
const { results, loading: searchLoading, search } = useSearch();
|
|
981
|
+
const { messages, sources, loading: chatLoading, error: chatError, send } = useChat();
|
|
982
|
+
const [chatInput, setChatInput] = (0, import_react9.useState)("");
|
|
983
|
+
const chatBottomRef = (0, import_react9.useRef)(null);
|
|
984
|
+
const chatTextareaRef = (0, import_react9.useRef)(null);
|
|
985
|
+
(0, import_react9.useEffect)(() => {
|
|
986
|
+
if (!initialProduct && !fetchedProduct) {
|
|
987
|
+
client.api.searchVector(productName, 1).then((res) => {
|
|
988
|
+
if (res.results && res.results.length > 0) {
|
|
989
|
+
setFetchedProduct(res.results[0].product);
|
|
990
|
+
}
|
|
991
|
+
}).catch((err) => console.error("[Huskel] Failed to fetch product details", err));
|
|
716
992
|
}
|
|
717
|
-
|
|
718
|
-
|
|
993
|
+
search(productName, limit);
|
|
994
|
+
}, [productName, initialProduct, fetchedProduct, client, limit, search]);
|
|
995
|
+
(0, import_react9.useEffect)(() => {
|
|
719
996
|
if (results.length > 0) onResult == null ? void 0 : onResult(results);
|
|
720
|
-
}, [results]);
|
|
721
|
-
(0,
|
|
997
|
+
}, [results, onResult]);
|
|
998
|
+
(0, import_react9.useEffect)(() => {
|
|
722
999
|
const h = (e) => {
|
|
723
1000
|
if (e.key === "Escape") onClose();
|
|
724
1001
|
};
|
|
725
1002
|
document.addEventListener("keydown", h);
|
|
726
1003
|
return () => document.removeEventListener("keydown", h);
|
|
727
|
-
}, []);
|
|
1004
|
+
}, [onClose]);
|
|
1005
|
+
(0, import_react9.useEffect)(() => {
|
|
1006
|
+
var _a2;
|
|
1007
|
+
(_a2 = chatBottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
|
|
1008
|
+
}, [messages, chatLoading]);
|
|
728
1009
|
const blurVal = typeof backdropBlur === "number" ? `${backdropBlur}px` : backdropBlur != null ? backdropBlur : "16px";
|
|
729
1010
|
const bg = backdropColor != null ? backdropColor : void 0;
|
|
730
1011
|
const handleNav = (r) => {
|
|
@@ -734,83 +1015,209 @@ function SparkleModal({
|
|
|
734
1015
|
if (r.product.url) window.location.href = r.product.url;
|
|
735
1016
|
}
|
|
736
1017
|
};
|
|
737
|
-
const
|
|
738
|
-
|
|
1018
|
+
const handleSend = async (text) => {
|
|
1019
|
+
const q = (text != null ? text : chatInput).trim();
|
|
1020
|
+
if (!q || chatLoading) return;
|
|
1021
|
+
setChatInput("");
|
|
1022
|
+
if (chatTextareaRef.current) {
|
|
1023
|
+
chatTextareaRef.current.style.height = "auto";
|
|
1024
|
+
}
|
|
1025
|
+
if (messages.length === 0 && displayProduct) {
|
|
1026
|
+
const contextQuery = `[Context: Shopper is viewing "${displayProduct.name}". Price: ${displayProduct.price}. Description: ${displayProduct.description || ""}]
|
|
1027
|
+
|
|
1028
|
+
Question: ${q}`;
|
|
1029
|
+
await send(contextQuery, q);
|
|
1030
|
+
} else {
|
|
1031
|
+
await send(q);
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
const handleKeyDown = (e) => {
|
|
1035
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
1036
|
+
e.preventDefault();
|
|
1037
|
+
handleSend();
|
|
1038
|
+
}
|
|
1039
|
+
};
|
|
1040
|
+
const handleInput = (e) => {
|
|
1041
|
+
setChatInput(e.target.value);
|
|
1042
|
+
const t = e.target;
|
|
1043
|
+
t.style.height = "auto";
|
|
1044
|
+
t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
|
|
1045
|
+
};
|
|
1046
|
+
const customStyles = __spreadValues(__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 }), (theme == null ? void 0 : theme.borderRadius) && { "--hsk-border-radius": theme.borderRadius });
|
|
1047
|
+
const displayMessages = messages.length === 0 && displayProduct ? [
|
|
1048
|
+
{
|
|
1049
|
+
role: "assistant",
|
|
1050
|
+
content: `Hi! I can help you with **${displayProduct.name}**. Ask me about its specifications, features, compare it with other options, or find alternatives!`
|
|
1051
|
+
}
|
|
1052
|
+
] : messages;
|
|
1053
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
739
1054
|
"div",
|
|
740
1055
|
{
|
|
741
|
-
className:
|
|
1056
|
+
className: cn("hsk-sp-backdrop", classNames.backdrop),
|
|
742
1057
|
onClick: onClose,
|
|
743
1058
|
style: __spreadValues({
|
|
744
1059
|
backdropFilter: `blur(${blurVal})`,
|
|
745
1060
|
WebkitBackdropFilter: `blur(${blurVal})`,
|
|
746
1061
|
background: bg != null ? bg : void 0
|
|
747
1062
|
}, customStyles),
|
|
748
|
-
children: /* @__PURE__ */ (0,
|
|
749
|
-
/* @__PURE__ */ (0,
|
|
750
|
-
/* @__PURE__ */ (0,
|
|
751
|
-
/* @__PURE__ */ (0,
|
|
752
|
-
/* @__PURE__ */ (0,
|
|
753
|
-
|
|
754
|
-
productName,
|
|
755
|
-
"\u201D"
|
|
756
|
-
] }),
|
|
757
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-header-sub", children: "AI vector similarity \xB7 instant results" })
|
|
1063
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: cn("hsk-sp-card hsk-sp-fullscreen", classNames.card), onClick: (e) => e.stopPropagation(), children: [
|
|
1064
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-header", children: [
|
|
1065
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-header-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SparkleIcon, {}) }),
|
|
1066
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-header-body", children: [
|
|
1067
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-header-title", children: (displayProduct == null ? void 0 : displayProduct.name) || productName }),
|
|
1068
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-header-sub", children: "Ask questions, compare specs, or check similar products" })
|
|
758
1069
|
] }),
|
|
759
|
-
/* @__PURE__ */ (0,
|
|
1070
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "hsk-sp-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CloseIcon, {}) })
|
|
760
1071
|
] }),
|
|
761
|
-
|
|
762
|
-
/* @__PURE__ */ (0,
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
1072
|
+
searchLoading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-bar" }),
|
|
1073
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-body", children: [
|
|
1074
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-details-pane", children: [
|
|
1075
|
+
displayProduct && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-product-profile-container", children: [
|
|
1076
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-product-profile", children: [
|
|
1077
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-details-imgwrap", children: ((_a = displayProduct.images) == null ? void 0 : _a[0]) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: displayProduct.images[0], alt: displayProduct.name }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
|
|
1078
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-details-meta", children: [
|
|
1079
|
+
displayProduct.brand && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-item-brand", children: displayProduct.brand }),
|
|
1080
|
+
displayProduct.category && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-item-cat", children: displayProduct.category }),
|
|
1081
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "hsk-sp-details-name", children: displayProduct.name }),
|
|
1082
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-item-price-row", children: [
|
|
1083
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-item-currency", children: (_b = displayProduct.currency) != null ? _b : "KES" }),
|
|
1084
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-item-price", children: parseFloat(((_c = displayProduct.price) == null ? void 0 : _c.replace(/[^0-9.]/g, "")) || "0").toLocaleString() }),
|
|
1085
|
+
displayProduct.originalPrice && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-item-original-price", children: parseFloat(displayProduct.originalPrice.replace(/[^0-9.]/g, "") || "0").toLocaleString() }),
|
|
1086
|
+
displayProduct.discount && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "hsk-sp-item-discount", children: [
|
|
1087
|
+
"(",
|
|
1088
|
+
displayProduct.discount,
|
|
1089
|
+
")"
|
|
1090
|
+
] })
|
|
1091
|
+
] }),
|
|
1092
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-item-meta-badges", children: [
|
|
1093
|
+
displayProduct.rating && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "hsk-sp-meta-badge hsk-sp-meta-badge-rating", children: [
|
|
1094
|
+
"\u2605 ",
|
|
1095
|
+
parseFloat(displayProduct.rating.toString()).toFixed(1),
|
|
1096
|
+
" ",
|
|
1097
|
+
displayProduct.reviewCount ? `(${displayProduct.reviewCount})` : ""
|
|
781
1098
|
] }),
|
|
782
|
-
/* @__PURE__ */ (0,
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
className: "hsk-sp-action hsk-sp-action-primary",
|
|
787
|
-
onClick: () => handleNav(r),
|
|
788
|
-
children: "View Product"
|
|
789
|
-
}
|
|
790
|
-
),
|
|
791
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
792
|
-
"button",
|
|
793
|
-
{
|
|
794
|
-
className: "hsk-sp-action hsk-sp-action-secondary",
|
|
795
|
-
onClick: () => onClose(),
|
|
796
|
-
children: "Add to Cart"
|
|
797
|
-
}
|
|
798
|
-
)
|
|
1099
|
+
displayProduct.availability && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `hsk-sp-meta-badge hsk-sp-meta-badge-avail ${displayProduct.availability.toLowerCase().includes("in") ? "in-stock" : "out-stock"}`, children: displayProduct.availability }),
|
|
1100
|
+
displayProduct.stock && !displayProduct.availability && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "hsk-sp-meta-badge hsk-sp-meta-badge-stock", children: [
|
|
1101
|
+
"Stock: ",
|
|
1102
|
+
displayProduct.stock
|
|
799
1103
|
] })
|
|
800
1104
|
] })
|
|
801
|
-
]
|
|
802
|
-
},
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
1105
|
+
] })
|
|
1106
|
+
] }),
|
|
1107
|
+
displayProduct.specs && Object.keys(displayProduct.specs).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-specs-horizontal", children: Object.entries(displayProduct.specs).map(([key, val]) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-spec-item-horizontal", children: [
|
|
1108
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "hsk-sp-spec-label-horizontal", children: [
|
|
1109
|
+
key,
|
|
1110
|
+
":"
|
|
1111
|
+
] }),
|
|
1112
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-spec-value-horizontal", title: val, children: val })
|
|
1113
|
+
] }, key)) }),
|
|
1114
|
+
displayProduct.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-details-desc", children: [
|
|
1115
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h4", { children: "Description" }),
|
|
1116
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: displayProduct.description })
|
|
1117
|
+
] })
|
|
1118
|
+
] }),
|
|
1119
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-similar-section", children: [
|
|
1120
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: "Similar Products" }),
|
|
1121
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-results", children: (() => {
|
|
1122
|
+
const similarProducts = results.filter(
|
|
1123
|
+
(r) => {
|
|
1124
|
+
var _a2;
|
|
1125
|
+
const isSameName = r.product.name.toLowerCase() === ((_a2 = displayProduct == null ? void 0 : displayProduct.name) == null ? void 0 : _a2.toLowerCase());
|
|
1126
|
+
const isSameSlug = r.product.slug && (displayProduct == null ? void 0 : displayProduct.slug) && r.product.slug.toLowerCase() === displayProduct.slug.toLowerCase();
|
|
1127
|
+
return !isSameName && !isSameSlug;
|
|
1128
|
+
}
|
|
1129
|
+
);
|
|
1130
|
+
if (!searchLoading && similarProducts.length === 0) {
|
|
1131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-empty", children: "No similar products found." });
|
|
1132
|
+
}
|
|
1133
|
+
return similarProducts.map((r, i) => {
|
|
1134
|
+
var _a2, _b2, _c2;
|
|
1135
|
+
const price = parseFloat(((_a2 = r.product.price) == null ? void 0 : _a2.replace(/[^0-9.]/g, "")) || "0");
|
|
1136
|
+
const currency = (_b2 = r.product.currency) != null ? _b2 : "KES";
|
|
1137
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1138
|
+
"div",
|
|
1139
|
+
{
|
|
1140
|
+
className: cn("hsk-sp-item", classNames.item),
|
|
1141
|
+
style: { animationDelay: `${i * 55}ms` },
|
|
1142
|
+
children: [
|
|
1143
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-img-wrap", children: ((_c2 = r.product.images) == null ? void 0 : _c2[0]) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: r.product.images[0], alt: r.product.name }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
|
|
1144
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-item-body", children: [
|
|
1145
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
1146
|
+
r.product.category && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-item-cat", children: r.product.category }),
|
|
1147
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-item-name", title: r.product.name, children: r.product.name })
|
|
1148
|
+
] }),
|
|
1149
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-item-price-row", children: [
|
|
1150
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-item-currency", children: currency }),
|
|
1151
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-item-price", children: price.toLocaleString() })
|
|
1152
|
+
] }),
|
|
1153
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-actions", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1154
|
+
"button",
|
|
1155
|
+
{
|
|
1156
|
+
className: "hsk-sp-action hsk-sp-action-primary",
|
|
1157
|
+
onClick: () => handleNav(r),
|
|
1158
|
+
children: "View"
|
|
1159
|
+
}
|
|
1160
|
+
) })
|
|
1161
|
+
] })
|
|
1162
|
+
]
|
|
1163
|
+
},
|
|
1164
|
+
r.id
|
|
1165
|
+
);
|
|
1166
|
+
});
|
|
1167
|
+
})() })
|
|
1168
|
+
] })
|
|
811
1169
|
] }),
|
|
812
|
-
/* @__PURE__ */ (0,
|
|
813
|
-
|
|
1170
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-sp-chat-pane", children: [
|
|
1171
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-cb-msgs", children: [
|
|
1172
|
+
displayMessages.map((msg, idx) => {
|
|
1173
|
+
const isUser = msg.role === "user";
|
|
1174
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-msg-group", children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-user-msg", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-user-bubble", children: msg.content }) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-cb-ai-msg", children: [
|
|
1175
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SparkleIcon, {}) }),
|
|
1176
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-ai-body", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }) })
|
|
1177
|
+
] }) }, idx);
|
|
1178
|
+
}),
|
|
1179
|
+
chatLoading && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-cb-typing-row", children: [
|
|
1180
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SparkleIcon, {}) }),
|
|
1181
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-cb-typing", children: [
|
|
1182
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-dot" }),
|
|
1183
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-dot" }),
|
|
1184
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-dot" })
|
|
1185
|
+
] })
|
|
1186
|
+
] }),
|
|
1187
|
+
chatError && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-error", children: getFriendlyError(chatError) }),
|
|
1188
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: chatBottomRef, style: { height: 1 } })
|
|
1189
|
+
] }),
|
|
1190
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-cb-input-wrap", children: [
|
|
1191
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-cb-input-box", children: [
|
|
1192
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1193
|
+
"textarea",
|
|
1194
|
+
{
|
|
1195
|
+
ref: chatTextareaRef,
|
|
1196
|
+
className: "hsk-cb-textarea",
|
|
1197
|
+
value: chatInput,
|
|
1198
|
+
onChange: handleInput,
|
|
1199
|
+
onKeyDown: handleKeyDown,
|
|
1200
|
+
placeholder: "Ask about this product, specs, or comparison...",
|
|
1201
|
+
rows: 1,
|
|
1202
|
+
disabled: chatLoading
|
|
1203
|
+
}
|
|
1204
|
+
),
|
|
1205
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1206
|
+
"button",
|
|
1207
|
+
{
|
|
1208
|
+
className: "hsk-cb-send",
|
|
1209
|
+
onClick: () => handleSend(),
|
|
1210
|
+
disabled: !chatInput.trim() || chatLoading,
|
|
1211
|
+
"aria-label": "Send message",
|
|
1212
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ArrowUpIcon, {})
|
|
1213
|
+
}
|
|
1214
|
+
)
|
|
1215
|
+
] }),
|
|
1216
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 instant product knowledge" })
|
|
1217
|
+
] })
|
|
1218
|
+
] })
|
|
1219
|
+
] }),
|
|
1220
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sp-footer", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-sp-esc", children: "Esc to close" }) })
|
|
814
1221
|
] })
|
|
815
1222
|
}
|
|
816
1223
|
);
|
|
@@ -824,39 +1231,41 @@ function Sparkle({
|
|
|
824
1231
|
className,
|
|
825
1232
|
onNavigate,
|
|
826
1233
|
theme,
|
|
827
|
-
classNames = {}
|
|
1234
|
+
classNames = {},
|
|
1235
|
+
product
|
|
828
1236
|
}) {
|
|
829
|
-
const [open, setOpen] = (0,
|
|
830
|
-
const [mounted, setMounted] = (0,
|
|
831
|
-
(0,
|
|
1237
|
+
const [open, setOpen] = (0, import_react9.useState)(false);
|
|
1238
|
+
const [mounted, setMounted] = (0, import_react9.useState)(false);
|
|
1239
|
+
(0, import_react9.useEffect)(() => {
|
|
832
1240
|
setMounted(true);
|
|
833
1241
|
}, []);
|
|
834
|
-
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 });
|
|
835
|
-
return /* @__PURE__ */ (0,
|
|
836
|
-
/* @__PURE__ */ (0,
|
|
1242
|
+
const customStyles = __spreadValues(__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 }), (theme == null ? void 0 : theme.borderRadius) && { "--hsk-border-radius": theme.borderRadius });
|
|
1243
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1244
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
837
1245
|
"button",
|
|
838
1246
|
{
|
|
839
|
-
className:
|
|
1247
|
+
className: cn("hsk-sp-btn", classNames.button, className),
|
|
840
1248
|
onClick: () => setOpen(true),
|
|
841
1249
|
style: customStyles,
|
|
842
1250
|
title: "Find similar products",
|
|
843
1251
|
"aria-label": "Find similar products",
|
|
844
|
-
children: /* @__PURE__ */ (0,
|
|
1252
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SparkleIcon, {})
|
|
845
1253
|
}
|
|
846
1254
|
),
|
|
847
1255
|
open && mounted && (0, import_react_dom.createPortal)(
|
|
848
|
-
/* @__PURE__ */ (0,
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
849
1257
|
SparkleModal,
|
|
850
1258
|
{
|
|
851
1259
|
productName,
|
|
852
1260
|
limit,
|
|
1261
|
+
onResult,
|
|
853
1262
|
backdropColor,
|
|
854
1263
|
backdropBlur,
|
|
855
1264
|
onClose: () => setOpen(false),
|
|
856
|
-
onResult,
|
|
857
1265
|
onNavigate,
|
|
858
1266
|
theme,
|
|
859
|
-
classNames
|
|
1267
|
+
classNames,
|
|
1268
|
+
product
|
|
860
1269
|
}
|
|
861
1270
|
),
|
|
862
1271
|
document.body
|
|
@@ -865,20 +1274,20 @@ function Sparkle({
|
|
|
865
1274
|
}
|
|
866
1275
|
|
|
867
1276
|
// src/components/ChatWidget.tsx
|
|
868
|
-
var
|
|
869
|
-
var
|
|
870
|
-
var SparkleIcon2 = () => /* @__PURE__ */ (0,
|
|
871
|
-
var
|
|
872
|
-
/* @__PURE__ */ (0,
|
|
873
|
-
/* @__PURE__ */ (0,
|
|
1277
|
+
var import_react10 = require("react");
|
|
1278
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1279
|
+
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" }) });
|
|
1280
|
+
var ArrowUpIcon2 = () => /* @__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: [
|
|
1281
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m5 12 7-7 7 7" }),
|
|
1282
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 19V5" })
|
|
874
1283
|
] });
|
|
875
1284
|
function SourceCard({ source, defaultCurrency, onSelect }) {
|
|
876
1285
|
var _a;
|
|
877
|
-
return /* @__PURE__ */ (0,
|
|
878
|
-
source.image && /* @__PURE__ */ (0,
|
|
879
|
-
/* @__PURE__ */ (0,
|
|
880
|
-
/* @__PURE__ */ (0,
|
|
881
|
-
source.price && /* @__PURE__ */ (0,
|
|
1286
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-source-card", onClick: () => onSelect == null ? void 0 : onSelect(source), children: [
|
|
1287
|
+
source.image && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: source.image, alt: source.name, className: "hsk-source-img" }),
|
|
1288
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
1289
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-source-name", children: source.name }),
|
|
1290
|
+
source.price && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-source-price", children: [
|
|
882
1291
|
(_a = source.currency) != null ? _a : defaultCurrency,
|
|
883
1292
|
" ",
|
|
884
1293
|
source.price
|
|
@@ -898,10 +1307,10 @@ function ChatWidget({
|
|
|
898
1307
|
onSelectSource
|
|
899
1308
|
}) {
|
|
900
1309
|
const { messages, sources, loading, error, send, reset } = useChat();
|
|
901
|
-
const [input, setInput] = (0,
|
|
902
|
-
const bottomRef = (0,
|
|
903
|
-
const textareaRef = (0,
|
|
904
|
-
(0,
|
|
1310
|
+
const [input, setInput] = (0, import_react10.useState)("");
|
|
1311
|
+
const bottomRef = (0, import_react10.useRef)(null);
|
|
1312
|
+
const textareaRef = (0, import_react10.useRef)(null);
|
|
1313
|
+
(0, import_react10.useEffect)(() => {
|
|
905
1314
|
var _a;
|
|
906
1315
|
(_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
|
|
907
1316
|
}, [messages, loading]);
|
|
@@ -924,48 +1333,55 @@ function ChatWidget({
|
|
|
924
1333
|
t.style.height = "auto";
|
|
925
1334
|
t.style.height = Math.min(t.scrollHeight, 120) + "px";
|
|
926
1335
|
};
|
|
927
|
-
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,
|
|
1336
|
+
const customStyles = __spreadValues(__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 }), (theme == null ? void 0 : theme.borderRadius) && { "--hsk-border-radius": theme.borderRadius });
|
|
1337
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
929
1338
|
"div",
|
|
930
1339
|
{
|
|
931
|
-
className:
|
|
1340
|
+
className: cn("hsk-chat-widget", classNames.root, className),
|
|
932
1341
|
style: customStyles,
|
|
933
1342
|
children: [
|
|
934
|
-
/* @__PURE__ */ (0,
|
|
935
|
-
/* @__PURE__ */ (0,
|
|
936
|
-
/* @__PURE__ */ (0,
|
|
937
|
-
/* @__PURE__ */ (0,
|
|
938
|
-
messages.length > 0 && /* @__PURE__ */ (0,
|
|
1343
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: cn("hsk-chat-header", classNames.header), children: [
|
|
1344
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-chat-header-icon", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) }),
|
|
1345
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-chat-title", children: title }),
|
|
1346
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-chat-badge", children: "AI" }),
|
|
1347
|
+
messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "hsk-chat-reset", onClick: reset, style: { marginLeft: "auto" }, children: "Clear" })
|
|
939
1348
|
] }),
|
|
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,
|
|
1349
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-chat-messages", children: [
|
|
1350
|
+
messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-chat-empty", children: [
|
|
1351
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-chat-empty-icon", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) }),
|
|
1352
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: emptyStateText }),
|
|
1353
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-chat-empty-suggestions", children: emptyStateSuggestions })
|
|
1354
|
+
] }) : messages.map((msg, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
1355
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `hsk-msg-row ${msg.role}`, children: [
|
|
1356
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: cn("hsk-msg-avatar", msg.role === "assistant" ? "ai" : "user"), children: msg.role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) : "U" }),
|
|
1357
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: cn("hsk-msg-bubble", msg.role, classNames.messageBubble), children: renderMarkdown(msg.content) })
|
|
949
1358
|
] }),
|
|
950
|
-
msg.role === "assistant" && idx === messages.length - 1 && sources.length > 0 && /* @__PURE__ */ (0,
|
|
1359
|
+
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
1360
|
] }, idx)),
|
|
952
|
-
loading && /* @__PURE__ */ (0,
|
|
953
|
-
/* @__PURE__ */ (0,
|
|
954
|
-
/* @__PURE__ */ (0,
|
|
955
|
-
/* @__PURE__ */ (0,
|
|
956
|
-
/* @__PURE__ */ (0,
|
|
957
|
-
/* @__PURE__ */ (0,
|
|
1361
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-msg-row", children: [
|
|
1362
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-msg-avatar ai", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) }),
|
|
1363
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-typing", children: [
|
|
1364
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-typing-dot" }),
|
|
1365
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-typing-dot" }),
|
|
1366
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-typing-dot" })
|
|
958
1367
|
] })
|
|
959
1368
|
] }),
|
|
960
|
-
error && /* @__PURE__ */ (0,
|
|
961
|
-
|
|
1369
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-chat-error", children: (() => {
|
|
1370
|
+
try {
|
|
1371
|
+
const parsed = JSON.parse(error);
|
|
1372
|
+
return parsed.error || parsed.message || error;
|
|
1373
|
+
} catch (e) {
|
|
1374
|
+
return error;
|
|
1375
|
+
}
|
|
1376
|
+
})() }),
|
|
1377
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: bottomRef })
|
|
962
1378
|
] }),
|
|
963
|
-
/* @__PURE__ */ (0,
|
|
964
|
-
/* @__PURE__ */ (0,
|
|
1379
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-chat-input-area", children: [
|
|
1380
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
965
1381
|
"textarea",
|
|
966
1382
|
{
|
|
967
1383
|
ref: textareaRef,
|
|
968
|
-
className:
|
|
1384
|
+
className: cn("hsk-chat-input", classNames.input),
|
|
969
1385
|
value: input,
|
|
970
1386
|
onChange: handleInput,
|
|
971
1387
|
onKeyDown: handleKey,
|
|
@@ -974,14 +1390,14 @@ function ChatWidget({
|
|
|
974
1390
|
disabled: loading
|
|
975
1391
|
}
|
|
976
1392
|
),
|
|
977
|
-
/* @__PURE__ */ (0,
|
|
1393
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
978
1394
|
"button",
|
|
979
1395
|
{
|
|
980
1396
|
className: "hsk-chat-send",
|
|
981
1397
|
onClick: handleSend,
|
|
982
1398
|
disabled: !input.trim() || loading,
|
|
983
1399
|
"aria-label": "Send message",
|
|
984
|
-
children: /* @__PURE__ */ (0,
|
|
1400
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArrowUpIcon2, {})
|
|
985
1401
|
}
|
|
986
1402
|
)
|
|
987
1403
|
] })
|
|
@@ -991,19 +1407,19 @@ function ChatWidget({
|
|
|
991
1407
|
}
|
|
992
1408
|
|
|
993
1409
|
// src/components/AIChatButton.tsx
|
|
994
|
-
var
|
|
1410
|
+
var import_react11 = require("react");
|
|
995
1411
|
var import_react_dom2 = require("react-dom");
|
|
996
|
-
var
|
|
997
|
-
var SparkleIcon3 = ({ className }) => /* @__PURE__ */ (0,
|
|
998
|
-
var
|
|
999
|
-
/* @__PURE__ */ (0,
|
|
1000
|
-
/* @__PURE__ */ (0,
|
|
1412
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1413
|
+
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" }) });
|
|
1414
|
+
var ArrowUpIcon3 = () => /* @__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: [
|
|
1415
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m5 12 7-7 7 7" }),
|
|
1416
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 19V5" })
|
|
1001
1417
|
] });
|
|
1002
|
-
var CloseIcon2 = () => /* @__PURE__ */ (0,
|
|
1003
|
-
/* @__PURE__ */ (0,
|
|
1004
|
-
/* @__PURE__ */ (0,
|
|
1418
|
+
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: [
|
|
1419
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1420
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
1005
1421
|
] });
|
|
1006
|
-
var ChevronRightIcon = () => /* @__PURE__ */ (0,
|
|
1422
|
+
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
1423
|
var DEFAULT_CHIPS = [
|
|
1008
1424
|
"Cheapest smartphone",
|
|
1009
1425
|
"Smart TV under KSh 20,000",
|
|
@@ -1011,15 +1427,15 @@ var DEFAULT_CHIPS = [
|
|
|
1011
1427
|
"Best laptop for students"
|
|
1012
1428
|
];
|
|
1013
1429
|
function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
1014
|
-
const railRef = (0,
|
|
1015
|
-
const [showNext, setShowNext] = (0,
|
|
1016
|
-
const measure = (0,
|
|
1430
|
+
const railRef = (0, import_react11.useRef)(null);
|
|
1431
|
+
const [showNext, setShowNext] = (0, import_react11.useState)(false);
|
|
1432
|
+
const measure = (0, import_react11.useCallback)(() => {
|
|
1017
1433
|
const el = railRef.current;
|
|
1018
1434
|
if (!el) return;
|
|
1019
1435
|
const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 8;
|
|
1020
1436
|
setShowNext(el.scrollWidth > el.clientWidth + 4 && !atEnd);
|
|
1021
1437
|
}, []);
|
|
1022
|
-
(0,
|
|
1438
|
+
(0, import_react11.useEffect)(() => {
|
|
1023
1439
|
measure();
|
|
1024
1440
|
const el = railRef.current;
|
|
1025
1441
|
if (!el) return;
|
|
@@ -1035,20 +1451,20 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
|
1035
1451
|
var _a;
|
|
1036
1452
|
(_a = railRef.current) == null ? void 0 : _a.scrollBy({ left: 170, behavior: "smooth" });
|
|
1037
1453
|
};
|
|
1038
|
-
return /* @__PURE__ */ (0,
|
|
1039
|
-
/* @__PURE__ */ (0,
|
|
1454
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-sources-wrap", children: [
|
|
1455
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-sources", ref: railRef, children: sources.map((src, si) => {
|
|
1040
1456
|
var _a;
|
|
1041
|
-
return /* @__PURE__ */ (0,
|
|
1457
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1042
1458
|
"div",
|
|
1043
1459
|
{
|
|
1044
1460
|
className: "hsk-cb-source",
|
|
1045
1461
|
style: { animationDelay: `${si * 50}ms` },
|
|
1046
1462
|
onClick: () => onSelectSource == null ? void 0 : onSelectSource(src),
|
|
1047
1463
|
children: [
|
|
1048
|
-
src.image ? /* @__PURE__ */ (0,
|
|
1049
|
-
/* @__PURE__ */ (0,
|
|
1050
|
-
/* @__PURE__ */ (0,
|
|
1051
|
-
src.price && /* @__PURE__ */ (0,
|
|
1464
|
+
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, {}) }),
|
|
1465
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-src-info", children: [
|
|
1466
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-src-name", children: src.name }),
|
|
1467
|
+
src.price && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-src-price", children: [
|
|
1052
1468
|
(_a = src.currency) != null ? _a : defaultCurrency,
|
|
1053
1469
|
" ",
|
|
1054
1470
|
parseFloat(src.price.replace(/[^0-9.]/g, "") || "0").toLocaleString()
|
|
@@ -1059,20 +1475,20 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
|
1059
1475
|
si
|
|
1060
1476
|
);
|
|
1061
1477
|
}) }),
|
|
1062
|
-
showNext && /* @__PURE__ */ (0,
|
|
1063
|
-
/* @__PURE__ */ (0,
|
|
1478
|
+
showNext && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1479
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1064
1480
|
"div",
|
|
1065
1481
|
{
|
|
1066
1482
|
className: "hsk-cb-sources-fade",
|
|
1067
1483
|
style: { background: "linear-gradient(to right, transparent, var(--hsk-fade-bg, #0e0e0f))" }
|
|
1068
1484
|
}
|
|
1069
1485
|
),
|
|
1070
|
-
/* @__PURE__ */ (0,
|
|
1486
|
+
/* @__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
1487
|
] })
|
|
1072
1488
|
] });
|
|
1073
1489
|
}
|
|
1074
1490
|
function ChatModal({
|
|
1075
|
-
title = "
|
|
1491
|
+
title = "Shopping Assistant",
|
|
1076
1492
|
placeholder = "Ask me anything \u2014 gifts, budget, use case\u2026",
|
|
1077
1493
|
backdropColor,
|
|
1078
1494
|
backdropBlur,
|
|
@@ -1085,15 +1501,15 @@ function ChatModal({
|
|
|
1085
1501
|
}) {
|
|
1086
1502
|
var _a, _b;
|
|
1087
1503
|
const { messages, sources, loading, error, send, reset } = useChat();
|
|
1088
|
-
const [input, setInput] = (0,
|
|
1089
|
-
const [selectedProduct, setSelectedProduct] = (0,
|
|
1090
|
-
const bottomRef = (0,
|
|
1091
|
-
const textareaRef = (0,
|
|
1092
|
-
(0,
|
|
1504
|
+
const [input, setInput] = (0, import_react11.useState)("");
|
|
1505
|
+
const [selectedProduct, setSelectedProduct] = (0, import_react11.useState)(null);
|
|
1506
|
+
const bottomRef = (0, import_react11.useRef)(null);
|
|
1507
|
+
const textareaRef = (0, import_react11.useRef)(null);
|
|
1508
|
+
(0, import_react11.useEffect)(() => {
|
|
1093
1509
|
var _a2;
|
|
1094
1510
|
(_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
|
|
1095
1511
|
}, [messages, loading, selectedProduct]);
|
|
1096
|
-
(0,
|
|
1512
|
+
(0, import_react11.useEffect)(() => {
|
|
1097
1513
|
const h = (e) => {
|
|
1098
1514
|
if (e.key === "Escape") onClose();
|
|
1099
1515
|
};
|
|
@@ -1130,36 +1546,32 @@ function ChatModal({
|
|
|
1130
1546
|
t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
|
|
1131
1547
|
};
|
|
1132
1548
|
const blurVal = typeof backdropBlur === "number" ? `${backdropBlur}px` : backdropBlur != null ? backdropBlur : "20px";
|
|
1133
|
-
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,
|
|
1549
|
+
const customStyles = __spreadValues(__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 }), (theme == null ? void 0 : theme.borderRadius) && { "--hsk-border-radius": theme.borderRadius });
|
|
1550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1135
1551
|
"div",
|
|
1136
1552
|
{
|
|
1137
|
-
className:
|
|
1553
|
+
className: cn("hsk-cb-overlay", classNames.overlay),
|
|
1138
1554
|
onClick: onClose,
|
|
1139
1555
|
style: __spreadValues(__spreadValues({
|
|
1140
1556
|
backdropFilter: `blur(${blurVal})`,
|
|
1141
1557
|
WebkitBackdropFilter: `blur(${blurVal})`
|
|
1142
1558
|
}, backdropColor ? { background: backdropColor } : {}), customStyles),
|
|
1143
|
-
children: /* @__PURE__ */ (0,
|
|
1144
|
-
/* @__PURE__ */ (0,
|
|
1145
|
-
/* @__PURE__ */ (0,
|
|
1146
|
-
/* @__PURE__ */ (0,
|
|
1147
|
-
/* @__PURE__ */ (0,
|
|
1148
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-topbar-title", children: title }),
|
|
1149
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-topbar-sub", children: "Powered by Huskel AI \xB7 searches the whole catalogue" })
|
|
1150
|
-
] })
|
|
1559
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: cn("hsk-cb-panel", classNames.panel), onClick: (e) => e.stopPropagation(), children: [
|
|
1560
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-topbar", children: [
|
|
1561
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-topbar-left", children: [
|
|
1562
|
+
/* @__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, {}) }),
|
|
1563
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-topbar-title", children: title }) })
|
|
1151
1564
|
] }),
|
|
1152
|
-
/* @__PURE__ */ (0,
|
|
1153
|
-
messages.length > 0 && /* @__PURE__ */ (0,
|
|
1154
|
-
/* @__PURE__ */ (0,
|
|
1565
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-topbar-actions", children: [
|
|
1566
|
+
messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-topbar-btn", onClick: reset, children: "Clear chat" }),
|
|
1567
|
+
/* @__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
1568
|
] })
|
|
1156
1569
|
] }),
|
|
1157
|
-
/* @__PURE__ */ (0,
|
|
1158
|
-
messages.length === 0 ? /* @__PURE__ */ (0,
|
|
1159
|
-
/* @__PURE__ */ (0,
|
|
1160
|
-
/* @__PURE__ */ (0,
|
|
1161
|
-
/* @__PURE__ */ (0,
|
|
1162
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-chips", children: chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1570
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-msgs", children: [
|
|
1571
|
+
messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-empty", children: [
|
|
1572
|
+
/* @__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, {}) }),
|
|
1573
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-empty-title", children: "Find exactly what you need" }),
|
|
1574
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-chips", children: chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1163
1575
|
"button",
|
|
1164
1576
|
{
|
|
1165
1577
|
className: "hsk-cb-chip",
|
|
@@ -1171,11 +1583,11 @@ function ChatModal({
|
|
|
1171
1583
|
] }) : messages.map((msg, idx) => {
|
|
1172
1584
|
const isLast = idx === messages.length - 1;
|
|
1173
1585
|
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,
|
|
1586
|
+
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: [
|
|
1587
|
+
/* @__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, {}) }),
|
|
1588
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-ai-body", children: [
|
|
1589
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }),
|
|
1590
|
+
isLast && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1179
1591
|
SourcesCarousel,
|
|
1180
1592
|
{
|
|
1181
1593
|
sources,
|
|
@@ -1186,16 +1598,16 @@ function ChatModal({
|
|
|
1186
1598
|
] })
|
|
1187
1599
|
] }) }, idx);
|
|
1188
1600
|
}),
|
|
1189
|
-
selectedProduct && loading && /* @__PURE__ */ (0,
|
|
1601
|
+
selectedProduct && loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1190
1602
|
"div",
|
|
1191
1603
|
{
|
|
1192
1604
|
className: "hsk-cb-selected-product",
|
|
1193
1605
|
onClick: () => selectedProduct.url && window.open(selectedProduct.url, "_blank"),
|
|
1194
1606
|
children: [
|
|
1195
|
-
selectedProduct.image && /* @__PURE__ */ (0,
|
|
1196
|
-
/* @__PURE__ */ (0,
|
|
1197
|
-
/* @__PURE__ */ (0,
|
|
1198
|
-
selectedProduct.price && /* @__PURE__ */ (0,
|
|
1607
|
+
selectedProduct.image && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { className: "hsk-cb-selected-img", src: selectedProduct.image, alt: selectedProduct.name }),
|
|
1608
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-selected-info", children: [
|
|
1609
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-selected-name", children: selectedProduct.name }),
|
|
1610
|
+
selectedProduct.price && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-selected-price", children: [
|
|
1199
1611
|
(_a = selectedProduct.currency) != null ? _a : defaultCurrency,
|
|
1200
1612
|
" ",
|
|
1201
1613
|
parseFloat(((_b = selectedProduct.price) != null ? _b : "").replace(/[^0-9.]/g, "") || "0").toLocaleString()
|
|
@@ -1204,24 +1616,24 @@ function ChatModal({
|
|
|
1204
1616
|
]
|
|
1205
1617
|
}
|
|
1206
1618
|
),
|
|
1207
|
-
loading && /* @__PURE__ */ (0,
|
|
1208
|
-
/* @__PURE__ */ (0,
|
|
1209
|
-
/* @__PURE__ */ (0,
|
|
1210
|
-
/* @__PURE__ */ (0,
|
|
1211
|
-
/* @__PURE__ */ (0,
|
|
1212
|
-
/* @__PURE__ */ (0,
|
|
1619
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-typing-row", children: [
|
|
1620
|
+
/* @__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, {}) }),
|
|
1621
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-typing", children: [
|
|
1622
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" }),
|
|
1623
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" }),
|
|
1624
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" })
|
|
1213
1625
|
] })
|
|
1214
1626
|
] }),
|
|
1215
|
-
error && /* @__PURE__ */ (0,
|
|
1216
|
-
/* @__PURE__ */ (0,
|
|
1627
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-error", children: error }),
|
|
1628
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: bottomRef, style: { height: 1 } })
|
|
1217
1629
|
] }),
|
|
1218
|
-
/* @__PURE__ */ (0,
|
|
1219
|
-
/* @__PURE__ */ (0,
|
|
1220
|
-
/* @__PURE__ */ (0,
|
|
1630
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-input-wrap", children: [
|
|
1631
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-input-box", children: [
|
|
1632
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1221
1633
|
"textarea",
|
|
1222
1634
|
{
|
|
1223
1635
|
ref: textareaRef,
|
|
1224
|
-
className:
|
|
1636
|
+
className: cn("hsk-cb-textarea", classNames.input),
|
|
1225
1637
|
value: input,
|
|
1226
1638
|
onChange: handleInput,
|
|
1227
1639
|
onKeyDown: handleKeyDown,
|
|
@@ -1231,18 +1643,18 @@ function ChatModal({
|
|
|
1231
1643
|
autoFocus: true
|
|
1232
1644
|
}
|
|
1233
1645
|
),
|
|
1234
|
-
/* @__PURE__ */ (0,
|
|
1646
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1235
1647
|
"button",
|
|
1236
1648
|
{
|
|
1237
|
-
className:
|
|
1649
|
+
className: cn("hsk-cb-send", classNames.sendButton),
|
|
1238
1650
|
onClick: () => handleSend(),
|
|
1239
1651
|
disabled: !input.trim() || loading,
|
|
1240
1652
|
"aria-label": "Send message",
|
|
1241
|
-
children: /* @__PURE__ */ (0,
|
|
1653
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArrowUpIcon3, {})
|
|
1242
1654
|
}
|
|
1243
1655
|
)
|
|
1244
1656
|
] }),
|
|
1245
|
-
/* @__PURE__ */ (0,
|
|
1657
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 searches the whole catalogue in real time" })
|
|
1246
1658
|
] })
|
|
1247
1659
|
] })
|
|
1248
1660
|
}
|
|
@@ -1261,28 +1673,28 @@ function AIChatButton({
|
|
|
1261
1673
|
theme,
|
|
1262
1674
|
classNames = {}
|
|
1263
1675
|
}) {
|
|
1264
|
-
const [open, setOpen] = (0,
|
|
1265
|
-
const [mounted, setMounted] = (0,
|
|
1266
|
-
(0,
|
|
1676
|
+
const [open, setOpen] = (0, import_react11.useState)(false);
|
|
1677
|
+
const [mounted, setMounted] = (0, import_react11.useState)(false);
|
|
1678
|
+
(0, import_react11.useEffect)(() => {
|
|
1267
1679
|
setMounted(true);
|
|
1268
1680
|
}, []);
|
|
1269
|
-
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,
|
|
1681
|
+
const customStyles = __spreadValues(__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 }), (theme == null ? void 0 : theme.borderRadius) && { "--hsk-border-radius": theme.borderRadius });
|
|
1682
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1683
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1272
1684
|
"button",
|
|
1273
1685
|
{
|
|
1274
|
-
className:
|
|
1686
|
+
className: cn("hsk-cb-btn", classNames.button, className),
|
|
1275
1687
|
onClick: () => setOpen(true),
|
|
1276
1688
|
style: customStyles,
|
|
1277
1689
|
"aria-label": "Open AI chat",
|
|
1278
1690
|
children: [
|
|
1279
|
-
/* @__PURE__ */ (0,
|
|
1691
|
+
/* @__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
1692
|
label !== void 0 ? label : null
|
|
1281
1693
|
]
|
|
1282
1694
|
}
|
|
1283
1695
|
),
|
|
1284
1696
|
open && mounted && (0, import_react_dom2.createPortal)(
|
|
1285
|
-
/* @__PURE__ */ (0,
|
|
1697
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1286
1698
|
ChatModal,
|
|
1287
1699
|
{
|
|
1288
1700
|
title,
|
|
@@ -1301,9 +1713,261 @@ function AIChatButton({
|
|
|
1301
1713
|
)
|
|
1302
1714
|
] });
|
|
1303
1715
|
}
|
|
1716
|
+
|
|
1717
|
+
// src/components/CartBadge.tsx
|
|
1718
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1719
|
+
function CartBadge({ className }) {
|
|
1720
|
+
const { cart } = useCart();
|
|
1721
|
+
if (!cart || cart.item_count === 0) return null;
|
|
1722
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: cn("hsk-cart-badge", className), children: cart.item_count });
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
// src/components/CartDrawer.tsx
|
|
1726
|
+
var import_react13 = require("react");
|
|
1727
|
+
var import_react_dom4 = require("react-dom");
|
|
1728
|
+
|
|
1729
|
+
// src/components/CheckoutModal.tsx
|
|
1730
|
+
var import_react12 = require("react");
|
|
1731
|
+
var import_react_dom3 = require("react-dom");
|
|
1732
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1733
|
+
function CheckoutModal({
|
|
1734
|
+
onClose,
|
|
1735
|
+
theme,
|
|
1736
|
+
customStyles,
|
|
1737
|
+
hskThemeAttr
|
|
1738
|
+
}) {
|
|
1739
|
+
var _a, _b, _c, _d;
|
|
1740
|
+
const { cart, loading: cartLoading } = useCart();
|
|
1741
|
+
const client = useHuskelContext();
|
|
1742
|
+
const [config, setConfig] = (0, import_react12.useState)(null);
|
|
1743
|
+
const [loading, setLoading] = (0, import_react12.useState)(true);
|
|
1744
|
+
const [checkingOut, setCheckingOut] = (0, import_react12.useState)(false);
|
|
1745
|
+
const [paymentSuccess, setPaymentSuccess] = (0, import_react12.useState)(false);
|
|
1746
|
+
(0, import_react12.useEffect)(() => {
|
|
1747
|
+
client.api.getCheckoutConfig().then((res) => setConfig(res.payment_methods)).catch((e) => console.error("[Huskel] Failed to fetch checkout config", e)).finally(() => setLoading(false));
|
|
1748
|
+
}, [client]);
|
|
1749
|
+
const handlePay = async (method) => {
|
|
1750
|
+
setCheckingOut(true);
|
|
1751
|
+
setTimeout(async () => {
|
|
1752
|
+
try {
|
|
1753
|
+
const payload = await client.api.checkoutCart();
|
|
1754
|
+
if (client.onCheckout) {
|
|
1755
|
+
client.onCheckout(payload);
|
|
1756
|
+
}
|
|
1757
|
+
setPaymentSuccess(true);
|
|
1758
|
+
setTimeout(() => {
|
|
1759
|
+
onClose();
|
|
1760
|
+
}, 3e3);
|
|
1761
|
+
} catch (e) {
|
|
1762
|
+
console.error("[Huskel] Checkout failed", e);
|
|
1763
|
+
setCheckingOut(false);
|
|
1764
|
+
}
|
|
1765
|
+
}, 1500);
|
|
1766
|
+
};
|
|
1767
|
+
const hasPaymentMethods = config && Object.values(config).some((m) => m.enabled);
|
|
1768
|
+
return (0, import_react_dom3.createPortal)(
|
|
1769
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1770
|
+
"div",
|
|
1771
|
+
{
|
|
1772
|
+
className: "hsk-cart-backdrop",
|
|
1773
|
+
style: __spreadProps(__spreadValues({}, customStyles), { zIndex: 999999 }),
|
|
1774
|
+
"data-hsk-theme": hskThemeAttr,
|
|
1775
|
+
onClick: onClose,
|
|
1776
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1777
|
+
"div",
|
|
1778
|
+
{
|
|
1779
|
+
className: "hsk-checkout-modal",
|
|
1780
|
+
style: customStyles,
|
|
1781
|
+
"data-hsk-theme": hskThemeAttr,
|
|
1782
|
+
onClick: (e) => e.stopPropagation(),
|
|
1783
|
+
children: [
|
|
1784
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-header", children: [
|
|
1785
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { children: "Secure Checkout" }),
|
|
1786
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: onClose, className: "hsk-close-btn", children: "\xD7" })
|
|
1787
|
+
] }),
|
|
1788
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-checkout-content", children: paymentSuccess ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-success", children: [
|
|
1789
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "hsk-success-icon", children: [
|
|
1790
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
|
|
1791
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polyline", { points: "22 4 12 14.01 9 11.01" })
|
|
1792
|
+
] }),
|
|
1793
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "Payment Successful!" }),
|
|
1794
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "Thank you for your order." })
|
|
1795
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-split", children: [
|
|
1796
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-summary", children: [
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "Order Summary" }),
|
|
1798
|
+
cartLoading || !cart ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "hsk-cart-loading", children: "Loading order..." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
1799
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "hsk-checkout-items", children: cart.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { children: [
|
|
1800
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
|
|
1801
|
+
item.quantity,
|
|
1802
|
+
"x ",
|
|
1803
|
+
item.name
|
|
1804
|
+
] }),
|
|
1805
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
|
|
1806
|
+
item.currency,
|
|
1807
|
+
" ",
|
|
1808
|
+
(item.price_numeric * item.quantity).toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1809
|
+
] })
|
|
1810
|
+
] }, item.id)) }),
|
|
1811
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-total", children: [
|
|
1812
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Total" }),
|
|
1813
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
|
|
1814
|
+
cart.currency,
|
|
1815
|
+
" ",
|
|
1816
|
+
cart.total.toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1817
|
+
] })
|
|
1818
|
+
] })
|
|
1819
|
+
] })
|
|
1820
|
+
] }),
|
|
1821
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-payment", children: [
|
|
1822
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "Payment Method" }),
|
|
1823
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "hsk-cart-loading", children: "Loading secure payment methods..." }) : !hasPaymentMethods ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "hsk-checkout-error", children: "No payment methods are currently available for this store." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-payment-options", children: [
|
|
1824
|
+
((_a = config == null ? void 0 : config.mpesa) == null ? void 0 : _a.enabled) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => handlePay("mpesa"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-mpesa", children: checkingOut ? "Processing..." : "Pay with M-Pesa" }),
|
|
1825
|
+
((_b = config == null ? void 0 : config.equity) == null ? void 0 : _b.enabled) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => handlePay("equity"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-equity", children: checkingOut ? "Processing..." : "Pay with Equity Bank" }),
|
|
1826
|
+
((_c = config == null ? void 0 : config.stripe) == null ? void 0 : _c.enabled) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => handlePay("stripe"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-stripe", children: checkingOut ? "Processing..." : "Pay with Card (Stripe)" }),
|
|
1827
|
+
((_d = config == null ? void 0 : config.paypal) == null ? void 0 : _d.enabled) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => handlePay("paypal"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-paypal", children: checkingOut ? "Processing..." : "Pay with PayPal" })
|
|
1828
|
+
] })
|
|
1829
|
+
] })
|
|
1830
|
+
] }) })
|
|
1831
|
+
]
|
|
1832
|
+
}
|
|
1833
|
+
)
|
|
1834
|
+
}
|
|
1835
|
+
),
|
|
1836
|
+
document.body
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
// src/components/CartDrawer.tsx
|
|
1841
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1842
|
+
function CartDrawer({
|
|
1843
|
+
trigger,
|
|
1844
|
+
className,
|
|
1845
|
+
theme
|
|
1846
|
+
}) {
|
|
1847
|
+
const { cart, loading } = useCart();
|
|
1848
|
+
const [open, setOpen] = (0, import_react13.useState)(false);
|
|
1849
|
+
const [showCheckout, setShowCheckout] = (0, import_react13.useState)(false);
|
|
1850
|
+
const [mounted, setMounted] = (0, import_react13.useState)(false);
|
|
1851
|
+
const client = useHuskelContext();
|
|
1852
|
+
(0, import_react13.useEffect)(() => {
|
|
1853
|
+
setMounted(true);
|
|
1854
|
+
const handleTriggerCheckout = () => {
|
|
1855
|
+
setShowCheckout(true);
|
|
1856
|
+
setOpen(false);
|
|
1857
|
+
};
|
|
1858
|
+
window.addEventListener("huskel:trigger_checkout", handleTriggerCheckout);
|
|
1859
|
+
return () => {
|
|
1860
|
+
window.removeEventListener("huskel:trigger_checkout", handleTriggerCheckout);
|
|
1861
|
+
};
|
|
1862
|
+
}, []);
|
|
1863
|
+
(0, import_react13.useEffect)(() => {
|
|
1864
|
+
if (open) {
|
|
1865
|
+
document.body.style.overflow = "hidden";
|
|
1866
|
+
} else {
|
|
1867
|
+
document.body.style.overflow = "";
|
|
1868
|
+
}
|
|
1869
|
+
return () => {
|
|
1870
|
+
document.body.style.overflow = "";
|
|
1871
|
+
};
|
|
1872
|
+
}, [open]);
|
|
1873
|
+
const handleCheckout = async () => {
|
|
1874
|
+
if (!cart || cart.items.length === 0) return;
|
|
1875
|
+
setShowCheckout(true);
|
|
1876
|
+
};
|
|
1877
|
+
const isStringTheme = typeof theme === "string";
|
|
1878
|
+
const hskThemeAttr = isStringTheme ? theme : void 0;
|
|
1879
|
+
const customStyles = !isStringTheme && theme ? __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, (theme == null ? void 0 : theme.primaryColor) && { "--hsk-primary": theme.primaryColor, "--hsk-primary-color": 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 }), (theme == null ? void 0 : theme.borderRadius) && { "--hsk-border-radius": theme.borderRadius }) : void 0;
|
|
1880
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
1881
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { onClick: () => setOpen(true), style: { display: "inline-block" }, children: trigger || /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1882
|
+
"button",
|
|
1883
|
+
{
|
|
1884
|
+
className: cn("hsk-cart-trigger", className),
|
|
1885
|
+
style: customStyles,
|
|
1886
|
+
"data-hsk-theme": hskThemeAttr,
|
|
1887
|
+
"aria-label": "Open cart",
|
|
1888
|
+
children: [
|
|
1889
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1890
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "9", cy: "21", r: "1" }),
|
|
1891
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "20", cy: "21", r: "1" }),
|
|
1892
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" })
|
|
1893
|
+
] }),
|
|
1894
|
+
cart && cart.item_count > 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "hsk-cart-trigger-badge", children: cart.item_count }) : null
|
|
1895
|
+
]
|
|
1896
|
+
}
|
|
1897
|
+
) }),
|
|
1898
|
+
open && mounted && (0, import_react_dom4.createPortal)(
|
|
1899
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1900
|
+
"div",
|
|
1901
|
+
{
|
|
1902
|
+
className: "hsk-cart-backdrop",
|
|
1903
|
+
style: customStyles,
|
|
1904
|
+
"data-hsk-theme": hskThemeAttr,
|
|
1905
|
+
onClick: () => setOpen(false),
|
|
1906
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1907
|
+
"div",
|
|
1908
|
+
{
|
|
1909
|
+
className: "hsk-cart-bottom-sheet",
|
|
1910
|
+
style: customStyles,
|
|
1911
|
+
"data-hsk-theme": hskThemeAttr,
|
|
1912
|
+
onClick: (e) => e.stopPropagation(),
|
|
1913
|
+
children: [
|
|
1914
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "hsk-cart-sheet-handle" }),
|
|
1915
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "hsk-cart-sheet-header", children: [
|
|
1916
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { children: "Your Cart" }),
|
|
1917
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: () => setOpen(false), className: "hsk-close-btn", children: "\xD7" })
|
|
1918
|
+
] }),
|
|
1919
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "hsk-cart-sheet-content", children: loading && !cart ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "hsk-cart-loading", children: "Loading cart..." }) : !cart || cart.items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "hsk-cart-empty", children: "Your cart is empty." }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("ul", { className: "hsk-cart-items", children: cart.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { className: "hsk-cart-item", children: [
|
|
1920
|
+
item.image && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: item.image, alt: item.name, className: "hsk-cart-item-img" }),
|
|
1921
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "hsk-cart-item-info", children: [
|
|
1922
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "hsk-cart-item-name", children: item.name }),
|
|
1923
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "hsk-cart-item-price", children: [
|
|
1924
|
+
item.currency,
|
|
1925
|
+
" ",
|
|
1926
|
+
item.price_numeric.toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1927
|
+
] })
|
|
1928
|
+
] }),
|
|
1929
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "hsk-cart-item-qty", children: [
|
|
1930
|
+
"x",
|
|
1931
|
+
item.quantity
|
|
1932
|
+
] })
|
|
1933
|
+
] }, item.id)) }) }),
|
|
1934
|
+
cart && cart.items.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "hsk-cart-sheet-footer", children: [
|
|
1935
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "hsk-cart-total", children: [
|
|
1936
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Total" }),
|
|
1937
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
|
|
1938
|
+
cart.currency,
|
|
1939
|
+
" ",
|
|
1940
|
+
cart.total.toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1941
|
+
] })
|
|
1942
|
+
] }),
|
|
1943
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: handleCheckout, className: "hsk-checkout-btn", children: "Checkout securely" })
|
|
1944
|
+
] })
|
|
1945
|
+
]
|
|
1946
|
+
}
|
|
1947
|
+
)
|
|
1948
|
+
}
|
|
1949
|
+
),
|
|
1950
|
+
document.body
|
|
1951
|
+
),
|
|
1952
|
+
showCheckout && mounted && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1953
|
+
CheckoutModal,
|
|
1954
|
+
{
|
|
1955
|
+
onClose: () => {
|
|
1956
|
+
setShowCheckout(false);
|
|
1957
|
+
setOpen(false);
|
|
1958
|
+
},
|
|
1959
|
+
theme: isStringTheme ? theme : void 0,
|
|
1960
|
+
customStyles,
|
|
1961
|
+
hskThemeAttr
|
|
1962
|
+
}
|
|
1963
|
+
)
|
|
1964
|
+
] });
|
|
1965
|
+
}
|
|
1304
1966
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1305
1967
|
0 && (module.exports = {
|
|
1306
1968
|
AIChatButton,
|
|
1969
|
+
CartBadge,
|
|
1970
|
+
CartDrawer,
|
|
1307
1971
|
ChatWidget,
|
|
1308
1972
|
HuskelAPI,
|
|
1309
1973
|
HuskelClient,
|
|
@@ -1312,6 +1976,7 @@ function AIChatButton({
|
|
|
1312
1976
|
Sparkle,
|
|
1313
1977
|
getHuskelClient,
|
|
1314
1978
|
initHuskel,
|
|
1979
|
+
useCart,
|
|
1315
1980
|
useChat,
|
|
1316
1981
|
useHuskel,
|
|
1317
1982
|
useIngest,
|