@wix/himalaya-cli 0.1579.0 → 0.1581.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 (54) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/app-config.d.ts +18 -3
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/builder.d.ts +3 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/motion/src/index.d.ts +51 -5
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/network/src/index.d.ts +39 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/sdk-types/src/capabilities.generated.d.ts +2 -1
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/webview-page/src/capabilities.generated.d.ts +2 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/define-app.d.ts +1 -1
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +12 -0
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/validator.d.ts +6 -0
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/media/src/index.d.ts +1 -0
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/media/src/rendition.d.ts +42 -0
  12. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/audio-record.mjs +1 -0
  13. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/calendar.mjs +1 -0
  14. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/calls.mjs +1 -0
  15. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/contacts.mjs +1 -0
  16. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +1 -0
  17. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/location.mjs +1 -0
  18. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/motion.mjs +23 -2
  19. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/network.mjs +45 -0
  20. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/photos.mjs +1 -0
  21. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdk-types.mjs +1 -0
  22. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +1 -0
  23. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/tasks.mjs +1 -0
  24. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-media.mjs +78 -0
  25. package/dist/authoring-runtime/node_modules/@wix/himalaya/package.json +4 -0
  26. package/dist/cli.mjs +9144 -7459
  27. package/dist/preview-assets.generated.bin +0 -0
  28. package/dist/quickjs-intl.js +462 -0
  29. package/dist/quickjs-web-globals.js +540 -0
  30. package/dist/quickjs-worker-entry.js +313 -0
  31. package/guides/app-config.md +1 -1
  32. package/guides/component-reference.json +5 -3
  33. package/guides/component-reference.md +10 -2
  34. package/guides/designing-himi-screens.md +24 -0
  35. package/guides/himi-json-output.md +30 -1
  36. package/guides/importable-sdks.md +1 -0
  37. package/guides/one-app-section-mappings.md +4 -5
  38. package/guides/one-app-to-himi-app.md +214 -6
  39. package/guides/one-app-viewer.md +2 -2
  40. package/guides/sdk-reference.json +74 -7
  41. package/guides/tier-5-add-a-screen.md +32 -0
  42. package/guides/web-page-sdk-reference.md +1 -0
  43. package/package.json +3 -2
  44. package/templates/beauty-salon/dev/index.ts +197 -304
  45. package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +44 -13
  46. package/templates/branded-lite/dev/index.ts +197 -304
  47. package/templates/branded-lite/l10n/en.json +2 -2
  48. package/templates/branded-lite/tier5-src/himi-store-product/index.ts +49 -17
  49. package/templates/gym-fitness/dev/index.ts +197 -304
  50. package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +44 -13
  51. package/templates/portfolio-creative/dev/index.ts +197 -304
  52. package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +44 -13
  53. package/templates/retail-store/dev/index.ts +197 -304
  54. package/templates/retail-store/tier5-src/himi-store-product/index.ts +49 -17
@@ -0,0 +1,313 @@
1
+ // ../../core/dev-server/src/quickjs-worker-entry.ts
2
+ import { parentPort, workerData } from "node:worker_threads";
3
+ import { pathToFileURL } from "node:url";
4
+ var CTL_READY = 0;
5
+ var CTL_LEN = 1;
6
+ var CTL_SEQ = 2;
7
+ var CTL_WORDS = 4;
8
+ var DATA_OFFSET = CTL_WORDS * 4;
9
+ var sessions = /* @__PURE__ */ new Map();
10
+ var { sab, intlPath, webGlobalsPath } = workerData;
11
+ var webGlobalsInstaller = null;
12
+ async function installWebGlobalsInto(ctx) {
13
+ webGlobalsInstaller ??= (await import(pathToFileURL(webGlobalsPath).href)).installWebGlobals;
14
+ webGlobalsInstaller(ctx);
15
+ }
16
+ var intlInstaller = null;
17
+ async function installIntlInto(ctx) {
18
+ intlInstaller ??= (await import(pathToFileURL(intlPath).href)).installIntl;
19
+ intlInstaller(ctx);
20
+ }
21
+ var ctl = new Int32Array(sab, 0, CTL_WORDS);
22
+ var data = new Uint8Array(sab, DATA_OFFSET);
23
+ var decoder = new TextDecoder();
24
+ var crossingSeq = 0;
25
+ function crossToHost(kind, method, path, body, waitMs, headers = "") {
26
+ const mine = ++crossingSeq;
27
+ const deadline = Date.now() + waitMs;
28
+ Atomics.store(ctl, CTL_SEQ, 0);
29
+ Atomics.store(ctl, CTL_READY, 0);
30
+ parentPort.postMessage({ want: { kind, method, path, body, headers, seq: mine } });
31
+ for (; ; ) {
32
+ const left = deadline - Date.now();
33
+ if (left <= 0) {
34
+ return JSON.stringify({ ok: false, e: `the host transport did not answer within ${waitMs}ms` });
35
+ }
36
+ const res = Atomics.wait(ctl, CTL_READY, 0, left);
37
+ if (res === "timed-out") {
38
+ return JSON.stringify({ ok: false, e: `the host transport did not answer within ${waitMs}ms` });
39
+ }
40
+ if (Atomics.load(ctl, CTL_SEQ) !== mine) {
41
+ Atomics.store(ctl, CTL_READY, 0);
42
+ continue;
43
+ }
44
+ const len = Atomics.load(ctl, CTL_LEN);
45
+ if (len < 0 || len > data.length) {
46
+ return JSON.stringify({ ok: false, e: `the host transport returned an out-of-range length (${len})` });
47
+ }
48
+ return decoder.decode(data.subarray(0, len));
49
+ }
50
+ }
51
+ var modulePromise = null;
52
+ async function realmModule() {
53
+ modulePromise ??= (async () => {
54
+ const { newQuickJSWASMModuleFromVariant } = await import("quickjs-emscripten");
55
+ const variant = await import("@jitl/quickjs-wasmfile-release-sync");
56
+ return newQuickJSWASMModuleFromVariant(variant.default ?? variant);
57
+ })();
58
+ return modulePromise;
59
+ }
60
+ async function boot(msg) {
61
+ const mod = await realmModule();
62
+ const runtime = mod.newRuntime();
63
+ runtime.setMemoryLimit(msg.memoryLimitBytes);
64
+ let deadline = Date.now() + (msg.timeoutMs > 0 ? msg.timeoutMs : 2e3);
65
+ let waiting = false;
66
+ runtime.setInterruptHandler(() => !waiting && Date.now() > deadline);
67
+ const ctx = runtime.newContext();
68
+ let keepAlive = false;
69
+ try {
70
+ const host = ctx.newFunction("__himiWixCall", (mH, pH, bH, hH) => {
71
+ waiting = true;
72
+ try {
73
+ const answer = crossToHost(
74
+ "wix",
75
+ ctx.getString(mH),
76
+ ctx.getString(pH),
77
+ ctx.getString(bH),
78
+ // An explicit per-crossing budget when the caller set one; otherwise the remaining boot
79
+ // time, floored, so a stalled transport still surfaces as a rejected call rather than as
80
+ // the whole boot timing out with no attribution.
81
+ msg.answerTimeoutMs !== void 0 ? msg.answerTimeoutMs : Math.max(1e3, deadline - Date.now()),
82
+ // `undefined` when the guest called with three arguments, which every caller did
83
+ // before per-call site scope existed.
84
+ hH === void 0 ? "" : ctx.getString(hH)
85
+ );
86
+ return ctx.newString(answer);
87
+ } finally {
88
+ waiting = false;
89
+ }
90
+ });
91
+ ctx.setProp(ctx.global, "__himiWixCall", host);
92
+ host.dispose();
93
+ const dispatchHost = ctx.newFunction("__himiDispatch", (aH, pH) => {
94
+ waiting = true;
95
+ try {
96
+ const answer = crossToHost(
97
+ "dispatch",
98
+ "",
99
+ ctx.getString(aH),
100
+ ctx.getString(pH),
101
+ msg.answerTimeoutMs !== void 0 ? msg.answerTimeoutMs : Math.max(1e3, deadline - Date.now())
102
+ );
103
+ return ctx.newString(answer);
104
+ } finally {
105
+ waiting = false;
106
+ }
107
+ });
108
+ ctx.setProp(ctx.global, "__himiDispatch", dispatchHost);
109
+ dispatchHost.dispose();
110
+ const pre = ctx.evalCode(msg.prelude, "himi-sandbox.js");
111
+ if (pre.error) {
112
+ const detail = ctx.dump(pre.error);
113
+ pre.error.dispose();
114
+ throw new Error(`sandbox prelude failed: ${String(detail?.message ?? detail)}`);
115
+ }
116
+ const bridge = pre.value;
117
+ const readback = ctx.getProp(bridge, "readback");
118
+ const dispatch2 = ctx.getProp(bridge, "dispatch");
119
+ const actionsReadback = ctx.getProp(bridge, "actions");
120
+ bridge.dispose();
121
+ if (msg.webGlobals && !msg.hide?.length) await installWebGlobalsInto(ctx);
122
+ if (msg.intl) await installIntlInto(ctx);
123
+ let bundleThrow = null;
124
+ try {
125
+ const res = ctx.evalCode(msg.bundleJs, msg.filename);
126
+ if (res.error) {
127
+ const detail = ctx.dump(res.error);
128
+ res.error.dispose();
129
+ bundleThrow = String(detail?.message ?? detail);
130
+ } else {
131
+ res.value.dispose();
132
+ }
133
+ let actions = "[]";
134
+ let sweepError = null;
135
+ try {
136
+ actions = drain(ctx, runtime, msg.skipLoad ? "himi.quickjs.actions" : "himi.quickjs.loads");
137
+ } catch (e) {
138
+ sweepError = String(e?.message ?? e);
139
+ }
140
+ const services = readbackJson(ctx, readback);
141
+ sessions.set(msg.id, {
142
+ runtime,
143
+ ctx,
144
+ dispatch: dispatch2,
145
+ actions: actionsReadback,
146
+ setDeadline(timeoutMs) {
147
+ deadline = Date.now() + (timeoutMs > 0 ? timeoutMs : 2e3);
148
+ }
149
+ });
150
+ keepAlive = true;
151
+ return { actions, error: bundleThrow ?? sweepError ?? void 0, console: services, sessionId: msg.id };
152
+ } finally {
153
+ readback.dispose();
154
+ if (!keepAlive) {
155
+ dispatch2.dispose();
156
+ actionsReadback.dispose();
157
+ }
158
+ }
159
+ } finally {
160
+ if (!keepAlive) {
161
+ try {
162
+ ctx.dispose();
163
+ } catch {
164
+ }
165
+ try {
166
+ runtime.dispose();
167
+ } catch {
168
+ }
169
+ }
170
+ }
171
+ }
172
+ function dispatch(msg) {
173
+ const session = sessions.get(msg.sessionId);
174
+ if (!session) throw new Error("the contained guest session is no longer available");
175
+ session.setDeadline(msg.timeoutMs);
176
+ const service = session.ctx.newString(msg.service);
177
+ const action = session.ctx.newString(msg.action);
178
+ const args = session.ctx.newString(msg.argsJson);
179
+ try {
180
+ const called = session.ctx.callFunction(
181
+ session.dispatch,
182
+ session.ctx.undefined,
183
+ service,
184
+ action,
185
+ args,
186
+ typeof msg.online === "boolean" ? msg.online ? session.ctx.true : session.ctx.false : session.ctx.undefined
187
+ );
188
+ if (called.error) {
189
+ const detail = session.ctx.dump(called.error);
190
+ called.error.dispose();
191
+ throw new Error(`the guest dispatch could not be started: ${String(detail?.message ?? detail)}`);
192
+ }
193
+ settlePromise(session.ctx, session.runtime, called.value, "guest dispatch");
194
+ drainDetachedJobs(session.ctx, session.runtime);
195
+ return { actions: readbackJson(session.ctx, session.actions) };
196
+ } finally {
197
+ service.dispose();
198
+ action.dispose();
199
+ args.dispose();
200
+ }
201
+ }
202
+ function release(sessionId) {
203
+ const session = sessions.get(sessionId);
204
+ if (!session) return;
205
+ sessions.delete(sessionId);
206
+ try {
207
+ session.dispatch.dispose();
208
+ } catch {
209
+ }
210
+ try {
211
+ session.actions.dispose();
212
+ } catch {
213
+ }
214
+ try {
215
+ session.ctx.dispose();
216
+ } catch {
217
+ }
218
+ try {
219
+ session.runtime.dispose();
220
+ } catch {
221
+ }
222
+ }
223
+ function drain(ctx, runtime, trigger) {
224
+ const trig = ctx.evalCode(`globalThis[${JSON.stringify(trigger)}]`, "himi-actions.js");
225
+ if (trig.error) {
226
+ const detail = ctx.dump(trig.error);
227
+ trig.error.dispose();
228
+ throw new Error(`the guest load sweep could not be started: ${String(detail?.message ?? detail)}`);
229
+ }
230
+ return settlePromise(ctx, runtime, trig.value, "guest load sweep");
231
+ }
232
+ function settlePromise(ctx, runtime, promise, label) {
233
+ try {
234
+ for (let i = 0; i < MAX_PUMP_ROUNDS; i++) {
235
+ const state = ctx.getPromiseState(promise);
236
+ if (state.type === "fulfilled") {
237
+ const out = String(ctx.dump(state.value));
238
+ state.value.dispose();
239
+ return out;
240
+ }
241
+ if (state.type === "rejected") {
242
+ const detail = ctx.dump(state.error);
243
+ state.error.dispose();
244
+ throw new Error(`the ${label} failed: ${String(detail?.message ?? detail)}`);
245
+ }
246
+ state.value?.dispose?.();
247
+ const pending = runtime.executePendingJobs();
248
+ if (pending.error) {
249
+ const detail = ctx.dump(pending.error);
250
+ pending.error.dispose();
251
+ throw new Error(`a guest job threw during the load sweep: ${String(detail?.message ?? detail)}`);
252
+ }
253
+ if (pending.value === 0) {
254
+ const ticked = ctx.evalCode(`globalThis["himi.quickjs.tick"]`, "himi-tick.js");
255
+ if (ticked.error) {
256
+ const detail = ctx.dump(ticked.error);
257
+ ticked.error.dispose();
258
+ throw new Error(`the guest timer pump failed: ${String(detail?.message ?? detail)}`);
259
+ }
260
+ const fired = ctx.dump(ticked.value) === true;
261
+ ticked.value.dispose();
262
+ if (fired) continue;
263
+ throw new Error(
264
+ `the ${label} is pending with no runnable jobs \u2014 a worker awaited something the sandbox never resolves (a capability this realm does not provide)`
265
+ );
266
+ }
267
+ }
268
+ throw new Error(`the ${label} did not settle within ${MAX_PUMP_ROUNDS} job rounds`);
269
+ } finally {
270
+ promise.dispose();
271
+ }
272
+ }
273
+ function drainDetachedJobs(ctx, runtime) {
274
+ for (let i = 0; i < MAX_PUMP_ROUNDS; i++) {
275
+ const pending = runtime.executePendingJobs();
276
+ if (pending.error) {
277
+ const detail = ctx.dump(pending.error);
278
+ pending.error.dispose();
279
+ throw new Error(`a nested guest job threw after dispatch: ${String(detail?.message ?? detail)}`);
280
+ }
281
+ if (pending.value === 0) {
282
+ const ticked = ctx.evalCode(`globalThis["himi.quickjs.tick"]`, "himi-tick.js");
283
+ if (ticked.error) {
284
+ const detail = ctx.dump(ticked.error);
285
+ ticked.error.dispose();
286
+ throw new Error(`the nested guest timer pump failed: ${String(detail?.message ?? detail)}`);
287
+ }
288
+ const fired = ctx.dump(ticked.value) === true;
289
+ ticked.value.dispose();
290
+ if (!fired) return;
291
+ }
292
+ }
293
+ throw new Error(`nested guest actions did not quiesce within ${MAX_PUMP_ROUNDS} job rounds`);
294
+ }
295
+ var MAX_PUMP_ROUNDS = 1e4;
296
+ parentPort.on("message", (msg) => {
297
+ if ("end" in msg) {
298
+ process.exit(0);
299
+ }
300
+ const operation = msg.kind === "warm" ? realmModule().then(() => ({})) : msg.kind === "boot" ? boot(msg) : msg.kind === "dispatch" ? Promise.resolve(dispatch(msg)) : Promise.resolve((release(msg.sessionId), {}));
301
+ operation.then((r) => parentPort.postMessage({ id: msg.id, ...r })).catch((e) => parentPort.postMessage({ id: msg.id, error: String(e?.message ?? e) }));
302
+ });
303
+ function readbackJson(ctx, readback) {
304
+ const called = ctx.callFunction(readback, ctx.undefined);
305
+ if (called.error) {
306
+ const detail = ctx.dump(called.error);
307
+ called.error.dispose();
308
+ throw new Error(String(detail?.message ?? detail));
309
+ }
310
+ const dumped = ctx.dump(called.value);
311
+ called.value.dispose();
312
+ return String(dumped);
313
+ }
@@ -137,7 +137,7 @@ does not include answers `module_not_linked` on device. Some components need one
137
137
  and `himi validate` errors on a screen that uses one the app does not declare.
138
138
  `presigned_upload.putFile` also requires `files` on the calling screen.
139
139
 
140
- App-level capability vocabulary: `camera`, `barcode_scanner`, `biometrics`, `secure_storage`, `share`, `push`, `file_picker`, `files`, `open_url`, `pdf`, `clipboard`, `theme`, `photos`, `presigned_upload`, `ui`, `calendar`, `contacts`, `tasks`, `audio_record`, `audio_record_background`, `calls`, `location`, `location_background`, `motion`, `health`, `media_playback`, `webview`, `browser_auth`, `haptic`, `himi_apps`, `iap`, `keyboard`, `wix_cashier`, `wix_chat`, `live_activity`, `local_database`, `speech`, `speaker_diarization`, `system_intents`, `on_device_ai`, `app_info`, `release_control`, `godot`, `base44`, `animation`.
140
+ App-level capability vocabulary: `camera`, `barcode_scanner`, `biometrics`, `secure_storage`, `share`, `push`, `file_picker`, `files`, `open_url`, `pdf`, `clipboard`, `theme`, `photos`, `presigned_upload`, `ui`, `calendar`, `contacts`, `tasks`, `audio_record`, `audio_record_background`, `calls`, `location`, `location_background`, `motion`, `health`, `media_playback`, `webview`, `browser_auth`, `haptic`, `himi_apps`, `iap`, `keyboard`, `network`, `wix_cashier`, `wix_chat`, `live_activity`, `local_database`, `speech`, `speaker_diarization`, `system_intents`, `on_device_ai`, `app_info`, `release_control`, `godot`, `base44`, `animation`.
141
141
 
142
142
  ## What `himi validate` checks (7 passes)
143
143
 
@@ -154,9 +154,10 @@
154
154
  "propsType": "ButtonProps",
155
155
  "bindings": [
156
156
  "disabledPath",
157
+ "loadingPath",
157
158
  "title"
158
159
  ],
159
- "propsSource": "export interface ButtonProps {\n /** Destructive/cancel role independent of style; unknown values warn and behave as absent. */\n role?: string;\n /** Button label — binding-aware (\"$item.\"/\"$service:\") on web, iOS and Android. */\n title: string;\n action: Action;\n style?: string;\n /** LITERAL disabled state. A `\"$service:…\"` string here fails `validate` (the wire type is\n * bool) — bind `disabledPath` instead. */\n disabled?: boolean;\n /** #3117 — `$service:`/`$item.` binding → a live bool that disables the button while true\n * (e.g. a submit while a request is in flight). Binding-aware. OR'd with `disabled`,\n * `loading` and an enclosing Form's validity: a bound `false` never re-enables a button one\n * of those disabled, and an unresolved binding leaves the literal in charge. */\n disabledPath?: string;\n /** Capability enrichments. */\n leadingIcon?: IconRef;\n trailingIcon?: IconRef;\n loading?: boolean;\n size?: \"sm\" | \"md\" | \"lg\";\n fullWidth?: boolean;\n iconOnly?: boolean;\n /** #1049 — corner radius; reshapes the button (overrides the forced pill). */\n cornerRadius?: number;\n /** #1049 — minimum height (pt/dp/px). */\n minHeight?: number;\n}"
160
+ "propsSource": "export interface ButtonProps {\n /** Destructive/cancel role independent of style; unknown values warn and behave as absent. */\n role?: string;\n /** Button label — binding-aware (\"$item.\"/\"$service:\") on web, iOS and Android. */\n title: string;\n action: Action;\n style?: string;\n /** LITERAL disabled state. A `\"$service:…\"` string here fails `validate` (the wire type is\n * bool) — bind `disabledPath` instead. */\n disabled?: boolean;\n /** #3117 — `$service:`/`$item.` binding → a live bool that disables the button while true\n * (e.g. a submit while a request is in flight). Binding-aware. OR'd with `disabled`,\n * `loading` and an enclosing Form's validity: a bound `false` never re-enables a button one\n * of those disabled, and an unresolved binding leaves the literal in charge. */\n disabledPath?: string;\n /** Capability enrichments. */\n leadingIcon?: IconRef;\n trailingIcon?: IconRef;\n loading?: boolean;\n size?: \"sm\" | \"md\" | \"lg\";\n fullWidth?: boolean;\n iconOnly?: boolean;\n /** #1049 — corner radius; reshapes the button (overrides the forced pill). */\n cornerRadius?: number;\n /** #1049 — minimum height (pt/dp/px). */\n minHeight?: number;\n /** Live loading bool; OR with literal loading. */\n loadingPath?: string;\n}"
160
161
  },
161
162
  {
162
163
  "name": "TextField",
@@ -510,9 +511,10 @@
510
511
  "category": "Navigation primitives",
511
512
  "propsType": "CarouselProps",
512
513
  "bindings": [
514
+ "currentIndexPath",
513
515
  "itemsPath"
514
516
  ],
515
- "propsSource": "export interface CarouselProps {\n items: Component[];\n currentIndex?: number;\n onChange?: Action;\n autoplayIntervalMs?: number;\n /** `$service:` binding → a live array to render as slides (overrides `items`). */\n itemsPath?: string;\n /** 0 (default) = all slides mounted; N ⇒ mount current ± N, unmount the rest. */\n liveWindow?: number;\n}"
517
+ "propsSource": "export interface CarouselProps {\n /** Opt-in paging for itemsPath; absent preserves the horizontal card rail. */\n paged?: boolean;\n /** Worker-driven page index; clamped to the current page count. */\n currentIndexPath?: string;\n items: Component[];\n currentIndex?: number;\n onChange?: Action;\n autoplayIntervalMs?: number;\n /** `$service:` binding → a live array to render as slides (overrides `items`). */\n itemsPath?: string;\n /** 0 (default) = all slides mounted; N ⇒ mount current ± N, unmount the rest. */\n liveWindow?: number;\n}"
516
518
  },
517
519
  {
518
520
  "name": "Pager",
@@ -1262,7 +1264,7 @@
1262
1264
  "ScreenDescriptor": "export interface ScreenDescriptor {\n screenId: string;\n renderer: RendererKind;\n title?: string;\n /** Present when `renderer` is `sdui_intent` or `sdui_layout`. */\n payload?: Component;\n /** A component pinned ABOVE the scrolling payload (sticky tab-header / toolbar).\n * Rendered by the host outside the screen scroll, so it stays put as content\n * scrolls. Optional; absent = no sticky header. */\n stickyHeader?: Component;\n /** A component pinned BELOW the scrolling payload (sticky footer — price/confirm\n * bar, peeking bottom-sheet). Rendered outside the screen scroll by the host. */\n stickyFooter?: Component;\n /** When true, the host wraps the screen in a scroll that starts pinned to the\n * bottom and shows a floating \"jump to latest\" button whenever the user scrolls\n * up (a chat transcript). Tapping it animates back to the bottom. Absent/false =\n * a normal top-anchored scroll. Honored by the app host (Router). */\n scrollToBottom?: boolean;\n /** The chat transcript scroll policy. On a send the host pins the\n * named component near the top and reserves the space the reply grows into;\n * when the reply outgrows it the host follows the tail. A drag pauses\n * following, and only then is a jump-to-latest control shown. */\n transcriptScroll?: TranscriptScroll;\n /** The page's title for a host that shows it in its own navigation bar (iOS `himalayaEmbedTitle`, Android\n * `ProvideHimalayaEmbedTitle`): it gets the title as soon as it has the descriptor, before the worker boots, and\n * `replaces` is not drawn. Every other host ignores it, so the in-content header stays. */\n hostTitle?: HostTitle;\n /** Present when `renderer` is `webview_native_bridge` or `webview_ds`. */\n url?: string;\n capabilitiesRequired?: string[];\n schemaVersion: number;\n togglesApplied?: string[];\n /** Per-session flag/experiment assignments baked in for this request. */\n assignments?: import(\"./flags.js\").Assignment[];\n /** Per-screen top bar (trailing actions +\n * leading/style override). Fully replaces `NavigationConfig.topBarDefault`. */\n topBar?: TopBarConfig;\n /** \"\" / \"visible\" (default) | \"hidden\": hide the bottom tab bar while this screen is the\n * visible top of its tab's stack. Static; no effect in a presentation. */\n tabBarVisibility?: string;\n etag?: string;\n cacheControl?: CacheControl;\n telemetryScreenName?: string;\n bundleSri?: string;\n manifestVersion?: number;\n /**\n * Present on Tier-5 screens. The presence of this field is the tier-5\n * signal — there is no separate `tier: 5` flag.\n */\n logicBundle?: LogicBundleRef;\n /**\n * Static per-screen input injected as `globalThis.himalaya.input` before the\n * Tier-5 worker bundle evaluates — the worker reads it synchronously in `load()`\n * (e.g. `himalaya.input.id` for a detail screen, or a baked catalog like\n * `himalaya.input.cmsCollections`).\n *\n * A push merges its navigation parameters into this object on web, iOS, and Android.\n * For example, `himi.open({ screenId: \"event\", id: \"$item.id\" })` makes the id\n * available as `himalaya.input.id` in the pushed worker; static `input` remains the\n * fallback for values baked into a descriptor or a templated route. The host also\n * supplies `screenId`. See docs/tier-5-add-a-screen.md → \"Navigate to a detail screen\".\n *\n * Arbitrary JSON object; consumed by the web host (ScreenController) and the\n * native JSWorker.\n */\n input?: Record<string, unknown>;\n /**\n * Tier 3 / Tier 4 — behavior of the hosted WebView. Ignored by SDUI tiers.\n * Authored in the descriptor builder (git); served OTA verbatim.\n */\n webViewConfig?: WebViewConfig;\n}",
1263
1265
  "Component": "export interface Component {\n id: string;\n body: ComponentBody;\n modifiers?: Modifiers;\n minVersion?: number;\n}",
1264
1266
  "LogicBundleRef": "export interface LogicBundleRef {\n url: string;\n /** \"sha256-...\" SRI string. */\n integrity: string;\n contentType?: string;\n /** SDK capability ids the bundle is permitted to import / invoke. */\n declaredCapabilities: string[];\n /** Bundle refuses to load on a binary older than this (semver). */\n minBinaryVersion?: string;\n /** Bundle's own semver. */\n bundleVersion?: string;\n /** Base64 detached publisher signature over the bundle bytes\n * (authenticity beyond SRI). Verified by a host-injected verifier when present;\n * unsigned bundles load as today. */\n signature?: string;\n /** The publisher key id a host pins, paired with `signature`. */\n keyId?: string;\n /** Ambient refresh (gap G2): the host invokes `action` (a `tier5.<svc>.<action>`\n * path) every `intervalMs` while the screen is visible — workers have no timer\n * of their own, so the host owns the cadence. */\n refresh?: { intervalMs: number; action: string };\n}",
1265
- "Modifiers": "export interface Modifiers {\n padding?: EdgeInsets;\n background?: TokenRef;\n cornerRadius?: number;\n /** Fixed intrinsic width (pt/dp/px). Honored by iOS (`FrameIfPresent`), Android\n * (frame) and web (`modifiers.ts`). With no `ScrollView` in the vocabulary and\n * regions clipped at `maxHeight`, `width`/`height`/`fillMaxWidth` are the main\n * sizing tools for embed/host content. */\n width?: number;\n /** Fixed intrinsic height (pt/dp/px). See `width`. */\n height?: number;\n /** Fill the available cross-axis width (keeps any fixed `height`). Mirrors\n * Compose `fillMaxWidth` / SwiftUI `.frame(maxWidth: .infinity)`. See `width`. */\n fillMaxWidth?: boolean;\n /** CAP the node's laid-out width at this many points. It CONSTRAINS, it\n * does not SET: a node narrower than the cap keeps its intrinsic width, so a\n * short chat bubble stays short while a long one stops at the cap — the\n * asymmetry `width` cannot express and `weight` cannot either (weights divide\n * the *available* space, so every child lands on the same fraction).\n * The cap covers the node's TOTAL width, padding + border included (CSS\n * `max-width` under `box-sizing: border-box`). `fillMaxWidth` grabs the axis but\n * THE CAP WINS — fill + cap is exactly `maxWidth` wide; a literal `width` larger\n * than the cap is clamped to it too. POINTS, not a fraction: a modifier is\n * resolved with no portable notion of \"the row\" on any renderer — compose a\n * percentage from a weighted sibling gap instead. `0`/absent = no cap. */\n maxWidth?: number;\n /** Flex weight along the parent stack's MAIN axis (HStack = width, VStack =\n * height). The stack measures its intrinsic (unweighted) children first, then\n * divides what is LEFT among the weighted ones in proportion to their weights:\n * two children at `1` split the leftover equally, `2` and `1` split it 2:1.\n * Absent (or <= 0) keeps the node at its intrinsic size. Use this — not\n * `fillMaxWidth` — when a node should claim the remaining space *beside* its\n * siblings; a full-bleed fill grabs the whole axis and starves them. Ignored\n * outside an HStack/VStack.\n * CAVEAT — row-sharing controls have NO intrinsic width to measure first: in an\n * HStack a `Button` and a `Toggle` are implicitly weighted, so they DIVIDE the\n * row with your weighted child instead of taking a width ahead of it —\n * `[VStack(weight:1), Button, Button]` is a three-way split, not \"the VStack gets\n * the remainder\". To opt a Button out on EVERY renderer set `fullWidth: false`\n * (an explicit flag wins in both directions; absent falls back to the style\n * default, which is \"share\" for every style but `tertiary`/`link`), or use\n * `iconOnly`. Otherwise give the actions their own row.\n * See `himi docs designing-himi-screens` §4. */\n weight?: number;\n /** RTL — pin the subtree's writing direction: `\"ltr\"` | `\"rtl\"` | `\"locale\"`\n * (inherit). The common RTL pattern is the inverse of `Text.textDirection`: pin a\n * container `ltr` (a logotype, an attachment-tile row, a composer button row)\n * inside an otherwise-RTL screen. Absent = inherit the ambient direction. #1060. */\n layoutDirection?: \"ltr\" | \"rtl\" | \"locale\";\n /** Absolute visual translation (pt/dp/px). Applied as the OUTERMOST transform so\n * it shifts the fully-styled node WITHOUT changing its layout footprint — lets a\n * ZStack place polar/absolute children (e.g. AgentOrbit). Negative allowed. */\n offsetX?: number;\n offsetY?: number;\n onTap?: Action;\n /** Double-tap gesture (e.g. Instagram double-tap-to-like). Distinct from onTap. */\n onDoubleTap?: Action;\n accessibilityLabel?: string;\n hidden?: boolean;\n /** Data-bound conditional visibility: a `\"$service:<svc>.<field>\"`\n * or `\"$item.<field>\"` sentinel resolving to a bool at render; when true the\n * subtree fully COLLAPSES (zero layout, like `hidden`), overriding the\n * literal `hidden`. */\n hiddenPath?: string;\n /** Long-press gesture modifier. */\n onLongPress?: Action;\n /** Fires once when the component first appears on screen. */\n onAppear?: Action;\n /** Scoped token overrides for the subtree. */\n theme?: ThemeOverrides;\n /** Names a token from the app's `shadows` registry. */\n shadow?: TokenRef;\n /** Names a token from the app's `gradients` registry (background fill). */\n backgroundGradient?: TokenRef;\n /** Value-driven gradient: a `$service:`/`$item.` path resolving to a gradient\n * TOKEN NAME used as the live background (e.g. weather sky day→night). */\n backgroundGradientPath?: string;\n /** Value-driven background COLOR: a `$service:`/`$item.` path resolving to a\n * color TOKEN NAME used as the live background. Honored on iOS + Android\n * (`BoundBackgroundIfPresent`); web render support is tracked in #1057. */\n backgroundColorPath?: string;\n /** Value-driven background IMAGE: a `$service:`/`$item.` path resolving to an\n * image URL used as the live background. Honored on iOS + Android; web render\n * support is tracked in #1057. */\n backgroundImagePath?: string;\n /** #1051 — MEASURE (publish): report this node's laid-out size into the renderer's\n * self-contained (worker-free) measure store under the named keys, so a SIBLING can\n * feed the number back into its own layout via a `$measure:<key>` binding. The SDUI\n * equivalent of SwiftUI `.onGeometryChange` / Compose `onGloballyPositioned`; the\n * canonical use is a pinned bottom bar whose measured height becomes a scroll view's\n * bottom inset (sticky-footer + scroll-inset, unrepresentable before — #1051). */\n onMeasure?: Measure;\n /** #1051 — bound height: a `\"$measure:<key>\"` (measure store) or\n * `\"$service:<svc>.<field>\"` / `\"$item.<field>\"` (worker/row state) sentinel resolving\n * to a NUMBER at render. Wins over the literal `height`. String path only, so a\n * binding can never blank the node (never overload the numeric `height`). */\n heightPath?: string;\n /** #1051 — bound width. See `heightPath`. Wins over the literal `width`. */\n widthPath?: string;\n /** #1051 — bound TOP inset (the header-height → top-inset idiom). A `$measure:`/\n * `$service:`/`$item.` sentinel resolving to a number; added as top padding. */\n paddingTopPath?: string;\n /** #1051 — bound BOTTOM inset (the sticky-footer-height → bottom-inset idiom). A\n * `$measure:`/`$service:`/`$item.` sentinel resolving to a number; added as bottom\n * padding so the last scroll row clears a pinned bottom bar. */\n paddingBottomPath?: string;\n /** #1053 — INLINE gradient background paint. Distinct from `backgroundGradient`\n * (which names a `gradients` registry token an embed host can't register): this\n * carries its stops/colours in the descriptor, so it works anywhere. Painted as\n * a surface (behind children), below `material`/`backgroundGradient` if those are\n * also set. */\n gradient?: Gradient;\n /** #1053 — gradient ALPHA MASK: the node shows through where the gradient is\n * opaque and fades where it's transparent (a transparent-token stop = fully\n * clear). The material-under-gradient-scrim / scroll-fade-out idiom. Mirrors iOS\n * `.mask()`, Android `BlendMode.DstIn`, web `mask-image`. */\n mask?: Gradient;\n /** Inline border/stroke. */\n border?: Border;\n /** Subtree alpha 0.0–1.0; absent = opaque. */\n opacity?: number;\n /** #433 — constrain the subtree to a width:height ratio (e.g. `1` = square,\n * `1.777` = 16:9). Width-driven: the height derives from the resolved width.\n * Lets a data-bound Grid cell / photo tile hold its shape at any width. */\n aspectRatio?: number;\n /** Names a token from the app's `motions` registry; the subtree\n * fades + slides up on first appearance. */\n appear?: TokenRef;\n /** Surface material token (flat/glass/frosted). */\n material?: TokenRef;\n /** Per-node construction-language override. */\n variant?: string;\n /** Per-node density / motion override. */\n density?: string;\n motion?: string;\n /** Motion grammar (Batch 1) — press/tap feedback style\n * (auto|none|scale|highlight|scaleHighlight|lift); timing comes from `motion`.\n * Applied only when the node is interactive. Unknown degrades to `auto`. */\n pressFeedback?: string;\n /** Motion grammar (Batch 1) — press-coupled haptic\n * (none|selection|light|medium|heavy|success|warning|error); fires with onTap.\n * Default none (opt-in). Web is a no-op. */\n haptic?: string;\n /** Motion grammar (Batch 1) — animate this container's size changes instead of\n * snapping (iOS `.animation(value:)`, Android `animateContentSize`, web height\n * transition). Curve from the active `motion` personality. */\n animateContentSize?: boolean;\n /** Motion grammar (Batch 1) — enter/exit transition; supersedes `appear` when\n * both are present. */\n transition?: Transition;\n /** Motion grammar (Batch 2, scroll-driven) — defer the `transition` enter until\n * the node first crosses into the scroll viewport (reveal-on-scroll). Gated by\n * reduced motion (shown immediately when reduced). */\n revealOnScroll?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — parallax factor: the node drifts\n * vertically at this fraction of the scroll delta as it moves through the\n * viewport. Gated by reduced motion. Generalizes CollapsingHeader parallaxFactor. */\n parallax?: number;\n /** Motion grammar (Batch 4, rich visual) — when a backgroundGradient is present,\n * continuously drift/rotate it (animated gradient). Gated by reduced motion. */\n animatedGradient?: boolean;\n /** Motion grammar (Batch 3, hero/nav) — shared-element key: a matching\n * transitionId on the source + destination screen morphs that element across\n * navigation (web View Transitions / iOS matchedTransitionSource / Android\n * SharedTransitionLayout). Gated by reduced motion. */\n transitionId?: string;\n /** Motion grammar (Batch 4, rich visual) — render the backgroundGradient as a\n * mesh (overlapping multi-point blend) rather than a linear sweep; combine with\n * animatedGradient to drift it. */\n meshGradient?: boolean;\n /** Motion grammar (Batch 4, celebration) — burst a one-shot confetti overlay\n * within the node on appear. Gated by reduced motion. */\n confetti?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — pin this node to the top of its\n * scroll container (sticky section header). Functional positioning; not gated\n * by reduced motion. */\n sticky?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — turn this thin node into a\n * reading-progress bar that fills left→right with scroll (scaleX). web reads\n * window scroll; native reads the host scroll fraction. Functional. */\n scrollProgress?: boolean;\n /** Motion grammar (Batch 4, rich vector) — a named built-in vector animation\n * (\"loader\" | \"success\" | \"pulse\"), dependency-free. Gated by reduced motion.\n * Arbitrary Lottie/Rive JSON is the lib-backed superset follow-up. */\n vectorAnim?: string;\n /** Motion grammar (Batch 1, choreography) — delay this node's ENTER transition\n * by N ms so a screen can SEQUENCE its entrance beyond list stagger (\"title\n * fades, subtitle slides in 100ms later\"). Adds to any list `staggerMs`. Gated\n * by reduced motion. 0/absent = no extra delay. */\n enterDelayMs?: number;\n /** Motion grammar (Phase-3, per-node MotionSpec) — overrides the PRESS-feedback\n * curve with a `motions`-registry token, mirroring `Transition.motion` for the\n * enter curve. Absent → press uses the global `motion` personality. */\n pressMotion?: TokenRef;\n /** Motion grammar (Phase-3, gesture-driven) — finger-tracked drag-to-dismiss; past\n * threshold along `direction` it animates off and fires `action`, else springs back. */\n swipeDismiss?: SwipeDismiss;\n /** Motion grammar (Phase-4, reduced-motion) — per-node escape from the reduced-motion\n * gate. Default true (respect). false = \"must move anyway\" (transition/press animate\n * even when reduced motion is on). */\n respectReducedMotion?: boolean;\n /** #1052 — uniform scale (1 = no change). */\n scale?: number;\n /** #1052 — rotation in DEGREES. */\n rotation?: number;\n /** #1052 — stacking order within a ZStack/overlay. */\n zIndex?: number;\n /** #1052 — gaussian blur radius (pt/dp/px). */\n blur?: number;\n /** #1052 — `false` = decorative (ignores taps, unlike `hidden` which stays hit-testable);\n * `true` = force hit-testable (a clear tap target). */\n hitTestable?: boolean;\n /** #1052 — explicit drop shadow (colour + radius + offset), for upward / multi-layer\n * shadows the `shadow` token can't express. */\n boxShadow?: Shadow;\n /** The role assistive technology should announce. Absent = the\n * renderer INFERS it from the component type, so an author never restates the\n * obvious. `\"decorative\"` is node-local: the node itself stops being an\n * accessibility element (no role, not a stop) while its children stay\n * traversable — the ARIA `role=\"presentation\"` semantic. To remove the subtree\n * too, use `accessibilityHidden`. */\n accessibilityRole?: AccessibilityRole;\n /** The supplementary \"what happens if I act\" phrase\n * (iOS `accessibilityHint`, Android `onClickLabel`, ARIA `aria-describedby`).\n * Never a substitute for `accessibilityLabel`: AT may not speak a hint at all. */\n accessibilityHint?: string;\n /** Remove this node AND its subtree from the accessibility tree.\n * Distinct from `hidden` (which removes it visually too) and from the\n * `\"decorative\"` role (node-local; children stay reachable). */\n accessibilityHidden?: boolean;\n /** Mark the node as a heading at level 1–6, for rotor / heading\n * navigation. Implies the `\"heading\"` role when `accessibilityRole` is absent.\n * The LEVEL reaches iOS and web only: Android (Compose `heading()`) and React Native\n * (`accessibilityRole: \"header\"`) have no level primitive and mark a plain heading. */\n headingLevel?: number;\n /** An explicit SPOKEN value where the visual and spoken values\n * differ (a rating \"3 of 5\", a gauge \"72 percent\"). Where the value is derivable\n * from a prop the renderer computes it and this override is unnecessary. */\n accessibilityValue?: string;\n /** Additive state that a single role cannot carry. A node may\n * hold several. */\n accessibilityTraits?: AccessibilityTrait[];\n /** Explicit traversal index within the nearest focus group. Absent ⇒\n * the arm's visual / DOM order. The lower index is reached FIRST, which is why iOS\n * (whose `accessibilitySortPriority` is descending) negates it. */\n focusOrder?: number;\n /** This node's subtree is ONE traversal scope: children are ordered\n * relative to each other, then the group takes its parent's slot (iOS\n * `shouldGroupAccessibilityChildren`, Android `isTraversalGroup`, ARIA grouping). */\n focusGroup?: boolean;\n /** AT / keyboard focus lands here when the screen or a newly presented\n * modal appears. At most one per focus scope. */\n initialFocus?: boolean;\n /** A general live-region announcement, on any node and any transition.\n * The generalization of `FragmentSlotCase.accessibilityAnnouncement`, which only ever\n * fired on a fragment-slot case swap. */\n announce?: LiveRegion;\n /** Clip ANY node (content + surface) to a shape:\n * \"rectangle\" | \"roundedRectangle\" | \"capsule\" | \"circle\" | \"ellipse\". Unknown/empty =\n * no shape clip (`cornerRadius` keeps its meaning). When set it replaces the\n * corner-radius-only clip; a \"roundedRectangle\" uses `clipCornerRadii`, else `cornerRadius`. */\n clipShape?: string;\n /** Uneven corner radii for `clipShape: \"roundedRectangle\"`. */\n clipCornerRadii?: CornerRadii;\n /** A recurring, finite, transform-only motion on a decorative node: a\n * living character's idle `hop`, a `blink`, a working `breathe`. Never changes the\n * node's layout footprint. Stopped by reduced motion unless `respectReducedMotion`\n * is false. */\n ambientMotion?: AmbientMotion;\n}",
1267
+ "Modifiers": "export interface Modifiers {\n /** Changing non-empty token: scroll this node into view and move accessibility focus to it. */\n scrollIntoViewPath?: string;\n padding?: EdgeInsets;\n background?: TokenRef;\n cornerRadius?: number;\n /** Fixed intrinsic width (pt/dp/px). Honored by iOS (`FrameIfPresent`), Android\n * (frame) and web (`modifiers.ts`). With no `ScrollView` in the vocabulary and\n * regions clipped at `maxHeight`, `width`/`height`/`fillMaxWidth` are the main\n * sizing tools for embed/host content. */\n width?: number;\n /** Fixed intrinsic height (pt/dp/px). See `width`. */\n height?: number;\n /** Fill the available cross-axis width (keeps any fixed `height`). Mirrors\n * Compose `fillMaxWidth` / SwiftUI `.frame(maxWidth: .infinity)`. See `width`. */\n fillMaxWidth?: boolean;\n /** CAP the node's laid-out width at this many points. It CONSTRAINS, it\n * does not SET: a node narrower than the cap keeps its intrinsic width, so a\n * short chat bubble stays short while a long one stops at the cap — the\n * asymmetry `width` cannot express and `weight` cannot either (weights divide\n * the *available* space, so every child lands on the same fraction).\n * The cap covers the node's TOTAL width, padding + border included (CSS\n * `max-width` under `box-sizing: border-box`). `fillMaxWidth` grabs the axis but\n * THE CAP WINS — fill + cap is exactly `maxWidth` wide; a literal `width` larger\n * than the cap is clamped to it too. POINTS, not a fraction: a modifier is\n * resolved with no portable notion of \"the row\" on any renderer — compose a\n * percentage from a weighted sibling gap instead. `0`/absent = no cap. */\n maxWidth?: number;\n /** Flex weight along the parent stack's MAIN axis (HStack = width, VStack =\n * height). The stack measures its intrinsic (unweighted) children first, then\n * divides what is LEFT among the weighted ones in proportion to their weights:\n * two children at `1` split the leftover equally, `2` and `1` split it 2:1.\n * Absent (or <= 0) keeps the node at its intrinsic size. Use this — not\n * `fillMaxWidth` — when a node should claim the remaining space *beside* its\n * siblings; a full-bleed fill grabs the whole axis and starves them. Ignored\n * outside an HStack/VStack.\n * CAVEAT — row-sharing controls have NO intrinsic width to measure first: in an\n * HStack a `Button` and a `Toggle` are implicitly weighted, so they DIVIDE the\n * row with your weighted child instead of taking a width ahead of it —\n * `[VStack(weight:1), Button, Button]` is a three-way split, not \"the VStack gets\n * the remainder\". To opt a Button out on EVERY renderer set `fullWidth: false`\n * (an explicit flag wins in both directions; absent falls back to the style\n * default, which is \"share\" for every style but `tertiary`/`link`), or use\n * `iconOnly`. Otherwise give the actions their own row.\n * See `himi docs designing-himi-screens` §4. */\n weight?: number;\n /** RTL — pin the subtree's writing direction: `\"ltr\"` | `\"rtl\"` | `\"locale\"`\n * (inherit). The common RTL pattern is the inverse of `Text.textDirection`: pin a\n * container `ltr` (a logotype, an attachment-tile row, a composer button row)\n * inside an otherwise-RTL screen. Absent = inherit the ambient direction. #1060. */\n layoutDirection?: \"ltr\" | \"rtl\" | \"locale\";\n /** Absolute visual translation (pt/dp/px). Applied as the OUTERMOST transform so\n * it shifts the fully-styled node WITHOUT changing its layout footprint — lets a\n * ZStack place polar/absolute children (e.g. AgentOrbit). Negative allowed. */\n offsetX?: number;\n offsetY?: number;\n onTap?: Action;\n /** Double-tap gesture (e.g. Instagram double-tap-to-like). Distinct from onTap. */\n onDoubleTap?: Action;\n accessibilityLabel?: string;\n hidden?: boolean;\n /** Data-bound conditional visibility: a `\"$service:<svc>.<field>\"`\n * or `\"$item.<field>\"` sentinel resolving to a bool at render; when true the\n * subtree fully COLLAPSES (zero layout, like `hidden`), overriding the\n * literal `hidden`. */\n hiddenPath?: string;\n /** Long-press gesture modifier. */\n onLongPress?: Action;\n /** Fires once when the component first appears on screen. */\n onAppear?: Action;\n /** Scoped token overrides for the subtree. */\n theme?: ThemeOverrides;\n /** Names a token from the app's `shadows` registry. */\n shadow?: TokenRef;\n /** Names a token from the app's `gradients` registry (background fill). */\n backgroundGradient?: TokenRef;\n /** Value-driven gradient: a `$service:`/`$item.` path resolving to a gradient\n * TOKEN NAME used as the live background (e.g. weather sky day→night). */\n backgroundGradientPath?: string;\n /** Value-driven background COLOR: a `$service:`/`$item.` path resolving to a\n * color TOKEN NAME used as the live background. Honored on iOS + Android\n * (`BoundBackgroundIfPresent`); web render support is tracked in #1057. */\n backgroundColorPath?: string;\n /** Value-driven background IMAGE: a `$service:`/`$item.` path resolving to an\n * image URL used as the live background. Honored on iOS + Android; web render\n * support is tracked in #1057. */\n backgroundImagePath?: string;\n /** #1051 — MEASURE (publish): report this node's laid-out size into the renderer's\n * self-contained (worker-free) measure store under the named keys, so a SIBLING can\n * feed the number back into its own layout via a `$measure:<key>` binding. The SDUI\n * equivalent of SwiftUI `.onGeometryChange` / Compose `onGloballyPositioned`; the\n * canonical use is a pinned bottom bar whose measured height becomes a scroll view's\n * bottom inset (sticky-footer + scroll-inset, unrepresentable before — #1051). */\n onMeasure?: Measure;\n /** #1051 — bound height: a `\"$measure:<key>\"` (measure store) or\n * `\"$service:<svc>.<field>\"` / `\"$item.<field>\"` (worker/row state) sentinel resolving\n * to a NUMBER at render. Wins over the literal `height`. String path only, so a\n * binding can never blank the node (never overload the numeric `height`). */\n heightPath?: string;\n /** #1051 — bound width. See `heightPath`. Wins over the literal `width`. */\n widthPath?: string;\n /** #1051 — bound TOP inset (the header-height → top-inset idiom). A `$measure:`/\n * `$service:`/`$item.` sentinel resolving to a number; added as top padding. */\n paddingTopPath?: string;\n /** #1051 — bound BOTTOM inset (the sticky-footer-height → bottom-inset idiom). A\n * `$measure:`/`$service:`/`$item.` sentinel resolving to a number; added as bottom\n * padding so the last scroll row clears a pinned bottom bar. */\n paddingBottomPath?: string;\n /** #1053 — INLINE gradient background paint. Distinct from `backgroundGradient`\n * (which names a `gradients` registry token an embed host can't register): this\n * carries its stops/colours in the descriptor, so it works anywhere. Painted as\n * a surface (behind children), below `material`/`backgroundGradient` if those are\n * also set. */\n gradient?: Gradient;\n /** #1053 — gradient ALPHA MASK: the node shows through where the gradient is\n * opaque and fades where it's transparent (a transparent-token stop = fully\n * clear). The material-under-gradient-scrim / scroll-fade-out idiom. Mirrors iOS\n * `.mask()`, Android `BlendMode.DstIn`, web `mask-image`. */\n mask?: Gradient;\n /** Inline border/stroke. */\n border?: Border;\n /** Subtree alpha 0.0–1.0; absent = opaque. */\n opacity?: number;\n /** #433 — constrain the subtree to a width:height ratio (e.g. `1` = square,\n * `1.777` = 16:9). Width-driven: the height derives from the resolved width.\n * Lets a data-bound Grid cell / photo tile hold its shape at any width. */\n aspectRatio?: number;\n /** Names a token from the app's `motions` registry; the subtree\n * fades + slides up on first appearance. */\n appear?: TokenRef;\n /** Surface material token (flat/glass/frosted). */\n material?: TokenRef;\n /** Per-node construction-language override. */\n variant?: string;\n /** Per-node density / motion override. */\n density?: string;\n motion?: string;\n /** Motion grammar (Batch 1) — press/tap feedback style\n * (auto|none|scale|highlight|scaleHighlight|lift); timing comes from `motion`.\n * Applied only when the node is interactive. Unknown degrades to `auto`. */\n pressFeedback?: string;\n /** Motion grammar (Batch 1) — press-coupled haptic\n * (none|selection|light|medium|heavy|success|warning|error); fires with onTap.\n * Default none (opt-in). Web is a no-op. */\n haptic?: string;\n /** Motion grammar (Batch 1) — animate this container's size changes instead of\n * snapping (iOS `.animation(value:)`, Android `animateContentSize`, web height\n * transition). Curve from the active `motion` personality. */\n animateContentSize?: boolean;\n /** Motion grammar (Batch 1) — enter/exit transition; supersedes `appear` when\n * both are present. */\n transition?: Transition;\n /** Motion grammar (Batch 2, scroll-driven) — defer the `transition` enter until\n * the node first crosses into the scroll viewport (reveal-on-scroll). Gated by\n * reduced motion (shown immediately when reduced). */\n revealOnScroll?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — parallax factor: the node drifts\n * vertically at this fraction of the scroll delta as it moves through the\n * viewport. Gated by reduced motion. Generalizes CollapsingHeader parallaxFactor. */\n parallax?: number;\n /** Motion grammar (Batch 4, rich visual) — when a backgroundGradient is present,\n * continuously drift/rotate it (animated gradient). Gated by reduced motion. */\n animatedGradient?: boolean;\n /** Motion grammar (Batch 3, hero/nav) — shared-element key: a matching\n * transitionId on the source + destination screen morphs that element across\n * navigation (web View Transitions / iOS matchedTransitionSource / Android\n * SharedTransitionLayout). Gated by reduced motion. */\n transitionId?: string;\n /** Motion grammar (Batch 4, rich visual) — render the backgroundGradient as a\n * mesh (overlapping multi-point blend) rather than a linear sweep; combine with\n * animatedGradient to drift it. */\n meshGradient?: boolean;\n /** Motion grammar (Batch 4, celebration) — burst a one-shot confetti overlay\n * within the node on appear. Gated by reduced motion. */\n confetti?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — pin this node to the top of its\n * scroll container (sticky section header). Functional positioning; not gated\n * by reduced motion. */\n sticky?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — turn this thin node into a\n * reading-progress bar that fills left→right with scroll (scaleX). web reads\n * window scroll; native reads the host scroll fraction. Functional. */\n scrollProgress?: boolean;\n /** Motion grammar (Batch 4, rich vector) — a named built-in vector animation\n * (\"loader\" | \"success\" | \"pulse\"), dependency-free. Gated by reduced motion.\n * Arbitrary Lottie/Rive JSON is the lib-backed superset follow-up. */\n vectorAnim?: string;\n /** Motion grammar (Batch 1, choreography) — delay this node's ENTER transition\n * by N ms so a screen can SEQUENCE its entrance beyond list stagger (\"title\n * fades, subtitle slides in 100ms later\"). Adds to any list `staggerMs`. Gated\n * by reduced motion. 0/absent = no extra delay. */\n enterDelayMs?: number;\n /** Motion grammar (Phase-3, per-node MotionSpec) — overrides the PRESS-feedback\n * curve with a `motions`-registry token, mirroring `Transition.motion` for the\n * enter curve. Absent → press uses the global `motion` personality. */\n pressMotion?: TokenRef;\n /** Motion grammar (Phase-3, gesture-driven) — finger-tracked drag-to-dismiss; past\n * threshold along `direction` it animates off and fires `action`, else springs back. */\n swipeDismiss?: SwipeDismiss;\n /** Motion grammar (Phase-4, reduced-motion) — per-node escape from the reduced-motion\n * gate. Default true (respect). false = \"must move anyway\" (transition/press animate\n * even when reduced motion is on). */\n respectReducedMotion?: boolean;\n /** #1052 — uniform scale (1 = no change). */\n scale?: number;\n /** #1052 — rotation in DEGREES. */\n rotation?: number;\n /** #1052 — stacking order within a ZStack/overlay. */\n zIndex?: number;\n /** #1052 — gaussian blur radius (pt/dp/px). */\n blur?: number;\n /** #1052 — `false` = decorative (ignores taps, unlike `hidden` which stays hit-testable);\n * `true` = force hit-testable (a clear tap target). */\n hitTestable?: boolean;\n /** #1052 — explicit drop shadow (colour + radius + offset), for upward / multi-layer\n * shadows the `shadow` token can't express. */\n boxShadow?: Shadow;\n /** The role assistive technology should announce. Absent = the\n * renderer INFERS it from the component type, so an author never restates the\n * obvious. `\"decorative\"` is node-local: the node itself stops being an\n * accessibility element (no role, not a stop) while its children stay\n * traversable — the ARIA `role=\"presentation\"` semantic. To remove the subtree\n * too, use `accessibilityHidden`. */\n accessibilityRole?: AccessibilityRole;\n /** The supplementary \"what happens if I act\" phrase\n * (iOS `accessibilityHint`, Android `onClickLabel`, ARIA `aria-describedby`).\n * Never a substitute for `accessibilityLabel`: AT may not speak a hint at all. */\n accessibilityHint?: string;\n /** Remove this node AND its subtree from the accessibility tree.\n * Distinct from `hidden` (which removes it visually too) and from the\n * `\"decorative\"` role (node-local; children stay reachable). */\n accessibilityHidden?: boolean;\n /** Mark the node as a heading at level 1–6, for rotor / heading\n * navigation. Implies the `\"heading\"` role when `accessibilityRole` is absent.\n * The LEVEL reaches iOS and web only: Android (Compose `heading()`) and React Native\n * (`accessibilityRole: \"header\"`) have no level primitive and mark a plain heading. */\n headingLevel?: number;\n /** An explicit SPOKEN value where the visual and spoken values\n * differ (a rating \"3 of 5\", a gauge \"72 percent\"). Where the value is derivable\n * from a prop the renderer computes it and this override is unnecessary. */\n accessibilityValue?: string;\n /** Additive state that a single role cannot carry. A node may\n * hold several. */\n accessibilityTraits?: AccessibilityTrait[];\n /** Explicit traversal index within the nearest focus group. Absent ⇒\n * the arm's visual / DOM order. The lower index is reached FIRST, which is why iOS\n * (whose `accessibilitySortPriority` is descending) negates it. */\n focusOrder?: number;\n /** This node's subtree is ONE traversal scope: children are ordered\n * relative to each other, then the group takes its parent's slot (iOS\n * `shouldGroupAccessibilityChildren`, Android `isTraversalGroup`, ARIA grouping). */\n focusGroup?: boolean;\n /** AT / keyboard focus lands here when the screen or a newly presented\n * modal appears. At most one per focus scope. */\n initialFocus?: boolean;\n /** A general live-region announcement, on any node and any transition.\n * The generalization of `FragmentSlotCase.accessibilityAnnouncement`, which only ever\n * fired on a fragment-slot case swap. */\n announce?: LiveRegion;\n /** Clip ANY node (content + surface) to a shape:\n * \"rectangle\" | \"roundedRectangle\" | \"capsule\" | \"circle\" | \"ellipse\". Unknown/empty =\n * no shape clip (`cornerRadius` keeps its meaning). When set it replaces the\n * corner-radius-only clip; a \"roundedRectangle\" uses `clipCornerRadii`, else `cornerRadius`. */\n clipShape?: string;\n /** Uneven corner radii for `clipShape: \"roundedRectangle\"`. */\n clipCornerRadii?: CornerRadii;\n /** A recurring, finite, transform-only motion on a decorative node: a\n * living character's idle `hop`, a `blink`, a working `breathe`. Never changes the\n * node's layout footprint. Stopped by reduced motion unless `respectReducedMotion`\n * is false. */\n ambientMotion?: AmbientMotion;\n}",
1266
1268
  "TokenRef": "export interface TokenRef {\n token: TokenName;\n}",
1267
1269
  "Action": "export interface Action {\n actionId: string;\n /** : applied before the worker action; its returned legacy _nav is ignored. */\n nav?: ActionNavigation;\n /** Backwards-compatible string-only params. Prefer `args` for typed payloads. */\n params?: Record<string, string>;\n /** Typed action args; nested objects/arrays preserved end-to-end. With `presentation: \"sheet\"`,\n * `args.detents` (fractions of the height in (0, 1], e.g. `[0.5, 1]`) sizes the presented\n * sheet; absent ⇒ the default sheet. Applies on iOS, Android and web; a macOS (and tvOS)\n * screen sheet is a window-modal panel sized to its content, so there it is accepted and\n * ignored, as for the BottomSheet component. */\n args?: Record<string, Value>;\n /** Navigation hint for the router; ignored for non-nav actions. */\n presentation?: Presentation;\n}",
1268
1270
  "Presentation": "export type Presentation =\n | \"push\"\n | \"sheet\"\n | \"replace\"\n | \"dismiss\"\n | \"back\"\n | \"full_screen\"\n | \"alert\"\n | \"snackbar\";",
@@ -41,7 +41,7 @@ See also **[Modifiers](#modifiers)** (the `modifiers` block) and **[Actions](#ac
41
41
  | `Text` | Core | `text` |
42
42
  | `Image` | Core | `url`, `initials`, `contentDescription` |
43
43
  | `Icon` | Core | — |
44
- | `Button` | Core | `disabledPath`, `title` |
44
+ | `Button` | Core | `disabledPath`, `loadingPath`, `title` |
45
45
  | `TextField` | Core | `valuePath`, `focusPath`, `errorPath` |
46
46
  | `Toggle` | Core | `valuePath` |
47
47
  | `Stepper` | Core | `valuePath`, `maxPath` |
@@ -62,7 +62,7 @@ See also **[Modifiers](#modifiers)** (the `modifiers` block) and **[Actions](#ac
62
62
  | `FilterChips` | Search & filter | `optionsPath`, `selectedIds` |
63
63
  | `Tabs` | Navigation primitives | `optionsPath`, `activeColorPath`, `selectedId` |
64
64
  | `SegmentedControl` | Navigation primitives | `optionsPath`, `selectedId` |
65
- | `Carousel` | Navigation primitives | `itemsPath` |
65
+ | `Carousel` | Navigation primitives | `currentIndexPath`, `itemsPath` |
66
66
  | `Pager` | Navigation primitives | — |
67
67
  | `StepIndicator` | Navigation primitives | — |
68
68
  | `CollapsingHeader` | Navigation primitives | — |
@@ -559,6 +559,8 @@ export interface ButtonProps {
559
559
  cornerRadius?: number;
560
560
  /** #1049 — minimum height (pt/dp/px). */
561
561
  minHeight?: number;
562
+ /** Live loading bool; OR with literal loading. */
563
+ loadingPath?: string;
562
564
  }
563
565
  ```
564
566
 
@@ -1057,6 +1059,10 @@ export interface SegmentedControlProps {
1057
1059
 
1058
1060
  ```ts
1059
1061
  export interface CarouselProps {
1062
+ /** Opt-in paging for itemsPath; absent preserves the horizontal card rail. */
1063
+ paged?: boolean;
1064
+ /** Worker-driven page index; clamped to the current page count. */
1065
+ currentIndexPath?: string;
1060
1066
  items: Component[];
1061
1067
  currentIndex?: number;
1062
1068
  onChange?: Action;
@@ -2536,6 +2542,8 @@ way round — see [Token slots](#token-slots-tokenref-object-vs-bare-tokenname-s
2536
2542
 
2537
2543
  ```ts
2538
2544
  export interface Modifiers {
2545
+ /** Changing non-empty token: scroll this node into view and move accessibility focus to it. */
2546
+ scrollIntoViewPath?: string;
2539
2547
  padding?: EdgeInsets;
2540
2548
  background?: TokenRef;
2541
2549
  cornerRadius?: number;
@@ -184,6 +184,25 @@ cells inside a `Card([VStack(spacing: 0)])`.
184
184
  > eagerly on Android to avoid nested-scroll measurement failures, even in a fixed-height panel. A
185
185
  > worker-bound horizontal `LazyList` is eager on Android too, so keep rails short.
186
186
 
187
+ **Cursor-backed lists and grids use automatic pagination.** Bind `pagination.cursor` and
188
+ `pagination.onLoadMore` on every layout, including compact and grid variants. Do not add a
189
+ "Show more" / "Load more" button. Keep loaded rows during a failed page, show a short error with
190
+ "Try again" below them, and retry the same cursor. Show one small trailing loader during a page
191
+ request; remove it when the final page has arrived. Retain pull-to-refresh. Keep a long lazy list
192
+ at the screen's scroll root; a nested eager list is not an infinite-scroll implementation.
193
+ `himi validate` warns (`manual_cursor_pagination`) when a button invokes a declared next-page action.
194
+
195
+ **Product galleries use paged carousels.** For worker images, use `Carousel` with `itemsPath`,
196
+ `paged: true`, and a stable `$item.id` on the page template. Each page fills the viewport without
197
+ side padding, spacing or corner rounding; keep a fixed image height so swiping cannot move the
198
+ content below it. Bind `currentIndexPath` when selecting an option should select its image.
199
+ Omitting `paged` preserves existing horizontal card rails. Page indicators show only for multiple images.
200
+
201
+ **Reveal invalid fields.** Increment a non-empty request token and bind it through the field row's
202
+ `modifiers.scrollIntoViewPath` to reveal the picker and its error, and move accessibility focus there.
203
+ Send a token only to the first invalid field. Clear it once that field is valid. This is scoped to
204
+ the rendered screen on iOS, Android, macOS and web; older hosts ignore the additive modifier.
205
+
187
206
  **Text** — `Text` for a single styled string (`maxLines` to truncate); `RichText` for inline mixed
188
207
  marks (bold/italic/link within one paragraph); `RichContent` for a Wix Ricos document with blocks,
189
208
  media, lists, or tables; `Label` for an icon+text pair; `Marquee` only for genuinely scrolling text.
@@ -241,6 +260,9 @@ validation, which is what it used to cost.
241
260
  for the standard case; `Fab` for a single floating primary action on a scroll; `Menu` for a set of
242
261
  secondary actions behind one affordance. *One `primary` button per screen.* An **`iconOnly` button still
243
262
  needs a `title`** (`Button.title` is non-optional in the native schema — it doubles as the a11y label).
263
+ For a pending worker request, keep that same button and bind `loadingPath: "$service:checkout.adding"`.
264
+ It shows a spinner, preserves the enabled palette and blocks duplicate taps until the worker clears it.
265
+ Use a live `title` binding to announce “Adding to cart” while pending; `loading` remains a literal fallback.
244
266
  > **Required fields still matter.** A single missing non-optional field (e.g. a titleless `Button`)
245
267
  > can make the **whole screen** fail to decode and fall back to the app's generic *"Couldn't load"*
246
268
  > error state. An unknown value in an optional enum field decodes to nil (the field is simply absent; only a required enum with an explicit fallback receives one), and an undecodable child
@@ -614,3 +636,5 @@ Read it top to bottom before authoring your first screen — it's the shortest p
614
636
  *The shipping vocabulary always wins over this guide:* `himi components`, `himi docs tokens`,
615
637
  `himi docs component-reference`. *Do the wiring with* `himi docs tier-5-add-a-screen`; *check your
616
638
  work with* `himi validate`, `himi render` and `himi test`.
639
+
640
+ The paged bound carousel and `scrollIntoViewPath` require a native host built with these schema additions. Older hosts ignore optional fields; ship the updated native renderer before relying on paging or validation focus in an OTA screen update.
@@ -6,7 +6,7 @@ repurposed when human wording changes. New codes may be added; consumers must
6
6
  handle an unknown code as a failure, without parsing prose.
7
7
 
8
8
  **Rollout:** this contract is enforced for **`himi push`**, including `--only`,
9
- first, for **`himi store iap connect`**, for **`himi site member-app`**, for **`himi fidelity`**, for the **`himi base44`** entity and function verbs, and for **`himi l10n import`**. Other subcommands still have legacy failures without codes and are a
9
+ first, for **`himi publish-size`**, for **`himi store iap connect`**, for **`himi site member-app`**, for **`himi fidelity`**, for the **`himi base44`** entity and function verbs, and for **`himi l10n import`**. Other subcommands still have legacy failures without codes and are a
10
10
  follow-up migration. This is an additive change: existing `error`, `errors`,
11
11
  `message`, `hint`, status, body, and gate-specific fields stay as they were.
12
12
  Some legacy envelopes have no singular `error` or `hint`; consumers must not
@@ -47,6 +47,22 @@ keeps its existing fields and adds the code:
47
47
  | `source_icon_unavailable` | Verified source evidence or its logo is missing, unreadable, unavailable, over the download/decoding budget, or cannot be normalized. |
48
48
  | `source_icon_unusable` | The prepared source mark fails mechanical launcher/store checks. Existing icon files and metadata are left unchanged. |
49
49
 
50
+ ## `himi publish-size` codes
51
+
52
+ This local planning command never claims or publishes. Every failure includes
53
+ `pushed: false` and `claimed: false`.
54
+
55
+ | Code | Meaning |
56
+ | --- | --- |
57
+ | `invalid_usage` | Unsupported, repeated, missing or invalid measurement options (exit 2). Publishing flags are refused. |
58
+ | `build_failed` | The contained local release could not be built completely, required host/network access, or the isolated build failed; measuring missing screens or bundles would undercount (exit 1). |
59
+ | `app_source_contains_secrets` | The shared source scanner found possible credentials (exit 1). |
60
+ | `app_source_too_large` | The base64 source archive exceeds its independent limit (exit 1). |
61
+ | `publish_body_too_large` | Measured draft JSON plus the explicit metadata planning reserve exceeds the publish budget (exit 1). `bodyBytes` and `overageBytes` describe the measured envelope; `planningBytes` and `planningOverageBytes` also include the reserve. |
62
+
63
+ `ok: true` means the measured draft plus its reserve fits. It does not certify
64
+ publication readiness or bound metadata not yet supplied to the final push.
65
+
50
66
  ## `himi one-app validate-payments` codes
51
67
 
52
68
  | Code | Meaning |
@@ -58,6 +74,19 @@ keeps its existing fields and adds the code:
58
74
  The checker validates recorded evidence and decisions; it does not independently
59
75
  verify merchant readiness or create transactions. General publication remains unchanged.
60
76
 
77
+ ## `himi one-app validate-identity` codes
78
+
79
+ | Code | Meaning |
80
+ | --- | --- |
81
+ | `invalid_usage` | Missing, repeated or unsupported identity-check arguments. |
82
+ | `identity_source_unverified` | Collected source hashes or the business display name cannot be verified. Recollect older evidence that did not save business identity. |
83
+ | `identity_target_unverified` | The technical app slug, target compilation or complete built descriptors are unavailable. |
84
+ | `literal_slug_visible` | Built display text or the known business-chat identity seed contains the technical slug instead of the collected business identity, without matching exact authored source copy. |
85
+
86
+ The check reads built descriptors, not every possible runtime string. A passing
87
+ result does not verify dynamic live data, translations, branding images or device
88
+ appearance. It does not change publication or coverage policy.
89
+
61
90
  ## Push codes
62
91
 
63
92
  | Code | Meaning |
@@ -45,6 +45,7 @@ package in one greppable file) or `GET /v1/sdk/<name>` over HTTP; in the CLI,
45
45
  - `@himalaya/location` — Tier-5 typed binding for device location.
46
46
  - `@himalaya/media-playback` — Tier-5 typed binding for background-capable media playback and offline downloads.
47
47
  - `@himalaya/motion` — Tier-5 typed binding for the device's motion sensors.
48
+ - `@himalaya/network` — Tier-5 typed binding for the platform's network capability.
48
49
  - `@himalaya/on-device-ai` — Tier-5 typed binding for ON-DEVICE text generation.
49
50
  - `@himalaya/open-url` — Tier-5 typed binding for opening links + external apps.
50
51
  - `@himalaya/pdf` — Tier-5 typed binding for viewing PDF documents.
@@ -16,7 +16,7 @@ Use this companion to the [Structure inference guide](https://apps.wix.com/_api/
16
16
  - Existing screen builders often return a complete screen. Preserve the source inline placement using supported composition; do not replace a mixed source screen with an unrelated full vertical screen.
17
17
  - Map data selectors, labels/media, design, permissions, CTA parameters and target destinations. The source definition's `settingsData.componentId` identifies a source editor component, not a Himalaya implementation or a complete settings schema.
18
18
 
19
- Source snapshots: Catalyst `617c06d43d2a5608b6876d724553269b1e5346f0`; Himalaya `97f1542f2f7d6e8cb7a902b912c87dcaa37e5a67`. GitHub references are pinned to these inspected versions. Recheck implementation contracts if using another version. No customer data was fetched, no app was generated, and no parity tests were run for this documentation inventory.
19
+ Source snapshots: Catalyst `617c06d43d2a5608b6876d724553269b1e5346f0`; Himalaya `97f1542f2f7d6e8cb7a902b912c87dcaa37e5a67`. GitHub references are pinned to these inspected versions. Recheck implementation contracts if using another version. No customer data was fetched, no app was generated, and no parity tests were run for the original documentation inventory. The Pages row now links the current source-selected editorial reconstruction recipe; its capture/decoder tests do not certify an individual app’s parity.
20
20
 
21
21
  ## Coverage summary
22
22
 
@@ -24,9 +24,9 @@ Source snapshots: Catalyst `617c06d43d2a5608b6876d724553269b1e5346f0`; Himalaya
24
24
  | --- | ---: |
25
25
  | Module | 13 |
26
26
  | Primitive | 7 |
27
- | Composition | 8 |
27
+ | Composition | 9 |
28
28
  | Partial | 24 |
29
- | Empty | 25 |
29
+ | Empty | 24 |
30
30
  | Total | 77 |
31
31
 
32
32
  ## Mappings by server group
@@ -169,7 +169,7 @@ carousel placement remain separate gaps; the mappings above stay Partial.
169
169
 
170
170
  | Source sectionDefinitionId | Source viewer component | Himalaya implementation | Placement / reuse | Class | Settings, actions and gaps |
171
171
  | --- | --- | --- | --- | --- | --- |
172
- | `pages_section` | `Pages.SectionPageClientScreen` | | | Empty | No matching Wix Pages content fetch/render adapter was found. Generic CMS or RichContent renderers do not identify the original page entity or its data contract. |
172
+ | `pages_section` | `Pages.SectionPageClientScreen` | [Pages capture recipe](https://apps.wix.com/_api/himi-serve/docs/one-app-to-himi-app.md#referenced-pages-content) / `oneAppPageSection` | Editable inline native RichContent | Composition | Select `settings.value.pageId`; use the collector’s Visitor-only referenced read and `pages-content.json`. Preserve source placement order, text, formatting and links. Ready/empty captures can be reconstructed; missing, forbidden, malformed and unsupported captures remain explicit gaps. This is editorial reconstruction, not a live runtime Pages adapter. |
173
173
 
174
174
  ### 12. photoalbum-section-definitions.json
175
175
 
@@ -358,7 +358,6 @@ These are the exact definitions whose target cells above remain empty. “Not fo
358
358
  | `loyalty-section-definitions.json` | `loyalty_status_section` | The existing Loyalty worker lists the program and rewards but does not load this member's points balance/tier/status. A rewards catalog is not a loyalty-status widget. |
359
359
  | `referrals-section-definitions.json` | `referrals_refer_section` | No consumer referral-code/link sharing and referral-status feature was found. Do not map to Loyalty or generic sharing without a user decision. A visitor-readable program read exists; see the row above. |
360
360
  | `referrals-section-definitions.json` | `referrals_description_section` | No referral-program description/data implementation was found. Text primitives alone do not resolve the business-bound referral content. A visitor-readable program read exists; see the row above. |
361
- | `pages-section-definitions.json` | `pages_section` | No matching Wix Pages content fetch/render adapter was found. Generic CMS or RichContent renderers do not identify the original page entity or its data contract. |
362
361
  | `photoalbum-section-definitions.json` | `photoAlbums_list_section` | Existing Photo Albums code is an empty-state placeholder with no album API wired. Pro Gallery uses a different data source and is not a substitute; choose an implementation explicitly. |
363
362
  | `photoalbum-section-definitions.json` | `photoAlbums_photos_list` | Existing Photo Albums code is an empty-state placeholder with no album API wired. Pro Gallery uses a different data source and is not a substitute; choose an implementation explicitly. |
364
363
  | `photoalbum-section-definitions.json` | `photoAlbums_carousel_section` | Existing Photo Albums code is an empty-state placeholder with no album API wired. Pro Gallery uses a different data source and is not a substitute; choose an implementation explicitly. |