@wix/himalaya-cli 0.1145.0 → 0.1147.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 (100) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/comments-v1/src/extensions.d.ts +4 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/forum/src/extensions.d.ts +10 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-comments.mjs +5 -2
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-forum.mjs +13 -0
  5. package/dist/cli.mjs +75 -19
  6. package/dist/preview-assets.generated.bin +0 -0
  7. package/guides/headless-loop.md +34 -0
  8. package/guides/himi-mcp.md +2 -0
  9. package/guides/sdk-reference.json +13 -1
  10. package/guides/tier-5-add-a-screen.md +4 -2
  11. package/package.json +2 -2
  12. package/templates/beauty-salon/dev/index.ts +62 -3
  13. package/templates/beauty-salon/tier5-src/_shared/rich-body.ts +10 -4
  14. package/templates/beauty-salon/tier5-src/himi-blog-post/index.ts +12 -2
  15. package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +6 -1
  16. package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +35 -4
  17. package/templates/beauty-salon-owner/tier5-src/_shared/rich-body.ts +10 -4
  18. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-home/index.ts +6 -1
  19. package/templates/branded-lite/dev/index.ts +273 -12
  20. package/templates/branded-lite/tier5-src/_shared/rich-body.ts +10 -4
  21. package/templates/branded-lite/tier5-src/himi-blog-post/index.ts +12 -2
  22. package/templates/branded-lite/tier5-src/himi-branded-lite-cms-collection/index.ts +43 -3
  23. package/templates/branded-lite/tier5-src/himi-branded-lite-faq/index.ts +69 -13
  24. package/templates/branded-lite/tier5-src/himi-branded-lite-feed/index.ts +23 -3
  25. package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +6 -1
  26. package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +40 -3
  27. package/templates/branded-lite/tier5-src/himi-store-product/index.ts +35 -4
  28. package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +29 -5
  29. package/templates/community-club/dev/index.ts +135 -5
  30. package/templates/community-club/tier5-src/_shared/rich-body.ts +10 -4
  31. package/templates/community-club/tier5-src/himi-blog-post/index.ts +12 -2
  32. package/templates/community-club/tier5-src/himi-branded-lite-cms-collection/index.ts +43 -3
  33. package/templates/community-club/tier5-src/himi-branded-lite-feed/index.ts +23 -3
  34. package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +6 -1
  35. package/templates/community-club/tier5-src/himi-events-detail/index.ts +40 -3
  36. package/templates/community-club-owner/tier5-src/_shared/rich-body.ts +10 -4
  37. package/templates/community-club-owner/tier5-src/himi-owner-lite-home/index.ts +6 -1
  38. package/templates/events-meetups/dev/index.ts +91 -4
  39. package/templates/events-meetups/tier5-src/_shared/rich-body.ts +10 -4
  40. package/templates/events-meetups/tier5-src/himi-blog-post/index.ts +12 -2
  41. package/templates/events-meetups/tier5-src/himi-branded-lite-feed/index.ts +23 -3
  42. package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +6 -1
  43. package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +40 -3
  44. package/templates/events-meetups-owner/tier5-src/_shared/rich-body.ts +10 -4
  45. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-home/index.ts +6 -1
  46. package/templates/gym-fitness/dev/index.ts +92 -4
  47. package/templates/gym-fitness/tier5-src/_shared/rich-body.ts +10 -4
  48. package/templates/gym-fitness/tier5-src/himi-blog-post/index.ts +12 -2
  49. package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +6 -1
  50. package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +40 -3
  51. package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +35 -4
  52. package/templates/gym-fitness-owner/tier5-src/_shared/rich-body.ts +10 -4
  53. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-home/index.ts +6 -1
  54. package/templates/nonprofit-cause/dev/index.ts +91 -4
  55. package/templates/nonprofit-cause/tier5-src/_shared/rich-body.ts +10 -4
  56. package/templates/nonprofit-cause/tier5-src/himi-blog-post/index.ts +12 -2
  57. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-feed/index.ts +23 -3
  58. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +6 -1
  59. package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +40 -3
  60. package/templates/nonprofit-cause-owner/tier5-src/_shared/rich-body.ts +10 -4
  61. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-home/index.ts +6 -1
  62. package/templates/owner-lite/tier5-src/_shared/rich-body.ts +10 -4
  63. package/templates/owner-lite/tier5-src/himi-owner-lite-home/index.ts +6 -1
  64. package/templates/podcast-creator/dev/index.ts +91 -4
  65. package/templates/podcast-creator/tier5-src/_shared/rich-body.ts +10 -4
  66. package/templates/podcast-creator/tier5-src/himi-blog-post/index.ts +12 -2
  67. package/templates/podcast-creator/tier5-src/himi-branded-lite-feed/index.ts +23 -3
  68. package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +6 -1
  69. package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +40 -3
  70. package/templates/podcast-creator-owner/tier5-src/_shared/rich-body.ts +10 -4
  71. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-home/index.ts +6 -1
  72. package/templates/portfolio-creative/dev/index.ts +92 -4
  73. package/templates/portfolio-creative/tier5-src/_shared/rich-body.ts +10 -4
  74. package/templates/portfolio-creative/tier5-src/himi-blog-post/index.ts +12 -2
  75. package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +6 -1
  76. package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +40 -3
  77. package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +35 -4
  78. package/templates/portfolio-creative-owner/tier5-src/_shared/rich-body.ts +10 -4
  79. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-home/index.ts +6 -1
  80. package/templates/restaurant/dev/index.ts +60 -2
  81. package/templates/restaurant/tier5-src/_shared/rich-body.ts +10 -4
  82. package/templates/restaurant/tier5-src/himi-blog-post/index.ts +12 -2
  83. package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +6 -1
  84. package/templates/restaurant/tier5-src/himi-events-detail/index.ts +40 -3
  85. package/templates/restaurant-owner/tier5-src/_shared/rich-body.ts +10 -4
  86. package/templates/restaurant-owner/tier5-src/himi-owner-lite-home/index.ts +6 -1
  87. package/templates/retail-store/dev/index.ts +62 -3
  88. package/templates/retail-store/tier5-src/_shared/rich-body.ts +10 -4
  89. package/templates/retail-store/tier5-src/himi-blog-post/index.ts +12 -2
  90. package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +6 -1
  91. package/templates/retail-store/tier5-src/himi-store-product/index.ts +35 -4
  92. package/templates/retail-store-owner/tier5-src/_shared/rich-body.ts +10 -4
  93. package/templates/retail-store-owner/tier5-src/himi-owner-lite-home/index.ts +6 -1
  94. package/templates/yoga-studio/dev/index.ts +60 -2
  95. package/templates/yoga-studio/tier5-src/_shared/rich-body.ts +10 -4
  96. package/templates/yoga-studio/tier5-src/himi-blog-post/index.ts +12 -2
  97. package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +6 -1
  98. package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +40 -3
  99. package/templates/yoga-studio-owner/tier5-src/_shared/rich-body.ts +10 -4
  100. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-home/index.ts +6 -1
@@ -10,6 +10,8 @@ import { cleanTitle } from "../_shared/himi-text.js";
10
10
  import { stashCopy, copyParam, copyTemplate } from "../_shared/himi-copy.js";
11
11
  import { isBridgeUnavailable } from "../_shared/owner-load-error.js";
12
12
  import { isProductSaved, toggleSavedProduct } from "../_shared/himi-store-saved.js";
13
+ import { openRichLink } from "../_shared/rich-content-link.js";
14
+ import { richBody } from "../_shared/rich-body.js";
13
15
 
14
16
  // Offline-first (data-offline): the detail is a public READ-THROUGH — when the live
15
17
  // product fetch fails (offline) fall back to the last-synced row the catalog cached
@@ -120,11 +122,27 @@ function optionsHint(groups: StoreOption[], selection: StoreOptionSelection): st
120
122
  return copyTemplate("product.optionsChoose", "Choose {options}", { options: missing.join(", ") });
121
123
  }
122
124
 
125
+ /** The description pair. V3's `description` is a Ricos OBJECT (the HTML `plainText` would throw on it): it
126
+ * becomes the RichContent document, and `plainDescription` (or the document's own text) the plain copy. A V1
127
+ * `description` is an HTML string: stripped text, no document. */
128
+ function productBody(p: StoreProduct): { description: string; descriptionJson: string } {
129
+ const raw = (p as { description?: unknown }).description;
130
+ const html = plainText(p.plainDescription || (typeof raw === "string" ? raw : ""));
131
+ const { bodyJson, bodyText } = richBody(typeof raw === "string" ? null : raw, html);
132
+ return { description: html || bodyText, descriptionJson: bodyJson };
133
+ }
134
+
123
135
  interface S {
124
136
  status: "loading" | "ready" | "error";
125
137
  name: string;
126
138
  price: string;
139
+ /** The plain-text description: RichContent's fallback, and what shows with no document. */
127
140
  description: string;
141
+ /** [rico] #3372 — a V3 product's Ricos `description` (read with `fields=DESCRIPTION`) as ONE document string
142
+ * for RichContent; "" for a V1 HTML description, none, or a document the encoder rejected. */
143
+ descriptionJson: string;
144
+ /** descriptionJson !== "" — gates the plain text, because RichContent draws nothing for "". */
145
+ hasDescriptionJson: boolean;
128
146
  image: string;
129
147
  /** FULL media gallery (media.items[] with the main image first) — the detail
130
148
  * screen renders a Carousel over it when 2+ images exist. */
@@ -253,6 +271,7 @@ function derive(s: S): S {
253
271
  const overflow = groups.length > OPTION_SLOTS;
254
272
  return {
255
273
  ...s,
274
+ hasDescriptionJson: s.descriptionJson !== "",
256
275
  ...slotsOf(groups, s.selection),
257
276
  hasOptions: groups.length > 0,
258
277
  noOptions: groups.length === 0,
@@ -296,12 +315,16 @@ const svc = defineService<S, {
296
315
  setCartCount: (s: S, arg?: unknown) => S;
297
316
  share: (s: S) => Promise<S>;
298
317
  toggleSave: (s: S) => S;
318
+ /** [rico] #3372 — RichContent's on_link: `{ url }` of the tapped link. */
319
+ openLink: (s: S, arg?: unknown) => Promise<S>;
299
320
  }>("himiStoreProduct", {
300
321
  state: derive({
301
322
  status: "loading",
302
323
  name: "",
303
324
  price: "",
304
325
  description: "",
326
+ descriptionJson: "",
327
+ hasDescriptionJson: false,
305
328
  image: "",
306
329
  gallery: [],
307
330
  hasGallery: false,
@@ -354,7 +377,7 @@ const svc = defineService<S, {
354
377
  if (!id) {
355
378
  lastProduct = undefined;
356
379
  lastLoadedId = undefined;
357
- return derive({ ...s, status: "ready", quantity: 1, optionGroups: [], selection: {}, optionsUnknown: false, emptyText: copyParam("product.notFoundText", "Product not found.") });
380
+ return derive({ ...s, status: "ready", quantity: 1, optionGroups: [], selection: {}, optionsUnknown: false, descriptionJson: "", emptyText: copyParam("product.notFoundText", "Product not found.") });
358
381
  }
359
382
  // The cart count only feeds the top-bar badge: ask for it NOW, beside the product, instead
360
383
  // of after it — it used to add a whole cart round trip before the page could paint.
@@ -365,7 +388,8 @@ const svc = defineService<S, {
365
388
  void cartCount.then((n) => { cartKnown = n; }, () => {});
366
389
  // The live product read goes out FIRST too — the local-db read below must never delay it.
367
390
  // Settled into a value so a failure is handled where it is awaited, never unhandled.
368
- const live = getStoreProduct(id).then(
391
+ // DESCRIPTION widens the default fieldset with the V3 Ricos description (a V1 catalog ignores it).
392
+ const live = getStoreProduct(id, ["DESCRIPTION"]).then(
369
393
  (r) => ({ ok: true as const, r }),
370
394
  () => ({ ok: false as const }),
371
395
  );
@@ -424,7 +448,7 @@ const svc = defineService<S, {
424
448
  optionsUnknown: false,
425
449
  name,
426
450
  price: productPrice(p),
427
- description: plainText(p.plainDescription || p.description),
451
+ ...productBody(p),
428
452
  image,
429
453
  gallery: productGallery(p),
430
454
  initial: (name[0] || "#").toUpperCase(),
@@ -455,6 +479,7 @@ const svc = defineService<S, {
455
479
  name,
456
480
  price: String(synced.subtitle ?? ""),
457
481
  description: "",
482
+ descriptionJson: "",
458
483
  image,
459
484
  initial: String(synced.initial ?? "") || (name[0] || "#").toUpperCase(),
460
485
  cartHint: copyParam("product.cartHintOffline", "Reconnect to choose options and add this to your cart."),
@@ -464,7 +489,7 @@ const svc = defineService<S, {
464
489
  }
465
490
  lastProduct = undefined;
466
491
  lastLoadedId = undefined;
467
- return derive({ ...s, status: "error", optionGroups: [], selection: {}, optionsUnknown: false, emptyText: copyParam("product.errorText", "Product could not load.") });
492
+ return derive({ ...s, status: "error", optionGroups: [], selection: {}, optionsUnknown: false, descriptionJson: "", emptyText: copyParam("product.errorText", "Product could not load.") });
468
493
  },
469
494
  // The stepper fires onChange with { value } on every tap; clamp 1–99 and
470
495
  // ignore unparsable/garbage args instead of silently zeroing the quantity.
@@ -519,6 +544,7 @@ const svc = defineService<S, {
519
544
  name,
520
545
  price: String(row.subtitle ?? ""),
521
546
  description: "",
547
+ descriptionJson: "",
522
548
  image: String(row.image ?? ""),
523
549
  gallery: [],
524
550
  initial: String(row.initial ?? "") || (name[0] || "#").toUpperCase(),
@@ -605,6 +631,11 @@ const svc = defineService<S, {
605
631
  return derive({ ...s, added: false, cartHint: copyParam("product.shareUnavailable", "Sharing is not available on this device.") });
606
632
  }
607
633
  },
634
+ // Only http(s), mailto and tel links reach open_url; a refused link, or one nothing opened, says so in the
635
+ // hint line (`added: false`, as for share, so it never renders inside the add-to-cart confirmation).
636
+ openLink: async (s, arg) => ((await openRichLink(arg))
637
+ ? s
638
+ : derive({ ...s, added: false, cartHint: copyParam("product.linkOpenFailed", "This link could not be opened.") })),
608
639
  },
609
640
  });
610
641
 
@@ -14,15 +14,21 @@ export interface RichBody {
14
14
  bodyText: string;
15
15
  }
16
16
 
17
+ /** [rico] #3372 — the media width for a document drawn in a LIST row (a feed post, a comment, a CMS item): the
18
+ * list-image size (WIX_LIST_IMAGE), so a scrolling list never downloads every image at detail size. */
19
+ export const ROW_IMAGE_WIDTH = 1000;
20
+
17
21
  /** Prepares `richContent` (a Ricos document, as a Wix API returns it, or its JSON string). No document, or one
18
22
  * that is not a document, gives `{ bodyJson: "", bodyText: fallback }`. A document past the viewer's limits
19
23
  * gives bodyJson "" and its plain text — except one rejected as too deep, which is not walked again (the
20
- * plain-text walk has no depth limit), so `fallback` stands in for it. */
21
- export function richBody(richContent: unknown, fallback = ""): RichBody {
24
+ * plain-text walk has no depth limit), so `fallback` stands in for it. `imageWidth` sizes its media (default
25
+ * the 1400 px detail budget). */
26
+ export function richBody(richContent: unknown, fallback = "", imageWidth = 1400): RichBody {
22
27
  const doc = parseRicosJson(richContent);
23
28
  if (!doc) return { bodyJson: "", bodyText: fallback };
24
- // 1400 px — the detail-image budget, which is also the encoder's maximum.
25
- const { json, rejected } = encodeRichContent(doc, { imageWidth: 1400 });
29
+ // 1400 px by default — the detail-image budget, which is also the encoder's maximum; list rows pass
30
+ // ROW_IMAGE_WIDTH.
31
+ const { json, rejected } = encodeRichContent(doc, { imageWidth });
26
32
  const text = rejected === "too_deep" ? "" : ricosToPlainText(doc);
27
33
  return { bodyJson: json, bodyText: text || fallback };
28
34
  }
@@ -309,7 +309,12 @@ async function measuredRevenue7d(
309
309
  if (typeof measure.total === "number") return measure.total;
310
310
  const values = measure.values ?? [];
311
311
  return values.length ? values.reduce((sum, v) => sum + (v.value || 0), 0) : undefined;
312
- } catch {
312
+ } catch (e) {
313
+ // The fallback is silent on screen (Home shows the order-row sum), so say it where
314
+ // an agent looks: the DEV panel's Console tab and `himi logs`. The status only — a Wix
315
+ // failure's message carries part of the upstream body, which may hold personal data.
316
+ const status = (e as { status?: unknown } | null)?.status ?? /\bHTTP (\d{3})\b/.exec(String(e))?.[1];
317
+ console.warn("[home.revenue] analytics failed; showing the order-row sum", status !== undefined ? `HTTP ${String(status)}` : "");
313
318
  return undefined;
314
319
  }
315
320
  }
@@ -28190,7 +28190,32 @@ const SCREENS = runtimeSetting({
28190
28190
  }
28191
28191
  },
28192
28192
  {
28193
- "id": "",
28193
+ "id": "feed_body_$item.id",
28194
+ "body": {
28195
+ "package": "himalaya",
28196
+ "type": "RichContent",
28197
+ "props": {
28198
+ "documentJsonPath": "$item.bodyJson",
28199
+ "onLink": {
28200
+ "actionId": "tier5.himiBrandedFeed.openLink"
28201
+ },
28202
+ "fallback": {
28203
+ "id": "",
28204
+ "body": {
28205
+ "package": "himalaya",
28206
+ "type": "Text",
28207
+ "props": {
28208
+ "text": "$item.text",
28209
+ "style": "body",
28210
+ "maxLines": 6
28211
+ }
28212
+ }
28213
+ }
28214
+ }
28215
+ }
28216
+ },
28217
+ {
28218
+ "id": "feed_body_text_$item.id",
28194
28219
  "body": {
28195
28220
  "package": "himalaya",
28196
28221
  "type": "Text",
@@ -28199,6 +28224,9 @@ const SCREENS = runtimeSetting({
28199
28224
  "style": "body",
28200
28225
  "maxLines": 6
28201
28226
  }
28227
+ },
28228
+ "modifiers": {
28229
+ "hiddenPath": "$item.hasBodyJson"
28202
28230
  }
28203
28231
  },
28204
28232
  {
@@ -28392,7 +28420,8 @@ const SCREENS = runtimeSetting({
28392
28420
  "integrity": "",
28393
28421
  "contentType": "application/javascript+ts-bundle",
28394
28422
  "declaredCapabilities": [
28395
- "wix_api"
28423
+ "wix_api",
28424
+ "open_url"
28396
28425
  ],
28397
28426
  "minBinaryVersion": "0.1.0",
28398
28427
  "bundleVersion": "0.0.1"
@@ -48300,7 +48329,32 @@ const SCREENS = runtimeSetting({
48300
48329
  }
48301
48330
  },
48302
48331
  {
48303
- "id": "",
48332
+ "id": "forum_body",
48333
+ "body": {
48334
+ "package": "himalaya",
48335
+ "type": "RichContent",
48336
+ "props": {
48337
+ "documentJsonPath": "$service:spacesForumPost.bodyJson",
48338
+ "onLink": {
48339
+ "actionId": "tier5.spacesForumPost.openLink"
48340
+ },
48341
+ "fallback": {
48342
+ "id": "",
48343
+ "body": {
48344
+ "package": "himalaya",
48345
+ "type": "Text",
48346
+ "props": {
48347
+ "text": "$service:spacesForumPost.body",
48348
+ "style": "body",
48349
+ "collapseWhenEmpty": true
48350
+ }
48351
+ }
48352
+ }
48353
+ }
48354
+ }
48355
+ },
48356
+ {
48357
+ "id": "forum_body_text",
48304
48358
  "body": {
48305
48359
  "package": "himalaya",
48306
48360
  "type": "Text",
@@ -48309,6 +48363,9 @@ const SCREENS = runtimeSetting({
48309
48363
  "style": "body",
48310
48364
  "collapseWhenEmpty": true
48311
48365
  }
48366
+ },
48367
+ "modifiers": {
48368
+ "hiddenPath": "$service:spacesForumPost.hasBodyJson"
48312
48369
  }
48313
48370
  }
48314
48371
  ]
@@ -48584,7 +48641,8 @@ const SCREENS = runtimeSetting({
48584
48641
  "integrity": "",
48585
48642
  "contentType": "application/javascript+ts-bundle",
48586
48643
  "declaredCapabilities": [
48587
- "wix_api"
48644
+ "wix_api",
48645
+ "open_url"
48588
48646
  ],
48589
48647
  "minBinaryVersion": "0.1.0",
48590
48648
  "bundleVersion": "0.0.1"
@@ -57815,7 +57873,32 @@ const SCREENS = runtimeSetting({
57815
57873
  "expandedPath": "$item.expanded",
57816
57874
  "content": [
57817
57875
  {
57818
- "id": "",
57876
+ "id": "faq_answer_$item.id",
57877
+ "body": {
57878
+ "package": "himalaya",
57879
+ "type": "RichContent",
57880
+ "props": {
57881
+ "documentJsonPath": "$item.answerJson",
57882
+ "onLink": {
57883
+ "actionId": "tier5.himiBrandedFaq.openLink"
57884
+ },
57885
+ "fallback": {
57886
+ "id": "",
57887
+ "body": {
57888
+ "package": "himalaya",
57889
+ "type": "Text",
57890
+ "props": {
57891
+ "text": "$item.answer",
57892
+ "style": "body",
57893
+ "color": "textSecondary"
57894
+ }
57895
+ }
57896
+ }
57897
+ }
57898
+ }
57899
+ },
57900
+ {
57901
+ "id": "faq_answer_text_$item.id",
57819
57902
  "body": {
57820
57903
  "package": "himalaya",
57821
57904
  "type": "Text",
@@ -57824,6 +57907,9 @@ const SCREENS = runtimeSetting({
57824
57907
  "style": "body",
57825
57908
  "color": "textSecondary"
57826
57909
  }
57910
+ },
57911
+ "modifiers": {
57912
+ "hiddenPath": "$item.hasAnswerJson"
57827
57913
  }
57828
57914
  }
57829
57915
  ],
@@ -58127,7 +58213,8 @@ const SCREENS = runtimeSetting({
58127
58213
  "integrity": "",
58128
58214
  "contentType": "application/javascript+ts-bundle",
58129
58215
  "declaredCapabilities": [
58130
- "wix_api"
58216
+ "wix_api",
58217
+ "open_url"
58131
58218
  ],
58132
58219
  "minBinaryVersion": "0.1.0",
58133
58220
  "bundleVersion": "0.0.1"
@@ -79110,6 +79197,48 @@ const SCREENS = runtimeSetting({
79110
79197
  ]
79111
79198
  }
79112
79199
  }
79200
+ },
79201
+ {
79202
+ "id": "cms_body_$item.id",
79203
+ "body": {
79204
+ "package": "himalaya",
79205
+ "type": "RichContent",
79206
+ "props": {
79207
+ "documentJsonPath": "$item.bodyJson",
79208
+ "onLink": {
79209
+ "actionId": "tier5.himiBrandedCmsCollection.openLink"
79210
+ },
79211
+ "fallback": {
79212
+ "id": "",
79213
+ "body": {
79214
+ "package": "himalaya",
79215
+ "type": "Text",
79216
+ "props": {
79217
+ "text": "$item.bodyText",
79218
+ "style": "body",
79219
+ "color": "textSecondary",
79220
+ "collapseWhenEmpty": true
79221
+ }
79222
+ }
79223
+ }
79224
+ }
79225
+ }
79226
+ },
79227
+ {
79228
+ "id": "cms_body_text_$item.id",
79229
+ "body": {
79230
+ "package": "himalaya",
79231
+ "type": "Text",
79232
+ "props": {
79233
+ "text": "$item.bodyText",
79234
+ "style": "body",
79235
+ "color": "textSecondary",
79236
+ "collapseWhenEmpty": true
79237
+ }
79238
+ },
79239
+ "modifiers": {
79240
+ "hiddenPath": "$item.hasBodyJson"
79241
+ }
79113
79242
  }
79114
79243
  ]
79115
79244
  }
@@ -79212,7 +79341,8 @@ const SCREENS = runtimeSetting({
79212
79341
  "contentType": "application/javascript+ts-bundle",
79213
79342
  "declaredCapabilities": [
79214
79343
  "wix_api",
79215
- "local_database"
79344
+ "local_database",
79345
+ "open_url"
79216
79346
  ],
79217
79347
  "minBinaryVersion": "0.1.0",
79218
79348
  "bundleVersion": "0.0.1"
@@ -80501,7 +80631,33 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
80501
80631
  }
80502
80632
  },
80503
80633
  {
80504
- "id": "",
80634
+ "id": "product_body",
80635
+ "body": {
80636
+ "package": "himalaya",
80637
+ "type": "RichContent",
80638
+ "props": {
80639
+ "documentJsonPath": "$service:himiStoreProduct.descriptionJson",
80640
+ "onLink": {
80641
+ "actionId": "tier5.himiStoreProduct.openLink"
80642
+ },
80643
+ "fallback": {
80644
+ "id": "",
80645
+ "body": {
80646
+ "package": "himalaya",
80647
+ "type": "Text",
80648
+ "props": {
80649
+ "text": "$service:himiStoreProduct.description",
80650
+ "style": "body",
80651
+ "color": "textSecondary",
80652
+ "collapseWhenEmpty": true
80653
+ }
80654
+ }
80655
+ }
80656
+ }
80657
+ }
80658
+ },
80659
+ {
80660
+ "id": "product_body_text",
80505
80661
  "body": {
80506
80662
  "package": "himalaya",
80507
80663
  "type": "Text",
@@ -80511,6 +80667,9 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
80511
80667
  "color": "textSecondary",
80512
80668
  "collapseWhenEmpty": true
80513
80669
  }
80670
+ },
80671
+ "modifiers": {
80672
+ "hiddenPath": "$service:himiStoreProduct.hasDescriptionJson"
80514
80673
  }
80515
80674
  }
80516
80675
  ]
@@ -81005,7 +81164,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
81005
81164
  "declaredCapabilities": [
81006
81165
  "wix_api",
81007
81166
  "share",
81008
- "local_database"
81167
+ "local_database",
81168
+ "open_url"
81009
81169
  ],
81010
81170
  "minBinaryVersion": "0.1.0",
81011
81171
  "bundleVersion": "0.0.1"
@@ -84329,7 +84489,33 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
84329
84489
  }
84330
84490
  },
84331
84491
  {
84332
- "id": "",
84492
+ "id": "event_body",
84493
+ "body": {
84494
+ "package": "himalaya",
84495
+ "type": "RichContent",
84496
+ "props": {
84497
+ "documentJsonPath": "$service:himiEventDetail.descriptionJson",
84498
+ "onLink": {
84499
+ "actionId": "tier5.himiEventDetail.openLink"
84500
+ },
84501
+ "fallback": {
84502
+ "id": "",
84503
+ "body": {
84504
+ "package": "himalaya",
84505
+ "type": "Text",
84506
+ "props": {
84507
+ "text": "$service:himiEventDetail.description",
84508
+ "style": "body",
84509
+ "color": "textSecondary",
84510
+ "collapseWhenEmpty": true
84511
+ }
84512
+ }
84513
+ }
84514
+ }
84515
+ }
84516
+ },
84517
+ {
84518
+ "id": "event_body_text",
84333
84519
  "body": {
84334
84520
  "package": "himalaya",
84335
84521
  "type": "Text",
@@ -84339,6 +84525,9 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
84339
84525
  "color": "textSecondary",
84340
84526
  "collapseWhenEmpty": true
84341
84527
  }
84528
+ },
84529
+ "modifiers": {
84530
+ "hiddenPath": "$service:himiEventDetail.hasDescriptionJson"
84342
84531
  }
84343
84532
  }
84344
84533
  ]
@@ -86233,7 +86422,33 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
86233
86422
  }
86234
86423
  },
86235
86424
  {
86236
- "id": "",
86425
+ "id": "comment_body_$item.id",
86426
+ "body": {
86427
+ "package": "himalaya",
86428
+ "type": "RichContent",
86429
+ "props": {
86430
+ "documentJsonPath": "$item.bodyJson",
86431
+ "onLink": {
86432
+ "actionId": "tier5.himiBlogPost.openLink"
86433
+ },
86434
+ "fallback": {
86435
+ "id": "",
86436
+ "body": {
86437
+ "package": "himalaya",
86438
+ "type": "Text",
86439
+ "props": {
86440
+ "text": "$item.text",
86441
+ "style": "body",
86442
+ "color": "textSecondary",
86443
+ "collapseWhenEmpty": true
86444
+ }
86445
+ }
86446
+ }
86447
+ }
86448
+ }
86449
+ },
86450
+ {
86451
+ "id": "comment_body_text_$item.id",
86237
86452
  "body": {
86238
86453
  "package": "himalaya",
86239
86454
  "type": "Text",
@@ -86243,6 +86458,9 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
86243
86458
  "color": "textSecondary",
86244
86459
  "collapseWhenEmpty": true
86245
86460
  }
86461
+ },
86462
+ "modifiers": {
86463
+ "hiddenPath": "$item.hasBodyJson"
86246
86464
  }
86247
86465
  }
86248
86466
  ]
@@ -87029,6 +87247,48 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
87029
87247
  ]
87030
87248
  }
87031
87249
  }
87250
+ },
87251
+ {
87252
+ "id": "cms_body_$item.id",
87253
+ "body": {
87254
+ "package": "himalaya",
87255
+ "type": "RichContent",
87256
+ "props": {
87257
+ "documentJsonPath": "$item.bodyJson",
87258
+ "onLink": {
87259
+ "actionId": "tier5.himiBrandedCmsCollection.openLink"
87260
+ },
87261
+ "fallback": {
87262
+ "id": "",
87263
+ "body": {
87264
+ "package": "himalaya",
87265
+ "type": "Text",
87266
+ "props": {
87267
+ "text": "$item.bodyText",
87268
+ "style": "body",
87269
+ "color": "textSecondary",
87270
+ "collapseWhenEmpty": true
87271
+ }
87272
+ }
87273
+ }
87274
+ }
87275
+ }
87276
+ },
87277
+ {
87278
+ "id": "cms_body_text_$item.id",
87279
+ "body": {
87280
+ "package": "himalaya",
87281
+ "type": "Text",
87282
+ "props": {
87283
+ "text": "$item.bodyText",
87284
+ "style": "body",
87285
+ "color": "textSecondary",
87286
+ "collapseWhenEmpty": true
87287
+ }
87288
+ },
87289
+ "modifiers": {
87290
+ "hiddenPath": "$item.hasBodyJson"
87291
+ }
87032
87292
  }
87033
87293
  ]
87034
87294
  }
@@ -87131,7 +87391,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
87131
87391
  "contentType": "application/javascript+ts-bundle",
87132
87392
  "declaredCapabilities": [
87133
87393
  "wix_api",
87134
- "local_database"
87394
+ "local_database",
87395
+ "open_url"
87135
87396
  ],
87136
87397
  "minBinaryVersion": "0.1.0",
87137
87398
  "bundleVersion": "0.0.1"
@@ -14,15 +14,21 @@ export interface RichBody {
14
14
  bodyText: string;
15
15
  }
16
16
 
17
+ /** [rico] #3372 — the media width for a document drawn in a LIST row (a feed post, a comment, a CMS item): the
18
+ * list-image size (WIX_LIST_IMAGE), so a scrolling list never downloads every image at detail size. */
19
+ export const ROW_IMAGE_WIDTH = 1000;
20
+
17
21
  /** Prepares `richContent` (a Ricos document, as a Wix API returns it, or its JSON string). No document, or one
18
22
  * that is not a document, gives `{ bodyJson: "", bodyText: fallback }`. A document past the viewer's limits
19
23
  * gives bodyJson "" and its plain text — except one rejected as too deep, which is not walked again (the
20
- * plain-text walk has no depth limit), so `fallback` stands in for it. */
21
- export function richBody(richContent: unknown, fallback = ""): RichBody {
24
+ * plain-text walk has no depth limit), so `fallback` stands in for it. `imageWidth` sizes its media (default
25
+ * the 1400 px detail budget). */
26
+ export function richBody(richContent: unknown, fallback = "", imageWidth = 1400): RichBody {
22
27
  const doc = parseRicosJson(richContent);
23
28
  if (!doc) return { bodyJson: "", bodyText: fallback };
24
- // 1400 px — the detail-image budget, which is also the encoder's maximum.
25
- const { json, rejected } = encodeRichContent(doc, { imageWidth: 1400 });
29
+ // 1400 px by default — the detail-image budget, which is also the encoder's maximum; list rows pass
30
+ // ROW_IMAGE_WIDTH.
31
+ const { json, rejected } = encodeRichContent(doc, { imageWidth });
26
32
  const text = rejected === "too_deep" ? "" : ricosToPlainText(doc);
27
33
  return { bodyJson: json, bodyText: text || fallback };
28
34
  }
@@ -8,7 +8,7 @@ import { isSavedId, toggleSavedId } from "../_shared/himi-blog-saved";
8
8
  import { stashCopy, copyParam, copyTemplate } from "../_shared/himi-copy.js";
9
9
  import { formatDateMedium, formatMonthDay } from "../_shared/engine-intl.js";
10
10
  import { openRichLink } from "../_shared/rich-content-link.js";
11
- import { richBody } from "../_shared/rich-body.js";
11
+ import { richBody, ROW_IMAGE_WIDTH } from "../_shared/rich-body.js";
12
12
 
13
13
  // Offline-first (data-offline): the post body is a public read-through — when the
14
14
  // live fetch fails (offline) fall back to the last-synced row the feed cached under
@@ -23,8 +23,14 @@ async function readCachedPost(id: string): Promise<DbEntity | null> {
23
23
  interface CommentRow {
24
24
  id: string;
25
25
  authorName: string;
26
+ /** The comment's plain text: the row RichContent's fallback. */
26
27
  text: string;
27
28
  meta: string;
29
+ /** [rico] #3372 — the comment's Ricos document as ONE string for the row's RichContent; "" when it has none
30
+ * or the encoder rejected it. */
31
+ bodyJson: string;
32
+ /** bodyJson !== "" — gates the row's plain text, because RichContent draws nothing for "". */
33
+ hasBodyJson: boolean;
28
34
  }
29
35
 
30
36
  interface S {
@@ -168,11 +174,15 @@ function commentMeta(row: WixBlogCommentRow): string {
168
174
  }
169
175
 
170
176
  function toCommentRow(row: WixBlogCommentRow): CommentRow {
177
+ // The plain text is the document's own (runs joined as written, not space-separated) when it has one.
178
+ const { bodyJson, bodyText } = richBody(row.richContent, row.text || "Comment", ROW_IMAGE_WIDTH);
171
179
  return {
172
180
  id: row.id,
173
181
  authorName: row.authorName || "Member",
174
- text: row.text || "Comment",
182
+ text: bodyText,
175
183
  meta: commentMeta(row),
184
+ bodyJson,
185
+ hasBodyJson: bodyJson !== "",
176
186
  };
177
187
  }
178
188