@wix/himalaya-cli 0.1539.0 → 0.1540.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.
Binary file
@@ -205,7 +205,7 @@
205
205
  ]
206
206
  }
207
207
  ],
208
- "propsSource": "export interface TextFieldProps {\n placeholder?: string;\n /** LITERAL initial text. Renderers paint it verbatim — a `$service:` sentinel\n * here shows the raw token to the user. Bind `valuePath` instead. */\n value?: string;\n /** `$service:`/`$item.` binding → live text from the worker (overrides `value`).\n * Adopted while the field is NOT focused; an empty worker value (a draft reset\n * after send/post/submit) is adopted even mid-focus, so a cleared draft empties\n * the field without the worker ever fighting the caret. */\n valuePath?: string;\n onChange?: Action;\n /** Text input enrichments. */\n keyboard?: Keyboard;\n secure?: boolean;\n multilineMaxLines?: number;\n maxLength?: number;\n autoCapitalization?: AutoCapitalization;\n onSubmit?: Action;\n /** Keyboard control. `suggestions: \"off\"` kills autocorrect + the suggestion\n * strip. `submitLabel` paints the return key and, with `onSubmit`, fires it —\n * submitting dismisses the keyboard except for `\"next\"`, which keeps it up so\n * a worker can move focus on. */\n suggestions?: Suggestions;\n submitLabel?: SubmitLabel;\n /** `$service:` binding → a TOKEN the worker changes to focus this field. Focus\n * is requested whenever the resolved value becomes a new NON-EMPTY value\n * (the first seed included, so a non-empty init focuses on open). Use a\n * monotonic nonce — `String(Date.now())` — since a latched boolean cannot\n * re-fire. Blur is the keyboard-dismiss capability, not a token here. */\n focusPath?: string;\n /** Accessory & state enrichments. */\n leadingIcon?: IconRef;\n trailingIcon?: IconRef;\n clearButton?: boolean;\n helperText?: string;\n error?: boolean;\n prefix?: string;\n suffix?: string;\n /** Drop the default rounded-border chrome so a caller background shows (composer pill). */\n borderless?: boolean;\n /** Declarative syntactic rules the RENDERER evaluates, so they hold with no\n * worker running (Tier 0-4) and with no bridge round-trip. Before this,\n * validation was reachable only by wrapping the field in `Form` -> `FormField`\n * - the shape the design guide steers you away from for hero flows. */\n validation?: Validation;\n /** `$service:`/`$item.` binding -> a worker-computed error string. Non-empty =\n * invalid. This is how SEMANTIC and CROSS-FIELD errors (\"passwords don't\n * match\", \"email taken\") reach the same error slot, with the same styling and\n * a11y wiring, as the declarative ones. Wins over `validation.message`. */\n errorPath?: string;\n /** LITERAL error string for a server-rendered screen with no worker to bind.\n * Lower precedence than `errorPath`, higher than the rules. */\n errorText?: string;\n /** Opt-in accessible show/hide button. Only applies to secure fields; initially masked. */\n visibilityToggle?: boolean;\n}"
208
+ "propsSource": "export interface TextFieldProps {\n placeholder?: string;\n /** LITERAL initial text. Renderers paint it verbatim — a `$service:` sentinel\n * here shows the raw token to the user. Bind `valuePath` instead. */\n value?: string;\n /** `$service:`/`$item.` binding → live text from the worker (overrides `value`).\n * Adopted while the field is NOT focused; an empty worker value (a draft reset\n * after send/post/submit) is adopted even mid-focus, so a cleared draft empties\n * the field without the worker ever fighting the caret. */\n valuePath?: string;\n onChange?: Action;\n /** Text input enrichments. */\n keyboard?: Keyboard;\n secure?: boolean;\n multilineMaxLines?: number;\n maxLength?: number;\n autoCapitalization?: AutoCapitalization;\n onSubmit?: Action;\n /** Keyboard control. `suggestions: \"off\"` kills autocorrect + the suggestion\n * strip. `submitLabel` paints the return key and, with `onSubmit`, fires it —\n * submitting dismisses the keyboard except for `\"next\"`, which keeps it up so\n * a worker can move focus on. */\n suggestions?: Suggestions;\n submitLabel?: SubmitLabel;\n /** `$service:` binding → a TOKEN the worker changes to focus this field. Focus\n * is requested whenever the resolved value becomes a new NON-EMPTY value\n * (the first seed included, so a non-empty init focuses on open). Use a\n * monotonic nonce — `String(Date.now())` — since a latched boolean cannot\n * re-fire. Blur is the keyboard-dismiss capability, not a token here. */\n focusPath?: string;\n /** Accessory & state enrichments. */\n leadingIcon?: IconRef;\n trailingIcon?: IconRef;\n clearButton?: boolean;\n helperText?: string;\n error?: boolean;\n prefix?: string;\n suffix?: string;\n /** Drop the default rounded-border chrome so a caller background shows (composer pill). */\n borderless?: boolean;\n /** Declarative syntactic rules the RENDERER evaluates, so they hold with no\n * worker running (Tier 0-4) and with no bridge round-trip. Before this,\n * validation was reachable only by wrapping the field in `Form` -> `FormField`\n * - the shape the design guide steers you away from for hero flows. */\n validation?: Validation;\n /** `$service:`/`$item.` binding -> a worker-computed error string. Non-empty =\n * invalid. This is how SEMANTIC and CROSS-FIELD errors (\"passwords don't\n * match\", \"email taken\") reach the same error slot, with the same styling and\n * a11y wiring, as the declarative ones. Wins over `validation.message`. */\n errorPath?: string;\n /** LITERAL error string for a server-rendered screen with no worker to bind.\n * Lower precedence than `errorPath`, higher than the rules. */\n errorText?: string;\n /** Opt-in accessible show/hide button. Only applies to secure fields; initially masked. */\n visibilityToggle?: boolean;\n /** `onChange` fires after this many ms of idle typing. 0 or unset = every change.\n * A submit fires a pending debounced `onChange` first. */\n debounceMs?: number;\n}"
209
209
  },
210
210
  {
211
211
  "name": "Toggle",
@@ -422,7 +422,9 @@
422
422
  "name": "SearchField",
423
423
  "category": "Search & filter",
424
424
  "propsType": "SearchFieldProps",
425
- "bindings": [],
425
+ "bindings": [
426
+ "focusPath"
427
+ ],
426
428
  "dispatches": [
427
429
  {
428
430
  "callback": "onChange",
@@ -437,7 +439,7 @@
437
439
  "note": "These two rows share a control and key, so they are NOT independently gated: a payload observation cannot say which callback produced it, and a helper-mediated dispatch does not name the callback at the write site at all. The gate proves the callback is still referenced in the component that dispatches, not that this callback carries the payload."
438
440
  }
439
441
  ],
440
- "propsSource": "export interface SearchFieldProps {\n placeholder?: string;\n value?: string;\n onChange?: Action;\n onSubmit?: Action;\n debounceMs?: number;\n}"
442
+ "propsSource": "export interface SearchFieldProps {\n placeholder?: string;\n value?: string;\n onChange?: Action;\n onSubmit?: Action;\n /** `onChange` fires after this many ms of idle typing; 0 or less (or unset) = 250 ms.\n * A submit fires a pending debounced `onChange` first. */\n debounceMs?: number;\n /** `$service:` binding → a focus TOKEN; `TextFieldProps.focusPath` semantics. */\n focusPath?: string;\n}"
441
443
  },
442
444
  {
443
445
  "name": "FilterChips",
@@ -58,7 +58,7 @@ See also **[Modifiers](#modifiers)** (the `modifiers` block) and **[Actions](#ac
58
58
  | `Skeleton` | State envelopes | — |
59
59
  | `FragmentSlot` | Descriptor-shipped dynamic structure | `casePath` |
60
60
  | `WorkerSubtree` | Bounded worker-authored component tree | `treeJsonPath` |
61
- | `SearchField` | Search & filter | — |
61
+ | `SearchField` | Search & filter | `focusPath` |
62
62
  | `FilterChips` | Search & filter | `optionsPath`, `selectedIds` |
63
63
  | `Tabs` | Navigation primitives | `optionsPath`, `activeColorPath`, `selectedId` |
64
64
  | `SegmentedControl` | Navigation primitives | `optionsPath`, `selectedId` |
@@ -620,6 +620,9 @@ export interface TextFieldProps {
620
620
  errorText?: string;
621
621
  /** Opt-in accessible show/hide button. Only applies to secure fields; initially masked. */
622
622
  visibilityToggle?: boolean;
623
+ /** `onChange` fires after this many ms of idle typing. 0 or unset = every change.
624
+ * A submit fires a pending debounced `onChange` first. */
625
+ debounceMs?: number;
623
626
  }
624
627
  ```
625
628
 
@@ -960,7 +963,11 @@ export interface SearchFieldProps {
960
963
  value?: string;
961
964
  onChange?: Action;
962
965
  onSubmit?: Action;
966
+ /** `onChange` fires after this many ms of idle typing; 0 or less (or unset) = 250 ms.
967
+ * A submit fires a pending debounced `onChange` first. */
963
968
  debounceMs?: number;
969
+ /** `$service:` binding → a focus TOKEN; `TextFieldProps.focusPath` semantics. */
970
+ focusPath?: string;
964
971
  }
965
972
  ```
966
973
 
@@ -80,6 +80,13 @@ The `ux_evidence_*` codes map to the existing typed `uxEvidence.state`; a succes
80
80
  `recorded` state has no refusal code. Successful pushes and warnings do not gain
81
81
  failure codes. A forced push still follows the existing override rules.
82
82
 
83
+ After a full `himi push --live`, push reads the app's rollout rings (`GET /v1/releases`). A ring
84
+ pinned to another release keeps serving that release to its devices, so the success envelope
85
+ then carries a `ring_pinned` entry in `warnings` (also printed to stderr) and
86
+ `ringPins: [{ ring, releaseId, promote }]`, where `promote` is the `himi promote … --ring <ring>`
87
+ that moves the ring to the new release. When the push passed `--control-url`, `promote` repeats it. It stays `ok: true`. When the rings cannot be read, push
88
+ prints neither.
89
+
83
90
  ## `himi cms seed` codes
84
91
 
85
92
  | Code | Meaning |
@@ -32,11 +32,12 @@ The dedicated flow has two renderers for one search model:
32
32
  new non-empty token when the screen opens; that focuses the field and opens
33
33
  the keyboard. If the screen has no worker, seed the token through local state.
34
34
  - **Debounce.** Let `onChange` update the query and start the search after the
35
- user pauses. The proposed native slot uses 250 ms when `debounceMs` is zero
36
- or unset; a pending change flushes before `onSubmit`, so the submitted query
37
- is current.
38
- For the existing inline `SearchField`, set `debounceMs` explicitly (150–300 ms
39
- for local filtering; 300–400 ms for a network search).
35
+ user pauses. `SearchField` (and the proposed native slot) uses 250 ms when
36
+ `debounceMs` is zero or unset, on every renderer; a pending change flushes
37
+ before `onSubmit`, so the submitted query is current. Set `debounceMs`
38
+ explicitly to tune it (150–300 ms for local filtering; 300–400 ms for a
39
+ network search). A `TextField` used as a search box debounces only when
40
+ `debounceMs` is set — unset, it sends every change.
40
41
  - **Clear.** Clear sets the query to `""`, runs `onChange` with the empty value,
41
42
  and returns to the initial state. It is not cancellation and must not invoke
42
43
  `onCancel`.
@@ -151,8 +152,9 @@ first; this recipe only adds the query layer.
151
152
  }
152
153
  ```
153
154
 
154
- - `debounceMs` batches keystrokes — 150–300ms is the sweet spot; without it every
155
- character dispatches a worker action.
155
+ - `debounceMs` batches keystrokes — 150–300ms is the sweet spot. Unset (or 0), a
156
+ `SearchField` waits 250 ms after the last keystroke; emptying the field sends `""`
157
+ at once.
156
158
  - `onSubmit` (keyboard search key) is optional when `onChange` already filters live.
157
159
  - For category/facet filtering alongside text search, add `FilterChips` with
158
160
  `optionsPath: "$service:posts.categories"` and a `selectCategory` action — same
@@ -16,6 +16,13 @@ areas, always in the same order, so you can skim for the one you care about.
16
16
 
17
17
  ## 2026-11-02
18
18
 
19
+ ### Getting started & the CLI
20
+
21
+ - The `restaurant`, `podcast-creator` and `podcast-creator-owner` starter templates no longer paint
22
+ their cream text on a light grey search field, segmented control and chips. Their `dark-luxe` kit
23
+ had no `surfaceTertiary`, so the base app's light value showed through at 1.04:1. The kit now
24
+ ships a dark `surfaceTertiary`. (#4999)
25
+
19
26
  ### Validate & test
20
27
 
21
28
  - **`himi validate` now judges `textPrimary` on card surfaces.** Body text was judged only against
@@ -25,6 +32,16 @@ areas, always in the same order, so you can skim for the one you care about.
25
32
  with an error (also for `textPrimary` on the page); below WCAG AA 4.5:1 it is a warning. The
26
33
  finding says when the page and the card sit on opposite sides of mid-grey, so no single text
27
34
  colour can serve both. (#4999)
35
+ - **`himi validate` now judges `textSecondary` too, and on every surface that text sits on.** Both
36
+ body text colours are judged against `surfaceSecondary` and `surfaceElevated` (cards, sheets, list
37
+ rows), `surfaceTertiary` (search fields, segmented controls, chips, badges) and `surfaceSubtle`
38
+ (Android's Material low containers). Below 1.5:1 validate fails with an error, on the page too;
39
+ below 4.5:1 it is a warning. (#4999)
40
+
41
+ ### Branded Lite & Owner Lite
42
+
43
+ - Branded Lite's dark-theme secondary text is now `#8B949E`. It clears 4.5:1 on the app's cards and
44
+ its search field; before, it was 4.31:1 on the search field. (#4999)
28
45
 
29
46
  ## 2026-11-01
30
47
 
@@ -143,6 +143,25 @@ preview — the `clientId` **is** the site binding (no separate MSID field). Pla
143
143
  `YOUR_WIX_CLIENT_ID` are not a real backend. Monorepo / native binary path:
144
144
  [branded-lite-site-binding.md](branded-lite-site-binding.md).
145
145
 
146
+ ### Keep the copied `_shared` helpers current
147
+
148
+ `init` copies the platform helpers under `tier5-src/_shared/` once, and nothing updates them. An old
149
+ copy keeps compiling against a newer platform and can break a feature without an error (an old
150
+ `iap-presentation.ts` left a Buy button disabled). So every build compares each copy with the ones
151
+ the running CLI ships. `himi build` lists a mismatch under `warnings`; `validate`, `push` and `pack`
152
+ report it as one issue (`pass: "stale-shared"`, `ruleCode: "shared-file-differs"`, `files: [{path,
153
+ shipped, alsoCopy}]`) with severity `warning`, which `--strict` does not promote. (`push --only`
154
+ lists it under `warnings`; `template publish` runs the same lint but prints issues only when it
155
+ refuses.) The message names each file and prints
156
+ the command that refreshes it, plus any `_shared` file the shipped copy imports that your package
157
+ lacks. A copy that matches any template's variant counts as current; the refresh takes the variant of
158
+ the template `init` recorded in `.himi/init-source.json`, or else the one closest to your copy. Files
159
+ the CLI does not ship are yours and are not checked.
160
+
161
+ When you changed a helper on purpose, add a `// himi:forked` line to it and the check skips that
162
+ file; `himi template sweep` adds that line to any `_shared` file it rewrites. Nothing else records
163
+ which files you edited after `init`, so an unmarked edit and an old copy look the same.
164
+
146
165
  ## Publish your own
147
166
 
148
167
  Any himi app can *become* a template. Add a `himalaya.template.json` next to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1539.0",
3
+ "version": "0.1540.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": "cf2aec518d6f8738d5ae5bc0969628fe4258bf6977f4621442bbd5d4"
74
+ "falconPackageHash": "a1d4dc831a852d303dab5ab6d7d7c2eea37f28a6f0c949a4d2c1ce6f"
75
75
  }
@@ -32,6 +32,10 @@
32
32
  "light": "#101319",
33
33
  "dark": "#101319"
34
34
  },
35
+ "surfaceTertiary": {
36
+ "light": "#232834",
37
+ "dark": "#232834"
38
+ },
35
39
  "textPrimary": {
36
40
  "light": "#F1E9D2",
37
41
  "dark": "#F1E9D2"
@@ -6,7 +6,7 @@
6
6
  "surfaceSubtle": { "light": "#F8FAF9", "dark": "#121821", "highContrast": "#111111" },
7
7
  "surfaceTertiary": { "light": "#E9EEF0", "dark": "#232A32", "highContrast": "#1A1A1A" },
8
8
  "textPrimary": { "light": "#1F2328", "dark": "#E6EDF3", "highContrast": "#FFFFFF" },
9
- "textSecondary": { "light": "#59636E", "dark": "#848D98", "highContrast": "#CCCCCC" },
9
+ "textSecondary": { "light": "#59636E", "dark": "#8B949E", "highContrast": "#CCCCCC" },
10
10
  "textTertiary": { "light": "#7D8791", "dark": "#6E7681", "highContrast": "#CCCCCC" },
11
11
  "textOnAccent": { "light": "#FFFFFF", "dark": "#FFFFFF", "highContrast": "#000000" },
12
12
  "accentPrimary": { "light": "#0A66C2", "dark": "#1E72C7", "highContrast": "#58A6FF" },
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "textSecondary": {
34
34
  "light": "#59636E",
35
- "dark": "#848D98",
35
+ "dark": "#8B949E",
36
36
  "highContrast": "#CCCCCC"
37
37
  },
38
38
  "textTertiary": {
@@ -21,8 +21,8 @@
21
21
  "highContrast": "#111111"
22
22
  },
23
23
  "surfaceTertiary": {
24
- "light": "#E9EEF0",
25
- "dark": "#232A32",
24
+ "light": "#232834",
25
+ "dark": "#232834",
26
26
  "highContrast": "#1A1A1A"
27
27
  },
28
28
  "textPrimary": {
@@ -96,8 +96,8 @@
96
96
  "highContrast": "#FFFFFF"
97
97
  },
98
98
  "accentText": {
99
- "light": "#8B6FFF",
100
- "dark": "#957CFF",
99
+ "light": "#947AFF",
100
+ "dark": "#947AFF",
101
101
  "highContrast": "#AE9AFF"
102
102
  }
103
103
  },
@@ -11,8 +11,8 @@
11
11
  "highContrast": "#111111"
12
12
  },
13
13
  "surfaceTertiary": {
14
- "light": "#E4E4E7",
15
- "dark": "#27272A",
14
+ "light": "#232834",
15
+ "dark": "#232834",
16
16
  "highContrast": "#111111"
17
17
  },
18
18
  "surfaceElevated": {
@@ -92,8 +92,8 @@
92
92
  "dark": "#221214"
93
93
  },
94
94
  "accentText": {
95
- "light": "#9474E1",
96
- "dark": "#9A7CE2",
95
+ "light": "#9B7DE3",
96
+ "dark": "#9B7DE3",
97
97
  "highContrast": "#B49DEA"
98
98
  }
99
99
  },
@@ -21,8 +21,8 @@
21
21
  "highContrast": "#111111"
22
22
  },
23
23
  "surfaceTertiary": {
24
- "light": "#E9EEF0",
25
- "dark": "#232A32",
24
+ "light": "#232834",
25
+ "dark": "#232834",
26
26
  "highContrast": "#1A1A1A"
27
27
  },
28
28
  "textPrimary": {
@@ -96,8 +96,8 @@
96
96
  "highContrast": "#FFFFFF"
97
97
  },
98
98
  "accentText": {
99
- "light": "#C36E65",
100
- "dark": "#C87B74",
99
+ "light": "#C87A72",
100
+ "dark": "#C87A72",
101
101
  "highContrast": "#D49892"
102
102
  }
103
103
  },