@bendyline/squisq-react 2.11.9 → 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(
@@ -742,6 +742,8 @@ function PageSectionView({ entry, featureFlip, isLeadProse }) {
742
742
  const hintAttrs = {};
743
743
  if (section.hints?.vignette) hintAttrs["data-hint-vignette"] = "";
744
744
  if (section.hints?.dropCap && isLeadProse) hintAttrs["data-hint-drop-cap"] = "";
745
+ const headingLevel = section.kind !== "hero" && section.slots.title !== void 0 ? section.slots.headingLevel ?? entry.block?.sourceHeading?.depth : void 0;
746
+ if (headingLevel !== void 0) hintAttrs["data-heading-level"] = String(headingLevel);
745
747
  return /* @__PURE__ */ jsx4(
746
748
  "section",
747
749
  {
@@ -803,6 +805,7 @@ function LinearDocView({
803
805
  imageDisplayMode = "inline",
804
806
  globalKeyboardShortcuts = false,
805
807
  showCover = true,
808
+ variant = "page",
806
809
  transformPage,
807
810
  showCodeCopyButton = false,
808
811
  onCopyCode,
@@ -824,8 +827,8 @@ function LinearDocView({
824
827
  return resolvedSurface ? applySurface(baseTheme, resolvedSurface) : baseTheme;
825
828
  }, [theme, resolvedSurface]);
826
829
  const pageStyle = useMemo2(
827
- () => resolvePageStyle2(activeTheme, transformPage),
828
- [activeTheme, transformPage]
830
+ () => resolvePageStyle2(activeTheme, transformPage, variant),
831
+ [activeTheme, transformPage, variant]
829
832
  );
830
833
  const widgetFenceLangs = useMemo2(() => fenceRendererLangs(fenceRenderers), [fenceRenderers]);
831
834
  const sections = useMemo2(() => {
@@ -836,9 +839,18 @@ function LinearDocView({
836
839
  customTemplates: resolvedDoc.customTemplates,
837
840
  cover: showCover !== false ? resolvedDoc.startBlock : false,
838
841
  transformPage,
842
+ variant,
839
843
  ...widgetFenceLangs.length > 0 ? { widgetFenceLangs } : {}
840
844
  });
841
- }, [resolvedDoc, activeTheme, activeViewport, showCover, transformPage, widgetFenceLangs]);
845
+ }, [
846
+ resolvedDoc,
847
+ activeTheme,
848
+ activeViewport,
849
+ showCover,
850
+ transformPage,
851
+ variant,
852
+ widgetFenceLangs
853
+ ]);
842
854
  const renderContext = useMemo2(
843
855
  () => ({
844
856
  theme: activeTheme,
@@ -887,12 +899,12 @@ function LinearDocView({
887
899
  if (entry.section.kind === "feature-split") {
888
900
  ordinals.set(entry.section.index, featureCount++);
889
901
  }
890
- if (firstProse < 0 && entry.section.kind === "prose") {
902
+ if (variant !== "document" && firstProse < 0 && entry.section.kind === "prose") {
891
903
  firstProse = entry.section.index;
892
904
  }
893
905
  }
894
906
  return { featureOrdinals: ordinals, leadProseIndex: firstProse };
895
- }, [sections]);
907
+ }, [sections, variant]);
896
908
  useEffect(() => {
897
909
  if (!globalKeyboardShortcuts) return;
898
910
  const handleKeyDown = (event) => {
@@ -932,6 +944,7 @@ function LinearDocView({
932
944
  "squisq-md",
933
945
  "squisq-page",
934
946
  thinMargins ? "squisq-linear-content--thin squisq-page--thin" : "",
947
+ variant === "document" ? "squisq-linear-content--document squisq-page--document" : "",
935
948
  imageDisplayMode === "thumbnail" ? "squisq-linear-content--thumbnail-images squisq-page--thumbnail-images" : ""
936
949
  ].filter(Boolean).join(" ");
937
950
  return /* @__PURE__ */ jsx5(
@@ -966,7 +979,7 @@ function LinearDocView({
966
979
  ...pageStyleDataAttributes(pageStyle),
967
980
  style: {
968
981
  lineHeight: lineHt,
969
- fontSize: "16px",
982
+ fontSize: variant === "document" ? "15px" : "16px",
970
983
  fontFamily: bodyFont,
971
984
  color: textColor,
972
985
  ...buildPageCssVars(activeTheme, pageStyle),
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BlockRenderer,
3
3
  LinearDocView
4
- } from "./chunk-LDXH4FOY.js";
4
+ } from "./chunk-CLP3VWJ2.js";
5
5
  import {
6
6
  isCaptureDrivenVideo,
7
7
  seekVideoToFrame
@@ -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",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  LinearDocView
3
- } from "./chunk-LDXH4FOY.js";
3
+ } from "./chunk-CLP3VWJ2.js";
4
4
 
5
5
  // src/PaginatedDocView.tsx
6
6
  import {
@@ -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,10 +15,10 @@ import {
15
15
  formatTime,
16
16
  resolveDocPlayerAppearance,
17
17
  useMediaClipDurations
18
- } from "./chunk-Y2ANJRT3.js";
18
+ } from "./chunk-HERX7GUP.js";
19
19
  import {
20
20
  PaginatedDocView
21
- } from "./chunk-SESRQ7XH.js";
21
+ } from "./chunk-X4I6Y3OP.js";
22
22
  import {
23
23
  BlockRenderer,
24
24
  CanvasSection,
@@ -26,7 +26,7 @@ import {
26
26
  PageSectionView,
27
27
  PageViewContext,
28
28
  usePageView
29
- } from "./chunk-LDXH4FOY.js";
29
+ } from "./chunk-CLP3VWJ2.js";
30
30
  import {
31
31
  ImageLayer,
32
32
  MapLayer,
@@ -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,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { Doc, ViewportConfig, Theme, SurfaceScheme, ThemePageStyle } from '@bendyline/squisq/schemas';
2
+ import { Doc, ViewportConfig, Theme, SurfaceScheme, PageVariant, ThemePageStyle } from '@bendyline/squisq/schemas';
3
3
  import { PageTransformHints, PageSectionMaterialization, PageSection, MaterializeBlockLayersOptions } from '@bendyline/squisq/doc';
4
4
  import { HtmlPolicy } from '@bendyline/squisq/markdown';
5
5
  import { FenceRendererMap } from '@bendyline/squisq/fence';
@@ -60,9 +60,18 @@ interface LinearDocViewProps {
60
60
  globalKeyboardShortcuts?: boolean;
61
61
  /**
62
62
  * Synthesize a hero section from `doc.startBlock` at the top of the
63
- * page (default: true). The editor's Cover toggle maps here.
63
+ * page (default: true). The editor's Cover toggle maps here. Under the
64
+ * `document` variant the cover is a left-aligned, title-only masthead.
64
65
  */
65
66
  showCover?: boolean;
67
+ /**
68
+ * Rendition register (default `'page'`, the art-directed scrolling site).
69
+ * `'document'` reads as a report: a masthead instead of a hero, one em-based
70
+ * heading scale that follows the markdown hierarchy, a 15px base, flat
71
+ * backgrounds, and auto-picked templates rendered as the prose they came
72
+ * from. Authored `{[template]}` annotations still render as typed sections.
73
+ */
74
+ variant?: PageVariant;
66
75
  /**
67
76
  * Page hints from the active transform (Summarize) style — spacing and
68
77
  * emphasis-curve adjustments defined by `TransformStyleConfig.page`.
@@ -94,7 +103,7 @@ type ImageDisplayMode = 'inline' | 'thumbnail';
94
103
  * <LinearDocView doc={doc} basePath="/media/" />
95
104
  * ```
96
105
  */
97
- declare function LinearDocView({ doc, markdown, basePath, viewport, className, theme, surface, animationsEnabled, thinMargins, imageDisplayMode, globalKeyboardShortcuts, showCover, transformPage, showCodeCopyButton, onCopyCode, fenceRenderers, htmlPolicy, linkSchemes, }: LinearDocViewProps): react_jsx_runtime.JSX.Element;
106
+ declare function LinearDocView({ doc, markdown, basePath, viewport, className, theme, surface, animationsEnabled, thinMargins, imageDisplayMode, globalKeyboardShortcuts, showCover, variant, transformPage, showCodeCopyButton, onCopyCode, fenceRenderers, htmlPolicy, linkSchemes, }: LinearDocViewProps): react_jsx_runtime.JSX.Element;
98
107
 
99
108
  interface PaginatedDocPage {
100
109
  key: string;
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  PaginatedDocView
3
- } from "../chunk-SESRQ7XH.js";
3
+ } from "../chunk-X4I6Y3OP.js";
4
4
  import {
5
5
  CanvasSection,
6
6
  LinearDocView,
7
7
  PageSectionView,
8
8
  PageViewContext,
9
9
  usePageView
10
- } from "../chunk-LDXH4FOY.js";
10
+ } from "../chunk-CLP3VWJ2.js";
11
11
  import "../chunk-W6MKRWL6.js";
12
12
  import "../chunk-TT6ENR6T.js";
13
13
  import "../chunk-6PUMLLJH.js";
@@ -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-Y2ANJRT3.js";
15
+ } from "../chunk-HERX7GUP.js";
16
16
  import {
17
17
  BlockRenderer
18
- } from "../chunk-LDXH4FOY.js";
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[];