@wix/himalaya-cli 0.1612.0 → 0.1614.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.
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/builder.d.ts +5 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/ui/src/index.d.ts +53 -13
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +15 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +4 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/ui.mjs +31 -5
- package/dist/cli.mjs +2189 -1377
- package/dist/preview-assets.generated.bin +0 -0
- package/fixtures/wix-canned.json +4 -0
- package/guides/component-reference.json +7 -4
- package/guides/component-reference.md +16 -2
- package/guides/designing-himi-screens.md +4 -1
- package/guides/getting-started.md +3 -2
- package/guides/hhp.md +2 -2
- package/guides/himi-json-output.md +3 -0
- package/guides/himi-skill.md +3 -2
- package/guides/himi-test.md +65 -2
- package/guides/one-app-to-himi-app.md +1 -1
- package/guides/recipe-form-submit.md +9 -0
- package/guides/sdk-reference.json +28 -4
- package/package.json +2 -2
- package/stdlib/fonts/catalog.json +141 -0
- package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +178 -12
- package/templates/branded-lite/dev/index.ts +192 -0
- package/templates/branded-lite/l10n/en.json +1 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +178 -12
- package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +178 -12
- package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +178 -12
- package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +178 -12
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +178 -12
- package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +178 -12
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +178 -12
- package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +178 -12
- package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +178 -12
- package/templates/yoga-studio/dev/index.ts +192 -0
- package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +178 -12
|
Binary file
|
package/fixtures/wix-canned.json
CHANGED
|
@@ -625,6 +625,10 @@
|
|
|
625
625
|
"total": 2
|
|
626
626
|
}
|
|
627
627
|
},
|
|
628
|
+
"POST /online-programs/participants/v3/join-applications/query": {
|
|
629
|
+
"joinApplications": [],
|
|
630
|
+
"pagingMetadata": { "count": 0, "total": 0 }
|
|
631
|
+
},
|
|
628
632
|
"GET /pricing-plans/v2/plans/public": {
|
|
629
633
|
"plans": [
|
|
630
634
|
{
|
|
@@ -515,14 +515,17 @@
|
|
|
515
515
|
"currentIndexPath",
|
|
516
516
|
"itemsPath"
|
|
517
517
|
],
|
|
518
|
-
"propsSource": "export interface CarouselProps {\n /** Opt-in paging for itemsPath; absent preserves the horizontal card rail. */\n paged?: boolean;\n /**
|
|
518
|
+
"propsSource": "export interface CarouselProps {\n /** Opt-in paging for itemsPath; absent preserves the horizontal card rail. */\n paged?: boolean;\n /** #4673 — `$service:` path to the page index, two-way with `onChange`: a CHANGE of the resolved\n * value animates the carousel there (instantly under reduced motion); a swipe still fires\n * `onChange` with `args.index`, which the worker writes back. Out of range ⇒ clamped; not an\n * integer ⇒ ignored; equal to the current page ⇒ nothing; a move the binding made fires no\n * `onChange`. Drives literal `items` and `paged` bound ones; inside a bound row a substituted\n * literal places the page like `currentIndex`. A rail (`itemsPath` without `paged`) ignores it. */\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}"
|
|
519
519
|
},
|
|
520
520
|
{
|
|
521
521
|
"name": "Pager",
|
|
522
522
|
"category": "Navigation primitives",
|
|
523
523
|
"propsType": "PagerProps",
|
|
524
|
-
"bindings": [
|
|
525
|
-
|
|
524
|
+
"bindings": [
|
|
525
|
+
"currentIndexPath",
|
|
526
|
+
"pagesPath"
|
|
527
|
+
],
|
|
528
|
+
"propsSource": "export interface PagerProps {\n pages: Component[];\n currentIndex?: number;\n onChange?: Action;\n showIndicator?: boolean;\n /** As {@link CarouselProps.liveWindow}. */\n liveWindow?: number;\n /** #4673 — as {@link CarouselProps.currentIndexPath}: a worker-driven page, two-way with `onChange`. */\n currentIndexPath?: string;\n /** #4673 — `$service:` path to an array: `pages[0]` is the page TEMPLATE, instantiated once per\n * element with `$item.` substitution (as `Carousel.itemsPath`). Pages are keyed by `$item.id`, so\n * the page on screen stays on screen when items are inserted before it. */\n pagesPath?: string;\n}"
|
|
526
529
|
},
|
|
527
530
|
{
|
|
528
531
|
"name": "StepIndicator",
|
|
@@ -562,7 +565,7 @@
|
|
|
562
565
|
"category": "Feedback",
|
|
563
566
|
"propsType": "SnackbarProps",
|
|
564
567
|
"bindings": [],
|
|
565
|
-
"propsSource": "export interface SnackbarProps {\n message: string;\n action?: Action;\n durationMs?: number;\n severity?: Severity;\n /** #1059 — action label; overridable for localized hosts. Default \"Take action\". */\n actionLabel?: string;\n}"
|
|
568
|
+
"propsSource": "export interface SnackbarProps {\n message: string;\n action?: Action;\n durationMs?: number;\n severity?: Severity;\n /** #1059 — action label; overridable for localized hosts. Default \"Take action\". */\n actionLabel?: string;\n /** #4661 — a 40 pt/dp thumbnail at the leading edge (`https:` or a local file/asset URL; any\n * other is ignored), drawn by the same presenter as a worker's `ui.snackbar({ imageUrl })`. */\n imageUrl?: string;\n}"
|
|
566
569
|
},
|
|
567
570
|
{
|
|
568
571
|
"name": "AlertDialog",
|
|
@@ -63,7 +63,7 @@ See also **[Modifiers](#modifiers)** (the `modifiers` block) and **[Actions](#ac
|
|
|
63
63
|
| `Tabs` | Navigation primitives | `optionsPath`, `activeColorPath`, `selectedId` |
|
|
64
64
|
| `SegmentedControl` | Navigation primitives | `optionsPath`, `selectedId` |
|
|
65
65
|
| `Carousel` | Navigation primitives | `currentIndexPath`, `itemsPath` |
|
|
66
|
-
| `Pager` | Navigation primitives |
|
|
66
|
+
| `Pager` | Navigation primitives | `currentIndexPath`, `pagesPath` |
|
|
67
67
|
| `StepIndicator` | Navigation primitives | — |
|
|
68
68
|
| `CollapsingHeader` | Navigation primitives | — |
|
|
69
69
|
| `AvatarStrip` | Navigation primitives | `avatarsPath` |
|
|
@@ -1075,7 +1075,12 @@ export interface SegmentedControlProps {
|
|
|
1075
1075
|
export interface CarouselProps {
|
|
1076
1076
|
/** Opt-in paging for itemsPath; absent preserves the horizontal card rail. */
|
|
1077
1077
|
paged?: boolean;
|
|
1078
|
-
/**
|
|
1078
|
+
/** #4673 — `$service:` path to the page index, two-way with `onChange`: a CHANGE of the resolved
|
|
1079
|
+
* value animates the carousel there (instantly under reduced motion); a swipe still fires
|
|
1080
|
+
* `onChange` with `args.index`, which the worker writes back. Out of range ⇒ clamped; not an
|
|
1081
|
+
* integer ⇒ ignored; equal to the current page ⇒ nothing; a move the binding made fires no
|
|
1082
|
+
* `onChange`. Drives literal `items` and `paged` bound ones; inside a bound row a substituted
|
|
1083
|
+
* literal places the page like `currentIndex`. A rail (`itemsPath` without `paged`) ignores it. */
|
|
1079
1084
|
currentIndexPath?: string;
|
|
1080
1085
|
items: Component[];
|
|
1081
1086
|
currentIndex?: number;
|
|
@@ -1098,6 +1103,12 @@ export interface PagerProps {
|
|
|
1098
1103
|
showIndicator?: boolean;
|
|
1099
1104
|
/** As {@link CarouselProps.liveWindow}. */
|
|
1100
1105
|
liveWindow?: number;
|
|
1106
|
+
/** #4673 — as {@link CarouselProps.currentIndexPath}: a worker-driven page, two-way with `onChange`. */
|
|
1107
|
+
currentIndexPath?: string;
|
|
1108
|
+
/** #4673 — `$service:` path to an array: `pages[0]` is the page TEMPLATE, instantiated once per
|
|
1109
|
+
* element with `$item.` substitution (as `Carousel.itemsPath`). Pages are keyed by `$item.id`, so
|
|
1110
|
+
* the page on screen stays on screen when items are inserted before it. */
|
|
1111
|
+
pagesPath?: string;
|
|
1101
1112
|
}
|
|
1102
1113
|
```
|
|
1103
1114
|
|
|
@@ -1189,6 +1200,9 @@ export interface SnackbarProps {
|
|
|
1189
1200
|
severity?: Severity;
|
|
1190
1201
|
/** #1059 — action label; overridable for localized hosts. Default "Take action". */
|
|
1191
1202
|
actionLabel?: string;
|
|
1203
|
+
/** #4661 — a 40 pt/dp thumbnail at the leading edge (`https:` or a local file/asset URL; any
|
|
1204
|
+
* other is ignored), drawn by the same presenter as a worker's `ui.snackbar({ imageUrl })`. */
|
|
1205
|
+
imageUrl?: string;
|
|
1192
1206
|
}
|
|
1193
1207
|
```
|
|
1194
1208
|
|
|
@@ -307,7 +307,10 @@ contextual actions/detail; `Tooltip`/`Popover` for a small anchored hint. *Reach
|
|
|
307
307
|
inline where you place it; for a transient toast that floats at the bottom of the screen — an app
|
|
308
308
|
error after a failed action, say — use the `snackbar` presentation verb or `ui.snackbar({ message,
|
|
309
309
|
tone: "danger" })` from a worker: it floats above the tab bar, animates in and out, queues, honours
|
|
310
|
-
`durationMs`, and is announced to screen readers on every platform.
|
|
310
|
+
`durationMs`, and is announced to screen readers on every platform. It stays a toast: two lines of
|
|
311
|
+
message, one optional 40 pt thumbnail (`imageUrl`), one action of up to 24 characters, 2–10 s on screen.
|
|
312
|
+
For "Item deleted — Undo", a screen worker can `await` it and read `actionTapped` instead of wiring an
|
|
313
|
+
action id; anything richer is a descriptor overlay, not a snackbar.
|
|
311
314
|
|
|
312
315
|
**Status & data-viz** — `Badge`/`Tag`/`Chip` for compact labels; `Rating` for stars (`size: "sm"` in a
|
|
313
316
|
dense row); `ProgressBar` for determinate progress, `ActivityIndicator` for indeterminate; `Gauge` for
|
|
@@ -256,8 +256,9 @@ product. Right after `init`:
|
|
|
256
256
|
typed input → submit → the persisted result with a typed journey in `himi-test.json`
|
|
257
257
|
(`.himi/himi-test.stub.json` has a skeleton per form: fill the values and expected text)
|
|
258
258
|
(`--deep` replays action chains but never types a field value), then prove the destination
|
|
259
|
-
and cancel/back recovery separately
|
|
260
|
-
|
|
259
|
+
and cancel/back recovery separately. For device-local records, add explicit `coldVisits`
|
|
260
|
+
assertions from `himi docs himi-test` so another cold worker must read the same saved record.
|
|
261
|
+
Cold visits do not drive navigation or prove warm repaint/relaunch: drive those in the UI. A fresh-screen crawl alone
|
|
261
262
|
does not prove a flow.
|
|
262
263
|
|
|
263
264
|
Before authoring a multi-screen mobile app, ensure the customer-approved `SPEC.md` and
|
package/guides/hhp.md
CHANGED
|
@@ -210,13 +210,13 @@ Declare the mockup each screen was designed from in `hhp.json`:
|
|
|
210
210
|
A screen may also carry `params` (the navigation input a device opens it with, used by native capture, #4935) and `fidelity: { "warn": 80, "fail": 60 }`. Thresholds can also be set app-wide at the top level.
|
|
211
211
|
|
|
212
212
|
```bash
|
|
213
|
-
himi fidelity --content <dir> [--screen a,b] [--variant light,dark,rtl,tablet] [--ios <udid>] [--android <serial>] [--ios-tablet <udid>] [--android-tablet <serial>] [--app-id <id>] [--release <id>] [--no-native] [--json] [--post-pr <n>]
|
|
213
|
+
himi fidelity --content <dir> [--screen a,b] [--variant light,dark,rtl,tablet] [--ios <udid>] [--android <serial>] [--ios-tablet <udid>] [--android-tablet <serial>] [--app-id <id>] [--release <id>] [--serve <port>] [--no-native] [--json] [--post-pr <n>]
|
|
214
214
|
```
|
|
215
215
|
|
|
216
216
|
For each screen and variant (`light`, `dark`, `rtl`, `tablet`), the command does four things:
|
|
217
217
|
|
|
218
218
|
1. **Captures the mockup.** It draws the page in Chromium at the variant's width. Dark mode is drawn by emulating `prefers-color-scheme`. RTL is drawn by setting `dir=rtl` on the page.
|
|
219
|
-
2. **Captures the web render** from the app's recorded data, as `himi render` does by default. Native frames come from Himi Player on the devices you pass: `--ios <udid>` and `--android <serial>`, plus `--ios-tablet` and `--android-tablet` for the tablet variant. Only those devices are touched. For each variant the command sets dark or light appearance and, for `rtl`, the Hebrew language (iOS `AppleLanguages`, Android per-app locale, API 33+). It opens `himi://app/<app>/screen/<screen>` and captures once two frames match. Every setting it changed is restored afterwards, also on Ctrl-C. A variant with no device is reported `skipped`, never passed. Without `--release <id>` the Player shows the app's **live** release, not your working copy. A screen that declares `params` is skipped natively for now, because the Player's deep link does not carry them yet. On a fresh iOS simulator, open one `himi://` link by hand first and accept iOS's "Open in Himi Player?" prompt: the command cannot answer it, and until it is accepted every iOS capture shows the prompt.
|
|
219
|
+
2. **Captures the web render** from the app's recorded data, as `himi render` does by default. Native frames come from Himi Player on the devices you pass: `--ios <udid>` and `--android <serial>`, plus `--ios-tablet` and `--android-tablet` for the tablet variant. Only those devices are touched. For each variant the command sets dark or light appearance and, for `rtl`, the Hebrew language (iOS `AppleLanguages`, Android per-app locale, API 33+). It opens `himi://app/<app>/screen/<screen>` and captures once two frames match. Every setting it changed is restored afterwards, also on Ctrl-C. A variant with no device is reported `skipped`, never passed. Without `--release <id>` or `--serve <port>` the Player shows the app's **live** release, not your working copy. `--serve <port>` serves the working copy on `127.0.0.1:<port>` as `himi run` does, so native and web render the same release. The Player reads a local origin only from a Debug build that compiled it in (see [View your package on a device](himi-player-implementation.md#view-your-package-on-a-device)): build it with `HIMALAYA_API_BASE_URL_INFO=http://127.0.0.1:<port>` on iOS and `-PhimalayaDevServeBaseUrl=http://10.0.2.2:<port>` on Android. An iOS device whose Player reads another origin is skipped with the rebuild to run. Android's origin cannot be read back from the installed app, so check that build yourself. A screen that declares `params` is skipped natively for now, because the Player's deep link does not carry them yet. On a fresh iOS simulator, open one `himi://` link by hand first and accept iOS's "Open in Himi Player?" prompt: the command cannot answer it, and until it is accepted every iOS capture shows the prompt.
|
|
220
220
|
3. **Scores mockup↔web** with ImageMagick SSIM, globally and per tile. The worst tile says where the screens differ most.
|
|
221
221
|
4. **Asks a fresh model** two questions, using the repo's fidelity judge prompt. The model never ran in the session that wrote the app.
|
|
222
222
|
- **Design:** does WEB look like the MOCKUP? This judges the port.
|
|
@@ -44,7 +44,10 @@ keeps its existing fields and adds the code:
|
|
|
44
44
|
|
|
45
45
|
| Code | Meaning |
|
|
46
46
|
| --- | --- |
|
|
47
|
+
| `test_journey_budget_exceeded` | The authored config exceeds a shared journey/visit/worker-segment/step/assertion work bound. Exit 2 with `ok: false` and the existing `error` on stderr before compilation or worker boot. Tokenless `POST /v1/test` uses the same code with HTTP422 before upload compilation/evaluation. Split the evaluation into separately identified bounded runs; no truncated pass is emitted. |
|
|
47
48
|
| `test_journey_failed` | At least one unsuppressed authored journey failed: its destination is absent, initialization/action failed, or the expected rendered values were missing. The existing `failures`, `journeys`, counts and exit 1 remain. This includes cold-entry journeys with `steps: []`. |
|
|
49
|
+
| `test_browser_cold_visits_unsupported` | `--browser` was requested with an authored `coldVisits` contract. Exit 2 before compilation or opening a browser; run without `--browser` to exercise those visits. The browser loop cannot certify saved-record return behavior. |
|
|
50
|
+
| `test_browser_journeys_unsupported` | `--browser` was requested with authored journeys that have no `coldVisits`. Exit 2 before compilation or opening a browser. The single-screen browser loop does not execute authored steps or root `expectText`/`absentText` assertions; run without `--browser` to execute that contract. The existing cold-visit code remains in use when any journey has `coldVisits`. |
|
|
48
51
|
| `test_input_evidence_requires_json` | `--input-evidence` was requested without explicit `--json`. Exit 2 before build or worker execution. |
|
|
49
52
|
| `test_input_evidence_unsupported_mode` | `--input-evidence` was combined with `--browser`, or the content is a functions package rather than an app worker crawl. Exit 2 before execution. |
|
|
50
53
|
| `test_input_evidence_unavailable` | The isolated input crawl cannot establish all contained realms, cannot read a stable source archive before/after the crawl, or fails to return one complete JSON report (including interruption, process timeout or output limit). Exit 1; no host-realm fallback or positive input acceptance. |
|
package/guides/himi-skill.md
CHANGED
|
@@ -279,8 +279,9 @@ product. Right after `init`:
|
|
|
279
279
|
typed input → submit → the persisted result with a typed journey in `himi-test.json`
|
|
280
280
|
(`.himi/himi-test.stub.json` has a skeleton per form: fill the values and expected text)
|
|
281
281
|
(`--deep` replays action chains but never types a field value), then prove the destination
|
|
282
|
-
and cancel/back recovery separately
|
|
283
|
-
|
|
282
|
+
and cancel/back recovery separately. For device-local records, add explicit `coldVisits`
|
|
283
|
+
assertions from `himi docs himi-test` so another cold worker must read the same saved record.
|
|
284
|
+
Cold visits do not drive navigation or prove warm repaint/relaunch: drive those in the UI. A fresh-screen crawl alone
|
|
284
285
|
does not prove a flow.
|
|
285
286
|
|
|
286
287
|
Before authoring a multi-screen mobile app, ensure the customer-approved `SPEC.md` and
|
package/guides/himi-test.md
CHANGED
|
@@ -110,7 +110,7 @@ The receipt has these fields:
|
|
|
110
110
|
| `execution` | CLI-established `devModule`, `appConfig`, `workerBoot` and `workerAction` modes, each `quickjs-wasm`. A v1 report missing these fields predates the contained CLI path; do not accept it as contained execution. This marker is meaningful only when captured from the trusted pinned producer. |
|
|
111
111
|
| `probes` | Each synthesized typed dispatch's screen/component/action/hook, contract control and injected field/value, actual argument list, precondition input/current value/state hash, post-state hash, and observed state/navigation/request/host-call/throw results. Zero probes is not proof that required controls ran. |
|
|
112
112
|
| `journeys` | Actual screen/input, each attempted step's scope, raw `declaredPayload`, effective row-resolved `descriptorPayload`, exact `testOverrides`, final merged `payload`, pre/post state hashes, executed/threw/not-dispatched status, final visible observations and evaluator-computed assertions. Unavailable actions remain not-dispatched, never successful steps. |
|
|
113
|
-
| `unavailableScreens`, `unavailableJourneys` | Requested coverage for which the evaluator did not supply v1 evidence, including older engines, invalid/collapsed screens and unavailable journeys. With positional screen IDs, these lists contain only requested roots; requested unknown IDs remain unavailable. Unrelated configured journeys do not run or fail a scoped run. These gaps are not accepted behavior. |
|
|
113
|
+
| `unavailableScreens`, `unavailableJourneys` | Requested coverage for which the evaluator did not supply evaluated v1 evidence, including older engines, invalid/collapsed screens and explicitly unavailable source journeys. An unavailable source can still retain evaluated nested cold-visit receipts. With positional screen IDs, these lists contain only requested roots; requested unknown IDs remain unavailable. Unrelated configured journeys do not run or fail a scoped run. These gaps are not accepted behavior. |
|
|
114
114
|
|
|
115
115
|
State hashes fingerprint the exact serialized worker snapshot captured at the dispatch
|
|
116
116
|
boundary. `currentValuePresent` distinguishes an absent current value from null.
|
|
@@ -189,6 +189,69 @@ edge would hand it — so the journey boots the way a real tap into it would. Ke
|
|
|
189
189
|
navigation in the ordinary edge pass, which boots the destination with real source
|
|
190
190
|
parameters. Pair dynamic journeys with app-specific `dev/net-mocks.json` fixtures.
|
|
191
191
|
|
|
192
|
+
### Prove a local mutation survives a cold visit
|
|
193
|
+
|
|
194
|
+
A success message proves only the current screen's outcome. It does not prove that another
|
|
195
|
+
screen can read the saved record. Add `coldVisits` to a typed journey when the approved flow
|
|
196
|
+
requires another screen worker to read a created, edited or removed authoritative local record:
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"journeys": [{
|
|
201
|
+
"name": "create then read the same record",
|
|
202
|
+
"screen": "record_form",
|
|
203
|
+
"steps": [{
|
|
204
|
+
"action": "tier5.records.save",
|
|
205
|
+
"params": { "id": "probe-record", "title": "Field notebook", "notes": "Keep this note" }
|
|
206
|
+
}],
|
|
207
|
+
"expectText": ["Saved"],
|
|
208
|
+
"coldVisits": [
|
|
209
|
+
{ "screen": "record_list", "expectText": ["Field notebook"] },
|
|
210
|
+
{ "screen": "record_detail", "input": { "id": "probe-record" },
|
|
211
|
+
"expectText": ["Field notebook", "Keep this note"] },
|
|
212
|
+
{ "screen": "record_list", "expectText": ["Field notebook"] }
|
|
213
|
+
]
|
|
214
|
+
}]
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Use the app's actual screen/action names, field payloads and independently chosen expected
|
|
219
|
+
values. Every visit boots a **new worker realm** with only that destination's descriptor and
|
|
220
|
+
`input`; module variables and service state do not cross. Visits within one journey share a
|
|
221
|
+
private hermetic `local_database`, `secure_storage` and app-scoped `himalaya.storage` store. Each journey starts with empty
|
|
222
|
+
stores. Destination capability declarations and the app's local-entity schema still apply.
|
|
223
|
+
The ordinary crawl's other axes keep their fresh stores.
|
|
224
|
+
|
|
225
|
+
A visit can also have `steps`, using the same declared worker-action contract as the initial
|
|
226
|
+
journey. For a destructive flow, request removal then cancel, cold-visit the list and require
|
|
227
|
+
the selected row plus an unrelated row. In a separate sequence, confirm, then require the
|
|
228
|
+
unrelated row and use `"absentText": ["unique removed title"]`. `absentText` works on the
|
|
229
|
+
initial journey too. Both text checks use substrings of the resolved visible render tree;
|
|
230
|
+
choose distinct values that cannot be satisfied by another row or a static heading. Assert
|
|
231
|
+
all required fields and displayed totals, not just a generic success line. A missing screen,
|
|
232
|
+
failed expectation or unexecuted visit fails the journey; JSON reports include a receipt for
|
|
233
|
+
each cold visit.
|
|
234
|
+
|
|
235
|
+
Authored work is admitted as a whole before any worker executes: at most 32 journeys,
|
|
236
|
+
32 cold visits per journey,128 total worker segments (sources plus visits),512 total
|
|
237
|
+
steps and2048 total positive/absent assertions. These shared limits apply to the CLI,
|
|
238
|
+
`POST /v1/test` and direct crawl callers. Over-budget work is rejected with
|
|
239
|
+
`test_journey_budget_exceeded`; visits or receipts are never silently truncated. Split a
|
|
240
|
+
larger evaluation into separately identified runs. A bundleless source retains an
|
|
241
|
+
explicit `worker-unavailable` receipt and every later visit's positional outcome.
|
|
242
|
+
Validation-blocked destination workers do not execute, but their baked descriptor
|
|
243
|
+
input and explicit visit overrides are retained in their unavailable receipt.
|
|
244
|
+
|
|
245
|
+
A same-screen favorite may legitimately use a retained worker snapshot; this no-snapshot check does not test that contract.
|
|
246
|
+
|
|
247
|
+
This is **headless local-store proof**, not a router drive or a real browser database
|
|
248
|
+
relaunch. It does not hydrate worker snapshots, run app services, synchronize with a backend,
|
|
249
|
+
or prove warm-screen refresh, native behavior, account switching or deployed-service
|
|
250
|
+
acceptance. Keep the normal navigation edge checks and drive the actual UI through return
|
|
251
|
+
and reload/relaunch on each required platform. Inspect storage separately when the row
|
|
252
|
+
exists but the screen is stale. The state ownership choices remain in
|
|
253
|
+
[tier-5-add-a-screen.md](tier-5-add-a-screen.md#state-does-not-cross-screens-automatically).
|
|
254
|
+
|
|
192
255
|
Journeys run only the initialization the bundle and descriptor actually declare: module-scope
|
|
193
256
|
calls and the root `onAppear`. Unlike the discovery crawl's convenience `load()` sweep, they do
|
|
194
257
|
not call an otherwise uncalled loader. A declaration of `actions.load` alone does not initialize
|
|
@@ -575,7 +638,7 @@ behavior. This instruction does not add an automatic suppression or retry gate t
|
|
|
575
638
|
| `--budget <ms>` | Per-action settle budget (default 2000). |
|
|
576
639
|
| `--native` | Also decode every baked descriptor, and the release's `tokens.json`, with the real native decoders; bare `--native` runs whichever toolchains are present and names each one it could NOT check in a top-level `skippedPlatforms: [{ platform, reason }]` (never a pass), while `--native ios` or `--native android` selects one and exits 2 when it is unavailable. The Android SDK is found from `ANDROID_HOME`, then `ANDROID_SDK_ROOT`, then `sdk.dir` in the source checkout's Android test harness `local.properties`, then the default install path (`~/Library/Android/sdk` on macOS, `~/Android/Sdk` on Linux). **Source-checkout only** — the decoder oracles are that tree's own HimalayaCore Swift package and Gradle harness, which a published install does not ship, so it cannot run this at all (and its coverage ledger stays silent about native decoding rather than suggesting it). |
|
|
577
640
|
| `--all-screens` | Crawl every templated instance instead of one representative per structure. |
|
|
578
|
-
| `--browser` / `--no-open` / `--timeout <ms>` | Run the loop in your own browser tab (one screen per run; refuses record/net/offline/hostile/deep/budget/shots/native). |
|
|
641
|
+
| `--browser` / `--no-open` / `--timeout <ms>` | Run the loop in your own browser tab (one screen per run; refuses all authored journeys, including root assertions and `coldVisits`, and record/net/offline/hostile/deep/budget/shots/native). |
|
|
579
642
|
| `himi test <screenId>…` | Focus one or more screens — the record and shots passes scope with them. |
|
|
580
643
|
|
|
581
644
|
**Screenshots** are on by default when a headless browser is installed: one PNG per screen
|
|
@@ -345,7 +345,7 @@ Apply these interpretation rules:
|
|
|
345
345
|
typography tokens use the body family. Numeric metrics stay unchanged. Read each
|
|
346
346
|
source heading/body family and check `himi fonts list`; use `himi fonts add <Family>`
|
|
347
347
|
when it is available. A known source family that Himi does not ship (for example
|
|
348
|
-
`
|
|
348
|
+
`sora` → Sora) is still compared with the actual target family.
|
|
349
349
|
Choose an available replacement, such as `$system`, only with the owner's explicit
|
|
350
350
|
instruction; this is an adaptation, never a font match. Sync the coverage ledger
|
|
351
351
|
against the current source evidence, then mark the corresponding `design` requirement
|
|
@@ -233,3 +233,12 @@ shape (heading swap, `savedScreenId`, back-navigation).
|
|
|
233
233
|
- `himi docs recipe-dynamic-list` — the list the created entity lands in.
|
|
234
234
|
- `himi components Form` / `FormField` / `TextField` / `Toggle` / `Picker`.
|
|
235
235
|
- `.agents/skills/himalaya/examples/form-submit.*` (written into the app by `himi init` / `himi agent-init`) — this recipe, runnable.
|
|
236
|
+
|
|
237
|
+
## Verify the saved record outside this worker
|
|
238
|
+
|
|
239
|
+
For a local create/edit flow, a saved banner alone is insufficient. Add a typed journey
|
|
240
|
+
with [cold visits](../himi-test.md#prove-a-local-mutation-survives-a-cold-visit) that reads
|
|
241
|
+
the same record from its list and detail, checks all entered fields and relevant totals,
|
|
242
|
+
and preserves an unrelated record. Then drive return and reload in the actual UI. Choose
|
|
243
|
+
the storage owner using [the cross-screen state contract](../tier-5-add-a-screen.md#state-does-not-cross-screens-automatically);
|
|
244
|
+
a worker snapshot is not a shared record store.
|
|
@@ -5332,19 +5332,43 @@
|
|
|
5332
5332
|
"name": "SnackbarOptions",
|
|
5333
5333
|
"kind": "interface",
|
|
5334
5334
|
"jsdoc": "",
|
|
5335
|
-
"decl": "export interface SnackbarOptions {\n message: string;\n /** Default \"info\". */\n tone?: SnackbarTone;\n /** Auto-dismiss delay in ms, on every host.
|
|
5335
|
+
"decl": "export interface SnackbarOptions {\n /** At most two lines on screen; a longer message is ellipsised. */\n message: string;\n /** Default \"info\". */\n tone?: SnackbarTone;\n /** Auto-dismiss delay in ms, clamped on every host to {@link SNACKBAR_LIMITS} (2–10 s, at least\n * 4 s with an `action` so it can be reached by VoiceOver / TalkBack before it goes). Absent, 0 or\n * less: the default, 3000 (4000 with an `action`). */\n durationMs?: number;\n /** A thumbnail of the thing the toast is about: a 40 pt square at the leading edge. `https:` on\n * every host; a local `file:` URL on iOS and macOS only, whose image pipeline already reads local\n * files. Any other URL is ignored (the toast shows without it), and an image that fails to load\n * just hides. */\n imageUrl?: string;\n /** One action button (\"Undo\", \"View\"). Tapping it dismisses the snackbar and resolves the call\n * `{ actionTapped: true }`. With an `actionId` the tap also runs that action with `args`. The id\n * must be a worker action:\n * - one of this bundle's own `tier5.<svc>.<action>` ids — run on THIS worker, never another\n * screen's or the app-level worker's;\n * - an app service's `app.<svc>.<action>` — needs `app_services` and an exported action (an app\n * service's own id needs neither). The web host refuses it until it hosts app services.\n * Any other id (`capability.*`, `himi.*`, a registered handler) is refused when the snackbar is\n * requested: the call rejects and nothing is shown. A host that cannot tell which worker asked\n * (React Native, an embedded web reg\n/* … */"
|
|
5336
5336
|
},
|
|
5337
5337
|
{
|
|
5338
5338
|
"name": "SnackbarAction",
|
|
5339
5339
|
"kind": "interface",
|
|
5340
5340
|
"jsdoc": "",
|
|
5341
|
-
"decl": "export interface SnackbarAction {\n /** The button's text — also its VoiceOver / TalkBack / screen-reader label. */\n label: string;\n /** What the tap runs: one of this bundle's own `tier5.<svc>.<action>` ids, or
|
|
5341
|
+
"decl": "export interface SnackbarAction {\n /** The button's text — also its VoiceOver / TalkBack / screen-reader label. At most 24\n * characters; a longer label is ellipsised. */\n label: string;\n /** What the tap runs, if anything: one of this bundle's own `tier5.<svc>.<action>` ids, or\n * `app.<svc>.<action>`. A screen worker may leave it out and branch on `actionTapped` instead\n * (\"Undo\"); an app service may not. */\n actionId?: string;\n /** The action's arguments, which the reducer receives as typed JSON. */\n args?: Record<string, unknown>;\n}"
|
|
5342
|
+
},
|
|
5343
|
+
{
|
|
5344
|
+
"name": "SnackbarResult",
|
|
5345
|
+
"kind": "interface",
|
|
5346
|
+
"jsdoc": "/** What a snackbar call resolves with. */",
|
|
5347
|
+
"decl": "export interface SnackbarResult {\n /** The user tapped the action. Always false for a snackbar without one (resolved on\n * presentation); with one, false when it was dismissed any other way — timed out, swiped, closed,\n * or replaced by a newer snackbar. */\n actionTapped: boolean;\n}"
|
|
5348
|
+
},
|
|
5349
|
+
{
|
|
5350
|
+
"name": "SNACKBAR_LIMITS",
|
|
5351
|
+
"kind": "const",
|
|
5352
|
+
"jsdoc": "/** #4661 — the constraints that keep a snackbar a toast, enforced here and again by every host. */",
|
|
5353
|
+
"decl": "export const SNACKBAR_LIMITS = {\n messageLines: 2,\n labelChars: 24,\n minDurationMs: 2000,\n /** The floor with an action: long enough to reach it with a screen reader. */\n minActionDurationMs: 4000,\n maxDurationMs: 10_000,\n /** The thumbnail's side, in pt / dp / CSS px. */\n imageSize: 40,\n} as const;"
|
|
5354
|
+
},
|
|
5355
|
+
{
|
|
5356
|
+
"name": "snackbarDurationMs",
|
|
5357
|
+
"kind": "function",
|
|
5358
|
+
"jsdoc": "/** The duration a host shows a snackbar for: absent / non-finite / 0 or less → undefined (the host\n * default applies), else clamped to {@link SNACKBAR_LIMITS}. */",
|
|
5359
|
+
"decl": "export function snackbarDurationMs(durationMs: number | undefined, hasAction: boolean): number | undefined;"
|
|
5360
|
+
},
|
|
5361
|
+
{
|
|
5362
|
+
"name": "snackbarLabel",
|
|
5363
|
+
"kind": "function",
|
|
5364
|
+
"jsdoc": "/** An action label as every host draws it: at most {@link SNACKBAR_LIMITS}.labelChars characters\n * (by code point, so an emoji is never cut in half), the last one an ellipsis when it was cut. */",
|
|
5365
|
+
"decl": "export function snackbarLabel(label: string): string;"
|
|
5342
5366
|
},
|
|
5343
5367
|
{
|
|
5344
5368
|
"name": "snackbar",
|
|
5345
5369
|
"kind": "function",
|
|
5346
|
-
"jsdoc": "/** Show a transient snackbar toast: it floats above the tab bar, slides and fades in and out\n * (fades only under reduced motion), queues behind one that is already showing, and is
|
|
5347
|
-
"decl": "export async function snackbar(options: SnackbarOptions): Promise<
|
|
5370
|
+
"jsdoc": "/** Show a transient snackbar toast: it floats above the tab bar, slides and fades in and out\n * (fades only under reduced motion), queues behind one that is already showing, and is announced\n * by VoiceOver / TalkBack / the screen reader. Without an `action` it resolves\n * `{ actionTapped: false }` once it is queued, as before; with one it resolves when the snackbar\n * goes, `{ actionTapped: true }` only if the action was tapped (see {@link SnackbarResult}).\n * Rejects with `no_ui_host` if no presenter is attached; with an `action`, also with\n * `invalid_action` (empty label), `action_id_required` (an app service with no `actionId`),\n * `action_not_allowed` (not one of this bundle's actions), or, for an `app.*` id, `undeclared` (no\n * `app_services`), `not_exported`, `no_service`, `no_action`, `not_booted` or `unavailable`. */",
|
|
5371
|
+
"decl": "export async function snackbar(options: SnackbarOptions): Promise<SnackbarResult>;"
|
|
5348
5372
|
},
|
|
5349
5373
|
{
|
|
5350
5374
|
"name": "version",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1614.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": [
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"artifactId": "himalaya-cli"
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
|
-
"falconPackageHash": "
|
|
75
|
+
"falconPackageHash": "5d96af6574de509f64d0dfcd8cb4660e4a7d81a0deb73b852e474acd"
|
|
76
76
|
}
|
|
@@ -210,6 +210,32 @@
|
|
|
210
210
|
}
|
|
211
211
|
]
|
|
212
212
|
},
|
|
213
|
+
"DM Serif Display": {
|
|
214
|
+
"category": "serif",
|
|
215
|
+
"dir": "ofl/dmserifdisplay",
|
|
216
|
+
"webFallback": "serif",
|
|
217
|
+
"aliases": [
|
|
218
|
+
"dmserifdisplay",
|
|
219
|
+
"dm serif display",
|
|
220
|
+
"dm serif"
|
|
221
|
+
],
|
|
222
|
+
"license": {
|
|
223
|
+
"id": "OFL-1.1",
|
|
224
|
+
"url": "https://raw.githubusercontent.com/google/fonts/8e44913e4ff26fc997e6856c1ec40ff4791c98c5/ofl/dmserifdisplay/OFL.txt",
|
|
225
|
+
"copyright": "Copyright 2014-2018 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. Copyright 2019 Google LLC."
|
|
226
|
+
},
|
|
227
|
+
"faces": [
|
|
228
|
+
{
|
|
229
|
+
"weight": 400,
|
|
230
|
+
"style": "normal",
|
|
231
|
+
"postScriptName": "DMSerifDisplay-Regular",
|
|
232
|
+
"file": "DMSerifDisplay-Regular.ttf",
|
|
233
|
+
"sha256": "8cc3643535edf039aa5d95440a8542735e9197e4f4b8d9303e980fefbf5ab616",
|
|
234
|
+
"sri": "sha256-jMNkNTXt8DmqXZVECoVCc16Rl+T0uNkwPpgP779athY=",
|
|
235
|
+
"bytes": 76580
|
|
236
|
+
}
|
|
237
|
+
]
|
|
238
|
+
},
|
|
213
239
|
"Dancing Script": {
|
|
214
240
|
"category": "handwriting",
|
|
215
241
|
"dir": "ofl/dancingscript",
|
|
@@ -279,6 +305,54 @@
|
|
|
279
305
|
}
|
|
280
306
|
]
|
|
281
307
|
},
|
|
308
|
+
"Fraunces": {
|
|
309
|
+
"category": "serif",
|
|
310
|
+
"dir": "ofl/fraunces",
|
|
311
|
+
"webFallback": "serif",
|
|
312
|
+
"aliases": [
|
|
313
|
+
"fraunces"
|
|
314
|
+
],
|
|
315
|
+
"license": {
|
|
316
|
+
"id": "OFL-1.1",
|
|
317
|
+
"url": "https://raw.githubusercontent.com/google/fonts/8e44913e4ff26fc997e6856c1ec40ff4791c98c5/ofl/fraunces/OFL.txt",
|
|
318
|
+
"copyright": "Copyright 2018 The Fraunces Project Authors (https://github.com/undercasetype/Fraunces)"
|
|
319
|
+
},
|
|
320
|
+
"variable": {
|
|
321
|
+
"axes": {
|
|
322
|
+
"opsz": {
|
|
323
|
+
"min": 9,
|
|
324
|
+
"default": 9,
|
|
325
|
+
"max": 144
|
|
326
|
+
},
|
|
327
|
+
"wght": {
|
|
328
|
+
"min": 100,
|
|
329
|
+
"default": 900,
|
|
330
|
+
"max": 900
|
|
331
|
+
},
|
|
332
|
+
"SOFT": {
|
|
333
|
+
"min": 0,
|
|
334
|
+
"default": 0,
|
|
335
|
+
"max": 100
|
|
336
|
+
},
|
|
337
|
+
"WONK": {
|
|
338
|
+
"min": 0,
|
|
339
|
+
"default": 1,
|
|
340
|
+
"max": 1
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
"faces": [
|
|
345
|
+
{
|
|
346
|
+
"weight": 900,
|
|
347
|
+
"style": "normal",
|
|
348
|
+
"postScriptName": "Fraunces-9ptBlack",
|
|
349
|
+
"file": "Fraunces[SOFT,WONK,opsz,wght].ttf",
|
|
350
|
+
"sha256": "177ff6c0f14e5550a3c624247cd1189611d4eb65d000b14944c63d967958abbb",
|
|
351
|
+
"sri": "sha256-F3/2wPFOVVCjxiQkfNEYlhHU62XQALFJRMY9lnlYq7s=",
|
|
352
|
+
"bytes": 360440
|
|
353
|
+
}
|
|
354
|
+
]
|
|
355
|
+
},
|
|
282
356
|
"IBM Plex Mono": {
|
|
283
357
|
"category": "mono",
|
|
284
358
|
"dir": "ofl/ibmplexmono",
|
|
@@ -863,6 +937,40 @@
|
|
|
863
937
|
}
|
|
864
938
|
]
|
|
865
939
|
},
|
|
940
|
+
"Space Grotesk": {
|
|
941
|
+
"category": "sans",
|
|
942
|
+
"dir": "ofl/spacegrotesk",
|
|
943
|
+
"webFallback": "sans-serif",
|
|
944
|
+
"aliases": [
|
|
945
|
+
"spacegrotesk",
|
|
946
|
+
"space grotesk"
|
|
947
|
+
],
|
|
948
|
+
"license": {
|
|
949
|
+
"id": "OFL-1.1",
|
|
950
|
+
"url": "https://raw.githubusercontent.com/google/fonts/8e44913e4ff26fc997e6856c1ec40ff4791c98c5/ofl/spacegrotesk/OFL.txt",
|
|
951
|
+
"copyright": "Copyright 2020 The Space Grotesk Project Authors (https://github.com/floriankarsten/space-grotesk)"
|
|
952
|
+
},
|
|
953
|
+
"variable": {
|
|
954
|
+
"axes": {
|
|
955
|
+
"wght": {
|
|
956
|
+
"min": 300,
|
|
957
|
+
"default": 300,
|
|
958
|
+
"max": 700
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
},
|
|
962
|
+
"faces": [
|
|
963
|
+
{
|
|
964
|
+
"weight": 300,
|
|
965
|
+
"style": "normal",
|
|
966
|
+
"postScriptName": "SpaceGrotesk-Light",
|
|
967
|
+
"file": "SpaceGrotesk[wght].ttf",
|
|
968
|
+
"sha256": "acad6de1fc93436f5c0f1f4137751ef04f1aea3063e7036535970ffcfbd79f72",
|
|
969
|
+
"sri": "sha256-rK1t4fyTQ29cDx9BN3Ue8E8a6jBj5wNlNZcP/PvXn3I=",
|
|
970
|
+
"bytes": 136676
|
|
971
|
+
}
|
|
972
|
+
]
|
|
973
|
+
},
|
|
866
974
|
"Space Mono": {
|
|
867
975
|
"category": "mono",
|
|
868
976
|
"dir": "ofl/spacemono",
|
|
@@ -897,6 +1005,39 @@
|
|
|
897
1005
|
}
|
|
898
1006
|
]
|
|
899
1007
|
},
|
|
1008
|
+
"Vollkorn": {
|
|
1009
|
+
"category": "serif",
|
|
1010
|
+
"dir": "ofl/vollkorn",
|
|
1011
|
+
"webFallback": "serif",
|
|
1012
|
+
"aliases": [
|
|
1013
|
+
"vollkorn"
|
|
1014
|
+
],
|
|
1015
|
+
"license": {
|
|
1016
|
+
"id": "OFL-1.1",
|
|
1017
|
+
"url": "https://raw.githubusercontent.com/google/fonts/8e44913e4ff26fc997e6856c1ec40ff4791c98c5/ofl/vollkorn/OFL.txt",
|
|
1018
|
+
"copyright": "Copyright 2017 The Vollkorn Project Authors (https://github.com/FAlthausen/Vollkorn-Typeface)"
|
|
1019
|
+
},
|
|
1020
|
+
"variable": {
|
|
1021
|
+
"axes": {
|
|
1022
|
+
"wght": {
|
|
1023
|
+
"min": 400,
|
|
1024
|
+
"default": 400,
|
|
1025
|
+
"max": 900
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
},
|
|
1029
|
+
"faces": [
|
|
1030
|
+
{
|
|
1031
|
+
"weight": 400,
|
|
1032
|
+
"style": "normal",
|
|
1033
|
+
"postScriptName": "Vollkorn-Regular",
|
|
1034
|
+
"file": "Vollkorn[wght].ttf",
|
|
1035
|
+
"sha256": "4ebef434f76f80286204840354a84db79a0dc3c87deeeb791e015b70b2ade49d",
|
|
1036
|
+
"sri": "sha256-Tr70NPdvgChiBIQDVKhNt5oNw8h97ut5HgFbcLKt5J0=",
|
|
1037
|
+
"bytes": 577864
|
|
1038
|
+
}
|
|
1039
|
+
]
|
|
1040
|
+
},
|
|
900
1041
|
"Work Sans": {
|
|
901
1042
|
"category": "sans",
|
|
902
1043
|
"dir": "ofl/worksans",
|