@wix/himalaya-cli 0.1078.0 → 0.1080.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/builder.d.ts +2 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/define-app.d.ts +9 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +41 -1
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/site-shell.d.ts +0 -14
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/validator.d.ts +36 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/rich-content/src/display.d.ts +26 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/rich-content/src/index.d.ts +79 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +26 -49
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +11 -3
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-rich-content.mjs +344 -0
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/package.json +4 -0
  12. package/dist/cli.mjs +104 -7
  13. package/guides/importable-sdks.md +1 -0
  14. package/guides/sdk-reference.json +74 -1
  15. package/guides/splash-screens.md +13 -1
  16. package/package.json +2 -2
  17. package/templates/beauty-salon-owner/tier5-src/_shared/owner-post-content.ts +7 -26
  18. package/templates/beauty-salon-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
  19. package/templates/community-club-owner/tier5-src/_shared/owner-post-content.ts +7 -26
  20. package/templates/community-club-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
  21. package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
  22. package/templates/events-meetups-owner/tier5-src/_shared/owner-post-content.ts +7 -26
  23. package/templates/events-meetups-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
  24. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
  25. package/templates/gym-fitness-owner/tier5-src/_shared/owner-post-content.ts +7 -26
  26. package/templates/gym-fitness-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
  27. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
  28. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-post-content.ts +7 -26
  29. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
  30. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
  31. package/templates/owner-lite/tier5-src/_shared/owner-post-content.ts +7 -26
  32. package/templates/owner-lite/tier5-src/_shared/owner-rich-guard.ts +12 -41
  33. package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
  34. package/templates/podcast-creator-owner/tier5-src/_shared/owner-post-content.ts +7 -26
  35. package/templates/podcast-creator-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
  36. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
  37. package/templates/portfolio-creative-owner/tier5-src/_shared/owner-post-content.ts +7 -26
  38. package/templates/portfolio-creative-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
  39. package/templates/restaurant-owner/tier5-src/_shared/owner-post-content.ts +7 -26
  40. package/templates/restaurant-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
  41. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
  42. package/templates/retail-store-owner/tier5-src/_shared/owner-post-content.ts +7 -26
  43. package/templates/retail-store-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
  44. package/templates/yoga-studio-owner/tier5-src/_shared/owner-post-content.ts +7 -26
  45. package/templates/yoga-studio-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
  46. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
@@ -134,6 +134,8 @@ export interface WebViewConfig {
134
134
  internalLinksAsScreens?: boolean;
135
135
  respectHttpCache?: boolean;
136
136
  coverBandColor?: string;
137
+ hideWixAds?: boolean;
138
+ hideHostedSiteFooter?: boolean;
137
139
  }
138
140
  export declare function vstack(props: {
139
141
  spacing?: number;
@@ -246,6 +246,15 @@ export interface AppConfig {
246
246
  * headroom, then move within it OTA. Full rationale: docs/orientation.md.
247
247
  */
248
248
  orientationEnvelope?: OrientationEnvelope;
249
+ /**
250
+ * RFC 0063 §9 — ship the Ricos engine page (`core/rich-content-editor/dist/editor.html`, several MB)
251
+ * inside this app's binaries, so its `RichContentEditor` screens can edit. `tools/native-config`
252
+ * writes the iOS/macOS folder reference; `scripts/gen.sh` copies the page into
253
+ * `localweb/ricos/editor.html` (iOS/macOS bundle) and `assets/localweb/ricos/editor.html`
254
+ * (Android). Absent/false ⇒ nothing is baked and an editor renders its `fallback`.
255
+ * Build-time only — cannot be switched on OTA.
256
+ */
257
+ richContentEditor?: boolean;
249
258
  /** Path to `tokens.json` (resolved relative to config.ts). */
250
259
  designTokensPath: string;
251
260
  /** Resolved tokens — populated by the loader, not the caller. */
@@ -2708,7 +2708,12 @@ export interface SplashConfig {
2708
2708
  /** Hard ceiling on the readiness hold, ms — a stuck boot can never wedge the
2709
2709
  * UI. 0 / absent ⇒ 3000. Clamped client-side to [0, 8000]. */
2710
2710
  maxHoldMs?: number;
2711
+ /** RFC 0062 — what readiness waits for beyond the seed. Absent / "" ⇒ seeded is
2712
+ * enough. "first_content" ⇒ also the first WebView page (a site shell), still
2713
+ * under `maxHoldMs`. Unknown coalesces to "" on device. */
2714
+ readySignal?: SplashReadySignal;
2711
2715
  }
2716
+ export type SplashReadySignal = "" | "first_content";
2712
2717
  export interface DeepLinkPattern {
2713
2718
  /** e.g. "wix-owner://blog/post/:id" */
2714
2719
  pattern: string;
@@ -2913,9 +2918,44 @@ export interface WebViewConfig {
2913
2918
  * cover is recreated on every present, so without this each open is a cold reload
2914
2919
  * (multi-second for a heavy SPA). With it, the host caches the WebView per screenId
2915
2920
  * (LRU) and re-shows the already-loaded page instantly — in-app login/session and
2916
- * scroll position are preserved. iOS-implemented; Android ignores it (cold-loads) for now.
2921
+ * scroll position are preserved. Both platforms (WebViewKeepAliveStore, keyed by screen id).
2917
2922
  */
2918
2923
  keepAlive?: boolean;
2924
+ /**
2925
+ * Pin an uncooperative hosted site's own header (`position: relative` on mobile, so it
2926
+ * scrolls away) to the top while the page scrolls — an app-like sticky menu. Injected CSS
2927
+ * makes it `position: sticky` under the safe-area inset with an opaque background.
2928
+ */
2929
+ pinHostedHeader?: boolean;
2930
+ /**
2931
+ * Turn internal navigations (real links + Wix SPA pushState routes) into native pushed
2932
+ * WebView screens with native back/close, kept warm in an LRU pool. Enable only on
2933
+ * hosted-site covers. See docs/webview-internal-links-as-screens.md.
2934
+ */
2935
+ internalLinksAsScreens?: boolean;
2936
+ /**
2937
+ * Honour the origin's HTTP caching (revalidate per Cache-Control). Default prefers a cached
2938
+ * copy for a faster load; set true for screens that must always be fresh. An explicit
2939
+ * `cacheMode` still wins.
2940
+ */
2941
+ respectHttpCache?: boolean;
2942
+ /**
2943
+ * Fallback band colour (any CSS colour). The dynamic band reads the site's own
2944
+ * `<meta name="theme-color">`; this colour is used until/when the site declares none. On
2945
+ * web (a cross-origin iframe can't be read) it is the only band source.
2946
+ */
2947
+ coverBandColor?: string;
2948
+ /**
2949
+ * RFC 0062 — hide the Wix free-site ads banner (`WIX_ADS`) and keep it hidden as the page
2950
+ * mutates. The script ships in the app binary; only this flag travels over the air, so an
2951
+ * owner can turn ads back on without a new build. No-op on web (cross-origin iframe).
2952
+ */
2953
+ hideWixAds?: boolean;
2954
+ /**
2955
+ * RFC 0062 — hide the hosted site's OWN footer (the native chrome replaces it). Binary-shipped
2956
+ * script, like {@link hideHostedSiteHeader}. No-op on web (cross-origin iframe).
2957
+ */
2958
+ hideHostedSiteFooter?: boolean;
2919
2959
  }
2920
2960
  /**
2921
2961
  * Embeddable WebView component (web-in-SDUI). WebViews have no intrinsic height,
@@ -87,20 +87,6 @@ export declare function siteAllowedOrigins(origin: string, kind: SiteKind): stri
87
87
  export declare function sitePageUrl(origin: string, path: string): string;
88
88
  /** `site_<page.id>`. */
89
89
  export declare function siteScreenId(page: Pick<SitePage, "id">): string;
90
- /**
91
- * Hide the hosted site's footer and/or the Wix ads banner, and keep them hidden as the
92
- * page mutates. Ported from `generateElementHidingScript` in the Wix RN app
93
- * (`@wix/hybrid-mobile-platform`), minus the header branch — header hiding is the
94
- * native `hideHostedSiteHeader` path, which keeps the home page's header.
95
- *
96
- * Interim delivery: this rides `injectJsDocumentEnd`. RFC 0062 phase 2 replaces it
97
- * with a binary-shipped adapter selected by id (`hostedSiteAdapter` + `hideWixAds`),
98
- * so no raw page script travels over the air.
99
- */
100
- export declare function siteChromeHidingScript(options: {
101
- hideFooter: boolean;
102
- hideWixAds: boolean;
103
- }): string;
104
90
  /** Resolved chrome flags — every absent flag is `true`. */
105
91
  export declare function resolveSiteChrome(chrome: SiteChrome | undefined): Required<SiteChrome>;
106
92
  /**
@@ -1033,6 +1033,24 @@ export declare const screenDescriptorSchema: {
1033
1033
  readonly keepAlive: {
1034
1034
  readonly type: "boolean";
1035
1035
  };
1036
+ readonly pinHostedHeader: {
1037
+ readonly type: "boolean";
1038
+ };
1039
+ readonly internalLinksAsScreens: {
1040
+ readonly type: "boolean";
1041
+ };
1042
+ readonly respectHttpCache: {
1043
+ readonly type: "boolean";
1044
+ };
1045
+ readonly coverBandColor: {
1046
+ readonly type: "string";
1047
+ };
1048
+ readonly hideWixAds: {
1049
+ readonly type: "boolean";
1050
+ };
1051
+ readonly hideHostedSiteFooter: {
1052
+ readonly type: "boolean";
1053
+ };
1036
1054
  };
1037
1055
  };
1038
1056
  };
@@ -1976,6 +1994,24 @@ export declare const widgetSnapshotSchema: {
1976
1994
  readonly keepAlive: {
1977
1995
  readonly type: "boolean";
1978
1996
  };
1997
+ readonly pinHostedHeader: {
1998
+ readonly type: "boolean";
1999
+ };
2000
+ readonly internalLinksAsScreens: {
2001
+ readonly type: "boolean";
2002
+ };
2003
+ readonly respectHttpCache: {
2004
+ readonly type: "boolean";
2005
+ };
2006
+ readonly coverBandColor: {
2007
+ readonly type: "string";
2008
+ };
2009
+ readonly hideWixAds: {
2010
+ readonly type: "boolean";
2011
+ };
2012
+ readonly hideHostedSiteFooter: {
2013
+ readonly type: "boolean";
2014
+ };
1979
2015
  };
1980
2016
  };
1981
2017
  };
@@ -0,0 +1,26 @@
1
+ export type ImageHolder = {
2
+ image?: {
3
+ src?: {
4
+ url?: unknown;
5
+ id?: unknown;
6
+ };
7
+ };
8
+ } | undefined;
9
+ /** Trims ASCII whitespace only — the set every platform's trim agrees on. Used for LINKS: the viewer applies the
10
+ * link rule to the value itself, so anything else at the ends (a BOM, a no-break space) stays and the rule then
11
+ * refuses it, rather than one renderer's trim accepting it. Media references are instead normalized by
12
+ * normalizeMediaRef, which trims fully: the encoder repairs media (ids resolved, Wix urls sized and upgraded) and
13
+ * emits the normalized value, so the url a renderer later checks carries no padding for any trim to disagree on. */
14
+ export declare function trimAscii(s: string): string;
15
+ /** A link as the viewer emits it: the trimmed value when it passes the link rule (http(s) with a host, mailto,
16
+ * tel), undefined when it does not. */
17
+ export declare function cleanLink(url: unknown): string | undefined;
18
+ /** The sized https URL for the image a Ricos holder points at (`imageData`, `{ image: galleryItem.image.media }`,
19
+ * `{ image: videoData.thumbnail }`): a Wix id or Wix URL is sized to `width` by wixMediaUrl, other https URLs pass
20
+ * through unchanged, anything else (non-Wix http, data:, relative, unknown) is "". */
21
+ export declare function ricosImageUrl(holder: ImageHolder, width: number): string;
22
+ /** A URL the viewer can show for a Ricos video `src`, decided in the viewer's order: an http(s) YouTube or Vimeo
23
+ * link that passes the link rule is a card (its url a link, ASCII-trimmed); any other https url is played natively
24
+ * (media, normalized); a Wix video id becomes its video.wixstatic.com file. "" otherwise — including a card-host
25
+ * url that fails the link rule, which never gets a second chance as media. */
26
+ export declare function ricosVideoUrl(src: unknown): string;
@@ -0,0 +1,79 @@
1
+ /** One Ricos node: a block (PARAGRAPH, HEADING, IMAGE, …) or an inline TEXT run. Type-specific data
2
+ * (`imageData`, `headingData`, …) rides along as extra keys. */
3
+ export type RicosNode = {
4
+ type?: string;
5
+ id?: string;
6
+ nodes?: RicosNode[];
7
+ textData?: {
8
+ text?: string;
9
+ decorations?: {
10
+ type: string;
11
+ linkData?: {
12
+ link?: {
13
+ url?: string;
14
+ };
15
+ };
16
+ }[];
17
+ };
18
+ [k: string]: unknown;
19
+ };
20
+ /** A Ricos document with its `nodes` checked to be an array (what parseRicosJson and plainTextToRicos return). */
21
+ export type RicosDocument = {
22
+ nodes: RicosNode[];
23
+ metadata?: unknown;
24
+ documentStyle?: unknown;
25
+ };
26
+ /** Any Ricos-shaped value, as Wix APIs return it (a blog post's `richContent`, an event's description) — their
27
+ * generated types declare `nodes` loosely, so the helpers accept it as is and check the shape at runtime. */
28
+ export type RicosInput = {
29
+ nodes?: unknown;
30
+ metadata?: unknown;
31
+ documentStyle?: unknown;
32
+ };
33
+ /** Why a document is not shown: the viewer draws its fallback instead and reports only this code. */
34
+ export type RejectReason = "invalid_json" | "too_large" | "too_many_nodes" | "too_deep" | "not_a_document";
35
+ /** The RichContent viewer's ceilings; every renderer rejects a document past any of them. Kept equal to the platform limits file by a test. */
36
+ export declare const RICH_CONTENT_LIMITS: {
37
+ readonly maxBytes: 262144;
38
+ readonly maxNodes: 5000;
39
+ readonly maxDepth: 16;
40
+ };
41
+ /** Reads a Ricos document from a JSON string or an already-parsed object (page messages arrive as either,
42
+ * depending on the platform). Returns null for empty input, invalid JSON, or anything without a `nodes` array. */
43
+ export declare function parseRicosJson(value: unknown): RicosDocument | null;
44
+ /** Plain text of a document. Paragraphs, headings and loose text runs are blocks joined by a blank line; runs
45
+ * inside a block join with nothing between them, so a word split across bold and plain runs stays one word.
46
+ * Media and other nodes contribute only the text nested inside them. Malformed parts (a non-object node, a
47
+ * `nodes` that is not an array) contribute nothing — it never throws. */
48
+ export declare function ricosToPlainText(doc: RicosInput | null | undefined): string;
49
+ /** A document of undecorated paragraphs, one per blank-line-separated block of `text` — the inverse of
50
+ * ricosToPlainText for plain text. An empty string gives one empty paragraph. */
51
+ export declare function plainTextToRicos(text: string): RicosDocument;
52
+ /** True when a plain-text editor can rewrite the document without losing anything: no document styling, and
53
+ * only paragraphs of undecorated text runs whose paragraph settings are the defaults a plain paragraph carries
54
+ * (`{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }`, or none). A heading, list, link, bold run,
55
+ * media node, alignment or indent makes it rich. A missing or empty document (no `nodes`, or `[]`) is plain —
56
+ * there is nothing in it to lose. Anything malformed is not: a value that is not a document object (a JSON
57
+ * string, an array), or `nodes` that is not an array, so a caller holding the wrong shape never overwrites it. */
58
+ export declare function isPlainRicos(doc: RicosInput | null | undefined): boolean;
59
+ /** True when an HTML description has no formatting to protect: empty, text with no tag in it (a lone `<` in
60
+ * prose is text), or exactly one attribute-free `<p>` around text with no inner tags. A form that edits plain
61
+ * text may overwrite only these. */
62
+ export declare function htmlIsTrivial(html: string | null | undefined): boolean;
63
+ /** Prepares a Ricos document for the RichContent viewer; bind the returned `json` as `documentJsonPath`.
64
+ * Wix media ids become sized https URLs (`imageWidth`, default and maximum 1400 px; galleries and posters at most
65
+ * 1000). Block ids follow the viewer's own rule on the encoded document — a block's missing, all-digits-and-dots
66
+ * or repeated id becomes its path index there ("0.2.1") — so every renderer shows the same ids and encoding twice
67
+ * changes nothing. Images, galleries and videos without https media (an http YouTube or Vimeo link is kept: the
68
+ * viewer shows it as a card), buttons without an http(s)/mailto/tel link, any other link url with a disallowed
69
+ * scheme (link decorations, card and image links) and inline HTML (`htmlData.html`, an embed's `oembed.html`) are
70
+ * removed; an allowed link is emitted trimmed of surrounding whitespace, as the viewer emits it. Text is never
71
+ * changed. Keys are sorted, so equal documents encode to equal strings. The input is not
72
+ * modified. A missing or empty document gives `{ json: "" }`; a non-document or one past the viewer's limits gives
73
+ * `{ json: "", rejected }` (checked in the order not_a_document, too_deep, too_large, too_many_nodes). */
74
+ export declare function encodeRichContent(doc: RicosInput | null | undefined, opts?: {
75
+ imageWidth?: number;
76
+ }): {
77
+ json: string;
78
+ rejected?: RejectReason;
79
+ };
@@ -7493,6 +7493,9 @@ function validateConfigShape(config) {
7493
7493
  throw new Error("defineApp: tier5Cap.maxBundleKilobytes must be positive.");
7494
7494
  }
7495
7495
  }
7496
+ if (config.richContentEditor !== void 0 && typeof config.richContentEditor !== "boolean") {
7497
+ throw new Error("defineApp: richContentEditor must be a boolean (RFC 0063 \xA79 opt-in).");
7498
+ }
7496
7499
  if (config.workerSubtreeScreens) {
7497
7500
  const seen = /* @__PURE__ */ new Set();
7498
7501
  for (const screenId of config.workerSubtreeScreens) {
@@ -7603,6 +7606,7 @@ function validateConfigShape(config) {
7603
7606
  var SPLASH_MIN_VISIBLE_MS_MAX = 2e3;
7604
7607
  var SPLASH_MAX_HOLD_MS_MAX = 8e3;
7605
7608
  var SPLASH_LAYOUTS = ["centered", "centeredStacked", "logoOnly"];
7609
+ var SPLASH_READY_SIGNALS = ["", "first_content"];
7606
7610
  function validateA11y(a11y) {
7607
7611
  if (!a11y || typeof a11y !== "object" || Array.isArray(a11y)) {
7608
7612
  throw new Error('defineApp: a11y must be an object like { enforce: "warn" }.');
@@ -7736,6 +7740,11 @@ function validateSplash(splash, colors, tokensPath) {
7736
7740
  `defineApp: splash.layout "${splash.layout}" is not one of ${SPLASH_LAYOUTS.join(", ")}.`
7737
7741
  );
7738
7742
  }
7743
+ if (splash.readySignal !== void 0 && !SPLASH_READY_SIGNALS.includes(splash.readySignal)) {
7744
+ throw new Error(
7745
+ `defineApp: splash.readySignal "${splash.readySignal}" is not one of "", "first_content".`
7746
+ );
7747
+ }
7739
7748
  for (const [field, value, max] of [
7740
7749
  ["logoWidth", splash.logoWidth, 1024],
7741
7750
  ["minVisibleMs", splash.minVisibleMs, SPLASH_MIN_VISIBLE_MS_MAX],
@@ -8531,9 +8540,17 @@ var screenDescriptorSchema = {
8531
8540
  hideHostedSiteHeader: { type: "boolean" },
8532
8541
  immersive: { type: "boolean" },
8533
8542
  showCloseButton: { type: "boolean" },
8534
- // Keep the native WebView instance warm across re-presentations (covers reload cold
8535
- // otherwise). iOS-implemented; Android ignores it (cold-loads) for now.
8536
- keepAlive: { type: "boolean" }
8543
+ // Keep the native WebView instance warm across re-presentations and tab switches
8544
+ // (per screen id, LRU — WebViewKeepAliveStore on both platforms).
8545
+ keepAlive: { type: "boolean" },
8546
+ // Hosted-site polish — native hosts honoured these before the validator knew them.
8547
+ pinHostedHeader: { type: "boolean" },
8548
+ internalLinksAsScreens: { type: "boolean" },
8549
+ respectHttpCache: { type: "boolean" },
8550
+ coverBandColor: { type: "string" },
8551
+ // RFC 0062 — binary-shipped chrome hiding for a hosted site (no page script over the air).
8552
+ hideWixAds: { type: "boolean" },
8553
+ hideHostedSiteFooter: { type: "boolean" }
8537
8554
  }
8538
8555
  }
8539
8556
  }
@@ -13031,47 +13048,6 @@ function sitePageUrl(origin, path) {
13031
13048
  function siteScreenId(page) {
13032
13049
  return `${SITE_SCREEN_PREFIX}${page.id}`;
13033
13050
  }
13034
- function siteChromeHidingScript(options) {
13035
- if (!options.hideFooter && !options.hideWixAds) return "";
13036
- return `(function(){
13037
- if (window.__himiSiteChromeHider) return;
13038
- window.__himiSiteChromeHider = true;
13039
- var HIDE_FOOTER = ${options.hideFooter ? "true" : "false"};
13040
- var HIDE_ADS = ${options.hideWixAds ? "true" : "false"};
13041
- function hide(el, collapse) {
13042
- if (!el) return;
13043
- el.style.display = 'none';
13044
- el.style.visibility = 'hidden';
13045
- if (collapse) { el.style.height = '0px'; el.style.overflow = 'hidden'; }
13046
- }
13047
- function pass() {
13048
- if (HIDE_FOOTER) {
13049
- hide(document.getElementsByTagName('footer')[0] ||
13050
- document.querySelector('footer[id^="comp-"]') ||
13051
- document.querySelector('[class*="wixui-footer"]') ||
13052
- document.querySelector('[data-mesh-id*="SITE_FOOTER"]') ||
13053
- document.querySelector('[id*="SITE_FOOTER"]'), false);
13054
- }
13055
- if (HIDE_ADS) {
13056
- hide(document.getElementById('WIX_ADS') ||
13057
- document.querySelector('.WIX_ADS') ||
13058
- document.querySelector('[class*="WIX_ADS"]'), true);
13059
- }
13060
- }
13061
- var scheduled = false;
13062
- function schedule() {
13063
- if (scheduled) return;
13064
- scheduled = true;
13065
- requestAnimationFrame(function(){ scheduled = false; pass(); });
13066
- }
13067
- function start() {
13068
- pass();
13069
- new MutationObserver(schedule).observe(document.documentElement, { childList: true, subtree: true });
13070
- }
13071
- if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start);
13072
- else start();
13073
- })();`;
13074
- }
13075
13051
  function resolveSiteChrome(chrome) {
13076
13052
  return {
13077
13053
  hideSiteHeader: chrome?.hideSiteHeader ?? true,
@@ -13081,11 +13057,15 @@ function resolveSiteChrome(chrome) {
13081
13057
  }
13082
13058
  function siteWebViewPreset(site) {
13083
13059
  const chrome = resolveSiteChrome(site.chrome);
13084
- const hidingScript = site.kind === "wix" ? siteChromeHidingScript({ hideFooter: chrome.hideSiteFooter, hideWixAds: chrome.hideWixAds }) : siteChromeHidingScript({ hideFooter: chrome.hideSiteFooter, hideWixAds: false });
13085
- const config = {
13060
+ return {
13086
13061
  layout: "inset",
13087
13062
  immersive: true,
13063
+ // Chrome hiding is binary-shipped (RFC 0062): only these flags travel over the air, so an
13064
+ // owner can flip one without a new build, and no page script is served OTA (invariant 15).
13088
13065
  hideHostedSiteHeader: chrome.hideSiteHeader,
13066
+ hideHostedSiteFooter: chrome.hideSiteFooter,
13067
+ // Wix ads only exist on Wix free sites; a Base44 site never gets the Wix selectors.
13068
+ hideWixAds: site.kind === "wix" && chrome.hideWixAds,
13089
13069
  pullToRefresh: true,
13090
13070
  showProgressBar: true,
13091
13071
  syncStatusBarAppearance: true,
@@ -13101,8 +13081,6 @@ function siteWebViewPreset(site) {
13101
13081
  // platforms; see SITE_SHELL_MAX_TABS for why the tab count stays within the store's cap).
13102
13082
  keepAlive: true
13103
13083
  };
13104
- if (hidingScript) config.injectJsDocumentEnd = hidingScript;
13105
- return config;
13106
13084
  }
13107
13085
  function defineSiteTab(site, page) {
13108
13086
  assertSitePage(page);
@@ -13282,7 +13260,6 @@ export {
13282
13260
  selectIdiomOverlay,
13283
13261
  shouldRevealError,
13284
13262
  siteAllowedOrigins,
13285
- siteChromeHidingScript,
13286
13263
  siteHost,
13287
13264
  siteNav,
13288
13265
  sitePageUrl,
@@ -7572,9 +7572,17 @@ var screenDescriptorSchema = {
7572
7572
  hideHostedSiteHeader: { type: "boolean" },
7573
7573
  immersive: { type: "boolean" },
7574
7574
  showCloseButton: { type: "boolean" },
7575
- // Keep the native WebView instance warm across re-presentations (covers reload cold
7576
- // otherwise). iOS-implemented; Android ignores it (cold-loads) for now.
7577
- keepAlive: { type: "boolean" }
7575
+ // Keep the native WebView instance warm across re-presentations and tab switches
7576
+ // (per screen id, LRU — WebViewKeepAliveStore on both platforms).
7577
+ keepAlive: { type: "boolean" },
7578
+ // Hosted-site polish — native hosts honoured these before the validator knew them.
7579
+ pinHostedHeader: { type: "boolean" },
7580
+ internalLinksAsScreens: { type: "boolean" },
7581
+ respectHttpCache: { type: "boolean" },
7582
+ coverBandColor: { type: "string" },
7583
+ // RFC 0062 — binary-shipped chrome hiding for a hosted site (no page script over the air).
7584
+ hideWixAds: { type: "boolean" },
7585
+ hideHostedSiteFooter: { type: "boolean" }
7578
7586
  }
7579
7587
  }
7580
7588
  }