@commerce-ai-tool/angular 2.4.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +16 -0
  4. package/dist/index.cjs +2811 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.css +1015 -0
  7. package/dist/index.css.map +1 -0
  8. package/dist/index.d.cts +9 -0
  9. package/dist/index.d.ts +9 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +2780 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/lib/camera.util.d.ts +7 -0
  14. package/dist/lib/camera.util.d.ts.map +1 -0
  15. package/dist/lib/commerce-ai-api.service.d.ts +35 -0
  16. package/dist/lib/commerce-ai-api.service.d.ts.map +1 -0
  17. package/dist/lib/commerce-ai-cart-panel.component.d.ts +34 -0
  18. package/dist/lib/commerce-ai-cart-panel.component.d.ts.map +1 -0
  19. package/dist/lib/commerce-ai-cart.service.d.ts +61 -0
  20. package/dist/lib/commerce-ai-cart.service.d.ts.map +1 -0
  21. package/dist/lib/commerce-ai-mission-results.component.d.ts +26 -0
  22. package/dist/lib/commerce-ai-mission-results.component.d.ts.map +1 -0
  23. package/dist/lib/commerce-ai-search.component.d.ts +141 -0
  24. package/dist/lib/commerce-ai-search.component.d.ts.map +1 -0
  25. package/dist/lib/mission.util.d.ts +6 -0
  26. package/dist/lib/mission.util.d.ts.map +1 -0
  27. package/dist/vendor-core/client.d.cts +12 -0
  28. package/dist/vendor-core/client.d.ts +12 -0
  29. package/dist/vendor-core/commercetools/facet-color.d.ts +12 -0
  30. package/dist/vendor-core/commercetools/facets.d.ts +31 -0
  31. package/dist/vendor-core/messages/index.d.ts +110 -0
  32. package/dist/vendor-core/payments/types.d.ts +35 -0
  33. package/dist/vendor-core/search/compound-shopping-list.d.ts +6 -0
  34. package/dist/vendor-core/types/index.d.ts +510 -0
  35. package/package.json +83 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,2811 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ CommerceAiApiService: () => CommerceAiApiService,
25
+ CommerceAiCartPanelComponent: () => CommerceAiCartPanelComponent,
26
+ CommerceAiCartService: () => CommerceAiCartService,
27
+ CommerceAiMissionResultsComponent: () => CommerceAiMissionResultsComponent,
28
+ CommerceAiSearchComponent: () => CommerceAiSearchComponent,
29
+ DEFAULT_COMMERCE_AI_SEARCH_MESSAGES: () => DEFAULT_COMMERCE_AI_SEARCH_MESSAGES,
30
+ resolveCommerceAISearchMessages: () => resolveCommerceAISearchMessages
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/lib/commerce-ai-search.component.ts
35
+ var import_core5 = require("@angular/core");
36
+ var import_forms2 = require("@angular/forms");
37
+
38
+ // ../core/dist/client.js
39
+ var CART_SESSION_HEADER = "x-commerce-ai-cart-session";
40
+ var DEFAULT_COMMERCE_AI_SEARCH_MESSAGES = {
41
+ placeholder: "What are you looking for?",
42
+ searchAriaLabel: "Search query",
43
+ productSearchAriaLabel: "Product search",
44
+ searchResultsAriaLabel: "Search results",
45
+ searching: "Searching...",
46
+ noProductsFound: "No products found",
47
+ searchedFor: "Searched for:",
48
+ dropImageToSearch: "Drop image to search",
49
+ voiceSearch: "Voice search",
50
+ stopRecording: "Stop recording",
51
+ searchByCamera: "Search by camera",
52
+ searchByImage: "Search by image",
53
+ replayVoiceSummary: "Replay voice result summary",
54
+ listening: "Listening\u2026",
55
+ tapMicToStop: "Tap mic to stop",
56
+ understandingQuery: "Understanding your query\u2026",
57
+ preparingAudioSummary: "Preparing audio summary\u2026",
58
+ cameraCapture: "Camera capture",
59
+ cameraPreview: "Camera preview",
60
+ capturePhoto: "Capture",
61
+ cancel: "Cancel",
62
+ dismiss: "Dismiss",
63
+ searchFailed: "Search failed",
64
+ imageSearchFailed: "Image search failed",
65
+ couldNotCapturePhoto: "Could not capture photo",
66
+ suggestionsAriaLabel: "Search suggestions",
67
+ loadingSuggestions: "Loading suggestions...",
68
+ noSuggestions: "No suggestions",
69
+ filtersAriaLabel: "Search filters",
70
+ clearFilters: "Clear filters",
71
+ narrowResults: "Narrow your results",
72
+ newSearch: "New search",
73
+ addToCart: "Add to cart",
74
+ cart: "Cart",
75
+ emptyCart: "Your cart is empty",
76
+ removeItem: "Remove item",
77
+ total: "Total",
78
+ each: "each",
79
+ cartAriaLabel: "Shopping cart",
80
+ itemAdded: "Added to cart",
81
+ increaseQuantity: "Increase quantity",
82
+ decreaseQuantity: "Decrease quantity",
83
+ closeCart: "Close cart",
84
+ unableToAddToCart: "This product cannot be added to the cart",
85
+ missionTitle: "Shopping list",
86
+ missionQuantity: "Looking for",
87
+ missionAddAll: "Add {count} top picks to cart",
88
+ missionItemsAdded: "Added to cart",
89
+ missionSelectProduct: "Select this product",
90
+ missionIntentEmpty: "No matching products",
91
+ missionIntentFailed: "Couldn't search this item",
92
+ signIn: "Sign in",
93
+ signInToSyncCart: "Sign in to sync your cart",
94
+ signOut: "Sign out",
95
+ email: "Email",
96
+ password: "Password",
97
+ signedInAs: "Signed in as",
98
+ invalidCredentials: "Invalid email or password",
99
+ signInFailed: "Sign in failed",
100
+ checkout: "Checkout",
101
+ checkoutTitle: "Complete your order",
102
+ orderSummary: "Order summary",
103
+ shippingAddress: "Shipping address",
104
+ billingAddress: "Billing address",
105
+ billingSameAsShipping: "Billing address is the same as shipping",
106
+ continueToDelivery: "Continue to delivery",
107
+ shippingMethod: "Delivery method",
108
+ selectShippingMethod: "Select a delivery method",
109
+ noShippingMethods: "No delivery methods are available for this address",
110
+ completeAddressToContinue: "Add a shipping address to continue",
111
+ selectDeliveryToContinue: "Select a delivery method to continue",
112
+ selectPaymentToContinue: "Select a payment method to continue",
113
+ checkoutStepAddress: "Address",
114
+ checkoutStepDelivery: "Delivery",
115
+ checkoutStepPayment: "Payment",
116
+ paymentMethod: "Payment method",
117
+ selectPaymentMethod: "Select a payment method",
118
+ paymentAuthorized: "Payment authorized",
119
+ paymentDeclined: "Payment was declined",
120
+ continueToPayment: "Continue to payment",
121
+ placeOrder: "Place order",
122
+ placingOrder: "Placing order\u2026",
123
+ orderPlaced: "Order placed",
124
+ viewOrderStatus: "View order status",
125
+ continueShopping: "Continue shopping",
126
+ checkoutFailed: "Checkout failed",
127
+ orderStatus: "Order status",
128
+ orderPlacedAt: "Placed",
129
+ paymentStateLabel: "Payment",
130
+ shipmentStateLabel: "Shipment",
131
+ trackingNumber: "Tracking number",
132
+ carrier: "Carrier",
133
+ noTrackingYet: "Tracking details will appear when the order ships",
134
+ orderNotFound: "Order not found",
135
+ noOrdersYet: "No orders yet",
136
+ yourOrders: "Your orders",
137
+ firstName: "First name",
138
+ lastName: "Last name",
139
+ streetName: "Street address",
140
+ additionalAddress: "Apartment, suite, etc. (optional)",
141
+ postalCode: "Postal code",
142
+ city: "City",
143
+ region: "State or region (optional)",
144
+ country: "Country"
145
+ };
146
+ function resolveCommerceAISearchMessages(overrides) {
147
+ return {
148
+ ...DEFAULT_COMMERCE_AI_SEARCH_MESSAGES,
149
+ ...overrides
150
+ };
151
+ }
152
+ __name(resolveCommerceAISearchMessages, "resolveCommerceAISearchMessages");
153
+ var COMPOUND_SHOPPING_LIST_PATTERN = /\band\b|,|\bplus\b|\boraz\b/i;
154
+ function looksLikeCompoundShoppingList(query) {
155
+ return COMPOUND_SHOPPING_LIST_PATTERN.test(query.trim());
156
+ }
157
+ __name(looksLikeCompoundShoppingList, "looksLikeCompoundShoppingList");
158
+ var CSS_NAMED_COLOR_TABLE = `
159
+ Alice Blue:f0f8ff
160
+ Antique White:faebd7
161
+ Aqua:00ffff
162
+ Aquamarine:7fffd4
163
+ Azure:f0ffff
164
+ Beige:f5f5dc
165
+ Bisque:ffe4c4
166
+ Black:000000
167
+ Blanched Almond:ffebcd
168
+ Blue:0000ff
169
+ Blue Violet:8a2be2
170
+ Brown:a52a2a
171
+ Burlywood:deb887
172
+ Cadet Blue:5f9ea0
173
+ Chartreuse:7fff00
174
+ Chocolate:d2691e
175
+ Coral:ff7f50
176
+ Cornflower Blue:6495ed
177
+ Cornsilk:fff8dc
178
+ Crimson:dc143c
179
+ Dark Blue:00008b
180
+ Dark Cyan:008b8b
181
+ Dark Goldenrod:b8860b
182
+ Dark Gray:a9a9a9
183
+ Dark Green:006400
184
+ Dark Khaki:bdb76b
185
+ Dark Magenta:8b008b
186
+ Dark Olive Green:556b2f
187
+ Dark Orange:ff8c00
188
+ Dark Orchid:9932cc
189
+ Dark Red:8b0000
190
+ Dark Salmon:e9967a
191
+ Dark Sea Green:8fbc8f
192
+ Dark Slate Blue:483d8b
193
+ Dark Slate Gray:2f4f4f
194
+ Dark Turquoise:00ced1
195
+ Dark Violet:9400d3
196
+ Deep Pink:ff1493
197
+ Deep Sky Blue:00bfff
198
+ Dim Gray:696969
199
+ Dodger Blue:1e90ff
200
+ Firebrick:b22222
201
+ Floral White:fffaf0
202
+ Forest Green:228b22
203
+ Gainsboro:dcdcdc
204
+ Ghost White:f8f8ff
205
+ Gold:ffd700
206
+ Goldenrod:daa520
207
+ Gray:808080
208
+ Green:008000
209
+ Green Yellow:adff2f
210
+ Honeydew:f0fff0
211
+ Hot Pink:ff69b4
212
+ Indian Red:cd5c5c
213
+ Indigo:4b0082
214
+ Ivory:fffff0
215
+ Khaki:f0e68c
216
+ Lavender:e6e6fa
217
+ Lavender Blush:fff0f5
218
+ Lawn Green:7cfc00
219
+ Lemon Chiffon:fffacd
220
+ Light Blue:add8e6
221
+ Light Coral:f08080
222
+ Light Cyan:e0ffff
223
+ Light Goldenrod Yellow:fafad2
224
+ Light Gray:d3d3d3
225
+ Light Green:90ee90
226
+ Light Pink:ffb6c1
227
+ Light Salmon:ffa07a
228
+ Light Sea Green:20b2aa
229
+ Light Sky Blue:87cefa
230
+ Light Slate Gray:778899
231
+ Light Steel Blue:b0c4de
232
+ Light Yellow:ffffe0
233
+ Lime:00ff00
234
+ Lime Green:32cd32
235
+ Linen:faf0e6
236
+ Magenta:ff00ff
237
+ Maroon:800000
238
+ Medium Aquamarine:66cdaa
239
+ Medium Blue:0000cd
240
+ Medium Orchid:ba55d3
241
+ Medium Purple:9370db
242
+ Medium Sea Green:3cb371
243
+ Medium Slate Blue:7b68ee
244
+ Medium Spring Green:00fa9a
245
+ Medium Turquoise:48d1cc
246
+ Medium Violet Red:c71585
247
+ Midnight Blue:191970
248
+ Mint Cream:f5fffa
249
+ Misty Rose:ffe4e1
250
+ Moccasin:ffe4b5
251
+ Navajo White:ffdead
252
+ Navy:000080
253
+ Old Lace:fdf5e6
254
+ Olive:808000
255
+ Olive Drab:6b8e23
256
+ Orange:ffa500
257
+ Orange Red:ff4500
258
+ Orchid:da70d6
259
+ Pale Goldenrod:eee8aa
260
+ Pale Green:98fb98
261
+ Pale Turquoise:afeeee
262
+ Pale Violet Red:db7093
263
+ Papaya Whip:ffefd5
264
+ Peach Puff:ffdab9
265
+ Peru:cd853f
266
+ Pink:ffc0cb
267
+ Plum:dda0dd
268
+ Powder Blue:b0e0e6
269
+ Purple:800080
270
+ Rebecca Purple:663399
271
+ Red:ff0000
272
+ Rosy Brown:bc8f8f
273
+ Royal Blue:4169e1
274
+ Saddle Brown:8b4513
275
+ Salmon:fa8072
276
+ Sandy Brown:f4a460
277
+ Sea Green:2e8b57
278
+ Seashell:fff5ee
279
+ Sienna:a0522d
280
+ Silver:c0c0c0
281
+ Sky Blue:87ceeb
282
+ Slate Blue:6a5acd
283
+ Slate Gray:708090
284
+ Snow:fffafa
285
+ Spring Green:00ff7f
286
+ Steel Blue:4682b4
287
+ Tan:d2b48c
288
+ Teal:008080
289
+ Thistle:d8bfd8
290
+ Tomato:ff6347
291
+ Turquoise:40e0d0
292
+ Violet:ee82ee
293
+ Wheat:f5deb3
294
+ White:ffffff
295
+ White Smoke:f5f5f5
296
+ Yellow:ffff00
297
+ Yellow Green:9acd32
298
+ `;
299
+ var HEX_COLOR_RE = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
300
+ var NAMED_COLORS = CSS_NAMED_COLOR_TABLE.trim().split("\n").flatMap((line) => {
301
+ const separator = line.indexOf(":");
302
+ if (separator <= 0) {
303
+ return [];
304
+ }
305
+ const name = line.slice(0, separator);
306
+ const hex = line.slice(separator + 1);
307
+ const n = Number.parseInt(hex, 16);
308
+ if (!name || !Number.isFinite(n)) {
309
+ return [];
310
+ }
311
+ return [
312
+ {
313
+ name,
314
+ r: n >> 16 & 255,
315
+ g: n >> 8 & 255,
316
+ b: n & 255
317
+ }
318
+ ];
319
+ });
320
+ function parseHexRgb(value) {
321
+ const match = HEX_COLOR_RE.exec(value.trim());
322
+ const captured = match?.[1];
323
+ if (!captured) {
324
+ return null;
325
+ }
326
+ const hex = captured.length === 3 ? `${captured[0]}${captured[0]}${captured[1]}${captured[1]}${captured[2]}${captured[2]}` : captured;
327
+ const n = Number.parseInt(hex, 16);
328
+ return [
329
+ n >> 16 & 255,
330
+ n >> 8 & 255,
331
+ n & 255
332
+ ];
333
+ }
334
+ __name(parseHexRgb, "parseHexRgb");
335
+ var MAX_NAMED_COLOR_DISTANCE_SQ = 40 * 40;
336
+ var COLOR_LIKE_FACET_NAME = /(?:^|[-_])colou?r(?:[-_]|$)|(?:^|[-_])finish(?:[-_]|$)/i;
337
+ function isColorLikeFacetName(name) {
338
+ return COLOR_LIKE_FACET_NAME.test(name.trim());
339
+ }
340
+ __name(isColorLikeFacetName, "isColorLikeFacetName");
341
+ function hexColorSwatchValue(value) {
342
+ const rgb = parseHexRgb(value);
343
+ if (!rgb) {
344
+ return void 0;
345
+ }
346
+ return `#${rgb.map((channel) => channel.toString(16).padStart(2, "0")).join("")}`;
347
+ }
348
+ __name(hexColorSwatchValue, "hexColorSwatchValue");
349
+ var PRICE_FACET_ID = "price";
350
+ var CATEGORIES_FACET_ID = "categories";
351
+ var DEFAULT_PRICE_RANGES = [
352
+ {
353
+ key: "under-50",
354
+ to: 5e3
355
+ },
356
+ {
357
+ key: "50-to-100",
358
+ from: 5e3,
359
+ to: 1e4
360
+ },
361
+ {
362
+ key: "100-plus",
363
+ from: 1e4
364
+ }
365
+ ];
366
+ function priceRangeToFilterValues(range) {
367
+ const filters = {};
368
+ if ("from" in range && range.from !== void 0) {
369
+ filters.priceMin = String(range.from / 100);
370
+ }
371
+ if ("to" in range && range.to !== void 0) {
372
+ filters.priceMax = String(range.to / 100);
373
+ }
374
+ return filters;
375
+ }
376
+ __name(priceRangeToFilterValues, "priceRangeToFilterValues");
377
+ function priceBucketKeyFromFilters(filters) {
378
+ const minCents = parseMajorToCents(filters.priceMin);
379
+ const maxCents = parseMajorToCents(filters.priceMax);
380
+ for (const range of DEFAULT_PRICE_RANGES) {
381
+ const from = "from" in range ? range.from : void 0;
382
+ const to = "to" in range ? range.to : void 0;
383
+ if (minCents === from && maxCents === to) {
384
+ return range.key;
385
+ }
386
+ }
387
+ return void 0;
388
+ }
389
+ __name(priceBucketKeyFromFilters, "priceBucketKeyFromFilters");
390
+ function isFacetFilterSelected(filters, facetId, bucketKey) {
391
+ if (facetId === CATEGORIES_FACET_ID) {
392
+ return filters.category === bucketKey;
393
+ }
394
+ if (facetId === PRICE_FACET_ID) {
395
+ return priceBucketKeyFromFilters(filters) === bucketKey;
396
+ }
397
+ return filters[facetId] === bucketKey;
398
+ }
399
+ __name(isFacetFilterSelected, "isFacetFilterSelected");
400
+ function toggleFacetFilter(filters, facetId, bucketKey) {
401
+ const next = {
402
+ ...filters
403
+ };
404
+ if (facetId === PRICE_FACET_ID) {
405
+ if (priceBucketKeyFromFilters(next) === bucketKey) {
406
+ delete next.priceMin;
407
+ delete next.priceMax;
408
+ return next;
409
+ }
410
+ delete next.priceMin;
411
+ delete next.priceMax;
412
+ const range = DEFAULT_PRICE_RANGES.find((candidate) => candidate.key === bucketKey);
413
+ if (!range) {
414
+ return next;
415
+ }
416
+ return {
417
+ ...next,
418
+ ...priceRangeToFilterValues(range)
419
+ };
420
+ }
421
+ const filterKey = facetId === CATEGORIES_FACET_ID ? "category" : facetId;
422
+ if (next[filterKey] === bucketKey) {
423
+ delete next[filterKey];
424
+ } else {
425
+ next[filterKey] = bucketKey;
426
+ }
427
+ return next;
428
+ }
429
+ __name(toggleFacetFilter, "toggleFacetFilter");
430
+ function parseMajorToCents(value) {
431
+ if (!value) {
432
+ return void 0;
433
+ }
434
+ const parsed = Number(value);
435
+ if (!Number.isFinite(parsed) || parsed < 0) {
436
+ return void 0;
437
+ }
438
+ return Math.round(parsed * 100);
439
+ }
440
+ __name(parseMajorToCents, "parseMajorToCents");
441
+
442
+ // src/lib/commerce-ai-api.service.ts
443
+ var import_core = require("@angular/core");
444
+ function _ts_decorate(decorators, target, key, desc) {
445
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
446
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
447
+ r = Reflect.decorate(decorators, target, key, desc);
448
+ } else {
449
+ for (var i = decorators.length - 1; i >= 0; i--) {
450
+ if (d = decorators[i]) {
451
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
452
+ }
453
+ }
454
+ }
455
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
456
+ }
457
+ __name(_ts_decorate, "_ts_decorate");
458
+ function buildLocalePayload(options) {
459
+ const payload = {};
460
+ const queryLocale = options.queryLocale ?? options.locale;
461
+ if (options.catalogLocale) {
462
+ payload.catalogLocale = options.catalogLocale;
463
+ }
464
+ if (queryLocale) {
465
+ payload.queryLocale = queryLocale;
466
+ }
467
+ return payload;
468
+ }
469
+ __name(buildLocalePayload, "buildLocalePayload");
470
+ var CommerceAiApiService = class {
471
+ static {
472
+ __name(this, "CommerceAiApiService");
473
+ }
474
+ search(apiBaseUrl, query, locales = {}, signal2, options = {}) {
475
+ const baseUrl = apiBaseUrl.replace(/\/$/, "");
476
+ return fetch(`${baseUrl}/search`, {
477
+ method: "POST",
478
+ headers: {
479
+ "Content-Type": "application/json"
480
+ },
481
+ body: JSON.stringify({
482
+ query,
483
+ ...options,
484
+ ...buildLocalePayload(locales)
485
+ }),
486
+ signal: signal2
487
+ }).then(async (response) => {
488
+ if (!response.ok) {
489
+ const data = await response.json();
490
+ throw new Error(data.error ?? "Search failed");
491
+ }
492
+ return response.json();
493
+ });
494
+ }
495
+ suggest(apiBaseUrl, query, locales = {}, signal2) {
496
+ const baseUrl = apiBaseUrl.replace(/\/$/, "");
497
+ return fetch(`${baseUrl}/search/suggestions`, {
498
+ method: "POST",
499
+ headers: {
500
+ "Content-Type": "application/json"
501
+ },
502
+ body: JSON.stringify({
503
+ query,
504
+ ...buildLocalePayload(locales)
505
+ }),
506
+ signal: signal2
507
+ }).then(async (response) => {
508
+ if (!response.ok) {
509
+ const data = await response.json();
510
+ throw new Error(data.error ?? "Suggestions failed");
511
+ }
512
+ return response.json();
513
+ });
514
+ }
515
+ searchByImage(apiBaseUrl, file, locales = {}, enableMissions = false) {
516
+ const baseUrl = apiBaseUrl.replace(/\/$/, "");
517
+ const formData = new FormData();
518
+ formData.append("image", file);
519
+ for (const [key, value] of Object.entries(buildLocalePayload(locales))) {
520
+ formData.append(key, value);
521
+ }
522
+ if (enableMissions) {
523
+ formData.append("enableMissions", "true");
524
+ }
525
+ return fetch(`${baseUrl}/search/image`, {
526
+ method: "POST",
527
+ body: formData
528
+ }).then(async (response) => {
529
+ if (!response.ok) {
530
+ const data = await response.json();
531
+ throw new Error(data.error ?? "Image search failed");
532
+ }
533
+ return response.json();
534
+ });
535
+ }
536
+ searchByVoice(apiBaseUrl, audio, locales = {}, enableTts = true, enableMissions = false) {
537
+ const baseUrl = apiBaseUrl.replace(/\/$/, "");
538
+ const formData = new FormData();
539
+ formData.append("audio", audio, "recording.webm");
540
+ for (const [key, value] of Object.entries(buildLocalePayload(locales))) {
541
+ formData.append(key, value);
542
+ }
543
+ formData.append("enableTts", String(enableTts));
544
+ if (enableMissions) {
545
+ formData.append("enableMissions", "true");
546
+ }
547
+ return fetch(`${baseUrl}/search/voice`, {
548
+ method: "POST",
549
+ body: formData
550
+ }).then(async (response) => {
551
+ if (!response.ok) {
552
+ const data = await response.json();
553
+ throw new Error(data.error ?? "Voice search failed");
554
+ }
555
+ return response.json();
556
+ });
557
+ }
558
+ synthesizeSpeech(apiBaseUrl, text) {
559
+ const baseUrl = apiBaseUrl.replace(/\/$/, "");
560
+ return fetch(`${baseUrl}/tts`, {
561
+ method: "POST",
562
+ headers: {
563
+ "Content-Type": "application/json"
564
+ },
565
+ body: JSON.stringify({
566
+ text
567
+ })
568
+ }).then(async (response) => {
569
+ if (!response.ok) {
570
+ throw new Error("TTS failed");
571
+ }
572
+ return response.blob();
573
+ });
574
+ }
575
+ };
576
+ CommerceAiApiService = _ts_decorate([
577
+ (0, import_core.Injectable)({
578
+ providedIn: "root"
579
+ })
580
+ ], CommerceAiApiService);
581
+
582
+ // src/lib/commerce-ai-cart-panel.component.ts
583
+ var import_core2 = require("@angular/core");
584
+ var import_forms = require("@angular/forms");
585
+ function _ts_decorate2(decorators, target, key, desc) {
586
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
587
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
588
+ r = Reflect.decorate(decorators, target, key, desc);
589
+ } else {
590
+ for (var i = decorators.length - 1; i >= 0; i--) {
591
+ if (d = decorators[i]) {
592
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
593
+ }
594
+ }
595
+ }
596
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
597
+ }
598
+ __name(_ts_decorate2, "_ts_decorate");
599
+ function _ts_metadata(metadataKey, metadataValue) {
600
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
601
+ return Reflect.metadata(metadataKey, metadataValue);
602
+ }
603
+ }
604
+ __name(_ts_metadata, "_ts_metadata");
605
+ function displayCartError(error, messages) {
606
+ if (!error) {
607
+ return null;
608
+ }
609
+ if (error === "Invalid credentials") {
610
+ return messages.invalidCredentials;
611
+ }
612
+ if (error === "Sign in failed" || error === "Login failed") {
613
+ return messages.signInFailed;
614
+ }
615
+ return error;
616
+ }
617
+ __name(displayCartError, "displayCartError");
618
+ function formatLineTotal(price, quantity, locale) {
619
+ if (!price) {
620
+ return null;
621
+ }
622
+ return new Intl.NumberFormat(locale || void 0, {
623
+ style: "currency",
624
+ currency: price.currency
625
+ }).format(price.amount * quantity);
626
+ }
627
+ __name(formatLineTotal, "formatLineTotal");
628
+ var CommerceAiCartPanelComponent = class {
629
+ static {
630
+ __name(this, "CommerceAiCartPanelComponent");
631
+ }
632
+ cart = null;
633
+ customer = null;
634
+ isLoading = false;
635
+ isLoggingIn = false;
636
+ error = null;
637
+ messages;
638
+ catalogLocale;
639
+ showCheckout = false;
640
+ close = new import_core2.EventEmitter();
641
+ remove = new import_core2.EventEmitter();
642
+ quantityChange = new import_core2.EventEmitter();
643
+ login = new import_core2.EventEmitter();
644
+ logout = new import_core2.EventEmitter();
645
+ checkout = new import_core2.EventEmitter();
646
+ email = "";
647
+ password = "";
648
+ isAuthExpanded = false;
649
+ get isEmpty() {
650
+ return !this.cart || this.cart.lineItems.length === 0;
651
+ }
652
+ get visibleError() {
653
+ return displayCartError(this.error, this.messages);
654
+ }
655
+ lineTotal(item) {
656
+ return formatLineTotal(item.price, item.quantity, this.catalogLocale);
657
+ }
658
+ onLoginSubmit() {
659
+ this.login.emit({
660
+ email: this.email,
661
+ password: this.password
662
+ });
663
+ this.password = "";
664
+ }
665
+ };
666
+ _ts_decorate2([
667
+ (0, import_core2.Input)(),
668
+ _ts_metadata("design:type", Object)
669
+ ], CommerceAiCartPanelComponent.prototype, "cart", void 0);
670
+ _ts_decorate2([
671
+ (0, import_core2.Input)(),
672
+ _ts_metadata("design:type", Object)
673
+ ], CommerceAiCartPanelComponent.prototype, "customer", void 0);
674
+ _ts_decorate2([
675
+ (0, import_core2.Input)()
676
+ ], CommerceAiCartPanelComponent.prototype, "isLoading", void 0);
677
+ _ts_decorate2([
678
+ (0, import_core2.Input)()
679
+ ], CommerceAiCartPanelComponent.prototype, "isLoggingIn", void 0);
680
+ _ts_decorate2([
681
+ (0, import_core2.Input)(),
682
+ _ts_metadata("design:type", Object)
683
+ ], CommerceAiCartPanelComponent.prototype, "error", void 0);
684
+ _ts_decorate2([
685
+ (0, import_core2.Input)({
686
+ required: true
687
+ }),
688
+ _ts_metadata("design:type", typeof CommerceAISearchMessages === "undefined" ? Object : CommerceAISearchMessages)
689
+ ], CommerceAiCartPanelComponent.prototype, "messages", void 0);
690
+ _ts_decorate2([
691
+ (0, import_core2.Input)(),
692
+ _ts_metadata("design:type", String)
693
+ ], CommerceAiCartPanelComponent.prototype, "catalogLocale", void 0);
694
+ _ts_decorate2([
695
+ (0, import_core2.Input)()
696
+ ], CommerceAiCartPanelComponent.prototype, "showCheckout", void 0);
697
+ _ts_decorate2([
698
+ (0, import_core2.Output)()
699
+ ], CommerceAiCartPanelComponent.prototype, "close", void 0);
700
+ _ts_decorate2([
701
+ (0, import_core2.Output)()
702
+ ], CommerceAiCartPanelComponent.prototype, "remove", void 0);
703
+ _ts_decorate2([
704
+ (0, import_core2.Output)()
705
+ ], CommerceAiCartPanelComponent.prototype, "quantityChange", void 0);
706
+ _ts_decorate2([
707
+ (0, import_core2.Output)()
708
+ ], CommerceAiCartPanelComponent.prototype, "login", void 0);
709
+ _ts_decorate2([
710
+ (0, import_core2.Output)()
711
+ ], CommerceAiCartPanelComponent.prototype, "logout", void 0);
712
+ _ts_decorate2([
713
+ (0, import_core2.Output)()
714
+ ], CommerceAiCartPanelComponent.prototype, "checkout", void 0);
715
+ CommerceAiCartPanelComponent = _ts_decorate2([
716
+ (0, import_core2.Component)({
717
+ selector: "commerce-ai-cart-panel",
718
+ standalone: true,
719
+ imports: [
720
+ import_forms.FormsModule
721
+ ],
722
+ template: `
723
+ <section
724
+ class="cat-cart-panel"
725
+ [class.cat-cart-panel--with-items]="!isEmpty"
726
+ [attr.aria-label]="messages.cartAriaLabel"
727
+ >
728
+ <header class="cat-cart-panel__header">
729
+ <h2 class="cat-cart-panel__title">{{ messages.cart }}</h2>
730
+ <button type="button" class="cat-icon-btn" (click)="close.emit()" [attr.aria-label]="messages.closeCart">
731
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
732
+ <path d="M18 6 6 18" />
733
+ <path d="m6 6 12 12" />
734
+ </svg>
735
+ </button>
736
+ </header>
737
+
738
+ @if (visibleError) {
739
+ <div class="cat-status cat-status--error" role="alert">{{ visibleError }}</div>
740
+ }
741
+
742
+ @if (isEmpty) {
743
+ <div class="cat-cart-panel__empty">
744
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
745
+ <path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z" />
746
+ <path d="M3 6h18" />
747
+ <path d="M16 10a4 4 0 0 1-8 0" />
748
+ </svg>
749
+ <span>{{ messages.emptyCart }}</span>
750
+ </div>
751
+ } @else {
752
+ <ul class="cat-cart-panel__items">
753
+ @for (item of cart!.lineItems; track item.id) {
754
+ <li class="cat-cart-item">
755
+ @if (item.imageUrl) {
756
+ <img [src]="item.imageUrl" alt="" class="cat-cart-item__image" />
757
+ } @else {
758
+ <div class="cat-cart-item__image cat-cart-item__image--placeholder">
759
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
760
+ <path d="M16.5 9.4 7.55 4.24" />
761
+ <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
762
+ <polyline points="3.29 7 12 12 20.71 7" />
763
+ <line x1="12" x2="12" y1="22" y2="12" />
764
+ </svg>
765
+ </div>
766
+ }
767
+ <div class="cat-cart-item__info">
768
+ <div class="cat-cart-item__name">{{ item.name }}</div>
769
+ @if (item.price) {
770
+ <div class="cat-cart-item__price">
771
+ <span>{{ item.price.formatted }} {{ messages.each }}</span>
772
+ <strong>{{ item.totalPrice?.formatted ?? lineTotal(item) }}</strong>
773
+ </div>
774
+ }
775
+ <div class="cat-cart-item__qty">
776
+ <button
777
+ type="button"
778
+ class="cat-cart-item__qty-btn"
779
+ [attr.aria-label]="messages.decreaseQuantity"
780
+ [disabled]="isLoading || item.quantity <= 1"
781
+ (click)="quantityChange.emit({ lineItemId: item.id, quantity: item.quantity - 1 })"
782
+ >
783
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
784
+ <path d="M5 12h14" />
785
+ </svg>
786
+ </button>
787
+ <span class="cat-cart-item__qty-value">{{ item.quantity }}</span>
788
+ <button
789
+ type="button"
790
+ class="cat-cart-item__qty-btn"
791
+ [attr.aria-label]="messages.increaseQuantity"
792
+ [disabled]="isLoading"
793
+ (click)="quantityChange.emit({ lineItemId: item.id, quantity: item.quantity + 1 })"
794
+ >
795
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
796
+ <path d="M5 12h14" />
797
+ <path d="M12 5v14" />
798
+ </svg>
799
+ </button>
800
+ </div>
801
+ </div>
802
+ <button
803
+ type="button"
804
+ class="cat-icon-btn"
805
+ [attr.aria-label]="messages.removeItem"
806
+ [disabled]="isLoading"
807
+ (click)="remove.emit(item.id)"
808
+ >
809
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
810
+ <path d="M3 6h18" />
811
+ <path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
812
+ <path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
813
+ </svg>
814
+ </button>
815
+ </li>
816
+ }
817
+ </ul>
818
+ }
819
+
820
+ <div class="cat-cart-panel__auth">
821
+ @if (customer) {
822
+ <div class="cat-cart-panel__signed-in">
823
+ <span>{{ messages.signedInAs }} {{ customer.email }}</span>
824
+ <button type="button" class="cat-cart-panel__sign-out" (click)="logout.emit()">
825
+ {{ messages.signOut }}
826
+ </button>
827
+ </div>
828
+ } @else {
829
+ <button
830
+ type="button"
831
+ class="cat-cart-panel__auth-toggle"
832
+ [disabled]="isLoggingIn"
833
+ [attr.aria-expanded]="isAuthExpanded"
834
+ (click)="isAuthExpanded = !isAuthExpanded"
835
+ >
836
+ <span class="cat-cart-panel__auth-toggle-copy">
837
+ <span class="cat-cart-panel__auth-icon" aria-hidden="true">
838
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
839
+ <circle cx="12" cy="8" r="5" />
840
+ <path d="M20 21a8 8 0 0 0-16 0" />
841
+ </svg>
842
+ </span>
843
+ {{ messages.signInToSyncCart }}
844
+ </span>
845
+ <svg
846
+ width="15"
847
+ height="15"
848
+ viewBox="0 0 24 24"
849
+ fill="none"
850
+ stroke="currentColor"
851
+ stroke-width="2"
852
+ aria-hidden="true"
853
+ [class.cat-cart-panel__auth-chevron--open]="isAuthExpanded"
854
+ >
855
+ <path d="m6 9 6 6 6-6" />
856
+ </svg>
857
+ </button>
858
+ @if (isAuthExpanded) {
859
+ <form class="cat-cart-panel__auth-form" (ngSubmit)="onLoginSubmit()">
860
+ <label class="cat-cart-panel__field">
861
+ <span class="cat-cart-panel__label">{{ messages.email }}</span>
862
+ <input
863
+ class="cat-cart-panel__input"
864
+ type="email"
865
+ name="email"
866
+ autocomplete="email"
867
+ required
868
+ [disabled]="isLoggingIn"
869
+ [(ngModel)]="email"
870
+ />
871
+ </label>
872
+ <label class="cat-cart-panel__field">
873
+ <span class="cat-cart-panel__label">{{ messages.password }}</span>
874
+ <input
875
+ class="cat-cart-panel__input"
876
+ type="password"
877
+ name="password"
878
+ autocomplete="current-password"
879
+ required
880
+ [disabled]="isLoggingIn"
881
+ [(ngModel)]="password"
882
+ />
883
+ </label>
884
+ <button type="submit" class="cat-cart-panel__submit" [disabled]="isLoggingIn">
885
+ {{ messages.signIn }}
886
+ </button>
887
+ </form>
888
+ }
889
+ }
890
+ </div>
891
+
892
+ @if (!isEmpty) {
893
+ <footer class="cat-cart-panel__footer">
894
+ <div class="cat-cart-panel__total">
895
+ <span>{{ messages.total }}</span>
896
+ <strong>{{ cart!.totalPrice.formatted }}</strong>
897
+ </div>
898
+ @if (showCheckout && cart) {
899
+ <button
900
+ type="button"
901
+ class="cat-checkout-cta"
902
+ [disabled]="isLoading"
903
+ (click)="checkout.emit(cart)"
904
+ >
905
+ <span>{{ messages.checkout }}</span>
906
+ <span class="cat-checkout-cta__icon" aria-hidden="true">
907
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
908
+ <path d="M5 12h14" />
909
+ <path d="m12 5 7 7-7 7" />
910
+ </svg>
911
+ </span>
912
+ </button>
913
+ }
914
+ </footer>
915
+ }
916
+ </section>
917
+ `
918
+ })
919
+ ], CommerceAiCartPanelComponent);
920
+
921
+ // src/lib/commerce-ai-cart.service.ts
922
+ var import_core3 = require("@angular/core");
923
+ function _ts_decorate3(decorators, target, key, desc) {
924
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
925
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
926
+ r = Reflect.decorate(decorators, target, key, desc);
927
+ } else {
928
+ for (var i = decorators.length - 1; i >= 0; i--) {
929
+ if (d = decorators[i]) {
930
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
931
+ }
932
+ }
933
+ }
934
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
935
+ }
936
+ __name(_ts_decorate3, "_ts_decorate");
937
+ var ANONYMOUS_ID_STORAGE_KEY = "commerce-ai-tool:anonymousId";
938
+ var CUSTOMER_SESSION_STORAGE_KEY = "commerce-ai-tool:customerSession";
939
+ var CUSTOMER_STORAGE_KEY = "commerce-ai-tool:customer";
940
+ function readStorage(key) {
941
+ if (typeof window === "undefined") {
942
+ return null;
943
+ }
944
+ try {
945
+ return window.localStorage.getItem(key);
946
+ } catch {
947
+ return null;
948
+ }
949
+ }
950
+ __name(readStorage, "readStorage");
951
+ function writeStorage(key, value) {
952
+ if (typeof window === "undefined") {
953
+ return;
954
+ }
955
+ try {
956
+ window.localStorage.setItem(key, value);
957
+ } catch {
958
+ }
959
+ }
960
+ __name(writeStorage, "writeStorage");
961
+ function removeStorage(key) {
962
+ if (typeof window === "undefined") {
963
+ return;
964
+ }
965
+ try {
966
+ window.localStorage.removeItem(key);
967
+ } catch {
968
+ }
969
+ }
970
+ __name(removeStorage, "removeStorage");
971
+ function readStoredCustomer() {
972
+ const raw = readStorage(CUSTOMER_STORAGE_KEY);
973
+ if (!raw) {
974
+ return null;
975
+ }
976
+ try {
977
+ const parsed = JSON.parse(raw);
978
+ if (typeof parsed.id === "string" && typeof parsed.email === "string") {
979
+ return parsed;
980
+ }
981
+ } catch {
982
+ return null;
983
+ }
984
+ return null;
985
+ }
986
+ __name(readStoredCustomer, "readStoredCustomer");
987
+ function createAnonymousId() {
988
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
989
+ return crypto.randomUUID();
990
+ }
991
+ return `anon-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
992
+ }
993
+ __name(createAnonymousId, "createAnonymousId");
994
+ function getOrCreateAnonymousId() {
995
+ const existing = readStorage(ANONYMOUS_ID_STORAGE_KEY);
996
+ if (existing) {
997
+ return existing;
998
+ }
999
+ const created = createAnonymousId();
1000
+ writeStorage(ANONYMOUS_ID_STORAGE_KEY, created);
1001
+ return created;
1002
+ }
1003
+ __name(getOrCreateAnonymousId, "getOrCreateAnonymousId");
1004
+ function rotateAnonymousId() {
1005
+ const created = createAnonymousId();
1006
+ writeStorage(ANONYMOUS_ID_STORAGE_KEY, created);
1007
+ return created;
1008
+ }
1009
+ __name(rotateAnonymousId, "rotateAnonymousId");
1010
+ var CartRequestError = class CartRequestError2 extends Error {
1011
+ static {
1012
+ __name(this, "CartRequestError");
1013
+ }
1014
+ status;
1015
+ constructor(message, status) {
1016
+ super(message);
1017
+ this.name = "CartRequestError";
1018
+ this.status = status;
1019
+ }
1020
+ };
1021
+ async function parseCartApi(response) {
1022
+ const body = await response.json();
1023
+ if (!response.ok) {
1024
+ throw new CartRequestError(body.error ?? "Cart request failed", response.status);
1025
+ }
1026
+ return body;
1027
+ }
1028
+ __name(parseCartApi, "parseCartApi");
1029
+ var CommerceAiCartService = class {
1030
+ static {
1031
+ __name(this, "CommerceAiCartService");
1032
+ }
1033
+ cart = (0, import_core3.signal)(null);
1034
+ anonymousId = (0, import_core3.signal)("");
1035
+ customer = (0, import_core3.signal)(null);
1036
+ isLoading = (0, import_core3.signal)(false);
1037
+ isMutating = (0, import_core3.signal)(false);
1038
+ isLoggingIn = (0, import_core3.signal)(false);
1039
+ error = (0, import_core3.signal)(null);
1040
+ isCartOpen = (0, import_core3.signal)(false);
1041
+ isAuthenticated = (0, import_core3.computed)(() => Boolean(this.customer() && this.sessionToken()));
1042
+ apiBaseUrl = "";
1043
+ currency;
1044
+ country;
1045
+ catalogLocale;
1046
+ enabled = false;
1047
+ onCartChange;
1048
+ sessionTokenValue = null;
1049
+ cartValue = null;
1050
+ mutationChain = Promise.resolve();
1051
+ started = false;
1052
+ sessionToken() {
1053
+ return this.sessionTokenValue;
1054
+ }
1055
+ configure(options) {
1056
+ this.apiBaseUrl = options.apiBaseUrl.replace(/\/$/, "");
1057
+ this.currency = options.currency;
1058
+ this.country = options.country;
1059
+ this.catalogLocale = options.catalogLocale;
1060
+ this.enabled = options.enabled !== false;
1061
+ this.onCartChange = options.onCartChange;
1062
+ if (!this.enabled) {
1063
+ return;
1064
+ }
1065
+ if (!this.started) {
1066
+ this.started = true;
1067
+ this.anonymousId.set(getOrCreateAnonymousId());
1068
+ const storedToken = readStorage(CUSTOMER_SESSION_STORAGE_KEY);
1069
+ const storedCustomer = readStoredCustomer();
1070
+ if (storedToken && storedCustomer) {
1071
+ this.sessionTokenValue = storedToken;
1072
+ this.customer.set(storedCustomer);
1073
+ }
1074
+ void this.refresh();
1075
+ }
1076
+ }
1077
+ openCart() {
1078
+ this.isCartOpen.set(true);
1079
+ }
1080
+ closeCart() {
1081
+ this.isCartOpen.set(false);
1082
+ }
1083
+ toggleCart() {
1084
+ this.isCartOpen.update((open) => !open);
1085
+ }
1086
+ async refresh() {
1087
+ const anonymousId = this.anonymousId();
1088
+ if (!this.enabled || !anonymousId) {
1089
+ return;
1090
+ }
1091
+ const params = new URLSearchParams();
1092
+ const headers = {};
1093
+ const token = this.sessionTokenValue;
1094
+ if (token) {
1095
+ headers[CART_SESSION_HEADER] = token;
1096
+ } else {
1097
+ params.set("anonymousId", anonymousId);
1098
+ }
1099
+ if (this.catalogLocale) {
1100
+ params.set("catalogLocale", this.catalogLocale);
1101
+ }
1102
+ this.isLoading.set(true);
1103
+ this.error.set(null);
1104
+ try {
1105
+ const query = params.toString();
1106
+ const url = `${this.apiBaseUrl}/cart${query ? `?${query}` : ""}`;
1107
+ const response = await (token ? fetch(url, {
1108
+ headers
1109
+ }) : fetch(url));
1110
+ const body = await parseCartApi(response);
1111
+ this.applyCart(body.cart ?? null);
1112
+ } catch (err) {
1113
+ if (err instanceof CartRequestError && err.status === 401 && this.sessionTokenValue) {
1114
+ this.clearSession();
1115
+ this.anonymousId.set(rotateAnonymousId());
1116
+ this.applyCart(null);
1117
+ this.error.set(err.message);
1118
+ this.isLoading.set(false);
1119
+ await this.refresh();
1120
+ return;
1121
+ }
1122
+ this.error.set(err instanceof Error ? err.message : "Cart request failed");
1123
+ } finally {
1124
+ this.isLoading.set(false);
1125
+ }
1126
+ }
1127
+ addToCart(item) {
1128
+ return this.mutate("/cart/add", {
1129
+ sku: item.sku,
1130
+ productId: item.productId,
1131
+ variantId: item.variantId,
1132
+ quantity: item.quantity
1133
+ });
1134
+ }
1135
+ addItems(items) {
1136
+ return this.mutate("/cart/add-items", {
1137
+ items
1138
+ });
1139
+ }
1140
+ removeFromCart(lineItemId) {
1141
+ return this.mutate("/cart/remove", {
1142
+ lineItemId
1143
+ });
1144
+ }
1145
+ updateQuantity(lineItemId, quantity) {
1146
+ return this.mutate("/cart/update-quantity", {
1147
+ lineItemId,
1148
+ quantity
1149
+ });
1150
+ }
1151
+ async login(input) {
1152
+ if (!this.enabled) {
1153
+ return null;
1154
+ }
1155
+ this.isLoggingIn.set(true);
1156
+ this.error.set(null);
1157
+ try {
1158
+ const response = await fetch(`${this.apiBaseUrl}/cart/login`, {
1159
+ method: "POST",
1160
+ headers: {
1161
+ "Content-Type": "application/json"
1162
+ },
1163
+ body: JSON.stringify({
1164
+ email: input.email,
1165
+ password: input.password,
1166
+ anonymousId: this.anonymousId(),
1167
+ catalogLocale: this.catalogLocale,
1168
+ cartId: this.cartValue?.id
1169
+ })
1170
+ });
1171
+ const body = await parseCartApi(response);
1172
+ if (!body.sessionToken || !body.customer) {
1173
+ throw new CartRequestError("Sign in failed", response.status);
1174
+ }
1175
+ this.persistSession(body.sessionToken, body.customer);
1176
+ this.applyCart(body.cart ?? null);
1177
+ return body.cart ?? null;
1178
+ } catch (err) {
1179
+ this.error.set(err instanceof Error ? err.message : "Sign in failed");
1180
+ return null;
1181
+ } finally {
1182
+ this.isLoggingIn.set(false);
1183
+ }
1184
+ }
1185
+ async logout() {
1186
+ try {
1187
+ await fetch(`${this.apiBaseUrl}/cart/logout`, {
1188
+ method: "POST",
1189
+ headers: {
1190
+ "Content-Type": "application/json"
1191
+ },
1192
+ body: JSON.stringify({})
1193
+ });
1194
+ } catch {
1195
+ }
1196
+ this.clearSession();
1197
+ this.applyCart(null);
1198
+ this.anonymousId.set(rotateAnonymousId());
1199
+ }
1200
+ mutate(path, body) {
1201
+ const run = /* @__PURE__ */ __name(async () => {
1202
+ const anonymousId = this.anonymousId();
1203
+ if (!this.enabled || !anonymousId) {
1204
+ return null;
1205
+ }
1206
+ this.isMutating.set(true);
1207
+ this.error.set(null);
1208
+ try {
1209
+ const response = await fetch(`${this.apiBaseUrl}${path}`, {
1210
+ method: "POST",
1211
+ headers: {
1212
+ "Content-Type": "application/json"
1213
+ },
1214
+ body: JSON.stringify({
1215
+ anonymousId,
1216
+ sessionToken: this.sessionTokenValue ?? void 0,
1217
+ currency: this.currency,
1218
+ country: this.country,
1219
+ catalogLocale: this.catalogLocale,
1220
+ cartId: this.cartValue?.id,
1221
+ cartVersion: this.cartValue?.version,
1222
+ ...body
1223
+ })
1224
+ });
1225
+ const next = await parseCartApi(response);
1226
+ this.applyCart(next.cart ?? null);
1227
+ return next.cart ?? null;
1228
+ } catch (err) {
1229
+ if (err instanceof CartRequestError && err.status === 401 && this.sessionTokenValue) {
1230
+ this.clearSession();
1231
+ this.anonymousId.set(rotateAnonymousId());
1232
+ this.applyCart(null);
1233
+ }
1234
+ this.error.set(err instanceof Error ? err.message : "Cart request failed");
1235
+ return null;
1236
+ } finally {
1237
+ this.isMutating.set(false);
1238
+ }
1239
+ }, "run");
1240
+ const pending = this.mutationChain.then(run, run);
1241
+ this.mutationChain = pending.then(() => void 0, () => void 0);
1242
+ return pending;
1243
+ }
1244
+ applyCart(next) {
1245
+ this.cartValue = next;
1246
+ this.cart.set(next);
1247
+ this.onCartChange?.(next);
1248
+ }
1249
+ persistSession(token, nextCustomer) {
1250
+ this.sessionTokenValue = token;
1251
+ this.customer.set(nextCustomer);
1252
+ writeStorage(CUSTOMER_SESSION_STORAGE_KEY, token);
1253
+ writeStorage(CUSTOMER_STORAGE_KEY, JSON.stringify(nextCustomer));
1254
+ }
1255
+ clearSession() {
1256
+ this.sessionTokenValue = null;
1257
+ this.customer.set(null);
1258
+ removeStorage(CUSTOMER_SESSION_STORAGE_KEY);
1259
+ removeStorage(CUSTOMER_STORAGE_KEY);
1260
+ }
1261
+ };
1262
+ CommerceAiCartService = _ts_decorate3([
1263
+ (0, import_core3.Injectable)()
1264
+ ], CommerceAiCartService);
1265
+
1266
+ // src/lib/commerce-ai-mission-results.component.ts
1267
+ var import_common = require("@angular/common");
1268
+ var import_core4 = require("@angular/core");
1269
+
1270
+ // src/lib/mission.util.ts
1271
+ function formatMissionAddAll(template, count) {
1272
+ const withPlural = count === 1 ? template.replaceAll("top picks", "top pick") : template;
1273
+ if (withPlural.includes("{count}")) {
1274
+ return withPlural.replaceAll("{count}", String(count));
1275
+ }
1276
+ return `${withPlural} (${count})`;
1277
+ }
1278
+ __name(formatMissionAddAll, "formatMissionAddAll");
1279
+ function toCartItem(product, quantity) {
1280
+ return product.sku ? {
1281
+ sku: product.sku,
1282
+ quantity
1283
+ } : {
1284
+ productId: product.id,
1285
+ variantId: product.variantId,
1286
+ quantity
1287
+ };
1288
+ }
1289
+ __name(toCartItem, "toCartItem");
1290
+ function lineItemKey(item) {
1291
+ if (item.sku) {
1292
+ return `sku:${item.sku}`;
1293
+ }
1294
+ return `id:${item.productId ?? ""}:${item.variantId ?? ""}`;
1295
+ }
1296
+ __name(lineItemKey, "lineItemKey");
1297
+ function starterBundleItems(mission) {
1298
+ const items = [];
1299
+ const seen = /* @__PURE__ */ new Set();
1300
+ for (const group of mission.intents) {
1301
+ if (group.failed) {
1302
+ continue;
1303
+ }
1304
+ const product = group.products[0];
1305
+ if (!product || !product.sku && !product.id) {
1306
+ continue;
1307
+ }
1308
+ const item = toCartItem(product, 1);
1309
+ const key = lineItemKey(item);
1310
+ if (seen.has(key)) {
1311
+ continue;
1312
+ }
1313
+ seen.add(key);
1314
+ items.push(item);
1315
+ }
1316
+ return items;
1317
+ }
1318
+ __name(starterBundleItems, "starterBundleItems");
1319
+
1320
+ // src/lib/commerce-ai-mission-results.component.ts
1321
+ function _ts_decorate4(decorators, target, key, desc) {
1322
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1323
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1324
+ r = Reflect.decorate(decorators, target, key, desc);
1325
+ } else {
1326
+ for (var i = decorators.length - 1; i >= 0; i--) {
1327
+ if (d = decorators[i]) {
1328
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1329
+ }
1330
+ }
1331
+ }
1332
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1333
+ }
1334
+ __name(_ts_decorate4, "_ts_decorate");
1335
+ function _ts_metadata2(metadataKey, metadataValue) {
1336
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
1337
+ return Reflect.metadata(metadataKey, metadataValue);
1338
+ }
1339
+ }
1340
+ __name(_ts_metadata2, "_ts_metadata");
1341
+ var missionTitleCounter = 0;
1342
+ var CommerceAiMissionResultsComponent = class {
1343
+ static {
1344
+ __name(this, "CommerceAiMissionResultsComponent");
1345
+ }
1346
+ mission;
1347
+ messages;
1348
+ enableCart = false;
1349
+ isMutating = false;
1350
+ addedProductIds = {};
1351
+ addAllHandler;
1352
+ productSelect = new import_core4.EventEmitter();
1353
+ addItem = new import_core4.EventEmitter();
1354
+ addAll = new import_core4.EventEmitter();
1355
+ titleId = `cat-mission-title-${missionTitleCounter += 1}`;
1356
+ justAddedAll = false;
1357
+ addedAllTimeout = null;
1358
+ lastMission = null;
1359
+ cachedBundleMission = null;
1360
+ cachedBundleItems = [];
1361
+ get bundleItems() {
1362
+ if (this.cachedBundleMission !== this.mission) {
1363
+ this.cachedBundleMission = this.mission;
1364
+ this.cachedBundleItems = starterBundleItems(this.mission);
1365
+ }
1366
+ return this.cachedBundleItems;
1367
+ }
1368
+ get addAllLabel() {
1369
+ const totalQuantity = this.bundleItems.reduce((sum, item) => sum + (item.quantity ?? 1), 0);
1370
+ return formatMissionAddAll(this.messages.missionAddAll, totalQuantity);
1371
+ }
1372
+ ngOnChanges() {
1373
+ if (this.mission !== this.lastMission) {
1374
+ this.lastMission = this.mission;
1375
+ this.justAddedAll = false;
1376
+ }
1377
+ }
1378
+ ngOnDestroy() {
1379
+ if (this.addedAllTimeout) {
1380
+ clearTimeout(this.addedAllTimeout);
1381
+ this.addedAllTimeout = null;
1382
+ }
1383
+ }
1384
+ addLabel(product) {
1385
+ if (this.addedProductIds[product.id]) {
1386
+ return this.messages.itemAdded;
1387
+ }
1388
+ return product.sku || product.id ? this.messages.addToCart : this.messages.unableToAddToCart;
1389
+ }
1390
+ onAddAllClick() {
1391
+ const items = this.bundleItems;
1392
+ this.addAll.emit(items);
1393
+ void this.addAllHandler?.(items).then((result) => {
1394
+ if (!result) {
1395
+ return;
1396
+ }
1397
+ this.justAddedAll = true;
1398
+ if (this.addedAllTimeout) {
1399
+ clearTimeout(this.addedAllTimeout);
1400
+ }
1401
+ this.addedAllTimeout = setTimeout(() => {
1402
+ this.justAddedAll = false;
1403
+ this.addedAllTimeout = null;
1404
+ }, 1200);
1405
+ });
1406
+ }
1407
+ };
1408
+ _ts_decorate4([
1409
+ (0, import_core4.Input)({
1410
+ required: true
1411
+ }),
1412
+ _ts_metadata2("design:type", typeof MissionSearchResult === "undefined" ? Object : MissionSearchResult)
1413
+ ], CommerceAiMissionResultsComponent.prototype, "mission", void 0);
1414
+ _ts_decorate4([
1415
+ (0, import_core4.Input)({
1416
+ required: true
1417
+ }),
1418
+ _ts_metadata2("design:type", typeof CommerceAISearchMessages === "undefined" ? Object : CommerceAISearchMessages)
1419
+ ], CommerceAiMissionResultsComponent.prototype, "messages", void 0);
1420
+ _ts_decorate4([
1421
+ (0, import_core4.Input)()
1422
+ ], CommerceAiMissionResultsComponent.prototype, "enableCart", void 0);
1423
+ _ts_decorate4([
1424
+ (0, import_core4.Input)()
1425
+ ], CommerceAiMissionResultsComponent.prototype, "isMutating", void 0);
1426
+ _ts_decorate4([
1427
+ (0, import_core4.Input)(),
1428
+ _ts_metadata2("design:type", typeof Record === "undefined" ? Object : Record)
1429
+ ], CommerceAiMissionResultsComponent.prototype, "addedProductIds", void 0);
1430
+ _ts_decorate4([
1431
+ (0, import_core4.Input)(),
1432
+ _ts_metadata2("design:type", Function)
1433
+ ], CommerceAiMissionResultsComponent.prototype, "addAllHandler", void 0);
1434
+ _ts_decorate4([
1435
+ (0, import_core4.Output)()
1436
+ ], CommerceAiMissionResultsComponent.prototype, "productSelect", void 0);
1437
+ _ts_decorate4([
1438
+ (0, import_core4.Output)()
1439
+ ], CommerceAiMissionResultsComponent.prototype, "addItem", void 0);
1440
+ _ts_decorate4([
1441
+ (0, import_core4.Output)()
1442
+ ], CommerceAiMissionResultsComponent.prototype, "addAll", void 0);
1443
+ CommerceAiMissionResultsComponent = _ts_decorate4([
1444
+ (0, import_core4.Component)({
1445
+ selector: "commerce-ai-mission-results",
1446
+ standalone: true,
1447
+ imports: [
1448
+ import_common.NgTemplateOutlet
1449
+ ],
1450
+ template: `
1451
+ <div class="cat-mission">
1452
+ <div class="cat-mission__header">
1453
+ <h2 [id]="titleId" class="cat-mission__title">{{ messages.missionTitle }}</h2>
1454
+ @if (mission.interpretation) {
1455
+ <p class="cat-mission__interpretation">{{ mission.interpretation }}</p>
1456
+ }
1457
+ </div>
1458
+
1459
+ <div class="cat-mission__lanes" role="list" [attr.aria-labelledby]="titleId">
1460
+ @for (group of mission.intents; track group.intent.id) {
1461
+ <section class="cat-mission-group" role="listitem">
1462
+ <div class="cat-mission-group__header">
1463
+ <h3 class="cat-mission-group__label">{{ group.intent.label }}</h3>
1464
+ @if (group.intent.quantity > 1) {
1465
+ <span class="cat-mission-group__qty">
1466
+ {{ messages.missionQuantity }} {{ group.intent.quantity }}
1467
+ </span>
1468
+ }
1469
+ </div>
1470
+
1471
+ @if (group.failed) {
1472
+ <div class="cat-status cat-status--empty" role="status">
1473
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
1474
+ <circle cx="11" cy="11" r="8" />
1475
+ <path d="m21 21-4.3-4.3" />
1476
+ <path d="m13 9-4 4" />
1477
+ <path d="m9 9 4 4" />
1478
+ </svg>
1479
+ <div class="cat-status__content">
1480
+ <div class="cat-status__title">{{ messages.missionIntentFailed }}</div>
1481
+ </div>
1482
+ </div>
1483
+ } @else if (group.products.length === 0) {
1484
+ <div class="cat-status cat-status--empty" role="status">
1485
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
1486
+ <circle cx="11" cy="11" r="8" />
1487
+ <path d="m21 21-4.3-4.3" />
1488
+ <path d="m13 9-4 4" />
1489
+ <path d="m9 9 4 4" />
1490
+ </svg>
1491
+ <div class="cat-status__content">
1492
+ <div class="cat-status__title">{{ messages.missionIntentEmpty }}</div>
1493
+ </div>
1494
+ </div>
1495
+ } @else {
1496
+ <div class="cat-mission-group__products">
1497
+ @for (product of group.products; track product.id; let index = $index) {
1498
+ <article
1499
+ class="cat-result-card"
1500
+ [class.cat-result-card--primary]="index === 0"
1501
+ [class.cat-result-card--compact]="index !== 0"
1502
+ >
1503
+ <div class="cat-result-card__core">
1504
+ @if (productSelect.observed) {
1505
+ <button type="button" class="cat-result-card__select" (click)="productSelect.emit(product)">
1506
+ <ng-container [ngTemplateOutlet]="cardBody" [ngTemplateOutletContext]="{ $implicit: product }" />
1507
+ </button>
1508
+ } @else {
1509
+ <div class="cat-result-card__select">
1510
+ <ng-container [ngTemplateOutlet]="cardBody" [ngTemplateOutletContext]="{ $implicit: product }" />
1511
+ </div>
1512
+ }
1513
+ @if (enableCart) {
1514
+ <button
1515
+ type="button"
1516
+ class="cat-icon-btn cat-result-card__add"
1517
+ [class.cat-result-card__add--added]="addedProductIds[product.id]"
1518
+ [attr.aria-label]="addLabel(product)"
1519
+ [disabled]="!(product.sku || product.id) || isMutating"
1520
+ (click)="addItem.emit(product)"
1521
+ >
1522
+ @if (addedProductIds[product.id]) {
1523
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
1524
+ <path d="M20 6 9 17l-5-5" />
1525
+ </svg>
1526
+ } @else {
1527
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
1528
+ <circle cx="8" cy="21" r="1" />
1529
+ <circle cx="19" cy="21" r="1" />
1530
+ <path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12" />
1531
+ </svg>
1532
+ }
1533
+ </button>
1534
+ }
1535
+ </div>
1536
+ </article>
1537
+ }
1538
+ </div>
1539
+ }
1540
+ </section>
1541
+ }
1542
+ </div>
1543
+
1544
+ @if (enableCart) {
1545
+ <div class="cat-mission__footer">
1546
+ <button
1547
+ type="button"
1548
+ class="cat-checkout-cta cat-mission__add-all"
1549
+ [class.cat-mission__add-all--added]="justAddedAll"
1550
+ [disabled]="isMutating || bundleItems.length === 0"
1551
+ (click)="onAddAllClick()"
1552
+ >
1553
+ {{ justAddedAll ? messages.missionItemsAdded : addAllLabel }}
1554
+ </button>
1555
+ </div>
1556
+ }
1557
+ </div>
1558
+
1559
+ <ng-template #cardBody let-product>
1560
+ @if (product.imageUrl) {
1561
+ <img [src]="product.imageUrl" alt="" class="cat-result-image" loading="lazy" />
1562
+ } @else {
1563
+ <div class="cat-result-image cat-result-image--placeholder">
1564
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
1565
+ <path d="M16.5 9.4 7.55 4.24" />
1566
+ <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
1567
+ <polyline points="3.29 7 12 12 20.71 7" />
1568
+ <line x1="12" x2="12" y1="22" y2="12" />
1569
+ </svg>
1570
+ </div>
1571
+ }
1572
+ <div class="cat-result-info">
1573
+ <div class="cat-result-name">{{ product.name }}</div>
1574
+ @if (product.price) {
1575
+ <div class="cat-result-price">{{ product.price.formatted }}</div>
1576
+ }
1577
+ </div>
1578
+ </ng-template>
1579
+ `
1580
+ })
1581
+ ], CommerceAiMissionResultsComponent);
1582
+
1583
+ // src/lib/camera.util.ts
1584
+ function prefersNativeCamera() {
1585
+ if (typeof navigator === "undefined") return false;
1586
+ return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent) || navigator.maxTouchPoints > 1 && matchMedia("(pointer: coarse)").matches;
1587
+ }
1588
+ __name(prefersNativeCamera, "prefersNativeCamera");
1589
+ function stopMediaStream(stream) {
1590
+ stream?.getTracks().forEach((track) => track.stop());
1591
+ }
1592
+ __name(stopMediaStream, "stopMediaStream");
1593
+ function getCameraErrorMessage(error) {
1594
+ if (error instanceof DOMException) {
1595
+ if (error.name === "NotAllowedError" || error.name === "PermissionDeniedError") {
1596
+ return "Camera access denied";
1597
+ }
1598
+ if (error.name === "NotFoundError" || error.name === "DevicesNotFoundError") {
1599
+ return "No camera found";
1600
+ }
1601
+ if (error.name === "NotReadableError" || error.name === "TrackStartError") {
1602
+ return "Camera is already in use";
1603
+ }
1604
+ }
1605
+ return "Camera unavailable";
1606
+ }
1607
+ __name(getCameraErrorMessage, "getCameraErrorMessage");
1608
+ function createJpegFileFromVideo(video, fileName = "camera-capture.jpg") {
1609
+ const width = video.videoWidth;
1610
+ const height = video.videoHeight;
1611
+ if (!width || !height) {
1612
+ return Promise.reject(new Error("Camera preview is not ready"));
1613
+ }
1614
+ const canvas = document.createElement("canvas");
1615
+ canvas.width = width;
1616
+ canvas.height = height;
1617
+ const context = canvas.getContext("2d");
1618
+ if (!context) {
1619
+ return Promise.reject(new Error("Could not capture photo"));
1620
+ }
1621
+ context.drawImage(video, 0, 0, width, height);
1622
+ return new Promise((resolve, reject) => {
1623
+ canvas.toBlob((blob) => {
1624
+ if (!blob) {
1625
+ reject(new Error("Could not capture photo"));
1626
+ return;
1627
+ }
1628
+ resolve(new File([
1629
+ blob
1630
+ ], fileName, {
1631
+ type: "image/jpeg"
1632
+ }));
1633
+ }, "image/jpeg", 0.92);
1634
+ });
1635
+ }
1636
+ __name(createJpegFileFromVideo, "createJpegFileFromVideo");
1637
+ function buildCameraConstraints(facingMode) {
1638
+ return {
1639
+ video: {
1640
+ facingMode: {
1641
+ ideal: facingMode
1642
+ },
1643
+ width: {
1644
+ ideal: 1920
1645
+ },
1646
+ height: {
1647
+ ideal: 1080
1648
+ }
1649
+ },
1650
+ audio: false
1651
+ };
1652
+ }
1653
+ __name(buildCameraConstraints, "buildCameraConstraints");
1654
+
1655
+ // src/lib/commerce-ai-search.component.ts
1656
+ function _ts_decorate5(decorators, target, key, desc) {
1657
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1658
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1659
+ r = Reflect.decorate(decorators, target, key, desc);
1660
+ } else {
1661
+ for (var i = decorators.length - 1; i >= 0; i--) {
1662
+ if (d = decorators[i]) {
1663
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1664
+ }
1665
+ }
1666
+ }
1667
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1668
+ }
1669
+ __name(_ts_decorate5, "_ts_decorate");
1670
+ function _ts_metadata3(metadataKey, metadataValue) {
1671
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
1672
+ return Reflect.metadata(metadataKey, metadataValue);
1673
+ }
1674
+ }
1675
+ __name(_ts_metadata3, "_ts_metadata");
1676
+ var CommerceAiSearchComponent = class {
1677
+ static {
1678
+ __name(this, "CommerceAiSearchComponent");
1679
+ }
1680
+ api = (0, import_core5.inject)(CommerceAiApiService);
1681
+ cdr = (0, import_core5.inject)(import_core5.ChangeDetectorRef);
1682
+ cart = (0, import_core5.inject)(CommerceAiCartService);
1683
+ apiBaseUrl = "/api/commerce-ai";
1684
+ theme = "auto";
1685
+ catalogLocale;
1686
+ queryLocale;
1687
+ /** @deprecated Use queryLocale */
1688
+ locale;
1689
+ placeholder = "What are you looking for?";
1690
+ messages;
1691
+ enableAutocomplete = false;
1692
+ enableFacets = false;
1693
+ /** Show microphone search controls. */
1694
+ enableVoice = true;
1695
+ /** Show local image upload and drag-and-drop search controls. */
1696
+ enableImageSearch = true;
1697
+ /** Show camera capture search controls independently of image upload. */
1698
+ enableCameraSearch = true;
1699
+ cameraFacingMode = "environment";
1700
+ enableTts = true;
1701
+ /** Enable built-in add-to-cart buttons and cart preview panel. Default false. */
1702
+ enableCart = false;
1703
+ /** Enable multi-item shopping missions on text, voice, and image search. Default false. */
1704
+ enableMissions = false;
1705
+ /** Currency for cart creation. Required when `enableCart` is true unless the server has a default. */
1706
+ currency;
1707
+ /** Optional ISO country code used for commercetools price selection. */
1708
+ country;
1709
+ productSelect = new import_core5.EventEmitter();
1710
+ /** Fires after every cart fetch or mutation when `enableCart` is true. */
1711
+ cartChange = new import_core5.EventEmitter();
1712
+ /** Opens the host-owned checkout route for the current non-empty cart. */
1713
+ checkout = new import_core5.EventEmitter();
1714
+ cameraInputRef;
1715
+ cameraVideoRef;
1716
+ query = "";
1717
+ suggestions = [];
1718
+ activeSuggestionIndex = -1;
1719
+ isLoadingSuggestions = false;
1720
+ suggestionsError = null;
1721
+ suggestionsReady = false;
1722
+ suggestionsDismissed = false;
1723
+ results = [];
1724
+ mission = null;
1725
+ hasFacetSession = false;
1726
+ addedProductIds = {};
1727
+ meta = null;
1728
+ facets = [];
1729
+ suggestedFacets = [];
1730
+ isLoading = false;
1731
+ hasSearched = false;
1732
+ error = null;
1733
+ isDragging = false;
1734
+ isRecording = false;
1735
+ isProcessing = false;
1736
+ isCameraOpen = false;
1737
+ cameraError = null;
1738
+ lastSearchMode = null;
1739
+ audioSummary = null;
1740
+ mediaRecorder = null;
1741
+ audioChunks = [];
1742
+ cameraStream = null;
1743
+ debounceTimer = null;
1744
+ suggestionsTimer = null;
1745
+ searchAbort = null;
1746
+ suggestionsAbort = null;
1747
+ searchRequestId = 0;
1748
+ suggestionsRequestId = 0;
1749
+ addedTimeouts = /* @__PURE__ */ new Map();
1750
+ destroyed = false;
1751
+ addMissionItems = /* @__PURE__ */ __name((items) => this.cart.addItems(items), "addMissionItems");
1752
+ ngOnInit() {
1753
+ this.syncCart();
1754
+ }
1755
+ ngOnChanges() {
1756
+ this.syncCart();
1757
+ }
1758
+ ngOnDestroy() {
1759
+ this.destroyed = true;
1760
+ for (const timeout of this.addedTimeouts.values()) {
1761
+ clearTimeout(timeout);
1762
+ }
1763
+ this.closeCamera();
1764
+ this.searchAbort?.abort();
1765
+ this.suggestionsAbort?.abort();
1766
+ }
1767
+ get resolvedMessages() {
1768
+ return resolveCommerceAISearchMessages({
1769
+ ...this.messages,
1770
+ placeholder: this.messages?.placeholder ?? this.placeholder
1771
+ });
1772
+ }
1773
+ get showSuggestions() {
1774
+ return this.enableAutocomplete && !this.suggestionsDismissed && !this.showResults && this.query.trim().length >= 2 && (this.isLoadingSuggestions || this.suggestions.length > 0 || !!this.suggestionsError || this.suggestionsReady);
1775
+ }
1776
+ get activeSuggestionId() {
1777
+ return this.activeSuggestionIndex >= 0 ? `cat-suggestion-${this.activeSuggestionIndex}` : null;
1778
+ }
1779
+ get showMission() {
1780
+ return Boolean(this.mission);
1781
+ }
1782
+ get showMissionVisible() {
1783
+ return this.showMission && !this.isLoading && !this.error;
1784
+ }
1785
+ get missionLaneCount() {
1786
+ if (!this.showMissionVisible) {
1787
+ return 0;
1788
+ }
1789
+ return Math.min(Math.max(this.mission?.intents.length ?? 1, 1), 5);
1790
+ }
1791
+ get cartQuantity() {
1792
+ return this.cart.cart()?.totalQuantity ?? 0;
1793
+ }
1794
+ get cartBadgeLabel() {
1795
+ return this.cartQuantity > 99 ? "99+" : String(this.cartQuantity);
1796
+ }
1797
+ get cartAriaLabel() {
1798
+ return this.cartQuantity > 0 ? `${this.resolvedMessages.cartAriaLabel} (${this.cartBadgeLabel})` : this.resolvedMessages.cartAriaLabel;
1799
+ }
1800
+ get showResults() {
1801
+ const hasQuery = this.query.trim().length > 0;
1802
+ const showEmptyResults = !this.isLoading && !this.error && this.hasSearched && this.results.length === 0 && !this.showMission && hasQuery;
1803
+ return hasQuery && (this.results.length > 0 || this.isLoading || !!this.error || showEmptyResults || this.showMission);
1804
+ }
1805
+ get showVoiceReplay() {
1806
+ return this.enableTts && this.lastSearchMode === "voice" && !!this.audioSummary;
1807
+ }
1808
+ onQueryChange(value) {
1809
+ this.activeSuggestionIndex = -1;
1810
+ this.suggestionsDismissed = false;
1811
+ if (this.debounceTimer) {
1812
+ clearTimeout(this.debounceTimer);
1813
+ this.debounceTimer = null;
1814
+ }
1815
+ if (this.suggestionsTimer) {
1816
+ clearTimeout(this.suggestionsTimer);
1817
+ this.suggestionsTimer = null;
1818
+ }
1819
+ const trimmed = value.trim();
1820
+ if (trimmed.length === 0 || trimmed.length < 2) {
1821
+ this.lastSearchMode = null;
1822
+ this.clearVoiceAudio();
1823
+ this.clearResults();
1824
+ return;
1825
+ }
1826
+ this.lastSearchMode = "text";
1827
+ this.clearVoiceAudio();
1828
+ if (this.enableAutocomplete) {
1829
+ this.results = [];
1830
+ this.mission = null;
1831
+ this.error = null;
1832
+ this.isLoading = false;
1833
+ this.hasSearched = false;
1834
+ this.isLoadingSuggestions = true;
1835
+ this.suggestionsTimer = setTimeout(() => this.fetchSuggestions(trimmed), 200);
1836
+ return;
1837
+ }
1838
+ this.isLoading = true;
1839
+ this.error = null;
1840
+ this.results = [];
1841
+ this.mission = null;
1842
+ this.meta = null;
1843
+ this.debounceTimer = setTimeout(() => this.onSubmit(), 250);
1844
+ }
1845
+ onSuggestionSelect(suggestion) {
1846
+ this.activeSuggestionIndex = -1;
1847
+ this.query = suggestion;
1848
+ this.suggestions = [];
1849
+ this.onSubmit();
1850
+ }
1851
+ onInputKeyDown(event) {
1852
+ if (event.key === "Escape") {
1853
+ this.activeSuggestionIndex = -1;
1854
+ this.suggestionsDismissed = true;
1855
+ return;
1856
+ }
1857
+ if (!this.showSuggestions || this.suggestions.length === 0) {
1858
+ return;
1859
+ }
1860
+ if (event.key === "ArrowDown") {
1861
+ event.preventDefault();
1862
+ this.activeSuggestionIndex = this.activeSuggestionIndex < this.suggestions.length - 1 ? this.activeSuggestionIndex + 1 : 0;
1863
+ return;
1864
+ }
1865
+ if (event.key === "ArrowUp") {
1866
+ event.preventDefault();
1867
+ this.activeSuggestionIndex = this.activeSuggestionIndex > 0 ? this.activeSuggestionIndex - 1 : this.suggestions.length - 1;
1868
+ return;
1869
+ }
1870
+ if (event.key === "Enter" && this.activeSuggestionIndex >= 0) {
1871
+ event.preventDefault();
1872
+ const suggestion = this.suggestions[this.activeSuggestionIndex];
1873
+ if (suggestion) {
1874
+ this.onSuggestionSelect(suggestion);
1875
+ }
1876
+ }
1877
+ }
1878
+ fetchSuggestions(searchQuery) {
1879
+ this.suggestionsAbort?.abort();
1880
+ const controller = new AbortController();
1881
+ this.suggestionsAbort = controller;
1882
+ const requestId = ++this.suggestionsRequestId;
1883
+ this.isLoadingSuggestions = true;
1884
+ this.suggestionsError = null;
1885
+ this.suggestionsReady = false;
1886
+ void this.api.suggest(this.apiBaseUrl, searchQuery, this.localeFields, controller.signal).then((data) => {
1887
+ if (requestId !== this.suggestionsRequestId) return;
1888
+ this.suggestions = data.suggestions;
1889
+ this.isLoadingSuggestions = false;
1890
+ this.suggestionsReady = true;
1891
+ this.cdr.detectChanges();
1892
+ }).catch((err) => {
1893
+ if (err.name === "AbortError") return;
1894
+ if (requestId !== this.suggestionsRequestId) return;
1895
+ this.suggestions = [];
1896
+ this.suggestionsError = err.message || "Suggestions failed";
1897
+ this.isLoadingSuggestions = false;
1898
+ this.suggestionsReady = true;
1899
+ this.cdr.detectChanges();
1900
+ });
1901
+ }
1902
+ clearResults() {
1903
+ this.searchAbort?.abort();
1904
+ this.searchAbort = null;
1905
+ this.suggestionsAbort?.abort();
1906
+ this.suggestionsAbort = null;
1907
+ if (this.debounceTimer) {
1908
+ clearTimeout(this.debounceTimer);
1909
+ this.debounceTimer = null;
1910
+ }
1911
+ if (this.suggestionsTimer) {
1912
+ clearTimeout(this.suggestionsTimer);
1913
+ this.suggestionsTimer = null;
1914
+ }
1915
+ this.results = [];
1916
+ this.mission = null;
1917
+ this.hasFacetSession = false;
1918
+ this.meta = null;
1919
+ this.error = null;
1920
+ this.isLoading = false;
1921
+ this.hasSearched = false;
1922
+ this.suggestions = [];
1923
+ this.isLoadingSuggestions = false;
1924
+ this.suggestionsError = null;
1925
+ this.suggestionsReady = false;
1926
+ this.suggestionsDismissed = false;
1927
+ this.activeSuggestionIndex = -1;
1928
+ }
1929
+ get localeFields() {
1930
+ return {
1931
+ catalogLocale: this.catalogLocale,
1932
+ queryLocale: this.queryLocale,
1933
+ locale: this.locale
1934
+ };
1935
+ }
1936
+ isFacetSelected(facetId, key) {
1937
+ return isFacetFilterSelected(this.meta?.appliedFilters ?? {}, facetId, key);
1938
+ }
1939
+ facetSwatch(facetId, key) {
1940
+ return isColorLikeFacetName(facetId) ? hexColorSwatchValue(key) : void 0;
1941
+ }
1942
+ get hasAppliedFilters() {
1943
+ return Object.keys(this.meta?.appliedFilters ?? {}).length > 0;
1944
+ }
1945
+ toggleFacet(facetId, key) {
1946
+ if (!this.meta?.searchTerms) return;
1947
+ const filters = toggleFacetFilter(this.meta.appliedFilters ?? {}, facetId, key);
1948
+ this.searchWithFacets(filters);
1949
+ }
1950
+ clearFacetFilters() {
1951
+ if (!this.meta?.searchTerms) return;
1952
+ this.searchWithFacets({});
1953
+ }
1954
+ startNewSearch() {
1955
+ const q = this.query.trim();
1956
+ if (!q) return;
1957
+ this.suggestedFacets = [];
1958
+ this.meta = this.meta ? {
1959
+ ...this.meta,
1960
+ searchTerms: void 0,
1961
+ appliedFilters: void 0
1962
+ } : null;
1963
+ this.lastSearchMode = "text";
1964
+ this.clearVoiceAudio();
1965
+ this.suggestions = [];
1966
+ this.activeSuggestionIndex = -1;
1967
+ this.searchAbort?.abort();
1968
+ const controller = new AbortController();
1969
+ this.searchAbort = controller;
1970
+ const requestId = ++this.searchRequestId;
1971
+ this.isLoading = true;
1972
+ this.error = null;
1973
+ this.results = [];
1974
+ this.mission = null;
1975
+ void this.api.search(this.apiBaseUrl, q, this.localeFields, controller.signal, {
1976
+ includeFacets: this.enableFacets,
1977
+ ...this.enableMissions ? {
1978
+ enableMissions: true
1979
+ } : {}
1980
+ }).then((data) => {
1981
+ if (requestId !== this.searchRequestId) return;
1982
+ this.applySearchResult(data);
1983
+ this.isLoading = false;
1984
+ this.hasSearched = true;
1985
+ this.cdr.detectChanges();
1986
+ }).catch((err) => {
1987
+ if (err.name === "AbortError") return;
1988
+ if (requestId !== this.searchRequestId) return;
1989
+ this.error = err.message;
1990
+ this.results = [];
1991
+ this.mission = null;
1992
+ this.hasFacetSession = false;
1993
+ this.meta = null;
1994
+ this.isLoading = false;
1995
+ this.hasSearched = true;
1996
+ this.cdr.detectChanges();
1997
+ });
1998
+ }
1999
+ searchWithFacets(filters, refineQuery) {
2000
+ if (!this.meta?.searchTerms) return;
2001
+ this.isLoading = true;
2002
+ this.error = null;
2003
+ const controller = new AbortController();
2004
+ this.searchAbort?.abort();
2005
+ this.searchAbort = controller;
2006
+ const requestId = ++this.searchRequestId;
2007
+ void this.api.search(this.apiBaseUrl, this.meta.queryInterpretation ?? this.query, this.localeFields, controller.signal, {
2008
+ filters,
2009
+ searchTerms: this.meta.searchTerms,
2010
+ refineQuery,
2011
+ suggestedFacets: this.suggestedFacets,
2012
+ includeFacets: true
2013
+ }).then((data) => {
2014
+ if (requestId !== this.searchRequestId) return;
2015
+ this.applySearchResult(data);
2016
+ this.isLoading = false;
2017
+ this.hasSearched = true;
2018
+ this.cdr.detectChanges();
2019
+ }).catch((error) => {
2020
+ if (requestId !== this.searchRequestId || error.name === "AbortError") return;
2021
+ this.error = error.message;
2022
+ this.isLoading = false;
2023
+ this.cdr.detectChanges();
2024
+ });
2025
+ }
2026
+ onSubmit() {
2027
+ const q = this.query.trim();
2028
+ if (!q) return;
2029
+ if (this.enableAutocomplete && this.activeSuggestionIndex >= 0 && this.suggestions[this.activeSuggestionIndex]) {
2030
+ this.onSuggestionSelect(this.suggestions[this.activeSuggestionIndex]);
2031
+ return;
2032
+ }
2033
+ this.lastSearchMode = "text";
2034
+ this.clearVoiceAudio();
2035
+ this.suggestions = [];
2036
+ this.activeSuggestionIndex = -1;
2037
+ this.searchAbort?.abort();
2038
+ const controller = new AbortController();
2039
+ this.searchAbort = controller;
2040
+ const requestId = ++this.searchRequestId;
2041
+ this.isLoading = true;
2042
+ this.error = null;
2043
+ this.results = [];
2044
+ this.mission = null;
2045
+ const previousMeta = this.meta;
2046
+ this.meta = null;
2047
+ const isRefinement = this.enableFacets && this.hasFacetSession && !(this.enableMissions && looksLikeCompoundShoppingList(q));
2048
+ void this.api.search(this.apiBaseUrl, isRefinement ? previousMeta?.queryInterpretation ?? q : q, this.localeFields, controller.signal, isRefinement ? {
2049
+ searchTerms: previousMeta?.searchTerms,
2050
+ filters: previousMeta?.appliedFilters,
2051
+ refineQuery: q,
2052
+ suggestedFacets: this.suggestedFacets,
2053
+ includeFacets: true
2054
+ } : {
2055
+ includeFacets: this.enableFacets,
2056
+ ...this.enableMissions ? {
2057
+ enableMissions: true
2058
+ } : {}
2059
+ }).then((data) => {
2060
+ if (requestId !== this.searchRequestId) return;
2061
+ this.applySearchResult(data);
2062
+ this.isLoading = false;
2063
+ this.hasSearched = true;
2064
+ }, (err) => {
2065
+ if (err.name === "AbortError") return;
2066
+ if (requestId !== this.searchRequestId) return;
2067
+ this.error = err.message;
2068
+ this.results = [];
2069
+ this.mission = null;
2070
+ this.hasFacetSession = false;
2071
+ this.meta = null;
2072
+ this.isLoading = false;
2073
+ this.hasSearched = true;
2074
+ });
2075
+ }
2076
+ onFileSelected(event) {
2077
+ const input = event.target;
2078
+ const file = input.files?.[0];
2079
+ if (file) void this.searchByImage(file);
2080
+ }
2081
+ onCameraFileSelected(event) {
2082
+ const input = event.target;
2083
+ const file = input.files?.[0];
2084
+ input.value = "";
2085
+ if (file) void this.searchByImage(file);
2086
+ }
2087
+ openCamera() {
2088
+ this.cameraError = null;
2089
+ if (prefersNativeCamera()) {
2090
+ this.cameraInputRef?.nativeElement.click();
2091
+ return;
2092
+ }
2093
+ void this.openCameraOverlay();
2094
+ }
2095
+ async openCameraOverlay() {
2096
+ this.cameraError = null;
2097
+ try {
2098
+ const stream = await navigator.mediaDevices.getUserMedia(buildCameraConstraints(this.cameraFacingMode));
2099
+ this.cameraStream = stream;
2100
+ this.isCameraOpen = true;
2101
+ this.cdr.detectChanges();
2102
+ const video = this.cameraVideoRef?.nativeElement;
2103
+ if (video) {
2104
+ video.srcObject = stream;
2105
+ await video.play();
2106
+ }
2107
+ } catch (err) {
2108
+ this.cameraError = getCameraErrorMessage(err);
2109
+ this.closeCamera();
2110
+ }
2111
+ }
2112
+ async capturePhoto() {
2113
+ const video = this.cameraVideoRef?.nativeElement;
2114
+ if (!video) return;
2115
+ try {
2116
+ const file = await createJpegFileFromVideo(video);
2117
+ this.closeCamera();
2118
+ void this.searchByImage(file);
2119
+ } catch (err) {
2120
+ this.closeCamera();
2121
+ this.error = err instanceof Error ? err.message : this.resolvedMessages.couldNotCapturePhoto;
2122
+ }
2123
+ }
2124
+ closeCamera() {
2125
+ stopMediaStream(this.cameraStream);
2126
+ this.cameraStream = null;
2127
+ this.isCameraOpen = false;
2128
+ const video = this.cameraVideoRef?.nativeElement;
2129
+ if (video) {
2130
+ video.srcObject = null;
2131
+ }
2132
+ }
2133
+ clearCameraError() {
2134
+ this.cameraError = null;
2135
+ }
2136
+ onDragOver(event) {
2137
+ if (!this.enableImageSearch) {
2138
+ return;
2139
+ }
2140
+ event.preventDefault();
2141
+ this.isDragging = true;
2142
+ }
2143
+ onDrop(event) {
2144
+ event.preventDefault();
2145
+ this.isDragging = false;
2146
+ if (!this.enableImageSearch) {
2147
+ return;
2148
+ }
2149
+ const file = event.dataTransfer?.files[0];
2150
+ if (file) void this.searchByImage(file);
2151
+ }
2152
+ searchByImage(file) {
2153
+ if (!file.type.startsWith("image/")) return;
2154
+ this.lastSearchMode = "image";
2155
+ this.clearVoiceAudio();
2156
+ this.searchAbort?.abort();
2157
+ if (this.debounceTimer) {
2158
+ clearTimeout(this.debounceTimer);
2159
+ this.debounceTimer = null;
2160
+ }
2161
+ this.isLoading = true;
2162
+ this.error = null;
2163
+ this.results = [];
2164
+ this.mission = null;
2165
+ this.meta = null;
2166
+ void this.api.searchByImage(this.apiBaseUrl, file, this.localeFields, this.enableMissions).then((data) => {
2167
+ this.applySearchResult(data);
2168
+ if (data.interpretation) this.query = data.interpretation;
2169
+ this.isLoading = false;
2170
+ this.hasSearched = true;
2171
+ }, (err) => {
2172
+ this.error = err.message;
2173
+ this.isLoading = false;
2174
+ this.hasSearched = true;
2175
+ });
2176
+ }
2177
+ async toggleRecording() {
2178
+ if (this.isRecording) {
2179
+ this.mediaRecorder?.stop();
2180
+ this.isRecording = false;
2181
+ return;
2182
+ }
2183
+ try {
2184
+ const stream = await navigator.mediaDevices.getUserMedia({
2185
+ audio: true
2186
+ });
2187
+ this.mediaRecorder = new MediaRecorder(stream);
2188
+ this.audioChunks = [];
2189
+ this.mediaRecorder.ondataavailable = (e) => {
2190
+ if (e.data.size > 0) this.audioChunks.push(e.data);
2191
+ };
2192
+ this.mediaRecorder.onstop = () => {
2193
+ stream.getTracks().forEach((t) => t.stop());
2194
+ const blob = new Blob(this.audioChunks, {
2195
+ type: "audio/webm"
2196
+ });
2197
+ this.isProcessing = true;
2198
+ this.clearVoiceAudio();
2199
+ void this.api.searchByVoice(this.apiBaseUrl, blob, this.localeFields, this.enableTts, this.enableMissions).then(async (data) => {
2200
+ this.lastSearchMode = "voice";
2201
+ this.query = data.transcript;
2202
+ this.applySearchResult({
2203
+ products: data.products,
2204
+ meta: data.meta,
2205
+ mission: data.mission
2206
+ });
2207
+ this.error = null;
2208
+ this.hasSearched = true;
2209
+ if (data.audioSummary) {
2210
+ this.audioSummary = data.audioSummary;
2211
+ const audio = new Audio(`data:audio/mpeg;base64,${data.audioSummary}`);
2212
+ void audio.play();
2213
+ } else if (this.enableTts && data.ttsText && data.ttsPending) {
2214
+ try {
2215
+ const blob2 = await this.api.synthesizeSpeech(this.apiBaseUrl, data.ttsText);
2216
+ const buffer = await blob2.arrayBuffer();
2217
+ const bytes = new Uint8Array(buffer);
2218
+ let binary = "";
2219
+ for (let i = 0; i < bytes.length; i++) {
2220
+ binary += String.fromCharCode(bytes[i]);
2221
+ }
2222
+ this.audioSummary = btoa(binary);
2223
+ const audio = new Audio(`data:audio/mpeg;base64,${this.audioSummary}`);
2224
+ void audio.play();
2225
+ } catch {
2226
+ this.audioSummary = null;
2227
+ }
2228
+ }
2229
+ }).catch((err) => {
2230
+ this.error = err.message;
2231
+ }).finally(() => {
2232
+ this.isProcessing = false;
2233
+ });
2234
+ };
2235
+ this.mediaRecorder.start();
2236
+ this.isRecording = true;
2237
+ } catch {
2238
+ this.error = "Microphone access denied";
2239
+ }
2240
+ }
2241
+ replayVoiceSummary() {
2242
+ if (!this.audioSummary) return;
2243
+ const audio = new Audio(`data:audio/mpeg;base64,${this.audioSummary}`);
2244
+ void audio.play();
2245
+ }
2246
+ addToCartLabel(product) {
2247
+ if (this.addedProductIds[product.id]) {
2248
+ return this.resolvedMessages.itemAdded;
2249
+ }
2250
+ return product.sku || product.id ? this.resolvedMessages.addToCart : this.resolvedMessages.unableToAddToCart;
2251
+ }
2252
+ onAddItem(product) {
2253
+ void this.handleAddToCart(product);
2254
+ }
2255
+ onCartRemove(lineItemId) {
2256
+ void this.cart.removeFromCart(lineItemId);
2257
+ }
2258
+ onCartQuantityChange(event) {
2259
+ void this.cart.updateQuantity(event.lineItemId, event.quantity);
2260
+ }
2261
+ onCartLogin(input) {
2262
+ void this.cart.login(input);
2263
+ }
2264
+ onCartLogout() {
2265
+ void this.cart.logout();
2266
+ }
2267
+ applySearchResult(data) {
2268
+ this.results = data.products;
2269
+ this.mission = data.mission ?? null;
2270
+ this.meta = data.meta;
2271
+ this.facets = data.facets ?? [];
2272
+ this.suggestedFacets = data.suggestedFacets ?? [];
2273
+ this.hasFacetSession = !data.mission && this.enableFacets && Boolean(data.meta.searchTerms);
2274
+ }
2275
+ syncCart() {
2276
+ this.cart.configure({
2277
+ apiBaseUrl: this.apiBaseUrl,
2278
+ currency: this.currency,
2279
+ country: this.country,
2280
+ catalogLocale: this.catalogLocale,
2281
+ enabled: this.enableCart,
2282
+ onCartChange: /* @__PURE__ */ __name((snapshot) => this.cartChange.emit(snapshot), "onCartChange")
2283
+ });
2284
+ }
2285
+ async handleAddToCart(product) {
2286
+ if (!product.sku && !product.id) {
2287
+ return;
2288
+ }
2289
+ const next = await this.cart.addToCart(toCartItem(product, 1));
2290
+ if (!next || this.destroyed) {
2291
+ return;
2292
+ }
2293
+ this.addedProductIds = {
2294
+ ...this.addedProductIds,
2295
+ [product.id]: true
2296
+ };
2297
+ const existing = this.addedTimeouts.get(product.id);
2298
+ if (existing) {
2299
+ clearTimeout(existing);
2300
+ }
2301
+ this.addedTimeouts.set(product.id, setTimeout(() => {
2302
+ if (this.destroyed) {
2303
+ return;
2304
+ }
2305
+ const { [product.id]: _removed, ...rest } = this.addedProductIds;
2306
+ this.addedProductIds = rest;
2307
+ this.addedTimeouts.delete(product.id);
2308
+ this.cdr.detectChanges();
2309
+ }, 1200));
2310
+ this.cdr.detectChanges();
2311
+ }
2312
+ clearVoiceAudio() {
2313
+ this.audioSummary = null;
2314
+ }
2315
+ };
2316
+ _ts_decorate5([
2317
+ (0, import_core5.Input)()
2318
+ ], CommerceAiSearchComponent.prototype, "apiBaseUrl", void 0);
2319
+ _ts_decorate5([
2320
+ (0, import_core5.Input)(),
2321
+ _ts_metadata3("design:type", typeof ThemeMode === "undefined" ? Object : ThemeMode)
2322
+ ], CommerceAiSearchComponent.prototype, "theme", void 0);
2323
+ _ts_decorate5([
2324
+ (0, import_core5.Input)(),
2325
+ _ts_metadata3("design:type", String)
2326
+ ], CommerceAiSearchComponent.prototype, "catalogLocale", void 0);
2327
+ _ts_decorate5([
2328
+ (0, import_core5.Input)(),
2329
+ _ts_metadata3("design:type", String)
2330
+ ], CommerceAiSearchComponent.prototype, "queryLocale", void 0);
2331
+ _ts_decorate5([
2332
+ (0, import_core5.Input)(),
2333
+ _ts_metadata3("design:type", String)
2334
+ ], CommerceAiSearchComponent.prototype, "locale", void 0);
2335
+ _ts_decorate5([
2336
+ (0, import_core5.Input)()
2337
+ ], CommerceAiSearchComponent.prototype, "placeholder", void 0);
2338
+ _ts_decorate5([
2339
+ (0, import_core5.Input)(),
2340
+ _ts_metadata3("design:type", typeof Partial === "undefined" ? Object : Partial)
2341
+ ], CommerceAiSearchComponent.prototype, "messages", void 0);
2342
+ _ts_decorate5([
2343
+ (0, import_core5.Input)()
2344
+ ], CommerceAiSearchComponent.prototype, "enableAutocomplete", void 0);
2345
+ _ts_decorate5([
2346
+ (0, import_core5.Input)()
2347
+ ], CommerceAiSearchComponent.prototype, "enableFacets", void 0);
2348
+ _ts_decorate5([
2349
+ (0, import_core5.Input)()
2350
+ ], CommerceAiSearchComponent.prototype, "enableVoice", void 0);
2351
+ _ts_decorate5([
2352
+ (0, import_core5.Input)()
2353
+ ], CommerceAiSearchComponent.prototype, "enableImageSearch", void 0);
2354
+ _ts_decorate5([
2355
+ (0, import_core5.Input)()
2356
+ ], CommerceAiSearchComponent.prototype, "enableCameraSearch", void 0);
2357
+ _ts_decorate5([
2358
+ (0, import_core5.Input)(),
2359
+ _ts_metadata3("design:type", typeof CameraFacingMode === "undefined" ? Object : CameraFacingMode)
2360
+ ], CommerceAiSearchComponent.prototype, "cameraFacingMode", void 0);
2361
+ _ts_decorate5([
2362
+ (0, import_core5.Input)()
2363
+ ], CommerceAiSearchComponent.prototype, "enableTts", void 0);
2364
+ _ts_decorate5([
2365
+ (0, import_core5.Input)()
2366
+ ], CommerceAiSearchComponent.prototype, "enableCart", void 0);
2367
+ _ts_decorate5([
2368
+ (0, import_core5.Input)()
2369
+ ], CommerceAiSearchComponent.prototype, "enableMissions", void 0);
2370
+ _ts_decorate5([
2371
+ (0, import_core5.Input)(),
2372
+ _ts_metadata3("design:type", String)
2373
+ ], CommerceAiSearchComponent.prototype, "currency", void 0);
2374
+ _ts_decorate5([
2375
+ (0, import_core5.Input)(),
2376
+ _ts_metadata3("design:type", String)
2377
+ ], CommerceAiSearchComponent.prototype, "country", void 0);
2378
+ _ts_decorate5([
2379
+ (0, import_core5.Output)()
2380
+ ], CommerceAiSearchComponent.prototype, "productSelect", void 0);
2381
+ _ts_decorate5([
2382
+ (0, import_core5.Output)()
2383
+ ], CommerceAiSearchComponent.prototype, "cartChange", void 0);
2384
+ _ts_decorate5([
2385
+ (0, import_core5.Output)()
2386
+ ], CommerceAiSearchComponent.prototype, "checkout", void 0);
2387
+ _ts_decorate5([
2388
+ (0, import_core5.ViewChild)("cameraInput"),
2389
+ _ts_metadata3("design:type", typeof import_core5.ElementRef === "undefined" ? Object : import_core5.ElementRef)
2390
+ ], CommerceAiSearchComponent.prototype, "cameraInputRef", void 0);
2391
+ _ts_decorate5([
2392
+ (0, import_core5.ViewChild)("cameraVideo"),
2393
+ _ts_metadata3("design:type", typeof import_core5.ElementRef === "undefined" ? Object : import_core5.ElementRef)
2394
+ ], CommerceAiSearchComponent.prototype, "cameraVideoRef", void 0);
2395
+ CommerceAiSearchComponent = _ts_decorate5([
2396
+ (0, import_core5.Component)({
2397
+ selector: "commerce-ai-search",
2398
+ standalone: true,
2399
+ imports: [
2400
+ import_forms2.FormsModule,
2401
+ CommerceAiMissionResultsComponent,
2402
+ CommerceAiCartPanelComponent
2403
+ ],
2404
+ providers: [
2405
+ CommerceAiCartService
2406
+ ],
2407
+ template: `
2408
+ <div
2409
+ class="cat-root cat-wrapper"
2410
+ [class.cat-root--mission]="showMissionVisible"
2411
+ [class.cat-root--mission-lanes-1]="missionLaneCount === 1"
2412
+ [class.cat-root--mission-lanes-2]="missionLaneCount === 2"
2413
+ [class.cat-root--mission-lanes-3]="missionLaneCount === 3"
2414
+ [class.cat-root--mission-lanes-4]="missionLaneCount === 4"
2415
+ [class.cat-root--mission-lanes-5]="missionLaneCount >= 5"
2416
+ [attr.data-theme]="theme"
2417
+ role="search"
2418
+ [attr.aria-label]="resolvedMessages.productSearchAriaLabel"
2419
+ (dragover)="onDragOver($event)"
2420
+ (dragleave)="isDragging = false"
2421
+ (drop)="onDrop($event)"
2422
+ >
2423
+ @if (isDragging && enableImageSearch) {
2424
+ <div class="cat-drag-overlay" aria-hidden="true">{{ resolvedMessages.dropImageToSearch }}</div>
2425
+ }
2426
+
2427
+ <form class="cat-search-bar" (ngSubmit)="onSubmit()">
2428
+ <svg
2429
+ width="18"
2430
+ height="18"
2431
+ viewBox="0 0 24 24"
2432
+ fill="none"
2433
+ stroke="currentColor"
2434
+ stroke-width="2"
2435
+ aria-hidden="true"
2436
+ >
2437
+ <circle cx="11" cy="11" r="8" />
2438
+ <path d="m21 21-4.3-4.3" />
2439
+ </svg>
2440
+
2441
+ <div class="cat-search-input-wrap">
2442
+ <input
2443
+ type="search"
2444
+ class="cat-search-input"
2445
+ [(ngModel)]="query"
2446
+ name="query"
2447
+ [placeholder]="resolvedMessages.placeholder"
2448
+ [attr.aria-label]="resolvedMessages.searchAriaLabel"
2449
+ [attr.role]="enableAutocomplete ? 'combobox' : null"
2450
+ [attr.aria-haspopup]="enableAutocomplete ? 'listbox' : null"
2451
+ [attr.aria-autocomplete]="enableAutocomplete ? 'list' : null"
2452
+ [attr.aria-expanded]="showSuggestions"
2453
+ [attr.aria-controls]="showSuggestions ? 'cat-suggestions-listbox' : null"
2454
+ [attr.aria-activedescendant]="activeSuggestionId"
2455
+ autocomplete="off"
2456
+ (ngModelChange)="onQueryChange($event)"
2457
+ (keydown)="onInputKeyDown($event)"
2458
+ />
2459
+ </div>
2460
+
2461
+ @if (enableVoice) {
2462
+ <button
2463
+ type="button"
2464
+ class="cat-icon-btn"
2465
+ [class.cat-icon-btn--active]="isRecording"
2466
+ [disabled]="isProcessing"
2467
+ [attr.aria-label]="isRecording ? resolvedMessages.stopRecording : resolvedMessages.voiceSearch"
2468
+ [attr.aria-pressed]="isRecording"
2469
+ (click)="toggleRecording()"
2470
+ >
2471
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2472
+ <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" />
2473
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
2474
+ <line x1="12" x2="12" y1="19" y2="22" />
2475
+ </svg>
2476
+ </button>
2477
+ }
2478
+
2479
+ @if (enableCameraSearch) {
2480
+ <button
2481
+ type="button"
2482
+ class="cat-icon-btn"
2483
+ [disabled]="isLoading"
2484
+ aria-label="{{ resolvedMessages.searchByCamera }}"
2485
+ (click)="openCamera()"
2486
+ >
2487
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2488
+ <path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z" />
2489
+ <circle cx="12" cy="13" r="3" />
2490
+ </svg>
2491
+ </button>
2492
+ <input
2493
+ #cameraInput
2494
+ type="file"
2495
+ accept="image/*"
2496
+ [attr.capture]="cameraFacingMode"
2497
+ class="cat-hidden-input"
2498
+ (change)="onCameraFileSelected($event)"
2499
+ />
2500
+ }
2501
+
2502
+ @if (enableImageSearch) {
2503
+ <button
2504
+ type="button"
2505
+ class="cat-icon-btn"
2506
+ [disabled]="isLoading"
2507
+ aria-label="{{ resolvedMessages.searchByImage }}"
2508
+ (click)="fileInput.click()"
2509
+ >
2510
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2511
+ <rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
2512
+ <circle cx="9" cy="9" r="2" />
2513
+ <path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
2514
+ </svg>
2515
+ </button>
2516
+ <input
2517
+ #fileInput
2518
+ type="file"
2519
+ accept="image/*"
2520
+ class="cat-hidden-input"
2521
+ (change)="onFileSelected($event)"
2522
+ />
2523
+ }
2524
+
2525
+ @if (showVoiceReplay) {
2526
+ <button
2527
+ type="button"
2528
+ class="cat-icon-btn"
2529
+ aria-label="{{ resolvedMessages.replayVoiceSummary }}"
2530
+ (click)="replayVoiceSummary()"
2531
+ >
2532
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2533
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
2534
+ <path d="M15.54 8.46a5 5 0 0 1 0 7.07" />
2535
+ </svg>
2536
+ </button>
2537
+ }
2538
+
2539
+ @if (enableCart) {
2540
+ <button
2541
+ type="button"
2542
+ class="cat-icon-btn cat-cart-toggle"
2543
+ [class.cat-cart-toggle--open]="cart.isCartOpen()"
2544
+ [attr.aria-label]="cartAriaLabel"
2545
+ [attr.aria-expanded]="cart.isCartOpen()"
2546
+ [attr.aria-pressed]="cart.isCartOpen()"
2547
+ (click)="cart.toggleCart()"
2548
+ >
2549
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
2550
+ <circle cx="8" cy="21" r="1" />
2551
+ <circle cx="19" cy="21" r="1" />
2552
+ <path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12" />
2553
+ </svg>
2554
+ @if (cartQuantity > 0) {
2555
+ <span class="cat-cart-badge" aria-hidden="true">{{ cartBadgeLabel }}</span>
2556
+ }
2557
+ </button>
2558
+ }
2559
+
2560
+ @if (showSuggestions) {
2561
+ <div
2562
+ id="cat-suggestions-listbox"
2563
+ class="cat-suggestions"
2564
+ role="listbox"
2565
+ [attr.aria-label]="resolvedMessages.suggestionsAriaLabel"
2566
+ >
2567
+ @if (isLoadingSuggestions && suggestions.length === 0 && !suggestionsError) {
2568
+ <div class="cat-suggestions__status">{{ resolvedMessages.loadingSuggestions }}</div>
2569
+ }
2570
+
2571
+ @if (suggestionsError) {
2572
+ <div class="cat-suggestions__status cat-suggestions__status--error" role="alert">
2573
+ {{ suggestionsError }}
2574
+ </div>
2575
+ }
2576
+
2577
+ @if (
2578
+ !isLoadingSuggestions &&
2579
+ !suggestionsError &&
2580
+ suggestionsReady &&
2581
+ suggestions.length === 0
2582
+ ) {
2583
+ <div class="cat-suggestions__status">{{ resolvedMessages.noSuggestions }}</div>
2584
+ }
2585
+
2586
+ @for (suggestion of suggestions; track $index; let index = $index) {
2587
+ <button
2588
+ type="button"
2589
+ class="cat-suggestions__item"
2590
+ [class.cat-suggestions__item--active]="index === activeSuggestionIndex"
2591
+ role="option"
2592
+ [attr.id]="'cat-suggestion-' + index"
2593
+ [attr.aria-selected]="index === activeSuggestionIndex"
2594
+ (mousedown)="$event.preventDefault()"
2595
+ (click)="onSuggestionSelect(suggestion)"
2596
+ >
2597
+ {{ suggestion }}
2598
+ </button>
2599
+ }
2600
+ </div>
2601
+ }
2602
+ </form>
2603
+
2604
+ @if (enableCameraSearch && (isCameraOpen || cameraError)) {
2605
+ <div class="cat-camera-overlay" role="dialog" [attr.aria-label]="resolvedMessages.cameraCapture">
2606
+ <div class="cat-camera-overlay__panel">
2607
+ @if (cameraError) {
2608
+ <div class="cat-camera-overlay__error" role="alert">
2609
+ <p>{{ cameraError }}</p>
2610
+ <button type="button" class="cat-camera-overlay__btn" (click)="clearCameraError()">
2611
+ {{ resolvedMessages.dismiss }}
2612
+ </button>
2613
+ </div>
2614
+ } @else {
2615
+ <video
2616
+ #cameraVideo
2617
+ class="cat-camera-preview"
2618
+ autoplay
2619
+ playsinline
2620
+ muted
2621
+ [attr.aria-label]="resolvedMessages.cameraPreview"
2622
+ ></video>
2623
+ <div class="cat-camera-actions">
2624
+ <button
2625
+ type="button"
2626
+ class="cat-camera-overlay__btn cat-camera-overlay__btn--secondary"
2627
+ [attr.aria-label]="resolvedMessages.cancel"
2628
+ (click)="closeCamera()"
2629
+ >
2630
+ {{ resolvedMessages.cancel }}
2631
+ </button>
2632
+ <button
2633
+ type="button"
2634
+ class="cat-camera-overlay__btn cat-camera-overlay__btn--primary"
2635
+ [attr.aria-label]="resolvedMessages.capturePhoto"
2636
+ (click)="capturePhoto()"
2637
+ >
2638
+ {{ resolvedMessages.capturePhoto }}
2639
+ </button>
2640
+ </div>
2641
+ }
2642
+ </div>
2643
+ </div>
2644
+ }
2645
+
2646
+ @if (enableCart && cart.isCartOpen()) {
2647
+ <commerce-ai-cart-panel
2648
+ [cart]="cart.cart()"
2649
+ [customer]="cart.customer()"
2650
+ [isLoading]="cart.isLoading() || cart.isMutating()"
2651
+ [isLoggingIn]="cart.isLoggingIn()"
2652
+ [error]="cart.error()"
2653
+ [messages]="resolvedMessages"
2654
+ [catalogLocale]="catalogLocale"
2655
+ [showCheckout]="checkout.observed"
2656
+ (close)="cart.closeCart()"
2657
+ (remove)="onCartRemove($event)"
2658
+ (quantityChange)="onCartQuantityChange($event)"
2659
+ (login)="onCartLogin($event)"
2660
+ (logout)="onCartLogout()"
2661
+ (checkout)="checkout.emit($event)"
2662
+ />
2663
+ }
2664
+
2665
+ @if (enableFacets && lastSearchMode === "text" && hasSearched && facets.length && !showMission) {
2666
+ <section class="cat-facets" [attr.aria-label]="resolvedMessages.filtersAriaLabel">
2667
+ <div class="cat-facets__header">
2668
+ <span>{{ resolvedMessages.narrowResults }}</span>
2669
+ <div class="cat-facets__actions">
2670
+ @if (hasAppliedFilters) {
2671
+ <button type="button" class="cat-facets__clear" (click)="clearFacetFilters()">
2672
+ {{ resolvedMessages.clearFilters }}
2673
+ </button>
2674
+ }
2675
+ <button type="button" class="cat-facets__clear" (click)="startNewSearch()">
2676
+ {{ resolvedMessages.newSearch }}
2677
+ </button>
2678
+ </div>
2679
+ </div>
2680
+ @for (facet of facets; track facet.id) {
2681
+ <div class="cat-facet-group" role="group" [attr.aria-label]="facet.label">
2682
+ <span class="cat-facet-group__label">{{ facet.label }}</span>
2683
+ <div class="cat-facet-group__options">
2684
+ @for (bucket of facet.buckets; track bucket.key) {
2685
+ <button
2686
+ type="button"
2687
+ class="cat-facet-chip"
2688
+ [class.cat-facet-chip--selected]="isFacetSelected(facet.id, bucket.key)"
2689
+ [attr.aria-pressed]="isFacetSelected(facet.id, bucket.key)"
2690
+ (click)="toggleFacet(facet.id, bucket.key)"
2691
+ >
2692
+ @if (facetSwatch(facet.id, bucket.key); as swatch) {
2693
+ <span class="cat-facet-chip__swatch" [style.background-color]="swatch" aria-hidden="true"></span>
2694
+ }
2695
+ {{ bucket.label }} <span aria-hidden="true">{{ bucket.count }}</span>
2696
+ </button>
2697
+ }
2698
+ </div>
2699
+ </div>
2700
+ }
2701
+ </section>
2702
+ }
2703
+
2704
+ @if (showResults && isLoading) {
2705
+ <div class="cat-status">{{ resolvedMessages.searching }}</div>
2706
+ }
2707
+ @if (showResults && error) {
2708
+ <div class="cat-status cat-status--error">{{ error }}</div>
2709
+ }
2710
+
2711
+ @if (!isLoading && !error && showResults && showMission && mission) {
2712
+ <commerce-ai-mission-results
2713
+ [mission]="mission"
2714
+ [messages]="resolvedMessages"
2715
+ [enableCart]="enableCart"
2716
+ [isMutating]="cart.isMutating()"
2717
+ [addedProductIds]="addedProductIds"
2718
+ [addAllHandler]="addMissionItems"
2719
+ (productSelect)="productSelect.emit($event)"
2720
+ (addItem)="onAddItem($event)"
2721
+ />
2722
+ }
2723
+
2724
+ @if (showResults && !showMission && !isLoading && !error) {
2725
+ <div
2726
+ class="cat-results"
2727
+ [attr.role]="results.length > 0 ? 'list' : null"
2728
+ [attr.aria-label]="results.length > 0 ? resolvedMessages.searchResultsAriaLabel : null"
2729
+ >
2730
+ @for (product of results; track product.id; let index = $index) {
2731
+ <article
2732
+ class="cat-result-card"
2733
+ role="listitem"
2734
+ [class.cat-result-card--featured]="index === 0"
2735
+ >
2736
+ <div class="cat-result-card__core">
2737
+ <button type="button" class="cat-result-card__select" (click)="productSelect.emit(product)">
2738
+ @if (product.imageUrl) {
2739
+ <img [src]="product.imageUrl" alt="" class="cat-result-image" loading="lazy" />
2740
+ } @else {
2741
+ <div class="cat-result-image cat-result-image--placeholder">\u{1F4E6}</div>
2742
+ }
2743
+ <div class="cat-result-info">
2744
+ <div class="cat-result-name">{{ product.name }}</div>
2745
+ @if (product.price) {
2746
+ <div class="cat-result-price">{{ product.price.formatted }}</div>
2747
+ }
2748
+ </div>
2749
+ </button>
2750
+ @if (enableCart) {
2751
+ <button
2752
+ type="button"
2753
+ class="cat-icon-btn cat-result-card__add"
2754
+ [class.cat-result-card__add--added]="addedProductIds[product.id]"
2755
+ [attr.aria-label]="addToCartLabel(product)"
2756
+ [disabled]="!(product.sku || product.id) || cart.isMutating()"
2757
+ (click)="onAddItem(product)"
2758
+ >
2759
+ @if (addedProductIds[product.id]) {
2760
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
2761
+ <path d="M20 6 9 17l-5-5" />
2762
+ </svg>
2763
+ } @else {
2764
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
2765
+ <circle cx="8" cy="21" r="1" />
2766
+ <circle cx="19" cy="21" r="1" />
2767
+ <path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12" />
2768
+ </svg>
2769
+ }
2770
+ </button>
2771
+ }
2772
+ </div>
2773
+ </article>
2774
+ }
2775
+
2776
+ @if (results.length === 0 && hasSearched && query.trim()) {
2777
+ <div class="cat-status cat-status--empty" role="status" aria-live="polite">
2778
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
2779
+ <circle cx="11" cy="11" r="8" />
2780
+ <path d="m21 21-4.3-4.3" />
2781
+ <path d="m13 9-4 4" />
2782
+ <path d="m9 9 4 4" />
2783
+ </svg>
2784
+ <div class="cat-status__content">
2785
+ <div class="cat-status__title">{{ resolvedMessages.noProductsFound }}</div>
2786
+ @if (meta?.queryInterpretation) {
2787
+ <div class="cat-status__hint">
2788
+ {{ resolvedMessages.searchedFor }} {{ meta?.queryInterpretation }}
2789
+ </div>
2790
+ }
2791
+ </div>
2792
+ </div>
2793
+ }
2794
+ </div>
2795
+ }
2796
+ </div>
2797
+ `,
2798
+ encapsulation: import_core5.ViewEncapsulation.None
2799
+ })
2800
+ ], CommerceAiSearchComponent);
2801
+ // Annotate the CommonJS export names for ESM import in node:
2802
+ 0 && (module.exports = {
2803
+ CommerceAiApiService,
2804
+ CommerceAiCartPanelComponent,
2805
+ CommerceAiCartService,
2806
+ CommerceAiMissionResultsComponent,
2807
+ CommerceAiSearchComponent,
2808
+ DEFAULT_COMMERCE_AI_SEARCH_MESSAGES,
2809
+ resolveCommerceAISearchMessages
2810
+ });
2811
+ //# sourceMappingURL=index.cjs.map