@wix/himalaya-cli 0.1592.0 → 0.1594.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 (38) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/contacts/src/index.d.ts +52 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/sdk-types/src/capabilities.generated.d.ts +1 -1
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/audio-record.mjs +1 -1
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/calendar.mjs +1 -1
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/calls.mjs +1 -1
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/contacts.mjs +90 -1
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/location.mjs +1 -1
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/motion.mjs +1 -1
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/photos.mjs +1 -1
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdk-types.mjs +1 -1
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/tasks.mjs +1 -1
  12. package/dist/cli.mjs +47 -41
  13. package/dist/preview-assets.generated.bin +0 -0
  14. package/guides/one-app-to-himi-app.md +12 -0
  15. package/guides/sdk-reference.json +60 -0
  16. package/guides/web-page-sdk-reference.md +1 -1
  17. package/package.json +2 -2
  18. package/templates/beauty-salon/dev/index.ts +216 -8
  19. package/templates/beauty-salon/tier5-src/_shared/himi-store-facets.ts +26 -2
  20. package/templates/beauty-salon/tier5-src/himi-branded-lite-category/index.ts +3 -3
  21. package/templates/beauty-salon/tier5-src/himi-branded-lite-shop/index.ts +4 -4
  22. package/templates/branded-lite/dev/index.ts +216 -8
  23. package/templates/branded-lite/tier5-src/_shared/business-chat-transport.ts +9 -0
  24. package/templates/branded-lite/tier5-src/_shared/himi-store-facets.ts +26 -2
  25. package/templates/branded-lite/tier5-src/himi-branded-lite-category/index.ts +3 -3
  26. package/templates/branded-lite/tier5-src/himi-branded-lite-shop/index.ts +3 -3
  27. package/templates/gym-fitness/dev/index.ts +216 -8
  28. package/templates/gym-fitness/tier5-src/_shared/himi-store-facets.ts +26 -2
  29. package/templates/gym-fitness/tier5-src/himi-branded-lite-category/index.ts +3 -3
  30. package/templates/gym-fitness/tier5-src/himi-branded-lite-shop/index.ts +4 -4
  31. package/templates/portfolio-creative/dev/index.ts +216 -8
  32. package/templates/portfolio-creative/tier5-src/_shared/himi-store-facets.ts +26 -2
  33. package/templates/portfolio-creative/tier5-src/himi-branded-lite-category/index.ts +3 -3
  34. package/templates/portfolio-creative/tier5-src/himi-branded-lite-shop/index.ts +4 -4
  35. package/templates/retail-store/dev/index.ts +216 -8
  36. package/templates/retail-store/tier5-src/_shared/himi-store-facets.ts +26 -2
  37. package/templates/retail-store/tier5-src/himi-branded-lite-category/index.ts +3 -3
  38. package/templates/retail-store/tier5-src/himi-branded-lite-shop/index.ts +3 -3
@@ -1180,6 +1180,66 @@
1180
1180
  "jsdoc": "/** Search contacts by name (requires read permission). [] when denied/no match. */",
1181
1181
  "decl": "export async function search(query: string, limit?: number): Promise<Contact[]>;"
1182
1182
  },
1183
+ {
1184
+ "name": "ContactField",
1185
+ "kind": "interface",
1186
+ "jsdoc": "/** One labelled phone number or email address. `label` is free text (\"work\", \"mobile\"…);\n * each host maps the common ones to its native label and keeps the rest as a custom label. */",
1187
+ "decl": "export interface ContactField {\n label?: string;\n value: string;\n}"
1188
+ },
1189
+ {
1190
+ "name": "NewContact",
1191
+ "kind": "interface",
1192
+ "jsdoc": "/** A contact to create. At least one of `givenName`, `familyName` or `organization` must be\n * non-blank; every list holds at most {@link MAX_CONTACT_ENTRIES} entries. */",
1193
+ "decl": "export interface NewContact {\n givenName?: string;\n familyName?: string;\n organization?: string;\n phones?: ContactField[];\n emails?: ContactField[];\n urls?: string[];\n note?: string;\n}"
1194
+ },
1195
+ {
1196
+ "name": "MAX_CONTACT_ENTRIES",
1197
+ "kind": "const",
1198
+ "jsdoc": "/** The most entries `phones`, `emails` or `urls` may carry. */",
1199
+ "decl": "export const MAX_CONTACT_ENTRIES = 20;"
1200
+ },
1201
+ {
1202
+ "name": "ContactsErrorCode",
1203
+ "kind": "type",
1204
+ "jsdoc": "/** Why a create verb rejected. */",
1205
+ "decl": "export type ContactsErrorCode = \"invalid_contact\" | \"permission_denied\" | \"unavailable\";"
1206
+ },
1207
+ {
1208
+ "name": "ContactsError",
1209
+ "kind": "interface",
1210
+ "jsdoc": "/** A create-verb rejection: an `Error` named \"ContactsError\" carrying a stable `code`. (A plain\n * Error, not a subclass — Tier-5 bundles target an engine without class syntax.) Branch on\n * `isContactsError(e) && e.code === \"permission_denied\"`. */",
1211
+ "decl": "export interface ContactsError extends Error {\n name: \"ContactsError\";\n code: ContactsErrorCode;\n}"
1212
+ },
1213
+ {
1214
+ "name": "contactsError",
1215
+ "kind": "function",
1216
+ "jsdoc": "/** Build a coded contacts rejection. */",
1217
+ "decl": "export function contactsError(code: ContactsErrorCode, message?: string): ContactsError;"
1218
+ },
1219
+ {
1220
+ "name": "isContactsError",
1221
+ "kind": "function",
1222
+ "jsdoc": "/** Whether `e` is a coded contacts rejection. */",
1223
+ "decl": "export function isContactsError(e: unknown): e is ContactsError;"
1224
+ },
1225
+ {
1226
+ "name": "normalizeNewContact",
1227
+ "kind": "function",
1228
+ "jsdoc": "/** Validate and normalize a {@link NewContact}: strings trimmed, blank entries dropped, blank\n * fields omitted. Throws `invalid_contact` when there is no name or organization, or a list\n * holds more than {@link MAX_CONTACT_ENTRIES} entries. Every host re-checks the same rules. */",
1229
+ "decl": "export function normalizeNewContact(contact: NewContact): NewContact;"
1230
+ },
1231
+ {
1232
+ "name": "presentNewContact",
1233
+ "kind": "function",
1234
+ "jsdoc": "/** Open the OS new-contact sheet prefilled with `contact` — the recommended way to save a\n * contact: it needs NO contacts permission, because the user reviews and confirms the save.\n * Resolves `{ saved: true }` when the user saved it, `{ saved: false }` on cancel. Web\n * downloads a `.vcf` instead (and resolves `saved: true` once the download is handed off).\n * Rejects `invalid_contact` (see {@link normalizeNewContact}) or `unavailable`. */",
1235
+ "decl": "export async function presentNewContact(contact: NewContact): Promise<{ saved: boolean }>;"
1236
+ },
1237
+ {
1238
+ "name": "add",
1239
+ "kind": "function",
1240
+ "jsdoc": "/** Write `contact` straight into the address book and resolve its new `id`. Needs contacts\n * WRITE permission — ask with {@link requestPermission} first; without it this rejects\n * `permission_denied` (it never prompts). Rejects `invalid_contact`, or `unavailable` where\n * the host has no address book (web). Prefer {@link presentNewContact} unless the app already\n * holds the permission. */",
1241
+ "decl": "export async function add(contact: NewContact): Promise<{ id: string }>;"
1242
+ },
1183
1243
  {
1184
1244
  "name": "version",
1185
1245
  "kind": "const",
@@ -23,7 +23,7 @@ the SDK sources, so it matches the shipped SDKs.
23
23
  - [`calls`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fcalls?format=md) — Real-time 1:1 audio calls (Stage 1). 16 methods.
24
24
  - [`camera`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fcamera?format=md) — Camera capability. 4 methods · returns FileHandles.
25
25
  - [`clipboard`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fclipboard?format=md) — System clipboard. 5 methods.
26
- - [`contacts`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fcontacts?format=md) — Device address book. 4 methods.
26
+ - [`contacts`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fcontacts?format=md) — Device address book. 6 methods.
27
27
  - [`file_picker`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Ffile_picker?format=md) — User-mediated document access OUTSIDE the app sandbox. 3 methods · returns FileHandles.
28
28
  - [`files`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Ffiles?format=md) — App-scoped document storage. 9 methods · returns FileHandles.
29
29
  - [`godot`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fgodot?format=md) — A Godot game surface. 3 methods · not in Brandy.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1592.0",
3
+ "version": "0.1594.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -72,5 +72,5 @@
72
72
  "artifactId": "himalaya-cli"
73
73
  }
74
74
  },
75
- "falconPackageHash": "30ac16c3a57a3badd6a591d62de023f863dfbe7e0491475aba89dba9"
75
+ "falconPackageHash": "dd497985a86806681ab680998c9ccbacefeafa76497a4557b76146a4"
76
76
  }
@@ -8991,7 +8991,33 @@ const SCREENS = runtimeSetting({
8991
8991
  "top": 2,
8992
8992
  "bottom": 2
8993
8993
  },
8994
- "hiddenPath": "$item.notOnSale"
8994
+ "hiddenPath": "$item.saleTagHidden"
8995
+ }
8996
+ },
8997
+ {
8998
+ "id": "",
8999
+ "body": {
9000
+ "package": "himalaya",
9001
+ "type": "Text",
9002
+ "props": {
9003
+ "text": "$item.ribbon",
9004
+ "style": "caption",
9005
+ "color": "accentPrimary",
9006
+ "maxLines": 1
9007
+ }
9008
+ },
9009
+ "modifiers": {
9010
+ "background": {
9011
+ "token": "accentSurface"
9012
+ },
9013
+ "cornerRadius": 6,
9014
+ "padding": {
9015
+ "leading": 6,
9016
+ "trailing": 6,
9017
+ "top": 2,
9018
+ "bottom": 2
9019
+ },
9020
+ "hiddenPath": "$item.noRibbon"
8995
9021
  }
8996
9022
  },
8997
9023
  {
@@ -9434,7 +9460,33 @@ const SCREENS = runtimeSetting({
9434
9460
  "top": 2,
9435
9461
  "bottom": 2
9436
9462
  },
9437
- "hiddenPath": "$item.notOnSale"
9463
+ "hiddenPath": "$item.saleTagHidden"
9464
+ }
9465
+ },
9466
+ {
9467
+ "id": "",
9468
+ "body": {
9469
+ "package": "himalaya",
9470
+ "type": "Text",
9471
+ "props": {
9472
+ "text": "$item.ribbon",
9473
+ "style": "caption",
9474
+ "color": "accentPrimary",
9475
+ "maxLines": 1
9476
+ }
9477
+ },
9478
+ "modifiers": {
9479
+ "background": {
9480
+ "token": "accentSurface"
9481
+ },
9482
+ "cornerRadius": 6,
9483
+ "padding": {
9484
+ "leading": 6,
9485
+ "trailing": 6,
9486
+ "top": 2,
9487
+ "bottom": 2
9488
+ },
9489
+ "hiddenPath": "$item.noRibbon"
9438
9490
  }
9439
9491
  },
9440
9492
  {
@@ -9909,7 +9961,33 @@ const SCREENS = runtimeSetting({
9909
9961
  "top": 2,
9910
9962
  "bottom": 2
9911
9963
  },
9912
- "hiddenPath": "$item.notOnSale"
9964
+ "hiddenPath": "$item.saleTagHidden"
9965
+ }
9966
+ },
9967
+ {
9968
+ "id": "",
9969
+ "body": {
9970
+ "package": "himalaya",
9971
+ "type": "Text",
9972
+ "props": {
9973
+ "text": "$item.ribbon",
9974
+ "style": "caption",
9975
+ "color": "accentPrimary",
9976
+ "maxLines": 1
9977
+ }
9978
+ },
9979
+ "modifiers": {
9980
+ "background": {
9981
+ "token": "accentSurface"
9982
+ },
9983
+ "cornerRadius": 6,
9984
+ "padding": {
9985
+ "leading": 6,
9986
+ "trailing": 6,
9987
+ "top": 2,
9988
+ "bottom": 2
9989
+ },
9990
+ "hiddenPath": "$item.noRibbon"
9913
9991
  }
9914
9992
  },
9915
9993
  {
@@ -10360,7 +10438,33 @@ const SCREENS = runtimeSetting({
10360
10438
  "top": 2,
10361
10439
  "bottom": 2
10362
10440
  },
10363
- "hiddenPath": "$item.notOnSale"
10441
+ "hiddenPath": "$item.saleTagHidden"
10442
+ }
10443
+ },
10444
+ {
10445
+ "id": "",
10446
+ "body": {
10447
+ "package": "himalaya",
10448
+ "type": "Text",
10449
+ "props": {
10450
+ "text": "$item.ribbon",
10451
+ "style": "caption",
10452
+ "color": "accentPrimary",
10453
+ "maxLines": 1
10454
+ }
10455
+ },
10456
+ "modifiers": {
10457
+ "background": {
10458
+ "token": "accentSurface"
10459
+ },
10460
+ "cornerRadius": 6,
10461
+ "padding": {
10462
+ "leading": 6,
10463
+ "trailing": 6,
10464
+ "top": 2,
10465
+ "bottom": 2
10466
+ },
10467
+ "hiddenPath": "$item.noRibbon"
10364
10468
  }
10365
10469
  },
10366
10470
  {
@@ -12990,7 +13094,33 @@ const SCREENS = runtimeSetting({
12990
13094
  "top": 2,
12991
13095
  "bottom": 2
12992
13096
  },
12993
- "hiddenPath": "$item.notOnSale"
13097
+ "hiddenPath": "$item.saleTagHidden"
13098
+ }
13099
+ },
13100
+ {
13101
+ "id": "",
13102
+ "body": {
13103
+ "package": "himalaya",
13104
+ "type": "Text",
13105
+ "props": {
13106
+ "text": "$item.ribbon",
13107
+ "style": "caption",
13108
+ "color": "accentPrimary",
13109
+ "maxLines": 1
13110
+ }
13111
+ },
13112
+ "modifiers": {
13113
+ "background": {
13114
+ "token": "accentSurface"
13115
+ },
13116
+ "cornerRadius": 6,
13117
+ "padding": {
13118
+ "leading": 6,
13119
+ "trailing": 6,
13120
+ "top": 2,
13121
+ "bottom": 2
13122
+ },
13123
+ "hiddenPath": "$item.noRibbon"
12994
13124
  }
12995
13125
  },
12996
13126
  {
@@ -13446,7 +13576,33 @@ const SCREENS = runtimeSetting({
13446
13576
  "top": 2,
13447
13577
  "bottom": 2
13448
13578
  },
13449
- "hiddenPath": "$item.notOnSale"
13579
+ "hiddenPath": "$item.saleTagHidden"
13580
+ }
13581
+ },
13582
+ {
13583
+ "id": "",
13584
+ "body": {
13585
+ "package": "himalaya",
13586
+ "type": "Text",
13587
+ "props": {
13588
+ "text": "$item.ribbon",
13589
+ "style": "caption",
13590
+ "color": "accentPrimary",
13591
+ "maxLines": 1
13592
+ }
13593
+ },
13594
+ "modifiers": {
13595
+ "background": {
13596
+ "token": "accentSurface"
13597
+ },
13598
+ "cornerRadius": 6,
13599
+ "padding": {
13600
+ "leading": 6,
13601
+ "trailing": 6,
13602
+ "top": 2,
13603
+ "bottom": 2
13604
+ },
13605
+ "hiddenPath": "$item.noRibbon"
13450
13606
  }
13451
13607
  },
13452
13608
  {
@@ -13934,7 +14090,33 @@ const SCREENS = runtimeSetting({
13934
14090
  "top": 2,
13935
14091
  "bottom": 2
13936
14092
  },
13937
- "hiddenPath": "$item.notOnSale"
14093
+ "hiddenPath": "$item.saleTagHidden"
14094
+ }
14095
+ },
14096
+ {
14097
+ "id": "",
14098
+ "body": {
14099
+ "package": "himalaya",
14100
+ "type": "Text",
14101
+ "props": {
14102
+ "text": "$item.ribbon",
14103
+ "style": "caption",
14104
+ "color": "accentPrimary",
14105
+ "maxLines": 1
14106
+ }
14107
+ },
14108
+ "modifiers": {
14109
+ "background": {
14110
+ "token": "accentSurface"
14111
+ },
14112
+ "cornerRadius": 6,
14113
+ "padding": {
14114
+ "leading": 6,
14115
+ "trailing": 6,
14116
+ "top": 2,
14117
+ "bottom": 2
14118
+ },
14119
+ "hiddenPath": "$item.noRibbon"
13938
14120
  }
13939
14121
  },
13940
14122
  {
@@ -14398,7 +14580,33 @@ const SCREENS = runtimeSetting({
14398
14580
  "top": 2,
14399
14581
  "bottom": 2
14400
14582
  },
14401
- "hiddenPath": "$item.notOnSale"
14583
+ "hiddenPath": "$item.saleTagHidden"
14584
+ }
14585
+ },
14586
+ {
14587
+ "id": "",
14588
+ "body": {
14589
+ "package": "himalaya",
14590
+ "type": "Text",
14591
+ "props": {
14592
+ "text": "$item.ribbon",
14593
+ "style": "caption",
14594
+ "color": "accentPrimary",
14595
+ "maxLines": 1
14596
+ }
14597
+ },
14598
+ "modifiers": {
14599
+ "background": {
14600
+ "token": "accentSurface"
14601
+ },
14602
+ "cornerRadius": 6,
14603
+ "padding": {
14604
+ "leading": 6,
14605
+ "trailing": 6,
14606
+ "top": 2,
14607
+ "bottom": 2
14608
+ },
14609
+ "hiddenPath": "$item.noRibbon"
14402
14610
  }
14403
14611
  },
14404
14612
  {
@@ -58,8 +58,14 @@ export interface ProductCard {
58
58
  swatch1: string; swatch2: string; swatch3: string; swatch4: string; swatch5: string;
59
59
  swatch1Hidden: boolean; swatch2Hidden: boolean; swatch3Hidden: boolean; swatch4Hidden: boolean; swatch5Hidden: boolean;
60
60
  noSwatches: boolean;
61
- /** Hides the Sale tag / swatch row when it has nothing to show. */
61
+ /** Hides the tag row (Sale tag, ribbon, swatches) when it has nothing to show. */
62
62
  noTagRow: boolean;
63
+ /** The merchant's ribbon text ("New", "Best Seller"…), "" when the product has none. The ribbon replaces
64
+ * the price-derived Sale tag, so a product never carries two tags. */
65
+ ribbon: string;
66
+ noRibbon: boolean;
67
+ /** Hides the price-derived Sale tag: the product is not on sale, or its ribbon takes the tag's place. */
68
+ saleTagHidden: boolean;
63
69
  /** The Wix price before any App Store presentation rewrites `price`. */
64
70
  commercePrice: string;
65
71
  /** Original Wix sale metadata retained when App Store presentation hides it. */
@@ -179,6 +185,21 @@ function swatchFields(hexes: string[], onSale: boolean) {
179
185
  };
180
186
  }
181
187
 
188
+ /** The ribbon the merchant set, whichever catalog answered: V3 `{ id, name }`, V1 a plain string. Read through a
189
+ * local type, not a `StoreProduct` member: other workers already extend that type with their own `ribbon`. */
190
+ export function ribbonText(p: StoreProduct): string {
191
+ const ribbon = (p as { ribbon?: unknown }).ribbon;
192
+ const name = typeof ribbon === "string" ? ribbon : typeof (ribbon as { name?: unknown } | null | undefined)?.name === "string" ? (ribbon as { name: string }).name : "";
193
+ return name.trim();
194
+ }
195
+
196
+ /** What the tag row (Sale tag, ribbon, swatches) shows. One rule for every screen that rebuilds it, including
197
+ * after the App Store presentation changes `onSale`: the ribbon wins over the Sale tag, so a product never
198
+ * carries two, and the row hides when it has nothing to show. */
199
+ export function tagFields(onSale: boolean, noSwatches: boolean, ribbon: string | undefined) {
200
+ return { ribbon: ribbon ?? "", noRibbon: !ribbon, saleTagHidden: !onSale || !!ribbon, noTagRow: !onSale && noSwatches && !ribbon };
201
+ }
202
+
182
203
  export function productCard(p: StoreProduct, saved: Set<string> = savedIds()): ProductCard {
183
204
  const { now, was } = prices(p);
184
205
  const groups = productOptions(p);
@@ -187,11 +208,14 @@ export function productCard(p: StoreProduct, saved: Set<string> = savedIds()): P
187
208
  const image = productImage(p);
188
209
  const id = p.id ?? "";
189
210
  const onSale = !!was;
211
+ const ribbon = ribbonText(p);
212
+ const swatches = swatchFields((color?.choices ?? []).map((c) => c.colorCode ?? ""), onSale);
190
213
  return {
191
214
  id, name, image, hasImage: !!image, noImage: !image, initial: initials(name),
192
215
  price: now, wasPrice: was, onSale, notOnSale: !onSale,
193
216
  saved: saved.has(id), notSaved: !saved.has(id),
194
- ...swatchFields((color?.choices ?? []).map((c) => c.colorCode ?? ""), onSale),
217
+ ...swatches,
218
+ ...tagFields(onSale, swatches.noSwatches, ribbon),
195
219
  commercePrice: now,
196
220
  detailScreenId: "himi_product",
197
221
  };
@@ -7,7 +7,7 @@ import { searchStoreProducts, type StoreProduct } from "../_shared/himi-store.js
7
7
  import { createCollectionQuery } from "../_shared/collection-query.js";
8
8
  import { detailInputId, presence, presenceAfterError, type PresenceFlags } from "../_shared/detail-presence.js";
9
9
  import { stashCopy, copyParam, copyTemplate } from "../_shared/himi-copy.js";
10
- import { FACETS_BOOT, PAGE_SIZE, applyFacets, currencyOf, facetActions, idOf, readViewPrefs, toRows, toggleSaved, type FacetState } from "../_shared/himi-store-facets.js";
10
+ import { FACETS_BOOT, PAGE_SIZE, applyFacets, currencyOf, facetActions, idOf, readViewPrefs, tagFields, toRows, toggleSaved, type FacetState } from "../_shared/himi-store-facets.js";
11
11
  import { withIapPresentation } from "../_shared/iap-presentation.js";
12
12
 
13
13
  interface State extends FacetState, PresenceFlags {
@@ -187,8 +187,8 @@ const register = withIapPresentation<State>({
187
187
  const onSale = stored.commerceOnSale ?? row.onSale;
188
188
  return { ...row, price: price(row.id, row.commercePrice) || row.price,
189
189
  commerceWasPrice: wasPrice, commerceOnSale: onSale,
190
- ...(apple(row.id) ? { wasPrice: "", onSale: false, notOnSale: true, noTagRow: row.noSwatches }
191
- : { wasPrice, onSale, notOnSale: !onSale, noTagRow: !onSale && row.noSwatches }),
190
+ ...(apple(row.id) ? { wasPrice: "", onSale: false, notOnSale: true, ...tagFields(false, row.noSwatches, row.ribbon) }
191
+ : { wasPrice, onSale, notOnSale: !onSale, ...tagFields(onSale, row.noSwatches, row.ribbon) }),
192
192
  };
193
193
  }) }),
194
194
  });
@@ -16,7 +16,7 @@ import { withIapPresentation } from "../_shared/iap-presentation.js";
16
16
  import { stashCopy, copyParam, copyTemplate } from "../_shared/himi-copy.js";
17
17
  import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
18
18
  import {
19
- FACETS_BOOT, PAGE_SIZE, applyFacets, countText, currencyOf, facetActions, idOf, option, readViewPrefs, selected, toRows, toggleSaved,
19
+ FACETS_BOOT, PAGE_SIZE, applyFacets, countText, currencyOf, facetActions, idOf, option, readViewPrefs, selected, tagFields, toRows, toggleSaved,
20
20
  type FacetState, type Option, type ProductCard, type Row,
21
21
  } from "../_shared/himi-store-facets.js";
22
22
 
@@ -187,7 +187,7 @@ function cachedCard(value: unknown): CachedCard | undefined {
187
187
  price, wasPrice: "", onSale: false, notOnSale: true, saved: false, notSaved: true,
188
188
  swatch1: "", swatch2: "", swatch3: "", swatch4: "", swatch5: "",
189
189
  swatch1Hidden: true, swatch2Hidden: true, swatch3Hidden: true, swatch4Hidden: true, swatch5Hidden: true,
190
- noSwatches: true, noTagRow: true, commercePrice: raw.commercePrice ?? price,
190
+ noSwatches: true, ...tagFields(false, true, ""), commercePrice: raw.commercePrice ?? price,
191
191
  detailScreenId: raw.detailScreenId || "himi_product", legacyCache: true,
192
192
  };
193
193
  }
@@ -616,7 +616,7 @@ const register = withIapPresentation<S>({
616
616
  wasPrice: "",
617
617
  onSale: false,
618
618
  notOnSale: true,
619
- noTagRow: row.noSwatches,
619
+ ...tagFields(false, row.noSwatches, row.ribbon),
620
620
  };
621
621
  return {
622
622
  ...row,
@@ -624,7 +624,7 @@ const register = withIapPresentation<S>({
624
624
  wasPrice,
625
625
  onSale,
626
626
  notOnSale: !onSale,
627
- noTagRow: !onSale && row.noSwatches,
627
+ ...tagFields(onSale, row.noSwatches, row.ribbon),
628
628
  };
629
629
  }) }),
630
630
  });