@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.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
- const err = { status: res.status, message: text };
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.shopperId,
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
- setError((_b = e.message) != null ? _b : "Search failed");
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 userMsg = { role: "user", content: query };
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
- const assistantMsg = { role: "assistant", content: res.answer };
568
- setMessages((prev) => [...prev, assistantMsg]);
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
- setError((_c = e == null ? void 0 : e.message) != null ? _c : "Chat request failed");
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
- setLoading(false);
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/components/SearchBar.tsx
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, import_react7.useState)("");
605
- const [open, setOpen] = (0, import_react7.useState)(false);
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, import_react7.useRef)();
608
- const wrap = (0, import_react7.useRef)(null);
609
- (0, import_react7.useEffect)(() => {
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, import_react7.useEffect)(() => {
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: `hsk-sb-wrap ${classNames.root || ""} ${className || ""}`, ref: wrap, style: customStyles, children: [
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: `hsk-sb-input ${classNames.input || ""} ${inputClassName || ""}`,
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: `hsk-sb-drop ${classNames.dropdown || ""} ${dropdownClassName || ""}`, style: { position: "absolute" }, children: [
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: `hsk-sb-row hsk-sb-fade ${classNames.row || ""}`,
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 import_react8 = require("react");
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 SparkleIcon = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime3.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_runtime3.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" }) });
695
- var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
696
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
697
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
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
- const { results, loading, search } = useSearch();
711
- const initiated = (0, import_react8.useRef)(false);
712
- (0, import_react8.useEffect)(() => {
713
- if (!initiated.current) {
714
- initiated.current = true;
715
- search(productName, limit);
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
- (0, import_react8.useEffect)(() => {
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, import_react8.useEffect)(() => {
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 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 });
738
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
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: `hsk-sp-backdrop ${classNames.backdrop || ""}`,
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, import_jsx_runtime3.jsxs)("div", { className: `hsk-sp-card ${classNames.card || ""}`, onClick: (e) => e.stopPropagation(), children: [
749
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-header", children: [
750
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-header-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SparkleIcon, {}) }),
751
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-header-body", children: [
752
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-header-title", children: [
753
- "Similar to \u201C",
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, import_jsx_runtime3.jsx)("button", { className: "hsk-sp-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}) })
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
- loading && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-bar" }),
762
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-results", children: [
763
- !loading && results.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-empty", children: "No similar products found." }),
764
- results.map((r, i) => {
765
- var _a, _b, _c;
766
- const price = parseFloat(((_a = r.product.price) == null ? void 0 : _a.replace(/[^0-9.]/g, "")) || "0");
767
- const currency = (_b = r.product.currency) != null ? _b : "KES";
768
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
769
- "div",
770
- {
771
- className: `hsk-sp-item ${classNames.item || ""}`,
772
- style: { animationDelay: `${i * 55}ms` },
773
- children: [
774
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-img-wrap", children: ((_c = r.product.images) == null ? void 0 : _c[0]) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("img", { src: r.product.images[0], alt: r.product.name }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
775
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-item-body", children: [
776
- r.product.category && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-item-cat", children: r.product.category }),
777
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-item-name", children: r.product.name }),
778
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-item-price-row", children: [
779
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-item-currency", children: currency }),
780
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-item-price", children: price.toLocaleString() })
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, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-actions", children: [
783
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
784
- "button",
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
- r.id
804
- );
805
- })
806
- ] }),
807
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-footer", children: [
808
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "hsk-sp-badge", style: { display: "inline-flex", alignItems: "center", gap: "4px" }, children: [
809
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SparkleIcon, {}),
810
- " Huskel AI"
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, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-esc", children: "Esc to close" })
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, import_react8.useState)(false);
830
- const [mounted, setMounted] = (0, import_react8.useState)(false);
831
- (0, import_react8.useEffect)(() => {
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, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
836
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
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: `hsk-sp-btn ${classNames.button || ""} ${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, import_jsx_runtime3.jsx)(SparkleIcon, {})
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, import_jsx_runtime3.jsx)(
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 import_react9 = require("react");
869
- var import_jsx_runtime4 = require("react/jsx-runtime");
870
- var SparkleIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime4.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_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" }) });
871
- 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: [
872
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m5 12 7-7 7 7" }),
873
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 19V5" })
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, import_jsx_runtime4.jsxs)("div", { className: "hsk-source-card", onClick: () => onSelect == null ? void 0 : onSelect(source), children: [
878
- source.image && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: source.image, alt: source.name, className: "hsk-source-img" }),
879
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
880
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-source-name", children: source.name }),
881
- source.price && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-source-price", children: [
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, import_react9.useState)("");
902
- const bottomRef = (0, import_react9.useRef)(null);
903
- const textareaRef = (0, import_react9.useRef)(null);
904
- (0, import_react9.useEffect)(() => {
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, import_jsx_runtime4.jsxs)(
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: `hsk-chat-widget ${classNames.root || ""} ${className || ""}`,
1340
+ className: cn("hsk-chat-widget", classNames.root, className),
932
1341
  style: customStyles,
933
1342
  children: [
934
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `hsk-chat-header ${classNames.header || ""}`, children: [
935
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-chat-header-icon", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SparkleIcon2, {}) }),
936
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-chat-title", children: title }),
937
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-chat-badge", children: "AI" }),
938
- messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "hsk-chat-reset", onClick: reset, style: { marginLeft: "auto" }, children: "Clear" })
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, import_jsx_runtime4.jsxs)("div", { className: "hsk-chat-messages", children: [
941
- messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-chat-empty", children: [
942
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-chat-empty-icon", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SparkleIcon2, {}) }),
943
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: emptyStateText }),
944
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-chat-empty-suggestions", children: emptyStateSuggestions })
945
- ] }) : messages.map((msg, idx) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
946
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `hsk-msg-row ${msg.role}`, children: [
947
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `hsk-msg-avatar ${msg.role === "assistant" ? "ai" : "user"}`, children: msg.role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SparkleIcon2, {}) : "U" }),
948
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `hsk-msg-bubble ${msg.role} ${classNames.messageBubble || ""}`, children: msg.content })
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, import_jsx_runtime4.jsx)("div", { className: "hsk-sources-container", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-sources", children: sources.map((src, si) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SourceCard, { source: src, defaultCurrency, onSelect: onSelectSource }, si)) }) })
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, import_jsx_runtime4.jsxs)("div", { className: "hsk-msg-row", children: [
953
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-msg-avatar ai", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SparkleIcon2, {}) }),
954
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-typing", children: [
955
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-typing-dot" }),
956
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-typing-dot" }),
957
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-typing-dot" })
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, import_jsx_runtime4.jsx)("div", { className: "hsk-chat-error", children: error }),
961
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: bottomRef })
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, import_jsx_runtime4.jsxs)("div", { className: "hsk-chat-input-area", children: [
964
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
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: `hsk-chat-input ${classNames.input || ""}`,
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, import_jsx_runtime4.jsx)(
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, import_jsx_runtime4.jsx)(ArrowUpIcon, {})
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 import_react10 = require("react");
1410
+ var import_react11 = require("react");
995
1411
  var import_react_dom2 = require("react-dom");
996
- var import_jsx_runtime5 = require("react/jsx-runtime");
997
- var SparkleIcon3 = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime5.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_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" }) });
998
- 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: [
999
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m5 12 7-7 7 7" }),
1000
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 19V5" })
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, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1003
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1004
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
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, 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: "m9 18 6-6-6-6" }) });
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, import_react10.useRef)(null);
1015
- const [showNext, setShowNext] = (0, import_react10.useState)(false);
1016
- const measure = (0, import_react10.useCallback)(() => {
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, import_react10.useEffect)(() => {
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, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-sources-wrap", children: [
1039
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-sources", ref: railRef, children: sources.map((src, si) => {
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, import_jsx_runtime5.jsxs)(
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, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-src-imgwrap", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: src.image, alt: src.name, loading: "lazy" }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-src-imgwrap-empty", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon3, {}) }),
1049
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-src-info", children: [
1050
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-src-name", children: src.name }),
1051
- src.price && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-src-price", children: [
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, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1063
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
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, import_jsx_runtime5.jsx)("button", { className: "hsk-cb-sources-next", onClick: scrollNext, "aria-label": "See more", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronRightIcon, {}) })
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 = "AI Shopping Assistant",
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, import_react10.useState)("");
1089
- const [selectedProduct, setSelectedProduct] = (0, import_react10.useState)(null);
1090
- const bottomRef = (0, import_react10.useRef)(null);
1091
- const textareaRef = (0, import_react10.useRef)(null);
1092
- (0, import_react10.useEffect)(() => {
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, import_react10.useEffect)(() => {
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, import_jsx_runtime5.jsx)(
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: `hsk-cb-overlay ${classNames.overlay || ""}`,
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, import_jsx_runtime5.jsxs)("div", { className: `hsk-cb-panel ${classNames.panel || ""}`, onClick: (e) => e.stopPropagation(), children: [
1144
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-topbar", children: [
1145
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-topbar-left", children: [
1146
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-cb-topbar-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon3, {}) }),
1147
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
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, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-topbar-actions", children: [
1153
- messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "hsk-cb-topbar-btn", onClick: reset, children: "Clear chat" }),
1154
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "hsk-cb-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseIcon2, {}) })
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, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-msgs", children: [
1158
- messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-empty", children: [
1159
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-empty-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon3, {}) }),
1160
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-empty-title", children: "What can I help you find?" }),
1161
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-empty-sub", children: "Ask about products, budgets, gift ideas, specs \u2014 I'll search the entire catalogue for you." }),
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, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-msg-group", children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-user-msg", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-user-bubble", children: msg.content }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-ai-msg", children: [
1175
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon3, {}) }),
1176
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-ai-body", children: [
1177
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-ai-text", children: msg.content }),
1178
- isLast && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
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, import_jsx_runtime5.jsxs)(
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, import_jsx_runtime5.jsx)("img", { className: "hsk-cb-selected-img", src: selectedProduct.image, alt: selectedProduct.name }),
1196
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-selected-info", children: [
1197
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-selected-name", children: selectedProduct.name }),
1198
- selectedProduct.price && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-selected-price", children: [
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, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-typing-row", children: [
1208
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon3, {}) }),
1209
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-typing", children: [
1210
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-dot" }),
1211
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-dot" }),
1212
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-dot" })
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, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-error", children: error }),
1216
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: bottomRef, style: { height: 1 } })
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, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-input-wrap", children: [
1219
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-input-box", children: [
1220
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
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: `hsk-cb-textarea ${classNames.input || ""}`,
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, import_jsx_runtime5.jsx)(
1646
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1235
1647
  "button",
1236
1648
  {
1237
- className: `hsk-cb-send ${classNames.sendButton || ""}`,
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, import_jsx_runtime5.jsx)(ArrowUpIcon2, {})
1653
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArrowUpIcon3, {})
1242
1654
  }
1243
1655
  )
1244
1656
  ] }),
1245
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 searches the whole catalogue in real time" })
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, import_react10.useState)(false);
1265
- const [mounted, setMounted] = (0, import_react10.useState)(false);
1266
- (0, import_react10.useEffect)(() => {
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, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1271
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
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: `hsk-cb-btn ${classNames.button || ""} ${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, import_jsx_runtime5.jsx)("span", { className: "hsk-cb-btn-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon3, {}) }),
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, import_jsx_runtime5.jsx)(
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,