@akropolys/kiku 1.7.3 → 1.7.4

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
@@ -1130,6 +1130,7 @@ var ArrowUpIcon2 = () => /* @__PURE__ */ jsxs6("svg", { width: "18", height: "18
1130
1130
  /* @__PURE__ */ jsx7("path", { d: "M12 19V5" })
1131
1131
  ] });
1132
1132
  var StopIcon = () => /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx7("rect", { x: "5", y: "5", width: "14", height: "14", rx: "2" }) });
1133
+ var ContinueIcon = () => /* @__PURE__ */ jsx7("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx7("path", { d: "M8 5v14l11-7z" }) });
1133
1134
  var CloseIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1134
1135
  /* @__PURE__ */ jsx7("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1135
1136
  /* @__PURE__ */ jsx7("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
@@ -1159,12 +1160,22 @@ var MicOffIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16",
1159
1160
  /* @__PURE__ */ jsx7("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }),
1160
1161
  /* @__PURE__ */ jsx7("line", { x1: "12", y1: "19", x2: "12", y2: "22" })
1161
1162
  ] });
1162
- var DEFAULT_CHIPS = [
1163
- "Cheapest smartphone",
1164
- "Smart TV under KSh 20,000",
1165
- "Noise-cancelling headphones",
1166
- "Best laptop for students"
1167
- ];
1163
+ var DEFAULT_CHIPS = [];
1164
+ function extractName(raw) {
1165
+ let s = raw.trim();
1166
+ if (!s || s.length > 40 || s.includes("?")) return null;
1167
+ s = s.replace(/^(hi|hey|hello|yo)[,!.\s]+/i, "");
1168
+ s = s.replace(/^(i['’]?m|im|my name is|call me|it['’]?s|this is|name['’]?s)\s+/i, "");
1169
+ s = s.trim().replace(/[.!,]+$/, "");
1170
+ const words = s.split(/\s+/);
1171
+ if (words.length === 0 || words.length > 3) return null;
1172
+ if (!/^[\p{L}][\p{L}\-'’ ]{0,30}$/u.test(s)) return null;
1173
+ const q = s.toLowerCase();
1174
+ const queryish = ["phone", "laptop", "tv", "cheap", "best", "under", "buy", "search", "find", "show", "need", "want", "price", "sofa", "shoe", "headphone", "camera", "gift", "help"];
1175
+ if (queryish.some((w) => q.includes(w))) return null;
1176
+ const name = words[0];
1177
+ return name.charAt(0).toUpperCase() + name.slice(1);
1178
+ }
1168
1179
  function parseAtKiku(raw) {
1169
1180
  const trimmed = raw.trim();
1170
1181
  if (!/^@kiku\b/i.test(trimmed)) return null;
@@ -1624,15 +1635,9 @@ function ThinkingBlock({ text, isComplete }) {
1624
1635
  }, children: text })
1625
1636
  ] });
1626
1637
  }
1627
- var PROPERTY_CHIPS = [
1628
- "3 bedroom apartments",
1629
- "Houses under KSh 5,000,000",
1630
- "Properties in Palm Beach",
1631
- "Studio apartments with pool"
1632
- ];
1633
1638
  function ChatModal({
1634
1639
  title = "kiku",
1635
- placeholder = "Ask me anything \u2014 gifts, budget, use case\u2026",
1640
+ placeholder = "Ask me anything\u2026",
1636
1641
  backdropColor,
1637
1642
  backdropBlur,
1638
1643
  onClose,
@@ -1647,8 +1652,17 @@ function ChatModal({
1647
1652
  visionCategoryHint
1648
1653
  }) {
1649
1654
  const client = useAkropolysContext3();
1650
- const { messages, sources, loading, streaming, error, lastAction, lastIntent, send, stop, reset, referencedIds } = useKiku2();
1655
+ const { messages, sources, loading, streaming, error, lastAction, lastIntent, send, stop, stopped, continueGenerating, reset, referencedIds } = useKiku2();
1651
1656
  const [input, setInput] = useState5("");
1657
+ const [shopperName, setShopperNameState] = useState5(() => {
1658
+ try {
1659
+ return client.getShopperName?.() ?? "";
1660
+ } catch {
1661
+ return "";
1662
+ }
1663
+ });
1664
+ const [nameSkipped, setNameSkipped] = useState5(false);
1665
+ const awaitingName = messages.length === 0 && !shopperName && !nameSkipped;
1652
1666
  const [attachments, setAttachments] = useState5([]);
1653
1667
  const imageInputRef = useRef5(null);
1654
1668
  const handleImageFiles = (files) => {
@@ -1717,10 +1731,9 @@ function ChatModal({
1717
1731
  useEffect3(() => {
1718
1732
  return () => recognitionRef.current?.abort();
1719
1733
  }, []);
1720
- const isProperty = client.vertical === "property";
1721
- const activeChips = chips === DEFAULT_CHIPS && isProperty ? PROPERTY_CHIPS : chips;
1734
+ const activeChips = chips;
1722
1735
  const activeTitle = title;
1723
- const activePlaceholder = placeholder === "Ask me anything \u2014 gifts, budget, use case\u2026" && isProperty ? "Ask me anything \u2014 location, budget, bedrooms\u2026" : placeholder;
1736
+ const activePlaceholder = awaitingName ? "Type your name\u2026" : placeholder;
1724
1737
  const [selectedProduct, setSelectedProduct] = useState5(null);
1725
1738
  const [lightboxSrc, setLightboxSrc] = useState5(null);
1726
1739
  const bottomRef = useRef5(null);
@@ -1728,6 +1741,7 @@ function ChatModal({
1728
1741
  const [keyInput, setKeyInput] = useState5("");
1729
1742
  const [keyPhase, setKeyPhase] = useState5("idle");
1730
1743
  const [mintedKey, setMintedKey] = useState5(null);
1744
+ const [mintedPub, setMintedPub] = useState5(null);
1731
1745
  const [keyCountdown, setKeyCountdown] = useState5(KIKU_KEY_REVEAL_SECONDS);
1732
1746
  const [minting, setMinting] = useState5(false);
1733
1747
  const [showKikuPicker, setShowKikuPicker] = useState5(false);
@@ -1746,6 +1760,7 @@ function ChatModal({
1746
1760
  if (s <= 1) {
1747
1761
  clearInterval(t);
1748
1762
  setMintedKey(null);
1763
+ setMintedPub(null);
1749
1764
  return 0;
1750
1765
  }
1751
1766
  return s - 1;
@@ -1767,9 +1782,9 @@ function ChatModal({
1767
1782
  if (lastUserMsg) await handleSend(lastUserMsg.content);
1768
1783
  };
1769
1784
  const handleUseExistingKey = async () => {
1770
- const key = keyInput.trim();
1771
- if (!key) return;
1772
- client.setKikuKey(key);
1785
+ const pub = keyInput.trim();
1786
+ if (!pub) return;
1787
+ client.setKikuPub(pub);
1773
1788
  setKeyInput("");
1774
1789
  setKeyPhase("idle");
1775
1790
  await retryLastMessage();
@@ -1778,8 +1793,9 @@ function ChatModal({
1778
1793
  if (minting) return;
1779
1794
  setMinting(true);
1780
1795
  try {
1781
- const key = await client.mintKikuKey();
1782
- setMintedKey(key);
1796
+ const { secret, publicId } = await client.mintKikuKey();
1797
+ setMintedKey(secret);
1798
+ setMintedPub(publicId);
1783
1799
  setKeyPhase("idle");
1784
1800
  await retryLastMessage();
1785
1801
  } catch {
@@ -1828,12 +1844,31 @@ function ChatModal({
1828
1844
  const handleSourceClick = (src) => {
1829
1845
  setSelectedProduct(src);
1830
1846
  onSelectSource?.(src);
1847
+ const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
1848
+ if (lastAssistant && lastAssistant.content.trim().endsWith("?")) {
1849
+ send(`The ${src.name}`);
1850
+ return;
1851
+ }
1831
1852
  const q = `Tell me more about the ${src.name}${src.price ? ` (${src.currency ?? defaultCurrency} ${src.price})` : ""} \u2014 what are its key specs, who is it best for, and is it worth buying?`;
1832
1853
  send(q);
1833
1854
  };
1834
1855
  const handleSend = async (text, extraAttachments, forcedIntent, captureTargets) => {
1835
1856
  const raw = (text ?? input).trim();
1836
1857
  if (!raw || loading) return;
1858
+ if (awaitingName) {
1859
+ const name = extractName(raw);
1860
+ if (name) {
1861
+ try {
1862
+ client.setShopperName?.(name);
1863
+ } catch {
1864
+ }
1865
+ setShopperNameState(name);
1866
+ setInput("");
1867
+ if (textareaRef.current) textareaRef.current.style.height = "auto";
1868
+ return;
1869
+ }
1870
+ setNameSkipped(true);
1871
+ }
1837
1872
  const kiku = parseAtKiku(raw);
1838
1873
  const q = kiku ? kiku.cleanQuery : raw;
1839
1874
  const resolvedForcedIntent = forcedIntent ?? kiku?.intent;
@@ -1916,7 +1951,7 @@ function ChatModal({
1916
1951
  setInput(val);
1917
1952
  const trimmed = val.trim();
1918
1953
  setShowAtPicker(trimmed === "@");
1919
- setShowKikuPicker(/^@kiku\b/i.test(trimmed));
1954
+ setShowKikuPicker(/^@kiku\s*$/i.test(trimmed));
1920
1955
  const t = e.target;
1921
1956
  t.style.height = "auto";
1922
1957
  t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
@@ -1979,9 +2014,23 @@ function ChatModal({
1979
2014
  ] }),
1980
2015
  /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-msgs", ref: msgsContainerRef, children: [
1981
2016
  displayMessages.length === 0 ? /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-empty", children: [
1982
- /* @__PURE__ */ jsx7("div", { className: "hsk-cb-empty-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx7(SparkleIcon2, {}) }),
1983
- /* @__PURE__ */ jsx7("div", { className: "hsk-cb-empty-title", children: "Find exactly what you need" }),
1984
- /* @__PURE__ */ jsx7("div", { className: "hsk-cb-chips", children: activeChips.map((chip) => /* @__PURE__ */ jsx7(
2017
+ /* @__PURE__ */ jsx7("div", { className: "hsk-cb-empty-logo", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 100 100", className: "hsk-brand-mark", children: /* @__PURE__ */ jsx7("path", { d: "M39.4 10.4 Q44 0 48.6 10.4 L86.1 95.8 Q88 100 83.4 100 L4.6 100 Q0 100 1.9 95.8 Z M24 100 L24 65 Q24 60 27.4 56.3 Q44 38 60.6 56.3 Q64 60 64 65 L64 100 Z", transform: "translate(22.7 19) scale(0.62)", fillRule: "evenodd" }) }) }),
2018
+ awaitingName ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
2019
+ /* @__PURE__ */ jsx7("div", { className: "hsk-cb-empty-title hsk-cb-onboard-title", children: "My name is Kiku" }),
2020
+ /* @__PURE__ */ jsx7("div", { className: "hsk-cb-empty-sub", children: "Ask me to search, visualize, or capture anything for you \u2014 on this site or any other." }),
2021
+ /* @__PURE__ */ jsx7("div", { className: "hsk-cb-onboard-ask", children: "What should I call you?" }),
2022
+ /* @__PURE__ */ jsx7("button", { className: "hsk-cb-onboard-skip", onClick: () => setNameSkipped(true), children: "Skip" })
2023
+ ] }) : shopperName ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
2024
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-empty-title", children: [
2025
+ "Hey, ",
2026
+ shopperName
2027
+ ] }),
2028
+ /* @__PURE__ */ jsx7("div", { className: "hsk-cb-empty-sub", children: "What can I find for you today?" })
2029
+ ] }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
2030
+ /* @__PURE__ */ jsx7("div", { className: "hsk-cb-empty-title", children: "Hey, I'm kiku" }),
2031
+ /* @__PURE__ */ jsx7("div", { className: "hsk-cb-empty-sub", children: "Ask me to search, visualize, or capture anything \u2014 I look across the whole site in real time." })
2032
+ ] }),
2033
+ !awaitingName && activeChips.length > 0 && /* @__PURE__ */ jsx7("div", { className: "hsk-cb-chips", children: activeChips.map((chip) => /* @__PURE__ */ jsx7(
1985
2034
  "button",
1986
2035
  {
1987
2036
  className: "hsk-cb-chip",
@@ -2103,17 +2152,24 @@ function ChatModal({
2103
2152
  ] }),
2104
2153
  /* @__PURE__ */ jsx7("span", { className: "hsk-cb-thinking-text", children: "Thinking\u2026" })
2105
2154
  ] }),
2155
+ stopped && !loading && !streaming && messages[messages.length - 1]?.role === "assistant" && /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-stopped", children: [
2156
+ /* @__PURE__ */ jsx7("span", { className: "hsk-cb-stopped-label", children: "You stopped this response." }),
2157
+ /* @__PURE__ */ jsxs6("button", { className: "hsk-cb-continue", onClick: continueGenerating, children: [
2158
+ /* @__PURE__ */ jsx7(ContinueIcon, {}),
2159
+ "Continue generating"
2160
+ ] })
2161
+ ] }),
2106
2162
  error && /* @__PURE__ */ jsx7("div", { className: "hsk-cb-error", children: getFriendlyError(error) }),
2107
2163
  keyPhase === "prompt_key" && /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-ai-msg", children: [
2108
2164
  /* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx7(SparkleIcon2, {}) }),
2109
2165
  /* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-body", children: /* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-text", children: /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-phone-form", children: [
2110
- /* @__PURE__ */ jsx7("label", { className: "hsk-cb-phone-label", children: "Paste your kiku key \u2014 or create one" }),
2166
+ /* @__PURE__ */ jsx7("label", { className: "hsk-cb-phone-label", children: "Paste your public id \u2014 or create one" }),
2111
2167
  /* @__PURE__ */ jsx7(
2112
2168
  "input",
2113
2169
  {
2114
2170
  type: "text",
2115
2171
  className: "hsk-cb-phone-input",
2116
- placeholder: "kiku_\u2026",
2172
+ placeholder: "your public id\u2026",
2117
2173
  value: keyInput,
2118
2174
  onChange: (e) => setKeyInput(e.target.value),
2119
2175
  onKeyDown: (e) => e.key === "Enter" && handleUseExistingKey(),
@@ -2121,28 +2177,44 @@ function ChatModal({
2121
2177
  }
2122
2178
  ),
2123
2179
  /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 8 }, children: [
2124
- /* @__PURE__ */ jsx7("button", { className: "hsk-cb-phone-submit", onClick: handleUseExistingKey, disabled: !keyInput.trim(), children: "Use my key" }),
2125
- /* @__PURE__ */ jsx7("button", { className: "hsk-cb-phone-submit", onClick: handleCreateKey, disabled: minting, children: minting ? "Creating\u2026" : "I'm new \u2014 create my key" })
2180
+ /* @__PURE__ */ jsx7("button", { className: "hsk-cb-phone-submit", onClick: handleUseExistingKey, disabled: !keyInput.trim(), children: "Use my id" }),
2181
+ /* @__PURE__ */ jsx7("button", { className: "hsk-cb-phone-submit", onClick: handleCreateKey, disabled: minting, children: minting ? "Creating\u2026" : "I'm new \u2014 create one" })
2126
2182
  ] })
2127
2183
  ] }) }) })
2128
2184
  ] }),
2129
2185
  mintedKey && /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-ai-msg", children: [
2130
2186
  /* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx7(SparkleIcon2, {}) }),
2131
- /* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-body", children: /* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-text", children: /* @__PURE__ */ jsxs6("div", { style: { padding: "10px 12px", border: "1px solid var(--hsk-border, #e5e5e5)", borderRadius: 8 }, children: [
2132
- /* @__PURE__ */ jsx7("div", { style: { fontSize: 12, fontWeight: 600, marginBottom: 4 }, children: "\u{1F511} Your kiku key \u2014 shown only once" }),
2133
- /* @__PURE__ */ jsx7("code", { style: { display: "block", fontSize: 14, fontWeight: 700, marginBottom: 6, wordBreak: "break-all" }, children: mintedKey }),
2134
- /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2135
- /* @__PURE__ */ jsx7("button", { className: "hsk-cb-phone-submit", style: { padding: "4px 10px" }, onClick: () => {
2136
- try {
2137
- navigator.clipboard?.writeText(mintedKey);
2138
- } catch {
2139
- }
2140
- }, children: "Copy" }),
2141
- /* @__PURE__ */ jsxs6("span", { style: { fontSize: 11, opacity: 0.7 }, children: [
2142
- "Disappears in ",
2143
- keyCountdown,
2144
- "s. Save it like a password \u2014 if lost, the memory it opens is lost with it."
2187
+ /* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-body", children: /* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-text", children: /* @__PURE__ */ jsxs6("div", { style: { padding: "12px 14px", border: "1px solid var(--hsk-border, #e5e5e5)", borderRadius: "var(--hsk-border-radius, 0px)", display: "flex", flexDirection: "column", gap: 12 }, children: [
2188
+ /* @__PURE__ */ jsxs6("div", { children: [
2189
+ /* @__PURE__ */ jsx7("div", { style: { fontSize: 12, fontWeight: 600, marginBottom: 4 }, children: "Your secret \u2014 shown only once" }),
2190
+ /* @__PURE__ */ jsx7("code", { style: { display: "block", fontSize: 14, fontWeight: 700, marginBottom: 6, wordBreak: "break-all" }, children: mintedKey }),
2191
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2192
+ /* @__PURE__ */ jsx7("button", { className: "hsk-cb-phone-submit", style: { padding: "4px 10px" }, onClick: () => {
2193
+ try {
2194
+ navigator.clipboard?.writeText(mintedKey);
2195
+ } catch {
2196
+ }
2197
+ }, children: "Copy secret" }),
2198
+ /* @__PURE__ */ jsx7("span", { style: { fontSize: 11, opacity: 0.7 }, children: "Keep it private \u2014 use it to unlock your memory at mimi.akropolys.cloud. If lost, the memory is lost with it." })
2145
2199
  ] })
2200
+ ] }),
2201
+ mintedPub && /* @__PURE__ */ jsxs6("div", { children: [
2202
+ /* @__PURE__ */ jsx7("div", { style: { fontSize: 12, fontWeight: 600, marginBottom: 4 }, children: "Your public id" }),
2203
+ /* @__PURE__ */ jsx7("code", { style: { display: "block", fontSize: 13, fontWeight: 600, marginBottom: 6, wordBreak: "break-all", opacity: 0.85 }, children: mintedPub }),
2204
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2205
+ /* @__PURE__ */ jsx7("button", { className: "hsk-cb-phone-submit", style: { padding: "4px 10px" }, onClick: () => {
2206
+ try {
2207
+ navigator.clipboard?.writeText(mintedPub);
2208
+ } catch {
2209
+ }
2210
+ }, children: "Copy id" }),
2211
+ /* @__PURE__ */ jsx7("span", { style: { fontSize: 11, opacity: 0.7 }, children: "Paste this on any site to keep saving to the same memory." })
2212
+ ] })
2213
+ ] }),
2214
+ /* @__PURE__ */ jsxs6("div", { style: { fontSize: 11, opacity: 0.6 }, children: [
2215
+ "Hidden in ",
2216
+ keyCountdown,
2217
+ "s."
2146
2218
  ] })
2147
2219
  ] }) }) })
2148
2220
  ] }),