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