@wix/himalaya-cli 0.994.0 → 0.996.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/ts/src/schema.d.ts +7 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/tokens.d.ts +7 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/bookings-v2/src/extensions.d.ts +38 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +2 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-bookings.mjs +10 -4
- package/dist/cli.mjs +3354 -2241
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/cms-collections.md +19 -11
- package/guides/component-reference.json +3 -2
- package/guides/component-reference.md +41 -2
- package/guides/designing-himi-screens.md +9 -5
- package/guides/getting-started.md +7 -5
- package/guides/headless-loop.md +11 -1
- package/guides/sdk-reference.json +15 -3
- package/guides/templates.md +7 -4
- package/guides/testing-himi-apps.md +47 -4
- package/guides/tier-5-add-a-screen.md +67 -13
- package/guides/tokens.md +3 -0
- package/guides/trim-a-template.md +63 -8
- package/package.json +2 -2
- package/stdlib/fonts/catalog.json +26 -0
|
Binary file
|
|
@@ -316,7 +316,8 @@ every **foreign** collection — one this app neither owns nor declares:
|
|
|
316
316
|
{ "id": "QaRecipes", "displayName": "Recipes", "readRole": "ANYONE", "itemCount": null,
|
|
317
317
|
"fields": [ { "key": "title", "type": "TEXT" }, { "key": "cookMinutes", "type": "NUMBER" } ] }
|
|
318
318
|
],
|
|
319
|
-
"truncated": 6
|
|
319
|
+
"truncated": 6,
|
|
320
|
+
"others": [ { "id": "Orders", "displayName": "Orders" } ]
|
|
320
321
|
}
|
|
321
322
|
}
|
|
322
323
|
```
|
|
@@ -333,16 +334,23 @@ before declaring it, rather than guessing; the scan will not invent one for you.
|
|
|
333
334
|
- **Foreign collections are never item-counted** (`itemCount: null`). A row count is one
|
|
334
335
|
query each, and fanning that across a whole site is the cost this scan must not pay.
|
|
335
336
|
Declare or adopt the collection and the count arrives with the next `himi cms query`.
|
|
336
|
-
- **Only the first 25 are described.** `foreign.count` is how many the
|
|
337
|
-
`foreign.truncated` says how many of those went undescribed
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
337
|
+
- **Only the first 25 are described; every one is named.** `foreign.count` is how many the
|
|
338
|
+
scan listed and `foreign.truncated` says how many of those went undescribed — and
|
|
339
|
+
`foreign.others` still lists each of them as `{ id, displayName }`, so "does this site
|
|
340
|
+
already have an orders collection?" is answerable from this one call. To see one of them
|
|
341
|
+
in full, declare it (`wixId`) and run `himi cms query` again, or use
|
|
342
|
+
[`himi site analyze --site <msid>`](wix-business-context.md) for the per-collection
|
|
343
|
+
picture — field types, read/write roles and sample rows.
|
|
344
|
+
- **The listing is followed page by page.** `GET /data/v2/collections` is offset-paged, and
|
|
345
|
+
the scan reads until Wix's `pagingMetadata.total` is reached — or, when Wix flags
|
|
346
|
+
`tooManyToCount` and sends no total, until a page comes back empty. If the walk stops
|
|
347
|
+
before that — Wix stopped honoring the offset, or the page backstop was hit — the report
|
|
348
|
+
carries `listingIncomplete: true`, and `foreign` covers only what was listed.
|
|
349
|
+
`unlistedCollections` adds how many were left out when Wix gave a total; with
|
|
350
|
+
`tooManyToCount` nobody knows, so only the flag is set. A declared collection the partial
|
|
351
|
+
listing did not reach is read by its id, so it is never reported as `missing` just
|
|
352
|
+
because the scan stopped short. When Wix reports no total and does not flag
|
|
353
|
+
`tooManyToCount`, one page is taken as the whole listing.
|
|
346
354
|
|
|
347
355
|
**The scan is owner-scoped, and there is no tokenless alternative.** It needs
|
|
348
356
|
`--site <msid>` (or `WIX_SITE_ID`) and owner auth via `wix token`, because enumerating
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"bindings": [
|
|
129
129
|
"title"
|
|
130
130
|
],
|
|
131
|
-
"propsSource": "export interface ButtonProps {\n /** Button label — binding-aware (\"$item.\"/\"$service:\") on web, iOS and Android. */\n title: string;\n action: Action;\n style?: string;\n disabled?: boolean;\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}"
|
|
131
|
+
"propsSource": "export interface ButtonProps {\n /** Button label — binding-aware (\"$item.\"/\"$service:\") on web, iOS and Android. */\n title: string;\n action: Action;\n style?: string;\n /** Literal only, NOT bindable: a `\"$service:…\"` string here fails `validate`, and there is no\n * `disabledPath`. To disable from worker state, author two buttons — the live one and a\n * `disabled: true` copy — and swap them with complementary `modifiers.hiddenPath` flags\n * (e.g. `canSubmit` / `cannotSubmit`); also guard the action so a stray tap is a no-op. */\n disabled?: boolean;\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}"
|
|
132
132
|
},
|
|
133
133
|
{
|
|
134
134
|
"name": "TextField",
|
|
@@ -475,7 +475,7 @@
|
|
|
475
475
|
]
|
|
476
476
|
}
|
|
477
477
|
],
|
|
478
|
-
"propsSource": "export interface FilterChipsProps {\n chips: FilterChip[];\n /** Literal ids, or
|
|
478
|
+
"propsSource": "export interface FilterChipsProps {\n chips: FilterChip[];\n /** Literal ids, or binding-aware: put the `$service:` sentinel INSIDE the array as its ONE\n * element — `selectedIds: [\"$service:<svc>.<field>\"]`, where the field holds the selected\n * ids. A bare `\"$service:…\"` string is rejected by `validate` — the prop is an array. */\n selectedIds?: string[];\n multiSelect?: boolean;\n onChange?: Action;\n /** `$service:` binding → a live options array (overrides `chips`). */\n optionsPath?: string;\n}"
|
|
479
479
|
},
|
|
480
480
|
{
|
|
481
481
|
"name": "Tabs",
|
|
@@ -1258,6 +1258,7 @@
|
|
|
1258
1258
|
"Presentation": "export type Presentation =\n | \"push\"\n | \"sheet\"\n | \"replace\"\n | \"dismiss\"\n | \"back\"\n | \"full_screen\"\n | \"alert\"\n | \"snackbar\";",
|
|
1259
1259
|
"FilterChip": "export interface FilterChip {\n id: string;\n label: string;\n leadingIcon?: IconRef;\n count?: number;\n}",
|
|
1260
1260
|
"TabItem": "export interface TabItem {\n id: string;\n label: string;\n badge?: string;\n}",
|
|
1261
|
+
"PickerOption": "export interface PickerOption {\n id: string;\n label: string;\n}",
|
|
1261
1262
|
"GaugeRing": "export interface GaugeRing {\n /** Fraction 0..1 for this ring. */\n value?: number;\n /** `$service:`/`$item.` binding → a live number (overrides `value`). */\n valuePath?: string;\n tint?: TokenName;\n}",
|
|
1262
1263
|
"WidgetTimeRef": "export interface WidgetTimeRef {\n /** Epoch seconds the style counts from/to. */\n at: number;\n /** SwiftUI `Text.DateStyle` / Android time style (default \"timer\"). */\n style?: \"timer\" | \"relative\" | \"date\" | \"time\" | \"offset\";\n}",
|
|
1263
1264
|
"Border": "export interface Border {\n width: number;\n color: TokenRef;\n}"
|
|
@@ -481,6 +481,10 @@ export interface ButtonProps {
|
|
|
481
481
|
title: string;
|
|
482
482
|
action: Action;
|
|
483
483
|
style?: string;
|
|
484
|
+
/** Literal only, NOT bindable: a `"$service:…"` string here fails `validate`, and there is no
|
|
485
|
+
* `disabledPath`. To disable from worker state, author two buttons — the live one and a
|
|
486
|
+
* `disabled: true` copy — and swap them with complementary `modifiers.hiddenPath` flags
|
|
487
|
+
* (e.g. `canSubmit` / `cannotSubmit`); also guard the action so a stray tap is a no-op. */
|
|
484
488
|
disabled?: boolean;
|
|
485
489
|
/** Capability enrichments. */
|
|
486
490
|
leadingIcon?: IconRef;
|
|
@@ -904,7 +908,9 @@ export interface SearchFieldProps {
|
|
|
904
908
|
```ts
|
|
905
909
|
export interface FilterChipsProps {
|
|
906
910
|
chips: FilterChip[];
|
|
907
|
-
/** Literal ids, or
|
|
911
|
+
/** Literal ids, or binding-aware: put the `$service:` sentinel INSIDE the array as its ONE
|
|
912
|
+
* element — `selectedIds: ["$service:<svc>.<field>"]`, where the field holds the selected
|
|
913
|
+
* ids. A bare `"$service:…"` string is rejected by `validate` — the prop is an array. */
|
|
908
914
|
selectedIds?: string[];
|
|
909
915
|
multiSelect?: boolean;
|
|
910
916
|
onChange?: Action;
|
|
@@ -2141,7 +2147,8 @@ export interface LogicBundleRef {
|
|
|
2141
2147
|
|
|
2142
2148
|
A few props take a typed **object** rather than nested components. Some are arrays of them:
|
|
2143
2149
|
`FilterChips.chips` is a `FilterChip[]`, `SegmentedControl.options` / `Tabs.tabs` are a
|
|
2144
|
-
`TabItem[]`,
|
|
2150
|
+
`TabItem[]`, `Picker.options` / `WheelPicker.options` are a `PickerOption[]`, and
|
|
2151
|
+
`Gauge.rings` is a `GaugeRing[]` (concentric rings, drawn outer→inner).
|
|
2145
2152
|
One is a single object: `Text.timeRef` is a `WidgetTimeRef`, the widget-safe live clock —
|
|
2146
2153
|
note its `at` is REQUIRED and is in epoch SECONDS. The prop blocks above name these types;
|
|
2147
2154
|
here are the interfaces themselves (for the arrays, each item's own fields — `id` is what an
|
|
@@ -2161,6 +2168,11 @@ export interface TabItem {
|
|
|
2161
2168
|
badge?: string;
|
|
2162
2169
|
}
|
|
2163
2170
|
|
|
2171
|
+
export interface PickerOption {
|
|
2172
|
+
id: string;
|
|
2173
|
+
label: string;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2164
2176
|
export interface GaugeRing {
|
|
2165
2177
|
/** Fraction 0..1 for this ring. */
|
|
2166
2178
|
value?: number;
|
|
@@ -2444,3 +2456,30 @@ export type Presentation =
|
|
|
2444
2456
|
| "alert"
|
|
2445
2457
|
| "snackbar";
|
|
2446
2458
|
```
|
|
2459
|
+
|
|
2460
|
+
### What each `presentation` does
|
|
2461
|
+
|
|
2462
|
+
The verb decides what happens to the navigation stack. Where there is a target screen it is
|
|
2463
|
+
`params.screenId`, and an action that carries a `screenId` but no verb is a `push`.
|
|
2464
|
+
|
|
2465
|
+
| `presentation` | Effect |
|
|
2466
|
+
|---|---|
|
|
2467
|
+
| `push` | Open `screenId` on top of the current screen; Back returns to where you were. The default. |
|
|
2468
|
+
| `replace` | Open `screenId` without keeping the current screen to go Back to. |
|
|
2469
|
+
| `back` | Pop the current screen (nothing happens at a stack root). On iOS and Android, inside a sheet or cover it pops the modal's own stack first and closes the modal at its root; on web it pops the tab's stack, so close a modal with `dismiss`. |
|
|
2470
|
+
| `dismiss` | Close the presented sheet or full-screen cover (on web, an open alert too). Nothing happens when no modal is up. |
|
|
2471
|
+
| `sheet` | Present `screenId` as a sheet over the current screen. One at a time: presenting another replaces it. |
|
|
2472
|
+
| `full_screen` | Present `screenId` as a full-screen cover. |
|
|
2473
|
+
| `alert` | Show an alert built from `args`: `title` (required), `message`, `confirmLabel`, `cancelLabel`, `destructive`, and `confirmActionId` / `cancelActionId` — the action each button runs, with this action's `params`. No `screenId`. |
|
|
2474
|
+
| `snackbar` | Show a transient message from `args`: `message` (required) and `tone`. No `screenId`. |
|
|
2475
|
+
|
|
2476
|
+
A Close or Done button is a descriptor action: `{ "actionId": "himi.open", "presentation": "dismiss" }`
|
|
2477
|
+
(or `"back"`). Two limits to know before you design a flow around them:
|
|
2478
|
+
|
|
2479
|
+
- **No action switches tabs.** A `himi.open` whose target is another tab's root screen PUSHES
|
|
2480
|
+
that screen onto the current tab's stack; it does not select the other tab. Only the tab bar
|
|
2481
|
+
and other app chrome select a tab.
|
|
2482
|
+
- **A worker can only push.** A worker action that returns `_nav` (`navigate()` from
|
|
2483
|
+
`@himalaya/state`, see `himi docs tier-5-add-a-screen`) opens a linkable screen as a
|
|
2484
|
+
`push`. There is no `_nav` form of `back` or `dismiss`: put those verbs on a descriptor
|
|
2485
|
+
action.
|
|
@@ -69,11 +69,15 @@ decision.
|
|
|
69
69
|
|
|
70
70
|
**Kit typography.** A design kit (or an app's tokens) may set a per-style `family`. The rules:
|
|
71
71
|
every typography entry must stay **complete** (`size`+`weight`+`line` — native decode is strict, a
|
|
72
|
-
family-only entry blanks the whole token set; CI-gated); a `family` must
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
`Georgia
|
|
76
|
-
|
|
72
|
+
family-only entry blanks the whole token set; CI-gated); a `family` must come from the **font
|
|
73
|
+
catalog** — `himi fonts list` is the whole vocabulary (curated families such as Playfair Display,
|
|
74
|
+
Karla, Oswald or Lora; the `Wix Madefor Text`/`Wix Madefor Display` pair every host carries; and
|
|
75
|
+
`Georgia`, an OS font on iOS/macOS/web approximated by the `serif` generic on Android) — or be
|
|
76
|
+
`$system` (the platform face by name — how an idiom overlay goes native over a brand base).
|
|
77
|
+
Anything else silently falls back to the system font. Naming a catalog family in tokens is all it
|
|
78
|
+
takes: `himi push` publishes its faces with the release and every renderer, web included, loads
|
|
79
|
+
them. Run `himi fonts check` before pushing — it fails on a name outside the catalog, and on a
|
|
80
|
+
family whose faces could not be fetched, which would otherwise ship as its fallback on web.
|
|
77
81
|
|
|
78
82
|
### Token discipline — every value is a `TokenRef`, never a literal
|
|
79
83
|
Colors, gradients, shadows, materials, motions are named tokens (`{ token: "accentPrimary" }`), never
|
|
@@ -70,9 +70,11 @@ the UX source of truth even if that guide is absent from the installed CLI.
|
|
|
70
70
|
`himi init my-shop --template branded-lite` (a full consumer reference over a Wix
|
|
71
71
|
site — store/bookings/events/blog/cart/CMS, wired to `@himalaya/wix-*` + a visitor session)
|
|
72
72
|
or `--template owner-lite` (owner CRUD/management: lists, details, forms). That reference is
|
|
73
|
-
**dozens of screens**
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
**dozens of screens** in one megabytes-long `dev/index.ts` of raw descriptors. Keep its
|
|
74
|
+
worker layer — trim it or mine it (`himi docs trim-a-template`): **trim** to the approved
|
|
75
|
+
`SPEC.md` when most of its screens fit, or **mine** it when few do — keep the
|
|
76
|
+
`tier5-src/_shared/` helpers and Wix worker chains and write your own screens. Do not delete
|
|
77
|
+
`tier5-src/` + `dev/` and start from blank. For a small **non-Wix** pattern, `himi init --template chat-bot --remote`. `himi template
|
|
76
78
|
list --remote` shows the full catalog (title/description/tags) — including templates published
|
|
77
79
|
to Wix — and `--remote` on `himi init` fetches one from there (the bundled copy is
|
|
78
80
|
the offline fallback). Publish any app of your own as a template with `himi template publish` —
|
|
@@ -86,8 +88,8 @@ template's identity, fixtures and boot-time worker calls would all have to be to
|
|
|
86
88
|
when consent is **synthetic/local-only** and you need something that cannot auto-fetch (see the
|
|
87
89
|
preflight below); or when you are **learning the wiring** and want one screen and one worker with
|
|
88
90
|
nothing else in the file. The recipes below are written for exactly these cases. What blank is
|
|
89
|
-
NOT good for is a real customer app whose shape a published template already has — trimming
|
|
90
|
-
template (`himi docs trim-a-template`) beats rebuilding
|
|
91
|
+
NOT good for is a real customer app whose shape a published template already has — trimming or
|
|
92
|
+
mining that template (`himi docs trim-a-template`) beats rebuilding its Wix wiring from scratch.
|
|
91
93
|
|
|
92
94
|
**A template is a SHOWCASE APP, so look at it before you pick it.** Each one is a real published
|
|
93
95
|
release running on the plane: `GET /v1/templates.md` gives every row a `preview/a/<id>` URL that
|
package/guides/headless-loop.md
CHANGED
|
@@ -210,10 +210,20 @@ JSC (iOS) and WebView (Android) worker scopes don't have them — so a bundle th
|
|
|
210
210
|
die on a real device fails on your laptop. The most common instance:
|
|
211
211
|
|
|
212
212
|
```js
|
|
213
|
+
async function loadList() { /* fetch + shape; return the next state */ } // module level
|
|
214
|
+
|
|
213
215
|
retry: async (s) => svc.actions.load(s), // ✗ `.actions` is undefined on device
|
|
214
|
-
retry: async (
|
|
216
|
+
retry: async () => loadList(), // ✓ call the helper, not the handle
|
|
215
217
|
```
|
|
216
218
|
|
|
219
|
+
Actions are DIRECT methods on the handle (`svc.load()`), and they take the action's payload
|
|
220
|
+
arguments, never the state. Even so, never call the handle from inside its own action:
|
|
221
|
+
`svc`'s type is inferred from the very object you are writing, so the reference is circular and
|
|
222
|
+
`validate --strict` rejects it (TS7022/TS7023), and passing `s` is
|
|
223
|
+
`Expected 0 arguments, but got 1`. Put the work in a module-level function, return it from both
|
|
224
|
+
`load` and `retry`, and keep `svc.load()` for the one module-eval kick at the bottom of the
|
|
225
|
+
bundle — the shape `himi docs recipe-dynamic-list` ships.
|
|
226
|
+
|
|
217
227
|
With no fixture the crawl runs **offline** (the Wix transport rejects), which exercises the
|
|
218
228
|
failure UI your screens should have. `--net` takes the ordinary net-mocks shape
|
|
219
229
|
(`{ "rules": [...] }`) — the same file the on-device `fake.net` seam reads.
|
|
@@ -7262,7 +7262,7 @@
|
|
|
7262
7262
|
"name": "WixServiceAvailabilityOptions",
|
|
7263
7263
|
"kind": "interface",
|
|
7264
7264
|
"jsdoc": "/** How to bound an availability query. `bookable` is a `query.filter` member — the\n * request's `query` has only `filter` and `sort`, so there is NO paging member and\n * a `paging: { limit }` you add is silently dropped rather than honoured. */",
|
|
7265
|
-
"decl": "export interface WixServiceAvailabilityOptions {\n /** Bookable slots only. Defaults to `true`; pass `false` to get the unbookable\n * ones too (a greyed-out calendar), which is what the endpoint returns unfiltered. */\n bookable?: boolean;\n}"
|
|
7265
|
+
"decl": "export interface WixServiceAvailabilityOptions {\n /** Bookable slots only. Defaults to `true`; pass `false` to get the unbookable\n * ones too (a greyed-out calendar), which is what the endpoint returns unfiltered.\n * `false` OMITS the filter key rather than sending `bookable: false` — the latter\n * is a filter for unbookable slots ONLY, i.e. an empty calendar on a live site.\n * Unfiltered is the large response described below, so keep the window to the\n * days on screen. For unbookable slots alone, filter the entries on\n * `entry.bookable === false`. */\n bookable?: boolean;\n}"
|
|
7266
7266
|
},
|
|
7267
7267
|
{
|
|
7268
7268
|
"name": "queryServiceAvailability",
|
|
@@ -7296,11 +7296,23 @@
|
|
|
7296
7296
|
"jsdoc": "/** Convenience: staff members = resources tagged \"staff\" (filtered client-side). */",
|
|
7297
7297
|
"decl": "export async function queryStaff(limit = 100): Promise<WixResource[]>;"
|
|
7298
7298
|
},
|
|
7299
|
+
{
|
|
7300
|
+
"name": "WixSelectedPaymentOption",
|
|
7301
|
+
"kind": "type",
|
|
7302
|
+
"jsdoc": "/** How the customer intends to pay — the Create Booking `SelectedPaymentOption` enum.\n * `OFFLINE` = pay in person at the venue; `ONLINE` = pay through checkout;\n * `MEMBERSHIP` = a Wix Pricing Plan; `MEMBERSHIP_OFFLINE` = a pricing plan the\n * business redeems by hand. It must be one the service offers. */",
|
|
7303
|
+
"decl": "export type WixSelectedPaymentOption = \"OFFLINE\" | \"ONLINE\" | \"MEMBERSHIP\" | \"MEMBERSHIP_OFFLINE\";"
|
|
7304
|
+
},
|
|
7305
|
+
{
|
|
7306
|
+
"name": "WixBookSlotOptions",
|
|
7307
|
+
"kind": "interface",
|
|
7308
|
+
"jsdoc": "",
|
|
7309
|
+
"decl": "export interface WixBookSlotOptions {\n /** Sent as `booking.selectedPaymentOption`. Omitted from the request when unset,\n * so a call without it sends exactly the body it always did. */\n selectedPaymentOption?: WixSelectedPaymentOption;\n}"
|
|
7310
|
+
},
|
|
7299
7311
|
{
|
|
7300
7312
|
"name": "bookSlot",
|
|
7301
7313
|
"kind": "function",
|
|
7302
|
-
"jsdoc": "/** Book a specific availability slot — POST /bookings/v2/bookings. Pass the FULL\n * slot from queryServiceAvailability + the booker's contact. The v1 createBooking\n * above rejects a bare slot; the v2 route with bookedEntity.slot is the one that\n * works. Verified live (status CREATED). */",
|
|
7303
|
-
"decl": "export function bookSlot(slot: WixBookingSlotFull
|
|
7314
|
+
"jsdoc": "/** Book a specific availability slot — POST /bookings/v2/bookings. Pass the FULL\n * slot from queryServiceAvailability + the booker's contact. The v1 createBooking\n * above rejects a bare slot; the v2 route with bookedEntity.slot is the one that\n * works. Verified live (status CREATED).\n *\n * A visitor booking a PAID service to pay in person (no on-device payment) is a\n * three-call chain:\n *\n * 1. `bookSlot(slot, first, last, email, { selectedPaymentOption: \"OFFLINE\" })`\n * → the booking, status CREATED.\n * 2. `createCheckout([{ quantity: 1, catalogReference: { appId: <Wix Bookings\n * appDefId 13d21c63-b5ec-5912-8397-c3a5ddb27a97>, catalogItemId: booking.id } }])`\n * from `@himalaya/wix-ecom` → the checkout id.\n * 3. `createOrder(checkoutId)` from `@himalaya/wix-ecom` → the order, with the\n * balance left for the venue to collect.\n *\n * This chain was verified with raw Wix calls, not through these wrappers.\n * It is not verified whether `createOrder` still returns a `paymentGatewayOrderId`\n * for an OFFLINE booking, so do not read one as \"charge now\" and send a\n * pay-in-person customer into the cashier.\n *\n * Wix documents `selectedPaymentOption` as REQUIRED whenever the booking is paid\n * through an eCommerce checkout, and it must be an option the service offers.\n * Leave `options` out and the request is the one this function always sent. */",
|
|
7315
|
+
"decl": "export function bookSlot(\n slot: WixBookingSlotFull,\n firstName: string,\n lastName: string,\n email: string,\n options?: WixBookSlotOptions,\n): Promise<WixCreateBookingResponse>;",
|
|
7304
7316
|
"http": "POST /bookings/v2/bookings"
|
|
7305
7317
|
},
|
|
7306
7318
|
{
|
package/guides/templates.md
CHANGED
|
@@ -83,10 +83,13 @@ back to the offline copy — so it never hard-fails. Then run the normal loop
|
|
|
83
83
|
(`himi build` / `validate` / `render` / `push --draft`).
|
|
84
84
|
|
|
85
85
|
**`branded-lite` is a full reference, not a small starter.** After `himi init --template
|
|
86
|
-
branded-lite`, trim it
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
`
|
|
86
|
+
branded-lite`, trim it or mine it rather than deleting `tier5-src/` + `dev/` and starting from
|
|
87
|
+
blank. **Trim** when most of the approved `SPEC.md` is screens the template already has: delete
|
|
88
|
+
the rest. **Mine** when few of its screens match: keep the `tier5-src/_shared/` helpers and the
|
|
89
|
+
Wix worker chains, and write your own screens in `dev/index.ts` — the file holds every screen
|
|
90
|
+
as a raw descriptor (megabytes for the full templates), so replacing it is often faster than
|
|
91
|
+
editing it. Both paths, and how `dev/index.ts`, `tier5-src/`, and `navigation.linkableScreens`
|
|
92
|
+
relate: `himi docs trim-a-template`.
|
|
90
93
|
|
|
91
94
|
**Point a `branded-lite` (or vertical overlay) scaffold at your site.** Set
|
|
92
95
|
`WIX_CLIENT_ID` (Headless OAuth client on that site) and `WIX_SITE_URL` before build /
|
|
@@ -137,12 +137,21 @@ worker scope: **no `setTimeout`/`setInterval`**. That makes a whole class of on-
|
|
|
137
137
|
fail locally — including this one, which every other local check passes:
|
|
138
138
|
|
|
139
139
|
```js
|
|
140
|
+
async function loadList() { /* fetch + shape; return the next state */ } // module level
|
|
141
|
+
|
|
140
142
|
// ✗ `.actions` does not exist on the handle — undefined on device
|
|
141
143
|
retry: async (s) => svc.actions.load({ ...s, status: "loading" }),
|
|
142
|
-
// ✓
|
|
143
|
-
retry: async (
|
|
144
|
+
// ✓ call the module-level helper, not the handle
|
|
145
|
+
retry: async () => loadList(),
|
|
144
146
|
```
|
|
145
147
|
|
|
148
|
+
Actions are DIRECT methods on the handle (`svc.load()`), and they take the action's payload
|
|
149
|
+
arguments, never the state. Even so, never call the handle from inside its own action: `svc`'s
|
|
150
|
+
type is inferred from the very object you are writing, so the reference is circular and
|
|
151
|
+
`validate --strict` rejects it (TS7022/TS7023), and passing `s` is `Expected 0 arguments, but got 1`. Put the work in a module-level function, return it
|
|
152
|
+
from both `load` and `retry`, and keep `svc.load()` for the one module-eval kick at the bottom
|
|
153
|
+
of the bundle — the shape `himi docs recipe-dynamic-list` ships.
|
|
154
|
+
|
|
146
155
|
In-repo, [`tier5-bundle-guardrails.test.ts`](../core/dev-server/tests/tier5-bundle-guardrails.test.ts)
|
|
147
156
|
forbids both statically across every committed bundle; that gate does not ship off-repo, which
|
|
148
157
|
is why the crawl enforces it dynamically. The clock and `Math.random` are pinned too, so two
|
|
@@ -394,8 +403,11 @@ Don't hand-write fixtures — capture the real thing once:
|
|
|
394
403
|
3. Replay it in any tier — as a `fake.net` fixture (tests) or an OTA scenario (live dev).
|
|
395
404
|
|
|
396
405
|
The transform lives in [`net-freeze.ts`](../core/dev-server/src/net-freeze.ts) (one static rule
|
|
397
|
-
per distinct `method+host+path`, last capture wins).
|
|
398
|
-
|
|
406
|
+
per distinct `method+host+path`, last capture wins). A request whose body names a
|
|
407
|
+
`dataCollectionId` is keyed on that too and frozen with `matcher.bodyContains`, so each Wix Data
|
|
408
|
+
collection's `POST /wix-data/v2/items/query` replays its own rows instead of the last one
|
|
409
|
+
recorded. Caveat: the traffic ring truncates large bodies — trim/verify a frozen fixture before
|
|
410
|
+
relying on it as a JSON mock.
|
|
399
411
|
|
|
400
412
|
---
|
|
401
413
|
|
|
@@ -557,6 +569,37 @@ Recorded so the next person does not re-walk them (#2231):
|
|
|
557
569
|
|
|
558
570
|
---
|
|
559
571
|
|
|
572
|
+
## Web preview: which Wix visitor you are
|
|
573
|
+
|
|
574
|
+
A web preview that nobody has signed into calls Wix as an **anonymous visitor**, and that
|
|
575
|
+
visitor owns what it writes: a cart, a booking, an order. The browser holds the identity
|
|
576
|
+
itself. It gets its own visitor straight from Wix, so there is **one anonymous Wix visitor per
|
|
577
|
+
browser, per origin, per app `clientId`**. The visitor's refresh token is kept in
|
|
578
|
+
`localStorage` under `himalaya.wixVisitor.<clientId>`; the short-lived access token stays in
|
|
579
|
+
memory and is renewed from it.
|
|
580
|
+
|
|
581
|
+
The visitor **survives** page reloads, draft pushes and new releases, a renewed preview link
|
|
582
|
+
(`pvt`), and other tabs of the same browser, which share it.
|
|
583
|
+
|
|
584
|
+
You get a **new** visitor, who cannot see the old one's records, when:
|
|
585
|
+
|
|
586
|
+
- the site data is cleared, or the preview is opened in a private window or another browser;
|
|
587
|
+
- the same app is opened from a different origin — the `www.wixapis.com` preview link and a
|
|
588
|
+
mount on another Wix host each keep their own store;
|
|
589
|
+
- renewing the stored visitor fails, so the browser starts a fresh one;
|
|
590
|
+
- the app's `clientId` changes.
|
|
591
|
+
|
|
592
|
+
One path is **not** stable at all. When the browser cannot reach Wix itself — its visitor mint
|
|
593
|
+
or a request fails, after which the rest of that page load stays on the fallback — or a call
|
|
594
|
+
carries its own headers or an absolute URL, the call goes through the preview server instead.
|
|
595
|
+
That server uses one anonymous visitor per server instance, **shared** by everyone it serves and
|
|
596
|
+
replaced when its token expires. A signed-in member is a separate identity (the member session)
|
|
597
|
+
and is not affected by any of this.
|
|
598
|
+
|
|
599
|
+
So when a visitor's own record vanishes from a preview, first check whether the browser, the
|
|
600
|
+
origin or the site data changed. A record that has to outlive those belongs to a signed-in
|
|
601
|
+
member, or should be read back by an id the app kept.
|
|
602
|
+
|
|
560
603
|
## Web automation: selector contract
|
|
561
604
|
|
|
562
605
|
Driving the web host (the `/preview` co-host, the Himi Player, or a standalone web build)
|
|
@@ -104,7 +104,7 @@ The first arg after `state` is the action's payload object: the descriptor's `Ac
|
|
|
104
104
|
| `TextField` / `RichTextField` | `onChange`, `onSubmit` | `{ value }` — the current text (onChange fires per keystroke) |
|
|
105
105
|
| `SearchField` | `onChange`, `onSubmit` | `{ value }` — the query, debounced by `debounceMs` |
|
|
106
106
|
| `Picker` / `SegmentedControl` / `Tabs` / `WheelPicker` | `onChange` | `{ selectedId }` — the id of the newly selected option |
|
|
107
|
-
| `FilterChips` | `onChange` | `{ selectedIds }` — the **array** of ids selected *after* the tap (honors `multiSelect`) |
|
|
107
|
+
| `FilterChips` | `onChange` | `{ selectedIds }` — the **array** of ids selected *after* the tap (honors `multiSelect`). To drive the highlight from state, bind the sentinel **inside** the array: `selectedIds: ["$service:<svc>.<field>"]` |
|
|
108
108
|
| `Toggle` | `onChange` | `{ value }` — the next state, as a boolean |
|
|
109
109
|
| `SelectionControl` | `onChange` | `{ value }` — the next checked state, as a boolean |
|
|
110
110
|
| `Stepper` | `onChange` | `{ value }` — the clamped next value, as an int |
|
|
@@ -395,13 +395,23 @@ mis-pointed `refresh` clobbers good data every tick); the module-eval `load()` i
|
|
|
395
395
|
*once, on entry*. Reach for module-eval `load()` for initial load; use `refresh`
|
|
396
396
|
only for genuinely live data.
|
|
397
397
|
|
|
398
|
-
> The `modifiers.onAppear` action
|
|
399
|
-
>
|
|
400
|
-
>
|
|
401
|
-
>
|
|
402
|
-
>
|
|
403
|
-
>
|
|
404
|
-
>
|
|
398
|
+
> The `modifiers.onAppear` action fires when the screen **mounts** — on web, iOS and Android.
|
|
399
|
+
> Use it to hand the worker navigation-borne or build-time params. Do **not** reach for it as
|
|
400
|
+
> your *initial-load* path: module-eval `svc.load()` above is the blessed initial-load
|
|
401
|
+
> primitive on every surface, and since #2282 it is honoured in the contained web frame as
|
|
402
|
+
> well.
|
|
403
|
+
>
|
|
404
|
+
> **It is not a "the user came back" hook.** Whether returning to a screen fires it again
|
|
405
|
+
> depends on whether the host kept that screen mounted, and that differs by platform. iOS
|
|
406
|
+
> fires it once per mounted view, and keeps both visited tabs and the screen under a pushed
|
|
407
|
+
> detail mounted, so a tab return or a Back does not re-fire it. Android fires it each time the
|
|
408
|
+
> node enters composition. On web it fires when the screen host mounts the screen.
|
|
409
|
+
>
|
|
410
|
+
> So for state another screen may have changed — a cart, a signed-in flag, a record saved
|
|
411
|
+
> elsewhere — re-read it in the action that loads the screen (`load`) or in an explicit
|
|
412
|
+
> `refresh`, not in `onAppear` (and see
|
|
413
|
+
> [State does not cross screens automatically](#state-does-not-cross-screens-automatically)
|
|
414
|
+
> for how to hand it off).
|
|
405
415
|
|
|
406
416
|
**Load with the sanctioned transport, not raw `fetch`.** A worker action calls the
|
|
407
417
|
typed Wix clients (`@himalaya/wix-*`) or the raw chokepoint
|
|
@@ -558,12 +568,15 @@ accepted limitations: [RFC 0040 § Amendment — the `_poll` directive](../core/
|
|
|
558
568
|
**1. The list opens the canonical detail screen, passing the id as a param** (only the
|
|
559
569
|
`screenId` is canonical — the id rides alongside it):
|
|
560
570
|
|
|
561
|
-
```
|
|
562
|
-
//
|
|
563
|
-
|
|
564
|
-
// descriptor onTap form: { actionId: "himi.open", params: { screenId: "event", id: "$item.id" } }
|
|
571
|
+
```jsonc
|
|
572
|
+
// the row's onTap — `$item.id` is substituted per row:
|
|
573
|
+
{ "actionId": "himi.open", "params": { "screenId": "event", "id": "$item.id" } }
|
|
565
574
|
```
|
|
566
575
|
|
|
576
|
+
When the decision to navigate is the worker's (after a save, or once a lookup resolves), return
|
|
577
|
+
`navigate(...)` from the action instead — see [Navigate from the worker](#navigate-from-the-worker-navigate-and-_nav)
|
|
578
|
+
below.
|
|
579
|
+
|
|
567
580
|
**2. Declare the linkable route with `paramKeys` in `config.ts`** (the id key the host
|
|
568
581
|
threads into `himalaya.input`):
|
|
569
582
|
|
|
@@ -575,7 +588,7 @@ linkableScreens: [
|
|
|
575
588
|
|
|
576
589
|
**3. The detail worker reads `himalaya.input.id`** and loads in module-eval `load()`
|
|
577
590
|
(the blessed initial-load primitive above), **not** an `onAppear` action — `onAppear`
|
|
578
|
-
|
|
591
|
+
fires on every platform, but it is a mount hook, not the initial-load path (see the
|
|
579
592
|
callout above):
|
|
580
593
|
|
|
581
594
|
```ts
|
|
@@ -645,6 +658,47 @@ For a bottom-tab app, list each tab as a `NavTabItem` in the app config's `navig
|
|
|
645
658
|
block — note the single-screen `himi init` scaffold ships **no** `navigation`, so a
|
|
646
659
|
multi-tab app adds it.
|
|
647
660
|
|
|
661
|
+
#### Navigate from the worker: `navigate()` and `_nav`
|
|
662
|
+
|
|
663
|
+
A worker action moves the user by returning its next state with a transient `_nav` field.
|
|
664
|
+
`navigate` from `@himalaya/state` writes it for you:
|
|
665
|
+
|
|
666
|
+
```ts
|
|
667
|
+
import { defineService, navigate } from "@himalaya/state";
|
|
668
|
+
|
|
669
|
+
// in an action — after the save lands, open the record it created:
|
|
670
|
+
save: async (s) => {
|
|
671
|
+
const id = await saveDraft(s.draft); // your module-level helper; returns the new id
|
|
672
|
+
return navigate({ ...s, saving: false }, { screenId: "event", args: { id } });
|
|
673
|
+
},
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
The runtime strips `_nav` before the state is stored, and the host opens `screenId` exactly as
|
|
677
|
+
a tapped `himi.open` would — including the `args` arriving as `himalaya.input` on the target.
|
|
678
|
+
The rules:
|
|
679
|
+
|
|
680
|
+
- `screenId` must be a **linkable** screen (`navigation.linkableScreens`), or the host
|
|
681
|
+
ignores the directive.
|
|
682
|
+
- `args` values are **strings only**; anything else is dropped rather than coerced.
|
|
683
|
+
- It always **pushes**. There is no `_nav` form of `back`, `dismiss`, `sheet` or `replace`.
|
|
684
|
+
|
|
685
|
+
`_nav` is the worker-side navigation all three hosts route. A worker's
|
|
686
|
+
`dispatch("himi.open", …)` is routed by the web host's nav bridge only; on iOS and Android a
|
|
687
|
+
worker `dispatch` reaches the native action registry, which has no `himi.open` handler.
|
|
688
|
+
|
|
689
|
+
#### Back, dismiss, and tabs
|
|
690
|
+
|
|
691
|
+
Everything that is not a push is a **descriptor** action with a `presentation` verb — a Close
|
|
692
|
+
or Done button is `{ "actionId": "himi.open", "presentation": "dismiss" }` (or `"back"`). The
|
|
693
|
+
component reference tabulates every verb (`himi components --json`, or the Actions section of
|
|
694
|
+
`himi docs component-reference`): `back` pops the current screen, `dismiss` closes a presented
|
|
695
|
+
sheet or cover.
|
|
696
|
+
|
|
697
|
+
**No action switches tabs.** A `himi.open` — tapped or from `_nav` — whose target is another
|
|
698
|
+
tab's root screen pushes that screen onto the *current* tab's stack; it does not select the
|
|
699
|
+
other tab. Only the tab bar and other app chrome select a tab, so design a cross-tab jump as a
|
|
700
|
+
push of the detail you want, not as a hop to the other tab's root.
|
|
701
|
+
|
|
648
702
|
### Enumerable variants with `FragmentSlot` (RFC 0045)
|
|
649
703
|
|
|
650
704
|
Use `fragmentSlot()` when a discriminator selects among a finite set of shipped
|
package/guides/tokens.md
CHANGED
|
@@ -29,6 +29,9 @@ tokens.json fail to decode on iOS + Android** — and the web renderer is lenien
|
|
|
29
29
|
the missing field), so the bug passes web review and only breaks on device. CI guards it:
|
|
30
30
|
`himi validate` reports a token set that would not decode on device.
|
|
31
31
|
|
|
32
|
+
`family` is optional: a font the app can ship (`himi fonts list`), or `"$system"` for each
|
|
33
|
+
platform's own UI face — SF Pro on iOS/macOS, Roboto on Android, the system UI stack on the web.
|
|
34
|
+
|
|
32
35
|
## Colors
|
|
33
36
|
|
|
34
37
|
A color is a hex string, OR a per-scheme object `{ light, dark?, highContrast? }`. In the
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
# Trim a template to N screens
|
|
2
2
|
|
|
3
3
|
`himi init --template branded-lite` (and the vertical overlays) scaffolds a **full
|
|
4
|
-
reference app**, not a 5-screen starter: on the order of **70 `tier5-src/` workers
|
|
5
|
-
|
|
6
|
-
`
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
reference app**, not a 5-screen starter: on the order of **70 `tier5-src/` workers** and
|
|
5
|
+
**70 screens**, plus a matching `navigation.linkableScreens` list. Every screen is a nested
|
|
6
|
+
raw descriptor in one `dev/index.ts`, and that file is big: about **4.5 MB / 80,000 lines**
|
|
7
|
+
for `branded-lite`, and **0.8–1.8 MB** for the vertical templates (`retail-store` ~0.85 MB,
|
|
8
|
+
`gym-fitness` ~1.8 MB / 32,000 lines). Its header says *"Edit them freely — this file is
|
|
9
|
+
yours now"*, and it is: nothing regenerates it after `himi init`.
|
|
10
|
+
|
|
11
|
+
Do not wipe `tier5-src/` + `dev/` and re-author from the blank `himi init` shape; that throws
|
|
12
|
+
away the live Wix wiring the template exists to give you. Vertical overlays retheme
|
|
13
|
+
copy/tokens; they do not shrink the screen count. There are two ways to keep that wiring:
|
|
14
|
+
|
|
15
|
+
- **Trim it** when the approved `SPEC.md` is mostly screens the template already has: keep
|
|
16
|
+
those, **delete what the plan does not need** ([How to trim](#how-to-trim)).
|
|
17
|
+
- **Mine it** when the app's screens are its own and few template screens match — the
|
|
18
|
+
realistic case for a multi-megabyte file of nested descriptors. Keep the worker layer,
|
|
19
|
+
replace the screens ([Or mine it](#or-mine-it-keep-the-wix-plumbing-write-your-own-screens)).
|
|
11
20
|
|
|
12
21
|
For a small **non-Wix** pattern (chat, feed, habits, tip calculator), skip this guide
|
|
13
22
|
and scaffold a remote starter instead: `himi init my-app --template chat-bot --remote`
|
|
@@ -20,7 +29,7 @@ A screen is not one file. After `himi init`, these three must agree:
|
|
|
20
29
|
| Keep / delete together | What it is |
|
|
21
30
|
|---|---|
|
|
22
31
|
| `tier5-src/<worker-dir>/` | The worker (state + actions). Directory name is the logic-bundle id (`himi-branded-lite-home`, `himi-booking-services`, …). |
|
|
23
|
-
| `dev/index.ts` `SCREENS` map | The
|
|
32
|
+
| `dev/index.ts` `SCREENS` map | The descriptors, as raw objects. After `himi init` the file is **yours** ("this file is yours now") — edit it, cut it down, or replace it. |
|
|
24
33
|
| `config.ts` `navigation.linkableScreens` | Deep-link / tab targets. `himalaya://screen/<id>` resolves **only** if the id is listed here. Tab roots also appear under `navigation.tabBar.items`. |
|
|
25
34
|
|
|
26
35
|
`himi validate --strict` fails if a tab/linkable id has no descriptor, or a descriptor
|
|
@@ -52,6 +61,52 @@ Worked example — keep Home + Shop + Account from branded-lite:
|
|
|
52
61
|
`himi_account`, … and drops `himi_services`, `himi_events`, …
|
|
53
62
|
- `linkableScreens` + `tabBar.items` match that set (Home / Shop / Profile).
|
|
54
63
|
|
|
64
|
+
## Or mine it: keep the Wix plumbing, write your own screens
|
|
65
|
+
|
|
66
|
+
When few of the template's screens match the app — say a class schedule with a day strip,
|
|
67
|
+
spots left and a waitlist, none of which the template draws — hand-editing the descriptor file
|
|
68
|
+
is not the fast path. What a template gives you that is hard to write and easy to reuse is the
|
|
69
|
+
**worker layer**. Keep that, and author the screens yourself:
|
|
70
|
+
|
|
71
|
+
1. **Keep the helpers.** `tier5-src/_shared/` holds the tested Wix plumbing: price and
|
|
72
|
+
currency formatting (`wix-service-price.ts`, `engine-intl.ts`), text helpers
|
|
73
|
+
(`himi-text.ts`), and the per-vertical shaping (`himi-bookings.ts`, `himi-store.ts`,
|
|
74
|
+
`himi-events.ts`, …). Import them from your own workers.
|
|
75
|
+
2. **Keep the write chains you need.** A transaction that spans several Wix calls is the
|
|
76
|
+
expensive part to rediscover — the booking worker's `createBooking → createCheckout →
|
|
77
|
+
createOrder` chain in `tier5-src/himi-booking-time/`, or the cart's checkout in
|
|
78
|
+
`tier5-src/himi-branded-lite-cart/`. Keep that worker and bind your screen to it, or move
|
|
79
|
+
the chain into your own worker.
|
|
80
|
+
3. **Delete the template screens.** Remove the worker dirs you are not keeping, and replace the
|
|
81
|
+
`SCREENS` map in `dev/index.ts` with your own screens (and `linkableScreens` /
|
|
82
|
+
`tabBar.items` to match).
|
|
83
|
+
4. **Write each screen as small local functions** in `dev/index.ts` that return raw descriptor
|
|
84
|
+
objects. They are plain TypeScript functions in your own file, not a package (there is no
|
|
85
|
+
builder-DSL package — `himi docs designing-himi-screens`), so they compile to the same raw
|
|
86
|
+
descriptors `himi init` writes:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
// dev/index.ts
|
|
90
|
+
const node = (id: string, type: string, props: Record<string, unknown>) =>
|
|
91
|
+
({ id, body: { package: "himalaya", type, props } });
|
|
92
|
+
const text = (id: string, value: string, style = "body") => node(id, "Text", { text: value, style });
|
|
93
|
+
|
|
94
|
+
function classDetail() {
|
|
95
|
+
return {
|
|
96
|
+
screenId: "class_detail",
|
|
97
|
+
// renderer, schemaVersion, cacheControl, logicBundle: exactly as `himi init` writes them
|
|
98
|
+
payload: node("root", "VStack", { spacing: 12, children: [
|
|
99
|
+
text("name", "$service:classDetail.name", "title1"),
|
|
100
|
+
text("spots", "$service:classDetail.spotsLabel", "caption"),
|
|
101
|
+
node("book", "Button", { title: "Book", style: "primary", action: { actionId: "tier5.classDetail.book" } }),
|
|
102
|
+
] }),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
5. **Prove it** the same way as a trim: `himi validate --strict`, `himi render <screen>`, then
|
|
108
|
+
`himi test`.
|
|
109
|
+
|
|
55
110
|
## What not to do
|
|
56
111
|
|
|
57
112
|
- Do not hand-edit the in-repo `apps/himi-branded-lite/dev/index.ts` to "slim the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.996.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
|
|
6
6
|
"keywords": [
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
"artifactId": "himalaya-cli"
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
|
-
"falconPackageHash": "
|
|
68
|
+
"falconPackageHash": "302697b73846010a0fe1e425721e1e74e406e07054033a4bcc1f89cf"
|
|
69
69
|
}
|