@wix/himalaya-cli 0.1397.0 → 0.1399.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 (88) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/serve-runtime/src/wix-bridge.d.ts +30 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/icon-catalog.generated.d.ts +1 -1
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +2 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/validation.d.ts +1 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/auth/src/copy.generated.d.ts +109 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/auth/src/feedback.d.ts +15 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/auth/src/index.d.ts +3 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/screens/src/index.d.ts +44 -0
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/auth.mjs +304 -18
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core/validation.mjs +1 -1
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +72 -1
  12. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +377 -20
  13. package/dist/cli.mjs +983 -342
  14. package/dist/preview-assets.generated.bin +0 -0
  15. package/guides/component-reference.json +1 -1
  16. package/guides/component-reference.md +2 -0
  17. package/guides/headless-loop.md +1 -1
  18. package/guides/importable-sdks.md +2 -1
  19. package/guides/one-app-to-himi-app.md +30 -0
  20. package/guides/one-app-viewer.md +8 -1
  21. package/guides/sdk-reference.json +92 -1
  22. package/package.json +2 -2
  23. package/templates/beauty-salon/dev/index.ts +41 -1
  24. package/templates/beauty-salon/tier5-src/himi-store-account/index.ts +90 -76
  25. package/templates/beauty-salon-owner/dev/index.ts +304 -7
  26. package/templates/beauty-salon-owner/tier5-src/_shared/aria-chat.ts +39 -33
  27. package/templates/beauty-salon-owner/tier5-src/_shared/aria-widget-model.ts +26 -4
  28. package/templates/beauty-salon-owner/tier5-src/_shared/aria-widget-view.ts +63 -16
  29. package/templates/branded-lite/dev/index.ts +41 -1
  30. package/templates/branded-lite/tier5-src/himi-store-account/index.ts +90 -76
  31. package/templates/community-club/dev/index.ts +41 -1
  32. package/templates/community-club/tier5-src/himi-store-account/index.ts +90 -76
  33. package/templates/community-club-owner/dev/index.ts +304 -7
  34. package/templates/community-club-owner/tier5-src/_shared/aria-chat.ts +39 -33
  35. package/templates/community-club-owner/tier5-src/_shared/aria-widget-model.ts +26 -4
  36. package/templates/community-club-owner/tier5-src/_shared/aria-widget-view.ts +63 -16
  37. package/templates/events-meetups/dev/index.ts +41 -1
  38. package/templates/events-meetups/tier5-src/himi-store-account/index.ts +90 -76
  39. package/templates/events-meetups-owner/dev/index.ts +304 -7
  40. package/templates/events-meetups-owner/tier5-src/_shared/aria-chat.ts +39 -33
  41. package/templates/events-meetups-owner/tier5-src/_shared/aria-widget-model.ts +26 -4
  42. package/templates/events-meetups-owner/tier5-src/_shared/aria-widget-view.ts +63 -16
  43. package/templates/gym-fitness/dev/index.ts +41 -1
  44. package/templates/gym-fitness/tier5-src/himi-store-account/index.ts +90 -76
  45. package/templates/gym-fitness-owner/dev/index.ts +304 -7
  46. package/templates/gym-fitness-owner/tier5-src/_shared/aria-chat.ts +39 -33
  47. package/templates/gym-fitness-owner/tier5-src/_shared/aria-widget-model.ts +26 -4
  48. package/templates/gym-fitness-owner/tier5-src/_shared/aria-widget-view.ts +63 -16
  49. package/templates/nonprofit-cause/dev/index.ts +41 -1
  50. package/templates/nonprofit-cause/tier5-src/himi-store-account/index.ts +90 -76
  51. package/templates/nonprofit-cause-owner/dev/index.ts +304 -7
  52. package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-chat.ts +39 -33
  53. package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-widget-model.ts +26 -4
  54. package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-widget-view.ts +63 -16
  55. package/templates/owner-lite/dev/index.ts +304 -7
  56. package/templates/owner-lite/tier5-src/_shared/aria-chat.ts +39 -33
  57. package/templates/owner-lite/tier5-src/_shared/aria-widget-model.ts +26 -4
  58. package/templates/owner-lite/tier5-src/_shared/aria-widget-view.ts +63 -16
  59. package/templates/podcast-creator/dev/index.ts +41 -1
  60. package/templates/podcast-creator/tier5-src/himi-store-account/index.ts +90 -76
  61. package/templates/podcast-creator-owner/dev/index.ts +304 -7
  62. package/templates/podcast-creator-owner/tier5-src/_shared/aria-chat.ts +39 -33
  63. package/templates/podcast-creator-owner/tier5-src/_shared/aria-widget-model.ts +26 -4
  64. package/templates/podcast-creator-owner/tier5-src/_shared/aria-widget-view.ts +63 -16
  65. package/templates/portfolio-creative/dev/index.ts +41 -1
  66. package/templates/portfolio-creative/tier5-src/himi-store-account/index.ts +90 -76
  67. package/templates/portfolio-creative-owner/dev/index.ts +304 -7
  68. package/templates/portfolio-creative-owner/tier5-src/_shared/aria-chat.ts +39 -33
  69. package/templates/portfolio-creative-owner/tier5-src/_shared/aria-widget-model.ts +26 -4
  70. package/templates/portfolio-creative-owner/tier5-src/_shared/aria-widget-view.ts +63 -16
  71. package/templates/restaurant/dev/index.ts +41 -1
  72. package/templates/restaurant/tier5-src/himi-store-account/index.ts +90 -76
  73. package/templates/restaurant-owner/dev/index.ts +304 -7
  74. package/templates/restaurant-owner/tier5-src/_shared/aria-chat.ts +39 -33
  75. package/templates/restaurant-owner/tier5-src/_shared/aria-widget-model.ts +26 -4
  76. package/templates/restaurant-owner/tier5-src/_shared/aria-widget-view.ts +63 -16
  77. package/templates/retail-store/dev/index.ts +41 -1
  78. package/templates/retail-store/tier5-src/himi-store-account/index.ts +90 -76
  79. package/templates/retail-store-owner/dev/index.ts +304 -7
  80. package/templates/retail-store-owner/tier5-src/_shared/aria-chat.ts +39 -33
  81. package/templates/retail-store-owner/tier5-src/_shared/aria-widget-model.ts +26 -4
  82. package/templates/retail-store-owner/tier5-src/_shared/aria-widget-view.ts +63 -16
  83. package/templates/yoga-studio/dev/index.ts +41 -1
  84. package/templates/yoga-studio/tier5-src/himi-store-account/index.ts +90 -76
  85. package/templates/yoga-studio-owner/dev/index.ts +304 -7
  86. package/templates/yoga-studio-owner/tier5-src/_shared/aria-chat.ts +39 -33
  87. package/templates/yoga-studio-owner/tier5-src/_shared/aria-widget-model.ts +26 -4
  88. package/templates/yoga-studio-owner/tier5-src/_shared/aria-widget-view.ts +63 -16
@@ -57330,6 +57330,7 @@ const SCREENS = runtimeSetting({
57330
57330
  "title": "$item.buttonLabel",
57331
57331
  "style": "secondary",
57332
57332
  "size": "sm",
57333
+ "fullWidth": false,
57333
57334
  "action": {
57334
57335
  "actionId": "tier5.himiOwnerLiteAria.widgetRespond",
57335
57336
  "params": {
@@ -58525,15 +58526,95 @@ const SCREENS = runtimeSetting({
58525
58526
  }
58526
58527
  },
58527
58528
  {
58528
- "id": "",
58529
+ "id": "himi_owner_lite_aria.widget.PLANS.benefits_$item.id",
58529
58530
  "body": {
58530
58531
  "package": "himalaya",
58531
- "type": "Text",
58532
+ "type": "LazyList",
58532
58533
  "props": {
58533
- "text": "$item.listText",
58534
- "style": "footnote",
58535
- "color": "textSecondary",
58536
- "maxLines": 8
58534
+ "itemsPath": "$item.benefits",
58535
+ "spacing": 4,
58536
+ "items": [
58537
+ {
58538
+ "id": "",
58539
+ "body": {
58540
+ "package": "himalaya",
58541
+ "type": "HStack",
58542
+ "props": {
58543
+ "spacing": 8,
58544
+ "children": [
58545
+ {
58546
+ "id": "",
58547
+ "body": {
58548
+ "package": "himalaya",
58549
+ "type": "Icon",
58550
+ "props": {
58551
+ "name": "$item.icon",
58552
+ "size": 12,
58553
+ "tint": "success"
58554
+ }
58555
+ },
58556
+ "modifiers": {
58557
+ "accessibilityRole": "decorative",
58558
+ "hiddenPath": "$item.successHidden"
58559
+ }
58560
+ },
58561
+ {
58562
+ "id": "",
58563
+ "body": {
58564
+ "package": "himalaya",
58565
+ "type": "Icon",
58566
+ "props": {
58567
+ "name": "$item.icon",
58568
+ "size": 12,
58569
+ "tint": "textPrimary"
58570
+ }
58571
+ },
58572
+ "modifiers": {
58573
+ "accessibilityRole": "decorative",
58574
+ "hiddenPath": "$item.plainHidden"
58575
+ }
58576
+ },
58577
+ {
58578
+ "id": "",
58579
+ "body": {
58580
+ "package": "himalaya",
58581
+ "type": "Icon",
58582
+ "props": {
58583
+ "name": "$item.icon",
58584
+ "size": 12,
58585
+ "tint": "accentPrimary"
58586
+ }
58587
+ },
58588
+ "modifiers": {
58589
+ "accessibilityRole": "decorative",
58590
+ "theme": {
58591
+ "colors": {
58592
+ "accentPrimary": "$item.colorHex"
58593
+ }
58594
+ },
58595
+ "hiddenPath": "$item.customHidden"
58596
+ }
58597
+ },
58598
+ {
58599
+ "id": "",
58600
+ "body": {
58601
+ "package": "himalaya",
58602
+ "type": "Text",
58603
+ "props": {
58604
+ "text": "$item.text",
58605
+ "style": "footnote",
58606
+ "maxLines": 8
58607
+ }
58608
+ },
58609
+ "modifiers": {
58610
+ "weight": 1
58611
+ }
58612
+ }
58613
+ ]
58614
+ }
58615
+ }
58616
+ }
58617
+ ]
58537
58618
  }
58538
58619
  },
58539
58620
  "modifiers": {
@@ -58933,6 +59014,181 @@ const SCREENS = runtimeSetting({
58933
59014
  }
58934
59015
  }
58935
59016
  },
59017
+ {
59018
+ "id": "himi_owner_lite_aria.widget.VISUAL_DISPLAY.variants",
59019
+ "body": {
59020
+ "package": "himalaya",
59021
+ "type": "LazyList",
59022
+ "props": {
59023
+ "itemsPath": "$service:himiOwnerLiteAria.widget.options",
59024
+ "spacing": 6,
59025
+ "items": [
59026
+ {
59027
+ "id": "himi_owner_lite_aria.widget.VISUAL_DISPLAY.variant_$item.id",
59028
+ "body": {
59029
+ "package": "himalaya",
59030
+ "type": "Card",
59031
+ "props": {
59032
+ "children": [
59033
+ {
59034
+ "id": "",
59035
+ "body": {
59036
+ "package": "himalaya",
59037
+ "type": "VStack",
59038
+ "props": {
59039
+ "spacing": 8,
59040
+ "children": [
59041
+ {
59042
+ "id": "",
59043
+ "body": {
59044
+ "package": "himalaya",
59045
+ "type": "Image",
59046
+ "props": {
59047
+ "url": "$item.pictureUrl",
59048
+ "fillWidth": true,
59049
+ "aspectRatio": 1.5,
59050
+ "contentMode": "fill",
59051
+ "contentDescription": "$item.title"
59052
+ }
59053
+ },
59054
+ "modifiers": {
59055
+ "hiddenPath": "$item.pictureHidden"
59056
+ }
59057
+ },
59058
+ {
59059
+ "id": "",
59060
+ "body": {
59061
+ "package": "himalaya",
59062
+ "type": "HStack",
59063
+ "props": {
59064
+ "spacing": 0,
59065
+ "children": [
59066
+ {
59067
+ "id": "",
59068
+ "body": {
59069
+ "package": "himalaya",
59070
+ "type": "Text",
59071
+ "props": {
59072
+ "text": "$item.badge",
59073
+ "style": "footnote",
59074
+ "color": "surfacePrimary"
59075
+ }
59076
+ },
59077
+ "modifiers": {
59078
+ "background": {
59079
+ "token": "textSecondary"
59080
+ },
59081
+ "cornerRadius": 4,
59082
+ "padding": {
59083
+ "leading": 8,
59084
+ "trailing": 8,
59085
+ "top": 2,
59086
+ "bottom": 2
59087
+ }
59088
+ }
59089
+ },
59090
+ {
59091
+ "id": "",
59092
+ "body": {
59093
+ "package": "himalaya",
59094
+ "type": "Spacer",
59095
+ "props": {}
59096
+ }
59097
+ }
59098
+ ]
59099
+ }
59100
+ },
59101
+ "modifiers": {
59102
+ "hiddenPath": "$item.badgeHidden"
59103
+ }
59104
+ },
59105
+ {
59106
+ "id": "",
59107
+ "body": {
59108
+ "package": "himalaya",
59109
+ "type": "Text",
59110
+ "props": {
59111
+ "text": "$item.title",
59112
+ "style": "bodyEmphasis"
59113
+ }
59114
+ }
59115
+ },
59116
+ {
59117
+ "id": "",
59118
+ "body": {
59119
+ "package": "himalaya",
59120
+ "type": "Text",
59121
+ "props": {
59122
+ "text": "$item.subtitle",
59123
+ "style": "body",
59124
+ "color": "textSecondary"
59125
+ }
59126
+ },
59127
+ "modifiers": {
59128
+ "hiddenPath": "$item.subtitleHidden"
59129
+ }
59130
+ },
59131
+ {
59132
+ "id": "",
59133
+ "body": {
59134
+ "package": "himalaya",
59135
+ "type": "Text",
59136
+ "props": {
59137
+ "text": "$item.text",
59138
+ "style": "body"
59139
+ }
59140
+ },
59141
+ "modifiers": {
59142
+ "hiddenPath": "$item.textHidden"
59143
+ }
59144
+ },
59145
+ {
59146
+ "id": "",
59147
+ "body": {
59148
+ "package": "himalaya",
59149
+ "type": "Text",
59150
+ "props": {
59151
+ "text": "$item.listText",
59152
+ "style": "body"
59153
+ }
59154
+ },
59155
+ "modifiers": {
59156
+ "hiddenPath": "$item.listHidden"
59157
+ }
59158
+ },
59159
+ {
59160
+ "id": "himi_owner_lite_aria.widget.VISUAL_DISPLAY.variant_$item.id.button",
59161
+ "body": {
59162
+ "package": "himalaya",
59163
+ "type": "Button",
59164
+ "props": {
59165
+ "title": "$item.buttonLabel",
59166
+ "style": "secondary",
59167
+ "size": "sm",
59168
+ "action": {
59169
+ "actionId": "tier5.himiOwnerLiteAria.widgetRespond",
59170
+ "params": {
59171
+ "value": "$item.id"
59172
+ }
59173
+ }
59174
+ }
59175
+ },
59176
+ "modifiers": {
59177
+ "hiddenPath": "$item.buttonHidden"
59178
+ }
59179
+ }
59180
+ ]
59181
+ }
59182
+ }
59183
+ }
59184
+ ]
59185
+ }
59186
+ }
59187
+ }
59188
+ ]
59189
+ }
59190
+ }
59191
+ },
58936
59192
  {
58937
59193
  "id": "",
58938
59194
  "body": {
@@ -60164,7 +60420,23 @@ const SCREENS = runtimeSetting({
60164
60420
  },
60165
60421
  "modifiers": {
60166
60422
  "accessibilityLabel": "$service:himiOwnerLiteAria.widget.text",
60167
- "hiddenPath": "$service:himiOwnerLiteAria.widget.chartPieHidden"
60423
+ "hiddenPath": "$service:himiOwnerLiteAria.widget.chartPlainPieHidden"
60424
+ }
60425
+ },
60426
+ {
60427
+ "id": "himi_owner_lite_aria.widget.CHART.doughnut",
60428
+ "body": {
60429
+ "package": "himalaya",
60430
+ "type": "DonutChart",
60431
+ "props": {
60432
+ "slicesPath": "$service:himiOwnerLiteAria.widget.slices",
60433
+ "thickness": 18,
60434
+ "size": 160
60435
+ }
60436
+ },
60437
+ "modifiers": {
60438
+ "accessibilityLabel": "$service:himiOwnerLiteAria.widget.text",
60439
+ "hiddenPath": "$service:himiOwnerLiteAria.widget.chartRingHidden"
60168
60440
  }
60169
60441
  },
60170
60442
  {
@@ -60183,6 +60455,21 @@ const SCREENS = runtimeSetting({
60183
60455
  "hiddenPath": "$service:himiOwnerLiteAria.widget.chartSeriesHidden"
60184
60456
  }
60185
60457
  },
60458
+ {
60459
+ "id": "himi_owner_lite_aria.widget.CHART.legend",
60460
+ "body": {
60461
+ "package": "himalaya",
60462
+ "type": "Legend",
60463
+ "props": {
60464
+ "itemsPath": "$service:himiOwnerLiteAria.widget.chartLegend",
60465
+ "orientation": "vertical"
60466
+ }
60467
+ },
60468
+ "modifiers": {
60469
+ "accessibilityLabel": "$service:himiOwnerLiteAria.widget.text",
60470
+ "hiddenPath": "$service:himiOwnerLiteAria.widget.chartPieHidden"
60471
+ }
60472
+ },
60186
60473
  {
60187
60474
  "id": "",
60188
60475
  "body": {
@@ -60275,6 +60562,16 @@ const SCREENS = runtimeSetting({
60275
60562
  }
60276
60563
  ]
60277
60564
  }
60565
+ },
60566
+ "modifiers": {
60567
+ "theme": {
60568
+ "colors": {
60569
+ "ariaChartBlue": "#126EFF",
60570
+ "ariaChartGreen": "#26A659",
60571
+ "ariaChartPurple": "#9926D6",
60572
+ "ariaChartGray": "#878AA6"
60573
+ }
60574
+ }
60278
60575
  }
60279
60576
  },
60280
60577
  "accessibilityAnnouncement": "$service:himiOwnerLiteAria.widget.text"
@@ -2570,6 +2570,14 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
2570
2570
  const duplicate = (s: AriaChatState, content: string): boolean =>
2571
2571
  isDuplicateSend(s, content) || inFlightSend === content;
2572
2572
 
2573
+ const withWidgetQuery = (w: WidgetView, query: string): WidgetView => {
2574
+ const needle = query.toLowerCase();
2575
+ const suggestions = !needle ? w.options : w.options.filter((option) =>
2576
+ `${option.title} ${option.subtitle} ${option.text}`.toLowerCase().includes(needle));
2577
+ return { ...w, query, suggestions, suggestionsEmpty: suggestions.length === 0,
2578
+ noMatches: !!needle && suggestions.length === 0 };
2579
+ };
2580
+
2573
2581
  /** Fold a freshly read server transcript into the observed-count marks.
2574
2582
  * Takes the RAW messages, not the bubbles — see `countTurns`. */
2575
2583
  /** Carry the owner's SELECTIONS across a rebuild.
@@ -2625,7 +2633,10 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
2625
2633
  }
2626
2634
  // The line explaining an option that could not open is the answer to a
2627
2635
  // tap, and a poll is not a reason to take it back.
2628
- const kept = prev.notice ? { ...next, notice: prev.notice, noticeHidden: prev.noticeHidden } : next;
2636
+ const withNotice = prev.notice ? { ...next, notice: prev.notice, noticeHidden: prev.noticeHidden } : next;
2637
+ // The same ask retains the owner's search, just as it retains selection.
2638
+ // Derive suggestions from the new canonical rows, never stale old objects.
2639
+ const kept = next.kind === "AUTOCOMPLETE" ? withWidgetQuery(withNotice, prev.query) : withNotice;
2629
2640
  if (!prev.options.some((o, i) => o.selected !== next.options[i].selected)) return kept;
2630
2641
  const options = next.options.map((o, i) => ({ ...o, selected: prev.options[i].selected }));
2631
2642
  return reselect({ ...kept, options }, options);
@@ -2683,9 +2694,9 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
2683
2694
  const pictureUrl = isWixMediaHost(hostOf(item.imageUrl)) ? item.imageUrl : "";
2684
2695
  return {
2685
2696
  id: `o${i}`, value: item.id, title: item.title, subtitle: item.subtitle,
2686
- subtitleHidden: !item.subtitle, text: "", textHidden: true,
2697
+ subtitleHidden: !item.subtitle, text: "", textHidden: true, badge: "", badgeHidden: true,
2687
2698
  pictureUrl, pictureHidden: !pictureUrl, buttonLabel: "", buttonHidden: true,
2688
- listText: "", listHidden: true, selected: false, respondText: "",
2699
+ listText: "", benefits: [], listHidden: true, selected: false, respondText: "",
2689
2700
  actionUrl: "", actionScreenId: "", notSupportedText: "",
2690
2701
  };
2691
2702
  });
@@ -3499,7 +3510,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
3499
3510
  // explains the refusal.
3500
3511
  const limitText = limitReached ? limitReachedText(w.maxSelected) : "";
3501
3512
  const disabled = w.submitting || (w.requiresSelection && chosen === 0);
3502
- return {
3513
+ const selected = {
3503
3514
  ...w,
3504
3515
  options,
3505
3516
  limitReached,
@@ -3508,6 +3519,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
3508
3519
  primaryDisabled: disabled,
3509
3520
  primaryEnabled: !disabled,
3510
3521
  };
3522
+ return w.kind === "AUTOCOMPLETE" ? withWidgetQuery(selected, w.query) : selected;
3511
3523
  };
3512
3524
 
3513
3525
  const impl: AriaChatCore = {
@@ -3713,6 +3725,10 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
3713
3725
  const wasFast = s.status === "sending";
3714
3726
  // Read BEFORE the await, compared after: see `turnSeq`.
3715
3727
  const seq = turnSeq;
3728
+ // Empty and failed reads retain the last good widget. Edits may have
3729
+ // committed while this request was pending, so retain its current query.
3730
+ const retainWidgetEdits = (): AriaChatState => s.widget.kind === "AUTOCOMPLETE" && committed
3731
+ ? { ...s, widget: committed.widget } : s;
3716
3732
  try {
3717
3733
  const gm = await getConversationMessages(s._convId);
3718
3734
  const raw = messagesOf(gm);
@@ -3746,9 +3762,9 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
3746
3762
  if (!raw.length) {
3747
3763
  if (slowed && live.takeActivity() === 0) {
3748
3764
  slowed = false;
3749
- return cadence(s, o.poll.fastMs, o.poll.streamMs ?? null);
3765
+ return cadence(retainWidgetEdits(), o.poll.fastMs, o.poll.streamMs ?? null);
3750
3766
  }
3751
- return s;
3767
+ return retainWidgetEdits();
3752
3768
  }
3753
3769
  const { messages, pending } = reconcile(server, s._pending, s._pendingAfter, you(), s._pendingId, s._pendingAttach ?? [], s._pendingRefs);
3754
3770
  // …but still never PAINT nothing over something. `messages` is only
@@ -3789,9 +3805,15 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
3789
3805
  const withItems = await withWidgetItems(withJob);
3790
3806
  if (turnSeq !== seq || abortingEpochs.has(seq)) return undefined;
3791
3807
  // Slow the ladder the moment the turn is over; leave it alone otherwise.
3808
+ // A query or selection may land while this transcript read is parked.
3809
+ // Retain those latest edits only when the canonical widget is still the
3810
+ // same ask; carrySelection resets them for new rows/kinds/options.
3811
+ const withCurrentQuery = withItems.widget.kind === "AUTOCOMPLETE" && committed
3812
+ ? { ...withItems, widget: carrySelection(withItems.widget, committed.widget) }
3813
+ : withItems;
3792
3814
  if (ended && wasFast) {
3793
3815
  slowed = false;
3794
- return cadence(withItems, o.poll.idleMs, o.poll.fastMs);
3816
+ return cadence(withCurrentQuery, o.poll.idleMs, o.poll.fastMs);
3795
3817
  }
3796
3818
  // The ladder runs slow only while the stream proves it is delivering.
3797
3819
  // A whole slow interval in which no frame changed anything is a stalled
@@ -3800,10 +3822,9 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
3800
3822
  // rate. No clock needed: this tick IS the interval.
3801
3823
  if (slowed && live.takeActivity() === 0) {
3802
3824
  slowed = false;
3803
- return cadence(withItems, o.poll.fastMs, o.poll.streamMs ?? null);
3825
+ return cadence(withCurrentQuery, o.poll.fastMs, o.poll.streamMs ?? null);
3804
3826
  }
3805
- return withItems;
3806
-
3827
+ return withCurrentQuery;
3807
3828
  } catch {
3808
3829
  // A read that THREW is exactly as stale as one that resolved: if a turn
3809
3830
  // boundary moved on while it was in flight, returning `s` merges the
@@ -3811,7 +3832,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
3811
3832
  // guard above prevents on the success path. Only a still-current tick
3812
3833
  // may hold the ladder up by keeping the state as it is.
3813
3834
  if (turnSeq !== seq || abortingEpochs.has(seq)) return undefined;
3814
- return s;
3835
+ return retainWidgetEdits();
3815
3836
  }
3816
3837
  },
3817
3838
 
@@ -3823,27 +3844,8 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
3823
3844
 
3824
3845
  widgetQuery: (s, arg) => {
3825
3846
  const q = typeof arg === "string" ? arg.trim() : firstString(arg?.value, arg?.text);
3826
- const w = s.widget;
3827
- const needle = q.toLowerCase();
3828
- // Match on everything the owner can SEE. Matching only `title` makes a
3829
- // subtitle that says "Downtown branch" unsearchable, which reads as the
3830
- // filter being broken rather than as a deliberate scope.
3831
- const suggestions = !needle
3832
- ? w.options
3833
- : w.options.filter((o) =>
3834
- `${o.title} ${o.subtitle} ${o.text}`.toLowerCase().includes(needle));
3835
- return {
3836
- ...s,
3837
- widget: {
3838
- ...w,
3839
- query: q,
3840
- suggestions,
3841
- suggestionsEmpty: suggestions.length === 0,
3842
- // Typed something and got nothing is a different state from having
3843
- // typed nothing at all, and says something different to the owner.
3844
- noMatches: !!needle && suggestions.length === 0,
3845
- },
3846
- };
3847
+ // Match title, subtitle and text with the same semantics after a poll.
3848
+ return { ...s, widget: withWidgetQuery(s.widget, q) };
3847
3849
  },
3848
3850
 
3849
3851
  widgetAnswer: (s, arg) => {
@@ -4026,7 +4028,11 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
4026
4028
  // to answer for it. The clone's positional form can only say `value`, so
4027
4029
  // a row id there is tried only once no option has claimed it.
4028
4030
  const sectionId = typeof arg === "object" ? firstString(arg?.section) : "";
4029
- const option = sectionId ? undefined : w.options.find((o) => o.value === which);
4031
+ // Display cards may have no wire selection value. Their descriptor names
4032
+ // them by stable row id; selectable kinds retain the wire-value contract.
4033
+ const option = sectionId ? undefined : w.kind === "VISUAL_DISPLAY"
4034
+ ? w.options.find((o) => o.id === which) ?? w.options.find((o) => o.value === which)
4035
+ : w.options.find((o) => o.value === which);
4030
4036
  const section = sectionId
4031
4037
  ? w.rows.find((r) => r.id === sectionId)
4032
4038
  : (option ? undefined : w.rows.find((r) => r.id === which));
@@ -59,14 +59,24 @@ export const WIDGET_KINDS: readonly WidgetKind[] = [
59
59
 
60
60
  /** One selectable row.
61
61
  *
62
- * Every field is present and scalar, always — never optional, never null. That
63
- * is not tidiness, it is a hard requirement of the renderer: `$item.`
64
- * substitutes WHOLE prop values only, a missing field fails substitution for
65
- * the entire row, and a non-scalar stringifies to `""`. The existing transcript
62
+ * Display fields are scalar because ordinary `$item.` substitutions consume
63
+ * whole scalar prop values. Benefit arrays instead use a nested repeater's
64
+ * `itemsPath: "$item.benefits"`, which expands each benefit in its own scope. The existing transcript
66
65
  * bubbles already carry fixed `link1..3Label/Url/Hidden` slots for exactly this
67
66
  * reason (`aria-chat.ts`). A row that omits `subtitle` renders nothing at all
68
67
  * rather than rendering without a subtitle. */
68
+ /** One PLANS benefit. `color` is the semantic tint; the three `*Hidden` flags pick which of the
69
+ * descriptor's statically tinted icons shows it, because a token prop cannot be `$item.`-bound
70
+ * and still pass static token validation. */
71
+ export interface WidgetBenefit {
72
+ text: string; icon: string; color: string; colorHex: string;
73
+ successHidden: boolean; plainHidden: boolean; customHidden: boolean;
74
+ }
75
+
69
76
  export interface WidgetOption {
77
+ benefits?: WidgetBenefit[];
78
+ /** A visual variant card badge, absent on ordinary selectable rows. */
79
+ badge?: string;
70
80
  title: string;
71
81
  subtitle: string;
72
82
  text: string;
@@ -294,6 +304,7 @@ export function optionsOf(o: Record<string, unknown> | null): WidgetOption[] {
294
304
  const sections = objectsAt(opt, "sections");
295
305
  const first = sections[0] ?? null;
296
306
  return {
307
+ ...(badgeTextOf(opt) ? {badge: badgeTextOf(opt)} : {}),
297
308
  title: textAt(opt, "title") || textAt(first, "title"),
298
309
  subtitle: textAt(opt, "subtitle") || textAt(first, "subtitle"),
299
310
  text: textAt(opt, "text"),
@@ -306,6 +317,17 @@ export function optionsOf(o: Record<string, unknown> | null): WidgetOption[] {
306
317
  actionScreenId: stringAt(objectAt(action?.screen), "id"),
307
318
  notSupportedText: textAt(objectAt(action?.notSupported), "description"),
308
319
  listText: objectsAt(opt, "list").map((x) => textOf(x)).filter(Boolean).join("\n"),
320
+ ...(objectsAt(opt, "list").length ? { benefits: objectsAt(opt, "list").map((entry) => {
321
+ const spec = objectAt(entry.icon);
322
+ const name = stringAt(spec, "name");
323
+ const color = stringAt(spec, "color");
324
+ const hex = /^#[0-9a-f]{6}$/i.test(color);
325
+ const tint = hex ? "accentPrimary" : !name || name === "Confirm" ? "success" : "textPrimary";
326
+ return { text: stripMarkdown(textOf(entry)),
327
+ icon: name === "Gift" ? "gift" : name === "Send" ? "paper_plane" : "check",
328
+ color: tint, colorHex: hex ? color : "",
329
+ successHidden: tint !== "success", plainHidden: tint !== "textPrimary", customHidden: tint !== "accentPrimary" };
330
+ }) } : {}),
309
331
  };
310
332
  });
311
333
  }