@bendyline/squisq-react 2.11.10 → 2.11.11

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.
@@ -494,7 +494,8 @@ function useDocPlayback(script, currentTime, options = {}) {
494
494
  viewport = VIEWPORT_PRESETS.landscape,
495
495
  theme,
496
496
  onSeek,
497
- useAudioSegmentTiming = true
497
+ useAudioSegmentTiming = true,
498
+ deterministicTransitions = false
498
499
  } = options;
499
500
  const blocks = useMemo3(() => {
500
501
  if (!script?.blocks) {
@@ -577,7 +578,7 @@ function useDocPlayback(script, currentTime, options = {}) {
577
578
  lastRenderedBlockRef.current = currentBlock;
578
579
  const transitionDuration = currentBlock?.transition ? resolveTransitionDuration(currentBlock.transition) : 0;
579
580
  const isEntering = !!currentBlock && transitionDuration > 0 && blockTime < transitionDuration;
580
- const outgoingBlock = outgoingBlockRef.current;
581
+ const outgoingBlock = deterministicTransitions ? currentBlockIndex > 0 ? blocks[currentBlockIndex - 1] ?? null : null : outgoingBlockRef.current;
581
582
  const isExiting = isEntering && outgoingBlock != null && outgoingBlock.id !== currentBlock?.id;
582
583
  const previousBlock = isExiting ? outgoingBlock : null;
583
584
  const goToBlock = useCallback2(
@@ -11,7 +11,7 @@ import {
11
11
  useDocPlayback,
12
12
  useMediaSchedule,
13
13
  useViewportOrientation
14
- } from "./chunk-MVJQL2W2.js";
14
+ } from "./chunk-3SKOLZUA.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: "18%" };
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 baseFontSize = Math.round(viewportHeight * 0.055);
584
- const fontSize = Math.max(24, Math.min(72, baseFontSize));
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
- textAlign: "center",
597
- padding: "0 8%",
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-block",
607
- lineHeight: 1.3
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
- fontFamily,
616
- fontSize: `${fontSize}px`,
617
- fontWeight: isActive ? 800 : 600,
618
- color: isActive ? primaryColor : "rgba(255, 255, 255, 0.9)",
619
- textShadow: "0 2px 8px rgba(0,0,0,0.7), 0 0 20px rgba(0,0,0,0.4)",
620
- marginRight: i < activeChunk.words.length - 1 ? "0.3em" : void 0,
621
- transition: "color 0.1s ease, font-weight 0.1s ease",
622
- textTransform: "uppercase"
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 = {
@@ -2770,6 +2804,9 @@ function DocPlayerContent({
2770
2804
  viewport: activeViewport,
2771
2805
  theme: effectiveTheme,
2772
2806
  onSeek: seekTo,
2807
+ // Offline capture may visit frames out of order; derive the crossfade
2808
+ // partner from block order rather than from what was rendered last.
2809
+ deterministicTransitions: renderMode,
2773
2810
  // A synthetic track is only the timer used by an unnarrated preview.
2774
2811
  // Narration pacing may compact short visual beats, but it must never
2775
2812
  // remove authored slides from the default loss-averse projection.
@@ -2839,7 +2876,7 @@ function DocPlayerContent({
2839
2876
  }, [isPlaying, coverBlock, renderMode, isSlideshowMode, appearance.coverSlideDuration]);
2840
2877
  useEffect7(() => () => clearTimeout(coverGraceTimer.current), []);
2841
2878
  useEffect7(() => () => clearTimeout(tapFeedbackTimer.current), []);
2842
- const showVideoCoverBlock = !isSlideshowMode && !isLinearMode && !!coverBlock && (coverForced || coverGraceActive || !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay);
2879
+ const showVideoCoverBlock = !isLinearMode && !!coverBlock && (coverForced || !isSlideshowMode && (coverGraceActive || !isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay));
2843
2880
  const effectiveSlideshowCoverVisible = coverVisible ?? slideshowCoverVisible;
2844
2881
  const showSlideshowCover = !!(isSlideshowMode && !isLinearMode && !renderMode && coverBlock && effectiveSlideshowCoverVisible);
2845
2882
  const showCoverBlock = coverVisible === void 0 ? showVideoCoverBlock || showSlideshowCover : !!coverBlock && coverVisible;
@@ -2879,6 +2916,7 @@ function DocPlayerContent({
2879
2916
  seekTo: (time) => current().seekTo(time),
2880
2917
  getRenderedTime: () => current().getRenderedTime(),
2881
2918
  getDuration: () => current().getDuration(),
2919
+ getViewport: () => current().getViewport(),
2882
2920
  getBlocks: () => current().getBlocks(),
2883
2921
  getAudioSegments: () => current().getAudioSegments(),
2884
2922
  getCaptions: () => current().getCaptions(),
@@ -2908,27 +2946,39 @@ function DocPlayerContent({
2908
2946
  } else {
2909
2947
  await seekTo(time);
2910
2948
  }
2911
- let blockStartTime = 0;
2949
+ let activeIndex = -1;
2912
2950
  for (let i = expandedBlocks.length - 1; i >= 0; i--) {
2913
2951
  if (time >= expandedBlocks[i].startTime) {
2914
- blockStartTime = expandedBlocks[i].startTime;
2952
+ activeIndex = i;
2915
2953
  break;
2916
2954
  }
2917
2955
  }
2956
+ const blockStartTime = activeIndex >= 0 ? expandedBlocks[activeIndex].startTime : 0;
2957
+ const previousStartTime = activeIndex > 0 ? expandedBlocks[activeIndex - 1].startTime : blockStartTime;
2918
2958
  const elapsedMs = (time - blockStartTime) * 1e3;
2959
+ const previousElapsedMs = (time - previousStartTime) * 1e3;
2919
2960
  const selectVideoFrame = async (video, targetTime) => {
2920
2961
  const selector = renderMode ? renderVideoFrameSelectorRef.current : void 0;
2921
2962
  if (selector && await selector(video, targetTime)) return;
2922
2963
  await seekVideoToFrame(video, targetTime);
2923
2964
  };
2924
- (root.getAnimations?.() ?? []).forEach((anim) => {
2965
+ (root.getAnimations?.({ subtree: true }) ?? []).forEach((anim) => {
2925
2966
  const target = anim.effect?.target;
2926
2967
  if (!target) return;
2927
- if (target.closest(".doc-player__block--active")) {
2928
- anim.currentTime = Math.max(0, elapsedMs);
2929
- } else if (target.closest(".doc-player__block--previous")) {
2930
- anim.currentTime = Math.max(0, elapsedMs);
2968
+ let position;
2969
+ if (target.closest(".doc-player__block--previous")) {
2970
+ position = target.classList.contains("block-svg") ? elapsedMs : previousElapsedMs;
2971
+ } else if (target.closest(".doc-player__block--active")) {
2972
+ position = elapsedMs;
2973
+ } else if (target.closest(".doc-player__block--cover")) {
2974
+ position = 0;
2975
+ } else {
2976
+ position = time * 1e3;
2977
+ }
2978
+ if (renderMode && typeof anim.pause === "function" && anim.playState !== "paused") {
2979
+ anim.pause();
2931
2980
  }
2981
+ anim.currentTime = Math.max(0, position);
2932
2982
  });
2933
2983
  const blockElapsed = time - blockStartTime;
2934
2984
  const videoSeekPromises = [];
@@ -3005,10 +3055,12 @@ function DocPlayerContent({
3005
3055
  return waitForVisualUpdate();
3006
3056
  };
3007
3057
  const hasCoverBlock = () => !!coverBlock;
3058
+ const getViewport = () => ({ width: activeViewport.width, height: activeViewport.height });
3008
3059
  const api = {
3009
3060
  seekTo: renderSeekTo,
3010
3061
  getRenderedTime: () => committedRenderTimeRef.current,
3011
3062
  getDuration,
3063
+ getViewport,
3012
3064
  getBlocks,
3013
3065
  getAudioSegments,
3014
3066
  getCaptions,
@@ -3031,7 +3083,8 @@ function DocPlayerContent({
3031
3083
  doc,
3032
3084
  externalAudioController,
3033
3085
  waitForRenderCommit,
3034
- isDashboardMode
3086
+ isDashboardMode,
3087
+ activeViewport
3035
3088
  ]);
3036
3089
  useEffect7(() => {
3037
3090
  if (isDashboardMode) return;
@@ -3445,7 +3498,7 @@ function DocPlayerContent({
3445
3498
  role: "region",
3446
3499
  "aria-label": "Document player",
3447
3500
  onKeyDown: renderMode ? void 0 : handleKeyDown,
3448
- className: `doc-player${swipeEnabled ? " doc-player--swipe" : ""}${swipe.phase === "dragging" ? " doc-player--grabbing" : ""}`,
3501
+ className: `doc-player${renderMode ? " doc-player--render-mode" : ""}${swipeEnabled ? " doc-player--swipe" : ""}${swipe.phase === "dragging" ? " doc-player--grabbing" : ""}`,
3449
3502
  onClick: handleContainerClick,
3450
3503
  onPointerDown: swipe.onPointerDown,
3451
3504
  style: {
@@ -3701,7 +3754,7 @@ function DocPlayerContent({
3701
3754
  }
3702
3755
  )
3703
3756
  ] }),
3704
- !isAvailable && unavailableMessage && /* @__PURE__ */ jsxs6(
3757
+ !renderMode && !isAvailable && unavailableMessage && /* @__PURE__ */ jsxs6(
3705
3758
  "div",
3706
3759
  {
3707
3760
  className: "doc-player__unavailable",
@@ -127,6 +127,14 @@ interface UseDocPlaybackOptions {
127
127
  * pacing rules.
128
128
  */
129
129
  useAudioSegmentTiming?: boolean;
130
+ /**
131
+ * Derive the outgoing (exiting) block from block order instead of from the
132
+ * block rendered last. Offline capture seeks frames out of order (resume,
133
+ * dedupe, sampling), so the crossfade partner must be a pure function of
134
+ * time. Live playback keeps the render-history path so swipes and manual
135
+ * jumps crossfade from what the viewer actually saw.
136
+ */
137
+ deterministicTransitions?: boolean;
130
138
  }
131
139
  declare function useDocPlayback(script: Doc | null, currentTime: number, options?: UseDocPlaybackOptions): PlaybackState & PlaybackActions;
132
140
 
@@ -6,7 +6,7 @@ import {
6
6
  useDocPlayback,
7
7
  useMediaSchedule,
8
8
  useViewportOrientation
9
- } from "../chunk-MVJQL2W2.js";
9
+ } from "../chunk-3SKOLZUA.js";
10
10
  import {
11
11
  useAutoSurface
12
12
  } from "../chunk-TT6ENR6T.js";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SquisqRenderAPI, V as VideoPresentation$1, P as PipSize, a as PipShape, b as PipPosition } from './player-5j10MhBq.js';
2
- export { B as BlockMarker, c as BlockRenderer, C as CaptionMode, d as CaptionOverlay, e as CaptionPosition, f as CaptionStyle, g as ControlsLayout, D as DisplayMode, h as DocControlsBottom, i as DocControlsOverlay, j as DocControlsSidebar, k as DocControlsSlideshow, l as DocPlayer, m as DocPlayerProps, n as DocPlayerWithSidebar, o as DocProgressBar, F as FlashcardFaceView, p as FlashcardFaceViewProps, q as FlashcardView, r as FlashcardViewProps, I as InlineAudioPlayer, s as InlineAudioPlayerProps, t as InlineVideoPlayer, u as InlineVideoPlayerProps, M as MediaClipLayer, v as MediaClipLayerProps, w as PlaybackActions, x as PlaybackState, R as RenderAudioSegmentInfo, y as RenderBlockInfo, z as RenderCaptionInfo, A as RenderChapterInfo, E as RenderVideoFrameSelector, G as SlideNavActions, H as SocialCaptionOverlay, J as formatTime } from './player-5j10MhBq.js';
1
+ import { S as SquisqRenderAPI, V as VideoPresentation$1, P as PipSize, a as PipShape, b as PipPosition } from './player-DOdWRK4X.js';
2
+ export { B as BlockMarker, c as BlockRenderer, C as CaptionMode, d as CaptionOverlay, e as CaptionPosition, f as CaptionStyle, g as ControlsLayout, D as DisplayMode, h as DocControlsBottom, i as DocControlsOverlay, j as DocControlsSidebar, k as DocControlsSlideshow, l as DocPlayer, m as DocPlayerProps, n as DocPlayerWithSidebar, o as DocProgressBar, F as FlashcardFaceView, p as FlashcardFaceViewProps, q as FlashcardView, r as FlashcardViewProps, I as InlineAudioPlayer, s as InlineAudioPlayerProps, t as InlineVideoPlayer, u as InlineVideoPlayerProps, M as MediaClipLayer, v as MediaClipLayerProps, w as PlaybackActions, x as PlaybackState, R as RenderAudioSegmentInfo, y as RenderBlockInfo, z as RenderCaptionInfo, A as RenderChapterInfo, E as RenderVideoFrameSelector, G as SlideNavActions, H as SocialCaptionOverlay, J as formatTime } from './player-DOdWRK4X.js';
3
3
  import { Theme, VideoPresentation, VideoPipSize, VideoPipShape, VideoPipPosition, Doc, ScheduledClip, MediaProvider } from '@bendyline/squisq/schemas';
4
4
  import { DashboardStyleId, ViewportConfig, CoverSlideTemplate, CoverSlidePlayback } from '@bendyline/squisq/doc';
5
5
  export { getAnimationStyle, getTransitionClass } from '@bendyline/squisq/doc';
@@ -100,6 +100,18 @@ interface MountOptions {
100
100
  * auto-play.
101
101
  */
102
102
  renderMode?: boolean;
103
+ /**
104
+ * Viewport the player composes for, in CSS pixels. Render pages pass their
105
+ * export dimensions so portrait and custom sizes are laid out natively
106
+ * instead of letterboxed inside the landscape preset. Omit for live hosts
107
+ * (the player sizes to its element) and for dashboard mounts, which always
108
+ * fill the host box.
109
+ */
110
+ viewport?: {
111
+ width: number;
112
+ height: number;
113
+ name?: string;
114
+ };
103
115
  /**
104
116
  * Whether to render slide transitions and per-layer animations (default: true).
105
117
  * Timed media continues to play when disabled.
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  formatTime,
16
16
  resolveDocPlayerAppearance,
17
17
  useMediaClipDurations
18
- } from "./chunk-4KBT2CXQ.js";
18
+ } from "./chunk-HERX7GUP.js";
19
19
  import {
20
20
  PaginatedDocView
21
21
  } from "./chunk-X4I6Y3OP.js";
@@ -48,7 +48,7 @@ import {
48
48
  useDocPlayback,
49
49
  useMediaSchedule,
50
50
  useViewportOrientation
51
- } from "./chunk-MVJQL2W2.js";
51
+ } from "./chunk-3SKOLZUA.js";
52
52
  import {
53
53
  JsonView,
54
54
  createJsonFormFenceRenderer
@@ -1,4 +1,4 @@
1
- export { B as BlockMarker, c as BlockRenderer, C as CaptionMode, d as CaptionOverlay, f as CaptionStyle, g as ControlsLayout, D as DisplayMode, h as DocControlsBottom, i as DocControlsOverlay, j as DocControlsSidebar, k as DocControlsSlideshow, l as DocPlayer, m as DocPlayerProps, n as DocPlayerWithSidebar, o as DocProgressBar, F as FlashcardFaceView, p as FlashcardFaceViewProps, q as FlashcardView, r as FlashcardViewProps, I as InlineAudioPlayer, s as InlineAudioPlayerProps, t as InlineVideoPlayer, u as InlineVideoPlayerProps, M as MediaClipLayer, v as MediaClipLayerProps, b as PipPosition, a as PipShape, P as PipSize, w as PlaybackActions, x as PlaybackState, R as RenderAudioSegmentInfo, y as RenderBlockInfo, z as RenderCaptionInfo, A as RenderChapterInfo, G as SlideNavActions, H as SocialCaptionOverlay, S as SquisqRenderAPI, V as VideoPresentation, J as formatTime } from '../player-5j10MhBq.js';
1
+ export { B as BlockMarker, c as BlockRenderer, C as CaptionMode, d as CaptionOverlay, f as CaptionStyle, g as ControlsLayout, D as DisplayMode, h as DocControlsBottom, i as DocControlsOverlay, j as DocControlsSidebar, k as DocControlsSlideshow, l as DocPlayer, m as DocPlayerProps, n as DocPlayerWithSidebar, o as DocProgressBar, F as FlashcardFaceView, p as FlashcardFaceViewProps, q as FlashcardView, r as FlashcardViewProps, I as InlineAudioPlayer, s as InlineAudioPlayerProps, t as InlineVideoPlayer, u as InlineVideoPlayerProps, M as MediaClipLayer, v as MediaClipLayerProps, b as PipPosition, a as PipShape, P as PipSize, w as PlaybackActions, x as PlaybackState, R as RenderAudioSegmentInfo, y as RenderBlockInfo, z as RenderCaptionInfo, A as RenderChapterInfo, G as SlideNavActions, H as SocialCaptionOverlay, S as SquisqRenderAPI, V as VideoPresentation, J as formatTime } from '../player-DOdWRK4X.js';
2
2
  export { a as CodeBlockCopyContext, C as CodeBlockCopyHandler } from '../MarkdownRenderer-CC9dOc6b.js';
3
3
  import 'react/jsx-runtime';
4
4
  import '@bendyline/squisq/schemas';
@@ -12,12 +12,12 @@ import {
12
12
  MediaClipLayer,
13
13
  SocialCaptionOverlay,
14
14
  formatTime
15
- } from "../chunk-4KBT2CXQ.js";
15
+ } from "../chunk-HERX7GUP.js";
16
16
  import {
17
17
  BlockRenderer
18
18
  } from "../chunk-CLP3VWJ2.js";
19
19
  import "../chunk-W6MKRWL6.js";
20
- import "../chunk-MVJQL2W2.js";
20
+ import "../chunk-3SKOLZUA.js";
21
21
  import "../chunk-TT6ENR6T.js";
22
22
  import {
23
23
  InlineAudioPlayer,
@@ -185,6 +185,11 @@ interface SquisqRenderAPI {
185
185
  /** Timeline time most recently committed to the player DOM. */
186
186
  getRenderedTime: () => number;
187
187
  getDuration: () => number;
188
+ /** Viewport (CSS px) the blocks were composed for; capture hosts verify it matches their export size. */
189
+ getViewport: () => {
190
+ width: number;
191
+ height: number;
192
+ };
188
193
  getBlocks: () => RenderBlockInfo[];
189
194
  getAudioSegments: () => RenderAudioSegmentInfo[];
190
195
  getCaptions: () => RenderCaptionInfo[];