@bendyline/squisq-react 0.1.3 → 1.0.1

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.
Files changed (139) hide show
  1. package/dist/BlockRenderer.d.ts +37 -0
  2. package/dist/BlockRenderer.d.ts.map +1 -0
  3. package/dist/BlockRenderer.js +51 -0
  4. package/dist/BlockRenderer.js.map +1 -0
  5. package/dist/CaptionOverlay.d.ts +24 -0
  6. package/dist/CaptionOverlay.d.ts.map +1 -0
  7. package/dist/CaptionOverlay.js +40 -0
  8. package/dist/CaptionOverlay.js.map +1 -0
  9. package/dist/DocControlsBottom.d.ts +28 -0
  10. package/dist/DocControlsBottom.d.ts.map +1 -0
  11. package/dist/DocControlsBottom.js +23 -0
  12. package/dist/DocControlsBottom.js.map +1 -0
  13. package/dist/DocControlsOverlay.d.ts +28 -0
  14. package/dist/DocControlsOverlay.d.ts.map +1 -0
  15. package/dist/DocControlsOverlay.js +75 -0
  16. package/dist/DocControlsOverlay.js.map +1 -0
  17. package/dist/DocControlsSidebar.d.ts +27 -0
  18. package/dist/DocControlsSidebar.d.ts.map +1 -0
  19. package/dist/DocControlsSidebar.js +6 -0
  20. package/dist/DocControlsSidebar.js.map +1 -0
  21. package/dist/DocControlsSlideshow.d.ts +20 -0
  22. package/dist/DocControlsSlideshow.d.ts.map +1 -0
  23. package/dist/DocControlsSlideshow.js +69 -0
  24. package/dist/DocControlsSlideshow.js.map +1 -0
  25. package/dist/DocPlayer.d.ts +87 -0
  26. package/dist/DocPlayer.d.ts.map +1 -0
  27. package/dist/DocPlayer.js +604 -0
  28. package/dist/DocPlayer.js.map +1 -0
  29. package/dist/DocPlayerWithSidebar.d.ts +41 -0
  30. package/dist/DocPlayerWithSidebar.d.ts.map +1 -0
  31. package/dist/DocPlayerWithSidebar.js +68 -0
  32. package/dist/DocPlayerWithSidebar.js.map +1 -0
  33. package/dist/DocProgressBar.d.ts +31 -0
  34. package/dist/DocProgressBar.d.ts.map +1 -0
  35. package/dist/DocProgressBar.js +122 -0
  36. package/dist/DocProgressBar.js.map +1 -0
  37. package/dist/LinearDocView.d.ts +47 -0
  38. package/dist/LinearDocView.d.ts.map +1 -0
  39. package/dist/LinearDocView.js +184 -0
  40. package/dist/LinearDocView.js.map +1 -0
  41. package/dist/MarkdownRenderer.d.ts +32 -0
  42. package/dist/MarkdownRenderer.d.ts.map +1 -0
  43. package/dist/MarkdownRenderer.js +133 -0
  44. package/dist/MarkdownRenderer.js.map +1 -0
  45. package/dist/__tests__/BlockRenderer.test.d.ts +2 -0
  46. package/dist/__tests__/BlockRenderer.test.d.ts.map +1 -0
  47. package/dist/__tests__/BlockRenderer.test.js +82 -0
  48. package/dist/__tests__/BlockRenderer.test.js.map +1 -0
  49. package/dist/__tests__/DocControlsSlideshow.test.d.ts +2 -0
  50. package/dist/__tests__/DocControlsSlideshow.test.d.ts.map +1 -0
  51. package/dist/__tests__/DocControlsSlideshow.test.js +88 -0
  52. package/dist/__tests__/DocControlsSlideshow.test.js.map +1 -0
  53. package/dist/__tests__/LinearDocView.test.d.ts +2 -0
  54. package/dist/__tests__/LinearDocView.test.d.ts.map +1 -0
  55. package/dist/__tests__/LinearDocView.test.js +160 -0
  56. package/dist/__tests__/LinearDocView.test.js.map +1 -0
  57. package/dist/__tests__/MarkdownRenderer.test.d.ts +2 -0
  58. package/dist/__tests__/MarkdownRenderer.test.d.ts.map +1 -0
  59. package/dist/__tests__/MarkdownRenderer.test.js +208 -0
  60. package/dist/__tests__/MarkdownRenderer.test.js.map +1 -0
  61. package/dist/__tests__/exports.test.d.ts +2 -0
  62. package/dist/__tests__/exports.test.d.ts.map +1 -0
  63. package/dist/__tests__/exports.test.js +48 -0
  64. package/dist/__tests__/exports.test.js.map +1 -0
  65. package/dist/hooks/AudioProvider.d.ts +71 -0
  66. package/dist/hooks/AudioProvider.d.ts.map +1 -0
  67. package/dist/hooks/AudioProvider.js +52 -0
  68. package/dist/hooks/AudioProvider.js.map +1 -0
  69. package/dist/hooks/MediaContext.d.ts +35 -0
  70. package/dist/hooks/MediaContext.d.ts.map +1 -0
  71. package/dist/hooks/MediaContext.js +66 -0
  72. package/dist/hooks/MediaContext.js.map +1 -0
  73. package/dist/hooks/index.d.ts +6 -0
  74. package/dist/hooks/index.d.ts.map +1 -0
  75. package/dist/hooks/index.js +5 -0
  76. package/dist/hooks/index.js.map +1 -0
  77. package/dist/hooks/useAudioSync.d.ts +18 -0
  78. package/dist/hooks/useAudioSync.d.ts.map +1 -0
  79. package/dist/hooks/useAudioSync.js +338 -0
  80. package/dist/hooks/useAudioSync.js.map +1 -0
  81. package/dist/hooks/useDocPlayback.d.ts +47 -0
  82. package/dist/hooks/useDocPlayback.d.ts.map +1 -0
  83. package/dist/hooks/useDocPlayback.js +176 -0
  84. package/dist/hooks/useDocPlayback.js.map +1 -0
  85. package/dist/hooks/useViewportOrientation.d.ts +31 -0
  86. package/dist/hooks/useViewportOrientation.d.ts.map +1 -0
  87. package/dist/hooks/useViewportOrientation.js +92 -0
  88. package/dist/hooks/useViewportOrientation.js.map +1 -0
  89. package/dist/index.d.ts +25 -563
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +26 -3179
  92. package/dist/index.js.map +1 -1
  93. package/dist/layers/ImageLayer.d.ts +23 -0
  94. package/dist/layers/ImageLayer.d.ts.map +1 -0
  95. package/dist/layers/ImageLayer.js +104 -0
  96. package/dist/layers/ImageLayer.js.map +1 -0
  97. package/dist/layers/MapLayer.d.ts +29 -0
  98. package/dist/layers/MapLayer.d.ts.map +1 -0
  99. package/dist/layers/MapLayer.js +97 -0
  100. package/dist/layers/MapLayer.js.map +1 -0
  101. package/dist/layers/ShapeLayer.d.ts +20 -0
  102. package/dist/layers/ShapeLayer.d.ts.map +1 -0
  103. package/dist/layers/ShapeLayer.js +35 -0
  104. package/dist/layers/ShapeLayer.js.map +1 -0
  105. package/dist/layers/TextLayer.d.ts +23 -0
  106. package/dist/layers/TextLayer.d.ts.map +1 -0
  107. package/dist/layers/TextLayer.js +126 -0
  108. package/dist/layers/TextLayer.js.map +1 -0
  109. package/dist/layers/VideoLayer.d.ts +40 -0
  110. package/dist/layers/VideoLayer.d.ts.map +1 -0
  111. package/dist/layers/VideoLayer.js +105 -0
  112. package/dist/layers/VideoLayer.js.map +1 -0
  113. package/dist/layers/index.d.ts +6 -0
  114. package/dist/layers/index.d.ts.map +1 -0
  115. package/dist/layers/index.js +6 -0
  116. package/dist/layers/index.js.map +1 -0
  117. package/dist/squisq-player.global.js +1 -1
  118. package/dist/standalone-entry.d.ts +61 -0
  119. package/dist/standalone-entry.d.ts.map +1 -0
  120. package/dist/standalone-entry.js +169 -0
  121. package/dist/standalone-entry.js.map +1 -0
  122. package/dist/standalone-source.js +1 -1
  123. package/dist/types.d.ts +131 -0
  124. package/dist/types.d.ts.map +1 -0
  125. package/dist/types.js +21 -0
  126. package/dist/types.js.map +1 -0
  127. package/dist/utils/animationUtils.d.ts +8 -0
  128. package/dist/utils/animationUtils.d.ts.map +1 -0
  129. package/dist/utils/animationUtils.js +8 -0
  130. package/dist/utils/animationUtils.js.map +1 -0
  131. package/dist/utils/layerUtils.d.ts +17 -0
  132. package/dist/utils/layerUtils.d.ts.map +1 -0
  133. package/dist/utils/layerUtils.js +37 -0
  134. package/dist/utils/layerUtils.js.map +1 -0
  135. package/dist/utils/mapTileUtils.d.ts +105 -0
  136. package/dist/utils/mapTileUtils.d.ts.map +1 -0
  137. package/dist/utils/mapTileUtils.js +268 -0
  138. package/dist/utils/mapTileUtils.js.map +1 -0
  139. package/package.json +3 -4
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/DocPlayer.tsx","../src/utils/animationUtils.ts","../src/utils/layerUtils.ts","../src/hooks/MediaContext.tsx","../src/layers/ImageLayer.tsx","../src/layers/TextLayer.tsx","../src/layers/ShapeLayer.tsx","../src/layers/MapLayer.tsx","../src/utils/mapTileUtils.ts","../src/layers/VideoLayer.tsx","../src/BlockRenderer.tsx","../src/CaptionOverlay.tsx","../src/hooks/useAudioSync.ts","../src/hooks/useDocPlayback.ts","../src/hooks/useViewportOrientation.ts","../src/DocProgressBar.tsx","../src/types.ts","../src/DocControlsOverlay.tsx","../src/DocControlsSlideshow.tsx","../src/LinearDocView.tsx","../src/MarkdownRenderer.tsx","../src/DocControlsBottom.tsx","../src/DocControlsSidebar.tsx","../src/DocPlayerWithSidebar.tsx"],"sourcesContent":["/**\n * DocPlayer Component\n *\n * Main component for playing visual stories. Combines audio playback\n * with synchronized SVG block animations. Supports both interactive\n * browser playback and headless rendering for video export.\n *\n * Features:\n * - Audio synchronization with multiple MP3 segments\n * - Block transitions (fade, dissolve, slide)\n * - Playback controls (play/pause, seek, next/prev)\n * - Progress display\n * - Render mode for video capture (via window.seekTo)\n * - Pluggable audio provider for different environments (browser, EFB)\n * - Multiple control layouts: overlay (default), sidebar, bottom\n *\n * Related Files:\n * - DocControlsOverlay.tsx -- Default overlay controls\n * - DocProgressBar.tsx -- Extracted progress bar\n * - DocControlsSidebar.tsx -- Vertical sidebar controls\n * - DocPlayerWithSidebar.tsx -- Wrapper for sidebar layout\n * - types.ts -- Shared control types\n */\n\nimport { Fragment, useRef, useState, useEffect, useCallback, useMemo } from 'react';\nimport type { Doc, Block, TextLayer, StartBlockConfig, DocBlock } from '@bendyline/squisq/schemas';\nimport { isTemplateBlock, getCaptionAtTime } from '@bendyline/squisq/schemas';\nimport type { Theme } from '@bendyline/squisq/schemas';\nimport { BlockRenderer } from './BlockRenderer';\nimport { CaptionOverlay } from './CaptionOverlay';\nimport { useAudioSync } from './hooks/useAudioSync';\nimport { useDocPlayback } from './hooks/useDocPlayback';\nimport { useViewportOrientation } from './hooks/useViewportOrientation';\nimport type { AudioProvider } from './hooks/AudioProvider';\nimport {\n expandCoverBlock,\n createTemplateContext,\n DEFAULT_THEME,\n VIEWPORT_PRESETS,\n type ViewportConfig,\n} from '@bendyline/squisq/doc';\nimport { DocControlsOverlay } from './DocControlsOverlay';\nimport { DocControlsSlideshow } from './DocControlsSlideshow';\nimport { DocProgressBar } from './DocProgressBar';\nimport { LinearDocView } from './LinearDocView';\nimport type {\n PlaybackState,\n PlaybackActions,\n BlockMarker,\n DisplayMode,\n SlideNavActions,\n SquisqWindow,\n} from './types';\n\nconst SMALL_WORDS = new Set([\n 'a',\n 'an',\n 'the',\n 'and',\n 'but',\n 'or',\n 'for',\n 'nor',\n 'on',\n 'at',\n 'to',\n 'in',\n 'of',\n 'by',\n 'is',\n]);\n\n/**\n * Build a map of audio segment index -> display-friendly title.\n * Uses sectionHeader blocks to find real titles, with fallbacks\n * for \"intro\" and slug-based names.\n */\nfunction buildSegmentTitleMap(script: Doc): Map<number, string> {\n const map = new Map<number, string>();\n\n // Scan blocks for sectionHeader templates which carry the real title\n for (const block of script.blocks as DocBlock[]) {\n if (isTemplateBlock(block) && block.template === 'sectionHeader' && 'title' in block) {\n const segIdx = block.audioSegment;\n if (!map.has(segIdx)) {\n map.set(segIdx, (block as { title: string }).title);\n }\n }\n }\n\n // Fill in any segments that weren't covered by sectionHeader blocks\n for (let i = 0; i < script.audio.segments.length; i++) {\n if (!map.has(i)) {\n const name = script.audio.segments[i].name;\n if (name === 'intro' || name.includes('intro')) {\n map.set(i, 'Introduction');\n } else if (name === 'flight-context' || name.includes('flight-context')) {\n map.set(i, 'Flight Context');\n } else {\n // Title-case the slug: \"hands-on-history\" -> \"Hands on History\"\n const words = name.split('-');\n const titled = words\n .map((w, idx) =>\n idx === 0 || !SMALL_WORDS.has(w) ? w.charAt(0).toUpperCase() + w.slice(1) : w,\n )\n .join(' ');\n map.set(i, titled);\n }\n }\n }\n\n return map;\n}\n\ninterface DocPlayerProps {\n /** Doc script to play */\n script: Doc;\n /** Base path for resolving media URLs */\n basePath: string;\n /** Render mode for video capture (hides controls, exposes seekTo) */\n renderMode?: boolean;\n /** Auto-play when loaded */\n autoPlay?: boolean;\n /** Callback when playback ends */\n onEnded?: () => void;\n /** Callback for time updates */\n onTimeUpdate?: (time: number) => void;\n /** Optional audio provider (if not provided, uses default HTML5 audio) */\n audioProvider?: AudioProvider;\n /** Show built-in controls (default: true). Set to false for custom controls. */\n showControls?: boolean;\n /** Show only the progress bar/scrubber at bottom (no other controls).\n * Only takes effect when showControls is false. Allows external controls\n * while keeping the scrubber in-video. */\n showScrubber?: boolean;\n /** Mute audio (default: false) */\n muted?: boolean;\n /** Enable captions (default: true) */\n captionsEnabled?: boolean;\n /** Callback when captions enabled state is toggled */\n onCaptionsToggle?: (enabled: boolean) => void;\n /** Callback for playback state changes (for external controls) */\n onPlaybackStateChange?: (state: PlaybackState) => void;\n /** Callback when playback controls are ready (for external controls) */\n onControlsReady?: (\n controls: PlaybackActions & {\n play: () => void;\n pause: () => void;\n },\n ) => void;\n /** Whether the player is currently in fullscreen mode */\n isFullscreen?: boolean;\n /** Callback to toggle fullscreen mode */\n onFullscreenToggle?: () => void;\n /** Callback when block markers are computed (for external progress bars) */\n onBlockMarkers?: (markers: BlockMarker[]) => void;\n /** Force a specific viewport preset, bypassing window-based orientation detection.\n * Used when the player is rendered in a constrained container (e.g., map overlay panel)\n * whose shape differs from the window's. */\n forceViewport?: ViewportConfig;\n /** Theme to use for rendering (default: DEFAULT_THEME from the theme library) */\n theme?: Theme;\n /**\n * Display mode for the player.\n * - `'video'` (default) — Traditional video playback with play/pause, scrub bar, auto-advance.\n * - `'slideshow'` — PowerPoint-style with prev/next buttons. Blocks are static slides\n * that only change on user click. No auto-advance, no scrub bar.\n * - `'linear'` — Long-scrolling document view. Renders markdown as readable HTML with\n * template-annotated sections as inline SVG cards. No audio, no timeline.\n */\n displayMode?: DisplayMode;\n}\n\nexport function DocPlayer({\n script,\n basePath,\n renderMode = false,\n autoPlay = false,\n onEnded,\n onTimeUpdate,\n audioProvider: externalAudioProvider,\n showControls = true,\n showScrubber = false,\n muted = false,\n captionsEnabled: captionsEnabledProp,\n onCaptionsToggle,\n onPlaybackStateChange,\n onControlsReady,\n isFullscreen = false,\n onFullscreenToggle,\n onBlockMarkers,\n forceViewport,\n displayMode = 'video',\n theme,\n}: DocPlayerProps) {\n const isSlideshowMode = displayMode === 'slideshow';\n const isLinearMode = displayMode === 'linear';\n const audioRef = useRef<HTMLAudioElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Tap-to-toggle play/pause feedback animation\n const [tapFeedback, setTapFeedback] = useState<'play' | 'pause' | null>(null);\n const tapFeedbackTimer = useRef<ReturnType<typeof setTimeout>>();\n\n // Detect viewport orientation for responsive docs\n // forceViewport takes precedence (used by render mode with explicit viewport and constrained panels)\n // In render mode without forceViewport, default to landscape for backward compatibility\n const { viewport, orientation } = useViewportOrientation();\n const activeViewport = forceViewport || (renderMode ? VIEWPORT_PRESETS.landscape : viewport);\n\n // Check for debug mode via URL parameter\n const isDebugMode = useMemo(() => {\n if (typeof window === 'undefined') return false;\n const params = new URLSearchParams(window.location.search);\n return params.get('debug') === 'true';\n }, []);\n\n // Use internal HTML5 audio sync if no external provider is given\n const internalAudio = useAudioSync(audioRef, script.audio, basePath);\n\n // Use external provider if provided, otherwise fall back to internal\n const audio = externalAudioProvider || internalAudio;\n\n // Destructure for convenience\n const {\n currentTime,\n isPlaying,\n currentSegment,\n totalDuration,\n isEnded,\n isReady: isAudioReady,\n isAvailable,\n unavailableMessage,\n play,\n pause,\n toggle,\n seekTo,\n skipToSegment: _skipToSegment,\n restart,\n } = audio;\n\n // Refs for frequently-changing values used in the keyboard handler,\n // so the handler callback doesn't need to be recreated every frame.\n const currentTimeRef = useRef(currentTime);\n currentTimeRef.current = currentTime;\n const totalDurationRef = useRef(totalDuration);\n totalDurationRef.current = totalDuration;\n const expandedBlocksLenRef = useRef(0);\n\n // Tap the player surface to toggle play/pause (disabled in slideshow and linear mode)\n const handleContainerClick = useCallback(\n (e: React.MouseEvent) => {\n if (renderMode || isSlideshowMode || isLinearMode) return;\n const target = e.target as HTMLElement;\n // Don't toggle if user clicked a control element\n if (\n target.closest(\n 'button, a, input, .doc-player__controls, .doc-player__scrubber, .doc-controls-sidebar, .doc-controls-slideshow',\n )\n )\n return;\n toggle();\n // Show visual feedback (show the state we're transitioning TO)\n const nextState = isPlaying ? 'play' : 'pause';\n setTapFeedback(nextState);\n clearTimeout(tapFeedbackTimer.current);\n tapFeedbackTimer.current = setTimeout(() => setTapFeedback(null), 600);\n },\n [renderMode, toggle, isPlaying, isSlideshowMode, isLinearMode],\n );\n\n // Doc playback hook - pass viewport for responsive template expansion\n const {\n currentBlock,\n currentBlockIndex,\n previousBlock,\n isEntering,\n isExiting,\n blockTime,\n blockProgress: _blockProgress,\n docProgress,\n nextBlock: _nextBlock,\n prevBlock: _prevBlock,\n blocks: expandedBlocks,\n } = useDocPlayback(script, currentTime, activeViewport, renderMode, theme);\n\n // Expand cover block (startBlock) if present - uses active viewport\n const coverBlock = useMemo((): Block | null => {\n const startBlockConfig = script.startBlock as StartBlockConfig | undefined;\n if (!startBlockConfig) return null;\n\n const context = createTemplateContext(theme ?? DEFAULT_THEME, 0, 1, activeViewport);\n const layers = expandCoverBlock(startBlockConfig, context);\n\n return {\n id: 'cover-block',\n startTime: -1, // Not part of timeline\n duration: 0, // Static\n audioSegment: -1,\n layers,\n };\n }, [script.startBlock, activeViewport, theme]);\n\n // Render-mode cover block control: allows Playwright to force-show the cover block\n const [coverForced, setCoverForced] = useState(false);\n\n // Grace period: keep cover block visible for 3s after first play press\n const [coverGraceActive, setCoverGraceActive] = useState(false);\n const coverGraceTimer = useRef<ReturnType<typeof setTimeout>>();\n const coverWasShowing = useRef(false);\n\n // Track when cover is showing at rest (before play)\n const atRest = !!(coverBlock && !isPlaying && currentTime === 0 && !renderMode && !autoPlay);\n if (atRest) coverWasShowing.current = true;\n\n useEffect(() => {\n if (isPlaying && coverWasShowing.current && coverBlock && !renderMode) {\n coverWasShowing.current = false;\n setCoverGraceActive(true);\n coverGraceTimer.current = setTimeout(() => setCoverGraceActive(false), 3000);\n return () => clearTimeout(coverGraceTimer.current);\n }\n }, [isPlaying, coverBlock, renderMode]);\n\n // Determine if we should show the cover block\n // Show cover when: has cover block, not playing, at time 0, not in render mode\n // OR during the grace period after first play, OR when coverForced (render mode)\n // Cover block is suppressed in slideshow and linear mode — start directly on content\n const showCoverBlock =\n !isSlideshowMode &&\n !isLinearMode &&\n coverBlock &&\n (coverForced ||\n coverGraceActive ||\n (!isPlaying && currentTime === 0 && !renderMode && !autoPlay));\n\n // Auto-play if enabled (wait for audio to be ready)\n // Use a ref to track if we've already auto-played to avoid repeating on every render\n const hasAutoPlayed = useRef(false);\n useEffect(() => {\n if (isAudioReady && autoPlay && !hasAutoPlayed.current) {\n hasAutoPlayed.current = true;\n play();\n }\n }, [isAudioReady, autoPlay, play]);\n\n // Callback for time updates\n useEffect(() => {\n onTimeUpdate?.(currentTime);\n }, [currentTime, onTimeUpdate]);\n\n // Callback for ended\n useEffect(() => {\n if (isEnded) {\n onEnded?.();\n }\n }, [isEnded, onEnded]);\n\n // Expose seekTo globally for render mode (Playwright) and debug mode (testing)\n useEffect(() => {\n if ((renderMode || isDebugMode) && typeof window !== 'undefined') {\n const w = window as SquisqWindow;\n w.seekTo = (time: number) => {\n seekTo(time);\n // After React renders the correct block, advance CSS animations\n // (Ken Burns, transitions) to match the doc timeline position.\n // Without this, animations restart from zero on each seekTo because\n // they run on the browser's real clock, not doc time.\n return new Promise<void>((resolve) => {\n requestAnimationFrame(() => {\n // Find the current block's start time\n let blockStartTime = 0;\n for (let i = expandedBlocks.length - 1; i >= 0; i--) {\n if (time >= expandedBlocks[i].startTime) {\n blockStartTime = expandedBlocks[i].startTime;\n break;\n }\n }\n const elapsedMs = (time - blockStartTime) * 1000;\n\n // Set all CSS animations to the correct timeline position\n document.getAnimations().forEach((anim) => {\n const target = (anim.effect as KeyframeEffect)?.target as Element | null;\n if (!target) return;\n\n // Animations on the active block: use current block elapsed time\n if (target.closest('.doc-player__block--active')) {\n anim.currentTime = Math.max(0, elapsedMs);\n }\n // Animations on the exiting block (during crossfade): use current\n // block elapsed for transition animations, keep Ken Burns at their\n // natural position based on when that block started\n // eslint-disable-next-line sonarjs/no-duplicated-branches\n else if (target.closest('.doc-player__block--previous')) {\n anim.currentTime = Math.max(0, elapsedMs);\n }\n });\n\n // Seek <video> elements in the active block to the correct clip position.\n // Each <video> carries data-clip-start/data-clip-end attributes set by\n // VideoLayer.tsx; we calculate targetTime = clipStart + blockElapsed.\n const blockElapsed = time - blockStartTime;\n const videoSeekPromises: Promise<void>[] = [];\n const activeBlockEl = document.querySelector('.doc-player__block--active');\n if (activeBlockEl) {\n const videos = activeBlockEl.querySelectorAll('video[data-clip-start]');\n videos.forEach((el) => {\n const video = el as HTMLVideoElement;\n const clipStart = parseFloat(video.dataset.clipStart || '0');\n const clipEnd = parseFloat(video.dataset.clipEnd || '0');\n const targetTime = Math.min(clipStart + Math.max(0, blockElapsed), clipEnd);\n\n video.pause();\n video.currentTime = targetTime;\n\n videoSeekPromises.push(\n new Promise<void>((r) => {\n if (Math.abs(video.currentTime - targetTime) < 0.1) {\n r();\n } else {\n video.addEventListener('seeked', () => r(), { once: true });\n setTimeout(r, 200); // Fallback if seeked never fires\n }\n }),\n );\n });\n }\n\n // Wait for video seeks + one more frame for the browser to render\n Promise.all(videoSeekPromises).then(() => {\n requestAnimationFrame(() => resolve());\n });\n });\n });\n };\n w.getDuration = () => totalDuration;\n // Expose block metadata for testing -- allows tests to find specific templates\n w.getBlocks = () =>\n expandedBlocks.map((s: Block) => ({\n id: s.id,\n template: (s as DocBlock).template ?? 'raw',\n startTime: s.startTime,\n duration: s.duration,\n }));\n // Audio segment info for video production -- returns the actual files in composition order\n w.getAudioSegments = () =>\n script.audio.segments.map((seg) => ({\n src: seg.src,\n name: seg.name,\n duration: seg.duration,\n startTime: seg.startTime,\n }));\n // Caption phrases for SRT/subtitle export\n w.getCaptions = () =>\n script.captions?.phrases?.map((p) => ({\n text: p.text,\n startTime: p.startTime,\n endTime: p.endTime,\n })) || [];\n // Chapter markers for YouTube timestamps -- uses segment titles from sectionHeader blocks\n w.getChapters = () => {\n const titleMap = buildSegmentTitleMap(script);\n return script.audio.segments.map((seg, i) => ({\n title: titleMap.get(i) || seg.name,\n startTime: seg.startTime,\n duration: seg.duration,\n }));\n };\n // Cover block control for video pre-roll -- force-show or hide the cover block\n w.showCover = () => {\n setCoverForced(true);\n return new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n };\n w.hideCover = () => {\n setCoverForced(false);\n return new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n };\n w.hasCoverBlock = () => !!coverBlock;\n }\n return () => {\n if (typeof window !== 'undefined') {\n const w = window as SquisqWindow;\n delete w.seekTo;\n delete w.getDuration;\n delete w.getBlocks;\n delete w.getAudioSegments;\n delete w.getCaptions;\n delete w.getChapters;\n delete w.showCover;\n delete w.hideCover;\n delete w.hasCoverBlock;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- script is a stable prop; re-registering on every script change is unnecessary\n }, [renderMode, isDebugMode, seekTo, totalDuration, expandedBlocks, coverBlock]);\n\n // Captions state: use prop if provided, otherwise default to true\n const captionsEnabled = captionsEnabledProp !== undefined ? captionsEnabledProp : true;\n const setCaptionsEnabled = useCallback(\n (enabled: boolean) => {\n onCaptionsToggle?.(enabled);\n },\n [onCaptionsToggle],\n );\n const hasCaptions = script.captions && script.captions.phrases.length > 0;\n\n // Map segment indices to human-readable titles (from sectionHeader blocks)\n const segmentTitleMap = useMemo(() => buildSegmentTitleMap(script), [script]);\n\n // Build shared playback state for extracted controls\n const playbackState: PlaybackState = useMemo(\n () => ({\n isPlaying,\n currentTime,\n totalDuration,\n currentBlockIndex,\n totalBlocks: expandedBlocks.length,\n docProgress,\n hasCaptions: !!hasCaptions,\n captionsEnabled,\n isFullscreen,\n currentSegmentIndex: currentSegment,\n currentSegmentName:\n segmentTitleMap.get(currentSegment) ?? script.audio.segments[currentSegment]?.name ?? null,\n currentBlock: currentBlock ?? null,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- script.audio.segments is stable within a given script\n [\n isPlaying,\n currentTime,\n totalDuration,\n currentBlockIndex,\n expandedBlocks.length,\n docProgress,\n hasCaptions,\n captionsEnabled,\n isFullscreen,\n currentSegment,\n segmentTitleMap,\n currentBlock,\n ],\n );\n\n // Build shared playback actions for extracted controls\n const playbackActions: PlaybackActions = useMemo(\n () => ({\n toggle,\n restart,\n seekTo,\n setCaptionsEnabled,\n toggleFullscreen: onFullscreenToggle,\n }),\n [toggle, restart, seekTo, setCaptionsEnabled, onFullscreenToggle],\n );\n\n // Slide navigation actions for slideshow mode\n // These seek to the target block's startTime and keep the player paused.\n const slideNavActions: SlideNavActions = useMemo(\n () => ({\n nextSlide: () => {\n if (currentBlockIndex < expandedBlocks.length - 1) {\n const target = expandedBlocks[currentBlockIndex + 1];\n if (target) {\n seekTo(target.startTime);\n pause();\n }\n }\n },\n prevSlide: () => {\n if (currentBlockIndex > 0) {\n const target = expandedBlocks[currentBlockIndex - 1];\n if (target) {\n seekTo(target.startTime);\n pause();\n }\n }\n },\n goToSlide: (index: number) => {\n if (index >= 0 && index < expandedBlocks.length) {\n const target = expandedBlocks[index];\n if (target) {\n seekTo(target.startTime);\n pause();\n }\n }\n },\n }),\n [currentBlockIndex, expandedBlocks, seekTo, pause],\n );\n\n // Callback for playback state changes (for external controls)\n useEffect(() => {\n onPlaybackStateChange?.(playbackState);\n }, [playbackState, onPlaybackStateChange]);\n\n // Callback when controls are ready (for external controls)\n // Fires every time playbackActions change so the external sidebar always holds\n // fresh function references (toggle closes over isPlaying, so it changes often).\n useEffect(() => {\n onControlsReady?.({ play, pause, ...playbackActions });\n }, [play, pause, playbackActions, onControlsReady]);\n\n // Extract display title from a block (handles both template and expanded blocks)\n const getBlockTitle = useCallback((block: Block): string => {\n // For template blocks, extract title from template-specific properties first\n const docBlock = block as DocBlock;\n if (isTemplateBlock(docBlock)) {\n const props = docBlock as unknown as Record<string, unknown>;\n if (typeof props.title === 'string') return props.title;\n if (typeof props.stat === 'string') return props.stat;\n if (typeof props.quote === 'string') {\n const firstLine = props.quote.split('\\n')[0];\n if (firstLine.length <= 30) return firstLine;\n return firstLine.slice(0, 27) + '...';\n }\n if (typeof props.date === 'string') return props.date;\n if (typeof props.fact === 'string') return props.fact;\n }\n\n // For expanded blocks with layers, try to find text content\n if (block.layers && Array.isArray(block.layers)) {\n const textLayer = block.layers.find((l): l is TextLayer => l.type === 'text');\n if (textLayer?.content?.text) {\n // Get first line of text, truncate if too long\n const firstLine = textLayer.content.text.split('\\n')[0];\n if (firstLine.length <= 30) return firstLine;\n return firstLine.slice(0, 27) + '...';\n }\n }\n\n // Fallback to formatted id\n return block.id.replace(/-/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n }, []);\n\n // Compute block markers for progress bar (using expanded blocks)\n const blockMarkers = useMemo(() => {\n if (!totalDuration || !expandedBlocks.length) return [];\n let prevSegment = -1;\n return expandedBlocks.map((block, index) => {\n const isSectionStart = block.audioSegment !== prevSegment;\n prevSegment = block.audioSegment;\n return {\n block,\n index,\n position: (block.startTime / totalDuration) * 100,\n title: getBlockTitle(block),\n isSectionStart,\n };\n });\n }, [expandedBlocks, totalDuration, getBlockTitle]);\n\n // Notify parent when block markers are computed\n useEffect(() => {\n if (blockMarkers.length > 0) {\n onBlockMarkers?.(blockMarkers);\n }\n }, [blockMarkers, onBlockMarkers]);\n\n // Keep expandedBlocks length in a ref so keyboard handler stays stable\n expandedBlocksLenRef.current = expandedBlocks.length;\n\n // Handle keyboard controls — uses refs for frequently-changing values\n // (currentTime, totalDuration, expandedBlocks.length) to avoid\n // re-registering the event listener on every animation frame.\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n // Don't capture keyboard events when focus is on an input/textarea\n const activeEl = document.activeElement;\n if (\n activeEl &&\n (activeEl.tagName === 'INPUT' ||\n activeEl.tagName === 'TEXTAREA' ||\n activeEl.tagName === 'SELECT')\n ) {\n return;\n }\n\n // Linear mode: no keyboard shortcuts (native scrolling handles it)\n if (isLinearMode) return;\n\n if (isSlideshowMode) {\n // Slideshow mode: arrow keys navigate slides\n switch (e.key) {\n case 'ArrowRight':\n case 'ArrowDown':\n case ' ':\n e.preventDefault();\n slideNavActions.nextSlide();\n break;\n case 'ArrowLeft':\n case 'ArrowUp':\n e.preventDefault();\n slideNavActions.prevSlide();\n break;\n case 'Home':\n e.preventDefault();\n slideNavActions.goToSlide(0);\n break;\n case 'End':\n e.preventDefault();\n slideNavActions.goToSlide(expandedBlocksLenRef.current - 1);\n break;\n }\n } else {\n // Video mode: standard playback controls\n switch (e.key) {\n case ' ':\n e.preventDefault();\n toggle();\n break;\n case 'ArrowRight':\n seekTo(Math.min(currentTimeRef.current + 10, totalDurationRef.current));\n break;\n case 'ArrowLeft':\n seekTo(Math.max(currentTimeRef.current - 10, 0));\n break;\n }\n }\n },\n [isSlideshowMode, isLinearMode, toggle, seekTo, slideNavActions],\n );\n\n useEffect(() => {\n if (renderMode) return; // No keyboard in render mode\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [handleKeyDown, renderMode]);\n\n // ── Linear mode: render as scrollable document ──────────────────\n if (isLinearMode) {\n return (\n <div\n ref={containerRef}\n className=\"doc-player doc-player--linear\"\n style={{\n position: 'relative',\n width: '100%',\n height: '100%',\n overflow: 'hidden',\n }}\n >\n <LinearDocView doc={script} basePath={basePath} viewport={activeViewport} />\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className=\"doc-player\"\n onClick={handleContainerClick}\n style={{\n position: 'relative',\n width: '100%',\n aspectRatio: `${activeViewport.width} / ${activeViewport.height}`,\n margin: '0 auto',\n overflow: 'hidden',\n cursor: renderMode ? undefined : 'pointer',\n }}\n >\n {/* Hidden audio element */}\n <audio ref={audioRef} preload=\"auto\" muted={muted} />\n\n {/* Block viewport */}\n <div className=\"doc-player__viewport\">\n {/* Cover block (shown at rest before playback) */}\n {showCoverBlock && coverBlock && (\n <div className=\"doc-player__block doc-player__block--cover\">\n <BlockRenderer\n block={coverBlock}\n blockTime={0}\n basePath={basePath}\n isEntering={false}\n viewport={activeViewport}\n />\n </div>\n )}\n\n {/* Previous block (during transition) */}\n {!showCoverBlock && previousBlock && isExiting && (\n <div className=\"doc-player__block doc-player__block--previous\">\n <BlockRenderer\n block={previousBlock}\n blockTime={blockTime}\n basePath={basePath}\n isExiting={true}\n viewport={activeViewport}\n />\n </div>\n )}\n\n {/* Current block */}\n {!showCoverBlock && currentBlock && (\n <div className=\"doc-player__block doc-player__block--active\">\n <BlockRenderer\n block={currentBlock}\n blockTime={blockTime}\n basePath={basePath}\n isEntering={isEntering}\n viewport={activeViewport}\n isPlaying={isPlaying}\n />\n </div>\n )}\n\n {/* Caption overlay -- hidden in render mode and when stopped on cover block */}\n {hasCaptions && !renderMode && (\n <CaptionOverlay\n captions={script.captions}\n currentTime={currentTime}\n enabled={captionsEnabled && (isPlaying || currentTime > 0)}\n fontSize={16}\n />\n )}\n\n {/* Debug overlay (when ?debug=true) */}\n {isDebugMode && (\n <div\n className=\"doc-player__debug\"\n style={{\n position: 'absolute',\n top: '8px',\n right: '8px',\n padding: '8px 12px',\n background: 'rgba(0, 0, 0, 0.85)',\n borderRadius: '6px',\n color: '#00ff00',\n fontFamily: 'monospace',\n fontSize: '11px',\n lineHeight: '1.5',\n zIndex: 200,\n maxWidth: '280px',\n pointerEvents: 'none',\n textAlign: 'left',\n }}\n >\n <div style={{ color: '#ffcc00', fontWeight: 'bold', marginBottom: '4px' }}>\n DEBUG MODE\n </div>\n <div>\n <span style={{ color: '#888' }}>template:</span>{' '}\n <span style={{ color: '#ff6b6b' }}>\n {(currentBlock as DocBlock | null)?.template ?? 'raw'}\n </span>\n </div>\n <div>\n <span style={{ color: '#888' }}>block:</span> {currentBlockIndex + 1}/\n {expandedBlocks.length}{' '}\n <span style={{ color: '#666' }}>({currentBlock?.id || 'none'})</span>\n </div>\n <div>\n <span style={{ color: '#888' }}>time:</span> {currentTime.toFixed(2)}s /{' '}\n {totalDuration.toFixed(1)}s\n </div>\n <div>\n <span style={{ color: '#888' }}>blockTime:</span> {blockTime.toFixed(2)}s /{' '}\n {(currentBlock?.duration || 0).toFixed(1)}s\n </div>\n <div>\n <span style={{ color: '#888' }}>segment:</span> {currentSegment}/\n {script.audio.segments.length - 1}{' '}\n <span style={{ color: '#666' }}>\n ({script.audio.segments[currentSegment]?.name || 'none'})\n </span>\n </div>\n <div>\n <span style={{ color: '#888' }}>viewport:</span>{' '}\n {activeViewport.name || `${activeViewport.width}x${activeViewport.height}`}{' '}\n <span style={{ color: '#666' }}>({orientation})</span>\n </div>\n <div>\n <span style={{ color: '#888' }}>playing:</span>{' '}\n <span style={{ color: isPlaying ? '#4ade80' : '#f87171' }}>\n {isPlaying ? 'yes' : 'no'}\n </span>\n {showCoverBlock && <span style={{ color: '#60a5fa' }}> (cover)</span>}\n </div>\n {hasCaptions &&\n (() => {\n const debugPhrase = getCaptionAtTime(script.captions!, currentTime);\n const debugEnabled = captionsEnabled && (isPlaying || currentTime > 0);\n return (\n <Fragment>\n <div>\n <span style={{ color: '#888' }}>captions:</span>{' '}\n {script.captions?.phrases.length || 0} phrases{' '}\n <span style={{ color: captionsEnabled ? '#4ade80' : '#666' }}>\n ({captionsEnabled ? 'on' : 'off'})\n </span>\n </div>\n <div>\n <span style={{ color: '#888' }}>cc.enabled:</span>{' '}\n <span style={{ color: debugEnabled ? '#4ade80' : '#f87171' }}>\n {String(debugEnabled)}\n </span>{' '}\n <span style={{ color: '#666' }}>\n (playing={String(isPlaying)} t&gt;0={String(currentTime > 0)})\n </span>\n </div>\n <div>\n <span style={{ color: '#888' }}>cc.phrase:</span>{' '}\n <span style={{ color: debugPhrase ? '#4ade80' : '#f87171' }}>\n {debugPhrase ? `\"${debugPhrase.text.slice(0, 30)}...\"` : 'null'}\n </span>\n </div>\n {debugPhrase && (\n <div>\n <span style={{ color: '#888' }}>cc.range:</span>{' '}\n <span style={{ color: '#60a5fa' }}>\n {debugPhrase.startTime.toFixed(2)}-{debugPhrase.endTime.toFixed(2)}\n </span>\n </div>\n )}\n </Fragment>\n );\n })()}\n </div>\n )}\n </div>\n\n {/* Audio unavailable overlay */}\n {!isAvailable && unavailableMessage && (\n <div\n className=\"doc-player__unavailable\"\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexDirection: 'column',\n gap: '16px',\n background: 'rgba(0, 0, 0, 0.7)',\n color: 'rgba(255, 255, 255, 0.9)',\n fontSize: '14px',\n zIndex: 50,\n }}\n >\n <span style={{ fontSize: '32px' }}>&#x1F50A;</span>\n <span>{unavailableMessage}</span>\n </div>\n )}\n\n {/* Full overlay controls (default video layout) */}\n {!renderMode && !isSlideshowMode && showControls && (\n <DocControlsOverlay\n state={playbackState}\n actions={playbackActions}\n blockMarkers={blockMarkers}\n expandedBlocks={expandedBlocks}\n getBlockTitle={getBlockTitle}\n />\n )}\n\n {/* Scrubber-only mode (for sidebar/bottom layouts where other controls are external) */}\n {!renderMode && !isSlideshowMode && !showControls && showScrubber && (\n <div\n className=\"doc-player__scrubber\"\n style={{\n position: 'absolute',\n bottom: 0,\n left: 0,\n right: 0,\n padding: '12px 16px 8px',\n background: 'linear-gradient(transparent, rgba(0,0,0,0.6))',\n display: 'flex',\n alignItems: 'center',\n zIndex: 100,\n }}\n >\n <DocProgressBar\n state={playbackState}\n actions={playbackActions}\n blockMarkers={blockMarkers}\n expandedBlocks={expandedBlocks}\n getBlockTitle={getBlockTitle}\n />\n </div>\n )}\n\n {/* Slideshow controls (prev / counter / next) */}\n {!renderMode && isSlideshowMode && (\n <DocControlsSlideshow state={playbackState} slideNav={slideNavActions} />\n )}\n\n {/* Tap feedback animation -- shows play/pause icon briefly on tap (video mode only) */}\n {!isSlideshowMode && tapFeedback && (\n <div className=\"doc-player__tap-feedback\" key={Date.now()}>\n <svg viewBox=\"0 0 24 24\" fill=\"white\" width=\"48\" height=\"48\">\n {tapFeedback === 'pause' ? (\n <path d=\"M6 19h4V5H6v14zm8-14v14h4V5h-4z\" />\n ) : (\n <path d=\"M8 5v14l11-7z\" />\n )}\n </svg>\n </div>\n )}\n </div>\n );\n}\n\nexport default DocPlayer;\n","/**\n * Animation Utilities (React)\n *\n * Re-exports the shared animation utilities for use in the React package.\n * All animation logic is in shared/doc/utils.\n */\n\nexport {\n getAnimationStyle,\n getDefaultAnimationDuration,\n getTransitionClass,\n getAnimationProgress,\n} from '@bendyline/squisq/doc';\n","/**\n * Shared utilities for layer components.\n *\n * Resolves position values and anchor offsets used by all layer types.\n */\n\n/**\n * Resolve a position value (number or percentage string) to pixels.\n */\nexport function resolveValue(value: number | string, dimension: number): number {\n if (typeof value === 'number') {\n return value;\n }\n if (value.endsWith('%')) {\n const percent = parseFloat(value);\n return (percent / 100) * dimension;\n }\n return parseFloat(value);\n}\n\n/**\n * Get offset based on anchor point.\n */\nexport function getAnchorOffset(\n anchor: string | undefined,\n width: number,\n height: number,\n): { x: number; y: number } {\n switch (anchor) {\n case 'center':\n return { x: -width / 2, y: -height / 2 };\n case 'top-right':\n return { x: -width, y: 0 };\n case 'bottom-left':\n return { x: 0, y: -height };\n case 'bottom-right':\n return { x: -width, y: -height };\n case 'top-left':\n default:\n return { x: 0, y: 0 };\n }\n}\n","/**\n * MediaContext — React context for providing a MediaProvider to layer components.\n *\n * When a MediaProvider is available in context, layer components (ImageLayer,\n * VideoLayer) will use it to resolve media URLs instead of the basePath prop.\n * This enables slot-based media storage where binary assets are served from\n * IndexedDB as blob URLs.\n *\n * Usage:\n * const provider = createSlotMediaProvider(slotId);\n * <MediaContext.Provider value={provider}>\n * <DocPlayer doc={doc} basePath=\"/\" />\n * </MediaContext.Provider>\n */\n\nimport { createContext, useContext, useState, useEffect, useMemo } from 'react';\nimport type { MediaProvider } from '@bendyline/squisq/schemas';\n\n/**\n * React context holding the current MediaProvider (or null if none provided).\n */\nexport const MediaContext = createContext<MediaProvider | null>(null);\n\n/**\n * Hook to access the current MediaProvider from context.\n * Returns null if no provider is set.\n */\nexport function useMediaProvider(): MediaProvider | null {\n return useContext(MediaContext);\n}\n\n/**\n * Hook to resolve a media URL via the MediaProvider (if available),\n * falling back to basePath-based resolution.\n *\n * Returns the resolved URL string. Updates when the provider or path changes.\n *\n * @param relativePath - Relative media path from the document (e.g., 'hero.jpg')\n * @param basePath - Fallback base path for URL construction\n */\nexport function useMediaUrl(relativePath: string, basePath: string): string {\n const provider = useMediaProvider();\n\n // For absolute/http URLs, skip resolution entirely\n const isAbsolute =\n relativePath.startsWith('http') ||\n relativePath.startsWith('/') ||\n relativePath.startsWith('data:') ||\n relativePath.startsWith('blob:');\n\n // Memoize fallback to avoid recalculating on every render\n const fallback = useMemo(\n () => (isAbsolute ? relativePath : `${basePath}/${relativePath}`),\n [isAbsolute, relativePath, basePath],\n );\n\n // Fast path: no provider or absolute URL — return synchronously, skip effect entirely\n const needsProvider = !isAbsolute && !!provider;\n\n const [url, setUrl] = useState(fallback);\n\n useEffect(() => {\n if (!needsProvider) {\n setUrl(fallback);\n return;\n }\n\n let cancelled = false;\n provider!.resolveUrl(relativePath).then((resolved) => {\n if (!cancelled) setUrl(resolved);\n });\n\n return () => {\n cancelled = true;\n };\n }, [needsProvider, provider, relativePath, fallback]);\n\n // When provider is not needed, return fallback directly to avoid\n // the one-frame delay from the initial useState → useEffect cycle\n return needsProvider ? url : fallback;\n}\n","/**\n * ImageLayer Component\n *\n * Renders an image layer within an SVG block. Supports Ken Burns and other\n * animations via CSS classes. Images are rendered using SVG <image> element\n * with proper aspect ratio handling.\n */\n\nimport type { ImageLayer as ImageLayerType, Animation } from '@bendyline/squisq/schemas';\nimport { getAnimationStyle } from '../utils/animationUtils';\nimport { resolveValue, getAnchorOffset } from '../utils/layerUtils';\nimport { useMediaUrl } from '../hooks/MediaContext';\n\ninterface ImageLayerProps {\n layer: ImageLayerType;\n /** Base path for resolving relative image URLs */\n basePath: string;\n /** Viewport dimensions for percentage calculations */\n viewport: { width: number; height: number };\n /** Current time relative to block start (for animation timing) */\n blockTime: number;\n}\n\nexport function ImageLayer({ layer, basePath, viewport, blockTime }: ImageLayerProps) {\n const { content, position, animation } = layer;\n\n // Resolve position values to pixels\n const x = resolveValue(position.x, viewport.width);\n const y = resolveValue(position.y, viewport.height);\n const width = position.width ? resolveValue(position.width, viewport.width) : viewport.width;\n const height = position.height ? resolveValue(position.height, viewport.height) : viewport.height;\n\n // Apply anchor offset\n const offset = getAnchorOffset(position.anchor, width, height);\n const finalX = x + offset.x;\n const finalY = y + offset.y;\n\n // Resolve image URL via MediaProvider (if available), falling back to basePath\n const src = useMediaUrl(content.src, basePath);\n\n // Get animation styles\n const animStyle = getAnimationStyle(animation, blockTime);\n\n // SVG preserveAspectRatio based on fit mode\n const preserveAspectRatio = getPreserveAspectRatio(content.fit);\n const isCover = content.fit === 'cover';\n\n // Detect spatial (transform-based) animations that need Ken Burns treatment.\n // These animations should move the image *content* within fixed bounds rather\n // than shifting the entire image container.\n const isSpatialAnim = animation && SPATIAL_ANIMATION_TYPES.has(animation.type);\n\n // Ken Burns mode: cover image with spatial animation.\n // Keep the container static, animate the inner <img> within clipped bounds.\n if (isCover && isSpatialAnim && animation) {\n const kbAnim = remapToKenBurns(animation);\n const kbStyle = getAnimationStyle(kbAnim, blockTime);\n\n return (\n <g className=\"block-layer block-layer--image\" data-layer-id={layer.id}>\n <foreignObject x={finalX} y={finalY} width={width} height={height}>\n <div\n style={{\n width: '100%',\n height: '100%',\n overflow: 'hidden',\n }}\n >\n <img\n src={src}\n alt={content.alt || ''}\n className={kbStyle.className}\n style={{\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n objectPosition: 'center',\n display: 'block',\n pointerEvents: 'none',\n transformOrigin: 'center center',\n ...kbStyle.style,\n }}\n />\n </div>\n </foreignObject>\n </g>\n );\n }\n\n // For cover mode, use foreignObject with CSS object-fit for more reliable coverage\n // SVG's preserveAspectRatio can be inconsistent across browsers\n if (isCover) {\n return (\n <g\n className={`block-layer block-layer--image ${animStyle.className}`}\n style={animStyle.style}\n data-layer-id={layer.id}\n >\n <foreignObject x={finalX} y={finalY} width={width} height={height}>\n <img\n src={src}\n alt={content.alt || ''}\n style={{\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n objectPosition: 'center',\n display: 'block',\n pointerEvents: 'none',\n }}\n />\n </foreignObject>\n </g>\n );\n }\n\n // For contain/fill modes, use SVG image element\n return (\n <g\n className={`block-layer block-layer--image ${animStyle.className}`}\n style={animStyle.style}\n data-layer-id={layer.id}\n >\n <image\n href={src}\n x={finalX}\n y={finalY}\n width={width}\n height={height}\n preserveAspectRatio={preserveAspectRatio}\n style={{ pointerEvents: 'none' }}\n />\n </g>\n );\n}\n\n/**\n * Map fit mode to SVG preserveAspectRatio.\n */\nfunction getPreserveAspectRatio(fit?: 'cover' | 'contain' | 'fill'): string {\n switch (fit) {\n case 'cover':\n return 'xMidYMid slice';\n case 'fill':\n return 'none';\n case 'contain':\n default:\n return 'xMidYMid meet';\n }\n}\n\n// ============================================\n// Ken Burns Helpers\n// ============================================\n\n/** Animation types that use CSS transforms (translate/scale). */\nconst SPATIAL_ANIMATION_TYPES = new Set(['panLeft', 'panRight', 'slowZoom', 'zoomIn', 'zoomOut']);\n\n/**\n * Remap animation for Ken Burns inner-image rendering.\n *\n * Pure pan animations (panLeft/panRight) get remapped to combined slowZoom+pan\n * to maintain minimum scale and avoid revealing gaps at image edges.\n * zoomIn/zoomOut get remapped to slowZoom variants (no opacity change, which\n * is inappropriate for sustained ambient motion).\n */\nfunction remapToKenBurns(anim: Animation): Animation {\n switch (anim.type) {\n case 'panLeft':\n return { ...anim, type: 'slowZoom', panDirection: 'left' };\n case 'panRight':\n return { ...anim, type: 'slowZoom', panDirection: 'right' };\n case 'zoomIn':\n return { ...anim, type: 'slowZoom', direction: 'in' };\n case 'zoomOut':\n return { ...anim, type: 'slowZoom', direction: 'out' };\n default:\n return anim; // slowZoom variants are already correct\n }\n}\n\nexport default ImageLayer;\n","/**\n * TextLayer Component\n *\n * Renders a text layer within an SVG block. Supports multi-line text,\n * styling options (font, color, shadow), and animations like fadeIn\n * and typewriter effects.\n *\n * Text is rendered using SVG <text> elements with <tspan> for line breaks.\n */\n\nimport type { TextLayer as TextLayerType } from '@bendyline/squisq/schemas';\nimport { DEFAULT_DOC_FONT } from '@bendyline/squisq/schemas';\nimport { getAnimationStyle } from '../utils/animationUtils';\nimport { resolveValue } from '../utils/layerUtils';\n\ninterface TextLayerProps {\n layer: TextLayerType;\n /** Viewport dimensions for percentage calculations */\n viewport: { width: number; height: number };\n /** Current time relative to block start */\n blockTime: number;\n}\n\nexport function TextLayer({ layer, viewport, blockTime }: TextLayerProps) {\n const { content, position, animation } = layer;\n const { text, style } = content;\n\n // Resolve position values to pixels\n const x = resolveValue(position.x, viewport.width);\n const y = resolveValue(position.y, viewport.height);\n const maxWidth = position.width ? resolveValue(position.width, viewport.width) : undefined;\n\n // Apply anchor offset for text alignment\n const textAnchor = getTextAnchor(style.textAlign, position.anchor);\n const dominantBaseline = getDominantBaseline(position.anchor);\n\n // Get animation styles\n const animStyle = getAnimationStyle(animation, blockTime);\n\n // Split text into lines, and wrap if maxWidth is specified\n const rawLines = text.split('\\n');\n let lines = maxWidth\n ? rawLines.reduce<string[]>(\n (acc, line) => acc.concat(wrapText(line, style.fontSize, maxWidth)),\n [],\n )\n : rawLines;\n\n // Truncate to maxLines if specified\n if (style.maxLines && lines.length > style.maxLines) {\n lines = lines.slice(0, style.maxLines);\n // Add ellipsis to last visible line\n const last = lines[lines.length - 1];\n lines[lines.length - 1] = last.replace(/\\s*$/, '') + '...';\n }\n const lineHeight = style.lineHeight || 1.4;\n const lineHeightPx = style.fontSize * lineHeight;\n\n // Build text styles\n const textStyles: Record<string, string | number> = {\n fontSize: `${style.fontSize}px`,\n fontFamily: style.fontFamily || DEFAULT_DOC_FONT,\n fontWeight: style.fontWeight || 'normal',\n fill: style.color,\n ...animStyle.style,\n };\n\n // Add shadow filter if requested\n const filterId = style.shadow ? `shadow-${layer.id}` : undefined;\n\n return (\n <g className={`block-layer block-layer--text ${animStyle.className}`} data-layer-id={layer.id}>\n {/* Shadow filter definition */}\n {style.shadow && (\n <defs>\n <filter id={filterId} x=\"-20%\" y=\"-20%\" width=\"140%\" height=\"140%\">\n <feDropShadow dx=\"0\" dy=\"2\" stdDeviation=\"3\" floodColor=\"rgba(0,0,0,0.7)\" />\n </filter>\n </defs>\n )}\n\n {/* Background box if specified */}\n {style.background && (\n <rect\n x={x - (style.padding || 16)}\n y={y - style.fontSize - (style.padding || 16)}\n width={getTextBoxWidth(lines, style) + (style.padding || 16) * 2}\n height={lines.length * lineHeightPx + (style.padding || 16) * 2}\n fill={style.background}\n rx={4}\n ry={4}\n />\n )}\n\n {/* Text element with tspans for each line */}\n <text\n x={x}\n y={y}\n textAnchor={textAnchor as 'start' | 'middle' | 'end'}\n dominantBaseline={dominantBaseline as 'text-before-edge' | 'middle' | 'text-after-edge'}\n style={textStyles}\n filter={filterId ? `url(#${filterId})` : undefined}\n >\n {lines.map((line, i) => (\n <tspan key={i} x={x} dy={i === 0 ? 0 : lineHeightPx}>\n {line || '\\u00A0'} {/* Non-breaking space for empty lines */}\n </tspan>\n ))}\n </text>\n </g>\n );\n}\n\n/**\n * Map text alignment to SVG text-anchor.\n */\nfunction getTextAnchor(align?: 'left' | 'center' | 'right', anchor?: string): string {\n // Explicit alignment takes precedence\n if (align === 'center') return 'middle';\n if (align === 'right') return 'end';\n if (align === 'left') return 'start';\n\n // Otherwise infer from position anchor\n if (anchor?.includes('right')) return 'end';\n if (anchor === 'center') return 'middle';\n return 'start';\n}\n\n/**\n * Map position anchor to SVG dominant-baseline.\n */\nfunction getDominantBaseline(anchor?: string): string {\n if (anchor?.includes('bottom')) return 'text-after-edge';\n if (anchor === 'center') return 'middle';\n return 'text-before-edge';\n}\n\n/**\n * Estimate text box width based on content (rough approximation).\n */\nfunction getTextBoxWidth(lines: string[], style: { fontSize: number }): number {\n const maxLineLength = Math.max(...lines.map((l) => l.length));\n // Rough estimate: average character width is ~0.5 * fontSize\n return maxLineLength * style.fontSize * 0.55;\n}\n\n/**\n * Wrap text to fit within a maximum width.\n * Uses character-based estimation for line breaking.\n */\nfunction wrapText(text: string, fontSize: number, maxWidth: number): string[] {\n if (!text.trim()) return [''];\n\n // Estimate characters per line (average char width ~0.5 * fontSize for most fonts)\n const avgCharWidth = fontSize * 0.5;\n const charsPerLine = Math.floor(maxWidth / avgCharWidth);\n\n if (charsPerLine <= 0) return [text];\n\n const words = text.split(/\\s+/);\n const lines: string[] = [];\n let currentLine = '';\n\n for (const word of words) {\n const testLine = currentLine ? `${currentLine} ${word}` : word;\n\n if (testLine.length <= charsPerLine) {\n currentLine = testLine;\n } else {\n // Current line is full, start new line\n if (currentLine) {\n lines.push(currentLine);\n }\n // Handle words longer than a line\n if (word.length > charsPerLine) {\n // Break long word\n let remaining = word;\n while (remaining.length > charsPerLine) {\n lines.push(remaining.slice(0, charsPerLine));\n remaining = remaining.slice(charsPerLine);\n }\n currentLine = remaining;\n } else {\n currentLine = word;\n }\n }\n }\n\n // Add remaining text\n if (currentLine) {\n lines.push(currentLine);\n }\n\n return lines.length > 0 ? lines : [''];\n}\n\nexport default TextLayer;\n","/**\n * ShapeLayer Component\n *\n * Renders simple geometric shapes (rect, circle, line) within an SVG block.\n * Useful for visual accents, dividers, and background elements.\n */\n\nimport type { ShapeLayer as ShapeLayerType } from '@bendyline/squisq/schemas';\nimport { getAnimationStyle } from '../utils/animationUtils';\nimport { resolveValue } from '../utils/layerUtils';\n\ninterface ShapeLayerProps {\n layer: ShapeLayerType;\n /** Viewport dimensions for percentage calculations */\n viewport: { width: number; height: number };\n /** Current time relative to block start */\n blockTime: number;\n}\n\nexport function ShapeLayer({ layer, viewport, blockTime }: ShapeLayerProps) {\n const { content, position, animation } = layer;\n\n // Resolve position values to pixels\n const x = resolveValue(position.x, viewport.width);\n const y = resolveValue(position.y, viewport.height);\n const width = position.width ? resolveValue(position.width, viewport.width) : 100;\n const height = position.height ? resolveValue(position.height, viewport.height) : 100;\n\n // Get animation styles\n const animStyle = getAnimationStyle(animation, blockTime);\n\n // Check if fill is a CSS gradient (SVG rect doesn't support CSS gradients natively)\n const fill = content.fill || 'none';\n const isCSSGradient = typeof fill === 'string' && fill.includes('gradient(');\n\n // For CSS gradients on rect, use foreignObject with an HTML div\n if (content.shape === 'rect' && isCSSGradient) {\n return (\n <g\n className={`block-layer block-layer--shape ${animStyle.className}`}\n style={animStyle.style}\n data-layer-id={layer.id}\n >\n <foreignObject x={x} y={y} width={width} height={height}>\n <div\n style={{\n width: '100%',\n height: '100%',\n background: fill,\n borderRadius: content.borderRadius ? `${content.borderRadius}px` : undefined,\n pointerEvents: 'none',\n }}\n />\n </foreignObject>\n </g>\n );\n }\n\n // Common style props for native SVG shapes\n const shapeProps = {\n fill: fill,\n stroke: content.stroke,\n strokeWidth: content.strokeWidth,\n };\n\n return (\n <g\n className={`block-layer block-layer--shape ${animStyle.className}`}\n style={animStyle.style}\n data-layer-id={layer.id}\n >\n {content.shape === 'rect' && (\n <rect\n x={x}\n y={y}\n width={width}\n height={height}\n rx={content.borderRadius}\n ry={content.borderRadius}\n {...shapeProps}\n />\n )}\n\n {content.shape === 'circle' && (\n <circle\n cx={x + width / 2}\n cy={y + height / 2}\n r={Math.min(width, height) / 2}\n {...shapeProps}\n />\n )}\n\n {content.shape === 'line' && (\n <line\n x1={x}\n y1={y}\n x2={x + width}\n y2={y + height}\n stroke={content.stroke || '#ffffff'}\n strokeWidth={content.strokeWidth || 2}\n />\n )}\n </g>\n );\n}\n\nexport default ShapeLayer;\n","/**\n * MapLayer Component\n *\n * Renders a geographic map layer within an SVG block. Maps are composed from\n * static tile images fetched from free/open-source providers.\n *\n * For video export reliability, maps can use pre-rendered static images via\n * the staticSrc property, avoiding tile loading race conditions during capture.\n *\n * Tile fetching: Tiles are loaded on mount and composited into a data URL\n * for SVG embedding. This ensures correct rendering in both browser and\n * Playwright screenshot contexts.\n */\n\nimport { useState, useEffect } from 'react';\nimport type { MapLayer as MapLayerType } from '@bendyline/squisq/schemas';\nimport { getAnimationStyle } from '../utils/animationUtils';\nimport { resolveValue, getAnchorOffset } from '../utils/layerUtils';\nimport { composeMapImage } from '../utils/mapTileUtils';\n\ninterface MapLayerProps {\n layer: MapLayerType;\n /** Base path for resolving relative image URLs */\n basePath: string;\n /** Viewport dimensions for percentage calculations */\n viewport: { width: number; height: number };\n /** Current time relative to block start (for animation timing) */\n blockTime: number;\n}\n\nexport function MapLayer({ layer, basePath, viewport, blockTime }: MapLayerProps) {\n const { content, position, animation } = layer;\n const [mapImageUrl, setMapImageUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n // Resolve position values to pixels\n const x = resolveValue(position.x, viewport.width);\n const y = resolveValue(position.y, viewport.height);\n const width = position.width ? resolveValue(position.width, viewport.width) : viewport.width;\n const height = position.height ? resolveValue(position.height, viewport.height) : viewport.height;\n\n // Apply anchor offset\n const offset = getAnchorOffset(position.anchor, width, height);\n const finalX = x + offset.x;\n const finalY = y + offset.y;\n\n // Use static image if provided, otherwise fetch and compose tiles\n useEffect(() => {\n let cancelled = false;\n\n if (content.staticSrc) {\n // Use pre-rendered static image\n const src = content.staticSrc.startsWith('http')\n ? content.staticSrc\n : `${basePath}/${content.staticSrc}`;\n setMapImageUrl(src);\n setIsLoading(false);\n return;\n }\n\n // Compose map from tiles\n setIsLoading(true);\n setError(null);\n\n composeMapImage({\n center: content.center,\n zoom: content.zoom,\n style: content.style,\n width,\n height,\n markers: content.markers,\n showAttribution: content.showAttribution !== false,\n })\n .then((dataUrl) => {\n if (!cancelled) {\n setMapImageUrl(dataUrl);\n setIsLoading(false);\n }\n })\n .catch((err: unknown) => {\n if (!cancelled) {\n console.error('Failed to compose map:', err);\n setError(err instanceof Error ? err.message : String(err));\n setIsLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- content properties are destructured below; center/markers/showAttribution are stable per-render\n }, [\n content.center.lat,\n content.center.lng,\n content.zoom,\n content.style,\n content.staticSrc,\n width,\n height,\n basePath,\n ]);\n\n // Get animation styles\n const animStyle = getAnimationStyle(animation, blockTime);\n\n // Render loading state\n if (isLoading) {\n return (\n <g\n className={`block-layer block-layer--map ${animStyle.className}`}\n style={animStyle.style}\n data-layer-id={layer.id}\n >\n <rect x={finalX} y={finalY} width={width} height={height} fill=\"#e5e7eb\" />\n <text\n x={finalX + width / 2}\n y={finalY + height / 2}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fill=\"#9ca3af\"\n fontSize=\"24\"\n fontFamily=\"system-ui, sans-serif\"\n >\n Loading map...\n </text>\n </g>\n );\n }\n\n // Render error state\n if (error || !mapImageUrl) {\n return (\n <g\n className={`block-layer block-layer--map ${animStyle.className}`}\n style={animStyle.style}\n data-layer-id={layer.id}\n >\n <rect x={finalX} y={finalY} width={width} height={height} fill=\"#fef2f2\" />\n <text\n x={finalX + width / 2}\n y={finalY + height / 2}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fill=\"#dc2626\"\n fontSize=\"18\"\n fontFamily=\"system-ui, sans-serif\"\n >\n Map failed to load\n </text>\n </g>\n );\n }\n\n return (\n <g\n className={`block-layer block-layer--map ${animStyle.className}`}\n style={animStyle.style}\n data-layer-id={layer.id}\n >\n {/* Clip path for overflow handling */}\n <defs>\n <clipPath id={`clip-${layer.id}`}>\n <rect x={finalX} y={finalY} width={width} height={height} />\n </clipPath>\n </defs>\n\n {/* Map image */}\n <g clipPath={`url(#clip-${layer.id})`}>\n <image\n href={mapImageUrl}\n x={finalX}\n y={finalY}\n width={width}\n height={height}\n preserveAspectRatio=\"xMidYMid slice\"\n style={{ pointerEvents: 'none' }}\n />\n </g>\n </g>\n );\n}\n\nexport default MapLayer;\n","/**\n * Map Tile Utilities\n *\n * Functions for fetching and composing map tiles from free/open-source providers.\n * Converts lat/lng to tile coordinates and composites multiple tiles into a\n * single image for SVG embedding.\n *\n * Supported Providers (all free with attribution):\n * - terrain: OpenTopoMap (CC-BY-SA)\n * - road: OpenStreetMap (ODbL)\n * - satellite: ESRI World Imagery (free with attribution)\n * - toner: Stadia/Stamen Toner (free tier)\n * - watercolor: Stadia/Stamen Watercolor (free tier)\n *\n * See docs/MAP_TILES.md for full provider details and terms.\n */\n\nimport type { MapTileStyle, MapMarker } from '@bendyline/squisq/schemas';\n\n/**\n * Tile provider configuration.\n */\nexport interface TileProvider {\n /** URL template with {z}, {x}, {y} placeholders */\n url: string;\n /** Attribution text (required for display) */\n attribution: string;\n /** Maximum zoom level */\n maxZoom: number;\n /** Tile size in pixels (default: 256) */\n tileSize?: number;\n}\n\n/**\n * Free tile providers for each map style.\n * All require attribution to be displayed.\n */\nexport const TILE_PROVIDERS: Record<MapTileStyle, TileProvider> = {\n terrain: {\n url: 'https://tile.opentopomap.org/{z}/{x}/{y}.png',\n attribution: 'Map: OpenTopoMap (CC-BY-SA)',\n maxZoom: 17,\n },\n road: {\n url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',\n attribution: '© OpenStreetMap contributors',\n maxZoom: 19,\n },\n satellite: {\n url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n attribution: 'Imagery: Esri, Maxar, Earthstar',\n maxZoom: 18,\n },\n toner: {\n url: 'https://tiles.stadiamaps.com/tiles/stamen_toner/{z}/{x}/{y}.png',\n attribution: 'Map: Stadia Maps, Stamen Design',\n maxZoom: 20,\n },\n watercolor: {\n url: 'https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg',\n attribution: 'Map: Stadia Maps, Stamen Design',\n maxZoom: 16,\n },\n};\n\n/**\n * Convert latitude/longitude to tile coordinates at a given zoom level.\n * Uses Web Mercator projection (EPSG:3857).\n */\nexport function latLngToTile(lat: number, lng: number, zoom: number): { x: number; y: number } {\n const n = Math.pow(2, zoom);\n const x = Math.floor(((lng + 180) / 360) * n);\n const latRad = (lat * Math.PI) / 180;\n const y = Math.floor(((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2) * n);\n return { x, y };\n}\n\n/**\n * Convert tile coordinates back to lat/lng (top-left corner of tile).\n */\nexport function tileToLatLng(x: number, y: number, zoom: number): { lat: number; lng: number } {\n const n = Math.pow(2, zoom);\n const lng = (x / n) * 360 - 180;\n const latRad = Math.atan(Math.sinh(Math.PI * (1 - (2 * y) / n)));\n const lat = (latRad * 180) / Math.PI;\n return { lat, lng };\n}\n\n/**\n * Get the pixel offset within a tile for a given lat/lng.\n */\nexport function getPixelOffset(\n lat: number,\n lng: number,\n zoom: number,\n tileSize: number = 256,\n): { x: number; y: number } {\n const n = Math.pow(2, zoom);\n const xTile = ((lng + 180) / 360) * n;\n const latRad = (lat * Math.PI) / 180;\n const yTile = ((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2) * n;\n\n return {\n x: (xTile - Math.floor(xTile)) * tileSize,\n y: (yTile - Math.floor(yTile)) * tileSize,\n };\n}\n\n/**\n * Calculate which tiles are needed to cover a viewport centered on lat/lng.\n */\nexport function getTilesForViewport(\n centerLat: number,\n centerLng: number,\n zoom: number,\n viewportWidth: number,\n viewportHeight: number,\n tileSize: number = 256,\n): Array<{ x: number; y: number; screenX: number; screenY: number }> {\n const centerTile = latLngToTile(centerLat, centerLng, zoom);\n const pixelOffset = getPixelOffset(centerLat, centerLng, zoom, tileSize);\n\n // How many tiles we need in each direction\n const tilesX = Math.ceil(viewportWidth / tileSize) + 1;\n const tilesY = Math.ceil(viewportHeight / tileSize) + 1;\n\n // Start tile offset\n const startX = centerTile.x - Math.floor(tilesX / 2);\n const startY = centerTile.y - Math.floor(tilesY / 2);\n\n // Screen position of the center tile\n const centerScreenX = viewportWidth / 2 - pixelOffset.x;\n const centerScreenY = viewportHeight / 2 - pixelOffset.y;\n\n const tiles: Array<{ x: number; y: number; screenX: number; screenY: number }> = [];\n\n for (let dy = 0; dy < tilesY; dy++) {\n for (let dx = 0; dx < tilesX; dx++) {\n const tileX = startX + dx;\n const tileY = startY + dy;\n const screenX = centerScreenX + (tileX - centerTile.x) * tileSize;\n const screenY = centerScreenY + (tileY - centerTile.y) * tileSize;\n\n tiles.push({ x: tileX, y: tileY, screenX, screenY });\n }\n }\n\n return tiles;\n}\n\n/**\n * Build a tile URL from the provider template.\n */\nexport function buildTileUrl(provider: TileProvider, x: number, y: number, z: number): string {\n return provider.url.replace('{z}', String(z)).replace('{x}', String(x)).replace('{y}', String(y));\n}\n\n/**\n * Fetch a single tile image as an HTMLImageElement.\n */\nasync function fetchTileImage(url: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(`Failed to load tile: ${url}`));\n img.src = url;\n });\n}\n\n/**\n * Options for composing a map image.\n */\nexport interface ComposeMapOptions {\n /** Center coordinates */\n center: { lat: number; lng: number };\n /** Zoom level */\n zoom: number;\n /** Map tile style */\n style: MapTileStyle;\n /** Output width in pixels */\n width: number;\n /** Output height in pixels */\n height: number;\n /** Optional markers to render */\n markers?: MapMarker[];\n /** Show attribution (default: true) */\n showAttribution?: boolean;\n}\n\n/**\n * Compose map tiles into a single data URL image.\n *\n * This is the main function for generating map images for blocks.\n * It fetches all needed tiles, composites them on a canvas, and\n * returns a data URL that can be used in an SVG <image> element.\n */\nexport async function composeMapImage(options: ComposeMapOptions): Promise<string> {\n const { center, zoom, style, width, height, markers = [], showAttribution = true } = options;\n\n const provider = TILE_PROVIDERS[style];\n const tileSize = provider.tileSize || 256;\n const clampedZoom = Math.min(zoom, provider.maxZoom);\n\n // Create canvas\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('Failed to get canvas context');\n\n // Fill with a neutral background in case tiles fail to load\n ctx.fillStyle = style === 'toner' ? '#ffffff' : '#e5e7eb';\n ctx.fillRect(0, 0, width, height);\n\n // Get tiles needed\n const tiles = getTilesForViewport(center.lat, center.lng, clampedZoom, width, height, tileSize);\n\n // Fetch and draw tiles\n const tilePromises = tiles.map(async (tile) => {\n const url = buildTileUrl(provider, tile.x, tile.y, clampedZoom);\n try {\n const img = await fetchTileImage(url);\n ctx.drawImage(img, tile.screenX, tile.screenY, tileSize, tileSize);\n } catch (err: unknown) {\n // Tile failed to load - leave background color\n console.warn(`Tile load failed: ${url}`, err);\n }\n });\n\n await Promise.all(tilePromises);\n\n // Draw markers\n for (const marker of markers) {\n drawMarker(ctx, marker, center, clampedZoom, width, height, tileSize);\n }\n\n // Draw attribution\n if (showAttribution) {\n drawAttribution(ctx, provider.attribution, width, height);\n }\n\n return canvas.toDataURL('image/png');\n}\n\n/**\n * Draw a marker on the canvas.\n */\nfunction drawMarker(\n ctx: CanvasRenderingContext2D,\n marker: MapMarker,\n center: { lat: number; lng: number },\n zoom: number,\n width: number,\n height: number,\n tileSize: number,\n): void {\n // Calculate screen position of marker relative to center\n const centerTile = latLngToTile(center.lat, center.lng, zoom);\n const markerTile = latLngToTile(marker.lat, marker.lng, zoom);\n\n const centerOffset = getPixelOffset(center.lat, center.lng, zoom, tileSize);\n const markerOffset = getPixelOffset(marker.lat, marker.lng, zoom, tileSize);\n\n const dx = (markerTile.x - centerTile.x) * tileSize + (markerOffset.x - centerOffset.x);\n const dy = (markerTile.y - centerTile.y) * tileSize + (markerOffset.y - centerOffset.y);\n\n const screenX = width / 2 + dx;\n const screenY = height / 2 + dy;\n\n // Draw marker\n const color = marker.color || '#ef4444';\n const icon = marker.icon || 'pin';\n\n ctx.save();\n\n if (icon === 'pin') {\n // Draw pin shape\n ctx.fillStyle = color;\n ctx.beginPath();\n ctx.arc(screenX, screenY - 12, 8, Math.PI, 0, false);\n ctx.lineTo(screenX, screenY);\n ctx.closePath();\n ctx.fill();\n\n // White dot in center\n ctx.fillStyle = '#ffffff';\n ctx.beginPath();\n ctx.arc(screenX, screenY - 12, 3, 0, Math.PI * 2);\n ctx.fill();\n } else if (icon === 'circle') {\n ctx.fillStyle = color;\n ctx.beginPath();\n ctx.arc(screenX, screenY, 8, 0, Math.PI * 2);\n ctx.fill();\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = 2;\n ctx.stroke();\n } else if (icon === 'star') {\n ctx.fillStyle = color;\n drawStar(ctx, screenX, screenY, 5, 10, 5);\n ctx.fill();\n }\n\n // Draw label if present\n if (marker.label) {\n ctx.fillStyle = '#1f2937';\n ctx.font = 'bold 12px system-ui, sans-serif';\n ctx.textAlign = 'center';\n ctx.fillText(marker.label, screenX, screenY + 20);\n }\n\n ctx.restore();\n}\n\n/**\n * Draw a star shape.\n */\nfunction drawStar(\n ctx: CanvasRenderingContext2D,\n cx: number,\n cy: number,\n spikes: number,\n outerRadius: number,\n innerRadius: number,\n): void {\n let rot = (Math.PI / 2) * 3;\n const step = Math.PI / spikes;\n\n ctx.beginPath();\n ctx.moveTo(cx, cy - outerRadius);\n\n for (let i = 0; i < spikes; i++) {\n ctx.lineTo(cx + Math.cos(rot) * outerRadius, cy + Math.sin(rot) * outerRadius);\n rot += step;\n ctx.lineTo(cx + Math.cos(rot) * innerRadius, cy + Math.sin(rot) * innerRadius);\n rot += step;\n }\n\n ctx.lineTo(cx, cy - outerRadius);\n ctx.closePath();\n}\n\n/**\n * Draw attribution text on the canvas.\n */\nfunction drawAttribution(\n ctx: CanvasRenderingContext2D,\n text: string,\n width: number,\n height: number,\n): void {\n ctx.save();\n\n // Semi-transparent background\n ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';\n const padding = 4;\n ctx.font = '10px system-ui, sans-serif';\n const textWidth = ctx.measureText(text).width;\n ctx.fillRect(width - textWidth - padding * 2 - 4, height - 16, textWidth + padding * 2, 14);\n\n // Text\n ctx.fillStyle = '#374151';\n ctx.textAlign = 'right';\n ctx.fillText(text, width - padding - 4, height - 5);\n\n ctx.restore();\n}\n\n/**\n * Get attribution text for a map style.\n */\nexport function getAttribution(style: MapTileStyle): string {\n return TILE_PROVIDERS[style].attribution;\n}\n","/**\n * VideoLayer Component\n *\n * Renders a video clip layer within an SVG block. Uses an HTML5 <video> element\n * inside a <foreignObject> (same pattern as ImageLayer for cover-mode images).\n * Videos are always muted — narration audio is the only sound track.\n *\n * Two modes of operation:\n * 1. Normal playback: Video auto-plays from clipStart to clipEnd on mount,\n * pausing when the clip ends or when the block is no longer active.\n * 2. Render/seekTo mode (Playwright frame capture): Video is paused and seeked\n * programmatically via data attributes read by DocPlayer's seekTo handler.\n *\n * The <video> element carries data-clip-start and data-clip-end attributes so\n * the seekTo handler can calculate the correct video time for any doc time.\n *\n * Related Files:\n * - schemas/Doc.ts — VideoLayer type definition\n * - shared/doc/templates/videoWithCaption.ts — template producing VideoLayers\n * - site/src/components/doc/DocPlayer.tsx — seekTo handler for video sync\n * - site/src/components/doc/layers/ImageLayer.tsx — similar foreignObject pattern\n */\n\nimport { useRef, useEffect } from 'react';\nimport type { VideoLayer as VideoLayerType } from '@bendyline/squisq/schemas';\nimport { useMediaUrl } from '../hooks/MediaContext';\nimport { resolveValue, getAnchorOffset } from '../utils/layerUtils';\n\ninterface VideoLayerProps {\n layer: VideoLayerType;\n /** Base path for resolving relative video URLs */\n basePath: string;\n /** Viewport dimensions for percentage calculations */\n viewport: { width: number; height: number };\n /** Current time relative to block start (for playback sync) */\n blockTime: number;\n /** Whether the doc is currently playing */\n isPlaying?: boolean;\n}\n\nexport function VideoLayer({\n layer,\n basePath,\n viewport,\n blockTime: _blockTime,\n isPlaying,\n}: VideoLayerProps) {\n const { content, position } = layer;\n const videoRef = useRef<HTMLVideoElement>(null);\n const hasStartedRef = useRef(false);\n\n // Resolve position values to pixels\n const x = resolveValue(position.x, viewport.width);\n const y = resolveValue(position.y, viewport.height);\n const width = position.width ? resolveValue(position.width, viewport.width) : viewport.width;\n const height = position.height ? resolveValue(position.height, viewport.height) : viewport.height;\n\n // Apply anchor offset\n const offset = getAnchorOffset(position.anchor, width, height);\n const finalX = x + offset.x;\n const finalY = y + offset.y;\n\n // Resolve video URL via MediaProvider (if available), falling back to basePath\n const src = useMediaUrl(content.src, basePath);\n\n // Always call the hook (Rules of Hooks), but pass empty string when no poster\n const resolvedPoster = useMediaUrl(content.posterSrc || '', basePath);\n const posterSrc = content.posterSrc ? resolvedPoster : undefined;\n\n // On mount: seek to clipStart and set up clipEnd boundary.\n // The video will be muted and play silently alongside the narration.\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n // Set initial time to clipStart\n video.currentTime = content.clipStart;\n hasStartedRef.current = true;\n\n // Start playing if doc is already playing\n if (isPlaying) {\n const playPromise = video.play();\n if (playPromise) {\n playPromise.catch(() => {\n // Autoplay blocked — fine for Playwright seekTo mode\n });\n }\n }\n\n // Monitor timeupdate to pause at clipEnd\n const handleTimeUpdate = () => {\n if (video.currentTime >= content.clipEnd) {\n video.pause();\n video.currentTime = content.clipEnd;\n }\n };\n\n video.addEventListener('timeupdate', handleTimeUpdate);\n return () => {\n video.removeEventListener('timeupdate', handleTimeUpdate);\n video.pause();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- isPlaying is handled by the separate sync effect below\n }, [content.src, content.clipStart, content.clipEnd]);\n\n // Sync video play/pause with doc playback state\n useEffect(() => {\n const video = videoRef.current;\n if (!video || !hasStartedRef.current) return;\n\n // Don't resume if clip has already reached its end\n if (video.currentTime >= content.clipEnd) return;\n\n if (isPlaying) {\n const playPromise = video.play();\n if (playPromise) {\n playPromise.catch(() => {});\n }\n } else {\n video.pause();\n }\n }, [isPlaying, content.clipEnd]);\n\n return (\n <g className=\"block-layer block-layer--video\" data-layer-id={layer.id}>\n <foreignObject x={finalX} y={finalY} width={width} height={height}>\n <video\n ref={videoRef}\n src={src}\n poster={posterSrc}\n muted\n playsInline\n preload=\"auto\"\n data-clip-start={content.clipStart}\n data-clip-end={content.clipEnd}\n style={{\n width: '100%',\n height: '100%',\n objectFit: content.fit || 'cover',\n objectPosition: 'center',\n display: 'block',\n pointerEvents: 'none',\n }}\n />\n </foreignObject>\n </g>\n );\n}\n\nexport default VideoLayer;\n","/**\n * BlockRenderer Component\n *\n * Renders a single block as an SVG element with all its layers.\n * Each layer is rendered back-to-front (first layer is background).\n * Handles positioning, animations, and transitions.\n */\n\nimport type { Block, Layer } from '@bendyline/squisq/schemas';\nimport { ImageLayer } from './layers/ImageLayer';\nimport { TextLayer } from './layers/TextLayer';\nimport { ShapeLayer } from './layers/ShapeLayer';\nimport { MapLayer } from './layers/MapLayer';\nimport { VideoLayer } from './layers/VideoLayer';\nimport { getTransitionClass } from './utils/animationUtils';\n\n/** Default viewport dimensions (1080p landscape) - for backwards compatibility */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const VIEWPORT = {\n width: 1920,\n height: 1080,\n};\n\n/** Viewport configuration type */\nexport interface ViewportDimensions {\n width: number;\n height: number;\n}\n\ninterface BlockRendererProps {\n /** The block to render */\n block: Block;\n /** Current time relative to block start (seconds) */\n blockTime: number;\n /** Base path for resolving media URLs */\n basePath: string;\n /** Whether this block is entering (for transition) */\n isEntering?: boolean;\n /** Whether this block is exiting (for transition) */\n isExiting?: boolean;\n /** Viewport dimensions (defaults to 1920x1080 landscape) */\n viewport?: ViewportDimensions;\n /** Whether the doc is currently playing (controls video playback) */\n isPlaying?: boolean;\n}\n\nexport function BlockRenderer({\n block,\n blockTime,\n basePath,\n isEntering = false,\n isExiting = false,\n viewport = VIEWPORT,\n isPlaying,\n}: BlockRendererProps) {\n // Build transition class and inline style for dynamic duration\n let transitionClass = '';\n const transitionStyle: Record<string, string> = {};\n if (block.transition && isEntering) {\n transitionClass = getTransitionClass(block.transition.type, true);\n transitionStyle['--transition-duration'] = `${block.transition.duration}s`;\n } else if (block.transition && isExiting) {\n transitionClass = getTransitionClass(block.transition.type, false);\n transitionStyle['--transition-duration'] = `${block.transition.duration}s`;\n }\n\n // Unique clip path ID per block to avoid conflicts when multiple blocks render simultaneously\n const clipId = `vb-clip-${block.id}`;\n\n return (\n <svg\n className={`block-svg ${transitionClass}`}\n style={transitionStyle}\n viewBox={`0 0 ${viewport.width} ${viewport.height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n overflow=\"hidden\"\n data-block-id={block.id}\n >\n {/* Clip path matching the viewBox -- prevents Ken Burns animations from\n bleeding outside the block area (foreignObject + transform: scale\n can escape SVG overflow=\"hidden\" in some browsers) */}\n <defs>\n <clipPath id={clipId}>\n <rect x=\"0\" y=\"0\" width={viewport.width} height={viewport.height} />\n </clipPath>\n </defs>\n\n {/* All layers clipped to viewBox bounds */}\n <g clipPath={`url(#${clipId})`}>\n {(block.layers ?? []).map((layer) => (\n <LayerRenderer\n key={layer.id}\n layer={layer}\n basePath={basePath}\n viewport={viewport}\n blockTime={blockTime}\n isPlaying={isPlaying}\n />\n ))}\n </g>\n </svg>\n );\n}\n\ninterface LayerRendererProps {\n layer: Layer;\n basePath: string;\n viewport: { width: number; height: number };\n blockTime: number;\n isPlaying?: boolean;\n}\n\n/**\n * Dispatch to the appropriate layer component based on type.\n */\nfunction LayerRenderer({ layer, basePath, viewport, blockTime, isPlaying }: LayerRendererProps) {\n switch (layer.type) {\n case 'image':\n return (\n <ImageLayer layer={layer} basePath={basePath} viewport={viewport} blockTime={blockTime} />\n );\n case 'text':\n return <TextLayer layer={layer} viewport={viewport} blockTime={blockTime} />;\n case 'shape':\n return <ShapeLayer layer={layer} viewport={viewport} blockTime={blockTime} />;\n case 'map':\n return (\n <MapLayer layer={layer} basePath={basePath} viewport={viewport} blockTime={blockTime} />\n );\n case 'video':\n return (\n <VideoLayer\n layer={layer}\n basePath={basePath}\n viewport={viewport}\n blockTime={blockTime}\n isPlaying={isPlaying}\n />\n );\n default:\n console.warn(`Unknown layer type: ${(layer as Layer).type}`);\n return null;\n }\n}\n\nexport default BlockRenderer;\n","/**\n * CaptionOverlay Component\n *\n * Displays closed captions synchronized with audio playback.\n * Captions cross-fade smoothly using CSS transitions on a single\n * persistent element (no mount/unmount flicker).\n *\n * The captions are generated by the postprocess-doc command based\n * on article text and audio segment timing.\n */\n\nimport type { CaptionTrack } from '@bendyline/squisq/schemas';\nimport { getCaptionAtTime } from '@bendyline/squisq/schemas';\n\ninterface CaptionOverlayProps {\n /** Caption track with timestamped phrases */\n captions: CaptionTrack | undefined;\n /** Current playback time in seconds */\n currentTime: number;\n /** Whether captions are enabled */\n enabled?: boolean;\n /** Font size in pixels (default: 16) */\n fontSize?: number;\n}\n\nexport function CaptionOverlay({\n captions,\n currentTime,\n enabled = true,\n fontSize = 16,\n}: CaptionOverlayProps) {\n // Derive caption directly from currentTime -- pure computation, no hooks.\n // This avoids race conditions between timeupdate events and async state updates.\n const phrase = enabled && captions ? getCaptionAtTime(captions, currentTime) : null;\n const captionText = phrase?.text ?? null;\n\n return (\n <div\n className=\"caption-overlay\"\n style={{\n position: 'absolute',\n top: '6px',\n left: '50%',\n transform: 'translateX(-50%)',\n zIndex: 50,\n pointerEvents: 'none',\n maxWidth: '100%',\n width: '100%',\n textAlign: 'center',\n opacity: captionText ? 1 : 0,\n transition: 'opacity 0.15s ease-in-out',\n padding: '0 4px',\n boxSizing: 'border-box',\n }}\n >\n {captionText && (\n <div\n style={{\n display: 'inline-block',\n padding: '3px 10px',\n background: 'rgba(0, 0, 0, 0.65)',\n borderRadius: '4px',\n backdropFilter: 'blur(4px)',\n }}\n >\n <span\n style={{\n color: '#ffffff',\n fontSize: `${fontSize}px`,\n fontFamily: \"'Hanken Grotesk', system-ui, sans-serif\",\n fontWeight: 500,\n lineHeight: 1.25,\n textShadow: '0 1px 3px rgba(0,0,0,0.5)',\n whiteSpace: 'pre-wrap',\n wordWrap: 'break-word',\n }}\n >\n {captionText}\n </span>\n </div>\n )}\n </div>\n );\n}\n\nexport default CaptionOverlay;\n","/**\n * useAudioSync Hook\n *\n * Synchronizes playback state with an audio element. Provides current\n * playback time, playing state, and methods to control audio playback.\n *\n * Handles multiple audio segments (MP3 files) by tracking which segment\n * is currently playing and calculating the overall timeline position.\n *\n * This is the HTML5 Audio implementation of the AudioProvider interface.\n * For EFB/MSFS environments, use useCompanionAudioSync instead.\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { RefObject } from 'react';\nimport type { AudioTrack } from '@bendyline/squisq/schemas';\nimport type { AudioProvider } from './AudioProvider';\n\nexport function useAudioSync(\n audioRef: RefObject<HTMLAudioElement>,\n audioTrack: AudioTrack | undefined,\n basePath: string = '',\n): AudioProvider {\n const [currentTime, setCurrentTime] = useState(0);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentSegment, setCurrentSegment] = useState(0);\n const [isEnded, setIsEnded] = useState(false);\n const [isAudioReady, setIsAudioReady] = useState(false);\n const [totalDuration, setTotalDuration] = useState(0);\n\n // Calculate segment start times\n const segmentStarts = useRef<number[]>([]);\n\n // Pending seek time (used when switching segments)\n const pendingSeekTime = useRef<number | null>(null);\n const shouldPlayAfterLoad = useRef(false);\n\n // Preloaded audio blob URLs (for seeking without range request support)\n const blobUrls = useRef<Map<string, string>>(new Map());\n const loadingPromises = useRef<Map<string, Promise<string>>>(new Map());\n\n // Fallback timer: when audio.play() is blocked (e.g., autoplay policy),\n // advance currentTime synthetically so blocks still progress without audio.\n const fallbackMode = useRef(false);\n\n useEffect(() => {\n if (!audioTrack?.segments) {\n return;\n }\n\n let time = 0;\n segmentStarts.current = audioTrack.segments.map((seg) => {\n const start = time;\n time += seg.duration;\n return start;\n });\n setTotalDuration(time);\n }, [audioTrack]);\n\n // Preload audio file as blob (enables seeking without range request support)\n const preloadAudio = useCallback(\n async (src: string): Promise<string> => {\n const audioUrl = basePath ? `${basePath}/${src}` : src;\n\n // Return cached blob URL if available\n if (blobUrls.current.has(src)) {\n return blobUrls.current.get(src)!;\n }\n\n // Return existing loading promise if in progress\n if (loadingPromises.current.has(src)) {\n return loadingPromises.current.get(src)!;\n }\n\n // Start loading\n const loadPromise = (async () => {\n try {\n const response = await fetch(audioUrl);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const blob = await response.blob();\n const blobUrl = URL.createObjectURL(blob);\n blobUrls.current.set(src, blobUrl);\n return blobUrl;\n } catch {\n // Fall back to direct URL if blob loading fails\n return audioUrl;\n } finally {\n loadingPromises.current.delete(src);\n }\n })();\n\n loadingPromises.current.set(src, loadPromise);\n return loadPromise;\n },\n [basePath],\n );\n\n // Preload all audio segments on mount\n useEffect(() => {\n if (!audioTrack?.segments) return;\n\n // Preload all segments in parallel\n audioTrack.segments.forEach((segment) => {\n preloadAudio(segment.src);\n });\n\n // Cleanup blob URLs on unmount\n const currentBlobUrls = blobUrls.current;\n return () => {\n currentBlobUrls.forEach((url) => {\n URL.revokeObjectURL(url);\n });\n currentBlobUrls.clear();\n };\n }, [audioTrack, preloadAudio]);\n\n // Handle audio time updates\n useEffect(() => {\n const audio = audioRef.current;\n if (!audio) return;\n\n const handleTimeUpdate = () => {\n // Calculate overall timeline position\n const segmentStart = segmentStarts.current[currentSegment] || 0;\n const overallTime = segmentStart + audio.currentTime;\n setCurrentTime(overallTime);\n };\n\n const handlePlay = () => {\n fallbackMode.current = false;\n setIsPlaying(true);\n };\n const handlePause = () => setIsPlaying(false);\n const handleError = () => {\n // Audio source failed to load (e.g., 404 in CI or missing files).\n // Set ready so the UI can still render controls and progress.\n setIsAudioReady(true);\n };\n const handleEnded = () => {\n // Move to next segment or end\n if (audioTrack && currentSegment < audioTrack.segments.length - 1) {\n // Auto-advance to next segment\n // Set shouldPlayAfterLoad so the next segment auto-plays after loading\n shouldPlayAfterLoad.current = true;\n setCurrentSegment((prev) => prev + 1);\n } else {\n setIsEnded(true);\n setIsPlaying(false);\n }\n };\n\n audio.addEventListener('timeupdate', handleTimeUpdate);\n audio.addEventListener('play', handlePlay);\n audio.addEventListener('pause', handlePause);\n audio.addEventListener('ended', handleEnded);\n audio.addEventListener('error', handleError);\n\n return () => {\n audio.removeEventListener('timeupdate', handleTimeUpdate);\n audio.removeEventListener('play', handlePlay);\n audio.removeEventListener('pause', handlePause);\n audio.removeEventListener('ended', handleEnded);\n audio.removeEventListener('error', handleError);\n };\n }, [audioRef, currentSegment, audioTrack]);\n\n // Load new segment when currentSegment changes\n useEffect(() => {\n const audio = audioRef.current;\n if (!audio || !audioTrack?.segments) return;\n\n const segment = audioTrack.segments[currentSegment];\n if (!segment) return;\n\n // Apply pending seek and play state\n const applyPendingSeek = () => {\n if (pendingSeekTime.current !== null) {\n const segmentStart = segmentStarts.current[currentSegment] || 0;\n const segmentTime = pendingSeekTime.current - segmentStart;\n audio.currentTime = Math.max(0, segmentTime);\n setCurrentTime(pendingSeekTime.current);\n pendingSeekTime.current = null;\n }\n\n if (shouldPlayAfterLoad.current) {\n audio.play().catch(() => {});\n shouldPlayAfterLoad.current = false;\n }\n };\n\n // Check if we're already on this source (avoid unnecessary reload)\n // For blob URLs, check by segment src key\n const currentSrc = audio.src;\n const cachedBlobUrl = blobUrls.current.get(segment.src);\n const isSameSource =\n currentSrc && (currentSrc === cachedBlobUrl || currentSrc.endsWith(segment.src));\n\n if (!isSameSource) {\n // Need to load new source - use preloaded blob URL\n const loadAndPlay = async () => {\n const blobUrl = await preloadAudio(segment.src);\n\n const handleCanPlay = () => {\n setIsAudioReady(true);\n applyPendingSeek();\n audio.removeEventListener('canplay', handleCanPlay);\n };\n\n audio.addEventListener('canplay', handleCanPlay);\n audio.src = blobUrl;\n audio.load();\n\n // If audio is already ready (blob is instant), canplay might not fire\n // Check after a microtask to see if it's ready\n await Promise.resolve();\n if (audio.readyState >= 3) {\n audio.removeEventListener('canplay', handleCanPlay);\n setIsAudioReady(true);\n applyPendingSeek();\n }\n };\n\n loadAndPlay();\n } else {\n // Same source - apply seek directly\n applyPendingSeek();\n }\n }, [audioRef, currentSegment, audioTrack, preloadAudio]);\n\n const play = useCallback(() => {\n const audio = audioRef.current;\n if (audio) {\n if (isEnded) {\n // Restart from beginning\n setCurrentSegment(0);\n setIsEnded(false);\n }\n audio\n .play()\n .then(() => {\n fallbackMode.current = false;\n })\n .catch(() => {\n // Audio playback failed (e.g., autoplay policy or 404).\n // Enable fallback timer so blocks progress without audio.\n fallbackMode.current = true;\n setIsPlaying(true);\n });\n }\n }, [audioRef, isEnded]);\n\n const pause = useCallback(() => {\n const audio = audioRef.current;\n if (audio) {\n audio.pause();\n }\n // Also set state directly for cases where audio isn't loaded\n // (pause event won't fire if audio has no valid source)\n setIsPlaying(false);\n }, [audioRef]);\n\n const toggle = useCallback(() => {\n const audio = audioRef.current;\n if (!audio) return;\n\n // Use component state instead of audio.paused to handle cases where\n // audio source isn't loaded (audio.paused is always true without a source)\n if (!isPlaying) {\n play();\n } else {\n pause();\n }\n }, [audioRef, isPlaying, play, pause]);\n\n const seekTo = useCallback(\n (time: number) => {\n const audio = audioRef.current;\n if (!audio || !audioTrack?.segments) return;\n\n // Clamp time to valid range\n const clampedTime = Math.max(0, Math.min(time, totalDuration));\n\n // Find which segment this time falls into\n let segmentIndex = 0;\n let segmentStart = 0;\n for (let i = 0; i < audioTrack.segments.length; i++) {\n const segEnd = segmentStart + audioTrack.segments[i].duration;\n if (clampedTime < segEnd) {\n segmentIndex = i;\n break;\n }\n segmentStart = segEnd;\n // Handle edge case: time exactly at end goes to last segment\n if (i === audioTrack.segments.length - 1) {\n segmentIndex = i;\n }\n }\n\n const wasPlaying = !audio.paused;\n setIsEnded(false);\n\n // Check if we need to switch segments\n if (segmentIndex !== currentSegment) {\n // Store pending seek time - will be applied after segment loads\n pendingSeekTime.current = clampedTime;\n shouldPlayAfterLoad.current = wasPlaying;\n setCurrentSegment(segmentIndex);\n } else {\n // Same segment - seek directly\n const segmentTime = clampedTime - segmentStart;\n audio.currentTime = Math.max(0, segmentTime);\n setCurrentTime(clampedTime);\n }\n },\n [audioRef, audioTrack, currentSegment, totalDuration],\n );\n\n const skipToSegment = useCallback(\n (index: number) => {\n if (!audioTrack?.segments || index < 0 || index >= audioTrack.segments.length) {\n return;\n }\n setCurrentSegment(index);\n setIsEnded(false);\n },\n [audioTrack],\n );\n\n // Restart from beginning\n const restart = useCallback(async () => {\n seekTo(0);\n // Small delay to ensure seek completes before playing\n await new Promise((resolve) => setTimeout(resolve, 50));\n play();\n }, [seekTo, play]);\n\n // Fallback timer: advance currentTime synthetically when audio.play() was blocked\n // (e.g., autoplay policy or missing audio). Blocks progress at real-time pace\n // without sound. Stops when audio actually starts playing or playback is paused.\n useEffect(() => {\n if (!isPlaying || !fallbackMode.current || !totalDuration) return;\n\n let lastTime = performance.now();\n let raf: number;\n\n const tick = (now: number) => {\n // Audio started playing for real — stop the fallback\n if (!fallbackMode.current) return;\n\n const dt = (now - lastTime) / 1000;\n lastTime = now;\n\n setCurrentTime((prev) => {\n const next = prev + dt;\n if (next >= totalDuration) {\n fallbackMode.current = false;\n setIsEnded(true);\n setIsPlaying(false);\n return totalDuration;\n }\n return next;\n });\n\n raf = requestAnimationFrame(tick);\n };\n\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n }, [isPlaying, totalDuration]);\n\n return {\n // State\n currentTime,\n isPlaying,\n currentSegment,\n totalDuration,\n isEnded,\n isReady: isAudioReady,\n isAvailable: true, // HTML5 audio is always available in browsers\n // Actions\n play: async () => play(),\n pause: async () => pause(),\n toggle: async () => toggle(),\n seekTo: async (time: number) => seekTo(time),\n skipToSegment: async (index: number) => skipToSegment(index),\n restart,\n };\n}\n\nexport default useAudioSync;\n","/**\n * useDocPlayback Hook\n *\n * Manages the playback state for a visual doc, including which block\n * is currently active, transition states, and synchronization with audio.\n *\n * This hook provides:\n * - Current block determination based on time\n * - Transition tracking (entering/exiting blocks)\n * - Manual navigation (next/prev block)\n * - Time-based seeking\n * - Automatic expansion of template blocks\n */\n\nimport { useState, useEffect, useMemo, useCallback, useRef } from 'react';\nimport type { Doc, Block, DocBlock } from '@bendyline/squisq/schemas';\nimport type { Theme } from '@bendyline/squisq/schemas';\nimport { getBlockAtTime } from '@bendyline/squisq/schemas';\nimport {\n expandDocBlocks,\n isTemplateBlock,\n VIEWPORT_PRESETS,\n type ViewportConfig,\n} from '@bendyline/squisq/doc';\n\ninterface PlaybackState {\n /** Currently visible block */\n currentBlock: Block | null;\n /** Index of current block */\n currentBlockIndex: number;\n /** Previous block (for transitions) */\n previousBlock: Block | null;\n /** Whether current block is entering */\n isEntering: boolean;\n /** Whether previous block is exiting */\n isExiting: boolean;\n /** Time relative to current block start */\n blockTime: number;\n /** Progress through current block (0-1) */\n blockProgress: number;\n /** Overall progress through doc (0-1) */\n docProgress: number;\n /** Expanded blocks (templates converted to full blocks with layers) */\n blocks: Block[];\n}\n\ninterface PlaybackActions {\n /** Go to next block */\n nextBlock: () => void;\n /** Go to previous block */\n prevBlock: () => void;\n /** Go to specific block by index */\n goToBlock: (index: number) => void;\n}\n\nexport function useDocPlayback(\n script: Doc | null,\n currentTime: number,\n viewport: ViewportConfig = VIEWPORT_PRESETS.landscape,\n renderMode: boolean = false,\n theme?: Theme,\n): PlaybackState & PlaybackActions {\n const [transitionState, setTransitionState] = useState<{\n entering: boolean;\n exiting: boolean;\n previousBlock: Block | null;\n }>({\n entering: false,\n exiting: false,\n previousBlock: null,\n });\n\n // Expand any template blocks into full blocks\n const blocks = useMemo(() => {\n if (!script?.blocks) {\n return [];\n }\n\n // Check if any blocks are templates\n const hasTemplates = script.blocks.some(isTemplateBlock);\n\n if (hasTemplates) {\n // Extract audio segment timing for proper block synchronization\n const audioSegments = script.audio?.segments?.map((seg) => ({\n startTime: seg.startTime,\n duration: seg.duration,\n }));\n\n // Expand template blocks with audio segment timing, viewport, and persistent layers\n const expanded = expandDocBlocks(script.blocks as DocBlock[], {\n audioSegments,\n viewport,\n persistentLayers: script.persistentLayers,\n theme,\n });\n return expanded;\n }\n\n // All raw blocks, use as-is\n return script.blocks;\n }, [script?.blocks, script?.audio?.segments, script?.persistentLayers, viewport, theme]);\n\n // Find current block based on time\n const currentBlock = useMemo(() => getBlockAtTime(blocks, currentTime), [blocks, currentTime]);\n\n const currentBlockIndex = useMemo(\n () => (currentBlock ? blocks.indexOf(currentBlock) : -1),\n [blocks, currentBlock],\n );\n\n // Calculate block-relative time\n const blockTime = useMemo(() => {\n if (!currentBlock) return 0;\n return Math.max(0, currentTime - currentBlock.startTime);\n }, [currentBlock, currentTime]);\n\n // Calculate progress values\n const blockProgress = useMemo(() => {\n if (!currentBlock || currentBlock.duration === 0) return 0;\n return Math.min(1, blockTime / currentBlock.duration);\n }, [currentBlock, blockTime]);\n\n const docProgress = useMemo(() => {\n if (!script || script.duration === 0) return 0;\n return Math.min(1, currentTime / script.duration);\n }, [script, currentTime]);\n\n // Track block transitions.\n // In render mode, transitions are computed from blockTime so they stay\n // synchronized with the seekTo timeline. In normal mode, setTimeout drives\n // transitions at the browser's real-time clock speed.\n const _prevBlockRef = useMemo(\n () => currentBlock,\n // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally keyed on index, not block reference\n [currentBlockIndex],\n );\n\n useEffect(() => {\n if (!currentBlock || renderMode) return;\n\n // When block changes, trigger transition (real-time mode only)\n if (transitionState.previousBlock?.id !== currentBlock.id) {\n const transition = currentBlock.transition;\n const transitionDuration = transition?.duration || 0;\n\n if (transitionDuration > 0) {\n // Start transition\n setTransitionState({\n entering: true,\n exiting: true,\n previousBlock: transitionState.previousBlock,\n });\n\n // End transition after duration\n const timer = setTimeout(() => {\n setTransitionState({\n entering: false,\n exiting: false,\n previousBlock: currentBlock,\n });\n }, transitionDuration * 1000);\n\n return () => clearTimeout(timer);\n } else {\n // Instant cut\n setTransitionState({\n entering: false,\n exiting: false,\n previousBlock: currentBlock,\n });\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally keyed on currentBlock?.id only; reading transitionState.previousBlock without dep to avoid infinite loop\n }, [currentBlock?.id, renderMode]);\n\n // Render mode: track previous block via ref and compute transition from time\n const renderPrevBlockRef = useRef<Block | null>(null);\n\n useEffect(() => {\n if (!renderMode || !currentBlock) return;\n\n if (transitionState.previousBlock?.id !== currentBlock.id) {\n // Block changed — remember the old block for crossfade\n const oldPrev = transitionState.previousBlock;\n renderPrevBlockRef.current = oldPrev;\n // Store current block as the \"last seen\" for next transition\n setTransitionState((prev) => ({\n ...prev,\n previousBlock: currentBlock,\n }));\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- same pattern: keyed on block identity change\n }, [currentBlock?.id, renderMode]);\n\n // In render mode, derive entering/exiting from blockTime\n const renderTransitionDuration = currentBlock?.transition?.duration || 0;\n const renderIsEntering =\n renderMode && renderTransitionDuration > 0 && blockTime < renderTransitionDuration;\n const renderIsExiting = renderIsEntering && renderPrevBlockRef.current !== null;\n\n // Manual navigation\n const goToBlock = useCallback(\n (index: number) => {\n if (!script || index < 0 || index >= blocks.length) return;\n // This would need to be coordinated with seekTo from audio sync\n // For now, just return the target block's start time\n const targetBlock = blocks[index];\n if (targetBlock) {\n // Caller should use this time with audio seekTo\n return targetBlock.startTime;\n }\n },\n [script, blocks],\n );\n\n const nextBlock = useCallback(() => {\n if (currentBlockIndex < blocks.length - 1) {\n return goToBlock(currentBlockIndex + 1);\n }\n }, [currentBlockIndex, blocks.length, goToBlock]);\n\n const prevBlock = useCallback(() => {\n if (currentBlockIndex > 0) {\n return goToBlock(currentBlockIndex - 1);\n }\n }, [currentBlockIndex, goToBlock]);\n\n return {\n currentBlock,\n currentBlockIndex,\n previousBlock: renderMode\n ? renderIsExiting\n ? renderPrevBlockRef.current\n : null\n : transitionState.exiting\n ? transitionState.previousBlock\n : null,\n isEntering: renderMode ? renderIsEntering : transitionState.entering,\n isExiting: renderMode ? renderIsExiting : transitionState.exiting,\n blockTime,\n blockProgress,\n docProgress,\n nextBlock,\n prevBlock,\n goToBlock,\n /** Expanded blocks (templates converted to full blocks with layers) */\n blocks,\n };\n}\n\nexport default useDocPlayback;\n","/**\n * useViewportOrientation Hook\n *\n * Detects the current viewport orientation and returns the appropriate\n * VIEWPORT_PRESET for rendering docs. Automatically updates when\n * the window is resized.\n *\n * Thresholds:\n * - Portrait: height > width * 1.2 (significantly taller than wide)\n * - Square: width and height within 20% of each other\n * - Landscape: width > height * 1.2 (significantly wider than tall)\n */\n\nimport { useState, useEffect, useMemo } from 'react';\nimport {\n VIEWPORT_PRESETS,\n type ViewportConfig,\n type ViewportOrientation,\n} from '@bendyline/squisq/doc';\n\ninterface UseViewportOrientationResult {\n /** Current viewport preset configuration */\n viewport: ViewportConfig;\n /** Current orientation name */\n orientation: ViewportOrientation;\n /** Current window dimensions */\n windowSize: { width: number; height: number };\n}\n\n/**\n * Determine viewport orientation from window dimensions.\n */\nfunction getOrientationFromWindow(width: number, height: number): ViewportOrientation {\n const ratio = width / height;\n\n // Use thresholds to determine orientation\n // - Ratio > 1.2 = landscape (wider than tall)\n // - Ratio < 0.83 (1/1.2) = portrait (taller than wide)\n // - Otherwise = square-ish, use landscape for better readability\n if (ratio > 1.2) {\n return 'landscape';\n } else if (ratio < 0.83) {\n return 'portrait';\n } else {\n // Near-square viewports: use landscape for better text readability\n // Could also use 'square' preset if available and desired\n return 'landscape';\n }\n}\n\n/**\n * Get the appropriate viewport preset for an orientation.\n */\nfunction getViewportForOrientation(orientation: ViewportOrientation): ViewportConfig {\n switch (orientation) {\n case 'portrait':\n return VIEWPORT_PRESETS.portrait;\n case 'square':\n return VIEWPORT_PRESETS.square;\n case 'landscape':\n default:\n return VIEWPORT_PRESETS.landscape;\n }\n}\n\n/**\n * Hook to detect viewport orientation and return appropriate preset.\n * Updates automatically when window is resized.\n */\nexport function useViewportOrientation(): UseViewportOrientationResult {\n const [windowSize, setWindowSize] = useState(() => ({\n width: typeof window !== 'undefined' ? window.innerWidth : 1920,\n height: typeof window !== 'undefined' ? window.innerHeight : 1080,\n }));\n\n // Listen for window resize\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const handleResize = () => {\n setWindowSize({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n };\n\n // Debounce resize handler to avoid excessive re-renders\n let timeoutId: ReturnType<typeof setTimeout>;\n const debouncedResize = () => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(handleResize, 100);\n };\n\n window.addEventListener('resize', debouncedResize);\n return () => {\n window.removeEventListener('resize', debouncedResize);\n clearTimeout(timeoutId);\n };\n }, []);\n\n // Calculate orientation from window size\n const orientation = useMemo(\n () => getOrientationFromWindow(windowSize.width, windowSize.height),\n [windowSize.width, windowSize.height],\n );\n\n // Get appropriate viewport preset\n const viewport = useMemo(() => getViewportForOrientation(orientation), [orientation]);\n\n return {\n viewport,\n orientation,\n windowSize,\n };\n}\n\nexport default useViewportOrientation;\n","/**\n * DocProgressBar Component\n *\n * Interactive progress/scrubber bar for doc playback. Shows a track with\n * progress fill, block markers as clickable dots, hover tooltip with time\n * and block title, and a hover line indicator.\n *\n * Extracted from DocPlayer to enable reuse across different control layouts\n * (overlay, sidebar, bottom). When used in sidebar/bottom layouts, this\n * component renders at the bottom of the video while other controls are\n * externalized.\n *\n * Related Files:\n * - DocPlayer.tsx -- Parent component\n * - DocControlsOverlay.tsx -- Uses this within the overlay\n * - types.ts -- Shared type definitions\n */\n\nimport { useRef, useState, useCallback } from 'react';\nimport type { PlaybackState, PlaybackActions, BlockMarker } from './types';\nimport { formatTime } from './types';\nimport type { Block } from '@bendyline/squisq/schemas';\n\ninterface DocProgressBarProps {\n state: PlaybackState;\n actions: PlaybackActions;\n blockMarkers: BlockMarker[];\n /** All expanded blocks for hover lookup */\n expandedBlocks: Block[];\n /** Optional: get block title for hover tooltip */\n getBlockTitle?: (block: Block) => string;\n}\n\nexport function DocProgressBar({\n state,\n actions,\n blockMarkers,\n expandedBlocks,\n getBlockTitle,\n}: DocProgressBarProps) {\n const progressBarRef = useRef<HTMLDivElement>(null);\n const [hoverPosition, setHoverPosition] = useState<number | null>(null);\n\n const handleProgressHover = useCallback((e: React.MouseEvent) => {\n const bar = progressBarRef.current;\n if (!bar) return;\n const rect = bar.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const progress = Math.max(0, Math.min(1, x / rect.width));\n setHoverPosition(progress);\n }, []);\n\n const handleProgressLeave = useCallback(() => {\n setHoverPosition(null);\n }, []);\n\n const getBlockAtTimeLocal = useCallback(\n (time: number): { block: Block; index: number } | null => {\n for (let i = expandedBlocks.length - 1; i >= 0; i--) {\n const blk = expandedBlocks[i];\n if (time >= blk.startTime) {\n return { block: blk, index: i };\n }\n }\n return expandedBlocks.length > 0 ? { block: expandedBlocks[0], index: 0 } : null;\n },\n [expandedBlocks],\n );\n\n return (\n <div\n ref={progressBarRef}\n style={{\n flex: 1,\n height: '24px',\n cursor: 'pointer',\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n }}\n onClick={(e) => {\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n const x = e.clientX - rect.left;\n const progress = x / rect.width;\n actions.seekTo(progress * state.totalDuration);\n }}\n onMouseMove={handleProgressHover}\n onMouseLeave={handleProgressLeave}\n >\n {/* Track background */}\n <div\n style={{\n position: 'absolute',\n left: 0,\n right: 0,\n height: '6px',\n background: 'rgba(255,255,255,0.2)',\n borderRadius: '3px',\n }}\n />\n\n {/* Progress fill */}\n <div\n style={{\n position: 'absolute',\n left: 0,\n width: `${state.docProgress * 100}%`,\n height: '6px',\n background: '#3d5a80',\n borderRadius: '3px',\n transition: 'width 0.1s',\n }}\n />\n\n {/* Block markers (dots) */}\n {blockMarkers.map((marker, i) => (\n <div\n key={`${marker.block.id}-${i}`}\n style={{\n position: 'absolute',\n left: `${marker.position}%`,\n transform: 'translateX(-50%)',\n width: '10px',\n height: '10px',\n borderRadius: '50%',\n background:\n marker.index === state.currentBlockIndex ? '#ffffff' : 'rgba(255,255,255,0.5)',\n border: '2px solid #3d5a80',\n cursor: 'pointer',\n zIndex: 2,\n transition: 'transform 0.15s, background 0.15s',\n }}\n title={marker.title}\n onClick={(e) => {\n e.stopPropagation();\n actions.seekTo(marker.block.startTime);\n }}\n onMouseEnter={(e) => {\n (e.currentTarget as HTMLElement).style.transform = 'translateX(-50%) scale(1.3)';\n }}\n onMouseLeave={(e) => {\n (e.currentTarget as HTMLElement).style.transform = 'translateX(-50%)';\n }}\n />\n ))}\n\n {/* Hover tooltip */}\n {hoverPosition !== null && (\n <div\n style={{\n position: 'absolute',\n left: `${hoverPosition * 100}%`,\n bottom: '100%',\n transform: 'translateX(-50%)',\n marginBottom: '8px',\n padding: '6px 10px',\n background: 'rgba(0,0,0,0.9)',\n borderRadius: '4px',\n whiteSpace: 'nowrap',\n pointerEvents: 'none',\n zIndex: 10,\n }}\n >\n <div style={{ color: 'white', fontSize: '12px', fontFamily: 'monospace' }}>\n {formatTime(hoverPosition * state.totalDuration)}\n </div>\n {(() => {\n const hoverTime = hoverPosition * state.totalDuration;\n const slideInfo = getBlockAtTimeLocal(hoverTime);\n if (slideInfo && getBlockTitle) {\n return (\n <div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '11px', marginTop: '2px' }}>\n {getBlockTitle(slideInfo.block)}\n </div>\n );\n }\n return null;\n })()}\n </div>\n )}\n\n {/* Hover line indicator */}\n {hoverPosition !== null && (\n <div\n style={{\n position: 'absolute',\n left: `${hoverPosition * 100}%`,\n top: '50%',\n transform: 'translate(-50%, -50%)',\n width: '2px',\n height: '16px',\n background: 'rgba(255,255,255,0.6)',\n pointerEvents: 'none',\n zIndex: 1,\n }}\n />\n )}\n </div>\n );\n}\n","/**\n * Doc Player Control Types\n *\n * Shared type definitions for doc player controls across different\n * layout modes (overlay, sidebar, bottom). These interfaces allow\n * control components to be decoupled from the core DocPlayer.\n *\n * Related Files:\n * - DocPlayer.tsx — Core player component\n * - DocProgressBar.tsx — Extracted progress bar\n * - DocControlsOverlay.tsx — Overlay controls (default)\n * - DocControlsSidebar.tsx — Vertical sidebar controls\n * - DocControlsBottom.tsx — Horizontal bottom strip controls\n */\n\nimport type { Block } from '@bendyline/squisq/schemas';\n\n/** Layout mode for doc player controls */\nexport type ControlsLayout = 'overlay' | 'sidebar' | 'bottom';\n\n/**\n * Display mode for DocPlayer.\n *\n * - `'video'` — Traditional video-style playback with play/pause, scrub bar,\n * and time-based auto-advance. Default mode.\n * - `'slideshow'` — PowerPoint-style presentation with prev/next navigation.\n * Blocks are treated as static slides that only change on user click.\n * No auto-advance, no scrub bar.\n * - `'linear'` — Long-scrolling document view. Renders the full markdown as\n * readable HTML with template-annotated sections shown as inline SVG cards.\n * No audio, no timeline, no controls — just a scrollable page.\n */\nexport type DisplayMode = 'video' | 'slideshow' | 'linear';\n\n/** Slide navigation actions for slideshow display mode */\nexport interface SlideNavActions {\n /** Navigate to the next slide */\n nextSlide: () => void;\n /** Navigate to the previous slide */\n prevSlide: () => void;\n /** Navigate to a specific slide by index (0-based) */\n goToSlide: (index: number) => void;\n}\n\n/** Playback state exposed to external control components */\nexport interface PlaybackState {\n isPlaying: boolean;\n currentTime: number;\n totalDuration: number;\n currentBlockIndex: number;\n totalBlocks: number;\n docProgress: number;\n hasCaptions: boolean;\n captionsEnabled: boolean;\n isFullscreen?: boolean;\n /** Current audio segment index (0-based) */\n currentSegmentIndex: number;\n /** Current audio segment name (e.g., 'intro', 'history') */\n currentSegmentName: string | null;\n /** Current block data (for extracting image info, etc.) */\n currentBlock: Block | null;\n}\n\n/** Playback actions exposed to external control components */\nexport interface PlaybackActions {\n toggle: () => void;\n restart: () => void;\n seekTo: (time: number) => void;\n setCaptionsEnabled: (enabled: boolean) => void;\n toggleFullscreen?: () => void;\n}\n\n/** Block marker data for the progress bar */\nexport interface BlockMarker {\n block: Block;\n index: number;\n position: number;\n title: string;\n /** True if this block is the first in a new audio segment (section boundary) */\n isSectionStart: boolean;\n}\n\n// ============================================\n// Render-mode API (exposed on window for Playwright / debug)\n// ============================================\n\n/** Block metadata returned by SquisqRenderAPI.getBlocks() */\nexport interface RenderBlockInfo {\n id: string;\n template: string;\n startTime: number;\n duration: number;\n}\n\n/** Audio segment info returned by SquisqRenderAPI.getAudioSegments() */\nexport interface RenderAudioSegmentInfo {\n src: string;\n name: string;\n duration: number;\n startTime: number;\n}\n\n/** Caption phrase info returned by SquisqRenderAPI.getCaptions() */\nexport interface RenderCaptionInfo {\n text: string;\n startTime: number;\n endTime: number;\n}\n\n/** Chapter marker returned by SquisqRenderAPI.getChapters() */\nexport interface RenderChapterInfo {\n title: string;\n startTime: number;\n duration: number;\n}\n\n/**\n * API surface exposed on `window` in render mode and debug mode.\n * Used by Playwright for video export and by ?debug=true for testing.\n *\n * @example\n * ```ts\n * // In Playwright:\n * const w = window as unknown as SquisqWindow;\n * await w.seekTo!(5.0);\n * const blocks = w.getBlocks!();\n * ```\n */\nexport interface SquisqRenderAPI {\n seekTo: (time: number) => Promise<void>;\n getDuration: () => number;\n getBlocks: () => RenderBlockInfo[];\n getAudioSegments: () => RenderAudioSegmentInfo[];\n getCaptions: () => RenderCaptionInfo[];\n getChapters: () => RenderChapterInfo[];\n showCover: () => Promise<void>;\n hideCover: () => Promise<void>;\n hasCoverBlock: () => boolean;\n}\n\n/**\n * Window augmented with optional SquisqRenderAPI properties.\n * Each property is optional because they're only present in render/debug mode.\n */\nexport type SquisqWindow = Window & typeof globalThis & Partial<SquisqRenderAPI>;\n\n/** Format time in seconds to MM:SS string */\nexport function formatTime(seconds: number): string {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n}\n","/**\n * DocControlsOverlay Component\n *\n * The default overlay controls that render at the bottom of the doc player\n * video with a gradient background. Contains all playback controls: restart,\n * play/pause, time display, progress bar with block markers, segment indicator,\n * and caption toggle.\n *\n * This is the original control layout extracted from DocPlayer. It's used\n * for the 'overlay' controls layout mode (Flow mode, landscape default).\n *\n * Related Files:\n * - DocPlayer.tsx -- Parent component\n * - DocProgressBar.tsx -- Progress bar sub-component\n * - types.ts -- Shared type definitions\n */\n\nimport { DocProgressBar } from './DocProgressBar';\nimport type { PlaybackState, PlaybackActions, BlockMarker } from './types';\nimport { formatTime } from './types';\nimport type { Block } from '@bendyline/squisq/schemas';\n\ninterface DocControlsOverlayProps {\n state: PlaybackState;\n actions: PlaybackActions;\n blockMarkers: BlockMarker[];\n expandedBlocks: Block[];\n getBlockTitle?: (block: Block) => string;\n}\n\nexport function DocControlsOverlay({\n state,\n actions,\n blockMarkers,\n expandedBlocks,\n getBlockTitle,\n}: DocControlsOverlayProps) {\n return (\n <div\n className=\"doc-player__controls\"\n style={{\n position: 'absolute',\n bottom: 0,\n left: 0,\n right: 0,\n padding: '12px 16px',\n background: 'linear-gradient(transparent, rgba(0,0,0,0.8))',\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n zIndex: 100,\n }}\n >\n {/* Restart button */}\n <button\n onClick={actions.restart}\n style={{\n background: 'none',\n border: 'none',\n color: 'rgba(255,255,255,0.7)',\n cursor: 'pointer',\n padding: '8px',\n fontSize: '14px',\n display: 'flex',\n alignItems: 'center',\n }}\n title=\"Restart\"\n aria-label=\"Restart from beginning\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n <path d=\"M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z\" />\n </svg>\n </button>\n\n {/* Play/Pause button */}\n <button\n onClick={actions.toggle}\n style={{\n background: 'rgba(255,255,255,0.2)',\n border: 'none',\n borderRadius: '50%',\n color: 'white',\n cursor: 'pointer',\n padding: '10px',\n fontSize: '16px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '40px',\n height: '40px',\n }}\n aria-label={state.isPlaying ? 'Pause' : 'Play'}\n >\n {state.isPlaying ? (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M6 19h4V5H6v14zm8-14v14h4V5h-4z\" />\n </svg>\n ) : (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M8 5v14l11-7z\" />\n </svg>\n )}\n </button>\n\n {/* Time display */}\n <span style={{ color: 'white', fontSize: '12px', minWidth: '80px', fontFamily: 'monospace' }}>\n {formatTime(state.currentTime)} / {formatTime(state.totalDuration)}\n </span>\n\n {/* Progress bar */}\n <DocProgressBar\n state={state}\n actions={actions}\n blockMarkers={blockMarkers}\n expandedBlocks={expandedBlocks}\n getBlockTitle={getBlockTitle}\n />\n\n {/* Segment indicator */}\n <span style={{ color: 'rgba(255,255,255,0.5)', fontSize: '11px', whiteSpace: 'nowrap' }}>\n {state.currentBlockIndex + 1}/{state.totalBlocks}\n </span>\n\n {/* Caption toggle button */}\n {state.hasCaptions && (\n <button\n onClick={() => actions.setCaptionsEnabled(!state.captionsEnabled)}\n style={{\n background: state.captionsEnabled ? 'rgba(255,255,255,0.2)' : 'none',\n border: 'none',\n color: state.captionsEnabled ? 'white' : 'rgba(255,255,255,0.5)',\n cursor: 'pointer',\n padding: '8px',\n fontSize: '12px',\n display: 'flex',\n alignItems: 'center',\n borderRadius: '4px',\n }}\n title={state.captionsEnabled ? 'Hide captions' : 'Show captions'}\n aria-label={state.captionsEnabled ? 'Hide captions' : 'Show captions'}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n <path d=\"M19 4H5a2 2 0 00-2 2v12a2 2 0 002 2h14a2 2 0 002-2V6a2 2 0 00-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1a1 1 0 01-1 1h-3a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1z\" />\n </svg>\n </button>\n )}\n\n {/* Fullscreen toggle button */}\n {actions.toggleFullscreen && (\n <button\n onClick={actions.toggleFullscreen}\n style={{\n background: state.isFullscreen ? 'rgba(255,255,255,0.2)' : 'none',\n border: 'none',\n color: state.isFullscreen ? 'white' : 'rgba(255,255,255,0.5)',\n cursor: 'pointer',\n padding: '8px',\n display: 'flex',\n alignItems: 'center',\n borderRadius: '4px',\n }}\n title={state.isFullscreen ? 'Exit fullscreen (F)' : 'Fullscreen (F)'}\n aria-label={state.isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}\n >\n {state.isFullscreen ? (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n <path d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\" />\n </svg>\n ) : (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n <path d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\" />\n </svg>\n )}\n </button>\n )}\n </div>\n );\n}\n","/**\n * DocControlsSlideshow Component\n *\n * Compact slide navigation controls for the DocPlayer slideshow display mode.\n * Renders in the lower-right corner with previous / slide counter / next buttons,\n * similar to PowerPoint/Keynote presentation navigation.\n *\n * Related Files:\n * - DocPlayer.tsx — Parent component (renders this when displayMode='slideshow')\n * - DocControlsOverlay.tsx — Video-mode controls (overlay layout)\n * - types.ts — Shared type definitions\n */\n\nimport type { PlaybackState, SlideNavActions } from './types';\n\ninterface DocControlsSlideshowProps {\n state: PlaybackState;\n slideNav: SlideNavActions;\n}\n\nexport function DocControlsSlideshow({ state, slideNav }: DocControlsSlideshowProps) {\n const { currentBlockIndex, totalBlocks } = state;\n const isFirst = currentBlockIndex <= 0;\n const isLast = currentBlockIndex >= totalBlocks - 1;\n\n return (\n <div\n className=\"doc-controls-slideshow\"\n data-testid=\"slideshow-controls\"\n style={{\n position: 'absolute',\n bottom: '16px',\n right: '16px',\n display: 'flex',\n alignItems: 'center',\n gap: '2px',\n background: 'rgba(0, 0, 0, 0.65)',\n borderRadius: '8px',\n padding: '4px 6px',\n zIndex: 100,\n userSelect: 'none',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n }}\n >\n {/* Previous button */}\n <button\n onClick={(e) => {\n e.stopPropagation();\n slideNav.prevSlide();\n }}\n disabled={isFirst}\n data-testid=\"slide-prev\"\n aria-label=\"Previous slide\"\n title=\"Previous slide\"\n style={{\n background: 'none',\n border: 'none',\n color: isFirst ? 'rgba(255,255,255,0.3)' : 'rgba(255,255,255,0.9)',\n cursor: isFirst ? 'default' : 'pointer',\n padding: '6px 8px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '4px',\n transition: 'background 0.15s',\n }}\n onMouseEnter={(e) => {\n if (!isFirst) e.currentTarget.style.background = 'rgba(255,255,255,0.1)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = 'none';\n }}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n <path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\" />\n </svg>\n </button>\n\n {/* Slide counter */}\n <span\n data-testid=\"slide-counter\"\n style={{\n color: 'rgba(255,255,255,0.9)',\n fontSize: '13px',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n fontVariantNumeric: 'tabular-nums',\n minWidth: '48px',\n textAlign: 'center',\n padding: '0 4px',\n letterSpacing: '0.02em',\n }}\n >\n {totalBlocks > 0 ? `${currentBlockIndex + 1} / ${totalBlocks}` : '—'}\n </span>\n\n {/* Next button */}\n <button\n onClick={(e) => {\n e.stopPropagation();\n slideNav.nextSlide();\n }}\n disabled={isLast}\n data-testid=\"slide-next\"\n aria-label=\"Next slide\"\n title=\"Next slide\"\n style={{\n background: 'none',\n border: 'none',\n color: isLast ? 'rgba(255,255,255,0.3)' : 'rgba(255,255,255,0.9)',\n cursor: isLast ? 'default' : 'pointer',\n padding: '6px 8px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '4px',\n transition: 'background 0.15s',\n }}\n onMouseEnter={(e) => {\n if (!isLast) e.currentTarget.style.background = 'rgba(255,255,255,0.1)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = 'none';\n }}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n <path d=\"M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z\" />\n </svg>\n </button>\n </div>\n );\n}\n","/**\n * LinearDocView Component\n *\n * Renders a Doc as a long-scrolling document view. Each block is displayed\n * as a readable section: non-annotated blocks render their markdown content\n * as HTML, while template-annotated blocks render as inline SVG visual cards\n * via BlockRenderer.\n *\n * This is the view used when `displayMode === 'linear'` in DocPlayer.\n *\n * Layout:\n * - Scrollable container with max-width for readability\n * - Headings from the block hierarchy rendered as HTML headings\n * - Body content rendered via MarkdownRenderer\n * - Template-annotated sections show an SVG card (BlockRenderer)\n * using `getLayers()` for on-demand layer computation\n * - Blocks are rendered recursively to preserve the heading hierarchy\n */\n\nimport { useMemo } from 'react';\nimport type { Doc, Block, DocBlock } from '@bendyline/squisq/schemas';\nimport type { ViewportConfig } from '@bendyline/squisq/schemas';\nimport type { Theme } from '@bendyline/squisq/schemas';\nimport { VIEWPORT_PRESETS } from '@bendyline/squisq/schemas';\nimport { getLayers, hasTemplate, DEFAULT_THEME } from '@bendyline/squisq/doc';\nimport type { RenderContext } from '@bendyline/squisq/doc';\nimport { extractPlainText } from '@bendyline/squisq/markdown';\nimport type { MarkdownBlockNode, MarkdownList } from '@bendyline/squisq/markdown';\nimport { BlockRenderer } from './BlockRenderer';\nimport { MarkdownRenderer } from './MarkdownRenderer';\n\n// ── Props ──────────────────────────────────────────────────────────\n\nexport interface LinearDocViewProps {\n /** The Doc to render */\n doc: Doc;\n /** Base path for resolving media URLs (images, etc.) */\n basePath?: string;\n /** Viewport config for SVG card rendering (default: landscape) */\n viewport?: ViewportConfig;\n /** Optional CSS class for the outer container */\n className?: string;\n /** Theme to use for rendering (default: DEFAULT_THEME from the theme library) */\n theme?: Theme;\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\n/**\n * Determine whether a block has a template annotation that should be\n * rendered as a visual SVG card. A block is \"annotated\" when:\n * 1. Its sourceHeading has a templateAnnotation, AND\n * 2. The annotated template exists in the registry\n */\nfunction isAnnotatedBlock(block: Block): boolean {\n const annotation = block.sourceHeading?.templateAnnotation;\n if (!annotation) return false;\n return hasTemplate(annotation.template);\n}\n\n/**\n * Count total blocks in a hierarchy (for RenderContext.totalBlocks).\n */\nfunction countAll(blocks: Block[]): number {\n let count = 0;\n for (const b of blocks) {\n count++;\n if (b.children) count += countAll(b.children);\n }\n return count;\n}\n\n// ── Block Section Renderer ─────────────────────────────────────────\n\ninterface BlockSectionProps {\n block: Block;\n basePath: string;\n viewport: ViewportConfig;\n renderContext: RenderContext;\n blockIndex: number;\n}\n\n/**\n * Render a single block section: heading + body content or SVG card.\n * Recurses into children to render the full heading tree.\n */\nfunction BlockSection({ block, basePath, viewport, renderContext, blockIndex }: BlockSectionProps) {\n const isAnnotated = isAnnotatedBlock(block);\n\n // For annotated blocks, compute layers and build a Block with them\n const visualBlock = useMemo(() => {\n if (!isAnnotated) return null;\n\n const annotation = block.sourceHeading!.templateAnnotation!;\n const headingText = extractPlainText(block.sourceHeading!);\n const bodyText = extractBodyPlainText(block.contents);\n\n // Build a TemplateBlock-compatible object\n const templateBlock: Record<string, unknown> = {\n id: block.id,\n template: annotation.template,\n startTime: 0,\n duration: 1,\n audioSegment: 0,\n title: headingText,\n ...getTemplateDefaults(annotation.template, headingText, bodyText, block.contents),\n ...annotation.params,\n ...block.templateOverrides,\n };\n\n // Compute layers via getLayers\n const ctx: RenderContext = {\n ...renderContext,\n blockIndex,\n };\n const layers = getLayers(templateBlock as unknown as DocBlock, ctx);\n\n return {\n ...block,\n layers,\n template: annotation.template,\n } as Block;\n }, [block, isAnnotated, renderContext, blockIndex]);\n\n return (\n <div\n className=\"squisq-linear-section\"\n data-block-id={block.id}\n data-template={isAnnotated ? block.sourceHeading?.templateAnnotation?.template : undefined}\n >\n {/* Render the heading (if present — preamble has no sourceHeading) */}\n {block.sourceHeading && !isAnnotated && <MarkdownRenderer nodes={[block.sourceHeading]} />}\n\n {/* Annotated block: render SVG card */}\n {isAnnotated && visualBlock && (\n <div className=\"squisq-linear-card\">\n {/* Optional heading label above the card */}\n {block.sourceHeading && (\n <div className=\"squisq-linear-card-label squisq-md\">\n <MarkdownRenderer nodes={[block.sourceHeading]} />\n </div>\n )}\n <div\n className=\"squisq-linear-card-svg\"\n style={{\n width: '100%',\n aspectRatio: `${viewport.width} / ${viewport.height}`,\n overflow: 'hidden',\n borderRadius: '8px',\n boxShadow: '0 2px 8px rgba(0, 0, 0, 0.12)',\n marginBottom: '1em',\n }}\n >\n <BlockRenderer\n block={visualBlock}\n blockTime={0}\n basePath={basePath}\n viewport={viewport}\n />\n </div>\n </div>\n )}\n\n {/* Body content (always render for non-annotated blocks, skipped for annotated) */}\n {!isAnnotated && block.contents && block.contents.length > 0 && (\n <MarkdownRenderer nodes={block.contents} />\n )}\n\n {/* Recurse into children */}\n {block.children && block.children.length > 0 && (\n <div className=\"squisq-linear-children\">\n {block.children.map((child, i) => (\n <BlockSection\n key={child.id}\n block={child}\n basePath={basePath}\n viewport={viewport}\n renderContext={renderContext}\n blockIndex={blockIndex + i + 1}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// ── Template Defaults (mirrored from PreviewPanel) ─────────────────\n\n/** Extract plain text from block contents. */\nfunction extractBodyPlainText(contents?: MarkdownBlockNode[]): string {\n if (!contents || contents.length === 0) return '';\n return contents\n .map((n) => extractPlainText(n))\n .join('\\n')\n .trim();\n}\n\n/** Extract list items as plain text. */\nfunction extractListItems(contents?: MarkdownBlockNode[]): string[] {\n if (!contents) return [];\n const items: string[] = [];\n for (const node of contents) {\n if (node.type === 'list') {\n for (const item of (node as MarkdownList).children) {\n const text = extractPlainText(item).trim();\n if (text) items.push(text);\n }\n }\n }\n return items;\n}\n\n/**\n * Provide sensible default fields for templates that require more than\n * just a `title`. Prevents crashes from undefined required fields.\n */\nfunction getTemplateDefaults(\n templateName: string,\n headingText: string,\n bodyText: string,\n contents?: MarkdownBlockNode[],\n): Record<string, unknown> {\n switch (templateName) {\n case 'statHighlight':\n return { stat: headingText, description: bodyText || headingText };\n case 'quoteBlock':\n case 'fullBleedQuote':\n case 'pullQuote':\n return { quote: bodyText || headingText };\n case 'factCard':\n return { fact: headingText, explanation: bodyText || headingText };\n case 'comparisonBar':\n return { leftLabel: 'A', leftValue: 60, rightLabel: 'B', rightValue: 40 };\n case 'listBlock':\n return { items: extractListItems(contents) || ['Item 1', 'Item 2', 'Item 3'] };\n case 'definitionCard':\n return { term: headingText, definition: bodyText || headingText };\n case 'dateEvent':\n return { date: headingText, description: bodyText || headingText };\n default:\n return {};\n }\n}\n\n// ── Main Component ─────────────────────────────────────────────────\n\n/**\n * Renders a Doc as a long-scrolling, readable document.\n *\n * Non-annotated blocks are rendered as HTML text (headings, paragraphs,\n * lists, etc.) via MarkdownRenderer. Template-annotated blocks are\n * rendered as inline SVG visual cards via BlockRenderer.\n *\n * @example\n * ```tsx\n * <LinearDocView doc={doc} basePath=\"/media/\" />\n * ```\n */\nexport function LinearDocView({\n doc,\n basePath = '/',\n viewport,\n className,\n theme,\n}: LinearDocViewProps) {\n const activeViewport = viewport ?? VIEWPORT_PRESETS.landscape;\n const totalBlocks = useMemo(() => countAll(doc.blocks), [doc.blocks]);\n\n const renderContext: RenderContext = useMemo(\n () => ({\n theme: theme ?? DEFAULT_THEME,\n viewport: activeViewport,\n totalBlocks,\n }),\n [activeViewport, totalBlocks, theme],\n );\n\n return (\n <div\n className={`squisq-linear ${className || ''}`}\n style={{\n width: '100%',\n height: '100%',\n overflowY: 'auto',\n overflowX: 'hidden',\n }}\n >\n <div\n className=\"squisq-linear-content\"\n style={{\n maxWidth: '720px',\n margin: '0 auto',\n padding: '24px 16px',\n lineHeight: 1.7,\n fontSize: '16px',\n color: 'var(--squisq-text, #1f2937)',\n }}\n >\n {doc.blocks.map((block, i) => (\n <BlockSection\n key={block.id}\n block={block}\n basePath={basePath}\n viewport={activeViewport}\n renderContext={renderContext}\n blockIndex={i}\n />\n ))}\n </div>\n </div>\n );\n}\n","/**\n * MarkdownRenderer Component\n *\n * Converts a MarkdownBlockNode[] AST into React elements for rendering\n * markdown content as readable HTML. Used by LinearDocView to display\n * non-annotated document sections as flowing text.\n *\n * Supports all block and inline node types from the markdown DOM:\n * - Block: paragraph, heading, blockquote, list, code, table,\n * thematicBreak, math, htmlBlock, definitionList, directives\n * - Inline: text, emphasis, strong, delete, inlineCode, link,\n * image, break, inlineMath, htmlInline, footnoteReference\n *\n * All elements use the `squisq-md-*` CSS class prefix for styling.\n */\n\nimport { Fragment } from 'react';\nimport type {\n MarkdownBlockNode,\n MarkdownInlineNode,\n MarkdownListItem,\n MarkdownTableRow,\n MarkdownTableCell,\n} from '@bendyline/squisq/markdown';\n\n// ── Props ──────────────────────────────────────────────────────────\n\nexport interface MarkdownRendererProps {\n /** Block-level AST nodes to render */\n nodes: MarkdownBlockNode[];\n /** Optional CSS class for the wrapper element */\n className?: string;\n}\n\n// ── Inline Renderer ────────────────────────────────────────────────\n\n/** Render an array of inline nodes into React elements. */\nfunction renderInline(nodes: MarkdownInlineNode[], keyPrefix = ''): React.ReactNode[] {\n return nodes.map((node, i) => {\n const key = `${keyPrefix}i${i}`;\n switch (node.type) {\n case 'text':\n return <Fragment key={key}>{node.value}</Fragment>;\n\n case 'emphasis':\n return (\n <em key={key} className=\"squisq-md-em\">\n {renderInline(node.children, key)}\n </em>\n );\n\n case 'strong':\n return (\n <strong key={key} className=\"squisq-md-strong\">\n {renderInline(node.children, key)}\n </strong>\n );\n\n case 'delete':\n return (\n <del key={key} className=\"squisq-md-del\">\n {renderInline(node.children, key)}\n </del>\n );\n\n case 'inlineCode':\n return (\n <code key={key} className=\"squisq-md-inline-code\">\n {node.value}\n </code>\n );\n\n case 'link':\n return (\n <a\n key={key}\n className=\"squisq-md-link\"\n href={node.url}\n title={node.title ?? undefined}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {renderInline(node.children, key)}\n </a>\n );\n\n case 'image':\n return (\n <img\n key={key}\n className=\"squisq-md-image\"\n src={node.url}\n alt={node.alt ?? ''}\n title={node.title ?? undefined}\n />\n );\n\n case 'break':\n return <br key={key} />;\n\n case 'inlineMath':\n return (\n <code key={key} className=\"squisq-md-inline-math\">\n {node.value}\n </code>\n );\n\n case 'htmlInline':\n return (\n <span\n key={key}\n className=\"squisq-md-html-inline\"\n dangerouslySetInnerHTML={{ __html: node.rawHtml }}\n />\n );\n\n case 'footnoteReference':\n return (\n <sup key={key} className=\"squisq-md-footnote-ref\">\n <a href={`#fn-${node.identifier}`}>[{node.label ?? node.identifier}]</a>\n </sup>\n );\n\n case 'linkReference':\n // Render as plain text (definition targets not available at render time)\n return (\n <span key={key} className=\"squisq-md-link-ref\">\n {renderInline(node.children, key)}\n </span>\n );\n\n case 'imageReference':\n return (\n <span key={key} className=\"squisq-md-image-ref\">\n [{node.alt ?? node.identifier}]\n </span>\n );\n\n case 'textDirective':\n return (\n <span key={key} className=\"squisq-md-text-directive\" data-directive={node.name}>\n {renderInline(node.children, key)}\n </span>\n );\n\n default:\n return null;\n }\n });\n}\n\n// ── Block Renderer ─────────────────────────────────────────────────\n\n/** Render a single block-level node into a React element. */\nfunction renderBlock(node: MarkdownBlockNode, key: string): React.ReactNode {\n switch (node.type) {\n case 'paragraph':\n return (\n <p key={key} className=\"squisq-md-p\">\n {renderInline(node.children, key)}\n </p>\n );\n\n case 'heading': {\n const Tag = `h${node.depth}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';\n return (\n <Tag key={key} className={`squisq-md-heading squisq-md-h${node.depth}`}>\n {renderInline(node.children, key)}\n </Tag>\n );\n }\n\n case 'blockquote':\n return (\n <blockquote key={key} className=\"squisq-md-blockquote\">\n {renderBlocks(node.children, key)}\n </blockquote>\n );\n\n case 'list':\n if (node.ordered) {\n return (\n <ol key={key} className=\"squisq-md-list squisq-md-ol\" start={node.start ?? undefined}>\n {node.children.map((item, i) => renderListItem(item, `${key}li${i}`))}\n </ol>\n );\n }\n return (\n <ul key={key} className=\"squisq-md-list squisq-md-ul\">\n {node.children.map((item, i) => renderListItem(item, `${key}li${i}`))}\n </ul>\n );\n\n case 'code':\n return (\n <pre key={key} className=\"squisq-md-code-block\">\n <code className={node.lang ? `language-${node.lang}` : undefined}>{node.value}</code>\n </pre>\n );\n\n case 'thematicBreak':\n return <hr key={key} className=\"squisq-md-hr\" />;\n\n case 'table':\n return renderTable(node.children, node.align, key);\n\n case 'htmlBlock':\n return (\n <div\n key={key}\n className=\"squisq-md-html-block\"\n dangerouslySetInnerHTML={{ __html: node.rawHtml }}\n />\n );\n\n case 'math':\n return (\n <pre key={key} className=\"squisq-md-math-block\">\n <code>{node.value}</code>\n </pre>\n );\n\n case 'definition':\n // Link definitions aren't rendered visually\n return null;\n\n case 'footnoteDefinition':\n return (\n <div key={key} className=\"squisq-md-footnote-def\" id={`fn-${node.identifier}`}>\n <sup>{node.label ?? node.identifier}</sup>\n {renderBlocks(node.children, key)}\n </div>\n );\n\n case 'containerDirective':\n return (\n <div\n key={key}\n className={`squisq-md-directive squisq-md-directive-${node.name}`}\n data-directive={node.name}\n >\n {node.label && <div className=\"squisq-md-directive-label\">{node.label}</div>}\n {renderBlocks(node.children, key)}\n </div>\n );\n\n case 'leafDirective':\n return (\n <div\n key={key}\n className={`squisq-md-directive squisq-md-directive-${node.name}`}\n data-directive={node.name}\n >\n {renderInline(node.children, key)}\n </div>\n );\n\n case 'definitionList':\n return (\n <dl key={key} className=\"squisq-md-dl\">\n {node.children.map((child, i) => {\n if (child.type === 'definitionTerm') {\n return (\n <dt key={`${key}dt${i}`} className=\"squisq-md-dt\">\n {renderInline(child.children, `${key}dt${i}`)}\n </dt>\n );\n }\n return (\n <dd key={`${key}dd${i}`} className=\"squisq-md-dd\">\n {renderBlocks(child.children, `${key}dd${i}`)}\n </dd>\n );\n })}\n </dl>\n );\n\n default:\n return null;\n }\n}\n\n/** Render a list item, including task-list checkbox support. */\nfunction renderListItem(item: MarkdownListItem, key: string): React.ReactNode {\n const isTask = item.checked !== null && item.checked !== undefined;\n return (\n <li key={key} className={`squisq-md-li${isTask ? ' squisq-md-task' : ''}`}>\n {isTask && (\n <input type=\"checkbox\" checked={!!item.checked} readOnly className=\"squisq-md-checkbox\" />\n )}\n {renderBlocks(item.children, key)}\n </li>\n );\n}\n\n/** Render a table from rows and alignment data. */\nfunction renderTable(\n rows: MarkdownTableRow[],\n align: (('left' | 'right' | 'center') | null)[] | undefined,\n key: string,\n): React.ReactNode {\n const [headerRow, ...bodyRows] = rows;\n return (\n <table key={key} className=\"squisq-md-table\">\n {headerRow && (\n <thead>\n <tr>\n {headerRow.children.map((cell: MarkdownTableCell, ci: number) => (\n <th\n key={`${key}th${ci}`}\n className=\"squisq-md-th\"\n style={align?.[ci] ? { textAlign: align[ci]! } : undefined}\n >\n {renderInline(cell.children, `${key}th${ci}`)}\n </th>\n ))}\n </tr>\n </thead>\n )}\n {bodyRows.length > 0 && (\n <tbody>\n {bodyRows.map((row, ri) => (\n <tr key={`${key}tr${ri}`}>\n {row.children.map((cell: MarkdownTableCell, ci: number) => (\n <td\n key={`${key}td${ri}-${ci}`}\n className=\"squisq-md-td\"\n style={align?.[ci] ? { textAlign: align[ci]! } : undefined}\n >\n {renderInline(cell.children, `${key}td${ri}-${ci}`)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n )}\n </table>\n );\n}\n\n/** Render an array of block-level nodes. */\nfunction renderBlocks(nodes: MarkdownBlockNode[], keyPrefix = ''): React.ReactNode[] {\n return nodes.map((node, i) => renderBlock(node, `${keyPrefix}b${i}`));\n}\n\n// ── Main Component ─────────────────────────────────────────────────\n\n/**\n * Renders MarkdownBlockNode[] AST as React HTML elements.\n *\n * @example\n * ```tsx\n * <MarkdownRenderer nodes={block.contents} />\n * ```\n */\nexport function MarkdownRenderer({ nodes, className }: MarkdownRendererProps) {\n if (!nodes || nodes.length === 0) return null;\n\n return <div className={`squisq-md ${className || ''}`}>{renderBlocks(nodes)}</div>;\n}\n","/**\n * DocControlsBottom Component\n *\n * Horizontal control strip that renders below the doc player video.\n * An alternative to the overlay for landscape mode -- controls sit in a\n * separate bar below the video instead of overlapping the content.\n *\n * Contains: restart, play/pause, time display, progress bar, segment\n * indicator, CC toggle. Same controls as the overlay but in an external bar.\n *\n * Related Files:\n * - DocControlsOverlay.tsx -- Overlay variant (default landscape)\n * - DocControlsSidebar.tsx -- Sidebar variant (portrait)\n * - DocProgressBar.tsx -- Shared progress bar component\n * - types.ts -- PlaybackState/PlaybackActions interfaces\n */\n\nimport { DocProgressBar } from './DocProgressBar';\nimport type { PlaybackState, PlaybackActions, BlockMarker } from './types';\nimport { formatTime } from './types';\nimport type { Block } from '@bendyline/squisq/schemas';\n\ninterface DocControlsBottomProps {\n state: PlaybackState;\n actions: PlaybackActions;\n blockMarkers: BlockMarker[];\n expandedBlocks: Block[];\n getBlockTitle?: (block: Block) => string;\n}\n\nexport function DocControlsBottom({\n state,\n actions,\n blockMarkers,\n expandedBlocks,\n getBlockTitle,\n}: DocControlsBottomProps) {\n return (\n <div className=\"doc-controls-bottom\">\n {/* Restart button */}\n <button\n className=\"bottom-ctrl-btn\"\n onClick={actions.restart}\n title=\"Restart\"\n aria-label=\"Restart from beginning\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n <path d=\"M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z\" />\n </svg>\n </button>\n\n {/* Play/Pause button */}\n <button\n className=\"bottom-ctrl-btn bottom-play-btn\"\n onClick={actions.toggle}\n aria-label={state.isPlaying ? 'Pause' : 'Play'}\n >\n {state.isPlaying ? (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M6 19h4V5H6v14zm8-14v14h4V5h-4z\" />\n </svg>\n ) : (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M8 5v14l11-7z\" />\n </svg>\n )}\n </button>\n\n {/* Time display */}\n <span className=\"bottom-time\">\n {formatTime(state.currentTime)} / {formatTime(state.totalDuration)}\n </span>\n\n {/* Progress bar */}\n <DocProgressBar\n state={state}\n actions={actions}\n blockMarkers={blockMarkers}\n expandedBlocks={expandedBlocks}\n getBlockTitle={getBlockTitle}\n />\n\n {/* Segment indicator */}\n <span className=\"bottom-segment\">\n {state.currentBlockIndex + 1}/{state.totalBlocks}\n </span>\n\n {/* Caption toggle */}\n {state.hasCaptions && (\n <button\n className={`bottom-ctrl-btn ${state.captionsEnabled ? 'bottom-ctrl-btn--active' : ''}`}\n onClick={() => actions.setCaptionsEnabled(!state.captionsEnabled)}\n title={state.captionsEnabled ? 'Hide captions' : 'Show captions'}\n aria-label={state.captionsEnabled ? 'Hide captions' : 'Show captions'}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n <path d=\"M19 4H5a2 2 0 00-2 2v12a2 2 0 002 2h14a2 2 0 002-2V6a2 2 0 00-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1a1 1 0 01-1 1h-3a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1z\" />\n </svg>\n </button>\n )}\n </div>\n );\n}\n","/**\n * DocControlsSidebar Component\n *\n * Vertical control strip that renders alongside the doc player video.\n * Used in portrait orientation to eliminate black bars -- controls sit in a\n * \"tab\" to the right of the 9:16 video instead of overlaying it.\n *\n * Contains: restart, play/pause, time display, segment indicator, CC toggle.\n * Does NOT contain the scrubber/progress bar (that stays in the video via\n * DocPlayer's showScrubber prop).\n *\n * The sidebar stretches to match the full height of the video and has rounded\n * corners on the right side to create a connected \"tab\" appearance.\n *\n * Related Files:\n * - DocPlayerWithSidebar.tsx -- Wrapper that composes player + sidebar\n * - DocPlayer.tsx -- Core player (renders with showScrubber=true)\n * - types.ts -- PlaybackState/PlaybackActions interfaces\n */\n\nimport type { PlaybackState, PlaybackActions } from './types';\nimport { formatTime } from './types';\n\ninterface DocControlsSidebarProps {\n state: PlaybackState;\n actions: PlaybackActions;\n}\n\nexport function DocControlsSidebar({ state, actions }: DocControlsSidebarProps) {\n return (\n <div className=\"doc-controls-sidebar\">\n {/* Restart button */}\n <button\n className=\"sidebar-ctrl-btn\"\n onClick={actions.restart}\n title=\"Restart\"\n aria-label=\"Restart from beginning\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z\" />\n </svg>\n </button>\n\n {/* Play/Pause button */}\n <button\n className=\"sidebar-ctrl-btn sidebar-play-btn\"\n onClick={actions.toggle}\n aria-label={state.isPlaying ? 'Pause' : 'Play'}\n >\n {state.isPlaying ? (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"22\" height=\"22\">\n <path d=\"M6 19h4V5H6v14zm8-14v14h4V5h-4z\" />\n </svg>\n ) : (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"22\" height=\"22\">\n <path d=\"M8 5v14l11-7z\" />\n </svg>\n )}\n </button>\n\n {/* Time display -- stacked current / total */}\n <div className=\"sidebar-time\">\n <div>{formatTime(state.currentTime)}</div>\n <div className=\"sidebar-time-total\">{formatTime(state.totalDuration)}</div>\n </div>\n\n {/* Segment indicator */}\n <div className=\"sidebar-segment\">\n {state.currentBlockIndex + 1}/{state.totalBlocks}\n </div>\n\n {/* Caption toggle */}\n {state.hasCaptions && (\n <button\n className={`sidebar-ctrl-btn ${state.captionsEnabled ? 'sidebar-ctrl-btn--active' : ''}`}\n onClick={() => actions.setCaptionsEnabled(!state.captionsEnabled)}\n title={state.captionsEnabled ? 'Hide captions' : 'Show captions'}\n aria-label={state.captionsEnabled ? 'Hide captions' : 'Show captions'}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M19 4H5a2 2 0 00-2 2v12a2 2 0 002 2h14a2 2 0 002-2V6a2 2 0 00-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1a1 1 0 01-1 1h-3a1 1 0 01-1-1v-4a1 1 0 011-1h3a1 1 0 011 1v1z\" />\n </svg>\n </button>\n )}\n\n {/* Fullscreen toggle */}\n {actions.toggleFullscreen && (\n <button\n className={`sidebar-ctrl-btn ${state.isFullscreen ? 'sidebar-ctrl-btn--active' : ''}`}\n onClick={actions.toggleFullscreen}\n title={state.isFullscreen ? 'Exit fullscreen (F)' : 'Fullscreen (F)'}\n aria-label={state.isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}\n >\n {state.isFullscreen ? (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\" />\n </svg>\n ) : (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\" />\n </svg>\n )}\n </button>\n )}\n </div>\n );\n}\n","/**\n * DocPlayerWithSidebar Component\n *\n * Wrapper that composes DocPlayer + DocControlsSidebar in a horizontal\n * flex layout. The video renders on the left with only a scrubber at the\n * bottom, and playback controls render in a vertical sidebar on the right.\n *\n * This layout eliminates black bars for portrait (9:16) video in portrait\n * viewports by placing controls in the space that would otherwise be unused.\n *\n * Uses refs + forceUpdate pattern to avoid infinite render loops between\n * the DocPlayer's callbacks and this component's state. The DocPlayer\n * fires onPlaybackStateChange frequently (every currentTime tick), so we\n * store state in a ref and only trigger re-renders at controlled intervals.\n *\n * Related Files:\n * - DocPlayer.tsx -- Core player (renders with showControls=false, showScrubber=true)\n * - DocControlsSidebar.tsx -- Vertical sidebar controls\n */\n\nimport { useRef, useState, useCallback, useEffect } from 'react';\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport type { ViewportConfig } from '@bendyline/squisq/schemas';\nimport type { AudioProvider } from './hooks/AudioProvider';\nimport { DocPlayer } from './DocPlayer';\nimport { DocControlsSidebar } from './DocControlsSidebar';\nimport type { PlaybackState, PlaybackActions } from './types';\n\ninterface DocPlayerWithSidebarProps {\n script: Doc;\n basePath: string;\n autoPlay?: boolean;\n onEnded?: () => void;\n onTimeUpdate?: (time: number) => void;\n audioProvider?: AudioProvider;\n muted?: boolean;\n captionsEnabled?: boolean;\n isFullscreen?: boolean;\n onFullscreenToggle?: () => void;\n /** Force a specific viewport preset, bypassing window-based orientation detection. */\n forceViewport?: ViewportConfig;\n /** Called when playing state changes */\n onPlayingChange?: (isPlaying: boolean) => void;\n}\n\nconst DEFAULT_STATE: PlaybackState = {\n isPlaying: false,\n currentTime: 0,\n totalDuration: 0,\n currentBlockIndex: 0,\n totalBlocks: 0,\n docProgress: 0,\n hasCaptions: false,\n captionsEnabled: false,\n currentSegmentIndex: 0,\n currentSegmentName: null,\n currentBlock: null,\n};\n\nexport function DocPlayerWithSidebar({\n script,\n basePath,\n autoPlay = false,\n onEnded,\n onTimeUpdate,\n audioProvider,\n muted,\n captionsEnabled,\n isFullscreen,\n onFullscreenToggle,\n forceViewport,\n onPlayingChange,\n}: DocPlayerWithSidebarProps) {\n // Store playback state in a ref to avoid triggering re-renders from DocPlayer callbacks\n const stateRef = useRef<PlaybackState>(DEFAULT_STATE);\n const actionsRef = useRef<PlaybackActions | null>(null);\n const wasPlayingRef = useRef(false);\n\n // Use a counter to force sidebar re-renders at controlled intervals\n const [, setTick] = useState(0);\n\n // Update ref without triggering re-render\n const handleStateChange = useCallback(\n (state: PlaybackState) => {\n stateRef.current = state;\n if (onPlayingChange && state.isPlaying !== wasPlayingRef.current) {\n wasPlayingRef.current = state.isPlaying;\n onPlayingChange(state.isPlaying);\n }\n },\n [onPlayingChange],\n );\n\n const handleControlsReady = useCallback(\n (controls: PlaybackActions & { play: () => void; pause: () => void }) => {\n const isFirst = !actionsRef.current;\n actionsRef.current = controls;\n // Force one re-render on first call to show the sidebar\n if (isFirst) setTick((t) => t + 1);\n },\n [],\n );\n\n // Periodically sync the ref to trigger sidebar UI updates\n useEffect(() => {\n const interval = setInterval(() => {\n setTick((t) => t + 1);\n }, 250); // 4 updates per second is smooth enough for time/progress display\n return () => clearInterval(interval);\n }, []);\n\n return (\n <div className=\"doc-player-sidebar-layout\">\n <div className=\"doc-player-sidebar-layout__video\">\n <DocPlayer\n script={script}\n basePath={basePath}\n autoPlay={autoPlay}\n onEnded={onEnded}\n onTimeUpdate={onTimeUpdate}\n audioProvider={audioProvider}\n muted={muted}\n captionsEnabled={captionsEnabled}\n showControls={isFullscreen}\n showScrubber={!isFullscreen}\n onPlaybackStateChange={handleStateChange}\n onControlsReady={handleControlsReady}\n isFullscreen={isFullscreen}\n onFullscreenToggle={onFullscreenToggle}\n forceViewport={forceViewport}\n />\n </div>\n {actionsRef.current && (\n <DocControlsSidebar state={stateRef.current} actions={actionsRef.current} />\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["Fragment","useRef","useState","useEffect","useCallback","useMemo","isTemplateBlock","getCaptionAtTime","jsx","jsx","jsxs","useState","useEffect","jsx","jsxs","useState","useEffect","useEffect","jsx","useEffect","jsx","jsxs","jsx","useState","useEffect","useRef","useState","useEffect","useMemo","useCallback","useRef","useState","useEffect","useMemo","VIEWPORT_PRESETS","DEFAULT_THEME","VIEWPORT_PRESETS","useRef","useState","useCallback","jsx","jsxs","useRef","useState","useCallback","jsx","jsxs","jsx","jsxs","useMemo","VIEWPORT_PRESETS","jsx","jsxs","jsx","jsxs","useMemo","VIEWPORT_PRESETS","jsx","jsxs","isTemplateBlock","useRef","useState","VIEWPORT_PRESETS","useMemo","useCallback","DEFAULT_THEME","useEffect","getCaptionAtTime","Fragment","jsx","jsxs","jsx","jsxs","useRef","useState","useCallback","useEffect","jsx","jsxs","useRef","useState","useCallback","useEffect"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAClB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,mBAAmB;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,QAAQ;AACR,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAkBtF,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,YAAY;AACZ,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}