@wix/himalaya-cli 0.1022.0 → 0.1023.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -6004,6 +6004,22 @@ var init_validator = __esm({
6004
6004
  enum: ["selected", "disabled", "updates_frequently", "plays_sound", "starts_media"]
6005
6005
  }
6006
6006
  },
6007
+ // RFC 0058 §4 — focus and announcement (core.proto Modifiers 70–73). `modifiers`
6008
+ // is `additionalProperties: false`, so a descriptor carrying one of these would be
6009
+ // REJECTED on every arm at once until the validator knows it — which makes this
6010
+ // part of the vocabulary rather than a downstream consumer of it.
6011
+ focusOrder: { type: "integer" },
6012
+ focusGroup: { type: "boolean" },
6013
+ initialFocus: { type: "boolean" },
6014
+ announce: {
6015
+ type: "object",
6016
+ additionalProperties: false,
6017
+ properties: {
6018
+ politeness: { type: "string", enum: ["polite", "assertive"] },
6019
+ text: { type: "string" },
6020
+ trigger: { type: "string", enum: ["on_change", "on_appear", "both", "none"] }
6021
+ }
6022
+ },
6007
6023
  // #1052 — explicit drop shadow (colour token + radius + offset), alongside the `shadow` token form.
6008
6024
  boxShadow: {
6009
6025
  type: "object",
@@ -8851,6 +8867,7 @@ var init_validate_descriptor_core = __esm({
8851
8867
  scrollToDatePath: "string",
8852
8868
  stickyHeaders: "boolean"
8853
8869
  },
8870
+ // RFC 0058 §4 — `modal` is the focus-trap tri-state on each of the four overlays.
8854
8871
  AlertDialog: {
8855
8872
  cancelAction: "any",
8856
8873
  cancelLabel: "string",
@@ -8858,6 +8875,7 @@ var init_validate_descriptor_core = __esm({
8858
8875
  confirmLabel: "string",
8859
8876
  destructive: "boolean",
8860
8877
  message: "string",
8878
+ modal: "boolean",
8861
8879
  title: "string"
8862
8880
  },
8863
8881
  Avatar: { imageUrl: "string", initials: "string", size: "number" },
@@ -8887,6 +8905,7 @@ var init_validate_descriptor_core = __esm({
8887
8905
  detents: "array",
8888
8906
  dismissible: "boolean",
8889
8907
  edge: "string",
8908
+ modal: "boolean",
8890
8909
  onDismiss: "any",
8891
8910
  presented: "boolean",
8892
8911
  presentedPath: "string",
@@ -9170,7 +9189,7 @@ var init_validate_descriptor_core = __esm({
9170
9189
  startIndex: "number",
9171
9190
  startPositionSeconds: "number"
9172
9191
  },
9173
- Menu: { anchor: "any", items: "array", itemsPath: "string", style: "string" },
9192
+ Menu: { anchor: "any", items: "array", itemsPath: "string", modal: "boolean", style: "string" },
9174
9193
  MonthCalendar: {
9175
9194
  decorations: "array",
9176
9195
  decorationsPath: "string",
@@ -9223,6 +9242,7 @@ var init_validate_descriptor_core = __esm({
9223
9242
  anchor: "any",
9224
9243
  content: "any",
9225
9244
  edge: "string",
9245
+ modal: "boolean",
9226
9246
  onDismiss: "any",
9227
9247
  presented: "boolean",
9228
9248
  presentedPath: "string"
@@ -9653,6 +9673,7 @@ var init_validate_descriptor_core = __esm({
9653
9673
  "blur",
9654
9674
  "cornerRadius",
9655
9675
  "enterDelayMs",
9676
+ "focusOrder",
9656
9677
  "headingLevel",
9657
9678
  "height",
9658
9679
  "maxWidth",
@@ -17303,6 +17324,21 @@ function absoluteUrlRejection(path2) {
17303
17324
  }
17304
17325
  return OWNER_SCOPED_WIX_HOST.test(host) ? "owner-only" : "open-proxy";
17305
17326
  }
17327
+ async function mintAnonymousVisitor(clientId, fetchImpl = fetch) {
17328
+ const res = await fetchImpl(`${WIX_API}/oauth2/token`, {
17329
+ method: "POST",
17330
+ headers: { "Content-Type": "application/json" },
17331
+ body: JSON.stringify({ clientId, grantType: "anonymous" })
17332
+ });
17333
+ const text5 = await res.text();
17334
+ let j = {};
17335
+ try {
17336
+ j = text5 ? JSON.parse(text5) : {};
17337
+ } catch {
17338
+ }
17339
+ if (res.status !== 200 || !j.access_token) throw new Error(`visitor mint failed (${res.status})`);
17340
+ return j.access_token;
17341
+ }
17306
17342
  async function wixVisitorCall(method, path2, body, token, fetchImpl = fetch, forwardedHeaders = {}) {
17307
17343
  if (/^[a-z][a-z0-9+.-]*:\/\//i.test(path2)) throw new Error("absolute URLs are not allowed");
17308
17344
  const url = `${WIX_API}${path2.startsWith("/") ? path2 : `/${path2}`}`;
@@ -17379,43 +17415,13 @@ function wixClientIdOf(appConfigJson) {
17379
17415
  if (typeof clientId !== "string" || !clientId) return { error: "no_client_id" };
17380
17416
  return { clientId };
17381
17417
  }
17382
- var WIX_API, ONE_APP_SESSION, OWNER_SCOPED_WIX_HOST, VisitorTokenCache, WIX_FORWARDABLE_HEADERS, OWNER_REFRESH_MARGIN_MS, OWNER_RENEW_MIN_AGE_MS, OWNER_MINT_TIMEOUT_MS, OwnerInstanceCache, OWNER_SITES_QUERY_PATH, OWNER_SITES_COUNT_PATH, OwnerSitesCache;
17418
+ var WIX_API, ONE_APP_SESSION, OWNER_SCOPED_WIX_HOST, WIX_FORWARDABLE_HEADERS, OWNER_REFRESH_MARGIN_MS, OWNER_RENEW_MIN_AGE_MS, OWNER_MINT_TIMEOUT_MS, OwnerInstanceCache, OWNER_SITES_QUERY_PATH, OWNER_SITES_COUNT_PATH, OwnerSitesCache;
17383
17419
  var init_wix_bridge = __esm({
17384
17420
  "../../core/serve-runtime/src/wix-bridge.ts"() {
17385
17421
  init_sha256();
17386
17422
  WIX_API = "https://www.wixapis.com";
17387
17423
  ONE_APP_SESSION = "https://apps.wix.com/_api/one-app-session-web/v3/businesses";
17388
17424
  OWNER_SCOPED_WIX_HOST = /(^|\.)wix\.com$/i;
17389
- VisitorTokenCache = class {
17390
- constructor(fetchImpl = fetch) {
17391
- this.fetchImpl = fetchImpl;
17392
- }
17393
- cache = /* @__PURE__ */ new Map();
17394
- async token(clientId, now = Date.now()) {
17395
- const hit = this.cache.get(clientId);
17396
- if (hit && hit.expiresAt > now) return hit.token;
17397
- const res = await this.fetchImpl(`${WIX_API}/oauth2/token`, {
17398
- method: "POST",
17399
- headers: { "Content-Type": "application/json" },
17400
- body: JSON.stringify({ clientId, grantType: "anonymous" })
17401
- });
17402
- const text5 = await res.text();
17403
- let j = {};
17404
- try {
17405
- j = text5 ? JSON.parse(text5) : {};
17406
- } catch {
17407
- }
17408
- if (res.status !== 200 || !j.access_token)
17409
- throw new Error(`visitor mint failed (${res.status})`);
17410
- const ttlMs = Math.max(60, (j.expires_in ?? 4 * 3600) - 60) * 1e3;
17411
- this.cache.set(clientId, { token: j.access_token, expiresAt: now + ttlMs });
17412
- return j.access_token;
17413
- }
17414
- /** Drop a cached token (e.g. after a 401 from wixapis) so the next call re-mints. */
17415
- invalidate(clientId) {
17416
- this.cache.delete(clientId);
17417
- }
17418
- };
17419
17425
  WIX_FORWARDABLE_HEADERS = /* @__PURE__ */ new Set([
17420
17426
  "x-wix-language",
17421
17427
  "x-wix-linguist",
@@ -20030,6 +20036,7 @@ function shouldRevealError(args) {
20030
20036
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
20031
20037
  var init_validation = __esm({
20032
20038
  "../../core/ts/src/validation.ts"() {
20039
+ "use strict";
20033
20040
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
20034
20041
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
20035
20042
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -23473,6 +23480,7 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
23473
23480
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
23474
23481
  var init_worker_subtree = __esm({
23475
23482
  "../../core/ts/src/worker-subtree.ts"() {
23483
+ "use strict";
23476
23484
  init_worker_subtree_safe_types_generated();
23477
23485
  WORKER_SUBTREE_CEILINGS = Object.freeze({
23478
23486
  maxEncodedBytes: 65536,
@@ -24142,6 +24150,9 @@ function autoGrowBox(contentHeight, lineHeight, verticalPadding, maxLines) {
24142
24150
  const cap = Math.max(floor, lineHeight * lines + verticalPadding);
24143
24151
  return { height: Math.max(floor, Math.min(contentHeight, cap)), scrolls: contentHeight > cap };
24144
24152
  }
24153
+ function wrapModalMenu(modal, label2, placement, menu) {
24154
+ return modal ? /* @__PURE__ */ jsx6("div", { role: "dialog", "aria-modal": "true", "aria-label": label2, style: placement, children: menu }) : menu;
24155
+ }
24145
24156
  function bindMediaSlot(value, sessionId) {
24146
24157
  if (typeof value === "string") {
24147
24158
  const prefix = `$media:${sessionId}.`;
@@ -24269,11 +24280,13 @@ function withExtras(name, render) {
24269
24280
  }
24270
24281
  const roleIsSynthetic = next[SYNTHETIC_TAP_ROLE] === true;
24271
24282
  delete next[SYNTHETIC_TAP_ROLE];
24283
+ const tabIndexIsSynthetic = next[SYNTHETIC_TABINDEX] === true;
24284
+ delete next[SYNTHETIC_TABINDEX];
24272
24285
  if (additive) next.style = additive;
24273
24286
  const ownsInteractiveSemantics = isNativeInteractiveElement(el) || hasInteractiveReactDescendant(el);
24274
24287
  if (ownsInteractiveSemantics) {
24275
24288
  if (roleIsSynthetic) delete next.role;
24276
- delete next.tabIndex;
24289
+ if (tabIndexIsSynthetic) delete next.tabIndex;
24277
24290
  delete next.onKeyDown;
24278
24291
  }
24279
24292
  if (pass.style) next.style = { ...additive ?? prev.style ?? {}, ...pass.style };
@@ -24709,6 +24722,57 @@ function calKeyActivate(enabled, activate) {
24709
24722
  activate();
24710
24723
  };
24711
24724
  }
24725
+ function restoreFocusOnDismiss(scope, opener) {
24726
+ const Observer = document.defaultView?.MutationObserver;
24727
+ if (!Observer) return;
24728
+ presentedOpeners.set(scope, opener);
24729
+ if (presentedObserver) return;
24730
+ presentedObserver = new Observer(() => {
24731
+ for (const [s, o] of presentedOpeners) {
24732
+ if (s.isConnected) continue;
24733
+ presentedOpeners.delete(s);
24734
+ const active = document.activeElement;
24735
+ if ((active == null || active === document.body) && o.isConnected && typeof o.focus === "function") o.focus();
24736
+ }
24737
+ if (presentedOpeners.size === 0) {
24738
+ presentedObserver?.disconnect();
24739
+ presentedObserver = null;
24740
+ }
24741
+ });
24742
+ presentedObserver.observe(document.body, { childList: true, subtree: true });
24743
+ }
24744
+ function ensureInitialFocusRuntime() {
24745
+ if (initialFocusScheduled || typeof document === "undefined") return;
24746
+ initialFocusScheduled = true;
24747
+ setTimeout(() => {
24748
+ initialFocusScheduled = false;
24749
+ const targets = Array.from(document.querySelectorAll("[data-himalaya-initial-focus]"));
24750
+ const active = document.activeElement;
24751
+ const unplaced = active == null || active === document.body;
24752
+ const fresh = targets.filter((t) => !initialFocusSeen.has(t));
24753
+ let presented;
24754
+ let presentedScope = null;
24755
+ for (const t of fresh) {
24756
+ const scope = t.closest(PRESENTED_SCOPE);
24757
+ if (scope && !(active && scope.contains(active))) {
24758
+ presented = t;
24759
+ presentedScope = scope;
24760
+ }
24761
+ }
24762
+ for (const t of fresh) initialFocusSeen.add(t);
24763
+ const el = presented ?? (unplaced ? fresh[0] : void 0);
24764
+ if (el && typeof el.focus === "function") {
24765
+ if (presented && presentedScope && !unplaced && active instanceof HTMLElement) {
24766
+ restoreFocusOnDismiss(presentedScope, active);
24767
+ }
24768
+ el.focus();
24769
+ }
24770
+ }, 0);
24771
+ }
24772
+ function ariaModal(modal, dflt) {
24773
+ const on3 = modal === true || modal === false ? modal : dflt;
24774
+ return on3 ? "true" : void 0;
24775
+ }
24712
24776
  function applyA11ySemantics(node, ctx, extra) {
24713
24777
  const m = node?.modifiers;
24714
24778
  if (!m) return void 0;
@@ -24764,6 +24828,32 @@ function applyA11ySemantics(node, ctx, extra) {
24764
24828
  }
24765
24829
  }
24766
24830
  }
24831
+ const focusOrder = m.focusOrder ?? m.focus_order;
24832
+ if (typeof focusOrder === "number" && Number.isFinite(focusOrder)) {
24833
+ extra.tabIndex = Math.max(1, Math.trunc(focusOrder) + 1);
24834
+ }
24835
+ if ((m.focusGroup ?? m.focus_group) === true && extra.role == null) extra.role = "group";
24836
+ if ((m.initialFocus ?? m.initial_focus) === true) {
24837
+ extra["data-himalaya-initial-focus"] = "1";
24838
+ if (extra.tabIndex == null) {
24839
+ extra.tabIndex = -1;
24840
+ extra[SYNTHETIC_TABINDEX] = true;
24841
+ }
24842
+ ensureInitialFocusRuntime();
24843
+ }
24844
+ const announce = m.announce;
24845
+ if (announce && typeof announce === "object" && !Array.isArray(announce)) {
24846
+ const trigger = str7(announce.trigger) ?? "on_change";
24847
+ if (trigger === "none") {
24848
+ extra["aria-live"] = "off";
24849
+ } else {
24850
+ extra["aria-live"] = str7(announce.politeness) === "assertive" ? "assertive" : "polite";
24851
+ extra["aria-atomic"] = "true";
24852
+ extra["aria-relevant"] = trigger === "on_appear" ? "additions" : trigger === "both" ? "additions text" : "text";
24853
+ const text5 = str7(announce.text);
24854
+ if (text5 && extra["aria-label"] == null) extra["aria-label"] = text5;
24855
+ }
24856
+ }
24767
24857
  return hint;
24768
24858
  }
24769
24859
  function renderNodeInner(node, ctx) {
@@ -24856,7 +24946,10 @@ function renderNodeInner(node, ctx) {
24856
24946
  extra.role = "button";
24857
24947
  if (typeof el.type !== "string") extra[SYNTHETIC_TAP_ROLE] = true;
24858
24948
  }
24859
- extra.tabIndex = 0;
24949
+ if (extra.tabIndex == null) {
24950
+ extra.tabIndex = 0;
24951
+ if (typeof el.type !== "string") extra[SYNTHETIC_TABINDEX] = true;
24952
+ }
24860
24953
  const prevOnKeyDown = el.props.onKeyDown;
24861
24954
  extra.onKeyDown = (e) => {
24862
24955
  prevOnKeyDown?.(e);
@@ -24904,6 +24997,7 @@ function renderNodeInner(node, ctx) {
24904
24997
  if (composed) extra.style = composed;
24905
24998
  }
24906
24999
  }
25000
+ if (typeof el.type === "string") delete extra[SYNTHETIC_TABINDEX];
24907
25001
  let out = Object.keys(extra).length ? React5.cloneElement(el, extra) : el;
24908
25002
  if (a11yHint) {
24909
25003
  out = /* @__PURE__ */ jsx6(A11yHintDescription, { hint: a11yHint, idBase: componentId, children: out });
@@ -25333,7 +25427,7 @@ function renderInner(node, ctx) {
25333
25427
  case "Popover": {
25334
25428
  return /* @__PURE__ */ jsxs3("div", { style: { display: "inline-flex", flexDirection: "column", alignItems: "flex-start", gap: 6, ...wrap }, children: [
25335
25429
  props.anchor ? renderNode(props.anchor, ctx) : null,
25336
- /* @__PURE__ */ jsx6("div", { role: "dialog", "aria-label": props.title != null ? asBoundText(props.title, ctx) : "Popover", style: { minWidth: 160, maxWidth: 280, background: C("surfacePrimary", "#fff"), border: `1px solid ${C("border", "#e3e3e3")}`, borderRadius: 12, boxShadow: "0 6px 20px rgba(0,0,0,.14)", padding: 12 }, children: props.content ? renderNode(props.content, ctx) : null })
25430
+ /* @__PURE__ */ jsx6("div", { role: "dialog", "aria-modal": ariaModal(props.modal, false), "aria-label": props.title != null ? asBoundText(props.title, ctx) : "Popover", style: { minWidth: 160, maxWidth: 280, background: C("surfacePrimary", "#fff"), border: `1px solid ${C("border", "#e3e3e3")}`, borderRadius: 12, boxShadow: "0 6px 20px rgba(0,0,0,.14)", padding: 12 }, children: props.content ? renderNode(props.content, ctx) : null })
25337
25431
  ] });
25338
25432
  }
25339
25433
  // ───────────── chips & selection (RFC 0030) ─────────────
@@ -25601,7 +25695,7 @@ function renderInner(node, ctx) {
25601
25695
  ] });
25602
25696
  }
25603
25697
  case "AlertDialog":
25604
- return /* @__PURE__ */ jsxs3("div", { role: "alertdialog", "aria-modal": "true", "aria-label": props.title != null ? asBoundText(props.title, ctx) : "Alert", style: { background: C("surfacePrimary", "#fff"), borderRadius: ctx.radius("lg", 14), padding: 18, width: "100%", boxSizing: "border-box", border: `1px solid ${C("border", "#e3e3e3")}`, boxShadow: "0 12px 30px -12px rgba(0,0,0,.4)", ...wrap }, children: [
25698
+ return /* @__PURE__ */ jsxs3("div", { role: "alertdialog", "aria-modal": ariaModal(props.modal, true), "aria-label": props.title != null ? asBoundText(props.title, ctx) : "Alert", style: { background: C("surfacePrimary", "#fff"), borderRadius: ctx.radius("lg", 14), padding: 18, width: "100%", boxSizing: "border-box", border: `1px solid ${C("border", "#e3e3e3")}`, boxShadow: "0 12px 30px -12px rgba(0,0,0,.4)", ...wrap }, children: [
25605
25699
  /* @__PURE__ */ jsx6("div", { style: { fontFamily: bodyFont(ctx), fontWeight: 700, fontSize: 17, color: C("textPrimary", "#111") }, children: txt(props.title, ctx) }),
25606
25700
  /* @__PURE__ */ jsx6("div", { style: { fontFamily: bodyFont(ctx), fontSize: 14, lineHeight: 1.45, color: C("textSecondary", "#666"), margin: "6px 0 16px" }, children: txt(props.message, ctx) }),
25607
25701
  /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: 10 }, children: [
@@ -25624,7 +25718,7 @@ function renderInner(node, ctx) {
25624
25718
  }
25625
25719
  }
25626
25720
  }
25627
- var useIsomorphicLayoutEffect, ResponsiveGrid, SPLIT_EXPANDED_MIN_WIDTH, SPLIT_COLUMN_GAP, ResponsiveSplitView, ABSOLUTE_URL_RE, appearKeyframesInjected, pressStylesInjected, FOCUS_RING_CSS, focusStylesInjected, HLIST_CHILD_CSS, hListStylesInjected, listAnimateRuntimeInstalled, transitionKeyframesInjected, countUpRuntimeInjected, MEASURE_PREFIX, measureRuntimeInjected, revealRuntimeInjected, vectorAnimStylesInjected, scrollProgressRuntimeInjected, confettiRuntimeInjected, lottieRuntimeInjected, riveRuntimeInjected, gradientKeyframesInjected, parallaxRuntimeInjected, spinKeyframesInjected, marqueeKeyframesInjected, alignItems, zJustifyItems, hAlignItems, bodyFont, kids, rowKids, staggeredKids, asTextL10n, asBoundText, useBoundString, InterpolatedTextView, txt, selectFragmentCase, fragmentValueHash, fragmentSlotContent, FragmentSlotNode, WorkerSubtreeNode, resolveItemDeep2, resolveSwipeActions, resolveModBindings, resolvedComponentId, directServiceBindings, ReactiveDirectScalars, HiddenGate, KEYBOARD_INPUT_MODES, AUTOCAPITALIZE_VALUES, ENTER_KEY_HINTS, AUTOGROW_CEILING, EditableField, MenuNode, LoadMoreSentinel, FieldErrorNode, FormView, MediaSlotProvider, MEDIA_ITEM_BINDABLE, MediaPlayerView, VideoNode, PdfViewNode, WebViewNode, InteractivePicker, InteractiveDatePicker, InteractiveSearchField, InteractiveStepper, SYNTHETIC_TAP_ROLE, INFERRED_DECORATIVE, ICON_GLYPH, INTERACTIVE_ROLES, ALWAYS_INTERACTIVE_COMPONENT_TYPES, controlNode, ToggleNode, SliderNode, SelectionControlNode, TabsNode, SegmentedControlNode, DisclosureGroupNode, GridNode, SplitViewNode, TextNode, ImageNode, AvatarNode, IconNode, ButtonNode, OtpInputNode, ProgressBarNode, ActivityIndicatorNode, BottomSheetNode, ChipsInputNode, ChipNode, TableNode, GaugeRing, GaugeNode, RATING_METRICS, ratingMetrics, RatingNode, AvatarStripNode, CHART_STROKE_CYCLE, chartStrokeAt, chartBarCenter, ChartNode, donutSliceOpacity, DonutChartNode, LegendMarker, LegendNode, cellAlign, DataTableNode, voiceNoteState, voiceNoteDelivery, VOICE_NOTE_DEFAULT_BARS, clampBarCount, VoiceNoteNode, TimelineNode, WheelPickerNode, ColorPickerNode, FilterChipsNode, TILE_PX, MAX_MERCATOR_LAT, DEFAULT_TILE_MAX_ZOOM, CIRCLE_SEGMENTS, METRES_PER_DEG_LAT, MARKER_HIT_RADIUS, SHAPE_HIT_RADIUS, MapNode, CollapsingHeaderNode, UnsupportedOnSurface, CALENDAR_DATE_ONLY_RE, CAL_DEFAULT_MAX_DOTS, CAL_AGENDA_INITIAL_DAYS, CAL_AGENDA_PAGE_DAYS, CAL_DEFAULT_SNAP_MINUTES, CAL_UNASSIGNED_LANE_ID, MonthCalendarNode, TimeGridNode, AgendaNode, ARIA_ROLE, A11yHintDescription, WebRenderer;
25721
+ var useIsomorphicLayoutEffect, ResponsiveGrid, SPLIT_EXPANDED_MIN_WIDTH, SPLIT_COLUMN_GAP, ResponsiveSplitView, ABSOLUTE_URL_RE, appearKeyframesInjected, pressStylesInjected, FOCUS_RING_CSS, focusStylesInjected, HLIST_CHILD_CSS, hListStylesInjected, listAnimateRuntimeInstalled, transitionKeyframesInjected, countUpRuntimeInjected, MEASURE_PREFIX, measureRuntimeInjected, revealRuntimeInjected, vectorAnimStylesInjected, scrollProgressRuntimeInjected, confettiRuntimeInjected, lottieRuntimeInjected, riveRuntimeInjected, gradientKeyframesInjected, parallaxRuntimeInjected, spinKeyframesInjected, marqueeKeyframesInjected, alignItems, zJustifyItems, hAlignItems, bodyFont, kids, rowKids, staggeredKids, asTextL10n, asBoundText, useBoundString, InterpolatedTextView, txt, selectFragmentCase, fragmentValueHash, fragmentSlotContent, FragmentSlotNode, WorkerSubtreeNode, resolveItemDeep2, resolveSwipeActions, resolveModBindings, resolvedComponentId, directServiceBindings, ReactiveDirectScalars, HiddenGate, KEYBOARD_INPUT_MODES, AUTOCAPITALIZE_VALUES, ENTER_KEY_HINTS, AUTOGROW_CEILING, EditableField, MenuNode, LoadMoreSentinel, FieldErrorNode, FormView, MediaSlotProvider, MEDIA_ITEM_BINDABLE, MediaPlayerView, VideoNode, PdfViewNode, WebViewNode, InteractivePicker, InteractiveDatePicker, InteractiveSearchField, InteractiveStepper, SYNTHETIC_TAP_ROLE, SYNTHETIC_TABINDEX, INFERRED_DECORATIVE, ICON_GLYPH, INTERACTIVE_ROLES, ALWAYS_INTERACTIVE_COMPONENT_TYPES, controlNode, ToggleNode, SliderNode, SelectionControlNode, TabsNode, SegmentedControlNode, DisclosureGroupNode, GridNode, SplitViewNode, TextNode, ImageNode, AvatarNode, IconNode, ButtonNode, OtpInputNode, ProgressBarNode, ActivityIndicatorNode, BottomSheetNode, ChipsInputNode, ChipNode, TableNode, GaugeRing, GaugeNode, RATING_METRICS, ratingMetrics, RatingNode, AvatarStripNode, CHART_STROKE_CYCLE, chartStrokeAt, chartBarCenter, ChartNode, donutSliceOpacity, DonutChartNode, LegendMarker, LegendNode, cellAlign, DataTableNode, voiceNoteState, voiceNoteDelivery, VOICE_NOTE_DEFAULT_BARS, clampBarCount, VoiceNoteNode, TimelineNode, WheelPickerNode, ColorPickerNode, FilterChipsNode, TILE_PX, MAX_MERCATOR_LAT, DEFAULT_TILE_MAX_ZOOM, CIRCLE_SEGMENTS, METRES_PER_DEG_LAT, MARKER_HIT_RADIUS, SHAPE_HIT_RADIUS, MapNode, CollapsingHeaderNode, UnsupportedOnSurface, CALENDAR_DATE_ONLY_RE, CAL_DEFAULT_MAX_DOTS, CAL_AGENDA_INITIAL_DAYS, CAL_AGENDA_PAGE_DAYS, CAL_DEFAULT_SNAP_MINUTES, CAL_UNASSIGNED_LANE_ID, MonthCalendarNode, TimeGridNode, AgendaNode, ARIA_ROLE, initialFocusScheduled, initialFocusSeen, PRESENTED_SCOPE, presentedOpeners, presentedObserver, A11yHintDescription, WebRenderer;
25628
25722
  var init_renderNode = __esm({
25629
25723
  "../../core/web-renderer/src/renderNode.tsx"() {
25630
25724
  "use strict";
@@ -25989,7 +26083,9 @@ var init_renderNode = __esm({
25989
26083
  AUTOCAPITALIZE_VALUES = /* @__PURE__ */ new Set(["none", "words", "sentences", "characters"]);
25990
26084
  ENTER_KEY_HINTS = /* @__PURE__ */ new Set(["done", "go", "next", "send", "search"]);
25991
26085
  AUTOGROW_CEILING = 12;
25992
- EditableField = ({ props, ctx, wrap, multiline, id: htmlId, "data-component-id": componentId, "data-component-type": componentType }) => {
26086
+ EditableField = ({ props, ctx, wrap, multiline, id: htmlId, "data-component-id": componentId, "data-component-type": componentType, tabIndex: tabIndexProp, "data-hm-synthetic-tabindex": tabIndexIsSynthetic, "data-himalaya-initial-focus": initialFocusMarker, "aria-live": ariaLive, "aria-atomic": ariaAtomic, "aria-relevant": ariaRelevant, "aria-label": ariaLabelProp }) => {
26087
+ const tabIndex = tabIndexIsSynthetic === true ? void 0 : tabIndexProp;
26088
+ const a11yFwd = { tabIndex, "data-himalaya-initial-focus": initialFocusMarker, "aria-live": ariaLive, "aria-atomic": ariaAtomic, "aria-relevant": ariaRelevant };
25993
26089
  const C = ctx.color;
25994
26090
  const ctrl = props.text ?? props.value;
25995
26091
  const cfg = ctrl && typeof ctrl === "object" ? ctrl : {};
@@ -26134,7 +26230,7 @@ var init_renderNode = __esm({
26134
26230
  /* @__PURE__ */ jsxs3("div", { style: box, children: [
26135
26231
  props.leadingIcon && accIcon(props.leadingIcon),
26136
26232
  props.prefix && /* @__PURE__ */ jsx6("span", { style: { color: muted, flex: "none" }, children: String(props.prefix) }),
26137
- multilineBox ? /* @__PURE__ */ jsx6("textarea", { "data-component-id": componentId, id: htmlId, "data-component-type": componentType, ref: inputRef, value: local, onChange: onInput, onFocus: onFieldFocus, onBlur: onFieldBlur, placeholder: placeholder2, rows: 2, "aria-label": placeholder2, "aria-invalid": errored || void 0, "aria-describedby": errorId, inputMode, autoCapitalize, maxLength, autoCorrect: suggestOff ? "off" : suggestOn ? "on" : void 0, spellCheck: suggestOff ? false : suggestOn ? true : void 0, autoComplete: suggestOff ? "off" : void 0, style: inputBase }) : /* @__PURE__ */ jsx6("input", { "data-component-id": componentId, id: htmlId, "data-component-type": componentType, ref: inputRef, type: secure ? "password" : "text", value: local, onChange: onInput, onKeyDown, onFocus: onFieldFocus, onBlur: onFieldBlur, placeholder: placeholder2, "aria-label": placeholder2, "aria-invalid": errored || void 0, "aria-describedby": errorId, inputMode, autoCapitalize, maxLength, autoCorrect: suggestOff ? "off" : suggestOn ? "on" : void 0, spellCheck: suggestOff ? false : suggestOn ? true : void 0, autoComplete: suggestOff ? "off" : void 0, enterKeyHint, style: inputBase }),
26233
+ multilineBox ? /* @__PURE__ */ jsx6("textarea", { "data-component-id": componentId, id: htmlId, "data-component-type": componentType, ...a11yFwd, ref: inputRef, value: local, onChange: onInput, onFocus: onFieldFocus, onBlur: onFieldBlur, placeholder: placeholder2, rows: 2, "aria-label": ariaLabelProp ?? placeholder2, "aria-invalid": errored || void 0, "aria-describedby": errorId, inputMode, autoCapitalize, maxLength, autoCorrect: suggestOff ? "off" : suggestOn ? "on" : void 0, spellCheck: suggestOff ? false : suggestOn ? true : void 0, autoComplete: suggestOff ? "off" : void 0, style: inputBase }) : /* @__PURE__ */ jsx6("input", { "data-component-id": componentId, id: htmlId, "data-component-type": componentType, ...a11yFwd, ref: inputRef, type: secure ? "password" : "text", value: local, onChange: onInput, onKeyDown, onFocus: onFieldFocus, onBlur: onFieldBlur, placeholder: placeholder2, "aria-label": ariaLabelProp ?? placeholder2, "aria-invalid": errored || void 0, "aria-describedby": errorId, inputMode, autoCapitalize, maxLength, autoCorrect: suggestOff ? "off" : suggestOn ? "on" : void 0, spellCheck: suggestOff ? false : suggestOn ? true : void 0, autoComplete: suggestOff ? "off" : void 0, enterKeyHint, style: inputBase }),
26138
26234
  props.suffix && /* @__PURE__ */ jsx6("span", { style: { color: muted, flex: "none" }, children: String(props.suffix) }),
26139
26235
  props.clearButton && local && /* @__PURE__ */ jsx6("span", { onClick: () => {
26140
26236
  setLocal("");
@@ -26166,15 +26262,47 @@ var init_renderNode = __esm({
26166
26262
  document.removeEventListener("keydown", onKey);
26167
26263
  };
26168
26264
  }, [open]);
26265
+ const modal = ariaModal(props.modal, false) === "true";
26266
+ const anchorLabel = props.anchor?.modifiers?.accessibilityLabel || "More actions";
26267
+ const popupPlacement = { position: "absolute", top: "100%", right: 0, zIndex: 20, marginTop: 4 };
26268
+ const anchorRef = React5.useRef(null);
26269
+ const menuRef = React5.useRef(null);
26270
+ const itemsKey = items.map((it, i) => `${it?.id ?? i}:${it?.disabled ? 0 : 1}`).join("|");
26271
+ React5.useEffect(() => {
26272
+ if (!open || !modal) return;
26273
+ const menu = menuRef.current;
26274
+ if (!menu || menu.contains(document.activeElement)) return;
26275
+ (menu.querySelector('[role="menuitem"][tabindex="0"]') ?? menu).focus();
26276
+ }, [open, modal, itemsKey]);
26277
+ React5.useEffect(() => {
26278
+ if (!open || !modal) return;
26279
+ return () => {
26280
+ const active = document.activeElement;
26281
+ if (active == null || active === document.body || !document.contains(active)) anchorRef.current?.focus();
26282
+ };
26283
+ }, [open, modal]);
26284
+ const trapTab = (e) => {
26285
+ if (!modal || e.key !== "Tab") return;
26286
+ const items2 = Array.from(menuRef.current?.querySelectorAll('[role="menuitem"][tabindex="0"]') ?? []);
26287
+ if (!items2.length) {
26288
+ e.preventDefault();
26289
+ return;
26290
+ }
26291
+ const at = items2.indexOf(document.activeElement);
26292
+ const next = e.shiftKey ? at <= 0 ? items2.length - 1 : at - 1 : at < 0 || at === items2.length - 1 ? 0 : at + 1;
26293
+ e.preventDefault();
26294
+ items2[next].focus();
26295
+ };
26169
26296
  return /* @__PURE__ */ jsxs3("span", { ref: hostRef, style: { position: "relative", display: "inline-flex", ...wrap }, children: [
26170
26297
  /* @__PURE__ */ jsx6(
26171
26298
  "span",
26172
26299
  {
26300
+ ref: anchorRef,
26173
26301
  role: "button",
26174
26302
  tabIndex: 0,
26175
26303
  "aria-haspopup": "menu",
26176
26304
  "aria-expanded": open,
26177
- "aria-label": props.anchor?.modifiers?.accessibilityLabel || "More actions",
26305
+ "aria-label": anchorLabel,
26178
26306
  onClick: (e) => {
26179
26307
  e.stopPropagation();
26180
26308
  setOpen((v) => !v);
@@ -26190,11 +26318,14 @@ var init_renderNode = __esm({
26190
26318
  children: props.anchor ? renderNode(props.anchor, ctx) : iconSvg("more", { size: 20, color: C("textSecondary", "#636b78") })
26191
26319
  }
26192
26320
  ),
26193
- open && /* @__PURE__ */ jsx6(
26321
+ open && wrapModalMenu(modal, anchorLabel, popupPlacement, /* @__PURE__ */ jsx6(
26194
26322
  "div",
26195
26323
  {
26324
+ ref: menuRef,
26196
26325
  role: "menu",
26197
- style: { position: "absolute", top: "100%", right: 0, zIndex: 20, marginTop: 4, display: "flex", flexDirection: "column", minWidth: 180, background: C("surfacePrimary", "#fff"), border: `1px solid ${C("border", "#e3e3e3")}`, borderRadius: 12, boxShadow: "0 6px 20px rgba(0,0,0,.12)", overflow: "hidden", padding: 4 },
26326
+ tabIndex: modal ? -1 : void 0,
26327
+ onKeyDown: trapTab,
26328
+ style: { ...modal ? {} : popupPlacement, display: "flex", flexDirection: "column", minWidth: 180, background: C("surfacePrimary", "#fff"), border: `1px solid ${C("border", "#e3e3e3")}`, borderRadius: 12, boxShadow: "0 6px 20px rgba(0,0,0,.12)", overflow: "hidden", padding: 4 },
26198
26329
  children: items.map((it, i) => /* @__PURE__ */ jsxs3(
26199
26330
  "div",
26200
26331
  {
@@ -26223,7 +26354,7 @@ var init_renderNode = __esm({
26223
26354
  it?.id ?? i
26224
26355
  ))
26225
26356
  }
26226
- )
26357
+ ))
26227
26358
  ] });
26228
26359
  });
26229
26360
  LoadMoreSentinel = ({ ctx, pagination }) => {
@@ -27100,6 +27231,7 @@ var init_renderNode = __esm({
27100
27231
  ] });
27101
27232
  });
27102
27233
  SYNTHETIC_TAP_ROLE = "data-hm-synthetic-role";
27234
+ SYNTHETIC_TABINDEX = "data-hm-synthetic-tabindex";
27103
27235
  INFERRED_DECORATIVE = "data-hm-inferred-decorative";
27104
27236
  ICON_GLYPH = "data-hm-icon-glyph";
27105
27237
  INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
@@ -27463,7 +27595,7 @@ var init_renderNode = __esm({
27463
27595
  if (gated && presented !== true && presented !== "true") return null;
27464
27596
  const grabber = props.showGrabber !== false;
27465
27597
  const sheet = idiomControls(ctx.idiom).sheet;
27466
- return /* @__PURE__ */ jsx6("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,.35)", zIndex: 50, display: "flex", flexDirection: "column", justifyContent: "flex-end", ...wrap }, children: /* @__PURE__ */ jsxs3("div", { role: "dialog", "aria-modal": "true", "aria-label": props.title != null ? sheetTitle : "Sheet", style: { width: "100%", background: C("surfacePrimary", "#fff"), borderRadius: `${sheet.cornerRadius}px ${sheet.cornerRadius}px 0 0`, boxShadow: "0 -4px 24px rgba(0,0,0,.12)", overflow: "hidden" }, children: [
27598
+ return /* @__PURE__ */ jsx6("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,.35)", zIndex: 50, display: "flex", flexDirection: "column", justifyContent: "flex-end", ...wrap }, children: /* @__PURE__ */ jsxs3("div", { role: "dialog", "aria-modal": ariaModal(props.modal, true), "aria-label": props.title != null ? sheetTitle : "Sheet", style: { width: "100%", background: C("surfacePrimary", "#fff"), borderRadius: `${sheet.cornerRadius}px ${sheet.cornerRadius}px 0 0`, boxShadow: "0 -4px 24px rgba(0,0,0,.12)", overflow: "hidden" }, children: [
27467
27599
  grabber && /* @__PURE__ */ jsx6("div", { style: { display: "flex", justifyContent: "center", padding: "8px 0 4px" }, children: /* @__PURE__ */ jsx6("div", { style: { width: sheet.grabberWidth, height: sheet.grabberHeight, borderRadius: 999, background: C("surfaceTertiary", "#d8dce2") } }) }),
27468
27600
  /* @__PURE__ */ jsx6("div", { style: { padding: "8px 16px 16px" }, children: props.content ? renderNode(props.content, ctx) : null })
27469
27601
  ] }) });
@@ -29001,6 +29133,11 @@ var init_renderNode = __esm({
29001
29133
  grid: "grid",
29002
29134
  gridcell: "gridcell"
29003
29135
  };
29136
+ initialFocusScheduled = false;
29137
+ initialFocusSeen = /* @__PURE__ */ new WeakSet();
29138
+ PRESENTED_SCOPE = '[aria-modal="true"], [role="dialog"], [role="alertdialog"], [role="menu"]';
29139
+ presentedOpeners = /* @__PURE__ */ new Map();
29140
+ presentedObserver = null;
29004
29141
  A11yHintDescription = ({ hint, idBase, children, ...forwarded }) => {
29005
29142
  const descId = `${idBase ?? "hm"}__hint${React5.useId()}`;
29006
29143
  const own3 = children.props;
@@ -39279,11 +39416,22 @@ function liveRun(dir) {
39279
39416
  const build2 = obj(rec?.build);
39280
39417
  try {
39281
39418
  process.kill(pid, 0);
39419
+ const at = text(build2?.at);
39282
39420
  return {
39283
39421
  url,
39422
+ // Which run this is: a restart on the same port serves the same URL from a new process.
39423
+ runId: `${pid}@${text(rec?.startedAt) ?? ""}`,
39284
39424
  buildError: build2?.ok === false ? text(build2.error) ?? "the last rebuild failed" : null,
39285
39425
  // Only `--watch` wrote this record before plain runs did, so a record without the key is one.
39286
- watching: rec?.watching !== false
39426
+ watching: rec?.watching !== false,
39427
+ lastBuild: build2 && at ? {
39428
+ at,
39429
+ ok: build2.ok !== false,
39430
+ ms: typeof build2.ms === "number" && Number.isFinite(build2.ms) ? Math.max(0, Math.round(build2.ms)) : null,
39431
+ screens: (Array.isArray(build2.screens) ? build2.screens : []).filter((x) => typeof x === "string").slice(0, 20),
39432
+ tokens: build2.tokens === true,
39433
+ config: build2.config === true
39434
+ } : null
39287
39435
  };
39288
39436
  } catch {
39289
39437
  return null;
@@ -39400,6 +39548,7 @@ function renderStudioHtml(initial) {
39400
39548
  <meta charset="utf-8">
39401
39549
  <meta name="viewport" content="width=device-width, initial-scale=1">
39402
39550
  <title>${escapeHtml(initial.app.name)} \xB7 build board</title>
39551
+ <link rel="icon" id="fav" href="data:,">
39403
39552
  <style>
39404
39553
  :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)}
39405
39554
  @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}}
@@ -39418,6 +39567,7 @@ h1{font-size:20px;font-weight:600;margin:0}.sub{color:var(--muted);font-size:13p
39418
39567
  h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin:0 0 10px}
39419
39568
  .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}
39420
39569
  .phone img,.phone iframe{width:100%;height:100%;object-fit:cover;object-position:top;border:0;display:block;cursor:zoom-in}
39570
+ .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}
39421
39571
  .cap{text-align:center;color:var(--muted);font-size:12px;margin-top:8px}
39422
39572
  .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}
39423
39573
  .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)}
@@ -39432,12 +39582,26 @@ h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;le
39432
39582
  .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}
39433
39583
  .empty{color:var(--faint);font-size:13px;padding:8px 0}
39434
39584
  #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}
39585
+ .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}}
39586
+ .changed .frame{animation:chg .9s ease both}@keyframes chg{35%{opacity:.25;transform:scale(.96)}100%{opacity:1;transform:none}}
39587
+ .step.fresh .bar{animation:fill .7s ease both;transform-origin:left}@keyframes fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
39588
+ .slidein{animation:slide .45s cubic-bezier(.2,.8,.2,1) both}@keyframes slide{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
39589
+ .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}}
39590
+ .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}}
39591
+ .icon,.step.cur .bar{transition:background-color .8s}.sw.pop span{animation:pop .6s ease both}@keyframes pop{50%{transform:scale(1.25)}}
39592
+ .quiet{color:var(--warn)}.quiet .dot{background:var(--warn)}.idle .dot{background:var(--faint)}
39593
+ .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)}
39594
+ #fx{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:8}
39595
+ @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}}
39596
+ @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}}
39435
39597
  </style>
39436
39598
  </head>
39437
39599
  <body>
39438
39600
  <div class="wrap">
39439
39601
  <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>
39440
- <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></div></header>
39602
+ <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>
39603
+ <div><span class="live idle" id="agent"><span class="dot"></span><span id="agentt">Waiting for the agent</span></span></div>
39604
+ <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>
39441
39605
  <div class="steps" id="steps"></div>
39442
39606
  <div class="card now"><div style="flex:1"><div class="meta" id="nowmeta">Now</div><div class="msg" id="nowmsg"></div></div></div>
39443
39607
  <div class="grid">
@@ -39450,6 +39614,7 @@ h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;le
39450
39614
  <div class="card section"><h2>Postcards</h2><ul class="log" id="log"></ul></div>
39451
39615
  </div></div></div>
39452
39616
  <div id="lb"><img alt=""></div>
39617
+ <canvas id="fx" aria-hidden="true"></canvas>
39453
39618
  <script>window.__himiInitial = ${scriptJson(initial)};</script>
39454
39619
  <script>
39455
39620
  (function(){
@@ -39464,27 +39629,33 @@ function zoomable(img){img.addEventListener("click",function(){var lb=$("lb");lb
39464
39629
  function img(src,cls){var i=el("img",cls);i.src=src;i.alt="";i.loading="lazy";zoomable(i);return i}
39465
39630
  function chip(txt,kind){return el("span","chip"+(kind?" "+kind:""),txt)}
39466
39631
  function render(s){
39467
- document.title=s.app.name+" \xB7 build board";$("name").textContent=s.app.name;
39632
+ 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;
39468
39633
  if(s.app.icon){$("icon").src=s.app.icon}else{$("icon").removeAttribute("src")}
39469
39634
  var steps=$("steps");clear(steps);var n=s.current?s.current.stepNumber:0;
39470
39635
  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)});
39471
39636
  var last=s.journal[0];
39472
39637
  $("nowmsg").textContent=last?last.message:"Waiting for the first postcard.";
39473
39638
  $("nowmeta").textContent=last?("Now \xB7 "+(last.step?("step "+last.stepNumber+" of 8, "+last.step+" \xB7 "):"")+ago(last.ts)):"Now";
39474
- var focus=$("focus");clear(focus);var film=$("film");clear(film);
39475
- if(s.live){$("focuslbl").textContent="Live preview";var f=el("div","phone");var fr=document.createElement("iframe");fr.src=s.live.url;fr.title="Live preview";f.appendChild(fr);focus.appendChild(f);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))}
39639
+ var focus=$("focus");var film=$("film");clear(film);
39640
+ /* The live frame is KEPT across renders while its URL and its run are unchanged (a restarted run on
39641
+ the same port is a different server, and the old frame would never see its builds). A new iframe is a full page
39642
+ load of the preview: every postcard, rebuild and the once-a-minute re-render used to throw away
39643
+ the in-place repaint, the preview's scroll and wherever the owner had navigated inside it. */
39644
+ 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;
39645
+ if(kept){while(focus.lastChild&&focus.lastChild!==kept.parentNode)focus.removeChild(focus.lastChild)}else clear(focus);
39646
+ 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))}
39476
39647
  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"))}
39477
39648
  else{$("focuslbl").textContent="Latest";focus.appendChild(el("div","empty","The first render appears here."))}
39478
39649
  if(s.focus&&s.focus.frames.length>1)s.focus.frames.forEach(function(fm){film.appendChild(img(fm.img))});
39479
39650
  var chips=$("chips");clear(chips);var h=s.health;
39480
39651
  if(h.validate)chips.appendChild(chip(h.validate.ok?"Validate passing":"Validate: "+h.validate.errors+" errors",h.validate.ok?"ok":"bad"));
39481
- if(h.test){var t=h.test;var trend=t.prevGaps!=null&&t.prevGaps!==t.gaps?(", gaps "+t.prevGaps+" \u2192 "+t.gaps):(", "+t.gaps+" gaps");chips.appendChild(chip("Test crawl: "+t.screens+" screens, "+t.actions+" actions"+trend,t.ok?"ok":"bad"))}
39652
+ 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)}
39482
39653
  h.ux.forEach(function(u){chips.appendChild(chip("UX drive "+u.platform+": "+u.status,u.status==="passed"?"ok":u.status==="failed"?"bad":"warn"))});
39483
39654
  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"));
39484
39655
  if(!chips.firstChild)chips.appendChild(chip("Checks appear here as validate, test and push run"));
39485
39656
  var wall=$("wall");clear(wall);
39486
39657
  if(!s.screens.length)wall.appendChild(el("div","empty","Screens appear here as they render."));
39487
- s.screens.forEach(function(sc){var d=el("div","shot");var fr=el("div","frame");fr.appendChild(img(sc.img));d.appendChild(fr);d.appendChild(el("div","id",sc.id));
39658
+ 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));
39488
39659
  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)});
39489
39660
  var brand=$("brand");clear(brand);$("brandcard").style.display=(s.brand.site||s.app.palette.length)?"":"none";
39490
39661
  if(s.brand.site){var si=img(s.brand.site,"site");brand.appendChild(si)}
@@ -39495,14 +39666,61 @@ function render(s){
39495
39666
  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));
39496
39667
  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)});
39497
39668
  }
39498
- window.__himiStudio=function(s){lastSeen=Date.now();if(!cur||cur.v!==s.v){cur=s;render(s)}else{cur.generatedAt=s.generatedAt}};
39669
+ /* Motion, sound and notices answer a CHANGE, never a redraw: effects() compares what is on the page
39670
+ now with what it showed last, so opening the board, the once-a-minute redraw and the two-second
39671
+ poll of an unchanged state do nothing, and each effect names a real event. */
39672
+ var seen=null,actx=null,sound=false,notices=false;try{sound=localStorage.getItem("himiStudioSound")==="1";notices=localStorage.getItem("himiStudioNotify")==="1"}catch(e){}
39673
+ var raf=window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(f){return setTimeout(function(){f(Date.now())},16)};
39674
+ function now(){return window.performance&&performance.now?performance.now():Date.now()}
39675
+ function reduced(){try{return matchMedia("(prefers-reduced-motion: reduce)").matches}catch(e){return false}}
39676
+ 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){}}
39677
+ 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")}};
39678
+ /* Notices are the BOARD's opt-in, not just the browser's: a permission granted to this origin once
39679
+ (an earlier board, another run) must not switch them on, and the toggle must switch them off. */
39680
+ function notify(title,body){try{if(notices&&"Notification" in window&&Notification.permission==="granted"&&document.hidden)new Notification(title,{body:body})}catch(e){}}
39681
+ function accentNow(){return (getComputedStyle(document.documentElement).getPropertyValue("--accent")||"").trim()||"#3b6fd8"}
39682
+ /* The preview lays out at a real phone's viewport (390 x 844 CSS px) and is scaled into the frame.
39683
+ Sized to the frame itself it was ~280px wide: narrower than any phone, so screens overflowed
39684
+ sideways and scrolled horizontally. */
39685
+ 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)))}
39686
+ window.addEventListener("resize",fit);
39687
+ 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){}}
39688
+ 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)})()}
39689
+ 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)})()}
39690
+ 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}}
39691
+ 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}
39692
+ function effects(s){var is=snap(s),was=seen;seen=is;if(is.accent)document.documentElement.style.setProperty("--accent",is.accent);
39693
+ /* The phone (kept across renders) and the Now line outlive a render: clear what they played last. */
39694
+ var ph=$("focus").querySelector(".phone"),nm=$("nowmsg");if(ph)ph.classList.remove("pulse","bad");nm.classList.remove("slidein");if(!was)return;
39695
+ 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();
39696
+ 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()}
39697
+ 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])}
39698
+ /* A rebuild is a later build of the SAME run: a run appearing, or restarting, records its first
39699
+ build too, and nothing was rebuilt then. */
39700
+ 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||"")}}
39701
+ if(was.gaps!=null&&is.gaps!=null&&is.gaps!==was.gaps)countTo($("gapn"),was.gaps,is.gaps);
39702
+ 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))}
39703
+ 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")}
39704
+ }
39705
+ 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}
39706
+ 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))}
39707
+ function setNotices(v){notices=v;try{localStorage.setItem("himiStudioNotify",v?"1":"0")}catch(e){}paintToggles()}
39708
+ $("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()});
39709
+ /* The click acts on what the button SAYS (the effective state), so a remembered opt-in whose browser
39710
+ permission was since reset reads "off" and one click asks for permission, not two. */
39711
+ $("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){}});
39712
+ document.addEventListener("click",function(){try{if(actx&&actx.state==="suspended")actx.resume()}catch(e){}},{once:true});
39713
+ paintToggles();
39714
+ window.__himiStudio=function(s){lastSeen=Date.now();if(!cur||cur.v!==s.v){cur=s;render(s);effects(s)}else{cur.generatedAt=s.generatedAt}};
39499
39715
  $("lb").addEventListener("click",function(){this.style.display="none"});
39500
39716
  window.__himiStudio(window.__himiInitial);
39501
39717
  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);
39502
39718
  var ticks=0;
39503
39719
  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;
39504
39720
  $("clock").textContent=(hh?hh+":":"")+(mm<10&&hh?"0":"")+mm+":"+(ss<10?"0":"")+ss;
39505
- $("updated").textContent="updated "+ago(cur.generatedAt);$("live").className="live"+(Date.now()-lastSeen>10000?" stale":"")},1000);
39721
+ $("updated").textContent="updated "+ago(cur.generatedAt);$("live").className="live"+(Date.now()-lastSeen>10000?" stale":"");
39722
+ var a=activityAt(cur),q=a?Date.now()-a:0,done=cur.current&&cur.current.stepNumber===8;
39723
+ $("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);
39506
39724
  })();
39507
39725
  </script>
39508
39726
  </body>
@@ -47241,7 +47459,6 @@ function makeServeHandler(opts) {
47241
47459
  );
47242
47460
  })();
47243
47461
  const webwixFetch = opts.webwixFetch ?? fetch;
47244
- const webwixTokens = new VisitorTokenCache(webwixFetch);
47245
47462
  const webwixOwnerInstances = new OwnerInstanceCache(webwixFetch);
47246
47463
  const webwixOwnerSites = new OwnerSitesCache(webwixFetch);
47247
47464
  const webwixOwnerRungOn = () => opts.webwixOwnerRung !== false;
@@ -47543,7 +47760,7 @@ ${releaseId}`;
47543
47760
  const pkce = await generatePkce();
47544
47761
  let authUrl;
47545
47762
  try {
47546
- const visitor = await webwixTokens.token(resolved2.config.clientId);
47763
+ const visitor = await mintAnonymousVisitor(resolved2.config.clientId, webwixFetch);
47547
47764
  authUrl = await hostedAuthUrl(
47548
47765
  { ...resolved2.config, mode: path2.endsWith("/register") ? "signup" : "login", state, challenge: pkce.challenge },
47549
47766
  visitor,
@@ -47614,7 +47831,14 @@ ${releaseId}`;
47614
47831
  const reqPath = String(call.path ?? "/");
47615
47832
  const body = call.body === void 0 ? void 0 : typeof call.body === "string" ? call.body : JSON.stringify(call.body);
47616
47833
  const forwardedHeaders = normalizeWixForwardedHeaders(call.headers);
47834
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(reqPath)) {
47835
+ return absoluteUrlRejection(reqPath) === "owner-only" ? json3(501, { error: "webwix_owner_only", app: app2 }) : json3(400, { error: "absolute_urls_not_allowed" });
47836
+ }
47617
47837
  const callerCookie = request.headers.get("cookie") ?? "";
47838
+ if (!call.checkOwnerSession && webwixOwnerRungOn() && method === "POST" && isOwnerSitesPath(reqPath) && !/wixSession2=/.test(callerCookie)) {
47839
+ served.rung = "sites";
47840
+ return json3(501, { error: "webwix_owner_no_session", app: app2 });
47841
+ }
47618
47842
  if (!call.checkOwnerSession && webwixOwnerRungOn() && method === "POST" && isOwnerSitesPath(reqPath) && /wixSession2=/.test(callerCookie)) {
47619
47843
  served.rung = "sites";
47620
47844
  try {
@@ -47633,32 +47857,8 @@ ${releaseId}`;
47633
47857
  }
47634
47858
  if (call.ownerMsid !== void 0) {
47635
47859
  served.rung = call.checkOwnerSession === true ? "gate" : "owner";
47636
- if (!webwixOwnerRungOn()) {
47637
- return json3(503, { error: "webwix_owner_disabled" });
47638
- }
47639
- const msid = String(call.ownerMsid);
47640
- if (!isSafeMsid(msid)) return json3(400, { error: "webwix_owner_bad_msid" });
47641
- const cookie = request.headers.get("cookie") ?? "";
47642
- if (!/wixSession2=/.test(cookie)) return json3(501, { error: "webwix_owner_no_session", app: app2 });
47643
- try {
47644
- const instance = await webwixOwnerInstances.instance(msid, cookie);
47645
- if (call.checkOwnerSession === true) return json3(200, { status: 200, body: JSON.stringify({ ready: true }) }, { "cache-control": "no-store" });
47646
- const first = await wixVisitorCall(method, reqPath, body, instance, webwixFetch, forwardedHeaders);
47647
- if (first.status !== 401) return json3(200, first);
47648
- webwixOwnerInstances.invalidate(msid, cookie);
47649
- const fresh = await webwixOwnerInstances.instance(msid, cookie);
47650
- return json3(200, await wixVisitorCall(method, reqPath, body, fresh, webwixFetch, forwardedHeaders));
47651
- } catch (e) {
47652
- const msg = String(e?.message ?? e);
47653
- if (msg.includes("absolute URLs")) {
47654
- return absoluteUrlRejection(reqPath) === "owner-only" ? json3(501, { error: "webwix_owner_only", app: app2 }) : json3(400, { error: "absolute_urls_not_allowed" });
47655
- }
47656
- if (msg.includes("owner mint failed")) {
47657
- if (/owner mint failed \(5\d\d\)/.test(msg)) return json3(502, { error: "webwix_bridge_upstream_failed" });
47658
- return json3(501, { error: "webwix_owner_mint_failed", app: app2 });
47659
- }
47660
- return json3(502, { error: "webwix_bridge_upstream_failed", detail: msg.slice(0, 200) });
47661
- }
47860
+ if (!webwixOwnerRungOn()) return json3(503, { error: "webwix_owner_disabled" });
47861
+ return json3(501, { error: "webwix_owner_direct_only", app: app2 });
47662
47862
  }
47663
47863
  const resolved2 = wixClientIdOf(utf8Decode(cfgBytes));
47664
47864
  if ("error" in resolved2) {
@@ -47675,28 +47875,11 @@ ${releaseId}`;
47675
47875
  try {
47676
47876
  return json3(200, await wixVisitorCall(method, reqPath, body, memberToken, webwixFetch, forwardedHeaders), { "cache-control": "no-store" });
47677
47877
  } catch (e) {
47678
- const msg = String(e?.message ?? e);
47679
- if (msg.includes("absolute URLs")) {
47680
- return absoluteUrlRejection(reqPath) === "owner-only" ? json3(501, { error: "webwix_owner_only", app: app2 }) : json3(400, { error: "absolute_urls_not_allowed" });
47681
- }
47682
- return json3(502, { error: "webwix_bridge_upstream_failed", detail: msg.slice(0, 200) });
47878
+ return json3(502, { error: "webwix_bridge_upstream_failed", detail: String(e?.message ?? e).slice(0, 200) });
47683
47879
  }
47684
47880
  }
47685
47881
  }
47686
- try {
47687
- const token = await webwixTokens.token(resolved2.clientId);
47688
- const first = await wixVisitorCall(method, reqPath, body, token, webwixFetch, forwardedHeaders);
47689
- if (first.status !== 401) return json3(200, first);
47690
- webwixTokens.invalidate(resolved2.clientId);
47691
- const fresh = await webwixTokens.token(resolved2.clientId);
47692
- return json3(200, await wixVisitorCall(method, reqPath, body, fresh, webwixFetch, forwardedHeaders));
47693
- } catch (e) {
47694
- const msg = String(e?.message ?? e);
47695
- if (msg.includes("absolute URLs")) {
47696
- return absoluteUrlRejection(reqPath) === "owner-only" ? json3(501, { error: "webwix_owner_only", app: app2 }) : json3(400, { error: "absolute_urls_not_allowed" });
47697
- }
47698
- return json3(502, { error: "webwix_bridge_upstream_failed", detail: msg.slice(0, 200) });
47699
- }
47882
+ return json3(501, { error: "webwix_visitor_direct_only", app: app2 });
47700
47883
  })();
47701
47884
  status = res.status;
47702
47885
  return res;
@@ -48280,7 +48463,7 @@ function indexHtml(asset, pathname, creds, handoverAttempted = false) {
48280
48463
  void pathname;
48281
48464
  const nonce = scriptNonce();
48282
48465
  const raw = new TextDecoder().decode(assetBytes(asset));
48283
- const head = `<script nonce="${nonce}">${PREVIEW_BASE_JS}</script>` + (creds ? `<script nonce="${nonce}">${previewCredentialsJs(creds)}</script>` : handoverAttempted ? `<script nonce="${nonce}">${stripHandoverParamJs()}</script>` : "");
48466
+ const head = `<script nonce="${nonce}">${PREVIEW_BASE_JS}</script><script nonce="${nonce}">${PREVIEW_ENTRY_RECOVERY_JS}</script>` + (creds ? `<script nonce="${nonce}">${previewCredentialsJs(creds)}</script>` : handoverAttempted ? `<script nonce="${nonce}">${stripHandoverParamJs()}</script>` : "");
48284
48467
  const html = raw.replace(/(=|url\()(["']?)\/preview\//g, "$1$2").replace(/<head(\s[^>]*)?>/i, (tag) => `${tag}${head}`);
48285
48468
  return new Response(html, {
48286
48469
  status: 200,
@@ -48332,7 +48515,7 @@ function previewResponse(pathname, assets, mount = "/preview", creds, handoverAt
48332
48515
  headers: { "content-type": "application/json; charset=utf-8" }
48333
48516
  });
48334
48517
  }
48335
- var T5_FRAME_CSP, PREVIEW_BASE_JS, MOUNT_STRIP;
48518
+ var T5_FRAME_CSP, PREVIEW_BASE_JS, PREVIEW_ENTRY_RECOVERY_JS, MOUNT_STRIP;
48336
48519
  var init_preview2 = __esm({
48337
48520
  "../../packages/serve/src/preview.ts"() {
48338
48521
  T5_FRAME_CSP = "default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval'; connect-src 'none'; img-src 'none'; media-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'";
@@ -48341,6 +48524,7 @@ var init_preview2 = __esm({
48341
48524
  // the EARLIEST mount segment wins, mirroring the SPA's own deployBase (an app literally
48342
48525
  // named "app" — /preview/a/app — must not hijack the base).
48343
48526
  '(function(){var m=location.pathname.match(/^(.*?\\/(?:preview|app))(?:\\/|$)/);var b=document.createElement("base");b.href=(m?m[1]:"")+"/";document.head.insertBefore(b,document.head.firstChild)})();';
48527
+ PREVIEW_ENTRY_RECOVERY_JS = '(function(){var K="himalaya.preview.entryReload";function show(){if(document.getElementById("himalaya-preview-load-failed"))return;var d=document.createElement("div");d.id="himalaya-preview-load-failed";d.setAttribute("role","alert");d.style.cssText="position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;box-sizing:border-box;padding:32px;text-align:center;font:15px/1.5 system-ui,sans-serif;background:#f5f7fa;color:#111";var p=document.createElement("p");p.textContent="This app couldn\\u2019t load \\u2014 a new version may have just been published.";var b=document.createElement("button");b.type="button";b.textContent="Reload";b.addEventListener("click",function(){location.reload()});d.appendChild(p);d.appendChild(b);(document.body||document.documentElement).appendChild(d)}function failed(){var now=Date.now(),last=0;try{last=+sessionStorage.getItem(K)||0}catch(e){}if(now-last>60000){try{sessionStorage.setItem(K,String(now));location.reload();return}catch(e){}}if(document.body)show();else document.addEventListener("DOMContentLoaded",show)}window.addEventListener("error",function(e){var t=e&&e.target;if(t&&t.tagName==="SCRIPT"&&t.type==="module")failed()},true)})();';
48344
48528
  MOUNT_STRIP = {
48345
48529
  // Precompiled — this runs on every request of the public serving hot path.
48346
48530
  "/preview": /^\/preview\/?/,
@@ -48587,21 +48771,21 @@ var init_preview_assets_generated = __esm({
48587
48771
  "../../packages/serve/src/preview-assets.generated.ts"() {
48588
48772
  init_preview_assets_format();
48589
48773
  PREVIEW_ASSET_MANIFEST = [
48590
- { key: "assets/index-FMHX2G25.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1031131, sha256: "0d386640e1979d4c81abbb29ccd0531913f1a759eba2e490160c7d3fac52618d" },
48591
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1031139, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
48592
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1035749, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
48593
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1524333, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
48594
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1615257, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
48595
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1706241, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
48596
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1797221, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
48597
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1888093, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
48598
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1979073, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
48599
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2087709, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
48600
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2196793, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
48601
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2305841, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
48602
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2413541, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
48603
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2501649, length: 4947, sha256: "1f91f02ba75063dd7b2cca2584fcb3cc692f7faf905e89c23bf790633fd3d0e9" },
48604
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2506596, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
48774
+ { key: "assets/index-hHpGDjBZ.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1038931, sha256: "65ef206c528bdc3e9b7ec6907e734267cd225ed33513982c46ecb3bbdd6d44ee" },
48775
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1038939, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
48776
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1043549, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
48777
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1532133, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
48778
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1623057, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
48779
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1714041, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
48780
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1805021, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
48781
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1895893, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
48782
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1986873, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
48783
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2095509, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
48784
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2204593, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
48785
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2313641, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
48786
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2421341, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
48787
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2509449, length: 4947, sha256: "f07de6435f49d7608a56b7af529229f2ccc10901ab33a9e600e01f10b60975d6" },
48788
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2514396, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
48605
48789
  ];
48606
48790
  }
48607
48791
  });
@@ -63412,9 +63596,10 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
63412
63596
  (--ai writes rows for you to review first)
63413
63597
  himi secrets set|list|rotate --app <app> --name <name> [--environment preview|production]
63414
63598
  encrypted secret metadata; set/rotate read the value from stdin
63415
- himi render <screenId> [--content <dir>] [--scheme dark] [--png <file>] [--tree] [--dump-state] [--static] [--live] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--load-budget <ms>]
63599
+ himi render <screenId> [--content <dir>] [--scheme dark] [--png [<file>]] [--tree] [--dump-state] [--static] [--live] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--load-budget <ms>]
63416
63600
  [--remote --app <app> --release <id>]
63417
63601
  headless web render + diagnostics (no sim). Default: local content dir.
63602
+ A bare --png writes .himi/shots/<screenId>.png (what the build board shows).
63418
63603
  --remote fetches GET /v1/preview/\u2026/render from the serve plane (post-push).
63419
63604
  It returns the pushed descriptor STRUCTURE only \u2014 NO png and NO live data
63420
63605
  (the serve has no headless browser); for a pixel view or REAL fetched data
@@ -63593,7 +63778,9 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
63593
63778
  plane, no dev-server. With no argument it packs the current
63594
63779
  content package first \u2014 edit, run, look at it.
63595
63780
  --watch rebuilds on every save and repaints the open tab
63596
- (a failed build keeps the last good preview). Blocks until Ctrl-C.
63781
+ (a failed build keeps the last good preview). The build board
63782
+ is served beside it at /__himi/studio/ with the preview framed
63783
+ in it. Blocks until Ctrl-C.
63597
63784
  himi flags list|set|ramp|rm [<key>] [--app <a>]
63598
63785
  read/change operational flags (RFC 0013); writes report
63599
63786
  servedNow from the device door, never just ok
@@ -63920,7 +64107,7 @@ var COMMANDS = [
63920
64107
  { name: "ux", summary: "audit or record mobile UX evidence. `audit` checks the public-safe contract, seeded blank acceptance rows, placeholder source copy, app-specific mocks, and iOS/Android audit records. It warns while existing templates migrate; `--strict` promotes missing evidence. `record` stores checks performed on an installed iOS/Android build: `--checks` is required for passed|failed and empty for unavailable, which drove nothing and needs a `--reason` instead. `record` also STAMPS the release it audited (it resolves the id from a build, the same one a push would publish), because an unforced `himi push` of a release with screens refuses when no drive is recorded for THAT release, or when the one that is recorded says `failed` \u2014 the artifact stopped being advice nobody reads (#2955). `unavailable` still satisfies it; only `failed` refuses.", required: "audit [--content <dir>] [--strict] [--json] | record --content <dir> --platform ios|android --status passed|failed|unavailable [--checks back,keyboard,...] [--reason <text>]" },
63921
64108
  { name: "progress", summary: 'the build JOURNAL an agent keeps for the owner while it works. `himi progress "<what changed>" --step <1-8|name> --image <png>` appends one postcard to .himi/progress.jsonl \u2014 the step is one of the customer journey\'s eight (discover, classify, plan, implement, brand, validate, review, download) \u2014 and COPIES each image, content-addressed, into .himi/history/, because .himi/shots/ is overwritten by every render and cleared by every full `himi test`, so a path into it would later show a different picture than the one the owner was sent. --image repeats. --notify also raises a best-effort desktop notification for an owner who stepped away. A bare `himi progress` prints the journal with the current step and elapsed time \u2014 also how an agent recovers its place after its context was compacted. Local only: nothing is sent anywhere.', required: '["<message>" [--step <1-8|name>] [--image <png>]\u2026 [--notify]] [--content <dir>]' },
63922
64109
  { name: "studio", summary: "open the BUILD BOARD \u2014 one local page the owner can leave open while an agent builds their app, which fills itself in: every screen as it renders (NEW/CHANGED badges, and each earlier version kept for a filmstrip), the journey stepper and the latest postcard from `himi progress`, the validate/test/UX/draft checks as they run, the site next to the app's home screen with its palette, and the navigation the test crawl followed. Writes .himi/studio/index.html and opens it; the page re-reads its state every two seconds, so nothing needs to be served. OPT-IN BY EXISTENCE: until this command has run, no other command touches .himi/studio/ \u2014 after it, render/test/validate/push/icon/site/ux/progress each refresh the board as they finish (best-effort; never fails a command). Every image is a content-addressed copy in .himi/history/, never a live path into .himi/shots/. Local only, no secrets: it never reads a preview token, handover code or control token. --no-open prints the path instead (SSH, CI). `himi studio reel` writes the MAKING-OF: one self-contained HTML file (every image inlined, nothing fetched) with a flipbook of the home screen version by version, captioned by the postcard in effect at the time, the final screens, and every postcard \u2014 sized to stay attachable, thinning frames first. Defaults to .himi/making-of.html (never archived by push); --out to put it elsewhere.", required: "[--content <dir>] [--no-open] | reel [--out <file>] [--no-open]" },
63923
- { name: "render", summary: "headless-render a screen with the web renderer: render-tree + diagnostics, optional --png <file>, --scheme dark, --tree. Local by default (content dir). Pass --remote --app --release to hit GET /v1/preview/\u2026/render on the serve plane after a push. Boots the worker so $service bindings resolve; --static raw bindings; --live real Wix fetches; --state injects worker state; --input supplies pre-boot navigation params; --width/--device for PNG viewport; --rtl for dir=rtl + $env:isRTL.", required: "<screenId> [--content <dir>] [--scheme dark] [--png <file>] [--tree] [--static] [--live] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--remote --app --release]" },
64110
+ { name: "render", summary: "headless-render a screen with the web renderer: render-tree + diagnostics, optional --png [<file>] (bare: .himi/shots/<id>.png, what the build board shows), --scheme dark, --tree. Local by default (content dir). Pass --remote --app --release to hit GET /v1/preview/\u2026/render on the serve plane after a push. Boots the worker so $service bindings resolve; --static raw bindings; --live real Wix fetches; --state injects worker state; --input supplies pre-boot navigation params; --width/--device for PNG viewport; --rtl for dir=rtl + $env:isRTL.", required: "<screenId> [--content <dir>] [--scheme dark] [--png [<file>]] [--tree] [--static] [--live] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--remote --app --release]" },
63924
64111
  { name: "cms", summary: "inspect, provision, and seed the app's CMS (Wix Data) collections, declared as a `cms` block in config.ts. `query` is READ-ONLY \u2014 by default it fetches ONLY the physical ids `config.ts` declares (item counts + schema drift) and reports `siteScanned: false`; it never lists the site, so an UNDECLARED `himi_<app>_` collection this app owns is not reached without `--all`. `--all` SCANS the site \u2014 every collection it lists is labeled `owned`/`adopted`/`foreign`, and it describes every foreign one \u2014 id, non-system field keys + types, and the site's read role \u2014 so an app with no `cms` block at all can DISCOVER a collection to adopt (`--all` is the one case where a missing `cms` block is not 'nothing to do'). Foreign collections are described up to a cap and are never item-counted (`itemCount: null`), because a row count is one query each; `foreign.count` is how many the scan LISTED \u2014 it follows the list endpoint's pages, and `listingIncomplete: true` says when the walk stopped short of the end, with `unlistedCollections` counting what it left out when Wix gave a paging total \u2014 and `foreign.truncated` says how many of those went undescribed, each still NAMED in `foreign.others` (id + displayName), so a collection the site already has is never hidden behind the cap. The scan is OWNER-scoped \u2014 it needs --site <msid> (or WIX_SITE_ID) plus owner auth via `wix token`; a visitor token can read a public collection's ITEMS but can never enumerate collections, so there is no tokenless discovery path. `create` reconciles declared collections additively and idempotently, and REFUSES any id outside the app's `himi_<app>_` namespace, so it is safe against a production site. It reports declared-vs-site `permissionDrift` but never writes permissions unless asked with --apply-permissions, so a field reconcile can't silently revert access control changed in the dashboard. `seed` inserts the git-tracked `seed:` rows into EMPTY owned collections (--force to add to a non-empty one; --ai writes placeholder rows plus a refinement prompt for you to review). Adopted collections (declared by `wixId`) are read-only and never created, updated, or seeded. Site from --site <msid> or WIX_SITE_ID; owner auth via `wix token`.", required: "query|create|seed [--site <msid>] [--content <dir>] [--all] [--dry-run] [--force] [--allow-destructive] [--apply-permissions] [--only <id>] [--ai] [--count <n>]" },
63925
64112
  { name: "data", summary: "read-only plan or explicitly provision/seed declared KV and Wix Data resources. Preview and production are separate scopes. KV administration uses the operator data plane from --data-url/HIMI_DATA_ADMIN_URL and HIMI_DATA_ADMIN_TOKEN; draft push never calls it.", required: "plan|create|seed [--site <msid>] [--environment preview|production] [--content <dir>] [--data-url <url>] [--dry-run] [--force]" },
63926
64113
  { name: "icons", summary: "list the valid Icon.name vocabulary (an unknown name renders a fallback glyph; validate warns on unknown names) (offline)", required: "[--json]" },
@@ -68447,7 +68634,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68447
68634
  current = fresh;
68448
68635
  servedMissing = outcome.missingBundles;
68449
68636
  for (const [event, data] of watchKit.reloadEvents(diff)) bus.publish(event, data);
68450
- record2(running, { ok: true, at: (/* @__PURE__ */ new Date()).toISOString() });
68637
+ record2(running, { ok: true, at: (/* @__PURE__ */ new Date()).toISOString(), ms: Date.now() - t0, screens: diff.screens, tokens: diff.tokens, config: diff.config });
68451
68638
  await refreshBoard();
68452
68639
  io.error(JSON.stringify({ watch: "rebuilt", ms: Date.now() - t0, screens: diff.screens, tokens: diff.tokens, config: diff.config, tabs: bus.listeners }));
68453
68640
  } catch (e) {