@wix/himalaya-cli 0.1454.0 → 0.1455.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/cli.mjs +565 -471
- package/guides/designing-himi-screens.md +7 -1
- package/guides/hhp.md +16 -0
- package/guides/recipe-search.md +125 -0
- package/package.json +2 -2
|
@@ -195,6 +195,12 @@ long-form Ricos content rather than flattening it into text rows.*
|
|
|
195
195
|
`Picker`/`WheelPicker`, `DatePicker`, `ColorPicker`, `OtpInput`, `ChipsInput`. *Reach for `Toggle` for
|
|
196
196
|
on/off, `SelectionControl` for pick-from-set — not a row of `Button`s.*
|
|
197
197
|
|
|
198
|
+
**Search** — use a dedicated search screen by default when search starts from a broader context
|
|
199
|
+
(such as Home or a catalog); keep search inline when it only filters the list already on screen.
|
|
200
|
+
Read `himi docs recipe-search` for the dedicated flow, including focus, debounce, result controls,
|
|
201
|
+
return navigation, states, and the documented `$env:topBarSearch` fallback for clients without the
|
|
202
|
+
native top-bar API.
|
|
203
|
+
|
|
198
204
|
**Validation is a prop on the field, not a reason to reach for `Form`.** Put a `validation` block on
|
|
199
205
|
any `TextField` — loose or wrapped — and the renderer evaluates it with no worker running and no
|
|
200
206
|
round-trip:
|
|
@@ -284,7 +290,7 @@ Concrete patterns lifted from real usage:
|
|
|
284
290
|
- **Screen root** — `withModifiers(root, { padding: { leading: 16, trailing: 16, top: 16, bottom: 40 }, background: { token: "surfacePrimary" } })`. Consistent gutter, comfortable bottom inset.
|
|
285
291
|
- **Row with a leading label and trailing detail** — `hstack({ spacing: 8, children: [ icon, text, spacer(), trailingText ] })`. The `spacer()` does the push; don't hand-pad.
|
|
286
292
|
- **Fill-width controls share a row** — put fill-width controls in **one** `HStack` and let them share it (weight/flex), rather than stacking `fillMaxWidth` elements. This is a real iOS↔Android parity requirement, not a preference — a full-width control alone in an HStack renders differently across platforms.
|
|
287
|
-
- **A screen that owns its top bar (search beside back)** — set `topBar: { leading: "none" }` and put the row in `stickyHeader`: `hstack([ iconButton({ icon: "arrow_left", accessibilityLabel: "Back", action: { actionId: "nav.back", presentation: "back" } }), withModifiers(textField({ placeholder: "Search", … }), { weight: 1 }) ])`. With **both** set, every shell draws no bar and no back control of its own (iOS hides the system bar, Android floats no chevron and reserves no band, web hides its bar on every look), so the row is the only top bar. `leading: "none"` without a `stickyHeader` keeps the bar's title and trailing actions but drops the back control (and the hamburger, for that screen). System back — hardware, gesture, edge swipe, browser — still pops. Any other `leading` value keeps today's chrome
|
|
293
|
+
- **A screen that owns its top bar (search beside back)** — set `topBar: { leading: "none" }` and put the row in `stickyHeader`: `hstack([ iconButton({ icon: "arrow_left", accessibilityLabel: "Back", action: { actionId: "nav.back", presentation: "back" } }), withModifiers(textField({ placeholder: "Search", … }), { weight: 1 }) ])`. With **both** set, every shell draws no bar and no back control of its own (iOS hides the system bar, Android floats no chevron and reserves no band, web hides its bar on every look), so the row is the only top bar. `leading: "none"` without a `stickyHeader` keeps the bar's title and trailing actions but drops the back control (and the hamburger, for that screen). System back — hardware, gesture, edge swipe, browser — still pops. Any other `leading` value keeps today's chrome; demo: `gallery_search_topbar`, linked from the gallery's "Lists & data".
|
|
288
294
|
- **Cell list** — `card([ vstack({ spacing: 0, alignment: "leading", children: [ …ListItem cells… ] }) ])`. `spacing: 0` because the cells own their own dividers/padding.
|
|
289
295
|
- **Overlay** — `zstack({ alignment: "center", children: [ backdropImage(cornerRadius 12), withModifiers(label, { padding, background: { token: "accentPrimary" }, cornerRadius: 10 }) ] })`.
|
|
290
296
|
- **Responsive** — `grid({ columns: 1, columnsMedium: 2, spacing: 12 })` for 1-up on phone / 2-up on tablet; `splitView({ primary, detail, primaryMaxWidth: 280 })` for master/detail that stacks on a phone; set `contentMaxWidth` in tokens so wide surfaces keep a readable column instead of running edge-to-edge.
|
package/guides/hhp.md
CHANGED
|
@@ -145,6 +145,22 @@ transitions, haptics, and measurement remain explicit native modifiers.
|
|
|
145
145
|
|
|
146
146
|
Local HTML images resolve relative to their document and must live in the app's
|
|
147
147
|
declared assets directory. They use the existing content-addressed asset channel.
|
|
148
|
+
The descriptor carries a host-less `/v1/asset/<sha>?app=<id>` URL. iOS and Android
|
|
149
|
+
resolve it against the origin that served the screen; on web it resolves against the page,
|
|
150
|
+
so it loads when the web host is served from the serve origin (`himi run`, `/app/a/<id>`)
|
|
151
|
+
but not yet behind a gateway path prefix or inside an embed.
|
|
152
|
+
|
|
153
|
+
iOS and Android have no SVG image decoder. When an `<img>` points at a package
|
|
154
|
+
`.svg`, the compiler renders it to a PNG at 3x the SVG's intrinsic size, writes
|
|
155
|
+
the PNG to `<assets>/hhp-raster/<name>.<hash>.png` (a nested name is flattened,
|
|
156
|
+
with `/` written as `__`), and references that PNG on every
|
|
157
|
+
surface. The compiler owns that folder: do not edit it. It does need to be
|
|
158
|
+
committed and pushed with the package. Rasterizing uses the optional image
|
|
159
|
+
toolchain (`sharp`). Without it, the build reuses a committed raster, but only
|
|
160
|
+
one rendered from the SVG's current bytes (the hash in its name). If no such
|
|
161
|
+
raster is committed, `himi hhp lint` and `himi build` fail with
|
|
162
|
+
`HHP-ASSET-SVG`. To fix it, export the image as PNG or WebP, or install `sharp`.
|
|
163
|
+
Images chosen from controller state are not supported yet.
|
|
148
164
|
|
|
149
165
|
## SDKs and preview fixtures
|
|
150
166
|
|
package/guides/recipe-search.md
CHANGED
|
@@ -1,5 +1,130 @@
|
|
|
1
1
|
# Recipe: search & filter a list
|
|
2
2
|
|
|
3
|
+
## Choose the right search surface
|
|
4
|
+
|
|
5
|
+
Use a **dedicated search screen by default** when search starts a broader discovery
|
|
6
|
+
flow—for example, the user taps Search from Home or a catalog. Push the search
|
|
7
|
+
screen over the current context so Back returns to the same place, and keep the
|
|
8
|
+
query, sort, and filters with that search session.
|
|
9
|
+
|
|
10
|
+
Use **inline filtering** when the user is already looking at a list and the query
|
|
11
|
+
only narrows the rows on that screen. The rest of this recipe's original
|
|
12
|
+
`SearchField` + worker example is that inline pattern.
|
|
13
|
+
|
|
14
|
+
## Dedicated search screen
|
|
15
|
+
|
|
16
|
+
The dedicated flow has two renderers for one search model:
|
|
17
|
+
|
|
18
|
+
1. Use the native top-bar search slot (`topBar.searchable`) only when the
|
|
19
|
+
current client explicitly supports it. This proposed slot belongs in the
|
|
20
|
+
shell-owned top bar; choose `placement: "bar"` for a pushed search screen so
|
|
21
|
+
the search field sits beside Back. The slot is **not implemented in the
|
|
22
|
+
current client schema or renderers**. Treat this shape as a target API; do
|
|
23
|
+
not expect today's `himi validate` or `himi render` to recognize it.
|
|
24
|
+
2. Until a client supports the slot, provide the `sticky_header` fallback
|
|
25
|
+
described below. The `$env:topBarSearch` gate hides that row only on a client
|
|
26
|
+
that actually draws native search.
|
|
27
|
+
|
|
28
|
+
### Query lifecycle
|
|
29
|
+
|
|
30
|
+
- **Enter and focus.** From a wider context, push the dedicated search screen
|
|
31
|
+
and give it a real Back route. With the native slot, seed `focusPath` with a
|
|
32
|
+
new non-empty token when the screen opens; that focuses the field and opens
|
|
33
|
+
the keyboard. If the screen has no worker, seed the token through local state.
|
|
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).
|
|
40
|
+
- **Clear.** Clear sets the query to `""`, runs `onChange` with the empty value,
|
|
41
|
+
and returns to the initial state. It is not cancellation and must not invoke
|
|
42
|
+
`onCancel`.
|
|
43
|
+
- **Submit.** Use `onSubmit` for the keyboard Search action, especially when
|
|
44
|
+
requests are expensive or search should wait for an explicit commit. It
|
|
45
|
+
receives the final text as `args.value`; do not issue a duplicate request if
|
|
46
|
+
the final debounced `onChange` already did so.
|
|
47
|
+
- **Results and controls.** Keep sort, filters, result count, and the result
|
|
48
|
+
list in the search screen. Bind them to the same worker state as the query.
|
|
49
|
+
Changing a filter reruns the search without losing the query; changing the
|
|
50
|
+
query should reset paging/scroll as appropriate. Keep the result count
|
|
51
|
+
consistent with the currently applied controls.
|
|
52
|
+
- **Return navigation.** Use the shell's Back action to pop the search screen
|
|
53
|
+
and reveal the prior screen in its existing state. Do not replace the home or
|
|
54
|
+
catalog route to enter search. If product requirements retain a query across
|
|
55
|
+
separate visits, persist it intentionally; otherwise start a fresh session.
|
|
56
|
+
|
|
57
|
+
### Screen states
|
|
58
|
+
|
|
59
|
+
Model these states separately in the worker; an empty `items` array alone does
|
|
60
|
+
not say why there are no rows.
|
|
61
|
+
|
|
62
|
+
| State | What to show |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| Initial, no query | Search prompt plus useful recent queries, categories, or suggestions. Avoid presenting this as a failed search. |
|
|
65
|
+
| Loading | First load: a skeleton or progress state. Refresh: keep current results visible and show a compact progress indicator when possible. |
|
|
66
|
+
| Results | Matching rows, result count, and the available sort/filter controls. |
|
|
67
|
+
| No matches | Explain that the current query or filters found nothing; offer Clear or a way to adjust them. |
|
|
68
|
+
| Empty catalog | Explain that the catalog itself has no items yet, independent of the query; provide a useful browse/back action. |
|
|
69
|
+
| Error | Show an error with Retry, retain the query and controls, and keep it distinct from no matches. |
|
|
70
|
+
|
|
71
|
+
### Native slot and old-client fallback
|
|
72
|
+
|
|
73
|
+
The following pairs the proposed native slot with the supported fallback. The
|
|
74
|
+
`searchable` field is illustrative: current clients do not decode it. The
|
|
75
|
+
`stickyHeader` root is gated, not just its search field, so the whole fallback
|
|
76
|
+
row disappears when native search is drawn. `leading: "none"` lets the fallback
|
|
77
|
+
own the bar on a client without the native slot. On a client that draws native
|
|
78
|
+
search, the shell must interpret this as `leading: "auto"` while search is
|
|
79
|
+
active so the native Back control remains visible.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const descriptor = {
|
|
83
|
+
topBar: {
|
|
84
|
+
// Native-search clients interpret this as "auto" to keep the shell Back control visible.
|
|
85
|
+
leading: "none",
|
|
86
|
+
// Proposed target API; current clients do not decode this yet.
|
|
87
|
+
searchable: {
|
|
88
|
+
placement: "bar",
|
|
89
|
+
placeholder: "Search products",
|
|
90
|
+
valuePath: "$service:search.query",
|
|
91
|
+
focusPath: "$service:search.focusToken",
|
|
92
|
+
debounceMs: 250,
|
|
93
|
+
onChange: { actionId: "tier5.search.setQuery" },
|
|
94
|
+
onSubmit: { actionId: "tier5.search.submit" },
|
|
95
|
+
onCancel: { actionId: "tier5.search.cancel" },
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
// Fallback for clients without native search. `hiddenPath` belongs on the sticky header root.
|
|
99
|
+
stickyHeader: withModifiers(hstack({
|
|
100
|
+
spacing: 4,
|
|
101
|
+
children: [
|
|
102
|
+
iconButton({
|
|
103
|
+
icon: "arrow_left",
|
|
104
|
+
accessibilityLabel: "Back",
|
|
105
|
+
action: { actionId: "nav.back", presentation: "back" },
|
|
106
|
+
}),
|
|
107
|
+
withModifiers(searchField({
|
|
108
|
+
placeholder: "Search products",
|
|
109
|
+
value: "$service:search.query",
|
|
110
|
+
debounceMs: 250,
|
|
111
|
+
onChange: { actionId: "tier5.search.setQuery" },
|
|
112
|
+
}), { weight: 1 }),
|
|
113
|
+
],
|
|
114
|
+
}), { hiddenPath: "$env:topBarSearch" }),
|
|
115
|
+
};
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The gate is a client `$env:` value, not an app configuration flag.
|
|
119
|
+
Clients that do not draw the slot leave the fallback visible. On older clients
|
|
120
|
+
that must open the fallback focused, use a `TextField` with its supported
|
|
121
|
+
`focusPath` and `valuePath`; `SearchField.focusPath` may not exist in those
|
|
122
|
+
binaries. Keep one search row: do not add a
|
|
123
|
+
second visible field outside the gated sticky header.
|
|
124
|
+
|
|
125
|
+
For the simpler inline list-filter case, continue with the worker-side recipe
|
|
126
|
+
below.
|
|
127
|
+
|
|
3
128
|
Add a **`SearchField`** above a dynamic list and filter **in the worker** — keep
|
|
4
129
|
the full result set in state, derive the visible rows from a query string. No
|
|
5
130
|
network round-trip per keystroke, instant results, and the empty state can say
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1455.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": "
|
|
74
|
+
"falconPackageHash": "99c3369e91c6a783a805a9e9b70e2804c74c021c27d655ee2704c9cb"
|
|
75
75
|
}
|