@webview-native-ui/engine 0.0.3 → 0.0.4

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.
@@ -1,5 +1,5 @@
1
1
  import { type ProjectionDestination } from '../projection/events.js';
2
- import type { ScrollPlacement, ListenerHandle, WebProjectionRequest } from '../types.js';
2
+ import type { Frame, ScrollPlacement, ListenerHandle, WebProjectionRequest } from '../types.js';
3
3
  /** Serialized by the native library. One registration owns one fixed fallback container. */
4
4
  export interface NativeSessionConfiguration {
5
5
  id: string;
@@ -12,6 +12,8 @@ export interface NativeSessionConfiguration {
12
12
  captureMode?: 'composited' | 'view';
13
13
  /** Native registration accepts a final placement in its reveal request. */
14
14
  placementHandoff?: boolean;
15
+ /** Android can cover prepared pixels before a Web scroll event arrives. */
16
+ nativeDragHandoff?: boolean;
15
17
  receiveListeners?: {
16
18
  update?: string[];
17
19
  projection?: Partial<Record<ProjectionDestination, string[]>>;
@@ -29,6 +31,13 @@ export interface NativeSessionBridge {
29
31
  action: 'track';
30
32
  viewportWidth: number;
31
33
  scroll?: ScrollPlacement;
34
+ gesture?: {
35
+ viewport: Frame;
36
+ offsetX: number;
37
+ offsetY: number;
38
+ rangeX: number;
39
+ rangeY: number;
40
+ };
32
41
  } | {
33
42
  action: 'connect';
34
43
  } | {
@@ -37,6 +46,7 @@ export interface NativeSessionBridge {
37
46
  }): Promise<any>;
38
47
  listen(listener: (event: {
39
48
  active: boolean;
49
+ precovered?: string;
40
50
  }) => void): Promise<ListenerHandle>;
41
51
  }
42
52
  /** Native startup calls this; application code does not initialize or wrap the plugin. */
@@ -21,6 +21,7 @@ export const startNativeSession = async (config, nativeBridge, plugin = {
21
21
  const overlays = getOverlayCoordinator(document);
22
22
  const effectiveDestination = () => requestedDestination === 'native' && target && overlays.isCovered(target) ? 'web' : requestedDestination;
23
23
  let automaticMotion = false;
24
+ let nativeDragging = false;
24
25
  let placementPending = false;
25
26
  let nativeScroll = false;
26
27
  let scrollOwner;
@@ -51,17 +52,26 @@ export const startNativeSession = async (config, nativeBridge, plugin = {
51
52
  }
52
53
  };
53
54
  const bridge = {
54
- listen: (listener) => nativeBridge.listen(listener),
55
+ listen: (listener) => nativeBridge.listen((event) => {
56
+ listener(event);
57
+ if (!event.active && nativeDragging) {
58
+ nativeDragging = false;
59
+ endAutomaticMotionLater();
60
+ }
61
+ if (event.precovered)
62
+ adoptNativeCover(event.precovered);
63
+ }),
55
64
  send: (request) => {
56
65
  if (!config.placementHandoff)
57
66
  return nativeBridge.send(request);
58
67
  const binding = projectionPlacement(target);
59
68
  if (request.action === 'cover') {
69
+ const alreadyCovered = coveredToken === request.token && binding?.owner === placementOwner && binding.projected;
60
70
  const epoch = ++placementEpoch;
61
71
  coveredToken = request.token;
62
72
  if (binding) {
63
73
  binding.owner = placementOwner;
64
- binding.projected = false;
74
+ binding.projected = !!alreadyCovered;
65
75
  }
66
76
  return nativeBridge
67
77
  .send(request)
@@ -97,7 +107,18 @@ export const startNativeSession = async (config, nativeBridge, plugin = {
97
107
  : request;
98
108
  return nativeBridge
99
109
  .send(payload)
100
- .then((result) => {
110
+ .then(async (result) => {
111
+ // A native drag can race retirement before its notification reaches JS.
112
+ // Keep the prepared underlay until native has applied a fresh return placement.
113
+ if (request.action === 'release' && result?.requiresPlacement) {
114
+ if (binding && (!binding.owner || binding.owner === placementOwner)) {
115
+ result = await nativeBridge.send({ ...request, placement: binding.measure() });
116
+ }
117
+ else {
118
+ await nativeBridge.send({ action: 'visibility', visible: false });
119
+ result = await nativeBridge.send(request);
120
+ }
121
+ }
101
122
  if (epoch === placementEpoch &&
102
123
  (request.action === 'resume' || releasing || request.action === 'reset' || request.action === 'visibility'))
103
124
  clearPlacement();
@@ -247,6 +268,7 @@ export const startNativeSession = async (config, nativeBridge, plugin = {
247
268
  const created = await createWebProjection(createWebProjectionScope(target, config.id), bridge, {
248
269
  wake,
249
270
  captureDelayMs: config.captureDelayMs,
271
+ retainPreparedUntilRelease: config.nativeDragHandoff,
250
272
  blocked: () => destination !== 'native' || automaticMotion || (placementPending && !manager?.token),
251
273
  revision: () => revision,
252
274
  captureRevision: config.captureMode === 'view' ? () => contentRevision : undefined,
@@ -282,7 +304,23 @@ export const startNativeSession = async (config, nativeBridge, plugin = {
282
304
  (element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth));
283
305
  });
284
306
  const scroll = scrollOwner && readScrollPlacement(target, scrollOwner, () => config.id);
285
- const tracking = await bridge.send({ action: 'track', viewportWidth: innerWidth, ...(scroll ? { scroll } : {}) });
307
+ // Gesture gating needs a hit region, not native compositor scroll eligibility.
308
+ // Rounded/clipped Ionic hosts can reject readScrollPlacement while still scrolling in Web.
309
+ const gesture = config.nativeDragHandoff && config.captureMode === 'view' && scrollOwner
310
+ ? {
311
+ viewport: scrollOwner.getBoundingClientRect(),
312
+ offsetX: scrollOwner.scrollLeft,
313
+ offsetY: scrollOwner.scrollTop,
314
+ rangeX: Math.max(0, scrollOwner.scrollWidth - scrollOwner.clientWidth),
315
+ rangeY: Math.max(0, scrollOwner.scrollHeight - scrollOwner.clientHeight),
316
+ }
317
+ : undefined;
318
+ const tracking = await bridge.send({
319
+ action: 'track',
320
+ viewportWidth: innerWidth,
321
+ ...(scroll ? { scroll } : {}),
322
+ ...(gesture ? { gesture } : {}),
323
+ });
286
324
  if (stopped || version !== generation)
287
325
  return;
288
326
  nativeScroll = tracking?.tracked === true;
@@ -356,8 +394,13 @@ export const startNativeSession = async (config, nativeBridge, plugin = {
356
394
  manager?.moved();
357
395
  else
358
396
  manager?.changed();
397
+ endAutomaticMotionLater();
398
+ };
399
+ function endAutomaticMotionLater() {
359
400
  clearTimeout(scrollTimer);
360
401
  scrollTimer = setTimeout(() => {
402
+ if (nativeDragging)
403
+ return;
361
404
  automaticMotion = false;
362
405
  if (stopped || destination !== 'native')
363
406
  return;
@@ -365,7 +408,31 @@ export const startNativeSession = async (config, nativeBridge, plugin = {
365
408
  if (!manager?.restoring)
366
409
  schedule();
367
410
  }, 100);
368
- };
411
+ }
412
+ function adoptNativeCover(token) {
413
+ if (stopped || destination !== 'native' || !target?.isConnected || !manager)
414
+ return;
415
+ nativeDragging = true;
416
+ // A scroll handler may have adopted the same native cover before its notification arrives.
417
+ if (manager.token)
418
+ return;
419
+ if (restoring || manager.restoring)
420
+ generation++;
421
+ coveredToken = token;
422
+ const binding = projectionPlacement(target);
423
+ if (binding) {
424
+ binding.owner = placementOwner;
425
+ binding.projected = true;
426
+ }
427
+ automaticMotion = true;
428
+ beginProjection();
429
+ if (!manager.token) {
430
+ automaticMotion = false;
431
+ void recoverFailedProjection();
432
+ return;
433
+ }
434
+ endAutomaticMotionLater();
435
+ }
369
436
  // Listen on composed-tree ancestors themselves: non-composed scroll events inside
370
437
  // Ionic's shadow tree never reach document, and Node.contains excludes slotted children.
371
438
  const refreshScrollListeners = () => {
@@ -408,6 +475,7 @@ export const startNativeSession = async (config, nativeBridge, plugin = {
408
475
  if (stopped || (!force && next === destination))
409
476
  return;
410
477
  automaticMotion = false;
478
+ nativeDragging = false;
411
479
  clearTimeout(scrollTimer);
412
480
  destination = next;
413
481
  generation++;
@@ -419,13 +487,13 @@ export const startNativeSession = async (config, nativeBridge, plugin = {
419
487
  .then(() => {
420
488
  if (destination !== 'none' || version !== generation || stopped)
421
489
  return;
422
- manager?.invalidate();
490
+ manager?.invalidate(true);
423
491
  setVisibility();
424
492
  })
425
493
  .catch(() => {
426
494
  if (destination !== 'none' || version !== generation || stopped)
427
495
  return;
428
- manager?.invalidate();
496
+ manager?.invalidate(true);
429
497
  setVisibility();
430
498
  });
431
499
  }
@@ -507,8 +575,9 @@ export const startNativeSession = async (config, nativeBridge, plugin = {
507
575
  requestedDestination = destination = 'none';
508
576
  generation++;
509
577
  automaticMotion = false;
578
+ nativeDragging = false;
510
579
  clearTimeout(scrollTimer);
511
- manager?.invalidate();
580
+ manager?.invalidate(true);
512
581
  setVisibility();
513
582
  },
514
583
  };
@@ -6,6 +6,8 @@ export interface WebProjectionOptions {
6
6
  events?: ProjectionEvents;
7
7
  /** Debounce before preparing replacement pixels, in milliseconds. Defaults to 50; never delays handoff. */
8
8
  captureDelayMs?: number;
9
+ /** Preserve an idle underlay while native disarms a possible early drag cover. */
10
+ retainPreparedUntilRelease?: boolean;
9
11
  wake(): void;
10
12
  blocked(): boolean;
11
13
  revision?(): number;
@@ -35,7 +37,7 @@ declare const createProjectionEngine: (scope: WebProjectionScope, transport: Web
35
37
  retireRestoration: () => void;
36
38
  end: () => void;
37
39
  updated: (token: string | undefined, restoredIDs?: string[], revision?: number) => void;
38
- invalidate: () => void;
40
+ invalidate: (nativeHidden?: boolean) => void;
39
41
  destroy(): Promise<void>;
40
42
  }>;
41
43
  type Engine = Awaited<ReturnType<typeof createProjectionEngine>>;
@@ -96,6 +96,7 @@ const createProjectionEngine = async (scope, transport, callbacks) => {
96
96
  let paintFrame = 0;
97
97
  let presentation = 0;
98
98
  let cached;
99
+ let retiring;
99
100
  let active;
100
101
  let touching = false;
101
102
  let ending = false;
@@ -672,12 +673,44 @@ const createProjectionEngine = async (scope, transport, callbacks) => {
672
673
  finishRestoration();
673
674
  schedule();
674
675
  };
675
- const invalidate = () => {
676
+ const invalidate = (nativeHidden = false) => {
676
677
  pendingCleanup.forEach((run) => run());
677
678
  generation++;
678
679
  win.clearTimeout(timer);
679
680
  const previous = cached;
680
681
  cached = undefined;
682
+ if (nativeHidden || stopped)
683
+ retiring = undefined;
684
+ if (callbacks.retainPreparedUntilRelease && !active && !nativeHidden && !stopped) {
685
+ if (previous) {
686
+ const pending = {};
687
+ retiring = pending;
688
+ void bounded(call({ action: 'release', token: previous.data.token }))
689
+ .catch(() => { })
690
+ .finally(() => {
691
+ previous.images.forEach((image) => {
692
+ image.src = '';
693
+ });
694
+ if (retiring !== pending)
695
+ return;
696
+ retiring = undefined;
697
+ if (cached || active)
698
+ return;
699
+ // Another session can already own the same host; retire only our own DOM markers.
700
+ for (const target of rendered) {
701
+ if (target.getAttribute(imageMarker) !== preparedKeys.get(target)) {
702
+ rendered.delete(target);
703
+ preparedTargets.delete(target);
704
+ }
705
+ }
706
+ detach();
707
+ preparedKeys.clear();
708
+ win.dispatchEvent(new Event(sourcesChangedEvent));
709
+ });
710
+ }
711
+ if (retiring)
712
+ return;
713
+ }
681
714
  dispose(previous);
682
715
  if (active)
683
716
  abandon(active, false);
@@ -828,7 +861,7 @@ export const createWebProjectionBatch = async (transport, options) => {
828
861
  end: () => engine?.end(),
829
862
  updated: (token, ids, revision) => engine?.updated(token, ids, revision),
830
863
  retireRestoration: () => engine?.retireRestoration(),
831
- invalidate: () => engine?.invalidate(),
864
+ invalidate: (nativeHidden) => engine?.invalidate(nativeHidden),
832
865
  async destroy() {
833
866
  stopped = true;
834
867
  await initializing;
@@ -338,3 +338,17 @@ appearance observed at 1000 ms. This is a recommendation for that interaction, n
338
338
  Apple timing contract or a universal Liquid Glass duration. Measure your control's
339
339
  selection, reselection and successive updates before choosing a value. Continuously
340
340
  changing content has no guaranteed settled snapshot.
341
+
342
+ ## Image color precision on iOS
343
+
344
+ Prepared window snapshots use an extended Display P3 Float16 bitmap during capture
345
+ and are encoded as 16-bit Display P3 PNGs on a background queue. The Web projection
346
+ displays the prepared image directly, without a Canvas or WebGPU conversion. Native
347
+ entry and return copies also use the decoded PNG, so both sides share the same
348
+ prepared pixels after color conversion. Encoding and decoding
349
+ stay outside the overlay handoff; consumers do not need a new setting or dependency.
350
+
351
+ This reduces color loss in wide-gamut content but does not guarantee identical Liquid
352
+ Glass appearance: native compositing and conversion to integer PNG can still change
353
+ pixels. Higher precision also increases capture memory and image size. The existing
354
+ fallback policy applies if preparation fails. Android retains its platform capture path.
@@ -1,4 +1,6 @@
1
1
  import UIKit
2
+ import CoreImage
3
+ import ImageIO
2
4
 
3
5
  /// Immutable, locally served images of controls that will be covered by a Web overlay.
4
6
  /// Native interaction invalidates in-flight captures before Web receives the activation.
@@ -15,6 +17,7 @@ public final class NativeUICoveredSnapshots {
15
17
  let parts: [Part]
16
18
  }
17
19
  public init() {}
20
+ private let imageContext = CIContext()
18
21
  private var captures: [String: Capture] = [:]
19
22
  private var restoration: (token: String, views: [UIImageView], entry: Bool)?
20
23
  private var hiddenControls: (token: String, restore: [() -> Void])?
@@ -80,18 +83,22 @@ public final class NativeUICoveredSnapshots {
80
83
  regions.append(([id], rect))
81
84
  }
82
85
  let bounds = regions.reduce(CGRect.null) { $0.union($1.rect) }
83
- let format = UIGraphicsImageRendererFormat()
84
- format.scale = scale
85
- format.opaque = true
86
- // Rasterize the visible window directly. A screen snapshot is a display
87
- // replica, not a bitmap source; drawing that replica can succeed with
88
- // black pixels on physical devices.
89
- var drawn = false
90
- let image = UIGraphicsImageRenderer(size: bounds.size, format: format).image { context in
91
- context.cgContext.translateBy(x: -bounds.minX, y: -bounds.minY)
92
- drawn = window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
93
- }
94
- guard drawn, let pixels = image.cgImage else { complete([:]); return }
86
+ // Preserve wide-gamut precision during capture. Encode to integer 16-bit
87
+ // Display P3 only off the presentation path, without a Canvas conversion.
88
+ guard let space = CGColorSpace(name: CGColorSpace.extendedDisplayP3),
89
+ let outputSpace = CGColorSpace(name: CGColorSpace.displayP3),
90
+ let context = CGContext(data: nil, width: Int(bounds.width * scale), height: Int(bounds.height * scale),
91
+ bitsPerComponent: 16, bytesPerRow: 0, space: space,
92
+ bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.floatComponents.rawValue | CGBitmapInfo.byteOrder16Little.rawValue)
93
+ else { complete([:]); return }
94
+ context.translateBy(x: 0, y: bounds.height * scale)
95
+ context.scaleBy(x: scale, y: -scale)
96
+ context.translateBy(x: -bounds.minX, y: -bounds.minY)
97
+ // Capture the window itself: display-replica views can rasterize as black on devices.
98
+ UIGraphicsPushContext(context)
99
+ let drawn = window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
100
+ UIGraphicsPopContext()
101
+ guard drawn, let pixels = context.makeImage() else { complete([:]); return }
95
102
  let token = UUID().uuidString
96
103
  let directory = self.directory
97
104
  let crops = regions.compactMap { region -> (ids: [String], frame: CGRect, image: UIImage)? in
@@ -101,15 +108,25 @@ public final class NativeUICoveredSnapshots {
101
108
  return (region.ids, window.convert(region.rect, to: webView), UIImage(cgImage: part, scale: scale, orientation: .up))
102
109
  }
103
110
  guard crops.count == regions.count else { complete([:]); return }
111
+ let imageContext = self.imageContext
104
112
  DispatchQueue.global(qos: .userInitiated).async { [weak self] in
105
113
  var parts: [Part] = []
106
114
  do {
107
115
  try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
108
116
  for (index, crop) in crops.enumerated() {
109
- guard let png = crop.image.pngData() else { throw CocoaError(.fileWriteUnknown) }
117
+ guard let pixels = crop.image.cgImage,
118
+ let png = imageContext.pngRepresentation(of: CIImage(cgImage: pixels), format: .RGBA16,
119
+ colorSpace: outputSpace, options: [:]) else { throw CocoaError(.fileWriteUnknown) }
120
+ // Native entry/return copies must use the same encoded pixels as Web,
121
+ // including gamut clipping performed by integer PNG conversion.
122
+ guard let source = CGImageSourceCreateWithData(png as CFData, nil),
123
+ let decoded = CGImageSourceCreateImageAtIndex(source, 0,
124
+ [kCGImageSourceShouldCacheImmediately: true] as CFDictionary)
125
+ else { throw CocoaError(.fileWriteUnknown) }
126
+ let image = UIImage(cgImage: decoded, scale: scale, orientation: .up)
110
127
  let file = directory.appendingPathComponent("\(token)-\(index).png")
111
128
  try png.write(to: file, options: .atomic)
112
- parts.append(Part(ids: crop.ids, frame: crop.frame, image: crop.image, file: file))
129
+ parts.append(Part(ids: crop.ids, frame: crop.frame, image: image, file: file))
113
130
  }
114
131
  } catch {
115
132
  parts.forEach { try? FileManager.default.removeItem(at: $0.file) }
@@ -1,4 +1,4 @@
1
- "use strict";var WebViewNativeUIEngineRuntime=(()=>{var ze=Object.defineProperty;var it=Object.getOwnPropertyDescriptor;var st=Object.getOwnPropertyNames;var at=Object.prototype.hasOwnProperty;var ct=(e,o)=>{for(var n in o)ze(e,n,{get:o[n],enumerable:!0})},lt=(e,o,n,f)=>{if(o&&typeof o=="object"||typeof o=="function")for(let v of st(o))!at.call(e,v)&&v!==n&&ze(e,v,{get:()=>o[v],enumerable:!(f=it(o,v))||f.enumerable});return e};var dt=e=>lt(ze({},"__esModule",{value:!0}),e);var jt={};ct(jt,{hide:()=>Lt,notify:()=>wt,receive:()=>Pt,show:()=>Tt,start:()=>St,stop:()=>Mt});var ut=Symbol.for("webview-native-ui-engine.projection-placement");var $e=e=>e?.[ut];var pt=(e,o)=>{if(e.matches("ion-toast, ion-menu"))return!1;let n=e;if(n.showBackdrop===!1)return!1;if(!e.matches("ion-modal"))return!0;let f=o??n.initialBreakpoint;return f===void 0||f>(n.backdropBreakpoint??0)},ft=Symbol.for("@webview-native-ui/engine/overlays/v1"),mt=e=>{let o=new Set,n=new Set,f=new Set,v=new WeakMap,c=new WeakSet,l=new Map,w=[],u={},S=new Map,j,M=m=>{for(let p of[...f])try{p(m)}catch(i){console.error("Native UI overlay listener failed",i)}},O=(m,p,i)=>{p==="willPresent"||p==="didPresent"?(c.delete(m),o.add(m)):p==="didDismiss"&&(c.add(m),o.delete(m),n.delete(m),v.delete(m));let T=i?.detail?.breakpoint;typeof T=="number"&&T>0&&v.set(m,T),M({element:m,phase:p,event:i})},R=(m=!1)=>{let p=!1;for(let i of o)(!i.isConnected||i.ownerDocument!==e||!w.some(({selector:T})=>i.matches(T)))&&(o.delete(i),n.delete(i),p=!0);for(let i of e.querySelectorAll(w.map(({selector:T})=>T).join(",")))!o.has(i)&&!c.has(i)&&(m||!i.matches("ion-popover"))&&(i.presented||i.classList.contains("show-menu"))&&(o.add(i),p=!0);p&&M({phase:"change"})};return{get active(){return o},subscribe(m){return f.add(m),()=>{f.delete(m)}},setNative(m,p){n.has(m)!==p&&(p?n.add(m):n.delete(m),M({element:m,phase:"change"}))},isExcluded(m){return!!u.exclude&&m.matches(u.exclude)},isNative(m){return n.has(m)},get stack(){let m=[...o].filter(i=>i.isConnected&&i.ownerDocument===e),p=i=>{let T=Number.parseFloat(e.defaultView.getComputedStyle(i).zIndex);return Number.isFinite(T)?T:i.overlayIndex??0};return m.sort((i,T)=>p(i)-p(T)),m},isCovered(m){let p=this.stack,i=p.reduce((T,N,d)=>N.contains(m)?d:T,-1);return p.some((T,N)=>{if(n.has(T)||u.exclude&&T.matches(u.exclude))return!1;let d=u.covers?.(T,m);return d!==void 0?d:N>i&&!T.contains(m)&&(T.matches("ion-menu")||pt(T,v.get(T)))})},enable(m,p){let i={...u,...p},T=[...m,...i.additional??[]];i.exclude&&e.querySelector(i.exclude);let N=new Map;for(let W of T){e.querySelector(W.selector);for(let[$,A]of Object.entries(W.events)){let F=N.get(A);if(F&&F.phase!==$)throw new TypeError(`Overlay event "${A}" has conflicting phases`);N.set(A,{phase:$,selector:F?`${F.selector}, ${W.selector}`:W.selector})}}u=i,w=T,S=N;for(let W of S.keys()){if(l.has(W))continue;let $=A=>{let F=S.get(W),C=A.composedPath().find(X=>X instanceof e.defaultView.HTMLElement&&X.matches(F.selector));C&&O(C,F.phase,A)};e.addEventListener(W,$,!0),l.set(W,()=>e.removeEventListener(W,$,!0))}for(let[W,$]of l)S.has(W)||($(),l.delete(W));let d=!j;j||(j=new MutationObserver(()=>R()),j.observe(e.documentElement,{childList:!0,subtree:!0}),e.addEventListener("nativeUIOverlayAdopted",W=>{let $=W.detail;$?.ownerDocument===e&&O($,"willPresent",W)})),R(d),(d||p)&&M({phase:"change"})}}},_e=e=>{let o=e;return o[ft]??=mt(e)};function*ae(e){let o=e;for(;o;){let n=o.getRootNode();o=o.assignedSlot??o.parentElement??(n instanceof ShadowRoot?n.host:null),o&&(yield o)}}var vt=({x:e,y:o,width:n,height:f})=>({x:e,y:o,width:n,height:f}),Ke=e=>{let o=e.getBoundingClientRect(),n=getComputedStyle(e),f=parseFloat(n.borderLeftWidth)||0,v=parseFloat(n.borderTopWidth)||0;return{x:o.left+f,y:o.top+v,width:o.width-f-(parseFloat(n.borderRightWidth)||0),height:o.height-v-(parseFloat(n.borderBottomWidth)||0)}},Ve=(e,o,n)=>{let f=[e,...ae(e)],v=f.indexOf(o);if(v<=0||o===e.ownerDocument.scrollingElement)return;let c=e.ownerDocument.defaultView;if(c.visualViewport&&(c.visualViewport.scale!==1||c.visualViewport.offsetLeft||c.visualViewport.offsetTop))return;let l=0,w=0,u=c.innerWidth,S=c.innerHeight;for(let[R,m]of f.entries()){let p=getComputedStyle(m);if(p.transform!=="none"&&!new DOMMatrixReadOnly(p.transform).isIdentity||p.zoom&&p.zoom!=="normal"&&Number(p.zoom)!==1||[p.translate,p.rotate,p.scale,p.perspective].some(d=>d&&d!=="none")||p.writingMode&&p.writingMode!=="horizontal-tb"||R<v&&/^(fixed|sticky)$/.test(p.position))return;if(R===0)continue;if(p.scrollbarGutter&&p.scrollbarGutter!=="auto")return;let i=/^(auto|scroll|hidden|clip)$/.test(p.overflowX),T=/^(auto|scroll|hidden|clip)$/.test(p.overflowY);if(R<v&&(i||T))return;let N=m===e.ownerDocument.scrollingElement;if(R>v&&((i||N)&&m.scrollWidth>m.clientWidth||(T||N)&&m.scrollHeight>m.clientHeight)||(i||T)&&[p.borderTopLeftRadius,p.borderTopRightRadius,p.borderBottomLeftRadius,p.borderBottomRightRadius].some(d=>parseFloat(d)>0)||p.clipPath&&p.clipPath!=="none"||p.maskImage&&p.maskImage!=="none")return;if(R>=v){let d=Ke(m);i&&(l=Math.max(l,d.x),u=Math.min(u,d.x+d.width)),T&&(w=Math.max(w,d.y),S=Math.min(S,d.y+d.height))}}let j=getComputedStyle(o);if(!/^(auto|scroll)$/.test(j.overflowX)&&!/^(auto|scroll)$/.test(j.overflowY))return;let M=Ke(o);if(u<=l||S<=w||!M.width||!M.height)return;let O=vt(e.getBoundingClientRect());return{id:n(o),offsetX:o.scrollLeft,offsetY:o.scrollTop,viewport:M,clip:{x:l,y:w,width:u-l,height:S-w},contentWidth:o.scrollWidth,contentHeight:o.scrollHeight,frame:{...O,x:O.x-M.x+o.scrollLeft,y:O.y-M.y+o.scrollTop}}};var Ge=e=>{let o=globalThis;if(o.Capacitor?.convertFileSrc)return o.Capacitor.convertFileSrc(e);if(!e.startsWith("file:"))return e;if(o.WkWebView?.convertFilePath)return o.WkWebView.convertFilePath(e);throw new Error("Native UI file URL requires a host resource handler")};var ce=e=>new Promise((o,n)=>{let f=setTimeout(()=>n(new Error("Native UI bridge timed out")),5e3);e.then(o,n).finally(()=>clearTimeout(f))}),Ne=async e=>{try{return await ce(e)}catch(o){throw e.then(n=>n.remove()).catch(()=>{}),o}};var Ue=async(e,o)=>{let n=new Map;for(let w of e.update??[])n.set(w,{updated:!0});for(let w of["native","web","none"])for(let u of e.projection?.[w]??[]){let S=n.get(u)??{updated:!1};if(S.destination&&S.destination!==w)throw new TypeError(`Projection event "${u}" has conflicting destinations`);n.set(u,{...S,destination:w})}let f=!1,v=[],c=async()=>{f=!0;let u=(await Promise.allSettled(v.splice(0).map(S=>Promise.resolve().then(()=>S.remove())))).find(S=>S.status==="rejected");if(u?.status==="rejected")throw u.reason},l=[...n].map(async([w,u])=>{let S=await Ne(Promise.resolve().then(()=>e.addListener(w,j=>{!f&&(!e.accepts||e.accepts(j))&&o(u.destination,u.updated)})));f?await S.remove():v.push(S)});try{return await Promise.all(l),{remove:c}}catch(w){throw await c().catch(()=>{}),w}};var Je=`
1
+ "use strict";var WebViewNativeUIEngineRuntime=(()=>{var Ve=Object.defineProperty;var st=Object.getOwnPropertyDescriptor;var at=Object.getOwnPropertyNames;var ct=Object.prototype.hasOwnProperty;var lt=(e,o)=>{for(var n in o)Ve(e,n,{get:o[n],enumerable:!0})},dt=(e,o,n,p)=>{if(o&&typeof o=="object"||typeof o=="function")for(let v of at(o))!ct.call(e,v)&&v!==n&&Ve(e,v,{get:()=>o[v],enumerable:!(p=st(o,v))||p.enumerable});return e};var ut=e=>dt(Ve({},"__esModule",{value:!0}),e);var xt={};lt(xt,{hide:()=>Mt,notify:()=>bt,receive:()=>Tt,show:()=>Lt,start:()=>jt,stop:()=>St});var ft=Symbol.for("webview-native-ui-engine.projection-placement");var De=e=>e?.[ft];var pt=(e,o)=>{if(e.matches("ion-toast, ion-menu"))return!1;let n=e;if(n.showBackdrop===!1)return!1;if(!e.matches("ion-modal"))return!0;let p=o??n.initialBreakpoint;return p===void 0||p>(n.backdropBreakpoint??0)},mt=Symbol.for("@webview-native-ui/engine/overlays/v1"),vt=e=>{let o=new Set,n=new Set,p=new Set,v=new WeakMap,l=new WeakSet,d=new Map,E=[],u={},S=new Map,M,T=h=>{for(let c of[...p])try{c(h)}catch(y){console.error("Native UI overlay listener failed",y)}},N=(h,c,y)=>{c==="willPresent"||c==="didPresent"?(l.delete(h),o.add(h)):c==="didDismiss"&&(l.add(h),o.delete(h),n.delete(h),v.delete(h));let a=y?.detail?.breakpoint;typeof a=="number"&&a>0&&v.set(h,a),T({element:h,phase:c,event:y})},O=(h=!1)=>{let c=!1;for(let y of o)(!y.isConnected||y.ownerDocument!==e||!E.some(({selector:a})=>y.matches(a)))&&(o.delete(y),n.delete(y),c=!0);for(let y of e.querySelectorAll(E.map(({selector:a})=>a).join(",")))!o.has(y)&&!l.has(y)&&(h||!y.matches("ion-popover"))&&(y.presented||y.classList.contains("show-menu"))&&(o.add(y),c=!0);c&&T({phase:"change"})};return{get active(){return o},subscribe(h){return p.add(h),()=>{p.delete(h)}},setNative(h,c){n.has(h)!==c&&(c?n.add(h):n.delete(h),T({element:h,phase:"change"}))},isExcluded(h){return!!u.exclude&&h.matches(u.exclude)},isNative(h){return n.has(h)},get stack(){let h=[...o].filter(y=>y.isConnected&&y.ownerDocument===e),c=y=>{let a=Number.parseFloat(e.defaultView.getComputedStyle(y).zIndex);return Number.isFinite(a)?a:y.overlayIndex??0};return h.sort((y,a)=>c(y)-c(a)),h},isCovered(h){let c=this.stack,y=c.reduce((a,U,j)=>U.contains(h)?j:a,-1);return c.some((a,U)=>{if(n.has(a)||u.exclude&&a.matches(u.exclude))return!1;let j=u.covers?.(a,h);return j!==void 0?j:U>y&&!a.contains(h)&&(a.matches("ion-menu")||pt(a,v.get(a)))})},enable(h,c){let y={...u,...c},a=[...h,...y.additional??[]];y.exclude&&e.querySelector(y.exclude);let U=new Map;for(let m of a){e.querySelector(m.selector);for(let[A,I]of Object.entries(m.events)){let F=U.get(I);if(F&&F.phase!==A)throw new TypeError(`Overlay event "${I}" has conflicting phases`);U.set(I,{phase:A,selector:F?`${F.selector}, ${m.selector}`:m.selector})}}u=y,E=a,S=U;for(let m of S.keys()){if(d.has(m))continue;let A=I=>{let F=S.get(m),q=I.composedPath().find(W=>W instanceof e.defaultView.HTMLElement&&W.matches(F.selector));q&&N(q,F.phase,I)};e.addEventListener(m,A,!0),d.set(m,()=>e.removeEventListener(m,A,!0))}for(let[m,A]of d)S.has(m)||(A(),d.delete(m));let j=!M;M||(M=new MutationObserver(()=>O()),M.observe(e.documentElement,{childList:!0,subtree:!0}),e.addEventListener("nativeUIOverlayAdopted",m=>{let A=m.detail;A?.ownerDocument===e&&N(A,"willPresent",m)})),O(j),(j||c)&&T({phase:"change"})}}},Ke=e=>{let o=e;return o[mt]??=vt(e)};function*fe(e){let o=e;for(;o;){let n=o.getRootNode();o=o.assignedSlot??o.parentElement??(n instanceof ShadowRoot?n.host:null),o&&(yield o)}}var ht=({x:e,y:o,width:n,height:p})=>({x:e,y:o,width:n,height:p}),Ge=e=>{let o=e.getBoundingClientRect(),n=getComputedStyle(e),p=parseFloat(n.borderLeftWidth)||0,v=parseFloat(n.borderTopWidth)||0;return{x:o.left+p,y:o.top+v,width:o.width-p-(parseFloat(n.borderRightWidth)||0),height:o.height-v-(parseFloat(n.borderBottomWidth)||0)}},Xe=(e,o,n)=>{let p=[e,...fe(e)],v=p.indexOf(o);if(v<=0||o===e.ownerDocument.scrollingElement)return;let l=e.ownerDocument.defaultView;if(l.visualViewport&&(l.visualViewport.scale!==1||l.visualViewport.offsetLeft||l.visualViewport.offsetTop))return;let d=0,E=0,u=l.innerWidth,S=l.innerHeight;for(let[O,h]of p.entries()){let c=getComputedStyle(h);if(c.transform!=="none"&&!new DOMMatrixReadOnly(c.transform).isIdentity||c.zoom&&c.zoom!=="normal"&&Number(c.zoom)!==1||[c.translate,c.rotate,c.scale,c.perspective].some(j=>j&&j!=="none")||c.writingMode&&c.writingMode!=="horizontal-tb"||O<v&&/^(fixed|sticky)$/.test(c.position))return;if(O===0)continue;if(c.scrollbarGutter&&c.scrollbarGutter!=="auto")return;let y=/^(auto|scroll|hidden|clip)$/.test(c.overflowX),a=/^(auto|scroll|hidden|clip)$/.test(c.overflowY);if(O<v&&(y||a))return;let U=h===e.ownerDocument.scrollingElement;if(O>v&&((y||U)&&h.scrollWidth>h.clientWidth||(a||U)&&h.scrollHeight>h.clientHeight)||(y||a)&&[c.borderTopLeftRadius,c.borderTopRightRadius,c.borderBottomLeftRadius,c.borderBottomRightRadius].some(j=>parseFloat(j)>0)||c.clipPath&&c.clipPath!=="none"||c.maskImage&&c.maskImage!=="none")return;if(O>=v){let j=Ge(h);y&&(d=Math.max(d,j.x),u=Math.min(u,j.x+j.width)),a&&(E=Math.max(E,j.y),S=Math.min(S,j.y+j.height))}}let M=getComputedStyle(o);if(!/^(auto|scroll)$/.test(M.overflowX)&&!/^(auto|scroll)$/.test(M.overflowY))return;let T=Ge(o);if(u<=d||S<=E||!T.width||!T.height)return;let N=ht(e.getBoundingClientRect());return{id:n(o),offsetX:o.scrollLeft,offsetY:o.scrollTop,viewport:T,clip:{x:d,y:E,width:u-d,height:S-E},contentWidth:o.scrollWidth,contentHeight:o.scrollHeight,frame:{...N,x:N.x-T.x+o.scrollLeft,y:N.y-T.y+o.scrollTop}}};var Je=e=>{let o=globalThis;if(o.Capacitor?.convertFileSrc)return o.Capacitor.convertFileSrc(e);if(!e.startsWith("file:"))return e;if(o.WkWebView?.convertFilePath)return o.WkWebView.convertFilePath(e);throw new Error("Native UI file URL requires a host resource handler")};var ce=e=>new Promise((o,n)=>{let p=setTimeout(()=>n(new Error("Native UI bridge timed out")),5e3);e.then(o,n).finally(()=>clearTimeout(p))}),Ue=async e=>{try{return await ce(e)}catch(o){throw e.then(n=>n.remove()).catch(()=>{}),o}};var Ie=async(e,o)=>{let n=new Map;for(let E of e.update??[])n.set(E,{updated:!0});for(let E of["native","web","none"])for(let u of e.projection?.[E]??[]){let S=n.get(u)??{updated:!1};if(S.destination&&S.destination!==E)throw new TypeError(`Projection event "${u}" has conflicting destinations`);n.set(u,{...S,destination:E})}let p=!1,v=[],l=async()=>{p=!0;let u=(await Promise.allSettled(v.splice(0).map(S=>Promise.resolve().then(()=>S.remove())))).find(S=>S.status==="rejected");if(u?.status==="rejected")throw u.reason},d=[...n].map(async([E,u])=>{let S=await Ue(Promise.resolve().then(()=>e.addListener(E,M=>{!p&&(!e.accepts||e.accepts(M))&&o(u.destination,u.updated)})));p?await S.remove():v.push(S)});try{return await Promise.all(d),{remove:l}}catch(E){throw await l().catch(()=>{}),E}};var Ze=`
2
2
  :is([data-native-ui-snapshot-source], [data-native-ui-snapshot-prepared]),
3
3
  :is([data-native-ui-snapshot-source], [data-native-ui-snapshot-prepared]) *,
4
4
  :is([data-native-ui-snapshot-source], [data-native-ui-snapshot-prepared])::after,
@@ -10,5 +10,5 @@
10
10
  }
11
11
  [data-native-ui-snapshot-visible] { opacity:1!important;overflow:visible!important; }
12
12
  [data-native-ui-snapshot-visible][data-native-ui-snapshot][data-native-ui-snapshot]::before { opacity:1!important; }
13
- `;var ht=(e,o=!1)=>JSON.stringify(e.map(({control:n})=>[n.id,n.kind,n.placement,o?0:n.x,o?0:n.y,n.width,n.height,n.dark,n.rtl])),Ze=(e,o,n=o.getComputedStyle(e))=>{let f=e.getBoundingClientRect(),v=(j,M)=>{let O=parseFloat(j);return n.display!=="inline"&&Number.isFinite(O)?O+(n.boxSizing==="border-box"?0:M.reduce((R,m)=>R+(parseFloat(n.getPropertyValue(m))||0),0)):NaN},c=v(n.width,["padding-left","padding-right","border-left-width","border-right-width"]),l=v(n.height,["padding-top","padding-bottom","border-top-width","border-bottom-width"]),w=1;if(!(c>0&&l>0))for(let j of[e,...ae(e)]){let M=(j===e?n:o.getComputedStyle(j)).getPropertyValue("zoom");w*=(parseFloat(M)||1)/(M.endsWith("%")?100:1)}let u=c>0?f.width/c:w,S=l>0?f.height/l:w;return{x:f.x,y:f.y,scaleX:u||1,scaleY:S||1,left:(parseFloat(n.borderLeftWidth)||0)-e.scrollLeft,top:(parseFloat(n.borderTopWidth)||0)-e.scrollTop,position:n.position}},yt=async(e,o,n)=>{let f=n.captureDelayMs??50;if(!Number.isFinite(f)||f<0||f>2147483647)throw new RangeError("captureDelayMs must be a finite, nonnegative timer interval");let v="native",c=e.element.ownerDocument,l=c.defaultView,w=Array.from(l.crypto.getRandomValues(new Uint32Array(4)),t=>t.toString(16)).join("-"),u=t=>t.isConnected&&t.ownerDocument===c,S=t=>!!t?.isConnected&&t.ownerDocument===c,j=o.send.bind(o),M="data-native-ui-snapshot",O=`${e.namespace}:sources-change`,R=new Set,m=new Set,p=new Set,i=new WeakMap,T=0,N=t=>JSON.stringify(t.map(a=>(i.has(a.scope)||i.set(a.scope,++T),[i.get(a.scope),ht([a],!!n.opaqueUnderlay)]))),d=new Map,W=t=>{let a=t.getRootNode();return a.nodeType===11&&"host"in a?a:c},$=t=>{let a=d.get(t);if(!a){let y=c.createElement("style"),r=c.createElement("style");y.setAttribute("data-native-ui-snapshot-base",""),r.setAttribute(M,""),y.textContent=Je,(t.nodeType===11?t:t.head).append(y,r),a={base:y,image:r},d.set(t,a)}return a.image},A=()=>`${l.innerWidth}:${l.innerHeight}:${l.devicePixelRatio}`,F=!1,C=!1,X=0,ue=0,P,g,ie=!1,U=!1,pe=0,ge=0,D=0,re=0,B=0,Me=-1,J=!1,we=!1,Y,be=new Set,_=(t,a,y)=>{let r=()=>{be.delete(r)&&j({action:t,token:a}).catch(()=>{})};be.add(r),y&&!J?l.requestAnimationFrame(()=>l.requestAnimationFrame(r)):r()},se=new Set,Ee=()=>{se.forEach(t=>p.delete(t)),se.clear()},Z=(t,a=!1)=>{!t||t===P||t===g||(t.images.forEach(y=>{y.src=""}),_("release",t.data.token,a))},Se=t=>t.filter(a=>{if(!S(a.scope.element)||![a.element,...a.sources??[]].every(u))return!1;if(a.control.scroll){let y=a.element.getBoundingClientRect(),r=a.control.scroll.clip;if(y.right<=r.x||y.bottom<=r.y||y.left>=r.x+r.width||y.top>=r.y+r.height)return!1}return n.eligible?.(a)??!0}),fe=async()=>{if(re=0,J||v!=="native"||we||ie||g||n.blocked()||!Y||P?.revision===Y.revision&&Me===B)return;let{revision:t,candidates:a}=Y,y=D,r=B,L=A(),x=Se(a);if(!x.length)return;we=!0;let k,G=!1,ee=[];try{let te=!1,he=j({action:"prepare",revision:n.captureRevision?.()??t,viewportWidth:l.innerWidth,ids:x.map(b=>b.control.id)});he.then(b=>{te&&b.token&&j({action:"release",token:b.token}).catch(()=>{})}).catch(()=>{});try{k=await ce(he)}finally{te=!0}if(!k.token||!k.parts?.length)return;let ye=new Set(x.map(b=>b.control.id));if(k.parts.some(b=>!b.url.startsWith("file:")&&!/^\/__native_ui_engine__\/nativeUI[a-fA-F0-9]+\/[a-fA-F0-9-]+-[0-9]+\.png$/.test(b.url)||!b.ids.length||b.ids.some(V=>!ye.has(V))||![b.x,b.y,b.width,b.height].every(Number.isFinite)||b.width<=0||b.height<=0)||new Set(k.parts.flatMap(b=>b.ids)).size!==ye.size||k.parts.some(b=>new Set(b.ids.map(V=>x.find(Ie=>Ie.control.id===V).scope)).size!==1)||(await ce(Promise.all(k.parts.map(async b=>{let V=c.createElement("img");return ee.push(V),V.src=Ge(b.url),await V.decode(),V}))),J||v!=="native"||ie||g||n.blocked()||D!==y||A()!==L||Y?.revision!==t||x.some(b=>!S(b.scope.element)||![b.element,...b.sources??[]].every(u))))return;let Le=P;P={data:k,images:ee,sources:Array.from(new Set(x.flatMap(b=>[b.element,...b.sources??[]]))),targets:k.parts.map(b=>x.find(V=>b.ids.includes(V.control.id)).element),scopes:k.parts.map(b=>x.find(V=>b.ids.includes(V.control.id)).scope),parents:k.parts.map(b=>x.find(V=>b.ids.includes(V.control.id)).scope.element.parentNode),frames:[],viewport:L,revision:t,layout:N(x)},Ee(),P.sources.forEach(b=>{p.add(b),se.add(b)}),Re(),n.opaqueUnderlay&&j({action:"prepared",token:k.token}).catch(()=>{}),l.dispatchEvent(new Event(O)),Me=r,G=!0,Z(Le)}catch{}finally{G||ee.forEach(te=>{te.src=""}),k?.token&&!G&&j({action:"release",token:k.token}).catch(()=>{}),we=!1,Y&&(Y.revision!==t||B!==r)&&ne()}},ne=()=>{l.clearTimeout(re),!J&&v==="native"&&!g&&!ie&&!n.blocked()&&(re=l.setTimeout(()=>{fe()},f))},Pe,Ce=(t,a)=>{if(a.some(r=>r.scope.element.ownerDocument!==c||[r.element,...r.sources??[]].some(L=>L.ownerDocument!==c)))throw new TypeError("All projection sources and fallback scopes must share one Document");if(n.blocked())return;!g&&P&&P.layout!==N(Se(a))&&ve();let y=N(a);Pe!==y&&(Pe=y,B++,D++),Y?.revision!==t&&D++,Y={revision:t,candidates:a},(P?.revision!==t||Me!==B)&&ne()},me=t=>{!g&&P&&t&&Y?.candidates.some(a=>a.scrollElement===t&&P.sources.includes(a.element))&&ve(),g&&(He(),De()),B++,D++,ne()},Oe=await Ne(o.listen(({active:t})=>{ie=t,D++,l.clearTimeout(re),t||ne()})),je=new Set,Te=t=>{je.delete(t)&&t.removeAttribute("data-native-ui-snapshot-prepared"),t.removeAttribute("data-native-ui-snapshot-visible"),t.removeAttribute(M),t.removeAttribute("data-native-ui-snapshot-source")},s=()=>{ue++,l.cancelAnimationFrame(X),l.cancelAnimationFrame(xe),xe=0,q.clear(),X=0,F=!1,C=!1,Ee(),g?.sources.forEach(t=>{R.delete(t),m.delete(t)}),z.forEach(Te),z.clear(),d.forEach(({base:t,image:a})=>{t.remove(),a.remove()}),d.clear()},h=(t=g,a=U)=>{if(g!==t)return;let y=new Set([g,P]);s(),g=void 0,P=void 0,U=!1,l.clearTimeout(pe),y.forEach(r=>Z(r,a)),n.wake()},E=()=>{if(!g)return;if(P!==g){h();return}let t=g;s(),g=void 0,U=!1,l.clearTimeout(pe),_("restored",t.data.token,!0),t.sources.forEach(a=>{p.add(a),se.add(a)}),Re(),n.wake()},K=()=>{U&&E(),be.forEach(t=>t())},I=t=>t.scopes.map(a=>S(a.element)?a.element:void 0),z=new Set,Q=new Map,q=new Map,xe=0,He=()=>{for(let[t,a]of q){if(!t.isConnected)continue;let y=l.getComputedStyle(t),r=`${a.x-(parseFloat(y.borderLeftWidth)||0)+t.scrollLeft}px`,L=`${a.y-(parseFloat(y.borderTopWidth)||0)+t.scrollTop}px`;a.style.left!==r&&a.style.setProperty("left",r,"important"),a.style.top!==L&&a.style.setProperty("top",L,"important")}},De=()=>{if(xe||!g)return;[...q.keys()].some(a=>a.getAnimations?.().some(y=>y.playState==="running"&&y.effect?.getKeyframes().some(r=>["borderLeftWidth","borderTopWidth","borderWidth"].some(L=>L in r))))&&(xe=l.requestAnimationFrame(()=>{xe=0,He(),De()}))},Re=()=>{let t=g??P;if(!t||U)return;if(!t.sources.every(u)||I(t).some(r=>!r)){ve();return}let a=I(t);if(g){if(a.some(L=>!L?.isConnected||!d.has(W(L)))){h(g,!1);return}let r=new Set;for(let[L,x]of t.targets.entries()){let k=Q.get(x),G=a[L];!k||!G?.isConnected||(G.setAttribute(M,k),G.setAttribute("data-native-ui-snapshot-source",""),G.toggleAttribute("data-native-ui-snapshot-visible",C),r.add(G))}for(let L of z)r.has(L)||Te(L);z.clear(),r.forEach(L=>z.add(L));return}Q.clear(),q.clear(),z.forEach(Te),z.clear();let y=new Map;for(let r of new Set(a)){if(!r)continue;z.add(r);let L=$(W(r)),x=y.get(L);x||(x={rules:[],positions:[]},y.set(L,x));let{rules:k,positions:G}=x;r!==t.targets[a.indexOf(r)]&&!r.hasAttribute("data-native-ui-snapshot-prepared")&&(r.setAttribute("data-native-ui-snapshot-prepared",""),je.add(r)),r.toggleAttribute("data-native-ui-snapshot-visible",!!g||!!n.opaqueUnderlay),g&&r.setAttribute("data-native-ui-snapshot-source","");let ee=a.flatMap((H,oe)=>H===r?[oe]:[]),te=l.getComputedStyle(r);{let H=Ze(r,l,te);ee.forEach(oe=>{let Fe=t.data.parts[oe];n.opaqueUnderlay&&t.frames[oe]||(t.frames[oe]={x:n.opaqueUnderlay?0:(Fe.x-H.x)/H.scaleX,y:n.opaqueUnderlay?0:(Fe.y-H.y)/H.scaleY,width:Fe.width/H.scaleX,height:Fe.height/H.scaleY})})}let he=ee.map(H=>t.frames[H]),ye=Math.min(...he.map(H=>H.x)),Le=Math.min(...he.map(H=>H.y)),b=Math.max(...he.map(H=>H.x+H.width)),V=Math.max(...he.map(H=>H.y+H.height)),Ie=(parseFloat(te.borderLeftWidth)||0)-r.scrollLeft,ot=(parseFloat(te.borderTopWidth)||0)-r.scrollTop,Ae=`${w}-${ee[0]}`;ee.forEach(H=>Q.set(t.targets[H],Ae)),r.setAttribute(M,Ae),k.push(`[${M}="${Ae}"][data-native-ui-snapshot-visible] { ${te.position==="static"?"position:relative!important;":""} }`);let rt=ee.map(H=>{let oe=t.frames[H];return`url(${JSON.stringify(t.images[H].src)}) ${oe.x-ye}px ${oe.y-Le}px / ${oe.width}px ${oe.height}px no-repeat`}),ke=c.createElement("span").style;ke.cssText=`left:${ye-Ie}px;top:${Le-ot}px;width:${b-ye}px;height:${V-Le}px;background:${rt.join(",")};`;for(let H of Array.from(ke))ke.setProperty(H,ke.getPropertyValue(H),"important");k.push(`[${M}="${Ae}"][${M}][${M}]::before { ${ke.cssText} }`),G.push({target:r,x:ye,y:Le})}t.parents=t.scopes.map(r=>r.element.parentNode);for(let[r,{rules:L,positions:x}]of y)r.textContent=L.join(`
14
- `),x.forEach(({target:k,x:G,y:ee},te)=>q.set(k,{x:G,y:ee,style:r.sheet.cssRules[te*2+1].style}))},Xe=()=>{if(K(),J||v==="none"||g)return;let t=P;if(!t||t.viewport!==A()||!t.sources.every(u)||n.current&&!n.current(t.targets)||t.targets.some(r=>!Q.has(r))||I(t).some(r=>!r?.isConnected)||[...z].some(r=>!r.isConnected))return;t.scopes.some((r,L)=>r.element.parentNode!==t.parents[L])&&Re(),He();let a=++ue,y=()=>ue===a;F=!0,C=!!n.opaqueUnderlay,g=t,D++,l.clearTimeout(re),t.sources.forEach(r=>{R.add(r)}),z.forEach(r=>{r.setAttribute("data-native-ui-snapshot-source",""),r.toggleAttribute("data-native-ui-snapshot-visible",C)}),X=l.requestAnimationFrame(()=>{X=0,y()&&(F=!1,n.wake())}),ce(j({action:"cover",token:t.data.token,...n.transient?{transient:!0}:{}})).then(r=>{if(y()){if(F=!1,r?.shown!==!0){h(t,!1);return}U||(C=!0,t.sources.forEach(L=>m.add(L)),Re(),He(),De()),l.cancelAnimationFrame(X),X=l.requestAnimationFrame(()=>{X=0,y()&&j({action:"painted",token:t.data.token}).catch(()=>{y()&&h(t,!1)})}),n.wake()}}).catch(()=>{y()&&h(t,!1)})},Ye=()=>{if(!(!g||J||U)){if(!g.sources.every(u)||I(g).some(t=>!t)||n.current&&!n.current(g.targets)){h(g,!1);return}if(!n.blocked()){if(n.transient){let t=g,a=I(t);if(a.some(r=>!r?.isConnected)){h(t,!1);return}let y=a.map((r,L)=>{let x=Ze(r,l),k=t.frames[L];return{...t.data.parts[L],x:x.x+k.x*x.scaleX,y:x.y+k.y*x.scaleY,width:k.width*x.scaleX,height:k.height*x.scaleY}});y.some((r,L)=>["x","y","width","height"].some(x=>r[x]!==t.data.parts[L][x]))&&(n.opaqueUnderlay?t.data={...t.data,parts:y}:P=void 0),j({action:"restore",token:t.data.token,viewportWidth:l.innerWidth,parts:y}).catch(()=>{})}ge=n.revision?.()??Y?.revision??0,U=!0,g.sources.forEach(t=>R.delete(t)),pe=l.setTimeout(()=>{U&&h(g,!1)},250),n.wake()}}},nt=(t,a,y)=>{!U||!g||y===void 0||y<=ge||!n.transient&&t||g.data.parts.every(r=>r.ids.every(L=>a?.includes(L)))&&(E(),ne())},ve=()=>{be.forEach(a=>a()),D++,l.clearTimeout(re);let t=P;P=void 0,Z(t),g?h(g,!1):(s(),Q.clear(),l.dispatchEvent(new Event(O)))},qe;try{n.events&&(qe=await Ue(n.events,(t,a)=>{if(!J){if(t&&(v=t),v==="none"){ve();return}t==="web"&&Xe(),t==="native"&&(Ye(),ne()),a&&me()}}))}catch(t){throw J=!0,ve(),await ce(Promise.resolve(Oe.remove())).catch(()=>{}),t}return{get owners(){return[...R]},get visibleOwners(){return[...m]},get preparedOwners(){return[...p]},consider:Ce,changed:me,moved:()=>{He(),De()},get token(){return U?void 0:g?.data.token},begin:Xe,get restoring(){return U},get handoffPending(){return F},retireRestoration:K,end:Ye,updated:nt,invalidate:ve,async destroy(){J=!0,ve(),await Promise.allSettled([ce(Promise.resolve().then(()=>Oe?.remove())),ce(Promise.resolve().then(()=>qe?.remove()))])}}},Qe=async(e,o,n)=>{let f=await yt(e,o,n),v=f.consider;return Object.assign(f,{consider:(c,l)=>v(c,l.map(w=>({...w,scope:e})))})};var et=(e,o)=>{if(e.nodeType!==1||e.namespaceURI!=="http://www.w3.org/1999/xhtml"||!e.ownerDocument||!("style"in e))throw new TypeError("A Web projection scope requires an HTMLElement fallback");if(e.matches("img, input, textarea, select, video, audio, canvas, iframe, embed, object, br, hr, area, base, link, meta, source, track, wbr"))throw new TypeError("A Web projection fallback must support ::before; use its existing container element");return Object.freeze({element:e,namespace:o})};var tt=async(e,o,n={addListener:async(f,v)=>globalThis.Capacitor.addListener(e.plugin,f,v)})=>{let f="view",v=e.receiveListeners,c=!1,l=!1,w="native",u="native",S=_e(document),j=()=>w==="native"&&d&&S.isCovered(d)?"web":w,M=!1,O=!1,R=!1,m,p,i,T=!1,N=()=>{let s=T;T=!0;try{i?.begin()}finally{T=s}},d,W={},$,A=0,F=()=>{A++,$=void 0;let s=$e(d);s?.owner===W&&(s.projected=!1,s.owner=void 0)},C={listen:s=>o.listen(s),send:s=>{if(!e.placementHandoff)return o.send(s);let h=$e(d);if(s.action==="cover"){let Q=++A;return $=s.token,h&&(h.owner=W,h.projected=!1),o.send(s).then(q=>(Q===A&&h===$e(d)&&h?.owner===W&&(h.projected=q?.shown===!0),q)).catch(q=>{throw Q===A&&h?.owner===W&&(F(),h.invalidate()),q})}let E=s.action==="release"&&s.token===$,K=$&&(s.action==="restore"||E||s.action==="reset"||s.action==="visibility"&&s.visible);K&&A++;let I=A,z=()=>{I!==A||h?.owner!==W||(F(),h.invalidate())};try{let Q=K&&h?.owner===W?{...s,token:"token"in s?s.token:$,placement:h.measure()}:s;return o.send(Q).then(q=>(I===A&&(s.action==="resume"||E||s.action==="reset"||s.action==="visibility")&&F(),q)).catch(q=>{throw z(),q})}catch(Q){return z(),Promise.reject(Q)}}},X=0,ue=0,P=0,g=!1,ie=!1,U=!1,pe=!1,ge=document.createElement("style"),D=`data-native-ui-session-${e.id.replace(/[^a-zA-Z0-9-]/g,"")}`;ge.textContent=`[${D}="native"], [${D}="native"] *, [${D}="none"], [${D}="none"] * {visibility:hidden!important}`,document.documentElement.append(ge);let re=()=>{let s=document.querySelectorAll(e.projectionSelector);return s.length===1&&s[0]instanceof HTMLElement?s[0]:void 0},B=()=>{d&&(u==="web"||u==="native"&&O?d.removeAttribute(D):d.setAttribute(D,u))},Me=()=>{if(!d?.isConnected)return[];let s=d.getBoundingClientRect(),h={x:s.x,y:s.y,width:s.width,height:s.height},E=m&&Ve(d,m,()=>e.id);return[{element:d,scrollElement:m,control:{id:f,...h,...E?{scroll:E}:{}}}]},J=async()=>{if(c)return;let s=P,h=e.failedFallback==="native"&&j()==="native"&&!!d?.isConnected;await C.send({action:"visibility",visible:h}).catch(()=>{}),!(c||s!==P)&&(h||u==="none"?B():d?.removeAttribute(D))},we=()=>{if(!M||u!=="native"||!d?.isConnected||i?.token||i?.restoring)return;let s=e.failedFallback==="native";C.send({action:"visibility",visible:s}).catch(()=>{}),s?B():d.removeAttribute(D)},Y=()=>{if(c||T)return;if(we(),u==="web"&&i&&!i.token&&!i.restoring){C.send({action:"visibility",visible:!1}).catch(()=>{}),d?.removeAttribute(D);return}if(g||!i?.restoring||u!=="native")return;g=!0;let s=P,h=i;C.send({action:"resume"}).then(()=>{c||s!==P||h!==i||(O=!1,h.updated(void 0,[f],++X),B(),_())}).catch(()=>{s!==P||c||(h.invalidate(),J())}).finally(()=>{g=!1,Y()})},be=async()=>{if(c||ie||M)return;if(i?.restoring){Y();return}let s=re();if(s!==d){if(P++,F(),d?.removeAttribute(D),await i?.destroy(),i=void 0,c)return;d=s,O=!1,Ee.disconnect(),d&&Ee.observe(d)}if(ne(),u=j(),!d){await C.send({action:"visibility",visible:!1});return}if(!i){ie=!0;let h=P;try{let E=await Qe(et(d,e.id),C,{wake:Y,captureDelayMs:e.captureDelayMs,blocked:()=>u!=="native"||M||O&&!i?.token,revision:()=>X,captureRevision:e.captureMode==="view"?()=>ue:void 0,transient:!0,opaqueUnderlay:e.captureMode==="view"});if(c||h!==P){await E.destroy();return}i=E}finally{ie=!1}}if(!c){if(u==="native"&&O){await C.send({action:"visibility",visible:!1}),B();return}if(u==="native"){let h=P;if(await C.send({action:"visibility",visible:!0}),c||h!==P||u!=="native")return;B(),m=[...ae(d)].find(I=>{let z=getComputedStyle(I);return/auto|scroll/.test(z.overflowX+z.overflowY)&&(I.scrollHeight>I.clientHeight||I.scrollWidth>I.clientWidth)});let E=m&&Ve(d,m,()=>e.id),K=await C.send({action:"track",viewportWidth:innerWidth,...E?{scroll:E}:{}});if(c||h!==P)return;R=K?.tracked===!0,i.consider(e.captureMode==="view"?ue:++X,Me())}else await C.send({action:"visibility",visible:!1}),B()}},_=()=>{c||(U=!0,!(!l||pe)&&(pe=!0,Promise.resolve().then(async()=>{try{for(;U&&!c;)U=!1,await be()}catch{await J()}finally{pe=!1,U&&_()}})))},se=()=>{i?.invalidate(),_()},Ee=new ResizeObserver(se);window.addEventListener("resize",se);let Z=new Set,Se=new WeakSet,fe=s=>{let h=s.composedPath()[0]??s.target;if(!(c||u!=="native"||!d||Se.has(s)||h!==document&&![...ae(d)].includes(h))){if(Se.add(s),R&&h===m){i?.changed(h);return}M?e.captureMode==="view"?i?.moved():i?.changed():((g||i?.restoring)&&P++,M=!0,N(),O=e.failedFallback!=="native",i?.token?d.removeAttribute(D):we()),clearTimeout(p),p=setTimeout(()=>{M=!1,!(c||u!=="native")&&(i?.end(),i?.restoring||_())},100)}},ne=()=>{let s=new Set(d?ae(d):[]),h=s.size!==Z.size||[...s].some(E=>!Z.has(E));for(let E of Z)s.has(E)||(E.removeEventListener("scroll",fe),E.removeEventListener("slotchange",Pe),Z.delete(E));for(let E of s)Z.has(E)||(E.addEventListener("scroll",fe,{passive:!0}),E.addEventListener("slotchange",Pe),Z.add(E));h&&(R=!1,m=void 0,i?.invalidate(),_())},Pe=()=>{ne(),_()};document.addEventListener("scroll",fe,!0);let Ce=new MutationObserver(s=>{ne(),re()!==d?_():d&&s.some(h=>h.type==="childList")&&me(j())});Ce.observe(document.documentElement,{childList:!0,attributes:!0,subtree:!0});let me=(s,h=!1)=>{if(c||!h&&s===u)return;M=!1,clearTimeout(p),u=s,P++;let E=P;u==="none"?C.send({action:"visibility",visible:!1}).then(()=>{u!=="none"||E!==P||c||(i?.invalidate(),B())}).catch(()=>{u!=="none"||E!==P||c||(i?.invalidate(),B())}):u==="web"?(N(),i?.token||C.send({action:"visibility",visible:!1}).catch(()=>{}),B()):u==="native"&&(i?.end(),i?.restoring||_())},Oe=S.subscribe(()=>queueMicrotask(()=>me(j()))),je,Te=async()=>{if(!c){c=!0,F(),P++,Oe(),Ce.disconnect(),Ee.disconnect(),window.removeEventListener("resize",se),document.removeEventListener("scroll",fe,!0);for(let s of Z)s.removeEventListener("scroll",fe),s.removeEventListener("slotchange",Pe);Z.clear(),clearTimeout(p),d?.removeAttribute(D),ge.remove(),await Promise.allSettled([Promise.resolve(je?.remove()),i?.destroy()])}};try{let s=P;je=await Ue({addListener:(E,K)=>n.addListener(E,K),update:v?.update,projection:v?.projection,accepts:E=>!v?.match||Object.entries(v?.match).every(([K,I])=>typeof E=="object"&&E!==null&&E[K]===I)},(E,K)=>{c||(E&&(E==="native"&&(O=!1),w=E,C.send({action:"destination",destination:E}).catch(()=>{}),me(j(),!0)),K&&(ue++,i?.changed(),u==="native"&&_()))});let h=await C.send({action:"connect"});return P===s&&["native","web","none"].includes(h?.destination)&&(w=h.destination),u=j(),l=!0,_(),{destroy:Te,show:()=>{O=!1,w="native",me(j(),!0)},hide:()=>{w=u="none",P++,M=!1,clearTimeout(p),i?.invalidate(),B()}}}catch(s){throw await Te(),s}};var le=new Map,de=new Map,We=new Map,gt=Array.from(crypto.getRandomValues(new Uint32Array(4)),e=>e.toString(16)).join("-"),Be=new Map,wt=(e,o,n)=>Be.get(e)?.get(o)?.(n),bt=0,Et=(e,o)=>new Promise((n,f)=>{let v=`${e}:${gt}:${++bt}`,c=setTimeout(()=>{de.delete(v),f(new Error("Native UI session timed out"))},5e3);de.set(v,{resolve:n,reject:f,timer:c});let l=JSON.stringify({key:v,request:o}),w=globalThis,u=w.webkit?.messageHandlers?.[e]??w[e]??w.nativeUIEngine;if(!u){clearTimeout(c),de.delete(v),f(new Error("Native UI session unavailable"));return}try{u.postMessage(l)}catch(S){clearTimeout(c),de.delete(v),f(S)}}),Pt=(e,o,n,f)=>{if(o===null){We.get(e)?.(n);return}let v=de.get(o);!v||!o.startsWith(`${e}:`)||(de.delete(o),clearTimeout(v.timer),f?v.reject(new Error(f)):v.resolve(n))},Tt=e=>{le.get(e)?.show()},Lt=e=>(le.get(e)?.hide(),!0),Mt=async e=>{let o=le.get(e);le.delete(e),Be.delete(e),We.delete(e);for(let[n,f]of de)n.startsWith(`${e}:`)&&(de.delete(n),clearTimeout(f.timer),f.reject(new Error("Native UI session closed")));await o?.destroy()},St=async e=>{if(le.has(e.id))return;let o,n=!1,f,v={show:()=>{f="native",o?.show()},hide:()=>{f="none",o?.hide()},destroy:async()=>{n=!0,await o?.destroy()}};le.set(e.id,v);try{if(document.readyState==="loading"&&await new Promise(l=>document.addEventListener("DOMContentLoaded",()=>l(),{once:!0})),n)return;let c=new Map;e.runtime==="cordova"&&Be.set(e.id,c),o=await tt(e,{send:l=>Et(e.id,l),listen:async l=>(We.set(e.id,l),{remove:()=>{We.get(e.id)===l&&We.delete(e.id)}})},e.runtime==="cordova"?{addListener:async(l,w)=>(c.set(l,w),{remove:()=>{c.delete(l)}})}:void 0),f==="none"?o.hide():f==="native"&&o.show(),n&&await o.destroy()}catch(c){throw le.get(e.id)===v&&(le.delete(e.id),Be.delete(e.id)),c}};return dt(jt);})();
13
+ `;var gt=(e,o=!1)=>JSON.stringify(e.map(({control:n})=>[n.id,n.kind,n.placement,o?0:n.x,o?0:n.y,n.width,n.height,n.dark,n.rtl])),Qe=(e,o,n=o.getComputedStyle(e))=>{let p=e.getBoundingClientRect(),v=(M,T)=>{let N=parseFloat(M);return n.display!=="inline"&&Number.isFinite(N)?N+(n.boxSizing==="border-box"?0:T.reduce((O,h)=>O+(parseFloat(n.getPropertyValue(h))||0),0)):NaN},l=v(n.width,["padding-left","padding-right","border-left-width","border-right-width"]),d=v(n.height,["padding-top","padding-bottom","border-top-width","border-bottom-width"]),E=1;if(!(l>0&&d>0))for(let M of[e,...fe(e)]){let T=(M===e?n:o.getComputedStyle(M)).getPropertyValue("zoom");E*=(parseFloat(T)||1)/(T.endsWith("%")?100:1)}let u=l>0?p.width/l:E,S=d>0?p.height/d:E;return{x:p.x,y:p.y,scaleX:u||1,scaleY:S||1,left:(parseFloat(n.borderLeftWidth)||0)-e.scrollLeft,top:(parseFloat(n.borderTopWidth)||0)-e.scrollTop,position:n.position}},yt=async(e,o,n)=>{let p=n.captureDelayMs??50;if(!Number.isFinite(p)||p<0||p>2147483647)throw new RangeError("captureDelayMs must be a finite, nonnegative timer interval");let v="native",l=e.element.ownerDocument,d=l.defaultView,E=Array.from(d.crypto.getRandomValues(new Uint32Array(4)),t=>t.toString(16)).join("-"),u=t=>t.isConnected&&t.ownerDocument===l,S=t=>!!t?.isConnected&&t.ownerDocument===l,M=o.send.bind(o),T="data-native-ui-snapshot",N=`${e.namespace}:sources-change`,O=new Set,h=new Set,c=new Set,y=new WeakMap,a=0,U=t=>JSON.stringify(t.map(s=>(y.has(s.scope)||y.set(s.scope,++a),[y.get(s.scope),gt([s],!!n.opaqueUnderlay)]))),j=new Map,m=t=>{let s=t.getRootNode();return s.nodeType===11&&"host"in s?s:l},A=t=>{let s=j.get(t);if(!s){let g=l.createElement("style"),r=l.createElement("style");g.setAttribute("data-native-ui-snapshot-base",""),r.setAttribute(T,""),g.textContent=Ze,(t.nodeType===11?t:t.head).append(g,r),s={base:g,image:r},j.set(t,s)}return s.image},I=()=>`${d.innerWidth}:${d.innerHeight}:${d.devicePixelRatio}`,F=!1,q=!1,W=0,ve=0,R,k,w,le=!1,z=!1,he=0,Pe=0,$=0,se=0,B=0,xe=-1,X=!1,Te=!1,_,Le=new Set,Z=(t,s,g)=>{let r=()=>{Le.delete(r)&&M({action:t,token:s}).catch(()=>{})};Le.add(r),g&&!X?d.requestAnimationFrame(()=>d.requestAnimationFrame(r)):r()},de=new Set,Me=()=>{de.forEach(t=>c.delete(t)),de.clear()},ee=(t,s=!1)=>{!t||t===R||t===w||(t.images.forEach(g=>{g.src=""}),Z("release",t.data.token,s))},He=t=>t.filter(s=>{if(!S(s.scope.element)||![s.element,...s.sources??[]].every(u))return!1;if(s.control.scroll){let g=s.element.getBoundingClientRect(),r=s.control.scroll.clip;if(g.right<=r.x||g.bottom<=r.y||g.left>=r.x+r.width||g.top>=r.y+r.height)return!1}return n.eligible?.(s)??!0}),ge=async()=>{if(se=0,X||v!=="native"||Te||le||w||n.blocked()||!_||R?.revision===_.revision&&xe===B)return;let{revision:t,candidates:s}=_,g=$,r=B,L=I(),x=He(s);if(!x.length)return;Te=!0;let C,Q=!1,ne=[];try{let oe=!1,be=M({action:"prepare",revision:n.captureRevision?.()??t,viewportWidth:d.innerWidth,ids:x.map(P=>P.control.id)});be.then(P=>{oe&&P.token&&M({action:"release",token:P.token}).catch(()=>{})}).catch(()=>{});try{C=await ce(be)}finally{oe=!0}if(!C.token||!C.parts?.length)return;let Ee=new Set(x.map(P=>P.control.id));if(C.parts.some(P=>!P.url.startsWith("file:")&&!/^\/__native_ui_engine__\/nativeUI[a-fA-F0-9]+\/[a-fA-F0-9-]+-[0-9]+\.png$/.test(P.url)||!P.ids.length||P.ids.some(V=>!Ee.has(V))||![P.x,P.y,P.width,P.height].every(Number.isFinite)||P.width<=0||P.height<=0)||new Set(C.parts.flatMap(P=>P.ids)).size!==Ee.size||C.parts.some(P=>new Set(P.ids.map(V=>x.find(Be=>Be.control.id===V).scope)).size!==1)||(await ce(Promise.all(C.parts.map(async P=>{let V=l.createElement("img");return ne.push(V),V.src=Je(P.url),await V.decode(),V}))),X||v!=="native"||le||w||n.blocked()||$!==g||I()!==L||_?.revision!==t||x.some(P=>!S(P.scope.element)||![P.element,...P.sources??[]].every(u))))return;let je=R;R={data:C,images:ne,sources:Array.from(new Set(x.flatMap(P=>[P.element,...P.sources??[]]))),targets:C.parts.map(P=>x.find(V=>P.ids.includes(V.control.id)).element),scopes:C.parts.map(P=>x.find(V=>P.ids.includes(V.control.id)).scope),parents:C.parts.map(P=>x.find(V=>P.ids.includes(V.control.id)).scope.element.parentNode),frames:[],viewport:L,revision:t,layout:U(x)},Me(),R.sources.forEach(P=>{c.add(P),de.add(P)}),Fe(),n.opaqueUnderlay&&M({action:"prepared",token:C.token}).catch(()=>{}),d.dispatchEvent(new Event(N)),xe=r,Q=!0,ee(je)}catch{}finally{Q||ne.forEach(oe=>{oe.src=""}),C?.token&&!Q&&M({action:"release",token:C.token}).catch(()=>{}),Te=!1,_&&(_.revision!==t||B!==r)&&re()}},re=()=>{d.clearTimeout(se),!X&&v==="native"&&!w&&!le&&!n.blocked()&&(se=d.setTimeout(()=>{ge()},p))},Ae,ke=(t,s)=>{if(s.some(r=>r.scope.element.ownerDocument!==l||[r.element,...r.sources??[]].some(L=>L.ownerDocument!==l)))throw new TypeError("All projection sources and fallback scopes must share one Document");if(n.blocked())return;!w&&R&&R.layout!==U(He(s))&&we();let g=U(s);Ae!==g&&(Ae=g,B++,$++),_?.revision!==t&&$++,_={revision:t,candidates:s},(R?.revision!==t||xe!==B)&&re()},Se=t=>{!w&&R&&t&&_?.candidates.some(s=>s.scrollElement===t&&R.sources.includes(s.element))&&we(),w&&(Y(),ae()),B++,$++,re()},Ce=await Ue(o.listen(({active:t})=>{le=t,$++,d.clearTimeout(se),t||re()})),ue=new Set,We=t=>{ue.delete(t)&&t.removeAttribute("data-native-ui-snapshot-prepared"),t.removeAttribute("data-native-ui-snapshot-visible"),t.removeAttribute(T),t.removeAttribute("data-native-ui-snapshot-source")},ye=()=>{ve++,d.cancelAnimationFrame(W),d.cancelAnimationFrame(J),J=0,G.clear(),W=0,F=!1,q=!1,Me(),w?.sources.forEach(t=>{O.delete(t),h.delete(t)}),D.forEach(We),D.clear(),j.forEach(({base:t,image:s})=>{t.remove(),s.remove()}),j.clear()},te=(t=w,s=z)=>{if(w!==t)return;let g=new Set([w,R]);ye(),w=void 0,R=void 0,z=!1,d.clearTimeout(he),g.forEach(r=>ee(r,s)),n.wake()},i=()=>{if(!w)return;if(R!==w){te();return}let t=w;ye(),w=void 0,z=!1,d.clearTimeout(he),Z("restored",t.data.token,!0),t.sources.forEach(s=>{c.add(s),de.add(s)}),Fe(),n.wake()},f=()=>{z&&i(),Le.forEach(t=>t())},b=t=>t.scopes.map(s=>S(s.element)?s.element:void 0),D=new Set,K=new Map,G=new Map,J=0,Y=()=>{for(let[t,s]of G){if(!t.isConnected)continue;let g=d.getComputedStyle(t),r=`${s.x-(parseFloat(g.borderLeftWidth)||0)+t.scrollLeft}px`,L=`${s.y-(parseFloat(g.borderTopWidth)||0)+t.scrollTop}px`;s.style.left!==r&&s.style.setProperty("left",r,"important"),s.style.top!==L&&s.style.setProperty("top",L,"important")}},ae=()=>{if(J||!w)return;[...G.keys()].some(s=>s.getAnimations?.().some(g=>g.playState==="running"&&g.effect?.getKeyframes().some(r=>["borderLeftWidth","borderTopWidth","borderWidth"].some(L=>L in r))))&&(J=d.requestAnimationFrame(()=>{J=0,Y(),ae()}))},Fe=()=>{let t=w??R;if(!t||z)return;if(!t.sources.every(u)||b(t).some(r=>!r)){we();return}let s=b(t);if(w){if(s.some(L=>!L?.isConnected||!j.has(m(L)))){te(w,!1);return}let r=new Set;for(let[L,x]of t.targets.entries()){let C=K.get(x),Q=s[L];!C||!Q?.isConnected||(Q.setAttribute(T,C),Q.setAttribute("data-native-ui-snapshot-source",""),Q.toggleAttribute("data-native-ui-snapshot-visible",q),r.add(Q))}for(let L of D)r.has(L)||We(L);D.clear(),r.forEach(L=>D.add(L));return}K.clear(),G.clear(),D.forEach(We),D.clear();let g=new Map;for(let r of new Set(s)){if(!r)continue;D.add(r);let L=A(m(r)),x=g.get(L);x||(x={rules:[],positions:[]},g.set(L,x));let{rules:C,positions:Q}=x;r!==t.targets[s.indexOf(r)]&&!r.hasAttribute("data-native-ui-snapshot-prepared")&&(r.setAttribute("data-native-ui-snapshot-prepared",""),ue.add(r)),r.toggleAttribute("data-native-ui-snapshot-visible",!!w||!!n.opaqueUnderlay),w&&r.setAttribute("data-native-ui-snapshot-source","");let ne=s.flatMap((H,ie)=>H===r?[ie]:[]),oe=d.getComputedStyle(r);{let H=Qe(r,d,oe);ne.forEach(ie=>{let Ne=t.data.parts[ie];n.opaqueUnderlay&&t.frames[ie]||(t.frames[ie]={x:n.opaqueUnderlay?0:(Ne.x-H.x)/H.scaleX,y:n.opaqueUnderlay?0:(Ne.y-H.y)/H.scaleY,width:Ne.width/H.scaleX,height:Ne.height/H.scaleY})})}let be=ne.map(H=>t.frames[H]),Ee=Math.min(...be.map(H=>H.x)),je=Math.min(...be.map(H=>H.y)),P=Math.max(...be.map(H=>H.x+H.width)),V=Math.max(...be.map(H=>H.y+H.height)),Be=(parseFloat(oe.borderLeftWidth)||0)-r.scrollLeft,rt=(parseFloat(oe.borderTopWidth)||0)-r.scrollTop,$e=`${E}-${ne[0]}`;ne.forEach(H=>K.set(t.targets[H],$e)),r.setAttribute(T,$e),C.push(`[${T}="${$e}"][data-native-ui-snapshot-visible] { ${oe.position==="static"?"position:relative!important;":""} }`);let it=ne.map(H=>{let ie=t.frames[H];return`url(${JSON.stringify(t.images[H].src)}) ${ie.x-Ee}px ${ie.y-je}px / ${ie.width}px ${ie.height}px no-repeat`}),Oe=l.createElement("span").style;Oe.cssText=`left:${Ee-Be}px;top:${je-rt}px;width:${P-Ee}px;height:${V-je}px;background:${it.join(",")};`;for(let H of Array.from(Oe))Oe.setProperty(H,Oe.getPropertyValue(H),"important");C.push(`[${T}="${$e}"][${T}][${T}]::before { ${Oe.cssText} }`),Q.push({target:r,x:Ee,y:je})}t.parents=t.scopes.map(r=>r.element.parentNode);for(let[r,{rules:L,positions:x}]of g)r.textContent=L.join(`
14
+ `),x.forEach(({target:C,x:Q,y:ne},oe)=>G.set(C,{x:Q,y:ne,style:r.sheet.cssRules[oe*2+1].style}))},Ye=()=>{if(f(),X||v==="none"||w)return;let t=R;if(!t||t.viewport!==I()||!t.sources.every(u)||n.current&&!n.current(t.targets)||t.targets.some(r=>!K.has(r))||b(t).some(r=>!r?.isConnected)||[...D].some(r=>!r.isConnected))return;t.scopes.some((r,L)=>r.element.parentNode!==t.parents[L])&&Fe(),Y();let s=++ve,g=()=>ve===s;F=!0,q=!!n.opaqueUnderlay,w=t,$++,d.clearTimeout(se),t.sources.forEach(r=>{O.add(r)}),D.forEach(r=>{r.setAttribute("data-native-ui-snapshot-source",""),r.toggleAttribute("data-native-ui-snapshot-visible",q)}),W=d.requestAnimationFrame(()=>{W=0,g()&&(F=!1,n.wake())}),ce(M({action:"cover",token:t.data.token,...n.transient?{transient:!0}:{}})).then(r=>{if(g()){if(F=!1,r?.shown!==!0){te(t,!1);return}z||(q=!0,t.sources.forEach(L=>h.add(L)),Fe(),Y(),ae()),d.cancelAnimationFrame(W),W=d.requestAnimationFrame(()=>{W=0,g()&&M({action:"painted",token:t.data.token}).catch(()=>{g()&&te(t,!1)})}),n.wake()}}).catch(()=>{g()&&te(t,!1)})},qe=()=>{if(!(!w||X||z)){if(!w.sources.every(u)||b(w).some(t=>!t)||n.current&&!n.current(w.targets)){te(w,!1);return}if(!n.blocked()){if(n.transient){let t=w,s=b(t);if(s.some(r=>!r?.isConnected)){te(t,!1);return}let g=s.map((r,L)=>{let x=Qe(r,d),C=t.frames[L];return{...t.data.parts[L],x:x.x+C.x*x.scaleX,y:x.y+C.y*x.scaleY,width:C.width*x.scaleX,height:C.height*x.scaleY}});g.some((r,L)=>["x","y","width","height"].some(x=>r[x]!==t.data.parts[L][x]))&&(n.opaqueUnderlay?t.data={...t.data,parts:g}:R=void 0),M({action:"restore",token:t.data.token,viewportWidth:d.innerWidth,parts:g}).catch(()=>{})}Pe=n.revision?.()??_?.revision??0,z=!0,w.sources.forEach(t=>O.delete(t)),he=d.setTimeout(()=>{z&&te(w,!1)},250),n.wake()}}},ot=(t,s,g)=>{!z||!w||g===void 0||g<=Pe||!n.transient&&t||w.data.parts.every(r=>r.ids.every(L=>s?.includes(L)))&&(i(),re())},we=(t=!1)=>{Le.forEach(g=>g()),$++,d.clearTimeout(se);let s=R;if(R=void 0,(t||X)&&(k=void 0),n.retainPreparedUntilRelease&&!w&&!t&&!X){if(s){let g={};k=g,ce(M({action:"release",token:s.data.token})).catch(()=>{}).finally(()=>{if(s.images.forEach(r=>{r.src=""}),k===g&&(k=void 0,!(R||w))){for(let r of D)r.getAttribute(T)!==K.get(r)&&(D.delete(r),ue.delete(r));ye(),K.clear(),d.dispatchEvent(new Event(N))}})}if(k)return}ee(s),w?te(w,!1):(ye(),K.clear(),d.dispatchEvent(new Event(N)))},_e;try{n.events&&(_e=await Ie(n.events,(t,s)=>{if(!X){if(t&&(v=t),v==="none"){we();return}t==="web"&&Ye(),t==="native"&&(qe(),re()),s&&Se()}}))}catch(t){throw X=!0,we(),await ce(Promise.resolve(Ce.remove())).catch(()=>{}),t}return{get owners(){return[...O]},get visibleOwners(){return[...h]},get preparedOwners(){return[...c]},consider:ke,changed:Se,moved:()=>{Y(),ae()},get token(){return z?void 0:w?.data.token},begin:Ye,get restoring(){return z},get handoffPending(){return F},retireRestoration:f,end:qe,updated:ot,invalidate:we,async destroy(){X=!0,we(),await Promise.allSettled([ce(Promise.resolve().then(()=>Ce?.remove())),ce(Promise.resolve().then(()=>_e?.remove()))])}}},et=async(e,o,n)=>{let p=await yt(e,o,n),v=p.consider;return Object.assign(p,{consider:(l,d)=>v(l,d.map(E=>({...E,scope:e})))})};var tt=(e,o)=>{if(e.nodeType!==1||e.namespaceURI!=="http://www.w3.org/1999/xhtml"||!e.ownerDocument||!("style"in e))throw new TypeError("A Web projection scope requires an HTMLElement fallback");if(e.matches("img, input, textarea, select, video, audio, canvas, iframe, embed, object, br, hr, area, base, link, meta, source, track, wbr"))throw new TypeError("A Web projection fallback must support ::before; use its existing container element");return Object.freeze({element:e,namespace:o})};var nt=async(e,o,n={addListener:async(p,v)=>globalThis.Capacitor.addListener(e.plugin,p,v)})=>{let p="view",v=e.receiveListeners,l=!1,d=!1,E="native",u="native",S=Ke(document),M=()=>E==="native"&&m&&S.isCovered(m)?"web":E,T=!1,N=!1,O=!1,h=!1,c,y,a,U=!1,j=()=>{let i=U;U=!0;try{a?.begin()}finally{U=i}},m,A={},I,F=0,q=()=>{F++,I=void 0;let i=De(m);i?.owner===A&&(i.projected=!1,i.owner=void 0)},W={listen:i=>o.listen(f=>{i(f),!f.active&&N&&(N=!1,re()),f.precovered&&Ae(f.precovered)}),send:i=>{if(!e.placementHandoff)return o.send(i);let f=De(m);if(i.action==="cover"){let J=I===i.token&&f?.owner===A&&f.projected,Y=++F;return I=i.token,f&&(f.owner=A,f.projected=!!J),o.send(i).then(ae=>(Y===F&&f===De(m)&&f?.owner===A&&(f.projected=ae?.shown===!0),ae)).catch(ae=>{throw Y===F&&f?.owner===A&&(q(),f.invalidate()),ae})}let b=i.action==="release"&&i.token===I,D=I&&(i.action==="restore"||b||i.action==="reset"||i.action==="visibility"&&i.visible);D&&F++;let K=F,G=()=>{K!==F||f?.owner!==A||(q(),f.invalidate())};try{let J=D&&f?.owner===A?{...i,token:"token"in i?i.token:I,placement:f.measure()}:i;return o.send(J).then(async Y=>(i.action==="release"&&Y?.requiresPlacement&&(f&&(!f.owner||f.owner===A)?Y=await o.send({...i,placement:f.measure()}):(await o.send({action:"visibility",visible:!1}),Y=await o.send(i))),K===F&&(i.action==="resume"||b||i.action==="reset"||i.action==="visibility")&&q(),Y)).catch(Y=>{throw G(),Y})}catch(J){return G(),Promise.reject(J)}}},ve=0,R=0,k=0,w=!1,le=!1,z=!1,he=!1,Pe=document.createElement("style"),$=`data-native-ui-session-${e.id.replace(/[^a-zA-Z0-9-]/g,"")}`;Pe.textContent=`[${$}="native"], [${$}="native"] *, [${$}="none"], [${$}="none"] * {visibility:hidden!important}`,document.documentElement.append(Pe);let se=()=>{let i=document.querySelectorAll(e.projectionSelector);return i.length===1&&i[0]instanceof HTMLElement?i[0]:void 0},B=()=>{m&&(u==="web"||u==="native"&&O?m.removeAttribute($):m.setAttribute($,u))},xe=()=>{if(!m?.isConnected)return[];let i=m.getBoundingClientRect(),f={x:i.x,y:i.y,width:i.width,height:i.height},b=c&&Xe(m,c,()=>e.id);return[{element:m,scrollElement:c,control:{id:p,...f,...b?{scroll:b}:{}}}]},X=async()=>{if(l)return;let i=k,f=e.failedFallback==="native"&&M()==="native"&&!!m?.isConnected;await W.send({action:"visibility",visible:f}).catch(()=>{}),!(l||i!==k)&&(f||u==="none"?B():m?.removeAttribute($))},Te=()=>{if(!T||u!=="native"||!m?.isConnected||a?.token||a?.restoring)return;let i=e.failedFallback==="native";W.send({action:"visibility",visible:i}).catch(()=>{}),i?B():m.removeAttribute($)},_=()=>{if(l||U)return;if(Te(),u==="web"&&a&&!a.token&&!a.restoring){W.send({action:"visibility",visible:!1}).catch(()=>{}),m?.removeAttribute($);return}if(w||!a?.restoring||u!=="native")return;w=!0;let i=k,f=a;W.send({action:"resume"}).then(()=>{l||i!==k||f!==a||(O=!1,f.updated(void 0,[p],++ve),B(),Z())}).catch(()=>{i!==k||l||(f.invalidate(),X())}).finally(()=>{w=!1,_()})},Le=async()=>{if(l||le||T)return;if(a?.restoring){_();return}let i=se();if(i!==m){if(k++,q(),m?.removeAttribute($),await a?.destroy(),a=void 0,l)return;m=i,O=!1,Me.disconnect(),m&&Me.observe(m)}if(ke(),u=M(),!m){await W.send({action:"visibility",visible:!1});return}if(!a){le=!0;let f=k;try{let b=await et(tt(m,e.id),W,{wake:_,captureDelayMs:e.captureDelayMs,retainPreparedUntilRelease:e.nativeDragHandoff,blocked:()=>u!=="native"||T||O&&!a?.token,revision:()=>ve,captureRevision:e.captureMode==="view"?()=>R:void 0,transient:!0,opaqueUnderlay:e.captureMode==="view"});if(l||f!==k){await b.destroy();return}a=b}finally{le=!1}}if(!l){if(u==="native"&&O){await W.send({action:"visibility",visible:!1}),B();return}if(u==="native"){let f=k;if(await W.send({action:"visibility",visible:!0}),l||f!==k||u!=="native")return;B(),c=[...fe(m)].find(G=>{let J=getComputedStyle(G);return/auto|scroll/.test(J.overflowX+J.overflowY)&&(G.scrollHeight>G.clientHeight||G.scrollWidth>G.clientWidth)});let b=c&&Xe(m,c,()=>e.id),D=e.nativeDragHandoff&&e.captureMode==="view"&&c?{viewport:c.getBoundingClientRect(),offsetX:c.scrollLeft,offsetY:c.scrollTop,rangeX:Math.max(0,c.scrollWidth-c.clientWidth),rangeY:Math.max(0,c.scrollHeight-c.clientHeight)}:void 0,K=await W.send({action:"track",viewportWidth:innerWidth,...b?{scroll:b}:{},...D?{gesture:D}:{}});if(l||f!==k)return;h=K?.tracked===!0,a.consider(e.captureMode==="view"?R:++ve,xe())}else await W.send({action:"visibility",visible:!1}),B()}},Z=()=>{l||(z=!0,!(!d||he)&&(he=!0,Promise.resolve().then(async()=>{try{for(;z&&!l;)z=!1,await Le()}catch{await X()}finally{he=!1,z&&Z()}})))},de=()=>{a?.invalidate(),Z()},Me=new ResizeObserver(de);window.addEventListener("resize",de);let ee=new Set,He=new WeakSet,ge=i=>{let f=i.composedPath()[0]??i.target;if(!(l||u!=="native"||!m||He.has(i)||f!==document&&![...fe(m)].includes(f))){if(He.add(i),h&&f===c){a?.changed(f);return}T?e.captureMode==="view"?a?.moved():a?.changed():((w||a?.restoring)&&k++,T=!0,j(),O=e.failedFallback!=="native",a?.token?m.removeAttribute($):Te()),re()}};function re(){clearTimeout(y),y=setTimeout(()=>{N||(T=!1,!(l||u!=="native")&&(a?.end(),a?.restoring||Z()))},100)}function Ae(i){if(l||u!=="native"||!m?.isConnected||!a||(N=!0,a.token))return;(w||a.restoring)&&k++,I=i;let f=De(m);if(f&&(f.owner=A,f.projected=!0),T=!0,j(),!a.token){T=!1,X();return}re()}let ke=()=>{let i=new Set(m?fe(m):[]),f=i.size!==ee.size||[...i].some(b=>!ee.has(b));for(let b of ee)i.has(b)||(b.removeEventListener("scroll",ge),b.removeEventListener("slotchange",Se),ee.delete(b));for(let b of i)ee.has(b)||(b.addEventListener("scroll",ge,{passive:!0}),b.addEventListener("slotchange",Se),ee.add(b));f&&(h=!1,c=void 0,a?.invalidate(),Z())},Se=()=>{ke(),Z()};document.addEventListener("scroll",ge,!0);let Ce=new MutationObserver(i=>{ke(),se()!==m?Z():m&&i.some(f=>f.type==="childList")&&ue(M())});Ce.observe(document.documentElement,{childList:!0,attributes:!0,subtree:!0});let ue=(i,f=!1)=>{if(l||!f&&i===u)return;T=!1,N=!1,clearTimeout(y),u=i,k++;let b=k;u==="none"?W.send({action:"visibility",visible:!1}).then(()=>{u!=="none"||b!==k||l||(a?.invalidate(!0),B())}).catch(()=>{u!=="none"||b!==k||l||(a?.invalidate(!0),B())}):u==="web"?(j(),a?.token||W.send({action:"visibility",visible:!1}).catch(()=>{}),B()):u==="native"&&(a?.end(),a?.restoring||Z())},We=S.subscribe(()=>queueMicrotask(()=>ue(M()))),ye,te=async()=>{if(!l){l=!0,q(),k++,We(),Ce.disconnect(),Me.disconnect(),window.removeEventListener("resize",de),document.removeEventListener("scroll",ge,!0);for(let i of ee)i.removeEventListener("scroll",ge),i.removeEventListener("slotchange",Se);ee.clear(),clearTimeout(y),m?.removeAttribute($),Pe.remove(),await Promise.allSettled([Promise.resolve(ye?.remove()),a?.destroy()])}};try{let i=k;ye=await Ie({addListener:(b,D)=>n.addListener(b,D),update:v?.update,projection:v?.projection,accepts:b=>!v?.match||Object.entries(v?.match).every(([D,K])=>typeof b=="object"&&b!==null&&b[D]===K)},(b,D)=>{l||(b&&(b==="native"&&(O=!1),E=b,W.send({action:"destination",destination:b}).catch(()=>{}),ue(M(),!0)),D&&(R++,a?.changed(),u==="native"&&Z()))});let f=await W.send({action:"connect"});return k===i&&["native","web","none"].includes(f?.destination)&&(E=f.destination),u=M(),d=!0,Z(),{destroy:te,show:()=>{O=!1,E="native",ue(M(),!0)},hide:()=>{E=u="none",k++,T=!1,N=!1,clearTimeout(y),a?.invalidate(!0),B()}}}catch(i){throw await te(),i}};var pe=new Map,me=new Map,Re=new Map,wt=Array.from(crypto.getRandomValues(new Uint32Array(4)),e=>e.toString(16)).join("-"),ze=new Map,bt=(e,o,n)=>ze.get(e)?.get(o)?.(n),Et=0,Pt=(e,o)=>new Promise((n,p)=>{let v=`${e}:${wt}:${++Et}`,l=setTimeout(()=>{me.delete(v),p(new Error("Native UI session timed out"))},5e3);me.set(v,{resolve:n,reject:p,timer:l});let d=JSON.stringify({key:v,request:o}),E=globalThis,u=E.webkit?.messageHandlers?.[e]??E[e]??E.nativeUIEngine;if(!u){clearTimeout(l),me.delete(v),p(new Error("Native UI session unavailable"));return}try{u.postMessage(d)}catch(S){clearTimeout(l),me.delete(v),p(S)}}),Tt=(e,o,n,p)=>{if(o===null){Re.get(e)?.(n);return}let v=me.get(o);!v||!o.startsWith(`${e}:`)||(me.delete(o),clearTimeout(v.timer),p?v.reject(new Error(p)):v.resolve(n))},Lt=e=>{pe.get(e)?.show()},Mt=e=>(pe.get(e)?.hide(),!0),St=async e=>{let o=pe.get(e);pe.delete(e),ze.delete(e),Re.delete(e);for(let[n,p]of me)n.startsWith(`${e}:`)&&(me.delete(n),clearTimeout(p.timer),p.reject(new Error("Native UI session closed")));await o?.destroy()},jt=async e=>{if(pe.has(e.id))return;let o,n=!1,p,v={show:()=>{p="native",o?.show()},hide:()=>{p="none",o?.hide()},destroy:async()=>{n=!0,await o?.destroy()}};pe.set(e.id,v);try{if(document.readyState==="loading"&&await new Promise(d=>document.addEventListener("DOMContentLoaded",()=>d(),{once:!0})),n)return;let l=new Map;e.runtime==="cordova"&&ze.set(e.id,l),o=await nt(e,{send:d=>Pt(e.id,d),listen:async d=>(Re.set(e.id,d),{remove:()=>{Re.get(e.id)===d&&Re.delete(e.id)}})},e.runtime==="cordova"?{addListener:async(d,E)=>(l.set(d,E),{remove:()=>{l.delete(d)}})}:void 0),p==="none"?o.hide():p==="native"&&o.show(),n&&await o.destroy()}catch(l){throw pe.get(e.id)===v&&(pe.delete(e.id),ze.delete(e.id)),l}};return ut(xt);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webview-native-ui/engine",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Native UI placement, scroll tracking, and Web projection for Capacitor and Cordova plugin maintainers.",
5
5
  "type": "module",
6
6
  "license": "MIT",