@huskel/sdk 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -557,6 +557,7 @@ function useChat() {
557
557
  if (!query.trim() || loading) return;
558
558
  (_a = abortRef.current) == null ? void 0 : _a.abort();
559
559
  abortRef.current = new AbortController();
560
+ const signal = abortRef.current.signal;
560
561
  const userMsg = { role: "user", content: query };
561
562
  setMessages((prev) => [...prev, userMsg]);
562
563
  setLoading(true);
@@ -564,20 +565,42 @@ function useChat() {
564
565
  try {
565
566
  const history = messages.map((m) => ({ role: m.role, content: m.content }));
566
567
  const res = await client.api.chat(query, history);
567
- const assistantMsg = { role: "assistant", content: res.answer };
568
- setMessages((prev) => [...prev, assistantMsg]);
568
+ if (signal.aborted) return;
569
+ const fullAnswer = res.answer || "";
570
+ const words = fullAnswer.split(/(\s+)/);
571
+ setMessages((prev) => [...prev, { role: "assistant", content: "" }]);
572
+ let currentContent = "";
573
+ for (const word of words) {
574
+ if (signal.aborted) return;
575
+ currentContent += word;
576
+ setMessages((prev) => {
577
+ const next = [...prev];
578
+ if (next.length > 0) {
579
+ next[next.length - 1] = { role: "assistant", content: currentContent };
580
+ }
581
+ return next;
582
+ });
583
+ await new Promise((resolve) => setTimeout(resolve, 25));
584
+ }
585
+ if (signal.aborted) return;
569
586
  setSources((_b = res.sources) != null ? _b : []);
570
587
  } catch (e) {
588
+ if (signal.aborted) return;
571
589
  setError((_c = e == null ? void 0 : e.message) != null ? _c : "Chat request failed");
572
590
  setMessages((prev) => prev.slice(0, -1));
573
591
  } finally {
574
- setLoading(false);
592
+ if (!signal.aborted) {
593
+ setLoading(false);
594
+ }
575
595
  }
576
596
  }, [client, messages, loading]);
577
597
  const reset = (0, import_react6.useCallback)(() => {
598
+ var _a;
599
+ (_a = abortRef.current) == null ? void 0 : _a.abort();
578
600
  setMessages([]);
579
601
  setSources([]);
580
602
  setError(null);
603
+ setLoading(false);
581
604
  }, []);
582
605
  return { messages, sources, loading, error, send, reset };
583
606
  }
@@ -585,134 +608,6 @@ function useChat() {
585
608
  // src/components/SearchBar.tsx
586
609
  var import_react7 = require("react");
587
610
  var import_jsx_runtime2 = require("react/jsx-runtime");
588
- var CSS = `
589
- /* \u2500\u2500 Light mode defaults (CSS custom properties) \u2500\u2500 */
590
- .hsk-sb-wrap {
591
- --hsk-bg: #fff;
592
- --hsk-border: #d1d5db;
593
- --hsk-text: #111;
594
- --hsk-muted: #9ca3af;
595
- --hsk-hover: #f3f4f6;
596
- --hsk-drop-shadow: 0 8px 30px rgba(0,0,0,.12);
597
- position: relative;
598
- width: 100%;
599
- font-family: inherit;
600
- }
601
-
602
- /* \u2500\u2500 Dark mode overrides at wrap level so ALL children inherit \u2500\u2500 */
603
- @media (prefers-color-scheme: dark) {
604
- .hsk-sb-wrap {
605
- --hsk-bg: #1a1a1b;
606
- --hsk-border: #2a2a2d;
607
- --hsk-text: #f3f3f2;
608
- --hsk-muted: #666;
609
- --hsk-hover: #1e1e1f;
610
- --hsk-drop-shadow: 0 12px 40px rgba(0,0,0,.5);
611
- }
612
- }
613
-
614
- /* \u2500\u2500 Input \u2500\u2500 */
615
- .hsk-sb-input {
616
- width: 100%;
617
- padding: 10px 16px 10px 40px;
618
- font-size: 14px;
619
- border-radius: 9999px;
620
- border: 1.5px solid var(--hsk-border);
621
- outline: none;
622
- box-sizing: border-box;
623
- background: var(--hsk-bg);
624
- color: var(--hsk-text);
625
- transition: border-color .15s, box-shadow .15s;
626
- font-family: inherit;
627
- }
628
- .hsk-sb-input::placeholder { color: var(--hsk-muted); }
629
- .hsk-sb-input:focus {
630
- border-color: #ff6a33;
631
- box-shadow: 0 0 0 3px rgba(255,106,51,.12);
632
- }
633
- .hsk-sb-icon {
634
- position: absolute;
635
- left: 14px;
636
- top: 50%;
637
- transform: translateY(-50%);
638
- color: var(--hsk-muted);
639
- pointer-events: none;
640
- display: flex;
641
- align-items: center;
642
- }
643
-
644
- /* \u2500\u2500 Dropdown \u2500\u2500 */
645
- .hsk-sb-drop {
646
- position: absolute;
647
- top: calc(100% + 6px);
648
- left: 0; right: 0;
649
- background: var(--hsk-bg);
650
- border: 1px solid var(--hsk-border);
651
- border-radius: 12px;
652
- box-shadow: var(--hsk-drop-shadow);
653
- z-index: 9999;
654
- overflow: hidden;
655
- padding: 6px 0;
656
- }
657
-
658
- /* \u2500\u2500 Row \u2500\u2500 */
659
- .hsk-sb-row {
660
- display: flex;
661
- align-items: center;
662
- gap: 12px;
663
- padding: 9px 16px;
664
- cursor: pointer;
665
- transition: background .1s;
666
- }
667
- .hsk-sb-row:hover { background: var(--hsk-hover); }
668
- .hsk-sb-row-icon {
669
- color: var(--hsk-muted);
670
- flex-shrink: 0;
671
- display: flex;
672
- align-items: center;
673
- }
674
- .hsk-sb-row-body { flex: 1; min-width: 0; }
675
- .hsk-sb-row-title {
676
- font-size: 13px;
677
- font-weight: 500;
678
- color: var(--hsk-text);
679
- white-space: nowrap;
680
- overflow: hidden;
681
- text-overflow: ellipsis;
682
- line-height: 1.3;
683
- }
684
- .hsk-sb-row-sub {
685
- font-size: 11px;
686
- color: var(--hsk-muted);
687
- margin-top: 2px;
688
- white-space: nowrap;
689
- overflow: hidden;
690
- text-overflow: ellipsis;
691
- }
692
- .hsk-sb-empty {
693
- padding: 14px 16px;
694
- font-size: 13px;
695
- color: var(--hsk-muted);
696
- }
697
-
698
- /* \u2500\u2500 Thin accent bar while loading (non-intrusive, no text) \u2500\u2500 */
699
- .hsk-sb-loading-bar {
700
- height: 2px;
701
- background: linear-gradient(90deg, transparent, #ff6a33, transparent);
702
- background-size: 200% 100%;
703
- animation: hsk-sweep 0.9s linear infinite;
704
- position: absolute;
705
- top: 0; left: 0; right: 0;
706
- }
707
- @keyframes hsk-sweep {
708
- 0% { background-position: 200% 0; }
709
- 100% { background-position: -200% 0; }
710
- }
711
-
712
- /* \u2500\u2500 Staggered fade-in \u2500\u2500 */
713
- .hsk-sb-fade { animation: hsk-fin .1s ease-out both; }
714
- @keyframes hsk-fin { from { opacity:0; transform:translateY(3px); } to { opacity:1; transform:none; } }
715
- `;
716
611
  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: [
717
612
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8.5", cy: "8.5", r: "5.5" }),
718
613
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "13", y1: "13", x2: "18", y2: "18" })
@@ -725,7 +620,9 @@ function SearchBar({
725
620
  className,
726
621
  inputClassName,
727
622
  dropdownClassName,
728
- renderResult
623
+ renderResult,
624
+ theme,
625
+ classNames = {}
729
626
  }) {
730
627
  const [query, setQuery] = (0, import_react7.useState)("");
731
628
  const [open, setOpen] = (0, import_react7.useState)(false);
@@ -758,59 +655,57 @@ function SearchBar({
758
655
  onSelect == null ? void 0 : onSelect(r);
759
656
  };
760
657
  const showDrop = open && query.trim().length > 0;
761
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
762
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: CSS }),
763
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `hsk-sb-wrap ${className != null ? className : ""}`, ref: wrap, children: [
764
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "hsk-sb-icon", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SearchIcon, {}) }),
765
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
766
- "input",
767
- {
768
- className: `hsk-sb-input ${inputClassName != null ? inputClassName : ""}`,
769
- type: "text",
770
- value: query,
771
- placeholder,
772
- onChange: (e) => setQuery(e.target.value),
773
- onFocus: () => results.length > 0 && query.trim() && setOpen(true),
774
- autoComplete: "off",
775
- spellCheck: false
776
- }
777
- ),
778
- showDrop && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `hsk-sb-drop ${dropdownClassName != null ? dropdownClassName : ""}`, style: { position: "absolute" }, children: [
779
- loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-loading-bar" }),
780
- results.length === 0 && !loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-empty", children: [
781
- "No results for \u201C",
782
- query,
783
- "\u201D"
784
- ] }),
785
- results.map((r, i) => {
786
- var _a;
787
- return renderResult ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
788
- "div",
789
- {
790
- onClick: () => handleSelect(r),
791
- className: "hsk-sb-fade",
792
- style: { animationDelay: `${i * 18}ms` },
793
- children: renderResult(r)
794
- },
795
- r.id
796
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
797
- "div",
798
- {
799
- className: "hsk-sb-row hsk-sb-fade",
800
- style: { animationDelay: `${i * 18}ms` },
801
- onClick: () => handleSelect(r),
802
- children: [
803
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "hsk-sb-row-icon", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SearchIcon, {}) }),
804
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-row-body", children: [
805
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-row-title", children: r.product.name }),
806
- (r.product.category || r.product.brand) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-row-sub", children: (_a = r.product.category) != null ? _a : r.product.brand })
807
- ] })
808
- ]
809
- },
810
- r.id
811
- );
812
- })
813
- ] })
658
+ 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 });
659
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `hsk-sb-wrap ${classNames.root || ""} ${className || ""}`, ref: wrap, style: customStyles, children: [
660
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "hsk-sb-icon", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SearchIcon, {}) }),
661
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
662
+ "input",
663
+ {
664
+ className: `hsk-sb-input ${classNames.input || ""} ${inputClassName || ""}`,
665
+ type: "text",
666
+ value: query,
667
+ placeholder,
668
+ onChange: (e) => setQuery(e.target.value),
669
+ onFocus: () => results.length > 0 && query.trim() && setOpen(true),
670
+ autoComplete: "off",
671
+ spellCheck: false
672
+ }
673
+ ),
674
+ showDrop && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `hsk-sb-drop ${classNames.dropdown || ""} ${dropdownClassName || ""}`, style: { position: "absolute" }, children: [
675
+ loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-loading-bar" }),
676
+ results.length === 0 && !loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-empty", children: [
677
+ "No results for \u201C",
678
+ query,
679
+ "\u201D"
680
+ ] }),
681
+ results.map((r, i) => {
682
+ var _a;
683
+ return renderResult ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
684
+ "div",
685
+ {
686
+ onClick: () => handleSelect(r),
687
+ className: "hsk-sb-fade",
688
+ style: { animationDelay: `${i * 18}ms` },
689
+ children: renderResult(r)
690
+ },
691
+ r.id
692
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
693
+ "div",
694
+ {
695
+ className: `hsk-sb-row hsk-sb-fade ${classNames.row || ""}`,
696
+ style: { animationDelay: `${i * 18}ms` },
697
+ onClick: () => handleSelect(r),
698
+ children: [
699
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "hsk-sb-row-icon", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SearchIcon, {}) }),
700
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-row-body", children: [
701
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-row-title", children: r.product.name }),
702
+ (r.product.category || r.product.brand) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-row-sub", children: (_a = r.product.category) != null ? _a : r.product.brand })
703
+ ] })
704
+ ]
705
+ },
706
+ r.id
707
+ );
708
+ })
814
709
  ] })
815
710
  ] });
816
711
  }
@@ -819,236 +714,22 @@ function SearchBar({
819
714
  var import_react8 = require("react");
820
715
  var import_react_dom = require("react-dom");
821
716
  var import_jsx_runtime3 = require("react/jsx-runtime");
822
- var CSS2 = `
823
- /* \u2500\u2500 Trigger button \u2500\u2500 */
824
- .hsk-sp-btn {
825
- display: inline-flex;
826
- align-items: center;
827
- justify-content: center;
828
- width: 32px;
829
- height: 32px;
830
- border-radius: 8px;
831
- border: 1px solid var(--hsk-sp-border, rgba(255,106,51,.35));
832
- background: var(--hsk-sp-bg, rgba(255,106,51,.08));
833
- color: #ff6a33;
834
- cursor: pointer;
835
- font-size: 15px;
836
- line-height: 1;
837
- transition: background .15s, border-color .15s, transform .12s;
838
- flex-shrink: 0;
839
- padding: 0;
840
- }
841
- .hsk-sp-btn:hover {
842
- background: rgba(255,106,51,.18);
843
- border-color: rgba(255,106,51,.7);
844
- transform: scale(1.1);
845
- }
846
- .hsk-sp-btn:active { transform: scale(.92); }
847
-
848
- /* \u2500\u2500 Backdrop \u2500\u2500 */
849
- .hsk-sp-backdrop {
850
- position: fixed;
851
- inset: 0;
852
- z-index: 99998;
853
- display: flex;
854
- align-items: flex-start;
855
- justify-content: center;
856
- padding: clamp(48px, 10vh, 96px) 16px 40px;
857
- animation: hsk-bd-in .2s ease-out both;
858
- overflow-y: auto;
859
- }
860
- @keyframes hsk-bd-in { from { opacity:0; } to { opacity:1; } }
861
-
862
- /* \u2500\u2500 Modal card \u2500\u2500 */
863
- .hsk-sp-card {
864
- width: 100%;
865
- max-width: 600px;
866
- border-radius: 18px;
867
- overflow: hidden;
868
- animation: hsk-card-in .24s cubic-bezier(.34,1.36,.64,1) both;
869
- flex-shrink: 0;
870
- /* light */
871
- background: var(--hsk-modal-card-bg, #fff);
872
- border: 1px solid var(--hsk-modal-card-border, rgba(0,0,0,.08));
873
- box-shadow: 0 32px 80px rgba(0,0,0,.18), 0 2px 8px rgba(0,0,0,.06);
874
- }
875
- @keyframes hsk-card-in {
876
- from { opacity:0; transform: scale(.96) translateY(-12px); }
877
- to { opacity:1; transform: scale(1) translateY(0); }
878
- }
879
-
880
- /* \u2500\u2500 Header \u2500\u2500 */
881
- .hsk-sp-header {
882
- display: flex;
883
- align-items: center;
884
- gap: 10px;
885
- padding: 18px 20px 14px;
886
- border-bottom: 1px solid var(--hsk-modal-divide, rgba(0,0,0,.07));
887
- }
888
- .hsk-sp-header-icon { font-size: 18px; color: #ff6a33; flex-shrink: 0; }
889
- .hsk-sp-header-body { flex: 1; min-width: 0; }
890
- .hsk-sp-header-title {
891
- font-size: 14px;
892
- font-weight: 600;
893
- color: var(--hsk-modal-text, #111);
894
- white-space: nowrap;
895
- overflow: hidden;
896
- text-overflow: ellipsis;
897
- }
898
- .hsk-sp-header-sub {
899
- font-size: 11px;
900
- color: var(--hsk-modal-muted, #888);
901
- margin-top: 2px;
902
- }
903
- .hsk-sp-close {
904
- width: 30px; height: 30px;
905
- border-radius: 8px;
906
- border: 1px solid var(--hsk-modal-divide, rgba(0,0,0,.1));
907
- background: none;
908
- color: var(--hsk-modal-muted, #888);
909
- cursor: pointer;
910
- font-size: 18px;
911
- display: flex; align-items: center; justify-content: center;
912
- transition: all .15s;
913
- flex-shrink: 0;
914
- }
915
- .hsk-sp-close:hover { border-color: #ff6a33; color: #ff6a33; }
916
-
917
- /* \u2500\u2500 Thin loading accent bar \u2500\u2500 */
918
- .hsk-sp-bar {
919
- height: 2px;
920
- background: linear-gradient(90deg, transparent 0%, #ff6a33 40%, #ffaa80 60%, transparent 100%);
921
- background-size: 200% 100%;
922
- animation: hsk-bar .9s linear infinite;
923
- }
924
- @keyframes hsk-bar { 0%{background-position:200% 0} 100%{background-position:-200% 0} }
925
-
926
- /* \u2500\u2500 Results list \u2500\u2500 */
927
- .hsk-sp-results { padding: 10px 12px; display: flex; flex-direction: column; gap: 8px; }
928
- .hsk-sp-empty { padding: 40px; text-align: center; font-size: 13px; color: var(--hsk-modal-muted, #aaa); }
929
-
930
- /* \u2500\u2500 Result card (toast-inspired: slides up from bottom) \u2500\u2500 */
931
- .hsk-sp-item {
932
- display: flex;
933
- gap: 14px;
934
- padding: 14px;
935
- border-radius: 12px;
936
- border: 1px solid var(--hsk-modal-item-border, rgba(0,0,0,.07));
937
- background: var(--hsk-modal-item-bg, #f9f9f9);
938
- animation: hsk-toast-up .28s cubic-bezier(.22,.68,0,1.2) both;
939
- overflow: hidden;
940
- }
941
- @keyframes hsk-toast-up {
942
- from { opacity:0; transform: translateY(18px) scale(.97); }
943
- to { opacity:1; transform: translateY(0) scale(1); }
944
- }
945
-
946
- /* image */
947
- .hsk-sp-img-wrap {
948
- width: 72px; height: 72px;
949
- border-radius: 10px;
950
- background: #fff;
951
- border: 1px solid var(--hsk-modal-divide, rgba(0,0,0,.07));
952
- flex-shrink: 0;
953
- overflow: hidden;
954
- display: flex; align-items: center; justify-content: center;
955
- padding: 6px;
956
- }
957
- .hsk-sp-img-wrap img { max-width: 100%; max-height: 100%; object-fit: contain; }
958
- .hsk-sp-img-placeholder { font-size: 26px; }
959
-
960
- /* body */
961
- .hsk-sp-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
962
- .hsk-sp-item-name {
963
- font-size: 14px;
964
- font-weight: 600;
965
- color: var(--hsk-modal-text, #111);
966
- line-height: 1.35;
967
- overflow: hidden;
968
- display: -webkit-box;
969
- -webkit-line-clamp: 2;
970
- -webkit-box-orient: vertical;
971
- }
972
- .hsk-sp-item-cat {
973
- font-size: 11px;
974
- font-weight: 600;
975
- color: #ff6a33;
976
- text-transform: uppercase;
977
- letter-spacing: .05em;
978
- }
979
- .hsk-sp-item-price-row { display: flex; align-items: baseline; gap: 8px; margin-top: 2px; }
980
- .hsk-sp-item-price {
981
- font-size: 18px;
982
- font-weight: 700;
983
- color: var(--hsk-modal-text, #111);
984
- }
985
- .hsk-sp-item-currency { font-size: 12px; color: var(--hsk-modal-muted, #888); }
986
-
987
- /* actions */
988
- .hsk-sp-actions { display: flex; gap: 6px; margin-top: 8px; }
989
- .hsk-sp-action {
990
- flex: 1;
991
- padding: 7px 10px;
992
- border-radius: 8px;
993
- font-size: 12px;
994
- font-weight: 600;
995
- cursor: pointer;
996
- border: 1px solid transparent;
997
- transition: all .15s;
998
- text-align: center;
999
- font-family: inherit;
1000
- }
1001
- .hsk-sp-action-primary {
1002
- background: #ff6a33;
1003
- color: #fff;
1004
- border-color: #ff6a33;
1005
- }
1006
- .hsk-sp-action-primary:hover { background: #e55d2a; }
1007
- .hsk-sp-action-secondary {
1008
- background: var(--hsk-action-sec-bg, rgba(0,0,0,.06));
1009
- color: var(--hsk-modal-muted, #666);
1010
- border-color: var(--hsk-modal-divide, rgba(0,0,0,.1));
1011
- }
1012
- .hsk-sp-action-secondary:hover {
1013
- background: var(--hsk-action-sec-bg-hover, rgba(0,0,0,.1));
1014
- color: var(--hsk-modal-text, #333);
1015
- }
1016
-
1017
- /* \u2500\u2500 Footer \u2500\u2500 */
1018
- .hsk-sp-footer {
1019
- padding: 12px 20px;
1020
- border-top: 1px solid var(--hsk-modal-divide, rgba(0,0,0,.07));
1021
- display: flex;
1022
- align-items: center;
1023
- gap: 8px;
1024
- }
1025
- .hsk-sp-badge {
1026
- font-size: 10px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
1027
- color: #ff6a33;
1028
- background: rgba(255,106,51,.1);
1029
- border: 1px solid rgba(255,106,51,.25);
1030
- padding: 2px 8px;
1031
- border-radius: 999px;
1032
- }
1033
- .hsk-sp-esc { font-size: 11px; color: var(--hsk-modal-muted, #bbb); margin-left: auto; }
1034
-
1035
- /* \u2500\u2500 Dark mode \u2500\u2500 */
1036
- @media (prefers-color-scheme: dark) {
1037
- .hsk-sp-card {
1038
- --hsk-modal-card-bg: #111112;
1039
- --hsk-modal-card-border: rgba(255,255,255,.07);
1040
- --hsk-modal-text: #f3f3f2;
1041
- --hsk-modal-muted: #666;
1042
- --hsk-modal-divide: rgba(255,255,255,.07);
1043
- --hsk-modal-item-bg: #1a1a1b;
1044
- --hsk-modal-item-border: rgba(255,255,255,.06);
1045
- --hsk-action-sec-bg: rgba(255,255,255,.07);
1046
- --hsk-action-sec-bg-hover: rgba(255,255,255,.12);
1047
- }
1048
- .hsk-sp-img-wrap { background: #242425; border-color: rgba(255,255,255,.08); }
1049
- }
1050
- `;
1051
- function SparkleModal({ productName, limit, backdropColor, backdropBlur, onClose, onNavigate, onResult }) {
717
+ 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" }) });
718
+ 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: [
719
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
720
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
721
+ ] });
722
+ function SparkleModal({
723
+ productName,
724
+ limit,
725
+ backdropColor,
726
+ backdropBlur,
727
+ onClose,
728
+ onNavigate,
729
+ onResult,
730
+ theme,
731
+ classNames = {}
732
+ }) {
1052
733
  const { results, loading, search } = useSearch();
1053
734
  const initiated = (0, import_react8.useRef)(false);
1054
735
  (0, import_react8.useEffect)(() => {
@@ -1076,109 +757,114 @@ function SparkleModal({ productName, limit, backdropColor, backdropBlur, onClose
1076
757
  if (r.product.url) window.location.href = r.product.url;
1077
758
  }
1078
759
  };
1079
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1080
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: CSS2 }),
1081
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1082
- "div",
1083
- {
1084
- className: "hsk-sp-backdrop",
1085
- onClick: onClose,
1086
- style: {
1087
- backdropFilter: `blur(${blurVal})`,
1088
- WebkitBackdropFilter: `blur(${blurVal})`,
1089
- background: bg != null ? bg : void 0
1090
- /* CSS handles light/dark via @media if bg not forced */
1091
- },
1092
- children: [
1093
- !bg && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `
1094
- @media (prefers-color-scheme: dark) { .hsk-sp-backdrop { background: rgba(0,0,0,.80); } }
1095
- @media (prefers-color-scheme: light) { .hsk-sp-backdrop { background: rgba(240,240,245,.70); } }
1096
- ` }),
1097
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-card", onClick: (e) => e.stopPropagation(), children: [
1098
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-header", children: [
1099
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-header-icon", children: "\u2726" }),
1100
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-header-body", children: [
1101
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-header-title", children: [
1102
- "Similar to \u201C",
1103
- productName,
1104
- "\u201D"
1105
- ] }),
1106
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-header-sub", children: "AI vector similarity \xB7 instant results" })
1107
- ] }),
1108
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "hsk-sp-close", onClick: onClose, "aria-label": "Close", children: "\xD7" })
760
+ 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 });
761
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
762
+ "div",
763
+ {
764
+ className: `hsk-sp-backdrop ${classNames.backdrop || ""}`,
765
+ onClick: onClose,
766
+ style: __spreadValues({
767
+ backdropFilter: `blur(${blurVal})`,
768
+ WebkitBackdropFilter: `blur(${blurVal})`,
769
+ background: bg != null ? bg : void 0
770
+ }, customStyles),
771
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `hsk-sp-card ${classNames.card || ""}`, onClick: (e) => e.stopPropagation(), children: [
772
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-header", children: [
773
+ /* @__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, {}) }),
774
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-header-body", children: [
775
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-header-title", children: [
776
+ "Similar to \u201C",
777
+ productName,
778
+ "\u201D"
1109
779
  ] }),
1110
- loading && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-bar" }),
1111
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-results", children: [
1112
- !loading && results.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-empty", children: "No similar products found." }),
1113
- results.map((r, i) => {
1114
- var _a, _b, _c;
1115
- const price = parseFloat(((_a = r.product.price) == null ? void 0 : _a.replace(/[^0-9.]/g, "")) || "0");
1116
- const currency = (_b = r.product.currency) != null ? _b : "KES";
1117
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1118
- "div",
1119
- {
1120
- className: "hsk-sp-item",
1121
- style: { animationDelay: `${i * 55}ms` },
1122
- children: [
1123
- /* @__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}" }) }),
1124
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-item-body", children: [
1125
- r.product.category && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-item-cat", children: r.product.category }),
1126
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-item-name", children: r.product.name }),
1127
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-item-price-row", children: [
1128
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-item-currency", children: currency }),
1129
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-item-price", children: price.toLocaleString() })
1130
- ] }),
1131
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-actions", children: [
1132
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1133
- "button",
1134
- {
1135
- className: "hsk-sp-action hsk-sp-action-primary",
1136
- onClick: () => handleNav(r),
1137
- children: "View Product"
1138
- }
1139
- ),
1140
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1141
- "button",
1142
- {
1143
- className: "hsk-sp-action hsk-sp-action-secondary",
1144
- onClick: () => onClose(),
1145
- children: "Add to Cart"
1146
- }
1147
- )
1148
- ] })
1149
- ] })
1150
- ]
1151
- },
1152
- r.id
1153
- );
1154
- })
1155
- ] }),
1156
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-footer", children: [
1157
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-badge", children: "\u2726 Huskel AI" }),
1158
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-esc", children: "Esc to close" })
1159
- ] })
1160
- ] })
1161
- ]
1162
- }
1163
- )
1164
- ] });
780
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-header-sub", children: "AI vector similarity \xB7 instant results" })
781
+ ] }),
782
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "hsk-sp-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}) })
783
+ ] }),
784
+ loading && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-bar" }),
785
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-results", children: [
786
+ !loading && results.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-empty", children: "No similar products found." }),
787
+ results.map((r, i) => {
788
+ var _a, _b, _c;
789
+ const price = parseFloat(((_a = r.product.price) == null ? void 0 : _a.replace(/[^0-9.]/g, "")) || "0");
790
+ const currency = (_b = r.product.currency) != null ? _b : "KES";
791
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
792
+ "div",
793
+ {
794
+ className: `hsk-sp-item ${classNames.item || ""}`,
795
+ style: { animationDelay: `${i * 55}ms` },
796
+ children: [
797
+ /* @__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}" }) }),
798
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-item-body", children: [
799
+ r.product.category && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-item-cat", children: r.product.category }),
800
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hsk-sp-item-name", children: r.product.name }),
801
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-item-price-row", children: [
802
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-item-currency", children: currency }),
803
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-item-price", children: price.toLocaleString() })
804
+ ] }),
805
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-actions", children: [
806
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
807
+ "button",
808
+ {
809
+ className: "hsk-sp-action hsk-sp-action-primary",
810
+ onClick: () => handleNav(r),
811
+ children: "View Product"
812
+ }
813
+ ),
814
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
815
+ "button",
816
+ {
817
+ className: "hsk-sp-action hsk-sp-action-secondary",
818
+ onClick: () => onClose(),
819
+ children: "Add to Cart"
820
+ }
821
+ )
822
+ ] })
823
+ ] })
824
+ ]
825
+ },
826
+ r.id
827
+ );
828
+ })
829
+ ] }),
830
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "hsk-sp-footer", children: [
831
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "hsk-sp-badge", style: { display: "inline-flex", alignItems: "center", gap: "4px" }, children: [
832
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SparkleIcon, {}),
833
+ " Huskel AI"
834
+ ] }),
835
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "hsk-sp-esc", children: "Esc to close" })
836
+ ] })
837
+ ] })
838
+ }
839
+ );
1165
840
  }
1166
- function Sparkle({ productName, limit = 8, onResult, backdropColor, backdropBlur, className, onNavigate }) {
841
+ function Sparkle({
842
+ productName,
843
+ limit = 8,
844
+ onResult,
845
+ backdropColor,
846
+ backdropBlur,
847
+ className,
848
+ onNavigate,
849
+ theme,
850
+ classNames = {}
851
+ }) {
1167
852
  const [open, setOpen] = (0, import_react8.useState)(false);
1168
853
  const [mounted, setMounted] = (0, import_react8.useState)(false);
1169
854
  (0, import_react8.useEffect)(() => {
1170
855
  setMounted(true);
1171
856
  }, []);
857
+ 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 });
1172
858
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1173
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: CSS2 }),
1174
859
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1175
860
  "button",
1176
861
  {
1177
- className: `hsk-sp-btn ${className != null ? className : ""}`,
862
+ className: `hsk-sp-btn ${classNames.button || ""} ${className || ""}`,
1178
863
  onClick: () => setOpen(true),
864
+ style: customStyles,
1179
865
  title: "Find similar products",
1180
866
  "aria-label": "Find similar products",
1181
- children: "\u2726"
867
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SparkleIcon, {})
1182
868
  }
1183
869
  ),
1184
870
  open && mounted && (0, import_react_dom.createPortal)(
@@ -1191,7 +877,9 @@ function Sparkle({ productName, limit = 8, onResult, backdropColor, backdropBlur
1191
877
  backdropBlur,
1192
878
  onClose: () => setOpen(false),
1193
879
  onResult,
1194
- onNavigate
880
+ onNavigate,
881
+ theme,
882
+ classNames
1195
883
  }
1196
884
  ),
1197
885
  document.body
@@ -1201,60 +889,124 @@ function Sparkle({ productName, limit = 8, onResult, backdropColor, backdropBlur
1201
889
 
1202
890
  // src/components/ChatWidget.tsx
1203
891
  var import_react9 = require("react");
892
+
893
+ // src/utils/markdown.tsx
1204
894
  var import_jsx_runtime4 = require("react/jsx-runtime");
1205
- var S = `
1206
- .hsk-chat-widget{display:flex;flex-direction:column;height:100%;min-height:320px;font-family:inherit;background:#0f0f10;border:1px solid #2a2a2d;border-radius:12px;overflow:hidden}
1207
- .hsk-chat-header{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid #1e1e1f;background:#111112;flex-shrink:0}
1208
- .hsk-chat-title{font-size:14px;font-weight:600;color:#f3f3f2}
1209
- .hsk-chat-badge{font-size:10px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:#ff6a33;background:#ff6a3315;border:1px solid #ff6a3330;padding:2px 8px;border-radius:20px}
1210
- .hsk-chat-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px;scroll-behavior:smooth}
1211
- .hsk-chat-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:8px;color:#555;font-size:13px;text-align:center;padding:24px}
1212
- .hsk-chat-empty-icon{font-size:28px;margin-bottom:4px}
1213
- .hsk-msg-row{display:flex;gap:8px;align-items:flex-start}
1214
- .hsk-msg-row.user{flex-direction:row-reverse}
1215
- .hsk-msg-avatar{width:28px;height:28px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700}
1216
- .hsk-msg-avatar.ai{background:#ff6a3320;border:1px solid #ff6a3340;color:#ff6a33}
1217
- .hsk-msg-avatar.user{background:#2a2a2d;color:#9a9aa1}
1218
- .hsk-msg-bubble{max-width:78%;padding:10px 14px;border-radius:12px;font-size:13px;line-height:1.6}
1219
- .hsk-msg-bubble.ai{background:#171718;border:1px solid #2a2a2d;color:#e8e8e7;border-radius:4px 12px 12px 12px}
1220
- .hsk-msg-bubble.user{background:#ff6a33;color:#fff;border-radius:12px 4px 12px 12px}
1221
- .hsk-sources{margin-top:10px;display:flex;flex-direction:column;gap:6px}
1222
- .hsk-source-card{display:flex;align-items:center;gap:10px;padding:8px 10px;background:#1a1a1b;border:1px solid #252527;border-radius:8px;cursor:pointer;transition:border-color 0.15s}
1223
- .hsk-source-card:hover{border-color:#ff6a3360}
1224
- .hsk-source-img{width:36px;height:36px;object-fit:cover;border-radius:4px;background:#fff}
1225
- .hsk-source-name{font-size:12px;font-weight:500;color:#e8e8e7;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
1226
- .hsk-source-price{font-size:11px;color:#ff6a33;font-weight:700;margin-top:2px}
1227
- .hsk-typing{display:flex;gap:4px;align-items:center;padding:10px 14px;background:#171718;border:1px solid #2a2a2d;border-radius:4px 12px 12px 12px;width:fit-content}
1228
- .hsk-typing-dot{width:6px;height:6px;background:#ff6a33;border-radius:50%;animation:hsk-chat-bounce 1.2s infinite}
1229
- .hsk-typing-dot:nth-child(2){animation-delay:0.2s}
1230
- .hsk-typing-dot:nth-child(3){animation-delay:0.4s}
1231
- @keyframes hsk-chat-bounce{0%,100%{opacity:0.3;transform:translateY(0)}50%{opacity:1;transform:translateY(-4px)}}
1232
- .hsk-chat-input-area{display:flex;align-items:center;gap:8px;padding:12px 14px;border-top:1px solid #1e1e1f;background:#111112;flex-shrink:0}
1233
- .hsk-chat-input{flex:1;background:#1a1a1b;border:1px solid #2a2a2d;border-radius:8px;padding:9px 14px;font-size:13px;color:#f3f3f2;outline:none;font-family:inherit;transition:border-color 0.2s;resize:none;min-height:38px;max-height:120px;line-height:1.5}
1234
- .hsk-chat-input::placeholder{color:#555}
1235
- .hsk-chat-input:focus{border-color:#ff6a33}
1236
- .hsk-chat-send{width:34px;height:34px;border-radius:8px;background:#ff6a33;border:none;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:16px;transition:opacity 0.15s,transform 0.1s}
1237
- .hsk-chat-send:hover{opacity:0.88}
1238
- .hsk-chat-send:active{transform:scale(0.93)}
1239
- .hsk-chat-send:disabled{opacity:0.4;cursor:not-allowed}
1240
- .hsk-chat-reset{font-size:11px;color:#555;cursor:pointer;padding:0 4px;transition:color 0.15s;background:none;border:none;font-family:inherit}
1241
- .hsk-chat-reset:hover{color:#ff6a33}
1242
- `;
1243
- function SourceCard({ source, onSelect }) {
895
+ var parseInline = (text, keyPrefix) => {
896
+ const tokenRegex = /(\[[^\]]+\]\([^)]+\)|\*\*[^*]+\*\*|`[^`]+`)/g;
897
+ const parts = text.split(tokenRegex);
898
+ return parts.map((part, index) => {
899
+ if (!part) return null;
900
+ const key = `${keyPrefix}-inline-${index}`;
901
+ if (part.startsWith("`") && part.endsWith("`")) {
902
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { className: "hsk-markdown-code", children: part.slice(1, -1) }, key);
903
+ }
904
+ if (part.startsWith("**") && part.endsWith("**")) {
905
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: parseInline(part.slice(2, -2), key) }, key);
906
+ }
907
+ const linkMatch = part.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
908
+ if (linkMatch) {
909
+ const url = linkMatch[2];
910
+ const isSafeUrl = /^(https?|mailto|tel):/i.test(url) || url.startsWith("/");
911
+ if (isSafeUrl) {
912
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: "hsk-markdown-link", children: parseInline(linkMatch[1], key) }, key);
913
+ }
914
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: parseInline(linkMatch[1], key) }, key);
915
+ }
916
+ return part;
917
+ });
918
+ };
919
+ function renderMarkdown(content) {
920
+ const lines = content.split("\n");
921
+ const elements = [];
922
+ let i = 0;
923
+ while (i < lines.length) {
924
+ const line = lines[i];
925
+ const key = `md-line-${i}`;
926
+ if (!line.trim()) {
927
+ i++;
928
+ continue;
929
+ }
930
+ const headerMatch = line.match(/^(#{1,3})\s+(.*)/);
931
+ if (headerMatch) {
932
+ const level = headerMatch[1].length;
933
+ const Tag = `h${level + 3}`;
934
+ elements.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tag, { className: `hsk-markdown-h${level}`, children: parseInline(headerMatch[2], key) }, key));
935
+ i++;
936
+ continue;
937
+ }
938
+ if (line.match(/^[-*]\s+/)) {
939
+ const listItems = [];
940
+ while (i < lines.length && lines[i].match(/^[-*]\s+/)) {
941
+ const itemText = lines[i].replace(/^[-*]\s+/, "");
942
+ listItems.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: parseInline(itemText, `li-${i}`) }, `li-${i}`));
943
+ i++;
944
+ }
945
+ elements.push(/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "hsk-markdown-list", children: listItems }, `ul-${key}`));
946
+ continue;
947
+ }
948
+ if (line.trim().startsWith("|")) {
949
+ const tableRows = [];
950
+ let isHeader = true;
951
+ while (i < lines.length && lines[i].trim().startsWith("|")) {
952
+ const rowLine = lines[i].trim();
953
+ if (rowLine.match(/^\|[-:| ]+\|$/)) {
954
+ i++;
955
+ isHeader = false;
956
+ continue;
957
+ }
958
+ const cells = rowLine.split("|").slice(1, -1).map((c) => c.trim());
959
+ const Tag = isHeader ? "th" : "td";
960
+ tableRows.push(
961
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tr", { children: cells.map((cell, cIdx) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tag, { children: parseInline(cell, `td-${i}-${cIdx}`) }, `td-${i}-${cIdx}`)) }, `tr-${i}`)
962
+ );
963
+ i++;
964
+ }
965
+ elements.push(
966
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-table-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("table", { className: "hsk-markdown-table", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: tableRows }) }) }, `table-wrapper-${key}`)
967
+ );
968
+ continue;
969
+ }
970
+ elements.push(
971
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "hsk-markdown-p", children: parseInline(line, key) }, key)
972
+ );
973
+ i++;
974
+ }
975
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: elements });
976
+ }
977
+
978
+ // src/components/ChatWidget.tsx
979
+ var import_jsx_runtime5 = require("react/jsx-runtime");
980
+ var SparkleIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" }) });
981
+ var ArrowUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
982
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m5 12 7-7 7 7" }),
983
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 19V5" })
984
+ ] });
985
+ function SourceCard({ source, defaultCurrency, onSelect }) {
1244
986
  var _a;
1245
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-source-card", onClick: () => onSelect == null ? void 0 : onSelect(source), children: [
1246
- source.image && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: source.image, alt: source.name, className: "hsk-source-img" }),
1247
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
1248
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-source-name", children: source.name }),
1249
- source.price && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-source-price", children: [
1250
- (_a = source.currency) != null ? _a : "KES",
987
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-source-card", onClick: () => onSelect == null ? void 0 : onSelect(source), children: [
988
+ source.image && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: source.image, alt: source.name, className: "hsk-source-img" }),
989
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
990
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-source-name", children: source.name }),
991
+ source.price && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-source-price", children: [
992
+ (_a = source.currency) != null ? _a : defaultCurrency,
1251
993
  " ",
1252
994
  source.price
1253
995
  ] })
1254
996
  ] })
1255
997
  ] });
1256
998
  }
1257
- function ChatWidget({ placeholder = "Ask about anything in our store\u2026", title = "AI Shopping Assistant", className, onSelectSource }) {
999
+ function ChatWidget({
1000
+ title = "AI Shopping Assistant",
1001
+ placeholder = "Ask about anything in our store\u2026",
1002
+ emptyStateText = "Ask me anything about our products",
1003
+ emptyStateSuggestions = '"Find me headphones under KSh 5,000" \xB7 "Gift ideas"',
1004
+ defaultCurrency = "KES",
1005
+ className,
1006
+ theme,
1007
+ classNames = {},
1008
+ onSelectSource
1009
+ }) {
1258
1010
  const { messages, sources, loading, error, send, reset } = useChat();
1259
1011
  const [input, setInput] = (0, import_react9.useState)("");
1260
1012
  const bottomRef = (0, import_react9.useRef)(null);
@@ -1282,581 +1034,93 @@ function ChatWidget({ placeholder = "Ask about anything in our store\u2026", tit
1282
1034
  t.style.height = "auto";
1283
1035
  t.style.height = Math.min(t.scrollHeight, 120) + "px";
1284
1036
  };
1285
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1286
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: S }),
1287
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `hsk-chat-widget ${className != null ? className : ""}`, children: [
1288
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-chat-header", children: [
1289
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: 16, color: "#ff6a33" }, children: "\u2726" }),
1290
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-chat-title", children: title }),
1291
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hsk-chat-badge", children: "AI" }),
1292
- messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "hsk-chat-reset", onClick: reset, style: { marginLeft: "auto" }, children: "Clear" })
1293
- ] }),
1294
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-chat-messages", children: [
1295
- messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-chat-empty", children: [
1296
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-chat-empty-icon", children: "\u2726" }),
1297
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: "Ask me anything about our products" }),
1298
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 12, color: "#444", marginTop: 4 }, children: '"Find me headphones under KSh 5,000" \xB7 "Gift ideas for a chef"' })
1299
- ] }) : messages.map((msg, idx) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
1300
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `hsk-msg-row ${msg.role}`, children: [
1301
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `hsk-msg-avatar ${msg.role === "assistant" ? "ai" : "user"}`, children: msg.role === "assistant" ? "\u2726" : "\u2191" }),
1302
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `hsk-msg-bubble ${msg.role === "assistant" ? "ai" : "user"}`, children: msg.content })
1037
+ const customStyles = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, (theme == null ? void 0 : theme.primaryColor) && { "--hsk-primary": theme.primaryColor }), (theme == null ? void 0 : theme.backgroundColor) && { "--hsk-bg": theme.backgroundColor }), (theme == null ? void 0 : theme.textColor) && { "--hsk-text": theme.textColor }), (theme == null ? void 0 : theme.fontFamily) && { "--hsk-font": theme.fontFamily });
1038
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1039
+ "div",
1040
+ {
1041
+ className: `hsk-chat-widget ${classNames.root || ""} ${className || ""}`,
1042
+ style: customStyles,
1043
+ children: [
1044
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `hsk-chat-header ${classNames.header || ""}`, children: [
1045
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-chat-header-icon", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) }),
1046
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-chat-title", children: title }),
1047
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-chat-badge", children: "AI" }),
1048
+ messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "hsk-chat-reset", onClick: reset, style: { marginLeft: "auto" }, children: "Clear" })
1049
+ ] }),
1050
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-chat-messages", children: [
1051
+ messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-chat-empty", children: [
1052
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-chat-empty-icon", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) }),
1053
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: emptyStateText }),
1054
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-chat-empty-suggestions", children: emptyStateSuggestions })
1055
+ ] }) : messages.map((msg, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1056
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `hsk-msg-row ${msg.role}`, children: [
1057
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `hsk-msg-avatar ${msg.role === "assistant" ? "ai" : "user"}`, children: msg.role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) : "U" }),
1058
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `hsk-msg-bubble ${msg.role} ${classNames.messageBubble || ""}`, children: renderMarkdown(msg.content) })
1059
+ ] }),
1060
+ msg.role === "assistant" && idx === messages.length - 1 && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-sources-container", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-sources", children: sources.map((src, si) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SourceCard, { source: src, defaultCurrency, onSelect: onSelectSource }, si)) }) })
1061
+ ] }, idx)),
1062
+ loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-msg-row", children: [
1063
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-msg-avatar ai", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) }),
1064
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-typing", children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-typing-dot" }),
1066
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-typing-dot" }),
1067
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-typing-dot" })
1068
+ ] })
1303
1069
  ] }),
1304
- msg.role === "assistant" && idx === messages.length - 1 && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { marginLeft: 36 }, 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, onSelect: onSelectSource }, si)) }) })
1305
- ] }, idx)),
1306
- loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-msg-row", children: [
1307
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-msg-avatar ai", children: "\u2726" }),
1308
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-typing", children: [
1309
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-typing-dot" }),
1310
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-typing-dot" }),
1311
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hsk-typing-dot" })
1312
- ] })
1070
+ error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-chat-error", children: error }),
1071
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: bottomRef })
1313
1072
  ] }),
1314
- error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 12, color: "#ef4444", textAlign: "center", padding: 8 }, children: error }),
1315
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: bottomRef })
1316
- ] }),
1317
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hsk-chat-input-area", children: [
1318
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1319
- "textarea",
1320
- {
1321
- ref: textareaRef,
1322
- className: "hsk-chat-input",
1323
- value: input,
1324
- onChange: handleInput,
1325
- onKeyDown: handleKey,
1326
- placeholder,
1327
- rows: 1,
1328
- disabled: loading
1329
- }
1330
- ),
1331
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1332
- "button",
1333
- {
1334
- className: "hsk-chat-send",
1335
- onClick: handleSend,
1336
- disabled: !input.trim() || loading,
1337
- "aria-label": "Send",
1338
- children: "\u2191"
1339
- }
1340
- )
1341
- ] })
1342
- ] })
1343
- ] });
1073
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-chat-input-area", children: [
1074
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1075
+ "textarea",
1076
+ {
1077
+ ref: textareaRef,
1078
+ className: `hsk-chat-input ${classNames.input || ""}`,
1079
+ value: input,
1080
+ onChange: handleInput,
1081
+ onKeyDown: handleKey,
1082
+ placeholder,
1083
+ rows: 1,
1084
+ disabled: loading
1085
+ }
1086
+ ),
1087
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1088
+ "button",
1089
+ {
1090
+ className: "hsk-chat-send",
1091
+ onClick: handleSend,
1092
+ disabled: !input.trim() || loading,
1093
+ "aria-label": "Send message",
1094
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArrowUpIcon, {})
1095
+ }
1096
+ )
1097
+ ] })
1098
+ ]
1099
+ }
1100
+ );
1344
1101
  }
1345
1102
 
1346
1103
  // src/components/AIChatButton.tsx
1347
1104
  var import_react10 = require("react");
1348
1105
  var import_react_dom2 = require("react-dom");
1349
- var import_jsx_runtime5 = require("react/jsx-runtime");
1350
- var CSS3 = `
1351
- /* \u2500\u2500 Trigger button \u2500\u2500 */
1352
- .hsk-cb-btn {
1353
- display: inline-flex;
1354
- align-items: center;
1355
- gap: 7px;
1356
- padding: 8px 16px;
1357
- border-radius: 9999px;
1358
- border: 1px solid rgba(255,106,51,.4);
1359
- background: rgba(255,106,51,.1);
1360
- color: #ff6a33;
1361
- font-size: 13px;
1362
- font-weight: 600;
1363
- cursor: pointer;
1364
- transition: background .15s, border-color .15s, transform .12s, box-shadow .15s;
1365
- font-family: inherit;
1366
- white-space: nowrap;
1367
- }
1368
- .hsk-cb-btn:hover {
1369
- background: rgba(255,106,51,.18);
1370
- border-color: rgba(255,106,51,.7);
1371
- box-shadow: 0 4px 16px rgba(255,106,51,.2);
1372
- }
1373
- .hsk-cb-btn:active { transform: scale(.95); }
1374
- .hsk-cb-btn-icon { font-size: 15px; line-height: 1; }
1375
-
1376
- /* \u2500\u2500 Full-screen overlay \u2500\u2500 */
1377
- .hsk-cb-overlay {
1378
- position: fixed;
1379
- inset: 0;
1380
- z-index: 99999;
1381
- display: flex;
1382
- flex-direction: column;
1383
- animation: hsk-overlay-in .2s ease-out both;
1384
- }
1385
- @keyframes hsk-overlay-in {
1386
- from { opacity: 0; }
1387
- to { opacity: 1; }
1388
- }
1389
-
1390
- /* \u2500\u2500 Panel (Claude-style, centered column) \u2500\u2500 */
1391
- .hsk-cb-panel {
1392
- position: relative;
1393
- display: flex;
1394
- flex-direction: column;
1395
- height: 100%;
1396
- max-width: 780px;
1397
- width: 100%;
1398
- margin: 0 auto;
1399
- animation: hsk-panel-in .28s cubic-bezier(.34,1.2,.64,1) both;
1400
- }
1401
- @keyframes hsk-panel-in {
1402
- from { opacity: 0; transform: translateY(24px); }
1403
- to { opacity: 1; transform: translateY(0); }
1404
- }
1405
-
1406
- /* \u2500\u2500 Top bar \u2500\u2500 */
1407
- .hsk-cb-topbar {
1408
- display: flex;
1409
- align-items: center;
1410
- justify-content: space-between;
1411
- padding: 20px 28px 12px;
1412
- flex-shrink: 0;
1413
- }
1414
- .hsk-cb-topbar-left {
1415
- display: flex;
1416
- align-items: center;
1417
- gap: 10px;
1418
- }
1419
- .hsk-cb-topbar-icon {
1420
- font-size: 22px;
1421
- color: #ff6a33;
1422
- line-height: 1;
1423
- animation: hsk-sparkle-spin 6s linear infinite;
1424
- }
1425
- @keyframes hsk-sparkle-spin {
1426
- 0%,100% { transform: rotate(0deg) scale(1); }
1427
- 25% { transform: rotate(15deg) scale(1.1); }
1428
- 75% { transform: rotate(-10deg) scale(.95); }
1429
- }
1430
- .hsk-cb-topbar-title {
1431
- font-size: 16px;
1432
- font-weight: 700;
1433
- color: var(--hsk-chat-text, #111);
1434
- letter-spacing: -.01em;
1435
- }
1436
- .hsk-cb-topbar-sub {
1437
- font-size: 12px;
1438
- color: var(--hsk-chat-muted, #888);
1439
- margin-top: 2px;
1440
- }
1441
- .hsk-cb-topbar-actions {
1442
- display: flex;
1443
- align-items: center;
1444
- gap: 8px;
1445
- }
1446
- .hsk-cb-topbar-btn {
1447
- height: 34px;
1448
- padding: 0 14px;
1449
- border-radius: 8px;
1450
- border: 1px solid var(--hsk-chat-divide, rgba(0,0,0,.1));
1451
- background: none;
1452
- color: var(--hsk-chat-muted, #888);
1453
- font-size: 12px;
1454
- font-weight: 500;
1455
- cursor: pointer;
1456
- transition: all .15s;
1457
- font-family: inherit;
1458
- }
1459
- .hsk-cb-topbar-btn:hover {
1460
- border-color: #ff6a33;
1461
- color: #ff6a33;
1462
- }
1463
- .hsk-cb-close {
1464
- width: 34px; height: 34px;
1465
- border-radius: 8px;
1466
- border: 1px solid var(--hsk-chat-divide, rgba(0,0,0,.1));
1467
- background: none;
1468
- color: var(--hsk-chat-muted, #888);
1469
- cursor: pointer;
1470
- font-size: 20px;
1471
- display: flex; align-items: center; justify-content: center;
1472
- transition: all .15s;
1473
- flex-shrink: 0;
1474
- font-family: inherit;
1475
- line-height: 1;
1476
- }
1477
- .hsk-cb-close:hover { border-color: #ff6a33; color: #ff6a33; }
1478
-
1479
- /* \u2500\u2500 Messages scroll area \u2500\u2500 */
1480
- .hsk-cb-msgs {
1481
- flex: 1;
1482
- overflow-y: auto;
1483
- padding: 8px 28px 0;
1484
- display: flex;
1485
- flex-direction: column;
1486
- gap: 0;
1487
- scroll-behavior: smooth;
1488
- scrollbar-width: thin;
1489
- scrollbar-color: var(--hsk-chat-divide, rgba(0,0,0,.1)) transparent;
1490
- }
1491
-
1492
- /* \u2500\u2500 Empty / welcome state \u2500\u2500 */
1493
- .hsk-cb-empty {
1494
- flex: 1;
1495
- display: flex;
1496
- flex-direction: column;
1497
- align-items: center;
1498
- justify-content: center;
1499
- gap: 20px;
1500
- padding: 60px 32px;
1501
- text-align: center;
1502
- }
1503
- .hsk-cb-empty-icon {
1504
- font-size: 48px;
1505
- color: #ff6a33;
1506
- animation: hsk-sparkle-spin 4s linear infinite;
1507
- }
1508
- .hsk-cb-empty-title {
1509
- font-size: 26px;
1510
- font-weight: 700;
1511
- color: var(--hsk-chat-text, #111);
1512
- letter-spacing: -.02em;
1513
- }
1514
- .hsk-cb-empty-sub {
1515
- font-size: 14px;
1516
- color: var(--hsk-chat-muted, #888);
1517
- line-height: 1.7;
1518
- max-width: 380px;
1519
- }
1520
- .hsk-cb-chips {
1521
- display: flex;
1522
- flex-wrap: wrap;
1523
- gap: 8px;
1524
- justify-content: center;
1525
- margin-top: 4px;
1526
- }
1527
- .hsk-cb-chip {
1528
- padding: 8px 16px;
1529
- border-radius: 9999px;
1530
- border: 1px solid var(--hsk-chat-divide, rgba(0,0,0,.1));
1531
- background: var(--hsk-chat-source-bg, rgba(0,0,0,.03));
1532
- color: var(--hsk-chat-text, #333);
1533
- font-size: 13px;
1534
- cursor: pointer;
1535
- transition: all .15s;
1536
- font-family: inherit;
1537
- }
1538
- .hsk-cb-chip:hover {
1539
- border-color: #ff6a33;
1540
- color: #ff6a33;
1541
- background: rgba(255,106,51,.06);
1542
- }
1543
-
1544
- /* \u2500\u2500 Message rows \u2500\u2500 */
1545
- .hsk-cb-msg-group {
1546
- padding: 20px 0;
1547
- border-bottom: 1px solid var(--hsk-chat-divide, rgba(0,0,0,.05));
1548
- animation: hsk-msg-in .22s ease-out both;
1549
- }
1550
- .hsk-cb-msg-group:last-child { border-bottom: none; }
1551
- @keyframes hsk-msg-in {
1552
- from { opacity: 0; transform: translateY(10px); }
1553
- to { opacity: 1; transform: translateY(0); }
1554
- }
1555
-
1556
- /* User message */
1557
- .hsk-cb-user-msg {
1558
- display: flex;
1559
- justify-content: flex-end;
1560
- margin-bottom: 20px;
1561
- }
1562
- .hsk-cb-user-bubble {
1563
- background: #ff6a33;
1564
- color: #fff;
1565
- padding: 12px 20px;
1566
- border-radius: 22px 22px 6px 22px;
1567
- font-size: 15px;
1568
- line-height: 1.6;
1569
- max-width: 72%;
1570
- font-weight: 500;
1571
- }
1572
-
1573
- /* AI message - no bubble, just clean text like Claude */
1574
- .hsk-cb-ai-msg {
1575
- display: flex;
1576
- align-items: flex-start;
1577
- gap: 14px;
1578
- }
1579
- .hsk-cb-ai-icon {
1580
- width: 28px; height: 28px;
1581
- border-radius: 50%;
1582
- background: rgba(255,106,51,.12);
1583
- border: 1px solid rgba(255,106,51,.25);
1584
- color: #ff6a33;
1585
- font-size: 13px;
1586
- display: flex; align-items: center; justify-content: center;
1587
- flex-shrink: 0;
1588
- margin-top: 2px;
1589
- }
1590
- .hsk-cb-ai-body { flex: 1; min-width: 0; }
1591
- .hsk-cb-ai-text {
1592
- font-size: 15px;
1593
- line-height: 1.75;
1594
- color: var(--hsk-chat-text, #111);
1595
- white-space: pre-wrap;
1596
- }
1597
-
1598
- /* \u2500\u2500 Sources horizontal carousel \u2500\u2500 */
1599
- .hsk-cb-sources-wrap {
1600
- position: relative;
1601
- margin-top: 20px;
1602
- }
1603
- .hsk-cb-sources {
1604
- display: flex;
1605
- flex-direction: row;
1606
- gap: 14px;
1607
- overflow-x: auto;
1608
- scroll-snap-type: x mandatory;
1609
- scrollbar-width: none;
1610
- -ms-overflow-style: none;
1611
- padding-bottom: 4px;
1612
- }
1613
- .hsk-cb-sources::-webkit-scrollbar { display: none; }
1614
- /* Feathered right edge */
1615
- .hsk-cb-sources-fade {
1616
- position: absolute;
1617
- right: 0; top: 0; bottom: 4px;
1618
- width: 90px;
1619
- pointer-events: none;
1620
- }
1621
- /* Scroll-next pill */
1622
- .hsk-cb-sources-next {
1623
- position: absolute;
1624
- right: 10px;
1625
- top: 50%;
1626
- transform: translateY(-50%);
1627
- width: 30px; height: 30px;
1628
- border-radius: 50%;
1629
- border: 1px solid var(--hsk-chat-divide, rgba(0,0,0,.12));
1630
- background: var(--hsk-chat-bg, #0e0e0f);
1631
- color: var(--hsk-chat-text, #eee);
1632
- cursor: pointer;
1633
- font-size: 16px;
1634
- display: flex; align-items: center; justify-content: center;
1635
- box-shadow: 0 2px 12px rgba(0,0,0,.2);
1636
- transition: all .15s;
1637
- z-index: 3;
1638
- font-family: inherit;
1639
- line-height: 1;
1640
- }
1641
- .hsk-cb-sources-next:hover { border-color: #ff6a33; color: #ff6a33; }
1642
- /* Card: no border, no bg, no radius \u2014 clean dark canvas */
1643
- .hsk-cb-source {
1644
- flex: 0 0 188px;
1645
- scroll-snap-align: start;
1646
- border-radius: 0;
1647
- border: none;
1648
- background: transparent;
1649
- cursor: pointer;
1650
- transition: transform .14s, opacity .14s;
1651
- animation: hsk-card-in .26s ease-out both;
1652
- overflow: visible;
1653
- }
1654
- @keyframes hsk-card-in {
1655
- from { opacity: 0; transform: translateX(16px); }
1656
- to { opacity: 1; transform: none; }
1657
- }
1658
- .hsk-cb-source:hover { transform: translateY(-3px); opacity: .92; }
1659
- .hsk-cb-src-imgwrap {
1660
- width: 188px;
1661
- height: 188px;
1662
- overflow: hidden;
1663
- border-radius: 0;
1664
- display: block;
1665
- }
1666
- .hsk-cb-src-imgwrap img {
1667
- width: 100%; height: 100%;
1668
- object-fit: cover;
1669
- transition: transform .22s;
1670
- display: block;
1671
- }
1672
- .hsk-cb-source:hover .hsk-cb-src-imgwrap img { transform: scale(1.05); }
1673
- .hsk-cb-src-imgwrap-empty {
1674
- width: 188px;
1675
- height: 188px;
1676
- background: var(--hsk-chat-divide, rgba(255,255,255,.06));
1677
- display: flex; align-items: center; justify-content: center;
1678
- color: var(--hsk-chat-muted, #555);
1679
- font-size: 32px;
1680
- }
1681
- .hsk-cb-src-info {
1682
- padding: 8px 2px 0;
1683
- }
1684
- .hsk-cb-src-name {
1685
- font-size: 13px;
1686
- font-weight: 600;
1687
- color: var(--hsk-chat-text, #eee);
1688
- line-height: 1.4;
1689
- display: -webkit-box;
1690
- -webkit-line-clamp: 2;
1691
- -webkit-box-orient: vertical;
1692
- overflow: hidden;
1693
- }
1694
- .hsk-cb-src-price {
1695
- font-size: 13px;
1696
- color: #ff6a33;
1697
- font-weight: 700;
1698
- margin-top: 3px;
1699
- }
1700
-
1701
- /* \u2500\u2500 Selected product inline card \u2500\u2500 */
1702
- .hsk-cb-selected-product {
1703
- display: flex;
1704
- align-items: flex-start;
1705
- gap: 14px;
1706
- margin-top: 16px;
1707
- padding: 14px;
1708
- border: 1px solid var(--hsk-chat-divide, rgba(255,255,255,.08));
1709
- border-left: 3px solid #ff6a33;
1710
- background: var(--hsk-chat-source-bg, rgba(255,255,255,.03));
1711
- cursor: pointer;
1712
- transition: border-color .15s;
1713
- animation: hsk-msg-in .2s ease-out both;
1714
- }
1715
- .hsk-cb-selected-product:hover { border-left-color: rgba(255,106,51,.6); }
1716
- .hsk-cb-selected-img {
1717
- width: 64px; height: 64px;
1718
- object-fit: cover;
1719
- flex-shrink: 0;
1720
- }
1721
- .hsk-cb-selected-info { flex: 1; min-width: 0; }
1722
- .hsk-cb-selected-name {
1723
- font-size: 13px; font-weight: 700;
1724
- color: var(--hsk-chat-text, #eee);
1725
- margin-bottom: 3px;
1726
- }
1727
- .hsk-cb-selected-price {
1728
- font-size: 13px; color: #ff6a33; font-weight: 700;
1729
- }
1730
-
1731
- /* \u2500\u2500 Typing indicator \u2500\u2500 */
1732
- .hsk-cb-typing-row {
1733
- display: flex;
1734
- align-items: flex-start;
1735
- gap: 14px;
1736
- padding: 20px 0;
1737
- }
1738
- .hsk-cb-typing {
1739
- display: flex;
1740
- gap: 5px;
1741
- padding: 14px 18px;
1742
- }
1743
- .hsk-cb-dot {
1744
- width: 7px; height: 7px;
1745
- border-radius: 50%;
1746
- background: var(--hsk-chat-muted, #ccc);
1747
- animation: hsk-dot-pulse 1.2s ease-in-out infinite;
1748
- }
1749
- .hsk-cb-dot:nth-child(2) { animation-delay: .18s; }
1750
- .hsk-cb-dot:nth-child(3) { animation-delay: .36s; }
1751
- @keyframes hsk-dot-pulse {
1752
- 0%,100% { opacity: .3; transform: scale(.75); }
1753
- 50% { opacity: 1; transform: scale(1); }
1754
- }
1755
-
1756
- /* \u2500\u2500 Input area \u2500\u2500 */
1757
- .hsk-cb-input-wrap {
1758
- padding: 16px 28px 28px;
1759
- flex-shrink: 0;
1760
- }
1761
- .hsk-cb-input-box {
1762
- display: flex;
1763
- align-items: flex-end;
1764
- gap: 10px;
1765
- background: var(--hsk-chat-input-bg, rgba(0,0,0,.04));
1766
- border: 1.5px solid var(--hsk-chat-divide, rgba(0,0,0,.1));
1767
- border-radius: 18px;
1768
- padding: 14px 14px 14px 20px;
1769
- transition: border-color .15s, box-shadow .15s;
1770
- }
1771
- .hsk-cb-input-box:focus-within {
1772
- border-color: #ff6a33;
1773
- box-shadow: 0 0 0 3px rgba(255,106,51,.1);
1774
- }
1775
- .hsk-cb-textarea {
1776
- flex: 1;
1777
- background: transparent;
1778
- border: none;
1779
- outline: none;
1780
- resize: none;
1781
- font-size: 15px;
1782
- color: var(--hsk-chat-text, #111);
1783
- min-height: 24px;
1784
- max-height: 140px;
1785
- line-height: 1.55;
1786
- font-family: inherit;
1787
- }
1788
- .hsk-cb-textarea::placeholder { color: var(--hsk-chat-muted, #aaa); }
1789
- .hsk-cb-send {
1790
- width: 38px; height: 38px;
1791
- border-radius: 10px;
1792
- background: #ff6a33;
1793
- border: none;
1794
- color: #fff;
1795
- cursor: pointer;
1796
- font-size: 18px;
1797
- display: flex; align-items: center; justify-content: center;
1798
- flex-shrink: 0;
1799
- transition: opacity .15s, transform .1s, background .15s;
1800
- font-family: inherit;
1801
- }
1802
- .hsk-cb-send:hover { opacity: .88; }
1803
- .hsk-cb-send:active { transform: scale(.9); }
1804
- .hsk-cb-send:disabled { opacity: .3; cursor: not-allowed; background: var(--hsk-chat-muted, #ccc); }
1805
- .hsk-cb-hint {
1806
- text-align: center;
1807
- font-size: 11px;
1808
- color: var(--hsk-chat-muted, #bbb);
1809
- margin-top: 10px;
1810
- }
1811
-
1812
- /* \u2500\u2500 Error \u2500\u2500 */
1813
- .hsk-cb-error {
1814
- margin: 8px 0;
1815
- padding: 10px 14px;
1816
- border-radius: 10px;
1817
- background: rgba(239,68,68,.08);
1818
- border: 1px solid rgba(239,68,68,.2);
1819
- color: #ef4444;
1820
- font-size: 13px;
1821
- }
1822
-
1823
- /* \u2500\u2500 Dark mode \u2500\u2500 */
1824
- @media (prefers-color-scheme: dark) {
1825
- .hsk-cb-overlay {
1826
- --hsk-chat-bg: #0e0e0f;
1827
- --hsk-chat-text: #f0efed;
1828
- --hsk-chat-muted: #555;
1829
- --hsk-chat-divide: rgba(255,255,255,.07);
1830
- --hsk-chat-input-bg: rgba(255,255,255,.05);
1831
- --hsk-chat-source-bg: rgba(255,255,255,.04);
1832
- --hsk-fade-bg: #0e0e0f;
1833
- }
1834
- .hsk-cb-overlay {
1835
- background: rgba(0,0,0,.92) !important;
1836
- }
1837
- }
1838
- @media (prefers-color-scheme: light) {
1839
- .hsk-cb-overlay {
1840
- --hsk-chat-bg: #fafafa;
1841
- --hsk-chat-text: #111;
1842
- --hsk-chat-muted: #999;
1843
- --hsk-chat-divide: rgba(0,0,0,.08);
1844
- --hsk-chat-input-bg: rgba(0,0,0,.04);
1845
- --hsk-chat-source-bg: rgba(0,0,0,.025);
1846
- --hsk-fade-bg: #fafafa;
1847
- }
1848
- .hsk-cb-overlay {
1849
- background: rgba(240,240,244,.88) !important;
1850
- }
1851
- }
1852
- `;
1853
- var CHIPS = [
1106
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1107
+ var SparkleIcon3 = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" }) });
1108
+ var ArrowUpIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1109
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m5 12 7-7 7 7" }),
1110
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 19V5" })
1111
+ ] });
1112
+ var CloseIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1113
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1114
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1115
+ ] });
1116
+ var ChevronRightIcon = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m9 18 6-6-6-6" }) });
1117
+ var DEFAULT_CHIPS = [
1854
1118
  "Cheapest smartphone",
1855
1119
  "Smart TV under KSh 20,000",
1856
1120
  "Noise-cancelling headphones",
1857
1121
  "Best laptop for students"
1858
1122
  ];
1859
- function SourcesCarousel({ sources, onSelectSource }) {
1123
+ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
1860
1124
  const railRef = (0, import_react10.useRef)(null);
1861
1125
  const [showNext, setShowNext] = (0, import_react10.useState)(false);
1862
1126
  const measure = (0, import_react10.useCallback)(() => {
@@ -1881,21 +1145,21 @@ function SourcesCarousel({ sources, onSelectSource }) {
1881
1145
  var _a;
1882
1146
  (_a = railRef.current) == null ? void 0 : _a.scrollBy({ left: 170, behavior: "smooth" });
1883
1147
  };
1884
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-sources-wrap", children: [
1885
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-sources", ref: railRef, children: sources.map((src, si) => {
1148
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-sources-wrap", children: [
1149
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-sources", ref: railRef, children: sources.map((src, si) => {
1886
1150
  var _a;
1887
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1151
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1888
1152
  "div",
1889
1153
  {
1890
1154
  className: "hsk-cb-source",
1891
1155
  style: { animationDelay: `${si * 50}ms` },
1892
1156
  onClick: () => onSelectSource == null ? void 0 : onSelectSource(src),
1893
1157
  children: [
1894
- 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: "\u2726" }),
1895
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-src-info", children: [
1896
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-src-name", children: src.name }),
1897
- src.price && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-src-price", children: [
1898
- (_a = src.currency) != null ? _a : "KES",
1158
+ src.image ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-src-imgwrap", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: src.image, alt: src.name, loading: "lazy" }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-src-imgwrap-empty", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
1159
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-src-info", children: [
1160
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-src-name", children: src.name }),
1161
+ src.price && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-src-price", children: [
1162
+ (_a = src.currency) != null ? _a : defaultCurrency,
1899
1163
  " ",
1900
1164
  parseFloat(src.price.replace(/[^0-9.]/g, "") || "0").toLocaleString()
1901
1165
  ] })
@@ -1905,15 +1169,15 @@ function SourcesCarousel({ sources, onSelectSource }) {
1905
1169
  si
1906
1170
  );
1907
1171
  }) }),
1908
- showNext && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1909
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1172
+ showNext && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1173
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1910
1174
  "div",
1911
1175
  {
1912
1176
  className: "hsk-cb-sources-fade",
1913
1177
  style: { background: "linear-gradient(to right, transparent, var(--hsk-fade-bg, #0e0e0f))" }
1914
1178
  }
1915
1179
  ),
1916
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "hsk-cb-sources-next", onClick: scrollNext, "aria-label": "See more", children: "\u203A" })
1180
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-sources-next", onClick: scrollNext, "aria-label": "See more", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronRightIcon, {}) })
1917
1181
  ] })
1918
1182
  ] });
1919
1183
  }
@@ -1923,7 +1187,11 @@ function ChatModal({
1923
1187
  backdropColor,
1924
1188
  backdropBlur,
1925
1189
  onClose,
1926
- onSelectSource
1190
+ onSelectSource,
1191
+ defaultCurrency = "KES",
1192
+ chips = DEFAULT_CHIPS,
1193
+ theme,
1194
+ classNames = {}
1927
1195
  }) {
1928
1196
  var _a, _b;
1929
1197
  const { messages, sources, loading, error, send, reset } = useChat();
@@ -1946,7 +1214,7 @@ function ChatModal({
1946
1214
  var _a2;
1947
1215
  setSelectedProduct(src);
1948
1216
  onSelectSource == null ? void 0 : onSelectSource(src);
1949
- const q = `Tell me more about the ${src.name}${src.price ? ` (${(_a2 = src.currency) != null ? _a2 : "KES"} ${src.price})` : ""} \u2014 what are its key specs, who is it best for, and is it worth buying?`;
1217
+ const q = `Tell me more about the ${src.name}${src.price ? ` (${(_a2 = src.currency) != null ? _a2 : defaultCurrency} ${src.price})` : ""} \u2014 what are its key specs, who is it best for, and is it worth buying?`;
1950
1218
  send(q);
1951
1219
  };
1952
1220
  const handleSend = async (text) => {
@@ -1972,124 +1240,123 @@ function ChatModal({
1972
1240
  t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
1973
1241
  };
1974
1242
  const blurVal = typeof backdropBlur === "number" ? `${backdropBlur}px` : backdropBlur != null ? backdropBlur : "20px";
1975
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1976
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: CSS3 }),
1977
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1978
- "div",
1979
- {
1980
- className: "hsk-cb-overlay",
1981
- onClick: onClose,
1982
- style: __spreadValues({
1983
- backdropFilter: `blur(${blurVal})`,
1984
- WebkitBackdropFilter: `blur(${blurVal})`
1985
- }, backdropColor ? { background: backdropColor } : {}),
1986
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-panel", onClick: (e) => e.stopPropagation(), children: [
1987
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-topbar", children: [
1988
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-topbar-left", children: [
1989
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-cb-topbar-icon", children: "\u2726" }),
1990
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1991
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-topbar-title", children: title }),
1992
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-topbar-sub", children: "Powered by Huskel AI \xB7 searches the whole catalogue" })
1993
- ] })
1994
- ] }),
1995
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-topbar-actions", children: [
1996
- messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "hsk-cb-topbar-btn", onClick: reset, children: "Clear chat" }),
1997
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "hsk-cb-close", onClick: onClose, "aria-label": "Close", children: "\xD7" })
1243
+ const customStyles = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, (theme == null ? void 0 : theme.primaryColor) && { "--hsk-primary": theme.primaryColor }), (theme == null ? void 0 : theme.backgroundColor) && { "--hsk-bg": theme.backgroundColor }), (theme == null ? void 0 : theme.textColor) && { "--hsk-text": theme.textColor }), (theme == null ? void 0 : theme.fontFamily) && { "--hsk-font": theme.fontFamily });
1244
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1245
+ "div",
1246
+ {
1247
+ className: `hsk-cb-overlay ${classNames.overlay || ""}`,
1248
+ onClick: onClose,
1249
+ style: __spreadValues(__spreadValues({
1250
+ backdropFilter: `blur(${blurVal})`,
1251
+ WebkitBackdropFilter: `blur(${blurVal})`
1252
+ }, backdropColor ? { background: backdropColor } : {}), customStyles),
1253
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `hsk-cb-panel ${classNames.panel || ""}`, onClick: (e) => e.stopPropagation(), children: [
1254
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-topbar", children: [
1255
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-topbar-left", children: [
1256
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "hsk-cb-topbar-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
1257
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1258
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-topbar-title", children: title }),
1259
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-topbar-sub", children: "Powered by Huskel AI \xB7 searches the whole catalogue" })
1998
1260
  ] })
1999
1261
  ] }),
2000
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-msgs", children: [
2001
- messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-empty", children: [
2002
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-empty-icon", children: "\u2726" }),
2003
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-empty-title", children: "What can I help you find?" }),
2004
- /* @__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." }),
2005
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-chips", children: CHIPS.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2006
- "button",
2007
- {
2008
- className: "hsk-cb-chip",
2009
- onClick: () => handleSend(chip),
2010
- children: chip
2011
- },
2012
- chip
2013
- )) })
2014
- ] }) : messages.map((msg, idx) => {
2015
- const isLast = idx === messages.length - 1;
2016
- const isUser = msg.role === "user";
2017
- 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: [
2018
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-ai-icon", children: "\u2726" }),
2019
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-ai-body", children: [
2020
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-ai-text", children: msg.content }),
2021
- isLast && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2022
- SourcesCarousel,
2023
- {
2024
- sources,
2025
- onSelectSource: handleSourceClick
2026
- }
2027
- )
2028
- ] })
2029
- ] }) }, idx);
2030
- }),
2031
- selectedProduct && loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2032
- "div",
1262
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-topbar-actions", children: [
1263
+ messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-topbar-btn", onClick: reset, children: "Clear chat" }),
1264
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CloseIcon2, {}) })
1265
+ ] })
1266
+ ] }),
1267
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-msgs", children: [
1268
+ messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-empty", children: [
1269
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-empty-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
1270
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-empty-title", children: "What can I help you find?" }),
1271
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-empty-sub", children: "Ask about products, budgets, gift ideas, specs \u2014 I'll search the entire catalogue for you." }),
1272
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-chips", children: chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1273
+ "button",
2033
1274
  {
2034
- className: "hsk-cb-selected-product",
2035
- onClick: () => selectedProduct.url && window.open(selectedProduct.url, "_blank"),
2036
- children: [
2037
- selectedProduct.image && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { className: "hsk-cb-selected-img", src: selectedProduct.image, alt: selectedProduct.name }),
2038
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-selected-info", children: [
2039
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-selected-name", children: selectedProduct.name }),
2040
- selectedProduct.price && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-selected-price", children: [
2041
- (_a = selectedProduct.currency) != null ? _a : "KES",
2042
- " ",
2043
- parseFloat(((_b = selectedProduct.price) != null ? _b : "").replace(/[^0-9.]/g, "") || "0").toLocaleString()
2044
- ] })
1275
+ className: "hsk-cb-chip",
1276
+ onClick: () => handleSend(chip),
1277
+ children: chip
1278
+ },
1279
+ chip
1280
+ )) })
1281
+ ] }) : messages.map((msg, idx) => {
1282
+ const isLast = idx === messages.length - 1;
1283
+ const isUser = msg.role === "user";
1284
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-msg-group", children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-user-msg", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-user-bubble", children: msg.content }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-ai-msg", children: [
1285
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
1286
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-ai-body", children: [
1287
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }),
1288
+ isLast && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1289
+ SourcesCarousel,
1290
+ {
1291
+ sources,
1292
+ defaultCurrency,
1293
+ onSelectSource: handleSourceClick
1294
+ }
1295
+ )
1296
+ ] })
1297
+ ] }) }, idx);
1298
+ }),
1299
+ selectedProduct && loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1300
+ "div",
1301
+ {
1302
+ className: "hsk-cb-selected-product",
1303
+ onClick: () => selectedProduct.url && window.open(selectedProduct.url, "_blank"),
1304
+ children: [
1305
+ selectedProduct.image && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { className: "hsk-cb-selected-img", src: selectedProduct.image, alt: selectedProduct.name }),
1306
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-selected-info", children: [
1307
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-selected-name", children: selectedProduct.name }),
1308
+ selectedProduct.price && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-selected-price", children: [
1309
+ (_a = selectedProduct.currency) != null ? _a : defaultCurrency,
1310
+ " ",
1311
+ parseFloat(((_b = selectedProduct.price) != null ? _b : "").replace(/[^0-9.]/g, "") || "0").toLocaleString()
2045
1312
  ] })
2046
- ]
1313
+ ] })
1314
+ ]
1315
+ }
1316
+ ),
1317
+ loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-typing-row", children: [
1318
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
1319
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-typing", children: [
1320
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" }),
1321
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" }),
1322
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" })
1323
+ ] })
1324
+ ] }),
1325
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-error", children: error }),
1326
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: bottomRef, style: { height: 1 } })
1327
+ ] }),
1328
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-input-wrap", children: [
1329
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-input-box", children: [
1330
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1331
+ "textarea",
1332
+ {
1333
+ ref: textareaRef,
1334
+ className: `hsk-cb-textarea ${classNames.input || ""}`,
1335
+ value: input,
1336
+ onChange: handleInput,
1337
+ onKeyDown: handleKeyDown,
1338
+ placeholder,
1339
+ rows: 1,
1340
+ disabled: loading,
1341
+ autoFocus: true
2047
1342
  }
2048
1343
  ),
2049
- loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-typing-row", children: [
2050
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-ai-icon", children: "\u2726" }),
2051
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-typing", children: [
2052
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-dot" }),
2053
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-dot" }),
2054
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-dot" })
2055
- ] })
2056
- ] }),
2057
- error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-error", children: error }),
2058
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: bottomRef, style: { height: 1 } })
1344
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1345
+ "button",
1346
+ {
1347
+ className: `hsk-cb-send ${classNames.sendButton || ""}`,
1348
+ onClick: () => handleSend(),
1349
+ disabled: !input.trim() || loading,
1350
+ "aria-label": "Send message",
1351
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArrowUpIcon2, {})
1352
+ }
1353
+ )
2059
1354
  ] }),
2060
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-input-wrap", children: [
2061
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-input-box", children: [
2062
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2063
- "textarea",
2064
- {
2065
- ref: textareaRef,
2066
- className: "hsk-cb-textarea",
2067
- value: input,
2068
- onChange: handleInput,
2069
- onKeyDown: handleKeyDown,
2070
- placeholder,
2071
- rows: 1,
2072
- disabled: loading,
2073
- autoFocus: true
2074
- }
2075
- ),
2076
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2077
- "button",
2078
- {
2079
- className: "hsk-cb-send",
2080
- onClick: () => handleSend(),
2081
- disabled: !input.trim() || loading,
2082
- "aria-label": "Send",
2083
- children: "\u2191"
2084
- }
2085
- )
2086
- ] }),
2087
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 searches the whole catalogue in real time" })
2088
- ] })
1355
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 searches the whole catalogue in real time" })
2089
1356
  ] })
2090
- }
2091
- )
2092
- ] });
1357
+ ] })
1358
+ }
1359
+ );
2093
1360
  }
2094
1361
  function AIChatButton({
2095
1362
  label,
@@ -2098,29 +1365,34 @@ function AIChatButton({
2098
1365
  backdropColor,
2099
1366
  backdropBlur,
2100
1367
  className,
2101
- onSelectSource
1368
+ onSelectSource,
1369
+ defaultCurrency,
1370
+ chips,
1371
+ theme,
1372
+ classNames = {}
2102
1373
  }) {
2103
1374
  const [open, setOpen] = (0, import_react10.useState)(false);
2104
1375
  const [mounted, setMounted] = (0, import_react10.useState)(false);
2105
1376
  (0, import_react10.useEffect)(() => {
2106
1377
  setMounted(true);
2107
1378
  }, []);
2108
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2109
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: CSS3 }),
2110
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1379
+ const customStyles = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, (theme == null ? void 0 : theme.primaryColor) && { "--hsk-primary": theme.primaryColor }), (theme == null ? void 0 : theme.backgroundColor) && { "--hsk-bg": theme.backgroundColor }), (theme == null ? void 0 : theme.textColor) && { "--hsk-text": theme.textColor }), (theme == null ? void 0 : theme.fontFamily) && { "--hsk-font": theme.fontFamily });
1380
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1381
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2111
1382
  "button",
2112
1383
  {
2113
- className: `hsk-cb-btn ${className != null ? className : ""}`,
1384
+ className: `hsk-cb-btn ${classNames.button || ""} ${className || ""}`,
2114
1385
  onClick: () => setOpen(true),
1386
+ style: customStyles,
2115
1387
  "aria-label": "Open AI chat",
2116
1388
  children: [
2117
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "hsk-cb-btn-icon", children: "\u2726" }),
1389
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "hsk-cb-btn-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
2118
1390
  label !== void 0 ? label : null
2119
1391
  ]
2120
1392
  }
2121
1393
  ),
2122
1394
  open && mounted && (0, import_react_dom2.createPortal)(
2123
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1395
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2124
1396
  ChatModal,
2125
1397
  {
2126
1398
  title,
@@ -2128,7 +1400,11 @@ function AIChatButton({
2128
1400
  backdropColor,
2129
1401
  backdropBlur,
2130
1402
  onClose: () => setOpen(false),
2131
- onSelectSource
1403
+ onSelectSource,
1404
+ defaultCurrency,
1405
+ chips,
1406
+ theme,
1407
+ classNames
2132
1408
  }
2133
1409
  ),
2134
1410
  document.body