@bendyline/squisq-react 2.11.6 → 2.11.8
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/README.md +19 -18
- package/dist/{chunk-ZY5N3JPM.js → chunk-6PUMLLJH.js} +281 -68
- package/dist/{chunk-ITQ4K5N6.js → chunk-KDTKUE53.js} +1 -1
- package/dist/{chunk-5UGIE2QO.js → chunk-LDXH4FOY.js} +2 -2
- package/dist/{chunk-7QA6EW6Y.js → chunk-SESRQ7XH.js} +1 -1
- package/dist/{chunk-XSOMFBH2.js → chunk-W6MKRWL6.js} +250 -2
- package/dist/{chunk-KGWT454C.js → chunk-Z64OLGMD.js} +136 -265
- package/dist/index.d.ts +36 -4
- package/dist/index.js +10 -6
- package/dist/json-view/index.js +2 -2
- package/dist/layers/index.js +1 -1
- package/dist/markdown/index.js +1 -1
- package/dist/page/index.js +4 -4
- package/dist/player/index.d.ts +1 -1
- package/dist/player/index.js +4 -4
- package/dist/{player-BlD8zREs.d.ts → player-5j10MhBq.d.ts} +24 -2
- package/dist/squisq-player.full.global.js +437 -437
- package/dist/squisq-player.global.js +64 -64
- package/dist/standalone-source.js +1 -1
- package/dist/styles/index.css +30 -0
- package/package.json +2 -2
|
@@ -9,14 +9,14 @@ import {
|
|
|
9
9
|
TreeLayer,
|
|
10
10
|
VideoLayer,
|
|
11
11
|
getTransitionClass
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-W6MKRWL6.js";
|
|
13
13
|
import {
|
|
14
14
|
useAutoSurface
|
|
15
15
|
} from "./chunk-TT6ENR6T.js";
|
|
16
16
|
import {
|
|
17
17
|
InlineVideoPlayer,
|
|
18
18
|
MarkdownRenderer
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-6PUMLLJH.js";
|
|
20
20
|
import {
|
|
21
21
|
useMediaUrl
|
|
22
22
|
} from "./chunk-LR3AIGDD.js";
|
|
@@ -1336,6 +1336,252 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1336
1336
|
|
|
1337
1337
|
// src/layers/VideoLayer.tsx
|
|
1338
1338
|
import { useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
1339
|
+
|
|
1340
|
+
// src/docPlayer/renderReadiness.ts
|
|
1341
|
+
var DEFAULT_VIDEO_FRAME_TIMEOUT_MS = 2e3;
|
|
1342
|
+
var VIDEO_FRAME_READINESS_POLL_MS = 16;
|
|
1343
|
+
var VIDEO_TIME_TOLERANCE_SECONDS = 0.01;
|
|
1344
|
+
var MAX_SEQUENTIAL_CAPTURE_STEP_SECONDS = 0.5;
|
|
1345
|
+
var SEQUENTIAL_CAPTURE_PLAYBACK_RATE = 1;
|
|
1346
|
+
function isCaptureDrivenVideo(video) {
|
|
1347
|
+
return video.dataset.captureSequential === "true" || video.dataset.captureDecoded === "true";
|
|
1348
|
+
}
|
|
1349
|
+
function formatMediaTime(time) {
|
|
1350
|
+
return Number.isFinite(time) ? `${time.toFixed(3)}s` : String(time);
|
|
1351
|
+
}
|
|
1352
|
+
function reachableMediaTime(video, targetTime) {
|
|
1353
|
+
const duration = video.duration;
|
|
1354
|
+
if (!Number.isFinite(duration)) return targetTime;
|
|
1355
|
+
return Math.max(0, Math.min(targetTime, duration));
|
|
1356
|
+
}
|
|
1357
|
+
function isAtReachableMediaTime(video, targetTime) {
|
|
1358
|
+
return Math.abs(video.currentTime - reachableMediaTime(video, targetTime)) <= VIDEO_TIME_TOLERANCE_SECONDS;
|
|
1359
|
+
}
|
|
1360
|
+
function isVisiblyPresented(video) {
|
|
1361
|
+
if (!video.isConnected) return false;
|
|
1362
|
+
const view = video.ownerDocument.defaultView;
|
|
1363
|
+
if (!view) return false;
|
|
1364
|
+
for (let element = video; element; element = element.parentElement) {
|
|
1365
|
+
const style = view.getComputedStyle(element);
|
|
1366
|
+
if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse" || Number.parseFloat(style.opacity || "1") <= 0) {
|
|
1367
|
+
return false;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
return true;
|
|
1371
|
+
}
|
|
1372
|
+
function createCaptureWatchdog(ownerDocument, timeoutMs, expire) {
|
|
1373
|
+
let timeout = null;
|
|
1374
|
+
const clear = () => {
|
|
1375
|
+
if (timeout === null) return;
|
|
1376
|
+
clearTimeout(timeout);
|
|
1377
|
+
timeout = null;
|
|
1378
|
+
};
|
|
1379
|
+
return {
|
|
1380
|
+
arm: () => {
|
|
1381
|
+
clear();
|
|
1382
|
+
if (ownerDocument.visibilityState !== "visible") return;
|
|
1383
|
+
timeout = setTimeout(expire, timeoutMs);
|
|
1384
|
+
},
|
|
1385
|
+
clear
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
function seekVideoByAssignment(video, targetTime, timeoutMs = DEFAULT_VIDEO_FRAME_TIMEOUT_MS) {
|
|
1389
|
+
video.pause();
|
|
1390
|
+
return new Promise((resolve, reject) => {
|
|
1391
|
+
let settled = false;
|
|
1392
|
+
let videoFrameRequest = null;
|
|
1393
|
+
const ownerDocument = video.ownerDocument;
|
|
1394
|
+
const isInactive = () => ownerDocument.visibilityState !== "visible";
|
|
1395
|
+
const cleanup = () => {
|
|
1396
|
+
watchdog.clear();
|
|
1397
|
+
clearInterval(readinessPoll);
|
|
1398
|
+
video.removeEventListener("seeked", handleMediaReady);
|
|
1399
|
+
video.removeEventListener("loadeddata", handleMediaReady);
|
|
1400
|
+
video.removeEventListener("canplay", handleMediaReady);
|
|
1401
|
+
ownerDocument.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1402
|
+
if (videoFrameRequest !== null && typeof video.cancelVideoFrameCallback === "function") {
|
|
1403
|
+
video.cancelVideoFrameCallback(videoFrameRequest);
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
const finish = () => {
|
|
1407
|
+
if (settled) return;
|
|
1408
|
+
settled = true;
|
|
1409
|
+
cleanup();
|
|
1410
|
+
resolve();
|
|
1411
|
+
};
|
|
1412
|
+
const fail = () => {
|
|
1413
|
+
if (settled) return;
|
|
1414
|
+
settled = true;
|
|
1415
|
+
cleanup();
|
|
1416
|
+
reject(
|
|
1417
|
+
new Error(
|
|
1418
|
+
`Video frame did not become ready at ${formatMediaTime(targetTime)} within ${timeoutMs}ms (currentTime=${formatMediaTime(video.currentTime)}, reachableTime=${formatMediaTime(reachableMediaTime(video, targetTime))}, duration=${formatMediaTime(video.duration)}, readyState=${video.readyState}, seeking=${String(video.seeking)}, visible=${String(isVisiblyPresented(video))}).`
|
|
1419
|
+
)
|
|
1420
|
+
);
|
|
1421
|
+
};
|
|
1422
|
+
const requestPresentedFrame = () => {
|
|
1423
|
+
if (settled || video.seeking || video.readyState < HTMLMediaElement.HAVE_CURRENT_DATA || !isAtReachableMediaTime(video, targetTime)) {
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
if (typeof video.requestVideoFrameCallback !== "function" || !isVisiblyPresented(video)) {
|
|
1427
|
+
finish();
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
if (videoFrameRequest !== null) return;
|
|
1431
|
+
videoFrameRequest = video.requestVideoFrameCallback(() => {
|
|
1432
|
+
videoFrameRequest = null;
|
|
1433
|
+
finish();
|
|
1434
|
+
});
|
|
1435
|
+
};
|
|
1436
|
+
function handleMediaReady() {
|
|
1437
|
+
requestPresentedFrame();
|
|
1438
|
+
}
|
|
1439
|
+
const watchdog = createCaptureWatchdog(ownerDocument, timeoutMs, fail);
|
|
1440
|
+
const assignTargetTime = () => {
|
|
1441
|
+
try {
|
|
1442
|
+
video.currentTime = reachableMediaTime(video, targetTime);
|
|
1443
|
+
queueMicrotask(requestPresentedFrame);
|
|
1444
|
+
return true;
|
|
1445
|
+
} catch (error) {
|
|
1446
|
+
settled = true;
|
|
1447
|
+
cleanup();
|
|
1448
|
+
reject(error);
|
|
1449
|
+
return false;
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
function handleVisibilityChange() {
|
|
1453
|
+
if (settled) return;
|
|
1454
|
+
if (isInactive()) {
|
|
1455
|
+
watchdog.clear();
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
requestPresentedFrame();
|
|
1459
|
+
if (settled) return;
|
|
1460
|
+
if (!video.seeking && !isAtReachableMediaTime(video, targetTime) && !assignTargetTime()) {
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
watchdog.arm();
|
|
1464
|
+
}
|
|
1465
|
+
const readinessPoll = setInterval(requestPresentedFrame, VIDEO_FRAME_READINESS_POLL_MS);
|
|
1466
|
+
video.addEventListener("seeked", handleMediaReady);
|
|
1467
|
+
video.addEventListener("loadeddata", handleMediaReady);
|
|
1468
|
+
video.addEventListener("canplay", handleMediaReady);
|
|
1469
|
+
ownerDocument.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1470
|
+
watchdog.arm();
|
|
1471
|
+
assignTargetTime();
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
function advanceSequentialCaptureVideo(video, targetTime, timeoutMs) {
|
|
1475
|
+
return new Promise((resolve, reject) => {
|
|
1476
|
+
let settled = false;
|
|
1477
|
+
const ownerDocument = video.ownerDocument;
|
|
1478
|
+
const isInactive = () => ownerDocument.visibilityState !== "visible";
|
|
1479
|
+
const cleanup = () => {
|
|
1480
|
+
watchdog.clear();
|
|
1481
|
+
clearInterval(readinessPoll);
|
|
1482
|
+
video.removeEventListener("timeupdate", check);
|
|
1483
|
+
video.removeEventListener("loadeddata", check);
|
|
1484
|
+
video.removeEventListener("canplay", check);
|
|
1485
|
+
video.removeEventListener("error", fail);
|
|
1486
|
+
ownerDocument.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1487
|
+
};
|
|
1488
|
+
const finish = () => {
|
|
1489
|
+
if (!settled) {
|
|
1490
|
+
settled = true;
|
|
1491
|
+
cleanup();
|
|
1492
|
+
resolve();
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
const fallbackToSeek = () => {
|
|
1496
|
+
if (settled) return;
|
|
1497
|
+
settled = true;
|
|
1498
|
+
video.pause();
|
|
1499
|
+
cleanup();
|
|
1500
|
+
void seekVideoByAssignment(video, targetTime, timeoutMs).then(resolve, reject);
|
|
1501
|
+
};
|
|
1502
|
+
const fail = () => {
|
|
1503
|
+
if (settled) return;
|
|
1504
|
+
settled = true;
|
|
1505
|
+
video.pause();
|
|
1506
|
+
cleanup();
|
|
1507
|
+
reject(
|
|
1508
|
+
new Error(
|
|
1509
|
+
`Video frame did not advance to ${formatMediaTime(targetTime)} within ${timeoutMs}ms (currentTime=${formatMediaTime(video.currentTime)}, readyState=${video.readyState}).`
|
|
1510
|
+
)
|
|
1511
|
+
);
|
|
1512
|
+
};
|
|
1513
|
+
function check() {
|
|
1514
|
+
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA && video.currentTime >= reachableMediaTime(video, targetTime)) {
|
|
1515
|
+
video.pause();
|
|
1516
|
+
finish();
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
const handlePlaybackFailure = () => {
|
|
1520
|
+
if (settled || isInactive()) return;
|
|
1521
|
+
fallbackToSeek();
|
|
1522
|
+
};
|
|
1523
|
+
const startPlayback = () => {
|
|
1524
|
+
if (settled || isInactive()) return;
|
|
1525
|
+
try {
|
|
1526
|
+
video.playbackRate = SEQUENTIAL_CAPTURE_PLAYBACK_RATE;
|
|
1527
|
+
if (video.paused) {
|
|
1528
|
+
void video.play().catch(handlePlaybackFailure);
|
|
1529
|
+
}
|
|
1530
|
+
} catch {
|
|
1531
|
+
handlePlaybackFailure();
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
const watchdog = createCaptureWatchdog(ownerDocument, timeoutMs, fail);
|
|
1535
|
+
const pauseWhileInactive = () => {
|
|
1536
|
+
video.pause();
|
|
1537
|
+
watchdog.clear();
|
|
1538
|
+
};
|
|
1539
|
+
const resumeWhenActive = () => {
|
|
1540
|
+
if (settled || isInactive()) return;
|
|
1541
|
+
check();
|
|
1542
|
+
if (settled) return;
|
|
1543
|
+
startPlayback();
|
|
1544
|
+
watchdog.arm();
|
|
1545
|
+
};
|
|
1546
|
+
function handleVisibilityChange() {
|
|
1547
|
+
if (ownerDocument.visibilityState !== "visible") {
|
|
1548
|
+
pauseWhileInactive();
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
resumeWhenActive();
|
|
1552
|
+
}
|
|
1553
|
+
const readinessPoll = setInterval(check, VIDEO_FRAME_READINESS_POLL_MS);
|
|
1554
|
+
video.addEventListener("timeupdate", check);
|
|
1555
|
+
video.addEventListener("loadeddata", check);
|
|
1556
|
+
video.addEventListener("canplay", check);
|
|
1557
|
+
video.addEventListener("error", fail, { once: true });
|
|
1558
|
+
ownerDocument.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1559
|
+
resumeWhenActive();
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
function seekVideoToFrame(video, targetTime, timeoutMs = DEFAULT_VIDEO_FRAME_TIMEOUT_MS) {
|
|
1563
|
+
if (video.readyState >= HTMLMediaElement.HAVE_METADATA && video.videoWidth <= 0 && video.videoHeight <= 0) {
|
|
1564
|
+
video.pause();
|
|
1565
|
+
return Promise.resolve();
|
|
1566
|
+
}
|
|
1567
|
+
const reachableTargetTime = reachableMediaTime(video, targetTime);
|
|
1568
|
+
const step = reachableTargetTime - video.currentTime;
|
|
1569
|
+
if (video.dataset.captureSequential === "true") {
|
|
1570
|
+
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA && step <= VIDEO_TIME_TOLERANCE_SECONDS) {
|
|
1571
|
+
if (step < -VIDEO_TIME_TOLERANCE_SECONDS) video.pause();
|
|
1572
|
+
return Promise.resolve();
|
|
1573
|
+
}
|
|
1574
|
+
if (step > 0 && step <= MAX_SEQUENTIAL_CAPTURE_STEP_SECONDS && video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
|
|
1575
|
+
return advanceSequentialCaptureVideo(video, targetTime, timeoutMs);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
video.pause();
|
|
1579
|
+
const alreadyReady = !video.seeking && video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA && isAtReachableMediaTime(video, targetTime);
|
|
1580
|
+
if (alreadyReady) return Promise.resolve();
|
|
1581
|
+
return seekVideoByAssignment(video, targetTime, timeoutMs);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// src/layers/VideoLayer.tsx
|
|
1339
1585
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1340
1586
|
var VIDEO_SYNC_DRIFT_SECONDS = 0.2;
|
|
1341
1587
|
function VideoLayer({
|
|
@@ -1389,7 +1635,7 @@ function VideoLayer({
|
|
|
1389
1635
|
const video = videoRef.current;
|
|
1390
1636
|
if (!video || !hasStartedRef.current) return;
|
|
1391
1637
|
const targetTime = gated ? content.clipStart : Math.min(content.clipEnd, content.clipStart + Math.max(0, blockTime - startAt));
|
|
1392
|
-
if (video
|
|
1638
|
+
if (!isCaptureDrivenVideo(video) && Math.abs(video.currentTime - targetTime) > VIDEO_SYNC_DRIFT_SECONDS) {
|
|
1393
1639
|
video.currentTime = targetTime;
|
|
1394
1640
|
}
|
|
1395
1641
|
if (gated) {
|
|
@@ -1406,7 +1652,7 @@ function VideoLayer({
|
|
|
1406
1652
|
playPromise.catch(() => {
|
|
1407
1653
|
});
|
|
1408
1654
|
}
|
|
1409
|
-
} else if (video
|
|
1655
|
+
} else if (!isCaptureDrivenVideo(video)) {
|
|
1410
1656
|
video.pause();
|
|
1411
1657
|
}
|
|
1412
1658
|
}, [isPlaying, gated, blockTime, startAt, src, content.clipStart, content.clipEnd]);
|
|
@@ -1741,6 +1987,8 @@ function MermaidLayer({ layer, viewport, blockTime, theme }) {
|
|
|
1741
1987
|
}
|
|
1742
1988
|
|
|
1743
1989
|
export {
|
|
1990
|
+
isCaptureDrivenVideo,
|
|
1991
|
+
seekVideoToFrame,
|
|
1744
1992
|
getAnimationStyle,
|
|
1745
1993
|
getTransitionClass,
|
|
1746
1994
|
ImageLayer,
|