@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.mjs CHANGED
@@ -63,7 +63,15 @@ var HuskelAPI = class {
63
63
  });
64
64
  if (!res.ok) {
65
65
  const text = await res.text();
66
- const err = { status: res.status, message: text };
66
+ let message = text;
67
+ try {
68
+ const parsed = JSON.parse(text);
69
+ if (parsed && typeof parsed.error === "string") {
70
+ message = parsed.error;
71
+ }
72
+ } catch (e) {
73
+ }
74
+ const err = { status: res.status, message };
67
75
  if (res.status >= 400 && res.status < 500) {
68
76
  log("error", `${path} failed [${res.status}]`, text);
69
77
  throw err;
@@ -114,6 +122,52 @@ var HuskelAPI = class {
114
122
  log("info", "chat query", query);
115
123
  return this.post("/chat", { query, siteId: this.siteId, history });
116
124
  }
125
+ // --- Cart System ---
126
+ buildHeaders() {
127
+ var _a, _b;
128
+ const headers = {
129
+ "Content-Type": "application/json",
130
+ "X-Huskel-Token": this.apiToken,
131
+ "X-Huskel-Site": this.siteId
132
+ };
133
+ const shopperId = (_a = this.getShopperId) == null ? void 0 : _a.call(this);
134
+ if (shopperId) headers["X-Huskel-Shopper-Id"] = shopperId;
135
+ const sessionId = (_b = this.getSessionId) == null ? void 0 : _b.call(this);
136
+ if (sessionId) headers["X-Huskel-Session-Id"] = sessionId;
137
+ return headers;
138
+ }
139
+ async getCart() {
140
+ const res = await fetch(`${this.apiUrl}/cart?siteId=${this.siteId}`, {
141
+ headers: this.buildHeaders()
142
+ });
143
+ if (!res.ok) throw new Error("Failed to fetch cart");
144
+ return res.json();
145
+ }
146
+ async clearCart() {
147
+ const res = await fetch(`${this.apiUrl}/cart?siteId=${this.siteId}`, {
148
+ method: "DELETE",
149
+ headers: this.buildHeaders()
150
+ });
151
+ if (!res.ok) throw new Error("Failed to clear cart");
152
+ return res.json();
153
+ }
154
+ async checkoutCart() {
155
+ const res = await fetch(`${this.apiUrl}/cart/checkout`, {
156
+ method: "POST",
157
+ headers: this.buildHeaders(),
158
+ body: JSON.stringify({ siteId: this.siteId })
159
+ });
160
+ if (!res.ok) throw new Error("Failed to checkout cart");
161
+ return res.json();
162
+ }
163
+ async getCheckoutConfig() {
164
+ const res = await fetch(`${this.apiUrl}/checkout/config?site_id=${this.siteId}`, {
165
+ method: "GET",
166
+ headers: this.buildHeaders()
167
+ });
168
+ if (!res.ok) throw new Error("Failed to fetch checkout config");
169
+ return res.json();
170
+ }
117
171
  };
118
172
 
119
173
  // src/client.ts
@@ -221,13 +275,14 @@ var _HuskelClient = class _HuskelClient {
221
275
  if (!apiUrl) console.error('[Huskel] Missing apiUrl. Set it via <HuskelProvider apiUrl="..."> or NEXT_PUBLIC_HUSKEL_API_URL.');
222
276
  if (!apiToken) console.error('[Huskel] Missing apiToken. Set it via <HuskelProvider apiToken="..."> or NEXT_PUBLIC_HUSKEL_API_TOKEN.');
223
277
  this.shopperId = config.shopperId;
278
+ this.onCheckout = config.onCheckout;
224
279
  this.initSession();
225
280
  this.loadIngestedCache();
226
281
  this.api = new HuskelAPI(
227
282
  apiUrl,
228
283
  siteId,
229
284
  apiToken,
230
- () => this.shopperId,
285
+ () => this.getShopperId(),
231
286
  () => this.sessionId
232
287
  );
233
288
  instance = this;
@@ -268,7 +323,7 @@ var _HuskelClient = class _HuskelClient {
268
323
  this.shopperId = id;
269
324
  }
270
325
  getShopperId() {
271
- return this.shopperId;
326
+ return this.shopperId || "guest_" + this.sessionId;
272
327
  }
273
328
  getSessionId() {
274
329
  return this.sessionId;
@@ -441,7 +496,15 @@ function useSearch() {
441
496
  }
442
497
  } catch (e) {
443
498
  if (gen === genRef.current) {
444
- setError((_b = e.message) != null ? _b : "Search failed");
499
+ let msg = (_b = e == null ? void 0 : e.message) != null ? _b : "Search failed";
500
+ try {
501
+ const parsed = JSON.parse(msg);
502
+ if (parsed && parsed.error) {
503
+ msg = parsed.error;
504
+ }
505
+ } catch (e2) {
506
+ }
507
+ setError(msg);
445
508
  }
446
509
  } finally {
447
510
  if (gen === genRef.current) setLoading(false);
@@ -516,38 +579,127 @@ function useChat() {
516
579
  const [loading, setLoading] = useState3(false);
517
580
  const [error, setError] = useState3(null);
518
581
  const abortRef = useRef5(null);
519
- const send = useCallback3(async (query) => {
520
- var _a, _b, _c;
582
+ const send = useCallback3(async (query, displayQuery) => {
583
+ var _a, _b, _c, _d, _e;
521
584
  if (!query.trim() || loading) return;
522
585
  (_a = abortRef.current) == null ? void 0 : _a.abort();
523
586
  abortRef.current = new AbortController();
524
- const userMsg = { role: "user", content: query };
587
+ const signal = abortRef.current.signal;
588
+ const userMsg = { role: "user", content: displayQuery != null ? displayQuery : query };
525
589
  setMessages((prev) => [...prev, userMsg]);
526
590
  setLoading(true);
527
591
  setError(null);
528
592
  try {
529
593
  const history = messages.map((m) => ({ role: m.role, content: m.content }));
530
594
  const res = await client.api.chat(query, history);
531
- const assistantMsg = { role: "assistant", content: res.answer };
532
- setMessages((prev) => [...prev, assistantMsg]);
595
+ if (signal.aborted) return;
596
+ const fullAnswer = res.answer || "";
597
+ const words = fullAnswer.split(/(\s+)/);
598
+ setMessages((prev) => [...prev, { role: "assistant", content: "" }]);
599
+ let currentContent = "";
600
+ for (const word of words) {
601
+ if (signal.aborted) return;
602
+ currentContent += word;
603
+ setMessages((prev) => {
604
+ const next = [...prev];
605
+ if (next.length > 0) {
606
+ next[next.length - 1] = { role: "assistant", content: currentContent };
607
+ }
608
+ return next;
609
+ });
610
+ await new Promise((resolve) => setTimeout(resolve, 25));
611
+ }
612
+ if (signal.aborted) return;
533
613
  setSources((_b = res.sources) != null ? _b : []);
614
+ if (((_c = res.action) == null ? void 0 : _c.type) === "add_to_cart" || res.checkout) {
615
+ if (typeof window !== "undefined") {
616
+ window.dispatchEvent(new CustomEvent("huskel:cart_updated", { detail: res.checkout }));
617
+ }
618
+ }
619
+ if (((_d = res.action) == null ? void 0 : _d.type) === "checkout") {
620
+ if (typeof window !== "undefined") {
621
+ window.dispatchEvent(new CustomEvent("huskel:trigger_checkout", { detail: res.checkout }));
622
+ }
623
+ }
624
+ if (res.checkout && client.onCheckout) {
625
+ client.onCheckout(res.checkout);
626
+ }
534
627
  } catch (e) {
535
- setError((_c = e == null ? void 0 : e.message) != null ? _c : "Chat request failed");
628
+ if (signal.aborted) return;
629
+ let msg = (_e = e == null ? void 0 : e.message) != null ? _e : "Chat request failed";
630
+ try {
631
+ const parsed = JSON.parse(msg);
632
+ if (parsed && parsed.error) {
633
+ msg = parsed.error;
634
+ }
635
+ } catch (e2) {
636
+ }
637
+ setError(msg);
536
638
  setMessages((prev) => prev.slice(0, -1));
537
639
  } finally {
538
- setLoading(false);
640
+ if (!signal.aborted) {
641
+ setLoading(false);
642
+ }
539
643
  }
540
644
  }, [client, messages, loading]);
541
645
  const reset = useCallback3(() => {
646
+ var _a;
647
+ (_a = abortRef.current) == null ? void 0 : _a.abort();
542
648
  setMessages([]);
543
649
  setSources([]);
544
650
  setError(null);
651
+ setLoading(false);
545
652
  }, []);
546
653
  return { messages, sources, loading, error, send, reset };
547
654
  }
548
655
 
656
+ // src/hooks/useCart.ts
657
+ import { useState as useState4, useEffect as useEffect3, useCallback as useCallback4 } from "react";
658
+ function useCart() {
659
+ const client = useHuskelContext();
660
+ const [cart, setCart] = useState4(null);
661
+ const [loading, setLoading] = useState4(false);
662
+ const shopperId = client.getShopperId();
663
+ const fetchCart = useCallback4(async () => {
664
+ if (!shopperId) return;
665
+ setLoading(true);
666
+ try {
667
+ const res = await client.api.getCart();
668
+ setCart(res);
669
+ } catch (e) {
670
+ console.error("[Huskel] Failed to fetch cart", e);
671
+ } finally {
672
+ setLoading(false);
673
+ }
674
+ }, [client, shopperId]);
675
+ useEffect3(() => {
676
+ fetchCart();
677
+ const handleCartUpdate = (e) => {
678
+ if (e.detail) {
679
+ setCart(e.detail);
680
+ } else {
681
+ fetchCart();
682
+ }
683
+ };
684
+ if (typeof window !== "undefined") {
685
+ window.addEventListener("huskel:cart_updated", handleCartUpdate);
686
+ return () => window.removeEventListener("huskel:cart_updated", handleCartUpdate);
687
+ }
688
+ }, [fetchCart, shopperId]);
689
+ return { cart, loading, fetchCart };
690
+ }
691
+
692
+ // src/components/SearchBar.tsx
693
+ import { useState as useState5, useEffect as useEffect4, useRef as useRef6 } from "react";
694
+
695
+ // src/utils/cn.ts
696
+ import { clsx } from "clsx";
697
+ import { twMerge } from "tailwind-merge";
698
+ function cn(...inputs) {
699
+ return twMerge(clsx(inputs));
700
+ }
701
+
549
702
  // src/components/SearchBar.tsx
550
- import { useState as useState4, useEffect as useEffect3, useRef as useRef6 } from "react";
551
703
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
552
704
  var SearchIcon = () => /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
553
705
  /* @__PURE__ */ jsx2("circle", { cx: "8.5", cy: "8.5", r: "5.5" }),
@@ -565,12 +717,12 @@ function SearchBar({
565
717
  theme,
566
718
  classNames = {}
567
719
  }) {
568
- const [query, setQuery] = useState4("");
569
- const [open, setOpen] = useState4(false);
720
+ const [query, setQuery] = useState5("");
721
+ const [open, setOpen] = useState5(false);
570
722
  const { results, loading, search, clear } = useSearch();
571
723
  const timer = useRef6();
572
724
  const wrap = useRef6(null);
573
- useEffect3(() => {
725
+ useEffect4(() => {
574
726
  clearTimeout(timer.current);
575
727
  if (!query.trim()) {
576
728
  clear();
@@ -583,7 +735,7 @@ function SearchBar({
583
735
  }, debounceMs);
584
736
  return () => clearTimeout(timer.current);
585
737
  }, [query]);
586
- useEffect3(() => {
738
+ useEffect4(() => {
587
739
  const h = (e) => {
588
740
  if (wrap.current && !wrap.current.contains(e.target)) setOpen(false);
589
741
  };
@@ -596,13 +748,13 @@ function SearchBar({
596
748
  onSelect == null ? void 0 : onSelect(r);
597
749
  };
598
750
  const showDrop = open && query.trim().length > 0;
599
- 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 });
600
- return /* @__PURE__ */ jsxs("div", { className: `hsk-sb-wrap ${classNames.root || ""} ${className || ""}`, ref: wrap, style: customStyles, children: [
751
+ 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 });
752
+ return /* @__PURE__ */ jsxs("div", { className: cn("hsk-sb-wrap", classNames.root, className), ref: wrap, style: customStyles, children: [
601
753
  /* @__PURE__ */ jsx2("span", { className: "hsk-sb-icon", children: /* @__PURE__ */ jsx2(SearchIcon, {}) }),
602
754
  /* @__PURE__ */ jsx2(
603
755
  "input",
604
756
  {
605
- className: `hsk-sb-input ${classNames.input || ""} ${inputClassName || ""}`,
757
+ className: cn("hsk-sb-input", classNames.input, inputClassName),
606
758
  type: "text",
607
759
  value: query,
608
760
  placeholder,
@@ -612,7 +764,7 @@ function SearchBar({
612
764
  spellCheck: false
613
765
  }
614
766
  ),
615
- showDrop && /* @__PURE__ */ jsxs("div", { className: `hsk-sb-drop ${classNames.dropdown || ""} ${dropdownClassName || ""}`, style: { position: "absolute" }, children: [
767
+ showDrop && /* @__PURE__ */ jsxs("div", { className: cn("hsk-sb-drop", classNames.dropdown, dropdownClassName), style: { position: "absolute" }, children: [
616
768
  loading && /* @__PURE__ */ jsx2("div", { className: "hsk-sb-loading-bar" }),
617
769
  results.length === 0 && !loading && /* @__PURE__ */ jsxs("div", { className: "hsk-sb-empty", children: [
618
770
  "No results for \u201C",
@@ -633,7 +785,7 @@ function SearchBar({
633
785
  ) : /* @__PURE__ */ jsxs(
634
786
  "div",
635
787
  {
636
- className: `hsk-sb-row hsk-sb-fade ${classNames.row || ""}`,
788
+ className: cn("hsk-sb-row hsk-sb-fade", classNames.row),
637
789
  style: { animationDelay: `${i * 18}ms` },
638
790
  onClick: () => handleSelect(r),
639
791
  children: [
@@ -652,14 +804,124 @@ function SearchBar({
652
804
  }
653
805
 
654
806
  // src/components/Sparkle.tsx
655
- import { useState as useState5, useEffect as useEffect4, useRef as useRef7 } from "react";
807
+ import { useState as useState6, useEffect as useEffect5, useRef as useRef7 } from "react";
656
808
  import { createPortal } from "react-dom";
657
- import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
658
- var SparkleIcon = ({ className }) => /* @__PURE__ */ jsx3("svg", { className, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx3("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" }) });
809
+
810
+ // src/utils/markdown.tsx
811
+ import { Fragment, jsx as jsx3 } from "react/jsx-runtime";
812
+ var parseInline = (text, keyPrefix) => {
813
+ const tokenRegex = /(\[[^\]]+\]\([^)]+\)|\*\*[^*]+\*\*|`[^`]+`)/g;
814
+ const parts = text.split(tokenRegex);
815
+ return parts.map((part, index) => {
816
+ if (!part) return null;
817
+ const key = `${keyPrefix}-inline-${index}`;
818
+ if (part.startsWith("`") && part.endsWith("`")) {
819
+ return /* @__PURE__ */ jsx3("code", { className: "hsk-markdown-code", children: part.slice(1, -1) }, key);
820
+ }
821
+ if (part.startsWith("**") && part.endsWith("**")) {
822
+ return /* @__PURE__ */ jsx3("strong", { children: parseInline(part.slice(2, -2), key) }, key);
823
+ }
824
+ const linkMatch = part.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
825
+ if (linkMatch) {
826
+ const url = linkMatch[2];
827
+ const isSafeUrl = /^(https?|mailto|tel):/i.test(url) || url.startsWith("/");
828
+ if (isSafeUrl) {
829
+ return /* @__PURE__ */ jsx3("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: "hsk-markdown-link", children: parseInline(linkMatch[1], key) }, key);
830
+ }
831
+ return /* @__PURE__ */ jsx3("span", { children: parseInline(linkMatch[1], key) }, key);
832
+ }
833
+ return part;
834
+ });
835
+ };
836
+ function renderMarkdown(content) {
837
+ const lines = content.split("\n");
838
+ const elements = [];
839
+ let i = 0;
840
+ while (i < lines.length) {
841
+ const line = lines[i];
842
+ const key = `md-line-${i}`;
843
+ if (!line.trim()) {
844
+ i++;
845
+ continue;
846
+ }
847
+ const headerMatch = line.match(/^(#{1,3})\s+(.*)/);
848
+ if (headerMatch) {
849
+ const level = headerMatch[1].length;
850
+ const Tag = `h${level + 3}`;
851
+ elements.push(/* @__PURE__ */ jsx3(Tag, { className: `hsk-markdown-h${level}`, children: parseInline(headerMatch[2], key) }, key));
852
+ i++;
853
+ continue;
854
+ }
855
+ if (line.match(/^[-*]\s+/)) {
856
+ const listItems = [];
857
+ while (i < lines.length && lines[i].match(/^[-*]\s+/)) {
858
+ const itemText = lines[i].replace(/^[-*]\s+/, "");
859
+ listItems.push(/* @__PURE__ */ jsx3("li", { children: parseInline(itemText, `li-${i}`) }, `li-${i}`));
860
+ i++;
861
+ }
862
+ elements.push(/* @__PURE__ */ jsx3("ul", { className: "hsk-markdown-list", children: listItems }, `ul-${key}`));
863
+ continue;
864
+ }
865
+ if (line.trim().startsWith("|")) {
866
+ const tableRows = [];
867
+ let isHeader = true;
868
+ while (i < lines.length && lines[i].trim().startsWith("|")) {
869
+ const rowLine = lines[i].trim();
870
+ if (rowLine.match(/^\|[-:| ]+\|$/)) {
871
+ i++;
872
+ isHeader = false;
873
+ continue;
874
+ }
875
+ const cells = rowLine.split("|").slice(1, -1).map((c) => c.trim());
876
+ const Tag = isHeader ? "th" : "td";
877
+ tableRows.push(
878
+ /* @__PURE__ */ jsx3("tr", { children: cells.map((cell, cIdx) => /* @__PURE__ */ jsx3(Tag, { children: parseInline(cell, `td-${i}-${cIdx}`) }, `td-${i}-${cIdx}`)) }, `tr-${i}`)
879
+ );
880
+ i++;
881
+ }
882
+ elements.push(
883
+ /* @__PURE__ */ jsx3("div", { className: "hsk-table-wrapper", children: /* @__PURE__ */ jsx3("table", { className: "hsk-markdown-table", children: /* @__PURE__ */ jsx3("tbody", { children: tableRows }) }) }, `table-wrapper-${key}`)
884
+ );
885
+ continue;
886
+ }
887
+ elements.push(
888
+ /* @__PURE__ */ jsx3("p", { className: "hsk-markdown-p", children: parseInline(line, key) }, key)
889
+ );
890
+ i++;
891
+ }
892
+ return /* @__PURE__ */ jsx3(Fragment, { children: elements });
893
+ }
894
+
895
+ // src/components/Sparkle.tsx
896
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
897
+ var SparkleIcon = ({ className }) => /* @__PURE__ */ jsx4("svg", { className, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx4("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" }) });
659
898
  var CloseIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
660
- /* @__PURE__ */ jsx3("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
661
- /* @__PURE__ */ jsx3("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
899
+ /* @__PURE__ */ jsx4("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
900
+ /* @__PURE__ */ jsx4("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
662
901
  ] });
902
+ var ArrowUpIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
903
+ /* @__PURE__ */ jsx4("path", { d: "m5 12 7-7 7 7" }),
904
+ /* @__PURE__ */ jsx4("path", { d: "M12 19V5" })
905
+ ] });
906
+ var getFriendlyError = (err) => {
907
+ let str = "";
908
+ if (typeof err === "string") str = err;
909
+ else if (err && typeof err === "object" && err.message) str = err.message;
910
+ else try {
911
+ str = JSON.stringify(err);
912
+ } catch (e) {
913
+ str = String(err);
914
+ }
915
+ if (str.toLowerCase().includes("token limit")) {
916
+ return "You've reached your usage limit. Please update your billing limits in your dashboard to continue.";
917
+ }
918
+ try {
919
+ const parsed = JSON.parse(str);
920
+ return parsed.error || parsed.message || str;
921
+ } catch (e) {
922
+ return str;
923
+ }
924
+ };
663
925
  function SparkleModal({
664
926
  productName,
665
927
  limit,
@@ -669,26 +931,42 @@ function SparkleModal({
669
931
  onNavigate,
670
932
  onResult,
671
933
  theme,
672
- classNames = {}
934
+ classNames = {},
935
+ product: initialProduct
673
936
  }) {
674
- const { results, loading, search } = useSearch();
675
- const initiated = useRef7(false);
676
- useEffect4(() => {
677
- if (!initiated.current) {
678
- initiated.current = true;
679
- search(productName, limit);
937
+ var _a, _b, _c;
938
+ const client = useHuskelContext();
939
+ const [fetchedProduct, setFetchedProduct] = useState6(null);
940
+ const displayProduct = initialProduct || fetchedProduct;
941
+ const { results, loading: searchLoading, search } = useSearch();
942
+ const { messages, sources, loading: chatLoading, error: chatError, send } = useChat();
943
+ const [chatInput, setChatInput] = useState6("");
944
+ const chatBottomRef = useRef7(null);
945
+ const chatTextareaRef = useRef7(null);
946
+ useEffect5(() => {
947
+ if (!initialProduct && !fetchedProduct) {
948
+ client.api.searchVector(productName, 1).then((res) => {
949
+ if (res.results && res.results.length > 0) {
950
+ setFetchedProduct(res.results[0].product);
951
+ }
952
+ }).catch((err) => console.error("[Huskel] Failed to fetch product details", err));
680
953
  }
681
- }, []);
682
- useEffect4(() => {
954
+ search(productName, limit);
955
+ }, [productName, initialProduct, fetchedProduct, client, limit, search]);
956
+ useEffect5(() => {
683
957
  if (results.length > 0) onResult == null ? void 0 : onResult(results);
684
- }, [results]);
685
- useEffect4(() => {
958
+ }, [results, onResult]);
959
+ useEffect5(() => {
686
960
  const h = (e) => {
687
961
  if (e.key === "Escape") onClose();
688
962
  };
689
963
  document.addEventListener("keydown", h);
690
964
  return () => document.removeEventListener("keydown", h);
691
- }, []);
965
+ }, [onClose]);
966
+ useEffect5(() => {
967
+ var _a2;
968
+ (_a2 = chatBottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
969
+ }, [messages, chatLoading]);
692
970
  const blurVal = typeof backdropBlur === "number" ? `${backdropBlur}px` : backdropBlur != null ? backdropBlur : "16px";
693
971
  const bg = backdropColor != null ? backdropColor : void 0;
694
972
  const handleNav = (r) => {
@@ -698,83 +976,209 @@ function SparkleModal({
698
976
  if (r.product.url) window.location.href = r.product.url;
699
977
  }
700
978
  };
701
- 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 });
702
- return /* @__PURE__ */ jsx3(
979
+ const handleSend = async (text) => {
980
+ const q = (text != null ? text : chatInput).trim();
981
+ if (!q || chatLoading) return;
982
+ setChatInput("");
983
+ if (chatTextareaRef.current) {
984
+ chatTextareaRef.current.style.height = "auto";
985
+ }
986
+ if (messages.length === 0 && displayProduct) {
987
+ const contextQuery = `[Context: Shopper is viewing "${displayProduct.name}". Price: ${displayProduct.price}. Description: ${displayProduct.description || ""}]
988
+
989
+ Question: ${q}`;
990
+ await send(contextQuery, q);
991
+ } else {
992
+ await send(q);
993
+ }
994
+ };
995
+ const handleKeyDown = (e) => {
996
+ if (e.key === "Enter" && !e.shiftKey) {
997
+ e.preventDefault();
998
+ handleSend();
999
+ }
1000
+ };
1001
+ const handleInput = (e) => {
1002
+ setChatInput(e.target.value);
1003
+ const t = e.target;
1004
+ t.style.height = "auto";
1005
+ t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
1006
+ };
1007
+ 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 });
1008
+ const displayMessages = messages.length === 0 && displayProduct ? [
1009
+ {
1010
+ role: "assistant",
1011
+ content: `Hi! I can help you with **${displayProduct.name}**. Ask me about its specifications, features, compare it with other options, or find alternatives!`
1012
+ }
1013
+ ] : messages;
1014
+ return /* @__PURE__ */ jsx4(
703
1015
  "div",
704
1016
  {
705
- className: `hsk-sp-backdrop ${classNames.backdrop || ""}`,
1017
+ className: cn("hsk-sp-backdrop", classNames.backdrop),
706
1018
  onClick: onClose,
707
1019
  style: __spreadValues({
708
1020
  backdropFilter: `blur(${blurVal})`,
709
1021
  WebkitBackdropFilter: `blur(${blurVal})`,
710
1022
  background: bg != null ? bg : void 0
711
1023
  }, customStyles),
712
- children: /* @__PURE__ */ jsxs2("div", { className: `hsk-sp-card ${classNames.card || ""}`, onClick: (e) => e.stopPropagation(), children: [
1024
+ children: /* @__PURE__ */ jsxs2("div", { className: cn("hsk-sp-card hsk-sp-fullscreen", classNames.card), onClick: (e) => e.stopPropagation(), children: [
713
1025
  /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-header", children: [
714
- /* @__PURE__ */ jsx3("span", { className: "hsk-sp-header-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx3(SparkleIcon, {}) }),
1026
+ /* @__PURE__ */ jsx4("span", { className: "hsk-sp-header-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx4(SparkleIcon, {}) }),
715
1027
  /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-header-body", children: [
716
- /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-header-title", children: [
717
- "Similar to \u201C",
718
- productName,
719
- "\u201D"
720
- ] }),
721
- /* @__PURE__ */ jsx3("div", { className: "hsk-sp-header-sub", children: "AI vector similarity \xB7 instant results" })
1028
+ /* @__PURE__ */ jsx4("div", { className: "hsk-sp-header-title", children: (displayProduct == null ? void 0 : displayProduct.name) || productName }),
1029
+ /* @__PURE__ */ jsx4("div", { className: "hsk-sp-header-sub", children: "Ask questions, compare specs, or check similar products" })
722
1030
  ] }),
723
- /* @__PURE__ */ jsx3("button", { className: "hsk-sp-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx3(CloseIcon, {}) })
1031
+ /* @__PURE__ */ jsx4("button", { className: "hsk-sp-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx4(CloseIcon, {}) })
724
1032
  ] }),
725
- loading && /* @__PURE__ */ jsx3("div", { className: "hsk-sp-bar" }),
726
- /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-results", children: [
727
- !loading && results.length === 0 && /* @__PURE__ */ jsx3("div", { className: "hsk-sp-empty", children: "No similar products found." }),
728
- results.map((r, i) => {
729
- var _a, _b, _c;
730
- const price = parseFloat(((_a = r.product.price) == null ? void 0 : _a.replace(/[^0-9.]/g, "")) || "0");
731
- const currency = (_b = r.product.currency) != null ? _b : "KES";
732
- return /* @__PURE__ */ jsxs2(
733
- "div",
734
- {
735
- className: `hsk-sp-item ${classNames.item || ""}`,
736
- style: { animationDelay: `${i * 55}ms` },
737
- children: [
738
- /* @__PURE__ */ jsx3("div", { className: "hsk-sp-img-wrap", children: ((_c = r.product.images) == null ? void 0 : _c[0]) ? /* @__PURE__ */ jsx3("img", { src: r.product.images[0], alt: r.product.name }) : /* @__PURE__ */ jsx3("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
739
- /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-item-body", children: [
740
- r.product.category && /* @__PURE__ */ jsx3("div", { className: "hsk-sp-item-cat", children: r.product.category }),
741
- /* @__PURE__ */ jsx3("div", { className: "hsk-sp-item-name", children: r.product.name }),
742
- /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-item-price-row", children: [
743
- /* @__PURE__ */ jsx3("span", { className: "hsk-sp-item-currency", children: currency }),
744
- /* @__PURE__ */ jsx3("span", { className: "hsk-sp-item-price", children: price.toLocaleString() })
1033
+ searchLoading && /* @__PURE__ */ jsx4("div", { className: "hsk-sp-bar" }),
1034
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-body", children: [
1035
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-details-pane", children: [
1036
+ displayProduct && /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-product-profile-container", children: [
1037
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-product-profile", children: [
1038
+ /* @__PURE__ */ jsx4("div", { className: "hsk-sp-details-imgwrap", children: ((_a = displayProduct.images) == null ? void 0 : _a[0]) ? /* @__PURE__ */ jsx4("img", { src: displayProduct.images[0], alt: displayProduct.name }) : /* @__PURE__ */ jsx4("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
1039
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-details-meta", children: [
1040
+ displayProduct.brand && /* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-brand", children: displayProduct.brand }),
1041
+ displayProduct.category && /* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-cat", children: displayProduct.category }),
1042
+ /* @__PURE__ */ jsx4("h2", { className: "hsk-sp-details-name", children: displayProduct.name }),
1043
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-item-price-row", children: [
1044
+ /* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-currency", children: (_b = displayProduct.currency) != null ? _b : "KES" }),
1045
+ /* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-price", children: parseFloat(((_c = displayProduct.price) == null ? void 0 : _c.replace(/[^0-9.]/g, "")) || "0").toLocaleString() }),
1046
+ displayProduct.originalPrice && /* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-original-price", children: parseFloat(displayProduct.originalPrice.replace(/[^0-9.]/g, "") || "0").toLocaleString() }),
1047
+ displayProduct.discount && /* @__PURE__ */ jsxs2("span", { className: "hsk-sp-item-discount", children: [
1048
+ "(",
1049
+ displayProduct.discount,
1050
+ ")"
1051
+ ] })
1052
+ ] }),
1053
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-item-meta-badges", children: [
1054
+ displayProduct.rating && /* @__PURE__ */ jsxs2("span", { className: "hsk-sp-meta-badge hsk-sp-meta-badge-rating", children: [
1055
+ "\u2605 ",
1056
+ parseFloat(displayProduct.rating.toString()).toFixed(1),
1057
+ " ",
1058
+ displayProduct.reviewCount ? `(${displayProduct.reviewCount})` : ""
745
1059
  ] }),
746
- /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-actions", children: [
747
- /* @__PURE__ */ jsx3(
748
- "button",
749
- {
750
- className: "hsk-sp-action hsk-sp-action-primary",
751
- onClick: () => handleNav(r),
752
- children: "View Product"
753
- }
754
- ),
755
- /* @__PURE__ */ jsx3(
756
- "button",
757
- {
758
- className: "hsk-sp-action hsk-sp-action-secondary",
759
- onClick: () => onClose(),
760
- children: "Add to Cart"
761
- }
762
- )
1060
+ displayProduct.availability && /* @__PURE__ */ jsx4("span", { className: `hsk-sp-meta-badge hsk-sp-meta-badge-avail ${displayProduct.availability.toLowerCase().includes("in") ? "in-stock" : "out-stock"}`, children: displayProduct.availability }),
1061
+ displayProduct.stock && !displayProduct.availability && /* @__PURE__ */ jsxs2("span", { className: "hsk-sp-meta-badge hsk-sp-meta-badge-stock", children: [
1062
+ "Stock: ",
1063
+ displayProduct.stock
763
1064
  ] })
764
1065
  ] })
765
- ]
766
- },
767
- r.id
768
- );
769
- })
770
- ] }),
771
- /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-footer", children: [
772
- /* @__PURE__ */ jsxs2("span", { className: "hsk-sp-badge", style: { display: "inline-flex", alignItems: "center", gap: "4px" }, children: [
773
- /* @__PURE__ */ jsx3(SparkleIcon, {}),
774
- " Huskel AI"
1066
+ ] })
1067
+ ] }),
1068
+ displayProduct.specs && Object.keys(displayProduct.specs).length > 0 && /* @__PURE__ */ jsx4("div", { className: "hsk-sp-specs-horizontal", children: Object.entries(displayProduct.specs).map(([key, val]) => /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-spec-item-horizontal", children: [
1069
+ /* @__PURE__ */ jsxs2("span", { className: "hsk-sp-spec-label-horizontal", children: [
1070
+ key,
1071
+ ":"
1072
+ ] }),
1073
+ /* @__PURE__ */ jsx4("span", { className: "hsk-sp-spec-value-horizontal", title: val, children: val })
1074
+ ] }, key)) }),
1075
+ displayProduct.description && /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-details-desc", children: [
1076
+ /* @__PURE__ */ jsx4("h4", { children: "Description" }),
1077
+ /* @__PURE__ */ jsx4("p", { children: displayProduct.description })
1078
+ ] })
1079
+ ] }),
1080
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-similar-section", children: [
1081
+ /* @__PURE__ */ jsx4("h3", { children: "Similar Products" }),
1082
+ /* @__PURE__ */ jsx4("div", { className: "hsk-sp-results", children: (() => {
1083
+ const similarProducts = results.filter(
1084
+ (r) => {
1085
+ var _a2;
1086
+ const isSameName = r.product.name.toLowerCase() === ((_a2 = displayProduct == null ? void 0 : displayProduct.name) == null ? void 0 : _a2.toLowerCase());
1087
+ const isSameSlug = r.product.slug && (displayProduct == null ? void 0 : displayProduct.slug) && r.product.slug.toLowerCase() === displayProduct.slug.toLowerCase();
1088
+ return !isSameName && !isSameSlug;
1089
+ }
1090
+ );
1091
+ if (!searchLoading && similarProducts.length === 0) {
1092
+ return /* @__PURE__ */ jsx4("div", { className: "hsk-sp-empty", children: "No similar products found." });
1093
+ }
1094
+ return similarProducts.map((r, i) => {
1095
+ var _a2, _b2, _c2;
1096
+ const price = parseFloat(((_a2 = r.product.price) == null ? void 0 : _a2.replace(/[^0-9.]/g, "")) || "0");
1097
+ const currency = (_b2 = r.product.currency) != null ? _b2 : "KES";
1098
+ return /* @__PURE__ */ jsxs2(
1099
+ "div",
1100
+ {
1101
+ className: cn("hsk-sp-item", classNames.item),
1102
+ style: { animationDelay: `${i * 55}ms` },
1103
+ children: [
1104
+ /* @__PURE__ */ jsx4("div", { className: "hsk-sp-img-wrap", children: ((_c2 = r.product.images) == null ? void 0 : _c2[0]) ? /* @__PURE__ */ jsx4("img", { src: r.product.images[0], alt: r.product.name }) : /* @__PURE__ */ jsx4("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
1105
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-item-body", children: [
1106
+ /* @__PURE__ */ jsxs2("div", { children: [
1107
+ r.product.category && /* @__PURE__ */ jsx4("div", { className: "hsk-sp-item-cat", children: r.product.category }),
1108
+ /* @__PURE__ */ jsx4("div", { className: "hsk-sp-item-name", title: r.product.name, children: r.product.name })
1109
+ ] }),
1110
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-item-price-row", children: [
1111
+ /* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-currency", children: currency }),
1112
+ /* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-price", children: price.toLocaleString() })
1113
+ ] }),
1114
+ /* @__PURE__ */ jsx4("div", { className: "hsk-sp-actions", children: /* @__PURE__ */ jsx4(
1115
+ "button",
1116
+ {
1117
+ className: "hsk-sp-action hsk-sp-action-primary",
1118
+ onClick: () => handleNav(r),
1119
+ children: "View"
1120
+ }
1121
+ ) })
1122
+ ] })
1123
+ ]
1124
+ },
1125
+ r.id
1126
+ );
1127
+ });
1128
+ })() })
1129
+ ] })
775
1130
  ] }),
776
- /* @__PURE__ */ jsx3("span", { className: "hsk-sp-esc", children: "Esc to close" })
777
- ] })
1131
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-chat-pane", children: [
1132
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-cb-msgs", children: [
1133
+ displayMessages.map((msg, idx) => {
1134
+ const isUser = msg.role === "user";
1135
+ return /* @__PURE__ */ jsx4("div", { className: "hsk-cb-msg-group", children: isUser ? /* @__PURE__ */ jsx4("div", { className: "hsk-cb-user-msg", children: /* @__PURE__ */ jsx4("div", { className: "hsk-cb-user-bubble", children: msg.content }) }) : /* @__PURE__ */ jsxs2("div", { className: "hsk-cb-ai-msg", children: [
1136
+ /* @__PURE__ */ jsx4("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx4(SparkleIcon, {}) }),
1137
+ /* @__PURE__ */ jsx4("div", { className: "hsk-cb-ai-body", children: /* @__PURE__ */ jsx4("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }) })
1138
+ ] }) }, idx);
1139
+ }),
1140
+ chatLoading && /* @__PURE__ */ jsxs2("div", { className: "hsk-cb-typing-row", children: [
1141
+ /* @__PURE__ */ jsx4("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx4(SparkleIcon, {}) }),
1142
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-cb-typing", children: [
1143
+ /* @__PURE__ */ jsx4("div", { className: "hsk-cb-dot" }),
1144
+ /* @__PURE__ */ jsx4("div", { className: "hsk-cb-dot" }),
1145
+ /* @__PURE__ */ jsx4("div", { className: "hsk-cb-dot" })
1146
+ ] })
1147
+ ] }),
1148
+ chatError && /* @__PURE__ */ jsx4("div", { className: "hsk-cb-error", children: getFriendlyError(chatError) }),
1149
+ /* @__PURE__ */ jsx4("div", { ref: chatBottomRef, style: { height: 1 } })
1150
+ ] }),
1151
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-cb-input-wrap", children: [
1152
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-cb-input-box", children: [
1153
+ /* @__PURE__ */ jsx4(
1154
+ "textarea",
1155
+ {
1156
+ ref: chatTextareaRef,
1157
+ className: "hsk-cb-textarea",
1158
+ value: chatInput,
1159
+ onChange: handleInput,
1160
+ onKeyDown: handleKeyDown,
1161
+ placeholder: "Ask about this product, specs, or comparison...",
1162
+ rows: 1,
1163
+ disabled: chatLoading
1164
+ }
1165
+ ),
1166
+ /* @__PURE__ */ jsx4(
1167
+ "button",
1168
+ {
1169
+ className: "hsk-cb-send",
1170
+ onClick: () => handleSend(),
1171
+ disabled: !chatInput.trim() || chatLoading,
1172
+ "aria-label": "Send message",
1173
+ children: /* @__PURE__ */ jsx4(ArrowUpIcon, {})
1174
+ }
1175
+ )
1176
+ ] }),
1177
+ /* @__PURE__ */ jsx4("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 instant product knowledge" })
1178
+ ] })
1179
+ ] })
1180
+ ] }),
1181
+ /* @__PURE__ */ jsx4("div", { className: "hsk-sp-footer", children: /* @__PURE__ */ jsx4("span", { className: "hsk-sp-esc", children: "Esc to close" }) })
778
1182
  ] })
779
1183
  }
780
1184
  );
@@ -788,39 +1192,41 @@ function Sparkle({
788
1192
  className,
789
1193
  onNavigate,
790
1194
  theme,
791
- classNames = {}
1195
+ classNames = {},
1196
+ product
792
1197
  }) {
793
- const [open, setOpen] = useState5(false);
794
- const [mounted, setMounted] = useState5(false);
795
- useEffect4(() => {
1198
+ const [open, setOpen] = useState6(false);
1199
+ const [mounted, setMounted] = useState6(false);
1200
+ useEffect5(() => {
796
1201
  setMounted(true);
797
1202
  }, []);
798
- 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 });
799
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
800
- /* @__PURE__ */ jsx3(
1203
+ 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 });
1204
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1205
+ /* @__PURE__ */ jsx4(
801
1206
  "button",
802
1207
  {
803
- className: `hsk-sp-btn ${classNames.button || ""} ${className || ""}`,
1208
+ className: cn("hsk-sp-btn", classNames.button, className),
804
1209
  onClick: () => setOpen(true),
805
1210
  style: customStyles,
806
1211
  title: "Find similar products",
807
1212
  "aria-label": "Find similar products",
808
- children: /* @__PURE__ */ jsx3(SparkleIcon, {})
1213
+ children: /* @__PURE__ */ jsx4(SparkleIcon, {})
809
1214
  }
810
1215
  ),
811
1216
  open && mounted && createPortal(
812
- /* @__PURE__ */ jsx3(
1217
+ /* @__PURE__ */ jsx4(
813
1218
  SparkleModal,
814
1219
  {
815
1220
  productName,
816
1221
  limit,
1222
+ onResult,
817
1223
  backdropColor,
818
1224
  backdropBlur,
819
1225
  onClose: () => setOpen(false),
820
- onResult,
821
1226
  onNavigate,
822
1227
  theme,
823
- classNames
1228
+ classNames,
1229
+ product
824
1230
  }
825
1231
  ),
826
1232
  document.body
@@ -829,19 +1235,19 @@ function Sparkle({
829
1235
  }
830
1236
 
831
1237
  // src/components/ChatWidget.tsx
832
- import { useState as useState6, useRef as useRef8, useEffect as useEffect5 } from "react";
833
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
834
- var SparkleIcon2 = () => /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx4("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" }) });
835
- var ArrowUpIcon = () => /* @__PURE__ */ jsxs3("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
836
- /* @__PURE__ */ jsx4("path", { d: "m5 12 7-7 7 7" }),
837
- /* @__PURE__ */ jsx4("path", { d: "M12 19V5" })
1238
+ import { useState as useState7, useRef as useRef8, useEffect as useEffect6 } from "react";
1239
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1240
+ var SparkleIcon2 = () => /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("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" }) });
1241
+ var ArrowUpIcon2 = () => /* @__PURE__ */ jsxs3("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1242
+ /* @__PURE__ */ jsx5("path", { d: "m5 12 7-7 7 7" }),
1243
+ /* @__PURE__ */ jsx5("path", { d: "M12 19V5" })
838
1244
  ] });
839
1245
  function SourceCard({ source, defaultCurrency, onSelect }) {
840
1246
  var _a;
841
1247
  return /* @__PURE__ */ jsxs3("div", { className: "hsk-source-card", onClick: () => onSelect == null ? void 0 : onSelect(source), children: [
842
- source.image && /* @__PURE__ */ jsx4("img", { src: source.image, alt: source.name, className: "hsk-source-img" }),
1248
+ source.image && /* @__PURE__ */ jsx5("img", { src: source.image, alt: source.name, className: "hsk-source-img" }),
843
1249
  /* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
844
- /* @__PURE__ */ jsx4("div", { className: "hsk-source-name", children: source.name }),
1250
+ /* @__PURE__ */ jsx5("div", { className: "hsk-source-name", children: source.name }),
845
1251
  source.price && /* @__PURE__ */ jsxs3("div", { className: "hsk-source-price", children: [
846
1252
  (_a = source.currency) != null ? _a : defaultCurrency,
847
1253
  " ",
@@ -862,10 +1268,10 @@ function ChatWidget({
862
1268
  onSelectSource
863
1269
  }) {
864
1270
  const { messages, sources, loading, error, send, reset } = useChat();
865
- const [input, setInput] = useState6("");
1271
+ const [input, setInput] = useState7("");
866
1272
  const bottomRef = useRef8(null);
867
1273
  const textareaRef = useRef8(null);
868
- useEffect5(() => {
1274
+ useEffect6(() => {
869
1275
  var _a;
870
1276
  (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
871
1277
  }, [messages, loading]);
@@ -888,48 +1294,55 @@ function ChatWidget({
888
1294
  t.style.height = "auto";
889
1295
  t.style.height = Math.min(t.scrollHeight, 120) + "px";
890
1296
  };
891
- 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 });
1297
+ 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 });
892
1298
  return /* @__PURE__ */ jsxs3(
893
1299
  "div",
894
1300
  {
895
- className: `hsk-chat-widget ${classNames.root || ""} ${className || ""}`,
1301
+ className: cn("hsk-chat-widget", classNames.root, className),
896
1302
  style: customStyles,
897
1303
  children: [
898
- /* @__PURE__ */ jsxs3("div", { className: `hsk-chat-header ${classNames.header || ""}`, children: [
899
- /* @__PURE__ */ jsx4("span", { className: "hsk-chat-header-icon", children: /* @__PURE__ */ jsx4(SparkleIcon2, {}) }),
900
- /* @__PURE__ */ jsx4("span", { className: "hsk-chat-title", children: title }),
901
- /* @__PURE__ */ jsx4("span", { className: "hsk-chat-badge", children: "AI" }),
902
- messages.length > 0 && /* @__PURE__ */ jsx4("button", { className: "hsk-chat-reset", onClick: reset, style: { marginLeft: "auto" }, children: "Clear" })
1304
+ /* @__PURE__ */ jsxs3("div", { className: cn("hsk-chat-header", classNames.header), children: [
1305
+ /* @__PURE__ */ jsx5("span", { className: "hsk-chat-header-icon", children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
1306
+ /* @__PURE__ */ jsx5("span", { className: "hsk-chat-title", children: title }),
1307
+ /* @__PURE__ */ jsx5("span", { className: "hsk-chat-badge", children: "AI" }),
1308
+ messages.length > 0 && /* @__PURE__ */ jsx5("button", { className: "hsk-chat-reset", onClick: reset, style: { marginLeft: "auto" }, children: "Clear" })
903
1309
  ] }),
904
1310
  /* @__PURE__ */ jsxs3("div", { className: "hsk-chat-messages", children: [
905
1311
  messages.length === 0 ? /* @__PURE__ */ jsxs3("div", { className: "hsk-chat-empty", children: [
906
- /* @__PURE__ */ jsx4("div", { className: "hsk-chat-empty-icon", children: /* @__PURE__ */ jsx4(SparkleIcon2, {}) }),
907
- /* @__PURE__ */ jsx4("div", { children: emptyStateText }),
908
- /* @__PURE__ */ jsx4("div", { className: "hsk-chat-empty-suggestions", children: emptyStateSuggestions })
1312
+ /* @__PURE__ */ jsx5("div", { className: "hsk-chat-empty-icon", children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
1313
+ /* @__PURE__ */ jsx5("div", { children: emptyStateText }),
1314
+ /* @__PURE__ */ jsx5("div", { className: "hsk-chat-empty-suggestions", children: emptyStateSuggestions })
909
1315
  ] }) : messages.map((msg, idx) => /* @__PURE__ */ jsxs3("div", { children: [
910
1316
  /* @__PURE__ */ jsxs3("div", { className: `hsk-msg-row ${msg.role}`, children: [
911
- /* @__PURE__ */ jsx4("div", { className: `hsk-msg-avatar ${msg.role === "assistant" ? "ai" : "user"}`, children: msg.role === "assistant" ? /* @__PURE__ */ jsx4(SparkleIcon2, {}) : "U" }),
912
- /* @__PURE__ */ jsx4("div", { className: `hsk-msg-bubble ${msg.role} ${classNames.messageBubble || ""}`, children: msg.content })
1317
+ /* @__PURE__ */ jsx5("div", { className: cn("hsk-msg-avatar", msg.role === "assistant" ? "ai" : "user"), children: msg.role === "assistant" ? /* @__PURE__ */ jsx5(SparkleIcon2, {}) : "U" }),
1318
+ /* @__PURE__ */ jsx5("div", { className: cn("hsk-msg-bubble", msg.role, classNames.messageBubble), children: renderMarkdown(msg.content) })
913
1319
  ] }),
914
- msg.role === "assistant" && idx === messages.length - 1 && sources.length > 0 && /* @__PURE__ */ jsx4("div", { className: "hsk-sources-container", children: /* @__PURE__ */ jsx4("div", { className: "hsk-sources", children: sources.map((src, si) => /* @__PURE__ */ jsx4(SourceCard, { source: src, defaultCurrency, onSelect: onSelectSource }, si)) }) })
1320
+ msg.role === "assistant" && idx === messages.length - 1 && sources.length > 0 && /* @__PURE__ */ jsx5("div", { className: "hsk-sources-container", children: /* @__PURE__ */ jsx5("div", { className: "hsk-sources", children: sources.map((src, si) => /* @__PURE__ */ jsx5(SourceCard, { source: src, defaultCurrency, onSelect: onSelectSource }, si)) }) })
915
1321
  ] }, idx)),
916
1322
  loading && /* @__PURE__ */ jsxs3("div", { className: "hsk-msg-row", children: [
917
- /* @__PURE__ */ jsx4("div", { className: "hsk-msg-avatar ai", children: /* @__PURE__ */ jsx4(SparkleIcon2, {}) }),
1323
+ /* @__PURE__ */ jsx5("div", { className: "hsk-msg-avatar ai", children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
918
1324
  /* @__PURE__ */ jsxs3("div", { className: "hsk-typing", children: [
919
- /* @__PURE__ */ jsx4("div", { className: "hsk-typing-dot" }),
920
- /* @__PURE__ */ jsx4("div", { className: "hsk-typing-dot" }),
921
- /* @__PURE__ */ jsx4("div", { className: "hsk-typing-dot" })
1325
+ /* @__PURE__ */ jsx5("div", { className: "hsk-typing-dot" }),
1326
+ /* @__PURE__ */ jsx5("div", { className: "hsk-typing-dot" }),
1327
+ /* @__PURE__ */ jsx5("div", { className: "hsk-typing-dot" })
922
1328
  ] })
923
1329
  ] }),
924
- error && /* @__PURE__ */ jsx4("div", { className: "hsk-chat-error", children: error }),
925
- /* @__PURE__ */ jsx4("div", { ref: bottomRef })
1330
+ error && /* @__PURE__ */ jsx5("div", { className: "hsk-chat-error", children: (() => {
1331
+ try {
1332
+ const parsed = JSON.parse(error);
1333
+ return parsed.error || parsed.message || error;
1334
+ } catch (e) {
1335
+ return error;
1336
+ }
1337
+ })() }),
1338
+ /* @__PURE__ */ jsx5("div", { ref: bottomRef })
926
1339
  ] }),
927
1340
  /* @__PURE__ */ jsxs3("div", { className: "hsk-chat-input-area", children: [
928
- /* @__PURE__ */ jsx4(
1341
+ /* @__PURE__ */ jsx5(
929
1342
  "textarea",
930
1343
  {
931
1344
  ref: textareaRef,
932
- className: `hsk-chat-input ${classNames.input || ""}`,
1345
+ className: cn("hsk-chat-input", classNames.input),
933
1346
  value: input,
934
1347
  onChange: handleInput,
935
1348
  onKeyDown: handleKey,
@@ -938,14 +1351,14 @@ function ChatWidget({
938
1351
  disabled: loading
939
1352
  }
940
1353
  ),
941
- /* @__PURE__ */ jsx4(
1354
+ /* @__PURE__ */ jsx5(
942
1355
  "button",
943
1356
  {
944
1357
  className: "hsk-chat-send",
945
1358
  onClick: handleSend,
946
1359
  disabled: !input.trim() || loading,
947
1360
  "aria-label": "Send message",
948
- children: /* @__PURE__ */ jsx4(ArrowUpIcon, {})
1361
+ children: /* @__PURE__ */ jsx5(ArrowUpIcon2, {})
949
1362
  }
950
1363
  )
951
1364
  ] })
@@ -955,19 +1368,19 @@ function ChatWidget({
955
1368
  }
956
1369
 
957
1370
  // src/components/AIChatButton.tsx
958
- import { useState as useState7, useEffect as useEffect6, useRef as useRef9, useCallback as useCallback4 } from "react";
1371
+ import { useState as useState8, useEffect as useEffect7, useRef as useRef9, useCallback as useCallback5 } from "react";
959
1372
  import { createPortal as createPortal2 } from "react-dom";
960
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
961
- var SparkleIcon3 = ({ className }) => /* @__PURE__ */ jsx5("svg", { className, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("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" }) });
962
- var ArrowUpIcon2 = () => /* @__PURE__ */ jsxs4("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
963
- /* @__PURE__ */ jsx5("path", { d: "m5 12 7-7 7 7" }),
964
- /* @__PURE__ */ jsx5("path", { d: "M12 19V5" })
1373
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1374
+ var SparkleIcon3 = ({ className }) => /* @__PURE__ */ jsx6("svg", { className, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("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" }) });
1375
+ var ArrowUpIcon3 = () => /* @__PURE__ */ jsxs4("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1376
+ /* @__PURE__ */ jsx6("path", { d: "m5 12 7-7 7 7" }),
1377
+ /* @__PURE__ */ jsx6("path", { d: "M12 19V5" })
965
1378
  ] });
966
1379
  var CloseIcon2 = () => /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
967
- /* @__PURE__ */ jsx5("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
968
- /* @__PURE__ */ jsx5("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1380
+ /* @__PURE__ */ jsx6("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1381
+ /* @__PURE__ */ jsx6("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
969
1382
  ] });
970
- var ChevronRightIcon = () => /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("path", { d: "m9 18 6-6-6-6" }) });
1383
+ var ChevronRightIcon = () => /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("path", { d: "m9 18 6-6-6-6" }) });
971
1384
  var DEFAULT_CHIPS = [
972
1385
  "Cheapest smartphone",
973
1386
  "Smart TV under KSh 20,000",
@@ -976,14 +1389,14 @@ var DEFAULT_CHIPS = [
976
1389
  ];
977
1390
  function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
978
1391
  const railRef = useRef9(null);
979
- const [showNext, setShowNext] = useState7(false);
980
- const measure = useCallback4(() => {
1392
+ const [showNext, setShowNext] = useState8(false);
1393
+ const measure = useCallback5(() => {
981
1394
  const el = railRef.current;
982
1395
  if (!el) return;
983
1396
  const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 8;
984
1397
  setShowNext(el.scrollWidth > el.clientWidth + 4 && !atEnd);
985
1398
  }, []);
986
- useEffect6(() => {
1399
+ useEffect7(() => {
987
1400
  measure();
988
1401
  const el = railRef.current;
989
1402
  if (!el) return;
@@ -1000,7 +1413,7 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
1000
1413
  (_a = railRef.current) == null ? void 0 : _a.scrollBy({ left: 170, behavior: "smooth" });
1001
1414
  };
1002
1415
  return /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-sources-wrap", children: [
1003
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-sources", ref: railRef, children: sources.map((src, si) => {
1416
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-sources", ref: railRef, children: sources.map((src, si) => {
1004
1417
  var _a;
1005
1418
  return /* @__PURE__ */ jsxs4(
1006
1419
  "div",
@@ -1009,9 +1422,9 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
1009
1422
  style: { animationDelay: `${si * 50}ms` },
1010
1423
  onClick: () => onSelectSource == null ? void 0 : onSelectSource(src),
1011
1424
  children: [
1012
- src.image ? /* @__PURE__ */ jsx5("div", { className: "hsk-cb-src-imgwrap", children: /* @__PURE__ */ jsx5("img", { src: src.image, alt: src.name, loading: "lazy" }) }) : /* @__PURE__ */ jsx5("div", { className: "hsk-cb-src-imgwrap-empty", children: /* @__PURE__ */ jsx5(SparkleIcon3, {}) }),
1425
+ src.image ? /* @__PURE__ */ jsx6("div", { className: "hsk-cb-src-imgwrap", children: /* @__PURE__ */ jsx6("img", { src: src.image, alt: src.name, loading: "lazy" }) }) : /* @__PURE__ */ jsx6("div", { className: "hsk-cb-src-imgwrap-empty", children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
1013
1426
  /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-src-info", children: [
1014
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-src-name", children: src.name }),
1427
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-src-name", children: src.name }),
1015
1428
  src.price && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-src-price", children: [
1016
1429
  (_a = src.currency) != null ? _a : defaultCurrency,
1017
1430
  " ",
@@ -1023,20 +1436,20 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
1023
1436
  si
1024
1437
  );
1025
1438
  }) }),
1026
- showNext && /* @__PURE__ */ jsxs4(Fragment2, { children: [
1027
- /* @__PURE__ */ jsx5(
1439
+ showNext && /* @__PURE__ */ jsxs4(Fragment3, { children: [
1440
+ /* @__PURE__ */ jsx6(
1028
1441
  "div",
1029
1442
  {
1030
1443
  className: "hsk-cb-sources-fade",
1031
1444
  style: { background: "linear-gradient(to right, transparent, var(--hsk-fade-bg, #0e0e0f))" }
1032
1445
  }
1033
1446
  ),
1034
- /* @__PURE__ */ jsx5("button", { className: "hsk-cb-sources-next", onClick: scrollNext, "aria-label": "See more", children: /* @__PURE__ */ jsx5(ChevronRightIcon, {}) })
1447
+ /* @__PURE__ */ jsx6("button", { className: "hsk-cb-sources-next", onClick: scrollNext, "aria-label": "See more", children: /* @__PURE__ */ jsx6(ChevronRightIcon, {}) })
1035
1448
  ] })
1036
1449
  ] });
1037
1450
  }
1038
1451
  function ChatModal({
1039
- title = "AI Shopping Assistant",
1452
+ title = "Shopping Assistant",
1040
1453
  placeholder = "Ask me anything \u2014 gifts, budget, use case\u2026",
1041
1454
  backdropColor,
1042
1455
  backdropBlur,
@@ -1049,15 +1462,15 @@ function ChatModal({
1049
1462
  }) {
1050
1463
  var _a, _b;
1051
1464
  const { messages, sources, loading, error, send, reset } = useChat();
1052
- const [input, setInput] = useState7("");
1053
- const [selectedProduct, setSelectedProduct] = useState7(null);
1465
+ const [input, setInput] = useState8("");
1466
+ const [selectedProduct, setSelectedProduct] = useState8(null);
1054
1467
  const bottomRef = useRef9(null);
1055
1468
  const textareaRef = useRef9(null);
1056
- useEffect6(() => {
1469
+ useEffect7(() => {
1057
1470
  var _a2;
1058
1471
  (_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
1059
1472
  }, [messages, loading, selectedProduct]);
1060
- useEffect6(() => {
1473
+ useEffect7(() => {
1061
1474
  const h = (e) => {
1062
1475
  if (e.key === "Escape") onClose();
1063
1476
  };
@@ -1094,36 +1507,32 @@ function ChatModal({
1094
1507
  t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
1095
1508
  };
1096
1509
  const blurVal = typeof backdropBlur === "number" ? `${backdropBlur}px` : backdropBlur != null ? backdropBlur : "20px";
1097
- 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 });
1098
- return /* @__PURE__ */ jsx5(
1510
+ 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 });
1511
+ return /* @__PURE__ */ jsx6(
1099
1512
  "div",
1100
1513
  {
1101
- className: `hsk-cb-overlay ${classNames.overlay || ""}`,
1514
+ className: cn("hsk-cb-overlay", classNames.overlay),
1102
1515
  onClick: onClose,
1103
1516
  style: __spreadValues(__spreadValues({
1104
1517
  backdropFilter: `blur(${blurVal})`,
1105
1518
  WebkitBackdropFilter: `blur(${blurVal})`
1106
1519
  }, backdropColor ? { background: backdropColor } : {}), customStyles),
1107
- children: /* @__PURE__ */ jsxs4("div", { className: `hsk-cb-panel ${classNames.panel || ""}`, onClick: (e) => e.stopPropagation(), children: [
1520
+ children: /* @__PURE__ */ jsxs4("div", { className: cn("hsk-cb-panel", classNames.panel), onClick: (e) => e.stopPropagation(), children: [
1108
1521
  /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-topbar", children: [
1109
1522
  /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-topbar-left", children: [
1110
- /* @__PURE__ */ jsx5("span", { className: "hsk-cb-topbar-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx5(SparkleIcon3, {}) }),
1111
- /* @__PURE__ */ jsxs4("div", { children: [
1112
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-topbar-title", children: title }),
1113
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-topbar-sub", children: "Powered by Huskel AI \xB7 searches the whole catalogue" })
1114
- ] })
1523
+ /* @__PURE__ */ jsx6("span", { className: "hsk-cb-topbar-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
1524
+ /* @__PURE__ */ jsx6("div", { children: /* @__PURE__ */ jsx6("div", { className: "hsk-cb-topbar-title", children: title }) })
1115
1525
  ] }),
1116
1526
  /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-topbar-actions", children: [
1117
- messages.length > 0 && /* @__PURE__ */ jsx5("button", { className: "hsk-cb-topbar-btn", onClick: reset, children: "Clear chat" }),
1118
- /* @__PURE__ */ jsx5("button", { className: "hsk-cb-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx5(CloseIcon2, {}) })
1527
+ messages.length > 0 && /* @__PURE__ */ jsx6("button", { className: "hsk-cb-topbar-btn", onClick: reset, children: "Clear chat" }),
1528
+ /* @__PURE__ */ jsx6("button", { className: "hsk-cb-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx6(CloseIcon2, {}) })
1119
1529
  ] })
1120
1530
  ] }),
1121
1531
  /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-msgs", children: [
1122
1532
  messages.length === 0 ? /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-empty", children: [
1123
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-empty-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx5(SparkleIcon3, {}) }),
1124
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-empty-title", children: "What can I help you find?" }),
1125
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-empty-sub", children: "Ask about products, budgets, gift ideas, specs \u2014 I'll search the entire catalogue for you." }),
1126
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-chips", children: chips.map((chip) => /* @__PURE__ */ jsx5(
1533
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-empty-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
1534
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-empty-title", children: "Find exactly what you need" }),
1535
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-chips", children: chips.map((chip) => /* @__PURE__ */ jsx6(
1127
1536
  "button",
1128
1537
  {
1129
1538
  className: "hsk-cb-chip",
@@ -1135,11 +1544,11 @@ function ChatModal({
1135
1544
  ] }) : messages.map((msg, idx) => {
1136
1545
  const isLast = idx === messages.length - 1;
1137
1546
  const isUser = msg.role === "user";
1138
- return /* @__PURE__ */ jsx5("div", { className: "hsk-cb-msg-group", children: isUser ? /* @__PURE__ */ jsx5("div", { className: "hsk-cb-user-msg", children: /* @__PURE__ */ jsx5("div", { className: "hsk-cb-user-bubble", children: msg.content }) }) : /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-ai-msg", children: [
1139
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx5(SparkleIcon3, {}) }),
1547
+ return /* @__PURE__ */ jsx6("div", { className: "hsk-cb-msg-group", children: isUser ? /* @__PURE__ */ jsx6("div", { className: "hsk-cb-user-msg", children: /* @__PURE__ */ jsx6("div", { className: "hsk-cb-user-bubble", children: msg.content }) }) : /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-ai-msg", children: [
1548
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
1140
1549
  /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-ai-body", children: [
1141
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-ai-text", children: msg.content }),
1142
- isLast && sources.length > 0 && /* @__PURE__ */ jsx5(
1550
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }),
1551
+ isLast && sources.length > 0 && /* @__PURE__ */ jsx6(
1143
1552
  SourcesCarousel,
1144
1553
  {
1145
1554
  sources,
@@ -1156,9 +1565,9 @@ function ChatModal({
1156
1565
  className: "hsk-cb-selected-product",
1157
1566
  onClick: () => selectedProduct.url && window.open(selectedProduct.url, "_blank"),
1158
1567
  children: [
1159
- selectedProduct.image && /* @__PURE__ */ jsx5("img", { className: "hsk-cb-selected-img", src: selectedProduct.image, alt: selectedProduct.name }),
1568
+ selectedProduct.image && /* @__PURE__ */ jsx6("img", { className: "hsk-cb-selected-img", src: selectedProduct.image, alt: selectedProduct.name }),
1160
1569
  /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-selected-info", children: [
1161
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-selected-name", children: selectedProduct.name }),
1570
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-selected-name", children: selectedProduct.name }),
1162
1571
  selectedProduct.price && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-selected-price", children: [
1163
1572
  (_a = selectedProduct.currency) != null ? _a : defaultCurrency,
1164
1573
  " ",
@@ -1169,23 +1578,23 @@ function ChatModal({
1169
1578
  }
1170
1579
  ),
1171
1580
  loading && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-typing-row", children: [
1172
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx5(SparkleIcon3, {}) }),
1581
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
1173
1582
  /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-typing", children: [
1174
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-dot" }),
1175
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-dot" }),
1176
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-dot" })
1583
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-dot" }),
1584
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-dot" }),
1585
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-dot" })
1177
1586
  ] })
1178
1587
  ] }),
1179
- error && /* @__PURE__ */ jsx5("div", { className: "hsk-cb-error", children: error }),
1180
- /* @__PURE__ */ jsx5("div", { ref: bottomRef, style: { height: 1 } })
1588
+ error && /* @__PURE__ */ jsx6("div", { className: "hsk-cb-error", children: error }),
1589
+ /* @__PURE__ */ jsx6("div", { ref: bottomRef, style: { height: 1 } })
1181
1590
  ] }),
1182
1591
  /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-input-wrap", children: [
1183
1592
  /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-input-box", children: [
1184
- /* @__PURE__ */ jsx5(
1593
+ /* @__PURE__ */ jsx6(
1185
1594
  "textarea",
1186
1595
  {
1187
1596
  ref: textareaRef,
1188
- className: `hsk-cb-textarea ${classNames.input || ""}`,
1597
+ className: cn("hsk-cb-textarea", classNames.input),
1189
1598
  value: input,
1190
1599
  onChange: handleInput,
1191
1600
  onKeyDown: handleKeyDown,
@@ -1195,18 +1604,18 @@ function ChatModal({
1195
1604
  autoFocus: true
1196
1605
  }
1197
1606
  ),
1198
- /* @__PURE__ */ jsx5(
1607
+ /* @__PURE__ */ jsx6(
1199
1608
  "button",
1200
1609
  {
1201
- className: `hsk-cb-send ${classNames.sendButton || ""}`,
1610
+ className: cn("hsk-cb-send", classNames.sendButton),
1202
1611
  onClick: () => handleSend(),
1203
1612
  disabled: !input.trim() || loading,
1204
1613
  "aria-label": "Send message",
1205
- children: /* @__PURE__ */ jsx5(ArrowUpIcon2, {})
1614
+ children: /* @__PURE__ */ jsx6(ArrowUpIcon3, {})
1206
1615
  }
1207
1616
  )
1208
1617
  ] }),
1209
- /* @__PURE__ */ jsx5("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 searches the whole catalogue in real time" })
1618
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 searches the whole catalogue in real time" })
1210
1619
  ] })
1211
1620
  ] })
1212
1621
  }
@@ -1225,28 +1634,28 @@ function AIChatButton({
1225
1634
  theme,
1226
1635
  classNames = {}
1227
1636
  }) {
1228
- const [open, setOpen] = useState7(false);
1229
- const [mounted, setMounted] = useState7(false);
1230
- useEffect6(() => {
1637
+ const [open, setOpen] = useState8(false);
1638
+ const [mounted, setMounted] = useState8(false);
1639
+ useEffect7(() => {
1231
1640
  setMounted(true);
1232
1641
  }, []);
1233
- 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 });
1234
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1642
+ 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 });
1643
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
1235
1644
  /* @__PURE__ */ jsxs4(
1236
1645
  "button",
1237
1646
  {
1238
- className: `hsk-cb-btn ${classNames.button || ""} ${className || ""}`,
1647
+ className: cn("hsk-cb-btn", classNames.button, className),
1239
1648
  onClick: () => setOpen(true),
1240
1649
  style: customStyles,
1241
1650
  "aria-label": "Open AI chat",
1242
1651
  children: [
1243
- /* @__PURE__ */ jsx5("span", { className: "hsk-cb-btn-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx5(SparkleIcon3, {}) }),
1652
+ /* @__PURE__ */ jsx6("span", { className: "hsk-cb-btn-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
1244
1653
  label !== void 0 ? label : null
1245
1654
  ]
1246
1655
  }
1247
1656
  ),
1248
1657
  open && mounted && createPortal2(
1249
- /* @__PURE__ */ jsx5(
1658
+ /* @__PURE__ */ jsx6(
1250
1659
  ChatModal,
1251
1660
  {
1252
1661
  title,
@@ -1265,8 +1674,260 @@ function AIChatButton({
1265
1674
  )
1266
1675
  ] });
1267
1676
  }
1677
+
1678
+ // src/components/CartBadge.tsx
1679
+ import { jsx as jsx7 } from "react/jsx-runtime";
1680
+ function CartBadge({ className }) {
1681
+ const { cart } = useCart();
1682
+ if (!cart || cart.item_count === 0) return null;
1683
+ return /* @__PURE__ */ jsx7("span", { className: cn("hsk-cart-badge", className), children: cart.item_count });
1684
+ }
1685
+
1686
+ // src/components/CartDrawer.tsx
1687
+ import { useState as useState10, useEffect as useEffect9 } from "react";
1688
+ import { createPortal as createPortal4 } from "react-dom";
1689
+
1690
+ // src/components/CheckoutModal.tsx
1691
+ import { useState as useState9, useEffect as useEffect8 } from "react";
1692
+ import { createPortal as createPortal3 } from "react-dom";
1693
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1694
+ function CheckoutModal({
1695
+ onClose,
1696
+ theme,
1697
+ customStyles,
1698
+ hskThemeAttr
1699
+ }) {
1700
+ var _a, _b, _c, _d;
1701
+ const { cart, loading: cartLoading } = useCart();
1702
+ const client = useHuskelContext();
1703
+ const [config, setConfig] = useState9(null);
1704
+ const [loading, setLoading] = useState9(true);
1705
+ const [checkingOut, setCheckingOut] = useState9(false);
1706
+ const [paymentSuccess, setPaymentSuccess] = useState9(false);
1707
+ useEffect8(() => {
1708
+ client.api.getCheckoutConfig().then((res) => setConfig(res.payment_methods)).catch((e) => console.error("[Huskel] Failed to fetch checkout config", e)).finally(() => setLoading(false));
1709
+ }, [client]);
1710
+ const handlePay = async (method) => {
1711
+ setCheckingOut(true);
1712
+ setTimeout(async () => {
1713
+ try {
1714
+ const payload = await client.api.checkoutCart();
1715
+ if (client.onCheckout) {
1716
+ client.onCheckout(payload);
1717
+ }
1718
+ setPaymentSuccess(true);
1719
+ setTimeout(() => {
1720
+ onClose();
1721
+ }, 3e3);
1722
+ } catch (e) {
1723
+ console.error("[Huskel] Checkout failed", e);
1724
+ setCheckingOut(false);
1725
+ }
1726
+ }, 1500);
1727
+ };
1728
+ const hasPaymentMethods = config && Object.values(config).some((m) => m.enabled);
1729
+ return createPortal3(
1730
+ /* @__PURE__ */ jsx8(
1731
+ "div",
1732
+ {
1733
+ className: "hsk-cart-backdrop",
1734
+ style: __spreadProps(__spreadValues({}, customStyles), { zIndex: 999999 }),
1735
+ "data-hsk-theme": hskThemeAttr,
1736
+ onClick: onClose,
1737
+ children: /* @__PURE__ */ jsxs5(
1738
+ "div",
1739
+ {
1740
+ className: "hsk-checkout-modal",
1741
+ style: customStyles,
1742
+ "data-hsk-theme": hskThemeAttr,
1743
+ onClick: (e) => e.stopPropagation(),
1744
+ children: [
1745
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-header", children: [
1746
+ /* @__PURE__ */ jsx8("h2", { children: "Secure Checkout" }),
1747
+ /* @__PURE__ */ jsx8("button", { onClick: onClose, className: "hsk-close-btn", children: "\xD7" })
1748
+ ] }),
1749
+ /* @__PURE__ */ jsx8("div", { className: "hsk-checkout-content", children: paymentSuccess ? /* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-success", children: [
1750
+ /* @__PURE__ */ jsxs5("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "hsk-success-icon", children: [
1751
+ /* @__PURE__ */ jsx8("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
1752
+ /* @__PURE__ */ jsx8("polyline", { points: "22 4 12 14.01 9 11.01" })
1753
+ ] }),
1754
+ /* @__PURE__ */ jsx8("h3", { children: "Payment Successful!" }),
1755
+ /* @__PURE__ */ jsx8("p", { children: "Thank you for your order." })
1756
+ ] }) : /* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-split", children: [
1757
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-summary", children: [
1758
+ /* @__PURE__ */ jsx8("h3", { children: "Order Summary" }),
1759
+ cartLoading || !cart ? /* @__PURE__ */ jsx8("p", { className: "hsk-cart-loading", children: "Loading order..." }) : /* @__PURE__ */ jsxs5(Fragment4, { children: [
1760
+ /* @__PURE__ */ jsx8("ul", { className: "hsk-checkout-items", children: cart.items.map((item) => /* @__PURE__ */ jsxs5("li", { children: [
1761
+ /* @__PURE__ */ jsxs5("span", { children: [
1762
+ item.quantity,
1763
+ "x ",
1764
+ item.name
1765
+ ] }),
1766
+ /* @__PURE__ */ jsxs5("span", { children: [
1767
+ item.currency,
1768
+ " ",
1769
+ (item.price_numeric * item.quantity).toLocaleString(void 0, { minimumFractionDigits: 2 })
1770
+ ] })
1771
+ ] }, item.id)) }),
1772
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-total", children: [
1773
+ /* @__PURE__ */ jsx8("span", { children: "Total" }),
1774
+ /* @__PURE__ */ jsxs5("span", { children: [
1775
+ cart.currency,
1776
+ " ",
1777
+ cart.total.toLocaleString(void 0, { minimumFractionDigits: 2 })
1778
+ ] })
1779
+ ] })
1780
+ ] })
1781
+ ] }),
1782
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-payment", children: [
1783
+ /* @__PURE__ */ jsx8("h3", { children: "Payment Method" }),
1784
+ loading ? /* @__PURE__ */ jsx8("p", { className: "hsk-cart-loading", children: "Loading secure payment methods..." }) : !hasPaymentMethods ? /* @__PURE__ */ jsx8("p", { className: "hsk-checkout-error", children: "No payment methods are currently available for this store." }) : /* @__PURE__ */ jsxs5("div", { className: "hsk-payment-options", children: [
1785
+ ((_a = config == null ? void 0 : config.mpesa) == null ? void 0 : _a.enabled) && /* @__PURE__ */ jsx8("button", { onClick: () => handlePay("mpesa"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-mpesa", children: checkingOut ? "Processing..." : "Pay with M-Pesa" }),
1786
+ ((_b = config == null ? void 0 : config.equity) == null ? void 0 : _b.enabled) && /* @__PURE__ */ jsx8("button", { onClick: () => handlePay("equity"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-equity", children: checkingOut ? "Processing..." : "Pay with Equity Bank" }),
1787
+ ((_c = config == null ? void 0 : config.stripe) == null ? void 0 : _c.enabled) && /* @__PURE__ */ jsx8("button", { onClick: () => handlePay("stripe"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-stripe", children: checkingOut ? "Processing..." : "Pay with Card (Stripe)" }),
1788
+ ((_d = config == null ? void 0 : config.paypal) == null ? void 0 : _d.enabled) && /* @__PURE__ */ jsx8("button", { onClick: () => handlePay("paypal"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-paypal", children: checkingOut ? "Processing..." : "Pay with PayPal" })
1789
+ ] })
1790
+ ] })
1791
+ ] }) })
1792
+ ]
1793
+ }
1794
+ )
1795
+ }
1796
+ ),
1797
+ document.body
1798
+ );
1799
+ }
1800
+
1801
+ // src/components/CartDrawer.tsx
1802
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1803
+ function CartDrawer({
1804
+ trigger,
1805
+ className,
1806
+ theme
1807
+ }) {
1808
+ const { cart, loading } = useCart();
1809
+ const [open, setOpen] = useState10(false);
1810
+ const [showCheckout, setShowCheckout] = useState10(false);
1811
+ const [mounted, setMounted] = useState10(false);
1812
+ const client = useHuskelContext();
1813
+ useEffect9(() => {
1814
+ setMounted(true);
1815
+ const handleTriggerCheckout = () => {
1816
+ setShowCheckout(true);
1817
+ setOpen(false);
1818
+ };
1819
+ window.addEventListener("huskel:trigger_checkout", handleTriggerCheckout);
1820
+ return () => {
1821
+ window.removeEventListener("huskel:trigger_checkout", handleTriggerCheckout);
1822
+ };
1823
+ }, []);
1824
+ useEffect9(() => {
1825
+ if (open) {
1826
+ document.body.style.overflow = "hidden";
1827
+ } else {
1828
+ document.body.style.overflow = "";
1829
+ }
1830
+ return () => {
1831
+ document.body.style.overflow = "";
1832
+ };
1833
+ }, [open]);
1834
+ const handleCheckout = async () => {
1835
+ if (!cart || cart.items.length === 0) return;
1836
+ setShowCheckout(true);
1837
+ };
1838
+ const isStringTheme = typeof theme === "string";
1839
+ const hskThemeAttr = isStringTheme ? theme : void 0;
1840
+ 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;
1841
+ return /* @__PURE__ */ jsxs6(Fragment5, { children: [
1842
+ /* @__PURE__ */ jsx9("div", { onClick: () => setOpen(true), style: { display: "inline-block" }, children: trigger || /* @__PURE__ */ jsxs6(
1843
+ "button",
1844
+ {
1845
+ className: cn("hsk-cart-trigger", className),
1846
+ style: customStyles,
1847
+ "data-hsk-theme": hskThemeAttr,
1848
+ "aria-label": "Open cart",
1849
+ children: [
1850
+ /* @__PURE__ */ jsxs6("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1851
+ /* @__PURE__ */ jsx9("circle", { cx: "9", cy: "21", r: "1" }),
1852
+ /* @__PURE__ */ jsx9("circle", { cx: "20", cy: "21", r: "1" }),
1853
+ /* @__PURE__ */ jsx9("path", { d: "M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" })
1854
+ ] }),
1855
+ cart && cart.item_count > 0 ? /* @__PURE__ */ jsx9("span", { className: "hsk-cart-trigger-badge", children: cart.item_count }) : null
1856
+ ]
1857
+ }
1858
+ ) }),
1859
+ open && mounted && createPortal4(
1860
+ /* @__PURE__ */ jsx9(
1861
+ "div",
1862
+ {
1863
+ className: "hsk-cart-backdrop",
1864
+ style: customStyles,
1865
+ "data-hsk-theme": hskThemeAttr,
1866
+ onClick: () => setOpen(false),
1867
+ children: /* @__PURE__ */ jsxs6(
1868
+ "div",
1869
+ {
1870
+ className: "hsk-cart-bottom-sheet",
1871
+ style: customStyles,
1872
+ "data-hsk-theme": hskThemeAttr,
1873
+ onClick: (e) => e.stopPropagation(),
1874
+ children: [
1875
+ /* @__PURE__ */ jsx9("div", { className: "hsk-cart-sheet-handle" }),
1876
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-cart-sheet-header", children: [
1877
+ /* @__PURE__ */ jsx9("h2", { children: "Your Cart" }),
1878
+ /* @__PURE__ */ jsx9("button", { onClick: () => setOpen(false), className: "hsk-close-btn", children: "\xD7" })
1879
+ ] }),
1880
+ /* @__PURE__ */ jsx9("div", { className: "hsk-cart-sheet-content", children: loading && !cart ? /* @__PURE__ */ jsx9("div", { className: "hsk-cart-loading", children: "Loading cart..." }) : !cart || cart.items.length === 0 ? /* @__PURE__ */ jsx9("div", { className: "hsk-cart-empty", children: "Your cart is empty." }) : /* @__PURE__ */ jsx9("ul", { className: "hsk-cart-items", children: cart.items.map((item) => /* @__PURE__ */ jsxs6("li", { className: "hsk-cart-item", children: [
1881
+ item.image && /* @__PURE__ */ jsx9("img", { src: item.image, alt: item.name, className: "hsk-cart-item-img" }),
1882
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-cart-item-info", children: [
1883
+ /* @__PURE__ */ jsx9("span", { className: "hsk-cart-item-name", children: item.name }),
1884
+ /* @__PURE__ */ jsxs6("span", { className: "hsk-cart-item-price", children: [
1885
+ item.currency,
1886
+ " ",
1887
+ item.price_numeric.toLocaleString(void 0, { minimumFractionDigits: 2 })
1888
+ ] })
1889
+ ] }),
1890
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-cart-item-qty", children: [
1891
+ "x",
1892
+ item.quantity
1893
+ ] })
1894
+ ] }, item.id)) }) }),
1895
+ cart && cart.items.length > 0 && /* @__PURE__ */ jsxs6("div", { className: "hsk-cart-sheet-footer", children: [
1896
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-cart-total", children: [
1897
+ /* @__PURE__ */ jsx9("span", { children: "Total" }),
1898
+ /* @__PURE__ */ jsxs6("span", { children: [
1899
+ cart.currency,
1900
+ " ",
1901
+ cart.total.toLocaleString(void 0, { minimumFractionDigits: 2 })
1902
+ ] })
1903
+ ] }),
1904
+ /* @__PURE__ */ jsx9("button", { onClick: handleCheckout, className: "hsk-checkout-btn", children: "Checkout securely" })
1905
+ ] })
1906
+ ]
1907
+ }
1908
+ )
1909
+ }
1910
+ ),
1911
+ document.body
1912
+ ),
1913
+ showCheckout && mounted && /* @__PURE__ */ jsx9(
1914
+ CheckoutModal,
1915
+ {
1916
+ onClose: () => {
1917
+ setShowCheckout(false);
1918
+ setOpen(false);
1919
+ },
1920
+ theme: isStringTheme ? theme : void 0,
1921
+ customStyles,
1922
+ hskThemeAttr
1923
+ }
1924
+ )
1925
+ ] });
1926
+ }
1268
1927
  export {
1269
1928
  AIChatButton,
1929
+ CartBadge,
1930
+ CartDrawer,
1270
1931
  ChatWidget,
1271
1932
  HuskelAPI,
1272
1933
  HuskelClient,
@@ -1275,6 +1936,7 @@ export {
1275
1936
  Sparkle,
1276
1937
  getHuskelClient,
1277
1938
  initHuskel,
1939
+ useCart,
1278
1940
  useChat,
1279
1941
  useHuskel,
1280
1942
  useIngest,