@webview-native-ui/engine 0.0.5 → 0.0.7

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 (47) hide show
  1. package/README.md +19 -12
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +3 -0
  4. package/dist/native/session.d.ts +5 -0
  5. package/dist/native/session.js +83 -14
  6. package/dist/native/updates.d.ts +16 -0
  7. package/dist/native/updates.js +108 -0
  8. package/dist/native/values.d.ts +5 -0
  9. package/dist/native/values.js +27 -0
  10. package/dist/projection/surface.d.ts +35 -0
  11. package/dist/projection/surface.js +81 -0
  12. package/dist/projection/surfaces.d.ts +20 -0
  13. package/dist/projection/surfaces.js +116 -0
  14. package/dist/projection/web.js +22 -12
  15. package/dist/types.d.ts +4 -0
  16. package/docs/README.md +70 -0
  17. package/docs/advanced/README.md +20 -0
  18. package/docs/advanced/android-placement.md +48 -0
  19. package/docs/advanced/native-handoff.md +89 -0
  20. package/docs/advanced/renderer.md +148 -0
  21. package/docs/advanced/update-queue.md +58 -0
  22. package/docs/advanced/web-projection.md +88 -0
  23. package/docs/assets/ios-native-capture-comparison.png +0 -0
  24. package/docs/concepts.md +97 -0
  25. package/docs/configuration/README.md +29 -0
  26. package/docs/configuration/capture.md +27 -0
  27. package/docs/configuration/events.md +35 -0
  28. package/docs/configuration/ionic-overlays.md +65 -0
  29. package/docs/configuration/values.md +125 -0
  30. package/docs/cordova.md +1 -1
  31. package/docs/{capacitor-plugin.md → getting-started/capacitor.md} +45 -47
  32. package/docs/{cordova-plugin.md → getting-started/cordova.md} +32 -15
  33. package/docs/getting-started/initialization.md +79 -0
  34. package/docs/maintenance.md +14 -2
  35. package/docs/{projection-containers.md → reference/containers.md} +6 -2
  36. package/docs/reference/platforms.md +137 -0
  37. package/docs/reference/projection.md +134 -0
  38. package/docs/reference/state.md +39 -0
  39. package/ios/Sources/WebViewNativeUIEngine/ClippedView.swift +12 -12
  40. package/ios/Sources/WebViewNativeUIEngine/CoveredSnapshots.swift +70 -9
  41. package/ios/Sources/WebViewNativeUIEngine/Frame.swift +10 -10
  42. package/ios/Sources/WebViewNativeUIEngine/NativeUIProjection.swift +92 -44
  43. package/ios/Sources/WebViewNativeUIEngine/Resources/native-ui-engine.js +3 -3
  44. package/ios/Sources/WebViewNativeUIEngine/ScrollBindings.swift +3 -0
  45. package/ios/Sources/WebViewNativeUIEngine/ScrollTracker.swift +10 -8
  46. package/package.json +10 -3
  47. package/docs/projection.md +0 -364
@@ -7,6 +7,16 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
7
7
  public enum Runtime: String { case capacitor, cordova }
8
8
  public enum FailedFallback: String { case web, native }
9
9
 
10
+ public struct ValueBinding {
11
+ public let property: String?
12
+ public init(property: String? = nil) { self.property = property }
13
+ fileprivate var configuration: [String: Any] {
14
+ var value: [String: Any] = [:]
15
+ if let property { value["property"] = property }
16
+ return value
17
+ }
18
+ }
19
+
10
20
  public struct ReceiveListeners {
11
21
  public struct Projection {
12
22
  public let native: [String]
@@ -30,11 +40,23 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
30
40
  private let views: () -> [String: UIView]
31
41
  private let snapshots: NativeUICoveredSnapshots
32
42
  private let scrolling = NativeUIScrollBindings()
33
- private var masks: [String: CAShapeLayer] = [:]
43
+ private struct ScrollMask {
44
+ weak var view: UIView?
45
+ let layer: CAShapeLayer
46
+ }
47
+ private var masks: [String: ScrollMask] = [:]
48
+ private func resetScrolling() {
49
+ scrolling.reset()
50
+ for mask in masks.values where mask.view?.layer.mask === mask.layer {
51
+ mask.view?.layer.mask = nil
52
+ }
53
+ masks.removeAll()
54
+ }
34
55
  private let id = "nativeUI" + UUID().uuidString.replacingOccurrences(of: "-", with: "")
35
56
  private let origins: Set<String>
36
57
  private let allowedOriginsJSON: String
37
58
  private let canCapture: () -> Bool
59
+ private let readValues: (() -> [String: Any])?
38
60
  private let runtime: Runtime
39
61
  private let receiveListeners: ReceiveListeners
40
62
  private let notificationLock = NSLock()
@@ -56,34 +78,44 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
56
78
  private var captured: [String: [String: UIView]] = [:]
57
79
 
58
80
  public convenience init(webView: WKWebView?, plugin: String, projectionSelector: String,
59
- receiveListeners: ReceiveListeners = .init(),
60
- allowedOrigins: Set<String> = ["capacitor://localhost", "app://localhost", "https://localhost", "http://localhost"],
61
- failedFallback: FailedFallback = .web, runtime: Runtime = .capacitor,
62
- captureDelayMs: Double = 50,
63
- canCapture: @escaping () -> Bool = { true }, view: @escaping () -> UIView?) throws {
81
+ receiveListeners: ReceiveListeners = .init(),
82
+ allowedOrigins: Set<String> = ["capacitor://localhost", "app://localhost", "https://localhost", "http://localhost"],
83
+ failedFallback: FailedFallback = .web, runtime: Runtime = .capacitor,
84
+ captureDelayMs: Double = 50,
85
+ values: [String: ValueBinding] = [:], readValues: (() -> [String: Any])? = nil,
86
+ canCapture: @escaping () -> Bool = { true }, view: @escaping () -> UIView?) throws {
64
87
  try self.init(webView: webView, plugin: plugin, projectionSelector: projectionSelector,
65
88
  receiveListeners: receiveListeners, allowedOrigins: allowedOrigins, failedFallback: failedFallback, runtime: runtime,
66
- captureDelayMs: captureDelayMs, canCapture: canCapture, snapshots: NativeUICoveredSnapshots(), view: view)
89
+ captureDelayMs: captureDelayMs, values: values, readValues: readValues, canCapture: canCapture,
90
+ snapshots: NativeUICoveredSnapshots(), view: view)
67
91
  }
68
92
  init(webView: WKWebView?, plugin: String, projectionSelector: String,
69
93
  receiveListeners: ReceiveListeners = .init(),
70
94
  allowedOrigins: Set<String> = ["capacitor://localhost", "app://localhost", "https://localhost", "http://localhost"],
71
95
  failedFallback: FailedFallback = .web, runtime: Runtime = .capacitor,
72
96
  captureDelayMs: Double = 50,
97
+ values: [String: ValueBinding] = [:], readValues: (() -> [String: Any])? = nil,
73
98
  canCapture: @escaping () -> Bool = { true }, snapshots: NativeUICoveredSnapshots,
74
99
  view: @escaping () -> UIView?) throws {
100
+ // JSONSerialization produces valid UTF-8.
101
+ // swiftlint:disable:next force_unwrapping
75
102
  self.allowedOriginsJSON = String(data: try JSONSerialization.data(withJSONObject: Array(allowedOrigins)), encoding: .utf8)!
76
103
  self.runtime = runtime
77
104
  self.snapshots = snapshots
78
105
  self.receiveListeners = receiveListeners
106
+ self.readValues = readValues
79
107
  guard let webView else { throw ProjectionError.missingWebView }
80
108
  self.webView = webView; self.views = { view().map { ["view": $0] } ?? [:] }; self.origins = allowedOrigins; self.canCapture = canCapture
81
109
  super.init()
82
110
  precondition(Thread.isMainThread)
83
111
  guard captureDelayMs.isFinite, captureDelayMs >= 0, captureDelayMs <= 2147483647,
84
112
  !projectionSelector.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { throw ProjectionError.invalidConfiguration }
85
- let config: [String: Any] = ["id": id, "plugin": plugin, "projectionSelector": projectionSelector,
86
- "captureDelayMs": captureDelayMs, "receiveListeners": receiveListeners.configuration, "failedFallback": failedFallback.rawValue, "runtime": runtime.rawValue]
113
+ var config: [String: Any] = ["id": id, "plugin": plugin, "projectionSelector": projectionSelector,
114
+ "captureDelayMs": captureDelayMs, "receiveListeners": receiveListeners.configuration,
115
+ "failedFallback": failedFallback.rawValue, "runtime": runtime.rawValue]
116
+ if readValues != nil { config["values"] = values.mapValues { $0.configuration } }
117
+ // JSONSerialization produces valid UTF-8.
118
+ // swiftlint:disable:next force_unwrapping
87
119
  let json = String(data: try JSONSerialization.data(withJSONObject: config), encoding: .utf8)!
88
120
  let allowed = allowedOriginsJSON
89
121
  let runtime = try Self.runtime()
@@ -99,8 +131,8 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
99
131
  public func receive(_ eventName: String, data: [String: Any]? = nil) {
100
132
  guard receiveListeners.match.allSatisfy({ key, expected in
101
133
  guard let actual = data?[key] else { return false }
102
- if let a = actual as? NSNumber, let b = expected as? NSNumber {
103
- return CFGetTypeID(a) == CFGetTypeID(b) && a == b
134
+ if let actualNumber = actual as? NSNumber, let expectedNumber = expected as? NSNumber {
135
+ return CFGetTypeID(actualNumber) == CFGetTypeID(expectedNumber) && actualNumber == expectedNumber
104
136
  }
105
137
  return (actual as? String).map { $0 == expected as? String } ?? false
106
138
  }) else { return }
@@ -145,13 +177,17 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
145
177
  let key = message["key"] as? String, key.hasPrefix(id + ":"),
146
178
  let request = message["request"] as? [String: Any] else { return }
147
179
  let page = key.split(separator: ":").dropFirst().first.map(String.init)
148
- if documentID != page { snapshots.reset(); scrolling.reset(); captured.removeAll(); anchors.removeAll(); documentID = page }
180
+ if documentID != page { snapshots.reset(); resetScrolling(); captured.removeAll(); anchors.removeAll(); documentID = page }
149
181
  let complete: ([String: Any]) -> Void = { [weak self] value in
150
182
  guard let self, !self.closed, self.documentID == page else { return }
151
183
  self.reply(key, value)
152
184
  }
153
185
  let token = request["token"] as? String ?? ""
154
186
  switch request["action"] as? String {
187
+ case "values":
188
+ let values = readValues?() ?? [:]
189
+ guard JSONSerialization.isValidJSONObject(values) else { self.reply(key, [:], error: "Native values must be JSON values"); return }
190
+ complete(["values": values])
155
191
  case "connect":
156
192
  if !connected { destination = (views().values.contains(where: { !$0.isHidden }) ? "native" : "none"); connected = true }
157
193
  complete(["destination": recordedDestination ?? destination])
@@ -159,9 +195,7 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
159
195
  if let next = request["destination"] as? String, ["native", "web", "none"].contains(next) { destination = next; connected = true }
160
196
  complete([:])
161
197
  case "track":
162
- scrolling.reset()
163
- for (id, mask) in masks { if views()[id]?.layer.mask === mask { views()[id]?.layer.mask = nil } }
164
- masks.removeAll()
198
+ resetScrolling()
165
199
  guard let raw = request["scroll"] as? [String: Any], let width = request["viewportWidth"] as? Double, width.isFinite, width > 0,
166
200
  let data = try? JSONSerialization.data(withJSONObject: raw),
167
201
  let placement = try? JSONDecoder().decode(NativeUIScrollPlacement.self, from: data), placement.isValid else {
@@ -172,11 +206,19 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
172
206
  guard !current.isEmpty, current.values.allSatisfy({ $0.layer.mask == nil }) else { complete(["tracked": false]); return }
173
207
  let nodes = current.map { id, view -> NativeUIScrollNode in
174
208
  let frame = view.convert(view.bounds, to: webView)
175
- return NativeUIScrollNode(id: id, scroll: NativeUIScrollPlacement(id: placement.id,
176
- offsetX: placement.offsetX, offsetY: placement.offsetY, viewport: placement.viewport, clip: placement.clip,
177
- contentWidth: placement.contentWidth, contentHeight: placement.contentHeight,
178
- frame: NativeUIFrame(x: frame.minX/scale-placement.viewport.x+placement.offsetX,
179
- y: frame.minY/scale-placement.viewport.y+placement.offsetY, width: frame.width/scale, height: frame.height/scale)))
209
+ return NativeUIScrollNode(
210
+ id: id,
211
+ scroll: NativeUIScrollPlacement(
212
+ id: placement.id, offsetX: placement.offsetX, offsetY: placement.offsetY,
213
+ viewport: placement.viewport, clip: placement.clip,
214
+ contentWidth: placement.contentWidth, contentHeight: placement.contentHeight,
215
+ frame: NativeUIFrame(
216
+ x: frame.minX/scale-placement.viewport.x+placement.offsetX,
217
+ y: frame.minY/scale-placement.viewport.y+placement.offsetY,
218
+ width: frame.width/scale, height: frame.height/scale
219
+ )
220
+ )
221
+ )
180
222
  }
181
223
  let rejected = scrolling.replace(nodes, in: webView, scale: scale) { [weak self, weak webView] id, frame, clip in
182
224
  guard let self, let webView, self.visible, !self.snapshots.hidesNativeControls,
@@ -185,15 +227,16 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
185
227
  let from = parent.convert(CGPoint(x: old.midX, y: old.midY), from: webView)
186
228
  let to = parent.convert(CGPoint(x: frame.midX, y: frame.midY), from: webView)
187
229
  view.transform.tx += to.x-from.x; view.transform.ty += to.y-from.y
188
- let mask = self.masks[id] ?? CAShapeLayer()
189
- self.masks[id] = mask
230
+ let mask = self.masks[id]?.layer ?? CAShapeLayer()
231
+ self.masks[id] = ScrollMask(view: view, layer: mask)
190
232
  mask.frame = view.bounds
191
233
  mask.path = UIBezierPath(rect: clip.isNull ? .zero : view.convert(clip, from: webView).intersection(view.bounds)).cgPath
192
234
  view.layer.mask = mask
193
235
  }
194
236
  complete(["tracked": rejected.isEmpty])
195
237
  case "prepare":
196
- guard visible, canCapture(), let ids = request["ids"] as? [String], let width = request["viewportWidth"] as? Double, width.isFinite, width > 0,
238
+ guard visible, canCapture(), let ids = request["ids"] as? [String],
239
+ let width = request["viewportWidth"] as? Double, width.isFinite, width > 0,
197
240
  let window = webView.window else { complete([:]); return }
198
241
  let selected = views().filter { ids.contains($0.key) }
199
242
  guard selected.count == ids.count else { complete([:]); return }
@@ -212,7 +255,11 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
212
255
  guard let ids = part["ids"] as? [String], let x = part["x"] as? CGFloat, let y = part["y"] as? CGFloat,
213
256
  let w = part["width"] as? CGFloat, let h = part["height"] as? CGFloat else { continue }
214
257
  let image = CGRect(x: x*scale, y: y*scale, width: w*scale, height: h*scale)
215
- for id in ids { if let view = selected[id] { entries[id] = Anchor(native: view.convert(view.bounds, to: webView), image: image) } }
258
+ for id in ids {
259
+ if let view = selected[id] {
260
+ entries[id] = Anchor(native: view.convert(view.bounds, to: webView), image: image)
261
+ }
262
+ }
216
263
  }
217
264
  self.anchors[token] = entries
218
265
  }
@@ -221,35 +268,33 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
221
268
  case "cover":
222
269
  let selected = captured[token] ?? [:], current = views()
223
270
  let valid = visible && canCapture() && !selected.isEmpty && selected.allSatisfy { current[$0.key] === $0.value }
271
+ let frames = request["parts"] == nil ? nil : snapshots.projectionFrames(request, in: webView)
272
+ guard request["parts"] == nil || frames != nil else { complete(["shown": false]); return }
224
273
  restoreAccepted = true
225
- complete(["shown": valid && snapshots.cover(token, in: webView, views: Array(Dictionary(grouping: selected.values, by: { ObjectIdentifier($0) }).values.compactMap { $0.first }), transient: true)])
274
+ complete(["shown": valid && snapshots.cover(
275
+ token, in: webView,
276
+ views: Array(Dictionary(grouping: selected.values, by: { ObjectIdentifier($0) }).values.compactMap { $0.first }),
277
+ transient: true, frames: frames
278
+ )])
226
279
  case "restore":
227
280
  restoreAccepted = false
228
- guard let width = request["viewportWidth"] as? Double, width.isFinite, width > 0,
229
- let parts = request["parts"] as? [[String: Any]] else { complete(["shown": false]); return }
230
- var frames: [String: CGRect] = [:]
231
- let scale = webView.bounds.width / width
232
- for part in parts {
233
- guard let ids = part["ids"] as? [String], let x = part["x"] as? Double, let y = part["y"] as? Double,
234
- let w = part["width"] as? Double, let h = part["height"] as? Double,
235
- [x,y,w,h].allSatisfy({ $0.isFinite }), w > 0, h > 0 else { complete(["shown": false]); return }
236
- for id in ids { frames[id] = CGRect(x: x*scale, y: y*scale, width: w*scale, height: h*scale) }
237
- }
238
- guard Set(frames.keys) == snapshots.identifiers(for: token), !frames.isEmpty else { complete(["shown": false]); return }
281
+ guard let frames = snapshots.projectionFrames(request, in: webView) else { complete(["shown": false]); return }
239
282
  let current = views()
240
283
  // Preserve renderer-owned constraints. Translation follows the corresponding DOM's movement.
241
284
  for (id, frame) in frames {
242
- guard let view = current[id], captured[token]?[id] === view, let anchor = anchors[token]?[id] else { complete(["shown": false]); return }
285
+ guard let view = current[id], captured[token]?[id] === view, let anchor = anchors[token]?[id]
286
+ else { complete(["shown": false]); return }
243
287
  let old = view.convert(view.bounds, to: webView)
244
288
  guard abs(old.width-anchor.native.width) < 0.1, abs(old.height-anchor.native.height) < 0.1,
245
- abs(frame.width-anchor.image.width) < 0.1, abs(frame.height-anchor.image.height) < 0.1 else { complete(["shown": false]); return }
289
+ abs(frame.width-anchor.image.width) < 0.1, abs(frame.height-anchor.image.height) < 0.1
290
+ else { complete(["shown": false]); return }
246
291
  }
247
292
  for (id, frame) in frames {
248
293
  guard let view = current[id], let parent = view.superview, let anchor = anchors[token]?[id] else { continue }
249
294
  let old = view.convert(view.bounds, to: webView)
250
295
  let from = parent.convert(CGPoint(x: old.midX, y: old.midY), from: webView)
251
296
  let to = parent.convert(CGPoint(x: anchor.native.midX+frame.minX-anchor.image.minX,
252
- y: anchor.native.midY+frame.minY-anchor.image.minY), from: webView)
297
+ y: anchor.native.midY+frame.minY-anchor.image.minY), from: webView)
253
298
  view.transform.tx += to.x-from.x; view.transform.ty += to.y-from.y
254
299
  }
255
300
  restoreAccepted = snapshots.restore(in: webView, token: token, frames: frames)
@@ -286,8 +331,8 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
286
331
  guard let data = try? JSONSerialization.data(withJSONObject: values), let json = String(data: data, encoding: .utf8) else { return }
287
332
  evaluate("globalThis.WebViewNativeUIEngineRuntime?.receive(...\(json))")
288
333
  }
289
- /// Call with the plugin's teardown. Does not destroy the renderer's views or DOM.
290
334
  deinit { destroy() }
335
+ /// Call with the plugin's teardown. Does not destroy the renderer's views or DOM.
291
336
  public func destroy() {
292
337
  guard !closed else { return }
293
338
  closed = true
@@ -298,11 +343,14 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
298
343
  controller.removeAllUserScripts()
299
344
  remaining.forEach { controller.addUserScript($0) }
300
345
  }
301
- snapshots.reset(); scrolling.reset(); captured.removeAll(); anchors.removeAll()
302
- if destination != "none" { hiddenByProjection.forEach { $0.isHidden = false } }
346
+ // A successful hide notification can precede its Web acknowledgement and teardown.
347
+ let keepHidden = (recordedDestination ?? destination) == "none"
348
+ let visibility = keepHidden
349
+ ? (Array(views().values) + hiddenByProjection + captured.values.flatMap { $0.values }).map { ($0, $0.isHidden) }
350
+ : []
351
+ snapshots.reset(); resetScrolling(); captured.removeAll(); anchors.removeAll()
352
+ if keepHidden { visibility.forEach { $0.0.isHidden = $0.1 } } else { hiddenByProjection.forEach { $0.isHidden = false } }
303
353
  hiddenByProjection.removeAll()
304
- for (id, mask) in masks { if views()[id]?.layer.mask === mask { views()[id]?.layer.mask = nil } }
305
- masks.removeAll()
306
354
  }
307
355
  }
308
356
  private final class WeakProjectionHandler: NSObject, WKScriptMessageHandler {
@@ -1,4 +1,4 @@
1
- "use strict";var WebViewNativeUIEngineRuntime=(()=>{var _e=Object.defineProperty;var lt=Object.getOwnPropertyDescriptor;var dt=Object.getOwnPropertyNames;var ut=Object.prototype.hasOwnProperty;var ft=(t,r)=>{for(var o in r)_e(t,o,{get:r[o],enumerable:!0})},pt=(t,r,o,v)=>{if(r&&typeof r=="object"||typeof r=="function")for(let h of dt(r))!ut.call(t,h)&&h!==o&&_e(t,h,{get:()=>r[h],enumerable:!(v=lt(r,h))||v.enumerable});return t};var mt=t=>pt(_e({},"__esModule",{value:!0}),t);var Ct={};ft(Ct,{hide:()=>Ht,notify:()=>Tt,receive:()=>jt,show:()=>St,start:()=>kt,stop:()=>xt});var vt=Symbol.for("webview-native-ui-engine.projection-placement");var Ne=t=>t?.[vt];var ht=(t,r)=>{if(t.matches("ion-toast, ion-menu"))return!1;let o=t;if(o.showBackdrop===!1)return!1;if(!t.matches("ion-modal"))return!0;let v=r??o.initialBreakpoint;return v===void 0||v>(o.backdropBreakpoint??0)},yt=Symbol.for("@webview-native-ui/engine/overlays/v1"),gt=t=>{let r=new Set,o=new Set,v=new Set,h=new WeakMap,d=new WeakSet,p=new Map,T=[],f={},M=new Map,C,j=y=>{for(let l of[...v])try{l(y)}catch(g){console.error("Native UI overlay listener failed",g)}},B=(y,l,g)=>{l==="willPresent"||l==="didPresent"?(d.delete(y),r.add(y)):l==="didDismiss"&&(d.add(y),r.delete(y),o.delete(y),h.delete(y));let c=g?.detail?.breakpoint;typeof c=="number"&&c>0&&h.set(y,c),j({element:y,phase:l,event:g})},S=(y=!1)=>{let l=!1;for(let g of r)(!g.isConnected||g.ownerDocument!==t||!T.some(({selector:c})=>g.matches(c)))&&(r.delete(g),o.delete(g),l=!0);for(let g of t.querySelectorAll(T.map(({selector:c})=>c).join(",")))!r.has(g)&&!d.has(g)&&(y||!g.matches("ion-popover"))&&(g.presented||g.classList.contains("show-menu"))&&(r.add(g),l=!0);l&&j({phase:"change"})};return{get active(){return r},subscribe(y){return v.add(y),()=>{v.delete(y)}},setNative(y,l){o.has(y)!==l&&(l?o.add(y):o.delete(y),j({element:y,phase:"change"}))},isExcluded(y){return!!f.exclude&&y.matches(f.exclude)},isNative(y){return o.has(y)},get stack(){let y=[...r].filter(g=>g.isConnected&&g.ownerDocument===t),l=g=>{let c=Number.parseFloat(t.defaultView.getComputedStyle(g).zIndex);return Number.isFinite(c)?c:g.overlayIndex??0};return y.sort((g,c)=>l(g)-l(c)),y},isCovered(y){let l=this.stack,g=l.reduce((c,D,H)=>D.contains(y)?H:c,-1);return l.some((c,D)=>{if(o.has(c)||f.exclude&&c.matches(f.exclude))return!1;let H=f.covers?.(c,y);return H!==void 0?H:D>g&&!c.contains(y)&&(c.matches("ion-menu")||ht(c,h.get(c)))})},enable(y,l){let g={...f,...l},c=[...y,...g.additional??[]];g.exclude&&t.querySelector(g.exclude);let D=new Map;for(let m of c){t.querySelector(m.selector);for(let[W,A]of Object.entries(m.events)){let N=D.get(A);if(N&&N.phase!==W)throw new TypeError(`Overlay event "${A}" has conflicting phases`);D.set(A,{phase:W,selector:N?`${N.selector}, ${m.selector}`:m.selector})}}f=g,T=c,M=D;for(let m of M.keys()){if(p.has(m))continue;let W=A=>{let N=M.get(m),oe=A.composedPath().find(F=>F instanceof t.defaultView.HTMLElement&&F.matches(N.selector));oe&&B(oe,N.phase,A)};t.addEventListener(m,W,!0),p.set(m,()=>t.removeEventListener(m,W,!0))}for(let[m,W]of p)M.has(m)||(W(),p.delete(m));let H=!C;C||(C=new MutationObserver(()=>S()),C.observe(t.documentElement,{childList:!0,subtree:!0}),t.addEventListener("nativeUIOverlayAdopted",m=>{let W=m.detail;W?.ownerDocument===t&&B(W,"willPresent",m)})),S(H),(H||l)&&j({phase:"change"})}}},et=t=>{let r=t;return r[yt]??=gt(t)};function*Oe(t){let r=t;for(;r;){let o=r.getRootNode();r=r.assignedSlot??r.parentElement??(o instanceof ShadowRoot?o.host:null),r&&(yield r)}}var wt=({x:t,y:r,width:o,height:v})=>({x:t,y:r,width:o,height:v}),tt=t=>{let r=t.getBoundingClientRect(),o=getComputedStyle(t),v=parseFloat(o.borderLeftWidth)||0,h=parseFloat(o.borderTopWidth)||0;return{x:r.left+v,y:r.top+h,width:r.width-v-(parseFloat(o.borderRightWidth)||0),height:r.height-h-(parseFloat(o.borderBottomWidth)||0)}},Ge=(t,r,o)=>{let v=[t,...Oe(t)],h=v.indexOf(r);if(h<=0||r===t.ownerDocument.scrollingElement)return;let d=t.ownerDocument.defaultView;if(d.visualViewport&&(d.visualViewport.scale!==1||d.visualViewport.offsetLeft||d.visualViewport.offsetTop))return;let p=0,T=0,f=d.innerWidth,M=d.innerHeight;for(let[S,y]of v.entries()){let l=getComputedStyle(y);if(l.transform!=="none"&&!new DOMMatrixReadOnly(l.transform).isIdentity||l.zoom&&l.zoom!=="normal"&&Number(l.zoom)!==1||[l.translate,l.rotate,l.scale,l.perspective].some(H=>H&&H!=="none")||l.writingMode&&l.writingMode!=="horizontal-tb"||S<h&&/^(fixed|sticky)$/.test(l.position))return;if(S===0)continue;if(l.scrollbarGutter&&l.scrollbarGutter!=="auto")return;let g=/^(auto|scroll|hidden|clip)$/.test(l.overflowX),c=/^(auto|scroll|hidden|clip)$/.test(l.overflowY);if(S<h&&(g||c))return;let D=y===t.ownerDocument.scrollingElement;if(S>h&&((g||D)&&y.scrollWidth>y.clientWidth||(c||D)&&y.scrollHeight>y.clientHeight)||(g||c)&&[l.borderTopLeftRadius,l.borderTopRightRadius,l.borderBottomLeftRadius,l.borderBottomRightRadius].some(H=>parseFloat(H)>0)||l.clipPath&&l.clipPath!=="none"||l.maskImage&&l.maskImage!=="none")return;if(S>=h){let H=tt(y);g&&(p=Math.max(p,H.x),f=Math.min(f,H.x+H.width)),c&&(T=Math.max(T,H.y),M=Math.min(M,H.y+H.height))}}let C=getComputedStyle(r);if(!/^(auto|scroll)$/.test(C.overflowX)&&!/^(auto|scroll)$/.test(C.overflowY))return;let j=tt(r);if(f<=p||M<=T||!j.width||!j.height)return;let B=wt(t.getBoundingClientRect());return{id:o(r),offsetX:r.scrollLeft,offsetY:r.scrollTop,viewport:j,clip:{x:p,y:T,width:f-p,height:M-T},contentWidth:r.scrollWidth,contentHeight:r.scrollHeight,frame:{...B,x:B.x-j.x+r.scrollLeft,y:B.y-j.y+r.scrollTop}}};var nt=t=>{let r=globalThis;if(r.Capacitor?.convertFileSrc)return r.Capacitor.convertFileSrc(t);if(!t.startsWith("file:"))return t;if(r.WkWebView?.convertFilePath)return r.WkWebView.convertFilePath(t);throw new Error("Native UI file URL requires a host resource handler")};var pe=t=>new Promise((r,o)=>{let v=setTimeout(()=>o(new Error("Native UI bridge timed out")),5e3);t.then(r,o).finally(()=>clearTimeout(v))}),Be=async t=>{try{return await pe(t)}catch(r){throw t.then(o=>o.remove()).catch(()=>{}),r}};var ze=async(t,r)=>{let o=new Map;for(let T of t.update??[])o.set(T,{updated:!0});for(let T of["native","web","none"])for(let f of t.projection?.[T]??[]){let M=o.get(f)??{updated:!1};if(M.destination&&M.destination!==T)throw new TypeError(`Projection event "${f}" has conflicting destinations`);o.set(f,{...M,destination:T})}let v=!1,h=[],d=async()=>{v=!0;let f=(await Promise.allSettled(h.splice(0).map(M=>Promise.resolve().then(()=>M.remove())))).find(M=>M.status==="rejected");if(f?.status==="rejected")throw f.reason},p=[...o].map(async([T,f])=>{let M=await Be(Promise.resolve().then(()=>t.addListener(T,C=>{!v&&(!t.accepts||t.accepts(C))&&r(f.destination,f.updated)})));v?await M.remove():h.push(M)});try{return await Promise.all(p),{remove:d}}catch(T){throw await d().catch(()=>{}),T}};var je=":is([data-native-ui-snapshot-source], [data-native-ui-snapshot-prepared], [data-native-ui-snapshot-content])",ot=`
1
+ "use strict";var WebViewNativeUIEngineRuntime=(()=>{var Je=Object.defineProperty;var ut=Object.getOwnPropertyDescriptor;var ft=Object.getOwnPropertyNames;var pt=Object.prototype.hasOwnProperty;var mt=(t,r)=>{for(var o in r)Je(t,o,{get:r[o],enumerable:!0})},vt=(t,r,o,v)=>{if(r&&typeof r=="object"||typeof r=="function")for(let h of ft(r))!pt.call(t,h)&&h!==o&&Je(t,h,{get:()=>r[h],enumerable:!(v=ut(r,h))||v.enumerable});return t};var ht=t=>vt(Je({},"__esModule",{value:!0}),t);var Ot={};mt(Ot,{hide:()=>kt,notify:()=>Mt,receive:()=>xt,show:()=>Ht,start:()=>Ct,stop:()=>Rt});var tt=(t,r,o)=>{for(let[v,h]of Object.entries(r)){if(!/^[a-z][a-z0-9-]*$/.test(v))throw new TypeError("Native value keys must be lowercase attribute names");let f=(Object.hasOwn(o,v)?o[v]:void 0)?.property;if(f&&(!/^[a-zA-Z][\w]*$/.test(f)||/^on/i.test(f)||["innerHTML","outerHTML","textContent","innerText","style","constructor","prototype"].includes(f)))throw new TypeError("Native value binding requires a data property");let P=JSON.stringify(h);if(P===void 0)throw new TypeError("Native values must be JSON values");if(f){let p=t;JSON.stringify(p[f])!==P&&(p[f]=h)}else{let p=`data-native-ui-value-${v}`,L=typeof h=="string"?h:P;t.getAttribute(p)!==L&&t.setAttribute(p,L)}}};var yt=Symbol.for("webview-native-ui-engine.projection-placement");var $e=t=>t?.[yt];var gt=(t,r)=>{if(t.matches("ion-toast, ion-menu"))return!1;let o=t;if(o.showBackdrop===!1)return!1;if(!t.matches("ion-modal"))return!0;let v=r??o.initialBreakpoint;return v===void 0||v>(o.backdropBreakpoint??0)},wt=Symbol.for("@webview-native-ui/engine/overlays/v1"),bt=t=>{let r=new Set,o=new Set,v=new Set,h=new WeakMap,m=new WeakSet,f=new Map,P=[],p={},L=new Map,x,M=g=>{for(let l of[...v])try{l(g)}catch(w){console.error("Native UI overlay listener failed",w)}},B=(g,l,w)=>{l==="willPresent"||l==="didPresent"?(m.delete(g),r.add(g)):l==="didDismiss"&&(m.add(g),r.delete(g),o.delete(g),h.delete(g));let c=w?.detail?.breakpoint;typeof c=="number"&&c>0&&h.set(g,c),M({element:g,phase:l,event:w})},H=(g=!1)=>{let l=!1;for(let w of r)(!w.isConnected||w.ownerDocument!==t||!P.some(({selector:c})=>w.matches(c)))&&(r.delete(w),o.delete(w),l=!0);for(let w of t.querySelectorAll(P.map(({selector:c})=>c).join(",")))!r.has(w)&&!m.has(w)&&(g||!w.matches("ion-popover"))&&(w.presented||w.classList.contains("show-menu"))&&(r.add(w),l=!0);l&&M({phase:"change"})};return{get active(){return r},subscribe(g){return v.add(g),()=>{v.delete(g)}},setNative(g,l){o.has(g)!==l&&(l?o.add(g):o.delete(g),M({element:g,phase:"change"}))},isExcluded(g){return!!p.exclude&&g.matches(p.exclude)},isNative(g){return o.has(g)},get stack(){let g=[...r].filter(w=>w.isConnected&&w.ownerDocument===t),l=w=>{let c=Number.parseFloat(t.defaultView.getComputedStyle(w).zIndex);return Number.isFinite(c)?c:w.overlayIndex??0};return g.sort((w,c)=>l(w)-l(c)),g},isCovered(g){let l=this.stack,w=l.reduce((c,N,k)=>N.contains(g)?k:c,-1);return l.some((c,N)=>{if(o.has(c)||p.exclude&&c.matches(p.exclude))return!1;let k=p.covers?.(c,g);return k!==void 0?k:N>w&&!c.contains(g)&&(c.matches("ion-menu")||gt(c,h.get(c)))})},enable(g,l){let w={...p,...l},c=[...g,...w.additional??[]];w.exclude&&t.querySelector(w.exclude);let N=new Map;for(let d of c){t.querySelector(d.selector);for(let[V,F]of Object.entries(d.events)){let G=N.get(F);if(G&&G.phase!==V)throw new TypeError(`Overlay event "${F}" has conflicting phases`);N.set(F,{phase:V,selector:G?`${G.selector}, ${d.selector}`:d.selector})}}p=w,P=c,L=N;for(let d of L.keys()){if(f.has(d))continue;let V=F=>{let G=L.get(d),pe=F.composedPath().find(Y=>Y instanceof t.defaultView.HTMLElement&&Y.matches(G.selector));pe&&B(pe,G.phase,F)};t.addEventListener(d,V,!0),f.set(d,()=>t.removeEventListener(d,V,!0))}for(let[d,V]of f)L.has(d)||(V(),f.delete(d));let k=!x;x||(x=new MutationObserver(()=>H()),x.observe(t.documentElement,{childList:!0,subtree:!0}),t.addEventListener("nativeUIOverlayAdopted",d=>{let V=d.detail;V?.ownerDocument===t&&B(V,"willPresent",d)})),H(k),(k||l)&&M({phase:"change"})}}},nt=t=>{let r=t;return r[wt]??=bt(t)};function*Ae(t){let r=t;for(;r;){let o=r.getRootNode();r=r.assignedSlot??r.parentElement??(o instanceof ShadowRoot?o.host:null),r&&(yield r)}}var Et=({x:t,y:r,width:o,height:v})=>({x:t,y:r,width:o,height:v}),ot=t=>{let r=t.getBoundingClientRect(),o=getComputedStyle(t),v=parseFloat(o.borderLeftWidth)||0,h=parseFloat(o.borderTopWidth)||0;return{x:r.left+v,y:r.top+h,width:r.width-v-(parseFloat(o.borderRightWidth)||0),height:r.height-h-(parseFloat(o.borderBottomWidth)||0)}},_e=(t,r,o)=>{let v=[t,...Ae(t)],h=v.indexOf(r);if(h<=0||r===t.ownerDocument.scrollingElement)return;let m=t.ownerDocument.defaultView;if(m.visualViewport&&(m.visualViewport.scale!==1||m.visualViewport.offsetLeft||m.visualViewport.offsetTop))return;let f=0,P=0,p=m.innerWidth,L=m.innerHeight;for(let[H,g]of v.entries()){let l=getComputedStyle(g);if(l.transform!=="none"&&!new DOMMatrixReadOnly(l.transform).isIdentity||l.zoom&&l.zoom!=="normal"&&Number(l.zoom)!==1||[l.translate,l.rotate,l.scale,l.perspective].some(k=>k&&k!=="none")||l.writingMode&&l.writingMode!=="horizontal-tb"||H<h&&/^(fixed|sticky)$/.test(l.position))return;if(H===0)continue;if(l.scrollbarGutter&&l.scrollbarGutter!=="auto")return;let w=/^(auto|scroll|hidden|clip)$/.test(l.overflowX),c=/^(auto|scroll|hidden|clip)$/.test(l.overflowY);if(H<h&&(w||c))return;let N=g===t.ownerDocument.scrollingElement;if(H>h&&((w||N)&&g.scrollWidth>g.clientWidth||(c||N)&&g.scrollHeight>g.clientHeight)||(w||c)&&[l.borderTopLeftRadius,l.borderTopRightRadius,l.borderBottomLeftRadius,l.borderBottomRightRadius].some(k=>parseFloat(k)>0)||l.clipPath&&l.clipPath!=="none"||l.maskImage&&l.maskImage!=="none")return;if(H>=h){let k=ot(g);w&&(f=Math.max(f,k.x),p=Math.min(p,k.x+k.width)),c&&(P=Math.max(P,k.y),L=Math.min(L,k.y+k.height))}}let x=getComputedStyle(r);if(!/^(auto|scroll)$/.test(x.overflowX)&&!/^(auto|scroll)$/.test(x.overflowY))return;let M=ot(r);if(p<=f||L<=P||!M.width||!M.height)return;let B=Et(t.getBoundingClientRect());return{id:o(r),offsetX:r.scrollLeft,offsetY:r.scrollTop,viewport:M,clip:{x:f,y:P,width:p-f,height:L-P},contentWidth:r.scrollWidth,contentHeight:r.scrollHeight,frame:{...B,x:B.x-M.x+r.scrollLeft,y:B.y-M.y+r.scrollTop}}};var it=t=>{let r=globalThis;if(r.Capacitor?.convertFileSrc)return r.Capacitor.convertFileSrc(t);if(!t.startsWith("file:"))return t;if(r.WkWebView?.convertFilePath)return r.WkWebView.convertFilePath(t);throw new Error("Native UI file URL requires a host resource handler")};var fe=t=>new Promise((r,o)=>{let v=setTimeout(()=>o(new Error("Native UI bridge timed out")),5e3);t.then(r,o).finally(()=>clearTimeout(v))}),ze=async t=>{try{return await fe(t)}catch(r){throw t.then(o=>o.remove()).catch(()=>{}),r}};var qe=async(t,r)=>{let o=new Map;for(let P of t.update??[])o.set(P,{updated:!0});for(let P of["native","web","none"])for(let p of t.projection?.[P]??[]){let L=o.get(p)??{updated:!1};if(L.destination&&L.destination!==P)throw new TypeError(`Projection event "${p}" has conflicting destinations`);o.set(p,{...L,destination:P})}let v=!1,h=[],m=async()=>{v=!0;let p=(await Promise.allSettled(h.splice(0).map(L=>Promise.resolve().then(()=>L.remove())))).find(L=>L.status==="rejected");if(p?.status==="rejected")throw p.reason},f=[...o].map(async([P,p])=>{let L=await ze(Promise.resolve().then(()=>t.addListener(P,x=>{!v&&(!t.accepts||t.accepts(x))&&r(p.destination,p.updated)})));v?await L.remove():h.push(L)});try{return await Promise.all(f),{remove:m}}catch(P){throw await m().catch(()=>{}),P}};var je=":is([data-native-ui-snapshot-source], [data-native-ui-snapshot-prepared], [data-native-ui-snapshot-content])",rt=`
2
2
  @layer native-ui-projection {
3
3
  ${je}, ${je} *, ${je}::part(native) { visibility: hidden !important; }
4
4
  ${je}::before, ${je}::after,
@@ -8,7 +8,7 @@ ${je} *::before, ${je} *::after {
8
8
  }
9
9
  [data-native-ui-snapshot-visible] { opacity:1!important;overflow:visible!important; }
10
10
  [data-native-ui-snapshot-visible][data-native-ui-snapshot-positioned] { position:relative!important; }
11
- `,it=`
11
+ `,st=`
12
12
  @layer native-ui-projection {
13
13
  :host([data-native-ui-snapshot-content]),
14
14
  :host([data-native-ui-snapshot-content])::before,
@@ -19,4 +19,4 @@ ${je} *::before, ${je} *::after {
19
19
  visibility: hidden !important;
20
20
  }
21
21
  }
22
- `;var bt=(t,r=!1)=>JSON.stringify(t.map(({control:o})=>[o.id,o.kind,o.placement,r?0:o.x,r?0:o.y,o.width,o.height,o.dark,o.rtl])),Et=async(t,r,o)=>{let v=o.captureDelayMs??50;if(!Number.isFinite(v)||v<0||v>2147483647)throw new RangeError("captureDelayMs must be a finite, nonnegative timer interval");let h="native",d=t.element.ownerDocument,p=d.defaultView,T=Array.from(p.crypto.getRandomValues(new Uint32Array(4)),e=>e.toString(16)).join("-"),f=e=>e.isConnected&&e.ownerDocument===d,M=e=>!!e?.isConnected&&e.ownerDocument===d,C=new Map,j=new Set,B=e=>{j.delete(e),e.style.setProperty("display","none","important"),e.removeAttribute("data-native-ui-projection-image"),e.removeAttribute("src"),le()},S=r.send.bind(r),y="data-native-ui-snapshot",l=`${t.namespace}:sources-change`,g=new Set,c=new Set,D=new Set,H=new WeakMap,m=0,W=e=>JSON.stringify(e.map(i=>(H.has(i.scope)||H.set(i.scope,++m),[H.get(i.scope),bt([i],!!o.opaqueUnderlay)]))),A=new Map,N=e=>{let i=e.getRootNode();return i.nodeType===11&&"host"in i?i:d},oe=e=>{if(A.has(e))return;let i=d.createElement("style");i.setAttribute("data-native-ui-snapshot-base",""),i.textContent=ot+(e.nodeType===11?it:""),(e.nodeType===11?e:e.head).append(i),A.set(e,i)},F=e=>{let i=[...e.getAnimations({subtree:!0}),...[...se.get(e)??[]].flatMap(n=>n.shadowRoot?.getAnimations()??[])];for(let n of i)"transitionProperty"in n&&n.transitionProperty==="visibility"&&n.cancel()},se=new Map,Se=e=>{let i=se.get(e)??new Set;se.set(e,i);let n=e.hasAttribute("data-native-ui-snapshot-source")||e.hasAttribute("data-native-ui-snapshot-prepared"),s=w=>{if(!w.hasAttribute("data-native-ui-projection-node")){if(w.shadowRoot){i.add(w),oe(w.shadowRoot);for(let L of w.shadowRoot.children)s(L)}for(let L of w.children)s(L)}};s(e);for(let w of i)w.toggleAttribute("data-native-ui-snapshot-content",n);F(e)},O=()=>`${p.innerWidth}:${p.innerHeight}:${p.devicePixelRatio}`,Z=!1,ae=!1,J=0,me=0,R,U,E,X=!1,Y=!1,we=0,We=0,ee=0,ve=0,z=0,be=-1,V=!1,te=!1,q,le=()=>{let e=new Set(q?.candidates.map(n=>n.scope.element));for(let[n,s]of C){let w=!V&&n.isConnected&&e.has(n);if(w&&(E||Y||Z))continue;let L=w?2*Math.max(1,R?.scopes.filter(k=>k.element===n).length??0):0,x=Math.max(0,L-s.filter(k=>j.has(k)).length),I=s.filter(k=>j.has(k)||x-- >0?!0:(k.remove(),!1));I.length?C.set(n,I):C.delete(n)}let i=new Set([...q?.candidates.map(n=>n.scope.element.getRootNode())??[],...E?.scopes.map(n=>n.element.getRootNode())??[],...R?.scopes.map(n=>n.element.getRootNode())??[],...[...j].map(n=>n.getRootNode()),...[...se.values()].flatMap(n=>[...n].map(s=>s.shadowRoot))]);for(let[n,s]of A)n.nodeType===11&&!i.has(n)&&(s.remove(),A.delete(n))},he=new Set,Ie=(e,i,n)=>{let s=()=>{he.delete(s)&&(S({action:e,token:i}).catch(()=>{}),e==="restored"&&le())};he.add(s),n&&!V?p.requestAnimationFrame(()=>p.requestAnimationFrame(s)):s()},de=new Set,He=()=>{de.forEach(e=>D.delete(e)),de.clear()},xe=(e,i=!1)=>{!e||e===R||e===E||(e.images.forEach(n=>{B(n)}),Ie("release",e.data.token,i))},Ee=e=>e.filter(i=>{if(!M(i.scope.element)||![i.element,...i.sources??[]].every(f))return!1;if(i.control.scroll){let n=i.element.getBoundingClientRect(),s=i.control.scroll.clip;if(n.right<=s.x||n.bottom<=s.y||n.left>=s.x+s.width||n.top>=s.y+s.height)return!1}return o.eligible?.(i)??!0}),Xe=async()=>{if(ve=0,V||h!=="native"||te||X||E||o.blocked()||!q||R?.revision===q.revision&&be===z)return;let{revision:e,candidates:i}=q,n=ee,s=z,w=O(),L=Ee(i);if(!L.length)return;te=!0;let x,I=!1,k=[];try{let ie=!1,re=S({action:"prepare",revision:o.captureRevision?.()??e,viewportWidth:p.innerWidth,ids:L.map(b=>b.control.id)});re.then(b=>{ie&&b.token&&S({action:"release",token:b.token}).catch(()=>{})}).catch(()=>{});try{x=await pe(re)}finally{ie=!0}if(!x.token||!x.parts?.length)return;let Ce=new Set(L.map(b=>b.control.id));if(x.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(G=>!Ce.has(G))||![b.x,b.y,b.width,b.height].every(Number.isFinite)||b.width<=0||b.height<=0)||new Set(x.parts.flatMap(b=>b.ids)).size!==Ce.size||x.parts.some(b=>new Set(b.ids.map(G=>L.find(Me=>Me.control.id===G).scope)).size!==1)||(await pe(Promise.all(x.parts.map(async b=>{let G=L.find(qe=>b.ids.includes(qe.control.id)).scope.element,Me=C.get(G);Me||(Me=[],C.set(G,Me));let fe=Me.find(qe=>!j.has(qe));return fe||(fe=d.createElement("img"),fe.setAttribute("data-native-ui-projection-node",""),Me.push(fe)),j.add(fe),k.push(fe),fe.src=nt(b.url),await fe.decode(),fe}))),V||h!=="native"||X||E||o.blocked()||ee!==n||O()!==w||q?.revision!==e||L.some(b=>!M(b.scope.element)||![b.element,...b.sources??[]].every(f))))return;let Re=R,Qe={data:x,images:k,sources:Array.from(new Set(L.flatMap(b=>[b.element,...b.sources??[]]))),targets:x.parts.map(b=>L.find(G=>b.ids.includes(G.control.id)).element),scopes:x.parts.map(b=>L.find(G=>b.ids.includes(G.control.id)).scope),parents:x.parts.map(b=>L.find(G=>b.ids.includes(G.control.id)).scope.element.parentNode),frames:[],viewport:w,revision:e,layout:W(L)};try{if(!Ue(Qe))throw new Error("Projection destination is unavailable")}catch(b){Q();try{Ue(),R?.sources.forEach(G=>{D.add(G),de.add(G)})}catch{$()}throw b}R=Qe,He(),R.sources.forEach(b=>{D.add(b),de.add(b)}),o.opaqueUnderlay&&S({action:"prepared",token:x.token}).catch(()=>{}),p.dispatchEvent(new Event(l)),be=s,I=!0,xe(Re)}catch{}finally{I||k.forEach(ie=>{B(ie)}),x?.token&&!I&&S({action:"release",token:x.token}).catch(()=>{}),te=!1,q&&(q.revision!==e||z!==s)&&ue()}},ue=()=>{p.clearTimeout(ve),!V&&h==="native"&&!E&&!X&&!o.blocked()&&(ve=p.setTimeout(()=>{Xe()},v))},Ae,a=(e,i)=>{if(i.some(s=>s.scope.element.ownerDocument!==d||[s.element,...s.sources??[]].some(w=>w.ownerDocument!==d)))throw new TypeError("All projection sources and fallback scopes must share one Document");if(o.blocked())return;!E&&R&&R.layout!==W(Ee(i))&&Le();let n=W(i);Ae!==n&&(Ae=n,z++,ee++),q?.revision!==e&&ee++,q={revision:e,candidates:i},le(),(R?.revision!==e||be!==z)&&ue()},u=e=>{!E&&R&&e&&q?.candidates.some(i=>i.scrollElement===e&&R.sources.includes(i.element))&&Le(),E&&ke(),z++,ee++,ue()},P=await Be(r.listen(({active:e})=>{X=e,ee++,p.clearTimeout(ve),e||ue()})),_=new Set,ce=e=>{se.get(e)?.forEach(i=>i.removeAttribute("data-native-ui-snapshot-content")),_.delete(e)&&e.removeAttribute("data-native-ui-snapshot-prepared"),e.removeAttribute("data-native-ui-snapshot-visible"),e.removeAttribute(y),e.removeAttribute("data-native-ui-snapshot-positioned"),e.removeAttribute("data-native-ui-snapshot-source"),F(e),se.delete(e)},Q=()=>{me++,p.cancelAnimationFrame(J);for(let e of Fe.keys())e.style.setProperty("opacity","0","important");Fe.clear(),J=0,Z=!1,ae=!1,He(),E?.sources.forEach(e=>{g.delete(e),c.delete(e)}),K.forEach(ce),K.clear(),A.forEach((e,i)=>{(V||i.nodeType===11&&!i.host.isConnected)&&(e.remove(),A.delete(i))})},$=(e=E,i=Y)=>{if(E!==e)return;let n=new Set([E,R]);Q(),E=void 0,R=void 0,Y=!1,p.clearTimeout(we),n.forEach(s=>xe(s,i)),o.wake()},ne=()=>{if(!E)return;if(R!==E){$();return}let e=E;me++,p.cancelAnimationFrame(J),J=0,Z=ae=!1,e.sources.forEach(i=>{g.delete(i),c.delete(i)}),K.forEach(i=>i.removeAttribute("data-native-ui-snapshot-source")),Ye(e,!!o.opaqueUnderlay),E=void 0,Y=!1,p.clearTimeout(we),Ie("restored",e.data.token,!0),e.sources.forEach(i=>{D.add(i),de.add(i)}),o.wake()},Pe=()=>{Y&&ne(),he.forEach(e=>e())},De=e=>e.scopes.map(i=>M(i.element)?i.element:void 0),K=new Set,Ye=(e,i)=>{for(let n of K)n.toggleAttribute("data-native-ui-snapshot-visible",i),Se(n);e.images.forEach(n=>n.style.setProperty("opacity",i?"1":"0","important"))},Te=new Map,Fe=new Map,ke=()=>{let e=[];for(let[i,n]of Fe){let{target:s,frame:w}=n;if(!s.isConnected){i.remove();continue}let L=s.shadowRoot??s;i.parentNode!==L&&L.insertBefore(i,null);let x=s.getBoundingClientRect(),I=i.getBoundingClientRect(),k=parseFloat(i.style.width)||w.width,ie=parseFloat(i.style.height)||w.height,re=I.width/k||1,Ce=I.height/ie||1,Re=n.scale??(n.scale={x:re,y:Ce});e.push({element:i,left:(parseFloat(i.style.left)||0)+(x.x+w.x*re/Re.x-I.x)/re,top:(parseFloat(i.style.top)||0)+(x.y+w.y*Ce/Re.y-I.y)/Ce,width:w.width/Re.x,height:w.height/Re.y})}for(let{element:i,...n}of e)for(let[s,w]of Object.entries(n)){let L=`${w}px`;i.style.getPropertyValue(s)!==L&&i.style.setProperty(s,L,"important")}},Ue=(e=E??R)=>{if(!e||Y)return!1;if(!e.sources.every(f)||De(e).some(n=>!n))return Le(),!1;let i=De(e);if(E){if(i.some(s=>!s?.isConnected||!A.has(N(s))))return $(E,!1),!1;let n=new Set;for(let[s,w]of e.targets.entries()){let L=Te.get(w),x=i[s];!L||!x?.isConnected||(x.setAttribute(y,L),x.setAttribute("data-native-ui-snapshot-source",""),n.add(x))}for(let s of K)n.has(s)||ce(s);return K.clear(),n.forEach(s=>K.add(s)),Ye(e,ae),!0}C.forEach(n=>n.forEach(s=>{e.images.includes(s)||(s.style.setProperty("display","none","important"),s.removeAttribute("data-native-ui-projection-image"))})),Te.clear(),Fe.clear(),K.forEach(ce),K.clear();for(let n of new Set(i)){if(!n)continue;K.add(n),oe(N(n));let s=i.flatMap((I,k)=>I===n?[k]:[]),w=`${T}-${s[0]}`;n.setAttribute(y,w),n!==e.targets[s[0]]&&(n.setAttribute("data-native-ui-snapshot-prepared",""),_.add(n)),n.toggleAttribute("data-native-ui-snapshot-visible",!!o.opaqueUnderlay),Se(n);let L=n.getBoundingClientRect();n.toggleAttribute("data-native-ui-snapshot-positioned",p.getComputedStyle(n).position==="static");let x=n.shadowRoot??n;for(let I of[...s].reverse()){let k=e.images[I],ie=e.data.parts[I],re=e.frames[I]??{x:o.opaqueUnderlay?0:ie.x-L.x,y:o.opaqueUnderlay?0:ie.y-L.y,width:ie.width,height:ie.height};e.frames[I]=re,Te.set(e.targets[I],w),k.inert=!0,k.setAttribute("aria-hidden","true"),k.setAttribute("data-native-ui-projection-image",w),k.style.cssText="all:initial!important;position:absolute!important;display:block!important;visibility:visible!important;pointer-events:none!important;image-rendering:pixelated!important;will-change:opacity!important;opacity:0!important;",k.style.setProperty("width",`${re.width}px`,"important"),k.style.setProperty("height",`${re.height}px`,"important"),k.style.setProperty("left","0px","important"),k.style.setProperty("top","0px","important"),k.style.setProperty("opacity",o.opaqueUnderlay?"1":"0","important"),k.parentNode!==x&&x.insertBefore(k,null),Fe.set(k,{target:n,frame:re})}}return e.parents=e.scopes.map(n=>n.element.parentNode),ke(),!0},Je=()=>{if(Pe(),V||h==="none"||E)return;let e=R;if(!e||e.viewport!==O()||!e.sources.every(f)||o.current&&!o.current(e.targets)||e.targets.some(s=>!Te.has(s))||De(e).some(s=>!s?.isConnected)||[...K].some(s=>!s.isConnected))return;e.scopes.some((s,w)=>s.element.parentNode!==e.parents[w])&&Ue(),ke();let i=++me,n=()=>me===i;Z=!0,ae=!!o.opaqueUnderlay,E=e,ee++,p.clearTimeout(ve),e.sources.forEach(s=>{g.add(s)}),K.forEach(s=>s.setAttribute("data-native-ui-snapshot-source","")),Ye(e,ae),J=p.requestAnimationFrame(()=>{J=0,n()&&(Z=!1,o.wake())}),pe(S({action:"cover",token:e.data.token,...o.transient?{transient:!0}:{}})).then(s=>{if(n()){if(Z=!1,s?.shown!==!0){$(e,!1);return}Y||(ae=!0,e.sources.forEach(w=>c.add(w)),Ue(),ke()),p.cancelAnimationFrame(J),J=p.requestAnimationFrame(()=>{J=0,n()&&S({action:"painted",token:e.data.token}).catch(()=>{n()&&$(e,!1)})}),o.wake()}}).catch(()=>{n()&&$(e,!1)})},Ke=()=>{if(!(!E||V||Y)){if(!E.sources.every(f)||De(E).some(e=>!e)||o.current&&!o.current(E.targets)){$(E,!1);return}if(!o.blocked()){if(ke(),o.transient){let e=E,i=De(e);if(i.some(s=>!s?.isConnected)){$(e,!1);return}let n=i.map((s,w)=>{let L=e.images[w].getBoundingClientRect();return{...e.data.parts[w],x:L.x,y:L.y,width:L.width,height:L.height}});n.some((s,w)=>["x","y","width","height"].some(L=>s[L]!==e.data.parts[w][L]))&&(o.opaqueUnderlay?e.data={...e.data,parts:n}:R=void 0),S({action:"restore",token:e.data.token,viewportWidth:p.innerWidth,parts:n}).catch(()=>{})}We=o.revision?.()??q?.revision??0,Y=!0,E.sources.forEach(e=>g.delete(e)),we=p.setTimeout(()=>{Y&&$(E,!1)},250),o.wake()}}},ct=(e,i,n)=>{!Y||!E||n===void 0||n<=We||!o.transient&&e||E.data.parts.every(s=>s.ids.every(w=>i?.includes(w)))&&(ne(),ue())},Le=(e=!1)=>{he.forEach(n=>n()),ee++,p.clearTimeout(ve);let i=R;if(R=void 0,(e||V)&&(U=void 0),o.retainPreparedUntilRelease&&!E&&!e&&!V){if(i){let n={};U=n,pe(S({action:"release",token:i.data.token})).catch(()=>{}).finally(()=>{if(i.images.forEach(s=>{B(s)}),U===n&&(U=void 0,!(R||E))){for(let s of K)s.getAttribute(y)!==Te.get(s)&&(K.delete(s),_.delete(s));Q(),Te.clear(),p.dispatchEvent(new Event(l))}})}if(U)return}xe(i),E?$(E,!1):(Q(),Te.clear(),p.dispatchEvent(new Event(l)))},Ze;try{o.events&&(Ze=await ze(o.events,(e,i)=>{if(!V){if(e&&(h=e),h==="none"){Le();return}e==="web"&&Je(),e==="native"&&(Ke(),ue()),i&&u()}}))}catch(e){throw V=!0,Le(),await pe(Promise.resolve(P.remove())).catch(()=>{}),e}return{get owners(){return[...g]},get visibleOwners(){return[...c]},get preparedOwners(){return[...D]},consider:a,changed:u,moved:ke,get token(){return Y?void 0:E?.data.token},begin:Je,get restoring(){return Y},get handoffPending(){return Z},retireRestoration:Pe,end:Ke,updated:ct,invalidate:Le,async destroy(){V=!0,Le(),C.forEach(e=>e.forEach(i=>i.remove())),C.clear(),await Promise.allSettled([pe(Promise.resolve().then(()=>P?.remove())),pe(Promise.resolve().then(()=>Ze?.remove()))])}}},rt=async(t,r,o)=>{let v=await Et(t,r,o),h=v.consider;return Object.assign(v,{consider:(d,p)=>h(d,p.map(T=>({...T,scope:t})))})};var st=(t,r)=>{if(t.nodeType!==1||t.namespaceURI!=="http://www.w3.org/1999/xhtml"||!t.ownerDocument||!("style"in t))throw new TypeError("A Web projection scope requires an HTMLElement fallback");if(t.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 be a container element; use the existing fallback container");return Object.freeze({element:t,namespace:r})};var at=async(t,r,o={addListener:async(v,h)=>globalThis.Capacitor.addListener(t.plugin,v,h)})=>{let v="view",h=t.receiveListeners,d=!1,p=!1,T="native",f="native",M=et(document),C=()=>T==="native"&&m&&M.isCovered(m)?"web":T,j=!1,B=!1,S=!1,y=!1,l,g,c,D=!1,H=()=>{let a=D;D=!0;try{c?.begin()}finally{D=a}},m,W={},A,N=0,oe=()=>{N++,A=void 0;let a=Ne(m);a?.owner===W&&(a.projected=!1,a.owner=void 0)},F={listen:a=>r.listen(u=>{a(u),!u.active&&B&&(B=!1,he()),u.precovered&&Ie(u.precovered)}),send:a=>{if(!t.placementHandoff)return r.send(a);let u=Ne(m);if(a.action==="cover"){let $=A===a.token&&u?.owner===W&&u.projected,ne=++N;return A=a.token,u&&(u.owner=W,u.projected=!!$),r.send(a).then(Pe=>(ne===N&&u===Ne(m)&&u?.owner===W&&(u.projected=Pe?.shown===!0),Pe)).catch(Pe=>{throw ne===N&&u?.owner===W&&(oe(),u.invalidate()),Pe})}let P=a.action==="release"&&a.token===A,_=A&&(a.action==="restore"||P||a.action==="reset"||a.action==="visibility"&&a.visible);_&&N++;let ce=N,Q=()=>{ce!==N||u?.owner!==W||(oe(),u.invalidate())};try{let $=_&&u?.owner===W?{...a,token:"token"in a?a.token:A,placement:u.measure()}:a;return r.send($).then(async ne=>(a.action==="release"&&ne?.requiresPlacement&&(u&&(!u.owner||u.owner===W)?ne=await r.send({...a,placement:u.measure()}):(await r.send({action:"visibility",visible:!1}),ne=await r.send(a))),ce===N&&(a.action==="resume"||P||a.action==="reset"||a.action==="visibility")&&oe(),ne)).catch(ne=>{throw Q(),ne})}catch($){return Q(),Promise.reject($)}}},se=0,Se=0,O=0,Z=!1,ae=!1,J=!1,me=!1,R=document.createElement("style"),U=`data-native-ui-session-${t.id.replace(/[^a-zA-Z0-9-]/g,"")}`;R.textContent=`[${U}="native"], [${U}="native"] *, [${U}="none"], [${U}="none"] * {visibility:hidden!important}`,document.documentElement.append(R);let E=()=>{let a=document.querySelectorAll(t.projectionSelector);return a.length===1&&a[0]instanceof HTMLElement?a[0]:void 0},X=()=>{m&&(f==="web"||f==="native"&&S?m.removeAttribute(U):m.setAttribute(U,f))},Y=()=>{if(!m?.isConnected)return[];let a=m.getBoundingClientRect(),u={x:a.x,y:a.y,width:a.width,height:a.height},P=l&&Ge(m,l,()=>t.id);return[{element:m,scrollElement:l,control:{id:v,...u,...P?{scroll:P}:{}}}]},we=async()=>{if(d)return;let a=O,u=t.failedFallback==="native"&&C()==="native"&&!!m?.isConnected;await F.send({action:"visibility",visible:u}).catch(()=>{}),!(d||a!==O)&&(u||f==="none"?X():m?.removeAttribute(U))},We=()=>{if(!j||f!=="native"||!m?.isConnected||c?.token||c?.restoring)return;let a=t.failedFallback==="native";F.send({action:"visibility",visible:a}).catch(()=>{}),a?X():m.removeAttribute(U)},ee=()=>{if(d||D)return;if(We(),f==="web"&&c&&!c.token&&!c.restoring){F.send({action:"visibility",visible:!1}).catch(()=>{}),m?.removeAttribute(U);return}if(Z||!c?.restoring||f!=="native")return;Z=!0;let a=O,u=c;F.send({action:"resume"}).then(()=>{d||a!==O||u!==c||(S=!1,u.updated(void 0,[v],++se),X(),z())}).catch(()=>{a!==O||d||(u.invalidate(),we())}).finally(()=>{Z=!1,ee()})},ve=async()=>{if(d||ae||j)return;if(c?.restoring){ee();return}let a=E();if(a!==m){if(O++,oe(),m?.removeAttribute(U),await c?.destroy(),c=void 0,d)return;m=a,S=!1,V.disconnect(),m&&V.observe(m)}if(de(),f=C(),!m){await F.send({action:"visibility",visible:!1});return}if(!c){ae=!0;let u=O;try{let P=await rt(st(m,t.id),F,{wake:ee,captureDelayMs:t.captureDelayMs,retainPreparedUntilRelease:t.nativeDragHandoff,blocked:()=>f!=="native"||j||S&&!c?.token,revision:()=>se,captureRevision:t.captureMode==="view"?()=>Se:void 0,transient:!0,opaqueUnderlay:t.captureMode==="view"});if(d||u!==O){await P.destroy();return}c=P}finally{ae=!1}}if(!d){if(f==="native"&&S){await F.send({action:"visibility",visible:!1}),X();return}if(f==="native"){let u=O;if(await F.send({action:"visibility",visible:!0}),d||u!==O||f!=="native")return;X(),l=[...Oe(m)].find(Q=>{let $=getComputedStyle(Q);return/auto|scroll/.test($.overflowX+$.overflowY)&&(Q.scrollHeight>Q.clientHeight||Q.scrollWidth>Q.clientWidth)});let P=l&&Ge(m,l,()=>t.id),_=t.nativeDragHandoff&&t.captureMode==="view"&&l?{viewport:l.getBoundingClientRect(),offsetX:l.scrollLeft,offsetY:l.scrollTop,rangeX:Math.max(0,l.scrollWidth-l.clientWidth),rangeY:Math.max(0,l.scrollHeight-l.clientHeight)}:void 0,ce=await F.send({action:"track",viewportWidth:innerWidth,...P?{scroll:P}:{},..._?{gesture:_}:{}});if(d||u!==O)return;y=ce?.tracked===!0,c.consider(t.captureMode==="view"?Se:++se,Y())}else await F.send({action:"visibility",visible:!1}),X()}},z=()=>{d||(J=!0,!(!p||me)&&(me=!0,Promise.resolve().then(async()=>{try{for(;J&&!d;)J=!1,await ve()}catch{await we()}finally{me=!1,J&&z()}})))},be=()=>{c?.invalidate(),z()},V=new ResizeObserver(be);window.addEventListener("resize",be);let te=new Set,q=new WeakSet,le=a=>{let u=a.composedPath()[0]??a.target;if(!(d||f!=="native"||!m||q.has(a)||u!==document&&![...Oe(m)].includes(u))){if(q.add(a),y&&u===l){c?.changed(u);return}j?t.captureMode==="view"?c?.moved():c?.changed():((Z||c?.restoring)&&O++,j=!0,H(),S=t.failedFallback!=="native",c?.token?m.removeAttribute(U):We()),he()}};function he(){clearTimeout(g),g=setTimeout(()=>{B||(j=!1,!(d||f!=="native")&&(c?.end(),c?.restoring||z()))},100)}function Ie(a){if(d||f!=="native"||!m?.isConnected||!c||(B=!0,c.token))return;(Z||c.restoring)&&O++,A=a;let u=Ne(m);if(u&&(u.owner=W,u.projected=!0),j=!0,H(),!c.token){j=!1,we();return}he()}let de=()=>{let a=new Set(m?Oe(m):[]),u=a.size!==te.size||[...a].some(P=>!te.has(P));for(let P of te)a.has(P)||(P.removeEventListener("scroll",le),P.removeEventListener("slotchange",He),te.delete(P));for(let P of a)te.has(P)||(P.addEventListener("scroll",le,{passive:!0}),P.addEventListener("slotchange",He),te.add(P));u&&(y=!1,l=void 0,c?.invalidate(),z())},He=()=>{de(),z()};document.addEventListener("scroll",le,!0);let xe=new MutationObserver(a=>{de(),E()!==m?z():m&&a.some(u=>u.type==="childList")&&Ee(C())});xe.observe(document.documentElement,{childList:!0,attributes:!0,subtree:!0});let Ee=(a,u=!1)=>{if(d||!u&&a===f)return;j=!1,B=!1,clearTimeout(g),f=a,O++;let P=O;f==="none"?F.send({action:"visibility",visible:!1}).then(()=>{f!=="none"||P!==O||d||(c?.invalidate(!0),X())}).catch(()=>{f!=="none"||P!==O||d||(c?.invalidate(!0),X())}):f==="web"?(H(),c?.token||F.send({action:"visibility",visible:!1}).catch(()=>{}),X()):f==="native"&&(c?.end(),c?.restoring||z())},Xe=M.subscribe(()=>queueMicrotask(()=>Ee(C()))),ue,Ae=async()=>{if(!d){d=!0,oe(),O++,Xe(),xe.disconnect(),V.disconnect(),window.removeEventListener("resize",be),document.removeEventListener("scroll",le,!0);for(let a of te)a.removeEventListener("scroll",le),a.removeEventListener("slotchange",He);te.clear(),clearTimeout(g),m?.removeAttribute(U),R.remove(),await Promise.allSettled([Promise.resolve(ue?.remove()),c?.destroy()])}};try{let a=O;ue=await ze({addListener:(P,_)=>o.addListener(P,_),update:h?.update,projection:h?.projection,accepts:P=>!h?.match||Object.entries(h?.match).every(([_,ce])=>typeof P=="object"&&P!==null&&P[_]===ce)},(P,_)=>{d||(P&&(P==="native"&&(S=!1),T=P,F.send({action:"destination",destination:P}).catch(()=>{}),Ee(C(),!0)),_&&(Se++,c?.changed(),f==="native"&&z()))});let u=await F.send({action:"connect"});return O===a&&["native","web","none"].includes(u?.destination)&&(T=u.destination),f=C(),p=!0,z(),{destroy:Ae,show:()=>{S=!1,T="native",Ee(C(),!0)},hide:()=>{T=f="none",O++,j=!1,B=!1,clearTimeout(g),c?.invalidate(!0),X()}}}catch(a){throw await Ae(),a}};var ye=new Map,ge=new Map,$e=new Map,Pt=Array.from(crypto.getRandomValues(new Uint32Array(4)),t=>t.toString(16)).join("-"),Ve=new Map,Tt=(t,r,o)=>Ve.get(t)?.get(r)?.(o),Lt=0,Mt=(t,r)=>new Promise((o,v)=>{let h=`${t}:${Pt}:${++Lt}`,d=setTimeout(()=>{ge.delete(h),v(new Error("Native UI session timed out"))},5e3);ge.set(h,{resolve:o,reject:v,timer:d});let p=JSON.stringify({key:h,request:r}),T=globalThis,f=T.webkit?.messageHandlers?.[t]??T[t]??T.nativeUIEngine;if(!f){clearTimeout(d),ge.delete(h),v(new Error("Native UI session unavailable"));return}try{f.postMessage(p)}catch(M){clearTimeout(d),ge.delete(h),v(M)}}),jt=(t,r,o,v)=>{if(r===null){$e.get(t)?.(o);return}let h=ge.get(r);!h||!r.startsWith(`${t}:`)||(ge.delete(r),clearTimeout(h.timer),v?h.reject(new Error(v)):h.resolve(o))},St=t=>{ye.get(t)?.show()},Ht=t=>(ye.get(t)?.hide(),!0),xt=async t=>{let r=ye.get(t);ye.delete(t),Ve.delete(t),$e.delete(t);for(let[o,v]of ge)o.startsWith(`${t}:`)&&(ge.delete(o),clearTimeout(v.timer),v.reject(new Error("Native UI session closed")));await r?.destroy()},kt=async t=>{if(ye.has(t.id))return;let r,o=!1,v,h={show:()=>{v="native",r?.show()},hide:()=>{v="none",r?.hide()},destroy:async()=>{o=!0,await r?.destroy()}};ye.set(t.id,h);try{if(document.readyState==="loading"&&await new Promise(p=>document.addEventListener("DOMContentLoaded",()=>p(),{once:!0})),o)return;let d=new Map;t.runtime==="cordova"&&Ve.set(t.id,d),r=await at(t,{send:p=>Mt(t.id,p),listen:async p=>($e.set(t.id,p),{remove:()=>{$e.get(t.id)===p&&$e.delete(t.id)}})},t.runtime==="cordova"?{addListener:async(p,T)=>(d.set(p,T),{remove:()=>{d.delete(p)}})}:void 0),v==="none"?r.hide():v==="native"&&r.show(),o&&await r.destroy()}catch(d){throw ye.get(t.id)===h&&(ye.delete(t.id),Ve.delete(t.id)),d}};return mt(Ct);})();
22
+ `;var Pt=(t,r=!1)=>JSON.stringify(t.map(({control:o})=>[o.id,o.kind,o.placement,r?0:o.x,r?0:o.y,o.width,o.height,o.dark,o.rtl])),Tt=async(t,r,o)=>{let v=o.captureDelayMs??50;if(!Number.isFinite(v)||v<0||v>2147483647)throw new RangeError("captureDelayMs must be a finite, nonnegative timer interval");let h="native",m=t.element.ownerDocument,f=m.defaultView,P=Array.from(f.crypto.getRandomValues(new Uint32Array(4)),e=>e.toString(16)).join("-"),p=e=>e.isConnected&&e.ownerDocument===m,L=e=>!!e?.isConnected&&e.ownerDocument===m,x=new Map,M=new Set,B=e=>{M.delete(e),e.style.setProperty("display","none","important"),e.removeAttribute("data-native-ui-projection-image"),e.removeAttribute("src"),Pe()},H=r.send.bind(r),g="data-native-ui-snapshot",l=`${t.namespace}:sources-change`,w=new Set,c=new Set,N=new Set,k=new WeakMap,d=0,V=e=>JSON.stringify(e.map(i=>{k.has(i.scope)||k.set(i.scope,++d);let n=o.transient&&!o.opaqueUnderlay?i.scope.element.getBoundingClientRect():void 0,s=n?{...i,control:{...i.control,x:i.control.x-n.x,y:i.control.y-n.y}}:i;return[k.get(i.scope),Pt([s],!!o.opaqueUnderlay)]})),F=new Map,G=e=>{let i=e.getRootNode();return i.nodeType===11&&"host"in i?i:m},pe=e=>{if(F.has(e))return;let i=m.createElement("style");i.setAttribute("data-native-ui-snapshot-base",""),i.textContent=rt+(e.nodeType===11?st:""),(e.nodeType===11?e:e.head).append(i),F.set(e,i)},Y=e=>{let i=[...e.getAnimations({subtree:!0}),...[...K.get(e)??[]].flatMap(n=>n.shadowRoot?.getAnimations()??[])];for(let n of i)"transitionProperty"in n&&n.transitionProperty==="visibility"&&n.cancel()},K=new Map,ne=e=>{let i=K.get(e)??new Set;K.set(e,i);let n=e.hasAttribute("data-native-ui-snapshot-source")||e.hasAttribute("data-native-ui-snapshot-prepared"),s=T=>{if(!T.hasAttribute("data-native-ui-projection-node")){if(T.shadowRoot){i.add(T),pe(T.shadowRoot);for(let S of T.shadowRoot.children)s(S)}for(let S of T.children)s(S)}};s(e);for(let T of i)T.toggleAttribute("data-native-ui-snapshot-content",n);Y(e)},le=()=>`${f.innerWidth}:${f.innerHeight}:${f.devicePixelRatio}`,A=!1,se=!1,Q=0,O=0,j,de,b,me=!1,z=!1,X=0,ve=0,$=0,he=0,ae=0,Se=-1,q=!1,We=!1,W,Pe=()=>{let e=new Set(W?.candidates.map(n=>n.scope.element));for(let[n,s]of x){let T=!q&&n.isConnected&&e.has(n);if(T&&(b||z||A))continue;let S=T?2*Math.max(1,j?.scopes.filter(C=>C.element===n).length??0):0,R=Math.max(0,S-s.filter(C=>M.has(C)).length),U=s.filter(C=>M.has(C)||R-- >0?!0:(C.remove(),!1));U.length?x.set(n,U):x.delete(n)}let i=new Set([...W?.candidates.map(n=>n.scope.element.getRootNode())??[],...b?.scopes.map(n=>n.element.getRootNode())??[],...j?.scopes.map(n=>n.element.getRootNode())??[],...[...M].map(n=>n.getRootNode()),...[...K.values()].flatMap(n=>[...n].map(s=>s.shadowRoot))]);for(let[n,s]of F)n.nodeType===11&&!i.has(n)&&(s.remove(),F.delete(n))},ye=new Set,oe=(e,i,n)=>{let s=()=>{ye.delete(s)&&(H({action:e,token:i}).catch(()=>{}),e==="restored"&&Pe())};ye.add(s),n&&!q?f.requestAnimationFrame(()=>f.requestAnimationFrame(s)):s()},ge=new Set,we=()=>{ge.forEach(e=>N.delete(e)),ge.clear()},Te=(e,i=!1)=>{!e||e===j||e===b||(e.images.forEach(n=>{B(n)}),oe("release",e.data.token,i))},Ue=e=>e.filter(i=>{if(!L(i.scope.element)||![i.element,...i.sources??[]].every(p))return!1;if(i.control.scroll){let n=i.element.getBoundingClientRect(),s=i.control.scroll.clip;if(n.right<=s.x||n.bottom<=s.y||n.left>=s.x+s.width||n.top>=s.y+s.height)return!1}return o.eligible?.(i)??!0}),xe=async()=>{if(he=0,q||h!=="native"||We||me||b||o.blocked()||!W||j?.revision===W.revision&&Se===ae)return;let{revision:e,candidates:i}=W,n=$,s=ae,T=le(),S=Ue(i);if(!S.length)return;We=!0;let R,U=!1,C=[];try{let ie=!1,re=H({action:"prepare",revision:o.captureRevision?.()??e,viewportWidth:f.innerWidth,ids:S.map(E=>E.control.id)});re.then(E=>{ie&&E.token&&H({action:"release",token:E.token}).catch(()=>{})}).catch(()=>{});try{R=await fe(re)}finally{ie=!0}if(!R.token||!R.parts?.length)return;let Ce=new Set(S.map(E=>E.control.id));if(R.parts.some(E=>!E.url.startsWith("file:")&&!/^\/__native_ui_engine__\/nativeUI[a-fA-F0-9]+\/[a-fA-F0-9-]+-[0-9]+\.png$/.test(E.url)||!E.ids.length||E.ids.some(_=>!Ce.has(_))||![E.x,E.y,E.width,E.height].every(Number.isFinite)||E.width<=0||E.height<=0)||new Set(R.parts.flatMap(E=>E.ids)).size!==Ce.size||R.parts.some(E=>new Set(E.ids.map(_=>S.find(Me=>Me.control.id===_).scope)).size!==1)||(await fe(Promise.all(R.parts.map(async E=>{let _=S.find(Ye=>E.ids.includes(Ye.control.id)).scope.element,Me=x.get(_);Me||(Me=[],x.set(_,Me));let ue=Me.find(Ye=>!M.has(Ye));return ue||(ue=m.createElement("img"),ue.setAttribute("data-native-ui-projection-node",""),Me.push(ue)),M.add(ue),C.push(ue),ue.src=it(E.url),await ue.decode(),ue}))),q||h!=="native"||me||b||o.blocked()||$!==n||le()!==T||W?.revision!==e||S.some(E=>!L(E.scope.element)||![E.element,...E.sources??[]].every(p))))return;let Oe=j,et={data:R,images:C,sources:Array.from(new Set(S.flatMap(E=>[E.element,...E.sources??[]]))),targets:R.parts.map(E=>S.find(_=>E.ids.includes(_.control.id)).element),scopes:R.parts.map(E=>S.find(_=>E.ids.includes(_.control.id)).scope),parents:R.parts.map(E=>S.find(_=>E.ids.includes(_.control.id)).scope.element.parentNode),frames:[],viewport:T,revision:e,layout:V(S)};try{if(!Ve(et))throw new Error("Projection destination is unavailable")}catch(E){u();try{Ve(),j?.sources.forEach(_=>{N.add(_),ge.add(_)})}catch{y()}throw E}j=et,we(),j.sources.forEach(E=>{N.add(E),ge.add(E)}),o.opaqueUnderlay&&H({action:"prepared",token:R.token}).catch(()=>{}),f.dispatchEvent(new Event(l)),Se=s,U=!0,Te(Oe)}catch{}finally{U||C.forEach(ie=>{B(ie)}),R?.token&&!U&&H({action:"release",token:R.token}).catch(()=>{}),We=!1,W&&(W.revision!==e||ae!==s)&&ce()}},ce=()=>{f.clearTimeout(he),!q&&h==="native"&&!b&&!me&&!o.blocked()&&(he=f.setTimeout(()=>{xe()},v))},De,He=(e,i)=>{if(i.some(s=>s.scope.element.ownerDocument!==m||[s.element,...s.sources??[]].some(T=>T.ownerDocument!==m)))throw new TypeError("All projection sources and fallback scopes must share one Document");if(o.blocked())return;!b&&j&&j.layout!==V(Ue(i))&&Le();let n=V(i);De!==n&&(De=n,ae++,$++),W?.revision!==e&&$++,W={revision:e,candidates:i},Pe(),(j?.revision!==e||Se!==ae)&&ce()},Be=e=>{!b&&j&&e&&W?.candidates.some(i=>i.scrollElement===e&&j.sources.includes(i.element))&&Le(),b&&Re(),ae++,$++,ce()},Ne=await ze(r.listen(({active:e})=>{me=e,$++,f.clearTimeout(he),e||ce()})),ke=new Set,a=e=>{K.get(e)?.forEach(i=>i.removeAttribute("data-native-ui-snapshot-content")),ke.delete(e)&&e.removeAttribute("data-native-ui-snapshot-prepared"),e.removeAttribute("data-native-ui-snapshot-visible"),e.removeAttribute(g),e.removeAttribute("data-native-ui-snapshot-positioned"),e.removeAttribute("data-native-ui-snapshot-source"),Y(e),K.delete(e)},u=()=>{O++,f.cancelAnimationFrame(Q);for(let e of Fe.keys())e.style.setProperty("opacity","0","important");Fe.clear(),Q=0,A=!1,se=!1,we(),b?.sources.forEach(e=>{w.delete(e),c.delete(e)}),D.forEach(a),D.clear(),F.forEach((e,i)=>{(q||i.nodeType===11&&!i.host.isConnected)&&(e.remove(),F.delete(i))})},y=(e=b,i=z)=>{if(b!==e)return;let n=new Set([b,j]);u(),b=void 0,j=void 0,z=!1,f.clearTimeout(X),n.forEach(s=>Te(s,i)),o.wake()},I=()=>{if(!b)return;if(j!==b){y();return}let e=b;O++,f.cancelAnimationFrame(Q),Q=0,A=se=!1,e.sources.forEach(i=>{w.delete(i),c.delete(i)}),D.forEach(i=>i.removeAttribute("data-native-ui-snapshot-source")),ee(e,!!o.opaqueUnderlay),b=void 0,z=!1,f.clearTimeout(X),oe("restored",e.data.token,!0),e.sources.forEach(i=>{N.add(i),ge.add(i)}),o.wake()},J=()=>{z&&I(),ye.forEach(e=>e())},Z=e=>e.scopes.map(i=>L(i.element)?i.element:void 0),D=new Set,ee=(e,i)=>{for(let n of D)n.toggleAttribute("data-native-ui-snapshot-visible",i),ne(n);e.images.forEach(n=>n.style.setProperty("opacity",i?"1":"0","important"))},te=new Map,Fe=new Map,Re=()=>{let e=[];for(let[i,n]of Fe){let{target:s,frame:T}=n;if(!s.isConnected){i.remove();continue}let S=s.shadowRoot??s;i.parentNode!==S&&S.insertBefore(i,null);let R=s.getBoundingClientRect(),U=i.getBoundingClientRect(),C=parseFloat(i.style.width)||T.width,ie=parseFloat(i.style.height)||T.height,re=U.width/C||1,Ce=U.height/ie||1,Oe=n.scale??(n.scale={x:re,y:Ce});e.push({element:i,left:(parseFloat(i.style.left)||0)+(R.x+T.x*re/Oe.x-U.x)/re,top:(parseFloat(i.style.top)||0)+(R.y+T.y*Ce/Oe.y-U.y)/Ce,width:T.width/Oe.x,height:T.height/Oe.y})}for(let{element:i,...n}of e)for(let[s,T]of Object.entries(n)){let S=`${T}px`;i.style.getPropertyValue(s)!==S&&i.style.setProperty(s,S,"important")}},Ve=(e=b??j)=>{if(!e||z)return!1;if(!e.sources.every(p)||Z(e).some(n=>!n))return Le(),!1;let i=Z(e);if(b){if(i.some(s=>!s?.isConnected||!F.has(G(s))))return y(b,!1),!1;let n=new Set;for(let[s,T]of e.targets.entries()){let S=te.get(T),R=i[s];!S||!R?.isConnected||(R.setAttribute(g,S),R.setAttribute("data-native-ui-snapshot-source",""),n.add(R))}for(let s of D)n.has(s)||a(s);return D.clear(),n.forEach(s=>D.add(s)),ee(e,se),!0}x.forEach(n=>n.forEach(s=>{e.images.includes(s)||(s.style.setProperty("display","none","important"),s.removeAttribute("data-native-ui-projection-image"))})),te.clear(),Fe.clear(),D.forEach(a),D.clear();for(let n of new Set(i)){if(!n)continue;D.add(n),pe(G(n));let s=i.flatMap((U,C)=>U===n?[C]:[]),T=`${P}-${s[0]}`;n.setAttribute(g,T),n!==e.targets[s[0]]&&(n.setAttribute("data-native-ui-snapshot-prepared",""),ke.add(n)),n.toggleAttribute("data-native-ui-snapshot-visible",!!o.opaqueUnderlay),ne(n);let S=n.getBoundingClientRect();n.toggleAttribute("data-native-ui-snapshot-positioned",f.getComputedStyle(n).position==="static");let R=n.shadowRoot??n;for(let U of[...s].reverse()){let C=e.images[U],ie=e.data.parts[U],re=e.frames[U]??{x:o.opaqueUnderlay?0:ie.x-S.x,y:o.opaqueUnderlay?0:ie.y-S.y,width:ie.width,height:ie.height};e.frames[U]=re,te.set(e.targets[U],T),C.inert=!0,C.setAttribute("aria-hidden","true"),C.setAttribute("data-native-ui-projection-image",T),C.style.cssText="all:initial!important;position:absolute!important;display:block!important;visibility:visible!important;pointer-events:none!important;image-rendering:pixelated!important;will-change:opacity!important;opacity:0!important;",C.style.setProperty("width",`${re.width}px`,"important"),C.style.setProperty("height",`${re.height}px`,"important"),C.style.setProperty("left","0px","important"),C.style.setProperty("top","0px","important"),C.style.setProperty("opacity",o.opaqueUnderlay?"1":"0","important"),C.parentNode!==R&&R.insertBefore(C,null),Fe.set(C,{target:n,frame:re})}}return e.parents=e.scopes.map(n=>n.element.parentNode),Re(),!0},Ge=e=>e.images.map((i,n)=>{let s=i.getBoundingClientRect();return{...e.data.parts[n],x:s.x,y:s.y,width:s.width,height:s.height}}),Ke=()=>{if(J(),q||h==="none"||b)return;let e=j;if(!e||e.viewport!==le()||!e.sources.every(p)||o.current&&!o.current(e.targets)||e.targets.some(s=>!te.has(s))||Z(e).some(s=>!s?.isConnected)||[...D].some(s=>!s.isConnected))return;e.scopes.some((s,T)=>s.element.parentNode!==e.parents[T])&&Ve(),Re();let i=++O,n=()=>O===i;A=!0,se=!!o.opaqueUnderlay,b=e,$++,f.clearTimeout(he),e.sources.forEach(s=>{w.add(s)}),D.forEach(s=>s.setAttribute("data-native-ui-snapshot-source","")),ee(e,se),Q=f.requestAnimationFrame(()=>{Q=0,n()&&(A=!1,o.wake())}),fe(H({action:"cover",token:e.data.token,...o.transient?{transient:!0,viewportWidth:f.innerWidth,parts:Ge(e)}:{}})).then(s=>{if(n()){if(A=!1,s?.shown!==!0){y(e,!1);return}z||(se=!0,e.sources.forEach(T=>c.add(T)),Ve(),Re()),f.cancelAnimationFrame(Q),Q=f.requestAnimationFrame(()=>{Q=0,n()&&H({action:"painted",token:e.data.token}).catch(()=>{n()&&y(e,!1)})}),o.wake()}}).catch(()=>{n()&&y(e,!1)})},Ze=()=>{if(!(!b||q||z)){if(!b.sources.every(p)||Z(b).some(e=>!e)||o.current&&!o.current(b.targets)){y(b,!1);return}if(!o.blocked()){if(Re(),o.transient){let e=b;if(Z(e).some(s=>!s?.isConnected)){y(e,!1);return}let n=Ge(e);o.opaqueUnderlay||n.every((s,T)=>s.width===e.data.parts[T].width&&s.height===e.data.parts[T].height)?e.data={...e.data,parts:n}:j=void 0,H({action:"restore",token:e.data.token,viewportWidth:f.innerWidth,parts:n}).catch(()=>{})}ve=o.revision?.()??W?.revision??0,z=!0,b.sources.forEach(e=>w.delete(e)),X=f.setTimeout(()=>{z&&y(b,!1)},250),o.wake()}}},dt=(e,i,n)=>{!z||!b||n===void 0||n<=ve||!o.transient&&e||b.data.parts.every(s=>s.ids.every(T=>i?.includes(T)))&&(I(),ce())},Le=(e=!1)=>{ye.forEach(n=>n()),$++,f.clearTimeout(he);let i=j;if(j=void 0,(e||q)&&(de=void 0),o.retainPreparedUntilRelease&&!b&&!e&&!q){if(i){let n={};de=n,fe(H({action:"release",token:i.data.token})).catch(()=>{}).finally(()=>{if(i.images.forEach(s=>{B(s)}),de===n&&(de=void 0,!(j||b))){for(let s of D)s.getAttribute(g)!==te.get(s)&&(D.delete(s),ke.delete(s));u(),te.clear(),f.dispatchEvent(new Event(l))}})}if(de)return}Te(i),b?y(b,!1):(u(),te.clear(),f.dispatchEvent(new Event(l)))},Qe;try{o.events&&(Qe=await qe(o.events,(e,i)=>{if(!q){if(e&&(h=e),h==="none"){Le();return}e==="web"&&Ke(),e==="native"&&(Ze(),ce()),i&&Be()}}))}catch(e){throw q=!0,Le(),await fe(Promise.resolve(Ne.remove())).catch(()=>{}),e}return{get owners(){return[...w]},get visibleOwners(){return[...c]},get preparedOwners(){return[...N]},consider:He,changed:Be,moved:Re,get token(){return z?void 0:b?.data.token},begin:Ke,get restoring(){return z},get handoffPending(){return A},retireRestoration:J,end:Ze,updated:dt,invalidate:Le,async destroy(){q=!0,Le(),x.forEach(e=>e.forEach(i=>i.remove())),x.clear(),await Promise.allSettled([fe(Promise.resolve().then(()=>Ne?.remove())),fe(Promise.resolve().then(()=>Qe?.remove()))])}}},at=async(t,r,o)=>{let v=await Tt(t,r,o),h=v.consider;return Object.assign(v,{consider:(m,f)=>h(m,f.map(P=>({...P,scope:t})))})};var ct=(t,r)=>{if(t.nodeType!==1||t.namespaceURI!=="http://www.w3.org/1999/xhtml"||!t.ownerDocument||!("style"in t))throw new TypeError("A Web projection scope requires an HTMLElement fallback");if(t.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 be a container element; use the existing fallback container");return Object.freeze({element:t,namespace:r})};var lt=async(t,r,o={addListener:async(v,h)=>globalThis.Capacitor.addListener(t.plugin,v,h)})=>{let v="view",h=t.receiveListeners,m=!1,f=!1,P="native",p="native",L=nt(document),x=()=>P==="native"&&d&&L.isCovered(d)?"web":P,M=!1,B=!1,H=!1,g=!1,l,w,c,N=!1,k=()=>{let a=N;N=!0;try{c?.begin()}finally{N=a}},d,V=0,F,G=()=>{V++,F?.(),F=void 0},pe=async()=>{if(!t.values||!d||m)return;let a=d;G();let u=V,y=()=>!m&&u===V&&d===a&&ve()===a&&a.isConnected;if(a.localName.includes("-")&&!a.matches(":defined")&&Object.values(t.values).some(J=>J.property)){if(await Promise.race([a.ownerDocument.defaultView.customElements.whenDefined(a.localName),new Promise(J=>{F=J})]),!y())return;F=void 0}let I=await r.send({action:"values"}).catch(()=>{});if(y()&&I?.values&&typeof I.values=="object"&&!Array.isArray(I.values))try{tt(a,I.values,t.values)}catch(J){console.warn("[Native UI Engine] Cannot reflect values",J)}},Y={},K,ne=0,le=()=>{ne++,K=void 0;let a=$e(d);a?.owner===Y&&(a.projected=!1,a.owner=void 0)},A={listen:a=>r.listen(u=>{a(u),!u.active&&B&&(B=!1,Te()),u.precovered&&Ue(u.precovered)}),send:a=>{if(!t.placementHandoff)return r.send(a);let u=$e(d);if(a.action==="cover"){let D=K===a.token&&u?.owner===Y&&u.projected,ee=++ne;return K=a.token,u&&(u.owner=Y,u.projected=!!D),r.send(a).then(te=>(ee===ne&&u===$e(d)&&u?.owner===Y&&(u.projected=te?.shown===!0),te)).catch(te=>{throw ee===ne&&u?.owner===Y&&(le(),u.invalidate()),te})}let y=a.action==="release"&&a.token===K,I=K&&(a.action==="restore"||y||a.action==="reset"||a.action==="visibility"&&a.visible);I&&ne++;let J=ne,Z=()=>{J!==ne||u?.owner!==Y||(le(),u.invalidate())};try{let D=I&&u?.owner===Y?{...a,token:"token"in a?a.token:K,placement:u.measure()}:a;return r.send(D).then(async ee=>(a.action==="release"&&ee?.requiresPlacement&&(u&&(!u.owner||u.owner===Y)?ee=await r.send({...a,placement:u.measure()}):(await r.send({action:"visibility",visible:!1}),ee=await r.send(a))),J===ne&&(a.action==="resume"||y||a.action==="reset"||a.action==="visibility")&&le(),ee)).catch(ee=>{throw Z(),ee})}catch(D){return Z(),Promise.reject(D)}}},se=0,Q=0,O=0,j,de=!1,b=!1,me=!1,z=document.createElement("style"),X=`data-native-ui-session-${t.id.replace(/[^a-zA-Z0-9-]/g,"")}`;z.textContent=`[${X}="native"], [${X}="native"] *, [${X}="none"], [${X}="none"] * {visibility:hidden!important}`,document.documentElement.append(z);let ve=()=>{let a=document.querySelectorAll(t.projectionSelector);return a.length===1&&a[0]instanceof HTMLElement?a[0]:void 0},$=()=>{let a=x();d&&d.getAttribute("data-native-ui-destination")!==a&&d.setAttribute("data-native-ui-destination",a),d&&(p==="web"||p==="native"&&H?d.removeAttribute(X):d.setAttribute(X,p))},he=()=>{if(!d?.isConnected)return[];let a=d.getBoundingClientRect(),u={x:a.x,y:a.y,width:a.width,height:a.height},y=l&&_e(d,l,()=>t.id);return[{element:d,scrollElement:l,control:{id:v,...u,...y?{scroll:y}:{}}}]},ae=async()=>{if(m)return;let a=O,u=t.failedFallback==="native"&&x()==="native"&&!!d?.isConnected;await A.send({action:"visibility",visible:u}).catch(()=>{}),!(m||a!==O)&&(u||p==="none"?$():d?.removeAttribute(X))},Se=()=>{if(!M||p!=="native"||!d?.isConnected||c?.token||c?.restoring)return;let a=t.failedFallback==="native";A.send({action:"visibility",visible:a}).catch(()=>{}),a?$():d.removeAttribute(X)},q=()=>{if(m||N)return;if(Se(),p==="web"&&c&&!c.token&&!c.restoring){A.send({action:"visibility",visible:!1}).catch(()=>{}),d?.removeAttribute(X);return}if(!c?.restoring||j===c||p!=="native")return;j=c;let a=O,u=c;A.send({action:"resume"}).then(()=>{m||a!==O||u!==c||ve()!==d||(H=!1,u.updated(void 0,[v],++se),$(),W())}).catch(()=>{a!==O||m||(u.invalidate(),ae())}).finally(()=>{j===u&&(j=void 0),q()})},We=async()=>{if(m||de)return;if(ve()!==d){O++,M=!1,B=!1,clearTimeout(w),le(),d?.removeAttribute(X),d?.removeAttribute("data-native-ui-destination"),G();let u=c,y=d;c=void 0,d=void 0,ye.disconnect(),xe();try{y&&await A.send({action:"visibility",visible:!1})}finally{await u?.destroy()}if(m)return;d=ve(),H=!1,d&&(ye.observe(d),pe())}if(xe(),!M){if(c?.restoring){q();return}if(p=x(),!d){await A.send({action:"visibility",visible:!1});return}if(!c){de=!0;let u=O;try{let y=await at(ct(d,t.id),A,{wake:q,captureDelayMs:t.captureDelayMs,retainPreparedUntilRelease:t.nativeDragHandoff,blocked:()=>p!=="native"||M||H&&!c?.token,revision:()=>se,captureRevision:t.captureMode==="view"?()=>Q:void 0,transient:!0,opaqueUnderlay:t.captureMode==="view"});if(m||u!==O){await y.destroy();return}c=y}finally{de=!1}}if(!m){if(p==="native"&&H){await A.send({action:"visibility",visible:!1}),$();return}if(p==="native"){let u=O;if(await A.send({action:"visibility",visible:!0}),m||u!==O||p!=="native")return;$(),l=[...Ae(d)].find(Z=>{let D=getComputedStyle(Z);return/auto|scroll/.test(D.overflowX+D.overflowY)&&(Z.scrollHeight>Z.clientHeight||Z.scrollWidth>Z.clientWidth)});let y=l&&_e(d,l,()=>t.id),I=t.nativeDragHandoff&&t.captureMode==="view"&&l?{viewport:l.getBoundingClientRect(),offsetX:l.scrollLeft,offsetY:l.scrollTop,rangeX:Math.max(0,l.scrollWidth-l.clientWidth),rangeY:Math.max(0,l.scrollHeight-l.clientHeight)}:void 0,J=await A.send({action:"track",viewportWidth:innerWidth,...y?{scroll:y}:{},...I?{gesture:I}:{}});if(m||u!==O)return;g=J?.tracked===!0,c.consider(t.captureMode==="view"?Q:++se,he())}else await A.send({action:"visibility",visible:!1}),$()}}},W=()=>{m||(b=!0,!(!f||me)&&(me=!0,Promise.resolve().then(async()=>{try{for(;b&&!m;)b=!1,await We()}catch{await ae()}finally{me=!1,b&&W()}})))},Pe=()=>{c?.invalidate(),W()},ye=new ResizeObserver(Pe);window.addEventListener("resize",Pe);let oe=new Set,ge=new WeakSet,we=a=>{let u=a.composedPath()[0]??a.target;if(!(m||p!=="native"||!d||ge.has(a)||u!==document&&![...Ae(d)].includes(u))){if(ge.add(a),g&&u===l){c?.changed(u);return}M?t.captureMode==="view"?c?.moved():c?.changed():((j||c?.restoring)&&O++,M=!0,k(),H=t.failedFallback!=="native",c?.token?d.removeAttribute(X):Se()),Te()}};function Te(){clearTimeout(w),w=setTimeout(()=>{B||(M=!1,!(m||p!=="native")&&(c?.end(),c?.restoring||W()))},100)}function Ue(a){if(m||p!=="native"||!d?.isConnected||!c||(B=!0,c.token))return;(j||c.restoring)&&O++,K=a;let u=$e(d);if(u&&(u.owner=Y,u.projected=!0),M=!0,k(),!c.token){M=!1,ae();return}Te()}let xe=()=>{let a=new Set(d?Ae(d):[]),u=a.size!==oe.size||[...a].some(y=>!oe.has(y));for(let y of oe)a.has(y)||(y.removeEventListener("scroll",we),y.removeEventListener("slotchange",ce),oe.delete(y));for(let y of a)oe.has(y)||(y.addEventListener("scroll",we,{passive:!0}),y.addEventListener("slotchange",ce),oe.add(y));u&&(g=!1,l=void 0,c?.invalidate(),W())},ce=()=>{xe(),W()};document.addEventListener("scroll",we,!0);let De=new MutationObserver(a=>{xe(),ve()!==d?W():d&&a.some(u=>u.type==="childList")&&He(x())});De.observe(document.documentElement,{childList:!0,attributes:!0,subtree:!0});let He=(a,u=!1)=>{if(m||!u&&a===p)return;M=!1,B=!1,clearTimeout(w),p=a,O++;let y=O;p==="none"?A.send({action:"visibility",visible:!1}).then(()=>{p!=="none"||y!==O||m||(c?.invalidate(!0),$())}).catch(()=>{p!=="none"||y!==O||m||(c?.invalidate(!0),$())}):p==="web"?(k(),c?.token||A.send({action:"visibility",visible:!1}).catch(()=>{}),$()):p==="native"&&(c?.end(),c?.restoring||W())},Be=L.subscribe(()=>queueMicrotask(()=>He(x()))),Ne,ke=async()=>{if(!m){m=!0,G(),le(),O++,Be(),De.disconnect(),ye.disconnect(),window.removeEventListener("resize",Pe),document.removeEventListener("scroll",we,!0);for(let a of oe)a.removeEventListener("scroll",we),a.removeEventListener("slotchange",ce);oe.clear(),clearTimeout(w),d?.removeAttribute(X),d?.removeAttribute("data-native-ui-destination"),z.remove(),await Promise.allSettled([Promise.resolve(Ne?.remove()),c?.destroy()])}};try{let a=O;Ne=await qe({addListener:(y,I)=>o.addListener(y,I),update:h?.update,projection:h?.projection,accepts:y=>!h?.match||Object.entries(h?.match).every(([I,J])=>typeof y=="object"&&y!==null&&y[I]===J)},(y,I)=>{m||(y&&(y==="native"&&(H=!1),P=y,A.send({action:"destination",destination:y}).catch(()=>{}),He(x(),!0)),I&&(pe(),Q++,c?.changed(),p==="native"&&W()))});let u=await A.send({action:"connect"});return O===a&&["native","web","none"].includes(u?.destination)&&(P=u.destination),p=x(),f=!0,W(),{destroy:ke,show:()=>{H=!1,P="native",He(x(),!0)},hide:()=>{P=p="none",O++,M=!1,B=!1,clearTimeout(w),c?.invalidate(!0),$()}}}catch(a){throw await ke(),a}};var be=new Map,Ee=new Map,Ie=new Map,Lt=Array.from(crypto.getRandomValues(new Uint32Array(4)),t=>t.toString(16)).join("-"),Xe=new Map,Mt=(t,r,o)=>Xe.get(t)?.get(r)?.(o),jt=0,St=(t,r)=>new Promise((o,v)=>{let h=`${t}:${Lt}:${++jt}`,m=setTimeout(()=>{Ee.delete(h),v(new Error("Native UI session timed out"))},5e3);Ee.set(h,{resolve:o,reject:v,timer:m});let f=JSON.stringify({key:h,request:r}),P=globalThis,p=P.webkit?.messageHandlers?.[t]??P[t]??P.nativeUIEngine;if(!p){clearTimeout(m),Ee.delete(h),v(new Error("Native UI session unavailable"));return}try{p.postMessage(f)}catch(L){clearTimeout(m),Ee.delete(h),v(L)}}),xt=(t,r,o,v)=>{if(r===null){Ie.get(t)?.(o);return}let h=Ee.get(r);!h||!r.startsWith(`${t}:`)||(Ee.delete(r),clearTimeout(h.timer),v?h.reject(new Error(v)):h.resolve(o))},Ht=t=>{be.get(t)?.show()},kt=t=>(be.get(t)?.hide(),!0),Rt=async t=>{let r=be.get(t);be.delete(t),Xe.delete(t),Ie.delete(t);for(let[o,v]of Ee)o.startsWith(`${t}:`)&&(Ee.delete(o),clearTimeout(v.timer),v.reject(new Error("Native UI session closed")));await r?.destroy()},Ct=async t=>{if(be.has(t.id))return;let r,o=!1,v,h={show:()=>{v="native",r?.show()},hide:()=>{v="none",r?.hide()},destroy:async()=>{o=!0,await r?.destroy()}};be.set(t.id,h);try{if(document.readyState==="loading"&&await new Promise(f=>document.addEventListener("DOMContentLoaded",()=>f(),{once:!0})),o)return;let m=new Map;t.runtime==="cordova"&&Xe.set(t.id,m),r=await lt(t,{send:f=>St(t.id,f),listen:async f=>(Ie.set(t.id,f),{remove:()=>{Ie.get(t.id)===f&&Ie.delete(t.id)}})},t.runtime==="cordova"?{addListener:async(f,P)=>(m.set(f,P),{remove:()=>{m.delete(f)}})}:void 0),v==="none"?r.hide():v==="native"&&r.show(),o&&await r.destroy()}catch(m){throw be.get(t.id)===h&&(be.delete(t.id),Xe.delete(t.id)),m}};return ht(Ot);})();
@@ -11,9 +11,12 @@ public final class NativeUIScrollBindings {
11
11
  let previous = trackers
12
12
  trackers.removeAll()
13
13
  var rejected: [String] = []
14
+ // The filter guarantees every grouped node has a scroll placement.
15
+ // swiftlint:disable:next force_unwrapping
14
16
  for (id, group) in Dictionary(grouping: nodes.filter { $0.scroll != nil }, by: { $0.scroll!.id }) {
15
17
  if group.contains(where: { $0.scroll?.reuse == true }) {
16
18
  if let tracker = previous[id], tracker.replace(group, webView: webView, scale: scale) {
19
+ tracker.render = render
17
20
  trackers[id] = tracker
18
21
  } else { rejected.append(contentsOf: group.map(\.id)) }
19
22
  } else if let tracker = NativeUIScrollTracker(webView: webView, nodes: group, scale: scale, render: render) {
@@ -20,7 +20,7 @@ public struct NativeUIScrollPlacement: Decodable, Equatable {
20
20
 
21
21
  public var isValid: Bool {
22
22
  !id.isEmpty && viewport.isValid && clip.isValid && frame.isValid &&
23
- offsetX.isFinite && offsetY.isFinite && contentWidth.isFinite && contentHeight.isFinite && contentWidth > 0 && contentHeight > 0
23
+ offsetX.isFinite && offsetY.isFinite && contentWidth.isFinite && contentHeight.isFinite && contentWidth > 0 && contentHeight > 0
24
24
  }
25
25
  }
26
26
 
@@ -34,15 +34,15 @@ public final class NativeUIScrollTracker {
34
34
  private let contentOrigin: CGPoint
35
35
  private let viewportCorrection: CGPoint
36
36
  private var frames: [String: CGRect] = [:]
37
- private let render: (String, CGRect, CGRect) -> Void
37
+ var render: (String, CGRect, CGRect) -> Void
38
38
  private var observation: NSKeyValueObservation?
39
39
 
40
40
  public init?(webView: WKWebView, nodes: [NativeUIScrollNode], scale: CGFloat,
41
- render: @escaping (String, CGRect, CGRect) -> Void) {
41
+ render: @escaping (String, CGRect, CGRect) -> Void) {
42
42
  guard let placement = nodes.first?.scroll, placement.isValid,
43
43
  nodes.allSatisfy({ $0.scroll?.id == placement.id && $0.scroll?.viewport == placement.viewport &&
44
- $0.scroll?.clip == placement.clip && $0.scroll?.contentWidth == placement.contentWidth &&
45
- $0.scroll?.contentHeight == placement.contentHeight }), scale.isFinite, scale > 0 else { return nil }
44
+ $0.scroll?.clip == placement.clip && $0.scroll?.contentWidth == placement.contentWidth &&
45
+ $0.scroll?.contentHeight == placement.contentHeight }), scale.isFinite, scale > 0 else { return nil }
46
46
  let transform = CGAffineTransform(scaleX: scale, y: scale)
47
47
  let viewport = placement.viewport.rect.applying(transform)
48
48
  let candidates = Self.scrollViews(in: webView).filter { scroll in
@@ -76,9 +76,9 @@ public final class NativeUIScrollTracker {
76
76
  guard self.webView === webView, let scrollView, scrollView.isDescendant(of: webView),
77
77
  !scrollView.isHidden, scrollView.window != nil, self.scale == scale,
78
78
  nodes.allSatisfy({ node in
79
- guard let next = node.scroll else { return false }
80
- return next.id == placement.id && next.viewport == placement.viewport && next.clip == placement.clip &&
81
- next.contentWidth == placement.contentWidth && next.contentHeight == placement.contentHeight
79
+ guard let next = node.scroll else { return false }
80
+ return next.id == placement.id && next.viewport == placement.viewport && next.clip == placement.clip &&
81
+ next.contentWidth == placement.contentWidth && next.contentHeight == placement.contentHeight
82
82
  }) else { return false }
83
83
  setFrames(nodes)
84
84
  return true
@@ -86,6 +86,8 @@ public final class NativeUIScrollTracker {
86
86
 
87
87
  private func setFrames(_ nodes: [NativeUIScrollNode]) {
88
88
  frames = Dictionary(uniqueKeysWithValues: nodes.map {
89
+ // init and replace validate every scroll placement before reaching here.
90
+ // swiftlint:disable:next force_unwrapping
89
91
  ($0.id, $0.scroll!.frame.rect.applying(CGAffineTransform(scaleX: scale, y: scale))
90
92
  .offsetBy(dx: contentOrigin.x, dy: contentOrigin.y))
91
93
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webview-native-ui/engine",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
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",
@@ -33,20 +33,27 @@
33
33
  "lint": "npm run typecheck && eslint . --max-warnings 0 && npm run format:check",
34
34
  "format": "prettier --write \"**/*.{ts,js,mjs,cjs,json,md,yml,yaml,html}\"",
35
35
  "prepublishOnly": "npm run build",
36
- "test:release": "node --test scripts/native-ui-engine-release/publish.test.mjs",
36
+ "test:release": "node --test scripts/native-ui-engine-release/*.test.mjs",
37
37
  "test:package": "node scripts/verify-package.mjs",
38
38
  "release": "np --no-publish --no-tests --branch main",
39
39
  "test:cordova": "node scripts/verify-cordova.mjs",
40
- "format:check": "prettier --check \"**/*.{ts,js,mjs,cjs,json,md,yml,yaml,html}\""
40
+ "format:check": "prettier --check \"**/*.{ts,js,mjs,cjs,json,md,yml,yaml,html}\"",
41
+ "swiftlint": "node-swiftlint",
42
+ "lint:swift": "swiftlint version && node-swiftlint lint --strict --no-cache",
43
+ "format:swift": "swiftlint version && node-swiftlint --fix --format --no-cache",
44
+ "lint:android": "android/gradlew -p android ktfmtCheck lintRelease",
45
+ "format:kotlin": "android/gradlew -p android ktfmtFormat"
41
46
  },
42
47
  "devDependencies": {
43
48
  "@eslint/js": "^10.0.1",
49
+ "@ionic/swiftlint-config": "^2.0.0",
44
50
  "@playwright/test": "^1.58.0",
45
51
  "esbuild": "^0.27.0",
46
52
  "eslint": "^10.13.0",
47
53
  "globals": "^17.13.0",
48
54
  "np": "^10.2.0",
49
55
  "prettier": "^3.6.2",
56
+ "swiftlint": "^2.0.0",
50
57
  "typescript": "^5.9.3",
51
58
  "typescript-eslint": "^8.72.1"
52
59
  },