@bendyline/squisq-react 2.11.10 → 2.11.12
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-X4I6Y3OP.js → chunk-7T26IKW2.js} +1 -1
- package/dist/{chunk-CLP3VWJ2.js → chunk-P3FJ3YKS.js} +6 -4
- package/dist/{chunk-4KBT2CXQ.js → chunk-QWUQNB4E.js} +116 -40
- package/dist/{chunk-MVJQL2W2.js → chunk-U2FIZWAL.js} +13 -2
- package/dist/{chunk-W6MKRWL6.js → chunk-WHVSQ42V.js} +193 -116
- package/dist/hooks/index.d.ts +14 -1
- package/dist/hooks/index.js +1 -1
- package/dist/index.d.ts +20 -3
- package/dist/index.js +5 -5
- package/dist/layers/index.js +1 -1
- package/dist/page/index.js +3 -3
- package/dist/player/index.d.ts +1 -1
- package/dist/player/index.js +4 -4
- package/dist/{player-5j10MhBq.d.ts → player-BCPc3Tv3.d.ts} +12 -1
- package/dist/squisq-player.full.global.js +428 -428
- package/dist/squisq-player.global.js +71 -71
- package/dist/standalone-source.js +1 -1
- package/dist/styles/index.css +236 -14
- package/package.json +2 -2
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
TreeLayer,
|
|
10
10
|
VideoLayer,
|
|
11
11
|
getTransitionClass
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-WHVSQ42V.js";
|
|
13
13
|
import {
|
|
14
14
|
useAutoSurface
|
|
15
15
|
} from "./chunk-TT6ENR6T.js";
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
// src/BlockRenderer.tsx
|
|
25
25
|
import { useId, useLayoutEffect, useRef, useState } from "react";
|
|
26
26
|
import { resolveTransitionDuration } from "@bendyline/squisq/schemas";
|
|
27
|
+
import { isGeometryBoundTemplate } from "@bendyline/squisq/doc";
|
|
27
28
|
|
|
28
29
|
// src/utils/textFit.ts
|
|
29
30
|
function largestFittingTextScale(fits, maximum = 2) {
|
|
@@ -138,6 +139,7 @@ function BlockRenderer({
|
|
|
138
139
|
const clipId = `vb-clip-${instanceId}-${block.id}`;
|
|
139
140
|
const svgRef = useRef(null);
|
|
140
141
|
const [textScale, setTextScale] = useState(1);
|
|
142
|
+
const growText = growTextToFit && !isGeometryBoundTemplate(block.template);
|
|
141
143
|
useLayoutEffect(() => {
|
|
142
144
|
const svg = svgRef.current;
|
|
143
145
|
let cancelled = false;
|
|
@@ -145,7 +147,7 @@ function BlockRenderer({
|
|
|
145
147
|
const measure = () => {
|
|
146
148
|
if (cancelled) return;
|
|
147
149
|
const targets = Array.from(svg.querySelectorAll("[data-squisq-text-fit]"));
|
|
148
|
-
if (!
|
|
150
|
+
if (!growText || targets.length === 0) {
|
|
149
151
|
applyTextScale(targets, 1);
|
|
150
152
|
setTextScale(1);
|
|
151
153
|
return;
|
|
@@ -175,7 +177,7 @@ function BlockRenderer({
|
|
|
175
177
|
return () => {
|
|
176
178
|
cancelled = true;
|
|
177
179
|
};
|
|
178
|
-
}, [block.layers,
|
|
180
|
+
}, [block.layers, growText, viewport.height, viewport.width]);
|
|
179
181
|
return /* @__PURE__ */ jsxs(
|
|
180
182
|
"svg",
|
|
181
183
|
{
|
|
@@ -186,7 +188,7 @@ function BlockRenderer({
|
|
|
186
188
|
preserveAspectRatio: "xMidYMid meet",
|
|
187
189
|
overflow: "hidden",
|
|
188
190
|
"data-block-id": block.id,
|
|
189
|
-
"data-squisq-text-scale":
|
|
191
|
+
"data-squisq-text-scale": growText ? textScale : void 0,
|
|
190
192
|
children: [
|
|
191
193
|
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", { id: clipId, children: /* @__PURE__ */ jsx("rect", { x: "0", y: "0", width: viewport.width, height: viewport.height }) }) }),
|
|
192
194
|
/* @__PURE__ */ jsx("g", { clipPath: `url(#${clipId})`, children: (block.layers ?? []).map((layer) => /* @__PURE__ */ jsx(
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BlockRenderer,
|
|
3
3
|
LinearDocView
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-P3FJ3YKS.js";
|
|
5
5
|
import {
|
|
6
6
|
isCaptureDrivenVideo,
|
|
7
7
|
seekVideoToFrame
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WHVSQ42V.js";
|
|
9
9
|
import {
|
|
10
10
|
useAudioSync,
|
|
11
11
|
useDocPlayback,
|
|
12
12
|
useMediaSchedule,
|
|
13
13
|
useViewportOrientation
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-U2FIZWAL.js";
|
|
15
15
|
import {
|
|
16
16
|
useAutoSurface
|
|
17
17
|
} from "./chunk-TT6ENR6T.js";
|
|
@@ -451,16 +451,26 @@ function useMediaClipDurations(schedule, basePath, mediaProviderOverride) {
|
|
|
451
451
|
import { useMemo as useMemo2 } from "react";
|
|
452
452
|
import { resolveFontFamily } from "@bendyline/squisq/schemas";
|
|
453
453
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
454
|
-
function positionStyle(position) {
|
|
454
|
+
function positionStyle(position, portrait) {
|
|
455
455
|
switch (position) {
|
|
456
456
|
case "top":
|
|
457
|
-
return { top: "8%" };
|
|
457
|
+
return { top: portrait ? "12%" : "8%" };
|
|
458
458
|
case "center":
|
|
459
459
|
return { top: "50%", transform: "translateY(-50%)" };
|
|
460
460
|
default:
|
|
461
|
-
return { bottom: "
|
|
461
|
+
return { bottom: portrait ? "24%" : "16%" };
|
|
462
462
|
}
|
|
463
463
|
}
|
|
464
|
+
function contrastText(hex) {
|
|
465
|
+
const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
|
|
466
|
+
if (!m) return "#ffffff";
|
|
467
|
+
const n = parseInt(m[1], 16);
|
|
468
|
+
const r = n >> 16 & 255;
|
|
469
|
+
const g = n >> 8 & 255;
|
|
470
|
+
const b = n & 255;
|
|
471
|
+
const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
472
|
+
return luma > 150 ? "#111111" : "#ffffff";
|
|
473
|
+
}
|
|
464
474
|
var TARGET_CHUNK_SIZE = 4;
|
|
465
475
|
var MIN_CHUNK_SIZE = 2;
|
|
466
476
|
var MAX_CHUNK_SIZE = 6;
|
|
@@ -526,7 +536,7 @@ function SocialCaptionOverlay({
|
|
|
526
536
|
className: "social-caption-overlay",
|
|
527
537
|
style: {
|
|
528
538
|
position: "absolute",
|
|
529
|
-
...positionStyle(position),
|
|
539
|
+
...positionStyle(position, (viewport?.height ?? 0) > (viewport?.width ?? 1)),
|
|
530
540
|
left: 0,
|
|
531
541
|
right: 0,
|
|
532
542
|
zIndex: 50,
|
|
@@ -579,22 +589,25 @@ function SocialCaptionOverlay({
|
|
|
579
589
|
}
|
|
580
590
|
const primaryColor = theme?.colors?.primary ?? "#5b9bd5";
|
|
581
591
|
const fontFamily = theme?.typography?.titleFont ? resolveFontFamily(theme.typography.titleFont, '"PT Serif", Georgia, serif') : '"PT Serif", Georgia, serif';
|
|
592
|
+
const viewportWidth = viewport?.width ?? 1280;
|
|
582
593
|
const viewportHeight = viewport?.height ?? 720;
|
|
583
|
-
const
|
|
584
|
-
const
|
|
594
|
+
const portrait = viewportHeight > viewportWidth;
|
|
595
|
+
const baseFontSize = Math.min(viewportHeight * 0.055, viewportWidth * 0.082);
|
|
596
|
+
const fontSize = Math.max(22, Math.min(96, Math.round(baseFontSize)));
|
|
585
597
|
return /* @__PURE__ */ jsx2(
|
|
586
598
|
"div",
|
|
587
599
|
{
|
|
588
600
|
className: "social-caption-overlay",
|
|
589
601
|
style: {
|
|
590
602
|
position: "absolute",
|
|
591
|
-
...positionStyle(position),
|
|
603
|
+
...positionStyle(position, portrait),
|
|
592
604
|
left: 0,
|
|
593
605
|
right: 0,
|
|
594
606
|
zIndex: 50,
|
|
595
607
|
pointerEvents: "none",
|
|
596
|
-
|
|
597
|
-
|
|
608
|
+
display: "flex",
|
|
609
|
+
justifyContent: "center",
|
|
610
|
+
padding: portrait ? "0 7%" : "0 10%",
|
|
598
611
|
boxSizing: "border-box",
|
|
599
612
|
opacity: 1,
|
|
600
613
|
transition: "opacity 0.15s ease-in-out"
|
|
@@ -602,24 +615,44 @@ function SocialCaptionOverlay({
|
|
|
602
615
|
children: /* @__PURE__ */ jsx2(
|
|
603
616
|
"div",
|
|
604
617
|
{
|
|
618
|
+
className: "social-caption-overlay__chunk",
|
|
605
619
|
style: {
|
|
606
|
-
display: "inline-
|
|
607
|
-
|
|
620
|
+
display: "inline-flex",
|
|
621
|
+
flexWrap: "wrap",
|
|
622
|
+
justifyContent: "center",
|
|
623
|
+
alignItems: "baseline",
|
|
624
|
+
maxWidth: "100%",
|
|
625
|
+
rowGap: "0.12em",
|
|
626
|
+
columnGap: "0.28em",
|
|
627
|
+
padding: "0.22em 0.55em",
|
|
628
|
+
borderRadius: "0.4em",
|
|
629
|
+
background: "rgba(10, 10, 12, 0.42)",
|
|
630
|
+
backdropFilter: "blur(8px)",
|
|
631
|
+
WebkitBackdropFilter: "blur(8px)",
|
|
632
|
+
fontFamily,
|
|
633
|
+
fontSize: `${fontSize}px`,
|
|
634
|
+
lineHeight: 1.18,
|
|
635
|
+
textTransform: "uppercase",
|
|
636
|
+
letterSpacing: "0.01em",
|
|
637
|
+
whiteSpace: "normal"
|
|
608
638
|
},
|
|
609
639
|
children: activeChunk.words.map((word, i) => {
|
|
610
640
|
const isActive = i === activeWordIndex;
|
|
611
641
|
return /* @__PURE__ */ jsx2(
|
|
612
642
|
"span",
|
|
613
643
|
{
|
|
644
|
+
className: isActive ? "social-caption-overlay__word is-active" : "social-caption-overlay__word",
|
|
614
645
|
style: {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
646
|
+
display: "inline-block",
|
|
647
|
+
whiteSpace: "nowrap",
|
|
648
|
+
padding: "0 0.16em",
|
|
649
|
+
borderRadius: "0.18em",
|
|
650
|
+
fontWeight: 800,
|
|
651
|
+
color: isActive ? contrastText(primaryColor) : "rgba(255, 255, 255, 0.95)",
|
|
652
|
+
background: isActive ? primaryColor : "transparent",
|
|
653
|
+
textShadow: isActive ? "none" : "0 2px 6px rgba(0,0,0,0.75), 0 0 18px rgba(0,0,0,0.45)",
|
|
654
|
+
transform: isActive ? "scale(1.05)" : "none",
|
|
655
|
+
transition: "color 0.1s ease, background-color 0.1s ease, transform 0.1s ease"
|
|
623
656
|
},
|
|
624
657
|
children: word.text
|
|
625
658
|
},
|
|
@@ -1457,6 +1490,7 @@ function DashboardView({
|
|
|
1457
1490
|
},
|
|
1458
1491
|
getRenderedTime: () => 0,
|
|
1459
1492
|
getDuration: () => 0,
|
|
1493
|
+
getViewport: () => ({ width: activeViewport.width, height: activeViewport.height }),
|
|
1460
1494
|
getBlocks: () => materialization.cells.map((cell) => ({
|
|
1461
1495
|
id: String(cell.block.id),
|
|
1462
1496
|
template: cell.block.template ?? "content",
|
|
@@ -1474,7 +1508,7 @@ function DashboardView({
|
|
|
1474
1508
|
};
|
|
1475
1509
|
onRenderAPIReady(api);
|
|
1476
1510
|
return () => onRenderAPIReady(null);
|
|
1477
|
-
}, [renderMode, onRenderAPIReady, materialization]);
|
|
1511
|
+
}, [activeViewport, renderMode, onRenderAPIReady, materialization]);
|
|
1478
1512
|
const canvasDimensions = { width: activeViewport.width, height: activeViewport.height };
|
|
1479
1513
|
const coverStyle = { position: "absolute", inset: 0 };
|
|
1480
1514
|
const canvasStyle = {
|
|
@@ -2254,7 +2288,8 @@ import {
|
|
|
2254
2288
|
isTemplateBlock as isTemplateBlock2,
|
|
2255
2289
|
getCaptionAtTime as getCaptionAtTime2,
|
|
2256
2290
|
resolveMediaSchedule,
|
|
2257
|
-
getDocPlaybackDuration
|
|
2291
|
+
getDocPlaybackDuration,
|
|
2292
|
+
resolveMotionForDoc
|
|
2258
2293
|
} from "@bendyline/squisq/schemas";
|
|
2259
2294
|
import { applySurface, pipStyleVars } from "@bendyline/squisq/schemas";
|
|
2260
2295
|
|
|
@@ -2551,6 +2586,7 @@ function DocPlayerContent({
|
|
|
2551
2586
|
coverSlidePlayback,
|
|
2552
2587
|
coverVisible,
|
|
2553
2588
|
theme,
|
|
2589
|
+
motion: motionProp,
|
|
2554
2590
|
surface,
|
|
2555
2591
|
captionStyle = "standard",
|
|
2556
2592
|
captionPosition,
|
|
@@ -2753,6 +2789,12 @@ function DocPlayerContent({
|
|
|
2753
2789
|
}),
|
|
2754
2790
|
[resolvedPipStyle]
|
|
2755
2791
|
);
|
|
2792
|
+
const hasAnchoredBlocks = useMemo5(
|
|
2793
|
+
() => doc.blocks.some(
|
|
2794
|
+
(block) => typeof block.sourceStartTime === "number"
|
|
2795
|
+
),
|
|
2796
|
+
[doc.blocks]
|
|
2797
|
+
);
|
|
2756
2798
|
const {
|
|
2757
2799
|
currentBlock,
|
|
2758
2800
|
currentBlockIndex,
|
|
@@ -2769,17 +2811,29 @@ function DocPlayerContent({
|
|
|
2769
2811
|
} = useDocPlayback(doc, currentTime, {
|
|
2770
2812
|
viewport: activeViewport,
|
|
2771
2813
|
theme: effectiveTheme,
|
|
2814
|
+
motion: motionProp,
|
|
2772
2815
|
onSeek: seekTo,
|
|
2816
|
+
// Offline capture may visit frames out of order; derive the crossfade
|
|
2817
|
+
// partner from block order rather than from what was rendered last.
|
|
2818
|
+
deterministicTransitions: renderMode,
|
|
2773
2819
|
// A synthetic track is only the timer used by an unnarrated preview.
|
|
2774
2820
|
// Narration pacing may compact short visual beats, but it must never
|
|
2775
2821
|
// remove authored slides from the default loss-averse projection.
|
|
2776
|
-
|
|
2822
|
+
// Offline capture also runs on the synthetic clock, yet a narrated doc
|
|
2823
|
+
// (blocks anchored to its segments by `sourceStartTime`) still takes its
|
|
2824
|
+
// timeline from those segments there — the exporter muxes the audio
|
|
2825
|
+
// afterwards. Without them, block durations would simply stack and every
|
|
2826
|
+
// gap between anchored blocks would pull the rest earlier, so the render
|
|
2827
|
+
// would no longer match live playback.
|
|
2828
|
+
useAudioSegmentTiming: audioMode !== "synthetic" || renderMode && hasAnchoredBlocks
|
|
2777
2829
|
});
|
|
2778
2830
|
const coverBlock = useMemo5(() => {
|
|
2779
2831
|
const startBlockConfig = doc.startBlock;
|
|
2780
2832
|
if (!appearance.showCoverSlide) return null;
|
|
2781
2833
|
if (!startBlockConfig) return null;
|
|
2782
|
-
const context = createTemplateContext(effectiveTheme, 0, 1, activeViewport
|
|
2834
|
+
const context = createTemplateContext(effectiveTheme, 0, 1, activeViewport, {
|
|
2835
|
+
motion: resolveMotionForDoc(doc, effectiveTheme, motionProp)
|
|
2836
|
+
});
|
|
2783
2837
|
const layers = expandCoverBlock(startBlockConfig, context, appearance.coverSlideTemplate);
|
|
2784
2838
|
return {
|
|
2785
2839
|
id: "cover-block",
|
|
@@ -2791,7 +2845,8 @@ function DocPlayerContent({
|
|
|
2791
2845
|
layers
|
|
2792
2846
|
};
|
|
2793
2847
|
}, [
|
|
2794
|
-
doc
|
|
2848
|
+
doc,
|
|
2849
|
+
motionProp,
|
|
2795
2850
|
activeViewport,
|
|
2796
2851
|
effectiveTheme,
|
|
2797
2852
|
appearance.showCoverSlide,
|
|
@@ -2839,7 +2894,7 @@ function DocPlayerContent({
|
|
|
2839
2894
|
}, [isPlaying, coverBlock, renderMode, isSlideshowMode, appearance.coverSlideDuration]);
|
|
2840
2895
|
useEffect7(() => () => clearTimeout(coverGraceTimer.current), []);
|
|
2841
2896
|
useEffect7(() => () => clearTimeout(tapFeedbackTimer.current), []);
|
|
2842
|
-
const showVideoCoverBlock = !
|
|
2897
|
+
const showVideoCoverBlock = !isLinearMode && !!coverBlock && (coverForced || !isSlideshowMode && (coverGraceActive || !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay));
|
|
2843
2898
|
const effectiveSlideshowCoverVisible = coverVisible ?? slideshowCoverVisible;
|
|
2844
2899
|
const showSlideshowCover = !!(isSlideshowMode && !isLinearMode && !renderMode && coverBlock && effectiveSlideshowCoverVisible);
|
|
2845
2900
|
const showCoverBlock = coverVisible === void 0 ? showVideoCoverBlock || showSlideshowCover : !!coverBlock && coverVisible;
|
|
@@ -2879,6 +2934,7 @@ function DocPlayerContent({
|
|
|
2879
2934
|
seekTo: (time) => current().seekTo(time),
|
|
2880
2935
|
getRenderedTime: () => current().getRenderedTime(),
|
|
2881
2936
|
getDuration: () => current().getDuration(),
|
|
2937
|
+
getViewport: () => current().getViewport(),
|
|
2882
2938
|
getBlocks: () => current().getBlocks(),
|
|
2883
2939
|
getAudioSegments: () => current().getAudioSegments(),
|
|
2884
2940
|
getCaptions: () => current().getCaptions(),
|
|
@@ -2908,27 +2964,42 @@ function DocPlayerContent({
|
|
|
2908
2964
|
} else {
|
|
2909
2965
|
await seekTo(time);
|
|
2910
2966
|
}
|
|
2911
|
-
let
|
|
2967
|
+
let activeIndex = -1;
|
|
2912
2968
|
for (let i = expandedBlocks.length - 1; i >= 0; i--) {
|
|
2913
2969
|
if (time >= expandedBlocks[i].startTime) {
|
|
2914
|
-
|
|
2970
|
+
activeIndex = i;
|
|
2915
2971
|
break;
|
|
2916
2972
|
}
|
|
2917
2973
|
}
|
|
2974
|
+
const blockStartTime = activeIndex >= 0 ? expandedBlocks[activeIndex].startTime : 0;
|
|
2975
|
+
const previousStartTime = activeIndex > 0 ? expandedBlocks[activeIndex - 1].startTime : blockStartTime;
|
|
2918
2976
|
const elapsedMs = (time - blockStartTime) * 1e3;
|
|
2977
|
+
const previousElapsedMs = (time - previousStartTime) * 1e3;
|
|
2919
2978
|
const selectVideoFrame = async (video, targetTime) => {
|
|
2920
2979
|
const selector = renderMode ? renderVideoFrameSelectorRef.current : void 0;
|
|
2921
2980
|
if (selector && await selector(video, targetTime)) return;
|
|
2922
2981
|
await seekVideoToFrame(video, targetTime);
|
|
2923
2982
|
};
|
|
2924
|
-
|
|
2983
|
+
let seekedActiveAnimations = 0;
|
|
2984
|
+
(root.getAnimations?.({ subtree: true }) ?? []).forEach((anim) => {
|
|
2925
2985
|
const target = anim.effect?.target;
|
|
2926
2986
|
if (!target) return;
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2987
|
+
let position;
|
|
2988
|
+
if (target.closest(".doc-player__block--previous")) {
|
|
2989
|
+
position = target.classList.contains("block-svg") ? elapsedMs : previousElapsedMs;
|
|
2990
|
+
} else if (target.closest(".doc-player__block--active")) {
|
|
2991
|
+
position = elapsedMs;
|
|
2992
|
+
} else if (target.closest(".doc-player__block--cover")) {
|
|
2993
|
+
position = 0;
|
|
2994
|
+
} else {
|
|
2995
|
+
position = time * 1e3;
|
|
2931
2996
|
}
|
|
2997
|
+
if (renderMode && typeof anim.pause === "function" && anim.playState !== "paused") {
|
|
2998
|
+
anim.pause();
|
|
2999
|
+
}
|
|
3000
|
+
anim.currentTime = Math.max(0, position);
|
|
3001
|
+
const end = anim.effect?.getComputedTiming?.().endTime;
|
|
3002
|
+
if (typeof end !== "number" || position < end) seekedActiveAnimations += 1;
|
|
2932
3003
|
});
|
|
2933
3004
|
const blockElapsed = time - blockStartTime;
|
|
2934
3005
|
const videoSeekPromises = [];
|
|
@@ -2964,7 +3035,9 @@ function DocPlayerContent({
|
|
|
2964
3035
|
const blocksUnchanged = previousBlocks !== null && previousBlocks.length === presentedBlocks.length && presentedBlocks.every(
|
|
2965
3036
|
(block, index) => block.element === previousBlocks[index].element && block.className === previousBlocks[index].className
|
|
2966
3037
|
);
|
|
2967
|
-
if (!renderMode || !blocksUnchanged
|
|
3038
|
+
if (!renderMode || !blocksUnchanged || seekedActiveAnimations > 0) {
|
|
3039
|
+
await waitForVisualUpdate();
|
|
3040
|
+
}
|
|
2968
3041
|
};
|
|
2969
3042
|
const getDuration = () => {
|
|
2970
3043
|
const mediaDuration = getDocPlaybackDuration(doc);
|
|
@@ -3005,10 +3078,12 @@ function DocPlayerContent({
|
|
|
3005
3078
|
return waitForVisualUpdate();
|
|
3006
3079
|
};
|
|
3007
3080
|
const hasCoverBlock = () => !!coverBlock;
|
|
3081
|
+
const getViewport = () => ({ width: activeViewport.width, height: activeViewport.height });
|
|
3008
3082
|
const api = {
|
|
3009
3083
|
seekTo: renderSeekTo,
|
|
3010
3084
|
getRenderedTime: () => committedRenderTimeRef.current,
|
|
3011
3085
|
getDuration,
|
|
3086
|
+
getViewport,
|
|
3012
3087
|
getBlocks,
|
|
3013
3088
|
getAudioSegments,
|
|
3014
3089
|
getCaptions,
|
|
@@ -3031,7 +3106,8 @@ function DocPlayerContent({
|
|
|
3031
3106
|
doc,
|
|
3032
3107
|
externalAudioController,
|
|
3033
3108
|
waitForRenderCommit,
|
|
3034
|
-
isDashboardMode
|
|
3109
|
+
isDashboardMode,
|
|
3110
|
+
activeViewport
|
|
3035
3111
|
]);
|
|
3036
3112
|
useEffect7(() => {
|
|
3037
3113
|
if (isDashboardMode) return;
|
|
@@ -3445,7 +3521,7 @@ function DocPlayerContent({
|
|
|
3445
3521
|
role: "region",
|
|
3446
3522
|
"aria-label": "Document player",
|
|
3447
3523
|
onKeyDown: renderMode ? void 0 : handleKeyDown,
|
|
3448
|
-
className: `doc-player${swipeEnabled ? " doc-player--swipe" : ""}${swipe.phase === "dragging" ? " doc-player--grabbing" : ""}`,
|
|
3524
|
+
className: `doc-player${renderMode ? " doc-player--render-mode" : ""}${swipeEnabled ? " doc-player--swipe" : ""}${swipe.phase === "dragging" ? " doc-player--grabbing" : ""}`,
|
|
3449
3525
|
onClick: handleContainerClick,
|
|
3450
3526
|
onPointerDown: swipe.onPointerDown,
|
|
3451
3527
|
style: {
|
|
@@ -3503,7 +3579,7 @@ function DocPlayerContent({
|
|
|
3503
3579
|
BlockRenderer,
|
|
3504
3580
|
{
|
|
3505
3581
|
block: previousBlock,
|
|
3506
|
-
blockTime,
|
|
3582
|
+
blockTime: Math.max(0, currentTime - previousBlock.startTime),
|
|
3507
3583
|
basePath,
|
|
3508
3584
|
isExiting: true,
|
|
3509
3585
|
transition: currentBlock?.transition,
|
|
@@ -3701,7 +3777,7 @@ function DocPlayerContent({
|
|
|
3701
3777
|
}
|
|
3702
3778
|
)
|
|
3703
3779
|
] }),
|
|
3704
|
-
!isAvailable && unavailableMessage && /* @__PURE__ */ jsxs6(
|
|
3780
|
+
!renderMode && !isAvailable && unavailableMessage && /* @__PURE__ */ jsxs6(
|
|
3705
3781
|
"div",
|
|
3706
3782
|
{
|
|
3707
3783
|
className: "doc-player__unavailable",
|
|
@@ -476,6 +476,7 @@ function useAudioSync(audioRef, track, basePath = "", enabled = true, mode = "me
|
|
|
476
476
|
|
|
477
477
|
// src/hooks/useDocPlayback.ts
|
|
478
478
|
import { useMemo as useMemo3, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
479
|
+
import { resolveMotionForDoc } from "@bendyline/squisq/schemas";
|
|
479
480
|
import {
|
|
480
481
|
DEFAULT_THEME,
|
|
481
482
|
getBlockAtTime,
|
|
@@ -494,7 +495,9 @@ function useDocPlayback(script, currentTime, options = {}) {
|
|
|
494
495
|
viewport = VIEWPORT_PRESETS.landscape,
|
|
495
496
|
theme,
|
|
496
497
|
onSeek,
|
|
497
|
-
useAudioSegmentTiming = true
|
|
498
|
+
useAudioSegmentTiming = true,
|
|
499
|
+
deterministicTransitions = false,
|
|
500
|
+
motion: motionOverride
|
|
498
501
|
} = options;
|
|
499
502
|
const blocks = useMemo3(() => {
|
|
500
503
|
if (!script?.blocks) {
|
|
@@ -518,6 +521,11 @@ function useDocPlayback(script, currentTime, options = {}) {
|
|
|
518
521
|
viewport,
|
|
519
522
|
persistentLayers,
|
|
520
523
|
theme,
|
|
524
|
+
motion: resolveMotionForDoc(
|
|
525
|
+
{ motion: script.motion, frontmatter: script.frontmatter },
|
|
526
|
+
resolvedTheme,
|
|
527
|
+
motionOverride
|
|
528
|
+
),
|
|
521
529
|
// Custom (user-defined) templates inlined into the doc's
|
|
522
530
|
// frontmatter — see CustomTemplates.ts. Merged onto the
|
|
523
531
|
// built-in registry so blocks annotated with `{[myhero]}`
|
|
@@ -535,6 +543,9 @@ function useDocPlayback(script, currentTime, options = {}) {
|
|
|
535
543
|
script?.audio?.segments,
|
|
536
544
|
script?.persistentLayers,
|
|
537
545
|
script?.customTemplates,
|
|
546
|
+
script?.motion,
|
|
547
|
+
script?.frontmatter,
|
|
548
|
+
motionOverride,
|
|
538
549
|
viewport,
|
|
539
550
|
theme,
|
|
540
551
|
useAudioSegmentTiming
|
|
@@ -577,7 +588,7 @@ function useDocPlayback(script, currentTime, options = {}) {
|
|
|
577
588
|
lastRenderedBlockRef.current = currentBlock;
|
|
578
589
|
const transitionDuration = currentBlock?.transition ? resolveTransitionDuration(currentBlock.transition) : 0;
|
|
579
590
|
const isEntering = !!currentBlock && transitionDuration > 0 && blockTime < transitionDuration;
|
|
580
|
-
const outgoingBlock = outgoingBlockRef.current;
|
|
591
|
+
const outgoingBlock = deterministicTransitions ? currentBlockIndex > 0 ? blocks[currentBlockIndex - 1] ?? null : null : outgoingBlockRef.current;
|
|
581
592
|
const isExiting = isEntering && outgoingBlock != null && outgoingBlock.id !== currentBlock?.id;
|
|
582
593
|
const previousBlock = isExiting ? outgoingBlock : null;
|
|
583
594
|
const goToBlock = useCallback2(
|