@bendyline/squisq-react 2.4.6 → 2.5.0
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/{chunk-ICM7AWQT.js → chunk-THDXCSPC.js} +1 -1
- package/dist/{chunk-TI5X7SBC.js → chunk-UJEHDVQO.js} +65 -14
- package/dist/{chunk-7CDO336T.js → chunk-ZKNX3EJI.js} +207 -33
- package/dist/index.d.ts +8 -1
- package/dist/index.js +3 -3
- package/dist/layers/index.js +1 -1
- package/dist/page/index.js +2 -2
- package/dist/player/index.d.ts +7 -1
- package/dist/player/index.js +3 -3
- package/dist/squisq-player.full.global.js +437 -436
- package/dist/squisq-player.global.js +59 -58
- package/dist/standalone-source.js +1 -1
- package/package.json +2 -2
|
@@ -239,7 +239,7 @@ function remapToKenBurns(anim) {
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
// src/layers/TextLayer.tsx
|
|
242
|
-
import { useId, useMemo } from "react";
|
|
242
|
+
import { useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
243
243
|
import { DEFAULT_DOC_FONT } from "@bendyline/squisq/schemas";
|
|
244
244
|
import {
|
|
245
245
|
parseHtmlToNodes,
|
|
@@ -454,6 +454,55 @@ function PlainTextLayer({ layer, viewport, blockTime }) {
|
|
|
454
454
|
}
|
|
455
455
|
const lineHeight = style.lineHeight || 1.4;
|
|
456
456
|
const lineHeightPx = style.fontSize * lineHeight;
|
|
457
|
+
const shrinkToFit = style.shrinkToFit === true;
|
|
458
|
+
const centerBlock = shrinkToFit && dominantBaseline === "middle";
|
|
459
|
+
const firstLineDy = centerBlock ? -((lines.length - 1) / 2) * lineHeightPx : 0;
|
|
460
|
+
const textRef = useRef(null);
|
|
461
|
+
const [fit, setFit] = useState(null);
|
|
462
|
+
const linesKey = lines.join("\n");
|
|
463
|
+
useLayoutEffect(() => {
|
|
464
|
+
if (!shrinkToFit) {
|
|
465
|
+
setFit(null);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const element = textRef.current;
|
|
469
|
+
if (!element || typeof element.getBBox !== "function") return;
|
|
470
|
+
const measure = () => {
|
|
471
|
+
try {
|
|
472
|
+
const bbox = element.getBBox();
|
|
473
|
+
if (bbox.width <= 0 || bbox.height <= 0) return;
|
|
474
|
+
const maxW = boxWidth ?? viewport.width;
|
|
475
|
+
const maxH = boxHeight ?? viewport.height;
|
|
476
|
+
const scale = Math.min(1, maxW / bbox.width, maxH / bbox.height);
|
|
477
|
+
setFit(
|
|
478
|
+
scale < 0.995 ? { scale, cx: bbox.x + bbox.width / 2, cy: bbox.y + bbox.height / 2 } : null
|
|
479
|
+
);
|
|
480
|
+
} catch {
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
measure();
|
|
484
|
+
const fonts = element.ownerDocument?.fonts;
|
|
485
|
+
if (fonts && fonts.status !== "loaded") {
|
|
486
|
+
let cancelled = false;
|
|
487
|
+
void fonts.ready.then(() => {
|
|
488
|
+
if (!cancelled) measure();
|
|
489
|
+
});
|
|
490
|
+
return () => {
|
|
491
|
+
cancelled = true;
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
}, [
|
|
495
|
+
shrinkToFit,
|
|
496
|
+
linesKey,
|
|
497
|
+
boxWidth,
|
|
498
|
+
boxHeight,
|
|
499
|
+
viewport.width,
|
|
500
|
+
viewport.height,
|
|
501
|
+
style.fontSize,
|
|
502
|
+
style.fontFamily,
|
|
503
|
+
style.fontWeight
|
|
504
|
+
]);
|
|
505
|
+
const fitTransform = fit ? `translate(${fit.cx} ${fit.cy}) scale(${fit.scale}) translate(${-fit.cx} ${-fit.cy})` : void 0;
|
|
457
506
|
const textStyles = {
|
|
458
507
|
fontSize: `${style.fontSize}px`,
|
|
459
508
|
fontFamily: style.fontFamily || DEFAULT_DOC_FONT,
|
|
@@ -486,13 +535,15 @@ function PlainTextLayer({ layer, viewport, blockTime }) {
|
|
|
486
535
|
/* @__PURE__ */ jsx3(
|
|
487
536
|
"text",
|
|
488
537
|
{
|
|
538
|
+
ref: textRef,
|
|
489
539
|
x,
|
|
490
540
|
y,
|
|
491
541
|
textAnchor,
|
|
492
542
|
dominantBaseline,
|
|
493
543
|
style: textStyles,
|
|
494
544
|
filter: filterId ? `url(#${filterId})` : void 0,
|
|
495
|
-
|
|
545
|
+
transform: fitTransform,
|
|
546
|
+
children: lines.map((line, i) => /* @__PURE__ */ jsxs2("tspan", { x, dy: i === 0 ? firstLineDy : lineHeightPx, children: [
|
|
496
547
|
line || "\xA0",
|
|
497
548
|
" "
|
|
498
549
|
] }, i))
|
|
@@ -893,7 +944,7 @@ function MarkerDef({
|
|
|
893
944
|
}
|
|
894
945
|
|
|
895
946
|
// src/layers/MapLayer.tsx
|
|
896
|
-
import { useId as useId4, useState, useEffect } from "react";
|
|
947
|
+
import { useId as useId4, useState as useState2, useEffect } from "react";
|
|
897
948
|
import { ResourcePolicyError as ResourcePolicyError2 } from "@bendyline/squisq/markdown";
|
|
898
949
|
|
|
899
950
|
// src/utils/mapTileUtils.ts
|
|
@@ -1092,10 +1143,10 @@ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
|
1092
1143
|
function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
1093
1144
|
const { content, position, animation } = layer;
|
|
1094
1145
|
const clipId = `map-clip-${useId4().replace(/:/g, "")}-${layer.id}`;
|
|
1095
|
-
const [mapImageUrl, setMapImageUrl] =
|
|
1096
|
-
const [isLoading, setIsLoading] =
|
|
1097
|
-
const [error, setError] =
|
|
1098
|
-
const [blockedByPolicy, setBlockedByPolicy] =
|
|
1146
|
+
const [mapImageUrl, setMapImageUrl] = useState2(null);
|
|
1147
|
+
const [isLoading, setIsLoading] = useState2(true);
|
|
1148
|
+
const [error, setError] = useState2(null);
|
|
1149
|
+
const [blockedByPolicy, setBlockedByPolicy] = useState2(false);
|
|
1099
1150
|
const x = resolveValue(position.x, viewport.width);
|
|
1100
1151
|
const y = resolveValue(position.y, viewport.height);
|
|
1101
1152
|
const width = position.width ? resolveValue(position.width, viewport.width) : viewport.width;
|
|
@@ -1242,7 +1293,7 @@ function MapLayer({ layer, basePath, viewport, blockTime }) {
|
|
|
1242
1293
|
}
|
|
1243
1294
|
|
|
1244
1295
|
// src/layers/VideoLayer.tsx
|
|
1245
|
-
import { useRef, useEffect as useEffect2 } from "react";
|
|
1296
|
+
import { useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
1246
1297
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1247
1298
|
var VIDEO_SYNC_DRIFT_SECONDS = 0.2;
|
|
1248
1299
|
function VideoLayer({
|
|
@@ -1254,8 +1305,8 @@ function VideoLayer({
|
|
|
1254
1305
|
muted = false
|
|
1255
1306
|
}) {
|
|
1256
1307
|
const { content, position } = layer;
|
|
1257
|
-
const videoRef =
|
|
1258
|
-
const hasStartedRef =
|
|
1308
|
+
const videoRef = useRef2(null);
|
|
1309
|
+
const hasStartedRef = useRef2(false);
|
|
1259
1310
|
const startAt = content.startAt ?? 0;
|
|
1260
1311
|
const gated = blockTime < startAt;
|
|
1261
1312
|
const x = resolveValue(position.x, viewport.width);
|
|
@@ -1296,7 +1347,7 @@ function VideoLayer({
|
|
|
1296
1347
|
const video = videoRef.current;
|
|
1297
1348
|
if (!video || !hasStartedRef.current) return;
|
|
1298
1349
|
const targetTime = gated ? content.clipStart : Math.min(content.clipEnd, content.clipStart + Math.max(0, blockTime - startAt));
|
|
1299
|
-
if (Math.abs(video.currentTime - targetTime) > VIDEO_SYNC_DRIFT_SECONDS) {
|
|
1350
|
+
if (video.dataset.captureSequential !== "true" && Math.abs(video.currentTime - targetTime) > VIDEO_SYNC_DRIFT_SECONDS) {
|
|
1300
1351
|
video.currentTime = targetTime;
|
|
1301
1352
|
}
|
|
1302
1353
|
if (gated) {
|
|
@@ -1313,7 +1364,7 @@ function VideoLayer({
|
|
|
1313
1364
|
playPromise.catch(() => {
|
|
1314
1365
|
});
|
|
1315
1366
|
}
|
|
1316
|
-
} else {
|
|
1367
|
+
} else if (video.dataset.captureSequential !== "true") {
|
|
1317
1368
|
video.pause();
|
|
1318
1369
|
}
|
|
1319
1370
|
}, [isPlaying, gated, blockTime, startAt, src, content.clipStart, content.clipEnd]);
|
|
@@ -1434,7 +1485,7 @@ function TableLayer({ layer, viewport, blockTime }) {
|
|
|
1434
1485
|
}
|
|
1435
1486
|
|
|
1436
1487
|
// src/layers/TreeLayer.tsx
|
|
1437
|
-
import { useState as
|
|
1488
|
+
import { useState as useState3 } from "react";
|
|
1438
1489
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1439
1490
|
function faClass(token, fallback) {
|
|
1440
1491
|
const name = token && token.trim() ? token.trim() : fallback;
|
|
@@ -1508,7 +1559,7 @@ function TreeRow({
|
|
|
1508
1559
|
style
|
|
1509
1560
|
}) {
|
|
1510
1561
|
const hasChildren = item.children.length > 0;
|
|
1511
|
-
const [collapsed, setCollapsed] =
|
|
1562
|
+
const [collapsed, setCollapsed] = useState3(false);
|
|
1512
1563
|
const isDir = item.isDir || hasChildren;
|
|
1513
1564
|
const iconCls = isDir ? faClass(style.folderIcon, collapsed ? "folder" : "folder-open") : faClass(style.fileIcon, "file");
|
|
1514
1565
|
return /* @__PURE__ */ jsxs7("li", { style: { position: "relative" }, children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BlockRenderer,
|
|
3
3
|
LinearDocView
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-THDXCSPC.js";
|
|
5
5
|
import {
|
|
6
6
|
useAudioSync,
|
|
7
7
|
useDocPlayback,
|
|
@@ -165,7 +165,7 @@ function MediaClipElement({
|
|
|
165
165
|
return;
|
|
166
166
|
}
|
|
167
167
|
const target = Math.max(0, clip.sourceIn + (currentTime - clip.absoluteStart));
|
|
168
|
-
if (renderMode || !isPlaying || Math.abs(el.currentTime - target) > DRIFT) {
|
|
168
|
+
if (el.dataset.captureSequential !== "true" && (renderMode || !isPlaying || Math.abs(el.currentTime - target) > DRIFT)) {
|
|
169
169
|
try {
|
|
170
170
|
el.currentTime = target;
|
|
171
171
|
} catch {
|
|
@@ -175,7 +175,7 @@ function MediaClipElement({
|
|
|
175
175
|
const p = el.play();
|
|
176
176
|
if (p) p.catch(() => {
|
|
177
177
|
});
|
|
178
|
-
} else {
|
|
178
|
+
} else if (el.dataset.captureSequential !== "true") {
|
|
179
179
|
el.pause();
|
|
180
180
|
}
|
|
181
181
|
}, [active, currentTime, isPlaying, renderMode, clip.sourceIn, clip.absoluteStart, src]);
|
|
@@ -1243,7 +1243,10 @@ function DocControlsSlideshow({
|
|
|
1243
1243
|
}
|
|
1244
1244
|
|
|
1245
1245
|
// src/docPlayer/playerAppearance.ts
|
|
1246
|
-
import {
|
|
1246
|
+
import {
|
|
1247
|
+
resolveCoverSlideSettings,
|
|
1248
|
+
resolveThemeForDoc
|
|
1249
|
+
} from "@bendyline/squisq/doc";
|
|
1247
1250
|
function readFrontmatterSetting(frontmatter, canonical, legacy) {
|
|
1248
1251
|
if (!frontmatter) return void 0;
|
|
1249
1252
|
return Object.prototype.hasOwnProperty.call(frontmatter, canonical) ? frontmatter[canonical] : frontmatter[legacy];
|
|
@@ -1291,20 +1294,14 @@ function resolvePipPosition(value) {
|
|
|
1291
1294
|
};
|
|
1292
1295
|
return aliases[normalized];
|
|
1293
1296
|
}
|
|
1294
|
-
function resolveBoolean(value) {
|
|
1295
|
-
if (typeof value === "boolean") return value;
|
|
1296
|
-
if (typeof value !== "string") return void 0;
|
|
1297
|
-
const normalized = value.trim().toLowerCase();
|
|
1298
|
-
if (normalized === "true" || normalized === "yes" || normalized === "on" || normalized === "show" || normalized === "visible") {
|
|
1299
|
-
return true;
|
|
1300
|
-
}
|
|
1301
|
-
if (normalized === "false" || normalized === "no" || normalized === "off" || normalized === "hide" || normalized === "hidden") {
|
|
1302
|
-
return false;
|
|
1303
|
-
}
|
|
1304
|
-
return void 0;
|
|
1305
|
-
}
|
|
1306
1297
|
function resolveDocPlayerAppearance(doc, overrides = {}) {
|
|
1307
1298
|
const frontmatter = doc.frontmatter;
|
|
1299
|
+
const cover = resolveCoverSlideSettings(frontmatter, {
|
|
1300
|
+
...overrides.showCoverSlide !== void 0 ? { enabled: overrides.showCoverSlide } : {},
|
|
1301
|
+
...overrides.coverSlideTemplate !== void 0 ? { template: overrides.coverSlideTemplate } : {},
|
|
1302
|
+
...overrides.coverSlideDuration !== void 0 ? { duration: overrides.coverSlideDuration } : {},
|
|
1303
|
+
...overrides.coverSlidePlayback !== void 0 ? { playback: overrides.coverSlidePlayback } : {}
|
|
1304
|
+
});
|
|
1308
1305
|
return {
|
|
1309
1306
|
theme: overrides.theme ?? resolveThemeForDoc(doc),
|
|
1310
1307
|
videoPresentation: overrides.videoPresentation ?? resolveVideoPresentation(
|
|
@@ -1315,7 +1312,10 @@ function resolveDocPlayerAppearance(doc, overrides = {}) {
|
|
|
1315
1312
|
pipPosition: overrides.pipPosition ?? resolvePipPosition(
|
|
1316
1313
|
readFrontmatterSetting(frontmatter, "squisq-pip-position", "pip-position")
|
|
1317
1314
|
) ?? "bottom-right",
|
|
1318
|
-
showCoverSlide:
|
|
1315
|
+
showCoverSlide: cover.enabled,
|
|
1316
|
+
coverSlideTemplate: cover.template,
|
|
1317
|
+
coverSlideDuration: cover.duration,
|
|
1318
|
+
coverSlidePlayback: cover.playback
|
|
1319
1319
|
};
|
|
1320
1320
|
}
|
|
1321
1321
|
|
|
@@ -1542,6 +1542,8 @@ function buildSegmentTitleMap(doc) {
|
|
|
1542
1542
|
var DEFAULT_VIDEO_FRAME_TIMEOUT_MS = 2e3;
|
|
1543
1543
|
var VIDEO_FRAME_READINESS_POLL_MS = 16;
|
|
1544
1544
|
var VIDEO_TIME_TOLERANCE_SECONDS = 0.01;
|
|
1545
|
+
var MAX_SEQUENTIAL_CAPTURE_STEP_SECONDS = 0.5;
|
|
1546
|
+
var SEQUENTIAL_CAPTURE_PLAYBACK_RATE = 1;
|
|
1545
1547
|
function formatMediaTime(time) {
|
|
1546
1548
|
return Number.isFinite(time) ? `${time.toFixed(3)}s` : String(time);
|
|
1547
1549
|
}
|
|
@@ -1565,19 +1567,36 @@ function isVisiblyPresented(video) {
|
|
|
1565
1567
|
}
|
|
1566
1568
|
return true;
|
|
1567
1569
|
}
|
|
1568
|
-
function
|
|
1570
|
+
function createCaptureWatchdog(ownerDocument, timeoutMs, expire) {
|
|
1571
|
+
let timeout = null;
|
|
1572
|
+
const clear = () => {
|
|
1573
|
+
if (timeout === null) return;
|
|
1574
|
+
clearTimeout(timeout);
|
|
1575
|
+
timeout = null;
|
|
1576
|
+
};
|
|
1577
|
+
return {
|
|
1578
|
+
arm: () => {
|
|
1579
|
+
clear();
|
|
1580
|
+
if (ownerDocument.visibilityState !== "visible") return;
|
|
1581
|
+
timeout = setTimeout(expire, timeoutMs);
|
|
1582
|
+
},
|
|
1583
|
+
clear
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
function seekVideoByAssignment(video, targetTime, timeoutMs = DEFAULT_VIDEO_FRAME_TIMEOUT_MS) {
|
|
1569
1587
|
video.pause();
|
|
1570
|
-
const alreadyReady = !video.seeking && video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA && isAtReachableMediaTime(video, targetTime);
|
|
1571
|
-
if (alreadyReady) return Promise.resolve();
|
|
1572
1588
|
return new Promise((resolve, reject) => {
|
|
1573
1589
|
let settled = false;
|
|
1574
1590
|
let videoFrameRequest = null;
|
|
1591
|
+
const ownerDocument = video.ownerDocument;
|
|
1592
|
+
const isInactive = () => ownerDocument.visibilityState !== "visible";
|
|
1575
1593
|
const cleanup = () => {
|
|
1576
|
-
|
|
1594
|
+
watchdog.clear();
|
|
1577
1595
|
clearInterval(readinessPoll);
|
|
1578
1596
|
video.removeEventListener("seeked", handleMediaReady);
|
|
1579
1597
|
video.removeEventListener("loadeddata", handleMediaReady);
|
|
1580
1598
|
video.removeEventListener("canplay", handleMediaReady);
|
|
1599
|
+
ownerDocument.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1581
1600
|
if (videoFrameRequest !== null && typeof video.cancelVideoFrameCallback === "function") {
|
|
1582
1601
|
video.cancelVideoFrameCallback(videoFrameRequest);
|
|
1583
1602
|
}
|
|
@@ -1615,21 +1634,150 @@ function seekVideoToFrame(video, targetTime, timeoutMs = DEFAULT_VIDEO_FRAME_TIM
|
|
|
1615
1634
|
function handleMediaReady() {
|
|
1616
1635
|
requestPresentedFrame();
|
|
1617
1636
|
}
|
|
1618
|
-
const
|
|
1637
|
+
const watchdog = createCaptureWatchdog(ownerDocument, timeoutMs, fail);
|
|
1638
|
+
const assignTargetTime = () => {
|
|
1639
|
+
try {
|
|
1640
|
+
video.currentTime = reachableMediaTime(video, targetTime);
|
|
1641
|
+
queueMicrotask(requestPresentedFrame);
|
|
1642
|
+
return true;
|
|
1643
|
+
} catch (error) {
|
|
1644
|
+
settled = true;
|
|
1645
|
+
cleanup();
|
|
1646
|
+
reject(error);
|
|
1647
|
+
return false;
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
function handleVisibilityChange() {
|
|
1651
|
+
if (settled) return;
|
|
1652
|
+
if (isInactive()) {
|
|
1653
|
+
watchdog.clear();
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
requestPresentedFrame();
|
|
1657
|
+
if (settled) return;
|
|
1658
|
+
if (!video.seeking && !isAtReachableMediaTime(video, targetTime) && !assignTargetTime()) {
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
watchdog.arm();
|
|
1662
|
+
}
|
|
1619
1663
|
const readinessPoll = setInterval(requestPresentedFrame, VIDEO_FRAME_READINESS_POLL_MS);
|
|
1620
1664
|
video.addEventListener("seeked", handleMediaReady);
|
|
1621
1665
|
video.addEventListener("loadeddata", handleMediaReady);
|
|
1622
1666
|
video.addEventListener("canplay", handleMediaReady);
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1667
|
+
ownerDocument.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1668
|
+
watchdog.arm();
|
|
1669
|
+
assignTargetTime();
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
function advanceSequentialCaptureVideo(video, targetTime, timeoutMs) {
|
|
1673
|
+
return new Promise((resolve, reject) => {
|
|
1674
|
+
let settled = false;
|
|
1675
|
+
const ownerDocument = video.ownerDocument;
|
|
1676
|
+
const isInactive = () => ownerDocument.visibilityState !== "visible";
|
|
1677
|
+
const cleanup = () => {
|
|
1678
|
+
watchdog.clear();
|
|
1679
|
+
clearInterval(readinessPoll);
|
|
1680
|
+
video.removeEventListener("timeupdate", check);
|
|
1681
|
+
video.removeEventListener("loadeddata", check);
|
|
1682
|
+
video.removeEventListener("canplay", check);
|
|
1683
|
+
video.removeEventListener("error", fail);
|
|
1684
|
+
ownerDocument.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1685
|
+
};
|
|
1686
|
+
const finish = () => {
|
|
1687
|
+
if (!settled) {
|
|
1688
|
+
settled = true;
|
|
1689
|
+
cleanup();
|
|
1690
|
+
resolve();
|
|
1691
|
+
}
|
|
1692
|
+
};
|
|
1693
|
+
const fallbackToSeek = () => {
|
|
1694
|
+
if (settled) return;
|
|
1695
|
+
settled = true;
|
|
1696
|
+
video.pause();
|
|
1697
|
+
cleanup();
|
|
1698
|
+
void seekVideoByAssignment(video, targetTime, timeoutMs).then(resolve, reject);
|
|
1699
|
+
};
|
|
1700
|
+
const fail = () => {
|
|
1701
|
+
if (settled) return;
|
|
1627
1702
|
settled = true;
|
|
1703
|
+
video.pause();
|
|
1628
1704
|
cleanup();
|
|
1629
|
-
reject(
|
|
1705
|
+
reject(
|
|
1706
|
+
new Error(
|
|
1707
|
+
`Video frame did not advance to ${formatMediaTime(targetTime)} within ${timeoutMs}ms (currentTime=${formatMediaTime(video.currentTime)}, readyState=${video.readyState}).`
|
|
1708
|
+
)
|
|
1709
|
+
);
|
|
1710
|
+
};
|
|
1711
|
+
function check() {
|
|
1712
|
+
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA && video.currentTime >= reachableMediaTime(video, targetTime)) {
|
|
1713
|
+
video.pause();
|
|
1714
|
+
finish();
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
const handlePlaybackFailure = () => {
|
|
1718
|
+
if (settled || isInactive()) return;
|
|
1719
|
+
fallbackToSeek();
|
|
1720
|
+
};
|
|
1721
|
+
const startPlayback = () => {
|
|
1722
|
+
if (settled || isInactive()) return;
|
|
1723
|
+
try {
|
|
1724
|
+
video.playbackRate = SEQUENTIAL_CAPTURE_PLAYBACK_RATE;
|
|
1725
|
+
if (video.paused) {
|
|
1726
|
+
void video.play().catch(handlePlaybackFailure);
|
|
1727
|
+
}
|
|
1728
|
+
} catch {
|
|
1729
|
+
handlePlaybackFailure();
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
const watchdog = createCaptureWatchdog(ownerDocument, timeoutMs, fail);
|
|
1733
|
+
const pauseWhileInactive = () => {
|
|
1734
|
+
video.pause();
|
|
1735
|
+
watchdog.clear();
|
|
1736
|
+
};
|
|
1737
|
+
const resumeWhenActive = () => {
|
|
1738
|
+
if (settled || isInactive()) return;
|
|
1739
|
+
check();
|
|
1740
|
+
if (settled) return;
|
|
1741
|
+
startPlayback();
|
|
1742
|
+
watchdog.arm();
|
|
1743
|
+
};
|
|
1744
|
+
function handleVisibilityChange() {
|
|
1745
|
+
if (ownerDocument.visibilityState !== "visible") {
|
|
1746
|
+
pauseWhileInactive();
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
resumeWhenActive();
|
|
1630
1750
|
}
|
|
1751
|
+
const readinessPoll = setInterval(check, VIDEO_FRAME_READINESS_POLL_MS);
|
|
1752
|
+
video.addEventListener("timeupdate", check);
|
|
1753
|
+
video.addEventListener("loadeddata", check);
|
|
1754
|
+
video.addEventListener("canplay", check);
|
|
1755
|
+
video.addEventListener("error", fail, { once: true });
|
|
1756
|
+
ownerDocument.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1757
|
+
resumeWhenActive();
|
|
1631
1758
|
});
|
|
1632
1759
|
}
|
|
1760
|
+
function seekVideoToFrame(video, targetTime, timeoutMs = DEFAULT_VIDEO_FRAME_TIMEOUT_MS) {
|
|
1761
|
+
if (video.readyState >= HTMLMediaElement.HAVE_METADATA && video.videoWidth <= 0 && video.videoHeight <= 0) {
|
|
1762
|
+
video.pause();
|
|
1763
|
+
return Promise.resolve();
|
|
1764
|
+
}
|
|
1765
|
+
const reachableTargetTime = reachableMediaTime(video, targetTime);
|
|
1766
|
+
const step = reachableTargetTime - video.currentTime;
|
|
1767
|
+
if (video.dataset.captureSequential === "true") {
|
|
1768
|
+
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA && step <= VIDEO_TIME_TOLERANCE_SECONDS) {
|
|
1769
|
+
if (step < -VIDEO_TIME_TOLERANCE_SECONDS) video.pause();
|
|
1770
|
+
return Promise.resolve();
|
|
1771
|
+
}
|
|
1772
|
+
if (step > 0 && step <= MAX_SEQUENTIAL_CAPTURE_STEP_SECONDS && video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
|
|
1773
|
+
return advanceSequentialCaptureVideo(video, targetTime, timeoutMs);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
video.pause();
|
|
1777
|
+
const alreadyReady = !video.seeking && video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA && isAtReachableMediaTime(video, targetTime);
|
|
1778
|
+
if (alreadyReady) return Promise.resolve();
|
|
1779
|
+
return seekVideoByAssignment(video, targetTime, timeoutMs);
|
|
1780
|
+
}
|
|
1633
1781
|
|
|
1634
1782
|
// src/DocPlayer.tsx
|
|
1635
1783
|
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
@@ -1699,6 +1847,9 @@ function DocPlayerContent({
|
|
|
1699
1847
|
forceViewport,
|
|
1700
1848
|
displayMode = "video",
|
|
1701
1849
|
showCoverSlide,
|
|
1850
|
+
coverSlideTemplate,
|
|
1851
|
+
coverSlideDuration,
|
|
1852
|
+
coverSlidePlayback,
|
|
1702
1853
|
coverVisible,
|
|
1703
1854
|
theme,
|
|
1704
1855
|
surface,
|
|
@@ -1854,9 +2005,23 @@ function DocPlayerContent({
|
|
|
1854
2005
|
pipSize,
|
|
1855
2006
|
pipShape,
|
|
1856
2007
|
pipPosition,
|
|
1857
|
-
showCoverSlide
|
|
2008
|
+
showCoverSlide,
|
|
2009
|
+
coverSlideTemplate,
|
|
2010
|
+
coverSlideDuration,
|
|
2011
|
+
coverSlidePlayback
|
|
1858
2012
|
}),
|
|
1859
|
-
[
|
|
2013
|
+
[
|
|
2014
|
+
doc,
|
|
2015
|
+
theme,
|
|
2016
|
+
videoPresentation,
|
|
2017
|
+
pipSize,
|
|
2018
|
+
pipShape,
|
|
2019
|
+
pipPosition,
|
|
2020
|
+
showCoverSlide,
|
|
2021
|
+
coverSlideTemplate,
|
|
2022
|
+
coverSlideDuration,
|
|
2023
|
+
coverSlidePlayback
|
|
2024
|
+
]
|
|
1860
2025
|
);
|
|
1861
2026
|
const effectiveTheme = useMemo3(() => {
|
|
1862
2027
|
const base = appearance.theme;
|
|
@@ -1899,7 +2064,7 @@ function DocPlayerContent({
|
|
|
1899
2064
|
if (!appearance.showCoverSlide) return null;
|
|
1900
2065
|
if (!startBlockConfig) return null;
|
|
1901
2066
|
const context = createTemplateContext(effectiveTheme, 0, 1, activeViewport);
|
|
1902
|
-
const layers = expandCoverBlock(startBlockConfig, context);
|
|
2067
|
+
const layers = expandCoverBlock(startBlockConfig, context, appearance.coverSlideTemplate);
|
|
1903
2068
|
return {
|
|
1904
2069
|
id: "cover-block",
|
|
1905
2070
|
startTime: -1,
|
|
@@ -1909,7 +2074,13 @@ function DocPlayerContent({
|
|
|
1909
2074
|
audioSegment: -1,
|
|
1910
2075
|
layers
|
|
1911
2076
|
};
|
|
1912
|
-
}, [
|
|
2077
|
+
}, [
|
|
2078
|
+
doc.startBlock,
|
|
2079
|
+
activeViewport,
|
|
2080
|
+
effectiveTheme,
|
|
2081
|
+
appearance.showCoverSlide,
|
|
2082
|
+
appearance.coverSlideTemplate
|
|
2083
|
+
]);
|
|
1913
2084
|
const hasManagedCover = !!coverBlock;
|
|
1914
2085
|
const [slideshowCoverVisible, setSlideshowCoverVisible] = useState5(false);
|
|
1915
2086
|
const [isSlideshowPickerOpen, setIsSlideshowPickerOpen] = useState5(false);
|
|
@@ -1947,9 +2118,12 @@ function DocPlayerContent({
|
|
|
1947
2118
|
coverWasShowing.current = false;
|
|
1948
2119
|
hasPlayedOnce.current = true;
|
|
1949
2120
|
setCoverGraceActive(true);
|
|
1950
|
-
coverGraceTimer.current = setTimeout(
|
|
2121
|
+
coverGraceTimer.current = setTimeout(
|
|
2122
|
+
() => setCoverGraceActive(false),
|
|
2123
|
+
appearance.coverSlideDuration * 1e3
|
|
2124
|
+
);
|
|
1951
2125
|
}
|
|
1952
|
-
}, [isPlaying, coverBlock, renderMode, isSlideshowMode]);
|
|
2126
|
+
}, [isPlaying, coverBlock, renderMode, isSlideshowMode, appearance.coverSlideDuration]);
|
|
1953
2127
|
useEffect5(() => () => clearTimeout(coverGraceTimer.current), []);
|
|
1954
2128
|
useEffect5(() => () => clearTimeout(tapFeedbackTimer.current), []);
|
|
1955
2129
|
const showVideoCoverBlock = !isSlideshowMode && !isLinearMode && !!coverBlock && (coverForced || coverGraceActive || !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,10 @@ import { Theme, VideoPresentation, VideoPipSize, VideoPipShape, VideoPipPosition
|
|
|
4
4
|
export { MarkdownRenderer, MermaidDiagram, MermaidDiagramProps } from './markdown/index.js';
|
|
5
5
|
export { CanvasSection, CanvasSectionProps, ImageDisplayMode, LinearDocView, LinearDocViewProps, PageSectionView, PageSectionViewProps, PageViewContext, PageViewContextValue, usePageView } from './page/index.js';
|
|
6
6
|
export { ImageLayer, MapLayer, MermaidLayer, PathLayer, ShapeLayer, TableLayer, TextLayer, TreeLayer, VideoLayer } from './layers/index.js';
|
|
7
|
+
import { CoverSlideTemplate, CoverSlidePlayback } from '@bendyline/squisq/doc';
|
|
8
|
+
export { getAnimationStyle, getTransitionClass } from '@bendyline/squisq/doc';
|
|
7
9
|
export { MediaContext, MediaScheduleController, ModalDialogOptions, ResourcePolicyContext, UseDocPlaybackOptions, useAudioSync, useAutoSurface, useDocPlayback, useMediaProvider, useMediaSchedule, useMediaUrl, useModalDialog, useResourcePolicy, useViewportOrientation } from './hooks/index.js';
|
|
8
10
|
export { A as AudioActions, a as AudioController, b as AudioState } from './AudioController-DwMsPe38.js';
|
|
9
|
-
export { getAnimationStyle, getTransitionClass } from '@bendyline/squisq/doc';
|
|
10
11
|
export { JsonView, JsonViewProps } from './json-view/index.js';
|
|
11
12
|
import 'react/jsx-runtime';
|
|
12
13
|
import 'react';
|
|
@@ -103,6 +104,9 @@ interface DocPlayerAppearanceOverrides {
|
|
|
103
104
|
pipShape?: PipShape;
|
|
104
105
|
pipPosition?: PipPosition;
|
|
105
106
|
showCoverSlide?: boolean;
|
|
107
|
+
coverSlideTemplate?: CoverSlideTemplate;
|
|
108
|
+
coverSlideDuration?: number;
|
|
109
|
+
coverSlidePlayback?: CoverSlidePlayback;
|
|
106
110
|
}
|
|
107
111
|
interface ResolvedDocPlayerAppearance {
|
|
108
112
|
theme: Theme;
|
|
@@ -111,6 +115,9 @@ interface ResolvedDocPlayerAppearance {
|
|
|
111
115
|
pipShape: PipShape;
|
|
112
116
|
pipPosition: PipPosition;
|
|
113
117
|
showCoverSlide: boolean;
|
|
118
|
+
coverSlideTemplate: CoverSlideTemplate;
|
|
119
|
+
coverSlideDuration: number;
|
|
120
|
+
coverSlidePlayback: CoverSlidePlayback;
|
|
114
121
|
}
|
|
115
122
|
/** Resolve the visual settings a standalone/export player must inherit from its Doc. */
|
|
116
123
|
declare function resolveDocPlayerAppearance(doc: Doc, overrides?: DocPlayerAppearanceOverrides): ResolvedDocPlayerAppearance;
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
formatTime,
|
|
13
13
|
resolveDocPlayerAppearance,
|
|
14
14
|
useMediaClipDurations
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-ZKNX3EJI.js";
|
|
16
16
|
import {
|
|
17
17
|
BlockRenderer,
|
|
18
18
|
CanvasSection,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
PageSectionView,
|
|
21
21
|
PageViewContext,
|
|
22
22
|
usePageView
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-THDXCSPC.js";
|
|
24
24
|
import {
|
|
25
25
|
ImageLayer,
|
|
26
26
|
MapLayer,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
VideoLayer,
|
|
34
34
|
getAnimationStyle,
|
|
35
35
|
getTransitionClass
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-UJEHDVQO.js";
|
|
37
37
|
import {
|
|
38
38
|
useModalDialog
|
|
39
39
|
} from "./chunk-65POMKHR.js";
|
package/dist/layers/index.js
CHANGED
package/dist/page/index.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
PageSectionView,
|
|
5
5
|
PageViewContext,
|
|
6
6
|
usePageView
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
} from "../chunk-THDXCSPC.js";
|
|
8
|
+
import "../chunk-UJEHDVQO.js";
|
|
9
9
|
import "../chunk-TT6ENR6T.js";
|
|
10
10
|
import "../chunk-VMPQEUJH.js";
|
|
11
11
|
import "../chunk-WLUZTUNZ.js";
|
package/dist/player/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { Block, VideoPresentation as VideoPresentation$1, VideoPipSize, VideoPipShape, VideoPipPosition, Doc, Theme, SurfaceScheme, Transition, CaptionTrack, ViewportConfig as ViewportConfig$1, ScheduledClip } from '@bendyline/squisq/schemas';
|
|
3
|
-
import { ViewportConfig } from '@bendyline/squisq/doc';
|
|
3
|
+
import { ViewportConfig, CoverSlideTemplate, CoverSlidePlayback } from '@bendyline/squisq/doc';
|
|
4
4
|
import { a as AudioController } from '../AudioController-DwMsPe38.js';
|
|
5
5
|
import { CSSProperties } from 'react';
|
|
6
6
|
|
|
@@ -222,6 +222,12 @@ interface DocPlayerProps {
|
|
|
222
222
|
/** Video, manual slideshow, or long-scrolling linear rendition. */
|
|
223
223
|
displayMode?: DisplayMode;
|
|
224
224
|
showCoverSlide?: boolean;
|
|
225
|
+
/** Visual template used to materialize the managed cover. */
|
|
226
|
+
coverSlideTemplate?: CoverSlideTemplate;
|
|
227
|
+
/** Seconds the cover remains visible after Video playback starts. */
|
|
228
|
+
coverSlideDuration?: number;
|
|
229
|
+
/** Whether video export advances or delays the story while the cover is visible. */
|
|
230
|
+
coverSlidePlayback?: CoverSlidePlayback;
|
|
225
231
|
coverVisible?: boolean;
|
|
226
232
|
captionStyle?: CaptionStyle;
|
|
227
233
|
/**
|
package/dist/player/index.js
CHANGED
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
MediaClipLayer,
|
|
11
11
|
SocialCaptionOverlay,
|
|
12
12
|
formatTime
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-ZKNX3EJI.js";
|
|
14
14
|
import {
|
|
15
15
|
BlockRenderer
|
|
16
|
-
} from "../chunk-
|
|
17
|
-
import "../chunk-
|
|
16
|
+
} from "../chunk-THDXCSPC.js";
|
|
17
|
+
import "../chunk-UJEHDVQO.js";
|
|
18
18
|
import "../chunk-BOZJ655L.js";
|
|
19
19
|
import "../chunk-TT6ENR6T.js";
|
|
20
20
|
import {
|