@wix/himalaya-cli 0.1028.0 → 0.1029.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 (29) hide show
  1. package/dist/cli.mjs +106 -24
  2. package/dist/preview-assets.generated.bin +0 -0
  3. package/package.json +2 -2
  4. package/templates/beauty-salon/dev/index.ts +15 -3
  5. package/templates/beauty-salon-owner/dev/index.ts +22 -27
  6. package/templates/beauty-salon-owner/tier5-src/_shared/aria-chat.ts +23 -3
  7. package/templates/branded-lite/dev/index.ts +32 -4
  8. package/templates/community-club-owner/dev/index.ts +22 -27
  9. package/templates/community-club-owner/tier5-src/_shared/aria-chat.ts +23 -3
  10. package/templates/events-meetups-owner/dev/index.ts +22 -27
  11. package/templates/events-meetups-owner/tier5-src/_shared/aria-chat.ts +23 -3
  12. package/templates/gym-fitness/dev/index.ts +15 -3
  13. package/templates/gym-fitness-owner/dev/index.ts +22 -27
  14. package/templates/gym-fitness-owner/tier5-src/_shared/aria-chat.ts +23 -3
  15. package/templates/nonprofit-cause-owner/dev/index.ts +22 -27
  16. package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-chat.ts +23 -3
  17. package/templates/owner-lite/dev/index.ts +22 -27
  18. package/templates/owner-lite/tier5-src/_shared/aria-chat.ts +23 -3
  19. package/templates/podcast-creator-owner/dev/index.ts +22 -27
  20. package/templates/podcast-creator-owner/tier5-src/_shared/aria-chat.ts +23 -3
  21. package/templates/portfolio-creative-owner/dev/index.ts +22 -27
  22. package/templates/portfolio-creative-owner/tier5-src/_shared/aria-chat.ts +23 -3
  23. package/templates/restaurant-owner/dev/index.ts +22 -27
  24. package/templates/restaurant-owner/tier5-src/_shared/aria-chat.ts +23 -3
  25. package/templates/retail-store-owner/dev/index.ts +22 -27
  26. package/templates/retail-store-owner/tier5-src/_shared/aria-chat.ts +23 -3
  27. package/templates/yoga-studio/dev/index.ts +15 -1
  28. package/templates/yoga-studio-owner/dev/index.ts +22 -27
  29. package/templates/yoga-studio-owner/tier5-src/_shared/aria-chat.ts +23 -3
package/dist/cli.mjs CHANGED
@@ -5351,6 +5351,7 @@ function walk(node, registry, path2, out) {
5351
5351
  var SYSTEM_FONT_FAMILY, TONAL_ELEVATION_ALPHA;
5352
5352
  var init_tokens = __esm({
5353
5353
  "../../core/ts/src/tokens.ts"() {
5354
+ "use strict";
5354
5355
  SYSTEM_FONT_FAMILY = "$system";
5355
5356
  TONAL_ELEVATION_ALPHA = [0, 0.05, 0.08, 0.11, 0.12, 0.14];
5356
5357
  }
@@ -5377,6 +5378,7 @@ function resolveTranslation(value, catalog, sourceCatalog) {
5377
5378
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
5378
5379
  var init_bindings = __esm({
5379
5380
  "../../core/ts/src/bindings.ts"() {
5381
+ "use strict";
5380
5382
  BINDING_SIGILS = ["service", "flag", "item", "t"];
5381
5383
  BINDING_SIGIL_RE = new RegExp(
5382
5384
  `^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
@@ -22588,19 +22590,24 @@ function currentOrigin() {
22588
22590
  }
22589
22591
  }
22590
22592
  function webviewFrameSandbox(url, grants2 = [], pageOrigin = currentOrigin()) {
22593
+ const target = classifyEmbedUrl(url, pageOrigin);
22594
+ if (!target || !NETWORK_SCHEMES.has(target.protocol)) return null;
22595
+ const tokens = ["allow-scripts", ...grants2.filter((g) => g !== "allow-scripts" && g !== "allow-same-origin")];
22596
+ if (target.crossOrigin) tokens.splice(1, 0, "allow-same-origin");
22597
+ return { src: target.crossOrigin ? target.href : url, sandbox: tokens.join(" ") };
22598
+ }
22599
+ function classifyEmbedUrl(url, pageOrigin = currentOrigin()) {
22591
22600
  const parsed = url.replace(/^[\u0000-\u0020]+|[\u0000-\u0020]+$/g, "").replace(/[\t\n\r]/g, "");
22592
22601
  const absolute = /^[a-z][a-z0-9+.-]*:/i.test(parsed);
22602
+ const schemeRelative = !absolute && /^[\\/]{2}/.test(parsed);
22593
22603
  let target;
22594
22604
  try {
22595
22605
  target = new URL(parsed, pageOrigin ?? "http://relative.invalid");
22596
22606
  } catch {
22597
22607
  return null;
22598
22608
  }
22599
- if (!NETWORK_SCHEMES.has(target.protocol)) return null;
22600
- const crossOrigin = pageOrigin !== null && absolute && target.origin !== pageOrigin;
22601
- const tokens = ["allow-scripts", ...grants2.filter((g) => g !== "allow-scripts" && g !== "allow-same-origin")];
22602
- if (crossOrigin) tokens.splice(1, 0, "allow-same-origin");
22603
- return { src: crossOrigin ? target.href : url, sandbox: tokens.join(" ") };
22609
+ const crossOrigin = pageOrigin !== null && target.origin !== pageOrigin;
22610
+ return { protocol: target.protocol, href: target.href, absolute, schemeRelative, crossOrigin };
22604
22611
  }
22605
22612
  function isWebviewUrlAllowed(url) {
22606
22613
  return webviewFrameSandbox(url, [], null) !== null;
@@ -22612,6 +22619,36 @@ var init_webviewFrame = __esm({
22612
22619
  }
22613
22620
  });
22614
22621
 
22622
+ // ../../core/web-renderer/src/pdfSource.ts
22623
+ function pdfEmbedPlan(url, pageOrigin = currentOrigin()) {
22624
+ const target = classifyEmbedUrl(url, pageOrigin);
22625
+ if (!target) return null;
22626
+ if (target.protocol === "http:" || target.protocol === "https:") {
22627
+ if (pageOrigin === null) {
22628
+ if (target.absolute) return { mode: "direct", src: target.href };
22629
+ if (target.schemeRelative) return { mode: "direct", src: target.href.replace(/^https?:/, "") };
22630
+ return null;
22631
+ }
22632
+ return { mode: target.crossOrigin ? "direct" : "retype", src: target.href };
22633
+ }
22634
+ if (target.protocol === "blob:" && pageOrigin !== null && !target.crossOrigin) {
22635
+ return { mode: "retype", src: target.href };
22636
+ }
22637
+ return null;
22638
+ }
22639
+ async function retypeAsPdf(src, fetchImpl = fetch) {
22640
+ const res = await fetchImpl(src, { credentials: "same-origin" });
22641
+ if (!res.ok) return null;
22642
+ if (/html|xml/i.test(res.headers.get("content-type") ?? "")) return null;
22643
+ const body = await res.blob();
22644
+ return URL.createObjectURL(body.slice(0, body.size, "application/pdf"));
22645
+ }
22646
+ var init_pdfSource = __esm({
22647
+ "../../core/web-renderer/src/pdfSource.ts"() {
22648
+ init_webviewFrame();
22649
+ }
22650
+ });
22651
+
22615
22652
  // ../../core/web-renderer/src/a11y/controls.tsx
22616
22653
  import React3 from "react";
22617
22654
  import { jsx as jsx4, jsxs } from "react/jsx-runtime";
@@ -24314,6 +24351,14 @@ function useResolvedMediaItems(items, ctx) {
24314
24351
  return out ?? it;
24315
24352
  }), [items, values, ctx]);
24316
24353
  }
24354
+ function sourceFragment(src) {
24355
+ if (!src) return "";
24356
+ try {
24357
+ return new URL(src).hash;
24358
+ } catch {
24359
+ return "";
24360
+ }
24361
+ }
24317
24362
  function isNativeInteractiveElement(el) {
24318
24363
  const props = el.props;
24319
24364
  return typeof el.type === "string" && (["button", "input", "select", "textarea"].includes(el.type) || el.type === "a" && typeof props.href === "string");
@@ -25877,6 +25922,7 @@ var init_renderNode = __esm({
25877
25922
  init_actionParams();
25878
25923
  init_icons();
25879
25924
  init_webviewFrame();
25925
+ init_pdfSource();
25880
25926
  init_icon_catalog_generated();
25881
25927
  init_controls();
25882
25928
  init_modalStack();
@@ -27054,8 +27100,32 @@ var init_renderNode = __esm({
27054
27100
  PdfViewNode = withExtras("PdfViewNode", ({ props, ctx, wrap }) => {
27055
27101
  const boundUrl = useResolvedScalar(props.url != null && isBinding(props.url) ? props.url : void 0, ctx);
27056
27102
  const url = props.url != null && isBinding(props.url) ? boundUrl : props.url;
27057
- const rawUrl = typeof url === "string" && !isBinding(url) ? url.trim() : "";
27058
- const ok = /^(https?:|blob:|\/)/i.test(rawUrl);
27103
+ const rawUrl = typeof url === "string" && !isBinding(url) ? url : "";
27104
+ const plan = rawUrl ? pdfEmbedPlan(rawUrl) : null;
27105
+ const retypeSrc = plan?.mode === "retype" ? plan.src : null;
27106
+ const [retyped, setRetyped] = React5.useState(null);
27107
+ React5.useEffect(() => {
27108
+ if (!retypeSrc) return;
27109
+ let live2 = true;
27110
+ let made = null;
27111
+ const settle = (u) => {
27112
+ made = u;
27113
+ if (!live2) {
27114
+ if (u) URL.revokeObjectURL(u);
27115
+ return;
27116
+ }
27117
+ setRetyped({ from: retypeSrc, url: u });
27118
+ };
27119
+ retypeAsPdf(retypeSrc).then(settle, () => settle(null));
27120
+ return () => {
27121
+ live2 = false;
27122
+ if (made) URL.revokeObjectURL(made);
27123
+ setRetyped((cur) => cur?.from === retypeSrc ? null : cur);
27124
+ };
27125
+ }, [retypeSrc]);
27126
+ const settled = retyped && retyped.from === retypeSrc ? retyped : null;
27127
+ const src = plan?.mode === "direct" ? plan.src : settled?.url ?? null;
27128
+ const loading = plan?.mode === "retype" && !settled;
27059
27129
  const ph = typeof props.height === "number" && props.height > 0 ? props.height : void 0;
27060
27130
  const frame = {
27061
27131
  width: "100%",
@@ -27066,7 +27136,19 @@ var init_renderNode = __esm({
27066
27136
  overflow: "hidden",
27067
27137
  ...wrap
27068
27138
  };
27069
- if (!ok) {
27139
+ if (loading) {
27140
+ return /* @__PURE__ */ jsx6(
27141
+ "div",
27142
+ {
27143
+ "data-himalaya-pdf": "loading",
27144
+ role: "img",
27145
+ "aria-label": "loading document",
27146
+ "aria-busy": "true",
27147
+ style: { ...frame, background: "#f4f4f4" }
27148
+ }
27149
+ );
27150
+ }
27151
+ if (!src) {
27070
27152
  return /* @__PURE__ */ jsx6(
27071
27153
  "div",
27072
27154
  {
@@ -27082,7 +27164,7 @@ var init_renderNode = __esm({
27082
27164
  "object",
27083
27165
  {
27084
27166
  "data-himalaya-pdf": "document",
27085
- data: rawUrl + startPage,
27167
+ data: src + (plan?.mode === "retype" ? startPage || sourceFragment(retypeSrc) : startPage),
27086
27168
  type: "application/pdf",
27087
27169
  "aria-label": typeof props.title === "string" && props.title ? props.title : "PDF document",
27088
27170
  style: frame
@@ -49241,21 +49323,21 @@ var init_preview_assets_generated = __esm({
49241
49323
  "../../packages/serve/src/preview-assets.generated.ts"() {
49242
49324
  init_preview_assets_format();
49243
49325
  PREVIEW_ASSET_MANIFEST = [
49244
- { key: "assets/index-Dn3oPAne.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1044844, sha256: "7b781f264f2aac500b00ea4def7cb6f3c19e46dd4ebb3b797de5da8386056543" },
49245
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1044852, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
49246
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1049462, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
49247
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1538046, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
49248
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1628970, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
49249
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1719954, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
49250
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1810934, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
49251
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1901806, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
49252
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1992786, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
49253
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2101422, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
49254
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2210506, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
49255
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2319554, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
49256
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2427254, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
49257
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2515362, length: 4947, sha256: "25d01e5f772c7b782ca7c68f997765d3838295369054c0855ba63320d071ec22" },
49258
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2520309, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
49326
+ { key: "assets/index---zsKTyk.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1046198, sha256: "4ca35bb22d26776fdb371a4cfb2137ecbb7bd03924695269ac6364171187fe89" },
49327
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1046206, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
49328
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1050816, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
49329
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1539400, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
49330
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1630324, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
49331
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1721308, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
49332
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1812288, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
49333
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1903160, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
49334
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1994140, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
49335
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2102776, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
49336
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2211860, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
49337
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2320908, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
49338
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2428608, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
49339
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2516716, length: 4947, sha256: "684c954fbc5b82e4513932286c45f00f08b2d988c71614f0700f3cc57716fa84" },
49340
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2521663, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
49259
49341
  ];
49260
49342
  }
49261
49343
  });
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1028.0",
3
+ "version": "0.1029.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -65,5 +65,5 @@
65
65
  "artifactId": "himalaya-cli"
66
66
  }
67
67
  },
68
- "falconPackageHash": "59c5cc0524f87f37cc078c95033304d90d56388bf25c7622625cbe9c"
68
+ "falconPackageHash": "785b6d68758b001717f983ccbdad398a5765dced9bf5544af3865811"
69
69
  }
@@ -144,10 +144,22 @@ const SCREENS = runtimeSetting({
144
144
  }
145
145
  },
146
146
  "cornerRadius": 8,
147
- "hiddenPath": "$service:himiBrandedHome.hideHeroSkeleton",
148
- "padding": {
149
- "bottom": 16
147
+ "hiddenPath": "$service:himiBrandedHome.hideHeroSkeleton"
148
+ }
149
+ },
150
+ {
151
+ "id": "himi_branded_home.salon.hero_skeleton.gap",
152
+ "body": {
153
+ "package": "himalaya",
154
+ "type": "VStack",
155
+ "props": {
156
+ "children": []
150
157
  }
158
+ },
159
+ "modifiers": {
160
+ "height": 16,
161
+ "fillMaxWidth": true,
162
+ "hiddenPath": "$service:himiBrandedHome.hideHeroSkeleton"
151
163
  }
152
164
  },
153
165
  {
@@ -55191,7 +55191,7 @@ const SCREENS = runtimeSetting({
55191
55191
  "spacing": 3,
55192
55192
  "children": [
55193
55193
  {
55194
- "id": "himi_owner_lite_aria.msg_$item.id.who",
55194
+ "id": "himi_owner_lite_aria.msg_$item.id.content",
55195
55195
  "body": {
55196
55196
  "package": "himalaya",
55197
55197
  "type": "HStack",
@@ -55210,43 +55210,34 @@ const SCREENS = runtimeSetting({
55210
55210
  }
55211
55211
  },
55212
55212
  {
55213
- "id": "",
55213
+ "id": "himi_owner_lite_aria.msg_$item.id.bubble",
55214
55214
  "body": {
55215
55215
  "package": "himalaya",
55216
55216
  "type": "Text",
55217
55217
  "props": {
55218
- "text": "$item.who",
55219
- "style": "footnote",
55220
- "color": "textSecondary",
55221
- "maxLines": 1
55218
+ "text": "$item.content",
55219
+ "style": "body",
55220
+ "maxLines": 50
55222
55221
  }
55223
- }
55224
- }
55225
- ]
55226
- }
55227
- }
55228
- },
55229
- {
55230
- "id": "himi_owner_lite_aria.msg_$item.id.content",
55231
- "body": {
55232
- "package": "himalaya",
55233
- "type": "HStack",
55234
- "props": {
55235
- "spacing": 0,
55236
- "children": [
55237
- {
55238
- "id": "",
55239
- "body": {
55240
- "package": "himalaya",
55241
- "type": "Spacer",
55242
- "props": {}
55243
55222
  },
55244
55223
  "modifiers": {
55224
+ "accessibilityLabel": "$item.spoken",
55225
+ "padding": {
55226
+ "leading": 16,
55227
+ "trailing": 16,
55228
+ "top": 8,
55229
+ "bottom": 8
55230
+ },
55231
+ "background": {
55232
+ "token": "surfaceSecondary"
55233
+ },
55234
+ "cornerRadius": 20,
55235
+ "maxWidth": 300,
55245
55236
  "hiddenPath": "$item.notUser"
55246
55237
  }
55247
55238
  },
55248
55239
  {
55249
- "id": "",
55240
+ "id": "himi_owner_lite_aria.msg_$item.id.text",
55250
55241
  "body": {
55251
55242
  "package": "himalaya",
55252
55243
  "type": "Text",
@@ -55255,6 +55246,10 @@ const SCREENS = runtimeSetting({
55255
55246
  "style": "body",
55256
55247
  "maxLines": 50
55257
55248
  }
55249
+ },
55250
+ "modifiers": {
55251
+ "accessibilityLabel": "$item.spoken",
55252
+ "hiddenPath": "$item.notAria"
55258
55253
  }
55259
55254
  }
55260
55255
  ]
@@ -83,6 +83,11 @@ export interface AriaBubble {
83
83
  * that right-aligns the owner renders two templates and collapses one. */
84
84
  notUser: boolean;
85
85
  notAria: boolean;
86
+ /** What a screen reader says for the row: the author, then the words. A
87
+ * screen that draws the speaker only as a bubble and a side of the screen
88
+ * (`messageStyle: 'userBubble'`) has no visible author line to read, and
89
+ * without this the owner's question and Aria's reply sound the same. */
90
+ spoken: string;
86
91
  link1Label: string; link1Url: string; link1Hidden: boolean;
87
92
  link2Label: string; link2Url: string; link2Hidden: boolean;
88
93
  link3Label: string; link3Url: string; link3Hidden: boolean;
@@ -561,9 +566,23 @@ export function disclosedLabel(link: { label: string; url: string }): string {
561
566
  * machinery dies. (Measured: 20 tests, every one of them about polling.) */
562
567
  export { stripMarkdown };
563
568
 
569
+ /** The screen-reader line for a bubble — see `AriaBubble.spoken`. */
570
+ export function spokenOf(who: string, content: string): string {
571
+ return who ? `${who}: ${content}` : content;
572
+ }
573
+
574
+ /** A persisted transcript from before `spoken` existed, made whole. A `$item.`
575
+ * path missing on a row does not substitute, so a row without the field would
576
+ * announce the literal binding until the next rebuild replaced it. */
577
+ export function withSpoken(bubbles: AriaBubble[]): AriaBubble[] {
578
+ return bubbles.every((b) => typeof b.spoken === "string")
579
+ ? bubbles
580
+ : bubbles.map((b) => (typeof b.spoken === "string" ? b : { ...b, spoken: spokenOf(b.who, b.content) }));
581
+ }
582
+
564
583
  /** A greeting or optimistic bubble: no links, all slots defaulted. */
565
584
  export function plainBubble(id: string, role: string, who: string, content: string): AriaBubble {
566
- return { id, role, who, content, notUser: role !== "USER", notAria: role === "USER", ...linkFieldsOf("", role) };
585
+ return { id, role, who, content, notUser: role !== "USER", notAria: role === "USER", spoken: spokenOf(who, content), ...linkFieldsOf("", role) };
567
586
  }
568
587
 
569
588
  /** Server transcript → bubbles. USER text is literal; ASSISTANT replies are
@@ -644,6 +663,7 @@ function bubbleOf(id: string, role: string, who: string, raw: string, writing: b
644
663
  content,
645
664
  notUser: role !== "USER",
646
665
  notAria: role === "USER",
666
+ spoken: spokenOf(who, content),
647
667
  ...linkFieldsOf(prose, role),
648
668
  };
649
669
  }
@@ -2004,7 +2024,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
2004
2024
  // a rehydrated turn there was no ladder at all, leaving "Aria is
2005
2025
  // typing…" on screen with nothing ever fetching the answer.
2006
2026
  if (s._pending || s.status === "sending") {
2007
- const held: AriaChatState = { ...s, status: "sending", prompts: [] };
2027
+ const held: AriaChatState = { ...s, status: "sending", prompts: [], messages: withSpoken(s.messages) };
2008
2028
  return cadence(held, o.poll ? o.poll.fastMs : null, null);
2009
2029
  }
2010
2030
  // …but the self-kick pass runs BEFORE the pack is stashed, so the
@@ -2013,7 +2033,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
2013
2033
  // still the only thing on screen — `prompts` is already recomputed
2014
2034
  // here, and a chip pack that localizes beside a greeting that does not
2015
2035
  // is the visible half of the same bug.
2016
- const messages = isGreetingOnly(s.messages) ? greetingOnly() : s.messages;
2036
+ const messages = isGreetingOnly(s.messages) ? greetingOnly() : withSpoken(s.messages);
2017
2037
  return { ...s, status: "ready", messages, prompts: promptsFor(messages, promptList()) };
2018
2038
  }
2019
2039
  try {
@@ -190,10 +190,22 @@ const SCREENS = runtimeSetting({
190
190
  }
191
191
  },
192
192
  "cornerRadius": 8,
193
- "hiddenPath": "$service:himiBrandedHome.hideHeroSkeleton",
194
- "padding": {
195
- "bottom": 16
193
+ "hiddenPath": "$service:himiBrandedHome.hideHeroSkeleton"
194
+ }
195
+ },
196
+ {
197
+ "id": "himi_branded_home.hero_skeleton.gap",
198
+ "body": {
199
+ "package": "himalaya",
200
+ "type": "VStack",
201
+ "props": {
202
+ "children": []
196
203
  }
204
+ },
205
+ "modifiers": {
206
+ "height": 16,
207
+ "fillMaxWidth": true,
208
+ "hiddenPath": "$service:himiBrandedHome.hideHeroSkeleton"
197
209
  }
198
210
  },
199
211
  {
@@ -294,8 +306,24 @@ const SCREENS = runtimeSetting({
294
306
  "leading": 24,
295
307
  "trailing": 24,
296
308
  "top": 24,
297
- "bottom": 16
309
+ "bottom": 24
298
310
  },
311
+ "fillMaxWidth": true,
312
+ "hiddenPath": "$service:himiBrandedHome.hideHeroFallback"
313
+ }
314
+ },
315
+ {
316
+ "id": "himi_branded_home.hero.gap",
317
+ "body": {
318
+ "package": "himalaya",
319
+ "type": "VStack",
320
+ "props": {
321
+ "children": []
322
+ }
323
+ },
324
+ "modifiers": {
325
+ "height": 16,
326
+ "fillMaxWidth": true,
299
327
  "hiddenPath": "$service:himiBrandedHome.hideHeroFallback"
300
328
  }
301
329
  },
@@ -55191,7 +55191,7 @@ const SCREENS = runtimeSetting({
55191
55191
  "spacing": 3,
55192
55192
  "children": [
55193
55193
  {
55194
- "id": "himi_owner_lite_aria.msg_$item.id.who",
55194
+ "id": "himi_owner_lite_aria.msg_$item.id.content",
55195
55195
  "body": {
55196
55196
  "package": "himalaya",
55197
55197
  "type": "HStack",
@@ -55210,43 +55210,34 @@ const SCREENS = runtimeSetting({
55210
55210
  }
55211
55211
  },
55212
55212
  {
55213
- "id": "",
55213
+ "id": "himi_owner_lite_aria.msg_$item.id.bubble",
55214
55214
  "body": {
55215
55215
  "package": "himalaya",
55216
55216
  "type": "Text",
55217
55217
  "props": {
55218
- "text": "$item.who",
55219
- "style": "footnote",
55220
- "color": "textSecondary",
55221
- "maxLines": 1
55218
+ "text": "$item.content",
55219
+ "style": "body",
55220
+ "maxLines": 50
55222
55221
  }
55223
- }
55224
- }
55225
- ]
55226
- }
55227
- }
55228
- },
55229
- {
55230
- "id": "himi_owner_lite_aria.msg_$item.id.content",
55231
- "body": {
55232
- "package": "himalaya",
55233
- "type": "HStack",
55234
- "props": {
55235
- "spacing": 0,
55236
- "children": [
55237
- {
55238
- "id": "",
55239
- "body": {
55240
- "package": "himalaya",
55241
- "type": "Spacer",
55242
- "props": {}
55243
55222
  },
55244
55223
  "modifiers": {
55224
+ "accessibilityLabel": "$item.spoken",
55225
+ "padding": {
55226
+ "leading": 16,
55227
+ "trailing": 16,
55228
+ "top": 8,
55229
+ "bottom": 8
55230
+ },
55231
+ "background": {
55232
+ "token": "surfaceSecondary"
55233
+ },
55234
+ "cornerRadius": 20,
55235
+ "maxWidth": 300,
55245
55236
  "hiddenPath": "$item.notUser"
55246
55237
  }
55247
55238
  },
55248
55239
  {
55249
- "id": "",
55240
+ "id": "himi_owner_lite_aria.msg_$item.id.text",
55250
55241
  "body": {
55251
55242
  "package": "himalaya",
55252
55243
  "type": "Text",
@@ -55255,6 +55246,10 @@ const SCREENS = runtimeSetting({
55255
55246
  "style": "body",
55256
55247
  "maxLines": 50
55257
55248
  }
55249
+ },
55250
+ "modifiers": {
55251
+ "accessibilityLabel": "$item.spoken",
55252
+ "hiddenPath": "$item.notAria"
55258
55253
  }
55259
55254
  }
55260
55255
  ]
@@ -83,6 +83,11 @@ export interface AriaBubble {
83
83
  * that right-aligns the owner renders two templates and collapses one. */
84
84
  notUser: boolean;
85
85
  notAria: boolean;
86
+ /** What a screen reader says for the row: the author, then the words. A
87
+ * screen that draws the speaker only as a bubble and a side of the screen
88
+ * (`messageStyle: 'userBubble'`) has no visible author line to read, and
89
+ * without this the owner's question and Aria's reply sound the same. */
90
+ spoken: string;
86
91
  link1Label: string; link1Url: string; link1Hidden: boolean;
87
92
  link2Label: string; link2Url: string; link2Hidden: boolean;
88
93
  link3Label: string; link3Url: string; link3Hidden: boolean;
@@ -561,9 +566,23 @@ export function disclosedLabel(link: { label: string; url: string }): string {
561
566
  * machinery dies. (Measured: 20 tests, every one of them about polling.) */
562
567
  export { stripMarkdown };
563
568
 
569
+ /** The screen-reader line for a bubble — see `AriaBubble.spoken`. */
570
+ export function spokenOf(who: string, content: string): string {
571
+ return who ? `${who}: ${content}` : content;
572
+ }
573
+
574
+ /** A persisted transcript from before `spoken` existed, made whole. A `$item.`
575
+ * path missing on a row does not substitute, so a row without the field would
576
+ * announce the literal binding until the next rebuild replaced it. */
577
+ export function withSpoken(bubbles: AriaBubble[]): AriaBubble[] {
578
+ return bubbles.every((b) => typeof b.spoken === "string")
579
+ ? bubbles
580
+ : bubbles.map((b) => (typeof b.spoken === "string" ? b : { ...b, spoken: spokenOf(b.who, b.content) }));
581
+ }
582
+
564
583
  /** A greeting or optimistic bubble: no links, all slots defaulted. */
565
584
  export function plainBubble(id: string, role: string, who: string, content: string): AriaBubble {
566
- return { id, role, who, content, notUser: role !== "USER", notAria: role === "USER", ...linkFieldsOf("", role) };
585
+ return { id, role, who, content, notUser: role !== "USER", notAria: role === "USER", spoken: spokenOf(who, content), ...linkFieldsOf("", role) };
567
586
  }
568
587
 
569
588
  /** Server transcript → bubbles. USER text is literal; ASSISTANT replies are
@@ -644,6 +663,7 @@ function bubbleOf(id: string, role: string, who: string, raw: string, writing: b
644
663
  content,
645
664
  notUser: role !== "USER",
646
665
  notAria: role === "USER",
666
+ spoken: spokenOf(who, content),
647
667
  ...linkFieldsOf(prose, role),
648
668
  };
649
669
  }
@@ -2004,7 +2024,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
2004
2024
  // a rehydrated turn there was no ladder at all, leaving "Aria is
2005
2025
  // typing…" on screen with nothing ever fetching the answer.
2006
2026
  if (s._pending || s.status === "sending") {
2007
- const held: AriaChatState = { ...s, status: "sending", prompts: [] };
2027
+ const held: AriaChatState = { ...s, status: "sending", prompts: [], messages: withSpoken(s.messages) };
2008
2028
  return cadence(held, o.poll ? o.poll.fastMs : null, null);
2009
2029
  }
2010
2030
  // …but the self-kick pass runs BEFORE the pack is stashed, so the
@@ -2013,7 +2033,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
2013
2033
  // still the only thing on screen — `prompts` is already recomputed
2014
2034
  // here, and a chip pack that localizes beside a greeting that does not
2015
2035
  // is the visible half of the same bug.
2016
- const messages = isGreetingOnly(s.messages) ? greetingOnly() : s.messages;
2036
+ const messages = isGreetingOnly(s.messages) ? greetingOnly() : withSpoken(s.messages);
2017
2037
  return { ...s, status: "ready", messages, prompts: promptsFor(messages, promptList()) };
2018
2038
  }
2019
2039
  try {