@akropolys/kiku 1.0.0

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 ADDED
@@ -0,0 +1,2504 @@
1
+ 'use client';
2
+
3
+ // src/components/SearchBar.tsx
4
+ import { useState, useEffect, useRef } from "react";
5
+ import { useSearch, useAkropolysContext } from "@akropolys/sdk";
6
+
7
+ // src/utils/cn.ts
8
+ import { clsx } from "clsx";
9
+ import { twMerge } from "tailwind-merge";
10
+ function cn(...inputs) {
11
+ return twMerge(clsx(inputs));
12
+ }
13
+
14
+ // src/components/SearchBar.tsx
15
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
+ var SearchIcon = () => /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
17
+ /* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "5.5" }),
18
+ /* @__PURE__ */ jsx("line", { x1: "13", y1: "13", x2: "18", y2: "18" })
19
+ ] });
20
+ function SearchBar({
21
+ placeholder = "Search products\u2026",
22
+ limit = 10,
23
+ debounceMs = 300,
24
+ onSelect,
25
+ className,
26
+ inputClassName,
27
+ dropdownClassName,
28
+ renderResult,
29
+ theme,
30
+ classNames = {}
31
+ }) {
32
+ const [query, setQuery] = useState("");
33
+ const [open, setOpen] = useState(false);
34
+ const [isDebouncing, setIsDebouncing] = useState(false);
35
+ const { results, loading, search, clear } = useSearch();
36
+ const client = useAkropolysContext();
37
+ const timer = useRef();
38
+ const wrap = useRef(null);
39
+ const ignoreNextQueryChange = useRef(false);
40
+ useEffect(() => {
41
+ if (ignoreNextQueryChange.current) {
42
+ ignoreNextQueryChange.current = false;
43
+ return;
44
+ }
45
+ clearTimeout(timer.current);
46
+ if (!query.trim()) {
47
+ clear();
48
+ setOpen(false);
49
+ setIsDebouncing(false);
50
+ return;
51
+ }
52
+ setOpen(true);
53
+ setIsDebouncing(true);
54
+ timer.current = setTimeout(() => {
55
+ setIsDebouncing(false);
56
+ search(query, limit);
57
+ }, debounceMs);
58
+ return () => clearTimeout(timer.current);
59
+ }, [query]);
60
+ useEffect(() => {
61
+ const h = (e) => {
62
+ if (wrap.current && !wrap.current.contains(e.target)) setOpen(false);
63
+ };
64
+ document.addEventListener("mousedown", h);
65
+ return () => document.removeEventListener("mousedown", h);
66
+ }, []);
67
+ const handleSelect = (r) => {
68
+ if (query.trim()) {
69
+ client.api.searchVector(query, 1).catch(() => {
70
+ });
71
+ }
72
+ ignoreNextQueryChange.current = true;
73
+ setOpen(false);
74
+ setQuery(r.product.name);
75
+ onSelect?.(r);
76
+ };
77
+ const handleCommitSearch = () => {
78
+ if (!query.trim()) return;
79
+ client.api.searchVector(query, 1).catch(() => {
80
+ });
81
+ if (results.length > 0) {
82
+ handleSelect(results[0]);
83
+ }
84
+ };
85
+ const showDrop = open && query.trim().length > 0;
86
+ const customStyles = {
87
+ ...theme?.primaryColor && { "--hsk-primary": theme.primaryColor },
88
+ ...theme?.backgroundColor && { "--hsk-bg": theme.backgroundColor },
89
+ ...theme?.textColor && { "--hsk-text": theme.textColor },
90
+ ...theme?.fontFamily && { "--hsk-font": theme.fontFamily },
91
+ ...theme?.borderRadius && { "--hsk-border-radius": theme.borderRadius }
92
+ };
93
+ return /* @__PURE__ */ jsxs("div", { className: cn("hsk-sb-wrap", classNames.root, className), ref: wrap, style: customStyles, children: [
94
+ /* @__PURE__ */ jsx("span", { className: "hsk-sb-icon", children: /* @__PURE__ */ jsx(SearchIcon, {}) }),
95
+ /* @__PURE__ */ jsx(
96
+ "input",
97
+ {
98
+ className: cn("hsk-sb-input", classNames.input, inputClassName),
99
+ type: "text",
100
+ value: query,
101
+ placeholder,
102
+ onChange: (e) => setQuery(e.target.value),
103
+ onFocus: () => results.length > 0 && query.trim() && setOpen(true),
104
+ onKeyDown: (e) => {
105
+ if (e.key === "Enter") {
106
+ handleCommitSearch();
107
+ }
108
+ },
109
+ autoComplete: "off",
110
+ spellCheck: false
111
+ }
112
+ ),
113
+ showDrop && /* @__PURE__ */ jsxs("div", { className: cn("hsk-sb-drop", classNames.dropdown, dropdownClassName), style: { position: "absolute" }, children: [
114
+ (loading || isDebouncing) && /* @__PURE__ */ jsx("div", { className: "hsk-sb-loading-bar" }),
115
+ (loading || isDebouncing) && results.length === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
116
+ /* @__PURE__ */ jsxs("div", { className: "hsk-sb-skeleton-row", children: [
117
+ /* @__PURE__ */ jsx("span", { className: "hsk-sb-skeleton-icon" }),
118
+ /* @__PURE__ */ jsxs("div", { className: "hsk-sb-row-body", children: [
119
+ /* @__PURE__ */ jsx("div", { className: "hsk-sb-skeleton-text1" }),
120
+ /* @__PURE__ */ jsx("div", { className: "hsk-sb-skeleton-text2" })
121
+ ] })
122
+ ] }),
123
+ /* @__PURE__ */ jsxs("div", { className: "hsk-sb-skeleton-row", children: [
124
+ /* @__PURE__ */ jsx("span", { className: "hsk-sb-skeleton-icon" }),
125
+ /* @__PURE__ */ jsxs("div", { className: "hsk-sb-row-body", children: [
126
+ /* @__PURE__ */ jsx("div", { className: "hsk-sb-skeleton-text1", style: { width: "45%" } }),
127
+ /* @__PURE__ */ jsx("div", { className: "hsk-sb-skeleton-text2", style: { width: "25%" } })
128
+ ] })
129
+ ] })
130
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
131
+ results.length === 0 && !loading && !isDebouncing && /* @__PURE__ */ jsxs("div", { className: "hsk-sb-empty", children: [
132
+ "No results for \u201C",
133
+ query,
134
+ "\u201D"
135
+ ] }),
136
+ results.map((r, i) => renderResult ? /* @__PURE__ */ jsx(
137
+ "div",
138
+ {
139
+ onClick: () => handleSelect(r),
140
+ className: "hsk-sb-fade",
141
+ style: { animationDelay: `${i * 18}ms` },
142
+ children: renderResult(r)
143
+ },
144
+ r.id
145
+ ) : /* @__PURE__ */ jsxs(
146
+ "div",
147
+ {
148
+ className: cn("hsk-sb-row hsk-sb-fade", classNames.row),
149
+ style: { animationDelay: `${i * 18}ms` },
150
+ onClick: () => handleSelect(r),
151
+ children: [
152
+ /* @__PURE__ */ jsx("span", { className: "hsk-sb-row-icon", children: /* @__PURE__ */ jsx(SearchIcon, {}) }),
153
+ /* @__PURE__ */ jsxs("div", { className: "hsk-sb-row-body", children: [
154
+ /* @__PURE__ */ jsx("div", { className: "hsk-sb-row-title", children: r.product.name }),
155
+ (r.product.category || r.product.brand) && /* @__PURE__ */ jsx("div", { className: "hsk-sb-row-sub", children: r.product.category ?? r.product.brand })
156
+ ] })
157
+ ]
158
+ },
159
+ r.id
160
+ ))
161
+ ] })
162
+ ] })
163
+ ] });
164
+ }
165
+
166
+ // src/components/ChatWidget.tsx
167
+ import { useState as useState2, useRef as useRef2, useEffect as useEffect2 } from "react";
168
+ import { useKiku } from "@akropolys/sdk";
169
+
170
+ // src/utils/markdown.tsx
171
+ import { Fragment as Fragment2, jsx as jsx2 } from "react/jsx-runtime";
172
+ var parseInline = (text, keyPrefix) => {
173
+ const tokenRegex = /(\[[^\]]+\]\([^)]+\)|\*\*[^*]+\*\*|`[^`]+`)/g;
174
+ const parts = text.split(tokenRegex);
175
+ return parts.map((part, index) => {
176
+ if (!part) return null;
177
+ const key = `${keyPrefix}-inline-${index}`;
178
+ if (part.startsWith("`") && part.endsWith("`")) {
179
+ return /* @__PURE__ */ jsx2("code", { className: "hsk-markdown-code", children: part.slice(1, -1) }, key);
180
+ }
181
+ if (part.startsWith("**") && part.endsWith("**")) {
182
+ return /* @__PURE__ */ jsx2("strong", { children: parseInline(part.slice(2, -2), key) }, key);
183
+ }
184
+ const linkMatch = part.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
185
+ if (linkMatch) {
186
+ const url = linkMatch[2];
187
+ const isSafeUrl = /^(https?|mailto|tel):/i.test(url) || url.startsWith("/");
188
+ if (isSafeUrl) {
189
+ return /* @__PURE__ */ jsx2("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: "hsk-markdown-link", children: parseInline(linkMatch[1], key) }, key);
190
+ }
191
+ return /* @__PURE__ */ jsx2("span", { children: parseInline(linkMatch[1], key) }, key);
192
+ }
193
+ return part;
194
+ });
195
+ };
196
+ function renderMarkdown(content) {
197
+ const lines = content.split("\n");
198
+ const elements = [];
199
+ let i = 0;
200
+ while (i < lines.length) {
201
+ const line = lines[i];
202
+ const key = `md-line-${i}`;
203
+ if (!line.trim()) {
204
+ i++;
205
+ continue;
206
+ }
207
+ const headerMatch = line.match(/^(#{1,3})\s+(.*)/);
208
+ if (headerMatch) {
209
+ const level = headerMatch[1].length;
210
+ const Tag = `h${level + 3}`;
211
+ elements.push(/* @__PURE__ */ jsx2(Tag, { className: `hsk-markdown-h${level}`, children: parseInline(headerMatch[2], key) }, key));
212
+ i++;
213
+ continue;
214
+ }
215
+ if (line.match(/^[-*]\s+/)) {
216
+ const listItems = [];
217
+ while (i < lines.length && lines[i].match(/^[-*]\s+/)) {
218
+ const itemText = lines[i].replace(/^[-*]\s+/, "");
219
+ listItems.push(/* @__PURE__ */ jsx2("li", { children: parseInline(itemText, `li-${i}`) }, `li-${i}`));
220
+ i++;
221
+ }
222
+ elements.push(/* @__PURE__ */ jsx2("ul", { className: "hsk-markdown-list", children: listItems }, `ul-${key}`));
223
+ continue;
224
+ }
225
+ if (line.trim().startsWith("|")) {
226
+ const tableRows = [];
227
+ let isHeader = true;
228
+ while (i < lines.length && lines[i].trim().startsWith("|")) {
229
+ const rowLine = lines[i].trim();
230
+ if (rowLine.match(/^\|[-:| ]+\|$/)) {
231
+ i++;
232
+ isHeader = false;
233
+ continue;
234
+ }
235
+ const cells = rowLine.split("|").slice(1, -1).map((c) => c.trim());
236
+ const Tag = isHeader ? "th" : "td";
237
+ tableRows.push(
238
+ /* @__PURE__ */ jsx2("tr", { children: cells.map((cell, cIdx) => /* @__PURE__ */ jsx2(Tag, { children: parseInline(cell, `td-${i}-${cIdx}`) }, `td-${i}-${cIdx}`)) }, `tr-${i}`)
239
+ );
240
+ i++;
241
+ }
242
+ elements.push(
243
+ /* @__PURE__ */ jsx2("div", { className: "hsk-table-wrapper", children: /* @__PURE__ */ jsx2("table", { className: "hsk-markdown-table", children: /* @__PURE__ */ jsx2("tbody", { children: tableRows }) }) }, `table-wrapper-${key}`)
244
+ );
245
+ continue;
246
+ }
247
+ elements.push(
248
+ /* @__PURE__ */ jsx2("p", { className: "hsk-markdown-p", children: parseInline(line, key) }, key)
249
+ );
250
+ i++;
251
+ }
252
+ return /* @__PURE__ */ jsx2(Fragment2, { children: elements });
253
+ }
254
+
255
+ // src/components/ChatWidget.tsx
256
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
257
+ var SparkleIcon = () => /* @__PURE__ */ jsx3("svg", { 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" }) });
258
+ var ArrowUpIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
259
+ /* @__PURE__ */ jsx3("path", { d: "m5 12 7-7 7 7" }),
260
+ /* @__PURE__ */ jsx3("path", { d: "M12 19V5" })
261
+ ] });
262
+ function SourceCard({ source, defaultCurrency, onSelect }) {
263
+ return /* @__PURE__ */ jsxs2("div", { className: "hsk-source-card", onClick: () => onSelect?.(source), children: [
264
+ source.image && /* @__PURE__ */ jsx3("img", { src: source.image, alt: source.name, className: "hsk-source-img" }),
265
+ /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0 }, children: [
266
+ /* @__PURE__ */ jsx3("div", { className: "hsk-source-name", children: source.name }),
267
+ source.price && /* @__PURE__ */ jsxs2("div", { className: "hsk-source-price", children: [
268
+ source.currency ?? defaultCurrency,
269
+ " ",
270
+ source.price
271
+ ] })
272
+ ] })
273
+ ] });
274
+ }
275
+ function ChatWidget({
276
+ title = "AI Shopping Assistant",
277
+ placeholder = "Ask about anything in our store\u2026",
278
+ emptyStateText = "Ask me anything about our products",
279
+ emptyStateSuggestions = '"Find me headphones under KSh 5,000" \xB7 "Gift ideas"',
280
+ defaultCurrency = "KES",
281
+ className,
282
+ theme,
283
+ classNames = {},
284
+ onSelectSource
285
+ }) {
286
+ const { messages, sources, loading, error, send, reset } = useKiku();
287
+ const [input, setInput] = useState2("");
288
+ const bottomRef = useRef2(null);
289
+ const textareaRef = useRef2(null);
290
+ useEffect2(() => {
291
+ bottomRef.current?.scrollIntoView({ behavior: "smooth" });
292
+ }, [messages, loading]);
293
+ const handleSend = async () => {
294
+ const q = input.trim();
295
+ if (!q || loading) return;
296
+ setInput("");
297
+ if (textareaRef.current) textareaRef.current.style.height = "auto";
298
+ await send(q);
299
+ };
300
+ const handleKey = (e) => {
301
+ if (e.key === "Enter" && !e.shiftKey) {
302
+ e.preventDefault();
303
+ handleSend();
304
+ }
305
+ };
306
+ const handleInput = (e) => {
307
+ setInput(e.target.value);
308
+ const t = e.target;
309
+ t.style.height = "auto";
310
+ t.style.height = Math.min(t.scrollHeight, 120) + "px";
311
+ };
312
+ const customStyles = {
313
+ ...theme?.primaryColor && { "--hsk-primary": theme.primaryColor },
314
+ ...theme?.backgroundColor && { "--hsk-bg": theme.backgroundColor },
315
+ ...theme?.textColor && { "--hsk-text": theme.textColor },
316
+ ...theme?.fontFamily && { "--hsk-font": theme.fontFamily },
317
+ ...theme?.borderRadius && { "--hsk-border-radius": theme.borderRadius }
318
+ };
319
+ return /* @__PURE__ */ jsxs2(
320
+ "div",
321
+ {
322
+ className: cn("hsk-chat-widget", classNames.root, className),
323
+ style: customStyles,
324
+ children: [
325
+ /* @__PURE__ */ jsxs2("div", { className: cn("hsk-chat-header", classNames.header), children: [
326
+ /* @__PURE__ */ jsx3("span", { className: "hsk-chat-header-icon", children: /* @__PURE__ */ jsx3(SparkleIcon, {}) }),
327
+ /* @__PURE__ */ jsx3("span", { className: "hsk-chat-title", children: title }),
328
+ /* @__PURE__ */ jsx3("span", { className: "hsk-chat-badge", children: "AI" }),
329
+ messages.length > 0 && /* @__PURE__ */ jsx3("button", { className: "hsk-chat-reset", onClick: reset, style: { marginLeft: "auto" }, children: "Clear" })
330
+ ] }),
331
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-chat-messages", children: [
332
+ messages.length === 0 ? /* @__PURE__ */ jsxs2("div", { className: "hsk-chat-empty", children: [
333
+ /* @__PURE__ */ jsx3("div", { className: "hsk-chat-empty-icon", children: /* @__PURE__ */ jsx3(SparkleIcon, {}) }),
334
+ /* @__PURE__ */ jsx3("div", { children: emptyStateText }),
335
+ /* @__PURE__ */ jsx3("div", { className: "hsk-chat-empty-suggestions", children: emptyStateSuggestions })
336
+ ] }) : messages.map((msg, idx) => /* @__PURE__ */ jsxs2("div", { children: [
337
+ /* @__PURE__ */ jsxs2("div", { className: `hsk-msg-row ${msg.role}`, children: [
338
+ /* @__PURE__ */ jsx3("div", { className: cn("hsk-msg-avatar", msg.role === "assistant" ? "ai" : "user"), children: msg.role === "assistant" ? /* @__PURE__ */ jsx3(SparkleIcon, {}) : "U" }),
339
+ /* @__PURE__ */ jsx3("div", { className: cn("hsk-msg-bubble", msg.role, classNames.messageBubble), children: renderMarkdown(msg.content) })
340
+ ] }),
341
+ msg.role === "assistant" && idx === messages.length - 1 && sources.length > 0 && /* @__PURE__ */ jsx3("div", { className: "hsk-sources-container", children: /* @__PURE__ */ jsx3("div", { className: "hsk-sources", children: sources.map((src, si) => /* @__PURE__ */ jsx3(SourceCard, { source: src, defaultCurrency, onSelect: onSelectSource }, si)) }) })
342
+ ] }, idx)),
343
+ loading && /* @__PURE__ */ jsxs2("div", { className: "hsk-msg-row", children: [
344
+ /* @__PURE__ */ jsx3("div", { className: "hsk-msg-avatar ai", children: /* @__PURE__ */ jsx3(SparkleIcon, {}) }),
345
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-pending", role: "status", "aria-live": "polite", children: [
346
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-pending-glyph", children: [
347
+ /* @__PURE__ */ jsx3("span", { className: "hsk-pending-ring" }),
348
+ /* @__PURE__ */ jsx3("span", { className: "hsk-pending-dot" })
349
+ ] }),
350
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-pending-text", children: [
351
+ /* @__PURE__ */ jsx3("span", { className: "hsk-pending-step step-1", children: "Searching catalog" }),
352
+ /* @__PURE__ */ jsx3("span", { className: "hsk-pending-step step-2", children: "Reasoning" }),
353
+ /* @__PURE__ */ jsx3("span", { className: "hsk-pending-step step-3", children: "Composing" })
354
+ ] })
355
+ ] })
356
+ ] }),
357
+ error && /* @__PURE__ */ jsx3("div", { className: "hsk-chat-error", children: (() => {
358
+ try {
359
+ const parsed = JSON.parse(error);
360
+ return parsed.error || parsed.message || error;
361
+ } catch {
362
+ return error;
363
+ }
364
+ })() }),
365
+ /* @__PURE__ */ jsx3("div", { ref: bottomRef })
366
+ ] }),
367
+ /* @__PURE__ */ jsxs2("div", { className: "hsk-chat-input-area", children: [
368
+ /* @__PURE__ */ jsx3(
369
+ "textarea",
370
+ {
371
+ ref: textareaRef,
372
+ className: cn("hsk-chat-input", classNames.input),
373
+ value: input,
374
+ onChange: handleInput,
375
+ onKeyDown: handleKey,
376
+ placeholder,
377
+ rows: 1,
378
+ disabled: loading
379
+ }
380
+ ),
381
+ /* @__PURE__ */ jsx3(
382
+ "button",
383
+ {
384
+ className: "hsk-chat-send",
385
+ onClick: handleSend,
386
+ disabled: !input.trim() || loading,
387
+ "aria-label": "Send message",
388
+ children: /* @__PURE__ */ jsx3(ArrowUpIcon, {})
389
+ }
390
+ )
391
+ ] })
392
+ ]
393
+ }
394
+ );
395
+ }
396
+
397
+ // src/components/KikuButton.tsx
398
+ import { useState as useState3, useEffect as useEffect3, useRef as useRef3, useCallback } from "react";
399
+ import { createPortal } from "react-dom";
400
+ import { useKiku as useKiku2 } from "@akropolys/sdk";
401
+ import { usePaymentPolling } from "@akropolys/sdk";
402
+ import { useAkropolysContext as useAkropolysContext2 } from "@akropolys/sdk";
403
+
404
+ // src/components/ComparisonMatrix.tsx
405
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
406
+ function extractSize(p) {
407
+ const name = p.name;
408
+ const cat = (p.category || "").toLowerCase();
409
+ const mExplicit = name.match(/(\d+(?:\.\d+)?)\s*(?:inch(?:es)?|["'″])/i);
410
+ if (mExplicit) {
411
+ return `${mExplicit[1]} inches`;
412
+ }
413
+ if (cat.includes("tv") || cat.includes("audio")) {
414
+ const mTv = name.match(/\b(\d{2})(?:[a-zA-Z]|\b)/);
415
+ if (mTv) {
416
+ const size = parseInt(mTv[1], 10);
417
+ if (size >= 24 && size <= 120) {
418
+ return `${size} inches`;
419
+ }
420
+ }
421
+ }
422
+ return null;
423
+ }
424
+ function extractResolution(name) {
425
+ if (/\b4K\b/i.test(name)) return "4K Ultra HD (2160p)";
426
+ if (/\bUHD\b/i.test(name)) return "Ultra HD (2160p)";
427
+ if (/\b(?:Full HD|FHD|1080p)\b/i.test(name)) return "Full HD (1080p)";
428
+ if (/\b(?:HD|720p)\b/i.test(name)) return "HD (720p)";
429
+ return null;
430
+ }
431
+ function extractStorage(name) {
432
+ const m = name.match(/(\d+)\s*GB(?!\s*RAM)/i);
433
+ return m ? `${m[1]} GB` : null;
434
+ }
435
+ function extractRAM(name) {
436
+ const m = name.match(/(\d+)\s*GB\s*RAM/i);
437
+ return m ? `${m[1]} GB` : null;
438
+ }
439
+ function extractCamera(name) {
440
+ const m = name.match(/(\d+)\s*MP/i);
441
+ return m ? `${m[1]} MP` : null;
442
+ }
443
+ function extractBattery(name) {
444
+ const m = name.match(/(\d{3,5})\s*mAh/i);
445
+ return m ? `${m[1]} mAh` : null;
446
+ }
447
+ function buildRows(products, currency) {
448
+ const rows = [];
449
+ rows.push({
450
+ label: "Product Preview",
451
+ values: products.map((s) => s.image || null),
452
+ type: "image"
453
+ });
454
+ const prices = products.map((s) => {
455
+ const n = parseFloat(String(s.price ?? "").replace(/[^0-9.]/g, ""));
456
+ return isNaN(n) ? null : n;
457
+ });
458
+ const priceLabels = products.map((s, i) => {
459
+ const c = s.currency || currency;
460
+ const n = prices[i];
461
+ return n !== null ? `${c} ${n.toLocaleString("en-KE", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : null;
462
+ });
463
+ const validPrices = prices.filter((p) => p !== null);
464
+ const minPrice = validPrices.length ? Math.min(...validPrices) : null;
465
+ rows.push({
466
+ label: "Price",
467
+ values: priceLabels,
468
+ type: "price",
469
+ bestIdx: minPrice !== null ? prices.indexOf(minPrice) : void 0
470
+ });
471
+ const brands = products.map((s) => s.brand || null);
472
+ if (brands.some(Boolean)) rows.push({ label: "Brand", values: brands });
473
+ const specDefs = [
474
+ { label: "Display Size", fn: extractSize },
475
+ { label: "Resolution", fn: (p) => extractResolution(p.name), higherIsBetter: true },
476
+ { label: "Storage", fn: (p) => extractStorage(p.name), higherIsBetter: true },
477
+ { label: "RAM", fn: (p) => extractRAM(p.name), higherIsBetter: true },
478
+ { label: "Camera", fn: (p) => extractCamera(p.name), higherIsBetter: true },
479
+ { label: "Battery", fn: (p) => extractBattery(p.name), higherIsBetter: true }
480
+ ];
481
+ const resOrder = ["4K Ultra HD (2160p)", "Ultra HD (2160p)", "Full HD (1080p)", "HD (720p)"];
482
+ for (const { label, fn, higherIsBetter } of specDefs) {
483
+ const vals = products.map((s) => fn(s));
484
+ if (!vals.some(Boolean)) continue;
485
+ let bestIdx;
486
+ if (higherIsBetter && vals.filter(Boolean).length > 1) {
487
+ if (label === "Resolution") {
488
+ bestIdx = vals.reduce((best, v, i) => {
489
+ const rank = resOrder.indexOf(v ?? "");
490
+ const bestRank = resOrder.indexOf(vals[best] ?? "");
491
+ return rank !== -1 && (bestRank === -1 || rank < bestRank) ? i : best;
492
+ }, 0);
493
+ } else {
494
+ const nums = vals.map((v) => parseFloat((v ?? "").replace(/[^0-9.]/g, "")));
495
+ const max = Math.max(...nums.filter((n) => !isNaN(n)));
496
+ bestIdx = nums.indexOf(max);
497
+ }
498
+ }
499
+ rows.push({ label, values: vals, bestIdx });
500
+ }
501
+ const avail = products.map((s) => {
502
+ const a = s.availability ?? "";
503
+ if (!a) return null;
504
+ if (/in.?stock/i.test(a)) return "In-Stock";
505
+ if (/out.?of.?stock/i.test(a)) return "Out of Stock";
506
+ return a;
507
+ });
508
+ if (avail.some(Boolean)) {
509
+ rows.push({ label: "Availability", values: avail, type: "availability" });
510
+ }
511
+ const cats = products.map((s) => s.category || null);
512
+ if (cats.some(Boolean)) rows.push({ label: "Category", values: cats });
513
+ return rows;
514
+ }
515
+ function ImageCell({ value, name }) {
516
+ if (!value) {
517
+ return /* @__PURE__ */ jsx4("div", { style: { fontSize: 28, textAlign: "center" }, children: "\u{1F4E6}" });
518
+ }
519
+ return /* @__PURE__ */ jsx4(
520
+ "img",
521
+ {
522
+ src: value,
523
+ alt: name,
524
+ style: {
525
+ width: 72,
526
+ height: 72,
527
+ objectFit: "contain",
528
+ borderRadius: 8,
529
+ background: "#f5f5f5",
530
+ display: "block"
531
+ }
532
+ }
533
+ );
534
+ }
535
+ function AvailabilityCell({ value }) {
536
+ if (!value) return /* @__PURE__ */ jsx4("span", { style: { color: "#9ca3af" }, children: "\u2014" });
537
+ const inStock = /in.?stock/i.test(value);
538
+ return /* @__PURE__ */ jsxs3("span", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: 13, color: "var(--hsk-text, #111827)" }, children: [
539
+ /* @__PURE__ */ jsx4("span", { style: {
540
+ width: 8,
541
+ height: 8,
542
+ borderRadius: "50%",
543
+ flexShrink: 0,
544
+ background: inStock ? "#22c55e" : "#ef4444",
545
+ boxShadow: inStock ? "0 0 0 3px rgba(34,197,94,0.2)" : "0 0 0 3px rgba(239,68,68,0.2)",
546
+ display: "inline-block"
547
+ } }),
548
+ value
549
+ ] });
550
+ }
551
+ function ComparisonMatrix({ sources, defaultCurrency = "KES" }) {
552
+ if (sources.length < 2) return null;
553
+ const products = sources.slice(0, 3);
554
+ const rows = buildRows(products, defaultCurrency);
555
+ const colTemplate = `140px repeat(${products.length}, 1fr)`;
556
+ const labelStyle = {
557
+ padding: "10px 12px",
558
+ fontSize: 11,
559
+ fontWeight: 700,
560
+ // Solid dark fallback — never inherit a muted ancestor color
561
+ color: "var(--hsk-text-muted, #4b5563)",
562
+ textTransform: "uppercase",
563
+ letterSpacing: "0.05em",
564
+ borderBottom: "1px solid var(--hsk-border, rgba(0,0,0,0.07))",
565
+ verticalAlign: "middle",
566
+ whiteSpace: "nowrap",
567
+ display: "flex",
568
+ alignItems: "center"
569
+ };
570
+ const cellBase = {
571
+ padding: "10px 14px",
572
+ fontSize: 13,
573
+ // Explicit color so cells are always readable regardless of parent theme
574
+ color: "var(--hsk-text, #111827)",
575
+ borderBottom: "1px solid var(--hsk-border, rgba(0,0,0,0.07))",
576
+ verticalAlign: "middle",
577
+ display: "flex",
578
+ alignItems: "center"
579
+ };
580
+ return /* @__PURE__ */ jsxs3(
581
+ "div",
582
+ {
583
+ className: "hsk-compare-matrix",
584
+ style: {
585
+ marginTop: 10,
586
+ borderRadius: 12,
587
+ overflow: "hidden",
588
+ border: "1px solid var(--hsk-border, rgba(0,0,0,0.09))",
589
+ background: "var(--hsk-surface, #fff)",
590
+ fontSize: 13
591
+ },
592
+ children: [
593
+ /* @__PURE__ */ jsxs3("div", { style: { display: "grid", gridTemplateColumns: colTemplate, background: "var(--hsk-surface2, #f9fafb)", borderBottom: "2px solid var(--hsk-border, rgba(0,0,0,0.09))" }, children: [
594
+ /* @__PURE__ */ jsx4("div", { style: { ...labelStyle, borderBottom: "none", color: "var(--hsk-text, #111)", fontSize: 12 }, children: "Feature" }),
595
+ products.map((p, i) => /* @__PURE__ */ jsx4(
596
+ "a",
597
+ {
598
+ href: p.url || "#",
599
+ target: "_blank",
600
+ rel: "noopener noreferrer",
601
+ style: {
602
+ display: "flex",
603
+ alignItems: "center",
604
+ padding: "10px 14px",
605
+ fontSize: 12,
606
+ fontWeight: 700,
607
+ color: "var(--hsk-primary, #16a34a)",
608
+ textDecoration: "none",
609
+ lineHeight: 1.3,
610
+ borderLeft: i > 0 ? "1px solid var(--hsk-border, rgba(0,0,0,0.07))" : "none"
611
+ },
612
+ children: p.name
613
+ },
614
+ i
615
+ ))
616
+ ] }),
617
+ rows.map((row, rowIdx) => /* @__PURE__ */ jsxs3(
618
+ "div",
619
+ {
620
+ style: {
621
+ display: "grid",
622
+ gridTemplateColumns: colTemplate,
623
+ background: rowIdx % 2 === 1 ? "var(--hsk-surface2, rgba(0,0,0,0.015))" : "transparent"
624
+ },
625
+ children: [
626
+ /* @__PURE__ */ jsx4("div", { style: labelStyle, children: row.label }),
627
+ products.map((p, i) => {
628
+ const val = row.values[i];
629
+ const isBest = row.bestIdx === i && row.values.filter(Boolean).length > 1;
630
+ if (row.type === "image") {
631
+ return /* @__PURE__ */ jsx4("div", { style: { ...cellBase, justifyContent: "center", padding: "12px", borderLeft: i > 0 ? "1px solid var(--hsk-border, rgba(0,0,0,0.07))" : "none" }, children: /* @__PURE__ */ jsx4(ImageCell, { value: val, name: p.name }) }, i);
632
+ }
633
+ if (row.type === "availability") {
634
+ return /* @__PURE__ */ jsx4("div", { style: { ...cellBase, borderLeft: i > 0 ? "1px solid var(--hsk-border, rgba(0,0,0,0.07))" : "none" }, children: /* @__PURE__ */ jsx4(AvailabilityCell, { value: val }) }, i);
635
+ }
636
+ return /* @__PURE__ */ jsx4(
637
+ "div",
638
+ {
639
+ style: {
640
+ ...cellBase,
641
+ fontWeight: isBest ? 700 : 400,
642
+ // Always use a solid dark fallback — never 'inherit' which can be muted/invisible
643
+ color: isBest ? "var(--hsk-primary, #ea580c)" : row.type === "price" ? "var(--hsk-text, #374151)" : "var(--hsk-text, #111827)",
644
+ borderLeft: i > 0 ? "1px solid var(--hsk-border, rgba(0,0,0,0.07))" : "none"
645
+ },
646
+ children: val ?? /* @__PURE__ */ jsx4("span", { style: { color: "#9ca3af" }, children: "\u2014" })
647
+ },
648
+ i
649
+ );
650
+ })
651
+ ]
652
+ },
653
+ rowIdx
654
+ ))
655
+ ]
656
+ }
657
+ );
658
+ }
659
+
660
+ // src/components/KikuButton.tsx
661
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
662
+ var AkropolysAIcon = ({ className, size = 18 }) => /* @__PURE__ */ jsxs4(
663
+ "svg",
664
+ {
665
+ className: cn("hsk-brand-a", className),
666
+ width: size,
667
+ height: size,
668
+ viewBox: "0 0 28 30",
669
+ fill: "none",
670
+ xmlns: "http://www.w3.org/2000/svg",
671
+ "aria-label": "Akropolys",
672
+ children: [
673
+ /* @__PURE__ */ jsx5(
674
+ "path",
675
+ {
676
+ d: "M14.5 4.5 L6.5 25",
677
+ stroke: "currentColor",
678
+ strokeWidth: "2.2",
679
+ strokeLinecap: "round"
680
+ }
681
+ ),
682
+ /* @__PURE__ */ jsx5(
683
+ "path",
684
+ {
685
+ d: "M14.5 4.5 L22.5 25",
686
+ stroke: "currentColor",
687
+ strokeWidth: "4.2",
688
+ strokeLinecap: "round"
689
+ }
690
+ ),
691
+ /* @__PURE__ */ jsx5(
692
+ "path",
693
+ {
694
+ d: "M9.5 17 H19.5",
695
+ stroke: "currentColor",
696
+ strokeWidth: "2",
697
+ strokeLinecap: "round"
698
+ }
699
+ ),
700
+ /* @__PURE__ */ jsx5(
701
+ "path",
702
+ {
703
+ d: "M3 25 H10",
704
+ stroke: "currentColor",
705
+ strokeWidth: "2",
706
+ strokeLinecap: "round"
707
+ }
708
+ ),
709
+ /* @__PURE__ */ jsx5(
710
+ "path",
711
+ {
712
+ d: "M19 25 H26",
713
+ stroke: "currentColor",
714
+ strokeWidth: "2.5",
715
+ strokeLinecap: "round"
716
+ }
717
+ ),
718
+ /* @__PURE__ */ jsx5(
719
+ "path",
720
+ {
721
+ d: "M8.5 2.5 C10 2.5, 11 3.5, 11 5 C11 7, 8.5 8.5, 7.5 9.5",
722
+ stroke: "currentColor",
723
+ strokeWidth: "2",
724
+ strokeLinecap: "round",
725
+ fill: "none",
726
+ className: "hsk-akr-breath"
727
+ }
728
+ )
729
+ ]
730
+ }
731
+ );
732
+ var SparkleIcon2 = AkropolysAIcon;
733
+ 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: [
734
+ /* @__PURE__ */ jsx5("path", { d: "m5 12 7-7 7 7" }),
735
+ /* @__PURE__ */ jsx5("path", { d: "M12 19V5" })
736
+ ] });
737
+ var CloseIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
738
+ /* @__PURE__ */ jsx5("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
739
+ /* @__PURE__ */ jsx5("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
740
+ ] });
741
+ var ChevronRightIcon = () => /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("path", { d: "m9 18 6-6-6-6" }) });
742
+ var HistoryIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
743
+ /* @__PURE__ */ jsx5("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
744
+ /* @__PURE__ */ jsx5("path", { d: "M3 3v5h5" }),
745
+ /* @__PURE__ */ jsx5("path", { d: "M12 7v5l4 2" })
746
+ ] });
747
+ var NewChatIcon = () => /* @__PURE__ */ jsx5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("path", { d: "M12 5v14M5 12h14" }) });
748
+ var ShoppingBagIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
749
+ /* @__PURE__ */ jsx5("path", { d: "M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z" }),
750
+ /* @__PURE__ */ jsx5("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
751
+ /* @__PURE__ */ jsx5("path", { d: "M16 10a4 4 0 0 1-8 0" })
752
+ ] });
753
+ var DEFAULT_CHIPS = [
754
+ "Cheapest smartphone",
755
+ "Smart TV under KSh 20,000",
756
+ "Noise-cancelling headphones",
757
+ "Best laptop for students"
758
+ ];
759
+ var SESSIONS_KEY = "akropolys_chat_sessions";
760
+ var MAX_SESSIONS = 20;
761
+ function loadSessions() {
762
+ try {
763
+ if (typeof window === "undefined") return [];
764
+ const raw = localStorage.getItem(SESSIONS_KEY);
765
+ return raw ? JSON.parse(raw) : [];
766
+ } catch {
767
+ return [];
768
+ }
769
+ }
770
+ function saveSessions(sessions) {
771
+ try {
772
+ if (typeof window === "undefined") return;
773
+ localStorage.setItem(SESSIONS_KEY, JSON.stringify(sessions.slice(0, MAX_SESSIONS)));
774
+ } catch {
775
+ }
776
+ }
777
+ function relativeTime(ts) {
778
+ const diff = Date.now() - ts;
779
+ const mins = Math.floor(diff / 6e4);
780
+ if (mins < 1) return "just now";
781
+ if (mins < 60) return `${mins}m ago`;
782
+ const hrs = Math.floor(mins / 60);
783
+ if (hrs < 24) return `${hrs}h ago`;
784
+ const days = Math.floor(hrs / 24);
785
+ return `${days}d ago`;
786
+ }
787
+ function CartContextCard({ cart, defaultCurrency }) {
788
+ if (!cart.items || cart.items.length === 0) return null;
789
+ const currency = cart.currency || defaultCurrency;
790
+ const total = cart.total ?? cart.items.reduce((s, i) => s + i.price_numeric * i.quantity, 0);
791
+ return /* @__PURE__ */ jsxs4("div", { className: "hsk-cart-card", children: [
792
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cart-card-header", children: [
793
+ /* @__PURE__ */ jsx5(ShoppingBagIcon, {}),
794
+ /* @__PURE__ */ jsxs4("span", { children: [
795
+ "Your cart \xB7 ",
796
+ cart.item_count ?? cart.items.length,
797
+ " item",
798
+ (cart.item_count ?? cart.items.length) !== 1 ? "s" : ""
799
+ ] })
800
+ ] }),
801
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cart-items", children: cart.items.map((item) => /* @__PURE__ */ jsxs4("div", { className: "hsk-cart-item", children: [
802
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cart-item-img-wrap", children: [
803
+ item.image ? /* @__PURE__ */ jsx5("img", { className: "hsk-cart-item-img", src: item.image, alt: item.name, loading: "lazy" }) : /* @__PURE__ */ jsx5("div", { className: "hsk-cart-item-img-placeholder", children: /* @__PURE__ */ jsx5(ShoppingBagIcon, {}) }),
804
+ item.quantity > 1 && /* @__PURE__ */ jsx5("span", { className: "hsk-cart-item-qty", children: item.quantity })
805
+ ] }),
806
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cart-item-info", children: [
807
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cart-item-name", children: item.name }),
808
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cart-item-price", children: [
809
+ item.quantity > 1 && /* @__PURE__ */ jsxs4("span", { className: "hsk-cart-item-qty-label", children: [
810
+ item.quantity,
811
+ "\xD7 "
812
+ ] }),
813
+ currency,
814
+ " ",
815
+ (item.price_numeric * item.quantity).toLocaleString()
816
+ ] })
817
+ ] })
818
+ ] }, item.id)) }),
819
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cart-total", children: [
820
+ /* @__PURE__ */ jsx5("span", { children: "Total" }),
821
+ /* @__PURE__ */ jsxs4("span", { className: "hsk-cart-total-amount", children: [
822
+ currency,
823
+ " ",
824
+ total.toLocaleString()
825
+ ] })
826
+ ] })
827
+ ] });
828
+ }
829
+ function ActionPills({ cart, actionType, onSend, loading }) {
830
+ const hasItems = cart.items && cart.items.length > 0;
831
+ const lastItem = hasItems ? cart.items[cart.items.length - 1] : null;
832
+ const pills = [];
833
+ if (hasItems && lastItem) {
834
+ if (actionType === "add_to_cart") {
835
+ pills.push({ emoji: "\u2795", label: "Add 1 more", query: `Add one more ${lastItem.name} to my cart` });
836
+ pills.push({ emoji: "\u{1F5D1}\uFE0F", label: `Remove ${lastItem.name.split(" ")[0]}`, query: `Remove the ${lastItem.name} from my cart` });
837
+ }
838
+ if (cart.items.length > 1) {
839
+ pills.push({ emoji: "\u{1F6D2}", label: "View cart", query: "Show me my cart" });
840
+ }
841
+ pills.push({ emoji: "\u{1F4B3}", label: "Checkout", query: "Proceed to checkout" });
842
+ pills.push({ emoji: "\u{1F6CD}\uFE0F", label: "Keep shopping", query: "What else do you recommend?" });
843
+ } else if (actionType === "clear_cart" || actionType === "remove_from_cart") {
844
+ pills.push({ emoji: "\u{1F6CD}\uFE0F", label: "Continue shopping", query: "Show me popular products" });
845
+ pills.push({ emoji: "\u{1F50D}", label: "Search again", query: "Help me find something" });
846
+ } else if (actionType === "view_cart") {
847
+ if (hasItems) {
848
+ pills.push({ emoji: "\u{1F4B3}", label: "Checkout", query: "Proceed to checkout" });
849
+ pills.push({ emoji: "\u{1F5D1}\uFE0F", label: "Clear cart", query: "Clear my cart" });
850
+ }
851
+ }
852
+ if (pills.length === 0) return null;
853
+ return /* @__PURE__ */ jsx5("div", { className: "hsk-action-pills", children: pills.map((pill) => /* @__PURE__ */ jsxs4(
854
+ "button",
855
+ {
856
+ className: "hsk-action-pill",
857
+ onClick: () => onSend(pill.query),
858
+ disabled: loading,
859
+ children: [
860
+ /* @__PURE__ */ jsx5("span", { className: "hsk-pill-emoji", children: pill.emoji }),
861
+ pill.label
862
+ ]
863
+ },
864
+ pill.query
865
+ )) });
866
+ }
867
+ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
868
+ const client = useAkropolysContext2();
869
+ const isProperty = client?.vertical === "property";
870
+ const railRef = useRef3(null);
871
+ const [showNext, setShowNext] = useState3(false);
872
+ const measure = useCallback(() => {
873
+ const el = railRef.current;
874
+ if (!el) return;
875
+ const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 8;
876
+ setShowNext(el.scrollWidth > el.clientWidth + 4 && !atEnd);
877
+ }, []);
878
+ useEffect3(() => {
879
+ measure();
880
+ const el = railRef.current;
881
+ if (!el) return;
882
+ const ro = new ResizeObserver(measure);
883
+ ro.observe(el);
884
+ el.addEventListener("scroll", measure, { passive: true });
885
+ return () => {
886
+ ro.disconnect();
887
+ el.removeEventListener("scroll", measure);
888
+ };
889
+ }, [measure, sources]);
890
+ const scrollNext = () => {
891
+ railRef.current?.scrollBy({ left: 170, behavior: "smooth" });
892
+ };
893
+ return /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-sources-wrap", children: [
894
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-sources", ref: railRef, children: sources.map((src, si) => /* @__PURE__ */ jsxs4(
895
+ "div",
896
+ {
897
+ className: "hsk-cb-source",
898
+ style: { animationDelay: `${si * 50}ms` },
899
+ onClick: () => onSelectSource?.(src),
900
+ children: [
901
+ src.image ? /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-src-imgwrap", style: { position: "relative" }, children: [
902
+ /* @__PURE__ */ jsx5("img", { src: src.image, alt: src.name, loading: "lazy" }),
903
+ isProperty && /* @__PURE__ */ jsx5("div", { style: {
904
+ position: "absolute",
905
+ top: "6px",
906
+ right: "6px",
907
+ background: "rgba(14, 14, 15, 0.75)",
908
+ backdropFilter: "blur(4px)",
909
+ borderRadius: "50%",
910
+ width: "24px",
911
+ height: "24px",
912
+ display: "flex",
913
+ alignItems: "center",
914
+ justifyContent: "center",
915
+ color: "#fbbf24",
916
+ // Gold sparkle badge
917
+ boxShadow: "0 2px 4px rgba(0,0,0,0.2)"
918
+ }, children: /* @__PURE__ */ jsx5(SparkleIcon2, { size: 12 }) })
919
+ ] }) : /* @__PURE__ */ jsx5("div", { className: "hsk-cb-src-imgwrap-empty", children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
920
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-src-info", children: [
921
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-src-name", children: src.name }),
922
+ src.price && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-src-price", children: [
923
+ src.currency ?? defaultCurrency,
924
+ " ",
925
+ parseFloat(String(src.price).replace(/[^0-9.]/g, "") || "0").toLocaleString()
926
+ ] })
927
+ ] })
928
+ ]
929
+ },
930
+ si
931
+ )) }),
932
+ showNext && /* @__PURE__ */ jsxs4(Fragment3, { children: [
933
+ /* @__PURE__ */ jsx5(
934
+ "div",
935
+ {
936
+ className: "hsk-cb-sources-fade",
937
+ style: { background: "linear-gradient(to right, transparent, var(--hsk-fade-bg, #0e0e0f))" }
938
+ }
939
+ ),
940
+ /* @__PURE__ */ jsx5("button", { className: "hsk-cb-sources-next", onClick: scrollNext, "aria-label": "See more", children: /* @__PURE__ */ jsx5(ChevronRightIcon, {}) })
941
+ ] })
942
+ ] });
943
+ }
944
+ function stripMarkdownTables(content) {
945
+ const lines = content.split("\n");
946
+ const out = [];
947
+ for (const line of lines) {
948
+ if (line.trim().startsWith("|")) continue;
949
+ out.push(line);
950
+ }
951
+ return out.join("\n").replace(/\n{3,}/g, "\n\n").trim();
952
+ }
953
+ function SmartContextPills({
954
+ intent,
955
+ sources,
956
+ onSend,
957
+ loading
958
+ }) {
959
+ const client = useAkropolysContext2();
960
+ const isProperty = client?.vertical === "property";
961
+ if (!intent) return null;
962
+ const pills = [];
963
+ const cheapest = sources.length > 0 ? sources.reduce((min, s) => {
964
+ const p = parseFloat(String(s.price ?? "").replace(/[^0-9.]/g, ""));
965
+ const m = parseFloat(String(min.price ?? "").replace(/[^0-9.]/g, ""));
966
+ return !isNaN(p) && (isNaN(m) || p < m) ? s : min;
967
+ }, sources[0]) : null;
968
+ const firstName = sources[0]?.name ?? "";
969
+ const firstTwo = sources.slice(0, 2).map((s) => s.name);
970
+ if (intent === "search" && sources.length > 0) {
971
+ if (firstTwo.length >= 2) {
972
+ pills.push({
973
+ emoji: "\u2696\uFE0F",
974
+ label: "Compare top 2",
975
+ query: `Compare the ${firstTwo[0]} and ${firstTwo[1]}`
976
+ });
977
+ }
978
+ if (cheapest && !isProperty) {
979
+ const short = cheapest.name.split(" ").slice(0, 3).join(" ");
980
+ pills.push({
981
+ emoji: "\u{1F6D2}",
982
+ label: `Add ${short}`,
983
+ query: `Add the ${cheapest.name} to my cart`
984
+ });
985
+ }
986
+ if (isProperty) {
987
+ pills.push({ emoji: "\u{1F4B0}", label: "Under KSh 5M", query: "Show me options under KSh 5,000,000" });
988
+ } else {
989
+ pills.push({ emoji: "\u{1F4B0}", label: "Under KSh 20K", query: "Show me options under KSh 20,000" });
990
+ }
991
+ } else if (intent === "compare" && sources.length > 0) {
992
+ if (cheapest && !isProperty) {
993
+ const short = cheapest.name.split(" ").slice(0, 3).join(" ");
994
+ pills.push({
995
+ emoji: "\u{1F6D2}",
996
+ label: `Add ${short}`,
997
+ query: `Add the ${cheapest.name} to my cart`
998
+ });
999
+ }
1000
+ if (firstName) {
1001
+ pills.push({
1002
+ emoji: "\u{1F50D}",
1003
+ label: "Similar options",
1004
+ query: isProperty ? `Show me more properties similar to the ${firstName}` : `Show me more products similar to the ${firstName}`
1005
+ });
1006
+ }
1007
+ pills.push({ emoji: "\u{1F4A1}", label: "Which is best?", query: "Which one would you recommend and why?" });
1008
+ } else if (intent === "specs" && sources.length > 0) {
1009
+ if (firstName) {
1010
+ if (!isProperty) {
1011
+ pills.push({ emoji: "\u{1F6D2}", label: "Add to cart", query: `Add the ${firstName} to my cart` });
1012
+ }
1013
+ pills.push({
1014
+ emoji: "\u{1F504}",
1015
+ label: "Find alternatives",
1016
+ query: `What are good alternatives to the ${firstName}?`
1017
+ });
1018
+ }
1019
+ } else if (intent === "general") {
1020
+ if (isProperty) {
1021
+ pills.push({ emoji: "\u{1F50D}", label: "Show popular listings", query: "What are your most popular properties?" });
1022
+ } else {
1023
+ pills.push({ emoji: "\u{1F50D}", label: "Show popular items", query: "What are your most popular products?" });
1024
+ }
1025
+ pills.push({ emoji: "\u{1F4A1}", label: "Recommend something", query: "What do you recommend for me?" });
1026
+ }
1027
+ if (pills.length === 0) return null;
1028
+ return /* @__PURE__ */ jsx5("div", { className: "hsk-action-pills", children: pills.map((pill) => /* @__PURE__ */ jsxs4(
1029
+ "button",
1030
+ {
1031
+ className: "hsk-action-pill",
1032
+ onClick: () => onSend(pill.query),
1033
+ disabled: loading,
1034
+ children: [
1035
+ /* @__PURE__ */ jsx5("span", { className: "hsk-pill-emoji", children: pill.emoji }),
1036
+ pill.label
1037
+ ]
1038
+ },
1039
+ pill.query
1040
+ )) });
1041
+ }
1042
+ var getFriendlyError = (err) => {
1043
+ let str = "";
1044
+ if (typeof err === "string") str = err;
1045
+ else if (err && typeof err === "object" && err.message) str = err.message;
1046
+ else try {
1047
+ str = JSON.stringify(err);
1048
+ } catch {
1049
+ str = String(err);
1050
+ }
1051
+ const lower = str.toLowerCase();
1052
+ if (lower.includes("429") || lower.includes("too many requests") || lower.includes("requests per minute limit exceeded") || lower.includes("too_many_requests_error") || lower.includes("request_quota_exceeded") || lower.includes("quota")) {
1053
+ return "The assistant is currently receiving too many requests. Please try again in a few moments.";
1054
+ }
1055
+ if (lower.includes("token limit")) {
1056
+ return "You've reached your usage limit. Please update your billing limits in your dashboard to continue.";
1057
+ }
1058
+ try {
1059
+ const parsed = JSON.parse(str);
1060
+ return parsed.error || parsed.message || str;
1061
+ } catch {
1062
+ return str;
1063
+ }
1064
+ };
1065
+ var LOADING_MESSAGES = [
1066
+ "Walking down the aisle...",
1067
+ "Consulting the blueprints...",
1068
+ "Polishing the keys...",
1069
+ "Checking the backyard...",
1070
+ "Checking the inventory...",
1071
+ "Inspecting the plumbing...",
1072
+ "Asking the concierge...",
1073
+ "Searching the shelves...",
1074
+ "Measuring the square footage...",
1075
+ "Dusting off the fireplace...",
1076
+ "Asking the store clerk..."
1077
+ ];
1078
+ var PROPERTY_CHIPS = [
1079
+ "3 bedroom apartments",
1080
+ "Houses under KSh 5,000,000",
1081
+ "Properties in Palm Beach",
1082
+ "Studio apartments with pool"
1083
+ ];
1084
+ function ChatModal({
1085
+ title = "Shopping Assistant",
1086
+ placeholder = "Ask me anything \u2014 gifts, budget, use case\u2026",
1087
+ backdropColor,
1088
+ backdropBlur,
1089
+ onClose,
1090
+ onSelectSource,
1091
+ defaultCurrency = "KES",
1092
+ chips = DEFAULT_CHIPS,
1093
+ theme,
1094
+ classNames = {}
1095
+ }) {
1096
+ const client = useAkropolysContext2();
1097
+ const { messages, sources, loading, streaming, error, lastAction, lastIntent, send, reset } = useKiku2();
1098
+ const [input, setInput] = useState3("");
1099
+ const [loadingMessageIndex, setLoadingMessageIndex] = useState3(0);
1100
+ const isProperty = client.vertical === "property";
1101
+ const activeChips = chips === DEFAULT_CHIPS && isProperty ? PROPERTY_CHIPS : chips;
1102
+ const activeTitle = title === "Shopping Assistant" && isProperty ? "Property Assistant" : title;
1103
+ const activePlaceholder = placeholder === "Ask me anything \u2014 gifts, budget, use case\u2026" && isProperty ? "Ask me anything \u2014 location, budget, bedrooms\u2026" : placeholder;
1104
+ useEffect3(() => {
1105
+ if (loading && !streaming) {
1106
+ const interval = setInterval(() => {
1107
+ setLoadingMessageIndex((prev) => (prev + 1) % LOADING_MESSAGES.length);
1108
+ }, 1500);
1109
+ return () => clearInterval(interval);
1110
+ } else {
1111
+ setLoadingMessageIndex(0);
1112
+ }
1113
+ }, [loading, streaming]);
1114
+ const [selectedProduct, setSelectedProduct] = useState3(null);
1115
+ const bottomRef = useRef3(null);
1116
+ const textareaRef = useRef3(null);
1117
+ const [phoneInput, setPhoneInput] = useState3(() => {
1118
+ if (typeof window !== "undefined") {
1119
+ return localStorage.getItem("akropolys_user_phone") || "";
1120
+ }
1121
+ return "";
1122
+ });
1123
+ const [merchantRef, setMerchantRef] = useState3(null);
1124
+ const [paymentPhase, setPaymentPhase] = useState3("idle");
1125
+ const [sessions, setSessions] = useState3(() => loadSessions());
1126
+ const [sidebarOpen, setSidebarOpen] = useState3(false);
1127
+ const [replayMessages, setReplayMessages] = useState3(null);
1128
+ const { status: pollStatus } = usePaymentPolling({
1129
+ client: client.api,
1130
+ merchantReference: merchantRef,
1131
+ onSuccess: () => {
1132
+ setPaymentPhase("done");
1133
+ setMerchantRef(null);
1134
+ },
1135
+ onFailure: () => {
1136
+ setPaymentPhase("failed");
1137
+ setMerchantRef(null);
1138
+ }
1139
+ });
1140
+ useEffect3(() => {
1141
+ if (!lastAction) return;
1142
+ if (lastAction.type === "request_phone") {
1143
+ setPaymentPhase("prompt_phone");
1144
+ } else if (lastAction.type === "awaiting_payment") {
1145
+ setMerchantRef(lastAction.merchantReference ?? null);
1146
+ setPaymentPhase("awaiting");
1147
+ }
1148
+ }, [lastAction]);
1149
+ const isStringTheme = typeof theme === "string";
1150
+ const hskThemeAttr = isStringTheme ? theme : void 0;
1151
+ const customStyles = !isStringTheme && theme ? {
1152
+ ...theme?.primaryColor && { "--hsk-primary": theme.primaryColor },
1153
+ ...theme?.backgroundColor && { "--hsk-bg": theme.backgroundColor },
1154
+ ...theme?.textColor && { "--hsk-text": theme.textColor },
1155
+ ...theme?.fontFamily && { "--hsk-font": theme.fontFamily },
1156
+ ...theme?.borderRadius && { "--hsk-border-radius": theme.borderRadius }
1157
+ } : void 0;
1158
+ const handlePhoneSubmit = async () => {
1159
+ if (!phoneInput.trim()) return;
1160
+ try {
1161
+ if (typeof window !== "undefined") {
1162
+ localStorage.setItem("akropolys_user_phone", phoneInput.trim());
1163
+ }
1164
+ const res = await client.api.initiatePayment(phoneInput.trim());
1165
+ setMerchantRef(res.merchantReference);
1166
+ setPaymentPhase("awaiting");
1167
+ } catch (e) {
1168
+ console.error("[Akropolys] initiatePayment error", e);
1169
+ setPaymentPhase("failed");
1170
+ }
1171
+ };
1172
+ const msgsContainerRef = useRef3(null);
1173
+ useEffect3(() => {
1174
+ const container = msgsContainerRef.current;
1175
+ if (!container) return;
1176
+ const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
1177
+ if (distanceFromBottom < 120) {
1178
+ bottomRef.current?.scrollIntoView({ behavior: "smooth" });
1179
+ }
1180
+ }, [messages, loading, selectedProduct]);
1181
+ useEffect3(() => {
1182
+ const h = (e) => {
1183
+ if (e.key === "Escape") onClose();
1184
+ };
1185
+ document.addEventListener("keydown", h);
1186
+ return () => document.removeEventListener("keydown", h);
1187
+ }, []);
1188
+ const saveCurrentSession = useCallback(() => {
1189
+ if (messages.length < 2) return;
1190
+ const firstUser = messages.find((m) => m.role === "user");
1191
+ const session = {
1192
+ id: `session_${Date.now()}`,
1193
+ title: firstUser ? firstUser.content.slice(0, 60) : "Chat session",
1194
+ messages,
1195
+ ts: Date.now()
1196
+ };
1197
+ const updated = [session, ...sessions].slice(0, MAX_SESSIONS);
1198
+ setSessions(updated);
1199
+ saveSessions(updated);
1200
+ }, [messages, sessions]);
1201
+ const handleReset = useCallback(() => {
1202
+ saveCurrentSession();
1203
+ reset();
1204
+ setReplayMessages(null);
1205
+ setPaymentPhase("idle");
1206
+ setMerchantRef(null);
1207
+ }, [reset, saveCurrentSession]);
1208
+ const handleSourceClick = (src) => {
1209
+ setSelectedProduct(src);
1210
+ onSelectSource?.(src);
1211
+ 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?`;
1212
+ send(q);
1213
+ };
1214
+ const handleSend = async (text) => {
1215
+ const q = (text ?? input).trim();
1216
+ if (!q || loading) return;
1217
+ setReplayMessages(null);
1218
+ setSelectedProduct(null);
1219
+ setInput("");
1220
+ if (textareaRef.current) {
1221
+ textareaRef.current.style.height = "auto";
1222
+ }
1223
+ await send(q);
1224
+ };
1225
+ const handleKeyDown = (e) => {
1226
+ if (e.key === "Enter" && !e.shiftKey) {
1227
+ e.preventDefault();
1228
+ handleSend();
1229
+ }
1230
+ };
1231
+ const handleInput = (e) => {
1232
+ setInput(e.target.value);
1233
+ const t = e.target;
1234
+ t.style.height = "auto";
1235
+ t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
1236
+ };
1237
+ const blurVal = typeof backdropBlur === "number" ? `${backdropBlur}px` : backdropBlur ?? "20px";
1238
+ const displayMessages = replayMessages ?? messages;
1239
+ const loadSession = (session) => {
1240
+ setReplayMessages(session.messages);
1241
+ setSidebarOpen(false);
1242
+ };
1243
+ const deleteSession = (id, e) => {
1244
+ e.stopPropagation();
1245
+ const updated = sessions.filter((s) => s.id !== id);
1246
+ setSessions(updated);
1247
+ saveSessions(updated);
1248
+ };
1249
+ return /* @__PURE__ */ jsx5(
1250
+ "div",
1251
+ {
1252
+ className: cn("hsk-cb-overlay", classNames.overlay),
1253
+ onClick: onClose,
1254
+ "data-hsk-theme": hskThemeAttr,
1255
+ style: {
1256
+ backdropFilter: `blur(${blurVal})`,
1257
+ WebkitBackdropFilter: `blur(${blurVal})`,
1258
+ ...backdropColor ? { background: backdropColor } : {},
1259
+ ...customStyles
1260
+ },
1261
+ children: /* @__PURE__ */ jsxs4("div", { className: cn("hsk-cb-panel hsk-cb-panel--with-sidebar", classNames.panel), onClick: (e) => e.stopPropagation(), children: [
1262
+ /* @__PURE__ */ jsxs4("div", { className: cn("hsk-cb-sidebar", sidebarOpen && "hsk-cb-sidebar--open"), children: [
1263
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-sidebar-header", children: [
1264
+ /* @__PURE__ */ jsx5("span", { className: "hsk-cb-sidebar-title", children: "History" }),
1265
+ /* @__PURE__ */ jsxs4(
1266
+ "button",
1267
+ {
1268
+ className: "hsk-cb-sidebar-new",
1269
+ onClick: handleReset,
1270
+ title: "New chat",
1271
+ children: [
1272
+ /* @__PURE__ */ jsx5(NewChatIcon, {}),
1273
+ /* @__PURE__ */ jsx5("span", { children: "New chat" })
1274
+ ]
1275
+ }
1276
+ )
1277
+ ] }),
1278
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-sidebar-list", children: sessions.length === 0 ? /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-sidebar-empty", children: [
1279
+ /* @__PURE__ */ jsx5(HistoryIcon, {}),
1280
+ /* @__PURE__ */ jsx5("span", { children: "No history yet" })
1281
+ ] }) : sessions.map((session) => /* @__PURE__ */ jsxs4(
1282
+ "div",
1283
+ {
1284
+ className: cn(
1285
+ "hsk-cb-sidebar-session",
1286
+ replayMessages === session.messages && "hsk-cb-sidebar-session--active"
1287
+ ),
1288
+ onClick: () => loadSession(session),
1289
+ children: [
1290
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-sidebar-session-title", children: session.title }),
1291
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-sidebar-session-meta", children: relativeTime(session.ts) }),
1292
+ /* @__PURE__ */ jsx5(
1293
+ "button",
1294
+ {
1295
+ className: "hsk-cb-sidebar-session-del",
1296
+ onClick: (e) => deleteSession(session.id, e),
1297
+ title: "Delete",
1298
+ children: "\xD7"
1299
+ }
1300
+ )
1301
+ ]
1302
+ },
1303
+ session.id
1304
+ )) })
1305
+ ] }),
1306
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-main", children: [
1307
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-topbar", children: [
1308
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-topbar-left", children: [
1309
+ /* @__PURE__ */ jsx5(
1310
+ "button",
1311
+ {
1312
+ className: cn("hsk-cb-sidebar-toggle", sidebarOpen && "hsk-cb-sidebar-toggle--active"),
1313
+ onClick: (e) => {
1314
+ e.stopPropagation();
1315
+ setSidebarOpen((v) => !v);
1316
+ },
1317
+ "aria-label": "Toggle history",
1318
+ children: /* @__PURE__ */ jsx5(HistoryIcon, {})
1319
+ }
1320
+ ),
1321
+ /* @__PURE__ */ jsx5("span", { className: "hsk-cb-topbar-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
1322
+ /* @__PURE__ */ jsx5("div", { children: /* @__PURE__ */ jsx5("div", { className: "hsk-cb-topbar-title", children: activeTitle }) })
1323
+ ] }),
1324
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-topbar-actions", children: [
1325
+ replayMessages ? /* @__PURE__ */ jsx5("button", { className: "hsk-cb-topbar-btn", onClick: () => {
1326
+ setReplayMessages(null);
1327
+ }, children: "\u2190 Live chat" }) : messages.length > 0 && /* @__PURE__ */ jsx5("button", { className: "hsk-cb-topbar-btn", onClick: handleReset, children: "Clear chat" }),
1328
+ /* @__PURE__ */ jsx5("button", { className: "hsk-cb-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx5(CloseIcon, {}) })
1329
+ ] })
1330
+ ] }),
1331
+ replayMessages && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-replay-banner", children: [
1332
+ /* @__PURE__ */ jsx5(HistoryIcon, {}),
1333
+ /* @__PURE__ */ jsx5("span", { children: "You're viewing a past conversation." }),
1334
+ /* @__PURE__ */ jsx5("button", { onClick: () => setReplayMessages(null), children: "Back to chat \u2192" })
1335
+ ] }),
1336
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-msgs", ref: msgsContainerRef, children: [
1337
+ displayMessages.length === 0 ? /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-empty", children: [
1338
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-empty-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
1339
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-empty-title", children: "Find exactly what you need" }),
1340
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-chips", children: activeChips.map((chip) => /* @__PURE__ */ jsx5(
1341
+ "button",
1342
+ {
1343
+ className: "hsk-cb-chip",
1344
+ onClick: () => handleSend(chip),
1345
+ children: chip
1346
+ },
1347
+ chip
1348
+ )) })
1349
+ ] }) : displayMessages.map((msg, idx) => {
1350
+ const isLast = idx === displayMessages.length - 1 && !replayMessages;
1351
+ const isUser = msg.role === "user";
1352
+ const displayContent = !isUser && isLast && lastIntent === "compare" && sources.length >= 2 && !replayMessages ? stripMarkdownTables(msg.content) : msg.content;
1353
+ 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: [
1354
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
1355
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-ai-body", children: [
1356
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-ai-text", children: renderMarkdown(displayContent) }),
1357
+ isLast && lastIntent === "compare" && sources.length >= 2 && !replayMessages && /* @__PURE__ */ jsx5(ComparisonMatrix, { sources, defaultCurrency }),
1358
+ isLast && sources.length > 0 && lastIntent !== "compare" && lastAction?.type !== "request_phone" && lastAction?.type !== "awaiting_payment" && lastAction?.type !== "checkout" && !msg.cartSnapshot && /* @__PURE__ */ jsx5(
1359
+ SourcesCarousel,
1360
+ {
1361
+ sources,
1362
+ defaultCurrency,
1363
+ onSelectSource: handleSourceClick
1364
+ }
1365
+ ),
1366
+ msg.cartSnapshot && msg.cartSnapshot.items?.length > 0 && /* @__PURE__ */ jsx5(
1367
+ CartContextCard,
1368
+ {
1369
+ cart: msg.cartSnapshot,
1370
+ defaultCurrency
1371
+ }
1372
+ ),
1373
+ isLast && msg.cartSnapshot && !loading && /* @__PURE__ */ jsx5(
1374
+ ActionPills,
1375
+ {
1376
+ cart: msg.cartSnapshot,
1377
+ actionType: msg.actionType,
1378
+ onSend: handleSend,
1379
+ loading
1380
+ }
1381
+ ),
1382
+ isLast && !loading && !msg.cartSnapshot && !replayMessages && /* @__PURE__ */ jsx5(
1383
+ SmartContextPills,
1384
+ {
1385
+ intent: lastIntent,
1386
+ sources,
1387
+ onSend: handleSend,
1388
+ loading
1389
+ }
1390
+ )
1391
+ ] })
1392
+ ] }) }, idx);
1393
+ }),
1394
+ selectedProduct && loading && /* @__PURE__ */ jsxs4(
1395
+ "div",
1396
+ {
1397
+ className: "hsk-cb-selected-product",
1398
+ onClick: () => selectedProduct.url && window.open(selectedProduct.url, "_blank"),
1399
+ children: [
1400
+ selectedProduct.image && /* @__PURE__ */ jsx5("img", { className: "hsk-cb-selected-img", src: selectedProduct.image, alt: selectedProduct.name }),
1401
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-selected-info", children: [
1402
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-selected-name", children: selectedProduct.name }),
1403
+ selectedProduct.price && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-selected-price", children: [
1404
+ selectedProduct.currency ?? defaultCurrency,
1405
+ " ",
1406
+ parseFloat(String(selectedProduct.price ?? "").replace(/[^0-9.]/g, "") || "0").toLocaleString()
1407
+ ] })
1408
+ ] })
1409
+ ]
1410
+ }
1411
+ ),
1412
+ loading && !streaming && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-typing-row", style: { display: "flex", alignItems: "flex-start", gap: "10px" }, children: [
1413
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center", marginTop: "4px" }, children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
1414
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: [
1415
+ /* @__PURE__ */ jsx5("style", { children: `
1416
+ @keyframes hsk-pulse {
1417
+ 0%, 100% { opacity: 0.6; }
1418
+ 50% { opacity: 1; }
1419
+ }
1420
+ ` }),
1421
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "0.85rem", color: "#6b7280", fontStyle: "italic", animation: "hsk-pulse 1.5s infinite ease-in-out" }, children: LOADING_MESSAGES[loadingMessageIndex] }),
1422
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-typing", style: { margin: 0, alignSelf: "flex-start" }, children: [
1423
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-dot" }),
1424
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-dot" }),
1425
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-dot" })
1426
+ ] })
1427
+ ] })
1428
+ ] }),
1429
+ error && /* @__PURE__ */ jsx5("div", { className: "hsk-cb-error", children: getFriendlyError(error) }),
1430
+ !replayMessages && paymentPhase === "prompt_phone" && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-payment-prompt", children: [
1431
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-payment-icon", children: "\u{1F4F1}" }),
1432
+ /* @__PURE__ */ jsx5("p", { className: "hsk-cb-payment-label", children: "Enter your M-Pesa number to pay" }),
1433
+ /* @__PURE__ */ jsx5(
1434
+ "input",
1435
+ {
1436
+ type: "tel",
1437
+ className: "hsk-cb-phone-input",
1438
+ placeholder: "e.g. 0712 345 678",
1439
+ value: phoneInput,
1440
+ onChange: (e) => setPhoneInput(e.target.value),
1441
+ onKeyDown: (e) => e.key === "Enter" && handlePhoneSubmit()
1442
+ }
1443
+ ),
1444
+ /* @__PURE__ */ jsx5("button", { className: "hsk-cb-pay-submit", onClick: handlePhoneSubmit, children: "Send STK Push \u2192" })
1445
+ ] }),
1446
+ !replayMessages && paymentPhase === "awaiting" && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-payment-prompt hsk-cb-payment-prompt--awaiting", children: [
1447
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-payment-pulse-ring" }),
1448
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-payment-icon-wrap", children: /* @__PURE__ */ jsx5("span", { style: { fontSize: "2rem" }, children: "\u{1F4F1}" }) }),
1449
+ /* @__PURE__ */ jsx5("p", { className: "hsk-cb-payment-label", style: { fontWeight: 600 }, children: "Check your phone" }),
1450
+ /* @__PURE__ */ jsxs4("p", { className: "hsk-cb-payment-sub", children: [
1451
+ "An M-Pesa STK push has been sent.",
1452
+ /* @__PURE__ */ jsx5("br", {}),
1453
+ "Enter your PIN to complete payment."
1454
+ ] }),
1455
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-payment-dots", children: [
1456
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-dot hsk-cb-dot--amber" }),
1457
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-dot hsk-cb-dot--amber" }),
1458
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-dot hsk-cb-dot--amber" })
1459
+ ] })
1460
+ ] }),
1461
+ !replayMessages && paymentPhase === "done" && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-payment-prompt hsk-cb-payment-prompt--success", children: [
1462
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-payment-success-ring" }),
1463
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-payment-icon-wrap", children: /* @__PURE__ */ jsx5("span", { style: { fontSize: "2.5rem" }, children: "\u2705" }) }),
1464
+ /* @__PURE__ */ jsx5("p", { className: "hsk-cb-payment-label", children: "Payment complete!" }),
1465
+ /* @__PURE__ */ jsx5("p", { className: "hsk-cb-payment-sub", children: "Thank you for your order. A confirmation has been sent." })
1466
+ ] }),
1467
+ !replayMessages && paymentPhase === "failed" && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-payment-prompt hsk-cb-payment-prompt--failed", children: [
1468
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-payment-icon-wrap", children: /* @__PURE__ */ jsx5("span", { style: { fontSize: "2.5rem" }, children: "\u274C" }) }),
1469
+ /* @__PURE__ */ jsx5("p", { className: "hsk-cb-payment-label", children: "Payment failed or timed out" }),
1470
+ /* @__PURE__ */ jsx5("p", { className: "hsk-cb-payment-sub", children: "Please check your M-Pesa PIN and try again, or contact support." }),
1471
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-payment-actions", children: [
1472
+ /* @__PURE__ */ jsx5(
1473
+ "button",
1474
+ {
1475
+ className: "hsk-cb-pay-submit",
1476
+ onClick: () => {
1477
+ setPaymentPhase("prompt_phone");
1478
+ setMerchantRef(null);
1479
+ },
1480
+ children: "Try again"
1481
+ }
1482
+ ),
1483
+ /* @__PURE__ */ jsx5(
1484
+ "button",
1485
+ {
1486
+ className: "hsk-cb-pay-secondary",
1487
+ onClick: () => {
1488
+ setPaymentPhase("idle");
1489
+ setMerchantRef(null);
1490
+ },
1491
+ children: "Cancel"
1492
+ }
1493
+ )
1494
+ ] })
1495
+ ] }),
1496
+ /* @__PURE__ */ jsx5("div", { ref: bottomRef, style: { height: 1 } })
1497
+ ] }),
1498
+ !replayMessages && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-input-wrap", children: [
1499
+ /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-input-box", children: [
1500
+ /* @__PURE__ */ jsx5(
1501
+ "textarea",
1502
+ {
1503
+ ref: textareaRef,
1504
+ className: cn("hsk-cb-textarea", classNames.input),
1505
+ value: input,
1506
+ onChange: handleInput,
1507
+ onKeyDown: handleKeyDown,
1508
+ placeholder: activePlaceholder,
1509
+ rows: 1,
1510
+ disabled: loading,
1511
+ autoFocus: true
1512
+ }
1513
+ ),
1514
+ /* @__PURE__ */ jsx5(
1515
+ "button",
1516
+ {
1517
+ className: cn("hsk-cb-send", classNames.sendButton),
1518
+ onClick: () => handleSend(),
1519
+ disabled: !input.trim() || loading,
1520
+ "aria-label": "Send message",
1521
+ children: /* @__PURE__ */ jsx5(ArrowUpIcon2, {})
1522
+ }
1523
+ )
1524
+ ] }),
1525
+ /* @__PURE__ */ jsx5("div", { className: "hsk-cb-hint", children: "Akropolys AI \xB7 searches the whole catalogue in real time" })
1526
+ ] })
1527
+ ] })
1528
+ ] })
1529
+ }
1530
+ );
1531
+ }
1532
+ function KikuButton({
1533
+ label,
1534
+ title,
1535
+ placeholder,
1536
+ backdropColor,
1537
+ backdropBlur,
1538
+ className,
1539
+ onSelectSource,
1540
+ defaultCurrency,
1541
+ chips,
1542
+ theme,
1543
+ classNames = {}
1544
+ }) {
1545
+ const [open, setOpen] = useState3(false);
1546
+ const [mounted, setMounted] = useState3(false);
1547
+ useEffect3(() => {
1548
+ setMounted(true);
1549
+ if (typeof window !== "undefined" && !window.__akropolys_nav_patched) {
1550
+ window.__akropolys_nav_patched = true;
1551
+ const originalPush = window.history.pushState;
1552
+ const originalReplace = window.history.replaceState;
1553
+ window.history.pushState = function(...args) {
1554
+ originalPush.apply(this, args);
1555
+ window.dispatchEvent(new CustomEvent("akropolys:navigation"));
1556
+ };
1557
+ window.history.replaceState = function(...args) {
1558
+ originalReplace.apply(this, args);
1559
+ window.dispatchEvent(new CustomEvent("akropolys:navigation"));
1560
+ };
1561
+ }
1562
+ const handleNavigation = () => {
1563
+ setOpen(false);
1564
+ };
1565
+ window.addEventListener("popstate", handleNavigation);
1566
+ window.addEventListener("akropolys:navigation", handleNavigation);
1567
+ return () => {
1568
+ window.removeEventListener("popstate", handleNavigation);
1569
+ window.removeEventListener("akropolys:navigation", handleNavigation);
1570
+ };
1571
+ }, []);
1572
+ const isStringTheme = typeof theme === "string";
1573
+ const hskThemeAttr = isStringTheme ? theme : void 0;
1574
+ const customStyles = !isStringTheme && theme ? {
1575
+ ...theme?.primaryColor && { "--hsk-primary": theme.primaryColor },
1576
+ ...theme?.backgroundColor && { "--hsk-bg": theme.backgroundColor },
1577
+ ...theme?.textColor && { "--hsk-text": theme.textColor },
1578
+ ...theme?.fontFamily && { "--hsk-font": theme.fontFamily },
1579
+ ...theme?.borderRadius && { "--hsk-border-radius": theme.borderRadius }
1580
+ } : void 0;
1581
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
1582
+ /* @__PURE__ */ jsxs4(
1583
+ "button",
1584
+ {
1585
+ className: cn("hsk-cb-btn", classNames.button, className),
1586
+ onClick: () => setOpen(true),
1587
+ style: customStyles,
1588
+ "data-hsk-theme": hskThemeAttr,
1589
+ "aria-label": "Open AI chat",
1590
+ children: [
1591
+ /* @__PURE__ */ jsx5("span", { className: "hsk-cb-btn-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
1592
+ label !== void 0 ? label : null
1593
+ ]
1594
+ }
1595
+ ),
1596
+ open && mounted && createPortal(
1597
+ /* @__PURE__ */ jsx5(
1598
+ ChatModal,
1599
+ {
1600
+ title,
1601
+ placeholder,
1602
+ backdropColor,
1603
+ backdropBlur,
1604
+ onClose: () => setOpen(false),
1605
+ onSelectSource,
1606
+ defaultCurrency,
1607
+ chips,
1608
+ theme,
1609
+ classNames
1610
+ }
1611
+ ),
1612
+ document.body
1613
+ )
1614
+ ] });
1615
+ }
1616
+
1617
+ // src/components/Sparkle.tsx
1618
+ import { useState as useState4, useEffect as useEffect4, useRef as useRef4 } from "react";
1619
+ import { createPortal as createPortal2 } from "react-dom";
1620
+ import { useSearch as useSearch2, useKiku as useKiku3, useAkropolysContext as useAkropolysContext3 } from "@akropolys/sdk";
1621
+ import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1622
+ var SparkleIcon3 = ({ className, size = 16 }) => /* @__PURE__ */ jsxs5(
1623
+ "svg",
1624
+ {
1625
+ className: cn("hsk-brand-a", className),
1626
+ width: size,
1627
+ height: size,
1628
+ viewBox: "0 0 28 30",
1629
+ fill: "none",
1630
+ xmlns: "http://www.w3.org/2000/svg",
1631
+ "aria-label": "Akropolys",
1632
+ children: [
1633
+ /* @__PURE__ */ jsx6(
1634
+ "path",
1635
+ {
1636
+ d: "M14.5 4.5 L6.5 25",
1637
+ stroke: "currentColor",
1638
+ strokeWidth: "2.2",
1639
+ strokeLinecap: "round"
1640
+ }
1641
+ ),
1642
+ /* @__PURE__ */ jsx6(
1643
+ "path",
1644
+ {
1645
+ d: "M14.5 4.5 L22.5 25",
1646
+ stroke: "currentColor",
1647
+ strokeWidth: "4.2",
1648
+ strokeLinecap: "round"
1649
+ }
1650
+ ),
1651
+ /* @__PURE__ */ jsx6(
1652
+ "path",
1653
+ {
1654
+ d: "M9.5 17 H19.5",
1655
+ stroke: "currentColor",
1656
+ strokeWidth: "2",
1657
+ strokeLinecap: "round"
1658
+ }
1659
+ ),
1660
+ /* @__PURE__ */ jsx6(
1661
+ "path",
1662
+ {
1663
+ d: "M3 25 H10",
1664
+ stroke: "currentColor",
1665
+ strokeWidth: "2",
1666
+ strokeLinecap: "round"
1667
+ }
1668
+ ),
1669
+ /* @__PURE__ */ jsx6(
1670
+ "path",
1671
+ {
1672
+ d: "M19 25 H26",
1673
+ stroke: "currentColor",
1674
+ strokeWidth: "2.5",
1675
+ strokeLinecap: "round"
1676
+ }
1677
+ ),
1678
+ /* @__PURE__ */ jsx6(
1679
+ "path",
1680
+ {
1681
+ d: "M8.5 2.5 C10 2.5, 11 3.5, 11 5 C11 7, 8.5 8.5, 7.5 9.5",
1682
+ stroke: "currentColor",
1683
+ strokeWidth: "2",
1684
+ strokeLinecap: "round",
1685
+ fill: "none",
1686
+ className: "hsk-akr-breath"
1687
+ }
1688
+ )
1689
+ ]
1690
+ }
1691
+ );
1692
+ var CloseIcon2 = () => /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1693
+ /* @__PURE__ */ jsx6("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1694
+ /* @__PURE__ */ jsx6("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1695
+ ] });
1696
+ var ArrowUpIcon3 = () => /* @__PURE__ */ jsxs5("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1697
+ /* @__PURE__ */ jsx6("path", { d: "m5 12 7-7 7 7" }),
1698
+ /* @__PURE__ */ jsx6("path", { d: "M12 19V5" })
1699
+ ] });
1700
+ var getFriendlyError2 = (err) => {
1701
+ let str = "";
1702
+ if (typeof err === "string") str = err;
1703
+ else if (err && typeof err === "object" && err.message) str = err.message;
1704
+ else try {
1705
+ str = JSON.stringify(err);
1706
+ } catch {
1707
+ str = String(err);
1708
+ }
1709
+ if (str.toLowerCase().includes("token limit")) {
1710
+ return "You've reached your usage limit. Please update your billing limits in your dashboard to continue.";
1711
+ }
1712
+ try {
1713
+ const parsed = JSON.parse(str);
1714
+ return parsed.error || parsed.message || str;
1715
+ } catch {
1716
+ return str;
1717
+ }
1718
+ };
1719
+ function SparkleModal({
1720
+ productName,
1721
+ limit,
1722
+ backdropColor,
1723
+ backdropBlur,
1724
+ onClose,
1725
+ onNavigate,
1726
+ onResult,
1727
+ theme,
1728
+ classNames = {},
1729
+ product: initialProduct
1730
+ }) {
1731
+ const client = useAkropolysContext3();
1732
+ const [fetchedProduct, setFetchedProduct] = useState4(null);
1733
+ const displayProduct = initialProduct || fetchedProduct;
1734
+ const { results, loading: searchLoading, search } = useSearch2();
1735
+ const { messages, sources, loading: chatLoading, error: chatError, send } = useKiku3();
1736
+ const [chatInput, setChatInput] = useState4("");
1737
+ const chatBottomRef = useRef4(null);
1738
+ const chatTextareaRef = useRef4(null);
1739
+ useEffect4(() => {
1740
+ if (!initialProduct && !fetchedProduct) {
1741
+ client.api.searchVector(productName, 1).then((res) => {
1742
+ if (res.results && res.results.length > 0) {
1743
+ setFetchedProduct(res.results[0].product);
1744
+ }
1745
+ }).catch((err) => console.error("[Akropolys] Failed to fetch product details", err));
1746
+ }
1747
+ search(productName, limit);
1748
+ }, [productName, initialProduct, fetchedProduct, client, limit, search]);
1749
+ useEffect4(() => {
1750
+ if (results.length > 0) onResult?.(results);
1751
+ }, [results, onResult]);
1752
+ useEffect4(() => {
1753
+ const h = (e) => {
1754
+ if (e.key === "Escape") onClose();
1755
+ };
1756
+ document.addEventListener("keydown", h);
1757
+ return () => document.removeEventListener("keydown", h);
1758
+ }, [onClose]);
1759
+ useEffect4(() => {
1760
+ chatBottomRef.current?.scrollIntoView({ behavior: "smooth" });
1761
+ }, [messages, chatLoading]);
1762
+ const blurVal = typeof backdropBlur === "number" ? `${backdropBlur}px` : backdropBlur ?? "16px";
1763
+ const bg = backdropColor ?? void 0;
1764
+ const handleNav = (r) => {
1765
+ const prevent = onNavigate?.(r);
1766
+ if (prevent !== false) {
1767
+ onClose();
1768
+ if (r.product.url) window.location.href = r.product.url;
1769
+ }
1770
+ };
1771
+ const handleSend = async (text) => {
1772
+ const q = (text ?? chatInput).trim();
1773
+ if (!q || chatLoading) return;
1774
+ setChatInput("");
1775
+ if (chatTextareaRef.current) {
1776
+ chatTextareaRef.current.style.height = "auto";
1777
+ }
1778
+ if (messages.length === 0 && displayProduct) {
1779
+ const contextQuery = `[Context: Shopper is viewing "${displayProduct.name}". Price: ${displayProduct.price}. Description: ${displayProduct.description || ""}]
1780
+
1781
+ Question: ${q}`;
1782
+ await send(contextQuery, q);
1783
+ } else {
1784
+ await send(q);
1785
+ }
1786
+ };
1787
+ const handleKeyDown = (e) => {
1788
+ if (e.key === "Enter" && !e.shiftKey) {
1789
+ e.preventDefault();
1790
+ handleSend();
1791
+ }
1792
+ };
1793
+ const handleInput = (e) => {
1794
+ setChatInput(e.target.value);
1795
+ const t = e.target;
1796
+ t.style.height = "auto";
1797
+ t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
1798
+ };
1799
+ const customStyles = {
1800
+ ...theme?.primaryColor && { "--hsk-primary": theme.primaryColor },
1801
+ ...theme?.backgroundColor && { "--hsk-bg": theme.backgroundColor },
1802
+ ...theme?.textColor && { "--hsk-text": theme.textColor },
1803
+ ...theme?.fontFamily && { "--hsk-font": theme.fontFamily },
1804
+ ...theme?.borderRadius && { "--hsk-border-radius": theme.borderRadius }
1805
+ };
1806
+ const displayMessages = messages.length === 0 && displayProduct ? [
1807
+ {
1808
+ role: "assistant",
1809
+ content: `Hi! I can help you with **${displayProduct.name}**. Ask me about its specifications, features, compare it with other options, or find alternatives!`
1810
+ }
1811
+ ] : messages;
1812
+ return /* @__PURE__ */ jsx6(
1813
+ "div",
1814
+ {
1815
+ className: cn("hsk-sp-backdrop", classNames.backdrop),
1816
+ onClick: onClose,
1817
+ style: {
1818
+ backdropFilter: `blur(${blurVal})`,
1819
+ WebkitBackdropFilter: `blur(${blurVal})`,
1820
+ background: bg ?? void 0,
1821
+ ...customStyles
1822
+ },
1823
+ children: /* @__PURE__ */ jsxs5("div", { className: cn("hsk-sp-card hsk-sp-fullscreen", classNames.card), onClick: (e) => e.stopPropagation(), children: [
1824
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-header", children: [
1825
+ /* @__PURE__ */ jsx6("span", { className: "hsk-sp-header-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
1826
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-header-body", children: [
1827
+ /* @__PURE__ */ jsx6("div", { className: "hsk-sp-header-title", children: displayProduct?.name || productName }),
1828
+ /* @__PURE__ */ jsx6("div", { className: "hsk-sp-header-sub", children: "Ask questions, compare specs, or check similar products" })
1829
+ ] }),
1830
+ /* @__PURE__ */ jsx6("button", { className: "hsk-sp-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx6(CloseIcon2, {}) })
1831
+ ] }),
1832
+ searchLoading && /* @__PURE__ */ jsx6("div", { className: "hsk-sp-bar" }),
1833
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-body", children: [
1834
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-details-pane", children: [
1835
+ displayProduct && /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-product-profile-container", children: [
1836
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-product-profile", children: [
1837
+ /* @__PURE__ */ jsx6("div", { className: "hsk-sp-details-imgwrap", children: displayProduct.images?.[0] ? /* @__PURE__ */ jsx6("img", { src: displayProduct.images[0], alt: displayProduct.name }) : /* @__PURE__ */ jsx6("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
1838
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-details-meta", children: [
1839
+ displayProduct.brand && /* @__PURE__ */ jsx6("span", { className: "hsk-sp-item-brand", children: displayProduct.brand }),
1840
+ displayProduct.category && /* @__PURE__ */ jsx6("span", { className: "hsk-sp-item-cat", children: displayProduct.category }),
1841
+ /* @__PURE__ */ jsx6("h2", { className: "hsk-sp-details-name", children: displayProduct.name }),
1842
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-item-price-row", children: [
1843
+ /* @__PURE__ */ jsx6("span", { className: "hsk-sp-item-currency", children: displayProduct.currency ?? "KES" }),
1844
+ /* @__PURE__ */ jsx6("span", { className: "hsk-sp-item-price", children: parseFloat(displayProduct.price?.replace(/[^0-9.]/g, "") || "0").toLocaleString() }),
1845
+ displayProduct.originalPrice && /* @__PURE__ */ jsx6("span", { className: "hsk-sp-item-original-price", children: parseFloat(displayProduct.originalPrice.replace(/[^0-9.]/g, "") || "0").toLocaleString() }),
1846
+ displayProduct.discount && /* @__PURE__ */ jsxs5("span", { className: "hsk-sp-item-discount", children: [
1847
+ "(",
1848
+ displayProduct.discount,
1849
+ ")"
1850
+ ] })
1851
+ ] }),
1852
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-item-meta-badges", children: [
1853
+ displayProduct.rating && /* @__PURE__ */ jsxs5("span", { className: "hsk-sp-meta-badge hsk-sp-meta-badge-rating", children: [
1854
+ "\u2605 ",
1855
+ parseFloat(displayProduct.rating.toString()).toFixed(1),
1856
+ " ",
1857
+ displayProduct.reviewCount ? `(${displayProduct.reviewCount})` : ""
1858
+ ] }),
1859
+ displayProduct.availability && /* @__PURE__ */ jsx6("span", { className: `hsk-sp-meta-badge hsk-sp-meta-badge-avail ${displayProduct.availability.toLowerCase().includes("in") ? "in-stock" : "out-stock"}`, children: displayProduct.availability }),
1860
+ displayProduct.stock && !displayProduct.availability && /* @__PURE__ */ jsxs5("span", { className: "hsk-sp-meta-badge hsk-sp-meta-badge-stock", children: [
1861
+ "Stock: ",
1862
+ displayProduct.stock
1863
+ ] })
1864
+ ] })
1865
+ ] })
1866
+ ] }),
1867
+ displayProduct.specs && Object.keys(displayProduct.specs).length > 0 && /* @__PURE__ */ jsx6("div", { className: "hsk-sp-specs-horizontal", children: Object.entries(displayProduct.specs).map(([key, val]) => /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-spec-item-horizontal", children: [
1868
+ /* @__PURE__ */ jsxs5("span", { className: "hsk-sp-spec-label-horizontal", children: [
1869
+ key,
1870
+ ":"
1871
+ ] }),
1872
+ /* @__PURE__ */ jsx6("span", { className: "hsk-sp-spec-value-horizontal", title: val, children: val })
1873
+ ] }, key)) }),
1874
+ displayProduct.description && /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-details-desc", children: [
1875
+ /* @__PURE__ */ jsx6("h4", { children: "Description" }),
1876
+ /* @__PURE__ */ jsx6("p", { children: displayProduct.description })
1877
+ ] })
1878
+ ] }),
1879
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-similar-section", children: [
1880
+ /* @__PURE__ */ jsx6("h3", { children: "Similar Products" }),
1881
+ /* @__PURE__ */ jsx6("div", { className: "hsk-sp-results", children: (() => {
1882
+ const similarProducts = results.filter(
1883
+ (r) => {
1884
+ const isSameName = r.product.name.toLowerCase() === displayProduct?.name?.toLowerCase();
1885
+ const isSameSlug = r.product.slug && displayProduct?.slug && r.product.slug.toLowerCase() === displayProduct.slug.toLowerCase();
1886
+ return !isSameName && !isSameSlug;
1887
+ }
1888
+ );
1889
+ if (!searchLoading && similarProducts.length === 0) {
1890
+ return /* @__PURE__ */ jsx6("div", { className: "hsk-sp-empty", children: "No similar products found." });
1891
+ }
1892
+ return similarProducts.map((r, i) => {
1893
+ const price = parseFloat(r.product.price?.replace(/[^0-9.]/g, "") || "0");
1894
+ const currency = r.product.currency ?? "KES";
1895
+ return /* @__PURE__ */ jsxs5(
1896
+ "div",
1897
+ {
1898
+ className: cn("hsk-sp-item", classNames.item),
1899
+ style: { animationDelay: `${i * 55}ms` },
1900
+ children: [
1901
+ /* @__PURE__ */ jsx6("div", { className: "hsk-sp-img-wrap", children: r.product.images?.[0] ? /* @__PURE__ */ jsx6("img", { src: r.product.images[0], alt: r.product.name }) : /* @__PURE__ */ jsx6("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
1902
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-item-body", children: [
1903
+ /* @__PURE__ */ jsxs5("div", { children: [
1904
+ r.product.category && /* @__PURE__ */ jsx6("div", { className: "hsk-sp-item-cat", children: r.product.category }),
1905
+ /* @__PURE__ */ jsx6("div", { className: "hsk-sp-item-name", title: r.product.name, children: r.product.name })
1906
+ ] }),
1907
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-item-price-row", children: [
1908
+ /* @__PURE__ */ jsx6("span", { className: "hsk-sp-item-currency", children: currency }),
1909
+ /* @__PURE__ */ jsx6("span", { className: "hsk-sp-item-price", children: price.toLocaleString() })
1910
+ ] }),
1911
+ /* @__PURE__ */ jsx6("div", { className: "hsk-sp-actions", children: /* @__PURE__ */ jsx6(
1912
+ "button",
1913
+ {
1914
+ className: "hsk-sp-action hsk-sp-action-primary",
1915
+ onClick: () => handleNav(r),
1916
+ children: "View"
1917
+ }
1918
+ ) })
1919
+ ] })
1920
+ ]
1921
+ },
1922
+ r.id
1923
+ );
1924
+ });
1925
+ })() })
1926
+ ] })
1927
+ ] }),
1928
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-sp-chat-pane", children: [
1929
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-cb-msgs", children: [
1930
+ displayMessages.map((msg, idx) => {
1931
+ const isUser = msg.role === "user";
1932
+ 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__ */ jsxs5("div", { className: "hsk-cb-ai-msg", children: [
1933
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
1934
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-ai-body", children: /* @__PURE__ */ jsx6("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }) })
1935
+ ] }) }, idx);
1936
+ }),
1937
+ chatLoading && /* @__PURE__ */ jsxs5("div", { className: "hsk-cb-typing-row", children: [
1938
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
1939
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-cb-typing", children: [
1940
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-dot" }),
1941
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-dot" }),
1942
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-dot" })
1943
+ ] })
1944
+ ] }),
1945
+ chatError && /* @__PURE__ */ jsx6("div", { className: "hsk-cb-error", children: getFriendlyError2(chatError) }),
1946
+ /* @__PURE__ */ jsx6("div", { ref: chatBottomRef, style: { height: 1 } })
1947
+ ] }),
1948
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-cb-input-wrap", children: [
1949
+ /* @__PURE__ */ jsxs5("div", { className: "hsk-cb-input-box", children: [
1950
+ /* @__PURE__ */ jsx6(
1951
+ "textarea",
1952
+ {
1953
+ ref: chatTextareaRef,
1954
+ className: "hsk-cb-textarea",
1955
+ value: chatInput,
1956
+ onChange: handleInput,
1957
+ onKeyDown: handleKeyDown,
1958
+ placeholder: "Ask about this product, specs, or comparison...",
1959
+ rows: 1,
1960
+ disabled: chatLoading
1961
+ }
1962
+ ),
1963
+ /* @__PURE__ */ jsx6(
1964
+ "button",
1965
+ {
1966
+ className: "hsk-cb-send",
1967
+ onClick: () => handleSend(),
1968
+ disabled: !chatInput.trim() || chatLoading,
1969
+ "aria-label": "Send message",
1970
+ children: /* @__PURE__ */ jsx6(ArrowUpIcon3, {})
1971
+ }
1972
+ )
1973
+ ] }),
1974
+ /* @__PURE__ */ jsx6("div", { className: "hsk-cb-hint", children: "Akropolys \xB7 instant product knowledge" })
1975
+ ] })
1976
+ ] })
1977
+ ] }),
1978
+ /* @__PURE__ */ jsx6("div", { className: "hsk-sp-footer", children: /* @__PURE__ */ jsx6("span", { className: "hsk-sp-esc", children: "Esc to close" }) })
1979
+ ] })
1980
+ }
1981
+ );
1982
+ }
1983
+ function Sparkle({
1984
+ productName,
1985
+ limit = 8,
1986
+ onResult,
1987
+ backdropColor,
1988
+ backdropBlur,
1989
+ className,
1990
+ onNavigate,
1991
+ theme,
1992
+ classNames = {},
1993
+ product
1994
+ }) {
1995
+ const [open, setOpen] = useState4(false);
1996
+ const [mounted, setMounted] = useState4(false);
1997
+ useEffect4(() => {
1998
+ setMounted(true);
1999
+ }, []);
2000
+ const customStyles = {
2001
+ ...theme?.primaryColor && { "--hsk-primary": theme.primaryColor },
2002
+ ...theme?.backgroundColor && { "--hsk-bg": theme.backgroundColor },
2003
+ ...theme?.textColor && { "--hsk-text": theme.textColor },
2004
+ ...theme?.fontFamily && { "--hsk-font": theme.fontFamily },
2005
+ ...theme?.borderRadius && { "--hsk-border-radius": theme.borderRadius }
2006
+ };
2007
+ return /* @__PURE__ */ jsxs5(Fragment4, { children: [
2008
+ /* @__PURE__ */ jsx6(
2009
+ "button",
2010
+ {
2011
+ className: cn("hsk-sp-btn", classNames.button, className),
2012
+ onClick: () => setOpen(true),
2013
+ style: customStyles,
2014
+ title: "Find similar products",
2015
+ "aria-label": "Find similar products",
2016
+ children: /* @__PURE__ */ jsx6(SparkleIcon3, {})
2017
+ }
2018
+ ),
2019
+ open && mounted && createPortal2(
2020
+ /* @__PURE__ */ jsx6(
2021
+ SparkleModal,
2022
+ {
2023
+ productName,
2024
+ limit,
2025
+ onResult,
2026
+ backdropColor,
2027
+ backdropBlur,
2028
+ onClose: () => setOpen(false),
2029
+ onNavigate,
2030
+ theme,
2031
+ classNames,
2032
+ product
2033
+ }
2034
+ ),
2035
+ document.body
2036
+ )
2037
+ ] });
2038
+ }
2039
+
2040
+ // src/components/CartBadge.tsx
2041
+ import { useCart } from "@akropolys/sdk";
2042
+ import { jsx as jsx7 } from "react/jsx-runtime";
2043
+ function CartBadge({ className }) {
2044
+ const { cart } = useCart();
2045
+ if (!cart || cart.item_count === 0) return null;
2046
+ return /* @__PURE__ */ jsx7("span", { className: cn("hsk-cart-badge", className), children: cart.item_count });
2047
+ }
2048
+
2049
+ // src/components/CartDrawer.tsx
2050
+ import { useState as useState6, useEffect as useEffect6 } from "react";
2051
+ import { createPortal as createPortal4 } from "react-dom";
2052
+ import { useCart as useCart3, useAkropolysContext as useAkropolysContext5 } from "@akropolys/sdk";
2053
+
2054
+ // src/components/CheckoutModal.tsx
2055
+ import { useState as useState5, useEffect as useEffect5 } from "react";
2056
+ import { createPortal as createPortal3 } from "react-dom";
2057
+ import { useCart as useCart2, useAkropolysContext as useAkropolysContext4, usePaymentPolling as usePaymentPolling2 } from "@akropolys/sdk";
2058
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
2059
+ function CheckoutModal({
2060
+ onClose,
2061
+ theme,
2062
+ customStyles,
2063
+ hskThemeAttr
2064
+ }) {
2065
+ const { cart, loading: cartLoading } = useCart2();
2066
+ const client = useAkropolysContext4();
2067
+ const [config, setConfig] = useState5(null);
2068
+ const [loadingConfig, setLoadingConfig] = useState5(true);
2069
+ const [phone, setPhone] = useState5(() => {
2070
+ if (typeof window !== "undefined") {
2071
+ return localStorage.getItem("akropolys_user_phone") || "";
2072
+ }
2073
+ return "";
2074
+ });
2075
+ const [email, setEmail] = useState5(() => {
2076
+ if (typeof window !== "undefined") {
2077
+ return localStorage.getItem("akropolys_user_email") || "";
2078
+ }
2079
+ return "";
2080
+ });
2081
+ const [firstName, setFirstName] = useState5(() => {
2082
+ if (typeof window !== "undefined") {
2083
+ return localStorage.getItem("akropolys_user_firstname") || "";
2084
+ }
2085
+ return "";
2086
+ });
2087
+ const [lastName, setLastName] = useState5(() => {
2088
+ if (typeof window !== "undefined") {
2089
+ return localStorage.getItem("akropolys_user_lastname") || "";
2090
+ }
2091
+ return "";
2092
+ });
2093
+ const [phase, setPhase] = useState5("idle");
2094
+ const [merchantRef, setMerchantRef] = useState5(null);
2095
+ const [payError, setPayError] = useState5(null);
2096
+ const {} = usePaymentPolling2({
2097
+ client: client.api,
2098
+ merchantReference: merchantRef,
2099
+ onSuccess: () => {
2100
+ setPhase("done");
2101
+ setMerchantRef(null);
2102
+ },
2103
+ onFailure: () => {
2104
+ setPhase("failed");
2105
+ setPayError("Payment failed or timed out. Please try again.");
2106
+ setMerchantRef(null);
2107
+ }
2108
+ });
2109
+ useEffect5(() => {
2110
+ client.api.getCheckoutConfig().then((res) => setConfig(res.payment_methods)).catch(() => {
2111
+ }).finally(() => setLoadingConfig(false));
2112
+ }, [client]);
2113
+ const hasPaymentMethods = config && Object.values(config).some((m) => m.enabled);
2114
+ const handlePay = async (e) => {
2115
+ e.preventDefault();
2116
+ if (!phone.trim()) {
2117
+ setPayError("Phone number is required.");
2118
+ return;
2119
+ }
2120
+ setPayError(null);
2121
+ setPhase("awaiting");
2122
+ try {
2123
+ if (typeof window !== "undefined") {
2124
+ localStorage.setItem("akropolys_user_phone", phone.trim());
2125
+ localStorage.setItem("akropolys_user_email", email.trim());
2126
+ localStorage.setItem("akropolys_user_firstname", firstName.trim());
2127
+ localStorage.setItem("akropolys_user_lastname", lastName.trim());
2128
+ }
2129
+ const res = await client.api.initiatePayment(phone.trim(), email, firstName, lastName);
2130
+ if (res?.merchantReference) {
2131
+ setMerchantRef(res.merchantReference);
2132
+ } else {
2133
+ throw new Error("No merchant reference returned.");
2134
+ }
2135
+ } catch (err) {
2136
+ setPhase("failed");
2137
+ setPayError(err.message || "Could not connect to payment processor.");
2138
+ }
2139
+ };
2140
+ const currency = cart?.currency || "KES";
2141
+ const total = cart?.total || 0;
2142
+ const backdropStyle = { ...customStyles, fontSize: "15px", fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', zIndex: 999999 };
2143
+ return createPortal3(
2144
+ /* @__PURE__ */ jsx8(
2145
+ "div",
2146
+ {
2147
+ className: "hsk-checkout-backdrop-full",
2148
+ style: backdropStyle,
2149
+ "data-hsk-theme": hskThemeAttr,
2150
+ children: /* @__PURE__ */ jsxs6(
2151
+ "div",
2152
+ {
2153
+ className: "hsk-checkout-modal-full",
2154
+ style: customStyles,
2155
+ "data-hsk-theme": hskThemeAttr,
2156
+ children: [
2157
+ /* @__PURE__ */ jsx8(
2158
+ "button",
2159
+ {
2160
+ onClick: (e) => {
2161
+ e.preventDefault();
2162
+ e.stopPropagation();
2163
+ onClose();
2164
+ },
2165
+ className: "hsk-checkout-close-x",
2166
+ "aria-label": "Close checkout",
2167
+ children: /* @__PURE__ */ jsxs6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2168
+ /* @__PURE__ */ jsx8("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2169
+ /* @__PURE__ */ jsx8("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2170
+ ] })
2171
+ }
2172
+ ),
2173
+ /* @__PURE__ */ jsx8("div", { className: "hsk-checkout-panel-left", children: /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-left-content", children: [
2174
+ /* @__PURE__ */ jsxs6("button", { onClick: (e) => {
2175
+ e.preventDefault();
2176
+ e.stopPropagation();
2177
+ onClose();
2178
+ }, className: "hsk-checkout-back-btn", children: [
2179
+ /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2180
+ /* @__PURE__ */ jsx8("line", { x1: "19", y1: "12", x2: "5", y2: "12" }),
2181
+ /* @__PURE__ */ jsx8("polyline", { points: "12 19 5 12 12 5" })
2182
+ ] }),
2183
+ "Back to store"
2184
+ ] }),
2185
+ /* @__PURE__ */ jsx8("div", { className: "hsk-checkout-store-info", children: /* @__PURE__ */ jsx8("h2", { children: "Secure Checkout" }) }),
2186
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-amount-due", children: [
2187
+ /* @__PURE__ */ jsx8("span", { className: "hsk-checkout-label-muted", children: "Pay total" }),
2188
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-grand-total", children: [
2189
+ currency,
2190
+ " ",
2191
+ total.toLocaleString(void 0, { minimumFractionDigits: 2 })
2192
+ ] })
2193
+ ] }),
2194
+ cartLoading || !cart ? /* @__PURE__ */ jsx8("p", { className: "hsk-cart-loading", children: "Loading order..." }) : /* @__PURE__ */ jsx8("div", { className: "hsk-checkout-items-list-wrap", children: /* @__PURE__ */ jsx8("ul", { className: "hsk-checkout-items-list", children: cart.items.map((item) => /* @__PURE__ */ jsxs6("li", { className: "hsk-checkout-item-row", children: [
2195
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-item-img-container", children: [
2196
+ item.image ? /* @__PURE__ */ jsx8("img", { src: item.image, alt: item.name, className: "hsk-checkout-item-img" }) : /* @__PURE__ */ jsx8("div", { className: "hsk-checkout-item-img-placeholder", children: "\u{1F6D2}" }),
2197
+ /* @__PURE__ */ jsx8("span", { className: "hsk-checkout-item-qty-badge", children: item.quantity })
2198
+ ] }),
2199
+ /* @__PURE__ */ jsx8("div", { className: "hsk-checkout-item-details", children: /* @__PURE__ */ jsx8("span", { className: "hsk-checkout-item-name", children: item.name }) }),
2200
+ /* @__PURE__ */ jsxs6("span", { className: "hsk-checkout-item-price", children: [
2201
+ item.currency,
2202
+ " ",
2203
+ (item.price_numeric * item.quantity).toLocaleString(void 0, { minimumFractionDigits: 2 })
2204
+ ] })
2205
+ ] }, item.id)) }) })
2206
+ ] }) }),
2207
+ /* @__PURE__ */ jsx8("div", { className: "hsk-checkout-panel-right", children: /* @__PURE__ */ jsx8("div", { className: "hsk-checkout-right-content", children: phase === "done" ? /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-status-card success", children: [
2208
+ /* @__PURE__ */ jsx8("div", { className: "hsk-status-icon-wrap success", children: /* @__PURE__ */ jsxs6("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2209
+ /* @__PURE__ */ jsx8("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
2210
+ /* @__PURE__ */ jsx8("polyline", { points: "22 4 12 14.01 9 11.01" })
2211
+ ] }) }),
2212
+ /* @__PURE__ */ jsx8("h3", { children: "Payment Successful!" }),
2213
+ /* @__PURE__ */ jsx8("p", { children: "Your transaction has been confirmed. Thank you for your order!" }),
2214
+ /* @__PURE__ */ jsx8("button", { onClick: onClose, className: "hsk-pay-btn hsk-btn-primary", style: { marginTop: "1.5rem" }, children: "Continue Shopping" })
2215
+ ] }) : phase === "awaiting" ? /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-status-card awaiting", children: [
2216
+ /* @__PURE__ */ jsx8("div", { className: "hsk-status-spinner-wrap", children: /* @__PURE__ */ jsx8("div", { className: "hsk-status-spinner" }) }),
2217
+ /* @__PURE__ */ jsx8("h3", { children: "Confirm payment on your phone" }),
2218
+ /* @__PURE__ */ jsxs6("p", { children: [
2219
+ "We've sent an M-Pesa STK push prompt to ",
2220
+ /* @__PURE__ */ jsxs6("strong", { children: [
2221
+ "254",
2222
+ phone
2223
+ ] }),
2224
+ "."
2225
+ ] }),
2226
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-stk-instructions", children: [
2227
+ /* @__PURE__ */ jsx8("p", { children: "1. Check your phone lockscreen for the M-Pesa prompt." }),
2228
+ /* @__PURE__ */ jsx8("p", { children: "2. Enter your M-Pesa PIN and press OK." }),
2229
+ /* @__PURE__ */ jsx8("p", { children: "3. Wait here \u2014 this page auto-updates once confirmed." })
2230
+ ] }),
2231
+ /* @__PURE__ */ jsx8(
2232
+ "button",
2233
+ {
2234
+ onClick: () => {
2235
+ setPhase("cancelled");
2236
+ setMerchantRef(null);
2237
+ },
2238
+ className: "hsk-checkout-cancel-btn",
2239
+ children: "Cancel payment"
2240
+ }
2241
+ )
2242
+ ] }) : phase === "cancelled" ? /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-status-card cancelled", children: [
2243
+ /* @__PURE__ */ jsx8("div", { className: "hsk-status-icon-wrap cancelled", children: /* @__PURE__ */ jsxs6("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2244
+ /* @__PURE__ */ jsx8("path", { d: "m15 9-6 6M9 9l6 6" }),
2245
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "10" })
2246
+ ] }) }),
2247
+ /* @__PURE__ */ jsx8("h3", { children: "Payment Cancelled" }),
2248
+ /* @__PURE__ */ jsx8("p", { children: "No charge was made. You can update your phone number and try again whenever you're ready." }),
2249
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-status-actions", children: [
2250
+ /* @__PURE__ */ jsx8("button", { onClick: () => {
2251
+ setPhase("idle");
2252
+ setPayError(null);
2253
+ }, className: "hsk-pay-btn hsk-btn-primary", children: "Try again" }),
2254
+ /* @__PURE__ */ jsx8("button", { onClick: onClose, className: "hsk-checkout-cancel-btn", children: "Back to cart" })
2255
+ ] })
2256
+ ] }) : phase === "failed" ? /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-status-card failed", children: [
2257
+ /* @__PURE__ */ jsx8("div", { className: "hsk-status-icon-wrap failed", children: /* @__PURE__ */ jsxs6("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2258
+ /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "10" }),
2259
+ /* @__PURE__ */ jsx8("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
2260
+ /* @__PURE__ */ jsx8("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
2261
+ ] }) }),
2262
+ /* @__PURE__ */ jsx8("h3", { children: "Payment Failed" }),
2263
+ /* @__PURE__ */ jsx8("p", { className: "hsk-checkout-error-text", children: payError || "Could not verify M-Pesa transaction. Please check your phone and try again." }),
2264
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-status-actions", children: [
2265
+ /* @__PURE__ */ jsx8("button", { onClick: () => {
2266
+ setPhase("idle");
2267
+ setPayError(null);
2268
+ }, className: "hsk-pay-btn hsk-btn-primary", children: "Try again" }),
2269
+ /* @__PURE__ */ jsx8("button", { onClick: onClose, className: "hsk-checkout-cancel-btn", children: "Back to cart" })
2270
+ ] })
2271
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: "hsk-checkout-payment-form-wrap", children: [
2272
+ /* @__PURE__ */ jsx8("h3", { className: "hsk-checkout-section-title", children: "Payment details" }),
2273
+ loadingConfig ? /* @__PURE__ */ jsx8("p", { className: "hsk-cart-loading", children: "Loading payment configuration..." }) : !hasPaymentMethods ? /* @__PURE__ */ jsx8("p", { className: "hsk-checkout-error", children: "No payment methods configured for this store." }) : /* @__PURE__ */ jsxs6("form", { onSubmit: handlePay, className: "hsk-stripe-checkout-form", children: [
2274
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-form-group", children: [
2275
+ /* @__PURE__ */ jsx8("label", { className: "hsk-form-label", children: "M-Pesa Mobile Number" }),
2276
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-phone-input-container", children: [
2277
+ /* @__PURE__ */ jsx8("span", { className: "hsk-phone-prefix", children: "254" }),
2278
+ /* @__PURE__ */ jsx8(
2279
+ "input",
2280
+ {
2281
+ type: "tel",
2282
+ required: true,
2283
+ placeholder: "712345678",
2284
+ pattern: "[0-9]{9}",
2285
+ maxLength: 9,
2286
+ value: phone,
2287
+ onChange: (e) => setPhone(e.target.value.replace(/\D/g, "")),
2288
+ className: "hsk-phone-input-field"
2289
+ }
2290
+ )
2291
+ ] }),
2292
+ /* @__PURE__ */ jsx8("span", { className: "hsk-form-hint", children: "Enter your 9-digit number (e.g. 712345678)" })
2293
+ ] }),
2294
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-form-group", children: [
2295
+ /* @__PURE__ */ jsx8("label", { className: "hsk-form-label", children: "Email address" }),
2296
+ /* @__PURE__ */ jsx8(
2297
+ "input",
2298
+ {
2299
+ type: "email",
2300
+ placeholder: "john.doe@example.com",
2301
+ value: email,
2302
+ onChange: (e) => setEmail(e.target.value),
2303
+ className: "hsk-form-input"
2304
+ }
2305
+ )
2306
+ ] }),
2307
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-form-row", children: [
2308
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-form-group", children: [
2309
+ /* @__PURE__ */ jsx8("label", { className: "hsk-form-label", children: "First Name" }),
2310
+ /* @__PURE__ */ jsx8(
2311
+ "input",
2312
+ {
2313
+ type: "text",
2314
+ placeholder: "John",
2315
+ value: firstName,
2316
+ onChange: (e) => setFirstName(e.target.value),
2317
+ className: "hsk-form-input"
2318
+ }
2319
+ )
2320
+ ] }),
2321
+ /* @__PURE__ */ jsxs6("div", { className: "hsk-form-group", children: [
2322
+ /* @__PURE__ */ jsx8("label", { className: "hsk-form-label", children: "Last Name" }),
2323
+ /* @__PURE__ */ jsx8(
2324
+ "input",
2325
+ {
2326
+ type: "text",
2327
+ placeholder: "Doe",
2328
+ value: lastName,
2329
+ onChange: (e) => setLastName(e.target.value),
2330
+ className: "hsk-form-input"
2331
+ }
2332
+ )
2333
+ ] })
2334
+ ] }),
2335
+ payError && /* @__PURE__ */ jsx8("div", { className: "hsk-form-error-banner", children: payError }),
2336
+ /* @__PURE__ */ jsxs6("button", { type: "submit", className: "hsk-checkout-submit-btn", children: [
2337
+ "Pay ",
2338
+ currency,
2339
+ " ",
2340
+ total.toLocaleString()
2341
+ ] }),
2342
+ /* @__PURE__ */ jsx8("div", { className: "hsk-checkout-footer-brand", children: /* @__PURE__ */ jsx8("span", { children: "Powered by Akropolys" }) })
2343
+ ] })
2344
+ ] }) }) })
2345
+ ]
2346
+ }
2347
+ )
2348
+ }
2349
+ ),
2350
+ document.body
2351
+ );
2352
+ }
2353
+
2354
+ // src/components/CartDrawer.tsx
2355
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2356
+ function CartDrawer({
2357
+ trigger,
2358
+ className,
2359
+ theme
2360
+ }) {
2361
+ const { cart, loading } = useCart3();
2362
+ const [open, setOpen] = useState6(false);
2363
+ const [showCheckout, setShowCheckout] = useState6(false);
2364
+ const [mounted, setMounted] = useState6(false);
2365
+ const client = useAkropolysContext5();
2366
+ useEffect6(() => {
2367
+ setMounted(true);
2368
+ const handleTriggerCheckout = () => {
2369
+ setShowCheckout(true);
2370
+ setOpen(false);
2371
+ };
2372
+ window.addEventListener("akropolys:trigger_checkout", handleTriggerCheckout);
2373
+ return () => {
2374
+ window.removeEventListener("akropolys:trigger_checkout", handleTriggerCheckout);
2375
+ };
2376
+ }, []);
2377
+ useEffect6(() => {
2378
+ if (open) {
2379
+ document.body.style.overflow = "hidden";
2380
+ } else {
2381
+ document.body.style.overflow = "";
2382
+ }
2383
+ return () => {
2384
+ document.body.style.overflow = "";
2385
+ };
2386
+ }, [open]);
2387
+ const handleCheckout = async () => {
2388
+ if (!cart || cart.items.length === 0) return;
2389
+ const event = new CustomEvent("akropolys:trigger_checkout", { cancelable: true });
2390
+ window.dispatchEvent(event);
2391
+ if (event.defaultPrevented) {
2392
+ setOpen(false);
2393
+ return;
2394
+ }
2395
+ setShowCheckout(true);
2396
+ };
2397
+ const isStringTheme = typeof theme === "string";
2398
+ const hskThemeAttr = isStringTheme ? theme : void 0;
2399
+ const customStyles = !isStringTheme && theme ? {
2400
+ ...theme?.primaryColor && { "--hsk-primary": theme.primaryColor, "--hsk-primary-color": theme.primaryColor },
2401
+ ...theme?.backgroundColor && { "--hsk-bg": theme.backgroundColor },
2402
+ ...theme?.textColor && { "--hsk-text": theme.textColor },
2403
+ ...theme?.fontFamily && { "--hsk-font": theme.fontFamily },
2404
+ ...theme?.borderRadius && { "--hsk-border-radius": theme.borderRadius }
2405
+ } : void 0;
2406
+ return /* @__PURE__ */ jsxs7(Fragment5, { children: [
2407
+ trigger ? /* @__PURE__ */ jsx9("div", { onClick: () => setOpen(true), style: { display: "inline-block" }, children: trigger }) : /* @__PURE__ */ jsxs7(
2408
+ "button",
2409
+ {
2410
+ onClick: () => setOpen(true),
2411
+ className: cn("hsk-cart-trigger", className),
2412
+ style: customStyles,
2413
+ "data-hsk-theme": hskThemeAttr,
2414
+ "aria-label": "Open cart",
2415
+ children: [
2416
+ /* @__PURE__ */ jsxs7("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2417
+ /* @__PURE__ */ jsx9("circle", { cx: "9", cy: "21", r: "1" }),
2418
+ /* @__PURE__ */ jsx9("circle", { cx: "20", cy: "21", r: "1" }),
2419
+ /* @__PURE__ */ jsx9("path", { d: "M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" })
2420
+ ] }),
2421
+ cart && cart.item_count > 0 ? /* @__PURE__ */ jsx9("span", { className: "hsk-cart-trigger-badge", children: cart.item_count }) : null
2422
+ ]
2423
+ }
2424
+ ),
2425
+ open && mounted && createPortal4(
2426
+ /* @__PURE__ */ jsx9(
2427
+ "div",
2428
+ {
2429
+ className: "hsk-cart-backdrop",
2430
+ style: customStyles,
2431
+ "data-hsk-theme": hskThemeAttr,
2432
+ onClick: () => setOpen(false),
2433
+ children: /* @__PURE__ */ jsxs7(
2434
+ "div",
2435
+ {
2436
+ className: "hsk-cart-bottom-sheet",
2437
+ style: customStyles,
2438
+ "data-hsk-theme": hskThemeAttr,
2439
+ onClick: (e) => e.stopPropagation(),
2440
+ children: [
2441
+ /* @__PURE__ */ jsx9("div", { className: "hsk-cart-sheet-handle" }),
2442
+ /* @__PURE__ */ jsxs7("div", { className: "hsk-cart-sheet-header", children: [
2443
+ /* @__PURE__ */ jsx9("h2", { children: "Your Cart" }),
2444
+ /* @__PURE__ */ jsx9("button", { onClick: () => setOpen(false), className: "hsk-close-btn", children: "\xD7" })
2445
+ ] }),
2446
+ /* @__PURE__ */ jsx9("div", { className: "hsk-cart-sheet-content", children: loading && !cart ? /* @__PURE__ */ jsx9("div", { className: "hsk-cart-loading", children: "Loading cart..." }) : !cart || cart.items.length === 0 ? /* @__PURE__ */ jsx9("div", { className: "hsk-cart-empty", children: "Your cart is empty." }) : /* @__PURE__ */ jsx9("ul", { className: "hsk-cart-items", children: cart.items.map((item) => /* @__PURE__ */ jsxs7("li", { className: "hsk-cart-item", children: [
2447
+ item.image && /* @__PURE__ */ jsx9("img", { src: item.image, alt: item.name, className: "hsk-cart-item-img" }),
2448
+ /* @__PURE__ */ jsxs7("div", { className: "hsk-cart-item-info", children: [
2449
+ /* @__PURE__ */ jsx9("span", { className: "hsk-cart-item-name", children: item.name }),
2450
+ /* @__PURE__ */ jsxs7("span", { className: "hsk-cart-item-price", children: [
2451
+ item.currency,
2452
+ " ",
2453
+ item.price_numeric.toLocaleString(void 0, { minimumFractionDigits: 2 })
2454
+ ] })
2455
+ ] }),
2456
+ /* @__PURE__ */ jsxs7("div", { className: "hsk-cart-item-qty", children: [
2457
+ "x",
2458
+ item.quantity
2459
+ ] })
2460
+ ] }, item.id)) }) }),
2461
+ cart && cart.items.length > 0 && /* @__PURE__ */ jsxs7("div", { className: "hsk-cart-sheet-footer", children: [
2462
+ /* @__PURE__ */ jsxs7("div", { className: "hsk-cart-total", children: [
2463
+ /* @__PURE__ */ jsx9("span", { children: "Total" }),
2464
+ /* @__PURE__ */ jsxs7("span", { children: [
2465
+ cart.currency,
2466
+ " ",
2467
+ cart.total.toLocaleString(void 0, { minimumFractionDigits: 2 })
2468
+ ] })
2469
+ ] }),
2470
+ /* @__PURE__ */ jsx9("button", { onClick: handleCheckout, className: "hsk-checkout-btn", children: "Checkout securely" })
2471
+ ] })
2472
+ ]
2473
+ }
2474
+ )
2475
+ }
2476
+ ),
2477
+ document.body
2478
+ ),
2479
+ showCheckout && mounted && /* @__PURE__ */ jsx9(
2480
+ CheckoutModal,
2481
+ {
2482
+ onClose: () => {
2483
+ setShowCheckout(false);
2484
+ setOpen(false);
2485
+ },
2486
+ theme: isStringTheme ? theme : void 0,
2487
+ customStyles,
2488
+ hskThemeAttr
2489
+ }
2490
+ )
2491
+ ] });
2492
+ }
2493
+ export {
2494
+ CartBadge,
2495
+ CartDrawer,
2496
+ ChatWidget,
2497
+ CheckoutModal,
2498
+ ComparisonMatrix,
2499
+ KikuButton,
2500
+ ChatWidget as KikuChat,
2501
+ SearchBar,
2502
+ Sparkle
2503
+ };
2504
+ //# sourceMappingURL=index.mjs.map