@wix/himalaya-cli 0.1025.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 (141) 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 -655
  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 +1476 -553
  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/tier5-src/_shared/detail-presence.ts +51 -0
  11. package/templates/beauty-salon/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  12. package/templates/beauty-salon/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  13. package/templates/beauty-salon/tier5-src/wix-spaces-create-post/index.ts +14 -4
  14. package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  15. package/templates/beauty-salon/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  16. package/templates/beauty-salon/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  17. package/templates/beauty-salon-owner/dev/index.ts +857 -458
  18. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +18 -8
  19. package/templates/beauty-salon-owner/tier5-src/_shared/collection-service.ts +28 -3
  20. package/templates/branded-lite/config.ts +3 -1
  21. package/templates/branded-lite/dev/index.ts +6123 -2565
  22. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +18 -8
  23. package/templates/branded-lite/tier5-src/_shared/collection-service.ts +28 -3
  24. package/templates/branded-lite/tier5-src/_shared/detail-presence.ts +51 -0
  25. package/templates/branded-lite/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  26. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +7 -2
  27. package/templates/branded-lite/tier5-src/himi-branded-lite-simple-detail/index.ts +4 -3
  28. package/templates/branded-lite/tier5-src/wix-spaces-chat-thread/index.ts +17 -4
  29. package/templates/branded-lite/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  30. package/templates/branded-lite/tier5-src/wix-spaces-coupon-detail/index.ts +7 -9
  31. package/templates/branded-lite/tier5-src/wix-spaces-create-post/index.ts +14 -4
  32. package/templates/branded-lite/tier5-src/wix-spaces-food-order-detail/index.ts +6 -3
  33. package/templates/branded-lite/tier5-src/wix-spaces-form-detail/index.ts +7 -9
  34. package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +7 -9
  35. package/templates/branded-lite/tier5-src/wix-spaces-invoice-detail/index.ts +7 -9
  36. package/templates/branded-lite/tier5-src/wix-spaces-member-detail/index.ts +8 -8
  37. package/templates/branded-lite/tier5-src/wix-spaces-photo-albums/index.ts +8 -2
  38. package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  39. package/templates/branded-lite/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  40. package/templates/branded-lite/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  41. package/templates/community-club/dev/index.ts +582 -239
  42. package/templates/community-club/tier5-src/_shared/collection-contract.ts +18 -8
  43. package/templates/community-club/tier5-src/_shared/collection-service.ts +28 -3
  44. package/templates/community-club/tier5-src/wix-spaces-create-post/index.ts +14 -4
  45. package/templates/community-club/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  46. package/templates/community-club-owner/dev/index.ts +857 -458
  47. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +18 -8
  48. package/templates/community-club-owner/tier5-src/_shared/collection-service.ts +28 -3
  49. package/templates/events-meetups/dev/index.ts +547 -226
  50. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +18 -8
  51. package/templates/events-meetups/tier5-src/_shared/collection-service.ts +28 -3
  52. package/templates/events-meetups/tier5-src/wix-spaces-create-post/index.ts +14 -4
  53. package/templates/events-meetups/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  54. package/templates/events-meetups-owner/dev/index.ts +815 -458
  55. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +18 -8
  56. package/templates/events-meetups-owner/tier5-src/_shared/collection-service.ts +28 -3
  57. package/templates/gym-fitness/dev/index.ts +1609 -569
  58. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +18 -8
  59. package/templates/gym-fitness/tier5-src/_shared/collection-service.ts +28 -3
  60. package/templates/gym-fitness/tier5-src/_shared/detail-presence.ts +51 -0
  61. package/templates/gym-fitness/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  62. package/templates/gym-fitness/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  63. package/templates/gym-fitness/tier5-src/wix-spaces-create-post/index.ts +14 -4
  64. package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  65. package/templates/gym-fitness/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  66. package/templates/gym-fitness/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  67. package/templates/gym-fitness-owner/dev/index.ts +758 -338
  68. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +18 -8
  69. package/templates/gym-fitness-owner/tier5-src/_shared/collection-service.ts +28 -3
  70. package/templates/himi-chat/dev/index.ts +26 -1
  71. package/templates/nonprofit-cause/dev/index.ts +978 -341
  72. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +18 -8
  73. package/templates/nonprofit-cause/tier5-src/_shared/collection-service.ts +28 -3
  74. package/templates/nonprofit-cause/tier5-src/_shared/detail-presence.ts +51 -0
  75. package/templates/nonprofit-cause/tier5-src/wix-spaces-create-post/index.ts +14 -4
  76. package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  77. package/templates/nonprofit-cause/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  78. package/templates/nonprofit-cause/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  79. package/templates/nonprofit-cause-owner/dev/index.ts +716 -338
  80. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +18 -8
  81. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-service.ts +28 -3
  82. package/templates/owner-lite/dev/index.ts +758 -338
  83. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +18 -8
  84. package/templates/owner-lite/tier5-src/_shared/collection-service.ts +28 -3
  85. package/templates/podcast-creator/dev/index.ts +1111 -399
  86. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +18 -8
  87. package/templates/podcast-creator/tier5-src/_shared/collection-service.ts +28 -3
  88. package/templates/podcast-creator/tier5-src/_shared/detail-presence.ts +51 -0
  89. package/templates/podcast-creator/tier5-src/himi-branded-lite-simple-detail/index.ts +4 -3
  90. package/templates/podcast-creator/tier5-src/wix-spaces-create-post/index.ts +14 -4
  91. package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  92. package/templates/podcast-creator/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  93. package/templates/podcast-creator/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  94. package/templates/podcast-creator-owner/dev/index.ts +857 -458
  95. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +18 -8
  96. package/templates/podcast-creator-owner/tier5-src/_shared/collection-service.ts +28 -3
  97. package/templates/portfolio-creative/dev/index.ts +964 -345
  98. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +18 -8
  99. package/templates/portfolio-creative/tier5-src/_shared/collection-service.ts +28 -3
  100. package/templates/portfolio-creative/tier5-src/_shared/detail-presence.ts +51 -0
  101. package/templates/portfolio-creative/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  102. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +7 -2
  103. package/templates/portfolio-creative/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  104. package/templates/portfolio-creative/tier5-src/wix-spaces-create-post/index.ts +14 -4
  105. package/templates/portfolio-creative/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  106. package/templates/portfolio-creative-owner/dev/index.ts +857 -458
  107. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +18 -8
  108. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-service.ts +28 -3
  109. package/templates/restaurant/dev/index.ts +738 -284
  110. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +18 -8
  111. package/templates/restaurant/tier5-src/_shared/collection-service.ts +28 -3
  112. package/templates/restaurant/tier5-src/_shared/detail-presence.ts +51 -0
  113. package/templates/restaurant/tier5-src/wix-spaces-create-post/index.ts +14 -4
  114. package/templates/restaurant/tier5-src/wix-spaces-food-order-detail/index.ts +6 -3
  115. package/templates/restaurant/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  116. package/templates/restaurant-owner/dev/index.ts +758 -338
  117. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +18 -8
  118. package/templates/restaurant-owner/tier5-src/_shared/collection-service.ts +28 -3
  119. package/templates/retail-store/dev/index.ts +768 -298
  120. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +18 -8
  121. package/templates/retail-store/tier5-src/_shared/collection-service.ts +28 -3
  122. package/templates/retail-store/tier5-src/_shared/detail-presence.ts +51 -0
  123. package/templates/retail-store/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  124. package/templates/retail-store/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  125. package/templates/retail-store/tier5-src/wix-spaces-create-post/index.ts +14 -4
  126. package/templates/retail-store/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  127. package/templates/retail-store-owner/dev/index.ts +716 -338
  128. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +18 -8
  129. package/templates/retail-store-owner/tier5-src/_shared/collection-service.ts +28 -3
  130. package/templates/surface-showcase/dev/index.ts +27 -0
  131. package/templates/yoga-studio/dev/index.ts +1147 -390
  132. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +18 -8
  133. package/templates/yoga-studio/tier5-src/_shared/collection-service.ts +28 -3
  134. package/templates/yoga-studio/tier5-src/_shared/detail-presence.ts +51 -0
  135. package/templates/yoga-studio/tier5-src/wix-spaces-create-post/index.ts +14 -4
  136. package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  137. package/templates/yoga-studio/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  138. package/templates/yoga-studio/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  139. package/templates/yoga-studio-owner/dev/index.ts +716 -338
  140. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +18 -8
  141. 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:`
@@ -20046,7 +20045,6 @@ function shouldRevealError(args) {
20046
20045
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
20047
20046
  var init_validation = __esm({
20048
20047
  "../../core/ts/src/validation.ts"() {
20049
- "use strict";
20050
20048
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
20051
20049
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
20052
20050
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -22908,6 +22906,65 @@ var init_controls = __esm({
22908
22906
  }
22909
22907
  });
22910
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
+
22911
22968
  // ../../core/web-renderer/src/listGestures.tsx
22912
22969
  import { useRef, useState as useState2 } from "react";
22913
22970
  import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
@@ -23490,7 +23547,6 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
23490
23547
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
23491
23548
  var init_worker_subtree = __esm({
23492
23549
  "../../core/ts/src/worker-subtree.ts"() {
23493
- "use strict";
23494
23550
  init_worker_subtree_safe_types_generated();
23495
23551
  WORKER_SUBTREE_CEILINGS = Object.freeze({
23496
23552
  maxEncodedBytes: 65536,
@@ -24160,8 +24216,8 @@ function autoGrowBox(contentHeight, lineHeight, verticalPadding, maxLines) {
24160
24216
  const cap = Math.max(floor, lineHeight * lines + verticalPadding);
24161
24217
  return { height: Math.max(floor, Math.min(contentHeight, cap)), scrolls: contentHeight > cap };
24162
24218
  }
24163
- function wrapModalMenu(modal, label2, placement, menu) {
24164
- 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;
24165
24221
  }
24166
24222
  function bindMediaSlot(value, sessionId) {
24167
24223
  if (typeof value === "string") {
@@ -24776,9 +24832,37 @@ function ensureInitialFocusRuntime() {
24776
24832
  restoreFocusOnDismiss(presentedScope, active);
24777
24833
  }
24778
24834
  el.focus();
24835
+ if (document.activeElement === el) enterAuthoredOrderOnTab(el);
24779
24836
  }
24780
24837
  }, 0);
24781
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
+ }
24782
24866
  function ariaModal(modal, dflt) {
24783
24867
  const on3 = modal === true || modal === false ? modal : dflt;
24784
24868
  return on3 ? "true" : void 0;
@@ -25747,6 +25831,7 @@ var init_renderNode = __esm({
25747
25831
  init_icons();
25748
25832
  init_icon_catalog_generated();
25749
25833
  init_controls();
25834
+ init_modalStack();
25750
25835
  init_listGestures();
25751
25836
  init_worker_subtree();
25752
25837
  init_preview_surfaces();
@@ -26277,6 +26362,7 @@ var init_renderNode = __esm({
26277
26362
  const popupPlacement = { position: "absolute", top: "100%", right: 0, zIndex: 20, marginTop: 4 };
26278
26363
  const anchorRef = React5.useRef(null);
26279
26364
  const menuRef = React5.useRef(null);
26365
+ const dialogRef = React5.useRef(null);
26280
26366
  const itemsKey = items.map((it, i) => `${it?.id ?? i}:${it?.disabled ? 0 : 1}`).join("|");
26281
26367
  React5.useEffect(() => {
26282
26368
  if (!open || !modal) return;
@@ -26286,7 +26372,9 @@ var init_renderNode = __esm({
26286
26372
  }, [open, modal, itemsKey]);
26287
26373
  React5.useEffect(() => {
26288
26374
  if (!open || !modal) return;
26375
+ const layer = pushModalLayer(() => dialogRef.current);
26289
26376
  return () => {
26377
+ layer.release();
26290
26378
  const active = document.activeElement;
26291
26379
  if (active == null || active === document.body || !document.contains(active)) anchorRef.current?.focus();
26292
26380
  };
@@ -26324,11 +26412,11 @@ var init_renderNode = __esm({
26324
26412
  setOpen((v) => !v);
26325
26413
  }
26326
26414
  },
26327
- style: { display: "inline-flex", alignItems: "center", cursor: "pointer" },
26415
+ style: { display: "inline-flex", alignItems: "center", justifyContent: "center", minWidth: 44, minHeight: 44, cursor: "pointer" },
26328
26416
  children: props.anchor ? renderNode(props.anchor, ctx) : iconSvg("more", { size: 20, color: C("textSecondary", "#636b78") })
26329
26417
  }
26330
26418
  ),
26331
- open && wrapModalMenu(modal, anchorLabel, popupPlacement, /* @__PURE__ */ jsx6(
26419
+ open && wrapModalMenu(modal, anchorLabel, popupPlacement, dialogRef, /* @__PURE__ */ jsx6(
26332
26420
  "div",
26333
26421
  {
26334
26422
  ref: menuRef,
@@ -27390,7 +27478,7 @@ var init_renderNode = __esm({
27390
27478
  const tAlign = props.textAlign === "center" ? "center" : props.textAlign === "trailing" ? "right" : props.textAlign === "justify" ? "justify" : props.textAlign === "leading" ? "left" : void 0;
27391
27479
  const tDecor = props.decoration === "strikethrough" ? "line-through" : props.decoration === "underline" ? "underline" : void 0;
27392
27480
  const ln = typeof props.lineSpacing === "number" ? pxToRem((f.line ?? 1.4) * f.size + props.lineSpacing) : f.line;
27393
- 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 });
27394
27482
  });
27395
27483
  ImageNode = withExtras("ImageNode", ({ props, ctx, wrap }) => {
27396
27484
  const C = ctx.color;
@@ -28266,18 +28354,23 @@ var init_renderNode = __esm({
28266
28354
  const next = multiSelect ? isSel ? selected.filter((x) => x !== id) : [...selected, id] : isSel ? [] : [id];
28267
28355
  ctx.onAction({ ...onChange, args: { ...onChange.args ?? {}, selectedIds: next } });
28268
28356
  };
28269
- return /* @__PURE__ */ jsx6("div", { role: "group", style: { display: "flex", gap: 8, flexWrap: "wrap", width: "100%", ...wrap }, children: chips.map((c, i) => {
28270
- const on3 = selected.includes(c.id);
28271
- 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 };
28272
- const inner = /* @__PURE__ */ jsxs3(Fragment3, { children: [
28273
- asText2(c.label),
28274
- c.count != null && /* @__PURE__ */ jsxs3(Fragment3, { children: [
28275
- " ",
28276
- /* @__PURE__ */ jsx6("span", { style: { opacity: 0.7, fontSize: 11 }, children: c.count })
28277
- ] })
28278
- ] });
28279
- 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);
28280
- }) });
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
+ );
28281
28374
  });
28282
28375
  TILE_PX = 256;
28283
28376
  MAX_MERCATOR_LAT = 85.05112878;
@@ -32791,18 +32884,18 @@ function startBundleArtifactWatcher(state) {
32791
32884
  });
32792
32885
  };
32793
32886
  try {
32794
- const watcher = watch(TIER5_ROOT2, { persistent: false }, (_event, filename) => {
32887
+ const watcher2 = watch(TIER5_ROOT2, { persistent: false }, (_event, filename) => {
32795
32888
  const bundle = builtBundleName(filename);
32796
32889
  if (!bundle) return;
32797
32890
  pending.add(bundle);
32798
32891
  if (timer) clearTimeout(timer);
32799
32892
  timer = setTimeout(flush, 120);
32800
32893
  });
32801
- watcher.on("error", (err) => {
32894
+ watcher2.on("error", (err) => {
32802
32895
  console.warn(`[himalaya-dev] bundle watcher stopped: ${err.message}`);
32803
32896
  });
32804
32897
  console.log(`[himalaya-dev] watching built bundles: ${TIER5_ROOT2}`);
32805
- return watcher;
32898
+ return watcher2;
32806
32899
  } catch (err) {
32807
32900
  console.warn(`[himalaya-dev] bundle watcher unavailable: ${err.message}`);
32808
32901
  return void 0;
@@ -32837,7 +32930,7 @@ function startBundleSourceWatcher(state) {
32837
32930
  building = false;
32838
32931
  };
32839
32932
  try {
32840
- 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) => {
32841
32934
  if (!filename || !/\.(ts|tsx)$/.test(filename)) return;
32842
32935
  const name = filename.split(sep7)[0];
32843
32936
  if (!name || name.startsWith(".")) return;
@@ -32845,11 +32938,11 @@ function startBundleSourceWatcher(state) {
32845
32938
  if (timer) clearTimeout(timer);
32846
32939
  timer = setTimeout(flush, 200);
32847
32940
  });
32848
- watcher.on("error", (err) => {
32941
+ watcher2.on("error", (err) => {
32849
32942
  console.warn(`[himalaya-dev] bundle source watcher stopped: ${err.message}`);
32850
32943
  });
32851
32944
  console.log(`[himalaya-dev] watching T5 bundle sources: ${TIER5_SRC_ROOT}`);
32852
- return watcher;
32945
+ return watcher2;
32853
32946
  } catch (err) {
32854
32947
  console.warn(`[himalaya-dev] bundle source watcher unavailable: ${err.message}`);
32855
32948
  return void 0;
@@ -33017,7 +33110,7 @@ function startAppSourceWatcher(state) {
33017
33110
  }
33018
33111
  };
33019
33112
  try {
33020
- const watcher = watch(appDir2, { persistent: false, recursive: true }, (_event, filename) => {
33113
+ const watcher2 = watch(appDir2, { persistent: false, recursive: true }, (_event, filename) => {
33021
33114
  if (!filename) return;
33022
33115
  const rel = String(filename).split(sep7).join("/");
33023
33116
  if (!classifyAppPath(rel)) return;
@@ -33025,11 +33118,11 @@ function startAppSourceWatcher(state) {
33025
33118
  if (timer) clearTimeout(timer);
33026
33119
  timer = setTimeout(flush, 200);
33027
33120
  });
33028
- watcher.on("error", (err) => {
33121
+ watcher2.on("error", (err) => {
33029
33122
  console.warn(`[himalaya-dev] app source watcher stopped: ${err.message}`);
33030
33123
  });
33031
33124
  console.log(`[himalaya-dev] watching app source: ${appDir2}`);
33032
- return watcher;
33125
+ return watcher2;
33033
33126
  } catch (err) {
33034
33127
  console.warn(`[himalaya-dev] app source watcher unavailable: ${err.message}`);
33035
33128
  return void 0;
@@ -35699,10 +35792,10 @@ function cmsAnswer(seed2, method, bare, body) {
35699
35792
  pagingMetadata: { count: rows.length, tooManyToCount: false }
35700
35793
  };
35701
35794
  }
35702
- function layeredTransport(layers) {
35703
- const rules = layers.rules?.length ? [...layers.rules] : null;
35704
- const cms = layers.cmsSeed && Object.keys(layers.cmsSeed).length ? layers.cmsSeed : null;
35705
- 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;
35706
35799
  const label2 = [rules && "mocked", cms && "cms", canned && "canned"].filter(Boolean).join("+") || "offline";
35707
35800
  const call = async (method, path2, body) => {
35708
35801
  const m = String(method).toUpperCase();
@@ -39032,6 +39125,332 @@ var init_net_freeze = __esm({
39032
39125
  }
39033
39126
  });
39034
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
+
39035
39454
  // src/progress.ts
39036
39455
  var progress_exports = {};
39037
39456
  __export(progress_exports, {
@@ -39278,6 +39697,152 @@ var init_progress = __esm({
39278
39697
  }
39279
39698
  });
39280
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
+
39281
39846
  // src/studio.ts
39282
39847
  var studio_exports = {};
39283
39848
  __export(studio_exports, {
@@ -39296,8 +39861,8 @@ __export(studio_exports, {
39296
39861
  writeStudioRecord: () => writeStudioRecord
39297
39862
  });
39298
39863
  import { createHash as createHash20 } from "node:crypto";
39299
- import { existsSync as existsSync39, readdirSync as readdirSync20, readFileSync as readFileSync42, realpathSync as realpathSync4, statSync as statSync8 } from "node:fs";
39300
- 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";
39301
39866
  import { pathToFileURL as pathToFileURL7 } from "node:url";
39302
39867
  function realInside(dir, abs) {
39303
39868
  try {
@@ -39322,12 +39887,12 @@ function archiveFile(dir, memory, abs) {
39322
39887
  if (size === 0) return null;
39323
39888
  const key2 = relative6(dir, abs);
39324
39889
  const hit = memory.files?.[key2];
39325
- 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}`))) {
39326
39891
  return { sha: hit.sha, ext: hit.ext, file: `${hit.sha}.${hit.ext}` };
39327
39892
  }
39328
39893
  let archived;
39329
39894
  try {
39330
- archived = archiveBytes(dir, readFileSync42(real));
39895
+ archived = archiveBytes(dir, readFileSync43(real));
39331
39896
  } catch {
39332
39897
  return null;
39333
39898
  }
@@ -39336,17 +39901,17 @@ function archiveFile(dir, memory, abs) {
39336
39901
  return { sha: archived.sha256, ext: archived.ext, file: `${archived.sha256}.${archived.ext}` };
39337
39902
  }
39338
39903
  function loadMemory(dir, now) {
39339
- const raw = obj(readJson2(join36(dir, MEMORY_FILE)));
39904
+ const raw = obj(readJson2(join37(dir, MEMORY_FILE)));
39340
39905
  if (raw && raw.version === 1 && obj(raw.shots) && Array.isArray(raw.timeline)) return raw;
39341
39906
  return { version: 1, createdAt: now, shots: {}, timeline: [] };
39342
39907
  }
39343
39908
  function observeShots(dir, memory, now) {
39344
- const shotsDir = join36(dir, ".himi", "shots");
39909
+ const shotsDir = join37(dir, ".himi", "shots");
39345
39910
  const seen = /* @__PURE__ */ new Set();
39346
- 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() : [];
39347
39912
  for (const f of files) {
39348
39913
  const id = basename9(f, extname3(f));
39349
- const archived = archiveFile(dir, memory, join36(shotsDir, f));
39914
+ const archived = archiveFile(dir, memory, join37(shotsDir, f));
39350
39915
  if (!archived) continue;
39351
39916
  seen.add(id);
39352
39917
  const { sha, ext } = archived;
@@ -39361,14 +39926,14 @@ function observeShots(dir, memory, now) {
39361
39926
  memory.shots[id] = { ...prev, present: true };
39362
39927
  }
39363
39928
  }
39364
- if (existsSync39(shotsDir)) {
39929
+ if (existsSync40(shotsDir)) {
39365
39930
  for (const [id, shot] of Object.entries(memory.shots)) if (!seen.has(id) && shot.present) memory.shots[id] = { ...shot, present: false };
39366
39931
  }
39367
39932
  if (memory.timeline.length > MAX_TIMELINE) memory.timeline = memory.timeline.slice(-MAX_TIMELINE);
39368
39933
  return memory;
39369
39934
  }
39370
39935
  function palette(dir) {
39371
- const colors = obj(obj(readJson2(join36(dir, "tokens.json")))?.colors);
39936
+ const colors = obj(obj(readJson2(join37(dir, "tokens.json")))?.colors);
39372
39937
  if (!colors) return [];
39373
39938
  const pick2 = (key2) => {
39374
39939
  const v = colors[key2];
@@ -39378,7 +39943,7 @@ function palette(dir) {
39378
39943
  return ["accentPrimary", "accentSecondary", "accentSurface", "surfacePrimary", "textPrimary"].map(pick2).filter((x) => !!x);
39379
39944
  }
39380
39945
  function appIdentity(dir) {
39381
- const manifest = obj(readJson2(join36(dir, "himalaya.content.json")));
39946
+ const manifest = obj(readJson2(join37(dir, "himalaya.content.json")));
39382
39947
  const name = text(manifest?.name) ?? basename9(resolve33(dir));
39383
39948
  const icon = text(manifest?.icon)?.trim();
39384
39949
  const iconPath = icon ? resolve33(dir, icon) : null;
@@ -39386,12 +39951,12 @@ function appIdentity(dir) {
39386
39951
  return { name, iconFile: real && statSync8(real).isFile() ? iconPath : null };
39387
39952
  }
39388
39953
  function testRuns(dir) {
39389
- const att = join36(dir, ".himi", "attestations");
39390
- if (!existsSync39(att)) return [];
39954
+ const att = join37(dir, ".himi", "attestations");
39955
+ if (!existsSync40(att)) return [];
39391
39956
  const runs = [];
39392
39957
  for (const f of readdirSync20(att)) {
39393
39958
  if (!f.endsWith(".json") || f === "latest.json") continue;
39394
- const r = obj(readJson2(join36(att, f)));
39959
+ const r = obj(readJson2(join37(att, f)));
39395
39960
  if (!r || r.tool !== "himi-test" || typeof r.runId !== "string") continue;
39396
39961
  const report2 = obj(r.report);
39397
39962
  const coverage = obj(r.coverage);
@@ -39407,20 +39972,20 @@ function testRuns(dir) {
39407
39972
  return runs.sort((a, b) => a.runId.localeCompare(b.runId));
39408
39973
  }
39409
39974
  function siteHome(dir) {
39410
- for (const pages of [join36(dir, ".himi", "business-book", "evidence", "pages"), join36(dir, "evidence", "pages")]) {
39411
- 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;
39412
39977
  const home = readdirSync20(pages).filter((f) => /^home.*\.(png|jpe?g)$/i.test(f)).sort()[0];
39413
- if (home) return join36(pages, home);
39978
+ if (home) return join37(pages, home);
39414
39979
  }
39415
39980
  return null;
39416
39981
  }
39417
39982
  function uxStatuses(dir) {
39418
- 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);
39419
39984
  if (!platforms) return [];
39420
39985
  return Object.entries(platforms).map(([platform, rec]) => ({ platform, status: text(obj(rec)?.status) ?? "" })).filter((p) => p.status);
39421
39986
  }
39422
39987
  function liveRun(dir) {
39423
- const rec = obj(readJson2(join36(dir, RUN_RECORD)));
39988
+ const rec = obj(readJson2(join37(dir, RUN_RECORD)));
39424
39989
  const url = text(rec?.url);
39425
39990
  const pid = rec?.pid;
39426
39991
  if (!url || !/^https?:\/\//i.test(url) || typeof pid !== "number") return null;
@@ -39470,9 +40035,9 @@ function buildView(dir, memory, now) {
39470
40035
  const runs = testRuns(dir);
39471
40036
  const last = runs[runs.length - 1];
39472
40037
  const prev = runs[runs.length - 2];
39473
- const validate3 = obj(readJson2(join36(dir, VALIDATE_RECORD)));
39474
- const push = obj(readJson2(join36(dir, PUSH_RECORD)));
39475
- 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));
39476
40041
  const nav = [];
39477
40042
  if (Array.isArray(edges)) {
39478
40043
  const seen = /* @__PURE__ */ new Set();
@@ -39510,9 +40075,11 @@ function buildView(dir, memory, now) {
39510
40075
  },
39511
40076
  brand: { site: siteImg },
39512
40077
  nav,
39513
- live: liveRun(dir)
40078
+ live: liveRun(dir),
40079
+ timelineFrames: 0,
40080
+ reveal: memory.reveal ?? null
39514
40081
  };
39515
- const { generatedAt: _ignored, ...stable } = view;
40082
+ const { generatedAt: _ignored, timelineFrames: _count, ...stable } = view;
39516
40083
  const v = createHash20("sha256").update(JSON.stringify(stable)).digest("hex").slice(0, 16);
39517
40084
  return { v, ...view };
39518
40085
  }
@@ -39523,16 +40090,33 @@ function writeBoard(dir, now) {
39523
40090
  if (!realDirIn(dir, STUDIO_DIR)) throw new UnsafeHimiDirError(STUDIO_DIR);
39524
40091
  if (!realDirIn(dir, HISTORY_DIR)) throw new UnsafeHimiDirError(HISTORY_DIR);
39525
40092
  const memory = observeShots(dir, loadMemory(dir, now), now);
39526
- const view = buildView(dir, memory, now);
39527
- writeAtomic(join36(dir, MEMORY_FILE), JSON.stringify(memory));
39528
- 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)});
39529
40113
  `);
39530
- writeAtomic(join36(dir, STUDIO_PAGE), renderStudioHtml(view));
40114
+ writeAtomic(join37(dir, STUDIO_PAGE), renderStudioHtml(view));
39531
40115
  return view;
39532
40116
  }
39533
40117
  function refreshStudio(dir, now = /* @__PURE__ */ new Date()) {
39534
40118
  try {
39535
- if (!existsSync39(join36(dir, STUDIO_PAGE))) return { refreshed: false };
40119
+ if (!existsSync40(join37(dir, STUDIO_PAGE))) return { refreshed: false };
39536
40120
  return { refreshed: true, view: writeBoard(dir, now.toISOString()) };
39537
40121
  } catch {
39538
40122
  return { refreshed: false };
@@ -39540,225 +40124,38 @@ function refreshStudio(dir, now = /* @__PURE__ */ new Date()) {
39540
40124
  }
39541
40125
  function openStudio(dir, now = /* @__PURE__ */ new Date()) {
39542
40126
  const view = writeBoard(dir, now.toISOString());
39543
- const page = join36(dir, STUDIO_PAGE);
40127
+ const page = join37(dir, STUDIO_PAGE);
39544
40128
  return { page, url: pathToFileURL7(page).href, view };
39545
40129
  }
39546
40130
  function writeStudioRecord(dir, record2, value) {
39547
40131
  try {
39548
40132
  if (!realDirIn(dir, ".himi")) return;
39549
- writeAtomic(join36(dir, record2), `${JSON.stringify(value, null, 2)}
40133
+ writeAtomic(join37(dir, record2), `${JSON.stringify(value, null, 2)}
39550
40134
  `);
39551
40135
  } catch {
39552
40136
  }
39553
40137
  }
39554
- function renderStudioHtml(initial) {
39555
- const accent = initial.app.palette.find((p) => p.name === "accentPrimary")?.hex ?? "#3b6fd8";
39556
- return `<!doctype html>
39557
- <html lang="en">
39558
- <head>
39559
- <meta charset="utf-8">
39560
- <meta name="viewport" content="width=device-width, initial-scale=1">
39561
- <title>${escapeHtml(initial.app.name)} \xB7 build board</title>
39562
- <link rel="icon" id="fav" href="data:,">
39563
- <style>
39564
- :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)}
39565
- @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}}
39566
- *{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}
39567
- .wrap{max-width:1180px;margin:0 auto;padding:20px 20px 60px}
39568
- header{display:flex;align-items:center;gap:12px;margin-bottom:16px}
39569
- .icon{width:40px;height:40px;border-radius:10px;background:var(--accent);object-fit:cover;flex:none}
39570
- h1{font-size:20px;font-weight:600;margin:0}.sub{color:var(--muted);font-size:13px}
39571
- .right{margin-left:auto;text-align:right}.clock{font-size:22px;font-variant-numeric:tabular-nums;font-weight:600}
39572
- .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)}
39573
- .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)}
39574
- .step .lbl{font-size:11px;color:var(--faint);margin-top:4px;text-transform:capitalize}.step.cur .lbl{color:var(--ink);font-weight:600}
39575
- .card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px;box-shadow:var(--shadow)}
39576
- .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}
39577
- .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}}
39578
- h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin:0 0 10px}
39579
- .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}
39580
- .phone img,.phone iframe{width:100%;height:100%;object-fit:cover;object-position:top;border:0;display:block;cursor:zoom-in}
39581
- .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}
39582
- .cap{text-align:center;color:var(--muted);font-size:12px;margin-top:8px}
39583
- .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}
39584
- .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)}
39585
- .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}
39586
- .wall{display:grid;grid-template-columns:repeat(auto-fill,minmax(112px,1fr));gap:14px}
39587
- .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}
39588
- .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)}
39589
- .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}
39590
- .sw{display:flex;gap:6px}.sw span{width:22px;height:22px;border-radius:50%;border:1px solid var(--line)}
39591
- .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)}
39592
- .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}
39593
- .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}
39594
- .empty{color:var(--faint);font-size:13px;padding:8px 0}
39595
- #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}
39596
- .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}}
39597
- .changed .frame{animation:chg .9s ease both}@keyframes chg{35%{opacity:.25;transform:scale(.96)}100%{opacity:1;transform:none}}
39598
- .step.fresh .bar{animation:fill .7s ease both;transform-origin:left}@keyframes fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
39599
- .slidein{animation:slide .45s cubic-bezier(.2,.8,.2,1) both}@keyframes slide{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
39600
- .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}}
39601
- .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}}
39602
- .icon,.step.cur .bar{transition:background-color .8s}.sw.pop span{animation:pop .6s ease both}@keyframes pop{50%{transform:scale(1.25)}}
39603
- .quiet{color:var(--warn)}.quiet .dot{background:var(--warn)}.idle .dot{background:var(--faint)}
39604
- .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)}
39605
- #fx{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:8}
39606
- @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}}
39607
- @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}}
39608
- </style>
39609
- </head>
39610
- <body>
39611
- <div class="wrap">
39612
- <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>
39613
- <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>
39614
- <div><span class="live idle" id="agent"><span class="dot"></span><span id="agentt">Waiting for the agent</span></span></div>
39615
- <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>
39616
- <div class="steps" id="steps"></div>
39617
- <div class="card now"><div style="flex:1"><div class="meta" id="nowmeta">Now</div><div class="msg" id="nowmsg"></div></div></div>
39618
- <div class="grid">
39619
- <div><div class="card"><h2 id="focuslbl">Latest</h2><div id="focus"></div><div class="film" id="film"></div></div></div>
39620
- <div>
39621
- <div class="chips" id="chips"></div>
39622
- <div class="card section"><h2>Screens</h2><div class="wall" id="wall"></div></div>
39623
- <div class="card section" id="brandcard"><h2>Brand</h2><div class="brand" id="brand"></div></div>
39624
- <div class="card section" id="navcard"><h2>Navigation the test crawl followed</h2><div class="nav" id="nav"></div></div>
39625
- <div class="card section"><h2>Postcards</h2><ul class="log" id="log"></ul></div>
39626
- </div></div></div>
39627
- <div id="lb"><img alt=""></div>
39628
- <canvas id="fx" aria-hidden="true"></canvas>
39629
- <script>window.__himiInitial = ${scriptJson(initial)};</script>
39630
- <script>
39631
- (function(){
39632
- var cur=null, lastSeen=Date.now();
39633
- function $(id){return document.getElementById(id)}
39634
- function el(tag,cls,txt){var e=document.createElement(tag);if(cls)e.className=cls;if(txt!=null)e.textContent=txt;return e}
39635
- function clear(e){while(e.firstChild)e.removeChild(e.firstChild)}
39636
- 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"}
39637
- function hhmm(ts){var d=new Date(ts);return d.toLocaleTimeString([], {hour:"2-digit",minute:"2-digit"})}
39638
- function recent(ts){return Date.now()-Date.parse(ts)<10*60*1000}
39639
- function zoomable(img){img.addEventListener("click",function(){var lb=$("lb");lb.firstChild.src=img.src;lb.style.display="flex"})}
39640
- function img(src,cls){var i=el("img",cls);i.src=src;i.alt="";i.loading="lazy";zoomable(i);return i}
39641
- function chip(txt,kind){return el("span","chip"+(kind?" "+kind:""),txt)}
39642
- function render(s){
39643
- 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;
39644
- if(s.app.icon){$("icon").src=s.app.icon}else{$("icon").removeAttribute("src")}
39645
- var steps=$("steps");clear(steps);var n=s.current?s.current.stepNumber:0;
39646
- 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)});
39647
- var last=s.journal[0];
39648
- $("nowmsg").textContent=last?last.message:"Waiting for the first postcard.";
39649
- $("nowmeta").textContent=last?("Now \xB7 "+(last.step?("step "+last.stepNumber+" of 8, "+last.step+" \xB7 "):"")+ago(last.ts)):"Now";
39650
- var focus=$("focus");var film=$("film");clear(film);
39651
- /* The live frame is KEPT across renders while its URL and its run are unchanged (a restarted run on
39652
- the same port is a different server, and the old frame would never see its builds). A new iframe is a full page
39653
- load of the preview: every postcard, rebuild and the once-a-minute re-render used to throw away
39654
- the in-place repaint, the preview's scroll and wherever the owner had navigated inside it. */
39655
- 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;
39656
- if(kept){while(focus.lastChild&&focus.lastChild!==kept.parentNode)focus.removeChild(focus.lastChild)}else clear(focus);
39657
- 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))}
39658
- 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"))}
39659
- else{$("focuslbl").textContent="Latest";focus.appendChild(el("div","empty","The first render appears here."))}
39660
- if(s.focus&&s.focus.frames.length>1)s.focus.frames.forEach(function(fm){film.appendChild(img(fm.img))});
39661
- var chips=$("chips");clear(chips);var h=s.health;
39662
- if(h.validate)chips.appendChild(chip(h.validate.ok?"Validate passing":"Validate: "+h.validate.errors+" errors",h.validate.ok?"ok":"bad"));
39663
- 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)}
39664
- h.ux.forEach(function(u){chips.appendChild(chip("UX drive "+u.platform+": "+u.status,u.status==="passed"?"ok":u.status==="failed"?"bad":"warn"))});
39665
- 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"));
39666
- if(!chips.firstChild)chips.appendChild(chip("Checks appear here as validate, test and push run"));
39667
- var wall=$("wall");clear(wall);
39668
- if(!s.screens.length)wall.appendChild(el("div","empty","Screens appear here as they render."));
39669
- 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));
39670
- 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)});
39671
- var brand=$("brand");clear(brand);$("brandcard").style.display=(s.brand.site||s.app.palette.length)?"":"none";
39672
- if(s.brand.site){var si=img(s.brand.site,"site");brand.appendChild(si)}
39673
- 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)}
39674
- 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)}
39675
- 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))});
39676
- var log=$("log");clear(log);if(!s.journal.length)log.appendChild(el("li","empty","Postcards the agent journals appear here."));
39677
- 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));
39678
- 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)});
39679
- }
39680
- /* Motion, sound and notices answer a CHANGE, never a redraw: effects() compares what is on the page
39681
- now with what it showed last, so opening the board, the once-a-minute redraw and the two-second
39682
- poll of an unchanged state do nothing, and each effect names a real event. */
39683
- var seen=null,actx=null,sound=false,notices=false;try{sound=localStorage.getItem("himiStudioSound")==="1";notices=localStorage.getItem("himiStudioNotify")==="1"}catch(e){}
39684
- var raf=window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(f){return setTimeout(function(){f(Date.now())},16)};
39685
- function now(){return window.performance&&performance.now?performance.now():Date.now()}
39686
- function reduced(){try{return matchMedia("(prefers-reduced-motion: reduce)").matches}catch(e){return false}}
39687
- 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){}}
39688
- 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")}};
39689
- /* Notices are the BOARD's opt-in, not just the browser's: a permission granted to this origin once
39690
- (an earlier board, another run) must not switch them on, and the toggle must switch them off. */
39691
- function notify(title,body){try{if(notices&&"Notification" in window&&Notification.permission==="granted"&&document.hidden)new Notification(title,{body:body})}catch(e){}}
39692
- function accentNow(){return (getComputedStyle(document.documentElement).getPropertyValue("--accent")||"").trim()||"#3b6fd8"}
39693
- /* The preview lays out at a real phone's viewport (390 x 844 CSS px) and is scaled into the frame.
39694
- Sized to the frame itself it was ~280px wide: narrower than any phone, so screens overflowed
39695
- sideways and scrolled horizontally. */
39696
- 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)))}
39697
- window.addEventListener("resize",fit);
39698
- 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){}}
39699
- 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)})()}
39700
- 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)})()}
39701
- 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}}
39702
- 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}
39703
- function effects(s){var is=snap(s),was=seen;seen=is;if(is.accent)document.documentElement.style.setProperty("--accent",is.accent);
39704
- /* The phone (kept across renders) and the Now line outlive a render: clear what they played last. */
39705
- var ph=$("focus").querySelector(".phone"),nm=$("nowmsg");if(ph)ph.classList.remove("pulse","bad");nm.classList.remove("slidein");if(!was)return;
39706
- 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();
39707
- 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()}
39708
- 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])}
39709
- /* A rebuild is a later build of the SAME run: a run appearing, or restarting, records its first
39710
- build too, and nothing was rebuilt then. */
39711
- 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||"")}}
39712
- if(was.gaps!=null&&is.gaps!=null&&is.gaps!==was.gaps)countTo($("gapn"),was.gaps,is.gaps);
39713
- 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))}
39714
- 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")}
39715
- }
39716
- 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}
39717
- 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))}
39718
- function setNotices(v){notices=v;try{localStorage.setItem("himiStudioNotify",v?"1":"0")}catch(e){}paintToggles()}
39719
- $("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()});
39720
- /* The click acts on what the button SAYS (the effective state), so a remembered opt-in whose browser
39721
- permission was since reset reads "off" and one click asks for permission, not two. */
39722
- $("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){}});
39723
- document.addEventListener("click",function(){try{if(actx&&actx.state==="suspended")actx.resume()}catch(e){}},{once:true});
39724
- paintToggles();
39725
- window.__himiStudio=function(s){lastSeen=Date.now();if(!cur||cur.v!==s.v){cur=s;render(s);effects(s)}else{cur.generatedAt=s.generatedAt}};
39726
- $("lb").addEventListener("click",function(){this.style.display="none"});
39727
- window.__himiStudio(window.__himiInitial);
39728
- 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);
39729
- var ticks=0;
39730
- 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;
39731
- $("clock").textContent=(hh?hh+":":"")+(mm<10&&hh?"0":"")+mm+":"+(ss<10?"0":"")+ss;
39732
- $("updated").textContent="updated "+ago(cur.generatedAt);$("live").className="live"+(Date.now()-lastSeen>10000?" stale":"");
39733
- var a=activityAt(cur),q=a?Date.now()-a:0,done=cur.current&&cur.current.stepNumber===8;
39734
- $("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);
39735
- })();
39736
- </script>
39737
- </body>
39738
- </html>
39739
- `;
39740
- }
39741
- function escapeHtml(s) {
39742
- return s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
39743
- }
39744
- 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;
39745
40139
  var init_studio = __esm({
39746
40140
  "src/studio.ts"() {
40141
+ init_studio_page();
40142
+ init_studio_timeline();
39747
40143
  init_progress();
39748
- STUDIO_DIR = join36(".himi", "studio");
39749
- STUDIO_PAGE = join36(STUDIO_DIR, "index.html");
39750
- STATE_FILE = join36(STUDIO_DIR, "state.js");
39751
- MEMORY_FILE = join36(STUDIO_DIR, "memory.json");
39752
- VALIDATE_RECORD = join36(".himi", "validate.json");
39753
- PUSH_RECORD = join36(".himi", "last-push.json");
39754
- EDGES_RECORD = join36(".himi", "test-edges.json");
39755
- 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");
39756
40153
  MAX_TIMELINE = 500;
39757
40154
  MAX_JOURNAL = 80;
39758
40155
  MAX_NAV_EDGES = 40;
39759
40156
  readJson2 = (file) => {
39760
40157
  try {
39761
- return JSON.parse(readFileSync42(file, "utf8"));
40158
+ return JSON.parse(readFileSync43(file, "utf8"));
39762
40159
  } catch {
39763
40160
  return null;
39764
40161
  }
@@ -39768,7 +40165,6 @@ var init_studio = __esm({
39768
40165
  text = (v) => typeof v === "string" && v ? v : null;
39769
40166
  historyUrl = (fileName) => `../history/${fileName}`;
39770
40167
  runTime = (runId) => runId.replace(/-[0-9a-f]+$/i, "");
39771
- scriptJson = (value) => JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
39772
40168
  }
39773
40169
  });
39774
40170
 
@@ -40247,11 +40643,11 @@ __export(captcha_exports, {
40247
40643
  captchaContentInput: () => captchaContentInput,
40248
40644
  runCaptcha: () => runCaptcha
40249
40645
  });
40250
- import { readFileSync as readFileSync43 } from "node:fs";
40646
+ import { readFileSync as readFileSync44 } from "node:fs";
40251
40647
  import { resolve as resolve34 } from "node:path";
40252
40648
  function readObject(path2) {
40253
40649
  try {
40254
- return JSON.parse(readFileSync43(path2, "utf8"));
40650
+ return JSON.parse(readFileSync44(path2, "utf8"));
40255
40651
  } catch {
40256
40652
  return {};
40257
40653
  }
@@ -40395,11 +40791,11 @@ var init_push_setup_apple_credentials = __esm({
40395
40791
  });
40396
40792
 
40397
40793
  // src/push-onboarding.ts
40398
- import { existsSync as existsSync40, mkdirSync as mkdirSync18, readFileSync as readFileSync44, statSync as statSync9, writeFileSync as writeFileSync14, chmodSync as chmodSync2 } from "node:fs";
40399
- 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";
40400
40796
  function readJson3(path2) {
40401
40797
  try {
40402
- const parsed = JSON.parse(readFileSync44(path2, "utf8"));
40798
+ const parsed = JSON.parse(readFileSync45(path2, "utf8"));
40403
40799
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
40404
40800
  } catch {
40405
40801
  return {};
@@ -40407,9 +40803,9 @@ function readJson3(path2) {
40407
40803
  }
40408
40804
  function pushInputsFromContent(dir) {
40409
40805
  const root = resolve35(dir);
40410
- if (!existsSync40(join37(root, "himalaya.content.json"))) return { initialized: false };
40411
- const content = readJson3(join37(root, "himalaya.content.json"));
40412
- 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"));
40413
40809
  const native = provisioning.native ?? {};
40414
40810
  return {
40415
40811
  initialized: true,
@@ -40484,7 +40880,7 @@ function readKeyFile(path2) {
40484
40880
  }
40485
40881
  let body;
40486
40882
  try {
40487
- body = readFileSync44(path2, "utf8");
40883
+ body = readFileSync45(path2, "utf8");
40488
40884
  } catch {
40489
40885
  return { ok: false, code: "key_file_unreadable", reason: "cannot read the selected key file; check its path and file permissions" };
40490
40886
  }
@@ -40538,13 +40934,13 @@ function buildPushSetupBody(i, keys) {
40538
40934
  };
40539
40935
  }
40540
40936
  function readPushOnboarding(dir) {
40541
- const path2 = join37(resolve35(dir), PUSH_ONBOARDING_PATH);
40542
- if (!existsSync40(path2)) return { version: 1 };
40937
+ const path2 = join38(resolve35(dir), PUSH_ONBOARDING_PATH);
40938
+ if (!existsSync41(path2)) return { version: 1 };
40543
40939
  const parsed = readJson3(path2);
40544
40940
  return { version: 1, ...parsed };
40545
40941
  }
40546
40942
  function writeFile(dir, file) {
40547
- const path2 = join37(resolve35(dir), PUSH_ONBOARDING_PATH);
40943
+ const path2 = join38(resolve35(dir), PUSH_ONBOARDING_PATH);
40548
40944
  mkdirSync18(dirname22(path2), { recursive: true, mode: 448 });
40549
40945
  writeFileSync14(path2, JSON.stringify(file, null, 2) + "\n", { mode: 384 });
40550
40946
  try {
@@ -40573,8 +40969,8 @@ function recordPushDeferral(dir, d) {
40573
40969
  return push;
40574
40970
  }
40575
40971
  function clearPushDeferral(dir) {
40576
- const path2 = join37(resolve35(dir), PUSH_ONBOARDING_PATH);
40577
- if (!existsSync40(path2)) return;
40972
+ const path2 = join38(resolve35(dir), PUSH_ONBOARDING_PATH);
40973
+ if (!existsSync41(path2)) return;
40578
40974
  const { push: _dropped, ...rest } = readPushOnboarding(dir);
40579
40975
  writeFile(dir, { ...rest, version: 1 });
40580
40976
  }
@@ -41307,8 +41703,8 @@ __export(template_trim_exports, {
41307
41703
  routeMatcher: () => routeMatcher,
41308
41704
  runTemplateTrim: () => runTemplateTrim
41309
41705
  });
41310
- 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";
41311
- 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";
41312
41708
  function routeMatcher(pattern) {
41313
41709
  const detail = /^\^([A-Za-z0-9_]+)\(\?:\[_\/\]\(\.\+\)\)\?\$$/.exec(pattern);
41314
41710
  if (detail) {
@@ -42002,20 +42398,20 @@ function homeModulesPass(app, kept, edited) {
42002
42398
  return { text: patched.src, dropped: patched.droppedModules };
42003
42399
  }
42004
42400
  function listWorkers(dir) {
42005
- const root = join38(dir, "tier5-src");
42006
- if (!existsSync41(root)) return { dirs: [], files: {} };
42007
- 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();
42008
42404
  const files = {};
42009
42405
  const walk2 = (abs, rel) => {
42010
42406
  for (const name of readdirSync21(abs)) {
42011
42407
  if (name === "node_modules") continue;
42012
- const p = join38(abs, name);
42408
+ const p = join39(abs, name);
42013
42409
  const r = `${rel}/${name}`;
42014
42410
  if (statSync10(p).isDirectory()) walk2(p, r);
42015
- 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");
42016
42412
  }
42017
42413
  };
42018
- 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}`);
42019
42415
  return { dirs, files };
42020
42416
  }
42021
42417
  function csv(v) {
@@ -42038,16 +42434,16 @@ async function runTemplateTrim(req, deps) {
42038
42434
  links = req.links;
42039
42435
  }
42040
42436
  const dir = req.dir;
42041
- const devPath = join38(dir, "dev/index.ts");
42042
- const configPath = join38(dir, "config.ts");
42043
- 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)) {
42044
42440
  return refuse3(new TrimRefusal(`${dir} has no dev/index.ts + config.ts \u2014 point --content at an app scaffolded with \`himi init --template\``));
42045
42441
  }
42046
42442
  if (isTemplateSourceTree(dir)) {
42047
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`));
42048
42444
  }
42049
- const dev = readFileSync45(devPath, "utf8");
42050
- const config = readFileSync45(configPath, "utf8");
42445
+ const dev = readFileSync46(devPath, "utf8");
42446
+ const config = readFileSync46(configPath, "utf8");
42051
42447
  const workers = listWorkers(dir);
42052
42448
  let plan;
42053
42449
  try {
@@ -42057,22 +42453,22 @@ async function runTemplateTrim(req, deps) {
42057
42453
  throw e;
42058
42454
  }
42059
42455
  if (req.dryRun) return { code: 0, result: { ok: true, dryRun: true, ...plan.report, validate: null } };
42060
- const hadHimi = existsSync41(join38(dir, ".himi"));
42061
- const backupRel = join38(".himi", "trim-backup", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
42062
- 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);
42063
42459
  const originals = [[devPath, "dev/index.ts"], [configPath, "config.ts"]];
42064
42460
  const moved = [];
42065
42461
  const restore = () => {
42066
42462
  for (const [path2, rel] of originals) {
42067
- const original = readFileSync45(join38(backup, rel));
42068
- 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);
42069
42465
  }
42070
- 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));
42071
42467
  };
42072
42468
  const dropBackup = () => {
42073
42469
  rmSync8(backup, { recursive: true, force: true });
42074
- for (const d of [dirname23(backup), join38(dir, ".himi")]) {
42075
- 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;
42076
42472
  try {
42077
42473
  if (!readdirSync21(d).length) rmSync8(d, { recursive: true });
42078
42474
  } catch {
@@ -42098,9 +42494,9 @@ async function runTemplateTrim(req, deps) {
42098
42494
  return { code: 1, result: { ok: false, error: `${error} (${message}); every file was restored`, rolledBack: true, ...base(validate4) } };
42099
42495
  };
42100
42496
  try {
42101
- mkdirSync19(join38(backup, "dev"), { recursive: true });
42102
- for (const [path2, rel] of originals) cpSync(path2, join38(backup, rel));
42103
- 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 });
42104
42500
  } catch (e) {
42105
42501
  dropBackup();
42106
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() } };
@@ -42108,7 +42504,7 @@ async function runTemplateTrim(req, deps) {
42108
42504
  try {
42109
42505
  const moveDir = deps.moveDir ?? renameSync4;
42110
42506
  for (const name of plan.removedWorkers) {
42111
- moveDir(join38(dir, "tier5-src", name), join38(backup, "tier5-src", name));
42507
+ moveDir(join39(dir, "tier5-src", name), join39(backup, "tier5-src", name));
42112
42508
  moved.push(name);
42113
42509
  }
42114
42510
  if (plan.dev !== dev) writeFileSync15(devPath, plan.dev);
@@ -42119,7 +42515,7 @@ async function runTemplateTrim(req, deps) {
42119
42515
  let failure;
42120
42516
  let validate3;
42121
42517
  try {
42122
- const written = parseApp(readFileSync45(devPath, "utf8"), readFileSync45(configPath, "utf8"));
42518
+ const written = parseApp(readFileSync46(devPath, "utf8"), readFileSync46(configPath, "utf8"));
42123
42519
  const leftovers = leftoverNavRefs(written.nav.value, plan.removedIds);
42124
42520
  if (leftovers.length) failure = { error: "navigation still names screens this trim removed", leftovers };
42125
42521
  else {
@@ -42813,8 +43209,8 @@ __export(pack_exports, {
42813
43209
  });
42814
43210
  import { createHash as createHash21 } from "node:crypto";
42815
43211
  import { deflateRawSync, inflateRawSync } from "node:zlib";
42816
- 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";
42817
- 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";
42818
43214
  function writeZip(entries) {
42819
43215
  const sorted2 = entries.slice().sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
42820
43216
  const total = sorted2.reduce((n, e) => n + e.bytes.length, 0);
@@ -42908,13 +43304,13 @@ function inflateEntry(buf, entry) {
42908
43304
  return bytes2;
42909
43305
  }
42910
43306
  function readIf(path2) {
42911
- return existsSync42(path2) && statSync11(path2).isFile() ? readFileSync46(path2) : null;
43307
+ return existsSync43(path2) && statSync11(path2).isFile() ? readFileSync47(path2) : null;
42912
43308
  }
42913
43309
  function listDir(dir) {
42914
- 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()) : [];
42915
43311
  }
42916
43312
  function gatherReleaseEntries(appDir2) {
42917
- const manifestPath = join39(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
43313
+ const manifestPath = join40(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
42918
43314
  const manifestBytes = readIf(manifestPath);
42919
43315
  if (!manifestBytes) throw new Error(`no ${HIMI_PACKAGE_MANIFEST_PATH} in ${appDir2} \u2014 build a release there first`);
42920
43316
  const manifest = JSON.parse(manifestBytes.toString("utf8"));
@@ -42938,7 +43334,7 @@ function gatherReleaseEntries(appDir2) {
42938
43334
  }
42939
43335
  const missing = [];
42940
43336
  for (const rel of wanted) {
42941
- const bytes2 = readIf(join39(appDir2, rel));
43337
+ const bytes2 = readIf(join40(appDir2, rel));
42942
43338
  if (bytes2) entries.push({ path: rel, bytes: bytes2 });
42943
43339
  else missing.push(rel);
42944
43340
  }
@@ -42949,12 +43345,12 @@ function gatherReleaseEntries(appDir2) {
42949
43345
  );
42950
43346
  }
42951
43347
  for (const name of SINGLETONS) {
42952
- const bytes2 = readIf(join39(appDir2, name));
43348
+ const bytes2 = readIf(join40(appDir2, name));
42953
43349
  if (bytes2) entries.push({ path: name, bytes: bytes2 });
42954
43350
  }
42955
43351
  const skipped = [];
42956
43352
  for (const sub of ["screens", "bundles", "assets", "widgets"]) {
42957
- for (const f of listDir(join39(appDir2, sub))) {
43353
+ for (const f of listDir(join40(appDir2, sub))) {
42958
43354
  if (!wanted.has(`${sub}/${f}`)) skipped.push(`${sub}/${f}`);
42959
43355
  }
42960
43356
  }
@@ -42987,18 +43383,18 @@ function buildEntries(appDir2, app, opts) {
42987
43383
  return { all, header, manifest, skipped };
42988
43384
  }
42989
43385
  function packRelease(releaseRoot, app, out, opts = {}) {
42990
- const appDir2 = join39(resolve37(releaseRoot), app);
43386
+ const appDir2 = join40(resolve37(releaseRoot), app);
42991
43387
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
42992
43388
  const buf = writeZip(all);
42993
- const isDir2 = existsSync42(out) && statSync11(out).isDirectory();
42994
- 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);
42995
43391
  mkdirSync20(dirname24(outFile), { recursive: true });
42996
43392
  writeFileSync16(outFile, buf);
42997
43393
  return { app, releaseId: header.releaseId, header, out: outFile, bytes: buf.length, entryCount: all.length, skipped };
42998
43394
  }
42999
43395
  function resolvePackageDirDest(outDir, app, releaseId) {
43000
43396
  const packagedApp = (dir) => {
43001
- const bytes2 = readIf(join39(dir, HIMI_PACKAGE_HEADER_PATH));
43397
+ const bytes2 = readIf(join40(dir, HIMI_PACKAGE_HEADER_PATH));
43002
43398
  if (!bytes2) return void 0;
43003
43399
  try {
43004
43400
  return JSON.parse(bytes2.toString("utf8")).app;
@@ -43007,15 +43403,15 @@ function resolvePackageDirDest(outDir, app, releaseId) {
43007
43403
  }
43008
43404
  };
43009
43405
  const replaceable = (dir) => {
43010
- if (!existsSync42(dir)) return true;
43406
+ if (!existsSync43(dir)) return true;
43011
43407
  if (!statSync11(dir).isDirectory()) return false;
43012
43408
  return readdirSync22(dir).length === 0 || packagedApp(dir) === app;
43013
43409
  };
43014
- if (existsSync42(outDir) && !statSync11(outDir).isDirectory()) {
43410
+ if (existsSync43(outDir) && !statSync11(outDir).isDirectory()) {
43015
43411
  throw new Error(`refusing to write a package tree over ${outDir} \u2014 it is a file, not a directory`);
43016
43412
  }
43017
43413
  if (replaceable(outDir)) return outDir;
43018
- const child = join39(outDir, packageDirName(app, releaseId));
43414
+ const child = join40(outDir, packageDirName(app, releaseId));
43019
43415
  if (!replaceable(child)) {
43020
43416
  throw new Error(
43021
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.`
@@ -43029,13 +43425,13 @@ function packReleaseToDir(releaseRoot, app, outDir, opts = {}) {
43029
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."
43030
43426
  );
43031
43427
  }
43032
- const appDir2 = join39(resolve37(releaseRoot), app);
43428
+ const appDir2 = join40(resolve37(releaseRoot), app);
43033
43429
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
43034
43430
  const dest = resolvePackageDirDest(resolve37(outDir), app, header.releaseId);
43035
43431
  rmSync9(dest, { recursive: true, force: true });
43036
43432
  let bytes2 = 0;
43037
43433
  for (const entry of all) {
43038
- const target = join39(dest, entry.path);
43434
+ const target = join40(dest, entry.path);
43039
43435
  mkdirSync20(dirname24(target), { recursive: true });
43040
43436
  writeFileSync16(target, entry.bytes);
43041
43437
  bytes2 += entry.bytes.length;
@@ -43058,21 +43454,21 @@ function openPackageBuffer(buf) {
43058
43454
  return openFromEntries(readZip(buf), { requireHeader: true });
43059
43455
  }
43060
43456
  function openPackageFile(file) {
43061
- return openPackageBuffer(readFileSync46(file));
43457
+ return openPackageBuffer(readFileSync47(file));
43062
43458
  }
43063
43459
  function openPackageDir(dir) {
43064
43460
  const root = resolve37(dir);
43065
43461
  const entries = /* @__PURE__ */ new Map();
43066
43462
  const walk2 = (rel) => {
43067
- const abs = rel ? join39(root, rel) : root;
43463
+ const abs = rel ? join40(root, rel) : root;
43068
43464
  for (const name of readdirSync22(abs)) {
43069
43465
  const childRel = rel ? `${rel}/${name}` : name;
43070
- const childAbs = join39(abs, name);
43466
+ const childAbs = join40(abs, name);
43071
43467
  if (statSync11(childAbs).isDirectory()) walk2(childRel);
43072
- else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync46(childAbs));
43468
+ else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync47(childAbs));
43073
43469
  }
43074
43470
  };
43075
- if (!existsSync42(root)) throw new Error(`no such package directory: ${root}`);
43471
+ if (!existsSync43(root)) throw new Error(`no such package directory: ${root}`);
43076
43472
  walk2("");
43077
43473
  const manifestBytes = entries.get(HIMI_PACKAGE_MANIFEST_PATH);
43078
43474
  if (manifestBytes) {
@@ -43114,10 +43510,10 @@ function unpackTo(buf, dest, appFallback) {
43114
43510
  if (!app) throw new Error("package declares no app id (no himi-package.json and no manifest.app)");
43115
43511
  if (!isSafeAppId(app)) throw new Error(`package declares an unsafe app id ${JSON.stringify(app)} \u2014 refusing to unpack it`);
43116
43512
  const root = resolve37(dest);
43117
- const appDir2 = join39(root, app);
43513
+ const appDir2 = join40(root, app);
43118
43514
  rmSync9(appDir2, { recursive: true, force: true });
43119
43515
  for (const [rel, bytes2] of entries) {
43120
- const target = join39(appDir2, rel);
43516
+ const target = join40(appDir2, rel);
43121
43517
  mkdirSync20(dirname24(target), { recursive: true });
43122
43518
  writeFileSync16(target, bytes2);
43123
43519
  }
@@ -43153,20 +43549,20 @@ __export(app_source_exports, {
43153
43549
  writeConflicts: () => writeConflicts,
43154
43550
  zipAppSource: () => zipAppSource
43155
43551
  });
43156
- 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";
43157
- 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";
43158
43554
  import { deflateRawSync as deflateRawSync2 } from "node:zlib";
43159
43555
  function walkSourceTree(dir, file, skip) {
43160
43556
  const walk2 = (relDir) => {
43161
- for (const name of readdirSync23(join40(dir, relDir))) {
43162
- const rel = relDir ? join40(relDir, name) : name;
43163
- 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));
43164
43560
  if (st.isSymbolicLink()) {
43165
43561
  skip(rel);
43166
43562
  continue;
43167
43563
  }
43168
43564
  if (st.isDirectory()) {
43169
- 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);
43170
43566
  else walk2(rel);
43171
43567
  continue;
43172
43568
  }
@@ -43184,7 +43580,7 @@ function collectAppSource(dir) {
43184
43580
  const skipped = [];
43185
43581
  walkSourceTree(
43186
43582
  dir,
43187
- (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync47(join40(dir, hostRel)) }),
43583
+ (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync48(join41(dir, hostRel)) }),
43188
43584
  (hostRel) => skipped.push(hostRel)
43189
43585
  );
43190
43586
  entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
@@ -43238,13 +43634,13 @@ function writeAppSource(entries, destDir) {
43238
43634
  const segments = entry.path.split("/");
43239
43635
  let dir = root;
43240
43636
  for (const segment of segments.slice(0, -1)) {
43241
- dir = join40(dir, segment);
43637
+ dir = join41(dir, segment);
43242
43638
  const kind = pathKind(dir);
43243
43639
  if (kind?.isSymbolicLink()) unlinkSync3(dir);
43244
43640
  else if (kind) continue;
43245
43641
  mkdirSync21(dir);
43246
43642
  }
43247
- const target = join40(root, ...segments);
43643
+ const target = join41(root, ...segments);
43248
43644
  if (pathKind(target)?.isSymbolicLink()) unlinkSync3(target);
43249
43645
  writeFileSync17(target, entry.bytes);
43250
43646
  written.push(entry.path);
@@ -43252,7 +43648,7 @@ function writeAppSource(entries, destDir) {
43252
43648
  return written;
43253
43649
  }
43254
43650
  function staleSourcePaths(entries, destDir) {
43255
- if (!existsSync43(destDir)) return [];
43651
+ if (!existsSync44(destDir)) return [];
43256
43652
  const archived = new Set(entries.map((e) => e.path));
43257
43653
  const stale = [];
43258
43654
  walkSourceTree(destDir, (archivePath) => {
@@ -43279,7 +43675,7 @@ function writeConflicts(entries, destDir) {
43279
43675
  let dir = root;
43280
43676
  let stop = false;
43281
43677
  for (const [i, segment] of segments.slice(0, -1).entries()) {
43282
- dir = join40(dir, segment);
43678
+ dir = join41(dir, segment);
43283
43679
  const kind2 = pathKind(dir);
43284
43680
  if (!kind2 || kind2.isSymbolicLink()) {
43285
43681
  stop = true;
@@ -43291,7 +43687,7 @@ function writeConflicts(entries, destDir) {
43291
43687
  break;
43292
43688
  }
43293
43689
  if (stop) continue;
43294
- const kind = pathKind(join40(root, ...segments));
43690
+ const kind = pathKind(join41(root, ...segments));
43295
43691
  if (kind && !kind.isSymbolicLink() && kind.isDirectory()) {
43296
43692
  say(entry.path, "is a directory here, but the release has a file at that path");
43297
43693
  }
@@ -43301,22 +43697,22 @@ function writeConflicts(entries, destDir) {
43301
43697
  function removeStaleSource(entries, destDir) {
43302
43698
  const root = resolve38(destDir);
43303
43699
  const removed = staleSourcePaths(entries, root);
43304
- 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 });
43305
43701
  const needed = /* @__PURE__ */ new Set();
43306
43702
  for (const entry of entries) {
43307
43703
  if (isDeniedSourcePath(entry.path)) continue;
43308
43704
  const segments = entry.path.split("/");
43309
43705
  for (let i = 1; i < segments.length; i++) needed.add(segments.slice(0, i).join("/"));
43310
43706
  }
43311
- if (existsSync43(root)) pruneEmptyDirs(root, "", needed, removed);
43707
+ if (existsSync44(root)) pruneEmptyDirs(root, "", needed, removed);
43312
43708
  return removed.sort();
43313
43709
  }
43314
43710
  function pruneEmptyDirs(root, rel, needed, removed) {
43315
43711
  let empty = true;
43316
- for (const name of readdirSync23(rel ? join40(root, rel) : root)) {
43317
- 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;
43318
43714
  const posix3 = childRel.split(sep10).join("/");
43319
- const kind = lstatSync6(join40(root, childRel));
43715
+ const kind = lstatSync6(join41(root, childRel));
43320
43716
  if (kind.isSymbolicLink() || !kind.isDirectory() || isDeniedSourceDir(name, rel)) {
43321
43717
  empty = false;
43322
43718
  continue;
@@ -43326,7 +43722,7 @@ function pruneEmptyDirs(root, rel, needed, removed) {
43326
43722
  empty = false;
43327
43723
  continue;
43328
43724
  }
43329
- rmdirSync(join40(root, childRel));
43725
+ rmdirSync(join41(root, childRel));
43330
43726
  removed.push(`${posix3}/`);
43331
43727
  }
43332
43728
  return empty;
@@ -43381,13 +43777,13 @@ __export(eject_exports, {
43381
43777
  EJECTABLE: () => EJECTABLE,
43382
43778
  ejectModule: () => ejectModule
43383
43779
  });
43384
- import { existsSync as existsSync44, mkdirSync as mkdirSync22, readFileSync as readFileSync48, writeFileSync as writeFileSync18 } from "node:fs";
43385
- 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";
43386
43782
  function locate(repoRootOrCwd, mod) {
43387
43783
  const inRepo = resolve39(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
43388
- if (existsSync44(inRepo)) return { path: inRepo, form: "source" };
43784
+ if (existsSync45(inRepo)) return { path: inRepo, form: "source" };
43389
43785
  const dist = resolve39(repoRootOrCwd, `node_modules/@wix/himalaya/dist/${mod}.mjs`);
43390
- if (existsSync44(dist)) return { path: dist, form: "bundle" };
43786
+ if (existsSync45(dist)) return { path: dist, form: "bundle" };
43391
43787
  return null;
43392
43788
  }
43393
43789
  function specifiersFor(mod) {
@@ -43411,7 +43807,7 @@ function ejectModule(opts) {
43411
43807
  };
43412
43808
  }
43413
43809
  const ext = found.form === "source" ? "ts" : "mjs";
43414
- const target = join41(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
43810
+ const target = join42(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
43415
43811
  mkdirSync22(dirname25(target), { recursive: true });
43416
43812
  const banner = `// EJECTED from @wix/himalaya/${mod}. This app owns this file now.
43417
43813
  //
@@ -43425,15 +43821,15 @@ function ejectModule(opts) {
43425
43821
  // gate still applies. Ejecting buys layout and flow, never the floor.
43426
43822
 
43427
43823
  `;
43428
- const normalized = readFileSync48(found.path, "utf8").replace(
43824
+ const normalized = readFileSync49(found.path, "utf8").replace(
43429
43825
  /(["'])@himalaya\/([a-z0-9-]+(?:\/[a-z0-9-]+)?)\1/g,
43430
43826
  (_m, q, sub) => `${q}@wix/himalaya/${sub}${q}`
43431
43827
  );
43432
43828
  writeFileSync18(target, banner + normalized);
43433
43829
  const rewired = [];
43434
43830
  for (const f of files) {
43435
- if (!existsSync44(f)) continue;
43436
- const before = readFileSync48(f, "utf8");
43831
+ if (!existsSync45(f)) continue;
43832
+ const before = readFileSync49(f, "utf8");
43437
43833
  let after = before;
43438
43834
  let rel = relative7(dirname25(f), target).split(sep11).join("/").replace(/\.tsx?$/, ".js");
43439
43835
  if (!rel.startsWith(".")) rel = `./${rel}`;
@@ -43481,14 +43877,14 @@ __export(test_exports, {
43481
43877
  scopeDescriptors: () => scopeDescriptors,
43482
43878
  writeRecordedMocks: () => writeRecordedMocks
43483
43879
  });
43484
- import { existsSync as existsSync45, mkdirSync as mkdirSync23, readFileSync as readFileSync49, writeFileSync as writeFileSync19 } from "node:fs";
43485
- 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";
43486
43882
  function readTestConfig(dir) {
43487
43883
  const p = resolve40(dir, TEST_CONFIG_FILE);
43488
- if (!existsSync45(p)) return {};
43884
+ if (!existsSync46(p)) return {};
43489
43885
  let parsed;
43490
43886
  try {
43491
- parsed = JSON.parse(readFileSync49(p, "utf8"));
43887
+ parsed = JSON.parse(readFileSync50(p, "utf8"));
43492
43888
  } catch (e) {
43493
43889
  throw new Error(`${TEST_CONFIG_FILE} is not valid JSON \u2014 ${e.message}`);
43494
43890
  }
@@ -43531,7 +43927,7 @@ function readTestConfig(dir) {
43531
43927
  function readNetRules(path2) {
43532
43928
  let parsed;
43533
43929
  try {
43534
- parsed = JSON.parse(readFileSync49(path2, "utf8"));
43930
+ parsed = JSON.parse(readFileSync50(path2, "utf8"));
43535
43931
  } catch (e) {
43536
43932
  throw new Error(`--net ${path2} could not be read as JSON \u2014 ${e.message}`);
43537
43933
  }
@@ -43552,7 +43948,7 @@ function readNetRules(path2) {
43552
43948
  function defaultNetRules(dir) {
43553
43949
  for (const rel of ["dev/net-mocks.json", "tests/net-mocks.json"]) {
43554
43950
  const p = resolve40(dir, rel);
43555
- if (existsSync45(p)) {
43951
+ if (existsSync46(p)) {
43556
43952
  const rules = readNetRules(p);
43557
43953
  if (rules.length) return { rules, from: rel };
43558
43954
  }
@@ -43568,7 +43964,7 @@ function readCannedResponses() {
43568
43964
  ];
43569
43965
  for (const url of candidates) {
43570
43966
  try {
43571
- const parsed = JSON.parse(readFileSync49(url, "utf8"));
43967
+ const parsed = JSON.parse(readFileSync50(url, "utf8"));
43572
43968
  const byKey = {};
43573
43969
  for (const [k, v] of Object.entries(parsed)) {
43574
43970
  if (!k.startsWith("//")) byKey[k] = v;
@@ -43660,8 +44056,8 @@ function writeRecordedMocks(opts) {
43660
44056
  mkdir: (p) => mkdirSync23(p, { recursive: true }),
43661
44057
  write: (p, s) => writeFileSync19(p, s)
43662
44058
  };
43663
- fs.mkdir(join42(opts.dir, "dev"));
43664
- 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");
43665
44061
  return { ok: true, rules, to: "dev/net-mocks.json" };
43666
44062
  }
43667
44063
  function scopeDescriptors(descriptors, requestedIds, invalidScreens) {
@@ -43975,12 +44371,12 @@ async function runCrawl(opts) {
43975
44371
  };
43976
44372
  }
43977
44373
  function describeTransport(net, opts, ran) {
43978
- const layers = [];
43979
- if (net) layers.push(`mocked (${net.from})`);
44374
+ const layers2 = [];
44375
+ if (net) layers2.push(`mocked (${net.from})`);
43980
44376
  const cmsCount = Object.keys(opts.cmsSeed ?? {}).length;
43981
- if (cmsCount) layers.push(`cms (${cmsCount} seeded collection${cmsCount === 1 ? "" : "s"})`);
43982
- if (opts.canned?.count) layers.push(`canned (${opts.canned.count} frozen endpoints, ${opts.canned.from} \u2014 exercises code paths, not API-contract truth)`);
43983
- 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)";
43984
44380
  const hostile = ran.hostile ? " + hostile boot passes (empty-lists, all-500)" : "";
43985
44381
  const relations = ran.relations ? " + relations" : "";
43986
44382
  const deep = ran.deep ? ` + deep chains (depth ${opts.deep})` : "";
@@ -44118,14 +44514,14 @@ var mobile_ux_lint_exports = {};
44118
44514
  __export(mobile_ux_lint_exports, {
44119
44515
  mobileUxIssues: () => mobileUxIssues
44120
44516
  });
44121
- import { existsSync as existsSync46, readFileSync as readFileSync50 } from "node:fs";
44122
- 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";
44123
44519
  function mobileUxIssues(dir, strict) {
44124
- const file = join43(dir, "MOBILE-UX.md");
44125
- if (!existsSync46(file)) return [];
44520
+ const file = join44(dir, "MOBILE-UX.md");
44521
+ if (!existsSync47(file)) return [];
44126
44522
  let text5;
44127
44523
  try {
44128
- text5 = readFileSync50(file, "utf8");
44524
+ text5 = readFileSync51(file, "utf8");
44129
44525
  } catch {
44130
44526
  return [];
44131
44527
  }
@@ -45800,8 +46196,8 @@ __export(functions_exports, {
45800
46196
  resolveFunctionsTarget: () => resolveFunctionsTarget,
45801
46197
  runRemoteFunctionOperation: () => runRemoteFunctionOperation
45802
46198
  });
45803
- import { readFileSync as readFileSync51, existsSync as existsSync47 } from "node:fs";
45804
- 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";
45805
46201
  function resolveFunctionsTarget(input) {
45806
46202
  const explicitUrl = input.functionsUrl?.trim();
45807
46203
  if (explicitUrl) return { mode: "remote", baseUrl: explicitUrl };
@@ -45820,9 +46216,9 @@ async function loadFunctionsApp(dir) {
45820
46216
  }
45821
46217
  const sources = {};
45822
46218
  for (const definition of config.functions.functions) {
45823
- 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);
45824
46220
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
45825
- sources[definition.name] = readFileSync51(source, "utf8");
46221
+ sources[definition.name] = readFileSync52(source, "utf8");
45826
46222
  }
45827
46223
  const data = config.cms || config.data ? mergeDataResources(config) : void 0;
45828
46224
  const dataErrors = validateDataResources(data, loaded.name).filter((issue2) => issue2.severity === "error");
@@ -45974,14 +46370,14 @@ __export(studio_reel_exports, {
45974
46370
  utcDay: () => utcDay
45975
46371
  });
45976
46372
  import { createHash as createHash24 } from "node:crypto";
45977
- import { lstatSync as lstatSync7, readFileSync as readFileSync52 } from "node:fs";
45978
- 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";
45979
46375
  function loadMemory2(dir) {
45980
- const m = readJson4(join45(dir, ".himi", "studio", "memory.json"));
45981
- 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;
45982
46378
  const shots = [];
45983
46379
  for (const [id, s] of Object.entries(m.shots)) {
45984
- if (!isObj(s)) continue;
46380
+ if (!isObj2(s)) continue;
45985
46381
  const file = historyName(s.sha, s.ext);
45986
46382
  const firstSeen = str(s.firstSeen);
45987
46383
  if (!file || !firstSeen) continue;
@@ -45990,7 +46386,7 @@ function loadMemory2(dir) {
45990
46386
  }
45991
46387
  const timeline = [];
45992
46388
  for (const t of m.timeline) {
45993
- if (!isObj(t)) continue;
46389
+ if (!isObj2(t)) continue;
45994
46390
  const file = historyName(t.sha, t.ext);
45995
46391
  const ts2 = str(t.ts);
45996
46392
  const id = str(t.id);
@@ -46014,8 +46410,8 @@ function planReel(dir) {
46014
46410
  const memory = loadMemory2(dir);
46015
46411
  const { entries } = readProgress(dir);
46016
46412
  const summary = summarizeProgress(entries);
46017
- const manifest = readJson4(join45(dir, "himalaya.content.json"));
46018
- const tokens = readJson4(join45(dir, "tokens.json"));
46413
+ const manifest = readJson4(join46(dir, "himalaya.content.json"));
46414
+ const tokens = readJson4(join46(dir, "tokens.json"));
46019
46415
  const accentToken = tokens?.colors?.accentPrimary;
46020
46416
  const accentRaw = typeof accentToken === "string" ? accentToken : accentToken?.light;
46021
46417
  const accent = typeof accentRaw === "string" && ACCENT.test(accentRaw) ? accentRaw : DEFAULT_ACCENT;
@@ -46030,7 +46426,7 @@ function planReel(dir) {
46030
46426
  ts: e.ts,
46031
46427
  message: e.message,
46032
46428
  step: str(e.step),
46033
- 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))
46034
46430
  }));
46035
46431
  const times = [summary.startedAt, ...timeline.map((t) => t.ts), memory?.createdAt].filter((x) => !!x).sort();
46036
46432
  const startedAt = times[0] ?? null;
@@ -46057,10 +46453,10 @@ function inlineImages(dir, plan, budget = REEL_BUDGET_BYTES) {
46057
46453
  const verified = (f) => {
46058
46454
  if (!HISTORY_NAME.test(f)) return null;
46059
46455
  try {
46060
- const path2 = join45(dir, HISTORY_DIR, f);
46456
+ const path2 = join46(dir, HISTORY_DIR, f);
46061
46457
  const st = lstatSync7(path2);
46062
46458
  if (!st.isFile() || st.nlink !== 1) return null;
46063
- const bytes2 = readFileSync52(path2);
46459
+ const bytes2 = readFileSync53(path2);
46064
46460
  return createHash24("sha256").update(bytes2).digest("hex") === f.slice(0, 64) && imageExtension(bytes2) === f.slice(65) ? bytes2 : null;
46065
46461
  } catch {
46066
46462
  return null;
@@ -46180,7 +46576,7 @@ function buildReel(dir, budget = REEL_BUDGET_BYTES) {
46180
46576
  const { plan, images } = inlineImages(dir, planReel(dir), budget);
46181
46577
  return { html: renderReel(plan, images), plan };
46182
46578
  }
46183
- 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;
46184
46580
  var init_studio_reel = __esm({
46185
46581
  "src/studio-reel.ts"() {
46186
46582
  init_progress();
@@ -46194,12 +46590,12 @@ var init_studio_reel = __esm({
46194
46590
  mimeOf = (file) => MIME2[file.slice(65)] ?? "image/png";
46195
46591
  readJson4 = (file) => {
46196
46592
  try {
46197
- return JSON.parse(readFileSync52(file, "utf8"));
46593
+ return JSON.parse(readFileSync53(file, "utf8"));
46198
46594
  } catch {
46199
46595
  return null;
46200
46596
  }
46201
46597
  };
46202
- isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
46598
+ isObj2 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
46203
46599
  str = (v) => typeof v === "string" && v ? v : null;
46204
46600
  historyName = (sha, ext) => typeof sha === "string" && typeof ext === "string" && HISTORY_NAME.test(`${sha}.${ext}`) ? `${sha}.${ext}` : null;
46205
46601
  cited = (p) => [
@@ -46225,9 +46621,9 @@ __export(native_decode_exports, {
46225
46621
  runNativeDecode: () => runNativeDecode
46226
46622
  });
46227
46623
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
46228
- 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";
46229
46625
  import { tmpdir as tmpdir3 } from "node:os";
46230
- import { join as join46 } from "node:path";
46626
+ import { join as join47 } from "node:path";
46231
46627
  import { fileURLToPath as fileURLToPath20 } from "node:url";
46232
46628
  function nativeDecodePackageDir() {
46233
46629
  return fileURLToPath20(new URL("../../../core/runtime/ios/HimalayaCore/", import.meta.url));
@@ -46237,8 +46633,8 @@ function nativeDecodeAndroidHarnessDir() {
46237
46633
  }
46238
46634
  function nativeOracleShipped() {
46239
46635
  return {
46240
- ios: existsSync48(join46(nativeDecodePackageDir(), "Package.swift")),
46241
- android: existsSync48(join46(nativeDecodeAndroidHarnessDir(), "gradlew"))
46636
+ ios: existsSync49(join47(nativeDecodePackageDir(), "Package.swift")),
46637
+ android: existsSync49(join47(nativeDecodeAndroidHarnessDir(), "gradlew"))
46242
46638
  };
46243
46639
  }
46244
46640
  function detectNativeDecode(options = {}) {
@@ -46249,7 +46645,7 @@ function detectNativeDecode(options = {}) {
46249
46645
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
46250
46646
  }
46251
46647
  if (!swiftAvailable) return { platform: "ios", available: false, reason: "swift is not on PATH" };
46252
- const packageAvailable = options.packageAvailable ?? existsSync48(join46(packageDir, "Package.swift"));
46648
+ const packageAvailable = options.packageAvailable ?? existsSync49(join47(packageDir, "Package.swift"));
46253
46649
  if (!packageAvailable) {
46254
46650
  return {
46255
46651
  platform: "ios",
@@ -46261,7 +46657,7 @@ function detectNativeDecode(options = {}) {
46261
46657
  }
46262
46658
  function localPropertiesSdk(harnessDir) {
46263
46659
  try {
46264
- 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);
46265
46661
  return match?.[1]?.trim().replace(/\\([ :\\])/g, "$1") ?? null;
46266
46662
  } catch {
46267
46663
  return null;
@@ -46269,8 +46665,8 @@ function localPropertiesSdk(harnessDir) {
46269
46665
  }
46270
46666
  function detectAndroidNativeDecode(options = {}) {
46271
46667
  const harnessDir = options.harnessDir ?? nativeDecodeAndroidHarnessDir();
46272
- const gradlew = join46(harnessDir, "gradlew");
46273
- const gradlewAvailable = options.gradlewAvailable ?? existsSync48(gradlew);
46668
+ const gradlew = join47(harnessDir, "gradlew");
46669
+ const gradlewAvailable = options.gradlewAvailable ?? existsSync49(gradlew);
46274
46670
  if (!gradlewAvailable) {
46275
46671
  return {
46276
46672
  platform: "android",
@@ -46280,7 +46676,7 @@ function detectAndroidNativeDecode(options = {}) {
46280
46676
  }
46281
46677
  const androidHome = options.androidHome ?? process.env.ANDROID_HOME ?? "";
46282
46678
  const sdk = androidHome.trim() || (options.localPropertiesSdk !== void 0 ? options.localPropertiesSdk : localPropertiesSdk(harnessDir));
46283
- 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));
46284
46680
  if (!sdkAvailable) {
46285
46681
  return {
46286
46682
  platform: "android",
@@ -46352,14 +46748,14 @@ function parseNativeDecodeOutput(stdout, expectedScreenIds, platform = "ios") {
46352
46748
  return verdicts;
46353
46749
  }
46354
46750
  function runNativeDecode(options) {
46355
- const descriptorDir = mkdtempSync2(join46(tmpdir3(), "himi-native-decode-"));
46751
+ const descriptorDir = mkdtempSync2(join47(tmpdir3(), "himi-native-decode-"));
46356
46752
  const startedAt = Date.now();
46357
46753
  try {
46358
46754
  for (const descriptor of options.descriptors) {
46359
46755
  if (typeof descriptor.descriptorJson !== "string") {
46360
46756
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
46361
46757
  }
46362
- writeFileSync20(join46(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
46758
+ writeFileSync20(join47(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
46363
46759
  }
46364
46760
  options.progress?.("building cached Swift oracle");
46365
46761
  execFileSync5("swift", ["build", "--package-path", options.packageDir, "--product", "himi-decode-oracle"], {
@@ -46371,7 +46767,7 @@ function runNativeDecode(options) {
46371
46767
  encoding: "utf8"
46372
46768
  }).trim();
46373
46769
  options.progress?.(`running real Swift decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
46374
- const stdout = execFileSync5(join46(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
46770
+ const stdout = execFileSync5(join47(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
46375
46771
  stdio: ["ignore", "pipe", "pipe"],
46376
46772
  encoding: "utf8"
46377
46773
  });
@@ -46389,15 +46785,15 @@ function runNativeDecode(options) {
46389
46785
  }
46390
46786
  }
46391
46787
  function runAndroidNativeDecode(options) {
46392
- const descriptorDir = mkdtempSync2(join46(tmpdir3(), "himi-native-decode-android-"));
46393
- const outputPath = join46(descriptorDir, "verdicts.ndjson");
46788
+ const descriptorDir = mkdtempSync2(join47(tmpdir3(), "himi-native-decode-android-"));
46789
+ const outputPath = join47(descriptorDir, "verdicts.ndjson");
46394
46790
  const startedAt = Date.now();
46395
46791
  try {
46396
46792
  for (const descriptor of options.descriptors) {
46397
46793
  if (typeof descriptor.descriptorJson !== "string") {
46398
46794
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
46399
46795
  }
46400
- writeFileSync20(join46(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
46796
+ writeFileSync20(join47(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
46401
46797
  }
46402
46798
  options.progress?.(`running real Kotlin decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
46403
46799
  execFileSync5(options.gradlew, [
@@ -46412,7 +46808,7 @@ function runAndroidNativeDecode(options) {
46412
46808
  stdio: ["ignore", "ignore", "pipe"],
46413
46809
  encoding: "utf8"
46414
46810
  });
46415
- const stdout = readFileSync53(outputPath, "utf8");
46811
+ const stdout = readFileSync54(outputPath, "utf8");
46416
46812
  return {
46417
46813
  platform: "android",
46418
46814
  verdicts: parseNativeDecodeOutput(stdout, options.descriptors.map((descriptor) => descriptor.screenId), "android"),
@@ -46482,13 +46878,13 @@ __export(browser_authoring_exports, {
46482
46878
  });
46483
46879
  import { createServer as createServer3 } from "node:http";
46484
46880
  import { createHash as createHash25, randomBytes as randomBytes4 } from "node:crypto";
46485
- import { existsSync as existsSync49, readdirSync as readdirSync24, readFileSync as readFileSync54, realpathSync as realpathSync5, statSync as statSync12 } from "node:fs";
46486
- 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";
46487
46883
  function humanMs(ms) {
46488
46884
  return ms < 1e3 ? `${ms}ms` : `${Math.round(ms / 1e3)}s`;
46489
46885
  }
46490
46886
  function collectWorkerSources(tier5SrcDir, bundleName) {
46491
- const root = join47(tier5SrcDir, bundleName);
46887
+ const root = join48(tier5SrcDir, bundleName);
46492
46888
  const files = {};
46493
46889
  const visitedDirs = /* @__PURE__ */ new Set();
46494
46890
  const walk2 = (abs, rel) => {
@@ -46496,16 +46892,16 @@ function collectWorkerSources(tier5SrcDir, bundleName) {
46496
46892
  if (visitedDirs.has(real)) return;
46497
46893
  visitedDirs.add(real);
46498
46894
  for (const name of readdirSync24(abs).sort()) {
46499
- const childAbs = join47(abs, name);
46895
+ const childAbs = join48(abs, name);
46500
46896
  const childRel = posix.join(rel, name);
46501
46897
  if (statSync12(childAbs).isDirectory()) {
46502
46898
  walk2(childAbs, childRel);
46503
46899
  } else if (/\.(ts|tsx|js|mjs|json)$/.test(name)) {
46504
- files[childRel] = readFileSync54(childAbs, "utf8");
46900
+ files[childRel] = readFileSync55(childAbs, "utf8");
46505
46901
  }
46506
46902
  }
46507
46903
  };
46508
- if (!existsSync49(root)) return null;
46904
+ if (!existsSync50(root)) return null;
46509
46905
  walk2(root, `/tier5-src/${bundleName}`);
46510
46906
  const entry = [`/tier5-src/${bundleName}/index.ts`, `/tier5-src/${bundleName}/index.js`].find((p) => p in files);
46511
46907
  if (!entry) return null;
@@ -46522,20 +46918,20 @@ function collectRelativeSiblings(files, tier5SrcDir) {
46522
46918
  const spec = m[1];
46523
46919
  const virtual = posix.normalize(posix.join(dir, spec));
46524
46920
  if (!virtual.startsWith("/tier5-src/")) continue;
46525
- const onDisk = join47(tier5SrcDir, virtual.slice("/tier5-src/".length));
46921
+ const onDisk = join48(tier5SrcDir, virtual.slice("/tier5-src/".length));
46526
46922
  const candidates = [
46527
46923
  onDisk,
46528
46924
  `${onDisk}.ts`,
46529
46925
  `${onDisk}.js`,
46530
46926
  onDisk.replace(/\.js$/, ".ts"),
46531
- join47(onDisk, "index.ts"),
46532
- join47(onDisk, "index.js")
46927
+ join48(onDisk, "index.ts"),
46928
+ join48(onDisk, "index.js")
46533
46929
  ];
46534
46930
  for (const abs of candidates) {
46535
- if (!existsSync49(abs) || statSync12(abs).isDirectory()) continue;
46931
+ if (!existsSync50(abs) || statSync12(abs).isDirectory()) continue;
46536
46932
  const key2 = `/tier5-src/${relative8(tier5SrcDir, abs).split(sep12).join(posix.sep)}`;
46537
46933
  if (key2 in files) break;
46538
- files[key2] = readFileSync54(abs, "utf8");
46934
+ files[key2] = readFileSync55(abs, "utf8");
46539
46935
  queue.push(key2);
46540
46936
  break;
46541
46937
  }
@@ -46570,7 +46966,7 @@ function collectSdkModules(files, resolveSubpath) {
46570
46966
  function sdkSubpathReader(resolve48) {
46571
46967
  return (sub) => {
46572
46968
  try {
46573
- return readFileSync54(resolve48(`@wix/himalaya/${sub}`), "utf8");
46969
+ return readFileSync55(resolve48(`@wix/himalaya/${sub}`), "utf8");
46574
46970
  } catch {
46575
46971
  return null;
46576
46972
  }
@@ -46742,8 +47138,8 @@ __export(coverage_exports, {
46742
47138
  suppressedNativeKeys: () => suppressedNativeKeys,
46743
47139
  trendAndStore: () => trendAndStore
46744
47140
  });
46745
- import { existsSync as existsSync50, mkdirSync as mkdirSync24, readFileSync as readFileSync55, writeFileSync as writeFileSync21 } from "node:fs";
46746
- 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";
46747
47143
  function suppressedNativeKeys(oracle) {
46748
47144
  return [...oracle.ios ? [] : ["native-decode|ios"], ...oracle.android ? [] : ["native-decode|android"]];
46749
47145
  }
@@ -46923,11 +47319,11 @@ function assembleLedger(r, options = {}) {
46923
47319
  return out;
46924
47320
  }
46925
47321
  function trendAndStore(dir, entries, suppressed) {
46926
- const p = join48(dir, LEDGER_FILE);
47322
+ const p = join49(dir, LEDGER_FILE);
46927
47323
  let previous = null;
46928
47324
  try {
46929
- if (existsSync50(p)) {
46930
- const parsed = JSON.parse(readFileSync55(p, "utf8"));
47325
+ if (existsSync51(p)) {
47326
+ const parsed = JSON.parse(readFileSync56(p, "utf8"));
46931
47327
  if (Array.isArray(parsed.keys)) previous = parsed.keys.filter((k) => typeof k === "string");
46932
47328
  }
46933
47329
  } catch {
@@ -46966,11 +47362,11 @@ function formatLedger(entries, trend) {
46966
47362
  return out.join("\n");
46967
47363
  }
46968
47364
  function diffAndStoreObservations(dir, current) {
46969
- const p = join48(dir, OBS_FILE);
47365
+ const p = join49(dir, OBS_FILE);
46970
47366
  let previous = null;
46971
47367
  try {
46972
- if (existsSync50(p)) {
46973
- const parsed = JSON.parse(readFileSync55(p, "utf8"));
47368
+ if (existsSync51(p)) {
47369
+ const parsed = JSON.parse(readFileSync56(p, "utf8"));
46974
47370
  if (parsed.screens && typeof parsed.screens === "object" && !Array.isArray(parsed.screens) && Object.values(parsed.screens).every(
46975
47371
  (v) => Array.isArray(v) && v.every((l) => typeof l === "string")
46976
47372
  )) {
@@ -47019,8 +47415,8 @@ var LEDGER_FILE, OBS_FILE;
47019
47415
  var init_coverage = __esm({
47020
47416
  "src/coverage.ts"() {
47021
47417
  init_native_decode();
47022
- LEDGER_FILE = join48(".himi", "test-ledger.json");
47023
- OBS_FILE = join48(".himi", "test-observations.json");
47418
+ LEDGER_FILE = join49(".himi", "test-ledger.json");
47419
+ OBS_FILE = join49(".himi", "test-observations.json");
47024
47420
  }
47025
47421
  });
47026
47422
 
@@ -47031,11 +47427,11 @@ __export(design_kits_exports, {
47031
47427
  kitsDir: () => kitsDir,
47032
47428
  listKits: () => listKits2
47033
47429
  });
47034
- import { existsSync as existsSync51, readFileSync as readFileSync56, readdirSync as readdirSync25 } from "node:fs";
47035
- 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";
47036
47432
  import { fileURLToPath as fileURLToPath21 } from "node:url";
47037
47433
  function kitsDir() {
47038
- return KIT_DIR_CANDIDATES.find((p) => existsSync51(p));
47434
+ return KIT_DIR_CANDIDATES.find((p) => existsSync52(p));
47039
47435
  }
47040
47436
  function listKits2() {
47041
47437
  const dir = kitsDir();
@@ -47059,7 +47455,7 @@ In a published @wix/himalaya-cli this is a packaging bug \u2014 the kits should
47059
47455
  if (!kits.includes(name)) {
47060
47456
  return { ok: false, kit: name, kits, problems: [`no design kit named "${name}". Available: ${kits.join(", ")}`] };
47061
47457
  }
47062
- const raw = readFileSync56(join49(dir, `${name}.json`), "utf8");
47458
+ const raw = readFileSync57(join50(dir, `${name}.json`), "utf8");
47063
47459
  let parsed;
47064
47460
  try {
47065
47461
  parsed = JSON.parse(raw);
@@ -47083,9 +47479,9 @@ var init_design_kits = __esm({
47083
47479
  "src/design-kits.ts"() {
47084
47480
  KIT_DIR_CANDIDATES = [
47085
47481
  // in-repo: tools/himi-cli/src/design-kits.ts → <repo>/stdlib/design-kits/
47086
- join49(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
47482
+ join50(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
47087
47483
  // published CLI: <pkg>/dist/cli.mjs → <pkg>/stdlib/design-kits/
47088
- join49(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
47484
+ join50(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
47089
47485
  ];
47090
47486
  }
47091
47487
  });
@@ -47099,11 +47495,11 @@ __export(assets_exports, {
47099
47495
  removeAssetPayload: () => removeAssetPayload,
47100
47496
  verifyReport: () => verifyReport
47101
47497
  });
47102
- import { readFileSync as readFileSync57 } from "node:fs";
47498
+ import { readFileSync as readFileSync58 } from "node:fs";
47103
47499
  import { basename as basename13 } from "node:path";
47104
47500
  function declaredAssets(dir) {
47105
47501
  return collectAssets(dir).map((a) => {
47106
- const bytes2 = readFileSync57(a.path);
47502
+ const bytes2 = readFileSync58(a.path);
47107
47503
  return {
47108
47504
  name: a.name,
47109
47505
  sha256: assetSha256(bytes2),
@@ -47180,7 +47576,7 @@ function verifyReport(dir, app, manifest) {
47180
47576
  };
47181
47577
  }
47182
47578
  function addAssetPayload(manifest, filePath, opts) {
47183
- const bytes2 = readFileSync57(filePath);
47579
+ const bytes2 = readFileSync58(filePath);
47184
47580
  const name = opts.name ?? basename13(filePath);
47185
47581
  const sha2565 = assetSha256(bytes2);
47186
47582
  const record2 = {
@@ -48782,21 +49178,21 @@ var init_preview_assets_generated = __esm({
48782
49178
  "../../packages/serve/src/preview-assets.generated.ts"() {
48783
49179
  init_preview_assets_format();
48784
49180
  PREVIEW_ASSET_MANIFEST = [
48785
- { key: "assets/index-CxGyJNjV.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1041209, sha256: "4eb08b810b7f8e5ae57dc8eef60fe084213c20e0ba7a9bc79b55c89b50cd1291" },
48786
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1041217, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
48787
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1045827, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
48788
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1534411, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
48789
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1625335, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
48790
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1716319, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
48791
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1807299, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
48792
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1898171, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
48793
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1989151, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
48794
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2097787, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
48795
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2206871, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
48796
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2315919, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
48797
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2423619, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
48798
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2511727, length: 4947, sha256: "8f89772181b62b7bda8f1620af71cca03ee0aae2471127d0633e07488edbf290" },
48799
- { 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" }
48800
49196
  ];
48801
49197
  }
48802
49198
  });
@@ -48812,9 +49208,9 @@ __export(run_exports, {
48812
49208
  runPackage: () => runPackage,
48813
49209
  withoutProdServe: () => withoutProdServe
48814
49210
  });
48815
- 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";
48816
49212
  import { tmpdir as tmpdir4 } from "node:os";
48817
- import { dirname as dirname27, join as join50 } from "node:path";
49213
+ import { dirname as dirname27, join as join51 } from "node:path";
48818
49214
  import { fileURLToPath as fileURLToPath22 } from "node:url";
48819
49215
  function makePackageRunHandler(opts) {
48820
49216
  return makeRunHandler({
@@ -48833,9 +49229,9 @@ async function fetchBytes(url) {
48833
49229
  }
48834
49230
  async function resolvePackage(source, workDir) {
48835
49231
  const isUrl2 = /^https?:\/\//i.test(source);
48836
- if (!isUrl2 && existsSync52(source) && statSync13(source).isDirectory()) {
48837
- const appDirManifest = join50(source, "release-manifest.json");
48838
- if (existsSync52(appDirManifest)) {
49232
+ if (!isUrl2 && existsSync53(source) && statSync13(source).isDirectory()) {
49233
+ const appDirManifest = join51(source, "release-manifest.json");
49234
+ if (existsSync53(appDirManifest)) {
48839
49235
  const opened2 = openPackageDir(source);
48840
49236
  if (!opened2.validation.ok) {
48841
49237
  throw new Error(`package is not valid:
@@ -48844,9 +49240,9 @@ async function resolvePackage(source, workDir) {
48844
49240
  const app2 = opened2.header?.app ?? opened2.manifest?.app;
48845
49241
  if (!app2) throw new Error(`${source} has no app id (no himi-package.json and no manifest.app)`);
48846
49242
  if (!isSafeAppId(app2)) throw new Error(`${source} declares an unsafe app id ${JSON.stringify(app2)} \u2014 refusing to open it`);
48847
- const root2 = workDir ?? mkdtempSync3(join50(tmpdir4(), "himi-run-"));
48848
- const target = join50(root2, app2);
48849
- if (join50(source) !== target) {
49243
+ const root2 = workDir ?? mkdtempSync3(join51(tmpdir4(), "himi-run-"));
49244
+ const target = join51(root2, app2);
49245
+ if (join51(source) !== target) {
48850
49246
  mkdirSync25(root2, { recursive: true });
48851
49247
  rmSync12(target, { recursive: true, force: true });
48852
49248
  cpSync2(source, target, { recursive: true });
@@ -48863,8 +49259,8 @@ async function resolvePackage(source, workDir) {
48863
49259
  }
48864
49260
  throw new Error(`${source} is a directory but has no release-manifest.json \u2014 is it a package?`);
48865
49261
  }
48866
- const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync58(source);
48867
- 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-"));
48868
49264
  const { app, appDir: appDir2 } = unpackTo(bytes2, root);
48869
49265
  const opened = openPackageDir(appDir2);
48870
49266
  if (!opened.validation.ok) {
@@ -48887,14 +49283,14 @@ async function loadPreviewAssets() {
48887
49283
  const mod = await Promise.resolve().then(() => (init_preview_assets_generated(), preview_assets_generated_exports));
48888
49284
  const here = dirname27(fileURLToPath22(import.meta.url));
48889
49285
  const candidates = [
48890
- join50(here, "preview-assets.generated.bin"),
49286
+ join51(here, "preview-assets.generated.bin"),
48891
49287
  // published: copied next to the bundled CLI
48892
- join50(here, "../../../packages/serve/src/preview-assets.generated.bin")
49288
+ join51(here, "../../../packages/serve/src/preview-assets.generated.bin")
48893
49289
  // in-repo checkout
48894
49290
  ];
48895
49291
  for (const path2 of candidates) {
48896
- if (!existsSync52(path2)) continue;
48897
- const blob = readFileSync58(path2);
49292
+ if (!existsSync53(path2)) continue;
49293
+ const blob = readFileSync59(path2);
48898
49294
  return mod.assemblePreviewAssetsFromBlob(new Uint8Array(blob.buffer, blob.byteOffset, blob.byteLength));
48899
49295
  }
48900
49296
  return /* @__PURE__ */ new Map();
@@ -48989,9 +49385,9 @@ __export(run_watch_exports, {
48989
49385
  watchedContentDir: () => watchedContentDir,
48990
49386
  writeRunRecord: () => writeRunRecord
48991
49387
  });
48992
- 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";
48993
49389
  import { randomUUID as randomUUID3 } from "node:crypto";
48994
- 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";
48995
49391
  function diffReleases(prev, next) {
48996
49392
  const bundles = Object.keys(next.bundles).filter((b) => prev.bundles[b]?.sha256 !== next.bundles[b].sha256).sort();
48997
49393
  const changedBundles = new Set(bundles);
@@ -49030,8 +49426,8 @@ function watchContent(dir, onChange, opts = {}) {
49030
49426
  const watch2 = opts.watch ?? ((p, o, l) => fsWatch(p, o, (event, file) => l(event, file === null ? null : String(file))));
49031
49427
  let timer = null;
49032
49428
  const pending = /* @__PURE__ */ new Set();
49033
- const watcher = watch2(dir, { recursive: true }, (_event, file) => {
49034
- 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;
49035
49431
  pending.add(file);
49036
49432
  if (timer) clearTimeout(timer);
49037
49433
  timer = setTimeout(() => {
@@ -49044,7 +49440,7 @@ function watchContent(dir, onChange, opts = {}) {
49044
49440
  return {
49045
49441
  close() {
49046
49442
  if (timer) clearTimeout(timer);
49047
- watcher.close();
49443
+ watcher2.close();
49048
49444
  }
49049
49445
  };
49050
49446
  }
@@ -49052,7 +49448,7 @@ function watchedContentDir(source) {
49052
49448
  if (!source || /^https?:\/\//i.test(source)) return null;
49053
49449
  const dir = resolve42(source);
49054
49450
  try {
49055
- 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;
49056
49452
  } catch {
49057
49453
  return null;
49058
49454
  }
@@ -49109,7 +49505,7 @@ function serialize(task) {
49109
49505
  });
49110
49506
  }
49111
49507
  function himiDir(dir) {
49112
- const root = join51(dir, ".himi");
49508
+ const root = join52(dir, ".himi");
49113
49509
  try {
49114
49510
  return lstatSync8(root).isDirectory() ? root : null;
49115
49511
  } catch (e) {
@@ -49121,11 +49517,11 @@ function himiDir(dir) {
49121
49517
  function writeRunRecord(dir, record2) {
49122
49518
  const root = himiDir(dir);
49123
49519
  if (!root) return false;
49124
- const tmp = join51(root, `.run.json.${process.pid}.${randomUUID3()}.tmp`);
49520
+ const tmp = join52(root, `.run.json.${process.pid}.${randomUUID3()}.tmp`);
49125
49521
  try {
49126
49522
  writeFileSync22(tmp, `${JSON.stringify(record2, null, 2)}
49127
49523
  `, { flag: "wx" });
49128
- renameSync5(tmp, join51(root, "run.json"));
49524
+ renameSync5(tmp, join52(root, "run.json"));
49129
49525
  } catch (e) {
49130
49526
  rmSync13(tmp, { force: true });
49131
49527
  throw e;
@@ -49133,7 +49529,7 @@ function writeRunRecord(dir, record2) {
49133
49529
  return true;
49134
49530
  }
49135
49531
  function removeRunRecord(dir, owner) {
49136
- const root = join51(dir, ".himi");
49532
+ const root = join52(dir, ".himi");
49137
49533
  try {
49138
49534
  if (!lstatSync8(root).isDirectory()) return;
49139
49535
  } catch {
@@ -49148,15 +49544,16 @@ function removeRunRecord(dir, owner) {
49148
49544
  }
49149
49545
  if (typeof pid === "number" && pid !== owner) return;
49150
49546
  }
49151
- rmSync13(join51(root, "run.json"), { force: true });
49547
+ rmSync13(join52(root, "run.json"), { force: true });
49152
49548
  }
49153
49549
  function boardFile(dir, pathname) {
49154
49550
  let rel = null;
49155
- if (pathname === "/__himi/studio/" || pathname === "/__himi/studio/index.html") rel = join51("studio", "index.html");
49156
- 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");
49157
49554
  else {
49158
49555
  const m = /^\/__himi\/history\/([0-9a-f]{64}\.(png|jpg|gif|webp))$/.exec(pathname);
49159
- if (m) rel = join51("history", m[1]);
49556
+ if (m) rel = join52("history", m[1]);
49160
49557
  }
49161
49558
  if (!rel) return null;
49162
49559
  const bytes2 = readConfined(resolve42(dir, ".himi"), rel);
@@ -49172,7 +49569,7 @@ function readConfined(root, rel) {
49172
49569
  const parts = rel.split(sep13);
49173
49570
  let at = root;
49174
49571
  for (let i = 0; i < parts.length; i++) {
49175
- at = join51(at, parts[i]);
49572
+ at = join52(at, parts[i]);
49176
49573
  const st = lstatSync8(at);
49177
49574
  if (st.isSymbolicLink() || !(i === parts.length - 1 ? st.isFile() : st.isDirectory())) return null;
49178
49575
  }
@@ -49181,7 +49578,7 @@ function readConfined(root, rel) {
49181
49578
  const fd = openSync2(at, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
49182
49579
  try {
49183
49580
  if (!fstatSync(fd).isFile()) return null;
49184
- return readFileSync59(fd);
49581
+ return readFileSync60(fd);
49185
49582
  } finally {
49186
49583
  closeSync2(fd);
49187
49584
  }
@@ -49232,7 +49629,7 @@ __export(run_browser_exports, {
49232
49629
  runInBrowser: () => runInBrowser
49233
49630
  });
49234
49631
  import { createServer as createServer5 } from "node:http";
49235
- import { existsSync as existsSync54, readFileSync as readFileSync60 } from "node:fs";
49632
+ import { existsSync as existsSync55, readFileSync as readFileSync61 } from "node:fs";
49236
49633
  import { spawnSync as spawnSync3 } from "node:child_process";
49237
49634
  import { dirname as dirname28, resolve as resolve43 } from "node:path";
49238
49635
  import { fileURLToPath as fileURLToPath23 } from "node:url";
@@ -49240,7 +49637,7 @@ function stagedDir() {
49240
49637
  return process.env.HIMI_BROWSER_PLANE_OUT ? resolve43(process.env.HIMI_BROWSER_PLANE_OUT) : resolve43(REPO, "packages", "serve", "dist", "browser-plane");
49241
49638
  }
49242
49639
  function missingStaged() {
49243
- return [...SERVED].filter((f) => !existsSync54(resolve43(stagedDir(), f))).sort();
49640
+ return [...SERVED].filter((f) => !existsSync55(resolve43(stagedDir(), f))).sort();
49244
49641
  }
49245
49642
  function ensureBrowserPlaneStaged() {
49246
49643
  if (missingStaged().length === 0) return { ok: true, detail: "already staged" };
@@ -49330,7 +49727,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
49330
49727
  }
49331
49728
  const name = path2 === "/" || path2 === "/index.html" ? "host.html" : path2.slice(1);
49332
49729
  if (SERVED.has(name)) {
49333
- if (!existsSync54(resolve43(stagedDir(), name))) {
49730
+ if (!existsSync55(resolve43(stagedDir(), name))) {
49334
49731
  const restaged = ensureBrowserPlaneStaged();
49335
49732
  if (!restaged.ok) {
49336
49733
  res.writeHead(503, { "content-type": "application/json" });
@@ -49340,7 +49737,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
49340
49737
  }
49341
49738
  let bytes2;
49342
49739
  try {
49343
- bytes2 = readFileSync60(resolve43(stagedDir(), name));
49740
+ bytes2 = readFileSync61(resolve43(stagedDir(), name));
49344
49741
  } catch (e) {
49345
49742
  res.writeHead(503, { "content-type": "application/json" });
49346
49743
  res.end(JSON.stringify({ error: "browser_plane_unstaged", file: name, detail: String(e) }));
@@ -49800,7 +50197,7 @@ __export(preflight_exports, {
49800
50197
  parseStoreConfig: () => parseStoreConfig,
49801
50198
  requireEnv: () => requireEnv
49802
50199
  });
49803
- import { existsSync as existsSync55, readFileSync as readFileSync61 } from "node:fs";
50200
+ import { existsSync as existsSync56, readFileSync as readFileSync62 } from "node:fs";
49804
50201
  import { isAbsolute as isAbsolute3, relative as relative9, resolve as resolve44 } from "node:path";
49805
50202
  function isInsideRepo(p) {
49806
50203
  const rel = relative9(REPO_ROOT3, resolve44(p));
@@ -49857,10 +50254,10 @@ function parseStoreConfig(text5) {
49857
50254
  }
49858
50255
  function loadStoreConfig(app) {
49859
50256
  const p = storeConfigPath(app);
49860
- if (!existsSync55(p)) {
50257
+ if (!existsSync56(p)) {
49861
50258
  throw new Error(`No store config at ${p}. Create apps/${app}/store/store.config.yaml first (see /setup-store-signing).`);
49862
50259
  }
49863
- return parseStoreConfig(readFileSync61(p, "utf8"));
50260
+ return parseStoreConfig(readFileSync62(p, "utf8"));
49864
50261
  }
49865
50262
  var init_preflight = __esm({
49866
50263
  "../store-release/preflight.ts"() {
@@ -49879,19 +50276,19 @@ __export(credential_profile_exports, {
49879
50276
  profilePath: () => profilePath,
49880
50277
  resolveStoreCredentials: () => resolveStoreCredentials
49881
50278
  });
49882
- import { existsSync as existsSync56, readFileSync as readFileSync62 } from "node:fs";
50279
+ import { existsSync as existsSync57, readFileSync as readFileSync63 } from "node:fs";
49883
50280
  import { homedir as homedir5 } from "node:os";
49884
- import { join as join52 } from "node:path";
50281
+ import { join as join53 } from "node:path";
49885
50282
  function credentialsDir(home = homedir5()) {
49886
- return join52(home, ".himalaya", "store-credentials");
50283
+ return join53(home, ".himalaya", "store-credentials");
49887
50284
  }
49888
50285
  function profilePath(name, home = homedir5()) {
49889
50286
  if (!PROFILE_NAME.test(name)) {
49890
50287
  throw new Error(
49891
- `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.`
49892
50289
  );
49893
50290
  }
49894
- return join52(credentialsDir(home), `${name}.json`);
50291
+ return join53(credentialsDir(home), `${name}.json`);
49895
50292
  }
49896
50293
  function parseProfile(text5, label2) {
49897
50294
  let doc;
@@ -49922,8 +50319,8 @@ function resolveStoreCredentials(opts) {
49922
50319
  const { app, cfg } = opts;
49923
50320
  const env = opts.env ?? process.env;
49924
50321
  const home = opts.home ?? homedir5();
49925
- const read = opts.readProfile ?? ((p) => readFileSync62(p, "utf8"));
49926
- const exists = opts.profileExists ?? existsSync56;
50322
+ const read = opts.readProfile ?? ((p) => readFileSync63(p, "utf8"));
50323
+ const exists = opts.profileExists ?? existsSync57;
49927
50324
  const ios = cfg.ios ?? {};
49928
50325
  const flagName = opts.profileFlag?.trim();
49929
50326
  const named = namedProfile(cfg, opts.profileFlag);
@@ -50003,9 +50400,9 @@ __export(preflight_app_exports, {
50003
50400
  preflightApp: () => preflightApp,
50004
50401
  profileAccount: () => profileAccount
50005
50402
  });
50006
- 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";
50007
50404
  import { createHash as createHash26 } from "node:crypto";
50008
- import { join as join53 } from "node:path";
50405
+ import { join as join54 } from "node:path";
50009
50406
  function analyze(projectYml, storeConfig, icon, account) {
50010
50407
  const f = [];
50011
50408
  const has = (re, s) => re.test(s);
@@ -50069,13 +50466,13 @@ function profileAccount(app, storeConfig, opts = {}) {
50069
50466
  }
50070
50467
  }
50071
50468
  async function preflightApp(app, opts = {}) {
50072
- const projectYml = existsSync57(iosProjectYml(app)) ? readFileSync63(iosProjectYml(app), "utf8") : "";
50073
- 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") : "";
50074
50471
  if (!storeConfig) return [{ level: "error", message: `no apps/${app}/store/store.config.yaml \u2014 run /setup-store-signing first.` }];
50075
50472
  const findings = analyze(
50076
50473
  projectYml,
50077
50474
  storeConfig,
50078
- await appIconState(join53(appDir(app), "ios")),
50475
+ await appIconState(join54(appDir(app), "ios")),
50079
50476
  profileAccount(app, storeConfig, opts)
50080
50477
  );
50081
50478
  findings.push(...analyzeListing(metadataFilesPresent(app), listScreenshots(app)));
@@ -50104,17 +50501,17 @@ function analyzeSubmissionRecord(appLevel, review, opts) {
50104
50501
  return f;
50105
50502
  }
50106
50503
  function appLevelMetadataFiles(app, locale = "en-US") {
50107
- const root = join53(storeDir(app), "metadata");
50504
+ const root = join54(storeDir(app), "metadata");
50108
50505
  const out = /* @__PURE__ */ new Set();
50109
- if (existsSync57(root)) {
50506
+ if (existsSync58(root)) {
50110
50507
  for (const n of readdirSync26(root)) if (n.endsWith(".txt")) out.add(n);
50111
50508
  }
50112
- 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");
50113
50510
  return out;
50114
50511
  }
50115
50512
  function reviewInfoFiles(app) {
50116
- const dir = join53(storeDir(app), "review_information");
50117
- if (!existsSync57(dir)) return /* @__PURE__ */ new Set();
50513
+ const dir = join54(storeDir(app), "review_information");
50514
+ if (!existsSync58(dir)) return /* @__PURE__ */ new Set();
50118
50515
  return new Set(readdirSync26(dir).filter((n) => n.endsWith(".txt")));
50119
50516
  }
50120
50517
  function isValidAppStoreSize(w, h) {
@@ -50224,12 +50621,12 @@ function analyzePlayListing(assets) {
50224
50621
  return f;
50225
50622
  }
50226
50623
  function playListingAssets(app, locale = "en-US") {
50227
- const dir = join53(storeDir(app), "play", locale, "images");
50624
+ const dir = join54(storeDir(app), "play", locale, "images");
50228
50625
  const read = (names) => {
50229
50626
  for (const name of names) {
50230
- const p = join53(dir, name);
50627
+ const p = join54(dir, name);
50231
50628
  try {
50232
- if (statSync15(p).isFile()) return { name, bytes: readFileSync63(p) };
50629
+ if (statSync15(p).isFile()) return { name, bytes: readFileSync64(p) };
50233
50630
  } catch {
50234
50631
  }
50235
50632
  }
@@ -50260,26 +50657,26 @@ function analyzeListing(metaFiles, shots) {
50260
50657
  return f;
50261
50658
  }
50262
50659
  function metadataFilesPresent(app, locale = "en-US") {
50263
- const dir = join53(storeDir(app), "metadata", locale);
50264
- if (!existsSync57(dir)) return /* @__PURE__ */ new Set();
50660
+ const dir = join54(storeDir(app), "metadata", locale);
50661
+ if (!existsSync58(dir)) return /* @__PURE__ */ new Set();
50265
50662
  return new Set(readdirSync26(dir).filter((n) => n.endsWith(".txt")));
50266
50663
  }
50267
50664
  function listScreenshots(app, locale = "en-US") {
50268
50665
  const out = [];
50269
- const flat = join53(storeDir(app), "screenshots", locale);
50270
- if (existsSync57(flat)) {
50666
+ const flat = join54(storeDir(app), "screenshots", locale);
50667
+ if (existsSync58(flat)) {
50271
50668
  for (const png of readdirSync26(flat).filter((n) => n.endsWith(".png"))) {
50272
- const sz = pngSize(readFileSync63(join53(flat, png)));
50669
+ const sz = pngSize(readFileSync64(join54(flat, png)));
50273
50670
  if (sz) out.push({ name: png, w: sz.w, h: sz.h });
50274
50671
  }
50275
50672
  }
50276
- const legacy = join53(storeDir(app), "metadata", locale, "screenshots");
50277
- if (existsSync57(legacy)) {
50673
+ const legacy = join54(storeDir(app), "metadata", locale, "screenshots");
50674
+ if (existsSync58(legacy)) {
50278
50675
  for (const deviceDir of readdirSync26(legacy)) {
50279
- const d = join53(legacy, deviceDir);
50676
+ const d = join54(legacy, deviceDir);
50280
50677
  if (!statSync15(d).isDirectory()) continue;
50281
50678
  for (const png of readdirSync26(d).filter((n) => n.endsWith(".png"))) {
50282
- const sz = pngSize(readFileSync63(join53(d, png)));
50679
+ const sz = pngSize(readFileSync64(join54(d, png)));
50283
50680
  if (sz) out.push({ name: `${deviceDir}/${png}`, w: sz.w, h: sz.h });
50284
50681
  }
50285
50682
  }
@@ -50287,13 +50684,13 @@ function listScreenshots(app, locale = "en-US") {
50287
50684
  return out;
50288
50685
  }
50289
50686
  function placeholderHashes2() {
50290
- const templateIos = join53(appDir("_template"), "ios");
50291
- if (!existsSync57(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
50687
+ const templateIos = join54(appDir("_template"), "ios");
50688
+ if (!existsSync58(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
50292
50689
  for (const target of readdirSync26(templateIos)) {
50293
- 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");
50294
50691
  try {
50295
50692
  if (!statSync15(p).isFile()) continue;
50296
- return [LEGACY_PLACEHOLDER_SHA2562, createHash26("sha256").update(readFileSync63(p)).digest("hex")];
50693
+ return [LEGACY_PLACEHOLDER_SHA2562, createHash26("sha256").update(readFileSync64(p)).digest("hex")];
50297
50694
  } catch {
50298
50695
  }
50299
50696
  }
@@ -50324,12 +50721,12 @@ async function pixelIssues(bytes2) {
50324
50721
  }
50325
50722
  }
50326
50723
  async function appIconState(iosDir) {
50327
- if (!existsSync57(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
50724
+ if (!existsSync58(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
50328
50725
  for (const target of readdirSync26(iosDir)) {
50329
- 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");
50330
50727
  try {
50331
50728
  if (!statSync15(p).isFile()) continue;
50332
- const bytes2 = readFileSync63(p);
50729
+ const bytes2 = readFileSync64(p);
50333
50730
  const hash = createHash26("sha256").update(bytes2).digest("hex");
50334
50731
  const pixels = await pixelIssues(bytes2);
50335
50732
  return {
@@ -50401,7 +50798,7 @@ __export(version_exports, {
50401
50798
  setAndroidVersion: () => setAndroidVersion,
50402
50799
  setIosVersion: () => setIosVersion
50403
50800
  });
50404
- import { readFileSync as readFileSync64, writeFileSync as writeFileSync23 } from "node:fs";
50801
+ import { readFileSync as readFileSync65, writeFileSync as writeFileSync23 } from "node:fs";
50405
50802
  function readIosVersion(projectYml) {
50406
50803
  const marketing = matchScalar(projectYml, "CFBundleShortVersionString");
50407
50804
  const build2 = matchScalar(projectYml, "CFBundleVersion");
@@ -50459,8 +50856,8 @@ function setAndroidVersion(gradle, v) {
50459
50856
  function bumpVersion(app, opts) {
50460
50857
  const iosPath = iosProjectYml(app);
50461
50858
  const gradlePath = androidGradle(app);
50462
- const iosYml = readFileSync64(iosPath, "utf8");
50463
- const gradle = readFileSync64(gradlePath, "utf8");
50859
+ const iosYml = readFileSync65(iosPath, "utf8");
50860
+ const gradle = readFileSync65(gradlePath, "utf8");
50464
50861
  const current = readIosVersion(iosYml);
50465
50862
  const next = {
50466
50863
  marketing: opts.marketing ?? current.marketing,
@@ -50477,7 +50874,7 @@ var init_version = __esm({
50477
50874
  });
50478
50875
 
50479
50876
  // ../store-release/register-bundle.ts
50480
- import { readFileSync as readFileSync65 } from "node:fs";
50877
+ import { readFileSync as readFileSync66 } from "node:fs";
50481
50878
  import { createPrivateKey as createPrivateKey2, sign } from "node:crypto";
50482
50879
  function b64url2(b) {
50483
50880
  return Buffer.from(b).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
@@ -50495,7 +50892,7 @@ function bundleIdBody(identifier, name, platform = "IOS") {
50495
50892
  async function registerBundleId(opts) {
50496
50893
  const env = requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
50497
50894
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
50498
- 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));
50499
50896
  const res = await fetch(`${ASC_API}/v1/bundleIds`, {
50500
50897
  method: "POST",
50501
50898
  headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
@@ -50549,7 +50946,7 @@ __export(asc_status_exports, {
50549
50946
  ascStatus: () => ascStatus,
50550
50947
  buildVerdict: () => buildVerdict
50551
50948
  });
50552
- import { readFileSync as readFileSync66 } from "node:fs";
50949
+ import { readFileSync as readFileSync67 } from "node:fs";
50553
50950
  function buildVerdict(builds) {
50554
50951
  if (!builds.length) {
50555
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).";
@@ -50563,7 +50960,7 @@ function buildVerdict(builds) {
50563
50960
  function client(creds) {
50564
50961
  const env = creds ?? requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
50565
50962
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
50566
- 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));
50567
50964
  return async (path2) => {
50568
50965
  const r = await fetch(`${ASC}${path2}`, { headers: { Authorization: `Bearer ${jwt}` } });
50569
50966
  return { status: r.status, json: await r.json().catch(() => ({})) };
@@ -50723,7 +51120,7 @@ __export(secrets_exports, {
50723
51120
  runRemoteSecretsCommand: () => runRemoteSecretsCommand,
50724
51121
  runSecretsCommand: () => runSecretsCommand
50725
51122
  });
50726
- import { readFileSync as readFileSync67 } from "node:fs";
51123
+ import { readFileSync as readFileSync68 } from "node:fs";
50727
51124
  function environment(value) {
50728
51125
  if (value === "preview" || value === "production") return value;
50729
51126
  throw new Error("--environment must be preview or production");
@@ -50754,7 +51151,7 @@ async function runSecretsCommand(operation, flags, io, options = {}) {
50754
51151
  io.log(JSON.stringify(await vault.list({ app, environment: env })));
50755
51152
  return 0;
50756
51153
  }
50757
- 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$/, "");
50758
51155
  if (operation === "set") await vault.set({ app, environment: env, name, value });
50759
51156
  else if (operation === "rotate") await vault.rotate({ app, environment: env, name, value });
50760
51157
  else throw new Error(`unknown secrets operation ${operation}`);
@@ -50783,7 +51180,7 @@ async function runRemoteSecretsCommand(operation, flags, io, options) {
50783
51180
  }
50784
51181
  let body;
50785
51182
  if (operation !== "list") {
50786
- 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$/, "");
50787
51184
  body = JSON.stringify({ value });
50788
51185
  }
50789
51186
  const response = await (options.fetchImpl ?? fetch)(url, { method: operation === "list" ? "GET" : operation === "set" ? "PUT" : "POST", headers: headers2, body });
@@ -50819,15 +51216,15 @@ __export(notify_exports, {
50819
51216
  runNotify: () => runNotify,
50820
51217
  saveAdminSession: () => saveAdminSession
50821
51218
  });
50822
- 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";
50823
51220
  import { homedir as homedir6 } from "node:os";
50824
- import { dirname as dirname30, join as join54 } from "node:path";
51221
+ import { dirname as dirname30, join as join55 } from "node:path";
50825
51222
  function defaultSessionsPath() {
50826
- 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");
50827
51224
  }
50828
51225
  function loadSessionsFile(path2 = defaultSessionsPath()) {
50829
51226
  try {
50830
- return JSON.parse(readFileSync68(path2, "utf8"));
51227
+ return JSON.parse(readFileSync69(path2, "utf8"));
50831
51228
  } catch {
50832
51229
  return {};
50833
51230
  }
@@ -54385,7 +54782,7 @@ var init_brand_font = __esm({
54385
54782
 
54386
54783
  // src/site-analyze/write-artifacts.ts
54387
54784
  import { mkdirSync as mkdirSync28, writeFileSync as writeFileSync25 } from "node:fs";
54388
- import { join as join55 } from "node:path";
54785
+ import { join as join56 } from "node:path";
54389
54786
  function attachVeloSources(book, sources, put) {
54390
54787
  const byPath = new Map((sources ?? []).map((s) => [s.path, s.text]));
54391
54788
  for (const f of book.velo.files) {
@@ -54540,12 +54937,12 @@ async function downloadMedia(book, fetchImpl, put) {
54540
54937
  }
54541
54938
  async function writeBusinessBook(result, opts) {
54542
54939
  const out = opts.outDir;
54543
- mkdirSync28(join55(out, "evidence/pages"), { recursive: true });
54544
- mkdirSync28(join55(out, "media/catalog"), { recursive: true });
54940
+ mkdirSync28(join56(out, "evidence/pages"), { recursive: true });
54941
+ mkdirSync28(join56(out, "media/catalog"), { recursive: true });
54545
54942
  const files = [];
54546
54943
  const put = (rel, body) => {
54547
- const p = join55(out, rel);
54548
- mkdirSync28(join55(p, ".."), { recursive: true });
54944
+ const p = join56(out, rel);
54945
+ mkdirSync28(join56(p, ".."), { recursive: true });
54549
54946
  writeFileSync25(p, body);
54550
54947
  files.push(rel);
54551
54948
  };
@@ -54639,8 +55036,8 @@ var init_customer_build = __esm({
54639
55036
  });
54640
55037
 
54641
55038
  // src/site-analyze/apply.ts
54642
- 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";
54643
- 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";
54644
55041
  import { spawnSync as spawnSync4 } from "node:child_process";
54645
55042
  function unpairedRedirectNote(overlay) {
54646
55043
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
@@ -54655,7 +55052,7 @@ function committedShellProvisioning(path2) {
54655
55052
  function findRepoRoot(cwd) {
54656
55053
  let dir = cwd;
54657
55054
  for (let i = 0; i < 8; i++) {
54658
- 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;
54659
55056
  const next = dirname31(dir);
54660
55057
  if (next === dir) break;
54661
55058
  dir = next;
@@ -54723,11 +55120,11 @@ function mergeCustomerOwned(committed, overlay) {
54723
55120
  }
54724
55121
  function buildMergedContract(options) {
54725
55122
  const { appDir: appDir2, packageDir, overlay } = options;
54726
- const committedPath = join56(appDir2, "provisioning.json");
54727
- if (!existsSync58(committedPath)) {
55123
+ const committedPath = join57(appDir2, "provisioning.json");
55124
+ if (!existsSync59(committedPath)) {
54728
55125
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
54729
55126
  }
54730
- const committed = JSON.parse(readFileSync69(committedPath, "utf8"));
55127
+ const committed = JSON.parse(readFileSync70(committedPath, "utf8"));
54731
55128
  const merged = mergeCustomerOwned(committed, overlay);
54732
55129
  const overlayFontFamily = (overlay.branding ?? {}).fontFamily;
54733
55130
  const branding = merged.branding ?? {};
@@ -54739,8 +55136,8 @@ function buildMergedContract(options) {
54739
55136
  };
54740
55137
  const stage = (fromRel, toRel) => {
54741
55138
  const from = contained(packageDir, fromRel);
54742
- const to = contained(join56(appDir2, STAGED), toRel.slice(STAGED.length + 1));
54743
- 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;
54744
55141
  let real;
54745
55142
  let realRoot;
54746
55143
  try {
@@ -54762,11 +55159,11 @@ function buildMergedContract(options) {
54762
55159
  };
54763
55160
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
54764
55161
  if (icon) branding.appIconPath = icon;
54765
- const facesPath = join56(packageDir, "media/fonts/faces.json");
54766
- if (existsSync58(facesPath)) {
55162
+ const facesPath = join57(packageDir, "media/fonts/faces.json");
55163
+ if (existsSync59(facesPath)) {
54767
55164
  let faces = [];
54768
55165
  try {
54769
- const parsed = JSON.parse(readFileSync69(facesPath, "utf8"));
55166
+ const parsed = JSON.parse(readFileSync70(facesPath, "utf8"));
54770
55167
  faces = Array.isArray(parsed) ? parsed : [];
54771
55168
  } catch {
54772
55169
  faces = [];
@@ -54787,9 +55184,9 @@ function buildMergedContract(options) {
54787
55184
  merged.branding = branding;
54788
55185
  const body = `${JSON.stringify(merged, null, 2)}
54789
55186
  `;
54790
- const path2 = join56(packageDir, "provisioning.contract.json");
55187
+ const path2 = join57(packageDir, "provisioning.contract.json");
54791
55188
  writeFileSync26(path2, body);
54792
- const localPath = join56(appDir2, LOCAL_CONTRACT_REL);
55189
+ const localPath = join57(appDir2, LOCAL_CONTRACT_REL);
54793
55190
  mkdirSync29(dirname31(localPath), { recursive: true });
54794
55191
  writeFileSync26(localPath, body);
54795
55192
  staged.push(localPath);
@@ -54799,8 +55196,8 @@ async function applyOverlays(input) {
54799
55196
  const cwd = resolve45(input.cwd);
54800
55197
  const shell = input.shell ?? DEFAULT_BRANDABLE_SHELL;
54801
55198
  assertKnownShell(shell);
54802
- const overlay = JSON.parse(readFileSync69(input.overlayPath, "utf8"));
54803
- 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"));
54804
55201
  const wrote = [];
54805
55202
  const notes = [];
54806
55203
  const repoRoot2 = findRepoRoot(cwd);
@@ -54810,7 +55207,7 @@ async function applyOverlays(input) {
54810
55207
  `shell "${shell}" cannot bake a customer brand \u2014 it ships no prepare:customer script`
54811
55208
  );
54812
55209
  }
54813
- const appDir2 = join56(repoRoot2, shellAppDir(shell));
55210
+ const appDir2 = join57(repoRoot2, shellAppDir(shell));
54814
55211
  const packageDir = dirname31(resolve45(input.overlayPath));
54815
55212
  const { path: contract, staged } = buildMergedContract({ appDir: appDir2, packageDir, overlay });
54816
55213
  wrote.push(...staged);
@@ -54832,10 +55229,10 @@ async function applyOverlays(input) {
54832
55229
  note: `prepare:customer --contract with a merged contract (committed provisioning.json untouched)${unpairedInRepo ? ` \xB7 ${unpairedInRepo}` : ""}`
54833
55230
  };
54834
55231
  }
54835
- const pkgProv = join56(cwd, "provisioning.json");
55232
+ const pkgProv = join57(cwd, "provisioning.json");
54836
55233
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
54837
- if (existsSync58(pkgProv)) {
54838
- const cur = JSON.parse(readFileSync69(pkgProv, "utf8"));
55234
+ if (existsSync59(pkgProv)) {
55235
+ const cur = JSON.parse(readFileSync70(pkgProv, "utf8"));
54839
55236
  const merged = deepMerge4(cur, overlay);
54840
55237
  writeFileSync26(pkgProv, JSON.stringify(merged, null, 2) + "\n");
54841
55238
  wrote.push(pkgProv);
@@ -54843,9 +55240,9 @@ async function applyOverlays(input) {
54843
55240
  writeFileSync26(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
54844
55241
  wrote.push(pkgProv);
54845
55242
  }
54846
- const pkgTokens = join56(cwd, "tokens.json");
54847
- if (existsSync58(pkgTokens) && (tokens.colors || tokens.typography)) {
54848
- 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"));
54849
55246
  const merged = deepMerge4(cur, tokens);
54850
55247
  const mergedColors = merged.colors;
54851
55248
  if (mergedColors && typeof mergedColors === "object" && !Array.isArray(mergedColors)) {
@@ -54864,9 +55261,9 @@ async function applyOverlays(input) {
54864
55261
  wrote.push(pkgTokens);
54865
55262
  }
54866
55263
  const homeAction = homeRefreshActionId(shell);
54867
- const homeSrc = join56(cwd, "dev/index.ts");
54868
- if (homeAction && existsSync58(homeSrc)) {
54869
- const prev = readFileSync69(homeSrc, "utf8");
55264
+ const homeSrc = join57(cwd, "dev/index.ts");
55265
+ if (homeAction && existsSync59(homeSrc)) {
55266
+ const prev = readFileSync70(homeSrc, "utf8");
54870
55267
  const patched = patchBrandParams(prev, overlay, homeAction);
54871
55268
  let next = patched.src;
54872
55269
  if (!patched.foreign && overlay.installedVerticals?.length) {
@@ -54889,13 +55286,13 @@ async function applyOverlays(input) {
54889
55286
  notes.push(`${patched.unparsed} onAppear params block(s) in ${homeSrc} could not be read \u2014 any brand values inside them were not applied`);
54890
55287
  }
54891
55288
  }
54892
- if (!input.iconPath || !existsSync58(input.iconPath)) {
55289
+ if (!input.iconPath || !existsSync59(input.iconPath)) {
54893
55290
  const book = input.iconPath ? dirname31(dirname31(resolve45(input.iconPath))) : void 0;
54894
55291
  notes.push(
54895
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"
54896
55293
  );
54897
55294
  }
54898
- if (input.iconPath && existsSync58(input.iconPath)) {
55295
+ if (input.iconPath && existsSync59(input.iconPath)) {
54899
55296
  try {
54900
55297
  const installed = await installIcon(cwd, input.iconPath, { derivePlay: true });
54901
55298
  wrote.push(...installed.wrote);
@@ -59050,7 +59447,7 @@ __export(cli_exports, {
59050
59447
  runSiteAnalyze: () => runSiteAnalyze,
59051
59448
  runSiteFunctionCli: () => runSiteFunctionCli
59052
59449
  });
59053
- import { readFileSync as readFileSync70 } from "node:fs";
59450
+ import { readFileSync as readFileSync71 } from "node:fs";
59054
59451
  import { resolve as resolve46 } from "node:path";
59055
59452
  function assetFetchFor(_opts) {
59056
59453
  return pinnedFetch2;
@@ -59137,7 +59534,7 @@ async function runSiteFunctionCli(opts) {
59137
59534
  let source;
59138
59535
  if (opts.sourcePath) {
59139
59536
  const p = resolve46(opts.cwd ?? process.cwd(), opts.sourcePath);
59140
- source = readFileSync70(p, "utf8");
59537
+ source = readFileSync71(p, "utf8");
59141
59538
  }
59142
59539
  let body;
59143
59540
  if (opts.bodyRaw != null) {
@@ -59394,7 +59791,7 @@ __export(site_create_exports, {
59394
59791
  runSiteList: () => runSiteList,
59395
59792
  writeProvisioningBinding: () => writeProvisioningBinding
59396
59793
  });
59397
- 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";
59398
59795
  import { resolve as resolvePath2 } from "node:path";
59399
59796
  function oauthClientName(projectName) {
59400
59797
  return `${projectName} client`;
@@ -59543,7 +59940,7 @@ async function runSiteCreate(opts) {
59543
59940
  if (!wantOauthApp) return { ...base, nextSteps: nextSteps(metaSiteId) };
59544
59941
  return { ...base, clientId, redirectUri, nextSteps: nextSteps(metaSiteId, clientId) };
59545
59942
  }
59546
- function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync59, readFileSync: readFileSync71, writeFileSync: writeFileSync27 }) {
59943
+ function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync60, readFileSync: readFileSync72, writeFileSync: writeFileSync27 }) {
59547
59944
  const msid = binding2.siteId;
59548
59945
  const path2 = resolvePath2(target);
59549
59946
  const committed = committedShellProvisioning(path2);
@@ -60216,11 +60613,11 @@ var init_context = __esm({
60216
60613
  });
60217
60614
 
60218
60615
  // src/cli.ts
60219
- 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";
60220
60617
  import { execFile, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn6 } from "node:child_process";
60221
60618
  import { createHash as createHash27, randomUUID as randomUUID5 } from "node:crypto";
60222
60619
  import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
60223
- 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";
60224
60621
  import { fileURLToPath as fileURLToPath24 } from "node:url";
60225
60622
 
60226
60623
  // ../serve-cli/src/client.ts
@@ -63496,8 +63893,8 @@ var str6 = (v) => typeof v === "string" ? v : void 0;
63496
63893
  var num2 = (v) => typeof v === "string" && v !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
63497
63894
  function embeddedTemplateCatalog() {
63498
63895
  return availableTemplates().map((name) => {
63499
- const path2 = join57(templateSourceDir(name), TEMPLATE_META_FILE);
63500
- const meta = JSON.parse(readFileSync72(path2, "utf8"));
63896
+ const path2 = join58(templateSourceDir(name), TEMPLATE_META_FILE);
63897
+ const meta = JSON.parse(readFileSync73(path2, "utf8"));
63501
63898
  if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
63502
63899
  throw new Error(`${path2} must be { title: string, description: string, tags: string[], category?: string }`);
63503
63900
  }
@@ -64166,7 +64563,7 @@ var COMMANDS = [
64166
64563
  ];
64167
64564
  function cliVersion() {
64168
64565
  try {
64169
- 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";
64170
64567
  } catch {
64171
64568
  return "unknown";
64172
64569
  }
@@ -64284,7 +64681,7 @@ function ageInDays(publishedAt, now = Date.now()) {
64284
64681
  }
64285
64682
  function packageVersion(path2) {
64286
64683
  try {
64287
- const version2 = JSON.parse(readFileSync72(path2, "utf8")).version;
64684
+ const version2 = JSON.parse(readFileSync73(path2, "utf8")).version;
64288
64685
  return typeof version2 === "string" && version2 ? version2 : void 0;
64289
64686
  } catch {
64290
64687
  return void 0;
@@ -64293,7 +64690,7 @@ function packageVersion(path2) {
64293
64690
  function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
64294
64691
  let dir = resolve47(fromDir);
64295
64692
  for (; ; ) {
64296
- const version2 = packageVersion(join57(dir, "node_modules", "@wix", "himalaya", "package.json"));
64693
+ const version2 = packageVersion(join58(dir, "node_modules", "@wix", "himalaya", "package.json"));
64297
64694
  if (version2) return { version: version2, source: "local" };
64298
64695
  const parent = dirname32(dir);
64299
64696
  if (parent === dir) break;
@@ -64303,7 +64700,7 @@ function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("
64303
64700
  if (bundled) return { version: bundled, source: "bundled" };
64304
64701
  try {
64305
64702
  const root = execFileSync7("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
64306
- 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;
64307
64704
  return version2 ? { version: version2, source: "global" } : {};
64308
64705
  } catch {
64309
64706
  return {};
@@ -64350,11 +64747,11 @@ var UPDATE_RETRY_AFTER_FAILURE_MS = 60 * 60 * 1e3;
64350
64747
  var UPDATE_SNOOZE_MS = 24 * 60 * 60 * 1e3;
64351
64748
  var UPDATE_REEXEC_ENV = "HIMI_UPDATED_ONCE";
64352
64749
  function updateCachePath(env = process.env) {
64353
- 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");
64354
64751
  }
64355
64752
  function readUpdateCache(path2) {
64356
64753
  try {
64357
- const parsed = JSON.parse(readFileSync72(path2, "utf8"));
64754
+ const parsed = JSON.parse(readFileSync73(path2, "utf8"));
64358
64755
  return parsed && typeof parsed === "object" ? parsed : {};
64359
64756
  } catch {
64360
64757
  return {};
@@ -64422,12 +64819,12 @@ function createUpdateRunner(spawn7 = spawnSync5) {
64422
64819
  },
64423
64820
  installedVersion: () => {
64424
64821
  const root = globalNodeModulesRoot();
64425
- 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;
64426
64823
  },
64427
64824
  reexec: (argv) => {
64428
64825
  const root = globalNodeModulesRoot();
64429
- const entry = root ? join57(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
64430
- 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;
64431
64828
  const result = spawnSync5(process.execPath, [entry, ...argv], {
64432
64829
  // inherit keeps PIPED STDIN working — commands that read stdin must survive a re-exec.
64433
64830
  stdio: "inherit",
@@ -64474,7 +64871,7 @@ function staleUpdateLockFile(cachePath, now) {
64474
64871
  for (const path2 of [`${cachePath}.lock.break`, `${cachePath}.lock`]) {
64475
64872
  let content;
64476
64873
  try {
64477
- content = readFileSync72(path2, "utf8");
64874
+ content = readFileSync73(path2, "utf8");
64478
64875
  } catch {
64479
64876
  continue;
64480
64877
  }
@@ -64497,7 +64894,7 @@ function acquireUpdateLock(cachePath, now, hooks = {}) {
64497
64894
  const mine = lockStamp(now);
64498
64895
  const readLock = (path2) => {
64499
64896
  try {
64500
- return readFileSync72(path2, "utf8");
64897
+ return readFileSync73(path2, "utf8");
64501
64898
  } catch {
64502
64899
  return void 0;
64503
64900
  }
@@ -64794,9 +65191,9 @@ function diagnoseBody(body) {
64794
65191
  const out = {};
64795
65192
  const emptied = [];
64796
65193
  for (const [key2, value] of Object.entries(body)) {
64797
- const fold = (key2 === "issues" || key2 === "failures" || key2 === "warnings") && folded(value);
64798
- if (fold) emptied.push(key2);
64799
- 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;
64800
65197
  }
64801
65198
  if (emptied.length) out.foldedIntoFindings = emptied;
64802
65199
  return out;
@@ -65035,7 +65432,7 @@ function executableOnPath(name, env = process.env) {
65035
65432
  const suffixes = process.platform === "win32" ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
65036
65433
  for (const dir of entries) {
65037
65434
  for (const suffix of suffixes) {
65038
- const candidate = join57(dir, `${name}${suffix}`);
65435
+ const candidate = join58(dir, `${name}${suffix}`);
65039
65436
  try {
65040
65437
  if (!statSync17(candidate).isFile()) continue;
65041
65438
  if (process.platform !== "win32") accessSync(candidate, constants2.X_OK);
@@ -65144,7 +65541,7 @@ async function nativeConfigAdvisories(app, configSource) {
65144
65541
  const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
65145
65542
  const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
65146
65543
  const iosYmlPath = iosProjectYml2(app);
65147
- const iosYml = existsSync60(iosYmlPath) ? readFileSync72(iosYmlPath, "utf8") : "";
65544
+ const iosYml = existsSync61(iosYmlPath) ? readFileSync73(iosYmlPath, "utf8") : "";
65148
65545
  let optedIn = iosYml.includes(LABEL_IOS_SPLASH2);
65149
65546
  if (!optedIn) {
65150
65547
  const { androidManifestPath: androidManifestPath3 } = await Promise.resolve().then(() => (init_gen_native_config(), gen_native_config_exports));
@@ -65158,7 +65555,7 @@ async function nativeConfigAdvisories(app, configSource) {
65158
65555
  }
65159
65556
  function readConfigSource(dir) {
65160
65557
  try {
65161
- return readFileSync72(join57(dir, "config.ts"), "utf8");
65558
+ return readFileSync73(join58(dir, "config.ts"), "utf8");
65162
65559
  } catch {
65163
65560
  return null;
65164
65561
  }
@@ -65170,7 +65567,7 @@ function writePngFile(path2, base64) {
65170
65567
  }
65171
65568
  async function initFromRemoteTemplate(name, parentDir, template, c) {
65172
65569
  const dir = resolve47(parentDir, name);
65173
- if (existsSync60(join57(dir, "himalaya.content.json"))) {
65570
+ if (existsSync61(join58(dir, "himalaya.content.json"))) {
65174
65571
  throw new Error(`content package already exists at ${dir}`);
65175
65572
  }
65176
65573
  const r = await c.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -65213,7 +65610,7 @@ function symlinkDestinationIssue(dest) {
65213
65610
  function restoreDestinationIssue(dest, force) {
65214
65611
  const link = symlinkDestinationIssue(dest);
65215
65612
  if (link) return link;
65216
- if (!existsSync60(dest)) return null;
65613
+ if (!existsSync61(dest)) return null;
65217
65614
  if (!statSync17(dest).isDirectory()) {
65218
65615
  return { error: `${dest} exists and is not a directory`, hint: "pass --out/--name to land somewhere else, or remove the file" };
65219
65616
  }
@@ -65228,7 +65625,7 @@ function restoreDestinationIssue(dest, force) {
65228
65625
  function isHimalayaMonorepoCheckout(start) {
65229
65626
  let dir = resolve47(start);
65230
65627
  for (; ; ) {
65231
- 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;
65232
65629
  const parent = dirname32(dir);
65233
65630
  if (parent === dir) return false;
65234
65631
  dir = parent;
@@ -65271,7 +65668,7 @@ function materializeForPreview(appDir2, slug, displayName) {
65271
65668
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
65272
65669
  );
65273
65670
  const repoRoot2 = resolve47(fileURLToPath24(new URL("../../..", import.meta.url)));
65274
- const root = join57(repoRoot2, "node_modules", ".cache", "himi-template-preview");
65671
+ const root = join58(repoRoot2, "node_modules", ".cache", "himi-template-preview");
65275
65672
  const stillRunning = (pid) => {
65276
65673
  try {
65277
65674
  process.kill(pid, 0);
@@ -65285,12 +65682,12 @@ function materializeForPreview(appDir2, slug, displayName) {
65285
65682
  if (!entry.isDirectory()) continue;
65286
65683
  const pid = Number(entry.name.startsWith(`${slug}-`) ? entry.name.slice(slug.length + 1) : NaN);
65287
65684
  if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && !stillRunning(pid)) {
65288
- rmSync14(join57(root, entry.name), { recursive: true, force: true });
65685
+ rmSync14(join58(root, entry.name), { recursive: true, force: true });
65289
65686
  }
65290
65687
  }
65291
65688
  } catch {
65292
65689
  }
65293
- const dest = join57(root, `${slug}-${process.pid}`);
65690
+ const dest = join58(root, `${slug}-${process.pid}`);
65294
65691
  rmSync14(dest, { recursive: true, force: true });
65295
65692
  mkdirSync30(dest, { recursive: true });
65296
65693
  materializeSource(forBuild, displayName, dest, slug);
@@ -65298,8 +65695,8 @@ function materializeForPreview(appDir2, slug, displayName) {
65298
65695
  }
65299
65696
  async function appId(dir) {
65300
65697
  try {
65301
- const { readFileSync: readFileSync73 } = await import("node:fs");
65302
- 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"));
65303
65700
  if (m.name) return m.name;
65304
65701
  } catch {
65305
65702
  }
@@ -65307,8 +65704,8 @@ async function appId(dir) {
65307
65704
  }
65308
65705
  async function contentVisibility(dir) {
65309
65706
  try {
65310
- const { readFileSync: readFileSync73 } = await import("node:fs");
65311
- 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"));
65312
65709
  if (m.visibility === "public" || m.visibility === "unlisted") return m.visibility;
65313
65710
  } catch {
65314
65711
  }
@@ -65532,7 +65929,7 @@ async function recordedReleaseForContentQuietly(dir, resolution, onDrift) {
65532
65929
  try {
65533
65930
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
65534
65931
  const bres = await buildContentForValidation2(dir, { resolution });
65535
- out = mkdtempSync4(join57(tmpdir5(), "himi-ux-record-"));
65932
+ out = mkdtempSync4(join58(tmpdir5(), "himi-ux-record-"));
65536
65933
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
65537
65934
  const outcome = await buildReleaseFromContent2(dir, out, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {}, collect: { lint: true } });
65538
65935
  const releaseId = outcome.manifest.releaseId;
@@ -65710,7 +66107,7 @@ async function producedWidgetFamilies(dir) {
65710
66107
  let js;
65711
66108
  for (const candidate of [`${name}.bundle.js`, `${name}.js`, name]) {
65712
66109
  try {
65713
- js = readFileSync72(join57(outDir, candidate), "utf8");
66110
+ js = readFileSync73(join58(outDir, candidate), "utf8");
65714
66111
  break;
65715
66112
  } catch {
65716
66113
  }
@@ -65741,7 +66138,7 @@ async function producedWidgetFamilies(dir) {
65741
66138
  }
65742
66139
  return { families, source: "dist/bundles" };
65743
66140
  }
65744
- var boardOpen = (dir) => existsSync60(join57(dir, ".himi", "studio", "index.html"));
66141
+ var boardOpen = (dir) => existsSync61(join58(dir, ".himi", "studio", "index.html"));
65745
66142
  var BOARD_REFRESHERS = /* @__PURE__ */ new Set(["render", "test", "validate", "build", "push", "icon", "site", "ux", "progress"]);
65746
66143
  async function leaveForBoard(dir, record2, value) {
65747
66144
  if (!boardOpen(dir)) return;
@@ -66129,7 +66526,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66129
66526
  const sourceDir = resolve47(str6(flags["source-dir"]) ?? appDir2);
66130
66527
  let meta;
66131
66528
  try {
66132
- meta = JSON.parse(readFileSync72(join57(sourceDir, TEMPLATE_META_FILE), "utf8"));
66529
+ meta = JSON.parse(readFileSync73(join58(sourceDir, TEMPLATE_META_FILE), "utf8"));
66133
66530
  } catch {
66134
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>)` }));
66135
66532
  return 2;
@@ -66139,7 +66536,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66139
66536
  return 2;
66140
66537
  }
66141
66538
  try {
66142
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync72(join57(sourceDir, "tokens.json"), "utf8")));
66539
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync73(join58(sourceDir, "tokens.json"), "utf8")));
66143
66540
  meta = { ...appearance, ...meta };
66144
66541
  } catch {
66145
66542
  }
@@ -66151,7 +66548,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66151
66548
  const force = flags.force === true;
66152
66549
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
66153
66550
  const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
66154
- const out = mkdtempSync4(join57(tmpdir5(), "himi-template-"));
66551
+ const out = mkdtempSync4(join58(tmpdir5(), "himi-template-"));
66155
66552
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
66156
66553
  const outcome = await buildReleaseFromContent2(buildDir, out, {
66157
66554
  ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
@@ -66204,7 +66601,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66204
66601
  let iconHash;
66205
66602
  if (iconPath) {
66206
66603
  try {
66207
- const bytes2 = readFileSync72(iconPath);
66604
+ const bytes2 = readFileSync73(iconPath);
66208
66605
  payload.icon = bytes2.toString("base64");
66209
66606
  iconHash = createHash27("sha256").update(bytes2).digest("hex");
66210
66607
  } catch {
@@ -66437,7 +66834,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66437
66834
  const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
66438
66835
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
66439
66836
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
66440
- const out = mkdtempSync4(join57(tmpdir5(), "himi-validate-"));
66837
+ const out = mkdtempSync4(join58(tmpdir5(), "himi-validate-"));
66441
66838
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
66442
66839
  const outcome = await buildReleaseFromContent2(dir, out, {
66443
66840
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -66480,7 +66877,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66480
66877
  const path2 = dpxSessionTokenPath();
66481
66878
  let raw;
66482
66879
  try {
66483
- raw = readFileSync72(path2, "utf8");
66880
+ raw = readFileSync73(path2, "utf8");
66484
66881
  } catch {
66485
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`);
66486
66883
  }
@@ -66600,13 +66997,13 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66600
66997
  case "studio": {
66601
66998
  const { openStudio: openStudio2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
66602
66999
  const dir = contentDir(flags);
66603
- if (!existsSync60(join57(dir, "himalaya.content.json"))) {
67000
+ if (!existsSync61(join58(dir, "himalaya.content.json"))) {
66604
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/" }));
66605
67002
  return 2;
66606
67003
  }
66607
67004
  if (pos[1] === "reel") {
66608
67005
  const { buildReel: buildReel2 } = await Promise.resolve().then(() => (init_studio_reel(), studio_reel_exports));
66609
- 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"));
66610
67007
  const { html, plan } = buildReel2(dir);
66611
67008
  mkdirSync30(dirname32(out), { recursive: true });
66612
67009
  writeFileSync28(out, html);
@@ -66656,7 +67053,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66656
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));
66657
67054
  const dir = contentDir(flags);
66658
67055
  const journal = journalPath2(dir);
66659
- 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" };
66660
67057
  const message = pos.slice(1).join(" ").trim();
66661
67058
  if (!message) {
66662
67059
  if (flags.step !== void 0 || flags.image !== void 0 || flags.notify === true) {
@@ -66748,7 +67145,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66748
67145
  case "test": {
66749
67146
  const dir = contentDir(flags);
66750
67147
  const structuredOutput = flags.json === true || io.isTTY !== true;
66751
- if (existsSync60(join57(dir, "functions"))) {
67148
+ if (existsSync61(join58(dir, "functions"))) {
66752
67149
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
66753
67150
  const loaded = await loadFunctionsApp2(dir);
66754
67151
  const results = [];
@@ -66835,7 +67232,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66835
67232
  }
66836
67233
  }
66837
67234
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
66838
- const out = mkdtempSync4(join57(tmpdir5(), "himi-test-"));
67235
+ const out = mkdtempSync4(join58(tmpdir5(), "himi-test-"));
66839
67236
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
66840
67237
  const outcome = await buildReleaseFromContent2(dir, out, {
66841
67238
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -66880,7 +67277,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
66880
67277
  io.error(JSON.stringify({ error: `screen "${screen.screenId}" has no Tier-5 worker, so there is nothing for the browser loop to build` }));
66881
67278
  return 2;
66882
67279
  }
66883
- const sources = collectWorkerSources2(join57(dir, "tier5-src"), screen.bundleName);
67280
+ const sources = collectWorkerSources2(join58(dir, "tier5-src"), screen.bundleName);
66884
67281
  if (!sources) {
66885
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` }));
66886
67283
  return 2;
@@ -67041,7 +67438,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67041
67438
  const sp = await runShotsPass2({
67042
67439
  descriptors: scopeDescriptors2(outcome.descriptors ?? [], pos.slice(1), invalidScreens),
67043
67440
  tokens: outcome.tokens,
67044
- outDir: join57(dir, ".himi", "shots"),
67441
+ outDir: join58(dir, ".himi", "shots"),
67045
67442
  width: 390,
67046
67443
  layers: { rules: shotRules, ...cmsSeed ? { cmsSeed } : {}, ...canned ? { canned: canned.byKey } : {} },
67047
67444
  ...budgetMs ? { budgetMs } : {},
@@ -67079,7 +67476,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67079
67476
  releaseId: result.releaseId,
67080
67477
  releasePayload: release,
67081
67478
  descriptors: attestedDescriptors,
67082
- 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)])),
67083
67480
  coverage: {
67084
67481
  screens: crawledScreens.size,
67085
67482
  actionsDispatched: result.actions.exercised,
@@ -67169,7 +67566,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67169
67566
  return 2;
67170
67567
  }
67171
67568
  try {
67172
- const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync72(stateArg.replace(/^@/, ""), "utf8");
67569
+ const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync73(stateArg.replace(/^@/, ""), "utf8");
67173
67570
  injectedState = JSON.parse(raw);
67174
67571
  } catch (e) {
67175
67572
  io.error(JSON.stringify({ error: `--state must be inline JSON or a path to a .json file \u2014 ${e.message}` }));
@@ -67184,7 +67581,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67184
67581
  return 2;
67185
67582
  }
67186
67583
  try {
67187
- const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync72(inputArg.replace(/^@/, ""), "utf8");
67584
+ const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync73(inputArg.replace(/^@/, ""), "utf8");
67188
67585
  const parsed = JSON.parse(raw);
67189
67586
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("must be a JSON object");
67190
67587
  injectedInput = parsed;
@@ -67233,7 +67630,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67233
67630
  }
67234
67631
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
67235
67632
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
67236
- const out = mkdtempSync4(join57(tmpdir5(), "himi-render-"));
67633
+ const out = mkdtempSync4(join58(tmpdir5(), "himi-render-"));
67237
67634
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
67238
67635
  const outcome = await buildReleaseFromContent2(dir, out, {
67239
67636
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -67247,7 +67644,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67247
67644
  const diagnostics = (outcome.lint ?? []).find((s) => s.screenId === screenId)?.issues ?? [];
67248
67645
  let pngFile;
67249
67646
  let attestationPngFile;
67250
- const shotPath = join57(dir, ".himi", "shots", `${screenId}.png`);
67647
+ const shotPath = join58(dir, ".himi", "shots", `${screenId}.png`);
67251
67648
  const pngTarget = typeof flags.png === "string" ? str6(flags.png) : flags.png === true ? shotPath : void 0;
67252
67649
  if (pngTarget && r.pngBase64) {
67253
67650
  pngFile = writePngFile(pngTarget, r.pngBase64);
@@ -67583,7 +67980,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
67583
67980
  io.error(JSON.stringify(linked));
67584
67981
  return 1;
67585
67982
  }
67586
- if (existsSync60(dest)) {
67983
+ if (existsSync61(dest)) {
67587
67984
  if (!statSync17(dest).isDirectory()) {
67588
67985
  io.error(JSON.stringify({
67589
67986
  error: `${dest} exists and is not a directory`,
@@ -67596,7 +67993,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
67596
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" }));
67597
67994
  return 1;
67598
67995
  }
67599
- if (replace && occupied && !existsSync60(join57(dest, "himalaya.content.json"))) {
67996
+ if (replace && occupied && !existsSync61(join58(dest, "himalaya.content.json"))) {
67600
67997
  io.error(JSON.stringify({
67601
67998
  error: `${dest} is not a content package \u2014 refusing to --replace it`,
67602
67999
  hint: "--replace only clears a directory that holds himalaya.content.json. Check --out/--name, or pass --force to merge into it instead."
@@ -67735,7 +68132,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
67735
68132
  const force = flags.force === true;
67736
68133
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
67737
68134
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
67738
- const out = mkdtempSync4(join57(tmpdir5(), "himi-push-"));
68135
+ const out = mkdtempSync4(join58(tmpdir5(), "himi-push-"));
67739
68136
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
67740
68137
  const outcome = await buildReleaseFromContent2(dir, out, {
67741
68138
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -68256,7 +68653,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68256
68653
  }
68257
68654
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68258
68655
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
68259
- const built = mkdtempSync4(join57(tmpdir5(), "himi-pack-"));
68656
+ const built = mkdtempSync4(join58(tmpdir5(), "himi-pack-"));
68260
68657
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68261
68658
  const outcome = await buildReleaseFromContent2(dir, built, {
68262
68659
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -68459,7 +68856,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68459
68856
  }));
68460
68857
  return 1;
68461
68858
  }
68462
- const archive = readFileSync72(resolve47(file));
68859
+ const archive = readFileSync73(resolve47(file));
68463
68860
  const result = await client2().createPackageShare({
68464
68861
  app: opened.header.app,
68465
68862
  releaseId: opened.header.releaseId,
@@ -68513,11 +68910,11 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68513
68910
  const app = await appId(dir2);
68514
68911
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68515
68912
  const bres = await buildContentBundles2(dir2, { resolution: resolutionFlag(flags) });
68516
- temp = mkdtempSync4(join57(tmpdir5(), "himi-run-build-"));
68913
+ temp = mkdtempSync4(join58(tmpdir5(), "himi-run-build-"));
68517
68914
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68518
68915
  const initial = await buildReleaseFromContent2(dir2, temp, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir2) } : {} });
68519
68916
  servedMissing = initial.missingBundles;
68520
- source = join57(temp, app);
68917
+ source = join58(temp, app);
68521
68918
  }
68522
68919
  const chromeArg = str6(flags.chrome) ?? "preview";
68523
68920
  if (chromeArg !== "preview" && chromeArg !== "app") {
@@ -68553,7 +68950,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68553
68950
  if (!recordKit) return;
68554
68951
  startedAt = (/* @__PURE__ */ new Date()).toISOString();
68555
68952
  const failure = record2(served2, { ok: true, at: startedAt });
68556
- 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.` }));
68557
68954
  await refreshBoard();
68558
68955
  };
68559
68956
  const retract = async () => {
@@ -68584,7 +68981,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68584
68981
  }
68585
68982
  const watchKit = watch2 ? await Promise.resolve().then(() => (init_run_watch(), run_watch_exports)) : null;
68586
68983
  const bus = watchKit ? new watchKit.ReloadBus() : void 0;
68587
- if (watch2) served = mkdtempSync4(join57(tmpdir5(), "himi-run-served-"));
68984
+ if (watch2) served = mkdtempSync4(join58(tmpdir5(), "himi-run-served-"));
68588
68985
  const running = await runPackage2(source, {
68589
68986
  port,
68590
68987
  chrome: chromeArg,
@@ -68596,15 +68993,15 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68596
68993
  ...recordKit ? { extraRoutes: (request) => recordKit.boardFile(dir, new URL(request.url).pathname) } : {},
68597
68994
  ...watchKit && served && bus ? { workDir: served, screenEvents: bus } : {}
68598
68995
  });
68599
- let watcher;
68996
+ let watcher2;
68600
68997
  let rebuild;
68601
68998
  let stopping = false;
68602
68999
  try {
68603
69000
  await announce(running);
68604
69001
  if (watchKit && bus) {
68605
69002
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68606
- const live2 = join57(running.releaseRoot, running.app);
68607
- 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"));
68608
69005
  let current = manifestOf(live2);
68609
69006
  const failed2 = async (message) => {
68610
69007
  if (stopping) return;
@@ -68615,7 +69012,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68615
69012
  };
68616
69013
  rebuild = watchKit.serialize(async () => {
68617
69014
  const t0 = Date.now();
68618
- const next = mkdtempSync4(join57(tmpdir5(), "himi-run-build-"));
69015
+ const next = mkdtempSync4(join58(tmpdir5(), "himi-run-build-"));
68619
69016
  try {
68620
69017
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
68621
69018
  if (bres.compileErrors.length) {
@@ -68632,14 +69029,14 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68632
69029
  return;
68633
69030
  }
68634
69031
  if (stopping) return;
68635
- const fresh = manifestOf(join57(next, running.app));
69032
+ const fresh = manifestOf(join58(next, running.app));
68636
69033
  const diff = watchKit.diffReleases(current, fresh);
68637
69034
  const retired = `${live2}.retired-${Date.now()}`;
68638
69035
  renameSync6(live2, retired);
68639
69036
  try {
68640
- renameSync6(join57(next, running.app), live2);
69037
+ renameSync6(join58(next, running.app), live2);
68641
69038
  } catch {
68642
- cpSync3(join57(next, running.app), live2, { recursive: true });
69039
+ cpSync3(join58(next, running.app), live2, { recursive: true });
68643
69040
  }
68644
69041
  rmSync14(retired, { recursive: true, force: true });
68645
69042
  current = fresh;
@@ -68654,7 +69051,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68654
69051
  rmSync14(next, { recursive: true, force: true });
68655
69052
  }
68656
69053
  });
68657
- watcher = watchKit.watchContent(dir, () => rebuild?.());
69054
+ watcher2 = watchKit.watchContent(dir, () => rebuild?.());
68658
69055
  }
68659
69056
  } catch (e) {
68660
69057
  await retract().catch(() => {
@@ -68676,7 +69073,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68676
69073
  await new Promise((done) => {
68677
69074
  const stop = async () => {
68678
69075
  stopping = true;
68679
- watcher?.close();
69076
+ watcher2?.close();
68680
69077
  try {
68681
69078
  await rebuild?.stop();
68682
69079
  await retract();
@@ -69237,7 +69634,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69237
69634
  io.error(JSON.stringify({ error: `patch ${kind} needs --file`, usage: PATCH_USAGE }));
69238
69635
  return 2;
69239
69636
  }
69240
- if (!existsSync60(file)) {
69637
+ if (!existsSync61(file)) {
69241
69638
  io.error(JSON.stringify({ error: `no such file: ${file}` }));
69242
69639
  return 2;
69243
69640
  }
@@ -69293,7 +69690,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69293
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."
69294
69691
  };
69295
69692
  if (kind === "tokens" || kind === "app-config") {
69296
- const body = readFileSync72(file, "utf8");
69693
+ const body = readFileSync73(file, "utf8");
69297
69694
  const patch2 = kind === "tokens" ? { tokens: body } : { appConfig: body };
69298
69695
  return runReleasePatch({
69299
69696
  ...common,
@@ -69308,7 +69705,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69308
69705
  io.error(JSON.stringify({ error: "patch screen needs a <screenId>", usage: PATCH_USAGE }));
69309
69706
  return 2;
69310
69707
  }
69311
- const descriptor = readFileSync72(file, "utf8");
69708
+ const descriptor = readFileSync73(file, "utf8");
69312
69709
  try {
69313
69710
  JSON.parse(descriptor);
69314
69711
  } catch (e) {
@@ -69348,7 +69745,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69348
69745
  extra: { kind, screen: id, file: { path: file, bytes: Buffer.byteLength(descriptor, "utf8") } }
69349
69746
  });
69350
69747
  }
69351
- const bytes2 = readFileSync72(file);
69748
+ const bytes2 = readFileSync73(file);
69352
69749
  const sha = createHash27("sha256").update(bytes2).digest("hex");
69353
69750
  const forScreens = (str6(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
69354
69751
  if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
@@ -69492,7 +69889,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69492
69889
  }
69493
69890
  let def;
69494
69891
  try {
69495
- 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);
69496
69893
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
69497
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}`] }));
69498
69895
  return 2;
@@ -69956,7 +70353,7 @@ ${label2}`);
69956
70353
  const platform = str6(flags.platform)?.split(",").map((p) => p.trim()).filter(Boolean);
69957
70354
  {
69958
70355
  const distDir = contentDir(flags);
69959
- if (existsSync60(join57(distDir, "himalaya.content.json"))) {
70356
+ if (existsSync61(join58(distDir, "himalaya.content.json"))) {
69960
70357
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
69961
70358
  const errors = issues.filter((i) => i.severity === "error");
69962
70359
  for (const i of issues.filter((i2) => i2.severity === "warning")) {
@@ -70225,7 +70622,7 @@ ${label2}`);
70225
70622
  const dpxPath = dpxSessionTokenPath();
70226
70623
  let dpx;
70227
70624
  try {
70228
- dpx = readFileSync72(dpxPath, "utf8").trim();
70625
+ dpx = readFileSync73(dpxPath, "utf8").trim();
70229
70626
  } catch {
70230
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` }));
70231
70628
  return 1;
@@ -70627,7 +71024,7 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
70627
71024
  const path2 = dpxSessionTokenPath();
70628
71025
  let raw;
70629
71026
  try {
70630
- raw = readFileSync72(path2, "utf8");
71027
+ raw = readFileSync73(path2, "utf8");
70631
71028
  } catch {
70632
71029
  io.error(JSON.stringify({ error: `no DPX BO session at ${path2}` }));
70633
71030
  return 1;