@wix/himalaya-cli 0.1026.0 → 0.1027.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/builder.d.ts +9 -0
  2. package/dist/cli.mjs +1052 -653
  3. package/dist/preview-assets.generated.bin +0 -0
  4. package/guides/app-config.md +6 -0
  5. package/guides/headless-loop.md +3 -1
  6. package/package.json +2 -2
  7. package/templates/beauty-salon/dev/index.ts +331 -199
  8. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +18 -8
  9. package/templates/beauty-salon/tier5-src/_shared/collection-service.ts +28 -3
  10. package/templates/beauty-salon-owner/dev/index.ts +857 -458
  11. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +18 -8
  12. package/templates/beauty-salon-owner/tier5-src/_shared/collection-service.ts +28 -3
  13. package/templates/branded-lite/dev/index.ts +1539 -623
  14. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +18 -8
  15. package/templates/branded-lite/tier5-src/_shared/collection-service.ts +28 -3
  16. package/templates/community-club/dev/index.ts +237 -142
  17. package/templates/community-club/tier5-src/_shared/collection-contract.ts +18 -8
  18. package/templates/community-club/tier5-src/_shared/collection-service.ts +28 -3
  19. package/templates/community-club-owner/dev/index.ts +857 -458
  20. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +18 -8
  21. package/templates/community-club-owner/tier5-src/_shared/collection-service.ts +28 -3
  22. package/templates/events-meetups/dev/index.ts +202 -129
  23. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +18 -8
  24. package/templates/events-meetups/tier5-src/_shared/collection-service.ts +28 -3
  25. package/templates/events-meetups-owner/dev/index.ts +815 -458
  26. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +18 -8
  27. package/templates/events-meetups-owner/tier5-src/_shared/collection-service.ts +28 -3
  28. package/templates/gym-fitness/dev/index.ts +461 -212
  29. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +18 -8
  30. package/templates/gym-fitness/tier5-src/_shared/collection-service.ts +28 -3
  31. package/templates/gym-fitness-owner/dev/index.ts +758 -338
  32. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +18 -8
  33. package/templates/gym-fitness-owner/tier5-src/_shared/collection-service.ts +28 -3
  34. package/templates/himi-chat/dev/index.ts +26 -1
  35. package/templates/nonprofit-cause/dev/index.ts +246 -153
  36. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +18 -8
  37. package/templates/nonprofit-cause/tier5-src/_shared/collection-service.ts +28 -3
  38. package/templates/nonprofit-cause-owner/dev/index.ts +716 -338
  39. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +18 -8
  40. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-service.ts +28 -3
  41. package/templates/owner-lite/dev/index.ts +758 -338
  42. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +18 -8
  43. package/templates/owner-lite/tier5-src/_shared/collection-service.ts +28 -3
  44. package/templates/podcast-creator/dev/index.ts +255 -164
  45. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +18 -8
  46. package/templates/podcast-creator/tier5-src/_shared/collection-service.ts +28 -3
  47. package/templates/podcast-creator-owner/dev/index.ts +857 -458
  48. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +18 -8
  49. package/templates/podcast-creator-owner/tier5-src/_shared/collection-service.ts +28 -3
  50. package/templates/portfolio-creative/dev/index.ts +287 -175
  51. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +18 -8
  52. package/templates/portfolio-creative/tier5-src/_shared/collection-service.ts +28 -3
  53. package/templates/portfolio-creative-owner/dev/index.ts +857 -458
  54. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +18 -8
  55. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-service.ts +28 -3
  56. package/templates/restaurant/dev/index.ts +200 -126
  57. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +18 -8
  58. package/templates/restaurant/tier5-src/_shared/collection-service.ts +28 -3
  59. package/templates/restaurant-owner/dev/index.ts +758 -338
  60. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +18 -8
  61. package/templates/restaurant-owner/tier5-src/_shared/collection-service.ts +28 -3
  62. package/templates/retail-store/dev/index.ts +113 -138
  63. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +18 -8
  64. package/templates/retail-store/tier5-src/_shared/collection-service.ts +28 -3
  65. package/templates/retail-store-owner/dev/index.ts +716 -338
  66. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +18 -8
  67. package/templates/retail-store-owner/tier5-src/_shared/collection-service.ts +28 -3
  68. package/templates/surface-showcase/dev/index.ts +27 -0
  69. package/templates/yoga-studio/dev/index.ts +427 -198
  70. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +18 -8
  71. package/templates/yoga-studio/tier5-src/_shared/collection-service.ts +28 -3
  72. package/templates/yoga-studio-owner/dev/index.ts +716 -338
  73. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +18 -8
  74. package/templates/yoga-studio-owner/tier5-src/_shared/collection-service.ts +28 -3
package/dist/cli.mjs CHANGED
@@ -5378,7 +5378,6 @@ function resolveTranslation(value, catalog, sourceCatalog) {
5378
5378
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
5379
5379
  var init_bindings = __esm({
5380
5380
  "../../core/ts/src/bindings.ts"() {
5381
- "use strict";
5382
5381
  BINDING_SIGILS = ["service", "flag", "item", "t"];
5383
5382
  BINDING_SIGIL_RE = new RegExp(
5384
5383
  `^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
@@ -22907,6 +22906,65 @@ var init_controls = __esm({
22907
22906
  }
22908
22907
  });
22909
22908
 
22909
+ // ../../core/web-renderer/src/a11y/modalStack.ts
22910
+ function pushModalLayer(el) {
22911
+ const layer = { el: typeof el === "function" ? el : () => el };
22912
+ layers.push(layer);
22913
+ sync();
22914
+ return {
22915
+ isTop: () => layers[layers.length - 1] === layer,
22916
+ release: () => {
22917
+ const i = layers.indexOf(layer);
22918
+ if (i < 0) return;
22919
+ layers.splice(i, 1);
22920
+ sync();
22921
+ }
22922
+ };
22923
+ }
22924
+ function topElement() {
22925
+ for (let i = layers.length - 1; i >= 0; i--) {
22926
+ const el = layers[i].el();
22927
+ if (el?.isConnected) return el;
22928
+ }
22929
+ return null;
22930
+ }
22931
+ function sync() {
22932
+ watcher?.disconnect();
22933
+ watcher = null;
22934
+ for (const el of madeInert) el.removeAttribute("inert");
22935
+ madeInert.clear();
22936
+ const top = topElement();
22937
+ if (!top) return;
22938
+ const scope = top.parentElement?.closest(`[${MODAL_SCOPE_ATTR}]`) ?? top.ownerDocument.body;
22939
+ const levels = [];
22940
+ let node = top;
22941
+ while (node !== scope && node.parentElement) {
22942
+ const parent = node.parentElement;
22943
+ levels.push(parent);
22944
+ for (const sibling of Array.from(parent.children)) {
22945
+ if (sibling === node || SKIP.has(sibling.tagName) || sibling.hasAttribute("inert")) continue;
22946
+ sibling.setAttribute("inert", "");
22947
+ madeInert.add(sibling);
22948
+ }
22949
+ node = parent;
22950
+ }
22951
+ const Observer = top.ownerDocument.defaultView?.MutationObserver;
22952
+ if (Observer && levels.length) {
22953
+ watcher = new Observer(() => sync());
22954
+ for (const level of levels) watcher.observe(level, { childList: true });
22955
+ }
22956
+ }
22957
+ var MODAL_SCOPE_ATTR, SKIP, layers, madeInert, watcher;
22958
+ var init_modalStack = __esm({
22959
+ "../../core/web-renderer/src/a11y/modalStack.ts"() {
22960
+ MODAL_SCOPE_ATTR = "data-himi-modal-scope";
22961
+ SKIP = /* @__PURE__ */ new Set(["SCRIPT", "STYLE", "LINK", "META", "TEMPLATE", "NOSCRIPT", "HEAD"]);
22962
+ layers = [];
22963
+ madeInert = /* @__PURE__ */ new Set();
22964
+ watcher = null;
22965
+ }
22966
+ });
22967
+
22910
22968
  // ../../core/web-renderer/src/listGestures.tsx
22911
22969
  import { useRef, useState as useState2 } from "react";
22912
22970
  import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
@@ -24158,8 +24216,8 @@ function autoGrowBox(contentHeight, lineHeight, verticalPadding, maxLines) {
24158
24216
  const cap = Math.max(floor, lineHeight * lines + verticalPadding);
24159
24217
  return { height: Math.max(floor, Math.min(contentHeight, cap)), scrolls: contentHeight > cap };
24160
24218
  }
24161
- function wrapModalMenu(modal, label2, placement, menu) {
24162
- return modal ? /* @__PURE__ */ jsx6("div", { role: "dialog", "aria-modal": "true", "aria-label": label2, style: placement, children: menu }) : menu;
24219
+ function wrapModalMenu(modal, label2, placement, ref, menu) {
24220
+ return modal ? /* @__PURE__ */ jsx6("div", { ref, role: "dialog", "aria-modal": "true", "aria-label": label2, style: placement, children: menu }) : menu;
24163
24221
  }
24164
24222
  function bindMediaSlot(value, sessionId) {
24165
24223
  if (typeof value === "string") {
@@ -24774,9 +24832,37 @@ function ensureInitialFocusRuntime() {
24774
24832
  restoreFocusOnDismiss(presentedScope, active);
24775
24833
  }
24776
24834
  el.focus();
24835
+ if (document.activeElement === el) enterAuthoredOrderOnTab(el);
24777
24836
  }
24778
24837
  }, 0);
24779
24838
  }
24839
+ function enterAuthoredOrderOnTab(target) {
24840
+ const doc = target.ownerDocument;
24841
+ const scope = target.closest(PRESENTED_SCOPE);
24842
+ const root = scope ?? doc;
24843
+ const onKeyDown = (e) => {
24844
+ if (e.key !== "Tab" || e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.defaultPrevented || e.target !== target) return;
24845
+ if (target.tabIndex > 0) return;
24846
+ const ordered = Array.from(root.querySelectorAll("[tabindex]")).filter((c) => c.tabIndex > 0 && c.closest(PRESENTED_SCOPE) === scope).sort((a, b) => a.tabIndex - b.tabIndex);
24847
+ for (const c of ordered) {
24848
+ c.focus();
24849
+ if (doc.activeElement === c) {
24850
+ e.preventDefault();
24851
+ e.stopPropagation();
24852
+ return;
24853
+ }
24854
+ }
24855
+ };
24856
+ const onBlur = () => setTimeout(() => {
24857
+ if (doc.activeElement !== target) disarm();
24858
+ }, 0);
24859
+ const disarm = () => {
24860
+ target.removeEventListener("keydown", onKeyDown);
24861
+ target.removeEventListener("blur", onBlur);
24862
+ };
24863
+ target.addEventListener("keydown", onKeyDown);
24864
+ target.addEventListener("blur", onBlur);
24865
+ }
24780
24866
  function ariaModal(modal, dflt) {
24781
24867
  const on3 = modal === true || modal === false ? modal : dflt;
24782
24868
  return on3 ? "true" : void 0;
@@ -25745,6 +25831,7 @@ var init_renderNode = __esm({
25745
25831
  init_icons();
25746
25832
  init_icon_catalog_generated();
25747
25833
  init_controls();
25834
+ init_modalStack();
25748
25835
  init_listGestures();
25749
25836
  init_worker_subtree();
25750
25837
  init_preview_surfaces();
@@ -26275,6 +26362,7 @@ var init_renderNode = __esm({
26275
26362
  const popupPlacement = { position: "absolute", top: "100%", right: 0, zIndex: 20, marginTop: 4 };
26276
26363
  const anchorRef = React5.useRef(null);
26277
26364
  const menuRef = React5.useRef(null);
26365
+ const dialogRef = React5.useRef(null);
26278
26366
  const itemsKey = items.map((it, i) => `${it?.id ?? i}:${it?.disabled ? 0 : 1}`).join("|");
26279
26367
  React5.useEffect(() => {
26280
26368
  if (!open || !modal) return;
@@ -26284,7 +26372,9 @@ var init_renderNode = __esm({
26284
26372
  }, [open, modal, itemsKey]);
26285
26373
  React5.useEffect(() => {
26286
26374
  if (!open || !modal) return;
26375
+ const layer = pushModalLayer(() => dialogRef.current);
26287
26376
  return () => {
26377
+ layer.release();
26288
26378
  const active = document.activeElement;
26289
26379
  if (active == null || active === document.body || !document.contains(active)) anchorRef.current?.focus();
26290
26380
  };
@@ -26322,11 +26412,11 @@ var init_renderNode = __esm({
26322
26412
  setOpen((v) => !v);
26323
26413
  }
26324
26414
  },
26325
- style: { display: "inline-flex", alignItems: "center", cursor: "pointer" },
26415
+ style: { display: "inline-flex", alignItems: "center", justifyContent: "center", minWidth: 44, minHeight: 44, cursor: "pointer" },
26326
26416
  children: props.anchor ? renderNode(props.anchor, ctx) : iconSvg("more", { size: 20, color: C("textSecondary", "#636b78") })
26327
26417
  }
26328
26418
  ),
26329
- open && wrapModalMenu(modal, anchorLabel, popupPlacement, /* @__PURE__ */ jsx6(
26419
+ open && wrapModalMenu(modal, anchorLabel, popupPlacement, dialogRef, /* @__PURE__ */ jsx6(
26330
26420
  "div",
26331
26421
  {
26332
26422
  ref: menuRef,
@@ -27388,7 +27478,7 @@ var init_renderNode = __esm({
27388
27478
  const tAlign = props.textAlign === "center" ? "center" : props.textAlign === "trailing" ? "right" : props.textAlign === "justify" ? "justify" : props.textAlign === "leading" ? "left" : void 0;
27389
27479
  const tDecor = props.decoration === "strikethrough" ? "line-through" : props.decoration === "underline" ? "underline" : void 0;
27390
27480
  const ln = typeof props.lineSpacing === "number" ? pxToRem((f.line ?? 1.4) * f.size + props.lineSpacing) : f.line;
27391
- return /* @__PURE__ */ jsx6("div", { ...countAttrs, style: { fontFamily: fontStack(f.family, ctx.idiom), fontSize: f.remSize, fontWeight: f.weight, lineHeight: ln, letterSpacing: f.letterSpacing, color: C(resolveScalarStatic(props.color, ctx) ?? "textPrimary", "#13171e"), whiteSpace: "pre-wrap", overflowWrap: "anywhere", wordBreak: "break-word", minWidth: 0, ...tAlign ? { textAlign: tAlign } : {}, ...tDecor ? { textDecoration: tDecor } : {}, ...props.maxLines ? { display: "-webkit-box", WebkitLineClamp: props.maxLines, WebkitBoxOrient: "vertical", overflow: "hidden" } : {}, ...wrap }, children: content == null || content === "" ? collapse ? null : "\xA0" : content });
27481
+ return /* @__PURE__ */ jsx6("div", { ...countAttrs, style: { fontFamily: fontStack(f.family, ctx.idiom), fontSize: f.remSize, fontWeight: f.weight, lineHeight: ln, letterSpacing: f.letterSpacing, color: C(resolveScalarStatic(props.color, ctx) ?? "textPrimary", "#13171e"), whiteSpace: props.maxLines === 1 ? "nowrap" : "pre-wrap", overflowWrap: "anywhere", wordBreak: "break-word", minWidth: 0, ...tAlign ? { textAlign: tAlign } : {}, ...tDecor ? { textDecoration: tDecor } : {}, ...props.maxLines === 1 ? { overflow: "hidden", textOverflow: "ellipsis" } : props.maxLines ? { display: "-webkit-box", WebkitLineClamp: props.maxLines, WebkitBoxOrient: "vertical", overflow: "hidden" } : {}, ...wrap }, children: content == null || content === "" ? collapse ? null : "\xA0" : content });
27392
27482
  });
27393
27483
  ImageNode = withExtras("ImageNode", ({ props, ctx, wrap }) => {
27394
27484
  const C = ctx.color;
@@ -28264,18 +28354,23 @@ var init_renderNode = __esm({
28264
28354
  const next = multiSelect ? isSel ? selected.filter((x) => x !== id) : [...selected, id] : isSel ? [] : [id];
28265
28355
  ctx.onAction({ ...onChange, args: { ...onChange.args ?? {}, selectedIds: next } });
28266
28356
  };
28267
- return /* @__PURE__ */ jsx6("div", { role: "group", style: { display: "flex", gap: 8, flexWrap: "wrap", width: "100%", ...wrap }, children: chips.map((c, i) => {
28268
- const on3 = selected.includes(c.id);
28269
- const chipStyle = { display: "flex", gap: 6, alignItems: "center", fontFamily: bodyFont(ctx), fontWeight: 500, fontSize: 13, border: `1px solid ${on3 ? "transparent" : C("border", "#ddd")}`, background: on3 ? C("accentPrimary", "#116DFF") : "transparent", color: on3 ? C("textOnAccent", "#fff") : C("textSecondary", "#666"), padding: "6px 12px", borderRadius: 999 };
28270
- const inner = /* @__PURE__ */ jsxs3(Fragment3, { children: [
28271
- asText2(c.label),
28272
- c.count != null && /* @__PURE__ */ jsxs3(Fragment3, { children: [
28273
- " ",
28274
- /* @__PURE__ */ jsx6("span", { style: { opacity: 0.7, fontSize: 11 }, children: c.count })
28275
- ] })
28276
- ] });
28277
- return interactive ? /* @__PURE__ */ jsx6("button", { type: "button", "aria-pressed": on3, onClick: () => toggle(c.id), style: { all: "unset", cursor: "pointer", boxSizing: "border-box", ...chipStyle }, children: inner }, c.id ?? i) : /* @__PURE__ */ jsx6("span", { style: chipStyle, children: inner }, c.id ?? i);
28278
- }) });
28357
+ return (
28358
+ // ONE horizontally scrolling row, like the iOS ScrollView(.horizontal) and the
28359
+ // Android LazyRow. Wrapping here turned a long filter set into a ragged grid and
28360
+ // pushed a sibling count onto two lines (#3080, #3004).
28361
+ /* @__PURE__ */ jsx6("div", { role: "group", style: { display: "flex", gap: 8, flexWrap: "nowrap", overflowX: "auto", scrollbarWidth: "none", width: "100%", minWidth: 0, ...wrap }, children: chips.map((c, i) => {
28362
+ const on3 = selected.includes(c.id);
28363
+ const chipStyle = { display: "flex", flex: "none", whiteSpace: "nowrap", gap: 6, alignItems: "center", fontFamily: bodyFont(ctx), fontWeight: 500, fontSize: 13, border: `1px solid ${on3 ? "transparent" : C("border", "#ddd")}`, background: on3 ? C("accentPrimary", "#116DFF") : "transparent", color: on3 ? C("textOnAccent", "#fff") : C("textSecondary", "#666"), padding: "6px 12px", borderRadius: 999 };
28364
+ const inner = /* @__PURE__ */ jsxs3(Fragment3, { children: [
28365
+ asText2(c.label),
28366
+ c.count != null && /* @__PURE__ */ jsxs3(Fragment3, { children: [
28367
+ " ",
28368
+ /* @__PURE__ */ jsx6("span", { style: { opacity: 0.7, fontSize: 11 }, children: c.count })
28369
+ ] })
28370
+ ] });
28371
+ return interactive ? /* @__PURE__ */ jsx6("button", { type: "button", "aria-pressed": on3, onClick: () => toggle(c.id), style: { all: "unset", cursor: "pointer", boxSizing: "border-box", ...chipStyle }, children: inner }, c.id ?? i) : /* @__PURE__ */ jsx6("span", { style: chipStyle, children: inner }, c.id ?? i);
28372
+ }) })
28373
+ );
28279
28374
  });
28280
28375
  TILE_PX = 256;
28281
28376
  MAX_MERCATOR_LAT = 85.05112878;
@@ -32789,18 +32884,18 @@ function startBundleArtifactWatcher(state) {
32789
32884
  });
32790
32885
  };
32791
32886
  try {
32792
- const watcher = watch(TIER5_ROOT2, { persistent: false }, (_event, filename) => {
32887
+ const watcher2 = watch(TIER5_ROOT2, { persistent: false }, (_event, filename) => {
32793
32888
  const bundle = builtBundleName(filename);
32794
32889
  if (!bundle) return;
32795
32890
  pending.add(bundle);
32796
32891
  if (timer) clearTimeout(timer);
32797
32892
  timer = setTimeout(flush, 120);
32798
32893
  });
32799
- watcher.on("error", (err) => {
32894
+ watcher2.on("error", (err) => {
32800
32895
  console.warn(`[himalaya-dev] bundle watcher stopped: ${err.message}`);
32801
32896
  });
32802
32897
  console.log(`[himalaya-dev] watching built bundles: ${TIER5_ROOT2}`);
32803
- return watcher;
32898
+ return watcher2;
32804
32899
  } catch (err) {
32805
32900
  console.warn(`[himalaya-dev] bundle watcher unavailable: ${err.message}`);
32806
32901
  return void 0;
@@ -32835,7 +32930,7 @@ function startBundleSourceWatcher(state) {
32835
32930
  building = false;
32836
32931
  };
32837
32932
  try {
32838
- const watcher = watch(TIER5_SRC_ROOT, { persistent: false, recursive: true }, (_event, filename) => {
32933
+ const watcher2 = watch(TIER5_SRC_ROOT, { persistent: false, recursive: true }, (_event, filename) => {
32839
32934
  if (!filename || !/\.(ts|tsx)$/.test(filename)) return;
32840
32935
  const name = filename.split(sep7)[0];
32841
32936
  if (!name || name.startsWith(".")) return;
@@ -32843,11 +32938,11 @@ function startBundleSourceWatcher(state) {
32843
32938
  if (timer) clearTimeout(timer);
32844
32939
  timer = setTimeout(flush, 200);
32845
32940
  });
32846
- watcher.on("error", (err) => {
32941
+ watcher2.on("error", (err) => {
32847
32942
  console.warn(`[himalaya-dev] bundle source watcher stopped: ${err.message}`);
32848
32943
  });
32849
32944
  console.log(`[himalaya-dev] watching T5 bundle sources: ${TIER5_SRC_ROOT}`);
32850
- return watcher;
32945
+ return watcher2;
32851
32946
  } catch (err) {
32852
32947
  console.warn(`[himalaya-dev] bundle source watcher unavailable: ${err.message}`);
32853
32948
  return void 0;
@@ -33015,7 +33110,7 @@ function startAppSourceWatcher(state) {
33015
33110
  }
33016
33111
  };
33017
33112
  try {
33018
- const watcher = watch(appDir2, { persistent: false, recursive: true }, (_event, filename) => {
33113
+ const watcher2 = watch(appDir2, { persistent: false, recursive: true }, (_event, filename) => {
33019
33114
  if (!filename) return;
33020
33115
  const rel = String(filename).split(sep7).join("/");
33021
33116
  if (!classifyAppPath(rel)) return;
@@ -33023,11 +33118,11 @@ function startAppSourceWatcher(state) {
33023
33118
  if (timer) clearTimeout(timer);
33024
33119
  timer = setTimeout(flush, 200);
33025
33120
  });
33026
- watcher.on("error", (err) => {
33121
+ watcher2.on("error", (err) => {
33027
33122
  console.warn(`[himalaya-dev] app source watcher stopped: ${err.message}`);
33028
33123
  });
33029
33124
  console.log(`[himalaya-dev] watching app source: ${appDir2}`);
33030
- return watcher;
33125
+ return watcher2;
33031
33126
  } catch (err) {
33032
33127
  console.warn(`[himalaya-dev] app source watcher unavailable: ${err.message}`);
33033
33128
  return void 0;
@@ -35697,10 +35792,10 @@ function cmsAnswer(seed2, method, bare, body) {
35697
35792
  pagingMetadata: { count: rows.length, tooManyToCount: false }
35698
35793
  };
35699
35794
  }
35700
- function layeredTransport(layers) {
35701
- const rules = layers.rules?.length ? [...layers.rules] : null;
35702
- const cms = layers.cmsSeed && Object.keys(layers.cmsSeed).length ? layers.cmsSeed : null;
35703
- const canned = layers.canned && Object.keys(layers.canned).length ? layers.canned : null;
35795
+ function layeredTransport(layers2) {
35796
+ const rules = layers2.rules?.length ? [...layers2.rules] : null;
35797
+ const cms = layers2.cmsSeed && Object.keys(layers2.cmsSeed).length ? layers2.cmsSeed : null;
35798
+ const canned = layers2.canned && Object.keys(layers2.canned).length ? layers2.canned : null;
35704
35799
  const label2 = [rules && "mocked", cms && "cms", canned && "canned"].filter(Boolean).join("+") || "offline";
35705
35800
  const call = async (method, path2, body) => {
35706
35801
  const m = String(method).toUpperCase();
@@ -39030,6 +39125,332 @@ var init_net_freeze = __esm({
39030
39125
  }
39031
39126
  });
39032
39127
 
39128
+ // src/studio-page.ts
39129
+ function renderStudioHtml(initial) {
39130
+ const accent = initial.app.palette.find((p) => p.name === "accentPrimary")?.hex ?? "#3b6fd8";
39131
+ return `<!doctype html>
39132
+ <html lang="en">
39133
+ <head>
39134
+ <meta charset="utf-8">
39135
+ <meta name="viewport" content="width=device-width, initial-scale=1">
39136
+ <title>${escapeHtml(initial.app.name)} \xB7 build board</title>
39137
+ <link rel="icon" id="fav" href="data:,">
39138
+ <style>
39139
+ :root{--bg:#f6f5f2;--card:#fff;--ink:#1c1c1a;--muted:#6b6a64;--faint:#a3a19a;--line:#e6e3dc;--ok:#1f7a4d;--okbg:#e3f3ea;--warn:#8a5a00;--warnbg:#fbefd6;--bad:#a12a2a;--badbg:#fbe4e4;--accent:${accent};--shadow:0 1px 2px rgba(0,0,0,.04),0 4px 16px rgba(0,0,0,.04)}
39140
+ @media (prefers-color-scheme:dark){:root{--bg:#121211;--card:#1c1c1a;--ink:#ecebe6;--muted:#a3a19a;--faint:#6b6a64;--line:#2c2b28;--okbg:#15301f;--ok:#7fd3a2;--warnbg:#33270f;--warn:#f0c46b;--badbg:#3a1616;--bad:#f09a9a;--shadow:none}}
39141
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
39142
+ .wrap{max-width:1180px;margin:0 auto;padding:20px 20px 60px}
39143
+ header{display:flex;align-items:center;gap:12px;margin-bottom:16px}
39144
+ .icon{width:40px;height:40px;border-radius:10px;background:var(--accent);object-fit:cover;flex:none}
39145
+ h1{font-size:20px;font-weight:600;margin:0}.sub{color:var(--muted);font-size:13px}
39146
+ .right{margin-left:auto;text-align:right}.clock{font-size:22px;font-variant-numeric:tabular-nums;font-weight:600}
39147
+ .live{display:inline-flex;align-items:center;gap:6px;color:var(--muted);font-size:12px}.dot{width:8px;height:8px;border-radius:50%;background:var(--ok)}.stale .dot{background:var(--faint)}
39148
+ .steps{display:grid;grid-template-columns:repeat(8,1fr);gap:6px;margin:4px 0 16px}.step .bar{height:6px;border-radius:3px;background:var(--line)}.step.done .bar{background:var(--ok)}.step.cur .bar{background:var(--accent)}
39149
+ .step .lbl{font-size:11px;color:var(--faint);margin-top:4px;text-transform:capitalize}.step.cur .lbl{color:var(--ink);font-weight:600}
39150
+ .card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px;box-shadow:var(--shadow)}
39151
+ .now{display:flex;gap:12px;align-items:flex-start;margin-bottom:16px}.now .msg{font-size:16px}.now .meta{color:var(--muted);font-size:12px}
39152
+ .grid{display:grid;grid-template-columns:320px 1fr;gap:16px;align-items:start}@media (max-width:860px){.grid{grid-template-columns:1fr}.steps .lbl{display:none}}
39153
+ h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin:0 0 10px}
39154
+ .phone{width:100%;max-width:300px;aspect-ratio:390/844;border-radius:34px;border:9px solid #111;background:#fff;overflow:hidden;margin:0 auto;display:block}
39155
+ .phone img,.phone iframe{width:100%;height:100%;object-fit:cover;object-position:top;border:0;display:block;cursor:zoom-in}
39156
+ .phone.vpf{position:relative}.phone iframe.vp{position:absolute;top:0;left:0;width:390px;height:844px;transform-origin:0 0;transform:scale(var(--k,.72));cursor:auto}
39157
+ .cap{text-align:center;color:var(--muted);font-size:12px;margin-top:8px}
39158
+ .film{display:flex;gap:6px;overflow-x:auto;margin-top:12px;padding-bottom:4px}.film img{width:48px;aspect-ratio:390/844;object-fit:cover;object-position:top;border-radius:8px;border:1px solid var(--line);cursor:zoom-in}
39159
+ .chips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px}.chip{font-size:12px;padding:5px 10px;border-radius:999px;background:var(--card);border:1px solid var(--line);color:var(--muted)}
39160
+ .chip.ok{background:var(--okbg);color:var(--ok);border-color:transparent}.chip.warn{background:var(--warnbg);color:var(--warn);border-color:transparent}.chip.bad{background:var(--badbg);color:var(--bad);border-color:transparent}
39161
+ .wall{display:grid;grid-template-columns:repeat(auto-fill,minmax(112px,1fr));gap:14px}
39162
+ .shot{text-align:center}.shot .frame{aspect-ratio:390/844;border-radius:16px;border:5px solid #111;overflow:hidden;background:#fff}.shot img{width:100%;height:100%;object-fit:cover;object-position:top;display:block;cursor:zoom-in}
39163
+ .shot .id{font-size:12px;margin-top:6px;word-break:break-all}.badge{display:inline-block;font-size:10px;padding:1px 7px;border-radius:999px;margin-top:3px}.badge.new{background:var(--okbg);color:var(--ok)}.badge.chg{background:var(--warnbg);color:var(--warn)}
39164
+ .section{margin-bottom:16px}.brand{display:flex;gap:14px;align-items:center;flex-wrap:wrap}.brand .site{width:220px;border-radius:10px;border:1px solid var(--line);cursor:zoom-in}
39165
+ .sw{display:flex;gap:6px}.sw span{width:22px;height:22px;border-radius:50%;border:1px solid var(--line)}
39166
+ .nav{display:flex;flex-wrap:wrap;gap:6px}.nav span{font-size:12px;padding:3px 8px;border-radius:6px;background:var(--bg);border:1px solid var(--line)}
39167
+ .log{list-style:none;margin:0;padding:0}.log li{display:flex;gap:10px;padding:10px 0;border-top:1px solid var(--line)}.log li:first-child{border-top:0}
39168
+ .log .t{color:var(--faint);font-size:12px;min-width:64px;font-variant-numeric:tabular-nums}.log .s{font-size:11px;color:var(--muted);text-transform:capitalize}.log .imgs{display:flex;gap:6px;margin-top:6px}.log .imgs img{height:72px;border-radius:8px;border:1px solid var(--line);cursor:zoom-in}
39169
+ .empty{color:var(--faint);font-size:13px;padding:8px 0}
39170
+ #lb{position:fixed;inset:0;background:rgba(0,0,0,.72);display:none;align-items:center;justify-content:center;z-index:9;cursor:zoom-out}#lb img{max-height:92vh;max-width:92vw;border-radius:14px;background:#fff}
39171
+ .enter{animation:enter .7s cubic-bezier(.2,.8,.2,1) both}@keyframes enter{from{opacity:0;transform:translateY(10px) scale(.94);filter:grayscale(1)}to{opacity:1;transform:none;filter:none}}
39172
+ .changed .frame{animation:chg .9s ease both}@keyframes chg{35%{opacity:.25;transform:scale(.96)}100%{opacity:1;transform:none}}
39173
+ .step.fresh .bar{animation:fill .7s ease both;transform-origin:left}@keyframes fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
39174
+ .slidein{animation:slide .45s cubic-bezier(.2,.8,.2,1) both}@keyframes slide{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
39175
+ .phone.pulse{animation:pulse 1s ease-out}.phone.pulse.bad{animation-name:pulsebad}@keyframes pulse{from{outline:3px solid var(--ok);outline-offset:0}to{outline:3px solid transparent;outline-offset:10px}}@keyframes pulsebad{from{outline:3px solid var(--bad);outline-offset:0}to{outline:3px solid transparent;outline-offset:10px}}
39176
+ .toast{display:block;width:max-content;max-width:100%;margin:8px auto 0;font-size:12px;padding:4px 10px;border-radius:999px;background:var(--okbg);color:var(--ok);animation:toast 3.4s ease both}.toast.bad{background:var(--badbg);color:var(--bad)}@keyframes toast{0%{opacity:0;transform:translateY(4px)}10%,80%{opacity:1;transform:none}100%{opacity:0}}
39177
+ .icon,.step.cur .bar{transition:background-color .8s}.sw.pop span{animation:pop .6s ease both}@keyframes pop{50%{transform:scale(1.25)}}
39178
+ .quiet{color:var(--warn)}.quiet .dot{background:var(--warn)}.idle .dot{background:var(--faint)}
39179
+ .tog{font:inherit;font-size:12px;color:var(--muted);background:none;border:1px solid var(--line);border-radius:999px;padding:2px 9px;margin:6px 0 0 6px;cursor:pointer}.tog.on{color:var(--ink);border-color:var(--ink)}
39180
+ #fx{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:8}
39181
+ @media (max-width:560px){header{flex-wrap:wrap}.right{margin-left:0;width:100%;text-align:left;display:flex;flex-wrap:wrap;align-items:center;gap:4px 12px}.right .clock{font-size:18px}.tog{margin:0 6px 0 0}}
39182
+ .scrub{display:flex;align-items:center;gap:8px;margin:-6px 0 4px}.scrub[hidden]{display:none}.scrub input{flex:1;min-width:0}
39183
+ .scrub .rd{font-size:12px;color:var(--muted);min-width:160px;font-variant-numeric:tabular-nums}
39184
+ .marks{position:relative;height:8px;margin:0 9px 12px}.marks span{position:absolute;top:0;width:7px;height:7px;border-radius:50%;background:var(--faint);transform:translateX(-50%)}.marks .ms{background:var(--warn)}.marks .bad{background:var(--bad)}
39185
+ .viewing{font-size:13px;padding:6px 10px;border-radius:8px;background:var(--warnbg);color:var(--warn);margin:0 0 12px}.viewing[hidden]{display:none}.viewing a{color:inherit}
39186
+ .newdot{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--bad);margin-left:6px;vertical-align:middle}.newdot[hidden]{display:none}
39187
+ #focus[hidden],#still[hidden]{display:none}
39188
+ .reveal{position:fixed;inset:0;z-index:7;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.5)}.reveal[hidden]{display:none}
39189
+ .rv{background:var(--card);border-radius:16px;padding:18px;max-width:760px;width:calc(100% - 32px)}.rv h3{margin:0;font-size:18px}.rv .row{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-top:12px}
39190
+ .rv figure{margin:0}.rv figcaption{font-size:12px;color:var(--muted);margin-bottom:6px}.rv .site img{width:100%;border-radius:10px;border:1px solid var(--line)}
39191
+ .rv .ph{position:relative;aspect-ratio:390/844;max-height:60vh;border-radius:22px;border:6px solid #111;overflow:hidden;background:#fff;margin:0 auto}.rv .ph img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:top}
39192
+ .rv .after{clip-path:inset(0 100% 0 0);transition:clip-path 1.4s cubic-bezier(.6,0,.2,1)}.rv.on .after{clip-path:inset(0 0 0 0)}
39193
+ @media (prefers-reduced-motion:reduce){.rv .after{transition:none}.enter,.changed .frame,.step.fresh .bar,.slidein,.phone.pulse,.sw.pop span,.toast{animation:none}.icon,.step.cur .bar{transition:none}}
39194
+ </style>
39195
+ </head>
39196
+ <body>
39197
+ <div class="wrap">
39198
+ <header><img class="icon" id="icon" alt=""><div><h1 id="name"></h1><div class="sub">Build board \xB7 updates itself as the app is built</div></div>
39199
+ <div class="right"><div class="clock" id="clock">00:00</div><div class="live" id="live"><span class="dot"></span><span id="updated">live</span></div>
39200
+ <div><span class="live idle" id="agent"><span class="dot"></span><span id="agentt">Waiting for the agent</span></span></div>
39201
+ <div><button class="tog" id="snd" type="button" aria-pressed="false">Sound off</button><button class="tog" id="say" type="button" aria-pressed="false" hidden>Read aloud: off</button><button class="tog" id="ntf" type="button" aria-pressed="false" hidden>Notices off</button></div></div></header>
39202
+ <div class="steps" id="steps"></div>
39203
+ <div class="scrub" id="scrub" hidden><button class="tog" id="replay" type="button">Replay</button><input type="range" id="slider" min="0" max="1" step="1" value="1" aria-label="Build timeline"><span class="rd" id="readout"></span><button class="tog" id="golive" type="button">Live<span class="newdot" id="newdot" hidden></span></button></div>
39204
+ <div class="marks" id="marks"></div>
39205
+ <div class="viewing" id="viewing" hidden>Viewing <span id="viewingt"></span> \xB7 <a href="#" id="backlive">Back to live</a></div>
39206
+ <div class="card now"><div style="flex:1"><div class="meta" id="nowmeta">Now</div><div class="msg" id="nowmsg"></div></div></div>
39207
+ <div class="grid">
39208
+ <div><div class="card"><h2 id="focuslbl">Latest</h2><div id="focus"></div><div id="still" hidden></div><div class="film" id="film"></div>
39209
+ <button class="tog" id="pop" type="button" hidden>Pop out phone</button></div></div>
39210
+ <div>
39211
+ <div class="chips" id="chips"></div>
39212
+ <div class="card section"><h2>Screens</h2><div class="wall" id="wall"></div></div>
39213
+ <div class="card section" id="brandcard"><h2>Brand</h2><div class="brand" id="brand"></div></div>
39214
+ <div class="card section" id="navcard"><h2>Navigation the test crawl followed</h2><div class="nav" id="nav"></div></div>
39215
+ <div class="card section"><h2>Postcards</h2><ul class="log" id="log"></ul></div>
39216
+ </div></div></div>
39217
+ <div id="lb"><img alt=""></div>
39218
+ <div class="reveal" id="reveal" hidden role="dialog" aria-labelledby="rvh"><div class="rv" id="rv"><div style="display:flex;justify-content:space-between;align-items:center;gap:12px"><h3 id="rvh">Your brand, in your app</h3><button class="tog" id="rvx" type="button">Close</button></div><div class="row" id="rvrow"></div></div></div>
39219
+ <canvas id="fx" aria-hidden="true"></canvas>
39220
+ <script>window.__himiInitial = ${scriptJson(initial)};</script>
39221
+ <script>
39222
+ (function(){
39223
+ var cur=null, lastSeen=Date.now();
39224
+ function $(id){return document.getElementById(id)}
39225
+ function el(tag,cls,txt){var e=document.createElement(tag);if(cls)e.className=cls;if(txt!=null)e.textContent=txt;return e}
39226
+ function clear(e){while(e.firstChild)e.removeChild(e.firstChild)}
39227
+ function nowMs(){return asOf||Date.now()}
39228
+ function ago(ts){var s=Math.max(0,Math.round((nowMs()-Date.parse(ts))/1000));if(s<60)return s+"s ago";var m=Math.round(s/60);if(m<60)return m+" min ago";return Math.round(m/60)+" h ago"}
39229
+ function hhmm(ts){var d=new Date(ts);return d.toLocaleTimeString([], {hour:"2-digit",minute:"2-digit"})}
39230
+ function recent(ts){return nowMs()-Date.parse(ts)<10*60*1000}
39231
+ function zoomable(img){img.addEventListener("click",function(){var lb=$("lb");lb.firstChild.src=img.src;lb.style.display="flex"})}
39232
+ function img(src,cls){var i=el("img",cls);i.src=src;i.alt="";i.loading="lazy";zoomable(i);return i}
39233
+ function chip(txt,kind){return el("span","chip"+(kind?" "+kind:""),txt)}
39234
+ function render(s){
39235
+ var sn=s.current?s.current.stepNumber:0;document.title=(sn?"("+sn+"/8) ":"")+s.app.name+" \xB7 build board";favicon(sn,!!(s.live&&s.live.buildError));$("name").textContent=s.app.name;
39236
+ if(s.app.icon){$("icon").src=s.app.icon}else{$("icon").removeAttribute("src")}
39237
+ var steps=$("steps");clear(steps);var n=s.current?s.current.stepNumber:0;
39238
+ s.steps.forEach(function(name,i){var d=el("div","step"+(i+1<n?" done":i+1===n?" cur":""));d.appendChild(el("div","bar"));d.appendChild(el("div","lbl",(i+1)+". "+name));steps.appendChild(d)});
39239
+ var last=s.journal[0];
39240
+ $("nowmsg").textContent=last?last.message:"Waiting for the first postcard.";
39241
+ $("nowmeta").textContent=last?("Now \xB7 "+(last.step?("step "+last.stepNumber+" of 8, "+last.step+" \xB7 "):"")+ago(last.ts)):"Now";
39242
+ var film=$("film");clear(film);
39243
+ if(!rewinding){var n0=s.timelineFrames||0;$("scrub").hidden=n0<2;$("slider").max=String(Math.max(1,n0-1));$("slider").value=$("slider").max}
39244
+ if(rewinding){paintStill(s)}else{$("still").hidden=true;$("focus").hidden=false;var focus=$("focus");
39245
+ /* The live frame is KEPT across renders while its URL and its run are unchanged (a restarted run on
39246
+ the same port is a different server, and the old frame would never see its builds). A new iframe is a full page
39247
+ load of the preview: every postcard, rebuild and the once-a-minute re-render used to throw away
39248
+ the in-place repaint, the preview's scroll and wherever the owner had navigated inside it. */
39249
+ if(pip&&(!s.live||pip.frame.getAttribute("src")!==s.live.url||pip.frame.getAttribute("data-run")!==s.live.runId))bringBack();
39250
+ if(pip){paintStrip(s)}else{
39251
+ var kept=s.live?focus.querySelector("iframe"):null;if(kept&&(kept.getAttribute("src")!==s.live.url||kept.getAttribute("data-run")!==s.live.runId))kept=null;
39252
+ if(kept){while(focus.lastChild&&focus.lastChild!==kept.parentNode)focus.removeChild(focus.lastChild)}else clear(focus);
39253
+ if(s.live){$("focuslbl").textContent="Live preview";if(!kept){var f=el("div","phone vpf");var fr=document.createElement("iframe");fr.className="vp";fr.setAttribute("width","390");fr.setAttribute("height","844");fr.setAttribute("data-run",s.live.runId);fr.src=s.live.url;fr.title="Live preview";f.appendChild(fr);focus.appendChild(f)}fit();if(s.live.buildError){var be=el("div","chip bad",null);be.style.display="block";be.style.marginTop="8px";be.textContent="Last edit did not build, still showing the last good version: "+s.live.buildError;focus.appendChild(be)}focus.appendChild(el("div","cap",(s.live.watching?"Repaints as the app is edited \xB7 ":"The build this run started with; himi run --watch repaints on every edit \xB7 ")+s.live.url))}
39254
+ else if(s.focus&&s.focus.frames.length){var fl=s.focus.frames;$("focuslbl").textContent="Latest \xB7 "+s.focus.id;var p=el("div","phone");p.appendChild(img(fl[fl.length-1].img));focus.appendChild(p);focus.appendChild(el("div","cap",fl.length+(fl.length===1?" version":" versions")+" so far"))}
39255
+ else{$("focuslbl").textContent="Latest";focus.appendChild(el("div","empty","The first render appears here."))}
39256
+ if(s.focus&&s.focus.frames.length>1)s.focus.frames.forEach(function(fm){film.appendChild(img(fm.img))});}}
39257
+ var chips=$("chips");clear(chips);var h=s.health;
39258
+ if(h.validate)chips.appendChild(chip(h.validate.ok?"Validate passing":"Validate: "+h.validate.errors+" errors",h.validate.ok?"ok":"bad"));
39259
+ if(h.test){var t=h.test;var moved=t.prevGaps!=null&&t.prevGaps!==t.gaps;var tc=chip("Test crawl: "+t.screens+" screens, "+t.actions+" actions, "+(moved?"gaps "+t.prevGaps+" \u2192 ":""),t.ok?"ok":"bad");var gn=el("span",null,String(t.gaps));gn.id="gapn";tc.appendChild(gn);if(!moved)tc.appendChild(document.createTextNode(" gaps"));chips.appendChild(tc)}
39260
+ h.ux.forEach(function(u){chips.appendChild(chip("UX drive "+u.platform+": "+u.status,u.status==="passed"?"ok":u.status==="failed"?"bad":"warn"))});
39261
+ if(h.push)chips.appendChild(chip((h.push.draft?"Draft published":"Published live")+" \xB7 "+h.push.releaseId.slice(0,10),h.push.draft?"warn":"ok"));
39262
+ if(!chips.firstChild)chips.appendChild(chip("Checks appear here as validate, test and push run"));
39263
+ var wall=$("wall");clear(wall);
39264
+ if(!s.screens.length)wall.appendChild(el("div","empty","Screens appear here as they render."));
39265
+ s.screens.forEach(function(sc){var d=el("div","shot");d.setAttribute("data-id",sc.id);var fr=el("div","frame");fr.appendChild(img(sc.img));d.appendChild(fr);d.appendChild(el("div","id",sc.id));
39266
+ if(recent(sc.firstSeen)&&sc.versions===1)d.appendChild(el("span","badge new","New"));else if(recent(sc.changedAt)&&sc.versions>1)d.appendChild(el("span","badge chg","Changed"));wall.appendChild(d)});
39267
+ var brand=$("brand");clear(brand);$("brandcard").style.display=(s.brand.site||s.app.palette.length)?"":"none";
39268
+ if(s.brand.site){var si=img(s.brand.site,"site");brand.appendChild(si)}
39269
+ if(s.brand.site&&s.focus&&s.focus.frames.length){brand.appendChild(el("span","sub","\u2192"));var pf=el("div","shot");pf.style.width="90px";var ff=el("div","frame");ff.appendChild(img(s.focus.frames[s.focus.frames.length-1].img));pf.appendChild(ff);brand.appendChild(pf)}
39270
+ if(s.app.palette.length){var sw=el("div","sw");s.app.palette.forEach(function(c){var d=el("span");d.style.background=c.hex;d.title=c.name+" "+c.hex;sw.appendChild(d)});brand.appendChild(sw)}
39271
+ var nav=$("nav");clear(nav);$("navcard").style.display=s.nav.length?"":"none";s.nav.forEach(function(e){nav.appendChild(el("span",null,e.from+" \u2192 "+e.to))});
39272
+ var log=$("log");clear(log);if(!s.journal.length)log.appendChild(el("li","empty","Postcards the agent journals appear here."));
39273
+ s.journal.forEach(function(j){var li=el("li");li.appendChild(el("div","t",hhmm(j.ts)));var body=el("div");body.appendChild(el("div",null,j.message));if(j.step)body.appendChild(el("div","s","step "+j.stepNumber+" \xB7 "+j.step));
39274
+ if(j.images.length){var row=el("div","imgs");j.images.forEach(function(u){row.appendChild(img(u))});body.appendChild(row)}li.appendChild(body);log.appendChild(li)});
39275
+ paintPop(s);
39276
+ }
39277
+ /* Motion, sound and notices answer a CHANGE, never a redraw: effects() compares what is on the page
39278
+ now with what it showed last, so opening the board, the once-a-minute redraw and the two-second
39279
+ poll of an unchanged state do nothing, and each effect names a real event. */
39280
+ var seen=null,actx=null,sound=false,notices=false;
39281
+ var tlStates=null,rewinding=false,replaying=false,asOf=0,replayTimer=null;
39282
+ function foldAll(frames){var out=[],st=null,meta={};(frames||[]).forEach(function(f){
39283
+ var ns={step:null,now:null,screens:{},palette:[],checks:{validate:null,test:null,push:null},build:null};
39284
+ if(!f.key&&st){ns.step=st.step;ns.now=st.now;for(var k in st.screens)ns.screens[k]=st.screens[k];ns.palette=st.palette;ns.checks=st.checks;ns.build=st.build}
39285
+ if("step" in f)ns.step=f.step;if("now" in f)ns.now=f.now;if(f.palette)ns.palette=f.palette;if(f.checks)ns.checks=f.checks;if("build" in f)ns.build=f.build;
39286
+ var sc=f.screens||{};for(var id in sc){if(sc[id]===null)delete ns.screens[id];else ns.screens[id]=sc[id]}
39287
+ for(var j in ns.screens){var m=meta[j];if(!m)meta[j]={first:f.ts,changed:f.ts,img:ns.screens[j],n:1};else if(m.img!==ns.screens[j]){m.changed=f.ts;m.img=ns.screens[j];m.n++}}
39288
+ var copy={};for(var q in meta)copy[q]={first:meta[q].first,changed:meta[q].changed,n:meta[q].n};
39289
+ ns.meta=copy;ns.ts=f.ts;ns.reveal=f.reveal||null;out.push(ns);st=ns});return out}
39290
+ function viewAt(i){var st=tlStates[i],b=cur;
39291
+ var screens=Object.keys(st.screens).sort().map(function(id){var m=st.meta[id];return{id:id,img:st.screens[id],firstSeen:m.first,changedAt:m.changed,versions:m.n}});
39292
+ var t=st.checks.test,prevGaps=null;for(var k=i-1;k>=0&&t;k--){var pt=tlStates[k].checks.test;if(pt&&pt.gaps!==t.gaps){prevGaps=pt.gaps;break}}
39293
+ var fid=b.focus?b.focus.id:null,fimg=fid&&st.screens[fid];
39294
+ return{v:"tl-"+i,generatedAt:st.ts,app:{name:b.app.name,icon:b.app.icon,palette:st.palette.map(function(p){return{name:p[0],hex:p[1]}})},steps:b.steps,
39295
+ current:st.step?{step:st.step.name,stepNumber:st.step.n,of:8}:null,startedAt:b.startedAt,
39296
+ journal:journalAt(st,b),screens:screens,focus:fimg?{id:fid,frames:[{ts:st.ts,img:fimg}]}:null,
39297
+ health:{validate:st.checks.validate?{ok:st.checks.validate.ok,errors:st.checks.validate.errors,warnings:0,at:st.ts}:null,
39298
+ test:t?{ok:t.ok,screens:t.screens,actions:t.actions,gaps:t.gaps,prevGaps:prevGaps,errors:0,at:st.ts}:null,ux:[],
39299
+ push:st.checks.push?{app:"",releaseId:st.checks.push.releaseId,draft:st.checks.push.draft,at:st.ts}:null},
39300
+ brand:b.brand,nav:b.nav,live:null,pastBuild:st.build,reveal:st.reveal?{at:st.ts,site:st.reveal.site,before:st.reveal.before,after:st.reveal.after}:null,timelineFrames:b.timelineFrames}}
39301
+ /* The board keeps only the newest postcards; the timeline remembers the one showing at each moment. */
39302
+ function journalAt(st,b){var jr=b.journal.filter(function(x){return x.ts<=st.ts});if(st.now&&(!jr[0]||jr[0].ts!==st.now.ts))jr.unshift({ts:st.now.ts,message:st.now.message,images:st.now.images,step:st.step?st.step.name:null,stepNumber:st.step?st.step.n:null});return jr}
39303
+ function loadTimeline(cb){window.__himiTimeline=function(frames){tlStates=foldAll(frames);cb()};var sc=document.createElement("script");sc.src="timeline.js?t="+Date.now();sc.onload=sc.onerror=function(){sc.remove()};document.head.appendChild(sc)}
39304
+ function readout(i){var st=tlStates[i],t0=Date.parse(tlStates[0].ts);return hhmm(st.ts)+(st.step?" \xB7 step "+st.step.n+", "+st.step.name:"")+" \xB7 "+Math.round((Date.parse(st.ts)-t0)/60000)+" min in"}
39305
+ function marks(){var m=$("marks");clear(m);var n=tlStates.length;tlStates.forEach(function(st,i){var p=tlStates[i-1],kind=null;
39306
+ if(st.build&&st.build.ok===false&&(!p||p.build!==st.build))kind="bad";
39307
+ else if(st.reveal||(st.checks.push&&(!p||!p.checks.push||p.checks.push.releaseId!==st.checks.push.releaseId))||(st.step&&st.step.n===8&&(!p||!p.step||p.step.n<8)))kind="ms";
39308
+ else if(st.now&&(!p||!p.now||p.now.ts!==st.now.ts))kind="";
39309
+ if(kind===null)return;var d=el("span",kind||null);d.style.left=(n>1?i/(n-1)*100:0)+"%";m.appendChild(d)})}
39310
+ function paintStill(s){$("focus").hidden=true;var still=$("still");still.hidden=false;clear(still);$("focuslbl").textContent="Home at "+hhmm(s.generatedAt);
39311
+ if(s.focus&&s.focus.frames.length){var p=el("div","phone");p.appendChild(img(s.focus.frames[0].img));still.appendChild(p)}else still.appendChild(el("div","empty","No picture of the home screen yet at this moment."))}
39312
+ function rewindTo(i){if(!tlStates||tlStates.length<2)return null;i=Math.max(0,Math.min(tlStates.length-1,i));rewinding=true;asOf=Date.parse(tlStates[i].ts);
39313
+ var sl=$("slider");sl.max=String(tlStates.length-1);sl.value=String(i);var r=readout(i);$("readout").textContent=r;sl.setAttribute("aria-valuetext",r);
39314
+ $("viewing").hidden=false;$("viewingt").textContent=hhmm(tlStates[i].ts);var s=viewAt(i);render(s);return s}
39315
+ function enterRewind(i){loadTimeline(function(){if(!tlStates||tlStates.length<2)return;marks();rewindTo(i==null?tlStates.length-1:i<0?tlStates.length-1+i:i)})}
39316
+ function stopReplay(){replaying=false;if(replayTimer){clearTimeout(replayTimer);replayTimer=null}$("replay").textContent="Replay"}
39317
+ function toLive(){stopReplay();rewinding=false;asOf=0;$("viewing").hidden=true;$("newdot").hidden=true;$("still").hidden=true;$("focus").hidden=false;$("readout").textContent="";seen=snap(cur);if(seen.accent)document.documentElement.style.setProperty("--accent",seen.accent);render(cur)}
39318
+ /* The replay plan: about 30 s whatever the build's length. Frames that carry a milestone (a step,
39319
+ a draft, a failed build, the reveal) always play; the rest are sampled down to 120 moments; gaps
39320
+ follow real time, 150 ms-1.5 s each, and are scaled down so the whole never runs past 30 s. */
39321
+ function replayPlan(){var n=tlStates.length,keep={0:1};keep[n-1]=1;
39322
+ for(var i=1;i<n;i++){var st=tlStates[i],p=tlStates[i-1];if(st.reveal||(st.step&&(!p.step||p.step.n!==st.step.n))||(st.checks.push&&(!p.checks.push||p.checks.push.releaseId!==st.checks.push.releaseId))||(st.build&&st.build.ok===false&&p.build!==st.build))keep[i]=1}
39323
+ var idx=[],MAX=120;if(n<=MAX){for(var a=0;a<n;a++)idx.push(a)}else{var room=MAX-Object.keys(keep).length;for(var k=1;k<=room;k++)keep[Math.round(k*(n-1)/(room+1))]=1;idx=Object.keys(keep).map(Number).sort(function(x,y){return x-y})}
39324
+ var t0=Date.parse(tlStates[0].ts),span=Math.max(1,Date.parse(tlStates[n-1].ts)-t0),gaps=[],sum=0;
39325
+ for(var q=1;q<idx.length;q++){var g=Math.min(1500,Math.max(150,(Date.parse(tlStates[idx[q]].ts)-Date.parse(tlStates[idx[q-1]].ts))/span*30000));gaps.push(g);sum+=g}
39326
+ var f=sum>30000?30000/sum:1;return{idx:idx,gaps:gaps.map(function(x){return x*f}),total:sum*f}}
39327
+ function replay(){loadTimeline(function(){if(!tlStates||tlStates.length<2)return;marks();replaying=true;$("replay").textContent="Pause";
39328
+ var scale=window.__himiReplayScale!=null?window.__himiReplayScale:1,plan=replayPlan();window.__himiReplayPlan={idx:plan.idx,totalMs:plan.total};
39329
+ seen=snap(rewindTo(0));var q=0;
39330
+ (function next(){if(!replaying)return;q++;if(q>=plan.idx.length){toLive();return}
39331
+ replayTimer=setTimeout(function(){var s=rewindTo(plan.idx[q]);if(s)effects(s);next()},plan.gaps[q-1]*scale)})()})}
39332
+ /* The floating phone: the ONE live frame moves into an always-on-top window (Document Picture-in-
39333
+ Picture; a small ordinary window elsewhere) and back. Two copies would run the app twice and
39334
+ drift apart as the owner taps; the cost of moving is one reload per move. */
39335
+ var pip=null;
39336
+ function canPip(){return !!window.documentPictureInPicture}
39337
+ function paintStrip(s){if(!pip)return;pip.strip.firstChild.textContent=s.current?s.current.stepNumber+"/8 \xB7 "+s.current.step:"";pip.strip.lastChild.textContent=s.journal[0]?s.journal[0].message:""}
39338
+ function fitPip(){if(!pip)return;var w=pip.w.innerWidth,h=pip.w.innerHeight-44;if(w>0&&h>0)pip.frame.style.transform="scale("+Math.min(w/390,h/844)+")"}
39339
+ function mountPip(w,fr){var d=w.document;d.title=cur.app.name+" \xB7 phone";
39340
+ var st=d.createElement("style");st.textContent="html,body{margin:0;height:100%;background:#111;font:12px -apple-system,Segoe UI,Roboto,sans-serif}.pp{position:absolute;top:0;left:0;right:0;bottom:44px;overflow:hidden}.pp iframe{position:absolute;top:0;left:0;width:390px;height:844px;border:0;transform-origin:0 0}.pp.pulse{outline:3px solid #1f7a4d;outline-offset:-3px}.pp.bad{outline-color:#a12a2a}.strip{position:absolute;left:0;right:0;bottom:0;height:44px;padding:4px 10px;box-sizing:border-box;background:#fff;color:#1c1c1a}.strip div:first-child{color:#6b6a64}.strip div:last-child{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}";
39341
+ d.head.appendChild(st);var box=d.createElement("div");box.className="pp";d.body.appendChild(box);
39342
+ var home=fr.parentNode;box.appendChild(fr);
39343
+ var strip=d.createElement("div");strip.className="strip";strip.appendChild(d.createElement("div"));strip.appendChild(d.createElement("div"));d.body.appendChild(strip);
39344
+ pip={w:w,box:box,frame:fr,strip:strip,home:home};
39345
+ var note=el("div","empty","Phone popped out \xB7 ");var back=el("a",null,"Bring back");back.href="#";back.addEventListener("click",function(e){e.preventDefault();bringBack()});note.appendChild(back);note.id="away";home.appendChild(note);
39346
+ w.addEventListener("resize",fitPip);w.addEventListener("pagehide",bringBack);fitPip();paintStrip(cur);paintPop(cur)}
39347
+ var pipRefused=false;
39348
+ function openWindow(fr,afterPip){var w=window.open("","himiPhone","width=340,height=760");if(!w){$("pop").textContent=afterPip?"Open in a window":"Pop-up blocked";return}mountPip(w,fr)}
39349
+ /* A browser can expose Document PiP and still refuse the window (an embedded webview does): fall
39350
+ back to the small window rather than a click that does nothing and says nothing. The refusal may
39351
+ have spent the click's user activation, so a popup opened from it can be blocked: then the button
39352
+ offers the window, and the next click opens it directly with an activation of its own. */
39353
+ function popOut(){var fr=$("focus").querySelector("iframe.vp");if(!fr||pip)return;
39354
+ if(canPip()&&!pipRefused){Promise.resolve().then(function(){return window.documentPictureInPicture.requestWindow({width:320,height:740})}).then(function(w){mountPip(w,fr)},function(){pipRefused=true;openWindow(fr,true)})}
39355
+ else openWindow(fr)}
39356
+ function bringBack(){if(!pip)return;var p=pip;pip=null;var away=document.getElementById("away");if(away&&away.parentNode)away.parentNode.removeChild(away);
39357
+ p.frame.style.transform="";if(p.home&&p.home.isConnected!==false)p.home.appendChild(p.frame);else if(p.frame.parentNode)p.frame.parentNode.removeChild(p.frame);try{p.w.close()}catch(e){}fit();paintPop(cur)}
39358
+ function paintPop(s){var b=$("pop");b.hidden=!(s&&s.live)||rewinding;b.textContent=pip?"Bring back":canPip()&&!pipRefused?"Pop out phone":"Open in a window"}
39359
+ /* The brand reveal (decided board-side, played here only when it ARRIVES as a change) and opt-in
39360
+ read aloud: postcards and milestones, never rebuild messages, the newer cancelling the older. */
39361
+ var revealTimer=null,speakOn=false;try{speakOn=localStorage.getItem("himiStudioSpeak")==="1"}catch(e){}
39362
+ function fig(cap,cls){var f=el("figure",cls||null);f.appendChild(el("figcaption",null,cap));return f}
39363
+ function phoneImg(srcs){var ph=el("div","ph");srcs.forEach(function(p){var i=el("img",p[1]||null);i.src=p[0];i.alt="";ph.appendChild(i)});return ph}
39364
+ function showReveal(r){var row=$("rvrow"),rv=$("rv");clear(row);rv.classList.remove("on");
39365
+ if(r.site){var fs=fig("Your website","site");var si=el("img");si.src=r.site;si.alt="";fs.appendChild(si);row.appendChild(fs)}
39366
+ var same=!r.before||r.before===r.after;
39367
+ if(same){var f1=fig("Your app, your brand");f1.appendChild(phoneImg([[r.after]]));row.appendChild(f1)}
39368
+ else if(reduced()){var fb=fig("Your app, before");fb.appendChild(phoneImg([[r.before]]));row.appendChild(fb);var fa=fig("Your app, your brand");fa.appendChild(phoneImg([[r.after]]));row.appendChild(fa)}
39369
+ else{var fw=fig("Your app");fw.appendChild(phoneImg([[r.before],[r.after,"after"]]));row.appendChild(fw);setTimeout(function(){rv.classList.add("on")},700)}
39370
+ var box=$("reveal");box.hidden=false;box.setAttribute("data-shown",String((+box.getAttribute("data-shown")||0)+1));clearTimeout(revealTimer);revealTimer=setTimeout(hideReveal,6000)}
39371
+ function hideReveal(){$("reveal").hidden=true;clearTimeout(revealTimer)}
39372
+ function speak(t){if(!speakOn||!t)return;try{var ss=window.speechSynthesis;if(!ss||!window.SpeechSynthesisUtterance)return;ss.cancel();var u=new SpeechSynthesisUtterance(t);u.lang=navigator.language||"en";ss.speak(u)}catch(e){}}try{sound=localStorage.getItem("himiStudioSound")==="1";notices=localStorage.getItem("himiStudioNotify")==="1"}catch(e){}
39373
+ var raf=window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(f){return setTimeout(function(){f(Date.now())},16)};
39374
+ function now(){return window.performance&&performance.now?performance.now():Date.now()}
39375
+ function reduced(){try{return matchMedia("(prefers-reduced-motion: reduce)").matches}catch(e){return false}}
39376
+ function tone(f,t,d,v,type){if(!sound)return;try{if(!actx){var AC=window.AudioContext||window.webkitAudioContext;if(!AC)return;actx=new AC()}var o=actx.createOscillator(),g=actx.createGain(),n=actx.currentTime+(t||0),dd=d||.18;o.type=type||"sine";o.frequency.value=f;g.gain.setValueAtTime(0,n);g.gain.linearRampToValueAtTime(v||.05,n+.01);g.gain.exponentialRampToValueAtTime(.0001,n+dd);o.connect(g);g.connect(actx.destination);o.start(n);o.stop(n+dd+.05)}catch(e){}}
39377
+ var SND={tick:function(){tone(1320,0,.05,.025,"triangle")},post:function(){tone(880,0,.08,.03)},step:function(){tone(523);tone(784,.12)},win:function(){tone(523);tone(659,.1);tone(784,.2);tone(1047,.3,.35)},fail:function(){tone(160,0,.3,.05,"sawtooth")}};
39378
+ /* Notices are the BOARD's opt-in, not just the browser's: a permission granted to this origin once
39379
+ (an earlier board, another run) must not switch them on, and the toggle must switch them off. */
39380
+ function notify(title,body){if(replaying)return;try{if(notices&&"Notification" in window&&Notification.permission==="granted"&&document.hidden)new Notification(title,{body:body})}catch(e){}}
39381
+ function accentNow(){return (getComputedStyle(document.documentElement).getPropertyValue("--accent")||"").trim()||"#3b6fd8"}
39382
+ /* The preview lays out at a real phone's viewport (390 x 844 CSS px) and is scaled into the frame.
39383
+ Sized to the frame itself it was ~280px wide: narrower than any phone, so screens overflowed
39384
+ sideways and scrolled horizontally. */
39385
+ function fit(){var ph=$("focus").querySelector(".phone.vpf");if(!ph)return;var w=ph.clientWidth,h=ph.clientHeight;if(w&&h)ph.style.setProperty("--k",String(Math.min(w/390,h/844)))}
39386
+ window.addEventListener("resize",fit);
39387
+ function favicon(n,bad){try{var c=document.createElement("canvas");c.width=c.height=32;var x=c.getContext&&c.getContext("2d");if(!x)return;x.lineWidth=5;x.strokeStyle="rgba(128,128,128,.35)";x.beginPath();x.arc(16,16,12,0,6.2832);x.stroke();x.strokeStyle=accentNow();x.beginPath();x.arc(16,16,12,-1.5708,-1.5708+6.2832*n/8);x.stroke();if(bad){x.fillStyle="#d33";x.beginPath();x.arc(25,7,6,0,6.2832);x.fill()}$("fav").href=c.toDataURL("image/png")}catch(e){}}
39388
+ function burst(){var c=$("fx");c.setAttribute("data-bursts",String((+c.getAttribute("data-bursts")||0)+1));if(reduced())return;var x=c.getContext&&c.getContext("2d");if(!x)return;var W=c.width=innerWidth,H=c.height=innerHeight,cols=["#1f7a4d","#3b6fd8","#e0a030","#d4537e",accentNow()],P=[];for(var i=0;i<120;i++)P.push({x:W/2,y:H*.35,vx:(Math.random()-.5)*14,vy:-Math.random()*12-4,c:cols[i%cols.length],r:Math.random()*6.28});var t0=now();(function f(){x.clearRect(0,0,W,H);P.forEach(function(p){p.x+=p.vx;p.y+=p.vy;p.vy+=.35;p.r+=.2;x.save();x.translate(p.x,p.y);x.rotate(p.r);x.fillStyle=p.c;x.fillRect(-4,-2,8,4);x.restore()});if(now()-t0<2200)raf(f);else x.clearRect(0,0,W,H)})()}
39389
+ function countTo(e,a,b){if(!e)return;if(reduced()){e.textContent=String(b);return}var t0=now();(function f(){var k=Math.min(1,(now()-t0)/900);e.textContent=String(Math.round(a+(b-a)*k));if(k<1)raf(f)})()}
39390
+ function snap(s){var sc={},acc=null;s.screens.forEach(function(x){sc[x.id]=x.img});s.app.palette.forEach(function(p){if(p.name==="accentPrimary")acc=p.hex});return{screens:sc,step:s.current?s.current.stepNumber:0,top:s.journal[0]?s.journal[0].ts:null,build:s.live&&s.live.lastBuild?s.live.lastBuild.at:s.pastBuild?s.pastBuild.at:null,run:s.live?s.live.runId:s.pastBuild?s.pastBuild.run:null,push:s.health.push?s.health.push.releaseId:null,gaps:s.health.test?s.health.test.gaps:null,accent:acc,reveal:s.reveal?s.reveal.at:null}}
39391
+ function buildLine(b){if(!b.ok)return "Build failed \xB7 still showing the last good version";var what=b.screens.length?b.screens.slice(0,3).join(", ")+(b.screens.length>3?" +"+(b.screens.length-3):""):b.tokens?"design tokens":b.config?"app config":"no visible change";return "Rebuilt"+(b.ms!=null?" in "+(b.ms/1000).toFixed(1)+" s":"")+" \xB7 "+what}
39392
+ function effects(s){var is=snap(s),was=seen;seen=is;if(is.accent)document.documentElement.style.setProperty("--accent",is.accent);
39393
+ /* The phone (kept across renders) and the Now line outlive a render: clear what they played last. */
39394
+ var stage=rewinding?$("still"):$("focus"),ph=stage.querySelector(".phone"),nm=$("nowmsg");if(ph)ph.classList.remove("pulse","bad");nm.classList.remove("slidein");if(!was)return;
39395
+ var ticked=false;[].forEach.call($("wall").children,function(d){var id=d.getAttribute("data-id");if(!id)return;if(!(id in was.screens)){d.classList.add("enter");ticked=true}else if(was.screens[id]!==is.screens[id]){d.classList.add("changed");ticked=true}});if(ticked)SND.tick();
39396
+ if(is.top&&is.top!==was.top){void nm.offsetWidth;nm.classList.add("slidein");var li=$("log").firstChild;if(li&&li.classList)li.classList.add("slidein");SND.post()}
39397
+ if(is.step>was.step){var cs=$("steps").children[is.step-1];if(cs)cs.classList.add("fresh");SND.step();notify(s.app.name+" \xB7 step "+is.step+" of 8",s.journal[0]?s.journal[0].message:s.steps[is.step-1])}
39398
+ /* A rebuild is a later build of the SAME run: a run appearing, or restarting, records its first
39399
+ build too, and nothing was rebuilt then. */
39400
+ if(is.build&&was.build&&is.run===was.run&&is.build!==was.build){var b=s.live&&s.live.lastBuild||s.pastBuild;if(ph){void ph.offsetWidth;ph.classList.add("pulse");if(!b.ok)ph.classList.add("bad")}if(pip){pip.box.classList.remove("pulse","bad");void pip.box.offsetWidth;pip.box.classList.add("pulse");if(!b.ok)pip.box.classList.add("bad");pip.strip.lastChild.textContent=buildLine(b)}var tt=el("div","toast"+(b.ok?"":" bad"),buildLine(b));stage.appendChild(tt);setTimeout(function(){if(tt.parentNode)tt.parentNode.removeChild(tt)},3500);if(b.ok)SND.tick();else{SND.fail();notify(s.app.name+" \xB7 build failed",s.live&&s.live.buildError||"")}}
39401
+ if(was.gaps!=null&&is.gaps!=null&&is.gaps!==was.gaps)countTo($("gapn"),was.gaps,is.gaps);
39402
+ if(is.accent&&was.accent&&is.accent!==was.accent){var sw=$("brand").querySelector(".sw");if(sw)sw.classList.add("pop");favicon(is.step,!!(s.live&&s.live.buildError))}
39403
+ var published=is.push&&is.push!==was.push,handed=is.step===8&&was.step<8;if(published||handed){burst();SND.win();notify(s.app.name,published?"Draft published":"Handed over")}
39404
+ var said=false;if(is.top&&is.top!==was.top){speak((s.current?"Step "+s.current.stepNumber+", "+s.current.step+". ":"")+(s.journal[0]?s.journal[0].message:""));said=true}
39405
+ if((published||handed)&&!said)speak(published?"Your draft is ready to open on your phone.":"Your app is handed over.");
39406
+ if(is.reveal&&is.reveal!==was.reveal)showReveal(s.reveal);
39407
+ }
39408
+ function activityAt(s){var m=0;function add(v){var d=v?Date.parse(v):0;if(d>m)m=d}if(s.journal[0])add(s.journal[0].ts);s.screens.forEach(function(x){add(x.changedAt)});if(s.live&&s.live.lastBuild)add(s.live.lastBuild.at);["validate","test","push"].forEach(function(k){if(s.health[k])add(s.health[k].at)});return m}
39409
+ function paintToggles(){var sy=$("say");sy.hidden=!("speechSynthesis" in window);sy.textContent="Read aloud: "+(speakOn?"on":"off");sy.className="tog"+(speakOn?" on":"");sy.setAttribute("aria-pressed",String(speakOn));var b=$("snd");b.textContent=sound?"Sound on":"Sound off";b.className="tog"+(sound?" on":"");b.setAttribute("aria-pressed",String(sound));var nb=$("ntf");if(!("Notification" in window)){nb.hidden=true;return}var p=Notification.permission,on=notices&&p==="granted";nb.hidden=p==="denied";nb.textContent=on?"Notices on":"Notices off";nb.className="tog"+(on?" on":"");nb.setAttribute("aria-pressed",String(on))}
39410
+ function setNotices(v){notices=v;try{localStorage.setItem("himiStudioNotify",v?"1":"0")}catch(e){}paintToggles()}
39411
+ $("snd").addEventListener("click",function(){sound=!sound;try{localStorage.setItem("himiStudioSound",sound?"1":"0")}catch(e){}if(sound){try{if(actx&&actx.resume)actx.resume()}catch(e){}SND.post()}paintToggles()});
39412
+ /* The click acts on what the button SAYS (the effective state), so a remembered opt-in whose browser
39413
+ permission was since reset reads "off" and one click asks for permission, not two. */
39414
+ $("ntf").addEventListener("click",function(){try{var p=Notification.permission;if(notices&&p==="granted"){setNotices(false);return}if(p==="granted"){setNotices(true);return}if(Notification.permission==="default")Promise.resolve(Notification.requestPermission()).then(function(p){setNotices(p==="granted")})}catch(e){}});
39415
+ document.addEventListener("click",function(){try{if(actx&&actx.state==="suspended")actx.resume()}catch(e){}},{once:true});
39416
+ paintToggles();
39417
+ $("slider").addEventListener("input",function(){var i=+this.value;if(replaying)stopReplay();if(!rewinding||!tlStates)enterRewind(i);else rewindTo(i)});
39418
+ $("replay").addEventListener("click",function(){if(replaying){stopReplay();return}replay()});
39419
+ $("golive").addEventListener("click",toLive);
39420
+ $("rvx").addEventListener("click",hideReveal);$("reveal").addEventListener("click",function(e){if(e.target===this)hideReveal()});
39421
+ $("say").addEventListener("click",function(){speakOn=!speakOn;try{localStorage.setItem("himiStudioSpeak",speakOn?"1":"0")}catch(e){}paintToggles();if(speakOn)speak("Read aloud is on.")});
39422
+ $("pop").addEventListener("click",function(){if(pip)bringBack();else popOut()});
39423
+ $("backlive").addEventListener("click",function(e){e.preventDefault();toLive()});
39424
+ document.addEventListener("keydown",function(e){if($("scrub").hidden)return;var tg=e.target,tn=tg&&tg.tagName;
39425
+ if(e.key==="Escape"&&rewinding){toLive();return}
39426
+ if((e.key==="ArrowLeft"||e.key==="ArrowRight")&&!(tg&&tg.id==="slider")){var d=e.key==="ArrowRight"?1:-1;if(!rewinding){if(d<0)enterRewind(d)}else rewindTo(+$("slider").value+d);e.preventDefault()}
39427
+ if(e.key===" "&&tn!=="BUTTON"&&tn!=="INPUT"){e.preventDefault();$("replay").click()}});
39428
+ window.__himiStudio=function(s){lastSeen=Date.now();if(!cur||cur.v!==s.v){cur=s;if(rewinding){$("newdot").hidden=false;return}render(s);effects(s)}else{cur.generatedAt=s.generatedAt}};
39429
+ $("lb").addEventListener("click",function(){this.style.display="none"});
39430
+ window.__himiStudio(window.__himiInitial);
39431
+ setInterval(function(){var sc=document.createElement("script");sc.src="state.js?t="+Date.now();sc.onload=sc.onerror=function(){sc.remove()};document.head.appendChild(sc)},2000);
39432
+ var ticks=0;
39433
+ setInterval(function(){if(!cur)return;if(++ticks%60===0&&!rewinding)render(cur);var start=cur.startedAt?Date.parse(cur.startedAt):Date.now();var s=Math.max(0,Math.floor((Date.now()-start)/1000));var hh=Math.floor(s/3600),mm=Math.floor(s%3600/60),ss=s%60;
39434
+ $("clock").textContent=(hh?hh+":":"")+(mm<10&&hh?"0":"")+mm+":"+(ss<10?"0":"")+ss;
39435
+ $("updated").textContent="updated "+ago(cur.generatedAt);$("live").className="live"+(Date.now()-lastSeen>10000?" stale":"");
39436
+ var a=activityAt(cur),q=a?Date.now()-a:0,done=cur.current&&cur.current.stepNumber===8;
39437
+ $("agent").className="live"+(!a?" idle":done?"":q>5*60*1000?" quiet":"");$("agentt").textContent=!a?"Waiting for the agent":done?"Handed over":q>5*60*1000?"Quiet for "+Math.floor(q/60000)+" min":"Agent active"},1000);
39438
+ })();
39439
+ </script>
39440
+ </body>
39441
+ </html>
39442
+ `;
39443
+ }
39444
+ function escapeHtml(s) {
39445
+ return s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
39446
+ }
39447
+ var scriptJson;
39448
+ var init_studio_page = __esm({
39449
+ "src/studio-page.ts"() {
39450
+ scriptJson = (value) => JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
39451
+ }
39452
+ });
39453
+
39033
39454
  // src/progress.ts
39034
39455
  var progress_exports = {};
39035
39456
  __export(progress_exports, {
@@ -39276,6 +39697,152 @@ var init_progress = __esm({
39276
39697
  }
39277
39698
  });
39278
39699
 
39700
+ // src/studio-timeline.ts
39701
+ import { existsSync as existsSync39, readFileSync as readFileSync42 } from "node:fs";
39702
+ import { join as join36 } from "node:path";
39703
+ function stateOf(view) {
39704
+ const j = view.journal[0];
39705
+ const h = view.health;
39706
+ const b = view.live?.lastBuild ?? null;
39707
+ return {
39708
+ step: view.current ? { n: view.current.stepNumber, name: view.current.step } : null,
39709
+ now: j ? { ts: j.ts, message: j.message, images: j.images } : null,
39710
+ screens: Object.fromEntries(view.screens.map((s) => [s.id, s.img])),
39711
+ palette: view.app.palette.map((p) => [p.name, p.hex]),
39712
+ checks: {
39713
+ validate: h.validate ? { ok: h.validate.ok, errors: h.validate.errors } : null,
39714
+ test: h.test ? { screens: h.test.screens, actions: h.test.actions, gaps: h.test.gaps, ok: h.test.ok } : null,
39715
+ push: h.push ? { draft: h.push.draft, releaseId: h.push.releaseId } : null
39716
+ },
39717
+ build: b && view.live ? { ok: b.ok, ms: b.ms, screens: b.screens, at: b.at, run: view.live.runId } : null
39718
+ };
39719
+ }
39720
+ function frameFor(state, prev, ts2, v, key2) {
39721
+ if (key2 || !prev) return { ts: ts2, v, key: true, ...state };
39722
+ const f = { ts: ts2, v };
39723
+ if (!same(state.step, prev.step)) f.step = state.step;
39724
+ if (!same(state.now, prev.now)) f.now = state.now;
39725
+ const screens = {};
39726
+ for (const [id, img] of Object.entries(state.screens)) if (prev.screens[id] !== img) screens[id] = img;
39727
+ for (const id of Object.keys(prev.screens)) if (!(id in state.screens)) screens[id] = null;
39728
+ if (Object.keys(screens).length) f.screens = screens;
39729
+ if (!same(state.palette, prev.palette)) f.palette = state.palette;
39730
+ if (!same(state.checks, prev.checks)) f.checks = state.checks;
39731
+ if (!same(state.build, prev.build)) f.build = state.build;
39732
+ return f;
39733
+ }
39734
+ function fold(prev, f) {
39735
+ const base = f.key || !prev ? EMPTY : prev;
39736
+ const screens = f.key ? {} : { ...base.screens };
39737
+ for (const [id, img] of Object.entries(f.screens ?? {})) {
39738
+ if (img === null) delete screens[id];
39739
+ else screens[id] = img;
39740
+ }
39741
+ return {
39742
+ step: "step" in f ? f.step ?? null : base.step,
39743
+ now: "now" in f ? f.now ?? null : base.now,
39744
+ screens,
39745
+ palette: f.palette ?? base.palette,
39746
+ checks: f.checks ?? base.checks,
39747
+ build: "build" in f ? f.build ?? null : base.build
39748
+ };
39749
+ }
39750
+ function validFrame(f) {
39751
+ if (!isObj(f) || !isStr(f.ts) || !isStr(f.v)) return false;
39752
+ if (f.key !== void 0 && f.key !== true) return false;
39753
+ if ("step" in f && !nullOr(f.step, (x) => typeof x.n === "number" && isStr(x.name))) return false;
39754
+ if ("now" in f && !nullOr(f.now, (x) => isStr(x.ts) && isStr(x.message) && strs(x.images))) return false;
39755
+ if ("screens" in f && !(isObj(f.screens) && Object.values(f.screens).every((x) => x === null || isStr(x)))) return false;
39756
+ if ("palette" in f && !(Array.isArray(f.palette) && f.palette.every((p) => Array.isArray(p) && p.length === 2 && p.every(isStr)))) return false;
39757
+ if ("checks" in f) {
39758
+ const c = f.checks;
39759
+ if (!isObj(c)) return false;
39760
+ if (!nullOr(c.validate ?? null, (x) => typeof x.ok === "boolean" && typeof x.errors === "number")) return false;
39761
+ if (!nullOr(c.test ?? null, (x) => ["screens", "actions", "gaps"].every((k) => typeof x[k] === "number") && typeof x.ok === "boolean")) return false;
39762
+ if (!nullOr(c.push ?? null, (x) => typeof x.draft === "boolean" && isStr(x.releaseId))) return false;
39763
+ }
39764
+ if ("build" in f && !nullOr(f.build, (x) => typeof x.ok === "boolean" && (x.ms === null || typeof x.ms === "number") && strs(x.screens) && isStr(x.at) && isStr(x.run))) return false;
39765
+ if ("reveal" in f && !(isObj(f.reveal) && (f.reveal.site === null || isStr(f.reveal.site)) && isStr(f.reveal.before) && isStr(f.reveal.after))) return false;
39766
+ return true;
39767
+ }
39768
+ function readTimeline(dir) {
39769
+ const file = join36(dir, TIMELINE_FILE);
39770
+ if (!existsSync39(file)) return [];
39771
+ const out = [];
39772
+ for (const line of readFileSync42(file, "utf8").split("\n")) {
39773
+ if (!line.trim()) continue;
39774
+ try {
39775
+ const f = JSON.parse(line);
39776
+ if (validFrame(f)) out.push(f);
39777
+ } catch {
39778
+ }
39779
+ }
39780
+ const first = out.findIndex((f) => f.key === true);
39781
+ return first < 0 ? [] : out.slice(first);
39782
+ }
39783
+ function trim(frames) {
39784
+ if (frames.length <= MAX_FRAMES) return frames;
39785
+ for (let i = 1; i < frames.length; i++) if (frames[i].key && frames.length - i <= MAX_FRAMES) return frames.slice(i);
39786
+ return frames.slice(-MAX_FRAMES);
39787
+ }
39788
+ function writeTimelineJs(dir, frames) {
39789
+ writeFileAtomic(join36(dir, TIMELINE_JS), `window.__himiTimeline && window.__himiTimeline(${scriptJson(frames)});
39790
+ `);
39791
+ }
39792
+ function appendFrame(dir, view, reveal = null) {
39793
+ const frames = readTimeline(dir);
39794
+ const last = frames[frames.length - 1];
39795
+ if (last && last.v === view.v && !reveal) {
39796
+ if (!existsSync39(join36(dir, TIMELINE_JS))) writeTimelineJs(dir, frames);
39797
+ return frames.length;
39798
+ }
39799
+ let prev = null;
39800
+ for (const f of frames) prev = fold(prev, f);
39801
+ const frame = frameFor(stateOf(view), prev, view.generatedAt, view.v, frames.length % KEYFRAME_EVERY === 0);
39802
+ if (reveal) frame.reveal = reveal;
39803
+ const kept = trim([...frames, frame]);
39804
+ writeFileAtomic(join36(dir, TIMELINE_FILE), kept.map((f) => JSON.stringify(f)).join("\n") + "\n");
39805
+ writeTimelineJs(dir, kept);
39806
+ return kept.length;
39807
+ }
39808
+ function brandLanded(view, first) {
39809
+ const was = accentOf(first?.palette);
39810
+ const is = accentOf(view.app.palette);
39811
+ return was !== null && is !== null && was !== is || view.journal.some((j) => j.step === "brand");
39812
+ }
39813
+ function revealFor(view, memory) {
39814
+ if (memory.revealedAt || !memory.brandSeenAt || !view.focus || !view.focus.frames.length) return null;
39815
+ const latest = view.focus.frames[view.focus.frames.length - 1];
39816
+ if (latest.ts < memory.brandSeenAt) return null;
39817
+ const firstVersion = memory.timeline.find((t) => t.id === view.focus.id);
39818
+ const before = firstVersion ? `../history/${firstVersion.sha}.${firstVersion.ext}` : latest.img;
39819
+ return { site: view.brand.site, before, after: latest.img };
39820
+ }
39821
+ var TIMELINE_FILE, TIMELINE_JS, MAX_FRAMES, KEYFRAME_EVERY, same, EMPTY, isStr, isObj, strs, nullOr, accentOf;
39822
+ var init_studio_timeline = __esm({
39823
+ "src/studio-timeline.ts"() {
39824
+ init_progress();
39825
+ init_studio_page();
39826
+ TIMELINE_FILE = join36(".himi", "studio", "timeline.jsonl");
39827
+ TIMELINE_JS = join36(".himi", "studio", "timeline.js");
39828
+ MAX_FRAMES = 2e3;
39829
+ KEYFRAME_EVERY = 50;
39830
+ same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
39831
+ EMPTY = { step: null, now: null, screens: {}, palette: [], checks: { validate: null, test: null, push: null }, build: null };
39832
+ isStr = (x) => typeof x === "string";
39833
+ isObj = (x) => typeof x === "object" && x !== null && !Array.isArray(x);
39834
+ strs = (x) => Array.isArray(x) && x.every(isStr);
39835
+ nullOr = (x, ok) => x === null || isObj(x) && ok(x);
39836
+ accentOf = (palette2) => {
39837
+ for (const p of palette2 ?? []) {
39838
+ const [name, hex2] = Array.isArray(p) ? p : [p.name, p.hex];
39839
+ if (name === "accentPrimary") return hex2;
39840
+ }
39841
+ return null;
39842
+ };
39843
+ }
39844
+ });
39845
+
39279
39846
  // src/studio.ts
39280
39847
  var studio_exports = {};
39281
39848
  __export(studio_exports, {
@@ -39294,8 +39861,8 @@ __export(studio_exports, {
39294
39861
  writeStudioRecord: () => writeStudioRecord
39295
39862
  });
39296
39863
  import { createHash as createHash20 } from "node:crypto";
39297
- import { existsSync as existsSync39, readdirSync as readdirSync20, readFileSync as readFileSync42, realpathSync as realpathSync4, statSync as statSync8 } from "node:fs";
39298
- import { basename as basename9, extname as extname3, join as join36, relative as relative6, resolve as resolve33, sep as sep9 } from "node:path";
39864
+ import { existsSync as existsSync40, readdirSync as readdirSync20, readFileSync as readFileSync43, realpathSync as realpathSync4, statSync as statSync8 } from "node:fs";
39865
+ import { basename as basename9, extname as extname3, join as join37, relative as relative6, resolve as resolve33, sep as sep9 } from "node:path";
39299
39866
  import { pathToFileURL as pathToFileURL7 } from "node:url";
39300
39867
  function realInside(dir, abs) {
39301
39868
  try {
@@ -39320,12 +39887,12 @@ function archiveFile(dir, memory, abs) {
39320
39887
  if (size === 0) return null;
39321
39888
  const key2 = relative6(dir, abs);
39322
39889
  const hit = memory.files?.[key2];
39323
- if (hit && hit.size === size && hit.mtimeMs === mtimeMs && hit.ino === ino && existsSync39(join36(dir, HISTORY_DIR, `${hit.sha}.${hit.ext}`))) {
39890
+ if (hit && hit.size === size && hit.mtimeMs === mtimeMs && hit.ino === ino && existsSync40(join37(dir, HISTORY_DIR, `${hit.sha}.${hit.ext}`))) {
39324
39891
  return { sha: hit.sha, ext: hit.ext, file: `${hit.sha}.${hit.ext}` };
39325
39892
  }
39326
39893
  let archived;
39327
39894
  try {
39328
- archived = archiveBytes(dir, readFileSync42(real));
39895
+ archived = archiveBytes(dir, readFileSync43(real));
39329
39896
  } catch {
39330
39897
  return null;
39331
39898
  }
@@ -39334,17 +39901,17 @@ function archiveFile(dir, memory, abs) {
39334
39901
  return { sha: archived.sha256, ext: archived.ext, file: `${archived.sha256}.${archived.ext}` };
39335
39902
  }
39336
39903
  function loadMemory(dir, now) {
39337
- const raw = obj(readJson2(join36(dir, MEMORY_FILE)));
39904
+ const raw = obj(readJson2(join37(dir, MEMORY_FILE)));
39338
39905
  if (raw && raw.version === 1 && obj(raw.shots) && Array.isArray(raw.timeline)) return raw;
39339
39906
  return { version: 1, createdAt: now, shots: {}, timeline: [] };
39340
39907
  }
39341
39908
  function observeShots(dir, memory, now) {
39342
- const shotsDir = join36(dir, ".himi", "shots");
39909
+ const shotsDir = join37(dir, ".himi", "shots");
39343
39910
  const seen = /* @__PURE__ */ new Set();
39344
- const files = existsSync39(shotsDir) ? readdirSync20(shotsDir).filter((f) => /\.(png|jpe?g)$/i.test(f)).sort() : [];
39911
+ const files = existsSync40(shotsDir) ? readdirSync20(shotsDir).filter((f) => /\.(png|jpe?g)$/i.test(f)).sort() : [];
39345
39912
  for (const f of files) {
39346
39913
  const id = basename9(f, extname3(f));
39347
- const archived = archiveFile(dir, memory, join36(shotsDir, f));
39914
+ const archived = archiveFile(dir, memory, join37(shotsDir, f));
39348
39915
  if (!archived) continue;
39349
39916
  seen.add(id);
39350
39917
  const { sha, ext } = archived;
@@ -39359,14 +39926,14 @@ function observeShots(dir, memory, now) {
39359
39926
  memory.shots[id] = { ...prev, present: true };
39360
39927
  }
39361
39928
  }
39362
- if (existsSync39(shotsDir)) {
39929
+ if (existsSync40(shotsDir)) {
39363
39930
  for (const [id, shot] of Object.entries(memory.shots)) if (!seen.has(id) && shot.present) memory.shots[id] = { ...shot, present: false };
39364
39931
  }
39365
39932
  if (memory.timeline.length > MAX_TIMELINE) memory.timeline = memory.timeline.slice(-MAX_TIMELINE);
39366
39933
  return memory;
39367
39934
  }
39368
39935
  function palette(dir) {
39369
- const colors = obj(obj(readJson2(join36(dir, "tokens.json")))?.colors);
39936
+ const colors = obj(obj(readJson2(join37(dir, "tokens.json")))?.colors);
39370
39937
  if (!colors) return [];
39371
39938
  const pick2 = (key2) => {
39372
39939
  const v = colors[key2];
@@ -39376,7 +39943,7 @@ function palette(dir) {
39376
39943
  return ["accentPrimary", "accentSecondary", "accentSurface", "surfacePrimary", "textPrimary"].map(pick2).filter((x) => !!x);
39377
39944
  }
39378
39945
  function appIdentity(dir) {
39379
- const manifest = obj(readJson2(join36(dir, "himalaya.content.json")));
39946
+ const manifest = obj(readJson2(join37(dir, "himalaya.content.json")));
39380
39947
  const name = text(manifest?.name) ?? basename9(resolve33(dir));
39381
39948
  const icon = text(manifest?.icon)?.trim();
39382
39949
  const iconPath = icon ? resolve33(dir, icon) : null;
@@ -39384,12 +39951,12 @@ function appIdentity(dir) {
39384
39951
  return { name, iconFile: real && statSync8(real).isFile() ? iconPath : null };
39385
39952
  }
39386
39953
  function testRuns(dir) {
39387
- const att = join36(dir, ".himi", "attestations");
39388
- if (!existsSync39(att)) return [];
39954
+ const att = join37(dir, ".himi", "attestations");
39955
+ if (!existsSync40(att)) return [];
39389
39956
  const runs = [];
39390
39957
  for (const f of readdirSync20(att)) {
39391
39958
  if (!f.endsWith(".json") || f === "latest.json") continue;
39392
- const r = obj(readJson2(join36(att, f)));
39959
+ const r = obj(readJson2(join37(att, f)));
39393
39960
  if (!r || r.tool !== "himi-test" || typeof r.runId !== "string") continue;
39394
39961
  const report2 = obj(r.report);
39395
39962
  const coverage = obj(r.coverage);
@@ -39405,20 +39972,20 @@ function testRuns(dir) {
39405
39972
  return runs.sort((a, b) => a.runId.localeCompare(b.runId));
39406
39973
  }
39407
39974
  function siteHome(dir) {
39408
- for (const pages of [join36(dir, ".himi", "business-book", "evidence", "pages"), join36(dir, "evidence", "pages")]) {
39409
- if (!existsSync39(pages)) continue;
39975
+ for (const pages of [join37(dir, ".himi", "business-book", "evidence", "pages"), join37(dir, "evidence", "pages")]) {
39976
+ if (!existsSync40(pages)) continue;
39410
39977
  const home = readdirSync20(pages).filter((f) => /^home.*\.(png|jpe?g)$/i.test(f)).sort()[0];
39411
- if (home) return join36(pages, home);
39978
+ if (home) return join37(pages, home);
39412
39979
  }
39413
39980
  return null;
39414
39981
  }
39415
39982
  function uxStatuses(dir) {
39416
- const platforms = obj(obj(readJson2(join36(dir, ".himi", "ux-audit.json")))?.platforms);
39983
+ const platforms = obj(obj(readJson2(join37(dir, ".himi", "ux-audit.json")))?.platforms);
39417
39984
  if (!platforms) return [];
39418
39985
  return Object.entries(platforms).map(([platform, rec]) => ({ platform, status: text(obj(rec)?.status) ?? "" })).filter((p) => p.status);
39419
39986
  }
39420
39987
  function liveRun(dir) {
39421
- const rec = obj(readJson2(join36(dir, RUN_RECORD)));
39988
+ const rec = obj(readJson2(join37(dir, RUN_RECORD)));
39422
39989
  const url = text(rec?.url);
39423
39990
  const pid = rec?.pid;
39424
39991
  if (!url || !/^https?:\/\//i.test(url) || typeof pid !== "number") return null;
@@ -39468,9 +40035,9 @@ function buildView(dir, memory, now) {
39468
40035
  const runs = testRuns(dir);
39469
40036
  const last = runs[runs.length - 1];
39470
40037
  const prev = runs[runs.length - 2];
39471
- const validate3 = obj(readJson2(join36(dir, VALIDATE_RECORD)));
39472
- const push = obj(readJson2(join36(dir, PUSH_RECORD)));
39473
- const edges = readJson2(join36(dir, EDGES_RECORD));
40038
+ const validate3 = obj(readJson2(join37(dir, VALIDATE_RECORD)));
40039
+ const push = obj(readJson2(join37(dir, PUSH_RECORD)));
40040
+ const edges = readJson2(join37(dir, EDGES_RECORD));
39474
40041
  const nav = [];
39475
40042
  if (Array.isArray(edges)) {
39476
40043
  const seen = /* @__PURE__ */ new Set();
@@ -39508,9 +40075,11 @@ function buildView(dir, memory, now) {
39508
40075
  },
39509
40076
  brand: { site: siteImg },
39510
40077
  nav,
39511
- live: liveRun(dir)
40078
+ live: liveRun(dir),
40079
+ timelineFrames: 0,
40080
+ reveal: memory.reveal ?? null
39512
40081
  };
39513
- const { generatedAt: _ignored, ...stable } = view;
40082
+ const { generatedAt: _ignored, timelineFrames: _count, ...stable } = view;
39514
40083
  const v = createHash20("sha256").update(JSON.stringify(stable)).digest("hex").slice(0, 16);
39515
40084
  return { v, ...view };
39516
40085
  }
@@ -39521,16 +40090,33 @@ function writeBoard(dir, now) {
39521
40090
  if (!realDirIn(dir, STUDIO_DIR)) throw new UnsafeHimiDirError(STUDIO_DIR);
39522
40091
  if (!realDirIn(dir, HISTORY_DIR)) throw new UnsafeHimiDirError(HISTORY_DIR);
39523
40092
  const memory = observeShots(dir, loadMemory(dir, now), now);
39524
- const view = buildView(dir, memory, now);
39525
- writeAtomic(join36(dir, MEMORY_FILE), JSON.stringify(memory));
39526
- writeAtomic(join36(dir, STATE_FILE), `window.__himiStudio && window.__himiStudio(${scriptJson(view)});
40093
+ let view = buildView(dir, memory, now);
40094
+ let reveal = null;
40095
+ try {
40096
+ if (!memory.brandSeenAt && brandLanded(view, readTimeline(dir)[0])) memory.brandSeenAt = now;
40097
+ reveal = revealFor(view, memory);
40098
+ if (reveal) {
40099
+ memory.revealedAt = now;
40100
+ memory.reveal = { at: now, ...reveal };
40101
+ view = buildView(dir, memory, now);
40102
+ }
40103
+ } catch {
40104
+ reveal = null;
40105
+ }
40106
+ try {
40107
+ view.timelineFrames = appendFrame(dir, view, reveal);
40108
+ } catch {
40109
+ view.timelineFrames = 0;
40110
+ }
40111
+ writeAtomic(join37(dir, MEMORY_FILE), JSON.stringify(memory));
40112
+ writeAtomic(join37(dir, STATE_FILE), `window.__himiStudio && window.__himiStudio(${scriptJson(view)});
39527
40113
  `);
39528
- writeAtomic(join36(dir, STUDIO_PAGE), renderStudioHtml(view));
40114
+ writeAtomic(join37(dir, STUDIO_PAGE), renderStudioHtml(view));
39529
40115
  return view;
39530
40116
  }
39531
40117
  function refreshStudio(dir, now = /* @__PURE__ */ new Date()) {
39532
40118
  try {
39533
- if (!existsSync39(join36(dir, STUDIO_PAGE))) return { refreshed: false };
40119
+ if (!existsSync40(join37(dir, STUDIO_PAGE))) return { refreshed: false };
39534
40120
  return { refreshed: true, view: writeBoard(dir, now.toISOString()) };
39535
40121
  } catch {
39536
40122
  return { refreshed: false };
@@ -39538,225 +40124,38 @@ function refreshStudio(dir, now = /* @__PURE__ */ new Date()) {
39538
40124
  }
39539
40125
  function openStudio(dir, now = /* @__PURE__ */ new Date()) {
39540
40126
  const view = writeBoard(dir, now.toISOString());
39541
- const page = join36(dir, STUDIO_PAGE);
40127
+ const page = join37(dir, STUDIO_PAGE);
39542
40128
  return { page, url: pathToFileURL7(page).href, view };
39543
40129
  }
39544
40130
  function writeStudioRecord(dir, record2, value) {
39545
40131
  try {
39546
40132
  if (!realDirIn(dir, ".himi")) return;
39547
- writeAtomic(join36(dir, record2), `${JSON.stringify(value, null, 2)}
40133
+ writeAtomic(join37(dir, record2), `${JSON.stringify(value, null, 2)}
39548
40134
  `);
39549
40135
  } catch {
39550
40136
  }
39551
40137
  }
39552
- function renderStudioHtml(initial) {
39553
- const accent = initial.app.palette.find((p) => p.name === "accentPrimary")?.hex ?? "#3b6fd8";
39554
- return `<!doctype html>
39555
- <html lang="en">
39556
- <head>
39557
- <meta charset="utf-8">
39558
- <meta name="viewport" content="width=device-width, initial-scale=1">
39559
- <title>${escapeHtml(initial.app.name)} \xB7 build board</title>
39560
- <link rel="icon" id="fav" href="data:,">
39561
- <style>
39562
- :root{--bg:#f6f5f2;--card:#fff;--ink:#1c1c1a;--muted:#6b6a64;--faint:#a3a19a;--line:#e6e3dc;--ok:#1f7a4d;--okbg:#e3f3ea;--warn:#8a5a00;--warnbg:#fbefd6;--bad:#a12a2a;--badbg:#fbe4e4;--accent:${accent};--shadow:0 1px 2px rgba(0,0,0,.04),0 4px 16px rgba(0,0,0,.04)}
39563
- @media (prefers-color-scheme:dark){:root{--bg:#121211;--card:#1c1c1a;--ink:#ecebe6;--muted:#a3a19a;--faint:#6b6a64;--line:#2c2b28;--okbg:#15301f;--ok:#7fd3a2;--warnbg:#33270f;--warn:#f0c46b;--badbg:#3a1616;--bad:#f09a9a;--shadow:none}}
39564
- *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
39565
- .wrap{max-width:1180px;margin:0 auto;padding:20px 20px 60px}
39566
- header{display:flex;align-items:center;gap:12px;margin-bottom:16px}
39567
- .icon{width:40px;height:40px;border-radius:10px;background:var(--accent);object-fit:cover;flex:none}
39568
- h1{font-size:20px;font-weight:600;margin:0}.sub{color:var(--muted);font-size:13px}
39569
- .right{margin-left:auto;text-align:right}.clock{font-size:22px;font-variant-numeric:tabular-nums;font-weight:600}
39570
- .live{display:inline-flex;align-items:center;gap:6px;color:var(--muted);font-size:12px}.dot{width:8px;height:8px;border-radius:50%;background:var(--ok)}.stale .dot{background:var(--faint)}
39571
- .steps{display:grid;grid-template-columns:repeat(8,1fr);gap:6px;margin:4px 0 16px}.step .bar{height:6px;border-radius:3px;background:var(--line)}.step.done .bar{background:var(--ok)}.step.cur .bar{background:var(--accent)}
39572
- .step .lbl{font-size:11px;color:var(--faint);margin-top:4px;text-transform:capitalize}.step.cur .lbl{color:var(--ink);font-weight:600}
39573
- .card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px;box-shadow:var(--shadow)}
39574
- .now{display:flex;gap:12px;align-items:flex-start;margin-bottom:16px}.now .msg{font-size:16px}.now .meta{color:var(--muted);font-size:12px}
39575
- .grid{display:grid;grid-template-columns:320px 1fr;gap:16px;align-items:start}@media (max-width:860px){.grid{grid-template-columns:1fr}.steps .lbl{display:none}}
39576
- h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin:0 0 10px}
39577
- .phone{width:100%;max-width:300px;aspect-ratio:390/844;border-radius:34px;border:9px solid #111;background:#fff;overflow:hidden;margin:0 auto;display:block}
39578
- .phone img,.phone iframe{width:100%;height:100%;object-fit:cover;object-position:top;border:0;display:block;cursor:zoom-in}
39579
- .phone.vpf{position:relative}.phone iframe.vp{position:absolute;top:0;left:0;width:390px;height:844px;transform-origin:0 0;transform:scale(var(--k,.72));cursor:auto}
39580
- .cap{text-align:center;color:var(--muted);font-size:12px;margin-top:8px}
39581
- .film{display:flex;gap:6px;overflow-x:auto;margin-top:12px;padding-bottom:4px}.film img{width:48px;aspect-ratio:390/844;object-fit:cover;object-position:top;border-radius:8px;border:1px solid var(--line);cursor:zoom-in}
39582
- .chips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px}.chip{font-size:12px;padding:5px 10px;border-radius:999px;background:var(--card);border:1px solid var(--line);color:var(--muted)}
39583
- .chip.ok{background:var(--okbg);color:var(--ok);border-color:transparent}.chip.warn{background:var(--warnbg);color:var(--warn);border-color:transparent}.chip.bad{background:var(--badbg);color:var(--bad);border-color:transparent}
39584
- .wall{display:grid;grid-template-columns:repeat(auto-fill,minmax(112px,1fr));gap:14px}
39585
- .shot{text-align:center}.shot .frame{aspect-ratio:390/844;border-radius:16px;border:5px solid #111;overflow:hidden;background:#fff}.shot img{width:100%;height:100%;object-fit:cover;object-position:top;display:block;cursor:zoom-in}
39586
- .shot .id{font-size:12px;margin-top:6px;word-break:break-all}.badge{display:inline-block;font-size:10px;padding:1px 7px;border-radius:999px;margin-top:3px}.badge.new{background:var(--okbg);color:var(--ok)}.badge.chg{background:var(--warnbg);color:var(--warn)}
39587
- .section{margin-bottom:16px}.brand{display:flex;gap:14px;align-items:center;flex-wrap:wrap}.brand .site{width:220px;border-radius:10px;border:1px solid var(--line);cursor:zoom-in}
39588
- .sw{display:flex;gap:6px}.sw span{width:22px;height:22px;border-radius:50%;border:1px solid var(--line)}
39589
- .nav{display:flex;flex-wrap:wrap;gap:6px}.nav span{font-size:12px;padding:3px 8px;border-radius:6px;background:var(--bg);border:1px solid var(--line)}
39590
- .log{list-style:none;margin:0;padding:0}.log li{display:flex;gap:10px;padding:10px 0;border-top:1px solid var(--line)}.log li:first-child{border-top:0}
39591
- .log .t{color:var(--faint);font-size:12px;min-width:64px;font-variant-numeric:tabular-nums}.log .s{font-size:11px;color:var(--muted);text-transform:capitalize}.log .imgs{display:flex;gap:6px;margin-top:6px}.log .imgs img{height:72px;border-radius:8px;border:1px solid var(--line);cursor:zoom-in}
39592
- .empty{color:var(--faint);font-size:13px;padding:8px 0}
39593
- #lb{position:fixed;inset:0;background:rgba(0,0,0,.72);display:none;align-items:center;justify-content:center;z-index:9;cursor:zoom-out}#lb img{max-height:92vh;max-width:92vw;border-radius:14px;background:#fff}
39594
- .enter{animation:enter .7s cubic-bezier(.2,.8,.2,1) both}@keyframes enter{from{opacity:0;transform:translateY(10px) scale(.94);filter:grayscale(1)}to{opacity:1;transform:none;filter:none}}
39595
- .changed .frame{animation:chg .9s ease both}@keyframes chg{35%{opacity:.25;transform:scale(.96)}100%{opacity:1;transform:none}}
39596
- .step.fresh .bar{animation:fill .7s ease both;transform-origin:left}@keyframes fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
39597
- .slidein{animation:slide .45s cubic-bezier(.2,.8,.2,1) both}@keyframes slide{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
39598
- .phone.pulse{animation:pulse 1s ease-out}.phone.pulse.bad{animation-name:pulsebad}@keyframes pulse{from{outline:3px solid var(--ok);outline-offset:0}to{outline:3px solid transparent;outline-offset:10px}}@keyframes pulsebad{from{outline:3px solid var(--bad);outline-offset:0}to{outline:3px solid transparent;outline-offset:10px}}
39599
- .toast{display:block;width:max-content;max-width:100%;margin:8px auto 0;font-size:12px;padding:4px 10px;border-radius:999px;background:var(--okbg);color:var(--ok);animation:toast 3.4s ease both}.toast.bad{background:var(--badbg);color:var(--bad)}@keyframes toast{0%{opacity:0;transform:translateY(4px)}10%,80%{opacity:1;transform:none}100%{opacity:0}}
39600
- .icon,.step.cur .bar{transition:background-color .8s}.sw.pop span{animation:pop .6s ease both}@keyframes pop{50%{transform:scale(1.25)}}
39601
- .quiet{color:var(--warn)}.quiet .dot{background:var(--warn)}.idle .dot{background:var(--faint)}
39602
- .tog{font:inherit;font-size:12px;color:var(--muted);background:none;border:1px solid var(--line);border-radius:999px;padding:2px 9px;margin:6px 0 0 6px;cursor:pointer}.tog.on{color:var(--ink);border-color:var(--ink)}
39603
- #fx{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:8}
39604
- @media (max-width:560px){header{flex-wrap:wrap}.right{margin-left:0;width:100%;text-align:left;display:flex;flex-wrap:wrap;align-items:center;gap:4px 12px}.right .clock{font-size:18px}.tog{margin:0 6px 0 0}}
39605
- @media (prefers-reduced-motion:reduce){.enter,.changed .frame,.step.fresh .bar,.slidein,.phone.pulse,.sw.pop span,.toast{animation:none}.icon,.step.cur .bar{transition:none}}
39606
- </style>
39607
- </head>
39608
- <body>
39609
- <div class="wrap">
39610
- <header><img class="icon" id="icon" alt=""><div><h1 id="name"></h1><div class="sub">Build board \xB7 updates itself as the app is built</div></div>
39611
- <div class="right"><div class="clock" id="clock">00:00</div><div class="live" id="live"><span class="dot"></span><span id="updated">live</span></div>
39612
- <div><span class="live idle" id="agent"><span class="dot"></span><span id="agentt">Waiting for the agent</span></span></div>
39613
- <div><button class="tog" id="snd" type="button" aria-pressed="false">Sound off</button><button class="tog" id="ntf" type="button" aria-pressed="false" hidden>Notices off</button></div></div></header>
39614
- <div class="steps" id="steps"></div>
39615
- <div class="card now"><div style="flex:1"><div class="meta" id="nowmeta">Now</div><div class="msg" id="nowmsg"></div></div></div>
39616
- <div class="grid">
39617
- <div><div class="card"><h2 id="focuslbl">Latest</h2><div id="focus"></div><div class="film" id="film"></div></div></div>
39618
- <div>
39619
- <div class="chips" id="chips"></div>
39620
- <div class="card section"><h2>Screens</h2><div class="wall" id="wall"></div></div>
39621
- <div class="card section" id="brandcard"><h2>Brand</h2><div class="brand" id="brand"></div></div>
39622
- <div class="card section" id="navcard"><h2>Navigation the test crawl followed</h2><div class="nav" id="nav"></div></div>
39623
- <div class="card section"><h2>Postcards</h2><ul class="log" id="log"></ul></div>
39624
- </div></div></div>
39625
- <div id="lb"><img alt=""></div>
39626
- <canvas id="fx" aria-hidden="true"></canvas>
39627
- <script>window.__himiInitial = ${scriptJson(initial)};</script>
39628
- <script>
39629
- (function(){
39630
- var cur=null, lastSeen=Date.now();
39631
- function $(id){return document.getElementById(id)}
39632
- function el(tag,cls,txt){var e=document.createElement(tag);if(cls)e.className=cls;if(txt!=null)e.textContent=txt;return e}
39633
- function clear(e){while(e.firstChild)e.removeChild(e.firstChild)}
39634
- function ago(ts){var s=Math.max(0,Math.round((Date.now()-Date.parse(ts))/1000));if(s<60)return s+"s ago";var m=Math.round(s/60);if(m<60)return m+" min ago";return Math.round(m/60)+" h ago"}
39635
- function hhmm(ts){var d=new Date(ts);return d.toLocaleTimeString([], {hour:"2-digit",minute:"2-digit"})}
39636
- function recent(ts){return Date.now()-Date.parse(ts)<10*60*1000}
39637
- function zoomable(img){img.addEventListener("click",function(){var lb=$("lb");lb.firstChild.src=img.src;lb.style.display="flex"})}
39638
- function img(src,cls){var i=el("img",cls);i.src=src;i.alt="";i.loading="lazy";zoomable(i);return i}
39639
- function chip(txt,kind){return el("span","chip"+(kind?" "+kind:""),txt)}
39640
- function render(s){
39641
- var sn=s.current?s.current.stepNumber:0;document.title=(sn?"("+sn+"/8) ":"")+s.app.name+" \xB7 build board";favicon(sn,!!(s.live&&s.live.buildError));$("name").textContent=s.app.name;
39642
- if(s.app.icon){$("icon").src=s.app.icon}else{$("icon").removeAttribute("src")}
39643
- var steps=$("steps");clear(steps);var n=s.current?s.current.stepNumber:0;
39644
- s.steps.forEach(function(name,i){var d=el("div","step"+(i+1<n?" done":i+1===n?" cur":""));d.appendChild(el("div","bar"));d.appendChild(el("div","lbl",(i+1)+". "+name));steps.appendChild(d)});
39645
- var last=s.journal[0];
39646
- $("nowmsg").textContent=last?last.message:"Waiting for the first postcard.";
39647
- $("nowmeta").textContent=last?("Now \xB7 "+(last.step?("step "+last.stepNumber+" of 8, "+last.step+" \xB7 "):"")+ago(last.ts)):"Now";
39648
- var focus=$("focus");var film=$("film");clear(film);
39649
- /* The live frame is KEPT across renders while its URL and its run are unchanged (a restarted run on
39650
- the same port is a different server, and the old frame would never see its builds). A new iframe is a full page
39651
- load of the preview: every postcard, rebuild and the once-a-minute re-render used to throw away
39652
- the in-place repaint, the preview's scroll and wherever the owner had navigated inside it. */
39653
- var kept=s.live?focus.querySelector("iframe"):null;if(kept&&(kept.getAttribute("src")!==s.live.url||kept.getAttribute("data-run")!==s.live.runId))kept=null;
39654
- if(kept){while(focus.lastChild&&focus.lastChild!==kept.parentNode)focus.removeChild(focus.lastChild)}else clear(focus);
39655
- if(s.live){$("focuslbl").textContent="Live preview";if(!kept){var f=el("div","phone vpf");var fr=document.createElement("iframe");fr.className="vp";fr.setAttribute("width","390");fr.setAttribute("height","844");fr.setAttribute("data-run",s.live.runId);fr.src=s.live.url;fr.title="Live preview";f.appendChild(fr);focus.appendChild(f)}fit();if(s.live.buildError){var be=el("div","chip bad",null);be.style.display="block";be.style.marginTop="8px";be.textContent="Last edit did not build, still showing the last good version: "+s.live.buildError;focus.appendChild(be)}focus.appendChild(el("div","cap",(s.live.watching?"Repaints as the app is edited \xB7 ":"The build this run started with; himi run --watch repaints on every edit \xB7 ")+s.live.url))}
39656
- else if(s.focus&&s.focus.frames.length){var fl=s.focus.frames;$("focuslbl").textContent="Latest \xB7 "+s.focus.id;var p=el("div","phone");p.appendChild(img(fl[fl.length-1].img));focus.appendChild(p);focus.appendChild(el("div","cap",fl.length+(fl.length===1?" version":" versions")+" so far"))}
39657
- else{$("focuslbl").textContent="Latest";focus.appendChild(el("div","empty","The first render appears here."))}
39658
- if(s.focus&&s.focus.frames.length>1)s.focus.frames.forEach(function(fm){film.appendChild(img(fm.img))});
39659
- var chips=$("chips");clear(chips);var h=s.health;
39660
- if(h.validate)chips.appendChild(chip(h.validate.ok?"Validate passing":"Validate: "+h.validate.errors+" errors",h.validate.ok?"ok":"bad"));
39661
- if(h.test){var t=h.test;var moved=t.prevGaps!=null&&t.prevGaps!==t.gaps;var tc=chip("Test crawl: "+t.screens+" screens, "+t.actions+" actions, "+(moved?"gaps "+t.prevGaps+" \u2192 ":""),t.ok?"ok":"bad");var gn=el("span",null,String(t.gaps));gn.id="gapn";tc.appendChild(gn);if(!moved)tc.appendChild(document.createTextNode(" gaps"));chips.appendChild(tc)}
39662
- h.ux.forEach(function(u){chips.appendChild(chip("UX drive "+u.platform+": "+u.status,u.status==="passed"?"ok":u.status==="failed"?"bad":"warn"))});
39663
- if(h.push)chips.appendChild(chip((h.push.draft?"Draft published":"Published live")+" \xB7 "+h.push.releaseId.slice(0,10),h.push.draft?"warn":"ok"));
39664
- if(!chips.firstChild)chips.appendChild(chip("Checks appear here as validate, test and push run"));
39665
- var wall=$("wall");clear(wall);
39666
- if(!s.screens.length)wall.appendChild(el("div","empty","Screens appear here as they render."));
39667
- s.screens.forEach(function(sc){var d=el("div","shot");d.setAttribute("data-id",sc.id);var fr=el("div","frame");fr.appendChild(img(sc.img));d.appendChild(fr);d.appendChild(el("div","id",sc.id));
39668
- if(recent(sc.firstSeen)&&sc.versions===1)d.appendChild(el("span","badge new","New"));else if(recent(sc.changedAt)&&sc.versions>1)d.appendChild(el("span","badge chg","Changed"));wall.appendChild(d)});
39669
- var brand=$("brand");clear(brand);$("brandcard").style.display=(s.brand.site||s.app.palette.length)?"":"none";
39670
- if(s.brand.site){var si=img(s.brand.site,"site");brand.appendChild(si)}
39671
- if(s.brand.site&&s.focus&&s.focus.frames.length){brand.appendChild(el("span","sub","\u2192"));var pf=el("div","shot");pf.style.width="90px";var ff=el("div","frame");ff.appendChild(img(s.focus.frames[s.focus.frames.length-1].img));pf.appendChild(ff);brand.appendChild(pf)}
39672
- if(s.app.palette.length){var sw=el("div","sw");s.app.palette.forEach(function(c){var d=el("span");d.style.background=c.hex;d.title=c.name+" "+c.hex;sw.appendChild(d)});brand.appendChild(sw)}
39673
- var nav=$("nav");clear(nav);$("navcard").style.display=s.nav.length?"":"none";s.nav.forEach(function(e){nav.appendChild(el("span",null,e.from+" \u2192 "+e.to))});
39674
- var log=$("log");clear(log);if(!s.journal.length)log.appendChild(el("li","empty","Postcards the agent journals appear here."));
39675
- s.journal.forEach(function(j){var li=el("li");li.appendChild(el("div","t",hhmm(j.ts)));var body=el("div");body.appendChild(el("div",null,j.message));if(j.step)body.appendChild(el("div","s","step "+j.stepNumber+" \xB7 "+j.step));
39676
- if(j.images.length){var row=el("div","imgs");j.images.forEach(function(u){row.appendChild(img(u))});body.appendChild(row)}li.appendChild(body);log.appendChild(li)});
39677
- }
39678
- /* Motion, sound and notices answer a CHANGE, never a redraw: effects() compares what is on the page
39679
- now with what it showed last, so opening the board, the once-a-minute redraw and the two-second
39680
- poll of an unchanged state do nothing, and each effect names a real event. */
39681
- var seen=null,actx=null,sound=false,notices=false;try{sound=localStorage.getItem("himiStudioSound")==="1";notices=localStorage.getItem("himiStudioNotify")==="1"}catch(e){}
39682
- var raf=window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(f){return setTimeout(function(){f(Date.now())},16)};
39683
- function now(){return window.performance&&performance.now?performance.now():Date.now()}
39684
- function reduced(){try{return matchMedia("(prefers-reduced-motion: reduce)").matches}catch(e){return false}}
39685
- function tone(f,t,d,v,type){if(!sound)return;try{if(!actx){var AC=window.AudioContext||window.webkitAudioContext;if(!AC)return;actx=new AC()}var o=actx.createOscillator(),g=actx.createGain(),n=actx.currentTime+(t||0),dd=d||.18;o.type=type||"sine";o.frequency.value=f;g.gain.setValueAtTime(0,n);g.gain.linearRampToValueAtTime(v||.05,n+.01);g.gain.exponentialRampToValueAtTime(.0001,n+dd);o.connect(g);g.connect(actx.destination);o.start(n);o.stop(n+dd+.05)}catch(e){}}
39686
- var SND={tick:function(){tone(1320,0,.05,.025,"triangle")},post:function(){tone(880,0,.08,.03)},step:function(){tone(523);tone(784,.12)},win:function(){tone(523);tone(659,.1);tone(784,.2);tone(1047,.3,.35)},fail:function(){tone(160,0,.3,.05,"sawtooth")}};
39687
- /* Notices are the BOARD's opt-in, not just the browser's: a permission granted to this origin once
39688
- (an earlier board, another run) must not switch them on, and the toggle must switch them off. */
39689
- function notify(title,body){try{if(notices&&"Notification" in window&&Notification.permission==="granted"&&document.hidden)new Notification(title,{body:body})}catch(e){}}
39690
- function accentNow(){return (getComputedStyle(document.documentElement).getPropertyValue("--accent")||"").trim()||"#3b6fd8"}
39691
- /* The preview lays out at a real phone's viewport (390 x 844 CSS px) and is scaled into the frame.
39692
- Sized to the frame itself it was ~280px wide: narrower than any phone, so screens overflowed
39693
- sideways and scrolled horizontally. */
39694
- function fit(){var ph=$("focus").querySelector(".phone.vpf");if(!ph)return;var w=ph.clientWidth,h=ph.clientHeight;if(w&&h)ph.style.setProperty("--k",String(Math.min(w/390,h/844)))}
39695
- window.addEventListener("resize",fit);
39696
- function favicon(n,bad){try{var c=document.createElement("canvas");c.width=c.height=32;var x=c.getContext&&c.getContext("2d");if(!x)return;x.lineWidth=5;x.strokeStyle="rgba(128,128,128,.35)";x.beginPath();x.arc(16,16,12,0,6.2832);x.stroke();x.strokeStyle=accentNow();x.beginPath();x.arc(16,16,12,-1.5708,-1.5708+6.2832*n/8);x.stroke();if(bad){x.fillStyle="#d33";x.beginPath();x.arc(25,7,6,0,6.2832);x.fill()}$("fav").href=c.toDataURL("image/png")}catch(e){}}
39697
- function burst(){var c=$("fx");c.setAttribute("data-bursts",String((+c.getAttribute("data-bursts")||0)+1));if(reduced())return;var x=c.getContext&&c.getContext("2d");if(!x)return;var W=c.width=innerWidth,H=c.height=innerHeight,cols=["#1f7a4d","#3b6fd8","#e0a030","#d4537e",accentNow()],P=[];for(var i=0;i<120;i++)P.push({x:W/2,y:H*.35,vx:(Math.random()-.5)*14,vy:-Math.random()*12-4,c:cols[i%cols.length],r:Math.random()*6.28});var t0=now();(function f(){x.clearRect(0,0,W,H);P.forEach(function(p){p.x+=p.vx;p.y+=p.vy;p.vy+=.35;p.r+=.2;x.save();x.translate(p.x,p.y);x.rotate(p.r);x.fillStyle=p.c;x.fillRect(-4,-2,8,4);x.restore()});if(now()-t0<2200)raf(f);else x.clearRect(0,0,W,H)})()}
39698
- function countTo(e,a,b){if(!e)return;if(reduced()){e.textContent=String(b);return}var t0=now();(function f(){var k=Math.min(1,(now()-t0)/900);e.textContent=String(Math.round(a+(b-a)*k));if(k<1)raf(f)})()}
39699
- function snap(s){var sc={},acc=null;s.screens.forEach(function(x){sc[x.id]=x.img});s.app.palette.forEach(function(p){if(p.name==="accentPrimary")acc=p.hex});return{screens:sc,step:s.current?s.current.stepNumber:0,top:s.journal[0]?s.journal[0].ts:null,build:s.live&&s.live.lastBuild?s.live.lastBuild.at:null,run:s.live?s.live.runId:null,push:s.health.push?s.health.push.releaseId:null,gaps:s.health.test?s.health.test.gaps:null,accent:acc}}
39700
- function buildLine(b){if(!b.ok)return "Build failed \xB7 still showing the last good version";var what=b.screens.length?b.screens.slice(0,3).join(", ")+(b.screens.length>3?" +"+(b.screens.length-3):""):b.tokens?"design tokens":b.config?"app config":"no visible change";return "Rebuilt"+(b.ms!=null?" in "+(b.ms/1000).toFixed(1)+" s":"")+" \xB7 "+what}
39701
- function effects(s){var is=snap(s),was=seen;seen=is;if(is.accent)document.documentElement.style.setProperty("--accent",is.accent);
39702
- /* The phone (kept across renders) and the Now line outlive a render: clear what they played last. */
39703
- var ph=$("focus").querySelector(".phone"),nm=$("nowmsg");if(ph)ph.classList.remove("pulse","bad");nm.classList.remove("slidein");if(!was)return;
39704
- var ticked=false;[].forEach.call($("wall").children,function(d){var id=d.getAttribute("data-id");if(!id)return;if(!(id in was.screens)){d.classList.add("enter");ticked=true}else if(was.screens[id]!==is.screens[id]){d.classList.add("changed");ticked=true}});if(ticked)SND.tick();
39705
- if(is.top&&is.top!==was.top){void nm.offsetWidth;nm.classList.add("slidein");var li=$("log").firstChild;if(li&&li.classList)li.classList.add("slidein");SND.post()}
39706
- if(is.step>was.step){var cs=$("steps").children[is.step-1];if(cs)cs.classList.add("fresh");SND.step();notify(s.app.name+" \xB7 step "+is.step+" of 8",s.journal[0]?s.journal[0].message:s.steps[is.step-1])}
39707
- /* A rebuild is a later build of the SAME run: a run appearing, or restarting, records its first
39708
- build too, and nothing was rebuilt then. */
39709
- if(is.build&&was.build&&is.run===was.run&&is.build!==was.build){var b=s.live.lastBuild;if(ph){void ph.offsetWidth;ph.classList.add("pulse");if(!b.ok)ph.classList.add("bad")}var tt=el("div","toast"+(b.ok?"":" bad"),buildLine(b));$("focus").appendChild(tt);setTimeout(function(){if(tt.parentNode)tt.parentNode.removeChild(tt)},3500);if(b.ok)SND.tick();else{SND.fail();notify(s.app.name+" \xB7 build failed",s.live.buildError||"")}}
39710
- if(was.gaps!=null&&is.gaps!=null&&is.gaps!==was.gaps)countTo($("gapn"),was.gaps,is.gaps);
39711
- if(is.accent&&was.accent&&is.accent!==was.accent){var sw=$("brand").querySelector(".sw");if(sw)sw.classList.add("pop");favicon(is.step,!!(s.live&&s.live.buildError))}
39712
- var published=is.push&&is.push!==was.push,handed=is.step===8&&was.step<8;if(published||handed){burst();SND.win();notify(s.app.name,published?"Draft published":"Handed over")}
39713
- }
39714
- function activityAt(s){var m=0;function add(v){var d=v?Date.parse(v):0;if(d>m)m=d}if(s.journal[0])add(s.journal[0].ts);s.screens.forEach(function(x){add(x.changedAt)});if(s.live&&s.live.lastBuild)add(s.live.lastBuild.at);["validate","test","push"].forEach(function(k){if(s.health[k])add(s.health[k].at)});return m}
39715
- function paintToggles(){var b=$("snd");b.textContent=sound?"Sound on":"Sound off";b.className="tog"+(sound?" on":"");b.setAttribute("aria-pressed",String(sound));var nb=$("ntf");if(!("Notification" in window)){nb.hidden=true;return}var p=Notification.permission,on=notices&&p==="granted";nb.hidden=p==="denied";nb.textContent=on?"Notices on":"Notices off";nb.className="tog"+(on?" on":"");nb.setAttribute("aria-pressed",String(on))}
39716
- function setNotices(v){notices=v;try{localStorage.setItem("himiStudioNotify",v?"1":"0")}catch(e){}paintToggles()}
39717
- $("snd").addEventListener("click",function(){sound=!sound;try{localStorage.setItem("himiStudioSound",sound?"1":"0")}catch(e){}if(sound){try{if(actx&&actx.resume)actx.resume()}catch(e){}SND.post()}paintToggles()});
39718
- /* The click acts on what the button SAYS (the effective state), so a remembered opt-in whose browser
39719
- permission was since reset reads "off" and one click asks for permission, not two. */
39720
- $("ntf").addEventListener("click",function(){try{var p=Notification.permission;if(notices&&p==="granted"){setNotices(false);return}if(p==="granted"){setNotices(true);return}if(Notification.permission==="default")Promise.resolve(Notification.requestPermission()).then(function(p){setNotices(p==="granted")})}catch(e){}});
39721
- document.addEventListener("click",function(){try{if(actx&&actx.state==="suspended")actx.resume()}catch(e){}},{once:true});
39722
- paintToggles();
39723
- window.__himiStudio=function(s){lastSeen=Date.now();if(!cur||cur.v!==s.v){cur=s;render(s);effects(s)}else{cur.generatedAt=s.generatedAt}};
39724
- $("lb").addEventListener("click",function(){this.style.display="none"});
39725
- window.__himiStudio(window.__himiInitial);
39726
- setInterval(function(){var sc=document.createElement("script");sc.src="state.js?t="+Date.now();sc.onload=sc.onerror=function(){sc.remove()};document.head.appendChild(sc)},2000);
39727
- var ticks=0;
39728
- setInterval(function(){if(!cur)return;if(++ticks%60===0)render(cur);var start=cur.startedAt?Date.parse(cur.startedAt):Date.now();var s=Math.max(0,Math.floor((Date.now()-start)/1000));var hh=Math.floor(s/3600),mm=Math.floor(s%3600/60),ss=s%60;
39729
- $("clock").textContent=(hh?hh+":":"")+(mm<10&&hh?"0":"")+mm+":"+(ss<10?"0":"")+ss;
39730
- $("updated").textContent="updated "+ago(cur.generatedAt);$("live").className="live"+(Date.now()-lastSeen>10000?" stale":"");
39731
- var a=activityAt(cur),q=a?Date.now()-a:0,done=cur.current&&cur.current.stepNumber===8;
39732
- $("agent").className="live"+(!a?" idle":done?"":q>5*60*1000?" quiet":"");$("agentt").textContent=!a?"Waiting for the agent":done?"Handed over":q>5*60*1000?"Quiet for "+Math.floor(q/60000)+" min":"Agent active"},1000);
39733
- })();
39734
- </script>
39735
- </body>
39736
- </html>
39737
- `;
39738
- }
39739
- function escapeHtml(s) {
39740
- return s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
39741
- }
39742
- var STUDIO_DIR, STUDIO_PAGE, STATE_FILE, MEMORY_FILE, VALIDATE_RECORD, PUSH_RECORD, EDGES_RECORD, RUN_RECORD, MAX_TIMELINE, MAX_JOURNAL, MAX_NAV_EDGES, readJson2, obj, num, text, historyUrl, runTime, scriptJson;
40138
+ var STUDIO_DIR, STUDIO_PAGE, STATE_FILE, MEMORY_FILE, VALIDATE_RECORD, PUSH_RECORD, EDGES_RECORD, RUN_RECORD, MAX_TIMELINE, MAX_JOURNAL, MAX_NAV_EDGES, readJson2, obj, num, text, historyUrl, runTime;
39743
40139
  var init_studio = __esm({
39744
40140
  "src/studio.ts"() {
40141
+ init_studio_page();
40142
+ init_studio_timeline();
39745
40143
  init_progress();
39746
- STUDIO_DIR = join36(".himi", "studio");
39747
- STUDIO_PAGE = join36(STUDIO_DIR, "index.html");
39748
- STATE_FILE = join36(STUDIO_DIR, "state.js");
39749
- MEMORY_FILE = join36(STUDIO_DIR, "memory.json");
39750
- VALIDATE_RECORD = join36(".himi", "validate.json");
39751
- PUSH_RECORD = join36(".himi", "last-push.json");
39752
- EDGES_RECORD = join36(".himi", "test-edges.json");
39753
- RUN_RECORD = join36(".himi", "run.json");
40144
+ init_studio_page();
40145
+ STUDIO_DIR = join37(".himi", "studio");
40146
+ STUDIO_PAGE = join37(STUDIO_DIR, "index.html");
40147
+ STATE_FILE = join37(STUDIO_DIR, "state.js");
40148
+ MEMORY_FILE = join37(STUDIO_DIR, "memory.json");
40149
+ VALIDATE_RECORD = join37(".himi", "validate.json");
40150
+ PUSH_RECORD = join37(".himi", "last-push.json");
40151
+ EDGES_RECORD = join37(".himi", "test-edges.json");
40152
+ RUN_RECORD = join37(".himi", "run.json");
39754
40153
  MAX_TIMELINE = 500;
39755
40154
  MAX_JOURNAL = 80;
39756
40155
  MAX_NAV_EDGES = 40;
39757
40156
  readJson2 = (file) => {
39758
40157
  try {
39759
- return JSON.parse(readFileSync42(file, "utf8"));
40158
+ return JSON.parse(readFileSync43(file, "utf8"));
39760
40159
  } catch {
39761
40160
  return null;
39762
40161
  }
@@ -39766,7 +40165,6 @@ var init_studio = __esm({
39766
40165
  text = (v) => typeof v === "string" && v ? v : null;
39767
40166
  historyUrl = (fileName) => `../history/${fileName}`;
39768
40167
  runTime = (runId) => runId.replace(/-[0-9a-f]+$/i, "");
39769
- scriptJson = (value) => JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
39770
40168
  }
39771
40169
  });
39772
40170
 
@@ -40245,11 +40643,11 @@ __export(captcha_exports, {
40245
40643
  captchaContentInput: () => captchaContentInput,
40246
40644
  runCaptcha: () => runCaptcha
40247
40645
  });
40248
- import { readFileSync as readFileSync43 } from "node:fs";
40646
+ import { readFileSync as readFileSync44 } from "node:fs";
40249
40647
  import { resolve as resolve34 } from "node:path";
40250
40648
  function readObject(path2) {
40251
40649
  try {
40252
- return JSON.parse(readFileSync43(path2, "utf8"));
40650
+ return JSON.parse(readFileSync44(path2, "utf8"));
40253
40651
  } catch {
40254
40652
  return {};
40255
40653
  }
@@ -40393,11 +40791,11 @@ var init_push_setup_apple_credentials = __esm({
40393
40791
  });
40394
40792
 
40395
40793
  // src/push-onboarding.ts
40396
- import { existsSync as existsSync40, mkdirSync as mkdirSync18, readFileSync as readFileSync44, statSync as statSync9, writeFileSync as writeFileSync14, chmodSync as chmodSync2 } from "node:fs";
40397
- import { basename as basename10, dirname as dirname22, join as join37, resolve as resolve35 } from "node:path";
40794
+ import { existsSync as existsSync41, mkdirSync as mkdirSync18, readFileSync as readFileSync45, statSync as statSync9, writeFileSync as writeFileSync14, chmodSync as chmodSync2 } from "node:fs";
40795
+ import { basename as basename10, dirname as dirname22, join as join38, resolve as resolve35 } from "node:path";
40398
40796
  function readJson3(path2) {
40399
40797
  try {
40400
- const parsed = JSON.parse(readFileSync44(path2, "utf8"));
40798
+ const parsed = JSON.parse(readFileSync45(path2, "utf8"));
40401
40799
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
40402
40800
  } catch {
40403
40801
  return {};
@@ -40405,9 +40803,9 @@ function readJson3(path2) {
40405
40803
  }
40406
40804
  function pushInputsFromContent(dir) {
40407
40805
  const root = resolve35(dir);
40408
- if (!existsSync40(join37(root, "himalaya.content.json"))) return { initialized: false };
40409
- const content = readJson3(join37(root, "himalaya.content.json"));
40410
- const provisioning = readJson3(join37(root, "provisioning.json"));
40806
+ if (!existsSync41(join38(root, "himalaya.content.json"))) return { initialized: false };
40807
+ const content = readJson3(join38(root, "himalaya.content.json"));
40808
+ const provisioning = readJson3(join38(root, "provisioning.json"));
40411
40809
  const native = provisioning.native ?? {};
40412
40810
  return {
40413
40811
  initialized: true,
@@ -40482,7 +40880,7 @@ function readKeyFile(path2) {
40482
40880
  }
40483
40881
  let body;
40484
40882
  try {
40485
- body = readFileSync44(path2, "utf8");
40883
+ body = readFileSync45(path2, "utf8");
40486
40884
  } catch {
40487
40885
  return { ok: false, code: "key_file_unreadable", reason: "cannot read the selected key file; check its path and file permissions" };
40488
40886
  }
@@ -40536,13 +40934,13 @@ function buildPushSetupBody(i, keys) {
40536
40934
  };
40537
40935
  }
40538
40936
  function readPushOnboarding(dir) {
40539
- const path2 = join37(resolve35(dir), PUSH_ONBOARDING_PATH);
40540
- if (!existsSync40(path2)) return { version: 1 };
40937
+ const path2 = join38(resolve35(dir), PUSH_ONBOARDING_PATH);
40938
+ if (!existsSync41(path2)) return { version: 1 };
40541
40939
  const parsed = readJson3(path2);
40542
40940
  return { version: 1, ...parsed };
40543
40941
  }
40544
40942
  function writeFile(dir, file) {
40545
- const path2 = join37(resolve35(dir), PUSH_ONBOARDING_PATH);
40943
+ const path2 = join38(resolve35(dir), PUSH_ONBOARDING_PATH);
40546
40944
  mkdirSync18(dirname22(path2), { recursive: true, mode: 448 });
40547
40945
  writeFileSync14(path2, JSON.stringify(file, null, 2) + "\n", { mode: 384 });
40548
40946
  try {
@@ -40571,8 +40969,8 @@ function recordPushDeferral(dir, d) {
40571
40969
  return push;
40572
40970
  }
40573
40971
  function clearPushDeferral(dir) {
40574
- const path2 = join37(resolve35(dir), PUSH_ONBOARDING_PATH);
40575
- if (!existsSync40(path2)) return;
40972
+ const path2 = join38(resolve35(dir), PUSH_ONBOARDING_PATH);
40973
+ if (!existsSync41(path2)) return;
40576
40974
  const { push: _dropped, ...rest } = readPushOnboarding(dir);
40577
40975
  writeFile(dir, { ...rest, version: 1 });
40578
40976
  }
@@ -41305,8 +41703,8 @@ __export(template_trim_exports, {
41305
41703
  routeMatcher: () => routeMatcher,
41306
41704
  runTemplateTrim: () => runTemplateTrim
41307
41705
  });
41308
- import { cpSync, existsSync as existsSync41, mkdirSync as mkdirSync19, readdirSync as readdirSync21, readFileSync as readFileSync45, renameSync as renameSync4, rmSync as rmSync8, statSync as statSync10, writeFileSync as writeFileSync15 } from "node:fs";
41309
- import { dirname as dirname23, join as join38 } from "node:path";
41706
+ import { cpSync, existsSync as existsSync42, mkdirSync as mkdirSync19, readdirSync as readdirSync21, readFileSync as readFileSync46, renameSync as renameSync4, rmSync as rmSync8, statSync as statSync10, writeFileSync as writeFileSync15 } from "node:fs";
41707
+ import { dirname as dirname23, join as join39 } from "node:path";
41310
41708
  function routeMatcher(pattern) {
41311
41709
  const detail = /^\^([A-Za-z0-9_]+)\(\?:\[_\/\]\(\.\+\)\)\?\$$/.exec(pattern);
41312
41710
  if (detail) {
@@ -42000,20 +42398,20 @@ function homeModulesPass(app, kept, edited) {
42000
42398
  return { text: patched.src, dropped: patched.droppedModules };
42001
42399
  }
42002
42400
  function listWorkers(dir) {
42003
- const root = join38(dir, "tier5-src");
42004
- if (!existsSync41(root)) return { dirs: [], files: {} };
42005
- const dirs = readdirSync21(root).filter((n) => statSync10(join38(root, n)).isDirectory()).sort();
42401
+ const root = join39(dir, "tier5-src");
42402
+ if (!existsSync42(root)) return { dirs: [], files: {} };
42403
+ const dirs = readdirSync21(root).filter((n) => statSync10(join39(root, n)).isDirectory()).sort();
42006
42404
  const files = {};
42007
42405
  const walk2 = (abs, rel) => {
42008
42406
  for (const name of readdirSync21(abs)) {
42009
42407
  if (name === "node_modules") continue;
42010
- const p = join38(abs, name);
42408
+ const p = join39(abs, name);
42011
42409
  const r = `${rel}/${name}`;
42012
42410
  if (statSync10(p).isDirectory()) walk2(p, r);
42013
- else if (/\.(?:[cm]?[jt]sx?|json)$/.test(name)) files[r] = readFileSync45(p, "utf8");
42411
+ else if (/\.(?:[cm]?[jt]sx?|json)$/.test(name)) files[r] = readFileSync46(p, "utf8");
42014
42412
  }
42015
42413
  };
42016
- for (const d of dirs) walk2(join38(root, d), `tier5-src/${d}`);
42414
+ for (const d of dirs) walk2(join39(root, d), `tier5-src/${d}`);
42017
42415
  return { dirs, files };
42018
42416
  }
42019
42417
  function csv(v) {
@@ -42036,16 +42434,16 @@ async function runTemplateTrim(req, deps) {
42036
42434
  links = req.links;
42037
42435
  }
42038
42436
  const dir = req.dir;
42039
- const devPath = join38(dir, "dev/index.ts");
42040
- const configPath = join38(dir, "config.ts");
42041
- if (!existsSync41(devPath) || !existsSync41(configPath)) {
42437
+ const devPath = join39(dir, "dev/index.ts");
42438
+ const configPath = join39(dir, "config.ts");
42439
+ if (!existsSync42(devPath) || !existsSync42(configPath)) {
42042
42440
  return refuse3(new TrimRefusal(`${dir} has no dev/index.ts + config.ts \u2014 point --content at an app scaffolded with \`himi init --template\``));
42043
42441
  }
42044
42442
  if (isTemplateSourceTree(dir)) {
42045
42443
  return refuse3(new TrimRefusal(`${dir} is a template source tree (its identity is still __HIMI_APP_*__ sentinels) \u2014 trim the app \`himi init --template\` scaffolds from it, not the template itself`));
42046
42444
  }
42047
- const dev = readFileSync45(devPath, "utf8");
42048
- const config = readFileSync45(configPath, "utf8");
42445
+ const dev = readFileSync46(devPath, "utf8");
42446
+ const config = readFileSync46(configPath, "utf8");
42049
42447
  const workers = listWorkers(dir);
42050
42448
  let plan;
42051
42449
  try {
@@ -42055,22 +42453,22 @@ async function runTemplateTrim(req, deps) {
42055
42453
  throw e;
42056
42454
  }
42057
42455
  if (req.dryRun) return { code: 0, result: { ok: true, dryRun: true, ...plan.report, validate: null } };
42058
- const hadHimi = existsSync41(join38(dir, ".himi"));
42059
- const backupRel = join38(".himi", "trim-backup", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
42060
- const backup = join38(dir, backupRel);
42456
+ const hadHimi = existsSync42(join39(dir, ".himi"));
42457
+ const backupRel = join39(".himi", "trim-backup", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
42458
+ const backup = join39(dir, backupRel);
42061
42459
  const originals = [[devPath, "dev/index.ts"], [configPath, "config.ts"]];
42062
42460
  const moved = [];
42063
42461
  const restore = () => {
42064
42462
  for (const [path2, rel] of originals) {
42065
- const original = readFileSync45(join38(backup, rel));
42066
- if (!existsSync41(path2) || !readFileSync45(path2).equals(original)) writeFileSync15(path2, original);
42463
+ const original = readFileSync46(join39(backup, rel));
42464
+ if (!existsSync42(path2) || !readFileSync46(path2).equals(original)) writeFileSync15(path2, original);
42067
42465
  }
42068
- for (const name of [...moved].reverse()) renameSync4(join38(backup, "tier5-src", name), join38(dir, "tier5-src", name));
42466
+ for (const name of [...moved].reverse()) renameSync4(join39(backup, "tier5-src", name), join39(dir, "tier5-src", name));
42069
42467
  };
42070
42468
  const dropBackup = () => {
42071
42469
  rmSync8(backup, { recursive: true, force: true });
42072
- for (const d of [dirname23(backup), join38(dir, ".himi")]) {
42073
- if (d === join38(dir, ".himi") && hadHimi) break;
42470
+ for (const d of [dirname23(backup), join39(dir, ".himi")]) {
42471
+ if (d === join39(dir, ".himi") && hadHimi) break;
42074
42472
  try {
42075
42473
  if (!readdirSync21(d).length) rmSync8(d, { recursive: true });
42076
42474
  } catch {
@@ -42096,9 +42494,9 @@ async function runTemplateTrim(req, deps) {
42096
42494
  return { code: 1, result: { ok: false, error: `${error} (${message}); every file was restored`, rolledBack: true, ...base(validate4) } };
42097
42495
  };
42098
42496
  try {
42099
- mkdirSync19(join38(backup, "dev"), { recursive: true });
42100
- for (const [path2, rel] of originals) cpSync(path2, join38(backup, rel));
42101
- if (plan.removedWorkers.length) mkdirSync19(join38(backup, "tier5-src"), { recursive: true });
42497
+ mkdirSync19(join39(backup, "dev"), { recursive: true });
42498
+ for (const [path2, rel] of originals) cpSync(path2, join39(backup, rel));
42499
+ if (plan.removedWorkers.length) mkdirSync19(join39(backup, "tier5-src"), { recursive: true });
42102
42500
  } catch (e) {
42103
42501
  dropBackup();
42104
42502
  return { code: 1, result: { ok: false, error: `could not back up the app before trimming it (${e.message}); nothing was changed`, rolledBack: true, ...base() } };
@@ -42106,7 +42504,7 @@ async function runTemplateTrim(req, deps) {
42106
42504
  try {
42107
42505
  const moveDir = deps.moveDir ?? renameSync4;
42108
42506
  for (const name of plan.removedWorkers) {
42109
- moveDir(join38(dir, "tier5-src", name), join38(backup, "tier5-src", name));
42507
+ moveDir(join39(dir, "tier5-src", name), join39(backup, "tier5-src", name));
42110
42508
  moved.push(name);
42111
42509
  }
42112
42510
  if (plan.dev !== dev) writeFileSync15(devPath, plan.dev);
@@ -42117,7 +42515,7 @@ async function runTemplateTrim(req, deps) {
42117
42515
  let failure;
42118
42516
  let validate3;
42119
42517
  try {
42120
- const written = parseApp(readFileSync45(devPath, "utf8"), readFileSync45(configPath, "utf8"));
42518
+ const written = parseApp(readFileSync46(devPath, "utf8"), readFileSync46(configPath, "utf8"));
42121
42519
  const leftovers = leftoverNavRefs(written.nav.value, plan.removedIds);
42122
42520
  if (leftovers.length) failure = { error: "navigation still names screens this trim removed", leftovers };
42123
42521
  else {
@@ -42811,8 +43209,8 @@ __export(pack_exports, {
42811
43209
  });
42812
43210
  import { createHash as createHash21 } from "node:crypto";
42813
43211
  import { deflateRawSync, inflateRawSync } from "node:zlib";
42814
- import { existsSync as existsSync42, mkdirSync as mkdirSync20, readFileSync as readFileSync46, readdirSync as readdirSync22, rmSync as rmSync9, statSync as statSync11, writeFileSync as writeFileSync16 } from "node:fs";
42815
- import { dirname as dirname24, join as join39, resolve as resolve37 } from "node:path";
43212
+ import { existsSync as existsSync43, mkdirSync as mkdirSync20, readFileSync as readFileSync47, readdirSync as readdirSync22, rmSync as rmSync9, statSync as statSync11, writeFileSync as writeFileSync16 } from "node:fs";
43213
+ import { dirname as dirname24, join as join40, resolve as resolve37 } from "node:path";
42816
43214
  function writeZip(entries) {
42817
43215
  const sorted2 = entries.slice().sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
42818
43216
  const total = sorted2.reduce((n, e) => n + e.bytes.length, 0);
@@ -42906,13 +43304,13 @@ function inflateEntry(buf, entry) {
42906
43304
  return bytes2;
42907
43305
  }
42908
43306
  function readIf(path2) {
42909
- return existsSync42(path2) && statSync11(path2).isFile() ? readFileSync46(path2) : null;
43307
+ return existsSync43(path2) && statSync11(path2).isFile() ? readFileSync47(path2) : null;
42910
43308
  }
42911
43309
  function listDir(dir) {
42912
- return existsSync42(dir) ? readdirSync22(dir).filter((f) => statSync11(join39(dir, f)).isFile()) : [];
43310
+ return existsSync43(dir) ? readdirSync22(dir).filter((f) => statSync11(join40(dir, f)).isFile()) : [];
42913
43311
  }
42914
43312
  function gatherReleaseEntries(appDir2) {
42915
- const manifestPath = join39(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
43313
+ const manifestPath = join40(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
42916
43314
  const manifestBytes = readIf(manifestPath);
42917
43315
  if (!manifestBytes) throw new Error(`no ${HIMI_PACKAGE_MANIFEST_PATH} in ${appDir2} \u2014 build a release there first`);
42918
43316
  const manifest = JSON.parse(manifestBytes.toString("utf8"));
@@ -42936,7 +43334,7 @@ function gatherReleaseEntries(appDir2) {
42936
43334
  }
42937
43335
  const missing = [];
42938
43336
  for (const rel of wanted) {
42939
- const bytes2 = readIf(join39(appDir2, rel));
43337
+ const bytes2 = readIf(join40(appDir2, rel));
42940
43338
  if (bytes2) entries.push({ path: rel, bytes: bytes2 });
42941
43339
  else missing.push(rel);
42942
43340
  }
@@ -42947,12 +43345,12 @@ function gatherReleaseEntries(appDir2) {
42947
43345
  );
42948
43346
  }
42949
43347
  for (const name of SINGLETONS) {
42950
- const bytes2 = readIf(join39(appDir2, name));
43348
+ const bytes2 = readIf(join40(appDir2, name));
42951
43349
  if (bytes2) entries.push({ path: name, bytes: bytes2 });
42952
43350
  }
42953
43351
  const skipped = [];
42954
43352
  for (const sub of ["screens", "bundles", "assets", "widgets"]) {
42955
- for (const f of listDir(join39(appDir2, sub))) {
43353
+ for (const f of listDir(join40(appDir2, sub))) {
42956
43354
  if (!wanted.has(`${sub}/${f}`)) skipped.push(`${sub}/${f}`);
42957
43355
  }
42958
43356
  }
@@ -42985,18 +43383,18 @@ function buildEntries(appDir2, app, opts) {
42985
43383
  return { all, header, manifest, skipped };
42986
43384
  }
42987
43385
  function packRelease(releaseRoot, app, out, opts = {}) {
42988
- const appDir2 = join39(resolve37(releaseRoot), app);
43386
+ const appDir2 = join40(resolve37(releaseRoot), app);
42989
43387
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
42990
43388
  const buf = writeZip(all);
42991
- const isDir2 = existsSync42(out) && statSync11(out).isDirectory();
42992
- const outFile = isDir2 ? join39(resolve37(out), packageFileName(app, header.releaseId)) : resolve37(out);
43389
+ const isDir2 = existsSync43(out) && statSync11(out).isDirectory();
43390
+ const outFile = isDir2 ? join40(resolve37(out), packageFileName(app, header.releaseId)) : resolve37(out);
42993
43391
  mkdirSync20(dirname24(outFile), { recursive: true });
42994
43392
  writeFileSync16(outFile, buf);
42995
43393
  return { app, releaseId: header.releaseId, header, out: outFile, bytes: buf.length, entryCount: all.length, skipped };
42996
43394
  }
42997
43395
  function resolvePackageDirDest(outDir, app, releaseId) {
42998
43396
  const packagedApp = (dir) => {
42999
- const bytes2 = readIf(join39(dir, HIMI_PACKAGE_HEADER_PATH));
43397
+ const bytes2 = readIf(join40(dir, HIMI_PACKAGE_HEADER_PATH));
43000
43398
  if (!bytes2) return void 0;
43001
43399
  try {
43002
43400
  return JSON.parse(bytes2.toString("utf8")).app;
@@ -43005,15 +43403,15 @@ function resolvePackageDirDest(outDir, app, releaseId) {
43005
43403
  }
43006
43404
  };
43007
43405
  const replaceable = (dir) => {
43008
- if (!existsSync42(dir)) return true;
43406
+ if (!existsSync43(dir)) return true;
43009
43407
  if (!statSync11(dir).isDirectory()) return false;
43010
43408
  return readdirSync22(dir).length === 0 || packagedApp(dir) === app;
43011
43409
  };
43012
- if (existsSync42(outDir) && !statSync11(outDir).isDirectory()) {
43410
+ if (existsSync43(outDir) && !statSync11(outDir).isDirectory()) {
43013
43411
  throw new Error(`refusing to write a package tree over ${outDir} \u2014 it is a file, not a directory`);
43014
43412
  }
43015
43413
  if (replaceable(outDir)) return outDir;
43016
- const child = join39(outDir, packageDirName(app, releaseId));
43414
+ const child = join40(outDir, packageDirName(app, releaseId));
43017
43415
  if (!replaceable(child)) {
43018
43416
  throw new Error(
43019
43417
  `refusing to replace ${child}: it is not empty and is not a ${app} package (no ${HIMI_PACKAGE_HEADER_PATH}). Pass --out to a new or empty directory.`
@@ -43027,13 +43425,13 @@ function packReleaseToDir(releaseRoot, app, outDir, opts = {}) {
43027
43425
  "a package carrying the app's source has no --format dir form: readers that predate the source entry drop it before hashing a directory and refuse the package as tampered. Pack it as a .himi archive."
43028
43426
  );
43029
43427
  }
43030
- const appDir2 = join39(resolve37(releaseRoot), app);
43428
+ const appDir2 = join40(resolve37(releaseRoot), app);
43031
43429
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
43032
43430
  const dest = resolvePackageDirDest(resolve37(outDir), app, header.releaseId);
43033
43431
  rmSync9(dest, { recursive: true, force: true });
43034
43432
  let bytes2 = 0;
43035
43433
  for (const entry of all) {
43036
- const target = join39(dest, entry.path);
43434
+ const target = join40(dest, entry.path);
43037
43435
  mkdirSync20(dirname24(target), { recursive: true });
43038
43436
  writeFileSync16(target, entry.bytes);
43039
43437
  bytes2 += entry.bytes.length;
@@ -43056,21 +43454,21 @@ function openPackageBuffer(buf) {
43056
43454
  return openFromEntries(readZip(buf), { requireHeader: true });
43057
43455
  }
43058
43456
  function openPackageFile(file) {
43059
- return openPackageBuffer(readFileSync46(file));
43457
+ return openPackageBuffer(readFileSync47(file));
43060
43458
  }
43061
43459
  function openPackageDir(dir) {
43062
43460
  const root = resolve37(dir);
43063
43461
  const entries = /* @__PURE__ */ new Map();
43064
43462
  const walk2 = (rel) => {
43065
- const abs = rel ? join39(root, rel) : root;
43463
+ const abs = rel ? join40(root, rel) : root;
43066
43464
  for (const name of readdirSync22(abs)) {
43067
43465
  const childRel = rel ? `${rel}/${name}` : name;
43068
- const childAbs = join39(abs, name);
43466
+ const childAbs = join40(abs, name);
43069
43467
  if (statSync11(childAbs).isDirectory()) walk2(childRel);
43070
- else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync46(childAbs));
43468
+ else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync47(childAbs));
43071
43469
  }
43072
43470
  };
43073
- if (!existsSync42(root)) throw new Error(`no such package directory: ${root}`);
43471
+ if (!existsSync43(root)) throw new Error(`no such package directory: ${root}`);
43074
43472
  walk2("");
43075
43473
  const manifestBytes = entries.get(HIMI_PACKAGE_MANIFEST_PATH);
43076
43474
  if (manifestBytes) {
@@ -43112,10 +43510,10 @@ function unpackTo(buf, dest, appFallback) {
43112
43510
  if (!app) throw new Error("package declares no app id (no himi-package.json and no manifest.app)");
43113
43511
  if (!isSafeAppId(app)) throw new Error(`package declares an unsafe app id ${JSON.stringify(app)} \u2014 refusing to unpack it`);
43114
43512
  const root = resolve37(dest);
43115
- const appDir2 = join39(root, app);
43513
+ const appDir2 = join40(root, app);
43116
43514
  rmSync9(appDir2, { recursive: true, force: true });
43117
43515
  for (const [rel, bytes2] of entries) {
43118
- const target = join39(appDir2, rel);
43516
+ const target = join40(appDir2, rel);
43119
43517
  mkdirSync20(dirname24(target), { recursive: true });
43120
43518
  writeFileSync16(target, bytes2);
43121
43519
  }
@@ -43151,20 +43549,20 @@ __export(app_source_exports, {
43151
43549
  writeConflicts: () => writeConflicts,
43152
43550
  zipAppSource: () => zipAppSource
43153
43551
  });
43154
- import { readdirSync as readdirSync23, lstatSync as lstatSync6, readFileSync as readFileSync47, mkdirSync as mkdirSync21, writeFileSync as writeFileSync17, existsSync as existsSync43, rmSync as rmSync10, rmdirSync, unlinkSync as unlinkSync3 } from "node:fs";
43155
- import { join as join40, resolve as resolve38, sep as sep10 } from "node:path";
43552
+ import { readdirSync as readdirSync23, lstatSync as lstatSync6, readFileSync as readFileSync48, mkdirSync as mkdirSync21, writeFileSync as writeFileSync17, existsSync as existsSync44, rmSync as rmSync10, rmdirSync, unlinkSync as unlinkSync3 } from "node:fs";
43553
+ import { join as join41, resolve as resolve38, sep as sep10 } from "node:path";
43156
43554
  import { deflateRawSync as deflateRawSync2 } from "node:zlib";
43157
43555
  function walkSourceTree(dir, file, skip) {
43158
43556
  const walk2 = (relDir) => {
43159
- for (const name of readdirSync23(join40(dir, relDir))) {
43160
- const rel = relDir ? join40(relDir, name) : name;
43161
- const st = lstatSync6(join40(dir, rel));
43557
+ for (const name of readdirSync23(join41(dir, relDir))) {
43558
+ const rel = relDir ? join41(relDir, name) : name;
43559
+ const st = lstatSync6(join41(dir, rel));
43162
43560
  if (st.isSymbolicLink()) {
43163
43561
  skip(rel);
43164
43562
  continue;
43165
43563
  }
43166
43564
  if (st.isDirectory()) {
43167
- if (isDeniedSourceDir(name, relDir) || existsSync43(join40(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
43565
+ if (isDeniedSourceDir(name, relDir) || existsSync44(join41(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
43168
43566
  else walk2(rel);
43169
43567
  continue;
43170
43568
  }
@@ -43182,7 +43580,7 @@ function collectAppSource(dir) {
43182
43580
  const skipped = [];
43183
43581
  walkSourceTree(
43184
43582
  dir,
43185
- (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync47(join40(dir, hostRel)) }),
43583
+ (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync48(join41(dir, hostRel)) }),
43186
43584
  (hostRel) => skipped.push(hostRel)
43187
43585
  );
43188
43586
  entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
@@ -43236,13 +43634,13 @@ function writeAppSource(entries, destDir) {
43236
43634
  const segments = entry.path.split("/");
43237
43635
  let dir = root;
43238
43636
  for (const segment of segments.slice(0, -1)) {
43239
- dir = join40(dir, segment);
43637
+ dir = join41(dir, segment);
43240
43638
  const kind = pathKind(dir);
43241
43639
  if (kind?.isSymbolicLink()) unlinkSync3(dir);
43242
43640
  else if (kind) continue;
43243
43641
  mkdirSync21(dir);
43244
43642
  }
43245
- const target = join40(root, ...segments);
43643
+ const target = join41(root, ...segments);
43246
43644
  if (pathKind(target)?.isSymbolicLink()) unlinkSync3(target);
43247
43645
  writeFileSync17(target, entry.bytes);
43248
43646
  written.push(entry.path);
@@ -43250,7 +43648,7 @@ function writeAppSource(entries, destDir) {
43250
43648
  return written;
43251
43649
  }
43252
43650
  function staleSourcePaths(entries, destDir) {
43253
- if (!existsSync43(destDir)) return [];
43651
+ if (!existsSync44(destDir)) return [];
43254
43652
  const archived = new Set(entries.map((e) => e.path));
43255
43653
  const stale = [];
43256
43654
  walkSourceTree(destDir, (archivePath) => {
@@ -43277,7 +43675,7 @@ function writeConflicts(entries, destDir) {
43277
43675
  let dir = root;
43278
43676
  let stop = false;
43279
43677
  for (const [i, segment] of segments.slice(0, -1).entries()) {
43280
- dir = join40(dir, segment);
43678
+ dir = join41(dir, segment);
43281
43679
  const kind2 = pathKind(dir);
43282
43680
  if (!kind2 || kind2.isSymbolicLink()) {
43283
43681
  stop = true;
@@ -43289,7 +43687,7 @@ function writeConflicts(entries, destDir) {
43289
43687
  break;
43290
43688
  }
43291
43689
  if (stop) continue;
43292
- const kind = pathKind(join40(root, ...segments));
43690
+ const kind = pathKind(join41(root, ...segments));
43293
43691
  if (kind && !kind.isSymbolicLink() && kind.isDirectory()) {
43294
43692
  say(entry.path, "is a directory here, but the release has a file at that path");
43295
43693
  }
@@ -43299,22 +43697,22 @@ function writeConflicts(entries, destDir) {
43299
43697
  function removeStaleSource(entries, destDir) {
43300
43698
  const root = resolve38(destDir);
43301
43699
  const removed = staleSourcePaths(entries, root);
43302
- for (const rel of removed) rmSync10(join40(root, ...rel.split("/")), { force: true });
43700
+ for (const rel of removed) rmSync10(join41(root, ...rel.split("/")), { force: true });
43303
43701
  const needed = /* @__PURE__ */ new Set();
43304
43702
  for (const entry of entries) {
43305
43703
  if (isDeniedSourcePath(entry.path)) continue;
43306
43704
  const segments = entry.path.split("/");
43307
43705
  for (let i = 1; i < segments.length; i++) needed.add(segments.slice(0, i).join("/"));
43308
43706
  }
43309
- if (existsSync43(root)) pruneEmptyDirs(root, "", needed, removed);
43707
+ if (existsSync44(root)) pruneEmptyDirs(root, "", needed, removed);
43310
43708
  return removed.sort();
43311
43709
  }
43312
43710
  function pruneEmptyDirs(root, rel, needed, removed) {
43313
43711
  let empty = true;
43314
- for (const name of readdirSync23(rel ? join40(root, rel) : root)) {
43315
- const childRel = rel ? join40(rel, name) : name;
43712
+ for (const name of readdirSync23(rel ? join41(root, rel) : root)) {
43713
+ const childRel = rel ? join41(rel, name) : name;
43316
43714
  const posix3 = childRel.split(sep10).join("/");
43317
- const kind = lstatSync6(join40(root, childRel));
43715
+ const kind = lstatSync6(join41(root, childRel));
43318
43716
  if (kind.isSymbolicLink() || !kind.isDirectory() || isDeniedSourceDir(name, rel)) {
43319
43717
  empty = false;
43320
43718
  continue;
@@ -43324,7 +43722,7 @@ function pruneEmptyDirs(root, rel, needed, removed) {
43324
43722
  empty = false;
43325
43723
  continue;
43326
43724
  }
43327
- rmdirSync(join40(root, childRel));
43725
+ rmdirSync(join41(root, childRel));
43328
43726
  removed.push(`${posix3}/`);
43329
43727
  }
43330
43728
  return empty;
@@ -43379,13 +43777,13 @@ __export(eject_exports, {
43379
43777
  EJECTABLE: () => EJECTABLE,
43380
43778
  ejectModule: () => ejectModule
43381
43779
  });
43382
- import { existsSync as existsSync44, mkdirSync as mkdirSync22, readFileSync as readFileSync48, writeFileSync as writeFileSync18 } from "node:fs";
43383
- import { dirname as dirname25, join as join41, relative as relative7, resolve as resolve39, sep as sep11 } from "node:path";
43780
+ import { existsSync as existsSync45, mkdirSync as mkdirSync22, readFileSync as readFileSync49, writeFileSync as writeFileSync18 } from "node:fs";
43781
+ import { dirname as dirname25, join as join42, relative as relative7, resolve as resolve39, sep as sep11 } from "node:path";
43384
43782
  function locate(repoRootOrCwd, mod) {
43385
43783
  const inRepo = resolve39(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
43386
- if (existsSync44(inRepo)) return { path: inRepo, form: "source" };
43784
+ if (existsSync45(inRepo)) return { path: inRepo, form: "source" };
43387
43785
  const dist = resolve39(repoRootOrCwd, `node_modules/@wix/himalaya/dist/${mod}.mjs`);
43388
- if (existsSync44(dist)) return { path: dist, form: "bundle" };
43786
+ if (existsSync45(dist)) return { path: dist, form: "bundle" };
43389
43787
  return null;
43390
43788
  }
43391
43789
  function specifiersFor(mod) {
@@ -43409,7 +43807,7 @@ function ejectModule(opts) {
43409
43807
  };
43410
43808
  }
43411
43809
  const ext = found.form === "source" ? "ts" : "mjs";
43412
- const target = join41(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
43810
+ const target = join42(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
43413
43811
  mkdirSync22(dirname25(target), { recursive: true });
43414
43812
  const banner = `// EJECTED from @wix/himalaya/${mod}. This app owns this file now.
43415
43813
  //
@@ -43423,15 +43821,15 @@ function ejectModule(opts) {
43423
43821
  // gate still applies. Ejecting buys layout and flow, never the floor.
43424
43822
 
43425
43823
  `;
43426
- const normalized = readFileSync48(found.path, "utf8").replace(
43824
+ const normalized = readFileSync49(found.path, "utf8").replace(
43427
43825
  /(["'])@himalaya\/([a-z0-9-]+(?:\/[a-z0-9-]+)?)\1/g,
43428
43826
  (_m, q, sub) => `${q}@wix/himalaya/${sub}${q}`
43429
43827
  );
43430
43828
  writeFileSync18(target, banner + normalized);
43431
43829
  const rewired = [];
43432
43830
  for (const f of files) {
43433
- if (!existsSync44(f)) continue;
43434
- const before = readFileSync48(f, "utf8");
43831
+ if (!existsSync45(f)) continue;
43832
+ const before = readFileSync49(f, "utf8");
43435
43833
  let after = before;
43436
43834
  let rel = relative7(dirname25(f), target).split(sep11).join("/").replace(/\.tsx?$/, ".js");
43437
43835
  if (!rel.startsWith(".")) rel = `./${rel}`;
@@ -43479,14 +43877,14 @@ __export(test_exports, {
43479
43877
  scopeDescriptors: () => scopeDescriptors,
43480
43878
  writeRecordedMocks: () => writeRecordedMocks
43481
43879
  });
43482
- import { existsSync as existsSync45, mkdirSync as mkdirSync23, readFileSync as readFileSync49, writeFileSync as writeFileSync19 } from "node:fs";
43483
- import { join as join42, resolve as resolve40 } from "node:path";
43880
+ import { existsSync as existsSync46, mkdirSync as mkdirSync23, readFileSync as readFileSync50, writeFileSync as writeFileSync19 } from "node:fs";
43881
+ import { join as join43, resolve as resolve40 } from "node:path";
43484
43882
  function readTestConfig(dir) {
43485
43883
  const p = resolve40(dir, TEST_CONFIG_FILE);
43486
- if (!existsSync45(p)) return {};
43884
+ if (!existsSync46(p)) return {};
43487
43885
  let parsed;
43488
43886
  try {
43489
- parsed = JSON.parse(readFileSync49(p, "utf8"));
43887
+ parsed = JSON.parse(readFileSync50(p, "utf8"));
43490
43888
  } catch (e) {
43491
43889
  throw new Error(`${TEST_CONFIG_FILE} is not valid JSON \u2014 ${e.message}`);
43492
43890
  }
@@ -43529,7 +43927,7 @@ function readTestConfig(dir) {
43529
43927
  function readNetRules(path2) {
43530
43928
  let parsed;
43531
43929
  try {
43532
- parsed = JSON.parse(readFileSync49(path2, "utf8"));
43930
+ parsed = JSON.parse(readFileSync50(path2, "utf8"));
43533
43931
  } catch (e) {
43534
43932
  throw new Error(`--net ${path2} could not be read as JSON \u2014 ${e.message}`);
43535
43933
  }
@@ -43550,7 +43948,7 @@ function readNetRules(path2) {
43550
43948
  function defaultNetRules(dir) {
43551
43949
  for (const rel of ["dev/net-mocks.json", "tests/net-mocks.json"]) {
43552
43950
  const p = resolve40(dir, rel);
43553
- if (existsSync45(p)) {
43951
+ if (existsSync46(p)) {
43554
43952
  const rules = readNetRules(p);
43555
43953
  if (rules.length) return { rules, from: rel };
43556
43954
  }
@@ -43566,7 +43964,7 @@ function readCannedResponses() {
43566
43964
  ];
43567
43965
  for (const url of candidates) {
43568
43966
  try {
43569
- const parsed = JSON.parse(readFileSync49(url, "utf8"));
43967
+ const parsed = JSON.parse(readFileSync50(url, "utf8"));
43570
43968
  const byKey = {};
43571
43969
  for (const [k, v] of Object.entries(parsed)) {
43572
43970
  if (!k.startsWith("//")) byKey[k] = v;
@@ -43658,8 +44056,8 @@ function writeRecordedMocks(opts) {
43658
44056
  mkdir: (p) => mkdirSync23(p, { recursive: true }),
43659
44057
  write: (p, s) => writeFileSync19(p, s)
43660
44058
  };
43661
- fs.mkdir(join42(opts.dir, "dev"));
43662
- fs.write(join42(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
44059
+ fs.mkdir(join43(opts.dir, "dev"));
44060
+ fs.write(join43(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
43663
44061
  return { ok: true, rules, to: "dev/net-mocks.json" };
43664
44062
  }
43665
44063
  function scopeDescriptors(descriptors, requestedIds, invalidScreens) {
@@ -43973,12 +44371,12 @@ async function runCrawl(opts) {
43973
44371
  };
43974
44372
  }
43975
44373
  function describeTransport(net, opts, ran) {
43976
- const layers = [];
43977
- if (net) layers.push(`mocked (${net.from})`);
44374
+ const layers2 = [];
44375
+ if (net) layers2.push(`mocked (${net.from})`);
43978
44376
  const cmsCount = Object.keys(opts.cmsSeed ?? {}).length;
43979
- if (cmsCount) layers.push(`cms (${cmsCount} seeded collection${cmsCount === 1 ? "" : "s"})`);
43980
- if (opts.canned?.count) layers.push(`canned (${opts.canned.count} frozen endpoints, ${opts.canned.from} \u2014 exercises code paths, not API-contract truth)`);
43981
- const base = layers.length ? layers.join(" + ") : "offline (no net fixture \u2014 screens are crawled against a rejecting transport, which exercises their failure UI)";
44377
+ if (cmsCount) layers2.push(`cms (${cmsCount} seeded collection${cmsCount === 1 ? "" : "s"})`);
44378
+ if (opts.canned?.count) layers2.push(`canned (${opts.canned.count} frozen endpoints, ${opts.canned.from} \u2014 exercises code paths, not API-contract truth)`);
44379
+ const base = layers2.length ? layers2.join(" + ") : "offline (no net fixture \u2014 screens are crawled against a rejecting transport, which exercises their failure UI)";
43982
44380
  const hostile = ran.hostile ? " + hostile boot passes (empty-lists, all-500)" : "";
43983
44381
  const relations = ran.relations ? " + relations" : "";
43984
44382
  const deep = ran.deep ? ` + deep chains (depth ${opts.deep})` : "";
@@ -44116,14 +44514,14 @@ var mobile_ux_lint_exports = {};
44116
44514
  __export(mobile_ux_lint_exports, {
44117
44515
  mobileUxIssues: () => mobileUxIssues
44118
44516
  });
44119
- import { existsSync as existsSync46, readFileSync as readFileSync50 } from "node:fs";
44120
- import { join as join43 } from "node:path";
44517
+ import { existsSync as existsSync47, readFileSync as readFileSync51 } from "node:fs";
44518
+ import { join as join44 } from "node:path";
44121
44519
  function mobileUxIssues(dir, strict) {
44122
- const file = join43(dir, "MOBILE-UX.md");
44123
- if (!existsSync46(file)) return [];
44520
+ const file = join44(dir, "MOBILE-UX.md");
44521
+ if (!existsSync47(file)) return [];
44124
44522
  let text5;
44125
44523
  try {
44126
- text5 = readFileSync50(file, "utf8");
44524
+ text5 = readFileSync51(file, "utf8");
44127
44525
  } catch {
44128
44526
  return [];
44129
44527
  }
@@ -45798,8 +46196,8 @@ __export(functions_exports, {
45798
46196
  resolveFunctionsTarget: () => resolveFunctionsTarget,
45799
46197
  runRemoteFunctionOperation: () => runRemoteFunctionOperation
45800
46198
  });
45801
- import { readFileSync as readFileSync51, existsSync as existsSync47 } from "node:fs";
45802
- import { join as join44, resolve as resolve41 } from "node:path";
46199
+ import { readFileSync as readFileSync52, existsSync as existsSync48 } from "node:fs";
46200
+ import { join as join45, resolve as resolve41 } from "node:path";
45803
46201
  function resolveFunctionsTarget(input) {
45804
46202
  const explicitUrl = input.functionsUrl?.trim();
45805
46203
  if (explicitUrl) return { mode: "remote", baseUrl: explicitUrl };
@@ -45818,9 +46216,9 @@ async function loadFunctionsApp(dir) {
45818
46216
  }
45819
46217
  const sources = {};
45820
46218
  for (const definition of config.functions.functions) {
45821
- const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join44(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync47);
46219
+ const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join45(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync48);
45822
46220
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
45823
- sources[definition.name] = readFileSync51(source, "utf8");
46221
+ sources[definition.name] = readFileSync52(source, "utf8");
45824
46222
  }
45825
46223
  const data = config.cms || config.data ? mergeDataResources(config) : void 0;
45826
46224
  const dataErrors = validateDataResources(data, loaded.name).filter((issue2) => issue2.severity === "error");
@@ -45972,14 +46370,14 @@ __export(studio_reel_exports, {
45972
46370
  utcDay: () => utcDay
45973
46371
  });
45974
46372
  import { createHash as createHash24 } from "node:crypto";
45975
- import { lstatSync as lstatSync7, readFileSync as readFileSync52 } from "node:fs";
45976
- import { basename as basename11, join as join45 } from "node:path";
46373
+ import { lstatSync as lstatSync7, readFileSync as readFileSync53 } from "node:fs";
46374
+ import { basename as basename11, join as join46 } from "node:path";
45977
46375
  function loadMemory2(dir) {
45978
- const m = readJson4(join45(dir, ".himi", "studio", "memory.json"));
45979
- if (!isObj(m) || m.version !== 1 || !isObj(m.shots) || !Array.isArray(m.timeline)) return null;
46376
+ const m = readJson4(join46(dir, ".himi", "studio", "memory.json"));
46377
+ if (!isObj2(m) || m.version !== 1 || !isObj2(m.shots) || !Array.isArray(m.timeline)) return null;
45980
46378
  const shots = [];
45981
46379
  for (const [id, s] of Object.entries(m.shots)) {
45982
- if (!isObj(s)) continue;
46380
+ if (!isObj2(s)) continue;
45983
46381
  const file = historyName(s.sha, s.ext);
45984
46382
  const firstSeen = str(s.firstSeen);
45985
46383
  if (!file || !firstSeen) continue;
@@ -45988,7 +46386,7 @@ function loadMemory2(dir) {
45988
46386
  }
45989
46387
  const timeline = [];
45990
46388
  for (const t of m.timeline) {
45991
- if (!isObj(t)) continue;
46389
+ if (!isObj2(t)) continue;
45992
46390
  const file = historyName(t.sha, t.ext);
45993
46391
  const ts2 = str(t.ts);
45994
46392
  const id = str(t.id);
@@ -46012,8 +46410,8 @@ function planReel(dir) {
46012
46410
  const memory = loadMemory2(dir);
46013
46411
  const { entries } = readProgress(dir);
46014
46412
  const summary = summarizeProgress(entries);
46015
- const manifest = readJson4(join45(dir, "himalaya.content.json"));
46016
- const tokens = readJson4(join45(dir, "tokens.json"));
46413
+ const manifest = readJson4(join46(dir, "himalaya.content.json"));
46414
+ const tokens = readJson4(join46(dir, "tokens.json"));
46017
46415
  const accentToken = tokens?.colors?.accentPrimary;
46018
46416
  const accentRaw = typeof accentToken === "string" ? accentToken : accentToken?.light;
46019
46417
  const accent = typeof accentRaw === "string" && ACCENT.test(accentRaw) ? accentRaw : DEFAULT_ACCENT;
@@ -46028,7 +46426,7 @@ function planReel(dir) {
46028
46426
  ts: e.ts,
46029
46427
  message: e.message,
46030
46428
  step: str(e.step),
46031
- files: (Array.isArray(e.images) ? e.images : []).map((i) => isObj(i) && typeof i.file === "string" ? basename11(i.file) : "").filter((f) => HISTORY_NAME.test(f))
46429
+ files: (Array.isArray(e.images) ? e.images : []).map((i) => isObj2(i) && typeof i.file === "string" ? basename11(i.file) : "").filter((f) => HISTORY_NAME.test(f))
46032
46430
  }));
46033
46431
  const times = [summary.startedAt, ...timeline.map((t) => t.ts), memory?.createdAt].filter((x) => !!x).sort();
46034
46432
  const startedAt = times[0] ?? null;
@@ -46055,10 +46453,10 @@ function inlineImages(dir, plan, budget = REEL_BUDGET_BYTES) {
46055
46453
  const verified = (f) => {
46056
46454
  if (!HISTORY_NAME.test(f)) return null;
46057
46455
  try {
46058
- const path2 = join45(dir, HISTORY_DIR, f);
46456
+ const path2 = join46(dir, HISTORY_DIR, f);
46059
46457
  const st = lstatSync7(path2);
46060
46458
  if (!st.isFile() || st.nlink !== 1) return null;
46061
- const bytes2 = readFileSync52(path2);
46459
+ const bytes2 = readFileSync53(path2);
46062
46460
  return createHash24("sha256").update(bytes2).digest("hex") === f.slice(0, 64) && imageExtension(bytes2) === f.slice(65) ? bytes2 : null;
46063
46461
  } catch {
46064
46462
  return null;
@@ -46178,7 +46576,7 @@ function buildReel(dir, budget = REEL_BUDGET_BYTES) {
46178
46576
  const { plan, images } = inlineImages(dir, planReel(dir), budget);
46179
46577
  return { html: renderReel(plan, images), plan };
46180
46578
  }
46181
- var REEL_BUDGET_BYTES, MAX_FLIP_FRAMES, DEFAULT_ACCENT, ACCENT, HISTORY_NAME, MIME2, mimeOf, readJson4, isObj, str, historyName, cited;
46579
+ var REEL_BUDGET_BYTES, MAX_FLIP_FRAMES, DEFAULT_ACCENT, ACCENT, HISTORY_NAME, MIME2, mimeOf, readJson4, isObj2, str, historyName, cited;
46182
46580
  var init_studio_reel = __esm({
46183
46581
  "src/studio-reel.ts"() {
46184
46582
  init_progress();
@@ -46192,12 +46590,12 @@ var init_studio_reel = __esm({
46192
46590
  mimeOf = (file) => MIME2[file.slice(65)] ?? "image/png";
46193
46591
  readJson4 = (file) => {
46194
46592
  try {
46195
- return JSON.parse(readFileSync52(file, "utf8"));
46593
+ return JSON.parse(readFileSync53(file, "utf8"));
46196
46594
  } catch {
46197
46595
  return null;
46198
46596
  }
46199
46597
  };
46200
- isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
46598
+ isObj2 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
46201
46599
  str = (v) => typeof v === "string" && v ? v : null;
46202
46600
  historyName = (sha, ext) => typeof sha === "string" && typeof ext === "string" && HISTORY_NAME.test(`${sha}.${ext}`) ? `${sha}.${ext}` : null;
46203
46601
  cited = (p) => [
@@ -46223,9 +46621,9 @@ __export(native_decode_exports, {
46223
46621
  runNativeDecode: () => runNativeDecode
46224
46622
  });
46225
46623
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
46226
- import { existsSync as existsSync48, mkdtempSync as mkdtempSync2, readFileSync as readFileSync53, rmSync as rmSync11, writeFileSync as writeFileSync20 } from "node:fs";
46624
+ import { existsSync as existsSync49, mkdtempSync as mkdtempSync2, readFileSync as readFileSync54, rmSync as rmSync11, writeFileSync as writeFileSync20 } from "node:fs";
46227
46625
  import { tmpdir as tmpdir3 } from "node:os";
46228
- import { join as join46 } from "node:path";
46626
+ import { join as join47 } from "node:path";
46229
46627
  import { fileURLToPath as fileURLToPath20 } from "node:url";
46230
46628
  function nativeDecodePackageDir() {
46231
46629
  return fileURLToPath20(new URL("../../../core/runtime/ios/HimalayaCore/", import.meta.url));
@@ -46235,8 +46633,8 @@ function nativeDecodeAndroidHarnessDir() {
46235
46633
  }
46236
46634
  function nativeOracleShipped() {
46237
46635
  return {
46238
- ios: existsSync48(join46(nativeDecodePackageDir(), "Package.swift")),
46239
- android: existsSync48(join46(nativeDecodeAndroidHarnessDir(), "gradlew"))
46636
+ ios: existsSync49(join47(nativeDecodePackageDir(), "Package.swift")),
46637
+ android: existsSync49(join47(nativeDecodeAndroidHarnessDir(), "gradlew"))
46240
46638
  };
46241
46639
  }
46242
46640
  function detectNativeDecode(options = {}) {
@@ -46247,7 +46645,7 @@ function detectNativeDecode(options = {}) {
46247
46645
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
46248
46646
  }
46249
46647
  if (!swiftAvailable) return { platform: "ios", available: false, reason: "swift is not on PATH" };
46250
- const packageAvailable = options.packageAvailable ?? existsSync48(join46(packageDir, "Package.swift"));
46648
+ const packageAvailable = options.packageAvailable ?? existsSync49(join47(packageDir, "Package.swift"));
46251
46649
  if (!packageAvailable) {
46252
46650
  return {
46253
46651
  platform: "ios",
@@ -46259,7 +46657,7 @@ function detectNativeDecode(options = {}) {
46259
46657
  }
46260
46658
  function localPropertiesSdk(harnessDir) {
46261
46659
  try {
46262
- const match = readFileSync53(join46(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
46660
+ const match = readFileSync54(join47(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
46263
46661
  return match?.[1]?.trim().replace(/\\([ :\\])/g, "$1") ?? null;
46264
46662
  } catch {
46265
46663
  return null;
@@ -46267,8 +46665,8 @@ function localPropertiesSdk(harnessDir) {
46267
46665
  }
46268
46666
  function detectAndroidNativeDecode(options = {}) {
46269
46667
  const harnessDir = options.harnessDir ?? nativeDecodeAndroidHarnessDir();
46270
- const gradlew = join46(harnessDir, "gradlew");
46271
- const gradlewAvailable = options.gradlewAvailable ?? existsSync48(gradlew);
46668
+ const gradlew = join47(harnessDir, "gradlew");
46669
+ const gradlewAvailable = options.gradlewAvailable ?? existsSync49(gradlew);
46272
46670
  if (!gradlewAvailable) {
46273
46671
  return {
46274
46672
  platform: "android",
@@ -46278,7 +46676,7 @@ function detectAndroidNativeDecode(options = {}) {
46278
46676
  }
46279
46677
  const androidHome = options.androidHome ?? process.env.ANDROID_HOME ?? "";
46280
46678
  const sdk = androidHome.trim() || (options.localPropertiesSdk !== void 0 ? options.localPropertiesSdk : localPropertiesSdk(harnessDir));
46281
- const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync48(sdk));
46679
+ const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync49(sdk));
46282
46680
  if (!sdkAvailable) {
46283
46681
  return {
46284
46682
  platform: "android",
@@ -46350,14 +46748,14 @@ function parseNativeDecodeOutput(stdout, expectedScreenIds, platform = "ios") {
46350
46748
  return verdicts;
46351
46749
  }
46352
46750
  function runNativeDecode(options) {
46353
- const descriptorDir = mkdtempSync2(join46(tmpdir3(), "himi-native-decode-"));
46751
+ const descriptorDir = mkdtempSync2(join47(tmpdir3(), "himi-native-decode-"));
46354
46752
  const startedAt = Date.now();
46355
46753
  try {
46356
46754
  for (const descriptor of options.descriptors) {
46357
46755
  if (typeof descriptor.descriptorJson !== "string") {
46358
46756
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
46359
46757
  }
46360
- writeFileSync20(join46(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
46758
+ writeFileSync20(join47(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
46361
46759
  }
46362
46760
  options.progress?.("building cached Swift oracle");
46363
46761
  execFileSync5("swift", ["build", "--package-path", options.packageDir, "--product", "himi-decode-oracle"], {
@@ -46369,7 +46767,7 @@ function runNativeDecode(options) {
46369
46767
  encoding: "utf8"
46370
46768
  }).trim();
46371
46769
  options.progress?.(`running real Swift decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
46372
- const stdout = execFileSync5(join46(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
46770
+ const stdout = execFileSync5(join47(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
46373
46771
  stdio: ["ignore", "pipe", "pipe"],
46374
46772
  encoding: "utf8"
46375
46773
  });
@@ -46387,15 +46785,15 @@ function runNativeDecode(options) {
46387
46785
  }
46388
46786
  }
46389
46787
  function runAndroidNativeDecode(options) {
46390
- const descriptorDir = mkdtempSync2(join46(tmpdir3(), "himi-native-decode-android-"));
46391
- const outputPath = join46(descriptorDir, "verdicts.ndjson");
46788
+ const descriptorDir = mkdtempSync2(join47(tmpdir3(), "himi-native-decode-android-"));
46789
+ const outputPath = join47(descriptorDir, "verdicts.ndjson");
46392
46790
  const startedAt = Date.now();
46393
46791
  try {
46394
46792
  for (const descriptor of options.descriptors) {
46395
46793
  if (typeof descriptor.descriptorJson !== "string") {
46396
46794
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
46397
46795
  }
46398
- writeFileSync20(join46(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
46796
+ writeFileSync20(join47(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
46399
46797
  }
46400
46798
  options.progress?.(`running real Kotlin decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
46401
46799
  execFileSync5(options.gradlew, [
@@ -46410,7 +46808,7 @@ function runAndroidNativeDecode(options) {
46410
46808
  stdio: ["ignore", "ignore", "pipe"],
46411
46809
  encoding: "utf8"
46412
46810
  });
46413
- const stdout = readFileSync53(outputPath, "utf8");
46811
+ const stdout = readFileSync54(outputPath, "utf8");
46414
46812
  return {
46415
46813
  platform: "android",
46416
46814
  verdicts: parseNativeDecodeOutput(stdout, options.descriptors.map((descriptor) => descriptor.screenId), "android"),
@@ -46480,13 +46878,13 @@ __export(browser_authoring_exports, {
46480
46878
  });
46481
46879
  import { createServer as createServer3 } from "node:http";
46482
46880
  import { createHash as createHash25, randomBytes as randomBytes4 } from "node:crypto";
46483
- import { existsSync as existsSync49, readdirSync as readdirSync24, readFileSync as readFileSync54, realpathSync as realpathSync5, statSync as statSync12 } from "node:fs";
46484
- import { join as join47, posix, relative as relative8, sep as sep12 } from "node:path";
46881
+ import { existsSync as existsSync50, readdirSync as readdirSync24, readFileSync as readFileSync55, realpathSync as realpathSync5, statSync as statSync12 } from "node:fs";
46882
+ import { join as join48, posix, relative as relative8, sep as sep12 } from "node:path";
46485
46883
  function humanMs(ms) {
46486
46884
  return ms < 1e3 ? `${ms}ms` : `${Math.round(ms / 1e3)}s`;
46487
46885
  }
46488
46886
  function collectWorkerSources(tier5SrcDir, bundleName) {
46489
- const root = join47(tier5SrcDir, bundleName);
46887
+ const root = join48(tier5SrcDir, bundleName);
46490
46888
  const files = {};
46491
46889
  const visitedDirs = /* @__PURE__ */ new Set();
46492
46890
  const walk2 = (abs, rel) => {
@@ -46494,16 +46892,16 @@ function collectWorkerSources(tier5SrcDir, bundleName) {
46494
46892
  if (visitedDirs.has(real)) return;
46495
46893
  visitedDirs.add(real);
46496
46894
  for (const name of readdirSync24(abs).sort()) {
46497
- const childAbs = join47(abs, name);
46895
+ const childAbs = join48(abs, name);
46498
46896
  const childRel = posix.join(rel, name);
46499
46897
  if (statSync12(childAbs).isDirectory()) {
46500
46898
  walk2(childAbs, childRel);
46501
46899
  } else if (/\.(ts|tsx|js|mjs|json)$/.test(name)) {
46502
- files[childRel] = readFileSync54(childAbs, "utf8");
46900
+ files[childRel] = readFileSync55(childAbs, "utf8");
46503
46901
  }
46504
46902
  }
46505
46903
  };
46506
- if (!existsSync49(root)) return null;
46904
+ if (!existsSync50(root)) return null;
46507
46905
  walk2(root, `/tier5-src/${bundleName}`);
46508
46906
  const entry = [`/tier5-src/${bundleName}/index.ts`, `/tier5-src/${bundleName}/index.js`].find((p) => p in files);
46509
46907
  if (!entry) return null;
@@ -46520,20 +46918,20 @@ function collectRelativeSiblings(files, tier5SrcDir) {
46520
46918
  const spec = m[1];
46521
46919
  const virtual = posix.normalize(posix.join(dir, spec));
46522
46920
  if (!virtual.startsWith("/tier5-src/")) continue;
46523
- const onDisk = join47(tier5SrcDir, virtual.slice("/tier5-src/".length));
46921
+ const onDisk = join48(tier5SrcDir, virtual.slice("/tier5-src/".length));
46524
46922
  const candidates = [
46525
46923
  onDisk,
46526
46924
  `${onDisk}.ts`,
46527
46925
  `${onDisk}.js`,
46528
46926
  onDisk.replace(/\.js$/, ".ts"),
46529
- join47(onDisk, "index.ts"),
46530
- join47(onDisk, "index.js")
46927
+ join48(onDisk, "index.ts"),
46928
+ join48(onDisk, "index.js")
46531
46929
  ];
46532
46930
  for (const abs of candidates) {
46533
- if (!existsSync49(abs) || statSync12(abs).isDirectory()) continue;
46931
+ if (!existsSync50(abs) || statSync12(abs).isDirectory()) continue;
46534
46932
  const key2 = `/tier5-src/${relative8(tier5SrcDir, abs).split(sep12).join(posix.sep)}`;
46535
46933
  if (key2 in files) break;
46536
- files[key2] = readFileSync54(abs, "utf8");
46934
+ files[key2] = readFileSync55(abs, "utf8");
46537
46935
  queue.push(key2);
46538
46936
  break;
46539
46937
  }
@@ -46568,7 +46966,7 @@ function collectSdkModules(files, resolveSubpath) {
46568
46966
  function sdkSubpathReader(resolve48) {
46569
46967
  return (sub) => {
46570
46968
  try {
46571
- return readFileSync54(resolve48(`@wix/himalaya/${sub}`), "utf8");
46969
+ return readFileSync55(resolve48(`@wix/himalaya/${sub}`), "utf8");
46572
46970
  } catch {
46573
46971
  return null;
46574
46972
  }
@@ -46740,8 +47138,8 @@ __export(coverage_exports, {
46740
47138
  suppressedNativeKeys: () => suppressedNativeKeys,
46741
47139
  trendAndStore: () => trendAndStore
46742
47140
  });
46743
- import { existsSync as existsSync50, mkdirSync as mkdirSync24, readFileSync as readFileSync55, writeFileSync as writeFileSync21 } from "node:fs";
46744
- import { dirname as dirname26, join as join48 } from "node:path";
47141
+ import { existsSync as existsSync51, mkdirSync as mkdirSync24, readFileSync as readFileSync56, writeFileSync as writeFileSync21 } from "node:fs";
47142
+ import { dirname as dirname26, join as join49 } from "node:path";
46745
47143
  function suppressedNativeKeys(oracle) {
46746
47144
  return [...oracle.ios ? [] : ["native-decode|ios"], ...oracle.android ? [] : ["native-decode|android"]];
46747
47145
  }
@@ -46921,11 +47319,11 @@ function assembleLedger(r, options = {}) {
46921
47319
  return out;
46922
47320
  }
46923
47321
  function trendAndStore(dir, entries, suppressed) {
46924
- const p = join48(dir, LEDGER_FILE);
47322
+ const p = join49(dir, LEDGER_FILE);
46925
47323
  let previous = null;
46926
47324
  try {
46927
- if (existsSync50(p)) {
46928
- const parsed = JSON.parse(readFileSync55(p, "utf8"));
47325
+ if (existsSync51(p)) {
47326
+ const parsed = JSON.parse(readFileSync56(p, "utf8"));
46929
47327
  if (Array.isArray(parsed.keys)) previous = parsed.keys.filter((k) => typeof k === "string");
46930
47328
  }
46931
47329
  } catch {
@@ -46964,11 +47362,11 @@ function formatLedger(entries, trend) {
46964
47362
  return out.join("\n");
46965
47363
  }
46966
47364
  function diffAndStoreObservations(dir, current) {
46967
- const p = join48(dir, OBS_FILE);
47365
+ const p = join49(dir, OBS_FILE);
46968
47366
  let previous = null;
46969
47367
  try {
46970
- if (existsSync50(p)) {
46971
- const parsed = JSON.parse(readFileSync55(p, "utf8"));
47368
+ if (existsSync51(p)) {
47369
+ const parsed = JSON.parse(readFileSync56(p, "utf8"));
46972
47370
  if (parsed.screens && typeof parsed.screens === "object" && !Array.isArray(parsed.screens) && Object.values(parsed.screens).every(
46973
47371
  (v) => Array.isArray(v) && v.every((l) => typeof l === "string")
46974
47372
  )) {
@@ -47017,8 +47415,8 @@ var LEDGER_FILE, OBS_FILE;
47017
47415
  var init_coverage = __esm({
47018
47416
  "src/coverage.ts"() {
47019
47417
  init_native_decode();
47020
- LEDGER_FILE = join48(".himi", "test-ledger.json");
47021
- OBS_FILE = join48(".himi", "test-observations.json");
47418
+ LEDGER_FILE = join49(".himi", "test-ledger.json");
47419
+ OBS_FILE = join49(".himi", "test-observations.json");
47022
47420
  }
47023
47421
  });
47024
47422
 
@@ -47029,11 +47427,11 @@ __export(design_kits_exports, {
47029
47427
  kitsDir: () => kitsDir,
47030
47428
  listKits: () => listKits2
47031
47429
  });
47032
- import { existsSync as existsSync51, readFileSync as readFileSync56, readdirSync as readdirSync25 } from "node:fs";
47033
- import { join as join49, basename as basename12 } from "node:path";
47430
+ import { existsSync as existsSync52, readFileSync as readFileSync57, readdirSync as readdirSync25 } from "node:fs";
47431
+ import { join as join50, basename as basename12 } from "node:path";
47034
47432
  import { fileURLToPath as fileURLToPath21 } from "node:url";
47035
47433
  function kitsDir() {
47036
- return KIT_DIR_CANDIDATES.find((p) => existsSync51(p));
47434
+ return KIT_DIR_CANDIDATES.find((p) => existsSync52(p));
47037
47435
  }
47038
47436
  function listKits2() {
47039
47437
  const dir = kitsDir();
@@ -47057,7 +47455,7 @@ In a published @wix/himalaya-cli this is a packaging bug \u2014 the kits should
47057
47455
  if (!kits.includes(name)) {
47058
47456
  return { ok: false, kit: name, kits, problems: [`no design kit named "${name}". Available: ${kits.join(", ")}`] };
47059
47457
  }
47060
- const raw = readFileSync56(join49(dir, `${name}.json`), "utf8");
47458
+ const raw = readFileSync57(join50(dir, `${name}.json`), "utf8");
47061
47459
  let parsed;
47062
47460
  try {
47063
47461
  parsed = JSON.parse(raw);
@@ -47081,9 +47479,9 @@ var init_design_kits = __esm({
47081
47479
  "src/design-kits.ts"() {
47082
47480
  KIT_DIR_CANDIDATES = [
47083
47481
  // in-repo: tools/himi-cli/src/design-kits.ts → <repo>/stdlib/design-kits/
47084
- join49(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
47482
+ join50(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
47085
47483
  // published CLI: <pkg>/dist/cli.mjs → <pkg>/stdlib/design-kits/
47086
- join49(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
47484
+ join50(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
47087
47485
  ];
47088
47486
  }
47089
47487
  });
@@ -47097,11 +47495,11 @@ __export(assets_exports, {
47097
47495
  removeAssetPayload: () => removeAssetPayload,
47098
47496
  verifyReport: () => verifyReport
47099
47497
  });
47100
- import { readFileSync as readFileSync57 } from "node:fs";
47498
+ import { readFileSync as readFileSync58 } from "node:fs";
47101
47499
  import { basename as basename13 } from "node:path";
47102
47500
  function declaredAssets(dir) {
47103
47501
  return collectAssets(dir).map((a) => {
47104
- const bytes2 = readFileSync57(a.path);
47502
+ const bytes2 = readFileSync58(a.path);
47105
47503
  return {
47106
47504
  name: a.name,
47107
47505
  sha256: assetSha256(bytes2),
@@ -47178,7 +47576,7 @@ function verifyReport(dir, app, manifest) {
47178
47576
  };
47179
47577
  }
47180
47578
  function addAssetPayload(manifest, filePath, opts) {
47181
- const bytes2 = readFileSync57(filePath);
47579
+ const bytes2 = readFileSync58(filePath);
47182
47580
  const name = opts.name ?? basename13(filePath);
47183
47581
  const sha2565 = assetSha256(bytes2);
47184
47582
  const record2 = {
@@ -48780,21 +49178,21 @@ var init_preview_assets_generated = __esm({
48780
49178
  "../../packages/serve/src/preview-assets.generated.ts"() {
48781
49179
  init_preview_assets_format();
48782
49180
  PREVIEW_ASSET_MANIFEST = [
48783
- { key: "assets/index-CxGyJNjV.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1041209, sha256: "4eb08b810b7f8e5ae57dc8eef60fe084213c20e0ba7a9bc79b55c89b50cd1291" },
48784
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1041217, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
48785
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1045827, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
48786
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1534411, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
48787
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1625335, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
48788
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1716319, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
48789
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1807299, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
48790
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1898171, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
48791
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1989151, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
48792
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2097787, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
48793
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2206871, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
48794
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2315919, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
48795
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2423619, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
48796
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2511727, length: 4947, sha256: "8f89772181b62b7bda8f1620af71cca03ee0aae2471127d0633e07488edbf290" },
48797
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2516674, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
49181
+ { key: "assets/index-BvsDhZYW.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1043409, sha256: "98882914dcb360867c5f265e3d6542774f18c0b0c8f18813b66b304a122c1b01" },
49182
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1043417, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
49183
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1048027, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
49184
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1536611, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
49185
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1627535, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
49186
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1718519, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
49187
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1809499, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
49188
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1900371, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
49189
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1991351, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
49190
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2099987, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
49191
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2209071, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
49192
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2318119, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
49193
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2425819, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
49194
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2513927, length: 4947, sha256: "3dd02534fef5e0c2bb40b6c6a6c994641728fde306921e66ffab3e9a7dfa6bbe" },
49195
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2518874, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
48798
49196
  ];
48799
49197
  }
48800
49198
  });
@@ -48810,9 +49208,9 @@ __export(run_exports, {
48810
49208
  runPackage: () => runPackage,
48811
49209
  withoutProdServe: () => withoutProdServe
48812
49210
  });
48813
- import { cpSync as cpSync2, existsSync as existsSync52, mkdirSync as mkdirSync25, mkdtempSync as mkdtempSync3, readFileSync as readFileSync58, rmSync as rmSync12, statSync as statSync13 } from "node:fs";
49211
+ import { cpSync as cpSync2, existsSync as existsSync53, mkdirSync as mkdirSync25, mkdtempSync as mkdtempSync3, readFileSync as readFileSync59, rmSync as rmSync12, statSync as statSync13 } from "node:fs";
48814
49212
  import { tmpdir as tmpdir4 } from "node:os";
48815
- import { dirname as dirname27, join as join50 } from "node:path";
49213
+ import { dirname as dirname27, join as join51 } from "node:path";
48816
49214
  import { fileURLToPath as fileURLToPath22 } from "node:url";
48817
49215
  function makePackageRunHandler(opts) {
48818
49216
  return makeRunHandler({
@@ -48831,9 +49229,9 @@ async function fetchBytes(url) {
48831
49229
  }
48832
49230
  async function resolvePackage(source, workDir) {
48833
49231
  const isUrl2 = /^https?:\/\//i.test(source);
48834
- if (!isUrl2 && existsSync52(source) && statSync13(source).isDirectory()) {
48835
- const appDirManifest = join50(source, "release-manifest.json");
48836
- if (existsSync52(appDirManifest)) {
49232
+ if (!isUrl2 && existsSync53(source) && statSync13(source).isDirectory()) {
49233
+ const appDirManifest = join51(source, "release-manifest.json");
49234
+ if (existsSync53(appDirManifest)) {
48837
49235
  const opened2 = openPackageDir(source);
48838
49236
  if (!opened2.validation.ok) {
48839
49237
  throw new Error(`package is not valid:
@@ -48842,9 +49240,9 @@ async function resolvePackage(source, workDir) {
48842
49240
  const app2 = opened2.header?.app ?? opened2.manifest?.app;
48843
49241
  if (!app2) throw new Error(`${source} has no app id (no himi-package.json and no manifest.app)`);
48844
49242
  if (!isSafeAppId(app2)) throw new Error(`${source} declares an unsafe app id ${JSON.stringify(app2)} \u2014 refusing to open it`);
48845
- const root2 = workDir ?? mkdtempSync3(join50(tmpdir4(), "himi-run-"));
48846
- const target = join50(root2, app2);
48847
- if (join50(source) !== target) {
49243
+ const root2 = workDir ?? mkdtempSync3(join51(tmpdir4(), "himi-run-"));
49244
+ const target = join51(root2, app2);
49245
+ if (join51(source) !== target) {
48848
49246
  mkdirSync25(root2, { recursive: true });
48849
49247
  rmSync12(target, { recursive: true, force: true });
48850
49248
  cpSync2(source, target, { recursive: true });
@@ -48861,8 +49259,8 @@ async function resolvePackage(source, workDir) {
48861
49259
  }
48862
49260
  throw new Error(`${source} is a directory but has no release-manifest.json \u2014 is it a package?`);
48863
49261
  }
48864
- const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync58(source);
48865
- const root = workDir ?? mkdtempSync3(join50(tmpdir4(), "himi-run-"));
49262
+ const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync59(source);
49263
+ const root = workDir ?? mkdtempSync3(join51(tmpdir4(), "himi-run-"));
48866
49264
  const { app, appDir: appDir2 } = unpackTo(bytes2, root);
48867
49265
  const opened = openPackageDir(appDir2);
48868
49266
  if (!opened.validation.ok) {
@@ -48885,14 +49283,14 @@ async function loadPreviewAssets() {
48885
49283
  const mod = await Promise.resolve().then(() => (init_preview_assets_generated(), preview_assets_generated_exports));
48886
49284
  const here = dirname27(fileURLToPath22(import.meta.url));
48887
49285
  const candidates = [
48888
- join50(here, "preview-assets.generated.bin"),
49286
+ join51(here, "preview-assets.generated.bin"),
48889
49287
  // published: copied next to the bundled CLI
48890
- join50(here, "../../../packages/serve/src/preview-assets.generated.bin")
49288
+ join51(here, "../../../packages/serve/src/preview-assets.generated.bin")
48891
49289
  // in-repo checkout
48892
49290
  ];
48893
49291
  for (const path2 of candidates) {
48894
- if (!existsSync52(path2)) continue;
48895
- const blob = readFileSync58(path2);
49292
+ if (!existsSync53(path2)) continue;
49293
+ const blob = readFileSync59(path2);
48896
49294
  return mod.assemblePreviewAssetsFromBlob(new Uint8Array(blob.buffer, blob.byteOffset, blob.byteLength));
48897
49295
  }
48898
49296
  return /* @__PURE__ */ new Map();
@@ -48987,9 +49385,9 @@ __export(run_watch_exports, {
48987
49385
  watchedContentDir: () => watchedContentDir,
48988
49386
  writeRunRecord: () => writeRunRecord
48989
49387
  });
48990
- import { closeSync as closeSync2, constants, existsSync as existsSync53, fstatSync, lstatSync as lstatSync8, statSync as statSync14, mkdirSync as mkdirSync26, openSync as openSync2, readFileSync as readFileSync59, realpathSync as realpathSync6, renameSync as renameSync5, rmSync as rmSync13, writeFileSync as writeFileSync22, watch as fsWatch } from "node:fs";
49388
+ import { closeSync as closeSync2, constants, existsSync as existsSync54, fstatSync, lstatSync as lstatSync8, statSync as statSync14, mkdirSync as mkdirSync26, openSync as openSync2, readFileSync as readFileSync60, realpathSync as realpathSync6, renameSync as renameSync5, rmSync as rmSync13, writeFileSync as writeFileSync22, watch as fsWatch } from "node:fs";
48991
49389
  import { randomUUID as randomUUID3 } from "node:crypto";
48992
- import { extname as extname4, join as join51, resolve as resolve42, sep as sep13 } from "node:path";
49390
+ import { extname as extname4, join as join52, resolve as resolve42, sep as sep13 } from "node:path";
48993
49391
  function diffReleases(prev, next) {
48994
49392
  const bundles = Object.keys(next.bundles).filter((b) => prev.bundles[b]?.sha256 !== next.bundles[b].sha256).sort();
48995
49393
  const changedBundles = new Set(bundles);
@@ -49028,8 +49426,8 @@ function watchContent(dir, onChange, opts = {}) {
49028
49426
  const watch2 = opts.watch ?? ((p, o, l) => fsWatch(p, o, (event, file) => l(event, file === null ? null : String(file))));
49029
49427
  let timer = null;
49030
49428
  const pending = /* @__PURE__ */ new Set();
49031
- const watcher = watch2(dir, { recursive: true }, (_event, file) => {
49032
- if (!file || ignoredChange(file, (rel) => isDir(join51(dir, rel)))) return;
49429
+ const watcher2 = watch2(dir, { recursive: true }, (_event, file) => {
49430
+ if (!file || ignoredChange(file, (rel) => isDir(join52(dir, rel)))) return;
49033
49431
  pending.add(file);
49034
49432
  if (timer) clearTimeout(timer);
49035
49433
  timer = setTimeout(() => {
@@ -49042,7 +49440,7 @@ function watchContent(dir, onChange, opts = {}) {
49042
49440
  return {
49043
49441
  close() {
49044
49442
  if (timer) clearTimeout(timer);
49045
- watcher.close();
49443
+ watcher2.close();
49046
49444
  }
49047
49445
  };
49048
49446
  }
@@ -49050,7 +49448,7 @@ function watchedContentDir(source) {
49050
49448
  if (!source || /^https?:\/\//i.test(source)) return null;
49051
49449
  const dir = resolve42(source);
49052
49450
  try {
49053
- return statSync14(dir).isDirectory() && existsSync53(join51(dir, "himalaya.content.json")) ? dir : null;
49451
+ return statSync14(dir).isDirectory() && existsSync54(join52(dir, "himalaya.content.json")) ? dir : null;
49054
49452
  } catch {
49055
49453
  return null;
49056
49454
  }
@@ -49107,7 +49505,7 @@ function serialize(task) {
49107
49505
  });
49108
49506
  }
49109
49507
  function himiDir(dir) {
49110
- const root = join51(dir, ".himi");
49508
+ const root = join52(dir, ".himi");
49111
49509
  try {
49112
49510
  return lstatSync8(root).isDirectory() ? root : null;
49113
49511
  } catch (e) {
@@ -49119,11 +49517,11 @@ function himiDir(dir) {
49119
49517
  function writeRunRecord(dir, record2) {
49120
49518
  const root = himiDir(dir);
49121
49519
  if (!root) return false;
49122
- const tmp = join51(root, `.run.json.${process.pid}.${randomUUID3()}.tmp`);
49520
+ const tmp = join52(root, `.run.json.${process.pid}.${randomUUID3()}.tmp`);
49123
49521
  try {
49124
49522
  writeFileSync22(tmp, `${JSON.stringify(record2, null, 2)}
49125
49523
  `, { flag: "wx" });
49126
- renameSync5(tmp, join51(root, "run.json"));
49524
+ renameSync5(tmp, join52(root, "run.json"));
49127
49525
  } catch (e) {
49128
49526
  rmSync13(tmp, { force: true });
49129
49527
  throw e;
@@ -49131,7 +49529,7 @@ function writeRunRecord(dir, record2) {
49131
49529
  return true;
49132
49530
  }
49133
49531
  function removeRunRecord(dir, owner) {
49134
- const root = join51(dir, ".himi");
49532
+ const root = join52(dir, ".himi");
49135
49533
  try {
49136
49534
  if (!lstatSync8(root).isDirectory()) return;
49137
49535
  } catch {
@@ -49146,15 +49544,16 @@ function removeRunRecord(dir, owner) {
49146
49544
  }
49147
49545
  if (typeof pid === "number" && pid !== owner) return;
49148
49546
  }
49149
- rmSync13(join51(root, "run.json"), { force: true });
49547
+ rmSync13(join52(root, "run.json"), { force: true });
49150
49548
  }
49151
49549
  function boardFile(dir, pathname) {
49152
49550
  let rel = null;
49153
- if (pathname === "/__himi/studio/" || pathname === "/__himi/studio/index.html") rel = join51("studio", "index.html");
49154
- else if (pathname === "/__himi/studio/state.js") rel = join51("studio", "state.js");
49551
+ if (pathname === "/__himi/studio/" || pathname === "/__himi/studio/index.html") rel = join52("studio", "index.html");
49552
+ else if (pathname === "/__himi/studio/state.js") rel = join52("studio", "state.js");
49553
+ else if (pathname === "/__himi/studio/timeline.js") rel = join52("studio", "timeline.js");
49155
49554
  else {
49156
49555
  const m = /^\/__himi\/history\/([0-9a-f]{64}\.(png|jpg|gif|webp))$/.exec(pathname);
49157
- if (m) rel = join51("history", m[1]);
49556
+ if (m) rel = join52("history", m[1]);
49158
49557
  }
49159
49558
  if (!rel) return null;
49160
49559
  const bytes2 = readConfined(resolve42(dir, ".himi"), rel);
@@ -49170,7 +49569,7 @@ function readConfined(root, rel) {
49170
49569
  const parts = rel.split(sep13);
49171
49570
  let at = root;
49172
49571
  for (let i = 0; i < parts.length; i++) {
49173
- at = join51(at, parts[i]);
49572
+ at = join52(at, parts[i]);
49174
49573
  const st = lstatSync8(at);
49175
49574
  if (st.isSymbolicLink() || !(i === parts.length - 1 ? st.isFile() : st.isDirectory())) return null;
49176
49575
  }
@@ -49179,7 +49578,7 @@ function readConfined(root, rel) {
49179
49578
  const fd = openSync2(at, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
49180
49579
  try {
49181
49580
  if (!fstatSync(fd).isFile()) return null;
49182
- return readFileSync59(fd);
49581
+ return readFileSync60(fd);
49183
49582
  } finally {
49184
49583
  closeSync2(fd);
49185
49584
  }
@@ -49230,7 +49629,7 @@ __export(run_browser_exports, {
49230
49629
  runInBrowser: () => runInBrowser
49231
49630
  });
49232
49631
  import { createServer as createServer5 } from "node:http";
49233
- import { existsSync as existsSync54, readFileSync as readFileSync60 } from "node:fs";
49632
+ import { existsSync as existsSync55, readFileSync as readFileSync61 } from "node:fs";
49234
49633
  import { spawnSync as spawnSync3 } from "node:child_process";
49235
49634
  import { dirname as dirname28, resolve as resolve43 } from "node:path";
49236
49635
  import { fileURLToPath as fileURLToPath23 } from "node:url";
@@ -49238,7 +49637,7 @@ function stagedDir() {
49238
49637
  return process.env.HIMI_BROWSER_PLANE_OUT ? resolve43(process.env.HIMI_BROWSER_PLANE_OUT) : resolve43(REPO, "packages", "serve", "dist", "browser-plane");
49239
49638
  }
49240
49639
  function missingStaged() {
49241
- return [...SERVED].filter((f) => !existsSync54(resolve43(stagedDir(), f))).sort();
49640
+ return [...SERVED].filter((f) => !existsSync55(resolve43(stagedDir(), f))).sort();
49242
49641
  }
49243
49642
  function ensureBrowserPlaneStaged() {
49244
49643
  if (missingStaged().length === 0) return { ok: true, detail: "already staged" };
@@ -49328,7 +49727,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
49328
49727
  }
49329
49728
  const name = path2 === "/" || path2 === "/index.html" ? "host.html" : path2.slice(1);
49330
49729
  if (SERVED.has(name)) {
49331
- if (!existsSync54(resolve43(stagedDir(), name))) {
49730
+ if (!existsSync55(resolve43(stagedDir(), name))) {
49332
49731
  const restaged = ensureBrowserPlaneStaged();
49333
49732
  if (!restaged.ok) {
49334
49733
  res.writeHead(503, { "content-type": "application/json" });
@@ -49338,7 +49737,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
49338
49737
  }
49339
49738
  let bytes2;
49340
49739
  try {
49341
- bytes2 = readFileSync60(resolve43(stagedDir(), name));
49740
+ bytes2 = readFileSync61(resolve43(stagedDir(), name));
49342
49741
  } catch (e) {
49343
49742
  res.writeHead(503, { "content-type": "application/json" });
49344
49743
  res.end(JSON.stringify({ error: "browser_plane_unstaged", file: name, detail: String(e) }));
@@ -49798,7 +50197,7 @@ __export(preflight_exports, {
49798
50197
  parseStoreConfig: () => parseStoreConfig,
49799
50198
  requireEnv: () => requireEnv
49800
50199
  });
49801
- import { existsSync as existsSync55, readFileSync as readFileSync61 } from "node:fs";
50200
+ import { existsSync as existsSync56, readFileSync as readFileSync62 } from "node:fs";
49802
50201
  import { isAbsolute as isAbsolute3, relative as relative9, resolve as resolve44 } from "node:path";
49803
50202
  function isInsideRepo(p) {
49804
50203
  const rel = relative9(REPO_ROOT3, resolve44(p));
@@ -49855,10 +50254,10 @@ function parseStoreConfig(text5) {
49855
50254
  }
49856
50255
  function loadStoreConfig(app) {
49857
50256
  const p = storeConfigPath(app);
49858
- if (!existsSync55(p)) {
50257
+ if (!existsSync56(p)) {
49859
50258
  throw new Error(`No store config at ${p}. Create apps/${app}/store/store.config.yaml first (see /setup-store-signing).`);
49860
50259
  }
49861
- return parseStoreConfig(readFileSync61(p, "utf8"));
50260
+ return parseStoreConfig(readFileSync62(p, "utf8"));
49862
50261
  }
49863
50262
  var init_preflight = __esm({
49864
50263
  "../store-release/preflight.ts"() {
@@ -49877,19 +50276,19 @@ __export(credential_profile_exports, {
49877
50276
  profilePath: () => profilePath,
49878
50277
  resolveStoreCredentials: () => resolveStoreCredentials
49879
50278
  });
49880
- import { existsSync as existsSync56, readFileSync as readFileSync62 } from "node:fs";
50279
+ import { existsSync as existsSync57, readFileSync as readFileSync63 } from "node:fs";
49881
50280
  import { homedir as homedir5 } from "node:os";
49882
- import { join as join52 } from "node:path";
50281
+ import { join as join53 } from "node:path";
49883
50282
  function credentialsDir(home = homedir5()) {
49884
- return join52(home, ".himalaya", "store-credentials");
50283
+ return join53(home, ".himalaya", "store-credentials");
49885
50284
  }
49886
50285
  function profilePath(name, home = homedir5()) {
49887
50286
  if (!PROFILE_NAME.test(name)) {
49888
50287
  throw new Error(
49889
- `credential profile ${JSON.stringify(name)} is not a profile name \u2014 use letters, digits, ".", "_" or "-" starting with a letter or digit. It names ${join52(credentialsDir(home), "<name>.json")}; it is never a path.`
50288
+ `credential profile ${JSON.stringify(name)} is not a profile name \u2014 use letters, digits, ".", "_" or "-" starting with a letter or digit. It names ${join53(credentialsDir(home), "<name>.json")}; it is never a path.`
49890
50289
  );
49891
50290
  }
49892
- return join52(credentialsDir(home), `${name}.json`);
50291
+ return join53(credentialsDir(home), `${name}.json`);
49893
50292
  }
49894
50293
  function parseProfile(text5, label2) {
49895
50294
  let doc;
@@ -49920,8 +50319,8 @@ function resolveStoreCredentials(opts) {
49920
50319
  const { app, cfg } = opts;
49921
50320
  const env = opts.env ?? process.env;
49922
50321
  const home = opts.home ?? homedir5();
49923
- const read = opts.readProfile ?? ((p) => readFileSync62(p, "utf8"));
49924
- const exists = opts.profileExists ?? existsSync56;
50322
+ const read = opts.readProfile ?? ((p) => readFileSync63(p, "utf8"));
50323
+ const exists = opts.profileExists ?? existsSync57;
49925
50324
  const ios = cfg.ios ?? {};
49926
50325
  const flagName = opts.profileFlag?.trim();
49927
50326
  const named = namedProfile(cfg, opts.profileFlag);
@@ -50001,9 +50400,9 @@ __export(preflight_app_exports, {
50001
50400
  preflightApp: () => preflightApp,
50002
50401
  profileAccount: () => profileAccount
50003
50402
  });
50004
- import { readFileSync as readFileSync63, existsSync as existsSync57, readdirSync as readdirSync26, statSync as statSync15 } from "node:fs";
50403
+ import { readFileSync as readFileSync64, existsSync as existsSync58, readdirSync as readdirSync26, statSync as statSync15 } from "node:fs";
50005
50404
  import { createHash as createHash26 } from "node:crypto";
50006
- import { join as join53 } from "node:path";
50405
+ import { join as join54 } from "node:path";
50007
50406
  function analyze(projectYml, storeConfig, icon, account) {
50008
50407
  const f = [];
50009
50408
  const has = (re, s) => re.test(s);
@@ -50067,13 +50466,13 @@ function profileAccount(app, storeConfig, opts = {}) {
50067
50466
  }
50068
50467
  }
50069
50468
  async function preflightApp(app, opts = {}) {
50070
- const projectYml = existsSync57(iosProjectYml(app)) ? readFileSync63(iosProjectYml(app), "utf8") : "";
50071
- const storeConfig = existsSync57(storeConfigPath(app)) ? readFileSync63(storeConfigPath(app), "utf8") : "";
50469
+ const projectYml = existsSync58(iosProjectYml(app)) ? readFileSync64(iosProjectYml(app), "utf8") : "";
50470
+ const storeConfig = existsSync58(storeConfigPath(app)) ? readFileSync64(storeConfigPath(app), "utf8") : "";
50072
50471
  if (!storeConfig) return [{ level: "error", message: `no apps/${app}/store/store.config.yaml \u2014 run /setup-store-signing first.` }];
50073
50472
  const findings = analyze(
50074
50473
  projectYml,
50075
50474
  storeConfig,
50076
- await appIconState(join53(appDir(app), "ios")),
50475
+ await appIconState(join54(appDir(app), "ios")),
50077
50476
  profileAccount(app, storeConfig, opts)
50078
50477
  );
50079
50478
  findings.push(...analyzeListing(metadataFilesPresent(app), listScreenshots(app)));
@@ -50102,17 +50501,17 @@ function analyzeSubmissionRecord(appLevel, review, opts) {
50102
50501
  return f;
50103
50502
  }
50104
50503
  function appLevelMetadataFiles(app, locale = "en-US") {
50105
- const root = join53(storeDir(app), "metadata");
50504
+ const root = join54(storeDir(app), "metadata");
50106
50505
  const out = /* @__PURE__ */ new Set();
50107
- if (existsSync57(root)) {
50506
+ if (existsSync58(root)) {
50108
50507
  for (const n of readdirSync26(root)) if (n.endsWith(".txt")) out.add(n);
50109
50508
  }
50110
- if (existsSync57(join53(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
50509
+ if (existsSync58(join54(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
50111
50510
  return out;
50112
50511
  }
50113
50512
  function reviewInfoFiles(app) {
50114
- const dir = join53(storeDir(app), "review_information");
50115
- if (!existsSync57(dir)) return /* @__PURE__ */ new Set();
50513
+ const dir = join54(storeDir(app), "review_information");
50514
+ if (!existsSync58(dir)) return /* @__PURE__ */ new Set();
50116
50515
  return new Set(readdirSync26(dir).filter((n) => n.endsWith(".txt")));
50117
50516
  }
50118
50517
  function isValidAppStoreSize(w, h) {
@@ -50222,12 +50621,12 @@ function analyzePlayListing(assets) {
50222
50621
  return f;
50223
50622
  }
50224
50623
  function playListingAssets(app, locale = "en-US") {
50225
- const dir = join53(storeDir(app), "play", locale, "images");
50624
+ const dir = join54(storeDir(app), "play", locale, "images");
50226
50625
  const read = (names) => {
50227
50626
  for (const name of names) {
50228
- const p = join53(dir, name);
50627
+ const p = join54(dir, name);
50229
50628
  try {
50230
- if (statSync15(p).isFile()) return { name, bytes: readFileSync63(p) };
50629
+ if (statSync15(p).isFile()) return { name, bytes: readFileSync64(p) };
50231
50630
  } catch {
50232
50631
  }
50233
50632
  }
@@ -50258,26 +50657,26 @@ function analyzeListing(metaFiles, shots) {
50258
50657
  return f;
50259
50658
  }
50260
50659
  function metadataFilesPresent(app, locale = "en-US") {
50261
- const dir = join53(storeDir(app), "metadata", locale);
50262
- if (!existsSync57(dir)) return /* @__PURE__ */ new Set();
50660
+ const dir = join54(storeDir(app), "metadata", locale);
50661
+ if (!existsSync58(dir)) return /* @__PURE__ */ new Set();
50263
50662
  return new Set(readdirSync26(dir).filter((n) => n.endsWith(".txt")));
50264
50663
  }
50265
50664
  function listScreenshots(app, locale = "en-US") {
50266
50665
  const out = [];
50267
- const flat = join53(storeDir(app), "screenshots", locale);
50268
- if (existsSync57(flat)) {
50666
+ const flat = join54(storeDir(app), "screenshots", locale);
50667
+ if (existsSync58(flat)) {
50269
50668
  for (const png of readdirSync26(flat).filter((n) => n.endsWith(".png"))) {
50270
- const sz = pngSize(readFileSync63(join53(flat, png)));
50669
+ const sz = pngSize(readFileSync64(join54(flat, png)));
50271
50670
  if (sz) out.push({ name: png, w: sz.w, h: sz.h });
50272
50671
  }
50273
50672
  }
50274
- const legacy = join53(storeDir(app), "metadata", locale, "screenshots");
50275
- if (existsSync57(legacy)) {
50673
+ const legacy = join54(storeDir(app), "metadata", locale, "screenshots");
50674
+ if (existsSync58(legacy)) {
50276
50675
  for (const deviceDir of readdirSync26(legacy)) {
50277
- const d = join53(legacy, deviceDir);
50676
+ const d = join54(legacy, deviceDir);
50278
50677
  if (!statSync15(d).isDirectory()) continue;
50279
50678
  for (const png of readdirSync26(d).filter((n) => n.endsWith(".png"))) {
50280
- const sz = pngSize(readFileSync63(join53(d, png)));
50679
+ const sz = pngSize(readFileSync64(join54(d, png)));
50281
50680
  if (sz) out.push({ name: `${deviceDir}/${png}`, w: sz.w, h: sz.h });
50282
50681
  }
50283
50682
  }
@@ -50285,13 +50684,13 @@ function listScreenshots(app, locale = "en-US") {
50285
50684
  return out;
50286
50685
  }
50287
50686
  function placeholderHashes2() {
50288
- const templateIos = join53(appDir("_template"), "ios");
50289
- if (!existsSync57(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
50687
+ const templateIos = join54(appDir("_template"), "ios");
50688
+ if (!existsSync58(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
50290
50689
  for (const target of readdirSync26(templateIos)) {
50291
- const p = join53(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
50690
+ const p = join54(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
50292
50691
  try {
50293
50692
  if (!statSync15(p).isFile()) continue;
50294
- return [LEGACY_PLACEHOLDER_SHA2562, createHash26("sha256").update(readFileSync63(p)).digest("hex")];
50693
+ return [LEGACY_PLACEHOLDER_SHA2562, createHash26("sha256").update(readFileSync64(p)).digest("hex")];
50295
50694
  } catch {
50296
50695
  }
50297
50696
  }
@@ -50322,12 +50721,12 @@ async function pixelIssues(bytes2) {
50322
50721
  }
50323
50722
  }
50324
50723
  async function appIconState(iosDir) {
50325
- if (!existsSync57(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
50724
+ if (!existsSync58(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
50326
50725
  for (const target of readdirSync26(iosDir)) {
50327
- const p = join53(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
50726
+ const p = join54(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
50328
50727
  try {
50329
50728
  if (!statSync15(p).isFile()) continue;
50330
- const bytes2 = readFileSync63(p);
50729
+ const bytes2 = readFileSync64(p);
50331
50730
  const hash = createHash26("sha256").update(bytes2).digest("hex");
50332
50731
  const pixels = await pixelIssues(bytes2);
50333
50732
  return {
@@ -50399,7 +50798,7 @@ __export(version_exports, {
50399
50798
  setAndroidVersion: () => setAndroidVersion,
50400
50799
  setIosVersion: () => setIosVersion
50401
50800
  });
50402
- import { readFileSync as readFileSync64, writeFileSync as writeFileSync23 } from "node:fs";
50801
+ import { readFileSync as readFileSync65, writeFileSync as writeFileSync23 } from "node:fs";
50403
50802
  function readIosVersion(projectYml) {
50404
50803
  const marketing = matchScalar(projectYml, "CFBundleShortVersionString");
50405
50804
  const build2 = matchScalar(projectYml, "CFBundleVersion");
@@ -50457,8 +50856,8 @@ function setAndroidVersion(gradle, v) {
50457
50856
  function bumpVersion(app, opts) {
50458
50857
  const iosPath = iosProjectYml(app);
50459
50858
  const gradlePath = androidGradle(app);
50460
- const iosYml = readFileSync64(iosPath, "utf8");
50461
- const gradle = readFileSync64(gradlePath, "utf8");
50859
+ const iosYml = readFileSync65(iosPath, "utf8");
50860
+ const gradle = readFileSync65(gradlePath, "utf8");
50462
50861
  const current = readIosVersion(iosYml);
50463
50862
  const next = {
50464
50863
  marketing: opts.marketing ?? current.marketing,
@@ -50475,7 +50874,7 @@ var init_version = __esm({
50475
50874
  });
50476
50875
 
50477
50876
  // ../store-release/register-bundle.ts
50478
- import { readFileSync as readFileSync65 } from "node:fs";
50877
+ import { readFileSync as readFileSync66 } from "node:fs";
50479
50878
  import { createPrivateKey as createPrivateKey2, sign } from "node:crypto";
50480
50879
  function b64url2(b) {
50481
50880
  return Buffer.from(b).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
@@ -50493,7 +50892,7 @@ function bundleIdBody(identifier, name, platform = "IOS") {
50493
50892
  async function registerBundleId(opts) {
50494
50893
  const env = requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
50495
50894
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
50496
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync65(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
50895
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync66(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
50497
50896
  const res = await fetch(`${ASC_API}/v1/bundleIds`, {
50498
50897
  method: "POST",
50499
50898
  headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
@@ -50547,7 +50946,7 @@ __export(asc_status_exports, {
50547
50946
  ascStatus: () => ascStatus,
50548
50947
  buildVerdict: () => buildVerdict
50549
50948
  });
50550
- import { readFileSync as readFileSync66 } from "node:fs";
50949
+ import { readFileSync as readFileSync67 } from "node:fs";
50551
50950
  function buildVerdict(builds) {
50552
50951
  if (!builds.length) {
50553
50952
  return "NO builds registered \u2014 if you uploaded recently and it's been >15 min, the binary was REJECTED IN PROCESSING (reason is email-only; run validate-binary.ts / scan-purpose-strings.ts to reproduce it).";
@@ -50561,7 +50960,7 @@ function buildVerdict(builds) {
50561
50960
  function client(creds) {
50562
50961
  const env = creds ?? requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
50563
50962
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
50564
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync66(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
50963
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync67(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
50565
50964
  return async (path2) => {
50566
50965
  const r = await fetch(`${ASC}${path2}`, { headers: { Authorization: `Bearer ${jwt}` } });
50567
50966
  return { status: r.status, json: await r.json().catch(() => ({})) };
@@ -50721,7 +51120,7 @@ __export(secrets_exports, {
50721
51120
  runRemoteSecretsCommand: () => runRemoteSecretsCommand,
50722
51121
  runSecretsCommand: () => runSecretsCommand
50723
51122
  });
50724
- import { readFileSync as readFileSync67 } from "node:fs";
51123
+ import { readFileSync as readFileSync68 } from "node:fs";
50725
51124
  function environment(value) {
50726
51125
  if (value === "preview" || value === "production") return value;
50727
51126
  throw new Error("--environment must be preview or production");
@@ -50752,7 +51151,7 @@ async function runSecretsCommand(operation, flags, io, options = {}) {
50752
51151
  io.log(JSON.stringify(await vault.list({ app, environment: env })));
50753
51152
  return 0;
50754
51153
  }
50755
- const value = options.readValue ? options.readValue() : readFileSync67(0, "utf8").replace(/\r?\n$/, "");
51154
+ const value = options.readValue ? options.readValue() : readFileSync68(0, "utf8").replace(/\r?\n$/, "");
50756
51155
  if (operation === "set") await vault.set({ app, environment: env, name, value });
50757
51156
  else if (operation === "rotate") await vault.rotate({ app, environment: env, name, value });
50758
51157
  else throw new Error(`unknown secrets operation ${operation}`);
@@ -50781,7 +51180,7 @@ async function runRemoteSecretsCommand(operation, flags, io, options) {
50781
51180
  }
50782
51181
  let body;
50783
51182
  if (operation !== "list") {
50784
- const value = options.readValue ? options.readValue() : readFileSync67(0, "utf8").replace(/\r?\n$/, "");
51183
+ const value = options.readValue ? options.readValue() : readFileSync68(0, "utf8").replace(/\r?\n$/, "");
50785
51184
  body = JSON.stringify({ value });
50786
51185
  }
50787
51186
  const response = await (options.fetchImpl ?? fetch)(url, { method: operation === "list" ? "GET" : operation === "set" ? "PUT" : "POST", headers: headers2, body });
@@ -50817,15 +51216,15 @@ __export(notify_exports, {
50817
51216
  runNotify: () => runNotify,
50818
51217
  saveAdminSession: () => saveAdminSession
50819
51218
  });
50820
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync27, readFileSync as readFileSync68, writeFileSync as writeFileSync24 } from "node:fs";
51219
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync27, readFileSync as readFileSync69, writeFileSync as writeFileSync24 } from "node:fs";
50821
51220
  import { homedir as homedir6 } from "node:os";
50822
- import { dirname as dirname30, join as join54 } from "node:path";
51221
+ import { dirname as dirname30, join as join55 } from "node:path";
50823
51222
  function defaultSessionsPath() {
50824
- return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join54(homedir6(), ".himalaya", "admin-sessions.json");
51223
+ return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join55(homedir6(), ".himalaya", "admin-sessions.json");
50825
51224
  }
50826
51225
  function loadSessionsFile(path2 = defaultSessionsPath()) {
50827
51226
  try {
50828
- return JSON.parse(readFileSync68(path2, "utf8"));
51227
+ return JSON.parse(readFileSync69(path2, "utf8"));
50829
51228
  } catch {
50830
51229
  return {};
50831
51230
  }
@@ -54383,7 +54782,7 @@ var init_brand_font = __esm({
54383
54782
 
54384
54783
  // src/site-analyze/write-artifacts.ts
54385
54784
  import { mkdirSync as mkdirSync28, writeFileSync as writeFileSync25 } from "node:fs";
54386
- import { join as join55 } from "node:path";
54785
+ import { join as join56 } from "node:path";
54387
54786
  function attachVeloSources(book, sources, put) {
54388
54787
  const byPath = new Map((sources ?? []).map((s) => [s.path, s.text]));
54389
54788
  for (const f of book.velo.files) {
@@ -54538,12 +54937,12 @@ async function downloadMedia(book, fetchImpl, put) {
54538
54937
  }
54539
54938
  async function writeBusinessBook(result, opts) {
54540
54939
  const out = opts.outDir;
54541
- mkdirSync28(join55(out, "evidence/pages"), { recursive: true });
54542
- mkdirSync28(join55(out, "media/catalog"), { recursive: true });
54940
+ mkdirSync28(join56(out, "evidence/pages"), { recursive: true });
54941
+ mkdirSync28(join56(out, "media/catalog"), { recursive: true });
54543
54942
  const files = [];
54544
54943
  const put = (rel, body) => {
54545
- const p = join55(out, rel);
54546
- mkdirSync28(join55(p, ".."), { recursive: true });
54944
+ const p = join56(out, rel);
54945
+ mkdirSync28(join56(p, ".."), { recursive: true });
54547
54946
  writeFileSync25(p, body);
54548
54947
  files.push(rel);
54549
54948
  };
@@ -54637,8 +55036,8 @@ var init_customer_build = __esm({
54637
55036
  });
54638
55037
 
54639
55038
  // src/site-analyze/apply.ts
54640
- import { existsSync as existsSync58, readFileSync as readFileSync69, writeFileSync as writeFileSync26, copyFileSync as copyFileSync3, mkdirSync as mkdirSync29, realpathSync as realpathSync7, statSync as statSync16 } from "node:fs";
54641
- import { dirname as dirname31, isAbsolute as isAbsolute4, join as join56, resolve as resolve45, sep as sep14 } from "node:path";
55039
+ import { existsSync as existsSync59, readFileSync as readFileSync70, writeFileSync as writeFileSync26, copyFileSync as copyFileSync3, mkdirSync as mkdirSync29, realpathSync as realpathSync7, statSync as statSync16 } from "node:fs";
55040
+ import { dirname as dirname31, isAbsolute as isAbsolute4, join as join57, resolve as resolve45, sep as sep14 } from "node:path";
54642
55041
  import { spawnSync as spawnSync4 } from "node:child_process";
54643
55042
  function unpairedRedirectNote(overlay) {
54644
55043
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
@@ -54653,7 +55052,7 @@ function committedShellProvisioning(path2) {
54653
55052
  function findRepoRoot(cwd) {
54654
55053
  let dir = cwd;
54655
55054
  for (let i = 0; i < 8; i++) {
54656
- if (SHELL_NAMES2.some((name) => existsSync58(join56(dir, shellAppDir(name), "package.json")))) return dir;
55055
+ if (SHELL_NAMES2.some((name) => existsSync59(join57(dir, shellAppDir(name), "package.json")))) return dir;
54657
55056
  const next = dirname31(dir);
54658
55057
  if (next === dir) break;
54659
55058
  dir = next;
@@ -54721,11 +55120,11 @@ function mergeCustomerOwned(committed, overlay) {
54721
55120
  }
54722
55121
  function buildMergedContract(options) {
54723
55122
  const { appDir: appDir2, packageDir, overlay } = options;
54724
- const committedPath = join56(appDir2, "provisioning.json");
54725
- if (!existsSync58(committedPath)) {
55123
+ const committedPath = join57(appDir2, "provisioning.json");
55124
+ if (!existsSync59(committedPath)) {
54726
55125
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
54727
55126
  }
54728
- const committed = JSON.parse(readFileSync69(committedPath, "utf8"));
55127
+ const committed = JSON.parse(readFileSync70(committedPath, "utf8"));
54729
55128
  const merged = mergeCustomerOwned(committed, overlay);
54730
55129
  const overlayFontFamily = (overlay.branding ?? {}).fontFamily;
54731
55130
  const branding = merged.branding ?? {};
@@ -54737,8 +55136,8 @@ function buildMergedContract(options) {
54737
55136
  };
54738
55137
  const stage = (fromRel, toRel) => {
54739
55138
  const from = contained(packageDir, fromRel);
54740
- const to = contained(join56(appDir2, STAGED), toRel.slice(STAGED.length + 1));
54741
- if (!from || !to || !existsSync58(from)) return void 0;
55139
+ const to = contained(join57(appDir2, STAGED), toRel.slice(STAGED.length + 1));
55140
+ if (!from || !to || !existsSync59(from)) return void 0;
54742
55141
  let real;
54743
55142
  let realRoot;
54744
55143
  try {
@@ -54760,11 +55159,11 @@ function buildMergedContract(options) {
54760
55159
  };
54761
55160
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
54762
55161
  if (icon) branding.appIconPath = icon;
54763
- const facesPath = join56(packageDir, "media/fonts/faces.json");
54764
- if (existsSync58(facesPath)) {
55162
+ const facesPath = join57(packageDir, "media/fonts/faces.json");
55163
+ if (existsSync59(facesPath)) {
54765
55164
  let faces = [];
54766
55165
  try {
54767
- const parsed = JSON.parse(readFileSync69(facesPath, "utf8"));
55166
+ const parsed = JSON.parse(readFileSync70(facesPath, "utf8"));
54768
55167
  faces = Array.isArray(parsed) ? parsed : [];
54769
55168
  } catch {
54770
55169
  faces = [];
@@ -54785,9 +55184,9 @@ function buildMergedContract(options) {
54785
55184
  merged.branding = branding;
54786
55185
  const body = `${JSON.stringify(merged, null, 2)}
54787
55186
  `;
54788
- const path2 = join56(packageDir, "provisioning.contract.json");
55187
+ const path2 = join57(packageDir, "provisioning.contract.json");
54789
55188
  writeFileSync26(path2, body);
54790
- const localPath = join56(appDir2, LOCAL_CONTRACT_REL);
55189
+ const localPath = join57(appDir2, LOCAL_CONTRACT_REL);
54791
55190
  mkdirSync29(dirname31(localPath), { recursive: true });
54792
55191
  writeFileSync26(localPath, body);
54793
55192
  staged.push(localPath);
@@ -54797,8 +55196,8 @@ async function applyOverlays(input) {
54797
55196
  const cwd = resolve45(input.cwd);
54798
55197
  const shell = input.shell ?? DEFAULT_BRANDABLE_SHELL;
54799
55198
  assertKnownShell(shell);
54800
- const overlay = JSON.parse(readFileSync69(input.overlayPath, "utf8"));
54801
- const tokens = JSON.parse(readFileSync69(input.tokensPath, "utf8"));
55199
+ const overlay = JSON.parse(readFileSync70(input.overlayPath, "utf8"));
55200
+ const tokens = JSON.parse(readFileSync70(input.tokensPath, "utf8"));
54802
55201
  const wrote = [];
54803
55202
  const notes = [];
54804
55203
  const repoRoot2 = findRepoRoot(cwd);
@@ -54808,7 +55207,7 @@ async function applyOverlays(input) {
54808
55207
  `shell "${shell}" cannot bake a customer brand \u2014 it ships no prepare:customer script`
54809
55208
  );
54810
55209
  }
54811
- const appDir2 = join56(repoRoot2, shellAppDir(shell));
55210
+ const appDir2 = join57(repoRoot2, shellAppDir(shell));
54812
55211
  const packageDir = dirname31(resolve45(input.overlayPath));
54813
55212
  const { path: contract, staged } = buildMergedContract({ appDir: appDir2, packageDir, overlay });
54814
55213
  wrote.push(...staged);
@@ -54830,10 +55229,10 @@ async function applyOverlays(input) {
54830
55229
  note: `prepare:customer --contract with a merged contract (committed provisioning.json untouched)${unpairedInRepo ? ` \xB7 ${unpairedInRepo}` : ""}`
54831
55230
  };
54832
55231
  }
54833
- const pkgProv = join56(cwd, "provisioning.json");
55232
+ const pkgProv = join57(cwd, "provisioning.json");
54834
55233
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
54835
- if (existsSync58(pkgProv)) {
54836
- const cur = JSON.parse(readFileSync69(pkgProv, "utf8"));
55234
+ if (existsSync59(pkgProv)) {
55235
+ const cur = JSON.parse(readFileSync70(pkgProv, "utf8"));
54837
55236
  const merged = deepMerge4(cur, overlay);
54838
55237
  writeFileSync26(pkgProv, JSON.stringify(merged, null, 2) + "\n");
54839
55238
  wrote.push(pkgProv);
@@ -54841,9 +55240,9 @@ async function applyOverlays(input) {
54841
55240
  writeFileSync26(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
54842
55241
  wrote.push(pkgProv);
54843
55242
  }
54844
- const pkgTokens = join56(cwd, "tokens.json");
54845
- if (existsSync58(pkgTokens) && (tokens.colors || tokens.typography)) {
54846
- const cur = JSON.parse(readFileSync69(pkgTokens, "utf8"));
55243
+ const pkgTokens = join57(cwd, "tokens.json");
55244
+ if (existsSync59(pkgTokens) && (tokens.colors || tokens.typography)) {
55245
+ const cur = JSON.parse(readFileSync70(pkgTokens, "utf8"));
54847
55246
  const merged = deepMerge4(cur, tokens);
54848
55247
  const mergedColors = merged.colors;
54849
55248
  if (mergedColors && typeof mergedColors === "object" && !Array.isArray(mergedColors)) {
@@ -54862,9 +55261,9 @@ async function applyOverlays(input) {
54862
55261
  wrote.push(pkgTokens);
54863
55262
  }
54864
55263
  const homeAction = homeRefreshActionId(shell);
54865
- const homeSrc = join56(cwd, "dev/index.ts");
54866
- if (homeAction && existsSync58(homeSrc)) {
54867
- const prev = readFileSync69(homeSrc, "utf8");
55264
+ const homeSrc = join57(cwd, "dev/index.ts");
55265
+ if (homeAction && existsSync59(homeSrc)) {
55266
+ const prev = readFileSync70(homeSrc, "utf8");
54868
55267
  const patched = patchBrandParams(prev, overlay, homeAction);
54869
55268
  let next = patched.src;
54870
55269
  if (!patched.foreign && overlay.installedVerticals?.length) {
@@ -54887,13 +55286,13 @@ async function applyOverlays(input) {
54887
55286
  notes.push(`${patched.unparsed} onAppear params block(s) in ${homeSrc} could not be read \u2014 any brand values inside them were not applied`);
54888
55287
  }
54889
55288
  }
54890
- if (!input.iconPath || !existsSync58(input.iconPath)) {
55289
+ if (!input.iconPath || !existsSync59(input.iconPath)) {
54891
55290
  const book = input.iconPath ? dirname31(dirname31(resolve45(input.iconPath))) : void 0;
54892
55291
  notes.push(
54893
55292
  book ? `no icon installed \u2014 ${siteLogoAbsence(book).hint}` : "no icon installed and no business book to say why \u2014 `himi icon generate` draws one from the accent, or `himi icon set <file.png>` installs the customer's own mark"
54894
55293
  );
54895
55294
  }
54896
- if (input.iconPath && existsSync58(input.iconPath)) {
55295
+ if (input.iconPath && existsSync59(input.iconPath)) {
54897
55296
  try {
54898
55297
  const installed = await installIcon(cwd, input.iconPath, { derivePlay: true });
54899
55298
  wrote.push(...installed.wrote);
@@ -59048,7 +59447,7 @@ __export(cli_exports, {
59048
59447
  runSiteAnalyze: () => runSiteAnalyze,
59049
59448
  runSiteFunctionCli: () => runSiteFunctionCli
59050
59449
  });
59051
- import { readFileSync as readFileSync70 } from "node:fs";
59450
+ import { readFileSync as readFileSync71 } from "node:fs";
59052
59451
  import { resolve as resolve46 } from "node:path";
59053
59452
  function assetFetchFor(_opts) {
59054
59453
  return pinnedFetch2;
@@ -59135,7 +59534,7 @@ async function runSiteFunctionCli(opts) {
59135
59534
  let source;
59136
59535
  if (opts.sourcePath) {
59137
59536
  const p = resolve46(opts.cwd ?? process.cwd(), opts.sourcePath);
59138
- source = readFileSync70(p, "utf8");
59537
+ source = readFileSync71(p, "utf8");
59139
59538
  }
59140
59539
  let body;
59141
59540
  if (opts.bodyRaw != null) {
@@ -59392,7 +59791,7 @@ __export(site_create_exports, {
59392
59791
  runSiteList: () => runSiteList,
59393
59792
  writeProvisioningBinding: () => writeProvisioningBinding
59394
59793
  });
59395
- import { existsSync as existsSync59, readFileSync as readFileSync71, writeFileSync as writeFileSync27 } from "node:fs";
59794
+ import { existsSync as existsSync60, readFileSync as readFileSync72, writeFileSync as writeFileSync27 } from "node:fs";
59396
59795
  import { resolve as resolvePath2 } from "node:path";
59397
59796
  function oauthClientName(projectName) {
59398
59797
  return `${projectName} client`;
@@ -59541,7 +59940,7 @@ async function runSiteCreate(opts) {
59541
59940
  if (!wantOauthApp) return { ...base, nextSteps: nextSteps(metaSiteId) };
59542
59941
  return { ...base, clientId, redirectUri, nextSteps: nextSteps(metaSiteId, clientId) };
59543
59942
  }
59544
- function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync59, readFileSync: readFileSync71, writeFileSync: writeFileSync27 }) {
59943
+ function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync60, readFileSync: readFileSync72, writeFileSync: writeFileSync27 }) {
59545
59944
  const msid = binding2.siteId;
59546
59945
  const path2 = resolvePath2(target);
59547
59946
  const committed = committedShellProvisioning(path2);
@@ -60214,11 +60613,11 @@ var init_context = __esm({
60214
60613
  });
60215
60614
 
60216
60615
  // src/cli.ts
60217
- import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync72, mkdirSync as mkdirSync30, writeFileSync as writeFileSync28, existsSync as existsSync60, rmSync as rmSync14, readdirSync as readdirSync27, statSync as statSync17, lstatSync as lstatSync9, accessSync, constants as constants2, renameSync as renameSync6, cpSync as cpSync3 } from "node:fs";
60616
+ import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync73, mkdirSync as mkdirSync30, writeFileSync as writeFileSync28, existsSync as existsSync61, rmSync as rmSync14, readdirSync as readdirSync27, statSync as statSync17, lstatSync as lstatSync9, accessSync, constants as constants2, renameSync as renameSync6, cpSync as cpSync3 } from "node:fs";
60218
60617
  import { execFile, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn6 } from "node:child_process";
60219
60618
  import { createHash as createHash27, randomUUID as randomUUID5 } from "node:crypto";
60220
60619
  import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
60221
- import { join as join57, resolve as resolve47, relative as relative10, basename as basename14, dirname as dirname32 } from "node:path";
60620
+ import { join as join58, resolve as resolve47, relative as relative10, basename as basename14, dirname as dirname32 } from "node:path";
60222
60621
  import { fileURLToPath as fileURLToPath24 } from "node:url";
60223
60622
 
60224
60623
  // ../serve-cli/src/client.ts
@@ -63494,8 +63893,8 @@ var str6 = (v) => typeof v === "string" ? v : void 0;
63494
63893
  var num2 = (v) => typeof v === "string" && v !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
63495
63894
  function embeddedTemplateCatalog() {
63496
63895
  return availableTemplates().map((name) => {
63497
- const path2 = join57(templateSourceDir(name), TEMPLATE_META_FILE);
63498
- const meta = JSON.parse(readFileSync72(path2, "utf8"));
63896
+ const path2 = join58(templateSourceDir(name), TEMPLATE_META_FILE);
63897
+ const meta = JSON.parse(readFileSync73(path2, "utf8"));
63499
63898
  if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
63500
63899
  throw new Error(`${path2} must be { title: string, description: string, tags: string[], category?: string }`);
63501
63900
  }
@@ -64164,7 +64563,7 @@ var COMMANDS = [
64164
64563
  ];
64165
64564
  function cliVersion() {
64166
64565
  try {
64167
- return JSON.parse(readFileSync72(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
64566
+ return JSON.parse(readFileSync73(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
64168
64567
  } catch {
64169
64568
  return "unknown";
64170
64569
  }
@@ -64282,7 +64681,7 @@ function ageInDays(publishedAt, now = Date.now()) {
64282
64681
  }
64283
64682
  function packageVersion(path2) {
64284
64683
  try {
64285
- const version2 = JSON.parse(readFileSync72(path2, "utf8")).version;
64684
+ const version2 = JSON.parse(readFileSync73(path2, "utf8")).version;
64286
64685
  return typeof version2 === "string" && version2 ? version2 : void 0;
64287
64686
  } catch {
64288
64687
  return void 0;
@@ -64291,7 +64690,7 @@ function packageVersion(path2) {
64291
64690
  function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
64292
64691
  let dir = resolve47(fromDir);
64293
64692
  for (; ; ) {
64294
- const version2 = packageVersion(join57(dir, "node_modules", "@wix", "himalaya", "package.json"));
64693
+ const version2 = packageVersion(join58(dir, "node_modules", "@wix", "himalaya", "package.json"));
64295
64694
  if (version2) return { version: version2, source: "local" };
64296
64695
  const parent = dirname32(dir);
64297
64696
  if (parent === dir) break;
@@ -64301,7 +64700,7 @@ function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("
64301
64700
  if (bundled) return { version: bundled, source: "bundled" };
64302
64701
  try {
64303
64702
  const root = execFileSync7("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
64304
- const version2 = root ? packageVersion(join57(root, "@wix", "himalaya", "package.json")) : void 0;
64703
+ const version2 = root ? packageVersion(join58(root, "@wix", "himalaya", "package.json")) : void 0;
64305
64704
  return version2 ? { version: version2, source: "global" } : {};
64306
64705
  } catch {
64307
64706
  return {};
@@ -64348,11 +64747,11 @@ var UPDATE_RETRY_AFTER_FAILURE_MS = 60 * 60 * 1e3;
64348
64747
  var UPDATE_SNOOZE_MS = 24 * 60 * 60 * 1e3;
64349
64748
  var UPDATE_REEXEC_ENV = "HIMI_UPDATED_ONCE";
64350
64749
  function updateCachePath(env = process.env) {
64351
- return env.HIMI_UPDATE_CACHE_FILE ?? join57(homedir7(), ".himalaya", "update-check.json");
64750
+ return env.HIMI_UPDATE_CACHE_FILE ?? join58(homedir7(), ".himalaya", "update-check.json");
64352
64751
  }
64353
64752
  function readUpdateCache(path2) {
64354
64753
  try {
64355
- const parsed = JSON.parse(readFileSync72(path2, "utf8"));
64754
+ const parsed = JSON.parse(readFileSync73(path2, "utf8"));
64356
64755
  return parsed && typeof parsed === "object" ? parsed : {};
64357
64756
  } catch {
64358
64757
  return {};
@@ -64420,12 +64819,12 @@ function createUpdateRunner(spawn7 = spawnSync5) {
64420
64819
  },
64421
64820
  installedVersion: () => {
64422
64821
  const root = globalNodeModulesRoot();
64423
- return root ? packageVersion(join57(root, "@wix", "himalaya-cli", "package.json")) : void 0;
64822
+ return root ? packageVersion(join58(root, "@wix", "himalaya-cli", "package.json")) : void 0;
64424
64823
  },
64425
64824
  reexec: (argv) => {
64426
64825
  const root = globalNodeModulesRoot();
64427
- const entry = root ? join57(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
64428
- if (!entry || !existsSync60(entry)) return void 0;
64826
+ const entry = root ? join58(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
64827
+ if (!entry || !existsSync61(entry)) return void 0;
64429
64828
  const result = spawnSync5(process.execPath, [entry, ...argv], {
64430
64829
  // inherit keeps PIPED STDIN working — commands that read stdin must survive a re-exec.
64431
64830
  stdio: "inherit",
@@ -64472,7 +64871,7 @@ function staleUpdateLockFile(cachePath, now) {
64472
64871
  for (const path2 of [`${cachePath}.lock.break`, `${cachePath}.lock`]) {
64473
64872
  let content;
64474
64873
  try {
64475
- content = readFileSync72(path2, "utf8");
64874
+ content = readFileSync73(path2, "utf8");
64476
64875
  } catch {
64477
64876
  continue;
64478
64877
  }
@@ -64495,7 +64894,7 @@ function acquireUpdateLock(cachePath, now, hooks = {}) {
64495
64894
  const mine = lockStamp(now);
64496
64895
  const readLock = (path2) => {
64497
64896
  try {
64498
- return readFileSync72(path2, "utf8");
64897
+ return readFileSync73(path2, "utf8");
64499
64898
  } catch {
64500
64899
  return void 0;
64501
64900
  }
@@ -64792,9 +65191,9 @@ function diagnoseBody(body) {
64792
65191
  const out = {};
64793
65192
  const emptied = [];
64794
65193
  for (const [key2, value] of Object.entries(body)) {
64795
- const fold = (key2 === "issues" || key2 === "failures" || key2 === "warnings") && folded(value);
64796
- if (fold) emptied.push(key2);
64797
- out[key2] = fold ? [] : value;
65194
+ const fold2 = (key2 === "issues" || key2 === "failures" || key2 === "warnings") && folded(value);
65195
+ if (fold2) emptied.push(key2);
65196
+ out[key2] = fold2 ? [] : value;
64798
65197
  }
64799
65198
  if (emptied.length) out.foldedIntoFindings = emptied;
64800
65199
  return out;
@@ -65033,7 +65432,7 @@ function executableOnPath(name, env = process.env) {
65033
65432
  const suffixes = process.platform === "win32" ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
65034
65433
  for (const dir of entries) {
65035
65434
  for (const suffix of suffixes) {
65036
- const candidate = join57(dir, `${name}${suffix}`);
65435
+ const candidate = join58(dir, `${name}${suffix}`);
65037
65436
  try {
65038
65437
  if (!statSync17(candidate).isFile()) continue;
65039
65438
  if (process.platform !== "win32") accessSync(candidate, constants2.X_OK);
@@ -65142,7 +65541,7 @@ async function nativeConfigAdvisories(app, configSource) {
65142
65541
  const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
65143
65542
  const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
65144
65543
  const iosYmlPath = iosProjectYml2(app);
65145
- const iosYml = existsSync60(iosYmlPath) ? readFileSync72(iosYmlPath, "utf8") : "";
65544
+ const iosYml = existsSync61(iosYmlPath) ? readFileSync73(iosYmlPath, "utf8") : "";
65146
65545
  let optedIn = iosYml.includes(LABEL_IOS_SPLASH2);
65147
65546
  if (!optedIn) {
65148
65547
  const { androidManifestPath: androidManifestPath3 } = await Promise.resolve().then(() => (init_gen_native_config(), gen_native_config_exports));
@@ -65156,7 +65555,7 @@ async function nativeConfigAdvisories(app, configSource) {
65156
65555
  }
65157
65556
  function readConfigSource(dir) {
65158
65557
  try {
65159
- return readFileSync72(join57(dir, "config.ts"), "utf8");
65558
+ return readFileSync73(join58(dir, "config.ts"), "utf8");
65160
65559
  } catch {
65161
65560
  return null;
65162
65561
  }
@@ -65168,7 +65567,7 @@ function writePngFile(path2, base64) {
65168
65567
  }
65169
65568
  async function initFromRemoteTemplate(name, parentDir, template, c) {
65170
65569
  const dir = resolve47(parentDir, name);
65171
- if (existsSync60(join57(dir, "himalaya.content.json"))) {
65570
+ if (existsSync61(join58(dir, "himalaya.content.json"))) {
65172
65571
  throw new Error(`content package already exists at ${dir}`);
65173
65572
  }
65174
65573
  const r = await c.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -65211,7 +65610,7 @@ function symlinkDestinationIssue(dest) {
65211
65610
  function restoreDestinationIssue(dest, force) {
65212
65611
  const link = symlinkDestinationIssue(dest);
65213
65612
  if (link) return link;
65214
- if (!existsSync60(dest)) return null;
65613
+ if (!existsSync61(dest)) return null;
65215
65614
  if (!statSync17(dest).isDirectory()) {
65216
65615
  return { error: `${dest} exists and is not a directory`, hint: "pass --out/--name to land somewhere else, or remove the file" };
65217
65616
  }
@@ -65226,7 +65625,7 @@ function restoreDestinationIssue(dest, force) {
65226
65625
  function isHimalayaMonorepoCheckout(start) {
65227
65626
  let dir = resolve47(start);
65228
65627
  for (; ; ) {
65229
- if (existsSync60(join57(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync60(join57(dir, "core", "serve", "src", "server.ts"))) return true;
65628
+ if (existsSync61(join58(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync61(join58(dir, "core", "serve", "src", "server.ts"))) return true;
65230
65629
  const parent = dirname32(dir);
65231
65630
  if (parent === dir) return false;
65232
65631
  dir = parent;
@@ -65269,7 +65668,7 @@ function materializeForPreview(appDir2, slug, displayName) {
65269
65668
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
65270
65669
  );
65271
65670
  const repoRoot2 = resolve47(fileURLToPath24(new URL("../../..", import.meta.url)));
65272
- const root = join57(repoRoot2, "node_modules", ".cache", "himi-template-preview");
65671
+ const root = join58(repoRoot2, "node_modules", ".cache", "himi-template-preview");
65273
65672
  const stillRunning = (pid) => {
65274
65673
  try {
65275
65674
  process.kill(pid, 0);
@@ -65283,12 +65682,12 @@ function materializeForPreview(appDir2, slug, displayName) {
65283
65682
  if (!entry.isDirectory()) continue;
65284
65683
  const pid = Number(entry.name.startsWith(`${slug}-`) ? entry.name.slice(slug.length + 1) : NaN);
65285
65684
  if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && !stillRunning(pid)) {
65286
- rmSync14(join57(root, entry.name), { recursive: true, force: true });
65685
+ rmSync14(join58(root, entry.name), { recursive: true, force: true });
65287
65686
  }
65288
65687
  }
65289
65688
  } catch {
65290
65689
  }
65291
- const dest = join57(root, `${slug}-${process.pid}`);
65690
+ const dest = join58(root, `${slug}-${process.pid}`);
65292
65691
  rmSync14(dest, { recursive: true, force: true });
65293
65692
  mkdirSync30(dest, { recursive: true });
65294
65693
  materializeSource(forBuild, displayName, dest, slug);
@@ -65296,8 +65695,8 @@ function materializeForPreview(appDir2, slug, displayName) {
65296
65695
  }
65297
65696
  async function appId(dir) {
65298
65697
  try {
65299
- const { readFileSync: readFileSync73 } = await import("node:fs");
65300
- const m = JSON.parse(readFileSync73(join57(dir, "himalaya.content.json"), "utf8"));
65698
+ const { readFileSync: readFileSync74 } = await import("node:fs");
65699
+ const m = JSON.parse(readFileSync74(join58(dir, "himalaya.content.json"), "utf8"));
65301
65700
  if (m.name) return m.name;
65302
65701
  } catch {
65303
65702
  }
@@ -65305,8 +65704,8 @@ async function appId(dir) {
65305
65704
  }
65306
65705
  async function contentVisibility(dir) {
65307
65706
  try {
65308
- const { readFileSync: readFileSync73 } = await import("node:fs");
65309
- const m = JSON.parse(readFileSync73(join57(dir, "himalaya.content.json"), "utf8"));
65707
+ const { readFileSync: readFileSync74 } = await import("node:fs");
65708
+ const m = JSON.parse(readFileSync74(join58(dir, "himalaya.content.json"), "utf8"));
65310
65709
  if (m.visibility === "public" || m.visibility === "unlisted") return m.visibility;
65311
65710
  } catch {
65312
65711
  }
@@ -65530,7 +65929,7 @@ async function recordedReleaseForContentQuietly(dir, resolution, onDrift) {
65530
65929
  try {
65531
65930
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
65532
65931
  const bres = await buildContentForValidation2(dir, { resolution });
65533
- out = mkdtempSync4(join57(tmpdir5(), "himi-ux-record-"));
65932
+ out = mkdtempSync4(join58(tmpdir5(), "himi-ux-record-"));
65534
65933
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
65535
65934
  const outcome = await buildReleaseFromContent2(dir, out, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {}, collect: { lint: true } });
65536
65935
  const releaseId = outcome.manifest.releaseId;
@@ -65708,7 +66107,7 @@ async function producedWidgetFamilies(dir) {
65708
66107
  let js;
65709
66108
  for (const candidate of [`${name}.bundle.js`, `${name}.js`, name]) {
65710
66109
  try {
65711
- js = readFileSync72(join57(outDir, candidate), "utf8");
66110
+ js = readFileSync73(join58(outDir, candidate), "utf8");
65712
66111
  break;
65713
66112
  } catch {
65714
66113
  }
@@ -65739,7 +66138,7 @@ async function producedWidgetFamilies(dir) {
65739
66138
  }
65740
66139
  return { families, source: "dist/bundles" };
65741
66140
  }
65742
- var boardOpen = (dir) => existsSync60(join57(dir, ".himi", "studio", "index.html"));
66141
+ var boardOpen = (dir) => existsSync61(join58(dir, ".himi", "studio", "index.html"));
65743
66142
  var BOARD_REFRESHERS = /* @__PURE__ */ new Set(["render", "test", "validate", "build", "push", "icon", "site", "ux", "progress"]);
65744
66143
  async function leaveForBoard(dir, record2, value) {
65745
66144
  if (!boardOpen(dir)) return;
@@ -66127,7 +66526,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66127
66526
  const sourceDir = resolve47(str6(flags["source-dir"]) ?? appDir2);
66128
66527
  let meta;
66129
66528
  try {
66130
- meta = JSON.parse(readFileSync72(join57(sourceDir, TEMPLATE_META_FILE), "utf8"));
66529
+ meta = JSON.parse(readFileSync73(join58(sourceDir, TEMPLATE_META_FILE), "utf8"));
66131
66530
  } catch {
66132
66531
  io.error(JSON.stringify({ error: `no ${TEMPLATE_META_FILE} in ${sourceDir} \u2014 a template must declare { title, description, tags } to be published`, hint: `add ${TEMPLATE_META_FILE} next to himalaya.content.json (or pass --source-dir <dir>)` }));
66133
66532
  return 2;
@@ -66137,7 +66536,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66137
66536
  return 2;
66138
66537
  }
66139
66538
  try {
66140
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync72(join57(sourceDir, "tokens.json"), "utf8")));
66539
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync73(join58(sourceDir, "tokens.json"), "utf8")));
66141
66540
  meta = { ...appearance, ...meta };
66142
66541
  } catch {
66143
66542
  }
@@ -66149,7 +66548,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66149
66548
  const force = flags.force === true;
66150
66549
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
66151
66550
  const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
66152
- const out = mkdtempSync4(join57(tmpdir5(), "himi-template-"));
66551
+ const out = mkdtempSync4(join58(tmpdir5(), "himi-template-"));
66153
66552
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
66154
66553
  const outcome = await buildReleaseFromContent2(buildDir, out, {
66155
66554
  ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
@@ -66202,7 +66601,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66202
66601
  let iconHash;
66203
66602
  if (iconPath) {
66204
66603
  try {
66205
- const bytes2 = readFileSync72(iconPath);
66604
+ const bytes2 = readFileSync73(iconPath);
66206
66605
  payload.icon = bytes2.toString("base64");
66207
66606
  iconHash = createHash27("sha256").update(bytes2).digest("hex");
66208
66607
  } catch {
@@ -66435,7 +66834,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66435
66834
  const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
66436
66835
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
66437
66836
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
66438
- const out = mkdtempSync4(join57(tmpdir5(), "himi-validate-"));
66837
+ const out = mkdtempSync4(join58(tmpdir5(), "himi-validate-"));
66439
66838
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
66440
66839
  const outcome = await buildReleaseFromContent2(dir, out, {
66441
66840
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -66478,7 +66877,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66478
66877
  const path2 = dpxSessionTokenPath();
66479
66878
  let raw;
66480
66879
  try {
66481
- raw = readFileSync72(path2, "utf8");
66880
+ raw = readFileSync73(path2, "utf8");
66482
66881
  } catch {
66483
66882
  throw new Error(`no DPX BO session at ${path2}; run dpx auth login, write dpx auth token to that path, then himi login --dpx --target bo`);
66484
66883
  }
@@ -66598,13 +66997,13 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66598
66997
  case "studio": {
66599
66998
  const { openStudio: openStudio2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
66600
66999
  const dir = contentDir(flags);
66601
- if (!existsSync60(join57(dir, "himalaya.content.json"))) {
67000
+ if (!existsSync61(join58(dir, "himalaya.content.json"))) {
66602
67001
  io.error(JSON.stringify({ ok: false, error: "not_a_content_package", dir, hint: "run from the app directory (or pass --content <dir>) \u2014 the board reads that package's .himi/" }));
66603
67002
  return 2;
66604
67003
  }
66605
67004
  if (pos[1] === "reel") {
66606
67005
  const { buildReel: buildReel2 } = await Promise.resolve().then(() => (init_studio_reel(), studio_reel_exports));
66607
- const out = resolve47(str6(flags.out) ?? join57(dir, ".himi", "making-of.html"));
67006
+ const out = resolve47(str6(flags.out) ?? join58(dir, ".himi", "making-of.html"));
66608
67007
  const { html, plan } = buildReel2(dir);
66609
67008
  mkdirSync30(dirname32(out), { recursive: true });
66610
67009
  writeFileSync28(out, html);
@@ -66654,7 +67053,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66654
67053
  const { appendProgress: appendProgress2, readProgress: readProgress2, summarizeProgress: summarizeProgress2, parseStep: parseStep2, notifyDesktop: notifyDesktop2, appLabel: appLabel2, journalPath: journalPath2, ProgressError: ProgressError2, JOURNEY_STEPS: JOURNEY_STEPS2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
66655
67054
  const dir = contentDir(flags);
66656
67055
  const journal = journalPath2(dir);
66657
- const hint = existsSync60(join57(dir, "himalaya.content.json")) ? {} : { hint: "no himalaya.content.json here \u2014 run from the app directory (or pass --content <dir>) so the build board finds this journal" };
67056
+ const hint = existsSync61(join58(dir, "himalaya.content.json")) ? {} : { hint: "no himalaya.content.json here \u2014 run from the app directory (or pass --content <dir>) so the build board finds this journal" };
66658
67057
  const message = pos.slice(1).join(" ").trim();
66659
67058
  if (!message) {
66660
67059
  if (flags.step !== void 0 || flags.image !== void 0 || flags.notify === true) {
@@ -66746,7 +67145,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66746
67145
  case "test": {
66747
67146
  const dir = contentDir(flags);
66748
67147
  const structuredOutput = flags.json === true || io.isTTY !== true;
66749
- if (existsSync60(join57(dir, "functions"))) {
67148
+ if (existsSync61(join58(dir, "functions"))) {
66750
67149
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
66751
67150
  const loaded = await loadFunctionsApp2(dir);
66752
67151
  const results = [];
@@ -66833,7 +67232,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66833
67232
  }
66834
67233
  }
66835
67234
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
66836
- const out = mkdtempSync4(join57(tmpdir5(), "himi-test-"));
67235
+ const out = mkdtempSync4(join58(tmpdir5(), "himi-test-"));
66837
67236
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
66838
67237
  const outcome = await buildReleaseFromContent2(dir, out, {
66839
67238
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -66878,7 +67277,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66878
67277
  io.error(JSON.stringify({ error: `screen "${screen.screenId}" has no Tier-5 worker, so there is nothing for the browser loop to build` }));
66879
67278
  return 2;
66880
67279
  }
66881
- const sources = collectWorkerSources2(join57(dir, "tier5-src"), screen.bundleName);
67280
+ const sources = collectWorkerSources2(join58(dir, "tier5-src"), screen.bundleName);
66882
67281
  if (!sources) {
66883
67282
  io.error(JSON.stringify({ error: `no worker source found at tier5-src/${screen.bundleName} \u2014 the browser loop compiles SOURCE, not the built bundle` }));
66884
67283
  return 2;
@@ -67039,7 +67438,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67039
67438
  const sp = await runShotsPass2({
67040
67439
  descriptors: scopeDescriptors2(outcome.descriptors ?? [], pos.slice(1), invalidScreens),
67041
67440
  tokens: outcome.tokens,
67042
- outDir: join57(dir, ".himi", "shots"),
67441
+ outDir: join58(dir, ".himi", "shots"),
67043
67442
  width: 390,
67044
67443
  layers: { rules: shotRules, ...cmsSeed ? { cmsSeed } : {}, ...canned ? { canned: canned.byKey } : {} },
67045
67444
  ...budgetMs ? { budgetMs } : {},
@@ -67077,7 +67476,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67077
67476
  releaseId: result.releaseId,
67078
67477
  releasePayload: release,
67079
67478
  descriptors: attestedDescriptors,
67080
- screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join57(dir, ".himi", "shots", name)])),
67479
+ screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join58(dir, ".himi", "shots", name)])),
67081
67480
  coverage: {
67082
67481
  screens: crawledScreens.size,
67083
67482
  actionsDispatched: result.actions.exercised,
@@ -67167,7 +67566,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67167
67566
  return 2;
67168
67567
  }
67169
67568
  try {
67170
- const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync72(stateArg.replace(/^@/, ""), "utf8");
67569
+ const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync73(stateArg.replace(/^@/, ""), "utf8");
67171
67570
  injectedState = JSON.parse(raw);
67172
67571
  } catch (e) {
67173
67572
  io.error(JSON.stringify({ error: `--state must be inline JSON or a path to a .json file \u2014 ${e.message}` }));
@@ -67182,7 +67581,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67182
67581
  return 2;
67183
67582
  }
67184
67583
  try {
67185
- const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync72(inputArg.replace(/^@/, ""), "utf8");
67584
+ const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync73(inputArg.replace(/^@/, ""), "utf8");
67186
67585
  const parsed = JSON.parse(raw);
67187
67586
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("must be a JSON object");
67188
67587
  injectedInput = parsed;
@@ -67231,7 +67630,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67231
67630
  }
67232
67631
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
67233
67632
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
67234
- const out = mkdtempSync4(join57(tmpdir5(), "himi-render-"));
67633
+ const out = mkdtempSync4(join58(tmpdir5(), "himi-render-"));
67235
67634
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
67236
67635
  const outcome = await buildReleaseFromContent2(dir, out, {
67237
67636
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -67245,7 +67644,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67245
67644
  const diagnostics = (outcome.lint ?? []).find((s) => s.screenId === screenId)?.issues ?? [];
67246
67645
  let pngFile;
67247
67646
  let attestationPngFile;
67248
- const shotPath = join57(dir, ".himi", "shots", `${screenId}.png`);
67647
+ const shotPath = join58(dir, ".himi", "shots", `${screenId}.png`);
67249
67648
  const pngTarget = typeof flags.png === "string" ? str6(flags.png) : flags.png === true ? shotPath : void 0;
67250
67649
  if (pngTarget && r.pngBase64) {
67251
67650
  pngFile = writePngFile(pngTarget, r.pngBase64);
@@ -67581,7 +67980,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
67581
67980
  io.error(JSON.stringify(linked));
67582
67981
  return 1;
67583
67982
  }
67584
- if (existsSync60(dest)) {
67983
+ if (existsSync61(dest)) {
67585
67984
  if (!statSync17(dest).isDirectory()) {
67586
67985
  io.error(JSON.stringify({
67587
67986
  error: `${dest} exists and is not a directory`,
@@ -67594,7 +67993,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
67594
67993
  io.error(JSON.stringify({ error: `${dest} already exists and is not empty`, hint: "pass --force to write into it anyway (merging, and naming what it keeps), --replace to restore exactly what was archived, or --out/--name to land somewhere else" }));
67595
67994
  return 1;
67596
67995
  }
67597
- if (replace && occupied && !existsSync60(join57(dest, "himalaya.content.json"))) {
67996
+ if (replace && occupied && !existsSync61(join58(dest, "himalaya.content.json"))) {
67598
67997
  io.error(JSON.stringify({
67599
67998
  error: `${dest} is not a content package \u2014 refusing to --replace it`,
67600
67999
  hint: "--replace only clears a directory that holds himalaya.content.json. Check --out/--name, or pass --force to merge into it instead."
@@ -67733,7 +68132,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
67733
68132
  const force = flags.force === true;
67734
68133
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
67735
68134
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
67736
- const out = mkdtempSync4(join57(tmpdir5(), "himi-push-"));
68135
+ const out = mkdtempSync4(join58(tmpdir5(), "himi-push-"));
67737
68136
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
67738
68137
  const outcome = await buildReleaseFromContent2(dir, out, {
67739
68138
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -68254,7 +68653,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68254
68653
  }
68255
68654
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68256
68655
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
68257
- const built = mkdtempSync4(join57(tmpdir5(), "himi-pack-"));
68656
+ const built = mkdtempSync4(join58(tmpdir5(), "himi-pack-"));
68258
68657
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68259
68658
  const outcome = await buildReleaseFromContent2(dir, built, {
68260
68659
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -68457,7 +68856,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68457
68856
  }));
68458
68857
  return 1;
68459
68858
  }
68460
- const archive = readFileSync72(resolve47(file));
68859
+ const archive = readFileSync73(resolve47(file));
68461
68860
  const result = await client2().createPackageShare({
68462
68861
  app: opened.header.app,
68463
68862
  releaseId: opened.header.releaseId,
@@ -68511,11 +68910,11 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68511
68910
  const app = await appId(dir2);
68512
68911
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68513
68912
  const bres = await buildContentBundles2(dir2, { resolution: resolutionFlag(flags) });
68514
- temp = mkdtempSync4(join57(tmpdir5(), "himi-run-build-"));
68913
+ temp = mkdtempSync4(join58(tmpdir5(), "himi-run-build-"));
68515
68914
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68516
68915
  const initial = await buildReleaseFromContent2(dir2, temp, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir2) } : {} });
68517
68916
  servedMissing = initial.missingBundles;
68518
- source = join57(temp, app);
68917
+ source = join58(temp, app);
68519
68918
  }
68520
68919
  const chromeArg = str6(flags.chrome) ?? "preview";
68521
68920
  if (chromeArg !== "preview" && chromeArg !== "app") {
@@ -68551,7 +68950,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68551
68950
  if (!recordKit) return;
68552
68951
  startedAt = (/* @__PURE__ */ new Date()).toISOString();
68553
68952
  const failure = record2(served2, { ok: true, at: startedAt });
68554
- if (!recorded) io.error(JSON.stringify({ run: "run_record_skipped", message: `${failure ?? `${join57(dir, ".himi")} is not a real directory`}, so the build board will not frame this preview.` }));
68953
+ if (!recorded) io.error(JSON.stringify({ run: "run_record_skipped", message: `${failure ?? `${join58(dir, ".himi")} is not a real directory`}, so the build board will not frame this preview.` }));
68555
68954
  await refreshBoard();
68556
68955
  };
68557
68956
  const retract = async () => {
@@ -68582,7 +68981,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68582
68981
  }
68583
68982
  const watchKit = watch2 ? await Promise.resolve().then(() => (init_run_watch(), run_watch_exports)) : null;
68584
68983
  const bus = watchKit ? new watchKit.ReloadBus() : void 0;
68585
- if (watch2) served = mkdtempSync4(join57(tmpdir5(), "himi-run-served-"));
68984
+ if (watch2) served = mkdtempSync4(join58(tmpdir5(), "himi-run-served-"));
68586
68985
  const running = await runPackage2(source, {
68587
68986
  port,
68588
68987
  chrome: chromeArg,
@@ -68594,15 +68993,15 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68594
68993
  ...recordKit ? { extraRoutes: (request) => recordKit.boardFile(dir, new URL(request.url).pathname) } : {},
68595
68994
  ...watchKit && served && bus ? { workDir: served, screenEvents: bus } : {}
68596
68995
  });
68597
- let watcher;
68996
+ let watcher2;
68598
68997
  let rebuild;
68599
68998
  let stopping = false;
68600
68999
  try {
68601
69000
  await announce(running);
68602
69001
  if (watchKit && bus) {
68603
69002
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68604
- const live2 = join57(running.releaseRoot, running.app);
68605
- const manifestOf = (tree) => JSON.parse(readFileSync72(join57(tree, "release-manifest.json"), "utf8"));
69003
+ const live2 = join58(running.releaseRoot, running.app);
69004
+ const manifestOf = (tree) => JSON.parse(readFileSync73(join58(tree, "release-manifest.json"), "utf8"));
68606
69005
  let current = manifestOf(live2);
68607
69006
  const failed2 = async (message) => {
68608
69007
  if (stopping) return;
@@ -68613,7 +69012,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68613
69012
  };
68614
69013
  rebuild = watchKit.serialize(async () => {
68615
69014
  const t0 = Date.now();
68616
- const next = mkdtempSync4(join57(tmpdir5(), "himi-run-build-"));
69015
+ const next = mkdtempSync4(join58(tmpdir5(), "himi-run-build-"));
68617
69016
  try {
68618
69017
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
68619
69018
  if (bres.compileErrors.length) {
@@ -68630,14 +69029,14 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68630
69029
  return;
68631
69030
  }
68632
69031
  if (stopping) return;
68633
- const fresh = manifestOf(join57(next, running.app));
69032
+ const fresh = manifestOf(join58(next, running.app));
68634
69033
  const diff = watchKit.diffReleases(current, fresh);
68635
69034
  const retired = `${live2}.retired-${Date.now()}`;
68636
69035
  renameSync6(live2, retired);
68637
69036
  try {
68638
- renameSync6(join57(next, running.app), live2);
69037
+ renameSync6(join58(next, running.app), live2);
68639
69038
  } catch {
68640
- cpSync3(join57(next, running.app), live2, { recursive: true });
69039
+ cpSync3(join58(next, running.app), live2, { recursive: true });
68641
69040
  }
68642
69041
  rmSync14(retired, { recursive: true, force: true });
68643
69042
  current = fresh;
@@ -68652,7 +69051,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68652
69051
  rmSync14(next, { recursive: true, force: true });
68653
69052
  }
68654
69053
  });
68655
- watcher = watchKit.watchContent(dir, () => rebuild?.());
69054
+ watcher2 = watchKit.watchContent(dir, () => rebuild?.());
68656
69055
  }
68657
69056
  } catch (e) {
68658
69057
  await retract().catch(() => {
@@ -68674,7 +69073,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68674
69073
  await new Promise((done) => {
68675
69074
  const stop = async () => {
68676
69075
  stopping = true;
68677
- watcher?.close();
69076
+ watcher2?.close();
68678
69077
  try {
68679
69078
  await rebuild?.stop();
68680
69079
  await retract();
@@ -69235,7 +69634,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69235
69634
  io.error(JSON.stringify({ error: `patch ${kind} needs --file`, usage: PATCH_USAGE }));
69236
69635
  return 2;
69237
69636
  }
69238
- if (!existsSync60(file)) {
69637
+ if (!existsSync61(file)) {
69239
69638
  io.error(JSON.stringify({ error: `no such file: ${file}` }));
69240
69639
  return 2;
69241
69640
  }
@@ -69291,7 +69690,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69291
69690
  lintNote: "`himi patch` sends a raw file. Nothing here built or linted your app \u2014 `himi push --only <selector>` is the same edit with the full lint gate in front of it."
69292
69691
  };
69293
69692
  if (kind === "tokens" || kind === "app-config") {
69294
- const body = readFileSync72(file, "utf8");
69693
+ const body = readFileSync73(file, "utf8");
69295
69694
  const patch2 = kind === "tokens" ? { tokens: body } : { appConfig: body };
69296
69695
  return runReleasePatch({
69297
69696
  ...common,
@@ -69306,7 +69705,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69306
69705
  io.error(JSON.stringify({ error: "patch screen needs a <screenId>", usage: PATCH_USAGE }));
69307
69706
  return 2;
69308
69707
  }
69309
- const descriptor = readFileSync72(file, "utf8");
69708
+ const descriptor = readFileSync73(file, "utf8");
69310
69709
  try {
69311
69710
  JSON.parse(descriptor);
69312
69711
  } catch (e) {
@@ -69346,7 +69745,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69346
69745
  extra: { kind, screen: id, file: { path: file, bytes: Buffer.byteLength(descriptor, "utf8") } }
69347
69746
  });
69348
69747
  }
69349
- const bytes2 = readFileSync72(file);
69748
+ const bytes2 = readFileSync73(file);
69350
69749
  const sha = createHash27("sha256").update(bytes2).digest("hex");
69351
69750
  const forScreens = (str6(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
69352
69751
  if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
@@ -69490,7 +69889,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69490
69889
  }
69491
69890
  let def;
69492
69891
  try {
69493
- const parsed = JSON.parse(arg.startsWith("@") ? readFileSync72(arg.slice(1), "utf8") : arg);
69892
+ const parsed = JSON.parse(arg.startsWith("@") ? readFileSync73(arg.slice(1), "utf8") : arg);
69494
69893
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
69495
69894
  io.error(JSON.stringify({ error: "invalid_definition", problems: [`--definition must be a JSON object, got ${Array.isArray(parsed) ? "an array" : parsed === null ? "null" : typeof parsed}`] }));
69496
69895
  return 2;
@@ -69954,7 +70353,7 @@ ${label2}`);
69954
70353
  const platform = str6(flags.platform)?.split(",").map((p) => p.trim()).filter(Boolean);
69955
70354
  {
69956
70355
  const distDir = contentDir(flags);
69957
- if (existsSync60(join57(distDir, "himalaya.content.json"))) {
70356
+ if (existsSync61(join58(distDir, "himalaya.content.json"))) {
69958
70357
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
69959
70358
  const errors = issues.filter((i) => i.severity === "error");
69960
70359
  for (const i of issues.filter((i2) => i2.severity === "warning")) {
@@ -70223,7 +70622,7 @@ ${label2}`);
70223
70622
  const dpxPath = dpxSessionTokenPath();
70224
70623
  let dpx;
70225
70624
  try {
70226
- dpx = readFileSync72(dpxPath, "utf8").trim();
70625
+ dpx = readFileSync73(dpxPath, "utf8").trim();
70227
70626
  } catch {
70228
70627
  io.error(JSON.stringify({ error: `no dpx session at ${dpxPath} \u2014 run \`dpx auth login\` then \`dpx auth token > ${dpxPath}\` (dpx 2.x keeps its own state in ~/.dpx/auth.json and does NOT write the legacy session file), or omit --dpx` }));
70229
70628
  return 1;
@@ -70625,7 +71024,7 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
70625
71024
  const path2 = dpxSessionTokenPath();
70626
71025
  let raw;
70627
71026
  try {
70628
- raw = readFileSync72(path2, "utf8");
71027
+ raw = readFileSync73(path2, "utf8");
70629
71028
  } catch {
70630
71029
  io.error(JSON.stringify({ error: `no DPX BO session at ${path2}` }));
70631
71030
  return 1;