@rdlabo/capacitor-native-ui 1.2.1 → 2.0.0-2

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 (67) hide show
  1. package/README.md +39 -24
  2. package/dist/core.d.ts +2 -2
  3. package/dist/native/activation.d.ts.map +1 -1
  4. package/dist/native/activation.js +19 -19
  5. package/dist/native/activation.js.map +1 -1
  6. package/dist/native/definitions.d.ts +22 -40
  7. package/dist/native/definitions.d.ts.map +1 -1
  8. package/dist/native/html.d.ts.map +1 -1
  9. package/dist/native/html.js +10 -1
  10. package/dist/native/html.js.map +1 -1
  11. package/dist/native/index.d.ts +2 -2
  12. package/dist/native/index.d.ts.map +1 -1
  13. package/dist/native/index.js +2 -11
  14. package/dist/native/index.js.map +1 -1
  15. package/dist/native/overlays/controller.js +1 -1
  16. package/dist/native/overlays/controller.js.map +1 -1
  17. package/dist/native/prehide.js +1 -1
  18. package/dist/native/prehide.js.map +1 -1
  19. package/dist/native/runtime.d.ts +1 -1
  20. package/dist/native/runtime.d.ts.map +1 -1
  21. package/dist/native/runtime.js +8 -20
  22. package/dist/native/runtime.js.map +1 -1
  23. package/dist/native/shared/candidate.d.ts +2 -2
  24. package/dist/native/shared/candidate.d.ts.map +1 -1
  25. package/dist/native/shared/candidate.js +2 -2
  26. package/dist/native/shared/candidate.js.map +1 -1
  27. package/dist/native/shared/control-options.d.ts +6 -0
  28. package/dist/native/shared/control-options.d.ts.map +1 -0
  29. package/dist/native/shared/control-options.js +17 -0
  30. package/dist/native/shared/control-options.js.map +1 -0
  31. package/dist/native/shared/toolbar-options.d.ts +6 -2
  32. package/dist/native/shared/toolbar-options.d.ts.map +1 -1
  33. package/dist/native/shared/toolbar-options.js +7 -15
  34. package/dist/native/shared/toolbar-options.js.map +1 -1
  35. package/dist/native/startup.d.ts.map +1 -1
  36. package/dist/native/startup.js +0 -16
  37. package/dist/native/startup.js.map +1 -1
  38. package/dist/native/vertical-bars-web.d.ts.map +1 -1
  39. package/dist/native/vertical-bars-web.js +3 -2
  40. package/dist/native/vertical-bars-web.js.map +1 -1
  41. package/dist/vertical-bars.d.ts +2 -2
  42. package/docs/appearance.md +45 -0
  43. package/docs/configuration.md +106 -74
  44. package/docs/framework-independent.md +52 -13
  45. package/docs/ionic-vertical-bars.md +186 -0
  46. package/docs/migration.md +67 -89
  47. package/docs/native-ui-extensions.md +61 -32
  48. package/docs/native-ui.md +187 -68
  49. package/docs/setup.md +46 -25
  50. package/docs/vertical-bars.md +118 -79
  51. package/ios/Sources/NativeUIPlugin/NativeUIPlugin.swift +1 -1
  52. package/package.json +1 -1
  53. package/src/core.ts +1 -2
  54. package/src/native/activation.ts +13 -22
  55. package/src/native/definitions.ts +40 -90
  56. package/src/native/html.ts +11 -5
  57. package/src/native/index.ts +2 -11
  58. package/src/native/overlays/controller.ts +1 -1
  59. package/src/native/prehide.ts +1 -1
  60. package/src/native/runtime.ts +7 -16
  61. package/src/native/shared/candidate.ts +4 -4
  62. package/src/native/shared/control-options.ts +20 -0
  63. package/src/native/shared/toolbar-options.ts +8 -12
  64. package/src/native/startup.ts +0 -16
  65. package/src/native/vertical-bars-web.ts +4 -2
  66. package/src/vertical-bars.ts +2 -1
  67. package/docs/native-mini-player.md +0 -47
package/README.md CHANGED
@@ -1,43 +1,58 @@
1
1
  # Capacitor Native UI
2
2
 
3
- > **Unreleased package:** the first npm release of `@rdlabo/capacitor-native-ui` is pending. The registry install commands in these guides apply after that release. To try the current code now, install a candidate archive as described below.
4
-
5
3
  Render supported Web controls with UIKit and SwiftUI on iOS while keeping your application state, routing, and handlers in the WebView. Use existing Ionic markup or explicit HTML buttons; custom SwiftUI or Android Compose content can be added through experimental app-local extensions.
6
4
 
7
5
  Native UI is opt-in and currently a preview. Standard controls on Android, browsers, older iOS, and unsupported controls retain Web presentation. Android supports experimental app-local Compose extensions. Keep your application's styles and layout; the iOS theme is optional.
8
6
 
7
+ ## How projection works
8
+
9
+ A **projection** is another presentation of a Web control. Your application keeps the original DOM element, state and event handlers. The plugin reads supported labels, icons, selected/disabled state and geometry, then associates the rendered control with that source element. It does not convert the page into a native screen or run a second copy of your application.
10
+
11
+ | Web source | Projected presentation |
12
+ | ----------------------------------------------- | ----------------------------------------------------------------------------------------- |
13
+ | Supported Ionic buttons, tabs and segments | Native UIKit/SwiftUI controls on supported iOS |
14
+ | Explicitly marked HTML buttons and back buttons | Native buttons on supported iOS |
15
+ | Eligible controls placed in Vertical Bars | SwiftUI controls in the available system rail; Web rail presentation otherwise |
16
+ | An app-local extension's DOM element | The SwiftUI or Compose content registered by your app, using props read from that element |
17
+
18
+ The original element **stays in the DOM** while projected. Its bindings and handlers remain attached. Once native presentation is accepted, the runtime hides the source visually, prevents direct pointer interaction and hides it from Web accessibility so there are not two interactive copies. Element projection retains the source's layout box; rail spacing is supplied by your app or theme. A Web rail similarly uses its adapter's Web presentation: Ionic layout/clones or HTML proxy buttons in your `webHost`.
19
+
20
+ For example, tapping a projected Save button activates the original Web button. Its existing handler performs the save and updates application state; supported DOM changes then update the projection. Tabs and other controls likewise route actions through their source adapters. Continue updating the source, rather than managing separate native state. For reader values that change without a DOM mutation, use `markForCheck()`.
21
+
22
+ Projection changes presentation; it does not translate arbitrary HTML or CSS into native views. Native controls use supported data and styling; a custom Web appearance is not guaranteed to be reproduced. Removing or disabling a source prevents further valid actions. If projection is unavailable or stopped, the runtime releases its hiding state and the source returns to Web presentation, or to an available Web rail. Your application's own hidden/disabled state still applies.
23
+
9
24
  ## Choose what to build
10
25
 
11
- | Goal | Start here |
12
- | --- | --- |
13
- | Render existing Ionic controls natively | [Ionic setup](docs/setup.md) |
14
- | Use native buttons without Ionic | [HTML setup](docs/framework-independent.md) |
15
- | Project only tabs and toolbar actions into a side rail | [Vertical Bars step by step](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md) |
26
+ | Goal | Start here |
27
+ | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
28
+ | Render existing Ionic controls natively | [Ionic setup](docs/setup.md) |
29
+ | Use native buttons without Ionic | [HTML setup](docs/framework-independent.md) |
30
+ | Add a side rail to Ionic while keeping your theme | [Vertical Bars step by step](docs/ionic-vertical-bars.md) |
31
+ | Add a side rail without Ionic | [HTML rail setup](docs/framework-independent.md) |
16
32
  | Add native controls to an existing iOS 27 theme app | [Add Native UI to your theme](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/add-native-ui.md) |
17
- | Render custom SwiftUI or Android Compose content | [App-local extensions (experimental)](docs/native-ui-extensions.md) |
33
+ | Render custom SwiftUI or Android Compose content | [App-local extensions (experimental)](docs/native-ui-extensions.md) |
18
34
 
19
- ## Try the unreleased packages
35
+ ## Install from npm
20
36
 
21
- 1. Open a successful [Package Candidate run](https://github.com/rdlabo-dev/ionic-theme-ios27/actions/workflows/package-candidate.yml) for the commit you want to try. Use the documentation from that same commit. Download and extract its `npm-candidate-…` artifact; GitHub requires sign-in to download artifacts.
22
- 2. Choose the packages for your goal. Replace `VERSION` and the paths with the downloaded filenames:
37
+ We use [Capacitor Foldable](https://github.com/erkamyaman/capacitor-foldable), an excellent community plugin by Erkam Yaman, as our standard device-layout integration. It exposes fold state, hinge geometry, bar placement and change events to your application, giving you control over how the layout responds. Foldable handles device reporting; the theme provides Web layout helpers and Native UI projects controls. Install it alongside the package you use, then connect its events in your app.
23
38
 
24
- **Native UI only** — in your Capacitor 8 app:
39
+ Install Native UI and Foldable together in your Capacitor application:
25
40
 
26
- ```sh
27
- npm install /path/to/rdlabo-capacitor-native-ui-VERSION.tgz
28
- ```
41
+ ```sh
42
+ npm install @rdlabo/capacitor-native-ui @erkamyaman/capacitor-foldable
43
+ ```
29
44
 
30
- **Theme only, or theme with Native UI** — install both archives from the same artifact so their versions match:
45
+ If you also use the iOS 27 theme, install or update both packages together:
31
46
 
32
- ```sh
33
- npm install /path/to/rdlabo-capacitor-native-ui-VERSION.tgz /path/to/rdlabo-ionic-theme-ios27-VERSION.tgz
34
- ```
47
+ ```sh
48
+ npm install @rdlabo/capacitor-native-ui @rdlabo/ionic-theme-ios27 @erkamyaman/capacitor-foldable
49
+ ```
35
50
 
36
- The theme depends on the Native UI package, so its unpublished dependency must also be supplied as an archive. **Theme-only browser apps do not need Capacitor, shell activation, or native sync.** Continue with [theme setup](../theme-ios27/docs/setup.md).
51
+ The theme and Native UI share the same release version; Foldable is versioned independently. Declare Native UI as a direct dependency so Capacitor can discover the plugin. The setup commands install Foldable as a separate application dependency. Installing these packages does not activate native controls; application startup chooses projection and connects device layout.
37
52
 
38
- 3. For Native UI, continue with the guide for your goal above. Build your Web assets, sync Capacitor and rebuild the native app as described there. The archive install replaces each guide's `npm install` step.
53
+ Continue with [Ionic setup](docs/setup.md), [HTML setup](docs/framework-independent.md), or [extensions](docs/native-ui-extensions.md). After configuring startup, build your application's Web assets, run `npx cap sync` and rebuild the native app. No repository checkout or archive download is needed.
39
54
 
40
- Already using published theme v1.2.1? Its bundled native APIs belong to the old setup. Follow [migration](docs/migration.md) when adopting these candidate packages. The [published documentation](https://docs.rdlabo.dev/projects/ionic-theme-ios27) describes that released version; this directory describes the upcoming split.
55
+ Upgrading from the native APIs bundled in theme 1.2.0 or 1.2.1? Follow the [migration guide](docs/migration.md) for import, configuration and native project changes.
41
56
 
42
57
  ## Requirements
43
58
 
@@ -45,10 +60,10 @@ Native startup requires Capacitor 8 and an iOS or Android project. Android exten
45
60
 
46
61
  For standard controls, follow the setup guide for your markup; the Ionic and HTML adapters use different imports. For custom native content, start directly with the [extensions guide](docs/native-ui-extensions.md).
47
62
 
48
- ## Reference and examples
63
+ ## Reference
49
64
 
50
65
  - [Supported Ionic controls](docs/native-ui.md#supported-markup) and fallback behavior.
51
66
  - [Configuration](docs/configuration.md): defaults, selected controls, rail-only mode, and live changes.
67
+ - [Light and dark appearance](docs/appearance.md): inherit the Web theme, configure custom HTML, and understand shared rail appearance.
52
68
  - [Vertical Bars](docs/vertical-bars.md): eligibility, appearance, and runtime API.
53
- - [Now Playing extension](docs/native-mini-player.md): a Web and SwiftUI example to adapt.
54
69
  - [Migration](docs/migration.md): package names, imports, and native project updates.
package/dist/core.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { NativeUIExtension, NativeUIImageReference, NativeUIReadContext, NativeUIValue, defineNativeUI } from "./native/extensions.js";
2
- import { AutoIonicControls, IonicProjectionOptions, NativeUIControls, NativeUIHandle, NativeUIOptions, NativeUIStatus, NativeUISuspension, NativeUIVerticalBarsOptions, ToolbarProjectionOptions } from "./native/definitions.js";
2
+ import { ControlProjection, IonicProjectionOptions, NativeUIControls, NativeUIHandle, NativeUIOptions, NativeUIStatus, NativeUISuspension, NativeUIVerticalBarsOptions } from "./native/definitions.js";
3
3
  import { enableNativeUI } from "./native/startup.js";
4
- export { type AutoIonicControls, type IonicProjectionOptions, type NativeUIControls, type NativeUIExtension, type NativeUIHandle, type NativeUIImageReference, type NativeUIOptions, type NativeUIReadContext, type NativeUIStatus, type NativeUISuspension, type NativeUIValue, type NativeUIVerticalBarsOptions, type ToolbarProjectionOptions, defineNativeUI, enableNativeUI };
4
+ export { type ControlProjection, type IonicProjectionOptions, type NativeUIControls, type NativeUIExtension, type NativeUIHandle, type NativeUIImageReference, type NativeUIOptions, type NativeUIReadContext, type NativeUIStatus, type NativeUISuspension, type NativeUIValue, type NativeUIVerticalBarsOptions, defineNativeUI, enableNativeUI };
@@ -1 +1 @@
1
- {"version":3,"file":"activation.d.ts","names":[],"sources":["../../src/native/activation.ts"],"mappings":";;;UASU;EACR,WAAW;EACX,WAAW;;KAER,kBACH,KAAK,UACL,QAAQ,gBACR,SAAS,wBACT,SAAS,mBAAmB,uCAC5B,8BACG,QAAQ;qBAkBA,UAAQ;qBAqGR,iCAAsC,QAAQ;qBAuB9C,iBAAkB,UAAS,wBAA6B,iBAAiB,mBAAiB,QAAQ"}
1
+ {"version":3,"file":"activation.d.ts","names":[],"sources":["../../src/native/activation.ts"],"mappings":";;;UAUU;EACR,WAAW;EACX,WAAW;;KAER,kBACH,KAAK,UACL,QAAQ,gBACR,SAAS,wBACT,SAAS,mBAAmB,uCAC5B,8BACG,QAAQ;qBAkBA,UAAQ;qBAqGR,iCAAsC,QAAQ;qBAuB9C,iBAAkB,UAAS,wBAA6B,iBAAiB,mBAAiB,QAAQ"}
@@ -1,3 +1,4 @@
1
+ import { controlDefault } from "./shared/control-options.js";
1
2
  import { observeVerticalBarsModals } from "./shared/modal.js";
2
3
  import { toolbarOptions } from "./shared/toolbar-options.js";
3
4
  import { createDiagnostics } from "./shared/diagnostics.js";
@@ -118,11 +119,6 @@ const webProjection = (doc, options, enabled = () => true) => {
118
119
  };
119
120
  /** Call once at application startup. Ionic markup remains the source of truth. */
120
121
  const enableNativeUI = (options = {}, overlayFactory) => {
121
- if (options.enabled === false) {
122
- const current = active;
123
- if (current) stopActivation(current, current.then((handle) => handle.destroy()));
124
- return (stopping ?? Promise.resolve()).then(() => web("Disabled"));
125
- }
126
122
  if (typeof document === "undefined") return Promise.resolve(web("Requires a document"));
127
123
  const extensions = [...options.extensions ?? []];
128
124
  const nativeState = options.nativeState = {};
@@ -131,26 +127,23 @@ const enableNativeUI = (options = {}, overlayFactory) => {
131
127
  } catch (error) {
132
128
  return Promise.reject(error);
133
129
  }
134
- const controls = options.controls;
135
130
  const configuration = JSON.stringify([
136
131
  options.source ?? "ionic",
137
132
  options.debug === true,
138
- options.explicitControls !== false,
133
+ options.ionic?.auto !== false,
139
134
  options.verticalBars?.enabled !== false,
140
135
  options.verticalBars?.placement?.edge,
141
136
  options.rail?.edge,
142
- options.verticalBarsOnly === true,
143
137
  toolbarOptions(options, false),
144
138
  toolbarOptions(options, true),
145
- options.buttonProjection ?? "system",
146
- options.buttonDefaultFill ?? null,
139
+ options.ionic?.verticalBars?.buttonProjection ?? "system",
140
+ options.ionic?.verticalBars?.buttonDefaultFill ?? "glass",
147
141
  ...[
148
142
  "tabs",
149
- "toolbar",
150
143
  "segment",
151
144
  "fab"
152
- ].map((component) => !controls || controls[component] === true),
153
- controls?.popover === true
145
+ ].flatMap((kind) => [controlDefault(options, kind, false), controlDefault(options, kind, true)]),
146
+ options.ionic?.verticalBars?.popover === true
154
147
  ]);
155
148
  if (active && activeFailed) {
156
149
  const previous = active;
@@ -186,7 +179,7 @@ const enableNativeUI = (options = {}, overlayFactory) => {
186
179
  const platform = Capacitor.getPlatform();
187
180
  const diagnose = createDiagnostics(options.debug);
188
181
  const stopModals = options.source === "html" ? () => {} : observeVerticalBarsModals(document);
189
- const prehide = platform !== "android" && options.source !== "html" && (options.controls === void 0 || options.controls.toolbar === true) ? prehideVerticalBarsToolbarSources(document, () => options.verticalBars?.enabled !== false, options.root, options) : void 0;
182
+ const prehide = platform !== "android" && options.source !== "html" && options.ionic?.auto !== false ? prehideVerticalBarsToolbarSources(document, () => options.verticalBars?.enabled !== false, options.root, options) : void 0;
190
183
  const fallback = (reason) => {
191
184
  diagnose(`web-fallback: ${reason}`);
192
185
  return manage(webProjection(document, options), {
@@ -225,7 +218,14 @@ const enableNativeUI = (options = {}, overlayFactory) => {
225
218
  }
226
219
  };
227
220
  try {
228
- const capabilities = await bounded(plugin.configure({ verticalBarsOnly: options.verticalBarsOnly === true }));
221
+ const ordinaryControls = [
222
+ "button",
223
+ "backButton",
224
+ "tabs",
225
+ "segment",
226
+ "fab"
227
+ ].some((kind) => controlDefault(options, kind, false));
228
+ const capabilities = await bounded(plugin.configure({ suppressScrollEdgeEffect: ordinaryControls }));
229
229
  if (!capabilities.supported) return fallback("Native UI is unavailable on this device");
230
230
  const unavailableExtensions = /* @__PURE__ */ new Map();
231
231
  for (const extension of extensions) if (platform === "android" && extension.placement === "tab-accessory") unavailableExtensions.set(extension.name, "unsupported-placement: tab-accessory requires a supported iOS tab host.");
@@ -236,7 +236,7 @@ const enableNativeUI = (options = {}, overlayFactory) => {
236
236
  source: "extensions",
237
237
  extensions,
238
238
  unavailableExtensions
239
- }, () => false, options.verticalBarsOnly === true, runtimeStopped);
239
+ }, () => false, runtimeStopped);
240
240
  runtime = combine(runtime, webProjection(document, options));
241
241
  metricsListener = await boundedListener(plugin.addListener("webViewMetricsChange", () => {
242
242
  document.defaultView?.dispatchEvent(new Event("nativeUIRefresh"));
@@ -265,7 +265,7 @@ const enableNativeUI = (options = {}, overlayFactory) => {
265
265
  return (app.classList.contains("ios-theme-vertical-bars-left") ? "left" : "right") === nativeState.edge;
266
266
  };
267
267
  const applyMetrics = (metrics) => {
268
- if (!options.verticalBarsOnly) nativeWebViewMetrics.radius = metrics.radius;
268
+ if (ordinaryControls) nativeWebViewMetrics.radius = metrics.radius;
269
269
  deviceEdge = metrics.verticalBarEdge ?? null;
270
270
  updateNativeEdge();
271
271
  document.defaultView?.dispatchEvent(new Event("nativeUIRefresh"));
@@ -276,9 +276,9 @@ const enableNativeUI = (options = {}, overlayFactory) => {
276
276
  ...options,
277
277
  extensions,
278
278
  unavailableExtensions
279
- }, nativeVerticalBars, options.verticalBarsOnly === true, runtimeStopped);
279
+ }, nativeVerticalBars, runtimeStopped);
280
280
  runtime = combine(native, webProjection(document, options, () => !nativeVerticalBars() || native.getStatus().projected === 0));
281
- if (controls?.popover === true && overlayFactory) overlays = await overlayFactory(document, plugin, options, native.retain, nativeVerticalBars);
281
+ if (options.ionic?.verticalBars?.popover === true && overlayFactory) overlays = await overlayFactory(document, plugin, options, native.retain, nativeVerticalBars);
282
282
  return manage(runtime, {
283
283
  suspend: async () => {
284
284
  const restorePrehide = prehide?.suspend();
@@ -1 +1 @@
1
- {"version":3,"file":"activation.js","names":[],"sources":["../../src/native/activation.ts"],"sourcesContent":["import { toolbarOptions } from './shared/toolbar-options';\nimport { createDiagnostics } from './shared/diagnostics';\nimport { createHTMLWebProjection } from './html';\nimport { validateExtensions, type NativeUIExtension } from './extensions';\nimport { Capacitor, registerPlugin } from '@capacitor/core';\nimport { nativeWebViewMetrics } from '../native-integration';\nimport type { NativeUIRuntimeHandle, NativeUIRuntimeOptions, NativeUIPlugin, VerticalBarEdge, WebViewMetrics } from './definitions';\nimport { createRuntime } from './runtime';\nimport { bounded, boundedListener } from './shared/bridge';\ninterface OverlayController {\n suspend(): Promise<() => void>;\n destroy(): Promise<void>;\n}\ntype OverlayFactory = (\n doc: Document,\n plugin: NativeUIPlugin,\n options: NativeUIRuntimeOptions,\n retain: (deferActivation?: (id: string) => boolean) => () => void,\n nativeRail: () => boolean,\n) => Promise<OverlayController>;\nimport { createVerticalBarsWebProjection } from './vertical-bars-web';\nimport { prehideVerticalBarsToolbarSources } from './prehide';\nimport { observeVerticalBarsModals } from './shared/modal';\nexport { defineNativeUI } from './extensions';\nexport type { NativeUIExtension, NativeUIValue } from './extensions';\nexport type {\n NativeUIComponent,\n NativeUIControls,\n NativeUIRuntimeHandle,\n NativeUIRuntimeOptions,\n NativeUIStatus,\n NativeUISuspension,\n VerticalBarEdge,\n WebViewMetrics,\n} from './definitions';\n\nconst plugin = registerPlugin<NativeUIPlugin>('NativeUI');\nexport const NativeUI = plugin;\nlet active: Promise<NativeUIRuntimeHandle> | undefined;\nlet activeFailed = false;\nlet activeRoot: HTMLElement | undefined;\nlet activeRailHost: HTMLElement | undefined;\nlet activeConfiguration: string | undefined;\nlet activeExtensions: readonly NativeUIExtension[] = [];\nlet stopping: Promise<void> | undefined;\n\n// Every stop entry point releases the activation immediately but reserves the\n// shared native/DOM resources until its teardown has settled.\nconst stopActivation = (owner: Promise<NativeUIRuntimeHandle>, destruction: Promise<void>) => {\n if (active !== owner) return;\n active = undefined;\n activeFailed = false;\n activeConfiguration = undefined;\n activeRoot = undefined;\n activeRailHost = undefined;\n activeExtensions = [];\n stopping = destruction;\n const release = () => {\n if (stopping === destruction) stopping = undefined;\n };\n void destruction.then(release, release);\n};\nconst web = (reason: string): NativeUIRuntimeHandle => ({\n markForCheck: () => {},\n getStatus: () => ({ state: 'web', projected: 0, updates: 0, reason }),\n suspend: async () => ({ resume: async () => {} }),\n destroy: async () => {},\n});\n\nconst combine = (native: NativeUIRuntimeHandle, fallback: NativeUIRuntimeHandle): NativeUIRuntimeHandle => ({\n markForCheck() {\n native.markForCheck();\n fallback.markForCheck();\n },\n getStatus() {\n const nativeStatus = native.getStatus();\n const fallbackStatus = fallback.getStatus();\n return {\n ...nativeStatus,\n state:\n nativeStatus.state === 'stopped' && fallbackStatus.state === 'stopped' ? 'stopped' : nativeStatus.projected > 0 ? 'native' : 'web',\n projected: nativeStatus.projected + fallbackStatus.projected,\n updates: nativeStatus.updates + fallbackStatus.updates,\n };\n },\n async suspend() {\n const [nativeLease, fallbackLease] = await Promise.all([native.suspend(), fallback.suspend()]);\n return { resume: async () => void (await Promise.all([nativeLease.resume(), fallbackLease.resume()])) };\n },\n async destroy() {\n await Promise.all([native.destroy(), fallback.destroy()]);\n },\n});\n\n/** Wraps a runtime handle with the shared enable-lifecycle: reason, suspend hooks and idempotent destroy. */\nconst manage = (\n handle: NativeUIRuntimeHandle,\n lifecycle: {\n reason?: string;\n suspend?: () => (() => void) | undefined | Promise<(() => void) | undefined>;\n /** Registers teardown before it starts so direct destroy and disable share the same queue. */\n stopping?: (destruction: Promise<void>) => void;\n destroy?: () => void | Promise<void>;\n } = {},\n): NativeUIRuntimeHandle => {\n let destruction: Promise<void> | undefined;\n return {\n markForCheck: () => {\n if (!destruction) handle.markForCheck();\n },\n getStatus: () => (lifecycle.reason ? { ...handle.getStatus(), reason: lifecycle.reason } : handle.getStatus()),\n async suspend() {\n const lease = await handle.suspend();\n const resume = await lifecycle.suspend?.();\n return {\n async resume() {\n await lease.resume();\n resume?.();\n },\n };\n },\n destroy() {\n if (!destruction) {\n destruction = Promise.resolve().then(async () => {\n try {\n await handle.destroy();\n } finally {\n await lifecycle.destroy?.();\n }\n });\n lifecycle.stopping?.(destruction);\n }\n return destruction;\n },\n };\n};\n\n/** Reads the current native WebView geometry and applies it to page transitions. */\nexport const configureNativeTransition = async (): Promise<WebViewMetrics> => {\n const metrics =\n typeof document !== 'undefined' && Capacitor.getPlatform() === 'ios' && Capacitor.isPluginAvailable('NativeUI')\n ? await bounded(plugin.getWebViewMetrics())\n : { radius: 0 };\n nativeWebViewMetrics.radius = metrics.radius;\n return metrics;\n};\n\n/** Resolves a logical vertical-bar edge to the physical side for the given direction. */\nconst physicalVerticalBarEdge = (edge: Exclude<VerticalBarEdge, null>, rtl: boolean): 'left' | 'right' =>\n (edge === 'leading') !== rtl ? 'left' : 'right';\n\nconst elementRtl = (element: Element): boolean => element.closest('[dir]')?.getAttribute('dir') === 'rtl';\n\nconst webProjection = (doc: Document, options: NativeUIRuntimeOptions, enabled: () => boolean = () => true) => {\n const ionic = () => createVerticalBarsWebProjection(doc, options, () => options.verticalBars?.enabled !== false && enabled());\n if (options.source === 'html') return createHTMLWebProjection(doc, options);\n if (options.source === 'auto') return combine(ionic(), createHTMLWebProjection(doc, options));\n return ionic();\n};\n\n/** Call once at application startup. Ionic markup remains the source of truth. */\nexport const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFactory?: OverlayFactory): Promise<NativeUIRuntimeHandle> => {\n if (options.enabled === false) {\n const current = active;\n if (current)\n stopActivation(\n current,\n current.then((handle) => handle.destroy()),\n );\n return (stopping ?? Promise.resolve()).then(() => web('Disabled'));\n }\n if (typeof document === 'undefined') return Promise.resolve(web('Requires a document'));\n const extensions = [...(options.extensions ?? [])];\n const nativeState: NonNullable<NativeUIRuntimeOptions['nativeState']> = (options.nativeState = {});\n try {\n validateExtensions(extensions, document);\n } catch (error) {\n return Promise.reject(error);\n }\n const controls = options.controls;\n const configuration = JSON.stringify([\n options.source ?? 'ionic',\n options.debug === true,\n options.explicitControls !== false,\n options.verticalBars?.enabled !== false,\n options.verticalBars?.placement?.edge,\n options.rail?.edge,\n options.verticalBarsOnly === true,\n toolbarOptions(options, false),\n toolbarOptions(options, true),\n options.buttonProjection ?? 'system',\n // Local source overrides can use this even when the startup projection is system.\n options.buttonDefaultFill ?? null,\n ...(['tabs', 'toolbar', 'segment', 'fab'] as const).map((component) => !controls || controls[component] === true),\n controls?.popover === true,\n ]);\n if (active && activeFailed) {\n const previous = active;\n stopActivation(\n previous,\n previous.then((handle) => handle.destroy()),\n );\n }\n if (\n active &&\n (activeConfiguration !== configuration ||\n activeRoot !== options.root ||\n activeRailHost !== options.rail?.host ||\n extensions.length !== activeExtensions.length ||\n extensions.some((extension, index) => extension !== activeExtensions[index]))\n )\n return Promise.reject(\n new Error('Native UI is already running with different controls or extensions; destroy it before changing configuration.'),\n );\n if (active) return active;\n activeConfiguration = configuration;\n activeRoot = options.root;\n activeRailHost = options.rail?.host;\n activeExtensions = extensions;\n const previousStop = stopping;\n let start: Promise<NativeUIRuntimeHandle>;\n const stop = (destruction: Promise<void>) => stopActivation(start, destruction);\n let releaseNative = async () => {};\n let retired = false;\n const pagehide = () => runtimeStopped('pagehide');\n const runtimeStopped = (cause: 'failure' | 'pagehide') => {\n if (cause === 'failure') {\n // Keep the Web rail usable, but replace this degraded activation on the next enable.\n if (active === start) activeFailed = true;\n void start\n .then(async () => {\n if (!retired) document.defaultView?.addEventListener('pagehide', pagehide, { once: true });\n await releaseNative();\n })\n .catch(() => {});\n return;\n }\n const destruction = start.then((handle) => handle.destroy());\n stop(destruction);\n void destruction.catch(() => {});\n };\n return (start = active =\n (async () => {\n // Native controls and DOM ownership are shared. A replacement activation\n // must wait until the old handle has finished clearing both surfaces.\n if (previousStop) await previousStop.catch(() => {});\n const platform = Capacitor.getPlatform();\n const diagnose = createDiagnostics(options.debug);\n const stopModals = options.source === 'html' ? () => {} : observeVerticalBarsModals(document);\n const prehide =\n platform !== 'android' && options.source !== 'html' && (options.controls === undefined || options.controls.toolbar === true)\n ? prehideVerticalBarsToolbarSources(document, () => options.verticalBars?.enabled !== false, options.root, options)\n : undefined;\n const fallback = (reason: string) => {\n diagnose(`web-fallback: ${reason}`);\n return manage(webProjection(document, options), {\n reason,\n suspend: () => prehide?.suspend(),\n destroy: () => {\n retired = true;\n document.defaultView?.removeEventListener('pagehide', pagehide);\n prehide?.stop();\n stopModals();\n },\n stopping: stop,\n });\n };\n if (platform !== 'ios' && platform !== 'android') return fallback('Requires Capacitor iOS or Android');\n if (!Capacitor.isPluginAvailable('NativeUI')) return fallback('Native UI plugin is not registered');\n let runtime: NativeUIRuntimeHandle | undefined;\n let overlays: OverlayController | undefined;\n let metricsListener: Awaited<ReturnType<typeof plugin.addListener>> | undefined;\n let nativeRelease: Promise<void> | undefined;\n releaseNative = () =>\n (nativeRelease ??= (async () => {\n try {\n await overlays?.destroy();\n } finally {\n if (metricsListener) await bounded(metricsListener.remove()).catch(() => {});\n }\n })());\n const cleanup = async () => {\n retired = true;\n try {\n await releaseNative();\n } finally {\n document.defaultView?.removeEventListener('pagehide', pagehide);\n prehide?.stop();\n stopModals();\n }\n };\n try {\n const capabilities = await bounded(plugin.configure({ verticalBarsOnly: options.verticalBarsOnly === true }));\n if (!capabilities.supported) return fallback('Native UI is unavailable on this device');\n const unavailableExtensions = new Map<string, string>();\n for (const extension of extensions) {\n if (platform === 'android' && extension.placement === 'tab-accessory')\n unavailableExtensions.set(extension.name, 'unsupported-placement: tab-accessory requires a supported iOS tab host.');\n else if (!capabilities.extensions?.includes(extension.name))\n unavailableExtensions.set(\n extension.name,\n 'unregistered-extension: register this name in the native app before enabling the shell, then rebuild.',\n );\n }\n if (platform === 'android') {\n runtime = await createRuntime(\n document,\n plugin,\n {\n ...options,\n source: 'extensions',\n extensions,\n unavailableExtensions,\n },\n () => false,\n options.verticalBarsOnly === true,\n runtimeStopped,\n );\n runtime = combine(runtime, webProjection(document, options));\n metricsListener = await boundedListener(\n plugin.addListener('webViewMetricsChange', () => {\n document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));\n }),\n );\n return manage(runtime, {\n stopping: stop,\n destroy: cleanup,\n });\n }\n let deviceEdge: VerticalBarEdge = null;\n const ionicRoot = () => {\n if (options.source === 'html') return null;\n const root = options.root ?? document.body;\n return root.closest<HTMLElement>('ion-app') ?? root.querySelector<HTMLElement>('ion-app');\n };\n const updateNativeEdge = () => {\n const requested = options.verticalBars?.placement?.edge;\n const root = ionicRoot() ?? options.root ?? document.body;\n nativeState.edge =\n deviceEdge && (!requested || requested === deviceEdge) ? physicalVerticalBarEdge(deviceEdge, elementRtl(root)) : undefined;\n };\n // Ionic layout is read-only input; device metrics determine native availability.\n const nativeVerticalBars = () => {\n updateNativeEdge();\n if (options.verticalBars?.enabled === false) return false;\n const app = ionicRoot();\n if (!app?.classList.contains('ios-theme-vertical-bars')) return !!nativeState.edge;\n const physicalEdge = app.classList.contains('ios-theme-vertical-bars-left') ? 'left' : 'right';\n return physicalEdge === nativeState.edge;\n };\n const applyMetrics = (metrics: WebViewMetrics) => {\n if (!options.verticalBarsOnly) nativeWebViewMetrics.radius = metrics.radius;\n deviceEdge = metrics.verticalBarEdge ?? null;\n updateNativeEdge();\n document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));\n };\n metricsListener = await boundedListener(plugin.addListener('webViewMetricsChange', applyMetrics));\n await bounded(Promise.resolve().then(() => plugin.getWebViewMetrics()))\n .then(applyMetrics)\n .catch(() => undefined);\n const native = await createRuntime(\n document,\n plugin,\n { ...options, extensions, unavailableExtensions },\n nativeVerticalBars,\n options.verticalBarsOnly === true,\n runtimeStopped,\n );\n runtime = combine(\n native,\n webProjection(document, options, () => !nativeVerticalBars() || native.getStatus().projected === 0),\n );\n if (controls?.popover === true && overlayFactory)\n overlays = await overlayFactory(document, plugin, options, native.retain, nativeVerticalBars);\n return manage(runtime, {\n suspend: async () => {\n const restorePrehide = prehide?.suspend();\n const resumeOverlays = await overlays?.suspend();\n return () => {\n restorePrehide?.();\n resumeOverlays?.();\n };\n },\n stopping: stop,\n destroy: cleanup,\n });\n } catch (error) {\n await overlays?.destroy().catch(() => {});\n await runtime?.destroy().catch(() => {});\n if (metricsListener) await bounded(metricsListener.remove()).catch(() => {});\n return fallback(error instanceof Error ? error.message : String(error));\n }\n })());\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"activation.js","names":[],"sources":["../../src/native/activation.ts"],"sourcesContent":["import { controlDefault } from './shared/control-options';\nimport { toolbarOptions } from './shared/toolbar-options';\nimport { createDiagnostics } from './shared/diagnostics';\nimport { createHTMLWebProjection } from './html';\nimport { validateExtensions, type NativeUIExtension } from './extensions';\nimport { Capacitor, registerPlugin } from '@capacitor/core';\nimport { nativeWebViewMetrics } from '../native-integration';\nimport type { NativeUIRuntimeHandle, NativeUIRuntimeOptions, NativeUIPlugin, VerticalBarEdge, WebViewMetrics } from './definitions';\nimport { createRuntime } from './runtime';\nimport { bounded, boundedListener } from './shared/bridge';\ninterface OverlayController {\n suspend(): Promise<() => void>;\n destroy(): Promise<void>;\n}\ntype OverlayFactory = (\n doc: Document,\n plugin: NativeUIPlugin,\n options: NativeUIRuntimeOptions,\n retain: (deferActivation?: (id: string) => boolean) => () => void,\n nativeRail: () => boolean,\n) => Promise<OverlayController>;\nimport { createVerticalBarsWebProjection } from './vertical-bars-web';\nimport { prehideVerticalBarsToolbarSources } from './prehide';\nimport { observeVerticalBarsModals } from './shared/modal';\nexport { defineNativeUI } from './extensions';\nexport type { NativeUIExtension, NativeUIValue } from './extensions';\nexport type {\n NativeUIComponent,\n NativeUIControls,\n NativeUIRuntimeHandle,\n NativeUIRuntimeOptions,\n NativeUIStatus,\n NativeUISuspension,\n VerticalBarEdge,\n WebViewMetrics,\n} from './definitions';\n\nconst plugin = registerPlugin<NativeUIPlugin>('NativeUI');\nexport const NativeUI = plugin;\nlet active: Promise<NativeUIRuntimeHandle> | undefined;\nlet activeFailed = false;\nlet activeRoot: HTMLElement | undefined;\nlet activeRailHost: HTMLElement | undefined;\nlet activeConfiguration: string | undefined;\nlet activeExtensions: readonly NativeUIExtension[] = [];\nlet stopping: Promise<void> | undefined;\n\n// Every stop entry point releases the activation immediately but reserves the\n// shared native/DOM resources until its teardown has settled.\nconst stopActivation = (owner: Promise<NativeUIRuntimeHandle>, destruction: Promise<void>) => {\n if (active !== owner) return;\n active = undefined;\n activeFailed = false;\n activeConfiguration = undefined;\n activeRoot = undefined;\n activeRailHost = undefined;\n activeExtensions = [];\n stopping = destruction;\n const release = () => {\n if (stopping === destruction) stopping = undefined;\n };\n void destruction.then(release, release);\n};\nconst web = (reason: string): NativeUIRuntimeHandle => ({\n markForCheck: () => {},\n getStatus: () => ({ state: 'web', projected: 0, updates: 0, reason }),\n suspend: async () => ({ resume: async () => {} }),\n destroy: async () => {},\n});\n\nconst combine = (native: NativeUIRuntimeHandle, fallback: NativeUIRuntimeHandle): NativeUIRuntimeHandle => ({\n markForCheck() {\n native.markForCheck();\n fallback.markForCheck();\n },\n getStatus() {\n const nativeStatus = native.getStatus();\n const fallbackStatus = fallback.getStatus();\n return {\n ...nativeStatus,\n state:\n nativeStatus.state === 'stopped' && fallbackStatus.state === 'stopped' ? 'stopped' : nativeStatus.projected > 0 ? 'native' : 'web',\n projected: nativeStatus.projected + fallbackStatus.projected,\n updates: nativeStatus.updates + fallbackStatus.updates,\n };\n },\n async suspend() {\n const [nativeLease, fallbackLease] = await Promise.all([native.suspend(), fallback.suspend()]);\n return { resume: async () => void (await Promise.all([nativeLease.resume(), fallbackLease.resume()])) };\n },\n async destroy() {\n await Promise.all([native.destroy(), fallback.destroy()]);\n },\n});\n\n/** Wraps a runtime handle with the shared enable-lifecycle: reason, suspend hooks and idempotent destroy. */\nconst manage = (\n handle: NativeUIRuntimeHandle,\n lifecycle: {\n reason?: string;\n suspend?: () => (() => void) | undefined | Promise<(() => void) | undefined>;\n /** Registers teardown before it starts so direct destroy and disable share the same queue. */\n stopping?: (destruction: Promise<void>) => void;\n destroy?: () => void | Promise<void>;\n } = {},\n): NativeUIRuntimeHandle => {\n let destruction: Promise<void> | undefined;\n return {\n markForCheck: () => {\n if (!destruction) handle.markForCheck();\n },\n getStatus: () => (lifecycle.reason ? { ...handle.getStatus(), reason: lifecycle.reason } : handle.getStatus()),\n async suspend() {\n const lease = await handle.suspend();\n const resume = await lifecycle.suspend?.();\n return {\n async resume() {\n await lease.resume();\n resume?.();\n },\n };\n },\n destroy() {\n if (!destruction) {\n destruction = Promise.resolve().then(async () => {\n try {\n await handle.destroy();\n } finally {\n await lifecycle.destroy?.();\n }\n });\n lifecycle.stopping?.(destruction);\n }\n return destruction;\n },\n };\n};\n\n/** Reads the current native WebView geometry and applies it to page transitions. */\nexport const configureNativeTransition = async (): Promise<WebViewMetrics> => {\n const metrics =\n typeof document !== 'undefined' && Capacitor.getPlatform() === 'ios' && Capacitor.isPluginAvailable('NativeUI')\n ? await bounded(plugin.getWebViewMetrics())\n : { radius: 0 };\n nativeWebViewMetrics.radius = metrics.radius;\n return metrics;\n};\n\n/** Resolves a logical vertical-bar edge to the physical side for the given direction. */\nconst physicalVerticalBarEdge = (edge: Exclude<VerticalBarEdge, null>, rtl: boolean): 'left' | 'right' =>\n (edge === 'leading') !== rtl ? 'left' : 'right';\n\nconst elementRtl = (element: Element): boolean => element.closest('[dir]')?.getAttribute('dir') === 'rtl';\n\nconst webProjection = (doc: Document, options: NativeUIRuntimeOptions, enabled: () => boolean = () => true) => {\n const ionic = () => createVerticalBarsWebProjection(doc, options, () => options.verticalBars?.enabled !== false && enabled());\n if (options.source === 'html') return createHTMLWebProjection(doc, options);\n if (options.source === 'auto') return combine(ionic(), createHTMLWebProjection(doc, options));\n return ionic();\n};\n\n/** Call once at application startup. Ionic markup remains the source of truth. */\nexport const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFactory?: OverlayFactory): Promise<NativeUIRuntimeHandle> => {\n if (typeof document === 'undefined') return Promise.resolve(web('Requires a document'));\n const extensions = [...(options.extensions ?? [])];\n const nativeState: NonNullable<NativeUIRuntimeOptions['nativeState']> = (options.nativeState = {});\n try {\n validateExtensions(extensions, document);\n } catch (error) {\n return Promise.reject(error);\n }\n const configuration = JSON.stringify([\n options.source ?? 'ionic',\n options.debug === true,\n options.ionic?.auto !== false,\n options.verticalBars?.enabled !== false,\n options.verticalBars?.placement?.edge,\n options.rail?.edge,\n toolbarOptions(options, false),\n toolbarOptions(options, true),\n options.ionic?.verticalBars?.buttonProjection ?? 'system',\n // Local source overrides can use this even when the startup projection is system.\n options.ionic?.verticalBars?.buttonDefaultFill ?? 'glass',\n ...(['tabs', 'segment', 'fab'] as const).flatMap((kind) => [controlDefault(options, kind, false), controlDefault(options, kind, true)]),\n options.ionic?.verticalBars?.popover === true,\n ]);\n if (active && activeFailed) {\n const previous = active;\n stopActivation(\n previous,\n previous.then((handle) => handle.destroy()),\n );\n }\n if (\n active &&\n (activeConfiguration !== configuration ||\n activeRoot !== options.root ||\n activeRailHost !== options.rail?.host ||\n extensions.length !== activeExtensions.length ||\n extensions.some((extension, index) => extension !== activeExtensions[index]))\n )\n return Promise.reject(\n new Error('Native UI is already running with different controls or extensions; destroy it before changing configuration.'),\n );\n if (active) return active;\n activeConfiguration = configuration;\n activeRoot = options.root;\n activeRailHost = options.rail?.host;\n activeExtensions = extensions;\n const previousStop = stopping;\n let start: Promise<NativeUIRuntimeHandle>;\n const stop = (destruction: Promise<void>) => stopActivation(start, destruction);\n let releaseNative = async () => {};\n let retired = false;\n const pagehide = () => runtimeStopped('pagehide');\n const runtimeStopped = (cause: 'failure' | 'pagehide') => {\n if (cause === 'failure') {\n // Keep the Web rail usable, but replace this degraded activation on the next enable.\n if (active === start) activeFailed = true;\n void start\n .then(async () => {\n if (!retired) document.defaultView?.addEventListener('pagehide', pagehide, { once: true });\n await releaseNative();\n })\n .catch(() => {});\n return;\n }\n const destruction = start.then((handle) => handle.destroy());\n stop(destruction);\n void destruction.catch(() => {});\n };\n return (start = active =\n (async () => {\n // Native controls and DOM ownership are shared. A replacement activation\n // must wait until the old handle has finished clearing both surfaces.\n if (previousStop) await previousStop.catch(() => {});\n const platform = Capacitor.getPlatform();\n const diagnose = createDiagnostics(options.debug);\n const stopModals = options.source === 'html' ? () => {} : observeVerticalBarsModals(document);\n const prehide =\n platform !== 'android' && options.source !== 'html' && options.ionic?.auto !== false\n ? prehideVerticalBarsToolbarSources(document, () => options.verticalBars?.enabled !== false, options.root, options)\n : undefined;\n const fallback = (reason: string) => {\n diagnose(`web-fallback: ${reason}`);\n return manage(webProjection(document, options), {\n reason,\n suspend: () => prehide?.suspend(),\n destroy: () => {\n retired = true;\n document.defaultView?.removeEventListener('pagehide', pagehide);\n prehide?.stop();\n stopModals();\n },\n stopping: stop,\n });\n };\n if (platform !== 'ios' && platform !== 'android') return fallback('Requires Capacitor iOS or Android');\n if (!Capacitor.isPluginAvailable('NativeUI')) return fallback('Native UI plugin is not registered');\n let runtime: NativeUIRuntimeHandle | undefined;\n let overlays: OverlayController | undefined;\n let metricsListener: Awaited<ReturnType<typeof plugin.addListener>> | undefined;\n let nativeRelease: Promise<void> | undefined;\n releaseNative = () =>\n (nativeRelease ??= (async () => {\n try {\n await overlays?.destroy();\n } finally {\n if (metricsListener) await bounded(metricsListener.remove()).catch(() => {});\n }\n })());\n const cleanup = async () => {\n retired = true;\n try {\n await releaseNative();\n } finally {\n document.defaultView?.removeEventListener('pagehide', pagehide);\n prehide?.stop();\n stopModals();\n }\n };\n try {\n const ordinaryControls = (['button', 'backButton', 'tabs', 'segment', 'fab'] as const).some((kind) =>\n controlDefault(options, kind, false),\n );\n const capabilities = await bounded(plugin.configure({ suppressScrollEdgeEffect: ordinaryControls }));\n if (!capabilities.supported) return fallback('Native UI is unavailable on this device');\n const unavailableExtensions = new Map<string, string>();\n for (const extension of extensions) {\n if (platform === 'android' && extension.placement === 'tab-accessory')\n unavailableExtensions.set(extension.name, 'unsupported-placement: tab-accessory requires a supported iOS tab host.');\n else if (!capabilities.extensions?.includes(extension.name))\n unavailableExtensions.set(\n extension.name,\n 'unregistered-extension: register this name in the native app before enabling the shell, then rebuild.',\n );\n }\n if (platform === 'android') {\n runtime = await createRuntime(\n document,\n plugin,\n {\n ...options,\n source: 'extensions',\n extensions,\n unavailableExtensions,\n },\n () => false,\n runtimeStopped,\n );\n runtime = combine(runtime, webProjection(document, options));\n metricsListener = await boundedListener(\n plugin.addListener('webViewMetricsChange', () => {\n document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));\n }),\n );\n return manage(runtime, {\n stopping: stop,\n destroy: cleanup,\n });\n }\n let deviceEdge: VerticalBarEdge = null;\n const ionicRoot = () => {\n if (options.source === 'html') return null;\n const root = options.root ?? document.body;\n return root.closest<HTMLElement>('ion-app') ?? root.querySelector<HTMLElement>('ion-app');\n };\n const updateNativeEdge = () => {\n const requested = options.verticalBars?.placement?.edge;\n const root = ionicRoot() ?? options.root ?? document.body;\n nativeState.edge =\n deviceEdge && (!requested || requested === deviceEdge) ? physicalVerticalBarEdge(deviceEdge, elementRtl(root)) : undefined;\n };\n // Ionic layout is read-only input; device metrics determine native availability.\n const nativeVerticalBars = () => {\n updateNativeEdge();\n if (options.verticalBars?.enabled === false) return false;\n const app = ionicRoot();\n if (!app?.classList.contains('ios-theme-vertical-bars')) return !!nativeState.edge;\n const physicalEdge = app.classList.contains('ios-theme-vertical-bars-left') ? 'left' : 'right';\n return physicalEdge === nativeState.edge;\n };\n const applyMetrics = (metrics: WebViewMetrics) => {\n if (ordinaryControls) nativeWebViewMetrics.radius = metrics.radius;\n deviceEdge = metrics.verticalBarEdge ?? null;\n updateNativeEdge();\n document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));\n };\n metricsListener = await boundedListener(plugin.addListener('webViewMetricsChange', applyMetrics));\n await bounded(Promise.resolve().then(() => plugin.getWebViewMetrics()))\n .then(applyMetrics)\n .catch(() => undefined);\n const native = await createRuntime(\n document,\n plugin,\n { ...options, extensions, unavailableExtensions },\n nativeVerticalBars,\n runtimeStopped,\n );\n runtime = combine(\n native,\n webProjection(document, options, () => !nativeVerticalBars() || native.getStatus().projected === 0),\n );\n if (options.ionic?.verticalBars?.popover === true && overlayFactory)\n overlays = await overlayFactory(document, plugin, options, native.retain, nativeVerticalBars);\n return manage(runtime, {\n suspend: async () => {\n const restorePrehide = prehide?.suspend();\n const resumeOverlays = await overlays?.suspend();\n return () => {\n restorePrehide?.();\n resumeOverlays?.();\n };\n },\n stopping: stop,\n destroy: cleanup,\n });\n } catch (error) {\n await overlays?.destroy().catch(() => {});\n await runtime?.destroy().catch(() => {});\n if (metricsListener) await bounded(metricsListener.remove()).catch(() => {});\n return fallback(error instanceof Error ? error.message : String(error));\n }\n })());\n};\n"],"mappings":";;;;;;;;;;;;;AAqCA,MAAM,SAAS,eAA+B,UAAU;AACxD,MAAa,WAAW;AACxB,IAAI;AACJ,IAAI,eAAe;AACnB,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI,mBAAiD,CAAC;AACtD,IAAI;AAIJ,MAAM,kBAAkB,OAAuC,gBAA+B;CAC5F,IAAI,WAAW,OAAO;CACtB,SAAS,KAAA;CACT,eAAe;CACf,sBAAsB,KAAA;CACtB,aAAa,KAAA;CACb,iBAAiB,KAAA;CACjB,mBAAmB,CAAC;CACpB,WAAW;CACX,MAAM,gBAAgB;EACpB,IAAI,aAAa,aAAa,WAAW,KAAA;CAC3C;CACA,YAAiB,KAAK,SAAS,OAAO;AACxC;AACA,MAAM,OAAO,YAA2C;CACtD,oBAAoB,CAAC;CACrB,kBAAkB;EAAE,OAAO;EAAO,WAAW;EAAG,SAAS;EAAG;CAAO;CACnE,SAAS,aAAa,EAAE,QAAQ,YAAY,CAAC,EAAE;CAC/C,SAAS,YAAY,CAAC;AACxB;AAEA,MAAM,WAAW,QAA+B,cAA4D;CAC1G,eAAe;EACb,OAAO,aAAa;EACpB,SAAS,aAAa;CACxB;CACA,YAAY;EACV,MAAM,eAAe,OAAO,UAAU;EACtC,MAAM,iBAAiB,SAAS,UAAU;EAC1C,OAAO;GACL,GAAG;GACH,OACE,aAAa,UAAU,aAAa,eAAe,UAAU,YAAY,YAAY,aAAa,YAAY,IAAI,WAAW;GAC/H,WAAW,aAAa,YAAY,eAAe;GACnD,SAAS,aAAa,UAAU,eAAe;EACjD;CACF;CACA,MAAM,UAAU;EACd,MAAM,CAAC,aAAa,iBAAiB,MAAM,QAAQ,IAAI,CAAC,OAAO,QAAQ,GAAG,SAAS,QAAQ,CAAC,CAAC;EAC7F,OAAO,EAAE,QAAQ,YAAY,KAAM,MAAM,QAAQ,IAAI,CAAC,YAAY,OAAO,GAAG,cAAc,OAAO,CAAC,CAAC,EAAG;CACxG;CACA,MAAM,UAAU;EACd,MAAM,QAAQ,IAAI,CAAC,OAAO,QAAQ,GAAG,SAAS,QAAQ,CAAC,CAAC;CAC1D;AACF;;AAGA,MAAM,UACJ,QACA,YAMI,CAAC,MACqB;CAC1B,IAAI;CACJ,OAAO;EACL,oBAAoB;GAClB,IAAI,CAAC,aAAa,OAAO,aAAa;EACxC;EACA,iBAAkB,UAAU,SAAS;GAAE,GAAG,OAAO,UAAU;GAAG,QAAQ,UAAU;EAAO,IAAI,OAAO,UAAU;EAC5G,MAAM,UAAU;GACd,MAAM,QAAQ,MAAM,OAAO,QAAQ;GACnC,MAAM,SAAS,MAAM,UAAU,UAAU;GACzC,OAAO,EACL,MAAM,SAAS;IACb,MAAM,MAAM,OAAO;IACnB,SAAS;GACX,EACF;EACF;EACA,UAAU;GACR,IAAI,CAAC,aAAa;IAChB,cAAc,QAAQ,QAAQ,CAAC,CAAC,KAAK,YAAY;KAC/C,IAAI;MACF,MAAM,OAAO,QAAQ;KACvB,UAAU;MACR,MAAM,UAAU,UAAU;KAC5B;IACF,CAAC;IACD,UAAU,WAAW,WAAW;GAClC;GACA,OAAO;EACT;CACF;AACF;;AAGA,MAAa,4BAA4B,YAAqC;CAC5E,MAAM,UACJ,OAAO,aAAa,eAAe,UAAU,YAAY,MAAM,SAAS,UAAU,kBAAkB,UAAU,IAC1G,MAAM,QAAQ,OAAO,kBAAkB,CAAC,IACxC,EAAE,QAAQ,EAAE;CAClB,qBAAqB,SAAS,QAAQ;CACtC,OAAO;AACT;;AAGA,MAAM,2BAA2B,MAAsC,QACpE,SAAS,cAAe,MAAM,SAAS;AAE1C,MAAM,cAAc,YAA8B,QAAQ,QAAQ,OAAO,CAAC,EAAE,aAAa,KAAK,MAAM;AAEpG,MAAM,iBAAiB,KAAe,SAAiC,gBAA+B,SAAS;CAC7G,MAAM,cAAc,gCAAgC,KAAK,eAAe,QAAQ,cAAc,YAAY,SAAS,QAAQ,CAAC;CAC5H,IAAI,QAAQ,WAAW,QAAQ,OAAO,wBAAwB,KAAK,OAAO;CAC1E,IAAI,QAAQ,WAAW,QAAQ,OAAO,QAAQ,MAAM,GAAG,wBAAwB,KAAK,OAAO,CAAC;CAC5F,OAAO,MAAM;AACf;;AAGA,MAAa,kBAAkB,UAAkC,CAAC,GAAG,mBAAoE;CACvI,IAAI,OAAO,aAAa,aAAa,OAAO,QAAQ,QAAQ,IAAI,qBAAqB,CAAC;CACtF,MAAM,aAAa,CAAC,GAAI,QAAQ,cAAc,CAAC,CAAE;CACjD,MAAM,cAAmE,QAAQ,cAAc,CAAC;CAChG,IAAI;EACF,mBAAmB,YAAY,QAAQ;CACzC,SAAS,OAAO;EACd,OAAO,QAAQ,OAAO,KAAK;CAC7B;CACA,MAAM,gBAAgB,KAAK,UAAU;EACnC,QAAQ,UAAU;EAClB,QAAQ,UAAU;EAClB,QAAQ,OAAO,SAAS;EACxB,QAAQ,cAAc,YAAY;EAClC,QAAQ,cAAc,WAAW;EACjC,QAAQ,MAAM;EACd,eAAe,SAAS,KAAK;EAC7B,eAAe,SAAS,IAAI;EAC5B,QAAQ,OAAO,cAAc,oBAAoB;EAEjD,QAAQ,OAAO,cAAc,qBAAqB;EAClD,GAAI;GAAC;GAAQ;GAAW;EAAK,CAAC,CAAW,SAAS,SAAS,CAAC,eAAe,SAAS,MAAM,KAAK,GAAG,eAAe,SAAS,MAAM,IAAI,CAAC,CAAC;EACtI,QAAQ,OAAO,cAAc,YAAY;CAC3C,CAAC;CACD,IAAI,UAAU,cAAc;EAC1B,MAAM,WAAW;EACjB,eACE,UACA,SAAS,MAAM,WAAW,OAAO,QAAQ,CAAC,CAC5C;CACF;CACA,IACE,WACC,wBAAwB,iBACvB,eAAe,QAAQ,QACvB,mBAAmB,QAAQ,MAAM,QACjC,WAAW,WAAW,iBAAiB,UACvC,WAAW,MAAM,WAAW,UAAU,cAAc,iBAAiB,MAAM,IAE7E,OAAO,QAAQ,uBACb,IAAI,MAAM,+GAA+G,CAC3H;CACF,IAAI,QAAQ,OAAO;CACnB,sBAAsB;CACtB,aAAa,QAAQ;CACrB,iBAAiB,QAAQ,MAAM;CAC/B,mBAAmB;CACnB,MAAM,eAAe;CACrB,IAAI;CACJ,MAAM,QAAQ,gBAA+B,eAAe,OAAO,WAAW;CAC9E,IAAI,gBAAgB,YAAY,CAAC;CACjC,IAAI,UAAU;CACd,MAAM,iBAAiB,eAAe,UAAU;CAChD,MAAM,kBAAkB,UAAkC;EACxD,IAAI,UAAU,WAAW;GAEvB,IAAI,WAAW,OAAO,eAAe;GACrC,MACG,KAAK,YAAY;IAChB,IAAI,CAAC,SAAS,SAAS,aAAa,iBAAiB,YAAY,UAAU,EAAE,MAAM,KAAK,CAAC;IACzF,MAAM,cAAc;GACtB,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;GACjB;EACF;EACA,MAAM,cAAc,MAAM,MAAM,WAAW,OAAO,QAAQ,CAAC;EAC3D,KAAK,WAAW;EAChB,YAAiB,YAAY,CAAC,CAAC;CACjC;CACA,OAAQ,QAAQ,UACb,YAAY;EAGX,IAAI,cAAc,MAAM,aAAa,YAAY,CAAC,CAAC;EACnD,MAAM,WAAW,UAAU,YAAY;EACvC,MAAM,WAAW,kBAAkB,QAAQ,KAAK;EAChD,MAAM,aAAa,QAAQ,WAAW,eAAe,CAAC,IAAI,0BAA0B,QAAQ;EAC5F,MAAM,UACJ,aAAa,aAAa,QAAQ,WAAW,UAAU,QAAQ,OAAO,SAAS,QAC3E,kCAAkC,gBAAgB,QAAQ,cAAc,YAAY,OAAO,QAAQ,MAAM,OAAO,IAChH,KAAA;EACN,MAAM,YAAY,WAAmB;GACnC,SAAS,iBAAiB,QAAQ;GAClC,OAAO,OAAO,cAAc,UAAU,OAAO,GAAG;IAC9C;IACA,eAAe,SAAS,QAAQ;IAChC,eAAe;KACb,UAAU;KACV,SAAS,aAAa,oBAAoB,YAAY,QAAQ;KAC9D,SAAS,KAAK;KACd,WAAW;IACb;IACA,UAAU;GACZ,CAAC;EACH;EACA,IAAI,aAAa,SAAS,aAAa,WAAW,OAAO,SAAS,mCAAmC;EACrG,IAAI,CAAC,UAAU,kBAAkB,UAAU,GAAG,OAAO,SAAS,oCAAoC;EAClG,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,sBACG,kBAAA,iBAAmB,YAAY;GAC9B,IAAI;IACF,MAAM,UAAU,QAAQ;GAC1B,UAAU;IACR,IAAI,iBAAiB,MAAM,QAAQ,gBAAgB,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;GAC7E;EACF,EAAA,CAAG;EACL,MAAM,UAAU,YAAY;GAC1B,UAAU;GACV,IAAI;IACF,MAAM,cAAc;GACtB,UAAU;IACR,SAAS,aAAa,oBAAoB,YAAY,QAAQ;IAC9D,SAAS,KAAK;IACd,WAAW;GACb;EACF;EACA,IAAI;GACF,MAAM,mBAAoB;IAAC;IAAU;IAAc;IAAQ;IAAW;GAAK,CAAC,CAAW,MAAM,SAC3F,eAAe,SAAS,MAAM,KAAK,CACrC;GACA,MAAM,eAAe,MAAM,QAAQ,OAAO,UAAU,EAAE,0BAA0B,iBAAiB,CAAC,CAAC;GACnG,IAAI,CAAC,aAAa,WAAW,OAAO,SAAS,yCAAyC;GACtF,MAAM,wCAAwB,IAAI,IAAoB;GACtD,KAAK,MAAM,aAAa,YACtB,IAAI,aAAa,aAAa,UAAU,cAAc,iBACpD,sBAAsB,IAAI,UAAU,MAAM,yEAAyE;QAChH,IAAI,CAAC,aAAa,YAAY,SAAS,UAAU,IAAI,GACxD,sBAAsB,IACpB,UAAU,MACV,uGACF;GAEJ,IAAI,aAAa,WAAW;IAC1B,UAAU,MAAM,cACd,UACA,QACA;KACE,GAAG;KACH,QAAQ;KACR;KACA;IACF,SACM,OACN,cACF;IACA,UAAU,QAAQ,SAAS,cAAc,UAAU,OAAO,CAAC;IAC3D,kBAAkB,MAAM,gBACtB,OAAO,YAAY,8BAA8B;KAC/C,SAAS,aAAa,cAAc,IAAI,MAAM,iBAAiB,CAAC;IAClE,CAAC,CACH;IACA,OAAO,OAAO,SAAS;KACrB,UAAU;KACV,SAAS;IACX,CAAC;GACH;GACA,IAAI,aAA8B;GAClC,MAAM,kBAAkB;IACtB,IAAI,QAAQ,WAAW,QAAQ,OAAO;IACtC,MAAM,OAAO,QAAQ,QAAQ,SAAS;IACtC,OAAO,KAAK,QAAqB,SAAS,KAAK,KAAK,cAA2B,SAAS;GAC1F;GACA,MAAM,yBAAyB;IAC7B,MAAM,YAAY,QAAQ,cAAc,WAAW;IACnD,MAAM,OAAO,UAAU,KAAK,QAAQ,QAAQ,SAAS;IACrD,YAAY,OACV,eAAe,CAAC,aAAa,cAAc,cAAc,wBAAwB,YAAY,WAAW,IAAI,CAAC,IAAI,KAAA;GACrH;GAEA,MAAM,2BAA2B;IAC/B,iBAAiB;IACjB,IAAI,QAAQ,cAAc,YAAY,OAAO,OAAO;IACpD,MAAM,MAAM,UAAU;IACtB,IAAI,CAAC,KAAK,UAAU,SAAS,yBAAyB,GAAG,OAAO,CAAC,CAAC,YAAY;IAE9E,QADqB,IAAI,UAAU,SAAS,8BAA8B,IAAI,SAAS,aAC/D,YAAY;GACtC;GACA,MAAM,gBAAgB,YAA4B;IAChD,IAAI,kBAAkB,qBAAqB,SAAS,QAAQ;IAC5D,aAAa,QAAQ,mBAAmB;IACxC,iBAAiB;IACjB,SAAS,aAAa,cAAc,IAAI,MAAM,iBAAiB,CAAC;GAClE;GACA,kBAAkB,MAAM,gBAAgB,OAAO,YAAY,wBAAwB,YAAY,CAAC;GAChG,MAAM,QAAQ,QAAQ,QAAQ,CAAC,CAAC,WAAW,OAAO,kBAAkB,CAAC,CAAC,CAAC,CACpE,KAAK,YAAY,CAAC,CAClB,YAAY,KAAA,CAAS;GACxB,MAAM,SAAS,MAAM,cACnB,UACA,QACA;IAAE,GAAG;IAAS;IAAY;GAAsB,GAChD,oBACA,cACF;GACA,UAAU,QACR,QACA,cAAc,UAAU,eAAe,CAAC,mBAAmB,KAAK,OAAO,UAAU,CAAC,CAAC,cAAc,CAAC,CACpG;GACA,IAAI,QAAQ,OAAO,cAAc,YAAY,QAAQ,gBACnD,WAAW,MAAM,eAAe,UAAU,QAAQ,SAAS,OAAO,QAAQ,kBAAkB;GAC9F,OAAO,OAAO,SAAS;IACrB,SAAS,YAAY;KACnB,MAAM,iBAAiB,SAAS,QAAQ;KACxC,MAAM,iBAAiB,MAAM,UAAU,QAAQ;KAC/C,aAAa;MACX,iBAAiB;MACjB,iBAAiB;KACnB;IACF;IACA,UAAU;IACV,SAAS;GACX,CAAC;EACH,SAAS,OAAO;GACd,MAAM,UAAU,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC;GACxC,MAAM,SAAS,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC;GACvC,IAAI,iBAAiB,MAAM,QAAQ,gBAAgB,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;GAC3E,OAAO,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EACxE;CACF,EAAA,CAAG;AACP"}
@@ -8,31 +8,34 @@ export interface NativeUIStatus {
8
8
  updates: number;
9
9
  reason?: string;
10
10
  }
11
- export interface ToolbarProjectionOptions {
12
- buttons?: boolean;
13
- button?: boolean;
14
- backButton?: boolean;
11
+ export type ControlProjection = boolean | 'horizontalOnly' | 'verticalOnly';
12
+ export interface NativeUIControls {
13
+ buttonGroup?: ControlProjection;
14
+ button?: ControlProjection;
15
+ backButton?: ControlProjection;
16
+ tabs?: ControlProjection;
17
+ segment?: ControlProjection;
18
+ fab?: ControlProjection;
15
19
  }
16
- export interface VerticalControlAreaOptions {
17
- extensions?: readonly NativeUIExtension[];
18
- controls?: Pick<NativeUIControls, 'popover'>;
19
- horizontal?: boolean | ToolbarProjectionOptions;
20
- vertical?: boolean | ToolbarProjectionOptions;
21
- buttonProjection?: 'source' | 'system';
22
- buttonDefaultFill?: 'solid' | null;
20
+ export interface IonicProjectionOptions {
21
+ auto?: boolean;
22
+ verticalBars?: {
23
+ buttonProjection?: 'system' | 'source';
24
+ buttonDefaultFill?: 'glass' | 'solid';
25
+ popover?: boolean;
26
+ };
23
27
  }
24
- export interface NativeUIOptions extends VerticalControlAreaOptions {
25
- debug?: boolean;
28
+ export interface NativeUIOptions {
29
+ root?: HTMLElement;
30
+ controls?: ControlProjection | NativeUIControls;
26
31
  verticalBars?: NativeUIVerticalBarsOptions;
32
+ extensions?: readonly NativeUIExtension[];
27
33
  ionic?: IonicProjectionOptions;
28
- root?: HTMLElement;
29
- enabled?: boolean;
30
- controls?: NativeUIControls;
31
- verticalBarsOnly?: boolean;
34
+ debug?: boolean;
32
35
  }
36
+ export type VerticalControlAreaOptions = NativeUIOptions;
33
37
  export interface NativeUIRuntimeOptions extends NativeUIOptions {
34
38
  source?: 'ionic' | 'html' | 'auto' | 'extensions';
35
- explicitControls?: boolean;
36
39
  nativeState?: {
37
40
  edge?: 'left' | 'right';
38
41
  };
@@ -42,33 +45,12 @@ export interface NativeUIRuntimeOptions extends NativeUIOptions {
42
45
  edge: 'left' | 'right';
43
46
  };
44
47
  }
45
- export interface IonicProjectionOptions {
46
- auto?: boolean | AutoIonicControls;
47
- }
48
- export interface AutoIonicControls {
49
- buttons?: boolean;
50
- tabs?: boolean;
51
- segment?: boolean;
52
- fab?: boolean;
53
- }
54
- export interface NativeUIControls {
55
- buttons?: boolean;
56
- tabs?: boolean;
57
- toolbar?: boolean;
58
- segment?: boolean;
59
- fab?: boolean;
60
- popover?: boolean;
61
- }
62
48
  export interface NativeUIHandle extends NativeUIRuntimeHandle {
63
49
  enableVerticalControlArea(): Promise<void>;
64
50
  disableVerticalControlArea(): Promise<void>;
65
51
  }
66
52
  export interface NativeUIVerticalBarsOptions {
67
53
  enabled?: boolean;
68
- only?: boolean;
69
- buttonProjection?: 'system' | 'source';
70
- buttonDefaultFill?: 'glass' | 'solid';
71
- popover?: boolean;
72
54
  placement?: {
73
55
  edge: Exclude<VerticalBarEdge, null>;
74
56
  webHost?: HTMLElement;
@@ -192,7 +174,7 @@ export interface WebViewMetrics {
192
174
  }
193
175
  export interface NativeUIPlugin {
194
176
  configure(options?: {
195
- verticalBarsOnly?: boolean;
177
+ suppressScrollEdgeEffect?: boolean;
196
178
  }): Promise<{
197
179
  supported: boolean;
198
180
  extensions?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.d.ts","names":[],"sources":["../../src/native/definitions.ts"],"mappings":";;;YAGY;iBASK;EACf;EACA;EACA;EACA;;iBAGe;EAEf;EAEA;EAEA;;iBAGe;EAIf,sBAAsB;EAEtB,WAAW,KAAK;EAKhB,uBAAuB;EAKvB,qBAAqB;EASrB;EAOA;;iBAGe,wBAAwB;EAEvC;EACA,eAAe;EAEf,QAAQ;EAER,OAAO;EAEP;EAEA,WAAW;EAEX;;iBAIe,+BAA+B;EAC9C;EAEA;EACA;IAAgB;;EAEhB,wBAAwB;EACxB;IAAS,MAAM;IAAa;;;iBAGb;EAEf,iBAAiB;;iBAGF;EACf;EACA;EACA;EACA;;iBAGe;EAEf;EAEA;EAEA;EAEA;EAEA;EAEA;;iBAIe,uBAAuB;EAEtC,6BAA6B;EAE7B,8BAA8B;;iBAGf;EAEf;EAEA;EACA;EAEA;EAEA;EAEA;IAAc,MAAM,QAAQ;IAAwB,UAAU;;;iBAG/C;EAEf;EAEA,aAAa;EAEb,WAAW,QAAQ;EAEnB,WAAW;;YAID;iBAEK;EACf,MAAM;EAEN;EAIA,aAAa;;iBAGE,kCAAkC;EAIjD,aAAa,WAAW,kBAAkB,sBAAsB;;iBAGjD;EAEf,UAAU;;iBAGK;EACf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;;iBAGe,qBAAqB;EACpC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;YAIU;iBASK,wBAAwB;EACvC;EACA,MAAM;EACN;IAAc;IAAc;IAAe,SAAS;IAAwB;;EAE5E;EAEA;EACA,OAAO;EACP;EACA;EACA;IAAiB;IAAgB;;EACjC,SAAS;;iBAGM;EACf;EACA,OAAO;EACP,SAAS;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe,4BAA4B;EAC3C;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EAEA;EAEA,mBAAmB;EACnB,UAAU;;iBAGK;EACf;EACA;EACA;EAEA;;iBAGe;EAEf;EAEA,kBAAkB;;iBAGH;EACf,UAAU;IAAY;MAA+B;IAAU;IAAoB;;EACnF,wBAAwB;IAAW;IAAmB;MAAsB;EAC5E,qBAAqB,QAAQ;EAC7B,wBAAwB;IAAW;IAAuB;MAAkB;IAAU;IAAmB,OAAO,eAAe;;EAC/H,eAAe;IAAW;MAAe;EACzC,eAAe;IAAW;IAAY,eAAe;MAAgC;EACrF,eAAe;IAAW;IAAY;MAAsB;EAC5D,aAAa;IAAW;MAAe;EACvC,gBAAgB;EAChB,OAAO,UAAU,mBAAmB;IAAU;IAAkB;IAA6B;;EAC7F,MAAM;IAAW;MAAqB;EACtC,YAAY,kBAAkB,WAAW,OAAO,8BAA8B,QAAQ;EACtF,YAAY,gBAAgB,WAAW,OAAO,+BAA+B,QAAQ;EACrF,YAAY,8BAA8B,WAAW,OAAO,0BAA0B,QAAQ;EAC9F,YAAY,iBAAiB,WAAW,OAAO,gCAAgC,QAAQ;;iBAGxE;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA"}
1
+ {"version":3,"file":"definitions.d.ts","names":[],"sources":["../../src/native/definitions.ts"],"mappings":";;;YAGY;iBASK;EACf;EACA;EACA;EACA;;YAMU;iBAEK;EAEf,cAAc;EAEd,SAAS;EACT,aAAa;EACb,OAAO;EACP,UAAU;EACV,MAAM;;iBAGS;EAEf;EACA;IAEE;IAEA;IAEA;;;iBAIa;EAEf,OAAO;EAEP,WAAW,oBAAoB;EAC/B,eAAe;EAIf,sBAAsB;EACtB,QAAQ;EAER;;YAIU,6BAA6B;iBAGxB,+BAA+B;EAC9C;EACA;IAAgB;;EAChB,wBAAwB;EACxB;IAAS,MAAM;IAAa;;;iBAIb,uBAAuB;EAEtC,6BAA6B;EAE7B,8BAA8B;;iBAGf;EAEf;EAEA;IAAc,MAAM,QAAQ;IAAwB,UAAU;;;iBAG/C;EAEf;EAEA,aAAa;EAEb,WAAW,QAAQ;EAEnB,WAAW;;YAID;iBAEK;EACf,MAAM;EAEN;EAIA,aAAa;;iBAGE,kCAAkC;EAIjD,aAAa,WAAW,kBAAkB,sBAAsB;;iBAGjD;EAEf,UAAU;;iBAGK;EACf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;;iBAGe,qBAAqB;EACpC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;YAIU;iBASK,wBAAwB;EACvC;EACA,MAAM;EACN;IAAc;IAAc;IAAe,SAAS;IAAwB;;EAE5E;EAEA;EACA,OAAO;EACP;EACA;EACA;IAAiB;IAAgB;;EACjC,SAAS;;iBAGM;EACf;EACA,OAAO;EACP,SAAS;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe,4BAA4B;EAC3C;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EAEA;EAEA,mBAAmB;EACnB,UAAU;;iBAGK;EACf;EACA;EACA;EAEA;;iBAGe;EAEf;EAEA,kBAAkB;;iBAGH;EACf,UAAU;IAAY;MAAuC;IAAU;IAAoB;;EAC3F,wBAAwB;IAAW;IAAmB;MAAsB;EAC5E,qBAAqB,QAAQ;EAC7B,wBAAwB;IAAW;IAAuB;MAAkB;IAAU;IAAmB,OAAO,eAAe;;EAC/H,eAAe;IAAW;MAAe;EACzC,eAAe;IAAW;IAAY,eAAe;MAAgC;EACrF,eAAe;IAAW;IAAY;MAAsB;EAC5D,aAAa;IAAW;MAAe;EACvC,gBAAgB;EAChB,OAAO,UAAU,mBAAmB;IAAU;IAAkB;IAA6B;;EAC7F,MAAM;IAAW;MAAqB;EACtC,YAAY,kBAAkB,WAAW,OAAO,8BAA8B,QAAQ;EACtF,YAAY,gBAAgB,WAAW,OAAO,+BAA+B,QAAQ;EACrF,YAAY,8BAA8B,WAAW,OAAO,0BAA0B,QAAQ;EAC9F,YAAY,iBAAiB,WAAW,OAAO,gCAAgC,QAAQ;;iBAGxE;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA"}
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","names":[],"sources":["../../src/native/html.ts"],"mappings":";;;qBAMa;qBACA,iBAAkB,SAAS;qBAG3B,kBAAmB,SAAS,aAAa,IAAI,UAAU,SAAS,2BAAyB;qBAoCzF,0BAA2B,KAAK,UAAU,SAAS,2BAAyB"}
1
+ {"version":3,"file":"html.d.ts","names":[],"sources":["../../src/native/html.ts"],"mappings":";;;qBAMa;qBACA,iBAAkB,SAAS;qBAG3B,kBAAmB,SAAS,aAAa,IAAI,UAAU,SAAS,2BAAyB;qBA+BzF,0BAA2B,KAAK,UAAU,SAAS,2BAAyB"}
@@ -10,7 +10,6 @@ const readHTMLControl = (element, id, options) => {
10
10
  if (!element.matches("button, ion-button, [role=\"button\"]") || !visible(element, htmlRailSource(element))) return;
11
11
  if (element.contains(element.ownerDocument.activeElement)) return;
12
12
  if (!htmlRailSource(element) && !fitsClip(element)) return;
13
- if (options.explicitControls === false || options.explicitControls === void 0 && (options.controls?.toolbar === false || options.controls?.buttons === false)) return;
14
13
  if (htmlRailSource(element) && options.verticalBars?.enabled === false) return;
15
14
  const kind = element.getAttribute("data-native-ui") === "back-button" ? "back-button" : "button";
16
15
  const candidate = createCandidate(element, kind, id, options);
@@ -142,6 +141,16 @@ const createHTMLWebProjection = (doc, options) => {
142
141
  }
143
142
  }
144
143
  for (const source of owned.keys()) if (!wanted.has(source)) restore(source);
144
+ const clones = Array.from(wanted, (source) => owned.get(source).clone);
145
+ const order = Array.from(host.children).filter((child) => clones.includes(child));
146
+ const focused = clones.find((clone) => clone === doc.activeElement);
147
+ clones.forEach((clone, index) => {
148
+ if (order[index] === clone) return;
149
+ host.insertBefore(clone, order[index]);
150
+ order.splice(order.indexOf(clone), 1);
151
+ order.splice(index, 0, clone);
152
+ });
153
+ if (focused && doc.activeElement !== focused) focused.focus({ preventScroll: true });
145
154
  observer.takeRecords();
146
155
  };
147
156
  const schedule = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"html.js","names":[],"sources":["../../src/native/html.ts"],"sourcesContent":["import { fitsClip } from './shared/clipping';\nimport type { NativeUIRuntimeOptions, NativeUIRuntimeHandle } from './definitions';\nimport { appendItem, createCandidate, type Candidate, type Identify } from './shared/candidate';\nimport { marker, unprojected, visible } from './shared/dom';\n\n/** Standard HTML sources use standard native renderers, not app-local Swift extensions. */\nexport const htmlSelector = '[data-native-ui=\"button\"], [data-native-ui=\"back-button\"]';\nexport const htmlRailSource = (element: Element) =>\n element.matches(htmlSelector) && element.getAttribute('data-native-ui-placement') === 'rail';\n\nexport const readHTMLControl = (element: HTMLElement, id: Identify, options: NativeUIRuntimeOptions): Candidate | undefined => {\n if (\n !(options.root ?? element.ownerDocument.body).contains(element) ||\n !element.matches(htmlSelector) ||\n element.closest('[data-native-ui-proxy]')\n )\n return;\n // Explicit opt-in; custom elements can implement click() and expose role=button.\n if (!element.matches('button, ion-button, [role=\"button\"]') || !visible(element, htmlRailSource(element))) return;\n if (element.contains(element.ownerDocument.activeElement)) return;\n if (!htmlRailSource(element) && !fitsClip(element)) return;\n if (\n options.explicitControls === false ||\n (options.explicitControls === undefined && (options.controls?.toolbar === false || options.controls?.buttons === false))\n )\n return;\n if (htmlRailSource(element) && options.verticalBars?.enabled === false) return;\n const kind = element.getAttribute('data-native-ui') === 'back-button' ? 'back-button' : 'button';\n const candidate = createCandidate(element, kind, id, options);\n const item = appendItem(candidate, element, id);\n if (!item) return;\n const labelledBy = element\n .getAttribute('aria-labelledby')\n ?.trim()\n .split(/\\s+/)\n .map((key) => element.ownerDocument.getElementById(key)?.textContent?.trim() ?? '')\n .filter(Boolean)\n .join(' ');\n item.accessibilityLabel = labelledBy || element.getAttribute('aria-label') || item.label;\n if (!item.accessibilityLabel) return;\n item.disabled ||= element.matches(':disabled, [disabled]') || !!element.closest('[aria-disabled=\"true\"]');\n if (htmlRailSource(element)) candidate.control.placement = 'vertical-bars';\n return candidate;\n};\n\n/** A rail host is supplied and styled by the application, never by the plugin. */\nexport const createHTMLWebProjection = (doc: Document, options: NativeUIRuntimeOptions): NativeUIRuntimeHandle => {\n const host = options.rail?.host;\n if (!host) {\n let stopped = false;\n return {\n markForCheck: () => {},\n getStatus: () => ({ state: stopped ? 'stopped' : 'web', projected: 0, updates: 0 }),\n suspend: async () => ({ resume: async () => {} }),\n destroy: async () => {\n stopped = true;\n },\n };\n }\n const win = doc.defaultView!;\n const owned = new Map<HTMLElement, { clone: HTMLButtonElement; aria: string | null; signature: string }>();\n const style = doc.createElement('style');\n style.textContent = `[data-native-ui-web-source] { visibility: hidden !important; pointer-events: none !important; }`;\n doc.head.append(style);\n let frame = 0;\n let stopped = false;\n let suspended = 0;\n let updates = 0;\n const restore = (source: HTMLElement) => {\n const state = owned.get(source);\n if (!state) return;\n const focused = state.clone.contains(doc.activeElement);\n state.clone.remove();\n source.removeAttribute('data-native-ui-web-source');\n if (focused && source.isConnected && !source.hasAttribute(marker)) source.focus();\n owned.delete(source);\n };\n const measure = <T>(source: HTMLElement, read: () => T): T => {\n const hidden = source.hasAttribute('data-native-ui-web-source');\n if (hidden) source.removeAttribute('data-native-ui-web-source');\n try {\n return unprojected([source], read);\n } finally {\n if (hidden) source.setAttribute('data-native-ui-web-source', '');\n }\n };\n const render = () => {\n frame = 0;\n if (stopped) return;\n observer.disconnect();\n observer.observe(doc.documentElement, { subtree: true, childList: true, attributes: true, characterData: true });\n const wanted = new Set<HTMLElement>();\n if (!suspended && options.verticalBars?.enabled !== false && host?.isConnected && options.root?.isConnected) {\n const sources = Array.from(options.root.querySelectorAll<HTMLElement>(htmlSelector));\n let back = false;\n for (const source of sources) {\n // An accepted native surface owns its source and descendants. Rejected sources\n // lose the marker and can return to the Web rail on the next render.\n if (!htmlRailSource(source) || host.contains(source) || source.closest(`[${marker}]`)) continue;\n const iconRoot = source.querySelector('ion-icon')?.shadowRoot;\n if (iconRoot) observer.observe(iconRoot, { subtree: true, childList: true, attributes: true, characterData: true });\n const candidate = measure(source, () => readHTMLControl(source, () => 'web', options));\n if (!candidate) continue;\n if (candidate.control.kind === 'back-button') {\n if (back) continue;\n back = true;\n }\n wanted.add(source);\n const item = candidate.control.items[0];\n const svg = source.querySelector('svg') ?? iconRoot?.querySelector('svg');\n const signature = JSON.stringify([item, source.innerHTML, svg?.outerHTML]);\n let state = owned.get(source);\n if (!state) {\n const clone = doc.createElement('button');\n clone.type = 'button';\n clone.setAttribute('data-native-ui-proxy', candidate.control.kind);\n state = { clone, aria: source.getAttribute('aria-hidden'), signature: '' };\n owned.set(source, state);\n clone.addEventListener('click', () => {\n const current = !stopped && !suspended && measure(source, () => readHTMLControl(source, () => 'web', options));\n if (\n options.verticalBars?.enabled === false ||\n !current ||\n !htmlRailSource(source) ||\n !host.isConnected ||\n current.control.items[0].disabled ||\n source.closest(`[${marker}]`)\n )\n return;\n source.click(); // Source owns submission, custom handlers and back navigation.\n schedule();\n });\n host.append(clone);\n }\n if (state.signature !== signature) {\n state.signature = signature;\n state.clone.replaceChildren();\n if (svg) {\n const icon = svg.cloneNode(true) as SVGElement;\n icon.querySelectorAll('[id]').forEach((node) => node.removeAttribute('id'));\n icon.removeAttribute('id');\n for (const node of [icon, ...Array.from(icon.querySelectorAll('*'))])\n for (const attr of Array.from(node.attributes)) if (attr.name.startsWith('on')) node.removeAttribute(attr.name);\n state.clone.append(icon);\n }\n state.clone.append(doc.createTextNode(item.label));\n state.clone.setAttribute('aria-label', item.accessibilityLabel);\n state.clone.disabled = item.disabled;\n updates++;\n }\n if (!source.hasAttribute('data-native-ui-web-source')) source.setAttribute('data-native-ui-web-source', '');\n }\n }\n for (const source of owned.keys()) if (!wanted.has(source)) restore(source);\n // Ignore our own synchronous DOM writes; later application mutations still schedule a refresh.\n observer.takeRecords();\n };\n const schedule = () => {\n if (!stopped && !frame) frame = win.requestAnimationFrame(render);\n };\n const observer = new MutationObserver(schedule);\n win.addEventListener('resize', schedule);\n win.addEventListener('nativeUIRefresh', schedule);\n render();\n return {\n markForCheck: schedule,\n getStatus: () => ({ state: stopped ? 'stopped' : 'web', projected: owned.size, updates }),\n async suspend() {\n suspended++;\n for (const source of owned.keys()) restore(source);\n let resumed = false;\n return {\n async resume() {\n if (!resumed) {\n resumed = true;\n suspended--;\n render();\n }\n },\n };\n },\n async destroy() {\n if (stopped) return;\n stopped = true;\n observer.disconnect();\n win.cancelAnimationFrame(frame);\n win.removeEventListener('resize', schedule);\n win.removeEventListener('nativeUIRefresh', schedule);\n for (const source of owned.keys()) restore(source);\n style.remove();\n },\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"html.js","names":[],"sources":["../../src/native/html.ts"],"sourcesContent":["import { fitsClip } from './shared/clipping';\nimport type { NativeUIRuntimeOptions, NativeUIRuntimeHandle } from './definitions';\nimport { appendItem, createCandidate, type Candidate, type Identify } from './shared/candidate';\nimport { marker, unprojected, visible } from './shared/dom';\n\n/** Standard HTML sources use standard native renderers, not app-local Swift extensions. */\nexport const htmlSelector = '[data-native-ui=\"button\"], [data-native-ui=\"back-button\"]';\nexport const htmlRailSource = (element: Element) =>\n element.matches(htmlSelector) && element.getAttribute('data-native-ui-placement') === 'rail';\n\nexport const readHTMLControl = (element: HTMLElement, id: Identify, options: NativeUIRuntimeOptions): Candidate | undefined => {\n if (\n !(options.root ?? element.ownerDocument.body).contains(element) ||\n !element.matches(htmlSelector) ||\n element.closest('[data-native-ui-proxy]')\n )\n return;\n // Explicit opt-in; custom elements can implement click() and expose role=button.\n if (!element.matches('button, ion-button, [role=\"button\"]') || !visible(element, htmlRailSource(element))) return;\n if (element.contains(element.ownerDocument.activeElement)) return;\n if (!htmlRailSource(element) && !fitsClip(element)) return;\n if (htmlRailSource(element) && options.verticalBars?.enabled === false) return;\n const kind = element.getAttribute('data-native-ui') === 'back-button' ? 'back-button' : 'button';\n const candidate = createCandidate(element, kind, id, options);\n const item = appendItem(candidate, element, id);\n if (!item) return;\n const labelledBy = element\n .getAttribute('aria-labelledby')\n ?.trim()\n .split(/\\s+/)\n .map((key) => element.ownerDocument.getElementById(key)?.textContent?.trim() ?? '')\n .filter(Boolean)\n .join(' ');\n item.accessibilityLabel = labelledBy || element.getAttribute('aria-label') || item.label;\n if (!item.accessibilityLabel) return;\n item.disabled ||= element.matches(':disabled, [disabled]') || !!element.closest('[aria-disabled=\"true\"]');\n if (htmlRailSource(element)) candidate.control.placement = 'vertical-bars';\n return candidate;\n};\n\n/** A rail host is supplied and styled by the application, never by the plugin. */\nexport const createHTMLWebProjection = (doc: Document, options: NativeUIRuntimeOptions): NativeUIRuntimeHandle => {\n const host = options.rail?.host;\n if (!host) {\n let stopped = false;\n return {\n markForCheck: () => {},\n getStatus: () => ({ state: stopped ? 'stopped' : 'web', projected: 0, updates: 0 }),\n suspend: async () => ({ resume: async () => {} }),\n destroy: async () => {\n stopped = true;\n },\n };\n }\n const win = doc.defaultView!;\n const owned = new Map<HTMLElement, { clone: HTMLButtonElement; aria: string | null; signature: string }>();\n const style = doc.createElement('style');\n style.textContent = `[data-native-ui-web-source] { visibility: hidden !important; pointer-events: none !important; }`;\n doc.head.append(style);\n let frame = 0;\n let stopped = false;\n let suspended = 0;\n let updates = 0;\n const restore = (source: HTMLElement) => {\n const state = owned.get(source);\n if (!state) return;\n const focused = state.clone.contains(doc.activeElement);\n state.clone.remove();\n source.removeAttribute('data-native-ui-web-source');\n if (focused && source.isConnected && !source.hasAttribute(marker)) source.focus();\n owned.delete(source);\n };\n const measure = <T>(source: HTMLElement, read: () => T): T => {\n const hidden = source.hasAttribute('data-native-ui-web-source');\n if (hidden) source.removeAttribute('data-native-ui-web-source');\n try {\n return unprojected([source], read);\n } finally {\n if (hidden) source.setAttribute('data-native-ui-web-source', '');\n }\n };\n const render = () => {\n frame = 0;\n if (stopped) return;\n observer.disconnect();\n observer.observe(doc.documentElement, { subtree: true, childList: true, attributes: true, characterData: true });\n const wanted = new Set<HTMLElement>();\n if (!suspended && options.verticalBars?.enabled !== false && host?.isConnected && options.root?.isConnected) {\n const sources = Array.from(options.root.querySelectorAll<HTMLElement>(htmlSelector));\n let back = false;\n for (const source of sources) {\n // An accepted native surface owns its source and descendants. Rejected sources\n // lose the marker and can return to the Web rail on the next render.\n if (!htmlRailSource(source) || host.contains(source) || source.closest(`[${marker}]`)) continue;\n const iconRoot = source.querySelector('ion-icon')?.shadowRoot;\n if (iconRoot) observer.observe(iconRoot, { subtree: true, childList: true, attributes: true, characterData: true });\n const candidate = measure(source, () => readHTMLControl(source, () => 'web', options));\n if (!candidate) continue;\n if (candidate.control.kind === 'back-button') {\n if (back) continue;\n back = true;\n }\n wanted.add(source);\n const item = candidate.control.items[0];\n const svg = source.querySelector('svg') ?? iconRoot?.querySelector('svg');\n const signature = JSON.stringify([item, source.innerHTML, svg?.outerHTML]);\n let state = owned.get(source);\n if (!state) {\n const clone = doc.createElement('button');\n clone.type = 'button';\n clone.setAttribute('data-native-ui-proxy', candidate.control.kind);\n state = { clone, aria: source.getAttribute('aria-hidden'), signature: '' };\n owned.set(source, state);\n clone.addEventListener('click', () => {\n const current = !stopped && !suspended && measure(source, () => readHTMLControl(source, () => 'web', options));\n if (\n options.verticalBars?.enabled === false ||\n !current ||\n !htmlRailSource(source) ||\n !host.isConnected ||\n current.control.items[0].disabled ||\n source.closest(`[${marker}]`)\n )\n return;\n source.click(); // Source owns submission, custom handlers and back navigation.\n schedule();\n });\n host.append(clone);\n }\n if (state.signature !== signature) {\n state.signature = signature;\n state.clone.replaceChildren();\n if (svg) {\n const icon = svg.cloneNode(true) as SVGElement;\n icon.querySelectorAll('[id]').forEach((node) => node.removeAttribute('id'));\n icon.removeAttribute('id');\n for (const node of [icon, ...Array.from(icon.querySelectorAll('*'))])\n for (const attr of Array.from(node.attributes)) if (attr.name.startsWith('on')) node.removeAttribute(attr.name);\n state.clone.append(icon);\n }\n state.clone.append(doc.createTextNode(item.label));\n state.clone.setAttribute('aria-label', item.accessibilityLabel);\n state.clone.disabled = item.disabled;\n updates++;\n }\n if (!source.hasAttribute('data-native-ui-web-source')) source.setAttribute('data-native-ui-web-source', '');\n }\n }\n for (const source of owned.keys()) if (!wanted.has(source)) restore(source);\n const clones = Array.from(wanted, (source) => owned.get(source)!.clone);\n const order = Array.from(host.children).filter((child): child is HTMLButtonElement => clones.includes(child as HTMLButtonElement));\n const focused = clones.find((clone) => clone === doc.activeElement);\n clones.forEach((clone, index) => {\n if (order[index] === clone) return;\n host.insertBefore(clone, order[index]);\n order.splice(order.indexOf(clone), 1);\n order.splice(index, 0, clone);\n });\n // Moving an existing button can blur it; retain keyboard focus across source reordering.\n if (focused && doc.activeElement !== focused) focused.focus({ preventScroll: true });\n // Ignore our own synchronous DOM writes; later application mutations still schedule a refresh.\n observer.takeRecords();\n };\n const schedule = () => {\n if (!stopped && !frame) frame = win.requestAnimationFrame(render);\n };\n const observer = new MutationObserver(schedule);\n win.addEventListener('resize', schedule);\n win.addEventListener('nativeUIRefresh', schedule);\n render();\n return {\n markForCheck: schedule,\n getStatus: () => ({ state: stopped ? 'stopped' : 'web', projected: owned.size, updates }),\n async suspend() {\n suspended++;\n for (const source of owned.keys()) restore(source);\n let resumed = false;\n return {\n async resume() {\n if (!resumed) {\n resumed = true;\n suspended--;\n render();\n }\n },\n };\n },\n async destroy() {\n if (stopped) return;\n stopped = true;\n observer.disconnect();\n win.cancelAnimationFrame(frame);\n win.removeEventListener('resize', schedule);\n win.removeEventListener('nativeUIRefresh', schedule);\n for (const source of owned.keys()) restore(source);\n style.remove();\n },\n };\n};\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { NativeUIExtension, NativeUIImageReference, NativeUIReadContext, NativeUIValue, defineNativeUI } from "./extensions.js";
2
- import { AutoIonicControls, IonicProjectionOptions, NativeUIComponent, NativeUIControls, NativeUIHandle, NativeUIOptions, NativeUIStatus, NativeUISuspension, NativeUIVerticalBarsOptions, ToolbarProjectionOptions, VerticalBarEdge, VerticalBarPlacement, VerticalControlAreaHandle, VerticalControlAreaOptions, WebViewMetrics } from "./definitions.js";
2
+ import { ControlProjection, IonicProjectionOptions, NativeUIComponent, NativeUIControls, NativeUIHandle, NativeUIOptions, NativeUIStatus, NativeUISuspension, NativeUIVerticalBarsOptions, VerticalBarEdge, VerticalBarPlacement, VerticalControlAreaHandle, VerticalControlAreaOptions, WebViewMetrics } from "./definitions.js";
3
3
  import { NativeUI, configureNativeTransition } from "./activation.js";
4
4
  import { setVerticalControlAreaPlacement } from "./legacy-layout.js";
5
5
  //#region src/native/index.d.ts
6
6
  export declare const enableNativeUI: (options?: NativeUIOptions) => Promise<NativeUIHandle>;
7
7
  export declare const enableVerticalControlArea: (options?: VerticalControlAreaOptions) => Promise<VerticalControlAreaHandle>;
8
8
  //#endregion
9
- export { type AutoIonicControls, type IonicProjectionOptions, NativeUI, type NativeUIComponent, type NativeUIControls, type NativeUIExtension, type NativeUIHandle, type NativeUIImageReference, type NativeUIOptions, type NativeUIReadContext, type NativeUIStatus, type NativeUISuspension, type NativeUIValue, type NativeUIVerticalBarsOptions, type ToolbarProjectionOptions, type VerticalBarEdge, type VerticalBarPlacement, type VerticalControlAreaHandle, type VerticalControlAreaOptions, type WebViewMetrics, configureNativeTransition, defineNativeUI, setVerticalControlAreaPlacement };
9
+ export { type ControlProjection, type IonicProjectionOptions, NativeUI, type NativeUIComponent, type NativeUIControls, type NativeUIExtension, type NativeUIHandle, type NativeUIImageReference, type NativeUIOptions, type NativeUIReadContext, type NativeUIStatus, type NativeUISuspension, type NativeUIValue, type NativeUIVerticalBarsOptions, type VerticalBarEdge, type VerticalBarPlacement, type VerticalControlAreaHandle, type VerticalControlAreaOptions, type WebViewMetrics, configureNativeTransition, defineNativeUI, setVerticalControlAreaPlacement };
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":["_$__definitions0.NativeUIHandle"],"sources":["../../src/native/index.ts"],"mappings":";;;;;qBA0Ba,iBAAkB,UAAS,oBAAoB,QAApBA;qBAG3B,4BAAmC,UAAS,+BAAkC,QAAQ"}
1
+ {"version":3,"file":"index.d.ts","names":["_$__definitions0.NativeUIHandle"],"sources":["../../src/native/index.ts"],"mappings":";;;;;qBAyBa,iBAAkB,UAAS,oBAAoB,QAApBA;qBAG3B,4BAAmC,UAAS,+BAAkC,QAAQ"}
@@ -7,17 +7,8 @@ const enableNativeUI = (options = {}) => startNativeUI(options, async (...args)
7
7
  const enableVerticalControlArea = async (options = {}) => {
8
8
  return {
9
9
  ...await enableNativeUI({
10
- extensions: options.extensions,
11
- horizontal: options.horizontal,
12
- vertical: options.vertical,
13
- buttonProjection: options.buttonProjection,
14
- buttonDefaultFill: options.buttonDefaultFill,
15
- controls: {
16
- tabs: true,
17
- toolbar: true,
18
- popover: options.controls?.popover === true
19
- },
20
- verticalBarsOnly: true
10
+ ...options,
11
+ controls: options.controls ?? "verticalOnly"
21
12
  }),
22
13
  setPlacement: setVerticalControlAreaPlacement
23
14
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["activate"],"sources":["../../src/native/index.ts"],"sourcesContent":["// Compatibility Ionic entrypoint. Standard HTML consumers can import /core without Ionic.\nimport { startNativeUI as activate } from './startup';\nimport type { NativeUIOptions, VerticalControlAreaOptions, VerticalControlAreaHandle } from './definitions';\nimport { setVerticalControlAreaPlacement } from './legacy-layout';\nexport { setVerticalControlAreaPlacement } from './legacy-layout';\nexport { configureNativeTransition, NativeUI } from './activation';\nexport { defineNativeUI } from './extensions';\nexport type { NativeUIExtension, NativeUIValue, NativeUIReadContext, NativeUIImageReference } from './extensions';\nexport type {\n NativeUIVerticalBarsOptions,\n NativeUIComponent,\n NativeUIControls,\n AutoIonicControls,\n IonicProjectionOptions,\n NativeUIHandle,\n NativeUIOptions,\n NativeUIStatus,\n NativeUISuspension,\n VerticalBarEdge,\n VerticalBarPlacement,\n VerticalControlAreaHandle,\n ToolbarProjectionOptions,\n VerticalControlAreaOptions,\n WebViewMetrics,\n} from './definitions';\n\nexport const enableNativeUI = (options: NativeUIOptions = {}) =>\n activate(options, async (...args) => (await import('./overlays/controller')).createOverlayController(...args));\n\nexport const enableVerticalControlArea = async (options: VerticalControlAreaOptions = {}): Promise<VerticalControlAreaHandle> => {\n const handle = await enableNativeUI({\n extensions: options.extensions,\n horizontal: options.horizontal,\n vertical: options.vertical,\n buttonProjection: options.buttonProjection,\n buttonDefaultFill: options.buttonDefaultFill,\n controls: { tabs: true, toolbar: true, popover: options.controls?.popover === true },\n verticalBarsOnly: true,\n });\n return { ...handle, setPlacement: setVerticalControlAreaPlacement };\n};\n"],"mappings":";;;;;AA0BA,MAAa,kBAAkB,UAA2B,CAAC,MACzDA,cAAS,SAAS,OAAO,GAAG,UAAU,MAAM,OAAO,4BAAA,CAA0B,wBAAwB,GAAG,IAAI,CAAC;AAE/G,MAAa,4BAA4B,OAAO,UAAsC,CAAC,MAA0C;CAU/H,OAAO;EAAE,GAAG,MATS,eAAe;GAClC,YAAY,QAAQ;GACpB,YAAY,QAAQ;GACpB,UAAU,QAAQ;GAClB,kBAAkB,QAAQ;GAC1B,mBAAmB,QAAQ;GAC3B,UAAU;IAAE,MAAM;IAAM,SAAS;IAAM,SAAS,QAAQ,UAAU,YAAY;GAAK;GACnF,kBAAkB;EACpB,CAAC;EACmB,cAAc;CAAgC;AACpE"}
1
+ {"version":3,"file":"index.js","names":["activate"],"sources":["../../src/native/index.ts"],"sourcesContent":["// Compatibility Ionic entrypoint. Standard HTML consumers can import /core without Ionic.\nimport { startNativeUI as activate } from './startup';\nimport type { NativeUIOptions, VerticalControlAreaOptions, VerticalControlAreaHandle } from './definitions';\nimport { setVerticalControlAreaPlacement } from './legacy-layout';\nexport { setVerticalControlAreaPlacement } from './legacy-layout';\nexport { configureNativeTransition, NativeUI } from './activation';\nexport { defineNativeUI } from './extensions';\nexport type { NativeUIExtension, NativeUIValue, NativeUIReadContext, NativeUIImageReference } from './extensions';\nexport type {\n NativeUIVerticalBarsOptions,\n NativeUIComponent,\n NativeUIControls,\n ControlProjection,\n IonicProjectionOptions,\n NativeUIHandle,\n NativeUIOptions,\n NativeUIStatus,\n NativeUISuspension,\n VerticalBarEdge,\n VerticalBarPlacement,\n VerticalControlAreaHandle,\n VerticalControlAreaOptions,\n WebViewMetrics,\n} from './definitions';\n\nexport const enableNativeUI = (options: NativeUIOptions = {}) =>\n activate(options, async (...args) => (await import('./overlays/controller')).createOverlayController(...args));\n\nexport const enableVerticalControlArea = async (options: VerticalControlAreaOptions = {}): Promise<VerticalControlAreaHandle> => {\n const handle = await enableNativeUI({ ...options, controls: options.controls ?? 'verticalOnly' });\n return { ...handle, setPlacement: setVerticalControlAreaPlacement };\n};\n"],"mappings":";;;;;AAyBA,MAAa,kBAAkB,UAA2B,CAAC,MACzDA,cAAS,SAAS,OAAO,GAAG,UAAU,MAAM,OAAO,4BAAA,CAA0B,wBAAwB,GAAG,IAAI,CAAC;AAE/G,MAAa,4BAA4B,OAAO,UAAsC,CAAC,MAA0C;CAE/H,OAAO;EAAE,GAAG,MADS,eAAe;GAAE,GAAG;GAAS,UAAU,QAAQ,YAAY;EAAe,CAAC;EAC5E,cAAc;CAAgC;AACpE"}