@wix/himalaya-cli 0.1134.0 → 0.1136.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 CHANGED
@@ -6789,6 +6789,7 @@ function resolveTranslation(value, catalog, sourceCatalog) {
6789
6789
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
6790
6790
  var init_bindings = __esm({
6791
6791
  "../../core/ts/src/bindings.ts"() {
6792
+ "use strict";
6792
6793
  BINDING_SIGILS = ["service", "flag", "item", "t"];
6793
6794
  BINDING_SIGIL_RE = new RegExp(
6794
6795
  `^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
@@ -14208,8 +14209,8 @@ async function loadSharp() {
14208
14209
  const failures = [];
14209
14210
  let presentButBroken = false;
14210
14211
  try {
14211
- const { createRequire: createRequire3 } = await import("node:module");
14212
- cachedSharp = createRequire3(import.meta.url)("sharp");
14212
+ const { createRequire: createRequire4 } = await import("node:module");
14213
+ cachedSharp = createRequire4(import.meta.url)("sharp");
14213
14214
  lastProblem = null;
14214
14215
  return cachedSharp;
14215
14216
  } catch (e) {
@@ -31389,6 +31390,9 @@ function renderInner(node, ctx) {
31389
31390
  case "SplitView":
31390
31391
  return /* @__PURE__ */ jsx8(SplitViewNode, { props, ctx, wrap });
31391
31392
  case "Divider":
31393
+ if (ctx.rowDivider) {
31394
+ return /* @__PURE__ */ jsx8("div", { style: { width: 1, alignSelf: "stretch", flexShrink: 0, margin: "4px 0", background: C("border", "#e6edf3"), ...wrap } });
31395
+ }
31392
31396
  return /* @__PURE__ */ jsx8("div", { style: { height: 1, alignSelf: "stretch", width: "100%", background: C("border", "#e6edf3"), ...wrap } });
31393
31397
  case "Spacer": {
31394
31398
  if (props.collapseWhenEmpty === true) {
@@ -31797,7 +31801,7 @@ function renderInner(node, ctx) {
31797
31801
  }
31798
31802
  }
31799
31803
  }
31800
- var useIsomorphicLayoutEffect, ResponsiveGrid, SPLIT_EXPANDED_MIN_WIDTH, SPLIT_COLUMN_GAP, ResponsiveSplitView, ABSOLUTE_URL_RE, appearKeyframesInjected, pressStylesInjected, FOCUS_RING_CSS, focusStylesInjected, HLIST_CHILD_CSS, hListStylesInjected, listAnimateRuntimeInstalled, transitionKeyframesInjected, countUpRuntimeInjected, MEASURE_PREFIX, measureRuntimeInjected, revealRuntimeInjected, vectorAnimStylesInjected, scrollProgressRuntimeInjected, confettiRuntimeInjected, lottieRuntimeInjected, riveRuntimeInjected, gradientKeyframesInjected, parallaxRuntimeInjected, spinKeyframesInjected, marqueeKeyframesInjected, alignItems, zJustifyItems, hAlignItems, bodyFont, kids2, rowKids, staggeredKids, asTextL10n, asBoundText, useBoundString, InterpolatedTextView, txt, selectFragmentCase, fragmentValueHash, fragmentSlotContent, FragmentSlotNode, RichContentEditorNode, WorkerSubtreeNode, RichContentNode, resolveItemDeep2, resolveSwipeActions, resolveModBindings, resolvedComponentId, directServiceBindings, ReactiveDirectScalars, HiddenGate, KEYBOARD_INPUT_MODES, AUTOCAPITALIZE_VALUES, ENTER_KEY_HINTS, AUTOGROW_CEILING, EditableField, MenuNode, LoadMoreSentinel, FieldErrorNode, FormView, MediaSlotProvider, MEDIA_ITEM_BINDABLE, MediaPlayerView, VideoNode, PdfViewNode, WebViewNode, InteractivePicker, InteractiveDatePicker, InteractiveSearchField, InteractiveStepper, SYNTHETIC_TAP_ROLE, SYNTHETIC_TABINDEX, INFERRED_DECORATIVE, SELF_RENDERED_LEAF, ROLE_WHEN_NAMED, AUTHORED_ROLE, PENDING_RANGE, RANGE_ROLES, A11Y_PART, NODE_A11Y_ATTRS, INTERACTIVE_ROLES, ALWAYS_INTERACTIVE_COMPONENT_TYPES, controlNode, ToggleNode, SliderNode, SelectionControlNode, TabsNode, SegmentedControlNode, DisclosureGroupNode, GridNode, SplitViewNode, TextNode, ImageNode, ANIMATION_LEAF, LottieNode, RiveNode, AvatarNode, IconNode, ButtonNode, OtpInputNode, ProgressBarNode, ActivityIndicatorNode, NATIVELY_TABBABLE, BottomSheetNode, PRESENTED_OVERLAY_ATTR, PopoverNode, ChipsInputNode, ChipNode, TableNode, GaugeRing, GaugeNode, RATING_METRICS, ratingMetrics, RatingNode, AvatarStripNode, CHART_STROKE_CYCLE, chartStrokeAt, chartBarCenter, ChartNode, donutSliceOpacity, DonutChartNode, LegendMarker, LegendNode, cellAlign, DataTableNode, voiceNoteState, voiceNoteDelivery, VOICE_NOTE_DEFAULT_BARS, clampBarCount, VoiceNoteNode, TimelineNode, WheelPickerNode, ColorPickerNode, FilterChipsNode, TILE_PX, MAX_MERCATOR_LAT, DEFAULT_TILE_MAX_ZOOM, CIRCLE_SEGMENTS, METRES_PER_DEG_LAT, MARKER_HIT_RADIUS, SHAPE_HIT_RADIUS, MapNode, CollapsingHeaderNode, UnsupportedOnSurface, CALENDAR_DATE_ONLY_RE, CAL_DEFAULT_MAX_DOTS, CAL_AGENDA_INITIAL_DAYS, CAL_AGENDA_PAGE_DAYS, CAL_DEFAULT_SNAP_MINUTES, CAL_UNASSIGNED_LANE_ID, MonthCalendarNode, TimeGridNode, AgendaNode, ARIA_ROLE, initialFocusScheduled, PRESENTED_SCOPE, focusRoots, A11yHintDescription, WebRenderer;
31804
+ var useIsomorphicLayoutEffect, ResponsiveGrid, SPLIT_EXPANDED_MIN_WIDTH, SPLIT_COLUMN_GAP, ResponsiveSplitView, ABSOLUTE_URL_RE, appearKeyframesInjected, pressStylesInjected, FOCUS_RING_CSS, focusStylesInjected, HLIST_CHILD_CSS, hListStylesInjected, listAnimateRuntimeInstalled, transitionKeyframesInjected, countUpRuntimeInjected, MEASURE_PREFIX, measureRuntimeInjected, revealRuntimeInjected, vectorAnimStylesInjected, scrollProgressRuntimeInjected, confettiRuntimeInjected, lottieRuntimeInjected, riveRuntimeInjected, gradientKeyframesInjected, parallaxRuntimeInjected, spinKeyframesInjected, marqueeKeyframesInjected, alignItems, zJustifyItems, hAlignItems, bodyFont, kids2, rowKidCtx, rowKids, staggeredKids, asTextL10n, asBoundText, useBoundString, InterpolatedTextView, txt, selectFragmentCase, fragmentValueHash, fragmentSlotContent, FragmentSlotNode, RichContentEditorNode, WorkerSubtreeNode, RichContentNode, resolveItemDeep2, resolveSwipeActions, resolveModBindings, resolvedComponentId, directServiceBindings, ReactiveDirectScalars, HiddenGate, KEYBOARD_INPUT_MODES, AUTOCAPITALIZE_VALUES, ENTER_KEY_HINTS, AUTOGROW_CEILING, EditableField, MenuNode, LoadMoreSentinel, FieldErrorNode, FormView, MediaSlotProvider, MEDIA_ITEM_BINDABLE, MediaPlayerView, VideoNode, PdfViewNode, WebViewNode, InteractivePicker, InteractiveDatePicker, InteractiveSearchField, InteractiveStepper, SYNTHETIC_TAP_ROLE, SYNTHETIC_TABINDEX, INFERRED_DECORATIVE, SELF_RENDERED_LEAF, ROLE_WHEN_NAMED, AUTHORED_ROLE, PENDING_RANGE, RANGE_ROLES, A11Y_PART, NODE_A11Y_ATTRS, INTERACTIVE_ROLES, ALWAYS_INTERACTIVE_COMPONENT_TYPES, controlNode, ToggleNode, SliderNode, SelectionControlNode, TabsNode, SegmentedControlNode, DisclosureGroupNode, GridNode, SplitViewNode, TextNode, ImageNode, ANIMATION_LEAF, LottieNode, RiveNode, AvatarNode, IconNode, ButtonNode, OtpInputNode, ProgressBarNode, ActivityIndicatorNode, NATIVELY_TABBABLE, BottomSheetNode, PRESENTED_OVERLAY_ATTR, PopoverNode, ChipsInputNode, ChipNode, TableNode, GaugeRing, GaugeNode, RATING_METRICS, ratingMetrics, RatingNode, AvatarStripNode, CHART_STROKE_CYCLE, chartStrokeAt, chartBarCenter, ChartNode, donutSliceOpacity, DonutChartNode, LegendMarker, LegendNode, cellAlign, DataTableNode, voiceNoteState, voiceNoteDelivery, VOICE_NOTE_DEFAULT_BARS, clampBarCount, VoiceNoteNode, TimelineNode, WheelPickerNode, ColorPickerNode, FilterChipsNode, TILE_PX, MAX_MERCATOR_LAT, DEFAULT_TILE_MAX_ZOOM, CIRCLE_SEGMENTS, METRES_PER_DEG_LAT, MARKER_HIT_RADIUS, SHAPE_HIT_RADIUS, MapNode, CollapsingHeaderNode, UnsupportedOnSurface, CALENDAR_DATE_ONLY_RE, CAL_DEFAULT_MAX_DOTS, CAL_AGENDA_INITIAL_DAYS, CAL_AGENDA_PAGE_DAYS, CAL_DEFAULT_SNAP_MINUTES, CAL_UNASSIGNED_LANE_ID, MonthCalendarNode, TimeGridNode, AgendaNode, ARIA_ROLE, initialFocusScheduled, PRESENTED_SCOPE, focusRoots, A11yHintDescription, WebRenderer;
31801
31805
  var init_renderNode = __esm({
31802
31806
  "../../core/web-renderer/src/renderNode.tsx"() {
31803
31807
  "use strict";
@@ -31942,7 +31946,8 @@ var init_renderNode = __esm({
31942
31946
  };
31943
31947
  bodyFont = (ctx) => fontStack(ctx.font("body").family, ctx.idiom);
31944
31948
  kids2 = (arr, ctx) => Array.isArray(arr) ? arr.map((n, i) => /* @__PURE__ */ jsx8(React7.Fragment, { children: renderNode(n, ctx) }, n?.id ?? i)) : null;
31945
- rowKids = (arr, ctx) => Array.isArray(arr) ? arr.map((n, i) => /* @__PURE__ */ jsx8(React7.Fragment, { children: renderNode(n, wantsRowStretch(n) ? { ...ctx, rowStretch: true } : ctx) }, n?.id ?? i)) : null;
31949
+ rowKidCtx = (n, ctx) => wantsRowStretch(n) ? { ...ctx, rowStretch: true } : normalize(n).type === "Divider" ? { ...ctx, rowDivider: true } : ctx;
31950
+ rowKids = (arr, ctx) => Array.isArray(arr) ? arr.map((n, i) => /* @__PURE__ */ jsx8(React7.Fragment, { children: renderNode(n, rowKidCtx(n, ctx)) }, n?.id ?? i)) : null;
31946
31951
  staggeredKids = (arr, ctx, staggerMs) => Array.isArray(arr) ? arr.map((n, i) => {
31947
31952
  const el = renderNode(n, ctx);
31948
31953
  if (!React7.isValidElement(el)) return /* @__PURE__ */ jsx8(React7.Fragment, { children: el }, n?.id ?? i);
@@ -48372,6 +48377,7 @@ __export(app_source_exports, {
48372
48377
  newestSourceChangeMs: () => newestSourceChangeMs,
48373
48378
  readAppSource: () => readAppSource,
48374
48379
  removeStaleSource: () => removeStaleSource,
48380
+ renderInputRootDirs: () => renderInputRootDirs,
48375
48381
  secretFindings: () => secretFindings,
48376
48382
  sourceFingerprint: () => sourceFingerprint,
48377
48383
  staleSourcePaths: () => staleSourcePaths,
@@ -48382,6 +48388,7 @@ __export(app_source_exports, {
48382
48388
  import { createHash as createHash24 } from "node:crypto";
48383
48389
  import { readdirSync as readdirSync25, lstatSync as lstatSync9, readFileSync as readFileSync50, mkdirSync as mkdirSync22, writeFileSync as writeFileSync17, existsSync as existsSync46, rmSync as rmSync10, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3 } from "node:fs";
48384
48390
  import { join as join44, resolve as resolve40, sep as sep11 } from "node:path";
48391
+ import { createRequire as createRequire3 } from "node:module";
48385
48392
  import { deflateRawSync as deflateRawSync2 } from "node:zlib";
48386
48393
  function walkSourceTree(dir, file, skip, enter = () => {
48387
48394
  }) {
@@ -48420,12 +48427,56 @@ function collectAppSource(dir) {
48420
48427
  skipped.sort();
48421
48428
  return { entries, skipped };
48422
48429
  }
48430
+ function renderInputRootDirs(dir) {
48431
+ const entries = [];
48432
+ walkSourceTree(
48433
+ dir,
48434
+ (archivePath, hostRel) => {
48435
+ const segs = archivePath.split("/");
48436
+ if (!CODE_FILE.test(archivePath) || /\.d\.[cm]?ts$/i.test(archivePath)) return;
48437
+ if (segs.length === 1 || segs[0] === "dev" || segs[0] === "tier5-src") entries.push(join44(dir, hostRel));
48438
+ },
48439
+ () => {
48440
+ }
48441
+ );
48442
+ const roots = new Set(RENDER_ROOT_DIRS);
48443
+ if (entries.length === 0) return roots;
48444
+ try {
48445
+ const esbuild = createRequire3(import.meta.url)("esbuild");
48446
+ const empty = Object.fromEntries(
48447
+ [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".ttf", ".otf", ".woff", ".woff2", ".css", ".txt", ".md", ".html", ".mp3", ".mp4"].map((e) => [e, "empty"])
48448
+ );
48449
+ const result = esbuild.buildSync({
48450
+ entryPoints: entries,
48451
+ absWorkingDir: dir,
48452
+ bundle: true,
48453
+ write: false,
48454
+ metafile: true,
48455
+ packages: "external",
48456
+ platform: "neutral",
48457
+ format: "esm",
48458
+ outdir: join44(dir, ".himi", "render-inputs-out"),
48459
+ loader: empty,
48460
+ logLevel: "silent"
48461
+ });
48462
+ for (const input of Object.keys(result.metafile?.inputs ?? {})) {
48463
+ const segs = input.split("/");
48464
+ if (segs.length > 1 && !input.startsWith("..") && !input.includes(":")) roots.add(segs[0]);
48465
+ }
48466
+ return roots;
48467
+ } catch {
48468
+ return void 0;
48469
+ }
48470
+ }
48423
48471
  function newestSourceChangeMs(dir) {
48472
+ const roots = renderInputRootDirs(dir);
48473
+ const inRead = (hostRel) => !roots || roots.has(hostRel.split(sep11)[0]);
48474
+ const counts = (hostRel) => !hostRel.includes(sep11) || inRead(hostRel);
48424
48475
  let newest = 0;
48425
48476
  walkSourceTree(
48426
48477
  dir,
48427
48478
  (archivePath, hostRel) => {
48428
- if (/\.md$/i.test(archivePath)) return;
48479
+ if (/\.md$/i.test(archivePath) || !counts(hostRel)) return;
48429
48480
  const m = lstatSync9(join44(dir, hostRel)).mtimeMs;
48430
48481
  if (m > newest) newest = m;
48431
48482
  },
@@ -48435,8 +48486,9 @@ function newestSourceChangeMs(dir) {
48435
48486
  // mtime moves. So every source directory counts too — else removing a screen after the shot
48436
48487
  // was taken would date that shot as current. Not the package ROOT: any file created there
48437
48488
  // (a plan, a build output) moves its mtime, and the screens live below it (dev/, tier5-src/).
48489
+ // Nor a root directory the render does not read: creating a file in `_qa/` moves `_qa/`.
48438
48490
  (hostRel) => {
48439
- if (hostRel === "") return;
48491
+ if (hostRel === "" || !inRead(hostRel)) return;
48440
48492
  const m = lstatSync9(join44(dir, hostRel)).mtimeMs;
48441
48493
  if (m > newest) newest = m;
48442
48494
  }
@@ -48597,7 +48649,7 @@ function pruneEmptyDirs(root, rel, needed, removed) {
48597
48649
  }
48598
48650
  return empty;
48599
48651
  }
48600
- var MAX_APP_SOURCE_BYTES, SECRET_PATTERNS;
48652
+ var MAX_APP_SOURCE_BYTES, SECRET_PATTERNS, RENDER_ROOT_DIRS, CODE_FILE;
48601
48653
  var init_app_source = __esm({
48602
48654
  "src/app-source.ts"() {
48603
48655
  init_source_deny();
@@ -48638,6 +48690,8 @@ var init_app_source = __esm({
48638
48690
  { re: /\bgh[pousr]_[A-Za-z0-9]{20,}\b/, reason: "a GitHub token" },
48639
48691
  { re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/, reason: "a Slack token" }
48640
48692
  ];
48693
+ RENDER_ROOT_DIRS = /* @__PURE__ */ new Set(["dev", "tier5-src", "assets", "seed", "strings", "l10n", "fonts", "images", "data"]);
48694
+ CODE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/i;
48641
48695
  }
48642
48696
  });
48643
48697
 
@@ -49910,7 +49964,8 @@ function watchReminder(dir, opts = {}) {
49910
49964
  return `the live preview is running at ${live2.url} (pid ${live2.pid}${live2.by === "init" ? ", started by himi init" : ""}) \u2014 it repaints on every save, and the build board is ${live2.board}; give the owner that link to click around while you work, and do not start another`;
49911
49965
  }
49912
49966
  const content = opts.content ?? displayPath(dir);
49913
- return `start the live preview and leave it running: \`himi run --watch --content ${shellQuote(content)} &\` (the consent preflight first, if a worker or config calls Wix at boot) \u2014 it repaints on every save; the build board is ${shellQuote(join48(content, ".himi", "studio", "index.html"))}, or /__himi/studio/ on the preview`;
49967
+ const remote = readInitSource(dir)?.source === "remote" ? "init did not start it because this template came off the serve plane \u2014 once you have read its code, " : "";
49968
+ return `${remote}start the live preview and leave it running: \`himi run --watch --content ${shellQuote(content)} &\` (the consent preflight first, if a worker or config calls Wix at boot) \u2014 it repaints on every save; the build board is ${shellQuote(join48(content, ".himi", "studio", "index.html"))}, or /__himi/studio/ on the preview`;
49914
49969
  } catch {
49915
49970
  return void 0;
49916
49971
  }
@@ -58106,21 +58161,21 @@ var init_preview_assets_generated = __esm({
58106
58161
  "../../packages/serve/src/preview-assets.generated.ts"() {
58107
58162
  init_preview_assets_format();
58108
58163
  PREVIEW_ASSET_MANIFEST = [
58109
- { key: "assets/index-B-1B15q7.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1104811, sha256: "bd81d6b74d2a1b72072c1c20303e225dbc620861e50f66e5b7a90a6d64c66ed2" },
58110
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1104819, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
58111
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1109429, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
58112
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1598013, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
58113
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1688937, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
58114
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1779921, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
58115
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1870901, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
58116
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1961773, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
58117
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 2052753, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
58118
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2161389, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
58119
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2270473, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
58120
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2379521, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
58121
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2487221, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
58122
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2575329, length: 4947, sha256: "e6135ff25467aef302a26590c7f740191c93ee3acc0abcfbd4f218780559ff9f" },
58123
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2580276, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
58164
+ { key: "assets/index-YVoPQOFl.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1105012, sha256: "40b489aee006a784e93cf93d75b92cdec7f4b90e185f72e3c31b482989e6b6b8" },
58165
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1105020, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
58166
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1109630, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
58167
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1598214, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
58168
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1689138, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
58169
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1780122, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
58170
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1871102, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
58171
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1961974, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
58172
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 2052954, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
58173
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2161590, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
58174
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2270674, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
58175
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2379722, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
58176
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2487422, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
58177
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2575530, length: 4947, sha256: "76370be356cffe5550b7214447ae46166c1854a8a617da0b2be8d562e52e7115" },
58178
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2580477, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
58124
58179
  ];
58125
58180
  }
58126
58181
  });
@@ -72405,7 +72460,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
72405
72460
  "no-first-look",
72406
72461
  "no-image",
72407
72462
  "no-watch",
72408
- "mocks"
72463
+ "mocks",
72464
+ "no-mocks"
72409
72465
  ]);
72410
72466
  var SHELL_ENUM = SHELL_NAMES2.join("|");
72411
72467
  var SWITCH_FLAGS = new Set([...BOOLEAN_FLAGS].filter((name) => name !== "deep"));
@@ -72613,7 +72669,7 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
72613
72669
  (--ai writes rows for you to review first)
72614
72670
  himi secrets set|list|rotate --app <app> --name <name> [--environment preview|production]
72615
72671
  encrypted secret metadata; set/rotate read the value from stdin
72616
- himi render <screenId> [--content <dir>] [--scheme dark] [--png [<file>]] [--tree] [--dump-state] [--static] [--live] [--mocks] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--load-budget <ms>]
72672
+ himi render <screenId> [--content <dir>] [--scheme dark] [--png [<file>]] [--tree] [--dump-state] [--static] [--live] [--mocks | --no-mocks] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--load-budget <ms>]
72617
72673
  [--remote --app <app> --release <id>]
72618
72674
  headless web render + diagnostics (no sim). Default: local content dir.
72619
72675
  A bare --png writes .himi/shots/<screenId>.png (what the build board shows).
@@ -72626,7 +72682,8 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
72626
72682
  --mocks runs load() against the app's OWN recorded data instead \u2014 dev/net-mocks.json
72627
72683
  (what \`himi test --record\` writes) and the config's cms seed rows, as \`himi test\`'s
72628
72684
  shots do; no request leaves this machine. With none, it renders the initial state.
72629
- The automatic pictures (init's first look, validate's refresh) use it;
72685
+ It is the DEFAULT whenever the app has recorded data (mocks.default in the output);
72686
+ --no-mocks renders the offline initial state instead;
72630
72687
  --tree adds stable raw structure plus a resolvedRenderTree when hydrated;
72631
72688
  --dump-state explicitly includes the full worker snapshot;
72632
72689
  --state '{"<svc>":{\u2026}}' injects worker state (render loaded/error states);
@@ -73189,7 +73246,7 @@ var COMMANDS = [
73189
73246
  { name: "ux", summary: "audit or record mobile UX evidence. `audit` checks the public-safe contract, seeded blank acceptance rows, placeholder source copy, app-specific mocks, and iOS/Android audit records. It warns while existing templates migrate; `--strict` promotes missing evidence. `record` stores checks performed on an installed iOS/Android build: `--checks` is required for passed|failed and empty for unavailable, which drove nothing and needs a `--reason` instead. `record` also STAMPS the release it audited (it resolves the id from a build, the same one a push would publish), because an unforced `himi push` of a release with screens refuses when no drive is recorded for THAT release, or when the one that is recorded says `failed` \u2014 the artifact stopped being advice nobody reads (#2955). `unavailable` still satisfies it; only `failed` refuses.", required: "audit [--content <dir>] [--strict] [--json] | record --content <dir> --platform ios|android --status passed|failed|unavailable [--checks back,keyboard,...] [--reason <text>]" },
73190
73247
  { name: "progress", summary: 'the build JOURNAL an agent keeps for the owner while it works. `himi progress "<what changed>" --step <1-8|name> --image <png>` appends one postcard to .himi/progress.jsonl \u2014 the step is one of the customer journey\'s eight (discover, classify, plan, implement, brand, validate, review, download) \u2014 and COPIES each image, content-addressed, into .himi/history/, because .himi/shots/ is overwritten by every render and cleared by every full `himi test`, so a path into it would later show a different picture than the one the owner was sent. --image repeats. With NO --image, the home screen\'s current shot is attached automatically, only when it is no older than the last source change and not the picture the latest journaled postcard already shows \u2014 and never a shot that landed on loading placeholders, an empty or an error state (.himi/shot-states.json): then the newest home picture of the current source that shows content goes on instead (its .himi/history copy, `imageNote` saying so), or none \u2014 except on the HANDOVER card (`--step review`), where a repeat is the point and it attaches anyway; that card\'s output also names the making-of (`makingOf`, .himi/making-of.html, rebuilt when missing or older than the journal so it ends on this card) and its `next` says to hand it to the owner beside the preview link; `imageAttached` in the output says whether the postcard carries a picture (with `imageNote` saying why not, or which picture went on instead), and `--no-image` sends a text-only postcard. --notify also raises a best-effort desktop notification for an owner who stepped away. A bare `himi progress` prints the journal with the current step and elapsed time \u2014 also how an agent recovers its place after its context was compacted. APPROVALS: `--approve <gate> "<what>"` records one the customer just gave (gates: site-create, connect, private-data, draft, promote, push-signing, qa-build) in .himi/approvals.jsonl, and `--done <gate>` / `--drop <gate>` closes it \u2014 an approval is owed until then, and the chat that held it does not survive a new session. `--brief` prints plain text for a session start: the current step, the last postcard, every open approval, and whether this app\'s agent files are stale \u2014 the SessionStart hook `himi agent-init` installs runs exactly this. The CLI also journals milestones by itself \u2014 the first PNG render of the home screen (for an app a current `himi init` started: once as the template\'s "Starting point" while the source is what init wrote, then once as "First look at your app" after it diverges, Markdown edits not counting; once per app otherwise), a `himi test` crawl whose outcome changed since the last one (its counts or its home picture; an identical re-run writes none, and a repeat names what changed: "The home screen changed since the last crawl." or the changed counts, was \u2192 now), every draft push (with the home picture only when that shot is newer than the last source edit \u2014 otherwise the postcard says why and names `himi render <home> --png`), and ONE rule for a long silence: a successful home `render --png` more than ten minutes after the newest postcard of any kind, with a picture the latest postcard does not show, journals "Still building: here\'s the home screen now." with it (never twice within the window). When a crawl\'s or a draft\'s home picture is the first of an app whose source diverged, "First look at your app" is journaled first, from that shot, once. All are marked `"by": "himi"`; they never move the journey step, carry no link or credential, and step aside (folded out of every reader) when the agent journals the same step or picture within five minutes. Local only: nothing is sent anywhere.', required: '["<message>" [--step <1-8|name>] [--image <png>]\u2026 [--no-image] [--notify]] [--content <dir>] | --approve|--done|--drop <gate> ["<note>"] | --brief' },
73191
73248
  { name: "studio", summary: "open the BUILD BOARD \u2014 one local page the owner can leave open while an agent builds their app, which fills itself in: every screen as it renders (NEW/CHANGED badges, and each earlier version kept for a filmstrip), the journey stepper and the latest postcard from `himi progress`, the validate/test/UX/draft checks as they run, the site next to the app's home screen with its palette, and the navigation the test crawl followed. Writes .himi/studio/index.html and opens it; the page re-reads its state every two seconds, so nothing needs to be served. OPT-IN BY EXISTENCE: until this command (or `himi init`, which makes the board for every app it creates, without opening a browser) has run, no other command touches .himi/studio/ \u2014 after it, render/test/validate/push/icon/site/ux/progress each refresh the board as they finish (best-effort; never fails a command). Every image is a content-addressed copy in .himi/history/, never a live path into .himi/shots/. Local only, no secrets: it never reads a preview token, handover code or control token. --no-open prints the path instead (SSH, CI). `himi studio reel` writes the MAKING-OF: one self-contained HTML file (every image inlined, nothing fetched) with a flipbook of the home screen version by version, captioned by the postcard in effect at the time, the final screens, and every postcard \u2014 sized to stay attachable, thinning frames first. Defaults to .himi/making-of.html (never archived by push), which every successful `himi push --draft` also rebuilds and names as `reel` in its output; --out to put it elsewhere. The output names that file as the one to hand the owner (`handover: {file, hint}` in JSON; two plain lines at a terminal) \u2014 beside the preview link, never instead of it.", required: "[--content <dir>] [--no-open] | reel [--out <file>] [--no-open]" },
73192
- { name: "render", summary: "headless-render a screen with the web renderer: render-tree + diagnostics, optional --png [<file>] (bare: .himi/shots/<id>.png, what the build board shows), --scheme dark, --tree. Local by default (content dir). Pass --remote --app --release to hit GET /v1/preview/\u2026/render on the serve plane after a push. Boots the worker so $service bindings resolve; --static raw bindings; --live real Wix fetches; --mocks runs load() against the app's own recorded data (dev/net-mocks.json from `himi test --record`, then the config's cms seed rows \u2014 hermetic, never a request; with neither it paints the initial state, and `mocks: {used: false}` says why), which is what the automatic pictures use, so they show the app's content instead of its offline placeholders; --state injects worker state; --input supplies pre-boot navigation params; --width/--device for PNG viewport; --rtl for dir=rtl + $env:isRTL.", required: "<screenId> [--content <dir>] [--scheme dark] [--png [<file>]] [--tree] [--static] [--live] [--mocks] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--remote --app --release]" },
73249
+ { name: "render", summary: "headless-render a screen with the web renderer: render-tree + diagnostics, optional --png [<file>] (bare: .himi/shots/<id>.png, what the build board shows), --scheme dark, --tree. Local by default (content dir). Pass --remote --app --release to hit GET /v1/preview/\u2026/render on the serve plane after a push. Boots the worker so $service bindings resolve; --static raw bindings; --live real Wix fetches; --mocks (the DEFAULT whenever the app has recorded data; --no-mocks for the offline initial state) runs load() against the app's own recorded data (dev/net-mocks.json from `himi test --record`, then the config's cms seed rows \u2014 hermetic, never a request; with neither it paints the initial state, and `mocks: {used: false}` says why), which is what the automatic pictures use, so they show the app's content instead of its offline placeholders; --state injects worker state; --input supplies pre-boot navigation params; --width/--device for PNG viewport; --rtl for dir=rtl + $env:isRTL.", required: "<screenId> [--content <dir>] [--scheme dark] [--png [<file>]] [--tree] [--static] [--live] [--mocks | --no-mocks] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--remote --app --release]" },
73193
73250
  { name: "cms", summary: "inspect, provision, and seed the app's CMS (Wix Data) collections, declared as a `cms` block in config.ts. `query` is READ-ONLY \u2014 by default it fetches ONLY the physical ids `config.ts` declares (item counts + schema drift) and reports `siteScanned: false`; it never lists the site, so an UNDECLARED `himi_<app>_` collection this app owns is not reached without `--all`. `--all` SCANS the site \u2014 every collection it lists is labeled `owned`/`adopted`/`foreign`, and it describes every foreign one \u2014 id, non-system field keys + types, and the site's read role \u2014 so an app with no `cms` block at all can DISCOVER a collection to adopt (`--all` is the one case where a missing `cms` block is not 'nothing to do'). Foreign collections are described up to a cap and are never item-counted (`itemCount: null`), because a row count is one query each; `foreign.count` is how many the scan LISTED \u2014 it follows the list endpoint's pages, and `listingIncomplete: true` says when the walk stopped short of the end, with `unlistedCollections` counting what it left out when Wix gave a paging total \u2014 and `foreign.truncated` says how many of those went undescribed, each still NAMED in `foreign.others` (id + displayName), so a collection the site already has is never hidden behind the cap. The scan is OWNER-scoped \u2014 it needs --site <msid> (or WIX_SITE_ID) plus owner auth via `wix token`; a visitor token can read a public collection's ITEMS but can never enumerate collections, so there is no tokenless discovery path. `create` reconciles declared collections additively and idempotently, and REFUSES any id outside the app's `himi_<app>_` namespace, so it is safe against a production site. It reports declared-vs-site `permissionDrift` but never writes permissions unless asked with --apply-permissions, so a field reconcile can't silently revert access control changed in the dashboard. `seed` inserts the git-tracked `seed:` rows into EMPTY owned collections (--force to add to a non-empty one; --ai writes placeholder rows plus a refinement prompt for you to review). Adopted collections (declared by `wixId`) are read-only and never created, updated, or seeded. Site from --site <msid> or WIX_SITE_ID; owner auth via `wix token`.", required: "query|create|seed [--site <msid>] [--content <dir>] [--all] [--dry-run] [--force] [--allow-destructive] [--apply-permissions] [--only <id>] [--ai] [--count <n>]" },
73194
73251
  { name: "data", summary: "read-only plan or explicitly provision/seed declared KV and Wix Data resources. Preview and production are separate scopes. KV administration uses the operator data plane from --data-url/HIMI_DATA_ADMIN_URL and HIMI_DATA_ADMIN_TOKEN; draft push never calls it.", required: "plan|create|seed [--site <msid>] [--environment preview|production] [--content <dir>] [--data-url <url>] [--dry-run] [--force]" },
73195
73252
  { name: "icons", summary: "list the valid Icon.name vocabulary (an unknown name renders a fallback glyph; validate warns on unknown names) (offline)", required: "[--json]" },
@@ -74040,7 +74097,7 @@ function serveUnreachableError(target, base, cause, plane = "serve") {
74040
74097
  function loopbackTargetWarning(base, explicitTarget) {
74041
74098
  if (explicitTarget) return null;
74042
74099
  if (!isLoopbackBase(base)) return null;
74043
- return `Published to the LOCAL dev serve (${base}). The default target is a local dev plane that only exists in a monorepo checkout, so this release is NOT on the bo.wix.com serve and its preview will 404. Off-repo, publish with \`--target bo\` (after \`himi login --dpx --target bo\`), or set HIMI_SERVE_TARGET=bo.`;
74100
+ return `Published to the LOCAL dev serve (${base}). The default target is a local dev plane that only exists in a monorepo checkout, so this release is NOT on the bo.wix.com serve and its preview will 404. Off-repo, publish with \`--target wix\` (after \`himi login --browser --target wix\`) \u2014 Wix employees can use \`--target bo\` (after \`himi login --dpx --target bo\`) \u2014 or set HIMI_SERVE_TARGET=wix.`;
74044
74101
  }
74045
74102
  function isLoopbackBase(base) {
74046
74103
  return /^https?:\/\/(127\.0\.0\.1|localhost|0\.0\.0\.0|\[::1\])(:|\/|$)/i.test(base);
@@ -75779,7 +75836,8 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
75779
75836
  loader: loaderArgs(process.execArgv)
75780
75837
  });
75781
75838
  const { postcard: picturePostcard, ...picture } = homePicture;
75782
- print({ ...finalReport, homePicture: picture, ...picturePostcard ? { postcard: picturePostcard } : {} });
75839
+ const validateReminder = process.env.HIMI_AUTO_RENDER ? void 0 : (await Promise.resolve().then(() => (init_milestones(), milestones_exports))).watchReminder(dir, { content: str9(flags.content) });
75840
+ print({ ...finalReport, homePicture: picture, ...picturePostcard ? { postcard: picturePostcard } : {}, ...validateReminder ? { reminder: validateReminder } : {} });
75783
75841
  return hasErrors ? 1 : 0;
75784
75842
  }
75785
75843
  case "functions": {
@@ -76107,7 +76165,8 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
76107
76165
  const { count: count2 } = summarizeProgress2(readProgress2(dir).entries);
76108
76166
  const imageAttached = (entry.images?.length ?? 0) > 0;
76109
76167
  const handed = handover && isContentPackage ? (await Promise.resolve().then(() => (init_milestones(), milestones_exports))).afterHandoverPostcard(dir, { content: str9(flags.content) }) : null;
76110
- print({ ok: true, entry, journal: journal2, count: count2, imageAttached, ...imageNote ? { imageNote } : {}, ...notified === void 0 ? {} : { notified }, ...handed ? { makingOf: handed.makingOf, next: handed.next } : {}, ...hint });
76168
+ const progressReminder = isContentPackage ? (await Promise.resolve().then(() => (init_milestones(), milestones_exports))).watchReminder(dir, { content: str9(flags.content) }) : void 0;
76169
+ print({ ok: true, entry, journal: journal2, count: count2, imageAttached, ...imageNote ? { imageNote } : {}, ...notified === void 0 ? {} : { notified }, ...handed ? { makingOf: handed.makingOf, next: handed.next } : {}, ...hint, ...progressReminder ? { reminder: progressReminder } : {} });
76111
76170
  return 0;
76112
76171
  } catch (e) {
76113
76172
  if (e instanceof ProgressError2) {
@@ -76633,6 +76692,10 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
76633
76692
  io.error(JSON.stringify({ error: `--mocks and ${live2 ? "--live" : "--static"} are mutually exclusive: --mocks loads the app's recorded data, ${live2 ? "--live real Wix data" : "--static no data at all"}` }));
76634
76693
  return 2;
76635
76694
  }
76695
+ if (flags.mocks === true && flags["no-mocks"] === true) {
76696
+ io.error(JSON.stringify({ error: "--mocks and --no-mocks are mutually exclusive" }));
76697
+ return 2;
76698
+ }
76636
76699
  let injectedState;
76637
76700
  if (flags.state !== void 0) {
76638
76701
  const stateArg = str9(flags.state);
@@ -76705,7 +76768,8 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
76705
76768
  }
76706
76769
  let mocks;
76707
76770
  let mocksNote;
76708
- if (flags.mocks === true) {
76771
+ const mocksByDefault = flags.mocks !== true && flags["no-mocks"] !== true && !live2 && flags.static !== true && injectedState === void 0;
76772
+ if (flags.mocks === true || mocksByDefault) {
76709
76773
  let rules = null;
76710
76774
  let bad2;
76711
76775
  try {
@@ -76717,7 +76781,7 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
76717
76781
  const seeded = cmsSeed && Object.keys(cmsSeed).length ? cmsSeed : null;
76718
76782
  const picked = renderMocksFrom(rules, bad2, seeded);
76719
76783
  mocks = picked.mocks;
76720
- mocksNote = picked.note;
76784
+ if (!mocksByDefault || bad2) mocksNote = picked.note;
76721
76785
  }
76722
76786
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
76723
76787
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
@@ -76759,7 +76823,7 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
76759
76823
  hydrated: live2 ? "live" : hydrate,
76760
76824
  dataSource: renderDataSource({ live: live2, hydrate, liveCalls: r.liveCalls, liveReturned: r.liveReturned, mocked: !!r.mockCalls }),
76761
76825
  // What --mocks loaded from, how many requests the load made, and the ones no layer held.
76762
- ...r.mockCalls && mocks ? { mocks: { used: true, from: mocks.from, calls: r.mockCalls.length, ...r.mockUnserved?.length ? { unserved: r.mockUnserved } : {}, ...mocks.ignored ? { ignored: mocks.ignored } : {} } } : mocksNote ? { mocks: mocksNote } : mocks ? { mocks: { used: false, reason: r.mockBootFailed ? "the screen's worker failed to boot against the recorded data, so its initial state was rendered" : "the screen has no worker bundle to load data into", ...mocks.ignored ? { ignored: mocks.ignored } : {} } } : {},
76826
+ ...r.mockCalls && mocks ? { mocks: { used: true, from: mocks.from, calls: r.mockCalls.length, ...r.mockUnserved?.length ? { unserved: r.mockUnserved } : {}, ...mocks.ignored ? { ignored: mocks.ignored } : {}, ...mocksByDefault ? { default: true, hint: "the app's recorded data loaded by default; --no-mocks renders the offline initial state, --live the real site" } : {} } } : mocksNote ? { mocks: mocksNote } : mocks ? { mocks: { used: false, reason: r.mockBootFailed ? "the screen's worker failed to boot against the recorded data, so its initial state was rendered" : "the screen has no worker bundle to load data into", ...mocks.ignored ? { ignored: mocks.ignored } : {} } } : {},
76763
76827
  ...live2 ? {
76764
76828
  liveCalls: r.liveCalls?.length ?? 0,
76765
76829
  // Beside the attempt count, because the two coming apart IS the signal (#1883).
Binary file
@@ -281,7 +281,7 @@ Concrete patterns lifted from real usage:
281
281
  - **Cell list** — `card([ vstack({ spacing: 0, alignment: "leading", children: [ …ListItem cells… ] }) ])`. `spacing: 0` because the cells own their own dividers/padding.
282
282
  - **Overlay** — `zstack({ alignment: "center", children: [ backdropImage(cornerRadius 12), withModifiers(label, { padding, background: { token: "accentPrimary" }, cornerRadius: 10 }) ] })`.
283
283
  - **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.
284
- - **Centered separator, not a flanked divider** — the classic "—— or ——" needs each `divider()` to take equal remaining width, and the HStack row-share only kicks in for controls with an intrinsic fill. **Never put a bare `divider()` inside an HStack**: it is a horizontal rule on every surface, and on web and Android it claims the row's full width and squeezes its siblings (a stat row's `12` wraps to `1`/`2`); only iOS draws a thin vertical line. For a stats row, separate the columns with `spacer()`s or spacing instead. Use a **centered caption** instead: `hstack({ children: [spacer(), text("or continue with", "caption", { color: "textSecondary" }), spacer()] })`, optionally over a single full-width `divider()`.
284
+ - **Centered separator, not a flanked divider** — the classic "—— or ——" needs each `divider()` to take equal remaining width, and the HStack row-share only kicks in for controls with an intrinsic fill. **Never put a bare `divider()` inside an HStack for a row that must render on Android**: iOS and web draw it as a thin vertical rule the height of the row, but Android still draws a horizontal rule that claims the row's full width and squeezes its siblings (a stat row's `12` wraps to `1`/`2`). For a stats row, separate the columns with `spacer()`s or spacing instead. Use a **centered caption** instead: `hstack({ children: [spacer(), text("or continue with", "caption", { color: "textSecondary" }), spacer()] })`, optionally over a single full-width `divider()`.
285
285
 
286
286
  > **Intrinsic-fill controls grab the row.** `Button` and `TextField` have an intrinsic FILL, so the next four idioms are about NOT letting that surprise you (they were the papercuts every off-repo app hit):
287
287
 
@@ -111,7 +111,8 @@ when that shot is newer than the last source edit, or else the reason and the `h
111
111
  whose picture is new ("Still building: here's the home screen now."). A passing `himi validate` after a source change
112
112
  refreshes the home picture itself (the same offline render, bounded to 30 seconds, never for an unread `--remote`
113
113
  template; reported as `homePicture`), and an unchanged `himi test` crawl's home shot counts too, so a long stretch of edits,
114
- validates and crawls still reaches the owner. Those automatic renders are `himi render <home> --mocks --png`: the worker's
114
+ validates and crawls still reaches the owner. Those automatic renders are `himi render <home> --mocks --png`, and so is
115
+ your own `himi render --png` whenever the app has recorded data (`--no-mocks` for the offline one): the worker's
115
116
  load runs against the app's own recorded data — `dev/net-mocks.json` (`himi test --record` writes it), then the config's
116
117
  cms seed rows — as `himi test`'s shots do, never a request, so the picture shows content; record the site's data once
117
118
  and every automatic picture after it shows the app as it works. When the home picture a postcard carries landed on an error, loading or
@@ -205,11 +205,13 @@ every write refused at the transport) — then crawls hermetically on what it fr
205
205
  every run; record against a **QA site**, never production. See
206
206
  `docs/dev-network-gateway.md` for the record→freeze machinery.
207
207
 
208
- **The pictures use it too.** `himi render <id> --png --mocks` runs the worker's load against the
209
- same recorded data — `dev/net-mocks.json`, then the config's cms seed rows, never the canned
210
- fixture and never a request — so a data-driven screen paints its content instead of its offline
211
- placeholders. The automatic pictures (`himi init`'s first look, `himi validate`'s refresh) are
212
- that render. A picture painted without the site's data — the initial state, or a load in which a
208
+ **The pictures use it too.** `himi render <id> --png` runs the worker's load against the same
209
+ recorded data whenever the app has some — `dev/net-mocks.json`, then the config's cms seed rows,
210
+ never the canned fixture and never a request — so a data-driven screen paints its content instead
211
+ of its offline placeholders. It is the default (`mocks.default` in the output); `--mocks` asks for
212
+ it explicitly, `--no-mocks` renders the offline initial state, and `--live`, `--static` and
213
+ `--state` each render the data they name instead. The automatic pictures (`himi init`'s first look,
214
+ `himi validate`'s refresh) are that render. A picture painted without the site's data — the initial state, or a load in which a
213
215
  request found no recorded data (a shot's `offline` flag in `.himi/shot-states.json`) — is
214
216
  captioned as an offline preview, never as the app failing.
215
217
 
@@ -43,7 +43,7 @@ State holds the **normalized rows** plus the display strings for every screen st
43
43
  Actions are `load` (kicked at boot) and `retry`; a `search` action filters in-worker.
44
44
 
45
45
  ```ts
46
- import { defineService } from "@himalaya/state";
46
+ import { defineService } from "@wix/himalaya/state";
47
47
 
48
48
  interface Row { // FLAT strings/booleans only — see "Normalize" below
49
49
  id: string; name: string; price: string; initial: string;
@@ -83,8 +83,8 @@ see [tier-5-add-a-screen.md](../tier-5-add-a-screen.md#cross-field-validation).
83
83
  ## 2. The worker: validate in `submit`, return the outcome as state
84
84
 
85
85
  ```ts
86
- import { defineService } from "@himalaya/state";
87
- import { createDraftPost, type WixDraftPostWrite } from "@himalaya/wix-blog";
86
+ import { defineService } from "@wix/himalaya/state";
87
+ import { createDraftPost, type WixDraftPostWrite } from "@wix/himalaya/wix-blog";
88
88
 
89
89
  interface FormState {
90
90
  title: string; body: string; // prefill values (edit mode / echo after save)
@@ -6,10 +6,10 @@ processing. A background build must declare `audio_record_background`; web and m
6
6
  foreground capture only.
7
7
 
8
8
  ```ts
9
- import { startSession, stopSession, listSegments } from "@himalaya/audio-record";
10
- import * as speech from "@himalaya/speech";
11
- import * as diarization from "@himalaya/speaker-diarization";
12
- import { upsert } from "@himalaya/local-db";
9
+ import { startSession, stopSession, listSegments } from "@wix/himalaya/audio-record";
10
+ import * as speech from "@wix/himalaya/speech";
11
+ import * as diarization from "@wix/himalaya/speaker-diarization";
12
+ import { upsert } from "@wix/himalaya/local-db";
13
13
 
14
14
  export async function beginVoiceJournal() {
15
15
  const session = await startSession({ mode: "foreground", chunkSeconds: 30 });
@@ -129,7 +129,17 @@ one object either way.
129
129
  > the two in step. Until that divergence list is empty, defensive reducers
130
130
  > (`String(p?.value ?? p?.text ?? "")`) remain the safe way to read a payload.
131
131
 
132
- So a text-input reducer is `setDraft: (s, { value }) => ({ ...s, draft: value })`, bound with `onChange: { actionId: "tier5.notes.setDraft" }`.
132
+ So a text-input reducer is `setDraft: (s, p) => ({ ...s, draft: String(p?.value ?? "") })`, bound with `onChange: { actionId: "tier5.notes.setDraft" }`.
133
+
134
+ **The payload argument can be absent.** A host calls the reducer with one merged object when
135
+ `params`/`args` carry anything, and with **no argument at all** when both are empty — so the
136
+ second parameter is `undefined`, not `{}`. A device always injects `{ value }` for the controls in
137
+ the table, but `himi test` does not always have a value to type: a field whose legal value it
138
+ cannot read off the app (a `Slider`, `Stepper`, `Toggle`, `DatePicker`, or a text field with no
139
+ seeded word to borrow) is dispatched **with no payload**, exactly as an argument-less button is.
140
+ Destructuring in the signature — `(s, { value }) => …` — then throws `Cannot destructure property
141
+ 'value' of 'undefined'`, and the crawl reports it against your reducer. Read through `p?.` (or
142
+ default the parameter, `(s, { value } = {}) => …`) and every reducer survives both callers.
133
143
 
134
144
  **Per-row actions carry `$item` fields.** In a bound list/grid, `$item.<field>` substitutes into an action's `params` per row — so a per-row delete is `action: { actionId: "tier5.notes.remove", params: { id: "$item.id" } }`, and the reducer reads `remove: (s, { id }) => …`. (`$item` also substitutes into any prop string, e.g. `text: "$item.title"`.)
135
145
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1134.0",
3
+ "version": "0.1136.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": [
@@ -67,5 +67,5 @@
67
67
  "artifactId": "himalaya-cli"
68
68
  }
69
69
  },
70
- "falconPackageHash": "b114c871da4cb1b2f6f20b7a14c5d491c36f951d089ce10a23f5ca76"
70
+ "falconPackageHash": "0c3678539afa365f0de04b4c967184453cab4af35a8dffde750e6583"
71
71
  }