@rdlabo/ionic-theme-ios27 1.2.0 → 1.2.1-beta.pr273.shae866fb1dca9f

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 (121) hide show
  1. package/dist/css/components/ion-button.css +1 -1
  2. package/dist/css/components/ion-toolbar.css +1 -1
  3. package/dist/css/ionic-theme-ios27.css +1 -1
  4. package/dist/css/vertical-bars.css +1 -1
  5. package/dist/native/components/index.js +2 -2
  6. package/dist/native/components/index.js.map +1 -1
  7. package/dist/native/definitions.d.ts +32 -0
  8. package/dist/native/definitions.d.ts.map +1 -1
  9. package/dist/native/index.d.ts.map +1 -1
  10. package/dist/native/index.js +23 -6
  11. package/dist/native/index.js.map +1 -1
  12. package/dist/native/overlays/content.d.ts +4 -0
  13. package/dist/native/overlays/content.d.ts.map +1 -0
  14. package/dist/native/overlays/content.js +51 -0
  15. package/dist/native/overlays/content.js.map +1 -0
  16. package/dist/native/overlays/controller.d.ts +8 -0
  17. package/dist/native/overlays/controller.d.ts.map +1 -0
  18. package/dist/native/overlays/controller.js +266 -0
  19. package/dist/native/overlays/controller.js.map +1 -0
  20. package/dist/native/overlays/dialog-animation.d.ts +8 -0
  21. package/dist/native/overlays/dialog-animation.d.ts.map +1 -0
  22. package/dist/native/overlays/dialog-animation.js +31 -0
  23. package/dist/native/overlays/dialog-animation.js.map +1 -0
  24. package/dist/native/overlays/dismiss-animation.d.ts +4 -0
  25. package/dist/native/overlays/dismiss-animation.d.ts.map +1 -0
  26. package/dist/native/overlays/dismiss-animation.js +18 -0
  27. package/dist/native/overlays/dismiss-animation.js.map +1 -0
  28. package/dist/native/overlays/focus.d.ts +5 -0
  29. package/dist/native/overlays/focus.d.ts.map +1 -0
  30. package/dist/native/overlays/focus.js +59 -0
  31. package/dist/native/overlays/focus.js.map +1 -0
  32. package/dist/native/overlays/popover.d.ts +11 -0
  33. package/dist/native/overlays/popover.d.ts.map +1 -0
  34. package/dist/native/overlays/popover.js +78 -0
  35. package/dist/native/overlays/popover.js.map +1 -0
  36. package/dist/native/overlays/styles.d.ts +9 -0
  37. package/dist/native/overlays/styles.d.ts.map +1 -0
  38. package/dist/native/overlays/styles.js +82 -0
  39. package/dist/native/overlays/styles.js.map +1 -0
  40. package/dist/native/prehide.d.ts.map +1 -1
  41. package/dist/native/prehide.js +6 -2
  42. package/dist/native/prehide.js.map +1 -1
  43. package/dist/native/runtime.d.ts +4 -1
  44. package/dist/native/runtime.d.ts.map +1 -1
  45. package/dist/native/runtime.js +29 -8
  46. package/dist/native/runtime.js.map +1 -1
  47. package/dist/native/shared/dom.d.ts +2 -0
  48. package/dist/native/shared/dom.d.ts.map +1 -1
  49. package/dist/native/shared/dom.js +5 -1
  50. package/dist/native/shared/dom.js.map +1 -1
  51. package/dist/native/shared/projection-id.d.ts +4 -0
  52. package/dist/native/shared/projection-id.d.ts.map +1 -0
  53. package/dist/native/shared/projection-id.js +7 -0
  54. package/dist/native/shared/projection-id.js.map +1 -0
  55. package/dist/native/vertical-bars-web.d.ts.map +1 -1
  56. package/dist/native/vertical-bars-web.js +4 -2
  57. package/dist/native/vertical-bars-web.js.map +1 -1
  58. package/dist/native-integration/index.d.ts +1 -1
  59. package/dist/native-integration/index.js.map +1 -1
  60. package/dist/native-integration/transition.d.ts +1 -1
  61. package/dist/native-integration/transition.js.map +1 -1
  62. package/dist/sheets-of-glass/animations.d.ts +1 -1
  63. package/dist/sheets-of-glass/animations.js +1 -1
  64. package/dist/sheets-of-glass/animations.js.map +1 -1
  65. package/dist/sheets-of-glass/index.js +1 -1
  66. package/dist/sheets-of-glass/index.js.map +1 -1
  67. package/dist/tab-bar-searchable/animations/enter.d.ts +1 -1
  68. package/dist/tab-bar-searchable/animations/enter.d.ts.map +1 -1
  69. package/dist/tab-bar-searchable/animations/enter.js +1 -1
  70. package/dist/tab-bar-searchable/animations/enter.js.map +1 -1
  71. package/dist/tab-bar-searchable/animations/leave.d.ts +1 -1
  72. package/dist/tab-bar-searchable/animations/leave.d.ts.map +1 -1
  73. package/dist/tab-bar-searchable/animations/leave.js +1 -1
  74. package/dist/tab-bar-searchable/animations/leave.js.map +1 -1
  75. package/dist/tab-bar-searchable/index.js +1 -1
  76. package/dist/tab-bar-searchable/index.js.map +1 -1
  77. package/dist/transition/index.d.ts +1 -1
  78. package/dist/transition/index.js +1 -1
  79. package/dist/transition/index.js.map +1 -1
  80. package/dist/transition/ios.transition.d.ts +1 -1
  81. package/dist/transition/ios.transition.js.map +1 -1
  82. package/dist/utils.d.ts +1 -1
  83. package/dist/utils.js.map +1 -1
  84. package/docs/native-ui-shell.md +20 -4
  85. package/docs/vertical-bars.md +3 -1
  86. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +65 -91
  87. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellTabBar.swift +9 -3
  88. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +79 -3
  89. package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +57 -1
  90. package/ios/Sources/IonicNativeUIShellPlugin/Overlays/ShellOverlayController.swift +116 -0
  91. package/ios/Sources/IonicNativeUIShellPlugin/Overlays/ShellOverlayHost.swift +36 -0
  92. package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +27 -0
  93. package/package.json +2 -2
  94. package/src/native/components/index.ts +3 -2
  95. package/src/native/definitions.ts +27 -1
  96. package/src/native/index.ts +23 -6
  97. package/src/native/overlays/content.ts +51 -0
  98. package/src/native/overlays/controller.ts +322 -0
  99. package/src/native/overlays/dialog-animation.ts +30 -0
  100. package/src/native/overlays/dismiss-animation.ts +15 -0
  101. package/src/native/overlays/focus.ts +64 -0
  102. package/src/native/overlays/popover.ts +80 -0
  103. package/src/native/overlays/styles.ts +80 -0
  104. package/src/native/prehide.ts +6 -1
  105. package/src/native/runtime.ts +39 -15
  106. package/src/native/shared/dom.ts +14 -0
  107. package/src/native/shared/projection-id.ts +2 -0
  108. package/src/native/vertical-bars-web.ts +11 -1
  109. package/src/native-integration/index.ts +1 -1
  110. package/src/native-integration/transition.ts +1 -1
  111. package/src/sheets-of-glass/animations.ts +2 -2
  112. package/src/sheets-of-glass/index.ts +2 -2
  113. package/src/styles/components/ion-button.scss +47 -0
  114. package/src/styles/components/ion-toolbar.scss +62 -7
  115. package/src/styles/vertical-bars.scss +23 -0
  116. package/src/tab-bar-searchable/animations/enter.ts +2 -1
  117. package/src/tab-bar-searchable/animations/leave.ts +2 -1
  118. package/src/tab-bar-searchable/index.ts +1 -1
  119. package/src/transition/index.ts +3 -3
  120. package/src/transition/ios.transition.ts +1 -1
  121. package/src/utils.ts +1 -1
@@ -0,0 +1,82 @@
1
+ //#region src/native/overlays/styles.ts
2
+ const sheetText = (sheets) => sheets.flatMap((sheet) => Array.from(sheet.cssRules, (rule) => rule.cssText)).join("\n");
3
+ const relayStyles = (source, destination, overlay) => {
4
+ const base = destination.createElement("base");
5
+ base.href = source.baseURI;
6
+ destination.head.append(base);
7
+ const copies = /* @__PURE__ */ new Map();
8
+ const sheets = destination.createElement("style");
9
+ destination.head.append(sheets);
10
+ const sync = () => {
11
+ sheets.textContent = sheetText(source.adoptedStyleSheets);
12
+ const sources = Array.from(source.querySelectorAll("meta[name=viewport],link[rel=stylesheet],style:not([data-overlay-relay-local])"));
13
+ for (const [node, copy] of copies) if (!sources.includes(node)) {
14
+ copy.remove();
15
+ copies.delete(node);
16
+ }
17
+ for (const node of sources) {
18
+ let copy = copies.get(node);
19
+ if (!copy) {
20
+ copy = node.cloneNode(true);
21
+ copies.set(node, copy);
22
+ } else {
23
+ for (const attribute of Array.from(copy.attributes)) copy.removeAttribute(attribute.name);
24
+ for (const attribute of Array.from(node.attributes)) copy.setAttribute(attribute.name, attribute.value);
25
+ copy.textContent = node.textContent;
26
+ }
27
+ destination.head.append(copy);
28
+ }
29
+ destination.head.append(surface);
30
+ };
31
+ const surface = destination.createElement("style");
32
+ surface.textContent = "html,body{background:transparent!important}";
33
+ const observers = [];
34
+ const mirror = (node, target, flatten = false) => {
35
+ const update = () => {
36
+ for (const attribute of Array.from(target.attributes)) if (!node.hasAttribute(attribute.name)) target.removeAttribute(attribute.name);
37
+ for (const attribute of Array.from(node.attributes)) target.setAttribute(attribute.name, attribute.value);
38
+ if (target.localName === "ion-app") target.classList.remove("ios-theme-vertical-bars");
39
+ if (flatten) target.style.setProperty("display", "contents", "important");
40
+ };
41
+ update();
42
+ const observer = new MutationObserver(update);
43
+ observer.observe(node, { attributes: true });
44
+ observers.push(observer);
45
+ };
46
+ mirror(source.documentElement, destination.documentElement);
47
+ mirror(source.body, destination.body);
48
+ const ancestors = [];
49
+ for (let node = overlay.parentElement; node && node !== source.body; node = node.parentElement) ancestors.unshift(node);
50
+ let parent = destination.body;
51
+ for (const node of ancestors) {
52
+ const shell = destination.createElement(node.localName);
53
+ mirror(node, shell, true);
54
+ parent.append(shell);
55
+ parent = shell;
56
+ }
57
+ sync();
58
+ const ready = Promise.all(Array.from(destination.querySelectorAll("link[rel=stylesheet]"), (link) => {
59
+ if (link.sheet) return Promise.resolve();
60
+ return new Promise((resolve, reject) => {
61
+ link.addEventListener("load", () => resolve(), { once: true });
62
+ link.addEventListener("error", () => reject(/* @__PURE__ */ new Error("Popover stylesheet unavailable")), { once: true });
63
+ });
64
+ })).then(() => void 0);
65
+ const observer = new MutationObserver(sync);
66
+ observer.observe(source.head, {
67
+ subtree: true,
68
+ childList: true,
69
+ characterData: true,
70
+ attributes: true
71
+ });
72
+ observers.push(observer);
73
+ return {
74
+ destination: parent,
75
+ ready,
76
+ stop: () => observers.forEach((item) => item.disconnect())
77
+ };
78
+ };
79
+ //#endregion
80
+ export { relayStyles, sheetText };
81
+
82
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","names":[],"sources":["../../../src/native/overlays/styles.ts"],"sourcesContent":["export const sheetText = (sheets: readonly CSSStyleSheet[]): string =>\n sheets.flatMap((sheet) => Array.from(sheet.cssRules, (rule) => rule.cssText)).join('\\n');\n\nexport const relayStyles = (\n source: Document,\n destination: Document,\n overlay: HTMLElement,\n): { destination: HTMLElement; ready: Promise<void>; stop: () => void } => {\n const base = destination.createElement('base');\n base.href = source.baseURI;\n destination.head.append(base);\n const copies = new Map<Element, Element>();\n const sheets = destination.createElement('style');\n destination.head.append(sheets);\n const sync = () => {\n sheets.textContent = sheetText(source.adoptedStyleSheets);\n const sources = Array.from(source.querySelectorAll('meta[name=viewport],link[rel=stylesheet],style:not([data-overlay-relay-local])'));\n for (const [node, copy] of copies)\n if (!sources.includes(node)) {\n copy.remove();\n copies.delete(node);\n }\n for (const node of sources) {\n let copy = copies.get(node);\n if (!copy) {\n copy = node.cloneNode(true) as Element;\n copies.set(node, copy);\n } else {\n for (const attribute of Array.from(copy.attributes)) copy.removeAttribute(attribute.name);\n for (const attribute of Array.from(node.attributes)) copy.setAttribute(attribute.name, attribute.value);\n copy.textContent = node.textContent;\n }\n destination.head.append(copy);\n }\n destination.head.append(surface);\n };\n const surface = destination.createElement('style');\n surface.textContent = 'html,body{background:transparent!important}';\n const observers: MutationObserver[] = [];\n const mirror = (node: Element, target: HTMLElement, flatten = false) => {\n const update = () => {\n for (const attribute of Array.from(target.attributes)) if (!node.hasAttribute(attribute.name)) target.removeAttribute(attribute.name);\n for (const attribute of Array.from(node.attributes)) target.setAttribute(attribute.name, attribute.value);\n // The popover content must not inherit the source page's rail layout.\n if (target.localName === 'ion-app') target.classList.remove('ios-theme-vertical-bars');\n if (flatten) target.style.setProperty('display', 'contents', 'important');\n };\n update();\n const observer = new MutationObserver(update);\n observer.observe(node, { attributes: true });\n observers.push(observer);\n };\n mirror(source.documentElement, destination.documentElement);\n mirror(source.body, destination.body);\n const ancestors: Element[] = [];\n for (let node = overlay.parentElement; node && node !== source.body; node = node.parentElement) ancestors.unshift(node);\n let parent = destination.body;\n for (const node of ancestors) {\n const shell = destination.createElement(node.localName);\n mirror(node, shell, true);\n parent.append(shell);\n parent = shell;\n }\n sync();\n // A copied external stylesheet can still be loading when Ionic's content is\n // ready. Do not expose an unstyled first native frame (notably button sizes).\n const ready = Promise.all(\n Array.from(destination.querySelectorAll<HTMLLinkElement>('link[rel=stylesheet]'), (link) => {\n if (link.sheet) return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n link.addEventListener('load', () => resolve(), { once: true });\n link.addEventListener('error', () => reject(new Error('Popover stylesheet unavailable')), { once: true });\n });\n }),\n ).then(() => undefined);\n const observer = new MutationObserver(sync);\n observer.observe(source.head, { subtree: true, childList: true, characterData: true, attributes: true });\n observers.push(observer);\n return { destination: parent, ready, stop: () => observers.forEach((item) => item.disconnect()) };\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"prehide.d.ts","names":[],"sources":["../../src/native/prehide.ts"],"mappings":";qBAkCa,oCAAqC,KAAK;EAAa;EAA2B"}
1
+ {"version":3,"file":"prehide.d.ts","names":[],"sources":["../../src/native/prehide.ts"],"mappings":";qBAmCa,oCAAqC,KAAK;EAAa;EAA2B"}
@@ -1,7 +1,7 @@
1
1
  import { VERTICAL_BARS_TRANSITION_CANCELED } from "../native-integration/index.js";
2
2
  import { modalUsesVerticalBars } from "./shared/modal.js";
3
- import { childElements, clearVerticalBarsPlacement, createVerticalBarsPageState, isPermanentlyExcluded, isShellDisabled, isVerticalBarsToolbarActionShape, prehiddenClass, prehideRootClass, setVerticalBarsPlacement, verticalBarsActionCandidate, verticalBarsBackCandidate, verticalBarsBackWebClass, verticalBarsOwned } from "./shared/dom.js";
4
- import { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from "@ionic/core";
3
+ import { childElements, clearVerticalBarsPlacement, createVerticalBarsPageState, isPermanentlyExcluded, isShellDisabled, isVerticalBarsToolbarActionShape, prehiddenClass, prehideRootClass, setVerticalBarsPlacement, syncToolbarText, verticalBarsActionCandidate, verticalBarsBackCandidate, verticalBarsBackWebClass, verticalBarsOwned } from "./shared/dom.js";
4
+ import { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from "@ionic/core/components/index.js";
5
5
  //#region src/native/prehide.ts
6
6
  const overlays = "ion-menu, ion-popover";
7
7
  const sourceSelector = "ion-back-button, ion-menu-button, ion-buttons, ion-button";
@@ -99,6 +99,7 @@ const prehideVerticalBarsToolbarSources = (doc) => {
99
99
  scopes.set(scope, owned);
100
100
  };
101
101
  const reconcile = () => {
102
+ syncToolbarText(doc);
102
103
  const verticalBars = root();
103
104
  if (!verticalBars) {
104
105
  Array.from(scopes.keys()).forEach(release);
@@ -160,6 +161,8 @@ const prehideVerticalBarsToolbarSources = (doc) => {
160
161
  doc.defaultView?.addEventListener("nativeUIShellRefresh", reconcile, { signal: listeners.signal });
161
162
  reconcile();
162
163
  const mutationRelevant = (record) => {
164
+ if (record.type === "characterData") return record.target.parentElement?.matches("ion-toolbar") === true;
165
+ if (record.type === "childList" && asElement(record.target)?.matches("ion-toolbar")) return true;
163
166
  if (record.type === "childList") return [...Array.from(record.addedNodes), ...Array.from(record.removedNodes)].some((node) => isHtmlElement(node) && (node.matches(`${sourceSelector}, .ion-page, ion-toolbar`) || !!node.querySelector(`${sourceSelector}, .ion-page, ion-toolbar`)));
164
167
  if (record.attributeName !== "class") return true;
165
168
  const current = record.target.className;
@@ -183,6 +186,7 @@ const prehideVerticalBarsToolbarSources = (doc) => {
183
186
  observer.observe(doc.documentElement, {
184
187
  subtree: true,
185
188
  childList: true,
189
+ characterData: true,
186
190
  attributes: true,
187
191
  attributeOldValue: true,
188
192
  attributeFilter: [
@@ -1 +1 @@
1
- {"version":3,"file":"prehide.js","names":[],"sources":["../../src/native/prehide.ts"],"sourcesContent":["import { modalUsesVerticalBars } from './shared/modal';\nimport { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from '@ionic/core';\nimport { VERTICAL_BARS_TRANSITION_CANCELED } from '../native-integration';\nimport {\n childElements,\n clearVerticalBarsPlacement,\n createVerticalBarsPageState,\n verticalBarsActionCandidate,\n verticalBarsBackCandidate,\n verticalBarsBackWebClass,\n verticalBarsOwned,\n isVerticalBarsToolbarActionShape,\n isPermanentlyExcluded,\n isShellDisabled,\n prehiddenClass,\n prehideRootClass,\n rejectedClass,\n setVerticalBarsPlacement,\n} from './shared/dom';\n\nconst overlays = 'ion-menu, ion-popover';\nconst sourceSelector = 'ion-back-button, ion-menu-button, ion-buttons, ion-button';\n// Realm- and teardown-safe: globals such as Element/HTMLElement may be gone\n// when a queued mutation microtask or a stale listener still runs.\nconst isHtmlElement = (node: unknown): node is HTMLElement => !!node && (node as Node).nodeType === 1;\nconst asElement = (node: Node | EventTarget | null): HTMLElement | undefined => (isHtmlElement(node) ? node : undefined);\nconst backSupported = (element: HTMLElement): boolean => {\n const back = element as HTMLIonBackButtonElement;\n return back.icon === undefined && back.color === undefined && !!back.shadowRoot;\n};\nconst eligibleBack = verticalBarsBackCandidate;\nconst eligible = verticalBarsActionCandidate;\n\n/** Capture toolbar placement once per routed-page epoch, before Ionic paints its transition. */\nexport const prehideVerticalBarsToolbarSources = (doc: Document): { suspend: () => () => void; stop: () => void } => {\n doc.documentElement.classList.add(prehideRootClass);\n const scopes = new Map<HTMLElement, Set<HTMLElement>>();\n const owner = new WeakMap<HTMLElement, HTMLElement>();\n const pendingBacks = new Map<HTMLElement, { scope: HTMLElement; timer: ReturnType<typeof setTimeout> }>();\n const verticalBarsPages = createVerticalBarsPageState();\n const listeners = new AbortController();\n const root = () => doc.querySelector<HTMLElement>('ion-app.ios-theme-vertical-bars');\n let suspended = 0;\n let stopped = false;\n const routedPage = (element: HTMLElement) => element.closest<HTMLElement>('.ion-page:not(ion-app, body)');\n const release = (scope: HTMLElement) => {\n for (const [element, pending] of pendingBacks) {\n if (pending.scope !== scope) continue;\n clearTimeout(pending.timer);\n pendingBacks.delete(element);\n }\n const owned = scopes.get(scope);\n if (!owned) return;\n owned.forEach((element) => {\n if (owner.get(element) !== scope) return;\n owner.delete(element);\n element.classList.remove(prehiddenClass);\n element.classList.remove(verticalBarsBackWebClass);\n clearVerticalBarsPlacement(element);\n });\n scopes.delete(scope);\n };\n const capture = (scope: HTMLElement) => {\n if (!root()?.contains(scope) || scope.closest(overlays) || !modalUsesVerticalBars(scope) || verticalBarsPages.isDeparted(scope)) return;\n const owned = scopes.get(scope) ?? new Set<HTMLElement>();\n const place = (element: HTMLElement, rail: boolean) => {\n if (owned.has(element)) return;\n const previousScope = owner.get(element);\n if (previousScope) scopes.get(previousScope)?.delete(element);\n owner.set(element, scope);\n setVerticalBarsPlacement(element, rail);\n if (element.matches('ion-back-button')) element.classList.toggle(verticalBarsBackWebClass, !rail);\n owned.add(element);\n };\n const sources = scope.matches('ion-back-button') ? [scope] : Array.from(scope.querySelectorAll<HTMLElement>(sourceSelector));\n sources.forEach((element) => {\n // Shell opt-out is temporary; decide initial placement once it is lifted.\n if (isShellDisabled(element)) return;\n if (!modalUsesVerticalBars(element) || element.closest(overlays) || (scope.matches('.ion-page') && routedPage(element) !== scope))\n return;\n if (element.matches('ion-buttons')) {\n const children = childElements(element);\n const actions = children.filter((child) => eligible(child) && isVerticalBarsToolbarActionShape(child));\n const group =\n eligible(element) &&\n actions.length === children.length &&\n (actions.length > 1 || (actions.length === 1 && actions[0].matches('ion-menu-button')));\n const freshGroup = !owned.has(element);\n place(element, group);\n children.forEach((child) => {\n if (!child.matches('ion-back-button')) place(child, freshGroup && actions.includes(child));\n });\n } else if (element.matches('ion-back-button') || !element.parentElement?.matches('ion-buttons')) {\n if (owned.has(element)) return;\n if (\n element.matches('ion-back-button') &&\n eligibleBack(element) &&\n (element as HTMLIonBackButtonElement).icon === undefined &&\n (element as HTMLIonBackButtonElement).color === undefined &&\n !element.shadowRoot &&\n !element.classList.contains('hydrated')\n ) {\n const previousPending = pendingBacks.get(element);\n if (previousPending && previousPending.scope !== scope) {\n clearTimeout(previousPending.timer);\n pendingBacks.delete(element);\n }\n if (!pendingBacks.has(element)) {\n const timer = setTimeout(() => {\n if (pendingBacks.get(element)?.scope !== scope) return;\n pendingBacks.delete(element);\n // A custom element that never hydrates must remain usable on Web.\n place(element, false);\n reconcile();\n }, 1500);\n pendingBacks.set(element, { scope, timer });\n }\n return;\n }\n const pending = pendingBacks.get(element);\n if (pending) {\n clearTimeout(pending.timer);\n pendingBacks.delete(element);\n }\n place(\n element,\n element.matches('ion-back-button')\n ? eligibleBack(element) && backSupported(element)\n : eligible(element) && isVerticalBarsToolbarActionShape(element),\n );\n }\n });\n scopes.set(scope, owned);\n };\n const reconcile = () => {\n const verticalBars = root();\n if (!verticalBars) {\n Array.from(scopes.keys()).forEach(release);\n return;\n }\n for (const scope of scopes.keys()) if (!scope.isConnected || !modalUsesVerticalBars(scope)) release(scope);\n for (const [element, pending] of pendingBacks) if (element.shadowRoot || element.classList.contains('hydrated')) capture(pending.scope);\n // Ionic inserts the destination as invisible before WillEnter. Hide its\n // sources in that same mutation microtask; capture new DOM identities on active pages too.\n verticalBars.querySelectorAll<HTMLElement>('.ion-page:not(ion-app, body, .ion-page-hidden)').forEach(capture);\n // Root toolbars can mount after startup; each new DOM identity is captured once.\n verticalBars.querySelectorAll<HTMLElement>(sourceSelector).forEach((element) => {\n if (routedPage(element) || element.closest(overlays)) return;\n const scope = element.closest<HTMLElement>('ion-toolbar') ?? (element.matches('ion-back-button') ? element : null);\n if (scope) capture(scope);\n });\n for (const owned of scopes.values()) {\n for (const element of owned) {\n const groupOwnsChildren =\n element.parentElement?.matches('ion-buttons') &&\n verticalBarsOwned(element.parentElement) &&\n childElements(element.parentElement).every((child) => verticalBarsOwned(child));\n const hide =\n !suspended &&\n verticalBarsOwned(element) &&\n !groupOwnsChildren &&\n element.isConnected &&\n !element.closest(`.${rejectedClass}`) &&\n !isPermanentlyExcluded(element) &&\n !isShellDisabled(element) &&\n (!element.matches('ion-back-button') || backSupported(element) || !element.classList.contains('hydrated')) &&\n (!element.matches('ion-buttons') || childElements(element).every((child) => verticalBarsOwned(child)));\n element.classList.toggle(prehiddenClass, hide);\n if (element.matches('ion-back-button')) element.classList.toggle(verticalBarsBackWebClass, !hide);\n }\n }\n };\n const onWillEnter = (event: Event) => {\n const page = asElement(event.target);\n if (page?.matches('.ion-page')) {\n verticalBarsPages.lifecycle(event);\n release(page); // Cached pages begin a new placement epoch.\n capture(page);\n reconcile();\n }\n };\n const onPageLifecycle: EventListener = (event) => {\n verticalBarsPages.lifecycle(event);\n if (event.type !== LIFECYCLE_DID_LEAVE) return;\n const page = asElement(event.target);\n if (page) release(page);\n };\n // A canceled transition keeps the leaving page active and abandons the entering one.\n const onTransitionCanceled: EventListener = (event) => {\n const leaving = asElement(event.target);\n if (!leaving) return;\n const entering = (event as CustomEvent<{ entering?: HTMLElement }>).detail?.entering;\n verticalBarsPages.cancel(entering, leaving);\n reconcile();\n };\n doc.addEventListener(LIFECYCLE_WILL_ENTER, onWillEnter, { capture: true, signal: listeners.signal });\n for (const name of [LIFECYCLE_WILL_LEAVE, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE])\n doc.addEventListener(name, onPageLifecycle, { capture: true, signal: listeners.signal });\n doc.addEventListener(VERTICAL_BARS_TRANSITION_CANCELED, onTransitionCanceled, { capture: true, signal: listeners.signal });\n doc.defaultView?.addEventListener('nativeUIShellRefresh', reconcile, { signal: listeners.signal });\n reconcile();\n const mutationRelevant = (record: MutationRecord) => {\n if (record.type === 'childList')\n return [...Array.from(record.addedNodes), ...Array.from(record.removedNodes)].some(\n (node) =>\n isHtmlElement(node) &&\n (node.matches(`${sourceSelector}, .ion-page, ion-toolbar`) || !!node.querySelector(`${sourceSelector}, .ion-page, ion-toolbar`)),\n );\n if (record.attributeName !== 'class') return true;\n const current = (record.target as Element).className;\n const previous = record.oldValue ?? '';\n const withoutOwned = (value: string) =>\n value\n .split(/\\s+/)\n .filter((name) => name && ![prehiddenClass, prehideRootClass, verticalBarsBackWebClass].includes(name))\n .join(' ');\n return typeof current !== 'string' || withoutOwned(previous) !== withoutOwned(current);\n };\n let scheduled = false;\n const observer = new (doc.defaultView?.MutationObserver ?? MutationObserver)((records) => {\n if (scheduled || stopped || !records.some(mutationRelevant)) return;\n scheduled = true;\n queueMicrotask(() => {\n scheduled = false;\n if (!stopped) reconcile();\n });\n });\n observer.observe(doc.documentElement, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeOldValue: true,\n attributeFilter: ['class', 'data-shell', 'hidden', 'inert', 'icon', 'color'],\n });\n return {\n suspend: () => {\n if (stopped) return () => {};\n suspended++;\n doc.documentElement.classList.remove(prehideRootClass);\n reconcile();\n let resumed = false;\n return () => {\n if (resumed || stopped) return;\n resumed = true;\n if (--suspended === 0) {\n doc.documentElement.classList.add(prehideRootClass);\n reconcile();\n }\n };\n },\n stop: () => {\n if (stopped) return;\n stopped = true;\n observer.disconnect();\n listeners.abort();\n doc.documentElement.classList.remove(prehideRootClass);\n Array.from(scopes.keys()).forEach(release);\n },\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"prehide.js","names":[],"sources":["../../src/native/prehide.ts"],"sourcesContent":["import { modalUsesVerticalBars } from './shared/modal';\nimport { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from '@ionic/core/components/index.js';\nimport { VERTICAL_BARS_TRANSITION_CANCELED } from '../native-integration';\nimport {\n childElements,\n clearVerticalBarsPlacement,\n createVerticalBarsPageState,\n verticalBarsActionCandidate,\n verticalBarsBackCandidate,\n verticalBarsBackWebClass,\n verticalBarsOwned,\n isVerticalBarsToolbarActionShape,\n isPermanentlyExcluded,\n isShellDisabled,\n prehiddenClass,\n prehideRootClass,\n rejectedClass,\n setVerticalBarsPlacement,\n syncToolbarText,\n} from './shared/dom';\n\nconst overlays = 'ion-menu, ion-popover';\nconst sourceSelector = 'ion-back-button, ion-menu-button, ion-buttons, ion-button';\n// Realm- and teardown-safe: globals such as Element/HTMLElement may be gone\n// when a queued mutation microtask or a stale listener still runs.\nconst isHtmlElement = (node: unknown): node is HTMLElement => !!node && (node as Node).nodeType === 1;\nconst asElement = (node: Node | EventTarget | null): HTMLElement | undefined => (isHtmlElement(node) ? node : undefined);\nconst backSupported = (element: HTMLElement): boolean => {\n const back = element as HTMLIonBackButtonElement;\n return back.icon === undefined && back.color === undefined && !!back.shadowRoot;\n};\nconst eligibleBack = verticalBarsBackCandidate;\nconst eligible = verticalBarsActionCandidate;\n\n/** Capture toolbar placement once per routed-page epoch, before Ionic paints its transition. */\nexport const prehideVerticalBarsToolbarSources = (doc: Document): { suspend: () => () => void; stop: () => void } => {\n doc.documentElement.classList.add(prehideRootClass);\n const scopes = new Map<HTMLElement, Set<HTMLElement>>();\n const owner = new WeakMap<HTMLElement, HTMLElement>();\n const pendingBacks = new Map<HTMLElement, { scope: HTMLElement; timer: ReturnType<typeof setTimeout> }>();\n const verticalBarsPages = createVerticalBarsPageState();\n const listeners = new AbortController();\n const root = () => doc.querySelector<HTMLElement>('ion-app.ios-theme-vertical-bars');\n let suspended = 0;\n let stopped = false;\n const routedPage = (element: HTMLElement) => element.closest<HTMLElement>('.ion-page:not(ion-app, body)');\n const release = (scope: HTMLElement) => {\n for (const [element, pending] of pendingBacks) {\n if (pending.scope !== scope) continue;\n clearTimeout(pending.timer);\n pendingBacks.delete(element);\n }\n const owned = scopes.get(scope);\n if (!owned) return;\n owned.forEach((element) => {\n if (owner.get(element) !== scope) return;\n owner.delete(element);\n element.classList.remove(prehiddenClass);\n element.classList.remove(verticalBarsBackWebClass);\n clearVerticalBarsPlacement(element);\n });\n scopes.delete(scope);\n };\n const capture = (scope: HTMLElement) => {\n if (!root()?.contains(scope) || scope.closest(overlays) || !modalUsesVerticalBars(scope) || verticalBarsPages.isDeparted(scope)) return;\n const owned = scopes.get(scope) ?? new Set<HTMLElement>();\n const place = (element: HTMLElement, rail: boolean) => {\n if (owned.has(element)) return;\n const previousScope = owner.get(element);\n if (previousScope) scopes.get(previousScope)?.delete(element);\n owner.set(element, scope);\n setVerticalBarsPlacement(element, rail);\n if (element.matches('ion-back-button')) element.classList.toggle(verticalBarsBackWebClass, !rail);\n owned.add(element);\n };\n const sources = scope.matches('ion-back-button') ? [scope] : Array.from(scope.querySelectorAll<HTMLElement>(sourceSelector));\n sources.forEach((element) => {\n // Shell opt-out is temporary; decide initial placement once it is lifted.\n if (isShellDisabled(element)) return;\n if (!modalUsesVerticalBars(element) || element.closest(overlays) || (scope.matches('.ion-page') && routedPage(element) !== scope))\n return;\n if (element.matches('ion-buttons')) {\n const children = childElements(element);\n const actions = children.filter((child) => eligible(child) && isVerticalBarsToolbarActionShape(child));\n const group =\n eligible(element) &&\n actions.length === children.length &&\n (actions.length > 1 || (actions.length === 1 && actions[0].matches('ion-menu-button')));\n const freshGroup = !owned.has(element);\n place(element, group);\n children.forEach((child) => {\n if (!child.matches('ion-back-button')) place(child, freshGroup && actions.includes(child));\n });\n } else if (element.matches('ion-back-button') || !element.parentElement?.matches('ion-buttons')) {\n if (owned.has(element)) return;\n if (\n element.matches('ion-back-button') &&\n eligibleBack(element) &&\n (element as HTMLIonBackButtonElement).icon === undefined &&\n (element as HTMLIonBackButtonElement).color === undefined &&\n !element.shadowRoot &&\n !element.classList.contains('hydrated')\n ) {\n const previousPending = pendingBacks.get(element);\n if (previousPending && previousPending.scope !== scope) {\n clearTimeout(previousPending.timer);\n pendingBacks.delete(element);\n }\n if (!pendingBacks.has(element)) {\n const timer = setTimeout(() => {\n if (pendingBacks.get(element)?.scope !== scope) return;\n pendingBacks.delete(element);\n // A custom element that never hydrates must remain usable on Web.\n place(element, false);\n reconcile();\n }, 1500);\n pendingBacks.set(element, { scope, timer });\n }\n return;\n }\n const pending = pendingBacks.get(element);\n if (pending) {\n clearTimeout(pending.timer);\n pendingBacks.delete(element);\n }\n place(\n element,\n element.matches('ion-back-button')\n ? eligibleBack(element) && backSupported(element)\n : eligible(element) && isVerticalBarsToolbarActionShape(element),\n );\n }\n });\n scopes.set(scope, owned);\n };\n const reconcile = () => {\n syncToolbarText(doc);\n const verticalBars = root();\n if (!verticalBars) {\n Array.from(scopes.keys()).forEach(release);\n return;\n }\n for (const scope of scopes.keys()) if (!scope.isConnected || !modalUsesVerticalBars(scope)) release(scope);\n for (const [element, pending] of pendingBacks) if (element.shadowRoot || element.classList.contains('hydrated')) capture(pending.scope);\n // Ionic inserts the destination as invisible before WillEnter. Hide its\n // sources in that same mutation microtask; capture new DOM identities on active pages too.\n verticalBars.querySelectorAll<HTMLElement>('.ion-page:not(ion-app, body, .ion-page-hidden)').forEach(capture);\n // Root toolbars can mount after startup; each new DOM identity is captured once.\n verticalBars.querySelectorAll<HTMLElement>(sourceSelector).forEach((element) => {\n if (routedPage(element) || element.closest(overlays)) return;\n const scope = element.closest<HTMLElement>('ion-toolbar') ?? (element.matches('ion-back-button') ? element : null);\n if (scope) capture(scope);\n });\n for (const owned of scopes.values()) {\n for (const element of owned) {\n const groupOwnsChildren =\n element.parentElement?.matches('ion-buttons') &&\n verticalBarsOwned(element.parentElement) &&\n childElements(element.parentElement).every((child) => verticalBarsOwned(child));\n const hide =\n !suspended &&\n verticalBarsOwned(element) &&\n !groupOwnsChildren &&\n element.isConnected &&\n !element.closest(`.${rejectedClass}`) &&\n !isPermanentlyExcluded(element) &&\n !isShellDisabled(element) &&\n (!element.matches('ion-back-button') || backSupported(element) || !element.classList.contains('hydrated')) &&\n (!element.matches('ion-buttons') || childElements(element).every((child) => verticalBarsOwned(child)));\n element.classList.toggle(prehiddenClass, hide);\n if (element.matches('ion-back-button')) element.classList.toggle(verticalBarsBackWebClass, !hide);\n }\n }\n };\n const onWillEnter = (event: Event) => {\n const page = asElement(event.target);\n if (page?.matches('.ion-page')) {\n verticalBarsPages.lifecycle(event);\n release(page); // Cached pages begin a new placement epoch.\n capture(page);\n reconcile();\n }\n };\n const onPageLifecycle: EventListener = (event) => {\n verticalBarsPages.lifecycle(event);\n if (event.type !== LIFECYCLE_DID_LEAVE) return;\n const page = asElement(event.target);\n if (page) release(page);\n };\n // A canceled transition keeps the leaving page active and abandons the entering one.\n const onTransitionCanceled: EventListener = (event) => {\n const leaving = asElement(event.target);\n if (!leaving) return;\n const entering = (event as CustomEvent<{ entering?: HTMLElement }>).detail?.entering;\n verticalBarsPages.cancel(entering, leaving);\n reconcile();\n };\n doc.addEventListener(LIFECYCLE_WILL_ENTER, onWillEnter, { capture: true, signal: listeners.signal });\n for (const name of [LIFECYCLE_WILL_LEAVE, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE])\n doc.addEventListener(name, onPageLifecycle, { capture: true, signal: listeners.signal });\n doc.addEventListener(VERTICAL_BARS_TRANSITION_CANCELED, onTransitionCanceled, { capture: true, signal: listeners.signal });\n doc.defaultView?.addEventListener('nativeUIShellRefresh', reconcile, { signal: listeners.signal });\n reconcile();\n const mutationRelevant = (record: MutationRecord) => {\n if (record.type === 'characterData') return record.target.parentElement?.matches('ion-toolbar') === true;\n if (record.type === 'childList' && asElement(record.target)?.matches('ion-toolbar')) return true;\n if (record.type === 'childList')\n return [...Array.from(record.addedNodes), ...Array.from(record.removedNodes)].some(\n (node) =>\n isHtmlElement(node) &&\n (node.matches(`${sourceSelector}, .ion-page, ion-toolbar`) || !!node.querySelector(`${sourceSelector}, .ion-page, ion-toolbar`)),\n );\n if (record.attributeName !== 'class') return true;\n const current = (record.target as Element).className;\n const previous = record.oldValue ?? '';\n const withoutOwned = (value: string) =>\n value\n .split(/\\s+/)\n .filter((name) => name && ![prehiddenClass, prehideRootClass, verticalBarsBackWebClass].includes(name))\n .join(' ');\n return typeof current !== 'string' || withoutOwned(previous) !== withoutOwned(current);\n };\n let scheduled = false;\n const observer = new (doc.defaultView?.MutationObserver ?? MutationObserver)((records) => {\n if (scheduled || stopped || !records.some(mutationRelevant)) return;\n scheduled = true;\n queueMicrotask(() => {\n scheduled = false;\n if (!stopped) reconcile();\n });\n });\n observer.observe(doc.documentElement, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n attributeOldValue: true,\n attributeFilter: ['class', 'data-shell', 'hidden', 'inert', 'icon', 'color'],\n });\n return {\n suspend: () => {\n if (stopped) return () => {};\n suspended++;\n doc.documentElement.classList.remove(prehideRootClass);\n reconcile();\n let resumed = false;\n return () => {\n if (resumed || stopped) return;\n resumed = true;\n if (--suspended === 0) {\n doc.documentElement.classList.add(prehideRootClass);\n reconcile();\n }\n };\n },\n stop: () => {\n if (stopped) return;\n stopped = true;\n observer.disconnect();\n listeners.abort();\n doc.documentElement.classList.remove(prehideRootClass);\n Array.from(scopes.keys()).forEach(release);\n },\n };\n};\n"],"mappings":"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"}
@@ -1,5 +1,8 @@
1
1
  import { NativeUIShellHandle, NativeUIShellOptions, NativeUIShellPlugin } from "./definitions.js";
2
2
  //#region src/native/runtime.d.ts
3
- export declare const createRuntime: (doc: Document, plugin: NativeUIShellPlugin, options?: NativeUIShellOptions, nativeVerticalBars?: () => boolean, verticalBarsOnly?: boolean) => Promise<NativeUIShellHandle>;
3
+ export declare const bounded: <T>(promise: Promise<T>) => Promise<T>;
4
+ export declare const createRuntime: (doc: Document, plugin: NativeUIShellPlugin, options?: NativeUIShellOptions, nativeVerticalBars?: () => boolean, verticalBarsOnly?: boolean) => Promise<NativeUIShellHandle & {
5
+ retain: (deferActivation?: (id: string) => boolean) => () => void;
6
+ }>;
4
7
  //#endregion
5
8
  //# sourceMappingURL=runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","names":[],"sources":["../../src/native/runtime.ts"],"mappings":";;qBAyCa,gBACX,KAAK,UACL,QAAQ,qBACR,UAAS,sBACT,oCACA,+BACC,QAAQ"}
1
+ {"version":3,"file":"runtime.d.ts","names":[],"sources":["../../src/native/runtime.ts"],"mappings":";;qBAsCa,UAAW,GAAG,SAAS,QAAQ,OAAK,QAAQ;qBAM5C,gBACX,KAAK,UACL,QAAQ,qBACR,UAAS,sBACT,oCACA,+BACC,QAAQ;EAAwB,SAAS,mBAAmB"}
@@ -1,12 +1,13 @@
1
1
  import { VERTICAL_BARS_TRANSITION_CANCELED, getNativeSearchBindings, setNativeUIShellIntegration } from "../native-integration/index.js";
2
+ import { projectionIds } from "./shared/projection-id.js";
2
3
  import { inVerticalBarsSurface, modalUsesVerticalBars, modalVerticalBarFrame, topModal } from "./shared/modal.js";
3
4
  import { createCrossfade, fadeMarker } from "./shared/crossfade.js";
4
- import { activateProjectedElement, createVerticalBarsPageState, isVerticalBarsSource, marker, preferredVerticalBarsBack, prehideOnlyMutation, rejectedClass, unprojected } from "./shared/dom.js";
5
+ import { activateProjectedElement, createVerticalBarsPageState, isVerticalBarsSource, marker, preferredVerticalBarsBack, prehideOnlyMutation, rejectedClass, syncToolbarText, unprojected } from "./shared/dom.js";
5
6
  import { createIconRenderer } from "./shared/icons.js";
6
7
  import { createSearchSupport } from "./components/searchable-tabs.js";
7
8
  import { isVerticalBarsCandidate, motionSelector, readCandidate, selector, shadowSelector } from "./components/index.js";
8
9
  import { CSS_MOTION_EVENTS } from "./shared/events.js";
9
- import { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from "@ionic/core";
10
+ import { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from "@ionic/core/components/index.js";
10
11
  //#region src/native/runtime.ts
11
12
  const overlays = "ion-modal, ion-popover, ion-alert, ion-action-sheet, ion-loading, ion-picker, ion-toast, ion-menu";
12
13
  const overlayNames = [
@@ -54,6 +55,7 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
54
55
  let dirty = false;
55
56
  let pending = false;
56
57
  let stopped = false;
58
+ const retentions = /* @__PURE__ */ new Set();
57
59
  /** True while a tab-switch handoff should skip crossfade. */
58
60
  let tabSwitchHandoff = false;
59
61
  /** Keep instant updates until this time — DidLeave often precedes the retire sync. */
@@ -85,6 +87,7 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
85
87
  if (!value) {
86
88
  value = `shell-${++nextId}`;
87
89
  ids.set(element, value);
90
+ projectionIds.set(element, value);
88
91
  }
89
92
  return value;
90
93
  };
@@ -240,9 +243,11 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
240
243
  frame = 0;
241
244
  dirty = false;
242
245
  pending = true;
246
+ syncToolbarText(doc);
243
247
  handoffInstant = tabSwitchHandoff || handoffAcrossPending || modalPresentHandoff || win.performance.now() < handoffUntil;
244
- modalPresentHandoff = false;
245
248
  try {
249
+ if (retentions.size && !manualSuspensions.size) return;
250
+ modalPresentHandoff = false;
246
251
  const size = `${win.innerWidth}:${win.innerHeight}`;
247
252
  if (viewport !== size) {
248
253
  viewport = size;
@@ -256,7 +261,7 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
256
261
  const field = icon.field ?? "icon";
257
262
  icon.item[field] = await icons.render(icon.source, field === "icon" ? icon.item.iconWidth : icon.item.closeIconWidth, field === "icon" ? icon.item.iconHeight : icon.item.closeIconHeight);
258
263
  }
259
- if (stopped || dirty) return;
264
+ if (stopped || dirty || retentions.size && !manualSuspensions.size) return;
260
265
  const retained = new Set(candidates.flatMap(candidateSources));
261
266
  const removed = Array.from(sources.keys()).filter((element) => !retained.has(element));
262
267
  if (removed.length) {
@@ -286,7 +291,7 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
286
291
  updates++;
287
292
  updatingRevision = snapshot.revision;
288
293
  const result = await bounded(plugin.update(snapshot));
289
- if (stopped) return;
294
+ if (stopped || retentions.size && !manualSuspensions.size) return;
290
295
  if (result.revision !== snapshot.revision) throw new Error("Native UI Shell revision mismatch");
291
296
  if (`${win.innerWidth}:${win.innerHeight}` !== size) {
292
297
  lastSnapshot = "";
@@ -372,7 +377,7 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
372
377
  attributeOldValue: true
373
378
  };
374
379
  const observer = new MutationObserver((records) => {
375
- if (records.some((record) => record.attributeName !== "data-native-ui-shell" && record.attributeName !== "data-native-ui-shell-fading" && !prehideOnlyMutation(record) && !(record.attributeName === "style" && measuringPointerPages.has(record.target)) && !(record.attributeName === "aria-hidden" && sources.has(record.target) && record.target.getAttribute("aria-hidden") === "true"))) schedule();
380
+ if (records.some((record) => record.attributeName !== "data-native-ui-shell" && record.attributeName !== "data-native-ui-shell-toolbar-text" && record.attributeName !== "data-native-ui-shell-fading" && !prehideOnlyMutation(record) && !(record.attributeName === "style" && measuringPointerPages.has(record.target)) && !(record.attributeName === "aria-hidden" && sources.has(record.target) && record.target.getAttribute("aria-hidden") === "true"))) schedule();
376
381
  });
377
382
  const resize = new ResizeObserver(schedule);
378
383
  const on = (target, name, callback = schedule) => target.addEventListener(name, callback, {
@@ -518,6 +523,16 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
518
523
  on(win.visualViewport, "scroll");
519
524
  }
520
525
  const handle = {
526
+ retain(deferActivation) {
527
+ const retention = { accepts: deferActivation };
528
+ retentions.add(retention);
529
+ return () => {
530
+ if (!retentions.delete(retention)) return;
531
+ const activation = retention.activation;
532
+ if (activation && retention.accepts?.(activation.id)) win.queueMicrotask(() => activate(activation));
533
+ schedule();
534
+ };
535
+ },
521
536
  getStatus: () => ({
522
537
  state: stopped ? "stopped" : sources.size ? "native" : "web",
523
538
  projected: sources.size,
@@ -571,7 +586,13 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
571
586
  }
572
587
  };
573
588
  const activate = (event) => {
574
- if (stopped || doc.hidden || event.revision < acceptedRevision || event.revision > revision || event.sequence <= lastSequence || overlayOpen(false, true)) return;
589
+ if (stopped || doc.hidden || event.revision < acceptedRevision || event.revision > revision || event.sequence <= lastSequence) return;
590
+ if (overlayOpen(false, true)) {
591
+ if (!manualSuspensions.size) {
592
+ for (const retention of retentions) if (retention.accepts?.(event.id) && (!retention.activation || event.sequence > retention.activation.sequence)) retention.activation = event;
593
+ }
594
+ return;
595
+ }
575
596
  if (event.revision > acceptedRevision && event.revision === updatingRevision) {
576
597
  pendingActivations.push(event);
577
598
  return;
@@ -634,6 +655,6 @@ const createRuntime = async (doc, plugin, options = {}, nativeVerticalBars = ()
634
655
  return handle;
635
656
  };
636
657
  //#endregion
637
- export { createRuntime };
658
+ export { bounded, createRuntime };
638
659
 
639
660
  //# sourceMappingURL=runtime.js.map