hyperframes 0.7.74 → 0.7.76
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/cli.js +270 -69
- package/dist/commands/layout-audit.browser.js +69 -39
- package/dist/hyperframes-player.global.js +1 -1
- package/dist/studio/assets/{hyperframes-player-wiJqS2i-.js → hyperframes-player-DsRdxz7A.js} +1 -1
- package/dist/studio/assets/{index-BXepgTJb.js → index-BLICKger.js} +1 -1
- package/dist/studio/assets/{index-CBDJuOGW.js → index-BdOfFsR8.js} +3 -3
- package/dist/studio/assets/{index-DGVNG1dd.js → index-Du1RoLiB.js} +1 -1
- package/dist/studio/index.html +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -50,7 +50,7 @@ var VERSION;
|
|
|
50
50
|
var init_version = __esm({
|
|
51
51
|
"src/version.ts"() {
|
|
52
52
|
"use strict";
|
|
53
|
-
VERSION = true ? "0.7.
|
|
53
|
+
VERSION = true ? "0.7.76" : "0.0.0-dev";
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
|
|
@@ -117756,11 +117756,11 @@ async function runCaptureStage(input2) {
|
|
|
117756
117756
|
captureCfg
|
|
117757
117757
|
);
|
|
117758
117758
|
captureBeyondViewport = session.options.captureBeyondViewport;
|
|
117759
|
-
if (probeSession) {
|
|
117760
|
-
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
117761
|
-
probeSession = null;
|
|
117762
|
-
}
|
|
117763
117759
|
try {
|
|
117760
|
+
if (probeSession) {
|
|
117761
|
+
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
117762
|
+
probeSession = null;
|
|
117763
|
+
}
|
|
117764
117764
|
if (!session.isInitialized) {
|
|
117765
117765
|
await initializeSession(session);
|
|
117766
117766
|
} else if (process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true") {
|
|
@@ -125538,6 +125538,14 @@ function isExtractionCacheCompleteSentinelPath(path2) {
|
|
|
125538
125538
|
const segments = path2.split("/");
|
|
125539
125539
|
return segments.length === 3 && segments[0] === "video-frames" && segments[1] !== "" && segments[2] === EXTRACTION_CACHE_COMPLETE_SENTINEL;
|
|
125540
125540
|
}
|
|
125541
|
+
function resolveExtractedVideoOutputDir(planDir, videoId) {
|
|
125542
|
+
const videoRoot = resolve36(planDir, "video-frames");
|
|
125543
|
+
const outputDir = resolve36(videoRoot, videoId);
|
|
125544
|
+
if (outputDir === videoRoot || !outputDir.startsWith(`${videoRoot}${sep10}`)) {
|
|
125545
|
+
throw new PlanV2IntegrityError(`unsafe extracted video id: ${JSON.stringify(videoId)}`);
|
|
125546
|
+
}
|
|
125547
|
+
return outputDir;
|
|
125548
|
+
}
|
|
125541
125549
|
function artifactTargets(path2, videoDependencies) {
|
|
125542
125550
|
if (path2 === PLAN_AUDIO_RELATIVE_PATH) return { chunks: [], assembler: true };
|
|
125543
125551
|
if (path2 === "plan.json" || path2 === "meta/chunks.json" || path2 === "meta/encoder.json") {
|
|
@@ -125556,7 +125564,7 @@ function artifactTargets(path2, videoDependencies) {
|
|
|
125556
125564
|
}
|
|
125557
125565
|
function listVideoFramePaths(planV1Dir, videos) {
|
|
125558
125566
|
return videos.extracted.map((video) => {
|
|
125559
|
-
const outputDir =
|
|
125567
|
+
const outputDir = resolveExtractedVideoOutputDir(planV1Dir, video.videoId);
|
|
125560
125568
|
const frameNames = readdirSync19(outputDir).sort();
|
|
125561
125569
|
const framePaths = /* @__PURE__ */ new Map();
|
|
125562
125570
|
for (const frameName of frameNames) {
|
|
@@ -125670,6 +125678,14 @@ function parsePlanVideosJson(value) {
|
|
|
125670
125678
|
});
|
|
125671
125679
|
return { videos, extracted };
|
|
125672
125680
|
}
|
|
125681
|
+
function materializeExtractedVideoDirectories(planDir) {
|
|
125682
|
+
const videosPath = join68(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
125683
|
+
if (!existsSync59(videosPath)) return;
|
|
125684
|
+
const videos = parsePlanVideosJson(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
|
|
125685
|
+
for (const video of videos.extracted) {
|
|
125686
|
+
mkdirSync31(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
|
|
125687
|
+
}
|
|
125688
|
+
}
|
|
125673
125689
|
function parseChunkSlices(value) {
|
|
125674
125690
|
if (!Array.isArray(value)) {
|
|
125675
125691
|
throw new PlanV2IntegrityError("meta/chunks.json must be an array");
|
|
@@ -126077,6 +126093,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir) {
|
|
|
126077
126093
|
mkdirSync31(dirname30(destinationPath), { recursive: true });
|
|
126078
126094
|
copyFileSync8(sourcePath, destinationPath);
|
|
126079
126095
|
}
|
|
126096
|
+
if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
|
|
126080
126097
|
writeFileSync23(
|
|
126081
126098
|
join68(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
|
|
126082
126099
|
canonicalJsonStringify({ manifest, target }),
|
|
@@ -126411,6 +126428,51 @@ var init_assemble = __esm({
|
|
|
126411
126428
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
126412
126429
|
import { existsSync as existsSync61, mkdirSync as mkdirSync34, readFileSync as readFileSync36, readdirSync as readdirSync21, rmSync as rmSync23, writeFileSync as writeFileSync25 } from "fs";
|
|
126413
126430
|
import { extname as extname15, join as join70 } from "path";
|
|
126431
|
+
async function createVerifiedDistributedCaptureSession(serverUrl, framesDir, captureOptions, cfg, dependencies = distributedCaptureSessionDependencies) {
|
|
126432
|
+
const session = await dependencies.createCaptureSession(
|
|
126433
|
+
serverUrl,
|
|
126434
|
+
framesDir,
|
|
126435
|
+
captureOptions,
|
|
126436
|
+
null,
|
|
126437
|
+
cfg
|
|
126438
|
+
);
|
|
126439
|
+
try {
|
|
126440
|
+
await dependencies.assertSwiftShader(session.page, dependencies.readWebGlVendorInfo);
|
|
126441
|
+
await dependencies.initializeSession(session);
|
|
126442
|
+
return session;
|
|
126443
|
+
} catch (error) {
|
|
126444
|
+
await dependencies.closeCaptureSession(session).catch(() => {
|
|
126445
|
+
});
|
|
126446
|
+
throw error;
|
|
126447
|
+
}
|
|
126448
|
+
}
|
|
126449
|
+
function createChunkVideoFrameInjectorFactory(frameLookup) {
|
|
126450
|
+
return () => createVideoFrameInjector(frameLookup);
|
|
126451
|
+
}
|
|
126452
|
+
function isCaptureMode(value) {
|
|
126453
|
+
return value === "beginframe" || value === "screenshot" || value === "drawelement";
|
|
126454
|
+
}
|
|
126455
|
+
function shouldRetryChunkCaptureWithScreenshot(error) {
|
|
126456
|
+
const failure = classifyCaptureFailure(error);
|
|
126457
|
+
if (failure.kind === "cancelled" || failure.kind === "memory_exhaustion") return false;
|
|
126458
|
+
return /HeadlessExperimental\.beginFrame/i.test(failure.message) || /beginFrame probe timeout/i.test(failure.message) || /Another frame is pending|Frame still pending/i.test(failure.message);
|
|
126459
|
+
}
|
|
126460
|
+
async function runCaptureWithScreenshotFallback(input2) {
|
|
126461
|
+
try {
|
|
126462
|
+
return await input2.run(input2.forceScreenshot);
|
|
126463
|
+
} catch (error) {
|
|
126464
|
+
if (input2.forceScreenshot || !shouldRetryChunkCaptureWithScreenshot(error)) throw error;
|
|
126465
|
+
input2.onFallback?.(error);
|
|
126466
|
+
await input2.resetForScreenshotRetry();
|
|
126467
|
+
return await input2.run(true);
|
|
126468
|
+
}
|
|
126469
|
+
}
|
|
126470
|
+
async function beginFrameSessionNeedsScreenshotFallback(session, probe = probeBeginFrameLiveness) {
|
|
126471
|
+
if (session.launchCaptureMode !== "beginframe") return false;
|
|
126472
|
+
const timeoutMs = Number(process.env.PRODUCER_BEGINFRAME_PROBE_TIMEOUT_MS) > 0 ? Number(process.env.PRODUCER_BEGINFRAME_PROBE_TIMEOUT_MS) : 3e4;
|
|
126473
|
+
const probeTick = Math.max(0, session.beginFrameTimeTicks - 5 * session.beginFrameIntervalMs);
|
|
126474
|
+
return !await probe(session.page, timeoutMs, probeTick, session.beginFrameIntervalMs);
|
|
126475
|
+
}
|
|
126414
126476
|
function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v1") {
|
|
126415
126477
|
const result = [];
|
|
126416
126478
|
for (const v2 of videos) {
|
|
@@ -126581,16 +126643,15 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
126581
126643
|
// the software-GPU clamp so buildChromeArgs includes BeginFrameControl.
|
|
126582
126644
|
forceScreenshotExplicitlyOptedOut: !encoder.forceScreenshot
|
|
126583
126645
|
};
|
|
126584
|
-
const
|
|
126585
|
-
|
|
126586
|
-
|
|
126587
|
-
|
|
126588
|
-
|
|
126589
|
-
|
|
126590
|
-
v2Manifest === null ? "dense-v1" : "sparse-v2"
|
|
126591
|
-
)
|
|
126646
|
+
const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? createFrameLookupTable(
|
|
126647
|
+
planVideos.videos,
|
|
126648
|
+
rebuildExtractedFramesFromPlanDir(
|
|
126649
|
+
planDir,
|
|
126650
|
+
planVideos.extracted,
|
|
126651
|
+
v2Manifest === null ? "dense-v1" : "sparse-v2"
|
|
126592
126652
|
)
|
|
126593
126653
|
) : null;
|
|
126654
|
+
const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(videoFrameLookup);
|
|
126594
126655
|
const videoCaptureBeyondViewport = resolveVideoCaptureBeyondViewport(
|
|
126595
126656
|
planVideos?.videos.length ?? 0
|
|
126596
126657
|
);
|
|
@@ -126634,61 +126695,142 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
126634
126695
|
let encodeStageMs = 0;
|
|
126635
126696
|
let captureMode;
|
|
126636
126697
|
const capturePerfs = [];
|
|
126698
|
+
const captureAttempts = [];
|
|
126699
|
+
let forceScreenshotForChunk = encoder.forceScreenshot;
|
|
126637
126700
|
try {
|
|
126638
|
-
if (chunkWorkerCount === 1) {
|
|
126701
|
+
if (chunkWorkerCount === 1 || !forceScreenshotForChunk) {
|
|
126639
126702
|
const bootStarted = Date.now();
|
|
126640
|
-
session = await
|
|
126641
|
-
|
|
126642
|
-
|
|
126703
|
+
session = await createVerifiedDistributedCaptureSession(
|
|
126704
|
+
fileServer.url,
|
|
126705
|
+
framesDir,
|
|
126706
|
+
captureOptions,
|
|
126707
|
+
cfg
|
|
126708
|
+
);
|
|
126643
126709
|
sessionBootMs = Date.now() - bootStarted;
|
|
126710
|
+
const browserSelectedScreenshot = session.launchCaptureMode === "screenshot";
|
|
126711
|
+
const beginFrameStalled = !browserSelectedScreenshot && await beginFrameSessionNeedsScreenshotFallback(session);
|
|
126712
|
+
if (browserSelectedScreenshot) {
|
|
126713
|
+
forceScreenshotForChunk = true;
|
|
126714
|
+
log2.warn(
|
|
126715
|
+
"[renderChunk] Browser capability probe selected screenshot capture for the entire chunk",
|
|
126716
|
+
{ chunkIndex }
|
|
126717
|
+
);
|
|
126718
|
+
if (chunkWorkerCount > 1) {
|
|
126719
|
+
await closeCaptureSession(session);
|
|
126720
|
+
session = null;
|
|
126721
|
+
}
|
|
126722
|
+
} else if (beginFrameStalled) {
|
|
126723
|
+
forceScreenshotForChunk = true;
|
|
126724
|
+
log2.warn(
|
|
126725
|
+
"[renderChunk] BeginFrame liveness probe failed; using screenshot capture for the entire chunk",
|
|
126726
|
+
{ chunkIndex }
|
|
126727
|
+
);
|
|
126728
|
+
await closeCaptureSession(session).catch(() => {
|
|
126729
|
+
});
|
|
126730
|
+
session = null;
|
|
126731
|
+
if (chunkWorkerCount === 1) {
|
|
126732
|
+
const screenshotBootStarted = Date.now();
|
|
126733
|
+
const screenshotCfg = {
|
|
126734
|
+
...cfg,
|
|
126735
|
+
forceScreenshot: true,
|
|
126736
|
+
forceScreenshotExplicitlyOptedOut: false
|
|
126737
|
+
};
|
|
126738
|
+
session = await createVerifiedDistributedCaptureSession(
|
|
126739
|
+
fileServer.url,
|
|
126740
|
+
framesDir,
|
|
126741
|
+
captureOptions,
|
|
126742
|
+
screenshotCfg
|
|
126743
|
+
);
|
|
126744
|
+
sessionBootMs += Date.now() - screenshotBootStarted;
|
|
126745
|
+
}
|
|
126746
|
+
} else if (chunkWorkerCount > 1) {
|
|
126747
|
+
await closeCaptureSession(session);
|
|
126748
|
+
session = null;
|
|
126749
|
+
}
|
|
126644
126750
|
}
|
|
126645
126751
|
const captureStarted = Date.now();
|
|
126646
|
-
|
|
126647
|
-
|
|
126648
|
-
|
|
126649
|
-
|
|
126650
|
-
|
|
126651
|
-
|
|
126652
|
-
|
|
126653
|
-
|
|
126654
|
-
|
|
126655
|
-
|
|
126656
|
-
|
|
126657
|
-
|
|
126658
|
-
|
|
126659
|
-
|
|
126660
|
-
|
|
126661
|
-
|
|
126662
|
-
|
|
126663
|
-
|
|
126664
|
-
|
|
126665
|
-
|
|
126666
|
-
|
|
126667
|
-
|
|
126668
|
-
|
|
126669
|
-
|
|
126670
|
-
|
|
126671
|
-
|
|
126672
|
-
|
|
126673
|
-
|
|
126674
|
-
|
|
126675
|
-
|
|
126676
|
-
|
|
126752
|
+
captureMode = await runCaptureWithScreenshotFallback({
|
|
126753
|
+
forceScreenshot: forceScreenshotForChunk,
|
|
126754
|
+
// fallow-ignore-next-line complexity
|
|
126755
|
+
run: async (forceScreenshot) => {
|
|
126756
|
+
const captureCfg = cfg.forceScreenshot === forceScreenshot ? cfg : {
|
|
126757
|
+
...cfg,
|
|
126758
|
+
forceScreenshot,
|
|
126759
|
+
forceScreenshotExplicitlyOptedOut: !forceScreenshot
|
|
126760
|
+
};
|
|
126761
|
+
if (chunkWorkerCount === 1 && session === null) {
|
|
126762
|
+
session = await createVerifiedDistributedCaptureSession(
|
|
126763
|
+
fileServer.url,
|
|
126764
|
+
framesDir,
|
|
126765
|
+
captureOptions,
|
|
126766
|
+
captureCfg
|
|
126767
|
+
);
|
|
126768
|
+
}
|
|
126769
|
+
const capturePlan = createCapturePlan({
|
|
126770
|
+
workerCount: chunkWorkerCount,
|
|
126771
|
+
forceScreenshot,
|
|
126772
|
+
useStreamingEncode: false,
|
|
126773
|
+
useLayeredComposite: false,
|
|
126774
|
+
usePageSideCompositing: false,
|
|
126775
|
+
hasHdrContent: false,
|
|
126776
|
+
needsAlpha: plan2.dimensions.format !== "mp4"
|
|
126777
|
+
});
|
|
126778
|
+
if (capturePlan.kind !== "sdr_disk") {
|
|
126779
|
+
throw new Error(`Distributed chunk requires sdr_disk plan; got ${capturePlan.kind}`);
|
|
126780
|
+
}
|
|
126781
|
+
const captureSession = session;
|
|
126782
|
+
session = null;
|
|
126783
|
+
await runCaptureStage({
|
|
126784
|
+
fileServer,
|
|
126785
|
+
workDir,
|
|
126786
|
+
framesDir,
|
|
126787
|
+
job,
|
|
126788
|
+
totalFrames: framesInChunk,
|
|
126789
|
+
cfg: captureCfg,
|
|
126790
|
+
plan: capturePlan,
|
|
126791
|
+
log: log2,
|
|
126792
|
+
probeSession: captureSession,
|
|
126793
|
+
captureAttempts,
|
|
126794
|
+
// This sink records each worker's effective capture mode so a
|
|
126795
|
+
// fallback remains observable to adapters and smoke tests.
|
|
126796
|
+
dedupPerfs: capturePerfs,
|
|
126797
|
+
buildCaptureOptions: () => captureOptions,
|
|
126798
|
+
createRenderVideoFrameInjector: createChunkVideoFrameInjector,
|
|
126799
|
+
abortSignal: void 0,
|
|
126800
|
+
assertNotAborted: () => {
|
|
126801
|
+
},
|
|
126802
|
+
frameRange: { startFrame: slice.startFrame, endFrame: slice.endFrame }
|
|
126803
|
+
});
|
|
126804
|
+
const observedModes = new Set(capturePerfs.map((perf) => perf.captureMode));
|
|
126805
|
+
if (observedModes.size !== 1 || ![...observedModes].every(isCaptureMode)) {
|
|
126806
|
+
throw new Error(
|
|
126807
|
+
`[renderChunk] capture workers reported invalid or inconsistent modes: ${[...observedModes].join(",") || "<none>"}`
|
|
126808
|
+
);
|
|
126809
|
+
}
|
|
126810
|
+
const [observedMode] = observedModes;
|
|
126811
|
+
if (!observedMode || !isCaptureMode(observedMode)) {
|
|
126812
|
+
throw new Error("[renderChunk] capture completed without an observed mode");
|
|
126813
|
+
}
|
|
126814
|
+
return observedMode;
|
|
126815
|
+
},
|
|
126816
|
+
resetForScreenshotRetry: () => {
|
|
126817
|
+
rmSync23(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
126818
|
+
mkdirSync34(framesDir, { recursive: true });
|
|
126819
|
+
capturePerfs.length = 0;
|
|
126820
|
+
captureAttempts.length = 0;
|
|
126821
|
+
job.framesRendered = 0;
|
|
126677
126822
|
},
|
|
126678
|
-
|
|
126823
|
+
onFallback: (error) => {
|
|
126824
|
+
log2.warn(
|
|
126825
|
+
"[renderChunk] BeginFrame capture failed; retrying the entire chunk once in screenshot mode",
|
|
126826
|
+
{
|
|
126827
|
+
chunkIndex,
|
|
126828
|
+
error: error instanceof Error ? error.message : String(error)
|
|
126829
|
+
}
|
|
126830
|
+
);
|
|
126831
|
+
}
|
|
126679
126832
|
});
|
|
126680
126833
|
captureStageMs = Date.now() - captureStarted;
|
|
126681
|
-
session = null;
|
|
126682
|
-
const observedModes = new Set(capturePerfs.map((perf) => perf.captureMode));
|
|
126683
|
-
const validModes = /* @__PURE__ */ new Set(["beginframe", "screenshot", "drawelement"]);
|
|
126684
|
-
if (observedModes.size !== 1 || ![...observedModes].every(
|
|
126685
|
-
(mode) => validModes.has(mode)
|
|
126686
|
-
)) {
|
|
126687
|
-
throw new Error(
|
|
126688
|
-
`[renderChunk] capture workers reported invalid or inconsistent modes: ${[...observedModes].join(",") || "<none>"}`
|
|
126689
|
-
);
|
|
126690
|
-
}
|
|
126691
|
-
captureMode = [...observedModes][0];
|
|
126692
126834
|
framesEncoded = framesInChunk;
|
|
126693
126835
|
const isPngSequence = plan2.dimensions.format === "png-sequence";
|
|
126694
126836
|
outputKind = isPngSequence ? "frame-dir" : "file";
|
|
@@ -126808,7 +126950,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
126808
126950
|
envRestore.restore();
|
|
126809
126951
|
}
|
|
126810
126952
|
}
|
|
126811
|
-
var FFMPEG_VERSION_MISMATCH, PLAN_HASH_MISMATCH, MISSING_PLAN_ARTIFACT, CHUNK_INDEX_OUT_OF_RANGE, MISSING_RUNTIME_ENV_SNAPSHOT, LEGACY_DISTRIBUTED_VP9_CPU_USED, RenderChunkValidationError;
|
|
126953
|
+
var FFMPEG_VERSION_MISMATCH, PLAN_HASH_MISMATCH, MISSING_PLAN_ARTIFACT, CHUNK_INDEX_OUT_OF_RANGE, MISSING_RUNTIME_ENV_SNAPSHOT, LEGACY_DISTRIBUTED_VP9_CPU_USED, RenderChunkValidationError, distributedCaptureSessionDependencies;
|
|
126812
126954
|
var init_renderChunk = __esm({
|
|
126813
126955
|
"../producer/src/services/distributed/renderChunk.ts"() {
|
|
126814
126956
|
"use strict";
|
|
@@ -126841,6 +126983,13 @@ var init_renderChunk = __esm({
|
|
|
126841
126983
|
this.code = code;
|
|
126842
126984
|
}
|
|
126843
126985
|
};
|
|
126986
|
+
distributedCaptureSessionDependencies = {
|
|
126987
|
+
createCaptureSession,
|
|
126988
|
+
assertSwiftShader,
|
|
126989
|
+
initializeSession,
|
|
126990
|
+
closeCaptureSession,
|
|
126991
|
+
readWebGlVendorInfo: readWebGlVendorInfoFromCanvas
|
|
126992
|
+
};
|
|
126844
126993
|
}
|
|
126845
126994
|
});
|
|
126846
126995
|
|
|
@@ -139638,13 +139787,63 @@ function hasEnoughRotationSamples(group) {
|
|
|
139638
139787
|
function isActuallySpinning(group) {
|
|
139639
139788
|
return maxAngleSpread(group.map((s2) => s2.angle)) > ROTATION_MIN_ANGLE_SPREAD_DEG;
|
|
139640
139789
|
}
|
|
139790
|
+
function aabbForRotatedRect(elemW, elemH, angleDeg) {
|
|
139791
|
+
const rad = angleDeg * Math.PI / 180;
|
|
139792
|
+
const cosAbs = Math.abs(Math.cos(rad));
|
|
139793
|
+
const sinAbs = Math.abs(Math.sin(rad));
|
|
139794
|
+
return { w: elemW * cosAbs + elemH * sinAbs, h: elemW * sinAbs + elemH * cosAbs };
|
|
139795
|
+
}
|
|
139796
|
+
function unrotatedSizeFromSample(sample) {
|
|
139797
|
+
const rad = sample.angle * Math.PI / 180;
|
|
139798
|
+
const cosAbs = Math.abs(Math.cos(rad));
|
|
139799
|
+
const sinAbs = Math.abs(Math.sin(rad));
|
|
139800
|
+
const det = cosAbs * cosAbs - sinAbs * sinAbs;
|
|
139801
|
+
if (Math.abs(det) < ROTATION_RIGID_ESTIMATE_MIN_DET) return null;
|
|
139802
|
+
const elemW = (cosAbs * sample.w - sinAbs * sample.h) / det;
|
|
139803
|
+
const elemH = (cosAbs * sample.h - sinAbs * sample.w) / det;
|
|
139804
|
+
if (!(elemW > 0) || !(elemH > 0)) return null;
|
|
139805
|
+
return { w: elemW, h: elemH };
|
|
139806
|
+
}
|
|
139807
|
+
function aabbMatchesSample(expected, sample) {
|
|
139808
|
+
if (expected.w <= 0 || expected.h <= 0 || sample.w <= 0 || sample.h <= 0) return false;
|
|
139809
|
+
return Math.max(expected.w, sample.w) / Math.min(expected.w, sample.w) <= ROTATION_RIGID_AABB_RATIO && Math.max(expected.h, sample.h) / Math.min(expected.h, sample.h) <= ROTATION_RIGID_AABB_RATIO;
|
|
139810
|
+
}
|
|
139811
|
+
function isSingularRotationAngle(angleDeg) {
|
|
139812
|
+
const rad = angleDeg * Math.PI / 180;
|
|
139813
|
+
const cosAbs = Math.abs(Math.cos(rad));
|
|
139814
|
+
const sinAbs = Math.abs(Math.sin(rad));
|
|
139815
|
+
return Math.abs(cosAbs * cosAbs - sinAbs * sinAbs) < ROTATION_RIGID_ESTIMATE_MIN_DET;
|
|
139816
|
+
}
|
|
139817
|
+
function isNearSquareAabb(sample) {
|
|
139818
|
+
if (sample.w <= 0 || sample.h <= 0) return false;
|
|
139819
|
+
return Math.max(sample.w, sample.h) / Math.min(sample.w, sample.h) <= ROTATION_RIGID_AABB_RATIO;
|
|
139820
|
+
}
|
|
139821
|
+
function fitsSingularPhaseRigidProjection(group) {
|
|
139822
|
+
if (group.length === 0 || !group.every((s2) => isSingularRotationAngle(s2.angle))) return false;
|
|
139823
|
+
if (!group.every(isNearSquareAabb)) return false;
|
|
139824
|
+
const ref2 = group[0];
|
|
139825
|
+
if (!ref2) return false;
|
|
139826
|
+
return group.every((sample) => aabbMatchesSample({ w: ref2.w, h: ref2.h }, sample));
|
|
139827
|
+
}
|
|
139828
|
+
function fitsOneRigidRectangle(group) {
|
|
139829
|
+
for (const ref2 of group) {
|
|
139830
|
+
const size = unrotatedSizeFromSample(ref2);
|
|
139831
|
+
if (!size) continue;
|
|
139832
|
+
if (group.every(
|
|
139833
|
+
(sample) => aabbMatchesSample(aabbForRotatedRect(size.w, size.h, sample.angle), sample)
|
|
139834
|
+
)) {
|
|
139835
|
+
return true;
|
|
139836
|
+
}
|
|
139837
|
+
}
|
|
139838
|
+
return fitsSingularPhaseRigidProjection(group);
|
|
139839
|
+
}
|
|
139641
139840
|
function isRotationSizeStable(group) {
|
|
139642
|
-
|
|
139643
|
-
const
|
|
139644
|
-
const
|
|
139645
|
-
|
|
139646
|
-
if (
|
|
139647
|
-
return
|
|
139841
|
+
if (group.some((s2) => s2.w <= 0 || s2.h <= 0)) return false;
|
|
139842
|
+
const longSides = group.map((s2) => Math.max(s2.w, s2.h));
|
|
139843
|
+
const minLong = Math.min(...longSides);
|
|
139844
|
+
if (minLong <= 0) return false;
|
|
139845
|
+
if (Math.max(...longSides) / minLong > ROTATION_MAX_SIZE_RATIO) return false;
|
|
139846
|
+
return fitsOneRigidRectangle(group);
|
|
139648
139847
|
}
|
|
139649
139848
|
function isSizableRotation(group) {
|
|
139650
139849
|
return median(group.map((s2) => s2.w * s2.h)) >= ROTATION_MIN_MEDIAN_AREA_PX;
|
|
@@ -140205,7 +140404,7 @@ async function captureFindingCrops2(project, options, requests) {
|
|
|
140205
140404
|
const module = await Promise.resolve().then(() => (init_checkBrowser(), checkBrowser_exports));
|
|
140206
140405
|
return module.captureFindingCrops(project, options, requests);
|
|
140207
140406
|
}
|
|
140208
|
-
var MOTION_FPS2, MOTION_MAX_SAMPLES2, ZERO_BBOX, FRAME_BREACH_FLOOR_PX, FRAME_BREACH_FLOOR_FRACTION, DEFAULT_CHECK_OPTIONS, OVERLAP_SAMPLE_FPS, OVERLAP_MAX_SAMPLES, SWEEP_STATIC_MIN_DURATION_SEC, ZERO_LAYOUT_RECT, ROTATION_MIN_SAMPLES, ROTATION_MIN_ANGLE_SPREAD_DEG, ROTATION_MAX_SIZE_RATIO, ROTATION_MIN_MEDIAN_AREA_PX, ROTATION_DRIFT_SIZE_FRACTION, ROTATION_DRIFT_VIEWPORT_FRACTION, INDICATOR_MIN_SAMPLES, INDICATOR_MIN_ANGLE_SPREAD_DEG, INDICATOR_MIN_HUB_CIRCLES, INDICATOR_MAX_FIT_RESIDUAL_FRACTION, INDICATOR_MAX_RIGID_LEN_VARIATION, INDICATOR_DRIFT_LENGTH_FRACTION, INDICATOR_MIN_HUB_PRESENCE_FRACTION, MAX_FINDING_CROPS, DEFAULT_DEPENDENCIES;
|
|
140407
|
+
var MOTION_FPS2, MOTION_MAX_SAMPLES2, ZERO_BBOX, FRAME_BREACH_FLOOR_PX, FRAME_BREACH_FLOOR_FRACTION, DEFAULT_CHECK_OPTIONS, OVERLAP_SAMPLE_FPS, OVERLAP_MAX_SAMPLES, SWEEP_STATIC_MIN_DURATION_SEC, ZERO_LAYOUT_RECT, ROTATION_MIN_SAMPLES, ROTATION_MIN_ANGLE_SPREAD_DEG, ROTATION_MAX_SIZE_RATIO, ROTATION_RIGID_AABB_RATIO, ROTATION_RIGID_ESTIMATE_MIN_DET, ROTATION_MIN_MEDIAN_AREA_PX, ROTATION_DRIFT_SIZE_FRACTION, ROTATION_DRIFT_VIEWPORT_FRACTION, INDICATOR_MIN_SAMPLES, INDICATOR_MIN_ANGLE_SPREAD_DEG, INDICATOR_MIN_HUB_CIRCLES, INDICATOR_MAX_FIT_RESIDUAL_FRACTION, INDICATOR_MAX_RIGID_LEN_VARIATION, INDICATOR_DRIFT_LENGTH_FRACTION, INDICATOR_MIN_HUB_PRESENCE_FRACTION, MAX_FINDING_CROPS, DEFAULT_DEPENDENCIES;
|
|
140209
140408
|
var init_checkPipeline = __esm({
|
|
140210
140409
|
"src/utils/checkPipeline.ts"() {
|
|
140211
140410
|
"use strict";
|
|
@@ -140248,6 +140447,8 @@ var init_checkPipeline = __esm({
|
|
|
140248
140447
|
ROTATION_MIN_SAMPLES = 3;
|
|
140249
140448
|
ROTATION_MIN_ANGLE_SPREAD_DEG = 20;
|
|
140250
140449
|
ROTATION_MAX_SIZE_RATIO = 1.6;
|
|
140450
|
+
ROTATION_RIGID_AABB_RATIO = 1.15;
|
|
140451
|
+
ROTATION_RIGID_ESTIMATE_MIN_DET = 0.15;
|
|
140251
140452
|
ROTATION_MIN_MEDIAN_AREA_PX = 2500;
|
|
140252
140453
|
ROTATION_DRIFT_SIZE_FRACTION = 0.1;
|
|
140253
140454
|
ROTATION_DRIFT_VIEWPORT_FRACTION = 0.02;
|
|
@@ -1198,6 +1198,7 @@
|
|
|
1198
1198
|
return issues;
|
|
1199
1199
|
}
|
|
1200
1200
|
|
|
1201
|
+
// Soft prior only — the counterfactual attach test (below) is what makes detachment a finding.
|
|
1201
1202
|
const CONNECTOR_NAME = /\b(conn(ector)?|arrow|edge|link|flow|wire)\b/i;
|
|
1202
1203
|
const CONNECTOR_SKIP_CONTAINERS = "defs, marker, clipPath, mask, symbol, pattern";
|
|
1203
1204
|
|
|
@@ -1207,14 +1208,16 @@
|
|
|
1207
1208
|
return `${element.id || ""} ${className}`;
|
|
1208
1209
|
}
|
|
1209
1210
|
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1211
|
+
function isConnectorPath(svg, path) {
|
|
1212
|
+
if (path.hasAttribute("marker-start") || path.hasAttribute("marker-end")) return true;
|
|
1213
|
+
return (
|
|
1214
|
+
CONNECTOR_NAME.test(connectorNameFor(svg)) || CONNECTOR_NAME.test(connectorNameFor(path))
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/** Raw `d`-space endpoints (no CTM) — the mapping authors use when they paste screen coords into `d`. */
|
|
1219
|
+
function pathUserEndpoints(path) {
|
|
1220
|
+
if (typeof path.getTotalLength !== "function" || typeof path.getPointAtLength !== "function") {
|
|
1218
1221
|
return null;
|
|
1219
1222
|
}
|
|
1220
1223
|
let total;
|
|
@@ -1224,6 +1227,20 @@
|
|
|
1224
1227
|
return null;
|
|
1225
1228
|
}
|
|
1226
1229
|
if (!Number.isFinite(total) || total <= 0) return null;
|
|
1230
|
+
const start = path.getPointAtLength(0);
|
|
1231
|
+
const end = path.getPointAtLength(total);
|
|
1232
|
+
return { start: { x: start.x, y: start.y }, end: { x: end.x, y: end.y } };
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// Screen endpoints via getScreenCTM (viewBox, preserveAspectRatio, group transforms).
|
|
1236
|
+
function pathScreenEndpoints(svg, path, user) {
|
|
1237
|
+
if (
|
|
1238
|
+
!user ||
|
|
1239
|
+
typeof path.getScreenCTM !== "function" ||
|
|
1240
|
+
typeof svg.createSVGPoint !== "function"
|
|
1241
|
+
) {
|
|
1242
|
+
return null;
|
|
1243
|
+
}
|
|
1227
1244
|
const matrix = path.getScreenCTM();
|
|
1228
1245
|
if (!matrix) return null;
|
|
1229
1246
|
const toScreen = (local) => {
|
|
@@ -1233,10 +1250,7 @@
|
|
|
1233
1250
|
const mapped = point.matrixTransform(matrix);
|
|
1234
1251
|
return { x: mapped.x, y: mapped.y };
|
|
1235
1252
|
};
|
|
1236
|
-
return {
|
|
1237
|
-
start: toScreen(path.getPointAtLength(0)),
|
|
1238
|
-
end: toScreen(path.getPointAtLength(total)),
|
|
1239
|
-
};
|
|
1253
|
+
return { start: toScreen(user.start), end: toScreen(user.end) };
|
|
1240
1254
|
}
|
|
1241
1255
|
|
|
1242
1256
|
function distanceToRect(point, rect) {
|
|
@@ -1246,6 +1260,7 @@
|
|
|
1246
1260
|
}
|
|
1247
1261
|
|
|
1248
1262
|
// Solid, compact elements a connector could plausibly anchor to.
|
|
1263
|
+
// Both tiers keep `element` so attachment identity is stable across containment vs near-miss.
|
|
1249
1264
|
function connectorAnchorRects(root, rootRect) {
|
|
1250
1265
|
const compact = [];
|
|
1251
1266
|
const painted = [];
|
|
@@ -1261,42 +1276,57 @@
|
|
|
1261
1276
|
if (area < 400) continue;
|
|
1262
1277
|
// Containment tier: large opaque targets only — a text-bearing wrapper contains its own diagram's endpoints.
|
|
1263
1278
|
if (opaque && area <= rootArea * 0.6) painted.push({ rect, element });
|
|
1264
|
-
if (area <= rootArea * 0.15) compact.push(rect);
|
|
1279
|
+
if (area <= rootArea * 0.15) compact.push({ rect, element });
|
|
1265
1280
|
}
|
|
1266
1281
|
return { compact, painted };
|
|
1267
1282
|
}
|
|
1268
1283
|
|
|
1269
|
-
|
|
1270
|
-
if (path.hasAttribute("marker-start") || path.hasAttribute("marker-end")) return true;
|
|
1271
|
-
return (
|
|
1272
|
-
CONNECTOR_NAME.test(connectorNameFor(svg)) || CONNECTOR_NAME.test(connectorNameFor(path))
|
|
1273
|
-
);
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
// A connector whose BOTH endpoints land far from every anchorable element was drawn in the wrong frame.
|
|
1277
|
-
// min over the two endpoints is intentional: a half-attached connector is a design choice, not frame drift.
|
|
1284
|
+
// Flag only the documented bug: rendered endpoints miss, but user-space-as-screen would attach.
|
|
1278
1285
|
function connectorDetachmentIssues(root, rootRect, time) {
|
|
1279
1286
|
const issues = [];
|
|
1280
1287
|
let anchors = null;
|
|
1288
|
+
// Attach near-miss tolerance (screen px). Separate from the closed-glyph chord floor.
|
|
1281
1289
|
const threshold = Math.max(32, Math.min(rootRect.width, rootRect.height) * 0.02);
|
|
1290
|
+
const MIN_CONNECTOR_CHORD_PX = 8;
|
|
1282
1291
|
for (const svg of Array.from(root.querySelectorAll("svg"))) {
|
|
1283
1292
|
if (!isVisibleElement(svg) || hasAllowOverflowFlag(svg)) continue;
|
|
1284
1293
|
for (const path of Array.from(svg.querySelectorAll("path"))) {
|
|
1285
1294
|
if (path.closest(CONNECTOR_SKIP_CONTAINERS)) continue;
|
|
1286
1295
|
if (!isConnectorPath(svg, path)) continue;
|
|
1287
|
-
const
|
|
1288
|
-
|
|
1296
|
+
const user = pathUserEndpoints(path);
|
|
1297
|
+
const rendered = pathScreenEndpoints(svg, path, user);
|
|
1298
|
+
if (!user || !rendered) continue;
|
|
1299
|
+
// Closed/glyph paths collapse to one point — compare in screen px (not user units).
|
|
1300
|
+
const renderedChord = Math.hypot(
|
|
1301
|
+
rendered.end.x - rendered.start.x,
|
|
1302
|
+
rendered.end.y - rendered.start.y,
|
|
1303
|
+
);
|
|
1304
|
+
if (renderedChord < MIN_CONNECTOR_CHORD_PX) continue;
|
|
1289
1305
|
if (anchors === null) anchors = connectorAnchorRects(root, rootRect);
|
|
1290
1306
|
if (anchors.compact.length < 2) return issues;
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1307
|
+
// Stable DOM identity across painted (inside) and compact (near-miss) tiers.
|
|
1308
|
+
const attachmentKey = (point) => {
|
|
1309
|
+
for (const anchor of anchors.painted) {
|
|
1310
|
+
if (!anchor.element.contains(svg) && distanceToRect(point, anchor.rect) === 0) {
|
|
1311
|
+
return anchor.element;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
for (const anchor of anchors.compact) {
|
|
1315
|
+
if (distanceToRect(point, anchor.rect) <= threshold) return anchor.element;
|
|
1316
|
+
}
|
|
1317
|
+
return null;
|
|
1318
|
+
};
|
|
1319
|
+
const attached = (point) => attachmentKey(point) !== null;
|
|
1320
|
+
// Half-attached as drawn is allowed; only full render-miss proceeds.
|
|
1321
|
+
if (attached(rendered.start) || attached(rendered.end)) continue;
|
|
1322
|
+
// Paste-into-`d` bug: both raw endpoints land on distinct anchors as screen pixels.
|
|
1323
|
+
const userStartKey = attachmentKey(user.start);
|
|
1324
|
+
const userEndKey = attachmentKey(user.end);
|
|
1325
|
+
if (!userStartKey || !userEndKey || userStartKey === userEndKey) continue;
|
|
1296
1326
|
const gap = Math.round(
|
|
1297
1327
|
Math.min(
|
|
1298
|
-
Math.min(...anchors.compact.map((
|
|
1299
|
-
Math.min(...anchors.compact.map((
|
|
1328
|
+
Math.min(...anchors.compact.map((a) => distanceToRect(rendered.start, a.rect))),
|
|
1329
|
+
Math.min(...anchors.compact.map((a) => distanceToRect(rendered.end, a.rect))),
|
|
1300
1330
|
),
|
|
1301
1331
|
);
|
|
1302
1332
|
issues.push({
|
|
@@ -1305,17 +1335,17 @@
|
|
|
1305
1335
|
time,
|
|
1306
1336
|
selector: selectorFor(path),
|
|
1307
1337
|
containerSelector: selectorFor(svg),
|
|
1308
|
-
message: `Connector path endpoints
|
|
1338
|
+
message: `Connector path endpoints render ${gap}px from the nearest anchorable element, but the path's user-space coordinates would attach if read as screen pixels — screen/viewport numbers were likely written into SVG \`d\` without inverting the CTM.`,
|
|
1309
1339
|
rect: toRect({
|
|
1310
|
-
left: Math.min(
|
|
1311
|
-
top: Math.min(
|
|
1312
|
-
right: Math.max(
|
|
1313
|
-
bottom: Math.max(
|
|
1314
|
-
width: Math.abs(
|
|
1315
|
-
height: Math.abs(
|
|
1340
|
+
left: Math.min(rendered.start.x, rendered.end.x),
|
|
1341
|
+
top: Math.min(rendered.start.y, rendered.end.y),
|
|
1342
|
+
right: Math.max(rendered.start.x, rendered.end.x),
|
|
1343
|
+
bottom: Math.max(rendered.start.y, rendered.end.y),
|
|
1344
|
+
width: Math.abs(rendered.end.x - rendered.start.x),
|
|
1345
|
+
height: Math.abs(rendered.end.y - rendered.start.y),
|
|
1316
1346
|
}),
|
|
1317
1347
|
fixHint:
|
|
1318
|
-
"
|
|
1348
|
+
"Convert measured screen coordinates into the SVG's user space (subtract the SVG rect / invert getScreenCTM) before writing path `d`, and keep the SVG a direct child of the stage.",
|
|
1319
1349
|
});
|
|
1320
1350
|
}
|
|
1321
1351
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var HyperframesPlayer=(()=>{var J=Object.defineProperty;var Ge=Object.getOwnPropertyDescriptor;var qe=Object.getOwnPropertyNames;var Ye=Object.prototype.hasOwnProperty;var Xe=(i,e)=>{for(var t in e)J(i,t,{get:e[t],enumerable:!0})},Ze=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of qe(e))!Ye.call(i,n)&&n!==t&&J(i,n,{get:()=>e[n],enumerable:!(r=Ge(e,n))||r.enumerable});return i};var Qe=i=>Ze(J({},"__esModule",{value:!0}),i);var Lt={};Xe(Lt,{HyperframesPlayer:()=>Q,SPEED_PRESETS:()=>te,formatSpeed:()=>N,formatTime:()=>$});function ye(i){return i.hasRuntime||i.runtimeInjected?!1:!!(i.hasNestedCompositions||i.hasTimelines&&i.attempts>=5)}function I(i){return typeof i=="object"&&i!==null}function Ee(i){return I(i)&&typeof i.getDuration=="function"}function Se(i){return I(i)&&typeof i.duration=="function"&&typeof i.time=="function"&&typeof i.seek=="function"&&typeof i.play=="function"&&typeof i.pause=="function"}var Je="https://cdn.jsdelivr.net/npm/@hyperframes/core@0.7.
|
|
1
|
+
"use strict";var HyperframesPlayer=(()=>{var J=Object.defineProperty;var Ge=Object.getOwnPropertyDescriptor;var qe=Object.getOwnPropertyNames;var Ye=Object.prototype.hasOwnProperty;var Xe=(i,e)=>{for(var t in e)J(i,t,{get:e[t],enumerable:!0})},Ze=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of qe(e))!Ye.call(i,n)&&n!==t&&J(i,n,{get:()=>e[n],enumerable:!(r=Ge(e,n))||r.enumerable});return i};var Qe=i=>Ze(J({},"__esModule",{value:!0}),i);var Lt={};Xe(Lt,{HyperframesPlayer:()=>Q,SPEED_PRESETS:()=>te,formatSpeed:()=>N,formatTime:()=>$});function ye(i){return i.hasRuntime||i.runtimeInjected?!1:!!(i.hasNestedCompositions||i.hasTimelines&&i.attempts>=5)}function I(i){return typeof i=="object"&&i!==null}function Ee(i){return I(i)&&typeof i.getDuration=="function"}function Se(i){return I(i)&&typeof i.duration=="function"&&typeof i.time=="function"&&typeof i.seek=="function"&&typeof i.play=="function"&&typeof i.pause=="function"}var Je="https://cdn.jsdelivr.net/npm/@hyperframes/core@0.7.76/dist/hyperframe.runtime.iife.js";function D(i){if(i===null)return null;let e=Number.parseInt(i,10);return Number.isFinite(e)&&e>0?e:null}function Ke(i){let e=i?.querySelector("[data-composition-id][data-width][data-height]")??i?.querySelector("[data-width][data-height]");if(!e)return null;let t=D(e.getAttribute("data-width")),r=D(e.getAttribute("data-height"));return t!==null&&r!==null?{width:t,height:r}:null}var j=class{constructor(e,t){this._iframe=e;this._callbacks=t}_iframe;_callbacks;_interval=null;_runtimeInjected=!1;get runtimeInjected(){return this._runtimeInjected}start(){this.stop(),this._runtimeInjected=!1;let e=0;this._interval=setInterval(()=>{e++;try{let t=this._iframe.contentWindow;if(!t)return;let r=!!(t.__hf||t.__player),n=!!(t.__timelines&&Object.keys(t.__timelines).length>0),o=!!this._iframe.contentDocument?.querySelector("[data-composition-src]");if(ye({hasRuntime:r,hasTimelines:n,hasNestedCompositions:o,runtimeInjected:this._runtimeInjected,attempts:e})){this._injectRuntime();return}if(this._runtimeInjected&&!r)return;let s=this._resolvePlaybackDurationAdapter(t);if(s&&s.getDuration()>0){this.stop();let l=Ke(this._iframe.contentDocument);this._callbacks.onReady({duration:s.getDuration(),adapter:s,compositionSize:l});return}}catch{}e>=40&&(this.stop(),this._callbacks.onError("Composition timeline not found after 8s"))},200)}stop(){this._interval!==null&&(clearInterval(this._interval),this._interval=null)}resolveDirectTimelineAdapter(){try{let e=this._iframe.contentWindow;return e?this._resolveDirectTimelineAdapterFromWindow(e):null}catch{return null}}resolveDirectTimelineAdapterFromWindow(e){return this._resolveDirectTimelineAdapterFromWindow(e)}hasRuntimeBridge(e){return Reflect.get(e,"__hf")!==void 0||I(Reflect.get(e,"__player"))}_injectRuntime(){this._runtimeInjected=!0;try{let e=this._iframe.contentDocument;if(!e)return;let t=e.createElement("script");t.src=Je,(e.head||e.documentElement).appendChild(t),this._callbacks.onRuntimeInjected?.()}catch{}}_resolveDirectTimelineAdapterFromWindow(e){if(this.hasRuntimeBridge(e))return null;let t=Reflect.get(e,"__timelines");if(!I(t))return null;let r=Object.keys(t);if(r.length===0)return null;let n=this._iframe.contentDocument?.querySelector("[data-composition-id]")?.getAttribute("data-composition-id"),o=n&&n in t?n:r[r.length-1],s=t[o];return Se(s)?s:null}_resolvePlaybackDurationAdapter(e){let t=Reflect.get(e,"__player");if(Ee(t))return{kind:"runtime",getDuration:()=>t.getDuration()};let r=this._resolveDirectTimelineAdapterFromWindow(e);return r?{kind:"direct-timeline",timeline:r,getDuration:()=>r.duration()}:null}};var Te=`
|
|
2
2
|
:host {
|
|
3
3
|
display: block;
|
|
4
4
|
position: relative;
|