@wix/himalaya-cli 0.1021.0 → 0.1023.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 +53 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/validator.d.ts +52 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +16 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +16 -0
- package/dist/cli.mjs +366 -170
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/component-reference.json +5 -5
- package/guides/component-reference.md +35 -0
- package/guides/designing-himi-screens.md +7 -3
- package/guides/headless-loop.md +8 -1
- package/guides/release-notes.md +40 -0
- package/guides/testing-himi-apps.md +9 -12
- package/guides/wix-api-usage.md +4 -2
- package/guides/wix-login-integration.md +2 -1
- package/guides/wix-sdk-overview.md +6 -1
- package/package.json +2 -2
|
Binary file
|
|
@@ -594,7 +594,7 @@
|
|
|
594
594
|
"category": "Feedback",
|
|
595
595
|
"propsType": "AlertDialogProps",
|
|
596
596
|
"bindings": [],
|
|
597
|
-
"propsSource": "export interface AlertDialogProps {\n title: string;\n message?: string;\n confirmAction: Action;\n cancelAction?: Action;\n destructive?: boolean;\n /** #1059 — confirm button label; overridable (mirrors NavigationActionRouter's\n * `confirmLabel`). Default \"Confirm\", or \"Delete\" when `destructive`. */\n confirmLabel?: string;\n /** #1059 — cancel button label; overridable. Default \"Cancel\". */\n cancelLabel?: string;\n}"
|
|
597
|
+
"propsSource": "export interface AlertDialogProps {\n title: string;\n message?: string;\n confirmAction: Action;\n cancelAction?: Action;\n destructive?: boolean;\n /** #1059 — confirm button label; overridable (mirrors NavigationActionRouter's\n * `confirmLabel`). Default \"Confirm\", or \"Delete\" when `destructive`. */\n confirmLabel?: string;\n /** #1059 — cancel button label; overridable. Default \"Cancel\". */\n cancelLabel?: string;\n /** The overlay traps focus and hides the rest of the tree from AT while\n * presented (iOS `accessibilityViewIsModal`, Android `isTraversalGroup`, ARIA\n * `aria-modal`). TRI-STATE: absent takes this component's default (**true**), an\n * explicit `false` opts it out. */\n modal?: boolean;\n}"
|
|
598
598
|
},
|
|
599
599
|
{
|
|
600
600
|
"name": "RichText",
|
|
@@ -754,7 +754,7 @@
|
|
|
754
754
|
"severity": "category",
|
|
755
755
|
"note": "Presentation is not implemented — BottomSheet renders as an always-visible inline surface, never a dismissible sheet. All six presentation props are dead. Use the WORKING alternative instead: present the content as its own screen with `presentation: \"sheet\"` on the navigating `Action` — that is a real dismissible sheet on every platform. Reach for BottomSheet only when an always-visible inline surface is what you actually want."
|
|
756
756
|
},
|
|
757
|
-
"propsSource": "export interface BottomSheetProps {\n /** Place the sheet as a root-payload sibling (normally the final child), not\n * inside the trigger row, so its presented overlay can cover the whole screen. */\n content?: Component;\n /** Fractional heights, e.g. [0.3, 0.9]. */\n detents?: number[];\n dismissible?: boolean;\n showGrabber?: boolean;\n onDismiss?: Action;\n presented?: boolean;\n /** \"$service:\" binding → bool (overrides `presented`). */\n presentedPath?: string;\n edge?: \"bottom\" | \"leading\" | \"trailing\";\n}"
|
|
757
|
+
"propsSource": "export interface BottomSheetProps {\n /** Place the sheet as a root-payload sibling (normally the final child), not\n * inside the trigger row, so its presented overlay can cover the whole screen. */\n content?: Component;\n /** Fractional heights, e.g. [0.3, 0.9]. */\n detents?: number[];\n dismissible?: boolean;\n showGrabber?: boolean;\n onDismiss?: Action;\n presented?: boolean;\n /** \"$service:\" binding → bool (overrides `presented`). */\n presentedPath?: string;\n edge?: \"bottom\" | \"leading\" | \"trailing\";\n /** The overlay traps focus and hides the rest of the tree from AT while\n * presented (iOS `accessibilityViewIsModal`, Android `isTraversalGroup`, ARIA\n * `aria-modal`). TRI-STATE: absent takes this component's default (**true**), an\n * explicit `false` opts it out. */\n modal?: boolean;\n}"
|
|
758
758
|
},
|
|
759
759
|
{
|
|
760
760
|
"name": "Menu",
|
|
@@ -772,7 +772,7 @@
|
|
|
772
772
|
"severity": "category",
|
|
773
773
|
"note": "`action_sheet`/`context` render as an always-open inline list; `itemsPath` does not bind items."
|
|
774
774
|
},
|
|
775
|
-
"propsSource": "export interface MenuProps {\n anchor?: Component;\n items?: MenuItem[];\n style?: \"dropdown\" | \"context\" | \"action_sheet\";\n /** \"$service:\" binding → MenuItem[]. */\n itemsPath?: string;\n}"
|
|
775
|
+
"propsSource": "export interface MenuProps {\n anchor?: Component;\n items?: MenuItem[];\n style?: \"dropdown\" | \"context\" | \"action_sheet\";\n /** \"$service:\" binding → MenuItem[]. */\n itemsPath?: string;\n /** The overlay traps focus and hides the rest of the tree from AT while\n * presented (iOS `accessibilityViewIsModal`, Android `isTraversalGroup`, ARIA\n * `aria-modal`). TRI-STATE: absent keeps today's NON-modal behaviour (flipping this\n * component's default to modal is #2900); `true` opts in, an explicit `false` stays out. */\n modal?: boolean;\n}"
|
|
776
776
|
},
|
|
777
777
|
{
|
|
778
778
|
"name": "Tooltip",
|
|
@@ -811,7 +811,7 @@
|
|
|
811
811
|
"severity": "category",
|
|
812
812
|
"note": "Not an overlay — renders always-expanded inline and is never dismissible."
|
|
813
813
|
},
|
|
814
|
-
"propsSource": "export interface PopoverProps {\n anchor?: Component;\n content?: Component;\n edge?: \"top\" | \"bottom\" | \"leading\" | \"trailing\" | \"auto\";\n presented?: boolean;\n presentedPath?: string;\n onDismiss?: Action;\n}"
|
|
814
|
+
"propsSource": "export interface PopoverProps {\n anchor?: Component;\n content?: Component;\n edge?: \"top\" | \"bottom\" | \"leading\" | \"trailing\" | \"auto\";\n presented?: boolean;\n presentedPath?: string;\n onDismiss?: Action;\n /** The overlay traps focus and hides the rest of the tree from AT while\n * presented (iOS `accessibilityViewIsModal`, Android `isTraversalGroup`, ARIA\n * `aria-modal`). TRI-STATE: absent keeps today's NON-modal behaviour (flipping this\n * component's default to modal is #2900); `true` opts in, an explicit `false` stays out. */\n modal?: boolean;\n}"
|
|
815
815
|
},
|
|
816
816
|
{
|
|
817
817
|
"name": "Chip",
|
|
@@ -1253,7 +1253,7 @@
|
|
|
1253
1253
|
"ScreenDescriptor": "export interface ScreenDescriptor {\n screenId: string;\n renderer: RendererKind;\n title?: string;\n /** Present when `renderer` is `sdui_intent` or `sdui_layout`. */\n payload?: Component;\n /** A component pinned ABOVE the scrolling payload (sticky tab-header / toolbar).\n * Rendered by the host outside the screen scroll, so it stays put as content\n * scrolls. Optional; absent = no sticky header. */\n stickyHeader?: Component;\n /** A component pinned BELOW the scrolling payload (sticky footer — price/confirm\n * bar, peeking bottom-sheet). Rendered outside the screen scroll by the host. */\n stickyFooter?: Component;\n /** When true, the host wraps the screen in a scroll that starts pinned to the\n * bottom and shows a floating \"jump to latest\" button whenever the user scrolls\n * up (a chat transcript). Tapping it animates back to the bottom. Absent/false =\n * a normal top-anchored scroll. Honored by the app host (Router). */\n scrollToBottom?: boolean;\n /** Present when `renderer` is `webview_native_bridge` or `webview_ds`. */\n url?: string;\n capabilitiesRequired?: string[];\n schemaVersion: number;\n togglesApplied?: string[];\n /** Per-session flag/experiment assignments baked in for this request. */\n assignments?: import(\"./flags.js\").Assignment[];\n /** Per-screen top bar (trailing actions +\n * leading/style override). Fully replaces `NavigationConfig.topBarDefault`. */\n topBar?: TopBarConfig;\n etag?: string;\n cacheControl?: CacheControl;\n telemetryScreenName?: string;\n bundleSri?: string;\n manifestVersion?: number;\n /**\n * Present on Tier-5 screens. The presence of this field is the tier-5\n * signal — there is no separate `tier: 5` flag.\n */\n logicBundle?: LogicBundleRef;\n /**\n * Static per-screen input injected as `globalThis.himalaya.input` before the\n * Tier-5 worker bundle evaluates — the worker reads it synchronously in `load()`\n * (e.g. `himalaya.input.id` for a detail screen, or a baked catalog like\n * `himalaya.input.cmsCollections`).\n *\n * A push merges its navigation parameters into this object on web, iOS, and Android.\n * For example, `himi.open({ screenId: \"event\", id: \"$item.id\" })` makes the id\n * available as `himalaya.input.id` in the pushed worker; static `input` remains the\n * fallback for values baked into a descriptor or a templated route. The host also\n * supplies `screenId`. See docs/tier-5-add-a-screen.md → \"Navigate to a detail screen\".\n *\n * Arbitrary JSON object; consumed by the web host (ScreenController) and the\n * native JSWorker.\n */\n input?: Record<string, unknown>;\n /**\n * Tier 3 / Tier 4 — behavior of the hosted WebView. Ignored by SDUI tiers.\n * Authored in the descriptor builder (git); served OTA verbatim.\n */\n webViewConfig?: WebViewConfig;\n}",
|
|
1254
1254
|
"Component": "export interface Component {\n id: string;\n body: ComponentBody;\n modifiers?: Modifiers;\n minVersion?: number;\n}",
|
|
1255
1255
|
"LogicBundleRef": "export interface LogicBundleRef {\n url: string;\n /** \"sha256-...\" SRI string. */\n integrity: string;\n contentType?: string;\n /** SDK capability ids the bundle is permitted to import / invoke. */\n declaredCapabilities: string[];\n /** Bundle refuses to load on a binary older than this (semver). */\n minBinaryVersion?: string;\n /** Bundle's own semver. */\n bundleVersion?: string;\n /** Base64 detached publisher signature over the bundle bytes\n * (authenticity beyond SRI). Verified by a host-injected verifier when present;\n * unsigned bundles load as today. */\n signature?: string;\n /** The publisher key id a host pins, paired with `signature`. */\n keyId?: string;\n /** Ambient refresh (gap G2): the host invokes `action` (a `tier5.<svc>.<action>`\n * path) every `intervalMs` while the screen is visible — workers have no timer\n * of their own, so the host owns the cadence. */\n refresh?: { intervalMs: number; action: string };\n}",
|
|
1256
|
-
"Modifiers": "export interface Modifiers {\n padding?: EdgeInsets;\n background?: TokenRef;\n cornerRadius?: number;\n /** Fixed intrinsic width (pt/dp/px). Honored by iOS (`FrameIfPresent`), Android\n * (frame) and web (`modifiers.ts`). With no `ScrollView` in the vocabulary and\n * regions clipped at `maxHeight`, `width`/`height`/`fillMaxWidth` are the main\n * sizing tools for embed/host content. */\n width?: number;\n /** Fixed intrinsic height (pt/dp/px). See `width`. */\n height?: number;\n /** Fill the available cross-axis width (keeps any fixed `height`). Mirrors\n * Compose `fillMaxWidth` / SwiftUI `.frame(maxWidth: .infinity)`. See `width`. */\n fillMaxWidth?: boolean;\n /** CAP the node's laid-out width at this many points. It CONSTRAINS, it\n * does not SET: a node narrower than the cap keeps its intrinsic width, so a\n * short chat bubble stays short while a long one stops at the cap — the\n * asymmetry `width` cannot express and `weight` cannot either (weights divide\n * the *available* space, so every child lands on the same fraction).\n * The cap covers the node's TOTAL width, padding + border included (CSS\n * `max-width` under `box-sizing: border-box`). `fillMaxWidth` grabs the axis but\n * THE CAP WINS — fill + cap is exactly `maxWidth` wide; a literal `width` larger\n * than the cap is clamped to it too. POINTS, not a fraction: a modifier is\n * resolved with no portable notion of \"the row\" on any renderer — compose a\n * percentage from a weighted sibling gap instead. `0`/absent = no cap. */\n maxWidth?: number;\n /** Flex weight along the parent stack's MAIN axis (HStack = width, VStack =\n * height). The stack measures its intrinsic (unweighted) children first, then\n * divides what is LEFT among the weighted ones in proportion to their weights:\n * two children at `1` split the leftover equally, `2` and `1` split it 2:1.\n * Absent (or <= 0) keeps the node at its intrinsic size. Use this — not\n * `fillMaxWidth` — when a node should claim the remaining space *beside* its\n * siblings; a full-bleed fill grabs the whole axis and starves them. Ignored\n * outside an HStack/VStack.\n * CAVEAT — row-sharing controls have NO intrinsic width to measure first: in an\n * HStack a `Button` and a `Toggle` are implicitly weighted, so they DIVIDE the\n * row with your weighted child instead of taking a width ahead of it —\n * `[VStack(weight:1), Button, Button]` is a three-way split, not \"the VStack gets\n * the remainder\". To opt a Button out on EVERY renderer set `fullWidth: false`\n * (an explicit flag wins in both directions; absent falls back to the style\n * default, which is \"share\" for every style but `tertiary`/`link`), or use\n * `iconOnly`. Otherwise give the actions their own row.\n * See `himi docs designing-himi-screens` §4. */\n weight?: number;\n /** RTL — pin the subtree's writing direction: `\"ltr\"` | `\"rtl\"` | `\"locale\"`\n * (inherit). The common RTL pattern is the inverse of `Text.textDirection`: pin a\n * container `ltr` (a logotype, an attachment-tile row, a composer button row)\n * inside an otherwise-RTL screen. Absent = inherit the ambient direction. #1060. */\n layoutDirection?: \"ltr\" | \"rtl\" | \"locale\";\n /** Absolute visual translation (pt/dp/px). Applied as the OUTERMOST transform so\n * it shifts the fully-styled node WITHOUT changing its layout footprint — lets a\n * ZStack place polar/absolute children (e.g. AgentOrbit). Negative allowed. */\n offsetX?: number;\n offsetY?: number;\n onTap?: Action;\n /** Double-tap gesture (e.g. Instagram double-tap-to-like). Distinct from onTap. */\n onDoubleTap?: Action;\n accessibilityLabel?: string;\n hidden?: boolean;\n /** Data-bound conditional visibility: a `\"$service:<svc>.<field>\"`\n * or `\"$item.<field>\"` sentinel resolving to a bool at render; when true the\n * subtree fully COLLAPSES (zero layout, like `hidden`), overriding the\n * literal `hidden`. */\n hiddenPath?: string;\n /** Long-press gesture modifier. */\n onLongPress?: Action;\n /** Fires once when the component first appears on screen. */\n onAppear?: Action;\n /** Scoped token overrides for the subtree. */\n theme?: ThemeOverrides;\n /** Names a token from the app's `shadows` registry. */\n shadow?: TokenRef;\n /** Names a token from the app's `gradients` registry (background fill). */\n backgroundGradient?: TokenRef;\n /** Value-driven gradient: a `$service:`/`$item.` path resolving to a gradient\n * TOKEN NAME used as the live background (e.g. weather sky day→night). */\n backgroundGradientPath?: string;\n /** Value-driven background COLOR: a `$service:`/`$item.` path resolving to a\n * color TOKEN NAME used as the live background. Honored on iOS + Android\n * (`BoundBackgroundIfPresent`); web render support is tracked in #1057. */\n backgroundColorPath?: string;\n /** Value-driven background IMAGE: a `$service:`/`$item.` path resolving to an\n * image URL used as the live background. Honored on iOS + Android; web render\n * support is tracked in #1057. */\n backgroundImagePath?: string;\n /** #1051 — MEASURE (publish): report this node's laid-out size into the renderer's\n * self-contained (worker-free) measure store under the named keys, so a SIBLING can\n * feed the number back into its own layout via a `$measure:<key>` binding. The SDUI\n * equivalent of SwiftUI `.onGeometryChange` / Compose `onGloballyPositioned`; the\n * canonical use is a pinned bottom bar whose measured height becomes a scroll view's\n * bottom inset (sticky-footer + scroll-inset, unrepresentable before — #1051). */\n onMeasure?: Measure;\n /** #1051 — bound height: a `\"$measure:<key>\"` (measure store) or\n * `\"$service:<svc>.<field>\"` / `\"$item.<field>\"` (worker/row state) sentinel resolving\n * to a NUMBER at render. Wins over the literal `height`. String path only, so a\n * binding can never blank the node (never overload the numeric `height`). */\n heightPath?: string;\n /** #1051 — bound width. See `heightPath`. Wins over the literal `width`. */\n widthPath?: string;\n /** #1051 — bound TOP inset (the header-height → top-inset idiom). A `$measure:`/\n * `$service:`/`$item.` sentinel resolving to a number; added as top padding. */\n paddingTopPath?: string;\n /** #1051 — bound BOTTOM inset (the sticky-footer-height → bottom-inset idiom). A\n * `$measure:`/`$service:`/`$item.` sentinel resolving to a number; added as bottom\n * padding so the last scroll row clears a pinned bottom bar. */\n paddingBottomPath?: string;\n /** #1053 — INLINE gradient background paint. Distinct from `backgroundGradient`\n * (which names a `gradients` registry token an embed host can't register): this\n * carries its stops/colours in the descriptor, so it works anywhere. Painted as\n * a surface (behind children), below `material`/`backgroundGradient` if those are\n * also set. */\n gradient?: Gradient;\n /** #1053 — gradient ALPHA MASK: the node shows through where the gradient is\n * opaque and fades where it's transparent (a transparent-token stop = fully\n * clear). The material-under-gradient-scrim / scroll-fade-out idiom. Mirrors iOS\n * `.mask()`, Android `BlendMode.DstIn`, web `mask-image`. */\n mask?: Gradient;\n /** Inline border/stroke. */\n border?: Border;\n /** Subtree alpha 0.0–1.0; absent = opaque. */\n opacity?: number;\n /** #433 — constrain the subtree to a width:height ratio (e.g. `1` = square,\n * `1.777` = 16:9). Width-driven: the height derives from the resolved width.\n * Lets a data-bound Grid cell / photo tile hold its shape at any width. */\n aspectRatio?: number;\n /** Names a token from the app's `motions` registry; the subtree\n * fades + slides up on first appearance. */\n appear?: TokenRef;\n /** Surface material token (flat/glass/frosted). */\n material?: TokenRef;\n /** Per-node construction-language override. */\n variant?: string;\n /** Per-node density / motion override. */\n density?: string;\n motion?: string;\n /** Motion grammar (Batch 1) — press/tap feedback style\n * (auto|none|scale|highlight|scaleHighlight|lift); timing comes from `motion`.\n * Applied only when the node is interactive. Unknown degrades to `auto`. */\n pressFeedback?: string;\n /** Motion grammar (Batch 1) — press-coupled haptic\n * (none|selection|light|medium|heavy|success|warning|error); fires with onTap.\n * Default none (opt-in). Web is a no-op. */\n haptic?: string;\n /** Motion grammar (Batch 1) — animate this container's size changes instead of\n * snapping (iOS `.animation(value:)`, Android `animateContentSize`, web height\n * transition). Curve from the active `motion` personality. */\n animateContentSize?: boolean;\n /** Motion grammar (Batch 1) — enter/exit transition; supersedes `appear` when\n * both are present. */\n transition?: Transition;\n /** Motion grammar (Batch 2, scroll-driven) — defer the `transition` enter until\n * the node first crosses into the scroll viewport (reveal-on-scroll). Gated by\n * reduced motion (shown immediately when reduced). */\n revealOnScroll?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — parallax factor: the node drifts\n * vertically at this fraction of the scroll delta as it moves through the\n * viewport. Gated by reduced motion. Generalizes CollapsingHeader parallaxFactor. */\n parallax?: number;\n /** Motion grammar (Batch 4, rich visual) — when a backgroundGradient is present,\n * continuously drift/rotate it (animated gradient). Gated by reduced motion. */\n animatedGradient?: boolean;\n /** Motion grammar (Batch 3, hero/nav) — shared-element key: a matching\n * transitionId on the source + destination screen morphs that element across\n * navigation (web View Transitions / iOS matchedTransitionSource / Android\n * SharedTransitionLayout). Gated by reduced motion. */\n transitionId?: string;\n /** Motion grammar (Batch 4, rich visual) — render the backgroundGradient as a\n * mesh (overlapping multi-point blend) rather than a linear sweep; combine with\n * animatedGradient to drift it. */\n meshGradient?: boolean;\n /** Motion grammar (Batch 4, celebration) — burst a one-shot confetti overlay\n * within the node on appear. Gated by reduced motion. */\n confetti?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — pin this node to the top of its\n * scroll container (sticky section header). Functional positioning; not gated\n * by reduced motion. */\n sticky?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — turn this thin node into a\n * reading-progress bar that fills left→right with scroll (scaleX). web reads\n * window scroll; native reads the host scroll fraction. Functional. */\n scrollProgress?: boolean;\n /** Motion grammar (Batch 4, rich vector) — a named built-in vector animation\n * (\"loader\" | \"success\" | \"pulse\"), dependency-free. Gated by reduced motion.\n * Arbitrary Lottie/Rive JSON is the lib-backed superset follow-up. */\n vectorAnim?: string;\n /** Motion grammar (Batch 1, choreography) — delay this node's ENTER transition\n * by N ms so a screen can SEQUENCE its entrance beyond list stagger (\"title\n * fades, subtitle slides in 100ms later\"). Adds to any list `staggerMs`. Gated\n * by reduced motion. 0/absent = no extra delay. */\n enterDelayMs?: number;\n /** Motion grammar (Phase-3, per-node MotionSpec) — overrides the PRESS-feedback\n * curve with a `motions`-registry token, mirroring `Transition.motion` for the\n * enter curve. Absent → press uses the global `motion` personality. */\n pressMotion?: TokenRef;\n /** Motion grammar (Phase-3, gesture-driven) — finger-tracked drag-to-dismiss; past\n * threshold along `direction` it animates off and fires `action`, else springs back. */\n swipeDismiss?: SwipeDismiss;\n /** Motion grammar (Phase-4, reduced-motion) — per-node escape from the reduced-motion\n * gate. Default true (respect). false = \"must move anyway\" (transition/press animate\n * even when reduced motion is on). */\n respectReducedMotion?: boolean;\n /** #1052 — uniform scale (1 = no change). */\n scale?: number;\n /** #1052 — rotation in DEGREES. */\n rotation?: number;\n /** #1052 — stacking order within a ZStack/overlay. */\n zIndex?: number;\n /** #1052 — gaussian blur radius (pt/dp/px). */\n blur?: number;\n /** #1052 — `false` = decorative (ignores taps, unlike `hidden` which stays hit-testable);\n * `true` = force hit-testable (a clear tap target). */\n hitTestable?: boolean;\n /** #1052 — explicit drop shadow (colour + radius + offset), for upward / multi-layer\n * shadows the `shadow` token can't express. */\n boxShadow?: Shadow;\n /** The role assistive technology should announce. Absent = the\n * renderer INFERS it from the component type, so an author never restates the\n * obvious. `\"decorative\"` is node-local: the node itself stops being an\n * accessibility element (no role, not a stop) while its children stay\n * traversable — the ARIA `role=\"presentation\"` semantic. To remove the subtree\n * too, use `accessibilityHidden`. */\n accessibilityRole?: AccessibilityRole;\n /** The supplementary \"what happens if I act\" phrase\n * (iOS `accessibilityHint`, Android `onClickLabel`, ARIA `aria-describedby`).\n * Never a substitute for `accessibilityLabel`: AT may not speak a hint at all. */\n accessibilityHint?: string;\n /** Remove this node AND its subtree from the accessibility tree.\n * Distinct from `hidden` (which removes it visually too) and from the\n * `\"decorative\"` role (node-local; children stay reachable). */\n accessibilityHidden?: boolean;\n /** Mark the node as a heading at level 1–6, for rotor / heading\n * navigation. Implies the `\"heading\"` role when `accessibilityRole` is absent.\n * The LEVEL reaches iOS and web only: Android (Compose `heading()`) and React Native\n * (`accessibilityRole: \"header\"`) have no level primitive and mark a plain heading. */\n headingLevel?: number;\n /** An explicit SPOKEN value where the visual and spoken values\n * differ (a rating \"3 of 5\", a gauge \"72 percent\"). Where the value is derivable\n * from a prop the renderer computes it and this override is unnecessary. */\n accessibilityValue?: string;\n /** Additive state that a single role cannot carry. A node may\n * hold several. */\n accessibilityTraits?: AccessibilityTrait[];\n}",
|
|
1256
|
+
"Modifiers": "export interface Modifiers {\n padding?: EdgeInsets;\n background?: TokenRef;\n cornerRadius?: number;\n /** Fixed intrinsic width (pt/dp/px). Honored by iOS (`FrameIfPresent`), Android\n * (frame) and web (`modifiers.ts`). With no `ScrollView` in the vocabulary and\n * regions clipped at `maxHeight`, `width`/`height`/`fillMaxWidth` are the main\n * sizing tools for embed/host content. */\n width?: number;\n /** Fixed intrinsic height (pt/dp/px). See `width`. */\n height?: number;\n /** Fill the available cross-axis width (keeps any fixed `height`). Mirrors\n * Compose `fillMaxWidth` / SwiftUI `.frame(maxWidth: .infinity)`. See `width`. */\n fillMaxWidth?: boolean;\n /** CAP the node's laid-out width at this many points. It CONSTRAINS, it\n * does not SET: a node narrower than the cap keeps its intrinsic width, so a\n * short chat bubble stays short while a long one stops at the cap — the\n * asymmetry `width` cannot express and `weight` cannot either (weights divide\n * the *available* space, so every child lands on the same fraction).\n * The cap covers the node's TOTAL width, padding + border included (CSS\n * `max-width` under `box-sizing: border-box`). `fillMaxWidth` grabs the axis but\n * THE CAP WINS — fill + cap is exactly `maxWidth` wide; a literal `width` larger\n * than the cap is clamped to it too. POINTS, not a fraction: a modifier is\n * resolved with no portable notion of \"the row\" on any renderer — compose a\n * percentage from a weighted sibling gap instead. `0`/absent = no cap. */\n maxWidth?: number;\n /** Flex weight along the parent stack's MAIN axis (HStack = width, VStack =\n * height). The stack measures its intrinsic (unweighted) children first, then\n * divides what is LEFT among the weighted ones in proportion to their weights:\n * two children at `1` split the leftover equally, `2` and `1` split it 2:1.\n * Absent (or <= 0) keeps the node at its intrinsic size. Use this — not\n * `fillMaxWidth` — when a node should claim the remaining space *beside* its\n * siblings; a full-bleed fill grabs the whole axis and starves them. Ignored\n * outside an HStack/VStack.\n * CAVEAT — row-sharing controls have NO intrinsic width to measure first: in an\n * HStack a `Button` and a `Toggle` are implicitly weighted, so they DIVIDE the\n * row with your weighted child instead of taking a width ahead of it —\n * `[VStack(weight:1), Button, Button]` is a three-way split, not \"the VStack gets\n * the remainder\". To opt a Button out on EVERY renderer set `fullWidth: false`\n * (an explicit flag wins in both directions; absent falls back to the style\n * default, which is \"share\" for every style but `tertiary`/`link`), or use\n * `iconOnly`. Otherwise give the actions their own row.\n * See `himi docs designing-himi-screens` §4. */\n weight?: number;\n /** RTL — pin the subtree's writing direction: `\"ltr\"` | `\"rtl\"` | `\"locale\"`\n * (inherit). The common RTL pattern is the inverse of `Text.textDirection`: pin a\n * container `ltr` (a logotype, an attachment-tile row, a composer button row)\n * inside an otherwise-RTL screen. Absent = inherit the ambient direction. #1060. */\n layoutDirection?: \"ltr\" | \"rtl\" | \"locale\";\n /** Absolute visual translation (pt/dp/px). Applied as the OUTERMOST transform so\n * it shifts the fully-styled node WITHOUT changing its layout footprint — lets a\n * ZStack place polar/absolute children (e.g. AgentOrbit). Negative allowed. */\n offsetX?: number;\n offsetY?: number;\n onTap?: Action;\n /** Double-tap gesture (e.g. Instagram double-tap-to-like). Distinct from onTap. */\n onDoubleTap?: Action;\n accessibilityLabel?: string;\n hidden?: boolean;\n /** Data-bound conditional visibility: a `\"$service:<svc>.<field>\"`\n * or `\"$item.<field>\"` sentinel resolving to a bool at render; when true the\n * subtree fully COLLAPSES (zero layout, like `hidden`), overriding the\n * literal `hidden`. */\n hiddenPath?: string;\n /** Long-press gesture modifier. */\n onLongPress?: Action;\n /** Fires once when the component first appears on screen. */\n onAppear?: Action;\n /** Scoped token overrides for the subtree. */\n theme?: ThemeOverrides;\n /** Names a token from the app's `shadows` registry. */\n shadow?: TokenRef;\n /** Names a token from the app's `gradients` registry (background fill). */\n backgroundGradient?: TokenRef;\n /** Value-driven gradient: a `$service:`/`$item.` path resolving to a gradient\n * TOKEN NAME used as the live background (e.g. weather sky day→night). */\n backgroundGradientPath?: string;\n /** Value-driven background COLOR: a `$service:`/`$item.` path resolving to a\n * color TOKEN NAME used as the live background. Honored on iOS + Android\n * (`BoundBackgroundIfPresent`); web render support is tracked in #1057. */\n backgroundColorPath?: string;\n /** Value-driven background IMAGE: a `$service:`/`$item.` path resolving to an\n * image URL used as the live background. Honored on iOS + Android; web render\n * support is tracked in #1057. */\n backgroundImagePath?: string;\n /** #1051 — MEASURE (publish): report this node's laid-out size into the renderer's\n * self-contained (worker-free) measure store under the named keys, so a SIBLING can\n * feed the number back into its own layout via a `$measure:<key>` binding. The SDUI\n * equivalent of SwiftUI `.onGeometryChange` / Compose `onGloballyPositioned`; the\n * canonical use is a pinned bottom bar whose measured height becomes a scroll view's\n * bottom inset (sticky-footer + scroll-inset, unrepresentable before — #1051). */\n onMeasure?: Measure;\n /** #1051 — bound height: a `\"$measure:<key>\"` (measure store) or\n * `\"$service:<svc>.<field>\"` / `\"$item.<field>\"` (worker/row state) sentinel resolving\n * to a NUMBER at render. Wins over the literal `height`. String path only, so a\n * binding can never blank the node (never overload the numeric `height`). */\n heightPath?: string;\n /** #1051 — bound width. See `heightPath`. Wins over the literal `width`. */\n widthPath?: string;\n /** #1051 — bound TOP inset (the header-height → top-inset idiom). A `$measure:`/\n * `$service:`/`$item.` sentinel resolving to a number; added as top padding. */\n paddingTopPath?: string;\n /** #1051 — bound BOTTOM inset (the sticky-footer-height → bottom-inset idiom). A\n * `$measure:`/`$service:`/`$item.` sentinel resolving to a number; added as bottom\n * padding so the last scroll row clears a pinned bottom bar. */\n paddingBottomPath?: string;\n /** #1053 — INLINE gradient background paint. Distinct from `backgroundGradient`\n * (which names a `gradients` registry token an embed host can't register): this\n * carries its stops/colours in the descriptor, so it works anywhere. Painted as\n * a surface (behind children), below `material`/`backgroundGradient` if those are\n * also set. */\n gradient?: Gradient;\n /** #1053 — gradient ALPHA MASK: the node shows through where the gradient is\n * opaque and fades where it's transparent (a transparent-token stop = fully\n * clear). The material-under-gradient-scrim / scroll-fade-out idiom. Mirrors iOS\n * `.mask()`, Android `BlendMode.DstIn`, web `mask-image`. */\n mask?: Gradient;\n /** Inline border/stroke. */\n border?: Border;\n /** Subtree alpha 0.0–1.0; absent = opaque. */\n opacity?: number;\n /** #433 — constrain the subtree to a width:height ratio (e.g. `1` = square,\n * `1.777` = 16:9). Width-driven: the height derives from the resolved width.\n * Lets a data-bound Grid cell / photo tile hold its shape at any width. */\n aspectRatio?: number;\n /** Names a token from the app's `motions` registry; the subtree\n * fades + slides up on first appearance. */\n appear?: TokenRef;\n /** Surface material token (flat/glass/frosted). */\n material?: TokenRef;\n /** Per-node construction-language override. */\n variant?: string;\n /** Per-node density / motion override. */\n density?: string;\n motion?: string;\n /** Motion grammar (Batch 1) — press/tap feedback style\n * (auto|none|scale|highlight|scaleHighlight|lift); timing comes from `motion`.\n * Applied only when the node is interactive. Unknown degrades to `auto`. */\n pressFeedback?: string;\n /** Motion grammar (Batch 1) — press-coupled haptic\n * (none|selection|light|medium|heavy|success|warning|error); fires with onTap.\n * Default none (opt-in). Web is a no-op. */\n haptic?: string;\n /** Motion grammar (Batch 1) — animate this container's size changes instead of\n * snapping (iOS `.animation(value:)`, Android `animateContentSize`, web height\n * transition). Curve from the active `motion` personality. */\n animateContentSize?: boolean;\n /** Motion grammar (Batch 1) — enter/exit transition; supersedes `appear` when\n * both are present. */\n transition?: Transition;\n /** Motion grammar (Batch 2, scroll-driven) — defer the `transition` enter until\n * the node first crosses into the scroll viewport (reveal-on-scroll). Gated by\n * reduced motion (shown immediately when reduced). */\n revealOnScroll?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — parallax factor: the node drifts\n * vertically at this fraction of the scroll delta as it moves through the\n * viewport. Gated by reduced motion. Generalizes CollapsingHeader parallaxFactor. */\n parallax?: number;\n /** Motion grammar (Batch 4, rich visual) — when a backgroundGradient is present,\n * continuously drift/rotate it (animated gradient). Gated by reduced motion. */\n animatedGradient?: boolean;\n /** Motion grammar (Batch 3, hero/nav) — shared-element key: a matching\n * transitionId on the source + destination screen morphs that element across\n * navigation (web View Transitions / iOS matchedTransitionSource / Android\n * SharedTransitionLayout). Gated by reduced motion. */\n transitionId?: string;\n /** Motion grammar (Batch 4, rich visual) — render the backgroundGradient as a\n * mesh (overlapping multi-point blend) rather than a linear sweep; combine with\n * animatedGradient to drift it. */\n meshGradient?: boolean;\n /** Motion grammar (Batch 4, celebration) — burst a one-shot confetti overlay\n * within the node on appear. Gated by reduced motion. */\n confetti?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — pin this node to the top of its\n * scroll container (sticky section header). Functional positioning; not gated\n * by reduced motion. */\n sticky?: boolean;\n /** Motion grammar (Batch 2, scroll-driven) — turn this thin node into a\n * reading-progress bar that fills left→right with scroll (scaleX). web reads\n * window scroll; native reads the host scroll fraction. Functional. */\n scrollProgress?: boolean;\n /** Motion grammar (Batch 4, rich vector) — a named built-in vector animation\n * (\"loader\" | \"success\" | \"pulse\"), dependency-free. Gated by reduced motion.\n * Arbitrary Lottie/Rive JSON is the lib-backed superset follow-up. */\n vectorAnim?: string;\n /** Motion grammar (Batch 1, choreography) — delay this node's ENTER transition\n * by N ms so a screen can SEQUENCE its entrance beyond list stagger (\"title\n * fades, subtitle slides in 100ms later\"). Adds to any list `staggerMs`. Gated\n * by reduced motion. 0/absent = no extra delay. */\n enterDelayMs?: number;\n /** Motion grammar (Phase-3, per-node MotionSpec) — overrides the PRESS-feedback\n * curve with a `motions`-registry token, mirroring `Transition.motion` for the\n * enter curve. Absent → press uses the global `motion` personality. */\n pressMotion?: TokenRef;\n /** Motion grammar (Phase-3, gesture-driven) — finger-tracked drag-to-dismiss; past\n * threshold along `direction` it animates off and fires `action`, else springs back. */\n swipeDismiss?: SwipeDismiss;\n /** Motion grammar (Phase-4, reduced-motion) — per-node escape from the reduced-motion\n * gate. Default true (respect). false = \"must move anyway\" (transition/press animate\n * even when reduced motion is on). */\n respectReducedMotion?: boolean;\n /** #1052 — uniform scale (1 = no change). */\n scale?: number;\n /** #1052 — rotation in DEGREES. */\n rotation?: number;\n /** #1052 — stacking order within a ZStack/overlay. */\n zIndex?: number;\n /** #1052 — gaussian blur radius (pt/dp/px). */\n blur?: number;\n /** #1052 — `false` = decorative (ignores taps, unlike `hidden` which stays hit-testable);\n * `true` = force hit-testable (a clear tap target). */\n hitTestable?: boolean;\n /** #1052 — explicit drop shadow (colour + radius + offset), for upward / multi-layer\n * shadows the `shadow` token can't express. */\n boxShadow?: Shadow;\n /** The role assistive technology should announce. Absent = the\n * renderer INFERS it from the component type, so an author never restates the\n * obvious. `\"decorative\"` is node-local: the node itself stops being an\n * accessibility element (no role, not a stop) while its children stay\n * traversable — the ARIA `role=\"presentation\"` semantic. To remove the subtree\n * too, use `accessibilityHidden`. */\n accessibilityRole?: AccessibilityRole;\n /** The supplementary \"what happens if I act\" phrase\n * (iOS `accessibilityHint`, Android `onClickLabel`, ARIA `aria-describedby`).\n * Never a substitute for `accessibilityLabel`: AT may not speak a hint at all. */\n accessibilityHint?: string;\n /** Remove this node AND its subtree from the accessibility tree.\n * Distinct from `hidden` (which removes it visually too) and from the\n * `\"decorative\"` role (node-local; children stay reachable). */\n accessibilityHidden?: boolean;\n /** Mark the node as a heading at level 1–6, for rotor / heading\n * navigation. Implies the `\"heading\"` role when `accessibilityRole` is absent.\n * The LEVEL reaches iOS and web only: Android (Compose `heading()`) and React Native\n * (`accessibilityRole: \"header\"`) have no level primitive and mark a plain heading. */\n headingLevel?: number;\n /** An explicit SPOKEN value where the visual and spoken values\n * differ (a rating \"3 of 5\", a gauge \"72 percent\"). Where the value is derivable\n * from a prop the renderer computes it and this override is unnecessary. */\n accessibilityValue?: string;\n /** Additive state that a single role cannot carry. A node may\n * hold several. */\n accessibilityTraits?: AccessibilityTrait[];\n /** Explicit traversal index within the nearest focus group. Absent ⇒\n * the arm's visual / DOM order. The lower index is reached FIRST, which is why iOS\n * (whose `accessibilitySortPriority` is descending) negates it. */\n focusOrder?: number;\n /** This node's subtree is ONE traversal scope: children are ordered\n * relative to each other, then the group takes its parent's slot (iOS\n * `shouldGroupAccessibilityChildren`, Android `isTraversalGroup`, ARIA grouping). */\n focusGroup?: boolean;\n /** AT / keyboard focus lands here when the screen or a newly presented\n * modal appears. At most one per focus scope. */\n initialFocus?: boolean;\n /** A general live-region announcement, on any node and any transition.\n * The generalization of `FragmentSlotCase.accessibilityAnnouncement`, which only ever\n * fired on a fragment-slot case swap. */\n announce?: LiveRegion;\n}",
|
|
1257
1257
|
"TokenRef": "export interface TokenRef {\n token: TokenName;\n}",
|
|
1258
1258
|
"Action": "export interface Action {\n actionId: string;\n /** Backwards-compatible string-only params. Prefer `args` for typed payloads. */\n params?: Record<string, string>;\n /** Typed action args; nested objects/arrays preserved end-to-end. */\n args?: Record<string, Value>;\n /** Navigation hint for the router; ignored for non-nav actions. */\n presentation?: Presentation;\n}",
|
|
1259
1259
|
"Presentation": "export type Presentation =\n | \"push\"\n | \"sheet\"\n | \"replace\"\n | \"dismiss\"\n | \"back\"\n | \"full_screen\"\n | \"alert\"\n | \"snackbar\";",
|
|
@@ -1113,6 +1113,11 @@ export interface AlertDialogProps {
|
|
|
1113
1113
|
confirmLabel?: string;
|
|
1114
1114
|
/** #1059 — cancel button label; overridable. Default "Cancel". */
|
|
1115
1115
|
cancelLabel?: string;
|
|
1116
|
+
/** The overlay traps focus and hides the rest of the tree from AT while
|
|
1117
|
+
* presented (iOS `accessibilityViewIsModal`, Android `isTraversalGroup`, ARIA
|
|
1118
|
+
* `aria-modal`). TRI-STATE: absent takes this component's default (**true**), an
|
|
1119
|
+
* explicit `false` opts it out. */
|
|
1120
|
+
modal?: boolean;
|
|
1116
1121
|
}
|
|
1117
1122
|
```
|
|
1118
1123
|
|
|
@@ -1280,6 +1285,11 @@ export interface BottomSheetProps {
|
|
|
1280
1285
|
/** "$service:" binding → bool (overrides `presented`). */
|
|
1281
1286
|
presentedPath?: string;
|
|
1282
1287
|
edge?: "bottom" | "leading" | "trailing";
|
|
1288
|
+
/** The overlay traps focus and hides the rest of the tree from AT while
|
|
1289
|
+
* presented (iOS `accessibilityViewIsModal`, Android `isTraversalGroup`, ARIA
|
|
1290
|
+
* `aria-modal`). TRI-STATE: absent takes this component's default (**true**), an
|
|
1291
|
+
* explicit `false` opts it out. */
|
|
1292
|
+
modal?: boolean;
|
|
1283
1293
|
}
|
|
1284
1294
|
```
|
|
1285
1295
|
|
|
@@ -1294,6 +1304,11 @@ export interface MenuProps {
|
|
|
1294
1304
|
style?: "dropdown" | "context" | "action_sheet";
|
|
1295
1305
|
/** "$service:" binding → MenuItem[]. */
|
|
1296
1306
|
itemsPath?: string;
|
|
1307
|
+
/** The overlay traps focus and hides the rest of the tree from AT while
|
|
1308
|
+
* presented (iOS `accessibilityViewIsModal`, Android `isTraversalGroup`, ARIA
|
|
1309
|
+
* `aria-modal`). TRI-STATE: absent keeps today's NON-modal behaviour (flipping this
|
|
1310
|
+
* component's default to modal is #2900); `true` opts in, an explicit `false` stays out. */
|
|
1311
|
+
modal?: boolean;
|
|
1297
1312
|
}
|
|
1298
1313
|
```
|
|
1299
1314
|
|
|
@@ -1325,6 +1340,11 @@ export interface PopoverProps {
|
|
|
1325
1340
|
presented?: boolean;
|
|
1326
1341
|
presentedPath?: string;
|
|
1327
1342
|
onDismiss?: Action;
|
|
1343
|
+
/** The overlay traps focus and hides the rest of the tree from AT while
|
|
1344
|
+
* presented (iOS `accessibilityViewIsModal`, Android `isTraversalGroup`, ARIA
|
|
1345
|
+
* `aria-modal`). TRI-STATE: absent keeps today's NON-modal behaviour (flipping this
|
|
1346
|
+
* component's default to modal is #2900); `true` opts in, an explicit `false` stays out. */
|
|
1347
|
+
modal?: boolean;
|
|
1328
1348
|
}
|
|
1329
1349
|
```
|
|
1330
1350
|
|
|
@@ -2454,6 +2474,21 @@ export interface Modifiers {
|
|
|
2454
2474
|
/** Additive state that a single role cannot carry. A node may
|
|
2455
2475
|
* hold several. */
|
|
2456
2476
|
accessibilityTraits?: AccessibilityTrait[];
|
|
2477
|
+
/** Explicit traversal index within the nearest focus group. Absent ⇒
|
|
2478
|
+
* the arm's visual / DOM order. The lower index is reached FIRST, which is why iOS
|
|
2479
|
+
* (whose `accessibilitySortPriority` is descending) negates it. */
|
|
2480
|
+
focusOrder?: number;
|
|
2481
|
+
/** This node's subtree is ONE traversal scope: children are ordered
|
|
2482
|
+
* relative to each other, then the group takes its parent's slot (iOS
|
|
2483
|
+
* `shouldGroupAccessibilityChildren`, Android `isTraversalGroup`, ARIA grouping). */
|
|
2484
|
+
focusGroup?: boolean;
|
|
2485
|
+
/** AT / keyboard focus lands here when the screen or a newly presented
|
|
2486
|
+
* modal appears. At most one per focus scope. */
|
|
2487
|
+
initialFocus?: boolean;
|
|
2488
|
+
/** A general live-region announcement, on any node and any transition.
|
|
2489
|
+
* The generalization of `FragmentSlotCase.accessibilityAnnouncement`, which only ever
|
|
2490
|
+
* fired on a fragment-slot case swap. */
|
|
2491
|
+
announce?: LiveRegion;
|
|
2457
2492
|
}
|
|
2458
2493
|
|
|
2459
2494
|
export interface TokenRef {
|
|
@@ -202,9 +202,13 @@ textField({
|
|
|
202
202
|
keyboard follows the type** — `type: "email"` gives the user the `@` key without a separate
|
|
203
203
|
`keyboard:`. An explicit `keyboard` still wins. (`regex` is still there for the genuinely custom
|
|
204
204
|
case; it is anchored on both sides, so `"[0-9]+"` rejects `"12a"`.)
|
|
205
|
-
- **The error renders itself**, in one slot,
|
|
206
|
-
would not get by hand.
|
|
207
|
-
|
|
205
|
+
- **The error renders itself**, in one slot, and is tied to its field for screen readers on every
|
|
206
|
+
platform — wiring you would not get by hand. On web the input is marked `aria-invalid` and
|
|
207
|
+
described by the message, which is announced as an alert; on Android the field carries it as its
|
|
208
|
+
semantic error, so TalkBack reads it with the field; on iOS VoiceOver reads it right after the
|
|
209
|
+
field's name and value, and announces it once when it appears or changes (not on first paint).
|
|
210
|
+
It appears **on blur** by default so the field does not shout mid-typing; `mode: "live"` or
|
|
211
|
+
`"on_submit"` to change that. Override the wording per field with `message`.
|
|
208
212
|
- **A blank OPTIONAL field is always valid** — only `required` fires on an empty value — so a rule on
|
|
209
213
|
a field the user may skip never silently disables the submit.
|
|
210
214
|
- **Semantic and cross-field errors come from the worker**, into that same slot, via `errorPath`
|
package/guides/headless-loop.md
CHANGED
|
@@ -92,7 +92,14 @@ validate, push and progress entry refreshes it, and the page picks that up by it
|
|
|
92
92
|
nothing to re-run (`--no-open` prints its path instead, for SSH). `himi run --watch` rebuilds on every
|
|
93
93
|
save and sends the open tab only what changed; a save that does not build keeps the last good preview
|
|
94
94
|
on screen and prints `build_failed` with the reason. It stays up until you stop it, so start it in the
|
|
95
|
-
background, and only after the template's startup requests are preflighted (step 4 of the entry skill).
|
|
95
|
+
background, and only after the template's startup requests are preflighted (step 4 of the entry skill).
|
|
96
|
+
While it runs it also serves the board at `/__himi/studio/` on the preview's own origin, with the live
|
|
97
|
+
preview framed in it; that frame is kept across board updates, so it repaints in place rather than
|
|
98
|
+
reloading. The board answers each real change and nothing else: a new screen develops in, a changed
|
|
99
|
+
one dips, the phone pulses with "Rebuilt in 2.3 s · home" (red when a save did not build), a step
|
|
100
|
+
fills, and a published draft or the handover is celebrated once. The tab title carries the step
|
|
101
|
+
("(5/8) salon"), and the owner can switch on Sound and desktop notices from the board itself — both
|
|
102
|
+
start off, so never promise them either. `himi test` writes one PNG per
|
|
96
103
|
screen to `.himi/shots/` on every run, so take the new screens from there instead of rendering each
|
|
97
104
|
one again; a bare `--png` on `himi render` writes `.himi/shots/<id>.png`. `himi progress` copies each image into `.himi/history/`
|
|
98
105
|
as it was when you journaled it, so a later render cannot change what the record says the owner saw;
|
package/guides/release-notes.md
CHANGED
|
@@ -14,10 +14,34 @@ areas, always in the same order, so you can skim for the one you care about.
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## 2026-09-25
|
|
18
|
+
|
|
19
|
+
### Preview, push & release
|
|
20
|
+
|
|
21
|
+
- **A saved screen edit now reaches the open `himi run --watch` preview.** Only `tokens.json`
|
|
22
|
+
edits used to land: every rebuild after the first served the first build's screens, because the
|
|
23
|
+
screen module was reloaded from the process's module cache, and the web host evicted a changed
|
|
24
|
+
screen without re-fetching the one on display. Rebuilds now load the source as it is on disk
|
|
25
|
+
(`dev/index.ts` and everything it imports, `config.ts` included), and the visible screen repaints
|
|
26
|
+
in place: no page reload, so the tab keeps its scroll and wherever you navigated. (#3209)
|
|
27
|
+
|
|
17
28
|
## 2026-09-24
|
|
18
29
|
|
|
19
30
|
### Getting started & the CLI
|
|
20
31
|
|
|
32
|
+
- **`himi progress`, `himi studio` and `himi studio reel` let an agent show the owner the app
|
|
33
|
+
taking shape during a long build.** `himi progress "<what changed>" --step <1-8|name> --image
|
|
34
|
+
<png>` journals a postcard to `.himi/progress.jsonl`, copying each image into `.himi/history/`
|
|
35
|
+
so a later render cannot change what the owner was shown; a bare `himi progress` prints the
|
|
36
|
+
journal, the current step and the elapsed time. `himi studio` writes a self-contained build
|
|
37
|
+
board to `.himi/studio/index.html` and opens it; from then on every render, test, validate, push
|
|
38
|
+
and postcard refreshes it with no server and nothing to re-run. It shows the journey stepper,
|
|
39
|
+
the latest postcard, every screen as it renders with New and Changed badges, the checks, the
|
|
40
|
+
palette, the navigation the test crawl followed and the postcard timeline. `himi studio reel`
|
|
41
|
+
writes the making-of, one HTML file with the home screen version by version and the postcards.
|
|
42
|
+
The served skill now tells the agent to show a first picture early, a postcard per milestone,
|
|
43
|
+
and never to go quiet for more than about five minutes. (#3177, #3178, #3180)
|
|
44
|
+
|
|
21
45
|
- **`himi template trim --keep <ids>` cuts a scaffolded template down to the screens you want,
|
|
22
46
|
then proves it still validates.** A starter template ships every screen in one generated
|
|
23
47
|
`dev/index.ts` — about 1.8 MB for `gym-fitness` and 4.5 MB for `branded-lite` — so trimming by
|
|
@@ -30,6 +54,22 @@ areas, always in the same order, so you can skim for the one you care about.
|
|
|
30
54
|
no longer tells you `validate --strict` catches a tab pointing at a deleted screen: it does not,
|
|
31
55
|
and `himi test` is what reports it. (#3149)
|
|
32
56
|
|
|
57
|
+
### Validate & test
|
|
58
|
+
|
|
59
|
+
- **A bare `himi render <screenId> --png` writes `.himi/shots/<screenId>.png`.** It used to render
|
|
60
|
+
and write nothing unless you named a file; that path is also where `himi test` puts its shots
|
|
61
|
+
and what the build board shows. (#3178)
|
|
62
|
+
|
|
63
|
+
### Preview, push & release
|
|
64
|
+
|
|
65
|
+
- **`himi run --watch` is a live preview of a content package.** It rebuilds on every save and
|
|
66
|
+
pushes only what changed to the open tab over the dev hot-reload events; a save that does not
|
|
67
|
+
build keeps the last good preview and prints `build_failed` with the reason. `--watch` with
|
|
68
|
+
`--browser` or `--spa` is refused. (#3179)
|
|
69
|
+
- **Every `himi run` frames its preview on the build board**, served on the preview's own origin at
|
|
70
|
+
`/__himi/studio/`, and takes the frame down when it stops; the board says whether that run
|
|
71
|
+
repaints on edits. (#3197)
|
|
72
|
+
|
|
33
73
|
## 2026-09-18
|
|
34
74
|
|
|
35
75
|
### Logic, data & SDKs
|
|
@@ -36,8 +36,8 @@ answers three questions:
|
|
|
36
36
|
> ```bash
|
|
37
37
|
> himi validate --content . --strict
|
|
38
38
|
> himi test --content . --json
|
|
39
|
-
> himi render home --content . --png home
|
|
40
|
-
> himi render home --content . --scheme dark --png home-dark.png
|
|
39
|
+
> himi render home --content . --png # bare --png: .himi/shots/home.png
|
|
40
|
+
> himi render home --content . --scheme dark --png .himi/shots/home-dark.png
|
|
41
41
|
> himi ux record --content . --platform ios --status unavailable --reason "no simulator off-repo"
|
|
42
42
|
> himi ux record --content . --platform android --status unavailable --reason "no emulator off-repo"
|
|
43
43
|
> himi ux audit --content . --strict
|
|
@@ -589,16 +589,13 @@ You get a **new** visitor, who cannot see the old one's records, when:
|
|
|
589
589
|
- renewing the stored visitor fails, so the browser starts a fresh one;
|
|
590
590
|
- the app's `clientId` changes.
|
|
591
591
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
That server uses one anonymous visitor per server instance, **shared** by everyone it serves and
|
|
600
|
-
replaced when its token expires. A signed-in member is a separate identity (the member session)
|
|
601
|
-
and is not affected by any of this.
|
|
592
|
+
There is no server-side visitor to fall back to: the preview server does not call Wix as a
|
|
593
|
+
visitor at all. When the browser cannot reach Wix itself (its visitor mint or the request
|
|
594
|
+
fails), that call fails with `HTTP 502` (`webwix_direct_failed`, with `"sent":true` when the
|
|
595
|
+
request may already have reached Wix) and the next call tries again. A per-call header
|
|
596
|
+
that Wix does not accept from a browser is dropped, not a reason to go through the server, and
|
|
597
|
+
an absolute URL is refused. A signed-in member is a separate identity (the member session,
|
|
598
|
+
served by the preview server) and is not affected by any of this.
|
|
602
599
|
|
|
603
600
|
So when a visitor's own record vanishes from a preview, first check whether the browser, the
|
|
604
601
|
origin or the site data changed. A record that has to outlive those belongs to a signed-in
|
package/guides/wix-api-usage.md
CHANGED
|
@@ -7,8 +7,10 @@ endpoints without a generated client, the **raw `wixApi` chokepoint** (L0). Star
|
|
|
7
7
|
It's **platform-agnostic**: the worker JS is identical on iOS, Android, macOS, and web — each host installs the
|
|
8
8
|
same `globalThis.himalaya.wixApi.call(method, path, body)` bridge over `WixApiCapability`.
|
|
9
9
|
|
|
10
|
-
On web the host
|
|
11
|
-
|
|
10
|
+
On a published web app the host calls `www.wixapis.com` from the browser itself, as this browser's own
|
|
11
|
+
anonymous visitor or — in an owner preview — with the business's signed instance; only a signed-in member's
|
|
12
|
+
calls go through the same-origin `/v1/__webwix/call` bridge, which attaches the member credential. The local
|
|
13
|
+
development bridge serves every call (visitor, member and owner). Keep `path`
|
|
12
14
|
**relative** — it resolves against `https://www.wixapis.com` on every surface. The published web app's bridge
|
|
13
15
|
refuses every absolute URL (an account host such as `users.wix.com` is reachable natively, not on web). The
|
|
14
16
|
local development bridge is looser on its visitor/member path: it accepts an absolute `https:` URL on a host
|
|
@@ -71,7 +71,8 @@ and required-auth state in the Mac app as well.
|
|
|
71
71
|
Web has **no native provider/gate** — member login is server-side, and **only a local dev server serves it**.
|
|
72
72
|
Register a `signIn` action that dispatches `<ns>.wix.signInDirect`; the web host POSTs that to the
|
|
73
73
|
`__webwix` bridge (`/v1/__webwix/login`). Worker Wix calls flow through `onWixCall` →
|
|
74
|
-
`/v1/__webwix/call`, which attaches the member
|
|
74
|
+
`/v1/__webwix/call`, which attaches the member token (a published web app's visitor and owner calls go
|
|
75
|
+
browser-direct to wixapis instead). See `core/web-host/src/App.tsx`. There is no
|
|
75
76
|
`AuthGateView` on web; gate screens with your own UI dispatching the `signIn` action.
|
|
76
77
|
|
|
77
78
|
> **A hosted web preview is visitor-only.** `login`, `register` and `logout` exist only in front of a local
|
|
@@ -24,10 +24,15 @@ L2 Cross-cutting facade @himalaya/wix-platform (bi · experiments · flags ·
|
|
|
24
24
|
session · auth · monitoring) — the worker-facing door. [declares wix_platform]
|
|
25
25
|
L1 Auth (boot) @himalaya/wix-auth (owner / consumer / native / api-key / app-oauth providers)
|
|
26
26
|
→ wired at APP BOOT; binds the transport's credentials.
|
|
27
|
-
L0 Transport WixApiCapability (iOS/Android class; web
|
|
27
|
+
L0 Transport WixApiCapability (iOS/Android class; web: wixCallOverBridge)
|
|
28
28
|
→ the one HTTP chokepoint; auth-bound; 401→refresh→retry. Everything Wix rides it.
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
On web, a published app's visitor and owner calls go from the browser straight to `www.wixapis.com`
|
|
32
|
+
(the browser's own anonymous visitor, or the business's signed instance from `GET /v1/owner/instance`);
|
|
33
|
+
the same-origin `__webwix` bridge serves only a signed-in member's calls and the account-level site list.
|
|
34
|
+
The local development bridge still serves every call, so its network mocks apply.
|
|
35
|
+
|
|
31
36
|
Everything above L0 ultimately calls `WixApiCapability`. Auth (L1) supplies its credentials; verticals (L3)
|
|
32
37
|
and the facade (L2) are the two things a worker actually imports.
|
|
33
38
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1023.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": "174b817297eb89cf9e0152be79f5ad65d6f4f153cafa9fa6516d3645"
|
|
69
69
|
}
|