@akropolys/kiku 1.1.0 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1194,6 +1194,15 @@ function ChatModal({
1194
1194
  if (typeof window !== "undefined") {
1195
1195
  localStorage.setItem("akropolys_user_phone", phoneInput.trim());
1196
1196
  }
1197
+ const isHistoryIntent = lastIntent === "capture" || lastIntent === "delete" || lastIntent === "view_history";
1198
+ if (isHistoryIntent) {
1199
+ setPaymentPhase("idle");
1200
+ const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
1201
+ if (lastUserMsg) {
1202
+ await send(lastUserMsg.content);
1203
+ }
1204
+ return;
1205
+ }
1197
1206
  const res = await client.api.initiatePayment(phoneInput.trim());
1198
1207
  setMerchantRef(res.merchantReference);
1199
1208
  setPaymentPhase("awaiting");
@@ -1460,22 +1469,31 @@ function ChatModal({
1460
1469
  ] })
1461
1470
  ] }),
1462
1471
  error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-error", children: getFriendlyError(error) }),
1463
- !replayMessages && paymentPhase === "prompt_phone" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-payment-prompt", children: [
1464
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-payment-icon", children: "\u{1F4F1}" }),
1465
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "hsk-cb-payment-label", children: "Enter your M-Pesa number to pay" }),
1466
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1467
- "input",
1468
- {
1469
- type: "tel",
1470
- className: "hsk-cb-phone-input",
1471
- placeholder: "e.g. 0712 345 678",
1472
- value: phoneInput,
1473
- onChange: (e) => setPhoneInput(e.target.value),
1474
- onKeyDown: (e) => e.key === "Enter" && handlePhoneSubmit()
1475
- }
1476
- ),
1477
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "hsk-cb-pay-submit", onClick: handlePhoneSubmit, children: "Send STK Push \u2192" })
1478
- ] }),
1472
+ !replayMessages && paymentPhase === "prompt_phone" && (() => {
1473
+ const isHistoryIntent = lastIntent === "capture" || lastIntent === "delete" || lastIntent === "view_history";
1474
+ if (isHistoryIntent) {
1475
+ return null;
1476
+ }
1477
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-ai-msg", children: [
1478
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparkleIcon2, {}) }),
1479
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-ai-body", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-ai-text", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-phone-form", children: [
1480
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "hsk-cb-phone-label", children: "Enter your M-Pesa number to pay" }),
1481
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1482
+ "input",
1483
+ {
1484
+ type: "tel",
1485
+ className: "hsk-cb-phone-input",
1486
+ placeholder: "0712 345 678",
1487
+ value: phoneInput,
1488
+ onChange: (e) => setPhoneInput(e.target.value.replace(/\D/g, "")),
1489
+ onKeyDown: (e) => e.key === "Enter" && handlePhoneSubmit(),
1490
+ autoFocus: true
1491
+ }
1492
+ ),
1493
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "hsk-cb-phone-submit", onClick: handlePhoneSubmit, children: "Send STK push" })
1494
+ ] }) }) })
1495
+ ] });
1496
+ })(),
1479
1497
  !replayMessages && paymentPhase === "awaiting" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-cb-payment-prompt hsk-cb-payment-prompt--awaiting", children: [
1480
1498
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-payment-pulse-ring" }),
1481
1499
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-cb-payment-icon-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: "2rem" }, children: "\u{1F4F1}" }) }),
@@ -1767,6 +1785,9 @@ function SparkleModal({
1767
1785
  const { results, loading: searchLoading, search } = (0, import_sdk6.useSearch)();
1768
1786
  const { messages, sources, loading: chatLoading, error: chatError, send } = (0, import_sdk6.useKiku)();
1769
1787
  const [chatInput, setChatInput] = (0, import_react4.useState)("");
1788
+ const [isMobile, setIsMobile] = (0, import_react4.useState)(false);
1789
+ const [showSpecs, setShowSpecs] = (0, import_react4.useState)(false);
1790
+ const [collapseSimilar, setCollapseSimilar] = (0, import_react4.useState)(false);
1770
1791
  const chatBottomRef = (0, import_react4.useRef)(null);
1771
1792
  const chatTextareaRef = (0, import_react4.useRef)(null);
1772
1793
  (0, import_react4.useEffect)(() => {
@@ -1779,6 +1800,14 @@ function SparkleModal({
1779
1800
  }
1780
1801
  search(productName, limit);
1781
1802
  }, [productName, initialProduct, fetchedProduct, client, limit, search]);
1803
+ (0, import_react4.useEffect)(() => {
1804
+ const handleResize = () => setIsMobile(window.innerWidth <= 768);
1805
+ handleResize();
1806
+ if (typeof window !== "undefined") {
1807
+ window.addEventListener("resize", handleResize);
1808
+ return () => window.removeEventListener("resize", handleResize);
1809
+ }
1810
+ }, []);
1782
1811
  (0, import_react4.useEffect)(() => {
1783
1812
  if (results.length > 0) onResult?.(results);
1784
1813
  }, [results, onResult]);
@@ -1842,6 +1871,170 @@ Question: ${q}`;
1842
1871
  content: `Hi! I can help you with **${displayProduct.name}**. Ask me about its specifications, features, compare it with other options, or find alternatives!`
1843
1872
  }
1844
1873
  ] : messages;
1874
+ if (isMobile) {
1875
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1876
+ "div",
1877
+ {
1878
+ className: cn("hsk-sp-backdrop hsk-sp-mobile-view", classNames.backdrop),
1879
+ onClick: onClose,
1880
+ style: {
1881
+ backdropFilter: `blur(${blurVal})`,
1882
+ WebkitBackdropFilter: `blur(${blurVal})`,
1883
+ background: bg ?? void 0,
1884
+ ...customStyles
1885
+ },
1886
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: cn("hsk-sp-card hsk-sp-fullscreen hsk-sp-mobile-card", classNames.card), onClick: (e) => e.stopPropagation(), children: [
1887
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-header", children: [
1888
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "hsk-sp-header-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
1889
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-header-body", children: [
1890
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-header-title-row", children: [
1891
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-header-title", children: displayProduct?.name || productName }),
1892
+ displayProduct && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1893
+ "button",
1894
+ {
1895
+ type: "button",
1896
+ className: "hsk-sp-header-specs-btn",
1897
+ onClick: () => setShowSpecs(true),
1898
+ children: "Specs"
1899
+ }
1900
+ )
1901
+ ] }),
1902
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-header-sub", children: "Akropolys AI Shopping Assistant" })
1903
+ ] }),
1904
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-sp-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CloseIcon2, {}) })
1905
+ ] }),
1906
+ searchLoading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-bar" }),
1907
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-chat-container", children: [
1908
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-msgs", children: [
1909
+ displayMessages.map((msg, idx) => {
1910
+ const isUser = msg.role === "user";
1911
+ 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: [
1912
+ /* @__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, {}) }),
1913
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-ai-body", children: [
1914
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }),
1915
+ idx === 0 && displayProduct && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-attachment-deck", children: [
1916
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-main-card", children: [
1917
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-mobile-main-card-img", children: displayProduct.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: displayProduct.images[0], alt: displayProduct.name }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u{1F6CD}" }) }),
1918
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-main-card-info", children: [
1919
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-mobile-main-card-brand", children: displayProduct.brand || displayProduct.category || "Product" }),
1920
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-mobile-main-card-name", children: displayProduct.name }),
1921
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-main-card-price", children: [
1922
+ displayProduct.currency ?? "KES",
1923
+ " ",
1924
+ parseFloat(displayProduct.price?.replace(/[^0-9.]/g, "") || "0").toLocaleString()
1925
+ ] })
1926
+ ] }),
1927
+ (displayProduct.specs && Object.keys(displayProduct.specs).length > 0 || displayProduct.description) && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1928
+ "button",
1929
+ {
1930
+ type: "button",
1931
+ className: "hsk-sp-mobile-main-card-specs-btn",
1932
+ onClick: () => setShowSpecs(true),
1933
+ children: "Specs"
1934
+ }
1935
+ )
1936
+ ] }),
1937
+ (() => {
1938
+ const similarProducts = results.filter(
1939
+ (r) => {
1940
+ const isSameName = r.product.name.toLowerCase() === displayProduct?.name?.toLowerCase();
1941
+ const isSameSlug = r.product.slug && displayProduct?.slug && r.product.slug.toLowerCase() === displayProduct.slug.toLowerCase();
1942
+ return !isSameName && !isSameSlug;
1943
+ }
1944
+ );
1945
+ if (similarProducts.length === 0) return null;
1946
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-similar-carousel-inline", children: [
1947
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-mobile-similar-carousel-title", children: "Similar Products" }),
1948
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-mobile-similar-carousel-list", children: similarProducts.map((r) => {
1949
+ const price = parseFloat(r.product.price?.replace(/[^0-9.]/g, "") || "0");
1950
+ const currency = r.product.currency ?? "KES";
1951
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1952
+ "div",
1953
+ {
1954
+ className: "hsk-sp-mobile-similar-carousel-item",
1955
+ onClick: () => handleNav(r),
1956
+ children: [
1957
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-mobile-similar-carousel-img", children: r.product.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: r.product.images[0], alt: r.product.name }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u{1F6CD}" }) }),
1958
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-similar-carousel-meta", children: [
1959
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-mobile-similar-carousel-name", title: r.product.name, children: r.product.name }),
1960
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-similar-carousel-price", children: [
1961
+ currency,
1962
+ " ",
1963
+ price.toLocaleString()
1964
+ ] })
1965
+ ] })
1966
+ ]
1967
+ },
1968
+ r.id
1969
+ );
1970
+ }) })
1971
+ ] });
1972
+ })()
1973
+ ] })
1974
+ ] })
1975
+ ] }) }, idx);
1976
+ }),
1977
+ chatLoading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-typing-row", children: [
1978
+ /* @__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, {}) }),
1979
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-typing", children: [
1980
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" }),
1981
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" }),
1982
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-dot" })
1983
+ ] })
1984
+ ] }),
1985
+ chatError && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-error", children: getFriendlyError2(chatError) }),
1986
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: chatBottomRef, style: { height: 1 } })
1987
+ ] }),
1988
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-input-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-input-box", children: [
1989
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1990
+ "textarea",
1991
+ {
1992
+ ref: chatTextareaRef,
1993
+ className: "hsk-cb-textarea",
1994
+ value: chatInput,
1995
+ onChange: handleInput,
1996
+ onKeyDown: handleKeyDown,
1997
+ placeholder: "Ask about this product, specs, or comparison...",
1998
+ rows: 1,
1999
+ disabled: chatLoading
2000
+ }
2001
+ ),
2002
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2003
+ "button",
2004
+ {
2005
+ className: "hsk-cb-send",
2006
+ onClick: () => handleSend(),
2007
+ disabled: !chatInput.trim() || chatLoading,
2008
+ "aria-label": "Send message",
2009
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArrowUpIcon3, {})
2010
+ }
2011
+ )
2012
+ ] }) })
2013
+ ] }),
2014
+ showSpecs && displayProduct && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-mobile-specs-overlay", onClick: () => setShowSpecs(false), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-specs-drawer", onClick: (e) => e.stopPropagation(), children: [
2015
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-specs-header", children: [
2016
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: "Specifications" }),
2017
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: () => setShowSpecs(false), children: "Close" })
2018
+ ] }),
2019
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-specs-body", children: [
2020
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h4", { className: "hsk-sp-mobile-specs-title", children: displayProduct.name }),
2021
+ displayProduct.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-specs-desc", children: [
2022
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h5", { children: "Description" }),
2023
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: displayProduct.description })
2024
+ ] }),
2025
+ displayProduct.specs && Object.keys(displayProduct.specs).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-specs-list", children: [
2026
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h5", { children: "Details" }),
2027
+ Object.entries(displayProduct.specs).map(([key, val]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-mobile-spec-row", children: [
2028
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "hsk-sp-mobile-spec-label", children: key }),
2029
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "hsk-sp-mobile-spec-value", children: val })
2030
+ ] }, key))
2031
+ ] })
2032
+ ] })
2033
+ ] }) })
2034
+ ] })
2035
+ }
2036
+ );
2037
+ }
1845
2038
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1846
2039
  "div",
1847
2040
  {
@@ -1929,7 +2122,8 @@ Question: ${q}`;
1929
2122
  "div",
1930
2123
  {
1931
2124
  className: cn("hsk-sp-item", classNames.item),
1932
- style: { animationDelay: `${i * 55}ms` },
2125
+ style: { animationDelay: `${i * 55}ms`, cursor: "pointer" },
2126
+ onClick: () => handleNav(r),
1933
2127
  children: [
1934
2128
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-sp-img-wrap", children: r.product.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: r.product.images[0], alt: r.product.name }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
1935
2129
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-sp-item-body", children: [
@@ -1945,7 +2139,10 @@ Question: ${q}`;
1945
2139
  "button",
1946
2140
  {
1947
2141
  className: "hsk-sp-action hsk-sp-action-primary",
1948
- onClick: () => handleNav(r),
2142
+ onClick: (e) => {
2143
+ e.stopPropagation();
2144
+ handleNav(r);
2145
+ },
1949
2146
  children: "View"
1950
2147
  }
1951
2148
  ) })