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