@akropolys/kiku 1.5.18 → 1.5.19

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/README.md CHANGED
@@ -1,30 +1,16 @@
1
1
  # @akropolys/kiku
2
2
 
3
- > Plug-and-play React AI chat, search, and commerce UI components.
3
+ [![npm](https://img.shields.io/npm/v/@akropolys/kiku?color=orange)](https://www.npmjs.com/package/@akropolys/kiku) [![License](https://img.shields.io/npm/l/@akropolys/kiku)](https://github.com/akropolys/sdk/blob/main/LICENSE) [![TypeScript](https://img.shields.io/badge/types-included-blue)](https://www.typescriptlang.org/)
4
4
 
5
- Kiku is the ready-made UI layer for Akropolys. Drop in a floating chat button, a search bar with AI-powered results, a product comparison matrix, or a full checkout modal — all styled, animated, and wired to the Akropolys backend out of the box.
6
-
7
- If you want full control over the UI, use [`@akropolys/sdk`](https://www.npmjs.com/package/@akropolys/sdk) directly.
5
+ **[Docs](https://akropolys.io/docs)**  •  **[GitHub](https://github.com/akropolys/sdk)**
8
6
 
9
7
  ---
10
8
 
11
- ## Install
12
-
13
- ```bash
14
- npm install @akropolys/kiku @akropolys/sdk
15
- # or
16
- pnpm add @akropolys/kiku @akropolys/sdk
17
- ```
18
-
19
- **Import the stylesheet once in your app entry point:**
9
+ ## What is `@akropolys/kiku`?
20
10
 
21
- ```ts
22
- import '@akropolys/kiku/styles.css';
23
- ```
24
-
25
- ---
11
+ Ready-made React UI components for the Akropolys intent platform. Drop in a floating chat assistant or AI search bar and your catalog is immediately searchable in natural language — no configuration required.
26
12
 
27
- ## Quick start
13
+ For full UI control, use [`@akropolys/sdk`](https://www.npmjs.com/package/@akropolys/sdk) directly.
28
14
 
29
15
  ```tsx
30
16
  import { AkropolysProvider } from '@akropolys/sdk';
@@ -34,169 +20,41 @@ import '@akropolys/kiku/styles.css';
34
20
  export default function App() {
35
21
  return (
36
22
  <AkropolysProvider
37
- siteId="your-site-id"
38
- apiUrl="https://api.akropolys.io"
39
- apiToken="your-api-token"
23
+ siteId={process.env.NEXT_PUBLIC_AKROPOLYS_SITE_ID}
24
+ apiUrl={process.env.NEXT_PUBLIC_AKROPOLYS_API_URL}
25
+ apiToken={process.env.NEXT_PUBLIC_AKROPOLYS_API_TOKEN}
40
26
  >
41
- {/* Floating chat button — renders the full AI shopping assistant */}
42
- <KikuButton label="Chat with Kiku" />
27
+ <KikuButton />
43
28
  </AkropolysProvider>
44
29
  );
45
30
  }
46
31
  ```
47
32
 
48
- ---
49
-
50
33
  ## Components
51
34
 
52
- ### `<KikuButton />` Floating AI chat assistant
53
-
54
- ```tsx
55
- import { KikuButton } from '@akropolys/kiku';
56
-
57
- <KikuButton
58
- label="Ask Kiku"
59
- title="Shopping Assistant"
60
- placeholder="What are you looking for?"
61
- theme="dark" // 'light' | 'dark' | AkropolysTheme
62
- chips={['Best phones', 'Laptops under KSh 50K']}
63
- onSelectSource={(src) => console.log(src)}
64
- />
65
- ```
66
-
67
- ---
68
-
69
- ### `<KikuChat />` — Inline embedded chat widget
70
-
71
- ```tsx
72
- import { KikuChat } from '@akropolys/kiku';
73
-
74
- <KikuChat
75
- title="AI Shopping Assistant"
76
- placeholder="Ask about anything in our store…"
77
- defaultCurrency="KES"
78
- />
79
- ```
80
-
81
- ---
82
-
83
- ### `<SearchBar />` — Debounced AI search with dropdown
84
-
85
- ```tsx
86
- import { SearchBar } from '@akropolys/kiku';
87
-
88
- <SearchBar
89
- placeholder="Search products…"
90
- limit={8}
91
- debounceMs={300}
92
- onSelect={(result) => {
93
- window.location.href = result.product.url;
94
- }}
95
- />
96
- ```
97
-
98
- ---
99
-
100
- ### `<Sparkle />` — Per-product AI assistant button
101
-
102
- ```tsx
103
- import { Sparkle } from '@akropolys/kiku';
104
-
105
- // Place next to any product on a listing or detail page
106
- <Sparkle productName="Samsung Galaxy S24" />
107
- ```
108
-
109
- ---
110
-
111
- ### `<ComparisonMatrix />` — Side-by-side product comparison table
112
-
113
- ```tsx
114
- import { ComparisonMatrix } from '@akropolys/kiku';
115
-
116
- // sources come from useKiku() or useSearch()
117
- <ComparisonMatrix sources={sources} defaultCurrency="KES" />
118
- ```
119
-
120
- ---
121
-
122
- ### `<CartBadge />` — Cart item count badge
123
-
124
- ```tsx
125
- import { CartBadge } from '@akropolys/kiku';
126
-
127
- <CartBadge className="my-badge" />
128
- ```
129
-
130
- ---
131
-
132
- ### `<CartDrawer />` — Slide-out cart drawer
133
-
134
- ```tsx
135
- import { CartDrawer } from '@akropolys/kiku';
136
-
137
- <CartDrawer trigger={<button>🛒 Cart</button>} theme="dark" />
138
- ```
35
+ | Component | Description |
36
+ |---|---|
37
+ | `<KikuButton />` | Floating AI chat assistant |
38
+ | `<KikuChat />` | Inline embedded chat widget |
39
+ | `<SearchBar />` | AI search with dropdown results |
40
+ | `<Sparkle />` | Per-product assistant button |
41
+ | `<ComparisonMatrix />` | Side-by-side product comparison |
42
+ | `<CartBadge />` | Cart item count |
43
+ | `<CartDrawer />` | Slide-out cart |
44
+ | `<CheckoutModal />` | Checkout modal |
139
45
 
140
- ---
141
-
142
- ### `<CheckoutModal />` — Full checkout modal (M-Pesa + extensible)
143
-
144
- ```tsx
145
- import { CheckoutModal } from '@akropolys/kiku';
146
-
147
- <CheckoutModal onClose={() => setOpen(false)} theme="dark" />
148
- ```
149
-
150
- ---
151
-
152
- ## Theming
153
-
154
- All components accept a `theme` prop. Pass `'light'` | `'dark'` for the built-in modes, or pass an `AkropolysTheme` object for full control:
155
-
156
- ```tsx
157
- <KikuButton
158
- theme={{
159
- primaryColor: '#6d28d9',
160
- backgroundColor: '#0f0f0f',
161
- textColor: '#f5f5f5',
162
- fontFamily: 'Inter, sans-serif',
163
- borderRadius: '12px',
164
- }}
165
- />
166
- ```
167
-
168
- Alternatively, override CSS variables globally in your stylesheet:
169
-
170
- ```css
171
- :root {
172
- --hsk-primary: #6d28d9;
173
- --hsk-bg: #0f0f0f;
174
- --hsk-text: #f5f5f5;
175
- --hsk-font: 'Inter', sans-serif;
176
- --hsk-border-radius: 12px;
177
- }
178
- ```
179
-
180
- ---
181
-
182
- ## Using the headless SDK instead
183
-
184
- If you want to build your own UI from scratch, install only the core:
46
+ ## Installation
185
47
 
186
48
  ```bash
187
- npm install @akropolys/sdk
49
+ npm install @akropolys/kiku @akropolys/sdk
188
50
  ```
189
51
 
190
- ```tsx
191
- import { useKiku } from '@akropolys/sdk';
192
-
193
- const { send, messages, loading, streaming } = useKiku({
194
- onToken: (token) => appendToMyUI(token),
195
- onMeta: (meta) => showProducts(meta.sources),
196
- onDone: (full) => saveToDatabase(full),
197
- });
52
+ ```ts
53
+ import '@akropolys/kiku/styles.css';
198
54
  ```
199
55
 
56
+ [Read the full docs →](https://akropolys.io/docs)
57
+
200
58
  ---
201
59
 
202
60
  ## License
package/dist/index.d.mts CHANGED
@@ -78,11 +78,11 @@ interface SparkleProps {
78
78
  onResult?: (results: SearchResult[]) => void;
79
79
  /** Override the backdrop colour (any CSS colour/gradient) */
80
80
  backdropColor?: string;
81
- /** Override backdrop blur e.g. "8px" or 8 */
81
+ /** Override backdrop blur — e.g. "8px" or 8 */
82
82
  backdropBlur?: string | number;
83
83
  /** Extra classes on the trigger button */
84
84
  className?: string;
85
- /** Called when user clicks a result return false to prevent default navigation */
85
+ /** Called when user clicks a result — return false to prevent default navigation */
86
86
  onNavigate?: (result: SearchResult) => boolean | void;
87
87
  theme?: AkropolysTheme;
88
88
  classNames?: {
package/dist/index.d.ts CHANGED
@@ -78,11 +78,11 @@ interface SparkleProps {
78
78
  onResult?: (results: SearchResult[]) => void;
79
79
  /** Override the backdrop colour (any CSS colour/gradient) */
80
80
  backdropColor?: string;
81
- /** Override backdrop blur e.g. "8px" or 8 */
81
+ /** Override backdrop blur — e.g. "8px" or 8 */
82
82
  backdropBlur?: string | number;
83
83
  /** Extra classes on the trigger button */
84
84
  className?: string;
85
- /** Called when user clicks a result return false to prevent default navigation */
85
+ /** Called when user clicks a result — return false to prevent default navigation */
86
86
  onNavigate?: (result: SearchResult) => boolean | void;
87
87
  theme?: AkropolysTheme;
88
88
  classNames?: {
package/dist/index.js CHANGED
@@ -106,7 +106,7 @@ function SearchBar({
106
106
  }
107
107
  ignoreNextQueryChange.current = true;
108
108
  setOpen(false);
109
- setQuery(r.product.name ?? "");
109
+ setQuery(r.entity.title ?? r.entity.name ?? "");
110
110
  onSelect?.(r);
111
111
  };
112
112
  const handleCommitSearch = () => {
@@ -186,8 +186,8 @@ function SearchBar({
186
186
  children: [
187
187
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "hsk-sb-row-icon", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) }),
188
188
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "hsk-sb-row-body", children: [
189
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "hsk-sb-row-title", children: r.product.name }),
190
- (r.product.category || r.product.brand) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "hsk-sb-row-sub", children: r.product.category ?? r.product.brand })
189
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "hsk-sb-row-title", children: r.entity.title ?? r.entity.name }),
190
+ (r.entity.category || r.entity.brand) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "hsk-sb-row-sub", children: r.entity.category ?? r.entity.brand })
191
191
  ] })
192
192
  ]
193
193
  },
@@ -688,7 +688,7 @@ I couldn't find any matching products in the store.`;
688
688
  }) }) });
689
689
  }
690
690
  const featured = sources.filter((src) => src.id && referencedIds.includes(src.id));
691
- const general = featured.length > 0 ? [] : sources.filter((src) => !src.id || !referencedIds.includes(src.id));
691
+ const general = referencedIds.length > 0 ? [] : sources.filter((src) => !src.id || !referencedIds.includes(src.id));
692
692
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-sources-container", children: [
693
693
  featured.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "hsk-sources-group", style: { marginBottom: "10px" }, children: [
694
694
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hsk-sources-group-title", children: "\u2B50 Featured in response" }),
@@ -1360,7 +1360,7 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource, referencedI
1360
1360
  railRef.current?.scrollBy({ left: 170, behavior: "smooth" });
1361
1361
  };
1362
1362
  const referenced = sources.filter((s) => s.id && referencedIds.includes(s.id));
1363
- const display = referenced.length > 0 ? referenced : sources;
1363
+ const display = referencedIds.length > 0 ? referenced : sources;
1364
1364
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "hsk-cb-sources-wrap", children: [
1365
1365
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "hsk-cb-sources", ref: railRef, children: display.map((src, si) => {
1366
1366
  const isReferenced = !!(src.id && referencedIds.includes(src.id));
@@ -2475,7 +2475,7 @@ function SparkleModal({
2475
2475
  if (!initialProduct && !fetchedProduct) {
2476
2476
  client.api.searchVector(productName, 1).then((res) => {
2477
2477
  if (res.results && res.results.length > 0) {
2478
- setFetchedProduct(res.results[0].product);
2478
+ setFetchedProduct(res.results[0].entity);
2479
2479
  }
2480
2480
  }).catch((err) => console.error("[Akropolys] Failed to fetch product details", err));
2481
2481
  }
@@ -2508,7 +2508,7 @@ function SparkleModal({
2508
2508
  const prevent = onNavigate?.(r);
2509
2509
  if (prevent !== false) {
2510
2510
  onClose();
2511
- if (r.product.url) window.location.href = r.product.url;
2511
+ if (r.entity.url) window.location.href = r.entity.url;
2512
2512
  }
2513
2513
  };
2514
2514
  const handleSend = async (text) => {
@@ -2595,7 +2595,7 @@ Question: ${q}`;
2595
2595
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }),
2596
2596
  idx === 0 && displayProduct && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-mobile-attachment-deck", children: [
2597
2597
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-mobile-main-card", children: [
2598
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-mobile-main-card-img", children: displayProduct.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: displayProduct.images[0], alt: displayProduct.name }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "\u{1F6CD}" }) }),
2598
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-mobile-main-card-img", children: displayProduct.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: displayProduct.images[0], alt: displayProduct.name }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "\xF0\u0178\u203A\x8D" }) }),
2599
2599
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-mobile-main-card-info", children: [
2600
2600
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-mobile-main-card-brand", children: displayProduct.brand || displayProduct.category || "Product" }),
2601
2601
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-mobile-main-card-name", children: displayProduct.name }),
@@ -2618,8 +2618,8 @@ Question: ${q}`;
2618
2618
  (() => {
2619
2619
  const similarProducts = results.filter(
2620
2620
  (r) => {
2621
- const isSameName = !!(r.product.name && displayProduct?.name && r.product.name.toLowerCase() === displayProduct.name.toLowerCase());
2622
- const isSameSlug = r.product.slug && displayProduct?.slug && r.product.slug.toLowerCase() === displayProduct.slug.toLowerCase();
2621
+ const isSameName = !!(r.entity.name && displayProduct?.name && r.entity.name.toLowerCase() === displayProduct.name.toLowerCase());
2622
+ const isSameSlug = r.entity.slug && displayProduct?.slug && r.entity.slug.toLowerCase() === displayProduct.slug.toLowerCase();
2623
2623
  return !isSameName && !isSameSlug;
2624
2624
  }
2625
2625
  );
@@ -2627,17 +2627,17 @@ Question: ${q}`;
2627
2627
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-mobile-similar-carousel-inline", children: [
2628
2628
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-mobile-similar-carousel-title", children: "Similar Products" }),
2629
2629
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-mobile-similar-carousel-list", children: similarProducts.map((r) => {
2630
- const price = parseFloat(r.product.price?.replace(/[^0-9.]/g, "") || "0");
2631
- const currency = r.product.currency ?? "KES";
2630
+ const price = parseFloat(r.entity.price?.replace(/[^0-9.]/g, "") || "0");
2631
+ const currency = r.entity.currency ?? "KES";
2632
2632
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2633
2633
  "div",
2634
2634
  {
2635
2635
  className: "hsk-sp-mobile-similar-carousel-item",
2636
2636
  onClick: () => handleNav(r),
2637
2637
  children: [
2638
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-mobile-similar-carousel-img", children: r.product.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: r.product.images[0], alt: r.product.name }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "\u{1F6CD}" }) }),
2638
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-mobile-similar-carousel-img", children: r.entity.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: r.entity.images[0], alt: r.entity.name }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "\xF0\u0178\u203A\x8D" }) }),
2639
2639
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-mobile-similar-carousel-meta", children: [
2640
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-mobile-similar-carousel-name", title: r.product.name, children: r.product.name }),
2640
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-mobile-similar-carousel-name", title: r.entity.name, children: r.entity.name }),
2641
2641
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-mobile-similar-carousel-price", children: [
2642
2642
  currency,
2643
2643
  " ",
@@ -2741,7 +2741,7 @@ Question: ${q}`;
2741
2741
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-details-pane", children: [
2742
2742
  displayProduct && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-product-profile-container", children: [
2743
2743
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-product-profile", children: [
2744
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-details-imgwrap", children: displayProduct.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: displayProduct.images[0], alt: displayProduct.name }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
2744
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-details-imgwrap", children: displayProduct.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: displayProduct.images[0], alt: displayProduct.name }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-sp-img-placeholder", children: "\xF0\u0178\u203A\x8D" }) }),
2745
2745
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-details-meta", children: [
2746
2746
  displayProduct.brand && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-sp-item-brand", children: displayProduct.brand }),
2747
2747
  displayProduct.category && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-sp-item-cat", children: displayProduct.category }),
@@ -2758,7 +2758,7 @@ Question: ${q}`;
2758
2758
  ] }),
2759
2759
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-item-meta-badges", children: [
2760
2760
  displayProduct.rating && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "hsk-sp-meta-badge hsk-sp-meta-badge-rating", children: [
2761
- "\u2605 ",
2761
+ "\xE2\u02DC\u2026 ",
2762
2762
  parseFloat(displayProduct.rating.toString()).toFixed(1),
2763
2763
  " ",
2764
2764
  displayProduct.reviewCount ? `(${displayProduct.reviewCount})` : ""
@@ -2788,8 +2788,8 @@ Question: ${q}`;
2788
2788
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-results", children: (() => {
2789
2789
  const similarProducts = results.filter(
2790
2790
  (r) => {
2791
- const isSameName = !!(r.product.name && displayProduct?.name && r.product.name.toLowerCase() === displayProduct.name.toLowerCase());
2792
- const isSameSlug = r.product.slug && displayProduct?.slug && r.product.slug.toLowerCase() === displayProduct.slug.toLowerCase();
2791
+ const isSameName = !!(r.entity.name && displayProduct?.name && r.entity.name.toLowerCase() === displayProduct.name.toLowerCase());
2792
+ const isSameSlug = r.entity.slug && displayProduct?.slug && r.entity.slug.toLowerCase() === displayProduct.slug.toLowerCase();
2793
2793
  return !isSameName && !isSameSlug;
2794
2794
  }
2795
2795
  );
@@ -2797,8 +2797,8 @@ Question: ${q}`;
2797
2797
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-empty", children: "No similar products found." });
2798
2798
  }
2799
2799
  return similarProducts.map((r, i) => {
2800
- const price = parseFloat(r.product.price?.replace(/[^0-9.]/g, "") || "0");
2801
- const currency = r.product.currency ?? "KES";
2800
+ const price = parseFloat(r.entity.price?.replace(/[^0-9.]/g, "") || "0");
2801
+ const currency = r.entity.currency ?? "KES";
2802
2802
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2803
2803
  "div",
2804
2804
  {
@@ -2806,11 +2806,11 @@ Question: ${q}`;
2806
2806
  style: { animationDelay: `${i * 55}ms`, cursor: "pointer" },
2807
2807
  onClick: () => handleNav(r),
2808
2808
  children: [
2809
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-img-wrap", children: r.product.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: r.product.images[0], alt: r.product.name }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
2809
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-img-wrap", children: r.entity.images?.[0] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: r.entity.images[0], alt: r.entity.name }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-sp-img-placeholder", children: "\xF0\u0178\u203A\x8D" }) }),
2810
2810
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-item-body", children: [
2811
2811
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
2812
- r.product.category && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-item-cat", children: r.product.category }),
2813
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-item-name", title: r.product.name, children: r.product.name })
2812
+ r.entity.category && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-item-cat", children: r.entity.category }),
2813
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-sp-item-name", title: r.entity.name, children: r.entity.name })
2814
2814
  ] }),
2815
2815
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-sp-item-price-row", children: [
2816
2816
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-sp-item-currency", children: currency }),