@webview-native-ui/engine 0.0.5 → 0.0.6
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.
- package/dist/projection/web.js +22 -12
- package/dist/types.d.ts +4 -0
- package/docs/projection.md +16 -12
- package/ios/Sources/WebViewNativeUIEngine/CoveredSnapshots.swift +2 -2
- package/ios/Sources/WebViewNativeUIEngine/NativeUIProjection.swift +35 -22
- package/ios/Sources/WebViewNativeUIEngine/Resources/native-ui-engine.js +2 -2
- package/ios/Sources/WebViewNativeUIEngine/ScrollBindings.swift +1 -0
- package/ios/Sources/WebViewNativeUIEngine/ScrollTracker.swift +1 -1
- package/package.json +2 -2
package/dist/projection/web.js
CHANGED
|
@@ -44,7 +44,11 @@ const createProjectionEngine = async (scope, transport, callbacks) => {
|
|
|
44
44
|
const signature = (candidates) => JSON.stringify(candidates.map((candidate) => {
|
|
45
45
|
if (!scopeIDs.has(candidate.scope))
|
|
46
46
|
scopeIDs.set(candidate.scope, ++scopeSerial);
|
|
47
|
-
|
|
47
|
+
const origin = callbacks.transient && !callbacks.opaqueUnderlay ? candidate.scope.element.getBoundingClientRect() : undefined;
|
|
48
|
+
const relative = origin
|
|
49
|
+
? { ...candidate, control: { ...candidate.control, x: candidate.control.x - origin.x, y: candidate.control.y - origin.y } }
|
|
50
|
+
: candidate;
|
|
51
|
+
return [scopeIDs.get(candidate.scope), layout([relative], !!callbacks.opaqueUnderlay)];
|
|
48
52
|
}));
|
|
49
53
|
const sheets = new Map();
|
|
50
54
|
const rootFor = (element) => {
|
|
@@ -609,6 +613,10 @@ const createProjectionEngine = async (scope, transport, callbacks) => {
|
|
|
609
613
|
updateOffsets();
|
|
610
614
|
return true;
|
|
611
615
|
};
|
|
616
|
+
const currentParts = (image) => image.images.map((element, index) => {
|
|
617
|
+
const rect = element.getBoundingClientRect();
|
|
618
|
+
return { ...image.data.parts[index], x: rect.x, y: rect.y, width: rect.width, height: rect.height };
|
|
619
|
+
});
|
|
612
620
|
const begin = () => {
|
|
613
621
|
retireRestoration();
|
|
614
622
|
if (stopped || destination === 'none' || active)
|
|
@@ -650,7 +658,11 @@ const createProjectionEngine = async (scope, transport, callbacks) => {
|
|
|
650
658
|
handoffPending = false;
|
|
651
659
|
callbacks.wake();
|
|
652
660
|
});
|
|
653
|
-
void bounded(call({
|
|
661
|
+
void bounded(call({
|
|
662
|
+
action: 'cover',
|
|
663
|
+
token: image.data.token,
|
|
664
|
+
...(callbacks.transient ? { transient: true, viewportWidth: win.innerWidth, parts: currentParts(image) } : {}),
|
|
665
|
+
}))
|
|
654
666
|
.then((result) => {
|
|
655
667
|
if (!isCurrent())
|
|
656
668
|
return;
|
|
@@ -704,16 +716,14 @@ const createProjectionEngine = async (scope, transport, callbacks) => {
|
|
|
704
716
|
abandon(image, false);
|
|
705
717
|
return;
|
|
706
718
|
}
|
|
707
|
-
const parts =
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
cached = undefined;
|
|
716
|
-
}
|
|
719
|
+
const parts = currentParts(image);
|
|
720
|
+
// Translation changes placement, not pixels. Keep the pre-interaction
|
|
721
|
+
// image until its replacement is ready; resizing still invalidates it.
|
|
722
|
+
if (callbacks.opaqueUnderlay ||
|
|
723
|
+
parts.every((part, index) => part.width === image.data.parts[index].width && part.height === image.data.parts[index].height))
|
|
724
|
+
image.data = { ...image.data, parts };
|
|
725
|
+
else
|
|
726
|
+
cached = undefined;
|
|
717
727
|
// Both requests use the same ordered bridge. Reprojection need not await this optional copy.
|
|
718
728
|
void call({ action: 'restore', token: image.data.token, viewportWidth: win.innerWidth, parts }).catch(() => { });
|
|
719
729
|
}
|
package/dist/types.d.ts
CHANGED
package/docs/projection.md
CHANGED
|
@@ -220,7 +220,7 @@ Use `createWebProjectionBatch(transport, options)` when several fallback element
|
|
|
220
220
|
|
|
221
221
|
The batch derives its Document from the first nonempty `consider`; all subsequent scopes and sources must belong to that Document. Before this first update, `begin()` does nothing and `destroy()` is safe. A changed fallback requires a new scope. Scope identity participates in cache invalidation; an active presentation retains its captured scopes until restoration finishes.
|
|
222
222
|
|
|
223
|
-
`WebProjectionRequest` is an action-discriminated union. `prepare` requires `revision`, `viewportWidth` and `ids`; `cover` requires `token
|
|
223
|
+
`WebProjectionRequest` is an action-discriminated union. `prepare` requires `revision`, `viewportWidth` and `ids`; `cover` requires `token` and includes `viewportWidth` and destination `parts` for transient movement; `restore` requires `token`, `viewportWidth` and destination `parts`; `painted`, `restored` and `release` require `token`; `reset` takes no payload. Empty preparation results and `{ shown: false }` mean the plugin should use its normal Web fallback. Use the plugin's existing ordered bridge; the engine does not retry user actions.
|
|
224
224
|
|
|
225
225
|
### Native handoff on both platforms
|
|
226
226
|
|
|
@@ -229,13 +229,17 @@ Call all view and snapshot lifecycle APIs on the main/UI thread. Native input fr
|
|
|
229
229
|
On both platforms, `cover` installs the prepared entry image **before hiding the supplied live views**, within the same UI turn. Its boolean result reports whether this was possible; failure leaves visibility unchanged. Supply the actual views for every captured component. Do not separately hide views or create another entry copy.
|
|
230
230
|
|
|
231
231
|
```swift
|
|
232
|
-
let shown = snapshots.cover(token, in: webView, views: renderedViews, transient: movingSurface)
|
|
232
|
+
let shown = snapshots.cover(token, in: webView, views: renderedViews, transient: movingSurface, frames: destinationFrames)
|
|
233
233
|
```
|
|
234
234
|
|
|
235
235
|
```kotlin
|
|
236
|
-
val shown = snapshots.cover(token, activity.window, webView, renderedViews, transient = movingSurface)
|
|
236
|
+
val shown = snapshots.cover(token, activity.window, webView, renderedViews, transient = movingSurface, frames = destinationFrames)
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
+
For a custom transport, convert request `parts` from CSS pixels to native frames using `webView.bounds.width / viewportWidth` on iOS or `webView.width / viewportWidth` on Android, mapping each part's IDs to its rectangle. Pass these as `destinationFrames` for both `cover` and `restore`. A retained image can move before its first cover or after a failed return copy; always use the supplied coordinates. When `parts` is absent, pass `nil` / `null` to retain the captured placement. The high-level `NativeUIProjection` registration handles this conversion automatically.
|
|
240
|
+
|
|
241
|
+
A transient surface keeps its image while its sources retain the same sizes and positions relative to their projection scopes. Moving the whole scope can reuse pixels; moving a source inside a fixed scope invalidates the cached layout.
|
|
242
|
+
|
|
239
243
|
`transient: true` is for a moving foreground surface: it does not replace the background's `coveringToken`. Retire the entry copy with `retireCopy(token)` after Web image readiness. Before applying a normal native update, undo the engine's visibility changes with iOS `restoreControls()` / Android `restoreViews()`. For return, `restore` installs the cached return image, the plugin renders underneath it, and `retireCopy(token, entry: false)` retires it after the corresponding acknowledgement/readiness. These platform-native names remain idiomatic; the lifecycle is the same. `cover` is an entry operation, not a method to repeat on each normal update.
|
|
240
244
|
|
|
241
245
|
Token cleanup uses `release(token)`; owner teardown uses `reset()` (and Android `close()` when disposing its encoder). A surface manager must not reset another surface's captures. Web fallback remains the renderer's responsibility when a capture or native placement is unavailable.
|
|
@@ -244,15 +248,15 @@ The optional `eligible(source)` and `current(sources)` callbacks keep framework-
|
|
|
244
248
|
|
|
245
249
|
The image transport accepts `WebProjectionRequest` and returns prepared token/parts or `{ shown: false }`. Parts use local `file:` URLs and CSS-pixel viewport coordinates. Requests use the plugin's existing ordered bridge:
|
|
246
250
|
|
|
247
|
-
| Action | Native responsibility
|
|
248
|
-
| ---------- |
|
|
249
|
-
| `prepare` | Validate the current revision and IDs, then `NativeUICoveredSnapshots.prepare` using actual rendered frames.
|
|
250
|
-
| `cover` | Validate the token and complete native surface ownership; create the entry copy and hide the associated native views. Return `shown: false` on failure. |
|
|
251
|
-
| `painted` | Retire only that token's entry copy.
|
|
252
|
-
| `restore` | Rebase the prepared return copy to supplied frames. Never capture here.
|
|
253
|
-
| `restored` | Retire only that token's return copy after the native update ACK and Web paint boundary.
|
|
254
|
-
| `release` | Release the named token, files and associated temporary views.
|
|
255
|
-
| `reset` | Owner teardown only; release all native capture resources.
|
|
251
|
+
| Action | Native responsibility |
|
|
252
|
+
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
253
|
+
| `prepare` | Validate the current revision and IDs, then `NativeUICoveredSnapshots.prepare` using actual rendered frames. |
|
|
254
|
+
| `cover` | Validate the token and complete native surface ownership; create the entry copy at supplied frames (or captured frames when absent) and hide the associated native views. Return `shown: false` on failure. |
|
|
255
|
+
| `painted` | Retire only that token's entry copy. |
|
|
256
|
+
| `restore` | Rebase the prepared return copy to supplied frames. Never capture here. |
|
|
257
|
+
| `restored` | Retire only that token's return copy after the native update ACK and Web paint boundary. |
|
|
258
|
+
| `release` | Release the named token, files and associated temporary views. |
|
|
259
|
+
| `reset` | Owner teardown only; release all native capture resources. |
|
|
256
260
|
|
|
257
261
|
Use `NativeUICoveredSnapshots.observe` to notify the JS listener when interaction starts/ends. On iOS, captures rasterize the visible window with `drawHierarchy(in:afterScreenUpdates:)`, including the background under the clipped native component. Screen snapshot views are display replicas and are not used as bitmap sources. UIKit rasterization can differ from the live compositor for materials such as Liquid Glass; validate the appearance on physical devices with the backgrounds your plugin supports. Cache validity therefore includes layout and the plugin's visual-change notifications. The engine does not capture continuously or guarantee every possible renderer can be captured.
|
|
258
262
|
|
|
@@ -150,8 +150,8 @@ public final class NativeUICoveredSnapshots {
|
|
|
150
150
|
|
|
151
151
|
/// Install prepared pixels before hiding live views, within the same main-thread turn.
|
|
152
152
|
/// A transient (moving) surface does not replace background covering ownership.
|
|
153
|
-
@discardableResult public func cover(_ token: String, in webView: UIView, views: [UIView], transient: Bool = false) -> Bool {
|
|
154
|
-
guard restore(in: webView, token: token, entry: true) else { return false }
|
|
153
|
+
@discardableResult public func cover(_ token: String, in webView: UIView, views: [UIView], transient: Bool = false, frames: [String: CGRect]? = nil) -> Bool {
|
|
154
|
+
guard restore(in: webView, token: token, frames: frames, entry: true) else { return false }
|
|
155
155
|
if !transient { coveringToken = token }
|
|
156
156
|
hide(views, for: token)
|
|
157
157
|
return true
|
|
@@ -30,7 +30,18 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
|
|
|
30
30
|
private let views: () -> [String: UIView]
|
|
31
31
|
private let snapshots: NativeUICoveredSnapshots
|
|
32
32
|
private let scrolling = NativeUIScrollBindings()
|
|
33
|
-
private
|
|
33
|
+
private struct ScrollMask {
|
|
34
|
+
weak var view: UIView?
|
|
35
|
+
let layer: CAShapeLayer
|
|
36
|
+
}
|
|
37
|
+
private var masks: [String: ScrollMask] = [:]
|
|
38
|
+
private func resetScrolling() {
|
|
39
|
+
scrolling.reset()
|
|
40
|
+
for mask in masks.values where mask.view?.layer.mask === mask.layer {
|
|
41
|
+
mask.view?.layer.mask = nil
|
|
42
|
+
}
|
|
43
|
+
masks.removeAll()
|
|
44
|
+
}
|
|
34
45
|
private let id = "nativeUI" + UUID().uuidString.replacingOccurrences(of: "-", with: "")
|
|
35
46
|
private let origins: Set<String>
|
|
36
47
|
private let allowedOriginsJSON: String
|
|
@@ -145,12 +156,26 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
|
|
|
145
156
|
let key = message["key"] as? String, key.hasPrefix(id + ":"),
|
|
146
157
|
let request = message["request"] as? [String: Any] else { return }
|
|
147
158
|
let page = key.split(separator: ":").dropFirst().first.map(String.init)
|
|
148
|
-
if documentID != page { snapshots.reset();
|
|
159
|
+
if documentID != page { snapshots.reset(); resetScrolling(); captured.removeAll(); anchors.removeAll(); documentID = page }
|
|
149
160
|
let complete: ([String: Any]) -> Void = { [weak self] value in
|
|
150
161
|
guard let self, !self.closed, self.documentID == page else { return }
|
|
151
162
|
self.reply(key, value)
|
|
152
163
|
}
|
|
153
164
|
let token = request["token"] as? String ?? ""
|
|
165
|
+
func projectionFrames() -> [String: CGRect]? {
|
|
166
|
+
guard let width = request["viewportWidth"] as? Double, width.isFinite, width > 0,
|
|
167
|
+
let parts = request["parts"] as? [[String: Any]] else { return nil }
|
|
168
|
+
var frames: [String: CGRect] = [:]
|
|
169
|
+
let scale = webView.bounds.width / width
|
|
170
|
+
for part in parts {
|
|
171
|
+
guard let ids = part["ids"] as? [String], let x = part["x"] as? Double, let y = part["y"] as? Double,
|
|
172
|
+
let w = part["width"] as? Double, let h = part["height"] as? Double,
|
|
173
|
+
[x,y,w,h].allSatisfy({ $0.isFinite }), w > 0, h > 0 else { return nil }
|
|
174
|
+
for id in ids { frames[id] = CGRect(x: x*scale, y: y*scale, width: w*scale, height: h*scale) }
|
|
175
|
+
}
|
|
176
|
+
guard Set(frames.keys) == snapshots.identifiers(for: token), !frames.isEmpty else { return nil }
|
|
177
|
+
return frames
|
|
178
|
+
}
|
|
154
179
|
switch request["action"] as? String {
|
|
155
180
|
case "connect":
|
|
156
181
|
if !connected { destination = (views().values.contains(where: { !$0.isHidden }) ? "native" : "none"); connected = true }
|
|
@@ -159,9 +184,7 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
|
|
|
159
184
|
if let next = request["destination"] as? String, ["native", "web", "none"].contains(next) { destination = next; connected = true }
|
|
160
185
|
complete([:])
|
|
161
186
|
case "track":
|
|
162
|
-
|
|
163
|
-
for (id, mask) in masks { if views()[id]?.layer.mask === mask { views()[id]?.layer.mask = nil } }
|
|
164
|
-
masks.removeAll()
|
|
187
|
+
resetScrolling()
|
|
165
188
|
guard let raw = request["scroll"] as? [String: Any], let width = request["viewportWidth"] as? Double, width.isFinite, width > 0,
|
|
166
189
|
let data = try? JSONSerialization.data(withJSONObject: raw),
|
|
167
190
|
let placement = try? JSONDecoder().decode(NativeUIScrollPlacement.self, from: data), placement.isValid else {
|
|
@@ -185,8 +208,8 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
|
|
|
185
208
|
let from = parent.convert(CGPoint(x: old.midX, y: old.midY), from: webView)
|
|
186
209
|
let to = parent.convert(CGPoint(x: frame.midX, y: frame.midY), from: webView)
|
|
187
210
|
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
|
|
211
|
+
let mask = self.masks[id]?.layer ?? CAShapeLayer()
|
|
212
|
+
self.masks[id] = ScrollMask(view: view, layer: mask)
|
|
190
213
|
mask.frame = view.bounds
|
|
191
214
|
mask.path = UIBezierPath(rect: clip.isNull ? .zero : view.convert(clip, from: webView).intersection(view.bounds)).cgPath
|
|
192
215
|
view.layer.mask = mask
|
|
@@ -221,21 +244,13 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
|
|
|
221
244
|
case "cover":
|
|
222
245
|
let selected = captured[token] ?? [:], current = views()
|
|
223
246
|
let valid = visible && canCapture() && !selected.isEmpty && selected.allSatisfy { current[$0.key] === $0.value }
|
|
247
|
+
let frames = request["parts"] == nil ? nil : projectionFrames()
|
|
248
|
+
guard request["parts"] == nil || frames != nil else { complete(["shown": false]); return }
|
|
224
249
|
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)])
|
|
250
|
+
complete(["shown": valid && snapshots.cover(token, in: webView, views: Array(Dictionary(grouping: selected.values, by: { ObjectIdentifier($0) }).values.compactMap { $0.first }), transient: true, frames: frames)])
|
|
226
251
|
case "restore":
|
|
227
252
|
restoreAccepted = false
|
|
228
|
-
guard let
|
|
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 }
|
|
253
|
+
guard let frames = projectionFrames() else { complete(["shown": false]); return }
|
|
239
254
|
let current = views()
|
|
240
255
|
// Preserve renderer-owned constraints. Translation follows the corresponding DOM's movement.
|
|
241
256
|
for (id, frame) in frames {
|
|
@@ -298,11 +313,9 @@ public final class NativeUIProjection: NSObject, WKScriptMessageHandler {
|
|
|
298
313
|
controller.removeAllUserScripts()
|
|
299
314
|
remaining.forEach { controller.addUserScript($0) }
|
|
300
315
|
}
|
|
301
|
-
snapshots.reset();
|
|
316
|
+
snapshots.reset(); resetScrolling(); captured.removeAll(); anchors.removeAll()
|
|
302
317
|
if destination != "none" { hiddenByProjection.forEach { $0.isHidden = false } }
|
|
303
318
|
hiddenByProjection.removeAll()
|
|
304
|
-
for (id, mask) in masks { if views()[id]?.layer.mask === mask { views()[id]?.layer.mask = nil } }
|
|
305
|
-
masks.removeAll()
|
|
306
319
|
}
|
|
307
320
|
}
|
|
308
321
|
private final class WeakProjectionHandler: NSObject, WKScriptMessageHandler {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var WebViewNativeUIEngineRuntime=(()=>{var _e=Object.defineProperty;var
|
|
1
|
+
"use strict";var WebViewNativeUIEngineRuntime=(()=>{var _e=Object.defineProperty;var dt=Object.getOwnPropertyDescriptor;var ut=Object.getOwnPropertyNames;var ft=Object.prototype.hasOwnProperty;var pt=(t,i)=>{for(var r in i)_e(t,r,{get:i[r],enumerable:!0})},mt=(t,i,r,v)=>{if(i&&typeof i=="object"||typeof i=="function")for(let h of ut(i))!ft.call(t,h)&&h!==r&&_e(t,h,{get:()=>i[h],enumerable:!(v=dt(i,h))||v.enumerable});return t};var vt=t=>mt(_e({},"__esModule",{value:!0}),t);var Rt={};pt(Rt,{hide:()=>Ht,notify:()=>Lt,receive:()=>St,show:()=>xt,start:()=>Ct,stop:()=>kt});var ht=Symbol.for("webview-native-ui-engine.projection-placement");var Ne=t=>t?.[ht];var yt=(t,i)=>{if(t.matches("ion-toast, ion-menu"))return!1;let r=t;if(r.showBackdrop===!1)return!1;if(!t.matches("ion-modal"))return!0;let v=i??r.initialBreakpoint;return v===void 0||v>(r.backdropBreakpoint??0)},gt=Symbol.for("@webview-native-ui/engine/overlays/v1"),wt=t=>{let i=new Set,r=new Set,v=new Set,h=new WeakMap,d=new WeakSet,p=new Map,T=[],f={},L=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),i.add(y)):l==="didDismiss"&&(d.add(y),i.delete(y),r.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 i)(!g.isConnected||g.ownerDocument!==t||!T.some(({selector:c})=>g.matches(c)))&&(i.delete(g),r.delete(g),l=!0);for(let g of t.querySelectorAll(T.map(({selector:c})=>c).join(",")))!i.has(g)&&!d.has(g)&&(y||!g.matches("ion-popover"))&&(g.presented||g.classList.contains("show-menu"))&&(i.add(g),l=!0);l&&j({phase:"change"})};return{get active(){return i},subscribe(y){return v.add(y),()=>{v.delete(y)}},setNative(y,l){r.has(y)!==l&&(l?r.add(y):r.delete(y),j({element:y,phase:"change"}))},isExcluded(y){return!!f.exclude&&y.matches(f.exclude)},isNative(y){return r.has(y)},get stack(){let y=[...i].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,x)=>D.contains(y)?x:c,-1);return l.some((c,D)=>{if(r.has(c)||f.exclude&&c.matches(f.exclude))return!1;let x=f.covers?.(c,y);return x!==void 0?x:D>g&&!c.contains(y)&&(c.matches("ion-menu")||yt(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[O,A]of Object.entries(m.events)){let N=D.get(A);if(N&&N.phase!==O)throw new TypeError(`Overlay event "${A}" has conflicting phases`);D.set(A,{phase:O,selector:N?`${N.selector}, ${m.selector}`:m.selector})}}f=g,T=c,L=D;for(let m of L.keys()){if(p.has(m))continue;let O=A=>{let N=L.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,O,!0),p.set(m,()=>t.removeEventListener(m,O,!0))}for(let[m,O]of p)L.has(m)||(O(),p.delete(m));let x=!C;C||(C=new MutationObserver(()=>S()),C.observe(t.documentElement,{childList:!0,subtree:!0}),t.addEventListener("nativeUIOverlayAdopted",m=>{let O=m.detail;O?.ownerDocument===t&&B(O,"willPresent",m)})),S(x),(x||l)&&j({phase:"change"})}}},tt=t=>{let i=t;return i[gt]??=wt(t)};function*We(t){let i=t;for(;i;){let r=i.getRootNode();i=i.assignedSlot??i.parentElement??(r instanceof ShadowRoot?r.host:null),i&&(yield i)}}var bt=({x:t,y:i,width:r,height:v})=>({x:t,y:i,width:r,height:v}),nt=t=>{let i=t.getBoundingClientRect(),r=getComputedStyle(t),v=parseFloat(r.borderLeftWidth)||0,h=parseFloat(r.borderTopWidth)||0;return{x:i.left+v,y:i.top+h,width:i.width-v-(parseFloat(r.borderRightWidth)||0),height:i.height-h-(parseFloat(r.borderBottomWidth)||0)}},Ge=(t,i,r)=>{let v=[t,...We(t)],h=v.indexOf(i);if(h<=0||i===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,L=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(x=>x&&x!=="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(x=>parseFloat(x)>0)||l.clipPath&&l.clipPath!=="none"||l.maskImage&&l.maskImage!=="none")return;if(S>=h){let x=nt(y);g&&(p=Math.max(p,x.x),f=Math.min(f,x.x+x.width)),c&&(T=Math.max(T,x.y),L=Math.min(L,x.y+x.height))}}let C=getComputedStyle(i);if(!/^(auto|scroll)$/.test(C.overflowX)&&!/^(auto|scroll)$/.test(C.overflowY))return;let j=nt(i);if(f<=p||L<=T||!j.width||!j.height)return;let B=bt(t.getBoundingClientRect());return{id:r(i),offsetX:i.scrollLeft,offsetY:i.scrollTop,viewport:j,clip:{x:p,y:T,width:f-p,height:L-T},contentWidth:i.scrollWidth,contentHeight:i.scrollHeight,frame:{...B,x:B.x-j.x+i.scrollLeft,y:B.y-j.y+i.scrollTop}}};var ot=t=>{let i=globalThis;if(i.Capacitor?.convertFileSrc)return i.Capacitor.convertFileSrc(t);if(!t.startsWith("file:"))return t;if(i.WkWebView?.convertFilePath)return i.WkWebView.convertFilePath(t);throw new Error("Native UI file URL requires a host resource handler")};var pe=t=>new Promise((i,r)=>{let v=setTimeout(()=>r(new Error("Native UI bridge timed out")),5e3);t.then(i,r).finally(()=>clearTimeout(v))}),Be=async t=>{try{return await pe(t)}catch(i){throw t.then(r=>r.remove()).catch(()=>{}),i}};var ze=async(t,i)=>{let r=new Map;for(let T of t.update??[])r.set(T,{updated:!0});for(let T of["native","web","none"])for(let f of t.projection?.[T]??[]){let L=r.get(f)??{updated:!1};if(L.destination&&L.destination!==T)throw new TypeError(`Projection event "${f}" has conflicting destinations`);r.set(f,{...L,destination:T})}let v=!1,h=[],d=async()=>{v=!0;let f=(await Promise.allSettled(h.splice(0).map(L=>Promise.resolve().then(()=>L.remove())))).find(L=>L.status==="rejected");if(f?.status==="rejected")throw f.reason},p=[...r].map(async([T,f])=>{let L=await Be(Promise.resolve().then(()=>t.addListener(T,C=>{!v&&(!t.accepts||t.accepts(C))&&i(f.destination,f.updated)})));v?await L.remove():h.push(L)});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])",rt=`
|
|
2
2
|
@layer native-ui-projection {
|
|
3
3
|
${je}, ${je} *, ${je}::part(native) { visibility: hidden !important; }
|
|
4
4
|
${je}::before, ${je}::after,
|
|
@@ -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 Et=(t,i=!1)=>JSON.stringify(t.map(({control:r})=>[r.id,r.kind,r.placement,i?0:r.x,i?0:r.y,r.width,r.height,r.dark,r.rtl])),Pt=async(t,i,r)=>{let v=r.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,L=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=i.send.bind(i),y="data-native-ui-snapshot",l=`${t.namespace}:sources-change`,g=new Set,c=new Set,D=new Set,x=new WeakMap,m=0,O=e=>JSON.stringify(e.map(o=>{x.has(o.scope)||x.set(o.scope,++m);let n=r.transient&&!r.opaqueUnderlay?o.scope.element.getBoundingClientRect():void 0,s=n?{...o,control:{...o.control,x:o.control.x-n.x,y:o.control.y-n.y}}:o;return[x.get(o.scope),Et([s],!!r.opaqueUnderlay)]})),A=new Map,N=e=>{let o=e.getRootNode();return o.nodeType===11&&"host"in o?o:d},oe=e=>{if(A.has(e))return;let o=d.createElement("style");o.setAttribute("data-native-ui-snapshot-base",""),o.textContent=rt+(e.nodeType===11?it:""),(e.nodeType===11?e:e.head).append(o),A.set(e,o)},F=e=>{let o=[...e.getAnimations({subtree:!0}),...[...se.get(e)??[]].flatMap(n=>n.shadowRoot?.getAnimations()??[])];for(let n of o)"transitionProperty"in n&&n.transitionProperty==="visibility"&&n.cancel()},se=new Map,Se=e=>{let o=se.get(e)??new Set;se.set(e,o);let n=e.hasAttribute("data-native-ui-snapshot-source")||e.hasAttribute("data-native-ui-snapshot-prepared"),s=P=>{if(!P.hasAttribute("data-native-ui-projection-node")){if(P.shadowRoot){o.add(P),oe(P.shadowRoot);for(let M of P.shadowRoot.children)s(M)}for(let M of P.children)s(M)}};s(e);for(let P of o)P.toggleAttribute("data-native-ui-snapshot-content",n);F(e)},W=()=>`${p.innerWidth}:${p.innerHeight}:${p.devicePixelRatio}`,Z=!1,ae=!1,J=0,me=0,R,U,b,q=!1,X=!1,we=0,Oe=0,ee=0,ve=0,z=0,be=-1,V=!1,te=!1,Y,le=()=>{let e=new Set(Y?.candidates.map(n=>n.scope.element));for(let[n,s]of C){let P=!V&&n.isConnected&&e.has(n);if(P&&(b||X||Z))continue;let M=P?2*Math.max(1,R?.scopes.filter(k=>k.element===n).length??0):0,H=Math.max(0,M-s.filter(k=>j.has(k)).length),I=s.filter(k=>j.has(k)||H-- >0?!0:(k.remove(),!1));I.length?C.set(n,I):C.delete(n)}let o=new Set([...Y?.candidates.map(n=>n.scope.element.getRootNode())??[],...b?.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&&!o.has(n)&&(s.remove(),A.delete(n))},he=new Set,Ie=(e,o,n)=>{let s=()=>{he.delete(s)&&(S({action:e,token:o}).catch(()=>{}),e==="restored"&&le())};he.add(s),n&&!V?p.requestAnimationFrame(()=>p.requestAnimationFrame(s)):s()},de=new Set,xe=()=>{de.forEach(e=>D.delete(e)),de.clear()},He=(e,o=!1)=>{!e||e===R||e===b||(e.images.forEach(n=>{B(n)}),Ie("release",e.data.token,o))},Ee=e=>e.filter(o=>{if(!L(o.scope.element)||![o.element,...o.sources??[]].every(f))return!1;if(o.control.scroll){let n=o.element.getBoundingClientRect(),s=o.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 r.eligible?.(o)??!0}),qe=async()=>{if(ve=0,V||h!=="native"||te||q||b||r.blocked()||!Y||R?.revision===Y.revision&&be===z)return;let{revision:e,candidates:o}=Y,n=ee,s=z,P=W(),M=Ee(o);if(!M.length)return;te=!0;let H,I=!1,k=[];try{let re=!1,ie=S({action:"prepare",revision:r.captureRevision?.()??e,viewportWidth:p.innerWidth,ids:M.map(w=>w.control.id)});ie.then(w=>{re&&w.token&&S({action:"release",token:w.token}).catch(()=>{})}).catch(()=>{});try{H=await pe(ie)}finally{re=!0}if(!H.token||!H.parts?.length)return;let Ce=new Set(M.map(w=>w.control.id));if(H.parts.some(w=>!w.url.startsWith("file:")&&!/^\/__native_ui_engine__\/nativeUI[a-fA-F0-9]+\/[a-fA-F0-9-]+-[0-9]+\.png$/.test(w.url)||!w.ids.length||w.ids.some(G=>!Ce.has(G))||![w.x,w.y,w.width,w.height].every(Number.isFinite)||w.width<=0||w.height<=0)||new Set(H.parts.flatMap(w=>w.ids)).size!==Ce.size||H.parts.some(w=>new Set(w.ids.map(G=>M.find(Me=>Me.control.id===G).scope)).size!==1)||(await pe(Promise.all(H.parts.map(async w=>{let G=M.find(Ye=>w.ids.includes(Ye.control.id)).scope.element,Me=C.get(G);Me||(Me=[],C.set(G,Me));let fe=Me.find(Ye=>!j.has(Ye));return fe||(fe=d.createElement("img"),fe.setAttribute("data-native-ui-projection-node",""),Me.push(fe)),j.add(fe),k.push(fe),fe.src=ot(w.url),await fe.decode(),fe}))),V||h!=="native"||q||b||r.blocked()||ee!==n||W()!==P||Y?.revision!==e||M.some(w=>!L(w.scope.element)||![w.element,...w.sources??[]].every(f))))return;let Re=R,et={data:H,images:k,sources:Array.from(new Set(M.flatMap(w=>[w.element,...w.sources??[]]))),targets:H.parts.map(w=>M.find(G=>w.ids.includes(G.control.id)).element),scopes:H.parts.map(w=>M.find(G=>w.ids.includes(G.control.id)).scope),parents:H.parts.map(w=>M.find(G=>w.ids.includes(G.control.id)).scope.element.parentNode),frames:[],viewport:P,revision:e,layout:O(M)};try{if(!Ue(et))throw new Error("Projection destination is unavailable")}catch(w){Q();try{Ue(),R?.sources.forEach(G=>{D.add(G),de.add(G)})}catch{$()}throw w}R=et,xe(),R.sources.forEach(w=>{D.add(w),de.add(w)}),r.opaqueUnderlay&&S({action:"prepared",token:H.token}).catch(()=>{}),p.dispatchEvent(new Event(l)),be=s,I=!0,He(Re)}catch{}finally{I||k.forEach(re=>{B(re)}),H?.token&&!I&&S({action:"release",token:H.token}).catch(()=>{}),te=!1,Y&&(Y.revision!==e||z!==s)&&ue()}},ue=()=>{p.clearTimeout(ve),!V&&h==="native"&&!b&&!q&&!r.blocked()&&(ve=p.setTimeout(()=>{qe()},v))},Ae,a=(e,o)=>{if(o.some(s=>s.scope.element.ownerDocument!==d||[s.element,...s.sources??[]].some(P=>P.ownerDocument!==d)))throw new TypeError("All projection sources and fallback scopes must share one Document");if(r.blocked())return;!b&&R&&R.layout!==O(Ee(o))&&Le();let n=O(o);Ae!==n&&(Ae=n,z++,ee++),Y?.revision!==e&&ee++,Y={revision:e,candidates:o},le(),(R?.revision!==e||be!==z)&&ue()},u=e=>{!b&&R&&e&&Y?.candidates.some(o=>o.scrollElement===e&&R.sources.includes(o.element))&&Le(),b&&ke(),z++,ee++,ue()},E=await Be(i.listen(({active:e})=>{q=e,ee++,p.clearTimeout(ve),e||ue()})),_=new Set,ce=e=>{se.get(e)?.forEach(o=>o.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,xe(),b?.sources.forEach(e=>{g.delete(e),c.delete(e)}),K.forEach(ce),K.clear(),A.forEach((e,o)=>{(V||o.nodeType===11&&!o.host.isConnected)&&(e.remove(),A.delete(o))})},$=(e=b,o=X)=>{if(b!==e)return;let n=new Set([b,R]);Q(),b=void 0,R=void 0,X=!1,p.clearTimeout(we),n.forEach(s=>He(s,o)),r.wake()},ne=()=>{if(!b)return;if(R!==b){$();return}let e=b;me++,p.cancelAnimationFrame(J),J=0,Z=ae=!1,e.sources.forEach(o=>{g.delete(o),c.delete(o)}),K.forEach(o=>o.removeAttribute("data-native-ui-snapshot-source")),Xe(e,!!r.opaqueUnderlay),b=void 0,X=!1,p.clearTimeout(we),Ie("restored",e.data.token,!0),e.sources.forEach(o=>{D.add(o),de.add(o)}),r.wake()},Pe=()=>{X&&ne(),he.forEach(e=>e())},De=e=>e.scopes.map(o=>L(o.element)?o.element:void 0),K=new Set,Xe=(e,o)=>{for(let n of K)n.toggleAttribute("data-native-ui-snapshot-visible",o),Se(n);e.images.forEach(n=>n.style.setProperty("opacity",o?"1":"0","important"))},Te=new Map,Fe=new Map,ke=()=>{let e=[];for(let[o,n]of Fe){let{target:s,frame:P}=n;if(!s.isConnected){o.remove();continue}let M=s.shadowRoot??s;o.parentNode!==M&&M.insertBefore(o,null);let H=s.getBoundingClientRect(),I=o.getBoundingClientRect(),k=parseFloat(o.style.width)||P.width,re=parseFloat(o.style.height)||P.height,ie=I.width/k||1,Ce=I.height/re||1,Re=n.scale??(n.scale={x:ie,y:Ce});e.push({element:o,left:(parseFloat(o.style.left)||0)+(H.x+P.x*ie/Re.x-I.x)/ie,top:(parseFloat(o.style.top)||0)+(H.y+P.y*Ce/Re.y-I.y)/Ce,width:P.width/Re.x,height:P.height/Re.y})}for(let{element:o,...n}of e)for(let[s,P]of Object.entries(n)){let M=`${P}px`;o.style.getPropertyValue(s)!==M&&o.style.setProperty(s,M,"important")}},Ue=(e=b??R)=>{if(!e||X)return!1;if(!e.sources.every(f)||De(e).some(n=>!n))return Le(),!1;let o=De(e);if(b){if(o.some(s=>!s?.isConnected||!A.has(N(s))))return $(b,!1),!1;let n=new Set;for(let[s,P]of e.targets.entries()){let M=Te.get(P),H=o[s];!M||!H?.isConnected||(H.setAttribute(y,M),H.setAttribute("data-native-ui-snapshot-source",""),n.add(H))}for(let s of K)n.has(s)||ce(s);return K.clear(),n.forEach(s=>K.add(s)),Xe(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(o)){if(!n)continue;K.add(n),oe(N(n));let s=o.flatMap((I,k)=>I===n?[k]:[]),P=`${T}-${s[0]}`;n.setAttribute(y,P),n!==e.targets[s[0]]&&(n.setAttribute("data-native-ui-snapshot-prepared",""),_.add(n)),n.toggleAttribute("data-native-ui-snapshot-visible",!!r.opaqueUnderlay),Se(n);let M=n.getBoundingClientRect();n.toggleAttribute("data-native-ui-snapshot-positioned",p.getComputedStyle(n).position==="static");let H=n.shadowRoot??n;for(let I of[...s].reverse()){let k=e.images[I],re=e.data.parts[I],ie=e.frames[I]??{x:r.opaqueUnderlay?0:re.x-M.x,y:r.opaqueUnderlay?0:re.y-M.y,width:re.width,height:re.height};e.frames[I]=ie,Te.set(e.targets[I],P),k.inert=!0,k.setAttribute("aria-hidden","true"),k.setAttribute("data-native-ui-projection-image",P),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",`${ie.width}px`,"important"),k.style.setProperty("height",`${ie.height}px`,"important"),k.style.setProperty("left","0px","important"),k.style.setProperty("top","0px","important"),k.style.setProperty("opacity",r.opaqueUnderlay?"1":"0","important"),k.parentNode!==H&&H.insertBefore(k,null),Fe.set(k,{target:n,frame:ie})}}return e.parents=e.scopes.map(n=>n.element.parentNode),ke(),!0},Je=e=>e.images.map((o,n)=>{let s=o.getBoundingClientRect();return{...e.data.parts[n],x:s.x,y:s.y,width:s.width,height:s.height}}),Ke=()=>{if(Pe(),V||h==="none"||b)return;let e=R;if(!e||e.viewport!==W()||!e.sources.every(f)||r.current&&!r.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,P)=>s.element.parentNode!==e.parents[P])&&Ue(),ke();let o=++me,n=()=>me===o;Z=!0,ae=!!r.opaqueUnderlay,b=e,ee++,p.clearTimeout(ve),e.sources.forEach(s=>{g.add(s)}),K.forEach(s=>s.setAttribute("data-native-ui-snapshot-source","")),Xe(e,ae),J=p.requestAnimationFrame(()=>{J=0,n()&&(Z=!1,r.wake())}),pe(S({action:"cover",token:e.data.token,...r.transient?{transient:!0,viewportWidth:p.innerWidth,parts:Je(e)}:{}})).then(s=>{if(n()){if(Z=!1,s?.shown!==!0){$(e,!1);return}X||(ae=!0,e.sources.forEach(P=>c.add(P)),Ue(),ke()),p.cancelAnimationFrame(J),J=p.requestAnimationFrame(()=>{J=0,n()&&S({action:"painted",token:e.data.token}).catch(()=>{n()&&$(e,!1)})}),r.wake()}}).catch(()=>{n()&&$(e,!1)})},Ze=()=>{if(!(!b||V||X)){if(!b.sources.every(f)||De(b).some(e=>!e)||r.current&&!r.current(b.targets)){$(b,!1);return}if(!r.blocked()){if(ke(),r.transient){let e=b;if(De(e).some(s=>!s?.isConnected)){$(e,!1);return}let n=Je(e);r.opaqueUnderlay||n.every((s,P)=>s.width===e.data.parts[P].width&&s.height===e.data.parts[P].height)?e.data={...e.data,parts:n}:R=void 0,S({action:"restore",token:e.data.token,viewportWidth:p.innerWidth,parts:n}).catch(()=>{})}Oe=r.revision?.()??Y?.revision??0,X=!0,b.sources.forEach(e=>g.delete(e)),we=p.setTimeout(()=>{X&&$(b,!1)},250),r.wake()}}},lt=(e,o,n)=>{!X||!b||n===void 0||n<=Oe||!r.transient&&e||b.data.parts.every(s=>s.ids.every(P=>o?.includes(P)))&&(ne(),ue())},Le=(e=!1)=>{he.forEach(n=>n()),ee++,p.clearTimeout(ve);let o=R;if(R=void 0,(e||V)&&(U=void 0),r.retainPreparedUntilRelease&&!b&&!e&&!V){if(o){let n={};U=n,pe(S({action:"release",token:o.data.token})).catch(()=>{}).finally(()=>{if(o.images.forEach(s=>{B(s)}),U===n&&(U=void 0,!(R||b))){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}He(o),b?$(b,!1):(Q(),Te.clear(),p.dispatchEvent(new Event(l)))},Qe;try{r.events&&(Qe=await ze(r.events,(e,o)=>{if(!V){if(e&&(h=e),h==="none"){Le();return}e==="web"&&Ke(),e==="native"&&(Ze(),ue()),o&&u()}}))}catch(e){throw V=!0,Le(),await pe(Promise.resolve(E.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 X?void 0:b?.data.token},begin:Ke,get restoring(){return X},get handoffPending(){return Z},retireRestoration:Pe,end:Ze,updated:lt,invalidate:Le,async destroy(){V=!0,Le(),C.forEach(e=>e.forEach(o=>o.remove())),C.clear(),await Promise.allSettled([pe(Promise.resolve().then(()=>E?.remove())),pe(Promise.resolve().then(()=>Qe?.remove()))])}}},st=async(t,i,r)=>{let v=await Pt(t,i,r),h=v.consider;return Object.assign(v,{consider:(d,p)=>h(d,p.map(T=>({...T,scope:t})))})};var at=(t,i)=>{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:i})};var ct=async(t,i,r={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",L=tt(document),C=()=>T==="native"&&m&&L.isCovered(m)?"web":T,j=!1,B=!1,S=!1,y=!1,l,g,c,D=!1,x=()=>{let a=D;D=!0;try{c?.begin()}finally{D=a}},m,O={},A,N=0,oe=()=>{N++,A=void 0;let a=Ne(m);a?.owner===O&&(a.projected=!1,a.owner=void 0)},F={listen:a=>i.listen(u=>{a(u),!u.active&&B&&(B=!1,he()),u.precovered&&Ie(u.precovered)}),send:a=>{if(!t.placementHandoff)return i.send(a);let u=Ne(m);if(a.action==="cover"){let $=A===a.token&&u?.owner===O&&u.projected,ne=++N;return A=a.token,u&&(u.owner=O,u.projected=!!$),i.send(a).then(Pe=>(ne===N&&u===Ne(m)&&u?.owner===O&&(u.projected=Pe?.shown===!0),Pe)).catch(Pe=>{throw ne===N&&u?.owner===O&&(oe(),u.invalidate()),Pe})}let E=a.action==="release"&&a.token===A,_=A&&(a.action==="restore"||E||a.action==="reset"||a.action==="visibility"&&a.visible);_&&N++;let ce=N,Q=()=>{ce!==N||u?.owner!==O||(oe(),u.invalidate())};try{let $=_&&u?.owner===O?{...a,token:"token"in a?a.token:A,placement:u.measure()}:a;return i.send($).then(async ne=>(a.action==="release"&&ne?.requiresPlacement&&(u&&(!u.owner||u.owner===O)?ne=await i.send({...a,placement:u.measure()}):(await i.send({action:"visibility",visible:!1}),ne=await i.send(a))),ce===N&&(a.action==="resume"||E||a.action==="reset"||a.action==="visibility")&&oe(),ne)).catch(ne=>{throw Q(),ne})}catch($){return Q(),Promise.reject($)}}},se=0,Se=0,W=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 b=()=>{let a=document.querySelectorAll(t.projectionSelector);return a.length===1&&a[0]instanceof HTMLElement?a[0]:void 0},q=()=>{m&&(f==="web"||f==="native"&&S?m.removeAttribute(U):m.setAttribute(U,f))},X=()=>{if(!m?.isConnected)return[];let a=m.getBoundingClientRect(),u={x:a.x,y:a.y,width:a.width,height:a.height},E=l&&Ge(m,l,()=>t.id);return[{element:m,scrollElement:l,control:{id:v,...u,...E?{scroll:E}:{}}}]},we=async()=>{if(d)return;let a=W,u=t.failedFallback==="native"&&C()==="native"&&!!m?.isConnected;await F.send({action:"visibility",visible:u}).catch(()=>{}),!(d||a!==W)&&(u||f==="none"?q():m?.removeAttribute(U))},Oe=()=>{if(!j||f!=="native"||!m?.isConnected||c?.token||c?.restoring)return;let a=t.failedFallback==="native";F.send({action:"visibility",visible:a}).catch(()=>{}),a?q():m.removeAttribute(U)},ee=()=>{if(d||D)return;if(Oe(),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=W,u=c;F.send({action:"resume"}).then(()=>{d||a!==W||u!==c||(S=!1,u.updated(void 0,[v],++se),q(),z())}).catch(()=>{a!==W||d||(u.invalidate(),we())}).finally(()=>{Z=!1,ee()})},ve=async()=>{if(d||ae||j)return;if(c?.restoring){ee();return}let a=b();if(a!==m){if(W++,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=W;try{let E=await st(at(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!==W){await E.destroy();return}c=E}finally{ae=!1}}if(!d){if(f==="native"&&S){await F.send({action:"visibility",visible:!1}),q();return}if(f==="native"){let u=W;if(await F.send({action:"visibility",visible:!0}),d||u!==W||f!=="native")return;q(),l=[...We(m)].find(Q=>{let $=getComputedStyle(Q);return/auto|scroll/.test($.overflowX+$.overflowY)&&(Q.scrollHeight>Q.clientHeight||Q.scrollWidth>Q.clientWidth)});let E=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,...E?{scroll:E}:{},..._?{gesture:_}:{}});if(d||u!==W)return;y=ce?.tracked===!0,c.consider(t.captureMode==="view"?Se:++se,X())}else await F.send({action:"visibility",visible:!1}),q()}},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,Y=new WeakSet,le=a=>{let u=a.composedPath()[0]??a.target;if(!(d||f!=="native"||!m||Y.has(a)||u!==document&&![...We(m)].includes(u))){if(Y.add(a),y&&u===l){c?.changed(u);return}j?t.captureMode==="view"?c?.moved():c?.changed():((Z||c?.restoring)&&W++,j=!0,x(),S=t.failedFallback!=="native",c?.token?m.removeAttribute(U):Oe()),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)&&W++,A=a;let u=Ne(m);if(u&&(u.owner=O,u.projected=!0),j=!0,x(),!c.token){j=!1,we();return}he()}let de=()=>{let a=new Set(m?We(m):[]),u=a.size!==te.size||[...a].some(E=>!te.has(E));for(let E of te)a.has(E)||(E.removeEventListener("scroll",le),E.removeEventListener("slotchange",xe),te.delete(E));for(let E of a)te.has(E)||(E.addEventListener("scroll",le,{passive:!0}),E.addEventListener("slotchange",xe),te.add(E));u&&(y=!1,l=void 0,c?.invalidate(),z())},xe=()=>{de(),z()};document.addEventListener("scroll",le,!0);let He=new MutationObserver(a=>{de(),b()!==m?z():m&&a.some(u=>u.type==="childList")&&Ee(C())});He.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,W++;let E=W;f==="none"?F.send({action:"visibility",visible:!1}).then(()=>{f!=="none"||E!==W||d||(c?.invalidate(!0),q())}).catch(()=>{f!=="none"||E!==W||d||(c?.invalidate(!0),q())}):f==="web"?(x(),c?.token||F.send({action:"visibility",visible:!1}).catch(()=>{}),q()):f==="native"&&(c?.end(),c?.restoring||z())},qe=L.subscribe(()=>queueMicrotask(()=>Ee(C()))),ue,Ae=async()=>{if(!d){d=!0,oe(),W++,qe(),He.disconnect(),V.disconnect(),window.removeEventListener("resize",be),document.removeEventListener("scroll",le,!0);for(let a of te)a.removeEventListener("scroll",le),a.removeEventListener("slotchange",xe);te.clear(),clearTimeout(g),m?.removeAttribute(U),R.remove(),await Promise.allSettled([Promise.resolve(ue?.remove()),c?.destroy()])}};try{let a=W;ue=await ze({addListener:(E,_)=>r.addListener(E,_),update:h?.update,projection:h?.projection,accepts:E=>!h?.match||Object.entries(h?.match).every(([_,ce])=>typeof E=="object"&&E!==null&&E[_]===ce)},(E,_)=>{d||(E&&(E==="native"&&(S=!1),T=E,F.send({action:"destination",destination:E}).catch(()=>{}),Ee(C(),!0)),_&&(Se++,c?.changed(),f==="native"&&z()))});let u=await F.send({action:"connect"});return W===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",W++,j=!1,B=!1,clearTimeout(g),c?.invalidate(!0),q()}}}catch(a){throw await Ae(),a}};var ye=new Map,ge=new Map,$e=new Map,Tt=Array.from(crypto.getRandomValues(new Uint32Array(4)),t=>t.toString(16)).join("-"),Ve=new Map,Lt=(t,i,r)=>Ve.get(t)?.get(i)?.(r),Mt=0,jt=(t,i)=>new Promise((r,v)=>{let h=`${t}:${Tt}:${++Mt}`,d=setTimeout(()=>{ge.delete(h),v(new Error("Native UI session timed out"))},5e3);ge.set(h,{resolve:r,reject:v,timer:d});let p=JSON.stringify({key:h,request:i}),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(L){clearTimeout(d),ge.delete(h),v(L)}}),St=(t,i,r,v)=>{if(i===null){$e.get(t)?.(r);return}let h=ge.get(i);!h||!i.startsWith(`${t}:`)||(ge.delete(i),clearTimeout(h.timer),v?h.reject(new Error(v)):h.resolve(r))},xt=t=>{ye.get(t)?.show()},Ht=t=>(ye.get(t)?.hide(),!0),kt=async t=>{let i=ye.get(t);ye.delete(t),Ve.delete(t),$e.delete(t);for(let[r,v]of ge)r.startsWith(`${t}:`)&&(ge.delete(r),clearTimeout(v.timer),v.reject(new Error("Native UI session closed")));await i?.destroy()},Ct=async t=>{if(ye.has(t.id))return;let i,r=!1,v,h={show:()=>{v="native",i?.show()},hide:()=>{v="none",i?.hide()},destroy:async()=>{r=!0,await i?.destroy()}};ye.set(t.id,h);try{if(document.readyState==="loading"&&await new Promise(p=>document.addEventListener("DOMContentLoaded",()=>p(),{once:!0})),r)return;let d=new Map;t.runtime==="cordova"&&Ve.set(t.id,d),i=await ct(t,{send:p=>jt(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"?i.hide():v==="native"&&i.show(),r&&await i.destroy()}catch(d){throw ye.get(t.id)===h&&(ye.delete(t.id),Ve.delete(t.id)),d}};return vt(Rt);})();
|
|
@@ -14,6 +14,7 @@ public final class NativeUIScrollBindings {
|
|
|
14
14
|
for (id, group) in Dictionary(grouping: nodes.filter { $0.scroll != nil }, by: { $0.scroll!.id }) {
|
|
15
15
|
if group.contains(where: { $0.scroll?.reuse == true }) {
|
|
16
16
|
if let tracker = previous[id], tracker.replace(group, webView: webView, scale: scale) {
|
|
17
|
+
tracker.render = render
|
|
17
18
|
trackers[id] = tracker
|
|
18
19
|
} else { rejected.append(contentsOf: group.map(\.id)) }
|
|
19
20
|
} else if let tracker = NativeUIScrollTracker(webView: webView, nodes: group, scale: scale, render: render) {
|
|
@@ -34,7 +34,7 @@ public final class NativeUIScrollTracker {
|
|
|
34
34
|
private let contentOrigin: CGPoint
|
|
35
35
|
private let viewportCorrection: CGPoint
|
|
36
36
|
private var frames: [String: CGRect] = [:]
|
|
37
|
-
|
|
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,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webview-native-ui/engine",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
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,7 +33,7 @@
|
|
|
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
|
|
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",
|