@wix/himalaya-cli 0.1525.0 → 0.1526.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 (45) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/builder.d.ts +14 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +22 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/worker-subtree-safe-types.generated.d.ts +4 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +7 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +13 -0
  6. package/dist/cli.mjs +137 -39
  7. package/dist/preview-assets.generated.bin +0 -0
  8. package/guides/designing-himi-screens.md +43 -18
  9. package/guides/recipe-dynamic-list.md +7 -7
  10. package/guides/recipe-four-state.md +47 -41
  11. package/guides/recipe-search.md +1 -1
  12. package/package.json +2 -2
  13. package/templates/beauty-salon/dev/index.ts +69 -40
  14. package/templates/beauty-salon/tier5-src/himi-branded-lite-agenda/index.ts +9 -1
  15. package/templates/beauty-salon/tier5-src/himi-branded-lite-place-summary/index.ts +12 -1
  16. package/templates/beauty-salon/tier5-src/himi-store-account/index.ts +69 -10
  17. package/templates/branded-lite/dev/index.ts +165 -88
  18. package/templates/branded-lite/tier5-src/business-chat/index.ts +4 -0
  19. package/templates/branded-lite/tier5-src/himi-branded-lite-agenda/index.ts +9 -1
  20. package/templates/branded-lite/tier5-src/himi-branded-lite-place-summary/index.ts +12 -1
  21. package/templates/branded-lite/tier5-src/himi-store-account/index.ts +69 -10
  22. package/templates/community-club/dev/index.ts +40 -21
  23. package/templates/community-club/tier5-src/himi-store-account/index.ts +69 -10
  24. package/templates/events-meetups/dev/index.ts +36 -19
  25. package/templates/events-meetups/tier5-src/himi-store-account/index.ts +69 -10
  26. package/templates/gym-fitness/dev/index.ts +73 -42
  27. package/templates/gym-fitness/tier5-src/himi-branded-lite-agenda/index.ts +9 -1
  28. package/templates/gym-fitness/tier5-src/himi-branded-lite-place-summary/index.ts +12 -1
  29. package/templates/gym-fitness/tier5-src/himi-store-account/index.ts +69 -10
  30. package/templates/nonprofit-cause/dev/index.ts +47 -29
  31. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-place-summary/index.ts +12 -1
  32. package/templates/nonprofit-cause/tier5-src/himi-store-account/index.ts +69 -10
  33. package/templates/podcast-creator/dev/index.ts +47 -29
  34. package/templates/podcast-creator/tier5-src/himi-branded-lite-place-summary/index.ts +12 -1
  35. package/templates/podcast-creator/tier5-src/himi-store-account/index.ts +69 -10
  36. package/templates/portfolio-creative/dev/index.ts +52 -27
  37. package/templates/portfolio-creative/tier5-src/himi-store-account/index.ts +69 -10
  38. package/templates/restaurant/dev/index.ts +48 -25
  39. package/templates/restaurant/tier5-src/himi-store-account/index.ts +69 -10
  40. package/templates/retail-store/dev/index.ts +44 -23
  41. package/templates/retail-store/tier5-src/himi-store-account/index.ts +69 -10
  42. package/templates/yoga-studio/dev/index.ts +57 -34
  43. package/templates/yoga-studio/tier5-src/himi-branded-lite-agenda/index.ts +9 -1
  44. package/templates/yoga-studio/tier5-src/himi-branded-lite-place-summary/index.ts +12 -1
  45. package/templates/yoga-studio/tier5-src/himi-store-account/index.ts +69 -10
Binary file
@@ -257,7 +257,7 @@ scroll. *Reach for `SegmentedControl` for 2–4 in-content modes; `Tabs`/tab-bar
257
257
  > in the *outer* column, between the pager and the bottom CTA). Its built-in indicator is faint; pair an
258
258
  > explicit `PageControl` if the dots need to read.
259
259
 
260
- **Feedback & overlays** — `Banner` for a persistent inline message (with a retry action when a refresh fails over content you kept; a screen with nothing to show uses an error `EmptyState`, see §5);
260
+ **Feedback & overlays** — `Banner` for a persistent inline message (with a retry action when a refresh fails over content you kept; a screen with nothing to show uses an `ErrorState`, see §5);
261
261
  `Snackbar` for a transient confirmation; `AlertDialog` for a blocking decision; `BottomSheet` for
262
262
  contextual actions/detail; `Tooltip`/`Popover` for a small anchored hint. *Reach for `Snackbar`, not
263
263
  `AlertDialog`, to confirm a completed action — don't block on success.* The `Snackbar` COMPONENT draws
@@ -345,7 +345,7 @@ to drive them:
345
345
  |---|---|---|
346
346
  | **Loading** | `Skeleton` (shape of the eventual content) or `StatefulSection` / `ActivityIndicator` | Required on every screen that loads data asynchronously, and re-checked on every redesign. Prefer a content-shaped `Skeleton` over a bare spinner — it previews the layout. The descriptor renders against the state-skeleton, then repaints as worker state lands. See [Loading states](#loading-states-part-of-the-design) below. |
347
347
  | **Empty** | `EmptyState` (`title`, optional `icon` / `subtitle`) **plus a directive action** (`action` + `actionLabel`) | An empty screen is an invitation to act, not a dead end. Give it the next step, for example `"actionLabel": "Add your first item"`. Use `EmptyState` for **every** empty state, so all empty states in the app have the same layout and style. See [Empty states](#empty-states-one-component-one-layout) below. |
348
- | **Error** | Classified first. Nothing on screen: an `EmptyState` per class (`title` + `subtitle`, **no icon**, an action only for a real recovery). Rows on screen: keep them, add a `Banner` with retry. Field and action errors stay local. | Say what happened in the user's words and offer recovery only when there is one; never a blank screen or a raw error string. See [Error states](#error-states-classify-then-present) below. |
348
+ | **Error** | Classified first. Nothing on screen: an `ErrorState` per class (user-facing `title` + `body`, a `cta` only for a real recovery). Rows on screen: keep them, add a `Banner` with retry. Field and action errors stay local. | Say what happened in the user's words and offer recovery only when there is one; never a blank screen or a raw error string. See [Error states](#error-states-classify-then-present) below. |
349
349
  | **Loaded** | the real content | The one everyone remembers to build. |
350
350
 
351
351
  Bind visibility to worker state with `hidden_path` (`$service:…`/`$item.` data-bound collapse in
@@ -433,13 +433,14 @@ its own recovery.
433
433
  | **Field validation** | A value the user typed is not valid. | Beside the field, in its own error slot (see **Inputs & forms** in §3). | "Enter a valid email." | The user corrects the field. No retry. |
434
434
  | **Action failure** | A write or a command failed (save, delete, book). | Near the action or the affected content — a line by the button, or a `danger` snackbar (`ui.snackbar({ message, tone: "danger" })`). | "Couldn't save your changes. Try again." | Keep what the user entered; the action itself is the retry. |
435
435
 
436
- - **A screen- or section-level error is an `EmptyState` with no icon.** Set `title` and
437
- `subtitle`; leave `icon` out. Add `action` + `actionLabel` only when the user has a useful
438
- recovery — retry, reload, the app's auth flow, or the site's join flow when available. Every error then has the same layout and style, as every
439
- empty state does. Do not build one by hand from `Icon`, `Text` and `Button` nodes.
440
- - **One node per class.** iOS and Android do not resolve a `$service:` binding in `EmptyState`
441
- `title` or `subtitle`, so the worker cannot swap the text. Give each class the screen can hit its
442
- own `EmptyState` with literal copy or `$t:` keys, and gate each one with `hiddenPath` bound to a
436
+ - **A screen- or section-level error is an `ErrorState`.** Set its `variant`, and `title` and
437
+ `body` when the default copy does not fit. Add a `cta` (with `ctaLabel` when the default label
438
+ does not fit) only when the user has a useful recovery — retry, reload, the app's auth flow, or
439
+ the site's join flow when available. Every error then has the same layout and style. Do not
440
+ build one by hand from `Icon`, `Text` and `Button` nodes.
441
+ - **One node per class.** iOS and Android do not resolve a `$service:` binding in `ErrorState`
442
+ `title` or `body`, so the worker cannot swap the text. Give each class the screen can hit its
443
+ own `ErrorState` with literal copy or `$t:` keys, and gate each one with `hiddenPath` bound to a
443
444
  worker flag. The worker classifies the failure once and sets the flags — see
444
445
  `himi docs recipe-four-state` for the whole pattern.
445
446
  - **User-facing language, never the raw error.** Say what happened and what the user can do. Never
@@ -461,17 +462,41 @@ its own recovery.
461
462
 
462
463
  ```jsonc
463
464
  { "id": "orders.error.offline",
464
- "body": { "package": "himalaya", "type": "EmptyState",
465
- "props": { "title": "Reconnect to the internet",
466
- "subtitle": "Connect to the internet, then try again.",
467
- "action": { "actionId": "tier5.orders.retry" },
468
- "actionLabel": "Try again" } },
465
+ "body": { "package": "himalaya", "type": "ErrorState",
466
+ "props": { "variant": "offline",
467
+ "cta": { "actionId": "tier5.orders.retry" } } },
469
468
  "modifiers": { "hiddenPath": "$service:orders.hideOfflineError" } }
470
469
  ```
471
470
 
472
- **When an `ErrorState` component ships.** A dedicated `ErrorState` component is planned. When it
473
- is available, use it in place of the icon-less `EmptyState`. Only the component changes: the
474
- classes, where each one shows, the copy rules and the recovery rules above stay the same.
471
+ **`ErrorState` draws the same layout on iOS, Android, macOS, web and React Native:** a centred
472
+ glyph, title, body and one primary button, with no icon tile and no card.
473
+
474
+ | `variant` | Glyph | Title | Body | Button |
475
+ |---|---|---|---|---|
476
+ | `server` (default) | error | Unable to load this content | There was a technical issue on our end. Please wait and try again later. | Try again |
477
+ | `offline` | wifi | Reconnect to the Internet | Connect to the internet, then try again. | Try again |
478
+ | `permission` | error | You need permission to view this screen | none | Contact customer care |
479
+ | `signIn` | person | Sign in to view this content | none | Sign in |
480
+
481
+ - `title`, `body` and `ctaLabel` replace the copy of the variant, for example
482
+ `"title": "Unable to load orders"`. Site membership has no variant: use `permission` with your
483
+ own `title` and `body`. Write the copy as literal text or `$t:` keys: iOS and Android show a
484
+ `$service:` binding there as raw text, so `himi validate` refuses it.
485
+ - `variantPath` is a `$service:` binding that selects the variant and wins over `variant`. An
486
+ unknown value (`membership`, `""`) keeps `variant`. It switches only the default copy and glyph:
487
+ `cta`, `title`, `body` and `ctaLabel` apply to every variant. So one node bound to the worker's
488
+ `errorKind` covers the kinds that share a button — offline and server, both **Try again** — and
489
+ sign-in (its own action) and permission (no button) each keep a node of their own.
490
+ - The button draws only when you set `cta`. Leave it out for a permission error.
491
+ - `placement` is `screen` (default, about 96px from the top) or `section` (about 24px).
492
+
493
+ ```jsonc
494
+ { "id": "orders.error",
495
+ "body": { "package": "himalaya", "type": "ErrorState",
496
+ "props": { "variantPath": "$service:orders.errorKind",
497
+ "cta": { "actionId": "tier5.orders.retry" } } },
498
+ "modifiers": { "hiddenPath": "$service:orders.hideRetryableError" } } // offline or server
499
+ ```
475
500
 
476
501
  **Loading & empty states are first-class — use the `Skeleton` and `EmptyState` component types.**
477
502
 
@@ -546,7 +571,7 @@ Run this punch list on the screen (it's also a good reviewer checklist):
546
571
 
547
572
  - [ ] **One** primary action, on the one `accentPrimary` surface?
548
573
  - [ ] All four states designed — loading / empty / error / loaded (empty has a directive action; error has a recovery action when one exists)?
549
- - [ ] Errors classified (offline / server / sign-in / site membership / permission), each an icon-less `EmptyState` with user-facing copy — no raw error text? Empty results shown as empty, not as an error? A failed refresh keeps the content?
574
+ - [ ] Errors classified (offline / server / sign-in / site membership / permission), each an `ErrorState` with user-facing copy — no raw error text? Empty results shown as empty, not as an error? A failed refresh keeps the content?
550
575
  - [ ] Loading state matches the loaded layout (a content-shaped `Skeleton` where the layout is known)? For a redesign, was the existing loading state reviewed against the new layout? A static screen has none?
551
576
  - [ ] Every value a token — zero literal hex / off-scale spacing / invented type sizes?
552
577
  - [ ] Is every user-facing string routed through a copy key with a sensible fallback (`copyOf(key, fallback)` backed by your own `COPY` map; worker strings via `onAppear` params) — none baked directly into the descriptor?
@@ -157,8 +157,9 @@ A list loads data, so it ships all four states — the full idiom is
157
157
  It shows only while the first load runs with nothing on screen; a pull-to-refresh
158
158
  keeps the rows and uses the list's own spinner.
159
159
  - **Empty** — an `EmptyState` with a directive action, gated by `notEmpty`.
160
- - **Error, nothing on screen** — one `EmptyState` per class `classifyError` can
161
- return (offline, server, sign-in, site membership, permission), **no icon**, user-facing copy.
160
+ - **Error, nothing on screen** — one `ErrorState` per class `classifyError` can
161
+ return (offline, server, sign-in, site membership, permission): no icon tile or custom icon
162
+ (the `variant` picks the glyph), user-facing copy.
162
163
  Offline and server get **Try again** → `tier5.products.retry`; sign-in gets
163
164
  **Sign in**; membership opens the site's join flow when available; permission gets no action. A list with no sign-in flow maps
164
165
  `"signIn"` to `"permission"` — a visitor still gets a 401 or 403 (Bookings
@@ -178,10 +179,9 @@ A list loads data, so it ships all four states — the full idiom is
178
179
  "props": { "height": 72, "cornerRadius": 12, "count": 6 } },
179
180
  "modifiers": { "hiddenPath": "$service:products.hideLoading" } },
180
181
  { "id": "error.server",
181
- "body": { "package": "himalaya", "type": "EmptyState",
182
- "props": { "title": "Unable to load products",
183
- "subtitle": "There was a technical issue on our end. Please wait and try again later.",
184
- "action": { "actionId": "tier5.products.retry" }, "actionLabel": "Try again" } },
182
+ "body": { "package": "himalaya", "type": "ErrorState",
183
+ "props": { "variant": "server", "title": "Unable to load products",
184
+ "cta": { "actionId": "tier5.products.retry" } } },
185
185
  "modifiers": { "hiddenPath": "$service:products.hideServerError" } }
186
186
  ```
187
187
 
@@ -234,7 +234,7 @@ word:
234
234
  - **Except when page 2 loses access.** A `signIn`, `membership` or `permission` failure on
235
235
  `loadMore` is not retryable and the rows already shown are no longer the
236
236
  user's to see: clear them and go to the same sign-in, membership or
237
- permission `EmptyState` a refresh uses — membership with its join action (`load()` in `himi docs recipe-four-state`). Keep
237
+ permission `ErrorState` a refresh uses — membership with its join action (`load()` in `himi docs recipe-four-state`). Keep
238
238
  page 1 and offer the cursor retry only for `offline` and `server`.
239
239
  - APIs that don't page (or v1 fallbacks without cursors) simply never set
240
240
  `nextCursor` — the gesture props are safe to leave in place.
@@ -13,7 +13,7 @@ Every data-backed screen has four states, and Himi's idiom renders all of them
13
13
  from **one flat layout** — no conditional trees, no separate loading screen. Each
14
14
  state is its own node, gated with the `hiddenPath` modifier bound to a worker
15
15
  boolean, and the worker derives every flag from one `status` field. The empty state
16
- and each error class are `EmptyState` nodes, gated the same way.
16
+ is an `EmptyState` node and each error class an `ErrorState` node, gated the same way.
17
17
 
18
18
  This is the corpus norm: of the in-repo worker bundles, ~3 in 4 model loading,
19
19
  more than half model error, and the reference apps (`apps/himi-owner-lite`,
@@ -32,7 +32,7 @@ interface State {
32
32
  // hiddenPath hides a node when its flag is TRUE — name flags in the hide direction.
33
33
  hideLoading: boolean; // the skeleton: shown only while loading with nothing on screen
34
34
  notEmpty: boolean; // the EmptyState: shown only when ready with zero rows
35
- hideOfflineError: boolean; // one EmptyState per error class …
35
+ hideOfflineError: boolean; // one ErrorState per error class …
36
36
  hideServerError: boolean;
37
37
  hideSignInError: boolean;
38
38
  hideMembershipError: boolean;
@@ -141,40 +141,40 @@ only when the screen shows the user something different for it.
141
141
  "actionLabel": "Add your first product" } },
142
142
  "modifiers": { "hiddenPath": "$service:products.notEmpty" } },
143
143
 
144
- // Errors: an EmptyState per class, with NO icon, user-facing copy, and an action
145
- // only when the user can recover.
144
+ // Errors: an ErrorState per class, with user-facing copy and a `cta` only when the
145
+ // user can recover. `variant` picks the glyph; the authored copy wins over its default.
146
146
  { "id": "error.offline",
147
- "body": { "package": "himalaya", "type": "EmptyState",
148
- "props": { "title": "Reconnect to the internet",
149
- "subtitle": "Connect to the internet, then try again.",
150
- "action": { "actionId": "tier5.products.retry" },
151
- "actionLabel": "Try again" } },
147
+ "body": { "package": "himalaya", "type": "ErrorState",
148
+ "props": { "variant": "offline", "title": "Reconnect to the internet",
149
+ "body": "Connect to the internet, then try again.",
150
+ "cta": { "actionId": "tier5.products.retry" },
151
+ "ctaLabel": "Try again" } },
152
152
  "modifiers": { "hiddenPath": "$service:products.hideOfflineError" } },
153
153
  { "id": "error.server",
154
- "body": { "package": "himalaya", "type": "EmptyState",
155
- "props": { "title": "Unable to load products",
156
- "subtitle": "There was a technical issue on our end. Please wait and try again later.",
157
- "action": { "actionId": "tier5.products.retry" },
158
- "actionLabel": "Try again" } },
154
+ "body": { "package": "himalaya", "type": "ErrorState",
155
+ "props": { "variant": "server", "title": "Unable to load products",
156
+ "body": "There was a technical issue on our end. Please wait and try again later.",
157
+ "cta": { "actionId": "tier5.products.retry" },
158
+ "ctaLabel": "Try again" } },
159
159
  "modifiers": { "hiddenPath": "$service:products.hideServerError" } },
160
160
  { "id": "error.signIn",
161
- "body": { "package": "himalaya", "type": "EmptyState",
162
- "props": { "title": "Sign in to view products",
163
- "subtitle": "Your session has ended.",
164
- "action": { "actionId": "tier5.products.openSignIn" },
165
- "actionLabel": "Sign in" } },
161
+ "body": { "package": "himalaya", "type": "ErrorState",
162
+ "props": { "variant": "signIn", "title": "Sign in to view products",
163
+ "body": "Your session has ended.",
164
+ "cta": { "actionId": "tier5.products.openSignIn" },
165
+ "ctaLabel": "Sign in" } },
166
166
  "modifiers": { "hiddenPath": "$service:products.hideSignInError" } },
167
167
  { "id": "error.membership",
168
- "body": { "package": "himalaya", "type": "EmptyState",
169
- "props": { "title": "Join this site to view products",
170
- "subtitle": "Use this site's join flow, or ask the owner for access.",
171
- "action": { "actionId": "tier5.products.openJoin" },
172
- "actionLabel": "Join this site" } },
168
+ "body": { "package": "himalaya", "type": "ErrorState",
169
+ "props": { "variant": "permission", "title": "Join this site to view products",
170
+ "body": "Use this site's join flow, or ask the owner for access.",
171
+ "cta": { "actionId": "tier5.products.openJoin" },
172
+ "ctaLabel": "Join this site" } },
173
173
  "modifiers": { "hiddenPath": "$service:products.hideMembershipError" } },
174
174
  { "id": "error.permission", // no action: retrying gives the same answer
175
- "body": { "package": "himalaya", "type": "EmptyState",
176
- "props": { "title": "You need permission to view this screen",
177
- "subtitle": "Ask the owner of this business for access." } },
175
+ "body": { "package": "himalaya", "type": "ErrorState",
176
+ "props": { "variant": "permission", "title": "You need permission to view this screen",
177
+ "body": "Ask the owner of this business for access." } },
178
178
  "modifiers": { "hiddenPath": "$service:products.hidePermissionError" } },
179
179
 
180
180
  // A refresh that failed over rows we kept: say so above the rows, keep the rows.
@@ -193,13 +193,13 @@ only when the screen shows the user something different for it.
193
193
  - Ready state is simply the data itself (the list rows, the detail fields) — it
194
194
  needs no dedicated marker.
195
195
  - **Sign in uses a real auth flow.** A renderer cannot dispatch `himi.wix.*`, so
196
- the sign-in `EmptyState` calls an app action that opens its configured auth gate
196
+ the sign-in `ErrorState` calls an app action that opens its configured auth gate
197
197
  or sign-in screen, then reloads after authentication. Do not dispatch
198
198
  `himi.wix.signInDirect` from this button: it requires the email and password
199
199
  collected by a credential form (`himi docs wix-login-integration`).
200
200
  - **Membership is not sign-in.** `NotSiteMember` (Android: `not a member of
201
201
  site`) means the account must join this site. Route it to the membership state;
202
- wire its action to the app's join/registration UI, or omit the action when no
202
+ wire its `cta` to the app's join/registration UI, or omit the `cta` when no
203
203
  such flow exists. A `403 WDE0027` is NOT membership: Wix Data returns it to a
204
204
  signed-in member without access too, so it stays a permission error.
205
205
  - **A 403 is a permission error** — the user lacks access and signing in again
@@ -211,29 +211,35 @@ only when the screen shows the user something different for it.
211
211
  layout — the same rows at about the same size. `StatefulSection` or
212
212
  `ActivityIndicator` are fine where they fit better. A plain `"Loading…"` `Text`
213
213
  bound to a worker string (`collapseWhenEmpty: true`) is the minimum, not the goal.
214
- - **Use `EmptyState` for every empty state and every screen-level error.** It
215
- draws one standard layout on every platform with the app's tokens. Only `title`
216
- is required. `actionLabel` is required when you set `action`: there is no
217
- default label, and `himi validate` reports a missing one. For an error, leave
218
- `icon` out.
219
- - Write the `EmptyState` title and subtitle as literal copy or `$t:` keys. Today,
220
- iOS and Android do not resolve a `$service:` binding in those props — which is
221
- why each error class is its own node instead of one node with bound text.
214
+ - **Use `EmptyState` for every empty state and `ErrorState` for every screen-level
215
+ error.** Each draws one standard layout on every platform with the app's tokens.
216
+ An `EmptyState` needs `title`, and `actionLabel` whenever it has an `action` (there
217
+ is no default label; `himi validate` reports a missing one). An `ErrorState` needs
218
+ nothing: its `variant` supplies default copy and a button label.
219
+ - Write the copy as literal text or `$t:` keys. Today, iOS and Android do not
220
+ resolve a `$service:` binding in those props (`himi validate` refuses one in
221
+ `ErrorState` copy) — which is why each error class is its own node instead of one
222
+ node with bound text.
222
223
  - **A node for every class `classifyError` can return** — otherwise that failure
223
224
  paints a blank screen. A screen with no sign-in flow still gets a 401 when a
224
225
  session ends; map `"signIn"` to `"permission"` in its classifier rather than drop
225
226
  the node.
226
- - **When a planned `ErrorState` component ships,** swap the error `EmptyState`
227
- nodes for it. The classes, the copy and the recovery rules stay as they are.
227
+ - **Fewer nodes with `variantPath`.** `variantPath: "$service:products.errorKind"`
228
+ switches only the default copy and glyph; `cta`, `title`, `body` and `ctaLabel`
229
+ apply to every variant. So `offline` and `server` can share ONE node with the retry
230
+ `cta` and no authored copy — gate it on one flag `shape()` sets for either
231
+ (`hideRetryableError: !(blank && (errorKind === "offline" || errorKind === "server"))`),
232
+ since `hiddenPath` takes a single boolean — while `signIn`, `permission` and
233
+ `membership` keep their own. See `himi docs designing-himi-screens` → Error states.
228
234
 
229
235
  ## 3. Which state when
230
236
 
231
237
  | State | Set where | User affordance |
232
238
  |---|---|---|
233
239
  | loading | initial state (`status: "loading"`) | content-shaped skeleton; NO retry |
234
- | error, nothing on screen | `catch` in the first load | the class's `EmptyState`: user-facing copy + a recovery action where one exists |
240
+ | error, nothing on screen | `catch` in the first load | the class's `ErrorState`: user-facing copy + a recovery `cta` where one exists |
235
241
  | error, rows on screen | `catch` in a refresh (offline or server) | rows stay; a `Banner` with **Try again** above them |
236
- | sign-in, membership or permission | `catch` in any load | rows go; the class's `EmptyState` (auth flow, join flow, or no action) |
242
+ | sign-in, membership or permission | `catch` in any load | rows go; the class's `ErrorState` (auth flow, join flow, or no `cta`) |
237
243
  | empty | derived: ready + 0 rows | say what *would* fill it, and how |
238
244
  | ready | successful fetch | the content |
239
245
 
@@ -66,7 +66,7 @@ not say why there are no rows.
66
66
  | Results | Matching rows, result count, and the available sort/filter controls. |
67
67
  | No matches | An `EmptyState` explaining that the current query or filters found nothing; offer a way to adjust them, and Clear only where the field is controlled (the native slot) — the inline `SearchField` is uncontrolled, so Clear there empties the query but not the field. |
68
68
  | Empty catalog | Explain that the catalog itself has no items yet, independent of the query; provide a useful browse/back action. |
69
- | Error | Classified as in `himi docs recipe-four-state`: an icon-less `EmptyState` per class with user-facing copy, Try again only for offline and server, the app's auth flow for sign-in, the site's join flow for `NotSiteMember`, and no action for permission. Retain the query and controls, and keep it distinct from no matches. |
69
+ | Error | Classified as in `himi docs recipe-four-state`: an `ErrorState` per class with user-facing copy, a Try again `cta` only for offline and server, the app's auth flow for sign-in, the site's join flow for `NotSiteMember`, and no `cta` for permission. Retain the query and controls, and keep it distinct from no matches. |
70
70
 
71
71
  ### Native slot and old-client fallback
72
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1525.0",
3
+ "version": "0.1526.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": [
@@ -71,5 +71,5 @@
71
71
  "artifactId": "himalaya-cli"
72
72
  }
73
73
  },
74
- "falconPackageHash": "4d57d913fdf7e7ed0944f6605a9a5093b4fe45fb1efa09885218796d"
74
+ "falconPackageHash": "78420cfba501244cd7abb4a48357db143350fcae0d24d8cf3027b161"
75
75
  }
@@ -2618,8 +2618,10 @@ const SCREENS = runtimeSetting({
2618
2618
  "actionId": "himi.open",
2619
2619
  "params": {
2620
2620
  "screenId": "himi_sign_in",
2621
- "mode": "signin"
2622
- }
2621
+ "mode": "signin",
2622
+ "modal": "1"
2623
+ },
2624
+ "presentation": "sheet"
2623
2625
  }
2624
2626
  }
2625
2627
  }
@@ -2637,8 +2639,10 @@ const SCREENS = runtimeSetting({
2637
2639
  "actionId": "himi.open",
2638
2640
  "params": {
2639
2641
  "screenId": "himi_sign_in",
2640
- "mode": "register"
2641
- }
2642
+ "mode": "register",
2643
+ "modal": "1"
2644
+ },
2645
+ "presentation": "sheet"
2642
2646
  }
2643
2647
  }
2644
2648
  }
@@ -3921,6 +3925,8 @@ const SCREENS = runtimeSetting({
3921
3925
  "memberAccountLinks": "1",
3922
3926
  "restorePurchases": "1",
3923
3927
  "signInScreen": "himi_sign_in",
3928
+ "signInPresentation": "sheet",
3929
+ "signInReturnTab": "profile",
3924
3930
  "pollIdentity": "1",
3925
3931
  "cartCount": "1",
3926
3932
  "copy": "{\"account.subtitle\":\"Orders, bookings, memberships and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — cart, bookings and memberships stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — cart, bookings and memberships stay with you.\"}"
@@ -4196,8 +4202,10 @@ const SCREENS = runtimeSetting({
4196
4202
  "params": {
4197
4203
  "screenId": "himi_sign_in",
4198
4204
  "mode": "signin",
4199
- "returnScreenId": "himi_account_settings"
4200
- }
4205
+ "returnScreenId": "himi_account_settings",
4206
+ "modal": "1"
4207
+ },
4208
+ "presentation": "sheet"
4201
4209
  }
4202
4210
  }
4203
4211
  },
@@ -6107,8 +6115,7 @@ const SCREENS = runtimeSetting({
6107
6115
  "style": "primary",
6108
6116
  "fullWidth": true,
6109
6117
  "action": {
6110
- "actionId": "nav.back",
6111
- "presentation": "back"
6118
+ "actionId": "tier5.himiStoreAccount.leave"
6112
6119
  }
6113
6120
  }
6114
6121
  }
@@ -6149,7 +6156,7 @@ const SCREENS = runtimeSetting({
6149
6156
  "declaredCapabilities": [
6150
6157
  "wix_api"
6151
6158
  ],
6152
- "minBinaryVersion": "0.2.0",
6159
+ "minBinaryVersion": "1.7.0",
6153
6160
  "bundleVersion": "0.0.1"
6154
6161
  }
6155
6162
  },
@@ -7016,8 +7023,10 @@ const SCREENS = runtimeSetting({
7016
7023
  "screenId": "himi_sign_in",
7017
7024
  "mode": "signin",
7018
7025
  "intent": "profile",
7019
- "returnScreenId": "himi_view_profile"
7020
- }
7026
+ "returnScreenId": "himi_view_profile",
7027
+ "modal": "1"
7028
+ },
7029
+ "presentation": "sheet"
7021
7030
  }
7022
7031
  }
7023
7032
  }
@@ -15843,8 +15852,10 @@ const SCREENS = runtimeSetting({
15843
15852
  "screenId": "himi_sign_in",
15844
15853
  "mode": "signin",
15845
15854
  "intent": "orders",
15846
- "returnScreenId": "himi_orders"
15847
- }
15855
+ "returnScreenId": "himi_orders",
15856
+ "modal": "1"
15857
+ },
15858
+ "presentation": "sheet"
15848
15859
  }
15849
15860
  }
15850
15861
  }
@@ -16477,8 +16488,10 @@ const SCREENS = runtimeSetting({
16477
16488
  "screenId": "himi_sign_in",
16478
16489
  "mode": "signin",
16479
16490
  "intent": "addresses",
16480
- "returnScreenId": "himi_addresses"
16481
- }
16491
+ "returnScreenId": "himi_addresses",
16492
+ "modal": "1"
16493
+ },
16494
+ "presentation": "sheet"
16482
16495
  }
16483
16496
  }
16484
16497
  }
@@ -17534,8 +17547,10 @@ const SCREENS = runtimeSetting({
17534
17547
  "screenId": "himi_sign_in",
17535
17548
  "mode": "signin",
17536
17549
  "intent": "addresses",
17537
- "returnScreenId": "himi_address"
17538
- }
17550
+ "returnScreenId": "himi_address",
17551
+ "modal": "1"
17552
+ },
17553
+ "presentation": "sheet"
17539
17554
  }
17540
17555
  }
17541
17556
  }
@@ -18205,8 +18220,10 @@ const SCREENS = runtimeSetting({
18205
18220
  "screenId": "himi_sign_in",
18206
18221
  "mode": "signin",
18207
18222
  "intent": "wallet",
18208
- "returnScreenId": "himi_wallet"
18209
- }
18223
+ "returnScreenId": "himi_wallet",
18224
+ "modal": "1"
18225
+ },
18226
+ "presentation": "sheet"
18210
18227
  }
18211
18228
  }
18212
18229
  }
@@ -18674,11 +18691,7 @@ const SCREENS = runtimeSetting({
18674
18691
  "size": "sm",
18675
18692
  "fullWidth": true,
18676
18693
  "action": {
18677
- "actionId": "himi.open",
18678
- "params": {
18679
- "screenId": "$service:himiPlaceSummary.ctaScreenId",
18680
- "mode": "$service:himiPlaceSummary.ctaMode"
18681
- }
18694
+ "actionId": "tier5.himiPlaceSummary.openCta"
18682
18695
  }
18683
18696
  }
18684
18697
  },
@@ -18719,7 +18732,8 @@ const SCREENS = runtimeSetting({
18719
18732
  "onAppear": {
18720
18733
  "actionId": "tier5.himiPlaceSummary.load",
18721
18734
  "params": {
18722
- "verticals": "[\"store\",\"bookings\",\"plans\",\"blog\"]"
18735
+ "verticals": "[\"store\",\"bookings\",\"plans\",\"blog\"]",
18736
+ "signInPresentation": "sheet"
18723
18737
  }
18724
18738
  }
18725
18739
  }
@@ -21907,8 +21921,10 @@ const SCREENS = runtimeSetting({
21907
21921
  "params": {
21908
21922
  "screenId": "himi_sign_in",
21909
21923
  "mode": "signin",
21910
- "returnScreenId": "himi_bookings"
21911
- }
21924
+ "returnScreenId": "himi_bookings",
21925
+ "modal": "1"
21926
+ },
21927
+ "presentation": "sheet"
21912
21928
  }
21913
21929
  }
21914
21930
  }
@@ -25292,7 +25308,8 @@ const SCREENS = runtimeSetting({
25292
25308
  "onAppear": {
25293
25309
  "actionId": "tier5.himiBrandedAgenda.load",
25294
25310
  "params": {
25295
- "firstWeekday": "0"
25311
+ "firstWeekday": "0",
25312
+ "signInPresentation": "sheet"
25296
25313
  }
25297
25314
  }
25298
25315
  }
@@ -28349,8 +28366,10 @@ const SCREENS = runtimeSetting({
28349
28366
  "actionId": "himi.open",
28350
28367
  "params": {
28351
28368
  "screenId": "himi_sign_in",
28352
- "mode": "signin"
28353
- }
28369
+ "mode": "signin",
28370
+ "modal": "1"
28371
+ },
28372
+ "presentation": "sheet"
28354
28373
  }
28355
28374
  }
28356
28375
  },
@@ -28735,8 +28754,10 @@ const SCREENS = runtimeSetting({
28735
28754
  "screenId": "himi_sign_in",
28736
28755
  "mode": "signin",
28737
28756
  "intent": "post",
28738
- "returnScreenId": "himi_create_post"
28739
- }
28757
+ "returnScreenId": "himi_create_post",
28758
+ "modal": "1"
28759
+ },
28760
+ "presentation": "sheet"
28740
28761
  }
28741
28762
  }
28742
28763
  }
@@ -32973,8 +32994,10 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
32973
32994
  "params": {
32974
32995
  "screenId": "himi_sign_in",
32975
32996
  "returnScreenId": "himi_order",
32976
- "returnId": "$service:himiBrandedOrder.id"
32977
- }
32997
+ "returnId": "$service:himiBrandedOrder.id",
32998
+ "modal": "1"
32999
+ },
33000
+ "presentation": "sheet"
32978
33001
  }
32979
33002
  }
32980
33003
  },
@@ -38401,8 +38424,10 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
38401
38424
  "params": {
38402
38425
  "screenId": "himi_sign_in",
38403
38426
  "mode": "signin",
38404
- "intent": "book"
38405
- }
38427
+ "intent": "book",
38428
+ "modal": "1"
38429
+ },
38430
+ "presentation": "sheet"
38406
38431
  }
38407
38432
  }
38408
38433
  },
@@ -38427,8 +38452,10 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
38427
38452
  "params": {
38428
38453
  "screenId": "himi_sign_in",
38429
38454
  "mode": "signin",
38430
- "intent": "book"
38431
- }
38455
+ "intent": "book",
38456
+ "modal": "1"
38457
+ },
38458
+ "presentation": "sheet"
38432
38459
  }
38433
38460
  }
38434
38461
  },
@@ -38453,8 +38480,10 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
38453
38480
  "params": {
38454
38481
  "screenId": "himi_sign_in",
38455
38482
  "mode": "signin",
38456
- "intent": "book"
38457
- }
38483
+ "intent": "book",
38484
+ "modal": "1"
38485
+ },
38486
+ "presentation": "sheet"
38458
38487
  }
38459
38488
  }
38460
38489
  },
@@ -598,6 +598,9 @@ function initial(): S {
598
598
  };
599
599
  }
600
600
 
601
+ /** How the app opens its sign-in screen, from the appear params ("" = push). */
602
+ let signInPresentation: "" | "sheet" | "full_screen" = "";
603
+
601
604
  const svc = defineService<S, {
602
605
  load: (s: S, p?: unknown) => Promise<S | undefined>;
603
606
  retry: (s: S) => Promise<S | undefined>;
@@ -614,6 +617,8 @@ const svc = defineService<S, {
614
617
  actions: {
615
618
  load: async (s, p) => {
616
619
  stashCopy(p);
620
+ const presentation = argOf(p, "signInPresentation");
621
+ if (presentation === "sheet" || presentation === "full_screen") signInPresentation = presentation;
617
622
  const fw = Number(argOf(p, "firstWeekday") || s.firstWeekday);
618
623
  const today = dayKey(new Date());
619
624
  // First appearance: open on this week. A re-appearance keeps the member's place and
@@ -739,7 +744,10 @@ const svc = defineService<S, {
739
744
  const before = await memberAccess().catch(() => undefined);
740
745
  if (!before?.email) {
741
746
  busy = false;
742
- return navigate(s, { screenId: "himi_sign_in", args: { mode: "signin", returnScreenId: "himi_agenda" } });
747
+ // As a modal it only closes, and this screen loads again (its re-appearance reload).
748
+ return navigate(s, signInPresentation
749
+ ? { screenId: "himi_sign_in", presentation: signInPresentation, args: { mode: "signin", modal: "1" } }
750
+ : { screenId: "himi_sign_in", args: { mode: "signin", returnScreenId: "himi_agenda" } });
743
751
  }
744
752
  const name = await memberName();
745
753
  const current = await memberAccess().catch(() => undefined);