@wix/himalaya-cli 0.1580.0 → 0.1582.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 (43) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/app-config.d.ts +18 -3
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/builder.d.ts +3 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/network/src/index.d.ts +39 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/sdk-types/src/capabilities.generated.d.ts +2 -1
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/webview-page/src/capabilities.generated.d.ts +2 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/define-app.d.ts +1 -1
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +12 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/validator.d.ts +6 -0
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/audio-record.mjs +1 -0
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/calendar.mjs +1 -0
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/calls.mjs +1 -0
  12. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/contacts.mjs +1 -0
  13. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +1 -0
  14. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/location.mjs +1 -0
  15. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/motion.mjs +1 -0
  16. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/network.mjs +45 -0
  17. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/photos.mjs +1 -0
  18. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdk-types.mjs +1 -0
  19. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +1 -0
  20. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/tasks.mjs +1 -0
  21. package/dist/authoring-runtime/node_modules/@wix/himalaya/package.json +4 -0
  22. package/dist/cli.mjs +16481 -16739
  23. package/dist/preview-assets.generated.bin +0 -0
  24. package/guides/app-config.md +1 -1
  25. package/guides/component-reference.json +5 -3
  26. package/guides/component-reference.md +10 -2
  27. package/guides/designing-himi-screens.md +24 -0
  28. package/guides/importable-sdks.md +1 -0
  29. package/guides/sdk-reference.json +38 -1
  30. package/guides/tier-5-add-a-screen.md +32 -0
  31. package/guides/web-page-sdk-reference.md +1 -0
  32. package/package.json +2 -2
  33. package/templates/beauty-salon/dev/index.ts +197 -304
  34. package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +44 -13
  35. package/templates/branded-lite/dev/index.ts +197 -304
  36. package/templates/branded-lite/l10n/en.json +2 -2
  37. package/templates/branded-lite/tier5-src/himi-store-product/index.ts +49 -17
  38. package/templates/gym-fitness/dev/index.ts +197 -304
  39. package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +44 -13
  40. package/templates/portfolio-creative/dev/index.ts +197 -304
  41. package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +44 -13
  42. package/templates/retail-store/dev/index.ts +197 -304
  43. package/templates/retail-store/tier5-src/himi-store-product/index.ts +49 -17
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$source": "en",
3
- "$generated": "2026-11-05T13:12:33.176Z",
3
+ "$generated": "2026-11-05T19:49:25.752Z",
4
4
  "messages": {
5
5
  "h_042c862e4467": {
6
6
  "source": "Try again",
@@ -8,7 +8,7 @@
8
8
  "occurrences": [
9
9
  "himi_branded_home#payload.body.props.children[0].body.props.children[1].body.props.title",
10
10
  "himi_account_settings#payload.body.props.children[3].body.props.children[1].body.props.title",
11
- "himi_shop#payload.body.props.children[7].body.props.children[3].body.props.title",
11
+ "himi_shop#payload.body.props.children[6].body.props.children[3].body.props.title",
12
12
  "himi_saved_products#payload.body.props.children[3].body.props.children[2].body.props.title",
13
13
  "himi_collections#payload.body.props.children[4].body.props.ctaLabel",
14
14
  "himi_collection#payload.body.props.children[0].body.props.children[8].body.props.ctaLabel",
@@ -33,6 +33,7 @@ async function cartItemCount(): Promise<number> {
33
33
  }
34
34
 
35
35
  interface GalleryItem {
36
+ id: string;
36
37
  url: string;
37
38
  }
38
39
 
@@ -104,8 +105,8 @@ function slotsOf(groups: StoreOption[], selection: StoreOptionSelection, showErr
104
105
  out[`opt${n}Chips`] = (g?.choices ?? []).map((c) => ({ id: c.id, label: c.label, ...(g.kind === "color" && c.colorCode ? { swatch: c.colorCode } : {}), disabled: c.inStock === false }));
105
106
  out[`opt${n}Selected`] = chosen ? [chosen] : [];
106
107
  out[`opt${n}SelectedId`] = chosen;
107
- out[`opt${n}Placeholder`] = g?.choices.find(c => c.id === chosen)?.label ?? copyTemplate("product.optionPlaceholder", "Choose {option}", { option: displayName || "option" });
108
- const error = showErrors && g && !chosen ? copyTemplate("product.optionRequired", "Choose a {option}", { option: g.name.toLowerCase() }) : "";
108
+ out[`opt${n}Placeholder`] = g?.choices.find(c => c.id === chosen)?.label ?? copyTemplate("product.optionPlaceholder", "Select {option}", { option: displayName || "option" });
109
+ const error = showErrors && g && !chosen ? copyTemplate("product.optionRequired", "Select a {option}", { option: g.name.toLowerCase() }) : "";
109
110
  out[`opt${n}Error`] = error;
110
111
  out[`noOpt${n}Error`] = !error;
111
112
  out[`hasOpt${n}Error`] = !!error;
@@ -231,6 +232,8 @@ interface S {
231
232
  /** FULL media gallery (media.items[] with the main image first) — the detail
232
233
  * screen renders a Carousel over it when 2+ images exist. */
233
234
  gallery: GalleryItem[];
235
+ galleryIndex: number;
236
+ galleryLabel: string;
234
237
  hasGallery: boolean;
235
238
  noGallery: boolean;
236
239
  initial: string;
@@ -240,6 +243,7 @@ interface S {
240
243
  cartCount: number;
241
244
  /** An add-to-cart write is in flight (pending line under the CTA). */
242
245
  adding: boolean;
246
+ addLabel: string;
243
247
  notAdding: boolean;
244
248
  /** The last add succeeded (confirmation + View cart under the CTA). */
245
249
  added: boolean;
@@ -276,6 +280,11 @@ interface S {
276
280
  /** Add to cart is blocked: this product has options and they are not all chosen. */
277
281
  optionsIncomplete: boolean;
278
282
  showOptionErrors: boolean;
283
+ optionValidationRequest: number;
284
+ optionValidationTarget: string;
285
+ opt1ScrollRequest: string;
286
+ opt2ScrollRequest: string;
287
+ opt3ScrollRequest: string;
279
288
  addDisabled: boolean;
280
289
  /** The product was served from the offline cache, which carries no option groups.
281
290
  * "No groups" here means UNKNOWN, not "none" — so the CTA must stay blocked rather
@@ -323,7 +332,8 @@ export function productGallery(p: StoreProduct): GalleryItem[] {
323
332
  // image no matter what the read asked for. One reader, not a private copy per
324
333
  // worker — that is how the two layouts drifted apart in the first place.
325
334
  for (const url of sdkProductGallery(p, WIX_DETAIL_IMAGE)) push(url);
326
- return urls.map((url) => ({ url }));
335
+ for (const group of productOptions(p, WIX_DETAIL_IMAGE)) for (const choice of group.choices) push(choice.image);
336
+ return urls.map((url) => ({ id: url, url }));
327
337
  }
328
338
 
329
339
  /** The product's public page URL when the payload carries one ("" otherwise) —
@@ -361,6 +371,7 @@ function parseQuantity(raw: string): number | undefined {
361
371
 
362
372
  function derive(s: S): S {
363
373
  const empty = s.status === "ready" && !s.name;
374
+ const galleryIndex = Math.min(Math.max(0, Math.floor(Number(s.galleryIndex) || 0)), Math.max(0, s.gallery.length - 1));
364
375
  const hasGallery = s.gallery.length > 1;
365
376
  const hasSingleImage = !!s.image && !hasGallery;
366
377
  const groups = s.optionGroups;
@@ -368,6 +379,8 @@ function derive(s: S): S {
368
379
  const optionsIncomplete = s.optionsUnknown || !!outOfStockPick(groups, s.selection) || (groups.length > 0 && (overflow || !isSelectionComplete(groups, s.selection)));
369
380
  return {
370
381
  ...s,
382
+ galleryIndex,
383
+ galleryLabel: s.gallery.length ? copyTemplate("product.galleryPage", "Image {index} of {count}", { index: String(galleryIndex + 1), count: String(s.gallery.length) }) : "",
371
384
  notOnSale: !s.wasPrice,
372
385
  wasPriceEmpty: !!s.wasPrice,
373
386
  noSku: !s.skuLabel,
@@ -396,9 +409,12 @@ function derive(s: S): S {
396
409
  // indistinguishable from one with none, and adding it would post the variant-less
397
410
  // line this whole feature exists to prevent.
398
411
  optionsIncomplete,
399
- addDisabled: optionsIncomplete,
412
+ ...Object.fromEntries([1, 2, 3].map(n => [`opt${n}ScrollRequest`,
413
+ s.showOptionErrors && groups[n - 1]?.id === s.optionValidationTarget && !s.selection[groups[n - 1].id] ? String(s.optionValidationRequest || "") : ""])),
414
+ addDisabled: s.adding || s.optionsUnknown || overflow || !!outOfStockPick(groups, s.selection),
400
415
  optionsHint: groups.length ? optionsHint(groups, s.selection) : "",
401
416
  saveLabel: s.saved ? copyParam("savedProducts.removeLabel", "Remove from wishlist") : copyParam("savedProducts.addLabel", "Add to wishlist"),
417
+ addLabel: s.adding ? copyParam("product.adding", "Adding to cart…") : copyParam("product.addToCart", "Add to cart"),
402
418
  notAdding: !s.adding,
403
419
  notAdded: !s.added,
404
420
  isLoading: s.status === "loading",
@@ -433,7 +449,7 @@ const registerProduct = withIapPresentation<S>({
433
449
  register: withOnlineWrites(registerState, ["beginAdd", "addToCart", "buy"], { derive, messageFields: action => action === "buy" ? ["buyNote"] : ["cartHint"] }),
434
450
  adapters: SELLS_THROUGH_APPLE ? [storesProductOrders] : [],
435
451
  onCreate: commerce => { productPricing = commerce; }, prefix: "product",
436
- transientKeys: ["price", "optionsIncomplete", "cartHint", "cartHidden", "buyHidden", "buyLabel", "buyDisabled", "buyNote", "purchasedNote", "addedSheetPresented", "shareHint"], contentActions: ["load", "readChanged"], concurrentPublishActions: ["buy"],
452
+ transientKeys: ["optionValidationTarget", "optionValidationRequest", "opt1ScrollRequest", "opt2ScrollRequest", "opt3ScrollRequest", "galleryIndex", "price", "optionsIncomplete", "cartHint", "cartHidden", "buyHidden", "buyLabel", "buyDisabled", "buyNote", "purchasedNote", "addedSheetPresented", "shareHint"], contentActions: ["load", "readChanged"], concurrentPublishActions: ["buy"],
437
453
  items: state => inputId() ? [{ reference: { kind: "storesProduct", id: inputId() }, commerceDisplayPrice: state.price }] : [],
438
454
  present: (state, price, allowed) => {
439
455
  const purchase = applePurchase(state);
@@ -441,9 +457,10 @@ const registerProduct = withIapPresentation<S>({
441
457
  const blocked = lastProduct?.productType?.toLowerCase() === "digital" || (!state.optionsUnknown && !allowed(inputId()));
442
458
  const cartHint = state.cartHint === offlineWriteMessage ? state.cartHint
443
459
  : !purchase.appleRoute && blocked ? unavailable : state.cartHint === unavailable ? "" : state.cartHint;
460
+ const productState = derive(state);
444
461
  return {
445
- ...state, price: price(inputId()), ...(purchase.appleRoute ? { wasPrice: "", wasPriceEmpty: false, notOnSale: true } : {}), optionsIncomplete: derive(state).optionsIncomplete || !allowed(inputId()) || lastProduct?.productType?.toLowerCase() === "digital",
446
- cartHint, noCartHint: !cartHint || state.added, addDisabled: state.addDisabled || blocked,
462
+ ...state, price: price(inputId()), ...(purchase.appleRoute ? { wasPrice: "", wasPriceEmpty: false, notOnSale: true } : {}), optionsIncomplete: productState.optionsIncomplete || !allowed(inputId()) || lastProduct?.productType?.toLowerCase() === "digital",
463
+ cartHint, noCartHint: !cartHint || state.added, addDisabled: productState.addDisabled || blocked,
447
464
  cartHidden: state.status !== "ready" || !state.name || purchase.appleRoute,
448
465
  buyHidden: state.status !== "ready" || !state.name || !purchase.appleRoute || purchase.accessReady, buyLabel: purchase.buyLabel, buyDisabled: purchase.buyDisabled,
449
466
  buyNote: state.buyNote === offlineWriteMessage ? state.buyNote : purchase.buyNote,
@@ -497,6 +514,7 @@ const svc = onlineService<S, {
497
514
  toggleReturnPolicy: (s: S) => S;
498
515
  toggleShippingInfo: (s: S) => S;
499
516
  selectOption: (s: S, arg?: unknown) => S;
517
+ setGalleryIndex: (s: S, arg?: unknown) => S;
500
518
  addToCart: (s: S) => Promise<S>;
501
519
  beginAdd: (s: S, arg?: unknown) => S;
502
520
  setCartCount: (s: S, arg?: unknown) => S;
@@ -541,6 +559,8 @@ const svc = onlineService<S, {
541
559
  shippingInfoExpanded: false,
542
560
  image: "",
543
561
  gallery: [],
562
+ galleryIndex: 0,
563
+ galleryLabel: "",
544
564
  hasGallery: false,
545
565
  noGallery: true,
546
566
  initial: "",
@@ -548,6 +568,7 @@ const svc = onlineService<S, {
548
568
  cartHint: "",
549
569
  cartCount: 0,
550
570
  adding: false,
571
+ addLabel: "",
551
572
  notAdding: true,
552
573
  added: false,
553
574
  notAdded: true,
@@ -573,6 +594,9 @@ const svc = onlineService<S, {
573
594
  noOptionsOverflow: true,
574
595
  optionsIncomplete: false,
575
596
  showOptionErrors: false,
597
+ optionValidationRequest: 0,
598
+ optionValidationTarget: "",
599
+ opt1ScrollRequest: "", opt2ScrollRequest: "", opt3ScrollRequest: "",
576
600
  addDisabled: false,
577
601
  optionsUnknown: false,
578
602
  optionsHint: "",
@@ -600,7 +624,8 @@ const svc = onlineService<S, {
600
624
  let saved = false;
601
625
  try { saved = isProductSaved(id); } catch { /* save action reports a retryable error */ }
602
626
  s = { ...s, saved, saveHint: "",
603
- ...(id !== lastLoadedId ? { adding: false, added: false, shareHint: "", addedSheetPresented: false, quantity: 1, showOptionErrors: false, returnPolicyExpanded: false, shippingInfoExpanded: false } : {}) };
627
+ optionValidationTarget: "",
628
+ ...(id !== lastLoadedId ? { adding: false, added: false, shareHint: "", addedSheetPresented: false, quantity: 1, showOptionErrors: false, optionValidationRequest: 0, galleryIndex: 0, returnPolicyExpanded: false, shippingInfoExpanded: false } : {}) };
604
629
  // A fresh navigation into the product resets any quantity left over
605
630
  // from a previous visit to the same worker instance.
606
631
  if (!id) {
@@ -617,8 +642,8 @@ const svc = onlineService<S, {
617
642
  void cartCount.then((n) => { cartKnown = n; }, () => {});
618
643
  // The live product read goes out FIRST too — the local-db read below must never delay it.
619
644
  // Settled into a value so a failure is handled where it is awaited, never unhandled.
620
- // DESCRIPTION widens the default fieldset with the V3 Ricos description (a V1 catalog ignores it).
621
- const live = getStoreProduct(id, ["DESCRIPTION"], readOptions(arg)).then(
645
+ // Read both the Ricos description and choice-linked images; V3 omits these fields by default.
646
+ const live = getStoreProduct(id, ["DESCRIPTION", "PRODUCT_CHOICES_DISPLAY_IMAGE"], readOptions(arg)).then(
622
647
  (r) => ({ ok: true as const, r }),
623
648
  (error) => ({ ok: false as const, error }),
624
649
  );
@@ -646,11 +671,12 @@ const svc = onlineService<S, {
646
671
  if (outcome.ok) {
647
672
  const p = (outcome.r.product ?? (outcome.r as unknown as StoreProduct)) as StoreProduct;
648
673
  const name = cleanTitle(p.name) || "Product";
649
- const image = productImage(p, WIX_DETAIL_IMAGE);
674
+ const gallery = productGallery(p);
675
+ const image = productImage(p, WIX_DETAIL_IMAGE) || gallery[0]?.url || "";
650
676
  lastProduct = p;
651
677
  // A group with exactly one choice is chosen for the shopper; a real choice
652
678
  // is left blank so the pick is deliberate.
653
- const optionGroups = productOptions(p);
679
+ const optionGroups = productOptions(p, WIX_DETAIL_IMAGE);
654
680
  const sale = productSale(p);
655
681
  // A pooled screen re-appearing re-runs `load`. Wiping `selection` there means a
656
682
  // trip to the cart and back silently clears the size the shopper picked and
@@ -680,7 +706,7 @@ const svc = onlineService<S, {
680
706
  shippingInfoBody: productPolicyBody(p, "shipping") || copyParam("product.shippingInfoBody", "") || copyParam("product.policyUnavailable", "Policy details have not been provided by this store."),
681
707
  ...productBody(p),
682
708
  image,
683
- gallery: productGallery(p),
709
+ gallery,
684
710
  initial: (name[0] || "#").toUpperCase(),
685
711
  shareUrl: productShareUrl(p),
686
712
  cartCount: badge(),
@@ -700,6 +726,11 @@ const svc = onlineService<S, {
700
726
  // One chip row per option group. `group` is the descriptor slot ("1".."3");
701
727
  // FilterChips sends back the WHOLE selected set, which for a single-select row
702
728
  // is either one id or none (re-tapping the active chip clears it).
729
+ setGalleryIndex: (s, arg) => {
730
+ const raw = argValue(arg, "index") || argValue(arg, "value");
731
+ const index = raw.trim() ? Number(raw) : NaN;
732
+ return Number.isInteger(index) && index >= 0 && index < s.gallery.length ? derive({ ...s, galleryIndex: index }) : s;
733
+ },
703
734
  selectOption: (s, arg) => {
704
735
  const slot = Math.floor(Number(argValue(arg, "group")));
705
736
  const group = s.optionGroups[slot - 1];
@@ -714,7 +745,9 @@ const svc = onlineService<S, {
714
745
  if (choice) selection[group.id] = choice; else delete selection[group.id];
715
746
  // A new choice invalidates the last add's confirmation — it was for the
716
747
  // previous variant.
717
- return derive({ ...s, selection, added: false, addedSheetPresented: false, cartHint: "" });
748
+ const image = group.choices.find(c => c.id === choice)?.image;
749
+ const imageIndex = image ? s.gallery.findIndex(page => page.url === image) : -1;
750
+ return derive({ ...s, selection, optionValidationTarget: "", ...(imageIndex >= 0 ? { galleryIndex: imageIndex } : {}), added: false, addedSheetPresented: false, cartHint: "" });
718
751
  },
719
752
  // The pending half of addToCart, as its own reducer so it PUBLISHES before the
720
753
  // network write starts: the pending line shows and the top-bar badge ticks up in the same
@@ -743,12 +776,11 @@ const svc = onlineService<S, {
743
776
  if (!id) return derive({ ...s, cartHint: copyParam("product.cartHintNoProduct", "No product to add.") });
744
777
  // A second tap while the first write is in flight must not add the line twice.
745
778
  if (s.adding) return s;
746
- // The CTA is already disabled on this state; this is the second gate, because
747
- // a voice-control/deep-link dispatch reaches the action without the button.
779
+ // Missing choices show field errors; unavailable products still fail closed.
748
780
  if (s.optionsIncomplete) {
749
781
  // With no groups in hand there is nothing for `optionsHint` to name, so the
750
782
  // offline case needs its own reason rather than an empty string.
751
- return derive({ ...s, showOptionErrors: true, cartHint: s.optionsUnknown
783
+ return derive({ ...s, showOptionErrors: true, optionValidationTarget: s.optionGroups.find(g => !s.selection[g.id])?.id ?? "", optionValidationRequest: (s.optionValidationRequest || 0) + 1, cartHint: s.optionsUnknown
752
784
  ? copyParam("product.cartHintOffline", "Reconnect to choose options and add this to your cart.")
753
785
  : optionsHint(s.optionGroups, s.selection) || copyParam("product.cartHintNoVariant", "That combination is not available. Try another.") });
754
786
  }