@wix/himalaya-cli 0.1022.0 → 0.1024.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +53 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/validator.d.ts +52 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +16 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +16 -0
- package/dist/cli.mjs +306 -121
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/component-reference.json +5 -5
- package/guides/component-reference.md +35 -0
- package/guides/designing-himi-screens.md +7 -3
- package/guides/headless-loop.md +8 -1
- package/guides/release-notes.md +40 -0
- package/guides/testing-himi-apps.md +9 -12
- package/guides/wix-api-usage.md +4 -2
- package/guides/wix-login-integration.md +2 -1
- package/guides/wix-sdk-overview.md +6 -1
- package/package.json +2 -2
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,
|
|
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",
|
|
@@ -24142,6 +24148,9 @@ function autoGrowBox(contentHeight, lineHeight, verticalPadding, maxLines) {
|
|
|
24142
24148
|
const cap = Math.max(floor, lineHeight * lines + verticalPadding);
|
|
24143
24149
|
return { height: Math.max(floor, Math.min(contentHeight, cap)), scrolls: contentHeight > cap };
|
|
24144
24150
|
}
|
|
24151
|
+
function wrapModalMenu(modal, label2, placement, menu) {
|
|
24152
|
+
return modal ? /* @__PURE__ */ jsx6("div", { role: "dialog", "aria-modal": "true", "aria-label": label2, style: placement, children: menu }) : menu;
|
|
24153
|
+
}
|
|
24145
24154
|
function bindMediaSlot(value, sessionId) {
|
|
24146
24155
|
if (typeof value === "string") {
|
|
24147
24156
|
const prefix = `$media:${sessionId}.`;
|
|
@@ -24269,11 +24278,13 @@ function withExtras(name, render) {
|
|
|
24269
24278
|
}
|
|
24270
24279
|
const roleIsSynthetic = next[SYNTHETIC_TAP_ROLE] === true;
|
|
24271
24280
|
delete next[SYNTHETIC_TAP_ROLE];
|
|
24281
|
+
const tabIndexIsSynthetic = next[SYNTHETIC_TABINDEX] === true;
|
|
24282
|
+
delete next[SYNTHETIC_TABINDEX];
|
|
24272
24283
|
if (additive) next.style = additive;
|
|
24273
24284
|
const ownsInteractiveSemantics = isNativeInteractiveElement(el) || hasInteractiveReactDescendant(el);
|
|
24274
24285
|
if (ownsInteractiveSemantics) {
|
|
24275
24286
|
if (roleIsSynthetic) delete next.role;
|
|
24276
|
-
delete next.tabIndex;
|
|
24287
|
+
if (tabIndexIsSynthetic) delete next.tabIndex;
|
|
24277
24288
|
delete next.onKeyDown;
|
|
24278
24289
|
}
|
|
24279
24290
|
if (pass.style) next.style = { ...additive ?? prev.style ?? {}, ...pass.style };
|
|
@@ -24709,6 +24720,57 @@ function calKeyActivate(enabled, activate) {
|
|
|
24709
24720
|
activate();
|
|
24710
24721
|
};
|
|
24711
24722
|
}
|
|
24723
|
+
function restoreFocusOnDismiss(scope, opener) {
|
|
24724
|
+
const Observer = document.defaultView?.MutationObserver;
|
|
24725
|
+
if (!Observer) return;
|
|
24726
|
+
presentedOpeners.set(scope, opener);
|
|
24727
|
+
if (presentedObserver) return;
|
|
24728
|
+
presentedObserver = new Observer(() => {
|
|
24729
|
+
for (const [s, o] of presentedOpeners) {
|
|
24730
|
+
if (s.isConnected) continue;
|
|
24731
|
+
presentedOpeners.delete(s);
|
|
24732
|
+
const active = document.activeElement;
|
|
24733
|
+
if ((active == null || active === document.body) && o.isConnected && typeof o.focus === "function") o.focus();
|
|
24734
|
+
}
|
|
24735
|
+
if (presentedOpeners.size === 0) {
|
|
24736
|
+
presentedObserver?.disconnect();
|
|
24737
|
+
presentedObserver = null;
|
|
24738
|
+
}
|
|
24739
|
+
});
|
|
24740
|
+
presentedObserver.observe(document.body, { childList: true, subtree: true });
|
|
24741
|
+
}
|
|
24742
|
+
function ensureInitialFocusRuntime() {
|
|
24743
|
+
if (initialFocusScheduled || typeof document === "undefined") return;
|
|
24744
|
+
initialFocusScheduled = true;
|
|
24745
|
+
setTimeout(() => {
|
|
24746
|
+
initialFocusScheduled = false;
|
|
24747
|
+
const targets = Array.from(document.querySelectorAll("[data-himalaya-initial-focus]"));
|
|
24748
|
+
const active = document.activeElement;
|
|
24749
|
+
const unplaced = active == null || active === document.body;
|
|
24750
|
+
const fresh = targets.filter((t) => !initialFocusSeen.has(t));
|
|
24751
|
+
let presented;
|
|
24752
|
+
let presentedScope = null;
|
|
24753
|
+
for (const t of fresh) {
|
|
24754
|
+
const scope = t.closest(PRESENTED_SCOPE);
|
|
24755
|
+
if (scope && !(active && scope.contains(active))) {
|
|
24756
|
+
presented = t;
|
|
24757
|
+
presentedScope = scope;
|
|
24758
|
+
}
|
|
24759
|
+
}
|
|
24760
|
+
for (const t of fresh) initialFocusSeen.add(t);
|
|
24761
|
+
const el = presented ?? (unplaced ? fresh[0] : void 0);
|
|
24762
|
+
if (el && typeof el.focus === "function") {
|
|
24763
|
+
if (presented && presentedScope && !unplaced && active instanceof HTMLElement) {
|
|
24764
|
+
restoreFocusOnDismiss(presentedScope, active);
|
|
24765
|
+
}
|
|
24766
|
+
el.focus();
|
|
24767
|
+
}
|
|
24768
|
+
}, 0);
|
|
24769
|
+
}
|
|
24770
|
+
function ariaModal(modal, dflt) {
|
|
24771
|
+
const on3 = modal === true || modal === false ? modal : dflt;
|
|
24772
|
+
return on3 ? "true" : void 0;
|
|
24773
|
+
}
|
|
24712
24774
|
function applyA11ySemantics(node, ctx, extra) {
|
|
24713
24775
|
const m = node?.modifiers;
|
|
24714
24776
|
if (!m) return void 0;
|
|
@@ -24764,6 +24826,32 @@ function applyA11ySemantics(node, ctx, extra) {
|
|
|
24764
24826
|
}
|
|
24765
24827
|
}
|
|
24766
24828
|
}
|
|
24829
|
+
const focusOrder = m.focusOrder ?? m.focus_order;
|
|
24830
|
+
if (typeof focusOrder === "number" && Number.isFinite(focusOrder)) {
|
|
24831
|
+
extra.tabIndex = Math.max(1, Math.trunc(focusOrder) + 1);
|
|
24832
|
+
}
|
|
24833
|
+
if ((m.focusGroup ?? m.focus_group) === true && extra.role == null) extra.role = "group";
|
|
24834
|
+
if ((m.initialFocus ?? m.initial_focus) === true) {
|
|
24835
|
+
extra["data-himalaya-initial-focus"] = "1";
|
|
24836
|
+
if (extra.tabIndex == null) {
|
|
24837
|
+
extra.tabIndex = -1;
|
|
24838
|
+
extra[SYNTHETIC_TABINDEX] = true;
|
|
24839
|
+
}
|
|
24840
|
+
ensureInitialFocusRuntime();
|
|
24841
|
+
}
|
|
24842
|
+
const announce = m.announce;
|
|
24843
|
+
if (announce && typeof announce === "object" && !Array.isArray(announce)) {
|
|
24844
|
+
const trigger = str7(announce.trigger) ?? "on_change";
|
|
24845
|
+
if (trigger === "none") {
|
|
24846
|
+
extra["aria-live"] = "off";
|
|
24847
|
+
} else {
|
|
24848
|
+
extra["aria-live"] = str7(announce.politeness) === "assertive" ? "assertive" : "polite";
|
|
24849
|
+
extra["aria-atomic"] = "true";
|
|
24850
|
+
extra["aria-relevant"] = trigger === "on_appear" ? "additions" : trigger === "both" ? "additions text" : "text";
|
|
24851
|
+
const text5 = str7(announce.text);
|
|
24852
|
+
if (text5 && extra["aria-label"] == null) extra["aria-label"] = text5;
|
|
24853
|
+
}
|
|
24854
|
+
}
|
|
24767
24855
|
return hint;
|
|
24768
24856
|
}
|
|
24769
24857
|
function renderNodeInner(node, ctx) {
|
|
@@ -24856,7 +24944,10 @@ function renderNodeInner(node, ctx) {
|
|
|
24856
24944
|
extra.role = "button";
|
|
24857
24945
|
if (typeof el.type !== "string") extra[SYNTHETIC_TAP_ROLE] = true;
|
|
24858
24946
|
}
|
|
24859
|
-
extra.tabIndex
|
|
24947
|
+
if (extra.tabIndex == null) {
|
|
24948
|
+
extra.tabIndex = 0;
|
|
24949
|
+
if (typeof el.type !== "string") extra[SYNTHETIC_TABINDEX] = true;
|
|
24950
|
+
}
|
|
24860
24951
|
const prevOnKeyDown = el.props.onKeyDown;
|
|
24861
24952
|
extra.onKeyDown = (e) => {
|
|
24862
24953
|
prevOnKeyDown?.(e);
|
|
@@ -24904,6 +24995,7 @@ function renderNodeInner(node, ctx) {
|
|
|
24904
24995
|
if (composed) extra.style = composed;
|
|
24905
24996
|
}
|
|
24906
24997
|
}
|
|
24998
|
+
if (typeof el.type === "string") delete extra[SYNTHETIC_TABINDEX];
|
|
24907
24999
|
let out = Object.keys(extra).length ? React5.cloneElement(el, extra) : el;
|
|
24908
25000
|
if (a11yHint) {
|
|
24909
25001
|
out = /* @__PURE__ */ jsx6(A11yHintDescription, { hint: a11yHint, idBase: componentId, children: out });
|
|
@@ -25333,7 +25425,7 @@ function renderInner(node, ctx) {
|
|
|
25333
25425
|
case "Popover": {
|
|
25334
25426
|
return /* @__PURE__ */ jsxs3("div", { style: { display: "inline-flex", flexDirection: "column", alignItems: "flex-start", gap: 6, ...wrap }, children: [
|
|
25335
25427
|
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 })
|
|
25428
|
+
/* @__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
25429
|
] });
|
|
25338
25430
|
}
|
|
25339
25431
|
// ───────────── chips & selection (RFC 0030) ─────────────
|
|
@@ -25601,7 +25693,7 @@ function renderInner(node, ctx) {
|
|
|
25601
25693
|
] });
|
|
25602
25694
|
}
|
|
25603
25695
|
case "AlertDialog":
|
|
25604
|
-
return /* @__PURE__ */ jsxs3("div", { role: "alertdialog", "aria-modal":
|
|
25696
|
+
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
25697
|
/* @__PURE__ */ jsx6("div", { style: { fontFamily: bodyFont(ctx), fontWeight: 700, fontSize: 17, color: C("textPrimary", "#111") }, children: txt(props.title, ctx) }),
|
|
25606
25698
|
/* @__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
25699
|
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: 10 }, children: [
|
|
@@ -25624,7 +25716,7 @@ function renderInner(node, ctx) {
|
|
|
25624
25716
|
}
|
|
25625
25717
|
}
|
|
25626
25718
|
}
|
|
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;
|
|
25719
|
+
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
25720
|
var init_renderNode = __esm({
|
|
25629
25721
|
"../../core/web-renderer/src/renderNode.tsx"() {
|
|
25630
25722
|
"use strict";
|
|
@@ -25989,7 +26081,9 @@ var init_renderNode = __esm({
|
|
|
25989
26081
|
AUTOCAPITALIZE_VALUES = /* @__PURE__ */ new Set(["none", "words", "sentences", "characters"]);
|
|
25990
26082
|
ENTER_KEY_HINTS = /* @__PURE__ */ new Set(["done", "go", "next", "send", "search"]);
|
|
25991
26083
|
AUTOGROW_CEILING = 12;
|
|
25992
|
-
EditableField = ({ props, ctx, wrap, multiline, id: htmlId, "data-component-id": componentId, "data-component-type": componentType }) => {
|
|
26084
|
+
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 }) => {
|
|
26085
|
+
const tabIndex = tabIndexIsSynthetic === true ? void 0 : tabIndexProp;
|
|
26086
|
+
const a11yFwd = { tabIndex, "data-himalaya-initial-focus": initialFocusMarker, "aria-live": ariaLive, "aria-atomic": ariaAtomic, "aria-relevant": ariaRelevant };
|
|
25993
26087
|
const C = ctx.color;
|
|
25994
26088
|
const ctrl = props.text ?? props.value;
|
|
25995
26089
|
const cfg = ctrl && typeof ctrl === "object" ? ctrl : {};
|
|
@@ -26134,7 +26228,7 @@ var init_renderNode = __esm({
|
|
|
26134
26228
|
/* @__PURE__ */ jsxs3("div", { style: box, children: [
|
|
26135
26229
|
props.leadingIcon && accIcon(props.leadingIcon),
|
|
26136
26230
|
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 }),
|
|
26231
|
+
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
26232
|
props.suffix && /* @__PURE__ */ jsx6("span", { style: { color: muted, flex: "none" }, children: String(props.suffix) }),
|
|
26139
26233
|
props.clearButton && local && /* @__PURE__ */ jsx6("span", { onClick: () => {
|
|
26140
26234
|
setLocal("");
|
|
@@ -26166,15 +26260,47 @@ var init_renderNode = __esm({
|
|
|
26166
26260
|
document.removeEventListener("keydown", onKey);
|
|
26167
26261
|
};
|
|
26168
26262
|
}, [open]);
|
|
26263
|
+
const modal = ariaModal(props.modal, false) === "true";
|
|
26264
|
+
const anchorLabel = props.anchor?.modifiers?.accessibilityLabel || "More actions";
|
|
26265
|
+
const popupPlacement = { position: "absolute", top: "100%", right: 0, zIndex: 20, marginTop: 4 };
|
|
26266
|
+
const anchorRef = React5.useRef(null);
|
|
26267
|
+
const menuRef = React5.useRef(null);
|
|
26268
|
+
const itemsKey = items.map((it, i) => `${it?.id ?? i}:${it?.disabled ? 0 : 1}`).join("|");
|
|
26269
|
+
React5.useEffect(() => {
|
|
26270
|
+
if (!open || !modal) return;
|
|
26271
|
+
const menu = menuRef.current;
|
|
26272
|
+
if (!menu || menu.contains(document.activeElement)) return;
|
|
26273
|
+
(menu.querySelector('[role="menuitem"][tabindex="0"]') ?? menu).focus();
|
|
26274
|
+
}, [open, modal, itemsKey]);
|
|
26275
|
+
React5.useEffect(() => {
|
|
26276
|
+
if (!open || !modal) return;
|
|
26277
|
+
return () => {
|
|
26278
|
+
const active = document.activeElement;
|
|
26279
|
+
if (active == null || active === document.body || !document.contains(active)) anchorRef.current?.focus();
|
|
26280
|
+
};
|
|
26281
|
+
}, [open, modal]);
|
|
26282
|
+
const trapTab = (e) => {
|
|
26283
|
+
if (!modal || e.key !== "Tab") return;
|
|
26284
|
+
const items2 = Array.from(menuRef.current?.querySelectorAll('[role="menuitem"][tabindex="0"]') ?? []);
|
|
26285
|
+
if (!items2.length) {
|
|
26286
|
+
e.preventDefault();
|
|
26287
|
+
return;
|
|
26288
|
+
}
|
|
26289
|
+
const at = items2.indexOf(document.activeElement);
|
|
26290
|
+
const next = e.shiftKey ? at <= 0 ? items2.length - 1 : at - 1 : at < 0 || at === items2.length - 1 ? 0 : at + 1;
|
|
26291
|
+
e.preventDefault();
|
|
26292
|
+
items2[next].focus();
|
|
26293
|
+
};
|
|
26169
26294
|
return /* @__PURE__ */ jsxs3("span", { ref: hostRef, style: { position: "relative", display: "inline-flex", ...wrap }, children: [
|
|
26170
26295
|
/* @__PURE__ */ jsx6(
|
|
26171
26296
|
"span",
|
|
26172
26297
|
{
|
|
26298
|
+
ref: anchorRef,
|
|
26173
26299
|
role: "button",
|
|
26174
26300
|
tabIndex: 0,
|
|
26175
26301
|
"aria-haspopup": "menu",
|
|
26176
26302
|
"aria-expanded": open,
|
|
26177
|
-
"aria-label":
|
|
26303
|
+
"aria-label": anchorLabel,
|
|
26178
26304
|
onClick: (e) => {
|
|
26179
26305
|
e.stopPropagation();
|
|
26180
26306
|
setOpen((v) => !v);
|
|
@@ -26190,11 +26316,14 @@ var init_renderNode = __esm({
|
|
|
26190
26316
|
children: props.anchor ? renderNode(props.anchor, ctx) : iconSvg("more", { size: 20, color: C("textSecondary", "#636b78") })
|
|
26191
26317
|
}
|
|
26192
26318
|
),
|
|
26193
|
-
open && /* @__PURE__ */ jsx6(
|
|
26319
|
+
open && wrapModalMenu(modal, anchorLabel, popupPlacement, /* @__PURE__ */ jsx6(
|
|
26194
26320
|
"div",
|
|
26195
26321
|
{
|
|
26322
|
+
ref: menuRef,
|
|
26196
26323
|
role: "menu",
|
|
26197
|
-
|
|
26324
|
+
tabIndex: modal ? -1 : void 0,
|
|
26325
|
+
onKeyDown: trapTab,
|
|
26326
|
+
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
26327
|
children: items.map((it, i) => /* @__PURE__ */ jsxs3(
|
|
26199
26328
|
"div",
|
|
26200
26329
|
{
|
|
@@ -26223,7 +26352,7 @@ var init_renderNode = __esm({
|
|
|
26223
26352
|
it?.id ?? i
|
|
26224
26353
|
))
|
|
26225
26354
|
}
|
|
26226
|
-
)
|
|
26355
|
+
))
|
|
26227
26356
|
] });
|
|
26228
26357
|
});
|
|
26229
26358
|
LoadMoreSentinel = ({ ctx, pagination }) => {
|
|
@@ -27100,6 +27229,7 @@ var init_renderNode = __esm({
|
|
|
27100
27229
|
] });
|
|
27101
27230
|
});
|
|
27102
27231
|
SYNTHETIC_TAP_ROLE = "data-hm-synthetic-role";
|
|
27232
|
+
SYNTHETIC_TABINDEX = "data-hm-synthetic-tabindex";
|
|
27103
27233
|
INFERRED_DECORATIVE = "data-hm-inferred-decorative";
|
|
27104
27234
|
ICON_GLYPH = "data-hm-icon-glyph";
|
|
27105
27235
|
INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
|
|
@@ -27463,7 +27593,7 @@ var init_renderNode = __esm({
|
|
|
27463
27593
|
if (gated && presented !== true && presented !== "true") return null;
|
|
27464
27594
|
const grabber = props.showGrabber !== false;
|
|
27465
27595
|
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":
|
|
27596
|
+
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
27597
|
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
27598
|
/* @__PURE__ */ jsx6("div", { style: { padding: "8px 16px 16px" }, children: props.content ? renderNode(props.content, ctx) : null })
|
|
27469
27599
|
] }) });
|
|
@@ -29001,6 +29131,11 @@ var init_renderNode = __esm({
|
|
|
29001
29131
|
grid: "grid",
|
|
29002
29132
|
gridcell: "gridcell"
|
|
29003
29133
|
};
|
|
29134
|
+
initialFocusScheduled = false;
|
|
29135
|
+
initialFocusSeen = /* @__PURE__ */ new WeakSet();
|
|
29136
|
+
PRESENTED_SCOPE = '[aria-modal="true"], [role="dialog"], [role="alertdialog"], [role="menu"]';
|
|
29137
|
+
presentedOpeners = /* @__PURE__ */ new Map();
|
|
29138
|
+
presentedObserver = null;
|
|
29004
29139
|
A11yHintDescription = ({ hint, idBase, children, ...forwarded }) => {
|
|
29005
29140
|
const descId = `${idBase ?? "hm"}__hint${React5.useId()}`;
|
|
29006
29141
|
const own3 = children.props;
|
|
@@ -39279,11 +39414,22 @@ function liveRun(dir) {
|
|
|
39279
39414
|
const build2 = obj(rec?.build);
|
|
39280
39415
|
try {
|
|
39281
39416
|
process.kill(pid, 0);
|
|
39417
|
+
const at = text(build2?.at);
|
|
39282
39418
|
return {
|
|
39283
39419
|
url,
|
|
39420
|
+
// Which run this is: a restart on the same port serves the same URL from a new process.
|
|
39421
|
+
runId: `${pid}@${text(rec?.startedAt) ?? ""}`,
|
|
39284
39422
|
buildError: build2?.ok === false ? text(build2.error) ?? "the last rebuild failed" : null,
|
|
39285
39423
|
// Only `--watch` wrote this record before plain runs did, so a record without the key is one.
|
|
39286
|
-
watching: rec?.watching !== false
|
|
39424
|
+
watching: rec?.watching !== false,
|
|
39425
|
+
lastBuild: build2 && at ? {
|
|
39426
|
+
at,
|
|
39427
|
+
ok: build2.ok !== false,
|
|
39428
|
+
ms: typeof build2.ms === "number" && Number.isFinite(build2.ms) ? Math.max(0, Math.round(build2.ms)) : null,
|
|
39429
|
+
screens: (Array.isArray(build2.screens) ? build2.screens : []).filter((x) => typeof x === "string").slice(0, 20),
|
|
39430
|
+
tokens: build2.tokens === true,
|
|
39431
|
+
config: build2.config === true
|
|
39432
|
+
} : null
|
|
39287
39433
|
};
|
|
39288
39434
|
} catch {
|
|
39289
39435
|
return null;
|
|
@@ -39400,6 +39546,7 @@ function renderStudioHtml(initial) {
|
|
|
39400
39546
|
<meta charset="utf-8">
|
|
39401
39547
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
39402
39548
|
<title>${escapeHtml(initial.app.name)} \xB7 build board</title>
|
|
39549
|
+
<link rel="icon" id="fav" href="data:,">
|
|
39403
39550
|
<style>
|
|
39404
39551
|
: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
39552
|
@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 +39565,7 @@ h1{font-size:20px;font-weight:600;margin:0}.sub{color:var(--muted);font-size:13p
|
|
|
39418
39565
|
h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin:0 0 10px}
|
|
39419
39566
|
.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
39567
|
.phone img,.phone iframe{width:100%;height:100%;object-fit:cover;object-position:top;border:0;display:block;cursor:zoom-in}
|
|
39568
|
+
.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
39569
|
.cap{text-align:center;color:var(--muted);font-size:12px;margin-top:8px}
|
|
39422
39570
|
.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
39571
|
.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 +39580,26 @@ h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;le
|
|
|
39432
39580
|
.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
39581
|
.empty{color:var(--faint);font-size:13px;padding:8px 0}
|
|
39434
39582
|
#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}
|
|
39583
|
+
.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}}
|
|
39584
|
+
.changed .frame{animation:chg .9s ease both}@keyframes chg{35%{opacity:.25;transform:scale(.96)}100%{opacity:1;transform:none}}
|
|
39585
|
+
.step.fresh .bar{animation:fill .7s ease both;transform-origin:left}@keyframes fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
|
|
39586
|
+
.slidein{animation:slide .45s cubic-bezier(.2,.8,.2,1) both}@keyframes slide{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
|
|
39587
|
+
.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}}
|
|
39588
|
+
.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}}
|
|
39589
|
+
.icon,.step.cur .bar{transition:background-color .8s}.sw.pop span{animation:pop .6s ease both}@keyframes pop{50%{transform:scale(1.25)}}
|
|
39590
|
+
.quiet{color:var(--warn)}.quiet .dot{background:var(--warn)}.idle .dot{background:var(--faint)}
|
|
39591
|
+
.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)}
|
|
39592
|
+
#fx{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:8}
|
|
39593
|
+
@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}}
|
|
39594
|
+
@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
39595
|
</style>
|
|
39436
39596
|
</head>
|
|
39437
39597
|
<body>
|
|
39438
39598
|
<div class="wrap">
|
|
39439
39599
|
<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
|
|
39600
|
+
<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>
|
|
39601
|
+
<div><span class="live idle" id="agent"><span class="dot"></span><span id="agentt">Waiting for the agent</span></span></div>
|
|
39602
|
+
<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
39603
|
<div class="steps" id="steps"></div>
|
|
39442
39604
|
<div class="card now"><div style="flex:1"><div class="meta" id="nowmeta">Now</div><div class="msg" id="nowmsg"></div></div></div>
|
|
39443
39605
|
<div class="grid">
|
|
@@ -39450,6 +39612,7 @@ h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;le
|
|
|
39450
39612
|
<div class="card section"><h2>Postcards</h2><ul class="log" id="log"></ul></div>
|
|
39451
39613
|
</div></div></div>
|
|
39452
39614
|
<div id="lb"><img alt=""></div>
|
|
39615
|
+
<canvas id="fx" aria-hidden="true"></canvas>
|
|
39453
39616
|
<script>window.__himiInitial = ${scriptJson(initial)};</script>
|
|
39454
39617
|
<script>
|
|
39455
39618
|
(function(){
|
|
@@ -39464,27 +39627,33 @@ function zoomable(img){img.addEventListener("click",function(){var lb=$("lb");lb
|
|
|
39464
39627
|
function img(src,cls){var i=el("img",cls);i.src=src;i.alt="";i.loading="lazy";zoomable(i);return i}
|
|
39465
39628
|
function chip(txt,kind){return el("span","chip"+(kind?" "+kind:""),txt)}
|
|
39466
39629
|
function render(s){
|
|
39467
|
-
document.title=s.app.name+" \xB7 build board";$("name").textContent=s.app.name;
|
|
39630
|
+
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
39631
|
if(s.app.icon){$("icon").src=s.app.icon}else{$("icon").removeAttribute("src")}
|
|
39469
39632
|
var steps=$("steps");clear(steps);var n=s.current?s.current.stepNumber:0;
|
|
39470
39633
|
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
39634
|
var last=s.journal[0];
|
|
39472
39635
|
$("nowmsg").textContent=last?last.message:"Waiting for the first postcard.";
|
|
39473
39636
|
$("nowmeta").textContent=last?("Now \xB7 "+(last.step?("step "+last.stepNumber+" of 8, "+last.step+" \xB7 "):"")+ago(last.ts)):"Now";
|
|
39474
|
-
var focus=$("focus");
|
|
39475
|
-
|
|
39637
|
+
var focus=$("focus");var film=$("film");clear(film);
|
|
39638
|
+
/* The live frame is KEPT across renders while its URL and its run are unchanged (a restarted run on
|
|
39639
|
+
the same port is a different server, and the old frame would never see its builds). A new iframe is a full page
|
|
39640
|
+
load of the preview: every postcard, rebuild and the once-a-minute re-render used to throw away
|
|
39641
|
+
the in-place repaint, the preview's scroll and wherever the owner had navigated inside it. */
|
|
39642
|
+
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;
|
|
39643
|
+
if(kept){while(focus.lastChild&&focus.lastChild!==kept.parentNode)focus.removeChild(focus.lastChild)}else clear(focus);
|
|
39644
|
+
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
39645
|
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
39646
|
else{$("focuslbl").textContent="Latest";focus.appendChild(el("div","empty","The first render appears here."))}
|
|
39478
39647
|
if(s.focus&&s.focus.frames.length>1)s.focus.frames.forEach(function(fm){film.appendChild(img(fm.img))});
|
|
39479
39648
|
var chips=$("chips");clear(chips);var h=s.health;
|
|
39480
39649
|
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
|
|
39650
|
+
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
39651
|
h.ux.forEach(function(u){chips.appendChild(chip("UX drive "+u.platform+": "+u.status,u.status==="passed"?"ok":u.status==="failed"?"bad":"warn"))});
|
|
39483
39652
|
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
39653
|
if(!chips.firstChild)chips.appendChild(chip("Checks appear here as validate, test and push run"));
|
|
39485
39654
|
var wall=$("wall");clear(wall);
|
|
39486
39655
|
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));
|
|
39656
|
+
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
39657
|
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
39658
|
var brand=$("brand");clear(brand);$("brandcard").style.display=(s.brand.site||s.app.palette.length)?"":"none";
|
|
39490
39659
|
if(s.brand.site){var si=img(s.brand.site,"site");brand.appendChild(si)}
|
|
@@ -39495,14 +39664,61 @@ function render(s){
|
|
|
39495
39664
|
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
39665
|
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
39666
|
}
|
|
39498
|
-
|
|
39667
|
+
/* Motion, sound and notices answer a CHANGE, never a redraw: effects() compares what is on the page
|
|
39668
|
+
now with what it showed last, so opening the board, the once-a-minute redraw and the two-second
|
|
39669
|
+
poll of an unchanged state do nothing, and each effect names a real event. */
|
|
39670
|
+
var seen=null,actx=null,sound=false,notices=false;try{sound=localStorage.getItem("himiStudioSound")==="1";notices=localStorage.getItem("himiStudioNotify")==="1"}catch(e){}
|
|
39671
|
+
var raf=window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(f){return setTimeout(function(){f(Date.now())},16)};
|
|
39672
|
+
function now(){return window.performance&&performance.now?performance.now():Date.now()}
|
|
39673
|
+
function reduced(){try{return matchMedia("(prefers-reduced-motion: reduce)").matches}catch(e){return false}}
|
|
39674
|
+
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){}}
|
|
39675
|
+
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")}};
|
|
39676
|
+
/* Notices are the BOARD's opt-in, not just the browser's: a permission granted to this origin once
|
|
39677
|
+
(an earlier board, another run) must not switch them on, and the toggle must switch them off. */
|
|
39678
|
+
function notify(title,body){try{if(notices&&"Notification" in window&&Notification.permission==="granted"&&document.hidden)new Notification(title,{body:body})}catch(e){}}
|
|
39679
|
+
function accentNow(){return (getComputedStyle(document.documentElement).getPropertyValue("--accent")||"").trim()||"#3b6fd8"}
|
|
39680
|
+
/* The preview lays out at a real phone's viewport (390 x 844 CSS px) and is scaled into the frame.
|
|
39681
|
+
Sized to the frame itself it was ~280px wide: narrower than any phone, so screens overflowed
|
|
39682
|
+
sideways and scrolled horizontally. */
|
|
39683
|
+
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)))}
|
|
39684
|
+
window.addEventListener("resize",fit);
|
|
39685
|
+
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){}}
|
|
39686
|
+
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)})()}
|
|
39687
|
+
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)})()}
|
|
39688
|
+
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}}
|
|
39689
|
+
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}
|
|
39690
|
+
function effects(s){var is=snap(s),was=seen;seen=is;if(is.accent)document.documentElement.style.setProperty("--accent",is.accent);
|
|
39691
|
+
/* The phone (kept across renders) and the Now line outlive a render: clear what they played last. */
|
|
39692
|
+
var ph=$("focus").querySelector(".phone"),nm=$("nowmsg");if(ph)ph.classList.remove("pulse","bad");nm.classList.remove("slidein");if(!was)return;
|
|
39693
|
+
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();
|
|
39694
|
+
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()}
|
|
39695
|
+
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])}
|
|
39696
|
+
/* A rebuild is a later build of the SAME run: a run appearing, or restarting, records its first
|
|
39697
|
+
build too, and nothing was rebuilt then. */
|
|
39698
|
+
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||"")}}
|
|
39699
|
+
if(was.gaps!=null&&is.gaps!=null&&is.gaps!==was.gaps)countTo($("gapn"),was.gaps,is.gaps);
|
|
39700
|
+
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))}
|
|
39701
|
+
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")}
|
|
39702
|
+
}
|
|
39703
|
+
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}
|
|
39704
|
+
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))}
|
|
39705
|
+
function setNotices(v){notices=v;try{localStorage.setItem("himiStudioNotify",v?"1":"0")}catch(e){}paintToggles()}
|
|
39706
|
+
$("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()});
|
|
39707
|
+
/* The click acts on what the button SAYS (the effective state), so a remembered opt-in whose browser
|
|
39708
|
+
permission was since reset reads "off" and one click asks for permission, not two. */
|
|
39709
|
+
$("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){}});
|
|
39710
|
+
document.addEventListener("click",function(){try{if(actx&&actx.state==="suspended")actx.resume()}catch(e){}},{once:true});
|
|
39711
|
+
paintToggles();
|
|
39712
|
+
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
39713
|
$("lb").addEventListener("click",function(){this.style.display="none"});
|
|
39500
39714
|
window.__himiStudio(window.__himiInitial);
|
|
39501
39715
|
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
39716
|
var ticks=0;
|
|
39503
39717
|
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
39718
|
$("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":"")
|
|
39719
|
+
$("updated").textContent="updated "+ago(cur.generatedAt);$("live").className="live"+(Date.now()-lastSeen>10000?" stale":"");
|
|
39720
|
+
var a=activityAt(cur),q=a?Date.now()-a:0,done=cur.current&&cur.current.stepNumber===8;
|
|
39721
|
+
$("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
39722
|
})();
|
|
39507
39723
|
</script>
|
|
39508
39724
|
</body>
|
|
@@ -47241,7 +47457,6 @@ function makeServeHandler(opts) {
|
|
|
47241
47457
|
);
|
|
47242
47458
|
})();
|
|
47243
47459
|
const webwixFetch = opts.webwixFetch ?? fetch;
|
|
47244
|
-
const webwixTokens = new VisitorTokenCache(webwixFetch);
|
|
47245
47460
|
const webwixOwnerInstances = new OwnerInstanceCache(webwixFetch);
|
|
47246
47461
|
const webwixOwnerSites = new OwnerSitesCache(webwixFetch);
|
|
47247
47462
|
const webwixOwnerRungOn = () => opts.webwixOwnerRung !== false;
|
|
@@ -47543,7 +47758,7 @@ ${releaseId}`;
|
|
|
47543
47758
|
const pkce = await generatePkce();
|
|
47544
47759
|
let authUrl;
|
|
47545
47760
|
try {
|
|
47546
|
-
const visitor = await
|
|
47761
|
+
const visitor = await mintAnonymousVisitor(resolved2.config.clientId, webwixFetch);
|
|
47547
47762
|
authUrl = await hostedAuthUrl(
|
|
47548
47763
|
{ ...resolved2.config, mode: path2.endsWith("/register") ? "signup" : "login", state, challenge: pkce.challenge },
|
|
47549
47764
|
visitor,
|
|
@@ -47614,7 +47829,14 @@ ${releaseId}`;
|
|
|
47614
47829
|
const reqPath = String(call.path ?? "/");
|
|
47615
47830
|
const body = call.body === void 0 ? void 0 : typeof call.body === "string" ? call.body : JSON.stringify(call.body);
|
|
47616
47831
|
const forwardedHeaders = normalizeWixForwardedHeaders(call.headers);
|
|
47832
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(reqPath)) {
|
|
47833
|
+
return absoluteUrlRejection(reqPath) === "owner-only" ? json3(501, { error: "webwix_owner_only", app: app2 }) : json3(400, { error: "absolute_urls_not_allowed" });
|
|
47834
|
+
}
|
|
47617
47835
|
const callerCookie = request.headers.get("cookie") ?? "";
|
|
47836
|
+
if (!call.checkOwnerSession && webwixOwnerRungOn() && method === "POST" && isOwnerSitesPath(reqPath) && !/wixSession2=/.test(callerCookie)) {
|
|
47837
|
+
served.rung = "sites";
|
|
47838
|
+
return json3(501, { error: "webwix_owner_no_session", app: app2 });
|
|
47839
|
+
}
|
|
47618
47840
|
if (!call.checkOwnerSession && webwixOwnerRungOn() && method === "POST" && isOwnerSitesPath(reqPath) && /wixSession2=/.test(callerCookie)) {
|
|
47619
47841
|
served.rung = "sites";
|
|
47620
47842
|
try {
|
|
@@ -47633,32 +47855,8 @@ ${releaseId}`;
|
|
|
47633
47855
|
}
|
|
47634
47856
|
if (call.ownerMsid !== void 0) {
|
|
47635
47857
|
served.rung = call.checkOwnerSession === true ? "gate" : "owner";
|
|
47636
|
-
if (!webwixOwnerRungOn()) {
|
|
47637
|
-
|
|
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
|
-
}
|
|
47858
|
+
if (!webwixOwnerRungOn()) return json3(503, { error: "webwix_owner_disabled" });
|
|
47859
|
+
return json3(501, { error: "webwix_owner_direct_only", app: app2 });
|
|
47662
47860
|
}
|
|
47663
47861
|
const resolved2 = wixClientIdOf(utf8Decode(cfgBytes));
|
|
47664
47862
|
if ("error" in resolved2) {
|
|
@@ -47675,28 +47873,11 @@ ${releaseId}`;
|
|
|
47675
47873
|
try {
|
|
47676
47874
|
return json3(200, await wixVisitorCall(method, reqPath, body, memberToken, webwixFetch, forwardedHeaders), { "cache-control": "no-store" });
|
|
47677
47875
|
} catch (e) {
|
|
47678
|
-
|
|
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) });
|
|
47876
|
+
return json3(502, { error: "webwix_bridge_upstream_failed", detail: String(e?.message ?? e).slice(0, 200) });
|
|
47683
47877
|
}
|
|
47684
47878
|
}
|
|
47685
47879
|
}
|
|
47686
|
-
|
|
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
|
-
}
|
|
47880
|
+
return json3(501, { error: "webwix_visitor_direct_only", app: app2 });
|
|
47700
47881
|
})();
|
|
47701
47882
|
status = res.status;
|
|
47702
47883
|
return res;
|
|
@@ -48280,7 +48461,7 @@ function indexHtml(asset, pathname, creds, handoverAttempted = false) {
|
|
|
48280
48461
|
void pathname;
|
|
48281
48462
|
const nonce = scriptNonce();
|
|
48282
48463
|
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>` : "");
|
|
48464
|
+
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
48465
|
const html = raw.replace(/(=|url\()(["']?)\/preview\//g, "$1$2").replace(/<head(\s[^>]*)?>/i, (tag) => `${tag}${head}`);
|
|
48285
48466
|
return new Response(html, {
|
|
48286
48467
|
status: 200,
|
|
@@ -48332,7 +48513,7 @@ function previewResponse(pathname, assets, mount = "/preview", creds, handoverAt
|
|
|
48332
48513
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
48333
48514
|
});
|
|
48334
48515
|
}
|
|
48335
|
-
var T5_FRAME_CSP, PREVIEW_BASE_JS, MOUNT_STRIP;
|
|
48516
|
+
var T5_FRAME_CSP, PREVIEW_BASE_JS, PREVIEW_ENTRY_RECOVERY_JS, MOUNT_STRIP;
|
|
48336
48517
|
var init_preview2 = __esm({
|
|
48337
48518
|
"../../packages/serve/src/preview.ts"() {
|
|
48338
48519
|
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 +48522,7 @@ var init_preview2 = __esm({
|
|
|
48341
48522
|
// the EARLIEST mount segment wins, mirroring the SPA's own deployBase (an app literally
|
|
48342
48523
|
// named "app" — /preview/a/app — must not hijack the base).
|
|
48343
48524
|
'(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)})();';
|
|
48525
|
+
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
48526
|
MOUNT_STRIP = {
|
|
48345
48527
|
// Precompiled — this runs on every request of the public serving hot path.
|
|
48346
48528
|
"/preview": /^\/preview\/?/,
|
|
@@ -48587,21 +48769,21 @@ var init_preview_assets_generated = __esm({
|
|
|
48587
48769
|
"../../packages/serve/src/preview-assets.generated.ts"() {
|
|
48588
48770
|
init_preview_assets_format();
|
|
48589
48771
|
PREVIEW_ASSET_MANIFEST = [
|
|
48590
|
-
{ key: "assets/index-
|
|
48591
|
-
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset:
|
|
48592
|
-
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset:
|
|
48593
|
-
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset:
|
|
48594
|
-
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
48595
|
-
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset:
|
|
48596
|
-
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset:
|
|
48597
|
-
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
48598
|
-
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset:
|
|
48599
|
-
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
48600
|
-
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset:
|
|
48601
|
-
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset:
|
|
48602
|
-
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
48603
|
-
{ key: "index.html", contentType: "text/html; charset=utf-8", offset:
|
|
48604
|
-
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset:
|
|
48772
|
+
{ key: "assets/index-hHpGDjBZ.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1038931, sha256: "65ef206c528bdc3e9b7ec6907e734267cd225ed33513982c46ecb3bbdd6d44ee" },
|
|
48773
|
+
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1038939, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
|
|
48774
|
+
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1043549, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
|
|
48775
|
+
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1532133, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
|
|
48776
|
+
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1623057, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
|
|
48777
|
+
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1714041, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
|
|
48778
|
+
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1805021, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
|
|
48779
|
+
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1895893, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
|
|
48780
|
+
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1986873, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
|
|
48781
|
+
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2095509, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
|
|
48782
|
+
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2204593, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
|
|
48783
|
+
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2313641, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
|
|
48784
|
+
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2421341, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
|
|
48785
|
+
{ key: "index.html", contentType: "text/html; charset=utf-8", offset: 2509449, length: 4947, sha256: "f07de6435f49d7608a56b7af529229f2ccc10901ab33a9e600e01f10b60975d6" },
|
|
48786
|
+
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2514396, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
|
|
48605
48787
|
];
|
|
48606
48788
|
}
|
|
48607
48789
|
});
|
|
@@ -63412,9 +63594,10 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
|
|
|
63412
63594
|
(--ai writes rows for you to review first)
|
|
63413
63595
|
himi secrets set|list|rotate --app <app> --name <name> [--environment preview|production]
|
|
63414
63596
|
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>]
|
|
63597
|
+
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
63598
|
[--remote --app <app> --release <id>]
|
|
63417
63599
|
headless web render + diagnostics (no sim). Default: local content dir.
|
|
63600
|
+
A bare --png writes .himi/shots/<screenId>.png (what the build board shows).
|
|
63418
63601
|
--remote fetches GET /v1/preview/\u2026/render from the serve plane (post-push).
|
|
63419
63602
|
It returns the pushed descriptor STRUCTURE only \u2014 NO png and NO live data
|
|
63420
63603
|
(the serve has no headless browser); for a pixel view or REAL fetched data
|
|
@@ -63593,7 +63776,9 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
|
|
|
63593
63776
|
plane, no dev-server. With no argument it packs the current
|
|
63594
63777
|
content package first \u2014 edit, run, look at it.
|
|
63595
63778
|
--watch rebuilds on every save and repaints the open tab
|
|
63596
|
-
(a failed build keeps the last good preview).
|
|
63779
|
+
(a failed build keeps the last good preview). The build board
|
|
63780
|
+
is served beside it at /__himi/studio/ with the preview framed
|
|
63781
|
+
in it. Blocks until Ctrl-C.
|
|
63597
63782
|
himi flags list|set|ramp|rm [<key>] [--app <a>]
|
|
63598
63783
|
read/change operational flags (RFC 0013); writes report
|
|
63599
63784
|
servedNow from the device door, never just ok
|
|
@@ -63920,7 +64105,7 @@ var COMMANDS = [
|
|
|
63920
64105
|
{ 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
64106
|
{ 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
64107
|
{ 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
|
|
64108
|
+
{ 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
64109
|
{ 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
64110
|
{ 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
64111
|
{ 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 +68632,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
68447
68632
|
current = fresh;
|
|
68448
68633
|
servedMissing = outcome.missingBundles;
|
|
68449
68634
|
for (const [event, data] of watchKit.reloadEvents(diff)) bus.publish(event, data);
|
|
68450
|
-
record2(running, { ok: true, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
68635
|
+
record2(running, { ok: true, at: (/* @__PURE__ */ new Date()).toISOString(), ms: Date.now() - t0, screens: diff.screens, tokens: diff.tokens, config: diff.config });
|
|
68451
68636
|
await refreshBoard();
|
|
68452
68637
|
io.error(JSON.stringify({ watch: "rebuilt", ms: Date.now() - t0, screens: diff.screens, tokens: diff.tokens, config: diff.config, tabs: bus.listeners }));
|
|
68453
68638
|
} catch (e) {
|