@wix/himalaya-cli 0.1145.0 → 0.1146.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 (74) 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 +0 -2
  6. package/guides/sdk-reference.json +13 -1
  7. package/package.json +2 -2
  8. package/templates/beauty-salon/dev/index.ts +62 -3
  9. package/templates/beauty-salon/tier5-src/_shared/rich-body.ts +10 -4
  10. package/templates/beauty-salon/tier5-src/himi-blog-post/index.ts +12 -2
  11. package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +35 -4
  12. package/templates/beauty-salon-owner/tier5-src/_shared/rich-body.ts +10 -4
  13. package/templates/branded-lite/dev/index.ts +273 -12
  14. package/templates/branded-lite/tier5-src/_shared/rich-body.ts +10 -4
  15. package/templates/branded-lite/tier5-src/himi-blog-post/index.ts +12 -2
  16. package/templates/branded-lite/tier5-src/himi-branded-lite-cms-collection/index.ts +43 -3
  17. package/templates/branded-lite/tier5-src/himi-branded-lite-faq/index.ts +69 -13
  18. package/templates/branded-lite/tier5-src/himi-branded-lite-feed/index.ts +23 -3
  19. package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +40 -3
  20. package/templates/branded-lite/tier5-src/himi-store-product/index.ts +35 -4
  21. package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +29 -5
  22. package/templates/community-club/dev/index.ts +135 -5
  23. package/templates/community-club/tier5-src/_shared/rich-body.ts +10 -4
  24. package/templates/community-club/tier5-src/himi-blog-post/index.ts +12 -2
  25. package/templates/community-club/tier5-src/himi-branded-lite-cms-collection/index.ts +43 -3
  26. package/templates/community-club/tier5-src/himi-branded-lite-feed/index.ts +23 -3
  27. package/templates/community-club/tier5-src/himi-events-detail/index.ts +40 -3
  28. package/templates/community-club-owner/tier5-src/_shared/rich-body.ts +10 -4
  29. package/templates/events-meetups/dev/index.ts +91 -4
  30. package/templates/events-meetups/tier5-src/_shared/rich-body.ts +10 -4
  31. package/templates/events-meetups/tier5-src/himi-blog-post/index.ts +12 -2
  32. package/templates/events-meetups/tier5-src/himi-branded-lite-feed/index.ts +23 -3
  33. package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +40 -3
  34. package/templates/events-meetups-owner/tier5-src/_shared/rich-body.ts +10 -4
  35. package/templates/gym-fitness/dev/index.ts +92 -4
  36. package/templates/gym-fitness/tier5-src/_shared/rich-body.ts +10 -4
  37. package/templates/gym-fitness/tier5-src/himi-blog-post/index.ts +12 -2
  38. package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +40 -3
  39. package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +35 -4
  40. package/templates/gym-fitness-owner/tier5-src/_shared/rich-body.ts +10 -4
  41. package/templates/nonprofit-cause/dev/index.ts +91 -4
  42. package/templates/nonprofit-cause/tier5-src/_shared/rich-body.ts +10 -4
  43. package/templates/nonprofit-cause/tier5-src/himi-blog-post/index.ts +12 -2
  44. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-feed/index.ts +23 -3
  45. package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +40 -3
  46. package/templates/nonprofit-cause-owner/tier5-src/_shared/rich-body.ts +10 -4
  47. package/templates/owner-lite/tier5-src/_shared/rich-body.ts +10 -4
  48. package/templates/podcast-creator/dev/index.ts +91 -4
  49. package/templates/podcast-creator/tier5-src/_shared/rich-body.ts +10 -4
  50. package/templates/podcast-creator/tier5-src/himi-blog-post/index.ts +12 -2
  51. package/templates/podcast-creator/tier5-src/himi-branded-lite-feed/index.ts +23 -3
  52. package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +40 -3
  53. package/templates/podcast-creator-owner/tier5-src/_shared/rich-body.ts +10 -4
  54. package/templates/portfolio-creative/dev/index.ts +92 -4
  55. package/templates/portfolio-creative/tier5-src/_shared/rich-body.ts +10 -4
  56. package/templates/portfolio-creative/tier5-src/himi-blog-post/index.ts +12 -2
  57. package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +40 -3
  58. package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +35 -4
  59. package/templates/portfolio-creative-owner/tier5-src/_shared/rich-body.ts +10 -4
  60. package/templates/restaurant/dev/index.ts +60 -2
  61. package/templates/restaurant/tier5-src/_shared/rich-body.ts +10 -4
  62. package/templates/restaurant/tier5-src/himi-blog-post/index.ts +12 -2
  63. package/templates/restaurant/tier5-src/himi-events-detail/index.ts +40 -3
  64. package/templates/restaurant-owner/tier5-src/_shared/rich-body.ts +10 -4
  65. package/templates/retail-store/dev/index.ts +62 -3
  66. package/templates/retail-store/tier5-src/_shared/rich-body.ts +10 -4
  67. package/templates/retail-store/tier5-src/himi-blog-post/index.ts +12 -2
  68. package/templates/retail-store/tier5-src/himi-store-product/index.ts +35 -4
  69. package/templates/retail-store-owner/tier5-src/_shared/rich-body.ts +10 -4
  70. package/templates/yoga-studio/dev/index.ts +60 -2
  71. package/templates/yoga-studio/tier5-src/_shared/rich-body.ts +10 -4
  72. package/templates/yoga-studio/tier5-src/himi-blog-post/index.ts +12 -2
  73. package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +40 -3
  74. package/templates/yoga-studio-owner/tier5-src/_shared/rich-body.ts +10 -4
@@ -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
 
@@ -10,6 +10,8 @@ import {
10
10
  import { copyParam, copyTemplate, stashCopy } from "../_shared/himi-copy.js";
11
11
  import { getAll, upsert, type DbEntity } from "@himalaya/local-db";
12
12
  import { formatDateMedium } from "../_shared/engine-intl.js";
13
+ import { openRichLink } from "../_shared/rich-content-link.js";
14
+ import { richBody, ROW_IMAGE_WIDTH } from "../_shared/rich-body.js";
13
15
 
14
16
  // Offline-first (data-offline): a collection's items are a public read-through against
15
17
  // the on-device `cms_items` store. Rows are cached per collection (the persisted key is
@@ -23,9 +25,10 @@ async function readCached(collectionId: string): Promise<ItemRow[]> {
23
25
  try {
24
26
  const prefix = `${collectionId}::`;
25
27
  const rows = await getAll(ENTITY);
28
+ // A row cached before [rico] #3372 carries no document fields: it shows its plain text.
26
29
  return rows
27
30
  .filter((r) => typeof r.id === "string" && r.id.startsWith(prefix))
28
- .map((r) => ({ ...(r as unknown as ItemRow), id: (r.id as string).slice(prefix.length) }));
31
+ .map((r) => ({ bodyJson: "", hasBodyJson: false, bodyText: "", ...(r as unknown as Partial<ItemRow>), id: (r.id as string).slice(prefix.length) }) as ItemRow);
29
32
  } catch { return []; }
30
33
  }
31
34
  async function writeCached(collectionId: string, rows: ItemRow[]): Promise<void> {
@@ -41,6 +44,13 @@ interface ItemRow {
41
44
  initial: string;
42
45
  hasImage: boolean;
43
46
  noImage: boolean;
47
+ /** [rico] #3372 — the item's first RICH_CONTENT field as ONE document string for the row's RichContent; ""
48
+ * when the collection has none, the item left it empty, or the encoder rejected it. */
49
+ bodyJson: string;
50
+ /** bodyJson !== "" — gates the row's plain text, because RichContent draws nothing for "". */
51
+ hasBodyJson: boolean;
52
+ /** That field's plain text: the RichContent fallback. */
53
+ bodyText: string;
44
54
  }
45
55
 
46
56
  interface State {
@@ -86,10 +96,26 @@ function dateValue(value: unknown): string {
86
96
  return Number.isNaN(date.getTime()) ? raw : formatDateMedium(date);
87
97
  }
88
98
 
99
+ /** [rico] #3372 — RICH_TEXT is HTML: its text, with the tags removed and the common entities decoded, so a
100
+ * row never shows raw markup. A block or line-break tag separates words; an inline one (`W<i>ix</i>`) joins. */
101
+ export function stripHtml(html: string): string {
102
+ return html
103
+ .replace(/<\/?(?:p|div|br|li|ul|ol|h[1-6]|tr|td|th|blockquote|section|article)\b[^>]*>/gi, " ")
104
+ .replace(/<[^>]+>/g, "")
105
+ .replace(/&nbsp;/g, " ")
106
+ .replace(/&lt;/g, "<")
107
+ .replace(/&gt;/g, ">")
108
+ .replace(/&quot;/g, "\"")
109
+ .replace(/&#39;/g, "'")
110
+ .replace(/&amp;/g, "&")
111
+ .replace(/\s+/g, " ")
112
+ .trim();
113
+ }
114
+
89
115
  function displayValue(value: unknown, type = ""): string {
90
116
  if (value == null) return "";
91
117
  if (type.includes("DATE")) return dateValue(value);
92
- if (typeof value === "string") return value.trim();
118
+ if (typeof value === "string") return type.includes("RICH_TEXT") ? stripHtml(value) : value.trim();
93
119
  if (typeof value === "number" || typeof value === "boolean") return String(value);
94
120
  if (Array.isArray(value)) return value.map((entry) => displayValue(entry)).filter(Boolean).slice(0, 3).join(", ");
95
121
  if (typeof value === "object") {
@@ -116,13 +142,16 @@ function imageValue(data: Record<string, unknown>, fields: CmsFieldDefinition[])
116
142
  function itemRow(item: WixDataItem, collection: CmsCollectionDefinition, index: number): ItemRow {
117
143
  const data = item.data ?? {};
118
144
  const primary = displayValue(data[collection.displayField], collection.fields.find((field) => field.key === collection.displayField)?.type);
145
+ // A RICH_CONTENT field renders as the row's document, never as a detail line.
119
146
  const details = collection.fields
120
- .filter((field) => field.key !== collection.displayField && !field.type.includes("IMAGE"))
147
+ .filter((field) => field.key !== collection.displayField && !field.type.includes("IMAGE") && !field.type.includes("RICH_CONTENT"))
121
148
  .map((field) => ({ label: field.displayName, value: displayValue(data[field.key], field.type) }))
122
149
  .filter((entry) => entry.value)
123
150
  .slice(0, 2);
124
151
  const title = primary || copyTemplate("cmsCollection.itemFallbackTitle", "Item {n}", { n: index + 1 });
125
152
  const image = imageValue(data, collection.fields);
153
+ const rich = collection.fields.find((field) => field.type.includes("RICH_CONTENT"));
154
+ const { bodyJson, bodyText } = richBody(rich ? data[rich.key] : null, "", ROW_IMAGE_WIDTH);
126
155
  return {
127
156
  id: item.id || String(data._id ?? `${collection.id}-${index}`),
128
157
  title,
@@ -132,6 +161,9 @@ function itemRow(item: WixDataItem, collection: CmsCollectionDefinition, index:
132
161
  initial: (title[0] || "C").toUpperCase(),
133
162
  hasImage: Boolean(image),
134
163
  noImage: !image,
164
+ bodyJson,
165
+ hasBodyJson: bodyJson !== "",
166
+ bodyText,
135
167
  };
136
168
  }
137
169
 
@@ -202,6 +234,8 @@ let loadingCollectionId = "";
202
234
  const svc = defineService<State, {
203
235
  load: (state: State, arg?: unknown) => Promise<State>;
204
236
  search: (state: State, arg?: unknown) => State;
237
+ /** [rico] #3372 — a row RichContent's on_link: `{ url }` of the tapped link. */
238
+ openLink: (state: State, arg?: unknown) => Promise<State>;
205
239
  }>("himiBrandedCmsCollection", {
206
240
  state: initial,
207
241
  actions: {
@@ -255,6 +289,12 @@ const svc = defineService<State, {
255
289
  : state.all;
256
290
  return { ...state, rows, noRows: rows.length === 0, noEmpty: rows.length > 0, emptyText: query ? copyParam("cmsCollection.searchEmptyText", "No items match your search.") : copyParam("cmsCollection.emptyText", "This collection has no published items yet.") };
257
291
  },
292
+ // Only http(s), mailto and tel links reach open_url; the collection has no message line, so a refused
293
+ // link simply does nothing.
294
+ openLink: async (state, arg) => {
295
+ await openRichLink(arg);
296
+ return state;
297
+ },
258
298
  },
259
299
  });
260
300