@bendyline/squisq-react 1.4.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -23
- package/dist/index.d.ts +81 -21
- package/dist/index.js +461 -178
- package/dist/index.js.map +1 -1
- package/dist/squisq-player.css +1 -1
- package/dist/squisq-player.css.map +1 -1
- package/dist/squisq-player.global.js +49 -17
- package/dist/squisq-player.global.js.map +1 -1
- package/dist/standalone-source.js +1 -1
- package/dist/styles/index.css +2263 -0
- package/package.json +8 -5
- package/src/DocControlsSlideshow.tsx +13 -2
- package/src/DocPlayer.tsx +255 -58
- package/src/DocPlayerWithSidebar.tsx +21 -9
- package/src/LinearDocView.tsx +59 -10
- package/src/MarkdownRenderer.tsx +52 -35
- package/src/__tests__/DocPlayer.test.tsx +85 -6
- package/src/__tests__/DocPlayerStylesSentinel.test.tsx +41 -0
- package/src/__tests__/LinearDocView.test.tsx +53 -1
- package/src/__tests__/MarkdownRenderer.test.tsx +18 -0
- package/src/__tests__/useJsonViewTokens.test.ts +41 -0
- package/src/__tests__/useSlideSwipe.test.ts +81 -0
- package/src/hooks/{AudioProvider.ts → AudioController.ts} +3 -3
- package/src/hooks/index.ts +7 -2
- package/src/hooks/useAudioSync.ts +5 -4
- package/src/hooks/useSlideSwipe.ts +265 -0
- package/src/index.ts +1 -1
- package/src/jsonView/useJsonViewTokens.ts +6 -31
- package/src/standalone-entry.tsx +1 -1
- package/src/styles/doc-animations.css +46 -0
- package/src/types.ts +6 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/DocPlayer.tsx","../src/MediaClipLayer.tsx","../src/hooks/MediaContext.tsx","../src/hooks/useMediaSchedule.ts","../src/BlockRenderer.tsx","../src/layers/ImageLayer.tsx","../src/utils/animationUtils.ts","../src/utils/layerUtils.ts","../src/layers/TextLayer.tsx","../src/utils/fillStyle.tsx","../src/layers/ShapeLayer.tsx","../src/layers/PathLayer.tsx","../src/layers/MapLayer.tsx","../src/utils/mapTileUtils.ts","../src/layers/VideoLayer.tsx","../src/layers/TableLayer.tsx","../src/CaptionOverlay.tsx","../src/SocialCaptionOverlay.tsx","../src/hooks/useAutoSurface.ts","../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/InlineVideoPlayer.tsx","../src/InlineAudioPlayer.tsx","../src/DocControlsBottom.tsx","../src/DocControlsSidebar.tsx","../src/DocPlayerWithSidebar.tsx","../src/jsonView/useJsonViewTokens.ts","../src/jsonView/RenderNode.tsx","../src/jsonView/viewers.tsx","../src/jsonView/JsonView.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 {\n isTemplateBlock,\n getCaptionAtTime,\n resolveMediaSchedule,\n getDocPlaybackDuration,\n} from '@bendyline/squisq/schemas';\nimport { MediaClipLayer } from './MediaClipLayer';\nimport type { SurfaceScheme, Theme } from '@bendyline/squisq/schemas';\nimport { applySurface } from '@bendyline/squisq/schemas';\nimport { BlockRenderer } from './BlockRenderer';\nimport { CaptionOverlay } from './CaptionOverlay';\nimport { useAutoSurface } from './hooks/useAutoSurface';\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 CaptionStyle,\n CaptionMode,\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 * Optional surface scheme (light / dark paper) overlaid on top of the\n * theme's colors. Passed through to the underlying LinearDocView when\n * `displayMode === 'linear'`; otherwise overlaid onto the theme that\n * renders the player's SVG blocks.\n */\n surface?: SurfaceScheme | 'auto';\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 /** Caption display style (default: 'standard').\n * 'social' shows large centered words with the active word highlighted. */\n captionStyle?: CaptionStyle;\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 surface,\n captionStyle = 'standard',\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 // Timed media clips (block.media + doc.documentMedia) resolved to absolute\n // doc-timeline coordinates. Empty for documents without the media model, so\n // <MediaClipLayer> renders nothing and the legacy audio path is unaffected.\n const mediaSchedule = useMemo(() => resolveMediaSchedule(script), [script]);\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 // Resolve surface (light/dark paper) and apply it to the theme before\n // handing off to downstream renderers. Orthogonal to the editorial theme.\n const autoSurface = useAutoSurface(surface === 'auto');\n const resolvedSurface = surface === 'auto' ? autoSurface : surface;\n const effectiveTheme = useMemo(() => {\n const base = theme ?? DEFAULT_THEME;\n return resolvedSurface ? applySurface(base, resolvedSurface) : base;\n }, [theme, resolvedSurface]);\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, effectiveTheme);\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(effectiveTheme, 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, effectiveTheme]);\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 // Track whether playback has ever been initiated — prevents the cover block\n // from re-appearing when paused at currentTime === 0 (e.g., no audio source).\n const hasPlayedOnce = useRef(false);\n\n // Track when cover is showing at rest (before play)\n const atRest = !!(\n coverBlock &&\n !isPlaying &&\n currentTime === 0 &&\n !hasPlayedOnce.current &&\n !renderMode &&\n !autoPlay\n );\n if (atRest) coverWasShowing.current = true;\n\n useEffect(() => {\n if (isPlaying && coverWasShowing.current && coverBlock && !renderMode) {\n coverWasShowing.current = false;\n hasPlayedOnce.current = true;\n setCoverGraceActive(true);\n // Intentionally no cleanup: if coverBlock's memoized reference changes\n // mid-grace (e.g., due to a preview re-render), clearing the timer would\n // leave coverGraceActive stuck at true because the effect body won't\n // re-run (coverWasShowing.current is now false).\n coverGraceTimer.current = setTimeout(() => setCoverGraceActive(false), 3000);\n }\n }, [isPlaying, coverBlock, renderMode]);\n\n // Always clear the grace timer on unmount\n useEffect(() => () => clearTimeout(coverGraceTimer.current), []);\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 && !hasPlayedOnce.current && !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 // Honor the per-clip startAt offset: before it, hold at the\n // in-point; after, advance by (blockElapsed - startAt).\n const startAt = parseFloat(video.dataset.startAt || '0');\n const targetTime = Math.min(\n clipStart + Math.max(0, blockElapsed - startAt),\n clipEnd,\n );\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 // Seek player-level scheduled videos (document-spanning clips\n // rendered by MediaClipLayer, outside any single block). Each\n // carries data-abs-start/data-abs-end/data-source-in.\n document.querySelectorAll('video[data-clip-id]').forEach((el) => {\n const video = el as HTMLVideoElement;\n const absStart = parseFloat(video.dataset.absStart || '0');\n const absEnd = parseFloat(video.dataset.absEnd || '0');\n const sourceIn = parseFloat(video.dataset.sourceIn || '0');\n video.pause();\n if (time < absStart || time >= absEnd) return;\n const targetTime = sourceIn + (time - absStart);\n video.currentTime = targetTime;\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);\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 = () => {\n // The larger of the audio/block timeline and any media that spills\n // past the last block (block-clip spillover or document-spanning\n // media), so frame capture covers the full tail.\n const mediaDuration = getDocPlaybackDuration(script);\n if (totalDuration > 0) return Math.max(totalDuration, mediaDuration);\n return mediaDuration;\n };\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 // Caption mode state: cycles through off → standard → social → off\n // The captionStyle prop sets the default active style; captionsEnabledProp\n // can override the initial on/off state.\n const defaultMode: CaptionMode =\n captionsEnabledProp === false ? 'off' : captionStyle || 'standard';\n const [captionMode, setCaptionMode] = useState<CaptionMode>(defaultMode);\n\n // Derive captionsEnabled and active style from the mode\n const captionsEnabled = captionMode !== 'off';\n const activeCaptionStyle: CaptionStyle = captionMode === 'social' ? 'social' : 'standard';\n\n const setCaptionsEnabled = useCallback(\n (enabled: boolean) => {\n // When re-enabling, restore the prop-specified style rather than\n // always defaulting to 'standard'\n setCaptionMode(enabled ? captionStyle || 'standard' : 'off');\n onCaptionsToggle?.(enabled);\n },\n [onCaptionsToggle, captionStyle],\n );\n\n const cycleCaptionMode = useCallback(() => {\n setCaptionMode((prev) => {\n const next: CaptionMode =\n prev === 'off' ? 'standard' : prev === 'standard' ? 'social' : 'off';\n onCaptionsToggle?.(next !== 'off');\n return next;\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 captionMode,\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 captionMode,\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 cycleCaptionMode,\n toggleFullscreen: onFullscreenToggle,\n }),\n [toggle, restart, seekTo, setCaptionsEnabled, cycleCaptionMode, 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\n doc={script}\n basePath={basePath}\n viewport={activeViewport}\n theme={theme}\n surface={surface}\n />\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 {/* Timed media clips (per-block + document-spanning audio/video). */}\n <MediaClipLayer\n schedule={mediaSchedule}\n currentTime={currentTime}\n isPlaying={isPlaying}\n basePath={basePath}\n renderMode={renderMode}\n />\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 // Keyed by block id so each block is its own DOM subtree: React never\n // reconciles one block's layers onto another's (templates reuse layer\n // ids like `title`/`background`), which would otherwise reuse stale\n // DOM / skip entrance animations mid-transition.\n <div key={previousBlock.id} className=\"doc-player__block doc-player__block--previous\">\n <BlockRenderer\n block={previousBlock}\n blockTime={blockTime}\n basePath={basePath}\n isExiting={true}\n transition={currentBlock?.transition}\n viewport={activeViewport}\n />\n </div>\n )}\n\n {/* Current block */}\n {!showCoverBlock && currentBlock && (\n <div key={currentBlock.id} 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 -- shown during playback and in render mode when captions are enabled */}\n {hasCaptions && (renderMode ? captionsEnabled : true) && (\n <CaptionOverlay\n captions={script.captions}\n currentTime={currentTime}\n enabled={captionsEnabled && (renderMode || isPlaying || currentTime > 0)}\n fontSize={16}\n captionStyle={activeCaptionStyle}\n theme={effectiveTheme}\n viewport={activeViewport}\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 <span style={{ color: '#666' }}>\n (progress: {(docProgress * 100).toFixed(1)}%, scriptDur:{' '}\n {script.duration.toFixed(1)})\n </span>\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>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' }}>🔊</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 * MediaClipLayer\n *\n * Player-level audio/video elements for the media-clip schedule. Renders one\n * element per scheduled clip (audio, or document-spanning video) and keeps each\n * mounted so re-entry doesn't reload; the drive effect seeks/plays only the\n * clips active at the current time. Multiple concurrent clips (e.g. a\n * document-spanning narration plus a block clip) are independent elements and\n * the browser mixes their output.\n *\n * Audio clips play unmuted during live playback (silent in render mode, where\n * frames are captured without sound and audio is muxed offline). Video clips\n * play muted — their audio, if any, is reproduced by the export mux.\n */\n\nimport { useEffect, useRef } from 'react';\nimport type { ScheduledClip } from '@bendyline/squisq/schemas';\nimport { useMediaUrl } from './hooks/MediaContext';\nimport { useMediaSchedule } from './hooks/useMediaSchedule';\n\n/** Re-seek an element only when it drifts this far from its target (seconds). */\nconst DRIFT = 0.25;\n\nexport interface MediaClipLayerProps {\n schedule: ScheduledClip[];\n currentTime: number;\n isPlaying: boolean;\n basePath: string;\n renderMode?: boolean;\n}\n\nexport function MediaClipLayer({\n schedule,\n currentTime,\n isPlaying,\n basePath,\n renderMode = false,\n}: MediaClipLayerProps) {\n const { renderClips, activeIds } = useMediaSchedule(schedule, currentTime);\n if (renderClips.length === 0) return null;\n return (\n <div className=\"doc-player__media-clips\" aria-hidden>\n {renderClips.map((clip) => (\n <MediaClipElement\n key={clip.id}\n clip={clip}\n active={activeIds.has(clip.id)}\n currentTime={currentTime}\n isPlaying={isPlaying}\n basePath={basePath}\n renderMode={renderMode}\n />\n ))}\n </div>\n );\n}\n\ninterface MediaClipElementProps {\n clip: ScheduledClip;\n active: boolean;\n currentTime: number;\n isPlaying: boolean;\n basePath: string;\n renderMode: boolean;\n}\n\nfunction MediaClipElement({\n clip,\n active,\n currentTime,\n isPlaying,\n basePath,\n renderMode,\n}: MediaClipElementProps) {\n const ref = useRef<HTMLMediaElement | null>(null);\n const src = useMediaUrl(clip.src, basePath);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n if (!active) {\n if (!el.paused) el.pause();\n return;\n }\n const target = Math.max(0, clip.sourceIn + (currentTime - clip.absoluteStart));\n if (renderMode || Math.abs(el.currentTime - target) > DRIFT) {\n try {\n el.currentTime = target;\n } catch {\n // Seeking before metadata loads throws; the next tick retries.\n }\n }\n if (isPlaying && !renderMode) {\n const p = el.play();\n if (p) p.catch(() => {});\n } else {\n el.pause();\n }\n }, [active, currentTime, isPlaying, renderMode, clip.sourceIn, clip.absoluteStart]);\n\n const isVideo = clip.kind === 'video';\n const common = {\n ref: ref as React.RefObject<never>,\n src,\n preload: 'auto' as const,\n 'data-clip-id': clip.id,\n 'data-abs-start': clip.absoluteStart,\n 'data-abs-end': clip.absoluteEnd,\n 'data-source-in': clip.sourceIn,\n };\n\n if (isVideo) {\n // Document-spanning video renders full-bleed behind the blocks.\n return (\n <video\n {...common}\n muted\n playsInline\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n zIndex: 0,\n pointerEvents: 'none',\n }}\n />\n );\n }\n\n return (\n <audio {...common} muted={renderMode} style={{ position: 'absolute', width: 0, height: 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 // Defensive: callers (esp. preview surfaces like InlinePreviewGutter)\n // sometimes feed in template-generated layers whose `content.src` is\n // undefined while the user is still authoring the block. Treat that\n // as an empty string rather than crashing the whole React tree.\n const safePath = typeof relativePath === 'string' ? relativePath : '';\n\n // For absolute/http URLs, skip resolution entirely\n const isAbsolute =\n !safePath ||\n safePath.startsWith('http') ||\n safePath.startsWith('/') ||\n safePath.startsWith('data:') ||\n safePath.startsWith('blob:');\n\n // Memoize fallback to avoid recalculating on every render\n const fallback = useMemo(\n () => (isAbsolute ? safePath : `${basePath}/${safePath}`),\n [isAbsolute, safePath, 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(safePath).then((resolved) => {\n if (!cancelled) setUrl(resolved);\n });\n\n return () => {\n cancelled = true;\n };\n }, [needsProvider, provider, safePath, 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 * useMediaSchedule\n *\n * Pure follower of the playback clock for the media-clip model. Given the\n * resolved {@link ScheduledClip}s and the current time, it returns the clips\n * the player should mount and which of them are active right now.\n * {@link MediaClipLayer} consumes this to drive one hidden `<audio>` /\n * full-bleed `<video>` element per clip. (Annotation-authored clips all render\n * at the player level; template-produced `VideoLayer`s are a separate path and\n * are not part of the schedule.)\n *\n * It owns no clock: `currentTime`/`isPlaying` come from the existing\n * `useAudioSync` provider via `DocPlayer`. With an empty schedule it returns\n * empty lists, so documents without the new media model are unaffected.\n */\n\nimport { useMemo } from 'react';\nimport type { ScheduledClip } from '@bendyline/squisq/schemas';\n\nexport interface MediaScheduleController {\n /** Clips the player mounts (every scheduled clip). */\n renderClips: ScheduledClip[];\n /** Ids of clips whose [absoluteStart, absoluteEnd) contains currentTime. */\n activeIds: Set<string>;\n}\n\nexport function useMediaSchedule(\n schedule: ScheduledClip[],\n currentTime: number,\n): MediaScheduleController {\n const activeIds = useMemo(() => {\n const ids = new Set<string>();\n for (const c of schedule) {\n if (currentTime >= c.absoluteStart && currentTime < c.absoluteEnd) ids.add(c.id);\n }\n return ids;\n }, [schedule, currentTime]);\n return { renderClips: schedule, activeIds };\n}\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, Transition } from '@bendyline/squisq/schemas';\nimport { resolveTransitionDuration } from '@bendyline/squisq/schemas';\nimport { ImageLayer } from './layers/ImageLayer';\nimport { TextLayer } from './layers/TextLayer';\nimport { ShapeLayer } from './layers/ShapeLayer';\nimport { PathLayer } from './layers/PathLayer';\nimport { MapLayer } from './layers/MapLayer';\nimport { VideoLayer } from './layers/VideoLayer';\nimport { TableLayer } from './layers/TableLayer';\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 /** Transition to apply. Defaults to block.transition. */\n transition?: Transition;\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 transition,\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 const activeTransition = transition ?? block.transition;\n if (activeTransition && isEntering) {\n transitionClass = getTransitionClass(activeTransition.type, true, activeTransition.direction);\n transitionStyle['--transition-duration'] = `${resolveTransitionDuration(activeTransition)}s`;\n } else if (activeTransition && isExiting) {\n transitionClass = getTransitionClass(activeTransition.type, false, activeTransition.direction);\n transitionStyle['--transition-duration'] = `${resolveTransitionDuration(activeTransition)}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 'path':\n return <PathLayer 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 case 'table':\n return <TableLayer layer={layer} viewport={viewport} blockTime={blockTime} />;\n default:\n console.warn(`Unknown layer type: ${(layer as Layer).type}`);\n return null;\n }\n}\n\nexport default BlockRenderer;\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 { cssFilterForTreatment } from '@bendyline/squisq/doc';\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 // Theme-derived photographic grade + optional blur, as a CSS filter\n // string (identical in the player and in headless frame capture).\n const filter = cssFilterForTreatment(content.treatment, content.blur);\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: `${width}px`,\n height: `${height}px`,\n overflow: 'hidden',\n }}\n >\n <img\n src={src}\n alt={content.alt || ''}\n className={kbStyle.className}\n style={{\n width: `${width}px`,\n height: `${height}px`,\n objectFit: 'cover',\n objectPosition: 'center',\n display: 'block',\n pointerEvents: 'none',\n transformOrigin: 'center center',\n ...(filter ? { filter } : {}),\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: `${width}px`,\n height: `${height}px`,\n objectFit: 'cover',\n objectPosition: 'center',\n display: 'block',\n pointerEvents: 'none',\n ...(filter ? { filter } : {}),\n // Over-scan blurred imagery so the soft edges never reveal\n // the frame behind the layer.\n ...(content.blur && content.blur > 0 ? { transform: 'scale(1.06)' } : {}),\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', ...(filter ? { filter } : {}) }}\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 * 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 * 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 * Two rendering paths:\n * - Plain text (`PlainTextLayer`) — SVG `<text>` + `<tspan>` per line, one\n * style for the whole layer. Used for the common case and for\n * backward-compat with content authored before rich text.\n * - Rich text (`RichTextLayer`) — when `content.html` is set, the sanitized\n * inline/block HTML renders inside a `<foreignObject>` so bold/italic/\n * links (and, for layout textboxes, headings/lists) format individual\n * runs. `<foreignObject>` is export-safe (video rasterizes via Chromium,\n * PDF bypasses SVG) and already used by Video/Table layers.\n */\n\nimport { useMemo, type CSSProperties } from 'react';\nimport type { TextLayer as TextLayerType } from '@bendyline/squisq/schemas';\nimport { DEFAULT_DOC_FONT } from '@bendyline/squisq/schemas';\nimport {\n parseHtmlToNodes,\n sanitizeHtmlNodes,\n stringifyHtmlNodes,\n} from '@bendyline/squisq/markdown';\n// Import from the standalone marker module (not the `icons` barrel) so the\n// player bundle doesn't pull in the ~2k-entry FontAwesome catalog.\nimport {\n hasIconMarker,\n splitIconMarkers,\n stripIconMarkers,\n iconClass,\n} from '@bendyline/squisq/icon-marker';\nimport { getAnimationStyle } from '../utils/animationUtils';\nimport { resolveValue } from '../utils/layerUtils';\nimport { resolveFill, borderDashArray } from '../utils/fillStyle';\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\n/**\n * Dispatch between the plain SVG-text renderer and the rich HTML renderer\n * based on whether the layer carries `content.html`.\n */\nexport function TextLayer(props: TextLayerProps) {\n if (props.layer.content.html?.trim()) return <RichTextLayer {...props} />;\n // Body text carrying inline-icon markers (authored `{[rocket]}` etc.) can't\n // render in the SVG `<text>` path — an icon is an `<i class=\"fa-…\">` glyph,\n // not a code point we can place in a tspan. Route it through a foreignObject\n // instead, sizing the box to the wrapped content so it doesn't clip.\n if (hasIconMarker(props.layer.content.text ?? '')) return <IconTextLayer {...props} />;\n return <PlainTextLayer {...props} />;\n}\n\n/** Escape text so it's safe inside the synthesized icon HTML. */\nfunction escapeHtml(value: string): string {\n return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n}\n\n/** Turn a marker-bearing string into HTML: text runs escaped, icons as `<i>`. */\nfunction iconRunsToHtml(text: string): string {\n return splitIconMarkers(text)\n .map((run) =>\n run.type === 'icon'\n ? `<i class=\"${iconClass(run.family, run.name)}\" aria-hidden=\"true\"></i>`\n : escapeHtml(run.text).replace(/\\n/g, '<br>'),\n )\n .join('');\n}\n\n/**\n * Renders template body text that contains inline icons. Geometry mirrors\n * `PlainTextLayer`/`layerBounds` (same anchor math), but the content lives in\n * a `<foreignObject>` so `<i class=\"fa-…\">` glyphs render (export-safe: video\n * rasterizes via Chromium, which has the FA webfont embedded in the render\n * page). Height is derived from the wrapped, icon-free projection when the\n * layer has no explicit height, and overflow is visible to guard estimates.\n */\nfunction IconTextLayer({ layer, viewport, blockTime }: TextLayerProps) {\n const { content, position, animation } = layer;\n const { text, style } = content;\n\n const rawX = resolveValue(position.x, viewport.width);\n const rawY = resolveValue(position.y, viewport.height);\n const boxWidth =\n position.width != null ? resolveValue(position.width, viewport.width) : viewport.width;\n const anchor = position.anchor ?? 'top-left';\n const lineHeight = style.lineHeight || 1.4;\n const lineHeightPx = style.fontSize * lineHeight;\n const padding = style.padding ?? 0;\n\n // Estimate wrapped line count from the icon-free text so the box is tall\n // enough. Icons roughly occupy one character; the estimate need not be exact\n // because the foreignObject is `overflow: visible`.\n const plain = stripIconMarkers(text ?? '');\n const lines = plain.split('\\n').flatMap((line) => wrapText(line, style.fontSize, boxWidth));\n const boxHeight =\n position.height != null\n ? resolveValue(position.height, viewport.height)\n : Math.max(lineHeightPx, lines.length * lineHeightPx) + padding * 2;\n\n const boxX = rawX - anchorAxis(anchor, boxWidth, 'x');\n const boxY = rawY - anchorAxis(anchor, boxHeight, 'y');\n\n const animStyle = getAnimationStyle(animation, blockTime);\n const html = useMemo(() => iconRunsToHtml(text ?? ''), [text]);\n\n const verticalJustify =\n style.verticalAlign === 'top'\n ? 'flex-start'\n : style.verticalAlign === 'bottom'\n ? 'flex-end'\n : 'center';\n\n const boxStyle: CSSProperties = {\n boxSizing: 'border-box',\n width: '100%',\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: verticalJustify,\n padding,\n color: style.color,\n fontSize: `${style.fontSize}px`,\n fontFamily: style.fontFamily || DEFAULT_DOC_FONT,\n fontWeight: style.fontWeight || 'normal',\n fontStyle: style.fontStyle || 'normal',\n lineHeight,\n textAlign: style.textAlign ?? 'left',\n ...(style.shadow ? { textShadow: '0 2px 3px rgba(0,0,0,0.7)' } : {}),\n ...animStyle.style,\n };\n\n return (\n <g className={`block-layer block-layer--text ${animStyle.className}`} data-layer-id={layer.id}>\n <foreignObject\n x={boxX}\n y={boxY}\n width={boxWidth}\n height={boxHeight}\n style={{ overflow: 'visible' }}\n >\n {/* Outer box owns the flex/vertical-centering; the inner div is a\n single flex item so the rich content (text + inline `<i>` icons)\n flows inline instead of each node becoming a stacked flex item. */}\n <div\n {...({ xmlns: 'http://www.w3.org/1999/xhtml' } as Record<string, string>)}\n style={boxStyle}\n >\n <div\n style={{ width: '100%', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}\n aria-label={plain}\n dangerouslySetInnerHTML={{ __html: html }}\n />\n </div>\n </foreignObject>\n </g>\n );\n}\n\nfunction PlainTextLayer({ layer, viewport, blockTime }: TextLayerProps) {\n const { content, position, animation } = layer;\n const { text, style } = content;\n\n // Resolve position values to pixels. `position.x/y` is the layer's\n // anchor *point*; `position.anchor` says where on the box that point\n // sits (matching `layerBounds` in the editor).\n const rawX = resolveValue(position.x, viewport.width);\n const rawY = resolveValue(position.y, viewport.height);\n const boxWidth =\n position.width != null ? resolveValue(position.width, viewport.width) : undefined;\n const boxHeight =\n position.height != null ? resolveValue(position.height, viewport.height) : undefined;\n const maxWidth = boxWidth;\n\n const textAnchor = getTextAnchor(style.textAlign, position.anchor);\n const dominantBaseline = getDominantBaseline(style.verticalAlign, position.anchor);\n\n // Place the text's pivot at the box edge/centre matching the chosen\n // alignment. This is algebraically identical to the legacy point-anchor\n // behavior for existing content (where x/y were already the desired\n // pivot — e.g. templates using x:'50%' + anchor:'center'), and makes\n // box-relative H/V alignment work for layers anchored at their\n // top-left (as the template designer creates them).\n const anchor = position.anchor ?? 'top-left';\n const x = pivotX(rawX, boxWidth, anchor, textAnchor);\n const y = pivotY(rawY, boxHeight, anchor, dominantBaseline);\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 fontStyle: style.fontStyle || '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 / border box. When the layer has an explicit box\n (width + height — as the template designer always creates), the\n fill and border cover that rectangle: text is, after all, a\n rectangle. Without a box we fall back to a snug rect hugging the\n text (legacy behavior for point-anchored text). */}\n <TextBox\n layerId={layer.id}\n style={style}\n box={\n boxWidth != null && boxHeight != null\n ? {\n x: rawX - anchorAxis(anchor, boxWidth, 'x'),\n y: rawY - anchorAxis(anchor, boxHeight, 'y'),\n width: boxWidth,\n height: boxHeight,\n }\n : {\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 }\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 * Rich-text path: render `content.html` as sanitized HTML inside a\n * `<foreignObject>` so individual runs can carry their own formatting\n * (bold/italic/links, and headings/lists for layout textboxes). The box\n * geometry matches `PlainTextLayer`/`layerBounds`, so selection and\n * drag/resize stay aligned. `content.text` remains the plain projection\n * used by export/search.\n */\nfunction RichTextLayer({ layer, viewport, blockTime }: TextLayerProps) {\n const { content, position, animation } = layer;\n const { html, style } = content;\n\n const rawX = resolveValue(position.x, viewport.width);\n const rawY = resolveValue(position.y, viewport.height);\n const boxWidth =\n position.width != null ? resolveValue(position.width, viewport.width) : viewport.width;\n const boxHeight =\n position.height != null\n ? resolveValue(position.height, viewport.height)\n : style.fontSize * (style.lineHeight || 1.4) * 2;\n const anchor = position.anchor ?? 'top-left';\n const boxX = rawX - anchorAxis(anchor, boxWidth, 'x');\n const boxY = rawY - anchorAxis(anchor, boxHeight, 'y');\n\n // Re-sanitize at render time — `html` is untrusted. Memoized so the\n // per-frame `blockTime` updates don't re-parse the HTML each frame.\n const safeHtml = useMemo(\n () => stringifyHtmlNodes(sanitizeHtmlNodes(parseHtmlToNodes(html ?? ''))),\n [html],\n );\n\n const animStyle = getAnimationStyle(animation, blockTime);\n\n const verticalJustify =\n style.verticalAlign === 'middle'\n ? 'center'\n : style.verticalAlign === 'bottom'\n ? 'flex-end'\n : 'flex-start';\n\n const hasBorder = !!(style.borderColor && (style.borderWidth ?? 0) > 0);\n // Box decoration uses CSS (foreignObject is HTML); gradients/backgroundOpacity\n // are deferred to the SVG path — solid background covers the common case.\n const boxStyle: CSSProperties = {\n boxSizing: 'border-box',\n width: '100%',\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: verticalJustify,\n padding: style.padding ?? 0,\n color: style.color,\n fontSize: `${style.fontSize}px`,\n fontFamily: style.fontFamily || DEFAULT_DOC_FONT,\n fontWeight: style.fontWeight || 'normal',\n fontStyle: style.fontStyle || 'normal',\n lineHeight: style.lineHeight || 1.4,\n textAlign: style.textAlign ?? 'left',\n overflow: 'hidden',\n ...(style.background ? { background: style.background } : {}),\n ...(hasBorder\n ? {\n border: `${style.borderWidth}px ${style.borderStyle ?? 'solid'} ${style.borderColor}`,\n borderRadius: 4,\n }\n : {}),\n ...(style.shadow ? { textShadow: '0 2px 3px rgba(0,0,0,0.7)' } : {}),\n ...animStyle.style,\n };\n\n // Scoped reset so authored block elements (headings, lists, paragraphs)\n // render tightly inside the box rather than with the UA's large margins.\n const cls = `squisq-rich-text-${cssId(layer.id)}`;\n const scopedCss =\n `.${cls}{margin:0}` +\n `.${cls} p{margin:0 0 .4em}` +\n `.${cls} h1,.${cls} h2,.${cls} h3,.${cls} h4,.${cls} h5,.${cls} h6{margin:0 0 .3em;line-height:1.2}` +\n // `list-style-position: inside` keeps the bullet/number next to its text\n // — with `outside` (the default) a centered or middle-aligned list leaves\n // the marker stranded at the box's left padding, far from the text. The\n // editor wraps each item's text in a `<p>`, so flatten that to inline or\n // the block paragraph drops below the (inline) marker.\n `.${cls} ul,.${cls} ol{margin:0 0 .4em;padding-left:1.2em;list-style-position:inside}` +\n `.${cls} li{margin:0}` +\n `.${cls} li>p{display:inline;margin:0}` +\n `.${cls} *:first-child{margin-top:0}.${cls} *:last-child{margin-bottom:0}` +\n `.${cls} a{color:inherit;text-decoration:underline}`;\n\n return (\n <g className={`block-layer block-layer--text ${animStyle.className}`} data-layer-id={layer.id}>\n <foreignObject x={boxX} y={boxY} width={boxWidth} height={boxHeight}>\n {/* xmlns is required for HTML inside SVG foreignObject (see TableLayer) */}\n <div\n {...({ xmlns: 'http://www.w3.org/1999/xhtml' } as Record<string, string>)}\n style={boxStyle}\n >\n <style>{scopedCss}</style>\n <div\n className={cls}\n aria-label={content.text}\n style={{ width: '100%', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}\n dangerouslySetInnerHTML={{ __html: safeHtml }}\n />\n </div>\n </foreignObject>\n </g>\n );\n}\n\n/** Sanitize a layer id into a CSS-class-safe suffix. */\nfunction cssId(id: string): string {\n return id.replace(/[^a-zA-Z0-9_-]/g, '-');\n}\n\n/**\n * The text layer's background + border rect. Renders nothing unless a\n * fill (solid or gradient) or a visible border is configured.\n */\nfunction TextBox({\n layerId,\n style,\n box,\n}: {\n layerId: string;\n style: import('@bendyline/squisq/schemas').TextStyle;\n box: { x: number; y: number; width: number; height: number };\n}) {\n const hasFill = !!(style.background || style.backgroundGradient);\n const hasBorder = !!(style.borderColor && (style.borderWidth ?? 0) > 0);\n if (!hasFill && !hasBorder) return null;\n\n const { fill, def } = resolveFill(layerId, style.background, style.backgroundGradient);\n const dash = borderDashArray(style.borderStyle, style.borderWidth);\n return (\n <>\n {def && <defs>{def}</defs>}\n <rect\n x={box.x}\n y={box.y}\n width={box.width}\n height={box.height}\n fill={hasFill ? fill : 'none'}\n fillOpacity={hasFill ? style.backgroundOpacity : undefined}\n stroke={hasBorder ? style.borderColor : undefined}\n strokeWidth={hasBorder ? style.borderWidth : undefined}\n strokeDasharray={hasBorder ? dash : undefined}\n rx={4}\n ry={4}\n />\n </>\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 vertical alignment (or, when unset, the position anchor) to SVG\n * dominant-baseline. Explicit `verticalAlign` wins so the editor can set\n * top/middle/bottom independently of the box-placement anchor.\n */\nfunction getDominantBaseline(verticalAlign?: 'top' | 'middle' | 'bottom', anchor?: string): string {\n if (verticalAlign === 'top') return 'text-before-edge';\n if (verticalAlign === 'middle') return 'middle';\n if (verticalAlign === 'bottom') return 'text-after-edge';\n\n if (anchor?.includes('bottom')) return 'text-after-edge';\n if (anchor === 'center') return 'middle';\n return 'text-before-edge';\n}\n\n/**\n * Horizontal pivot for the text. Derives the box's left edge from the\n * anchor point (`rawX`) and the box width, then offsets to the edge/centre\n * matching `textAnchor`. Falls back to the raw point when no width is set.\n */\nfunction pivotX(\n rawX: number,\n width: number | undefined,\n anchor: string,\n textAnchor: string,\n): number {\n if (width == null) return rawX;\n const boxLeft = rawX - anchorAxis(anchor, width, 'x');\n if (textAnchor === 'middle') return boxLeft + width / 2;\n if (textAnchor === 'end') return boxLeft + width;\n return boxLeft;\n}\n\n/**\n * Vertical pivot for the text. Mirror of {@link pivotX} for the box's top\n * edge and the resolved dominant-baseline. Falls back to the raw point\n * when no height is set.\n */\nfunction pivotY(\n rawY: number,\n height: number | undefined,\n anchor: string,\n dominantBaseline: string,\n): number {\n if (height == null) return rawY;\n const boxTop = rawY - anchorAxis(anchor, height, 'y');\n if (dominantBaseline === 'middle') return boxTop + height / 2;\n if (dominantBaseline === 'text-after-edge') return boxTop + height;\n return boxTop;\n}\n\n/** Anchor-point offset along one axis, matching `layerBounds` in the editor. */\nfunction anchorAxis(anchor: string, size: number, axis: 'x' | 'y'): number {\n if (anchor === 'center') return size / 2;\n if (axis === 'x') return anchor.includes('right') ? size : 0;\n return anchor.includes('bottom') ? size : 0;\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 * Shared fill / border helpers for the SVG layer renderers (shape, path,\n * and the text-box background). Keeping the gradient `<defs>` markup and\n * the dash-pattern math in one place means rect, circle, path, and text\n * all paint fills and borders identically.\n */\n\nimport type {\n LinearGradient,\n BorderStyle,\n ShapePattern,\n ShapeFilter,\n} from '@bendyline/squisq/schemas';\n\n/**\n * SVG `stroke-dasharray` for a border style, scaled by stroke width so the\n * pattern stays proportional. `solid`/undefined → no dashes.\n */\nexport function borderDashArray(\n style: BorderStyle | undefined,\n strokeWidth: number | undefined,\n): string | undefined {\n if (!style || style === 'solid') return undefined;\n const w = Math.max(1, strokeWidth ?? 1);\n if (style === 'dotted') return `${w} ${w * 2}`;\n return `${w * 3} ${w * 2}`; // dashed\n}\n\n/**\n * Gradient line endpoints in objectBoundingBox units (0–1) for an angle in\n * degrees: 0 = top→bottom, 90 = left→right, increasing clockwise.\n */\nfunction gradientVector(angle = 0): { x1: number; y1: number; x2: number; y2: number } {\n const a = (angle * Math.PI) / 180;\n const dx = Math.sin(a);\n const dy = Math.cos(a);\n return { x1: 0.5 - dx / 2, y1: 0.5 - dy / 2, x2: 0.5 + dx / 2, y2: 0.5 + dy / 2 };\n}\n\n/** Stable id for a layer's gradient def. */\nexport function gradientDefId(layerId: string): string {\n return `squisq-grad-${layerId}`;\n}\n\n/**\n * Resolve a fill to an SVG `fill` value plus the gradient `<defs>` to\n * render (or null). When a gradient is present it wins over the solid\n * color; otherwise the color (or undefined) is returned verbatim.\n */\nexport function resolveFill(\n layerId: string,\n color: string | undefined,\n gradient: LinearGradient | undefined,\n pattern?: ShapePattern,\n): { fill: string | undefined; def: JSX.Element | null } {\n if (pattern) {\n const id = `squisq-pattern-${layerId}`;\n return { fill: `url(#${id})`, def: patternDef(id, pattern) };\n }\n if (gradient) {\n const id = gradientDefId(layerId);\n const v = gradientVector(gradient.angle);\n return {\n fill: `url(#${id})`,\n def: (\n <linearGradient id={id} x1={v.x1} y1={v.y1} x2={v.x2} y2={v.y2}>\n <stop offset=\"0%\" stopColor={gradient.from} />\n <stop offset=\"100%\" stopColor={gradient.to} />\n </linearGradient>\n ),\n };\n }\n return { fill: color, def: null };\n}\n\n/**\n * Native SVG `<pattern>` def for a repeating shape fill — fully vector,\n * identical in the player and headless frame capture.\n */\nfunction patternDef(id: string, pattern: ShapePattern): JSX.Element {\n const size = pattern.size ?? 24;\n const opacity = pattern.opacity ?? 1;\n const color = pattern.color;\n return (\n <pattern\n id={id}\n width={size}\n height={size}\n patternUnits=\"userSpaceOnUse\"\n // Diagonal tiles rotate the whole pattern; dots/grid stay axis-aligned.\n patternTransform={pattern.kind === 'diagonal' ? 'rotate(45)' : undefined}\n >\n {pattern.kind === 'dots' && (\n <circle\n cx={size / 2}\n cy={size / 2}\n r={Math.max(1, size / 12)}\n fill={color}\n opacity={opacity}\n />\n )}\n {pattern.kind === 'grid' && (\n <path\n d={`M ${size} 0 L 0 0 0 ${size}`}\n fill=\"none\"\n stroke={color}\n strokeWidth={1}\n opacity={opacity}\n />\n )}\n {pattern.kind === 'diagonal' && (\n <line x1={0} y1={0} x2={0} y2={size} stroke={color} strokeWidth={1} opacity={opacity} />\n )}\n </pattern>\n );\n}\n\n/**\n * SVG filter def for a shape's procedural filter (static film grain via\n * feTurbulence). Returns the def plus the `filter` attribute value.\n */\nexport function resolveShapeFilter(\n layerId: string,\n filter: ShapeFilter | undefined,\n): { filterAttr: string | undefined; def: JSX.Element | null } {\n if (!filter || filter.type !== 'noise') return { filterAttr: undefined, def: null };\n const id = `squisq-noise-${layerId}`;\n const opacity = filter.opacity ?? 0.05;\n return {\n filterAttr: `url(#${id})`,\n def: (\n <filter id={id} x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\">\n <feTurbulence\n type=\"fractalNoise\"\n baseFrequency={filter.baseFrequency ?? 0.8}\n numOctaves={2}\n stitchTiles=\"stitch\"\n result=\"noise\"\n />\n <feColorMatrix in=\"noise\" type=\"saturate\" values=\"0\" result=\"mono\" />\n <feComponentTransfer in=\"mono\" result=\"faded\">\n <feFuncA type=\"linear\" slope={opacity} intercept={0} />\n </feComponentTransfer>\n <feComposite in=\"faded\" in2=\"SourceGraphic\" operator=\"in\" />\n </filter>\n ),\n };\n}\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, getAnchorOffset } from '../utils/layerUtils';\nimport { resolveFill, resolveShapeFilter, borderDashArray } from '../utils/fillStyle';\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 rawX = resolveValue(position.x, viewport.width);\n const rawY = 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 // Apply anchor offset (e.g., 'center' shifts x by -width/2 and y by -height/2)\n const anchorOffset = getAnchorOffset(position.anchor, width, height);\n const x = rawX + anchorOffset.x;\n const y = rawY + anchorOffset.y;\n\n // Get animation styles\n const animStyle = getAnimationStyle(animation, blockTime);\n\n const fill = content.fill || 'none';\n // Legacy: a CSS gradient string baked into `fill` (e.g. from older docs)\n // only works as an HTML background, so rect renders it via foreignObject.\n // The structured `content.gradient` (preferred) is handled below for all\n // shapes via an SVG <linearGradient>.\n const isCSSGradient = typeof fill === 'string' && fill.includes('gradient(');\n\n if (content.shape === 'rect' && isCSSGradient && !content.gradient) {\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: `${width}px`,\n height: `${height}px`,\n background: fill,\n borderRadius: content.borderRadius ? `${content.borderRadius}px` : undefined,\n pointerEvents: 'none',\n }}\n />\n </foreignObject>\n </g>\n );\n }\n\n const { fill: fillValue, def: fillDef } = resolveFill(\n layer.id,\n fill,\n content.gradient,\n content.pattern,\n );\n const { filterAttr, def: filterDef } = resolveShapeFilter(layer.id, content.filter);\n const dash = borderDashArray(content.borderStyle, content.strokeWidth);\n\n // Common style props for native SVG shapes. `line` is stroke-only.\n const shapeProps = {\n fill: fillValue,\n fillOpacity: content.fillOpacity,\n stroke: content.stroke,\n strokeWidth: content.strokeWidth,\n strokeDasharray: dash,\n ...(filterAttr ? { filter: filterAttr } : {}),\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 {(fillDef || filterDef) && (\n <defs>\n {fillDef}\n {filterDef}\n </defs>\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 strokeDasharray={dash}\n />\n )}\n </g>\n );\n}\n\nexport default ShapeLayer;\n","/**\n * PathLayer Component\n *\n * Renders an SVG `<path>` for arbitrary curves, connectors, arrows, and the\n * drawing template's computed shapes. Used by the diagram template for edges\n * between nodes; usable by any template that needs a non-rect/circle/line\n * shape.\n *\n * The path's `d` attribute uses absolute SVG coordinates relative to the\n * block viewport (independent of the layer's `position` box, which is\n * present only so animations and clipping match the other layer types).\n *\n * Exception: when `content.shapeKind` is set (a standard named shape like\n * `diamond` / `star` / `arrow-right`), `d` is re-derived from the layer's\n * `position` box resolved against the viewport. That keeps named shapes\n * movable, resizable, and aspect-ratio-adaptive — matching how the native\n * rect/circle/line `ShapeLayer` behaves — rather than pinned to a baked\n * absolute path.\n *\n * End markers are configured via `startMarker`/`endMarker` (with the legacy\n * `arrow` flag mapping to a filled triangle). Marker geometry comes from\n * `markerPath` in core so the SSR renderer and the editor agree.\n */\n\nimport type { PathLayer as PathLayerType, MarkerStyle } from '@bendyline/squisq/schemas';\nimport { markerPath, shapePath } from '@bendyline/squisq/doc';\nimport { getAnimationStyle } from '../utils/animationUtils';\nimport { resolveValue, getAnchorOffset } from '../utils/layerUtils';\nimport { resolveFill, borderDashArray } from '../utils/fillStyle';\n\ninterface PathLayerProps {\n layer: PathLayerType;\n /** Viewport dimensions — used to resolve `%` positions for named shapes. */\n viewport: { width: number; height: number };\n /** Current time relative to block start. */\n blockTime: number;\n}\n\n/**\n * The effective `d`: a named shape (`content.shapeKind`) is re-derived\n * from the resolved `position` box so it tracks moves/resizes and adapts\n * to the viewport; everything else uses the stored absolute path.\n */\nfunction effectivePath(layer: PathLayerType, viewport: { width: number; height: number }): string {\n const { content, position } = layer;\n if (!content.shapeKind) return content.d;\n const w = position.width ? resolveValue(position.width, viewport.width) : 0;\n const h = position.height ? resolveValue(position.height, viewport.height) : 0;\n const rawX = resolveValue(position.x, viewport.width);\n const rawY = resolveValue(position.y, viewport.height);\n const anchor = getAnchorOffset(position.anchor, w, h);\n const derived = shapePath(content.shapeKind, rawX + anchor.x, rawY + anchor.y, w, h);\n // Fall back to the stored path if the kind is unknown to `shapePath`.\n return derived ?? content.d;\n}\n\n/** Resolve the effective marker for an endpoint (explicit field, else `arrow`). */\nfunction effectiveMarker(\n explicit: MarkerStyle | undefined,\n arrow: PathLayerType['content']['arrow'],\n end: 'start' | 'end',\n): MarkerStyle {\n if (explicit) return explicit;\n const wants = arrow === 'both' || arrow === end;\n return wants ? 'arrow' : 'none';\n}\n\nexport function PathLayer({ layer, viewport, blockTime }: PathLayerProps) {\n const { content, animation, id } = layer;\n const d = effectivePath(layer, viewport);\n const stroke = content.stroke ?? '#1e293b';\n const strokeWidth = content.strokeWidth ?? 2;\n const { fill, def: fillDef } = resolveFill(id, content.fill ?? 'none', content.gradient);\n // `borderStyle` (named shapes) takes precedence over a raw `dasharray`.\n const dash = content.borderStyle\n ? borderDashArray(content.borderStyle, strokeWidth)\n : content.dasharray;\n const animStyle = getAnimationStyle(animation, blockTime);\n\n const startId = `marker-start-${id}`;\n const endId = `marker-end-${id}`;\n const start = markerPath(effectiveMarker(content.startMarker, content.arrow, 'start'), 'start');\n const end = markerPath(effectiveMarker(content.endMarker, content.arrow, 'end'), 'end');\n\n return (\n <g\n className={`block-layer block-layer--path ${animStyle.className}`}\n style={animStyle.style}\n data-layer-id={id}\n >\n <defs>\n {fillDef}\n {end && <MarkerDef id={endId} dir=\"end\" d={end.d} filled={end.filled} stroke={stroke} />}\n {start && (\n <MarkerDef id={startId} dir=\"start\" d={start.d} filled={start.filled} stroke={stroke} />\n )}\n </defs>\n <path\n d={d}\n stroke={stroke}\n strokeWidth={strokeWidth}\n fill={fill}\n fillOpacity={content.fillOpacity}\n strokeDasharray={dash}\n markerStart={start ? `url(#${startId})` : undefined}\n markerEnd={end ? `url(#${endId})` : undefined}\n />\n </g>\n );\n}\n\nfunction MarkerDef({\n id,\n dir,\n d,\n filled,\n stroke,\n}: {\n id: string;\n dir: 'start' | 'end';\n d: string;\n filled: boolean;\n stroke: string;\n}) {\n return (\n <marker\n id={id}\n viewBox=\"0 0 10 10\"\n refX={dir === 'end' ? 9 : 1}\n refY={5}\n markerWidth={4}\n markerHeight={4}\n orient=\"auto-start-reverse\"\n markerUnits=\"strokeWidth\"\n >\n <path\n d={d}\n fill={filled ? stroke : 'none'}\n stroke={filled ? 'none' : stroke}\n strokeWidth={filled ? undefined : 1.5}\n />\n </marker>\n );\n}\n\nexport default PathLayer;\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({ layer, basePath, viewport, blockTime, isPlaying }: VideoLayerProps) {\n const { content, position } = layer;\n const videoRef = useRef<HTMLVideoElement>(null);\n const hasStartedRef = useRef(false);\n\n // Seconds into the block before this clip begins. Until then the video is\n // held (paused) at its in-point so a `startAt` offset reads as a delay.\n const startAt = content.startAt ?? 0;\n const gated = blockTime < startAt;\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, honoring the startAt gate.\n useEffect(() => {\n const video = videoRef.current;\n if (!video || !hasStartedRef.current) return;\n\n // Before the clip's startAt offset, hold at the in-point.\n if (gated) {\n video.pause();\n video.currentTime = content.clipStart;\n return;\n }\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, gated, content.clipStart, 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 data-start-at={startAt}\n style={{\n width: `${width}px`,\n height: `${height}px`,\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 * TableLayer Component\n *\n * Renders a data table within an SVG block using a <foreignObject> to embed\n * an HTML table. This gives us native table layout inside SVG viewports.\n *\n * The table is styled inline using the TableLayerStyle properties, which are\n * typically derived from the active theme by the dataTable template.\n */\n\nimport type { TableLayer as TableLayerType } from '@bendyline/squisq/schemas';\nimport { resolveValue, getAnchorOffset } from '../utils/layerUtils';\nimport { getAnimationStyle } from '../utils/animationUtils';\n\ninterface TableLayerProps {\n layer: TableLayerType;\n /** Viewport dimensions for percentage calculations */\n viewport: { width: number; height: number };\n /** Current time relative to block start (for animation) */\n blockTime: number;\n}\n\nexport function TableLayer({ layer, viewport, blockTime }: TableLayerProps) {\n const { content, position, animation } = layer;\n const { headers, rows, align, 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 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 // Build animation style\n const animStyle = animation ? getAnimationStyle(animation, blockTime) : {};\n\n const cellAlign = (ci: number): React.CSSProperties | undefined => {\n const a = align?.[ci];\n return a ? { textAlign: a } : undefined;\n };\n\n const borderRadius = style.borderRadius ?? 8;\n\n return (\n <foreignObject x={finalX} y={finalY} width={width} height={height} style={animStyle}>\n <div\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n {...({ xmlns: 'http://www.w3.org/1999/xhtml' } as any)}\n style={{\n width: `${width}px`,\n height: `${height}px`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '16px',\n boxSizing: 'border-box',\n }}\n >\n <table\n style={{\n width: '100%',\n borderCollapse: 'separate',\n borderSpacing: 0,\n fontSize: `${style.fontSize}px`,\n fontFamily: style.fontFamily ?? 'system-ui, sans-serif',\n overflow: 'hidden',\n borderRadius: `${borderRadius}px`,\n border: `1px solid ${style.borderColor}`,\n }}\n >\n {headers.length > 0 && (\n <thead>\n <tr>\n {headers.map((header, ci) => (\n <th\n key={ci}\n style={{\n background: style.headerBackground,\n color: style.headerColor,\n fontFamily:\n style.headerFontFamily ?? style.fontFamily ?? 'system-ui, sans-serif',\n fontWeight: 600,\n padding: '12px 16px',\n borderBottom: `2px solid ${style.borderColor}`,\n borderRight:\n ci < headers.length - 1 ? `1px solid ${style.borderColor}` : undefined,\n ...cellAlign(ci),\n }}\n >\n {header}\n </th>\n ))}\n </tr>\n </thead>\n )}\n {rows.length > 0 && (\n <tbody>\n {rows.map((row, ri) => (\n <tr key={ri}>\n {row.map((cell, ci) => (\n <td\n key={ci}\n style={{\n background: style.cellBackground,\n color: style.cellColor,\n padding: '10px 16px',\n borderBottom:\n ri < rows.length - 1 ? `1px solid ${style.borderColor}` : undefined,\n borderRight:\n ci < row.length - 1 ? `1px solid ${style.borderColor}` : undefined,\n ...cellAlign(ci),\n }}\n >\n {cell}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n )}\n </table>\n </div>\n </foreignObject>\n );\n}\n","/**\n * CaptionOverlay Component\n *\n * Displays closed captions synchronized with audio playback.\n * Supports two styles:\n *\n * - `'standard'` (default): Traditional broadcast captions — small white text\n * on a semi-transparent black badge at the top of the player.\n * - `'social'`: Social media-style (Instagram/TikTok) — large centered words\n * with the active word highlighted in the theme's primary color.\n */\n\nimport type { CaptionTrack, ViewportConfig } from '@bendyline/squisq/schemas';\nimport { getCaptionAtTime } from '@bendyline/squisq/schemas';\nimport type { Theme } from '@bendyline/squisq/schemas';\nimport type { CaptionStyle } from './types';\nimport { SocialCaptionOverlay } from './SocialCaptionOverlay';\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 for standard style (default: 16) */\n fontSize?: number;\n /** Caption display style (default: 'standard') */\n captionStyle?: CaptionStyle;\n /** Theme for social-style caption colors and fonts */\n theme?: Theme;\n /** Viewport config for social-style font scaling */\n viewport?: ViewportConfig;\n}\n\nexport function CaptionOverlay({\n captions,\n currentTime,\n enabled = true,\n fontSize = 16,\n captionStyle = 'standard',\n theme,\n viewport,\n}: CaptionOverlayProps) {\n // Delegate to social overlay when that style is active\n if (captionStyle === 'social') {\n return (\n <SocialCaptionOverlay\n captions={captions}\n currentTime={currentTime}\n enabled={enabled}\n theme={theme}\n viewport={viewport}\n />\n );\n }\n\n // Standard caption rendering\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 * SocialCaptionOverlay Component\n *\n * Social media-style captions (Instagram/TikTok): large centered words\n * showing 3-5 words at a time with the currently-spoken word highlighted\n * in the theme's primary color. Font and colors are pulled from the\n * active theme.\n *\n * Words are gathered across all caption phrases into a continuous stream,\n * then chunked into uniform groups for smooth, consistent pacing.\n *\n * Supports two timing modes:\n * 1. Precise: uses per-word timestamps from CaptionPhrase.words\n * 2. Interpolated: distributes timing evenly within each phrase\n */\n\nimport { useMemo } from 'react';\nimport type { CaptionTrack, CaptionPhrase, ViewportConfig } from '@bendyline/squisq/schemas';\nimport type { Theme } from '@bendyline/squisq/schemas';\nimport { resolveFontFamily } from '@bendyline/squisq/schemas';\n\n/** Target words per visible chunk. */\nconst TARGET_CHUNK_SIZE = 4;\nconst MIN_CHUNK_SIZE = 2;\nconst MAX_CHUNK_SIZE = 6;\n\n/** A timed word derived from phrase data. */\ninterface TimedWord {\n text: string;\n startTime: number;\n endTime: number;\n}\n\n/** A chunk of words displayed together. */\ninterface WordChunk {\n words: TimedWord[];\n startTime: number;\n endTime: number;\n}\n\n/**\n * Resolve per-word timing for a single phrase.\n * Uses precise word timestamps when available, otherwise interpolates.\n */\nfunction resolvePhraseTiming(phrase: CaptionPhrase): TimedWord[] {\n // Use precise timing when available\n if (phrase.words && phrase.words.length > 0) {\n return phrase.words.map((w) => ({\n text: w.text,\n startTime: w.startTime,\n endTime: w.endTime,\n }));\n }\n\n // Interpolate: split text into words, distribute evenly\n const rawWords = phrase.text.split(/\\s+/).filter((w) => w.length > 0);\n if (rawWords.length === 0) return [];\n\n const duration = phrase.endTime - phrase.startTime;\n const wordDuration = duration / rawWords.length;\n\n return rawWords.map((text, i) => ({\n text,\n startTime: phrase.startTime + i * wordDuration,\n endTime: phrase.startTime + (i + 1) * wordDuration,\n }));\n}\n\n/**\n * Build a continuous stream of timed words from ALL caption phrases,\n * then group into uniform display chunks. This eliminates the \"pulsy\"\n * feeling caused by per-phrase chunking with variable phrase lengths.\n */\nfunction buildWordStream(captions: CaptionTrack): { words: TimedWord[]; chunks: WordChunk[] } {\n // Gather all words across all phrases\n const allWords: TimedWord[] = [];\n for (const phrase of captions.phrases) {\n allWords.push(...resolvePhraseTiming(phrase));\n }\n\n if (allWords.length === 0) return { words: [], chunks: [] };\n\n // Determine uniform chunk size\n let chunkSize = TARGET_CHUNK_SIZE;\n if (allWords.length > chunkSize) {\n const numChunks = Math.ceil(allWords.length / chunkSize);\n chunkSize = Math.ceil(allWords.length / numChunks);\n chunkSize = Math.min(MAX_CHUNK_SIZE, Math.max(MIN_CHUNK_SIZE, chunkSize));\n } else {\n chunkSize = Math.max(MIN_CHUNK_SIZE, allWords.length);\n }\n\n // Build chunks\n const chunks: WordChunk[] = [];\n for (let i = 0; i < allWords.length; i += chunkSize) {\n const chunkWords = allWords.slice(i, i + chunkSize);\n chunks.push({\n words: chunkWords,\n startTime: chunkWords[0].startTime,\n endTime: chunkWords[chunkWords.length - 1].endTime,\n });\n }\n\n return { words: allWords, chunks };\n}\n\ninterface SocialCaptionOverlayProps {\n captions: CaptionTrack | undefined;\n currentTime: number;\n enabled?: boolean;\n theme?: Theme;\n viewport?: ViewportConfig;\n}\n\nexport function SocialCaptionOverlay({\n captions,\n currentTime,\n enabled = true,\n theme,\n viewport,\n}: SocialCaptionOverlayProps) {\n // Build the word stream once when captions change (memoized)\n const { chunks } = useMemo(\n () => (captions ? buildWordStream(captions) : { words: [], chunks: [] }),\n [captions],\n );\n\n if (!enabled || chunks.length === 0) {\n return (\n <div\n className=\"social-caption-overlay\"\n style={{\n position: 'absolute',\n bottom: '18%',\n left: 0,\n right: 0,\n zIndex: 50,\n pointerEvents: 'none',\n opacity: 0,\n transition: 'opacity 0.15s ease-in-out',\n }}\n />\n );\n }\n\n // Find the active chunk and word using binary-style search\n let activeChunk: WordChunk | null = null;\n let activeWordIndex = -1;\n\n for (const chunk of chunks) {\n if (currentTime >= chunk.startTime && currentTime < chunk.endTime) {\n activeChunk = chunk;\n break;\n }\n }\n\n // If between chunks (gap), show the nearest chunk\n if (!activeChunk) {\n for (let i = 0; i < chunks.length - 1; i++) {\n if (currentTime >= chunks[i].endTime && currentTime < chunks[i + 1].startTime) {\n // In a gap — show the chunk we just left (feels more natural)\n activeChunk = chunks[i];\n activeWordIndex = activeChunk.words.length - 1;\n break;\n }\n }\n // Past all chunks\n if (!activeChunk && chunks.length > 0 && currentTime >= chunks[chunks.length - 1].startTime) {\n activeChunk = chunks[chunks.length - 1];\n activeWordIndex = activeChunk.words.length - 1;\n }\n }\n\n if (!activeChunk) return null;\n\n // Find active word within chunk (if not already set from gap handling)\n if (activeWordIndex === -1) {\n for (let i = 0; i < activeChunk.words.length; i++) {\n const word = activeChunk.words[i];\n if (currentTime >= word.startTime && currentTime < word.endTime) {\n activeWordIndex = i;\n break;\n }\n }\n // Fallback: last word before currentTime\n if (activeWordIndex === -1) {\n for (let i = activeChunk.words.length - 1; i >= 0; i--) {\n if (currentTime >= activeChunk.words[i].startTime) {\n activeWordIndex = i;\n break;\n }\n }\n }\n // Final fallback\n if (activeWordIndex === -1) activeWordIndex = 0;\n }\n\n // Theme-derived styling\n const primaryColor = theme?.colors?.primary ?? '#5b9bd5';\n const fontFamily = theme?.typography?.titleFont\n ? resolveFontFamily(theme.typography.titleFont, '\"PT Serif\", Georgia, serif')\n : '\"PT Serif\", Georgia, serif';\n\n // Scale font to viewport — aim for ~5.5% of viewport height\n const viewportHeight = viewport?.height ?? 720;\n const baseFontSize = Math.round(viewportHeight * 0.055);\n const fontSize = Math.max(24, Math.min(72, baseFontSize));\n\n return (\n <div\n className=\"social-caption-overlay\"\n style={{\n position: 'absolute',\n bottom: '18%',\n left: 0,\n right: 0,\n zIndex: 50,\n pointerEvents: 'none',\n textAlign: 'center',\n padding: '0 8%',\n boxSizing: 'border-box',\n opacity: 1,\n transition: 'opacity 0.15s ease-in-out',\n }}\n >\n <div\n style={{\n display: 'inline-block',\n lineHeight: 1.3,\n }}\n >\n {activeChunk.words.map((word, i) => {\n const isActive = i === activeWordIndex;\n return (\n <span\n key={`${word.startTime}-${i}`}\n style={{\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight: isActive ? 800 : 600,\n color: isActive ? primaryColor : 'rgba(255, 255, 255, 0.9)',\n textShadow: '0 2px 8px rgba(0,0,0,0.7), 0 0 20px rgba(0,0,0,0.4)',\n marginRight: i < activeChunk!.words.length - 1 ? '0.3em' : undefined,\n transition: 'color 0.1s ease, font-weight 0.1s ease',\n textTransform: 'uppercase',\n }}\n >\n {word.text}\n </span>\n );\n })}\n </div>\n </div>\n );\n}\n","import { useCallback, useMemo, useSyncExternalStore } from 'react';\nimport { DARK_SURFACE, LIGHT_SURFACE, type SurfaceScheme } from '@bendyline/squisq/schemas';\n\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\nconst getServerSnapshot = () => LIGHT_SURFACE;\n\n/**\n * Live-track `prefers-color-scheme` and return a stable SurfaceScheme.\n * `enabled: false` short-circuits to LIGHT_SURFACE (callers pass `false`\n * when a static surface was provided so the hook never observes the\n * media query). The `MediaQueryList` and the `subscribe`/`getSnapshot`\n * callbacks are memoized so `useSyncExternalStore` doesn't resubscribe on\n * every parent render.\n */\nexport function useAutoSurface(enabled: boolean): SurfaceScheme {\n const mql = useMemo(\n () => (enabled && typeof window !== 'undefined' ? window.matchMedia(DARK_QUERY) : null),\n [enabled],\n );\n\n const subscribe = useCallback(\n (cb: () => void) => {\n if (!mql) return () => {};\n mql.addEventListener('change', cb);\n return () => mql.removeEventListener('change', cb);\n },\n [mql],\n );\n\n const getSnapshot = useCallback(() => (mql?.matches ? DARK_SURFACE : LIGHT_SURFACE), [mql]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\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 // In fallback mode the <audio> element is NOT the clock — there's no real\n // source (e.g. the editor preview's synthetic, empty-src track), so the\n // synthetic timer and `seekTo` own `currentTime`. A `timeupdate` fired as\n // a side effect of programmatically setting `audio.currentTime` (during a\n // seek) would otherwise clobber the just-seeked position with the empty\n // element's unreliable `currentTime`, snapping the scrubber back.\n if (fallbackMode.current) return;\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 // Don't clear `fallbackMode` here. Whether the <audio> element is really\n // the clock is decided authoritatively by the play() promise: it only\n // resolves (clearing fallback, see `play`) when a real source actually\n // plays. The 'play' event, by contrast, can fire spuriously on the\n // source-less preview element — and clearing fallback there makes the\n // synthetic timer's tick guard bail on its next frame, freezing the\n // clock and the scrubber after a seek/resume.\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 // When totalDuration is 0 (no audio segments), don't clamp — allow\n // seeking by block timing alone (used in render mode / preview).\n const clampedTime =\n totalDuration > 0 ? Math.max(0, Math.min(time, totalDuration)) : Math.max(0, time);\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 { useMemo, useCallback, useRef } from 'react';\nimport type { Doc, Block, DocBlock } from '@bendyline/squisq/schemas';\nimport type { Theme } from '@bendyline/squisq/schemas';\nimport {\n DEFAULT_THEME,\n getBlockAtTime,\n resolveBlockTransition,\n resolveTransitionDuration,\n} from '@bendyline/squisq/schemas';\nimport {\n expandDocBlocks,\n flattenRenderableBlocks,\n isTemplateBlock,\n resolvePersistentLayers,\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 // `renderMode` is retained for API/signature compatibility; block transitions\n // are now computed identically for real-time and render (export) modes.\n void renderMode;\n // Expand any template blocks into full blocks\n const blocks = useMemo(() => {\n if (!script?.blocks) {\n return [];\n }\n\n // Flatten nested block hierarchy (markdown-derived docs have children).\n // `flattenRenderableBlocks` skips the children of container templates\n // (`diagram`, `drawing`) — those are consumed by the parent's render as\n // nodes/shapes, so they must not also appear as their own slides.\n const hasChildren = script.blocks.some((b) => b.children && b.children.length > 0);\n const flatBlocks = hasChildren ? flattenRenderableBlocks(script.blocks) : script.blocks;\n\n // Check if any blocks are templates\n const hasTemplates = flatBlocks.some(isTemplateBlock);\n\n // Doc persistent layers win wholesale; docs without any inherit the\n // theme's (see resolvePersistentLayers). Passed as a narrow object so\n // the memo deps stay field-precise.\n const resolvedTheme = theme ?? DEFAULT_THEME;\n const persistentLayers = resolvePersistentLayers(\n { persistentLayers: script.persistentLayers },\n resolvedTheme,\n );\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(flatBlocks as DocBlock[], {\n audioSegments,\n viewport,\n persistentLayers,\n theme,\n // Custom (user-defined) templates inlined into the doc's\n // frontmatter — see CustomTemplates.ts. Merged onto the\n // built-in registry so blocks annotated with `{[myhero]}`\n // resolve through the user's design.\n customTemplates: script.customTemplates,\n });\n return expanded;\n }\n\n // All raw blocks — used as-is except for the theme's default transition\n // fallback (copies, never mutations: these blocks are caller-owned).\n return flatBlocks.map((block, index) => {\n const transition = resolveBlockTransition(block, resolvedTheme, index);\n return transition !== block.transition ? { ...block, transition } : block;\n });\n }, [\n script?.blocks,\n script?.audio?.segments,\n script?.persistentLayers,\n script?.customTemplates,\n viewport,\n theme,\n ]);\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 // ─── Transition tracking (synchronous — no effect lag) ──────────────\n // `isEntering` is simply \"we are within the block's entrance window\"\n // (blockTime < the transition's duration). Deriving it during render —\n // rather than flipping it in an effect a frame AFTER the block changes —\n // means a newly-active block renders WITH its entrance state on its very\n // first frame. Otherwise the block paints once fully settled and then, a\n // frame later, snaps back to the start of its entrance animation: the brief\n // \"flash then re-animate\" seen between blocks. This runs identically for\n // real-time playback and frame-seeked render (export) mode.\n //\n // The block we transitioned FROM (for the crossfade) is tracked with refs\n // updated during render — the standard \"previous value\" pattern — so the\n // outgoing block is known on the SAME frame the new block becomes active\n // (an effect would lag a frame and drop the crossfade's first frames).\n const outgoingBlockRef = useRef<Block | null>(null);\n const activeBlockIdRef = useRef<string | null>(null);\n const lastRenderedBlockRef = useRef<Block | null>(null);\n if (currentBlock && currentBlock.id !== activeBlockIdRef.current) {\n outgoingBlockRef.current = lastRenderedBlockRef.current;\n activeBlockIdRef.current = currentBlock.id;\n }\n lastRenderedBlockRef.current = currentBlock;\n\n const transitionDuration = currentBlock?.transition\n ? resolveTransitionDuration(currentBlock.transition)\n : 0;\n const isEntering = !!currentBlock && transitionDuration > 0 && blockTime < transitionDuration;\n const outgoingBlock = outgoingBlockRef.current;\n // Only crossfade a genuinely different outgoing block (guards restarts/seeks\n // where the \"previous\" resolves to the same block).\n const isExiting = isEntering && outgoingBlock != null && outgoingBlock.id !== currentBlock?.id;\n const previousBlock = isExiting ? outgoingBlock : 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,\n isEntering,\n isExiting,\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 // Fill position tracks the same timeline as the clock readout and the block\n // marker dots — elapsed playback time over the audio `totalDuration`. (We\n // deliberately don't use `state.docProgress`, which is keyed to the doc's\n // estimated `script.duration`; when that diverges from the actual playback\n // length — e.g. the editor preview's reading-time estimate — the fill would\n // desync from the clock and the dots.)\n const playProgress =\n state.totalDuration > 0 ? Math.max(0, Math.min(1, state.currentTime / state.totalDuration)) : 0;\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 No CSS `width` transition: the fill is driven by frequent JS updates\n (the synthetic fallback timer runs ~60fps; real audio fires\n `timeupdate` several times/sec), so it's already smooth. A CSS\n transition here actively breaks playback — after a discontinuous\n backward seek, the per-frame inline-width updates on resume keep\n interrupting the in-flight transition before it advances, so the\n *rendered* width latches at the seek position even though the inline\n style (and the clock) keep climbing. The result is a frozen-looking\n bar while time advances. Update width instantly instead. */}\n <div\n data-testid=\"doc-progress-fill\"\n style={{\n position: 'absolute',\n left: 0,\n width: `${playProgress * 100}%`,\n height: '6px',\n background: '#5b9bd5',\n borderRadius: '3px',\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 #5b9bd5',\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 * - `'page'` — Plain semantic HTML preview matching what the\n * `markdownDocToPlainHtml` export produces. No SquisqPlayer, no SVG\n * cards — just `<h1>`/`<p>`/`<ul>` etc. inside a sandboxed iframe.\n * Use when you want a WYSIWYG view of the simple HTML export.\n */\nexport type DisplayMode = 'video' | 'slideshow' | 'linear' | 'page';\n\n/**\n * Caption display style.\n *\n * - `'standard'` — Traditional broadcast-style captions: small white text\n * on a semi-transparent black badge at the top of the player.\n * - `'social'` — Social media-style (Instagram/TikTok): large centered words\n * showing 3-5 words at a time with the active word highlighted in the\n * theme's primary color. Font and colors pulled from the active theme.\n */\nexport type CaptionStyle = 'standard' | 'social';\n\n/**\n * Caption display mode — combines enable/disable with style selection.\n * The CC button cycles through: off → standard → social → off.\n */\nexport type CaptionMode = 'off' | 'standard' | 'social';\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 /** Current caption display mode (off, standard, social). */\n captionMode: CaptionMode;\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 /** Cycle caption mode: off → standard → social → off */\n cycleCaptionMode: () => 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 mode cycle button: off → standard → social → off */}\n {state.hasCaptions && (\n <button\n onClick={() => actions.cycleCaptionMode()}\n style={{\n background: state.captionMode !== 'off' ? 'rgba(255,255,255,0.2)' : 'none',\n border: 'none',\n color: state.captionMode !== 'off' ? 'white' : 'rgba(255,255,255,0.5)',\n cursor: 'pointer',\n padding: '8px',\n fontSize: '12px',\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n borderRadius: '4px',\n }}\n title={\n state.captionMode === 'off'\n ? 'Captions: Off (click for Standard)'\n : state.captionMode === 'standard'\n ? 'Captions: Standard (click for Social)'\n : 'Captions: Social (click to turn off)'\n }\n aria-label=\"Cycle caption style\"\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 {state.captionMode !== 'off' && (\n <span style={{ fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.5px' }}>\n {state.captionMode === 'standard' ? 'CC' : 'SM'}\n </span>\n )}\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 { useAutoSurface } from './hooks/useAutoSurface';\nimport type { Doc, Block, DocBlock } from '@bendyline/squisq/schemas';\nimport type { ViewportConfig } from '@bendyline/squisq/schemas';\nimport {\n applySurface,\n resolveFontFamily,\n type SurfaceScheme,\n type Theme,\n} from '@bendyline/squisq/schemas';\nimport { VIEWPORT_PRESETS } from '@bendyline/squisq/schemas';\nimport { getLayers, hasTemplate, DEFAULT_THEME, deriveTemplateInputs } from '@bendyline/squisq/doc';\nimport type { RenderContext } from '@bendyline/squisq/doc';\nimport { extractPlainText } 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 * Optional surface scheme (light / dark paper) overlaid on top of the\n * theme's colors. Orthogonal to `theme` — a theme picks editorial\n * identity, a surface picks the paper. Pass `'auto'` to follow the\n * user's OS `prefers-color-scheme`, a `SurfaceScheme` object to force a\n * specific surface, or omit to use the theme's built-in colors.\n */\n surface?: SurfaceScheme | 'auto';\n /**\n * Use tight padding + a wider content column. The default layout is\n * designed for a reading surface with breathing room (720px column,\n * 24×16px padding). Short conversational snippets like chat replies\n * benefit from a much tighter layout. Set to `true` to render with\n * minimal padding and no max-width cap so the content hugs its\n * container.\n */\n thinMargins?: boolean;\n /**\n * How images inside the doc should be sized. `'inline'` (default)\n * flows them at natural size up to the column width; `'thumbnail'`\n * constrains each image to a 100×100 box with aspect-preserving\n * containment — use for chat history and other dense surfaces where\n * full-size images would dominate the layout.\n */\n imageDisplayMode?: ImageDisplayMode;\n}\n\nexport type ImageDisplayMode = 'inline' | 'thumbnail';\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 !!annotation.template && 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\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 ...(deriveTemplateInputs(\n annotation.template ?? 'sectionHeader',\n headingText,\n block.contents,\n {\n placeholders: true,\n },\n ) ?? {}),\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 The heading is intentionally *not* duplicated above the card —\n every template card renders its own title layer internally, so\n a separate `squisq-linear-card-label` only made the heading\n appear twice in the linear view. The card also drops its\n rounded border + drop shadow so it reads as a continuation of\n the surrounding page rather than a chrome'd preview. */}\n {isAnnotated && visualBlock && (\n <div className=\"squisq-linear-card\">\n <div\n className=\"squisq-linear-card-svg\"\n style={{\n width: '100%',\n aspectRatio: `${viewport.width} / ${viewport.height}`,\n overflow: 'hidden',\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// ── 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 surface,\n thinMargins = false,\n imageDisplayMode = 'inline',\n}: LinearDocViewProps) {\n const activeViewport = viewport ?? VIEWPORT_PRESETS.landscape;\n const totalBlocks = useMemo(() => countAll(doc.blocks), [doc.blocks]);\n const autoSurface = useAutoSurface(surface === 'auto');\n const resolvedSurface: SurfaceScheme | undefined = surface === 'auto' ? autoSurface : surface;\n\n const renderContext: RenderContext = useMemo(() => {\n const baseTheme = theme ?? DEFAULT_THEME;\n const effectiveTheme = resolvedSurface ? applySurface(baseTheme, resolvedSurface) : baseTheme;\n return {\n theme: effectiveTheme,\n viewport: activeViewport,\n totalBlocks,\n // Theme atmosphere (vignette/grain/gradient persistent layers) shows\n // on the inline template cards so they match the player's look.\n persistentLayers: effectiveTheme.persistentLayers,\n };\n }, [activeViewport, totalBlocks, theme, resolvedSurface]);\n\n const activeTheme = renderContext.theme!;\n const bgColor = activeTheme.colors.background;\n const textColor = activeTheme.colors.text;\n const mutedColor = activeTheme.colors.textMuted;\n const primaryColor = activeTheme.colors.primary;\n const bodyFont = resolveFontFamily(activeTheme.typography.bodyFont, 'system-ui, sans-serif');\n const titleFont = resolveFontFamily(activeTheme.typography.titleFont, 'Georgia, serif');\n const lineHt = activeTheme.typography.lineHeight ?? 1.7;\n\n return (\n <div\n className={`squisq-linear ${className || ''}`}\n style={{\n width: '100%',\n // Thin-margins mode is the \"embedded in someone else's container\"\n // signal (chat bubble, sidebar preview). Fit to content there so\n // the host's bubble doesn't render a tall empty box when the doc\n // is short. Standalone mode keeps height:100% for full-viewport\n // scrolling.\n height: thinMargins ? 'auto' : '100%',\n overflowY: thinMargins ? 'visible' : 'auto',\n overflowX: 'hidden',\n background: bgColor,\n }}\n >\n <div\n className={`squisq-linear-content squisq-md${thinMargins ? ' squisq-linear-content--thin' : ''}${imageDisplayMode === 'thumbnail' ? ' squisq-linear-content--thumbnail-images' : ''}`}\n style={\n {\n // Thin-margins mode drops the 720px reading column + generous\n // page padding (right for standalone docs) in favor of a tight\n // layout that hugs its container (right for chat bubbles and\n // sidebar previews).\n maxWidth: thinMargins ? 'none' : '720px',\n margin: thinMargins ? '0' : '0 auto',\n padding: thinMargins ? '0' : '24px 16px',\n lineHeight: lineHt,\n fontSize: '16px',\n fontFamily: bodyFont,\n color: textColor,\n // CSS custom properties for MarkdownRenderer / nested elements\n '--squisq-linear-title-font': titleFont,\n '--squisq-linear-body-font': bodyFont,\n '--squisq-linear-text': textColor,\n '--squisq-linear-muted': mutedColor,\n '--squisq-linear-primary': primaryColor,\n '--squisq-linear-bg': bgColor,\n } as React.CSSProperties\n }\n >\n {/* Theme-aware typography and layout for document mode */}\n <style>{`\n .squisq-linear-content h1,\n .squisq-linear-content h2,\n .squisq-linear-content h3,\n .squisq-linear-content h4,\n .squisq-linear-content h5,\n .squisq-linear-content h6 {\n font-family: var(--squisq-linear-title-font);\n color: var(--squisq-linear-text);\n margin-top: 1.2em;\n margin-bottom: 0.4em;\n }\n .squisq-linear-content h1 { font-size: 2em; }\n .squisq-linear-content h2 { font-size: 1.5em; }\n .squisq-linear-content h3 { font-size: 1.25em; }\n .squisq-linear-content p {\n margin-bottom: 0.75em;\n }\n .squisq-linear-content ul,\n .squisq-linear-content ol {\n padding-left: 2em;\n margin-bottom: 0.75em;\n }\n .squisq-linear-content li {\n margin-bottom: 0.3em;\n }\n .squisq-linear-content a {\n /* Blend the theme's primary toward the body text color so\n links stay theme-flavored but always have enough contrast\n against the background. Some themes (e.g. Gezellig) pick a\n mid-tone primary that's nearly invisible on a dark page\n without this lift. */\n color: color-mix(in srgb, var(--squisq-linear-primary) 65%, var(--squisq-linear-text));\n text-decoration: underline;\n text-decoration-thickness: 1px;\n text-underline-offset: 2px;\n }\n .squisq-linear-content a:hover {\n color: var(--squisq-linear-primary);\n }\n .squisq-linear-content code {\n color: var(--squisq-linear-primary);\n font-size: 0.9em;\n padding: 0.15em 0.3em;\n border-radius: 3px;\n background: rgba(128, 128, 128, 0.15);\n }\n .squisq-linear-content pre {\n padding: 1em;\n border-radius: 6px;\n background: rgba(0, 0, 0, 0.2);\n overflow-x: auto;\n margin-bottom: 0.75em;\n }\n .squisq-linear-content pre code {\n padding: 0;\n background: none;\n }\n .squisq-linear-content blockquote {\n border-left: 3px solid var(--squisq-linear-muted);\n color: var(--squisq-linear-muted);\n padding-left: 1em;\n margin-left: 0;\n margin-bottom: 0.75em;\n }\n .squisq-linear-content hr {\n border: none;\n border-top: 1px solid var(--squisq-linear-muted);\n margin: 1.5em 0;\n }\n .squisq-linear-content img {\n max-width: 100%;\n height: auto;\n border-radius: 6px;\n margin: 0.5em 0;\n }\n .squisq-linear-content--thumbnail-images img {\n max-width: 100px;\n max-height: 100px;\n width: auto;\n height: auto;\n object-fit: contain;\n display: block;\n }\n .squisq-linear-content strong {\n font-weight: 700;\n }\n .squisq-linear-content em {\n font-style: italic;\n }\n .squisq-linear-content table {\n width: 100%;\n border-collapse: collapse;\n margin: 1em 0;\n font-size: 0.95em;\n }\n .squisq-linear-content thead th {\n background: var(--squisq-linear-primary);\n color: var(--squisq-linear-bg);\n font-family: var(--squisq-linear-title-font);\n font-weight: 600;\n padding: 10px 14px;\n text-align: left;\n }\n .squisq-linear-content tbody td {\n padding: 8px 14px;\n border-bottom: 1px solid color-mix(in srgb, var(--squisq-linear-muted) 30%, transparent);\n }\n .squisq-linear-content tbody tr:hover {\n background: color-mix(in srgb, var(--squisq-linear-primary) 8%, transparent);\n }\n `}</style>\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 {\n sanitizeHtmlNodes,\n sanitizeUrl,\n type HtmlPolicy,\n type HtmlElement,\n type HtmlNode,\n MarkdownBlockNode,\n MarkdownInlineNode,\n MarkdownListItem,\n MarkdownTableRow,\n MarkdownTableCell,\n} from '@bendyline/squisq/markdown';\nimport { useMediaUrl } from './hooks/MediaContext';\nimport { InlineVideoPlayer } from './InlineVideoPlayer.js';\nimport { InlineAudioPlayer } from './InlineAudioPlayer.js';\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 * Raw HTML policy. Defaults to `sanitize`, which removes unsafe tags,\n * event handlers, and executable URL schemes before rendering.\n */\n htmlPolicy?: HtmlPolicy;\n}\n\n// ── Inline Renderer ────────────────────────────────────────────────\n\n/** Render an array of inline nodes into React elements. */\nfunction renderInline(\n nodes: MarkdownInlineNode[],\n keyPrefix = '',\n htmlPolicy: HtmlPolicy = 'sanitize',\n): React.ReactNode[] {\n return nodes.map((node, i) => {\n const key = `${keyPrefix}i${i}`;\n switch (node.type) {\n case 'text': {\n // Soft breaks (newlines without two trailing spaces) become \\n in\n // text nodes. HTML collapses \\n to whitespace, which loses the visual\n // line break the author wanted, so render <br> for each newline.\n if (!node.value.includes('\\n')) {\n return <Fragment key={key}>{node.value}</Fragment>;\n }\n const parts = node.value.split('\\n');\n return (\n <Fragment key={key}>\n {parts.map((part, j) => (\n <Fragment key={j}>\n {j > 0 && <br />}\n {part}\n </Fragment>\n ))}\n </Fragment>\n );\n }\n\n case 'emphasis':\n return (\n <em key={key} className=\"squisq-md-em\">\n {renderInline(node.children, key, htmlPolicy)}\n </em>\n );\n\n case 'strong':\n return (\n <strong key={key} className=\"squisq-md-strong\">\n {renderInline(node.children, key, htmlPolicy)}\n </strong>\n );\n\n case 'delete':\n return (\n <del key={key} className=\"squisq-md-del\">\n {renderInline(node.children, key, htmlPolicy)}\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 const href = sanitizeUrl(node.url, 'link');\n if (!href) {\n return (\n <span key={key} className=\"squisq-md-link squisq-md-link--blocked\">\n {renderInline(node.children, key, htmlPolicy)}\n </span>\n );\n }\n return (\n <a\n key={key}\n className=\"squisq-md-link\"\n href={href}\n title={node.title ?? undefined}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {renderInline(node.children, key, htmlPolicy)}\n </a>\n );\n }\n\n case 'image':\n return (\n <MdImage key={key} src={node.url} alt={node.alt ?? ''} title={node.title ?? undefined} />\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 if (htmlPolicy === 'strip') return null;\n // Fast path: no <video>/<audio> in the subtree → use the original\n // rawHtml passthrough (preserves arbitrary HTML for custom embeds).\n if (\n htmlPolicy === 'trusted' &&\n !containsMediaTag(node.htmlChildren) &&\n !containsDangerousTag(node.htmlChildren) &&\n !hasDangerousRawHtml(node.rawHtml)\n ) {\n return (\n <span\n key={key}\n className=\"squisq-md-html-inline\"\n dangerouslySetInnerHTML={{ __html: node.rawHtml }}\n />\n );\n }\n // Otherwise reconstruct the subtree as React so <video>/<audio>\n // go through MediaContext-aware player components.\n return (\n <span key={key} className=\"squisq-md-html-inline\">\n {renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, htmlPolicy), `${key}h`)}\n </span>\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, htmlPolicy)}\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, htmlPolicy)}\n </span>\n );\n\n case 'mention':\n return (\n <span\n key={key}\n className=\"squisq-md-mention mention\"\n data-mention=\"true\"\n data-kind={node.targetKind}\n data-id={node.targetId}\n data-label={node.displayName}\n >\n @{node.displayName}\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(\n node: MarkdownBlockNode,\n key: string,\n htmlPolicy: HtmlPolicy = 'sanitize',\n): React.ReactNode {\n switch (node.type) {\n case 'paragraph':\n return (\n <p key={key} className=\"squisq-md-p\">\n {renderInline(node.children, key, htmlPolicy)}\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, htmlPolicy)}\n </Tag>\n );\n }\n\n case 'blockquote':\n return (\n <blockquote key={key} className=\"squisq-md-blockquote\">\n {renderBlocks(node.children, key, htmlPolicy)}\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}`, htmlPolicy))}\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}`, htmlPolicy))}\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, htmlPolicy);\n\n case 'htmlBlock':\n if (htmlPolicy === 'strip') return null;\n // Fast path: no <video>/<audio> → preserve the existing rawHtml\n // passthrough so arbitrary HTML embeds still survive verbatim.\n if (\n htmlPolicy === 'trusted' &&\n !containsMediaTag(node.htmlChildren) &&\n !containsDangerousTag(node.htmlChildren) &&\n !hasDangerousRawHtml(node.rawHtml)\n ) {\n return (\n <div\n key={key}\n className=\"squisq-md-html-block\"\n dangerouslySetInnerHTML={{ __html: node.rawHtml }}\n />\n );\n }\n // Otherwise reconstruct subtree as React so <video>/<audio>\n // route through the player components and resolve via MediaContext.\n return (\n <div key={key} className=\"squisq-md-html-block\">\n {renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, htmlPolicy), `${key}h`)}\n </div>\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, htmlPolicy)}\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, htmlPolicy)}\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, htmlPolicy)}\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}`, htmlPolicy)}\n </dt>\n );\n }\n return (\n <dd key={`${key}dd${i}`} className=\"squisq-md-dd\">\n {renderBlocks(child.children, `${key}dd${i}`, htmlPolicy)}\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(\n item: MarkdownListItem,\n key: string,\n htmlPolicy: HtmlPolicy = 'sanitize',\n): 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, htmlPolicy)}\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 htmlPolicy: HtmlPolicy = 'sanitize',\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}`, htmlPolicy)}\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}`, htmlPolicy)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n )}\n </table>\n );\n}\n\n/** Render an array of block-level nodes. */\nfunction renderBlocks(\n nodes: MarkdownBlockNode[],\n keyPrefix = '',\n htmlPolicy: HtmlPolicy = 'sanitize',\n): React.ReactNode[] {\n return nodes.map((node, i) => renderBlock(node, `${keyPrefix}b${i}`, htmlPolicy));\n}\n\n// ── Image with MediaProvider resolution ───────────────────────────\n\n/** Renders an <img> that resolves its src through the MediaProvider when available. */\nfunction MdImage({ src, alt, title }: { src: string; alt: string; title?: string }) {\n const safeSrc = sanitizeUrl(src, 'media');\n const resolved = useMediaUrl(safeSrc ?? '', '.');\n if (!safeSrc) return null;\n return <img className=\"squisq-md-image\" src={resolved} alt={alt} title={title} />;\n}\n\n// ── Raw-HTML walker (intercepts <video>/<audio>) ─────────────────\n\n/** True when the htmlElement subtree contains a tag we want to swap\n * for a React component. Cheap recursive scan — lets us keep the\n * `dangerouslySetInnerHTML` fast path for everything else. */\nfunction resolveHtmlNodes(nodes: HtmlNode[], htmlPolicy: HtmlPolicy): HtmlNode[] {\n if (htmlPolicy === 'strip') return [];\n if (htmlPolicy === 'trusted') return nodes;\n return sanitizeHtmlNodes(nodes);\n}\n\nfunction containsMediaTag(nodes: HtmlNode[]): boolean {\n for (const node of nodes) {\n if (node.type !== 'htmlElement') continue;\n const tagName = node.tagName.toLowerCase();\n if (tagName === 'video' || tagName === 'audio') return true;\n if (containsMediaTag(node.children)) return true;\n }\n return false;\n}\n\n/**\n * Tags that can escape their container and affect the whole host\n * document — global styling, script execution, external/resource loads,\n * or framing. Markdown content is frequently untrusted (LLM output,\n * pasted snippets), and these tags have no business mutating the page\n * they're embedded in, so they are *always* dropped — even under the\n * `'trusted'` policy. \"Trusted\" means \"render this HTML's structure\n * verbatim,\" not \"let it restyle or script the host.\" A stray `<style>`\n * applies document-wide (CSS has no per-element scoping outside shadow\n * DOM / iframes), which is exactly how an embedded game's `<style>`\n * leaked onto the surrounding app chrome.\n */\nconst DANGEROUS_HTML_TAGS = new Set([\n 'base',\n 'embed',\n 'iframe',\n 'link',\n 'meta',\n 'object',\n 'script',\n 'style',\n 'title',\n]);\n\n/** True when the subtree contains any host-affecting tag (see\n * {@link DANGEROUS_HTML_TAGS}). Mirrors {@link containsMediaTag}: keeps\n * such content off the verbatim `dangerouslySetInnerHTML` fast path so\n * it routes through the React reconstruction, which drops the tag. */\nfunction containsDangerousTag(nodes: HtmlNode[]): boolean {\n for (const node of nodes) {\n if (node.type !== 'htmlElement') continue;\n if (DANGEROUS_HTML_TAGS.has(node.tagName.toLowerCase())) return true;\n if (containsDangerousTag(node.children)) return true;\n }\n return false;\n}\n\n/**\n * Raw-string backstop for {@link DANGEROUS_HTML_TAGS}. The structural\n * {@link containsDangerousTag} check covers the normal case, but a block\n * parsed with `parseHtml: false` carries an empty `htmlChildren` while\n * `rawHtml` still holds the markup — so the verbatim fast path scans the\n * raw string too, guaranteeing a `<style>`/`<script>` can never be\n * injected into the host document by that path no matter how the node\n * was produced. The `\\b` keeps `<styled-thing>` from matching `<style>`.\n */\nconst DANGEROUS_RAW_HTML_RE =\n /<\\s*\\/?\\s*(?:base|embed|iframe|link|meta|object|script|style|title)\\b/i;\n\nfunction hasDangerousRawHtml(rawHtml: string): boolean {\n return DANGEROUS_RAW_HTML_RE.test(rawHtml);\n}\n\n/** A pragmatic shortlist of HTML attributes the raw-HTML walker\n * passes through to React when reconstructing a non-media element.\n * Anything outside this list is silently dropped — the media-tag\n * fast path means most authors will never hit this code, so we\n * keep the surface narrow to avoid React warnings about unknown\n * attributes. */\nconst PASSTHROUGH_ATTRS: Record<string, string> = {\n // common\n class: 'className',\n id: 'id',\n title: 'title',\n style: 'style',\n // media-adjacent (used when video/audio appear inside other wrappers)\n width: 'width',\n height: 'height',\n src: 'src',\n alt: 'alt',\n loading: 'loading',\n decoding: 'decoding',\n // anchor\n href: 'href',\n target: 'target',\n rel: 'rel',\n};\n\nfunction reactPropsFromAttrs(attrs: Record<string, string>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(attrs)) {\n const propName = PASSTHROUGH_ATTRS[name];\n if (!propName) continue;\n if (propName === 'style') {\n // We can't safely parse arbitrary `style=\"...\"` strings without\n // an HTML parser; preserve as a data attribute so authors can\n // see it survived round-trip without crashing React.\n out['data-style'] = value;\n continue;\n }\n out[propName] = value;\n }\n return out;\n}\n\nfunction renderHtmlElement(el: HtmlElement, key: string): React.ReactNode {\n const tagName = el.tagName.toLowerCase();\n // Final safety net: never reconstruct a host-affecting element (e.g. a\n // <style> that would leak globally), whatever the policy. The fast path\n // is gated by containsDangerousTag, so trusted content carrying these\n // tags lands here — drop the tag and keep the rest of the subtree.\n if (DANGEROUS_HTML_TAGS.has(tagName)) return null;\n if (tagName === 'video') {\n return (\n <InlineVideoPlayer\n key={key}\n src={el.attributes.src ?? ''}\n width={el.attributes.width}\n height={el.attributes.height}\n poster={el.attributes.poster}\n // The `controls` attribute is a boolean — present means true,\n // even if its value is an empty string.\n controls={'controls' in el.attributes}\n preload={\n el.attributes.preload === 'none' ||\n el.attributes.preload === 'metadata' ||\n el.attributes.preload === 'auto'\n ? el.attributes.preload\n : undefined\n }\n />\n );\n }\n if (tagName === 'audio') {\n return (\n <InlineAudioPlayer\n key={key}\n src={el.attributes.src ?? ''}\n controls={'controls' in el.attributes}\n preload={\n el.attributes.preload === 'none' ||\n el.attributes.preload === 'metadata' ||\n el.attributes.preload === 'auto'\n ? el.attributes.preload\n : undefined\n }\n />\n );\n }\n\n const Tag = tagName as keyof JSX.IntrinsicElements;\n const props = reactPropsFromAttrs(el.attributes);\n if (el.selfClosing) {\n return <Tag key={key} {...props} />;\n }\n return (\n <Tag key={key} {...props}>\n {renderHtmlNodes(el.children, `${key}c`)}\n </Tag>\n );\n}\n\nfunction renderHtmlNodes(nodes: HtmlNode[], keyPrefix: string): React.ReactNode[] {\n return nodes.map((node, i) => {\n const key = `${keyPrefix}${i}`;\n switch (node.type) {\n case 'htmlElement':\n return renderHtmlElement(node, key);\n case 'htmlText':\n return <Fragment key={key}>{node.value}</Fragment>;\n case 'htmlComment':\n // Comments don't render in React; ignore.\n return null;\n default:\n return null;\n }\n });\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({\n nodes,\n className,\n htmlPolicy = 'sanitize',\n}: MarkdownRendererProps) {\n if (!nodes || nodes.length === 0) return null;\n\n return (\n <div className={`squisq-md ${className || ''}`}>{renderBlocks(nodes, '', htmlPolicy)}</div>\n );\n}\n","/**\n * InlineVideoPlayer\n *\n * Small inline HTML5 video player for `<video>` tags embedded directly in\n * a markdown document — e.g. a recording dropped into the doc by the\n * recorder. Resolves the `src` through `MediaContext` (same path\n * `VideoLayer` uses) so a container-backed `audio/foo.webm` or\n * `video/foo.mp4` resolves to a blob URL instead of 404-ing on the\n * filesystem path.\n *\n * Deliberately thin: native browser controls, intrinsic aspect ratio,\n * no autoplay. The MarkdownRenderer swaps this in for a raw `<video>`\n * htmlElement; the editor-react Tiptap video node uses the same\n * component for its NodeView.\n */\nimport { useMediaUrl } from './hooks/MediaContext.js';\n\nexport interface InlineVideoPlayerProps {\n /** Source path — resolved through MediaContext when relative. */\n src: string;\n /** Base path used when no MediaProvider is in context. */\n basePath?: string;\n /** Optional explicit width (pixels or CSS length). */\n width?: number | string;\n /** Optional explicit height (pixels or CSS length). */\n height?: number | string;\n /** Optional poster image src — also resolved through MediaContext. */\n poster?: string;\n /** Whether to show native controls. Defaults to true. */\n controls?: boolean;\n /** `preload` attribute passthrough. Defaults to `'metadata'`. */\n preload?: 'none' | 'metadata' | 'auto';\n /** Extra className on the wrapper. */\n className?: string;\n}\n\nexport function InlineVideoPlayer({\n src,\n basePath = '',\n width,\n height,\n poster,\n controls = true,\n preload = 'metadata',\n className,\n}: InlineVideoPlayerProps) {\n const resolvedSrc = useMediaUrl(src, basePath);\n // Always call the hook (Rules of Hooks); only use the result if a poster\n // was actually requested. Empty input → fallback path which we discard.\n const resolvedPoster = useMediaUrl(poster ?? '', basePath);\n const posterUrl = poster ? resolvedPoster : undefined;\n\n if (!resolvedSrc) return null;\n\n return (\n <span className={`squisq-inline-video-player ${className ?? ''}`.trim()}>\n <video\n src={resolvedSrc}\n poster={posterUrl}\n controls={controls}\n preload={preload}\n width={width}\n height={height}\n playsInline\n />\n </span>\n );\n}\n\nexport default InlineVideoPlayer;\n","/**\n * InlineAudioPlayer\n *\n * Small inline HTML5 audio player for `<audio>` tags embedded directly in\n * a markdown document — e.g. a narration recording the user wants to\n * audition in the editor. Resolves the `src` through `MediaContext` so a\n * container-backed `audio/foo.webm` path returns a playable blob URL.\n *\n * Deliberately thin: native browser controls, no autoplay. The\n * MarkdownRenderer swaps this in for raw `<audio>` htmlElements; the\n * editor-react Tiptap audio node reuses it for its NodeView.\n */\nimport { useMediaUrl } from './hooks/MediaContext.js';\n\nexport interface InlineAudioPlayerProps {\n /** Source path — resolved through MediaContext when relative. */\n src: string;\n /** Base path used when no MediaProvider is in context. */\n basePath?: string;\n /** Whether to show native controls. Defaults to true. */\n controls?: boolean;\n /** `preload` attribute passthrough. Defaults to `'metadata'`. */\n preload?: 'none' | 'metadata' | 'auto';\n /** Extra className on the wrapper. */\n className?: string;\n}\n\nexport function InlineAudioPlayer({\n src,\n basePath = '',\n controls = true,\n preload = 'metadata',\n className,\n}: InlineAudioPlayerProps) {\n const resolvedSrc = useMediaUrl(src, basePath);\n\n if (!resolvedSrc) return null;\n\n return (\n <span className={`squisq-inline-audio-player ${className ?? ''}`.trim()}>\n <audio src={resolvedSrc} controls={controls} preload={preload} />\n </span>\n );\n}\n\nexport default InlineAudioPlayer;\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 mode cycle */}\n {state.hasCaptions && (\n <button\n className={`bottom-ctrl-btn ${state.captionMode !== 'off' ? 'bottom-ctrl-btn--active' : ''}`}\n onClick={() => actions.cycleCaptionMode()}\n title={\n state.captionMode === 'off'\n ? 'Captions: Off'\n : state.captionMode === 'standard'\n ? 'Captions: Standard'\n : 'Captions: Social'\n }\n aria-label=\"Cycle caption style\"\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 mode cycle */}\n {state.hasCaptions && (\n <button\n className={`sidebar-ctrl-btn ${state.captionMode !== 'off' ? 'sidebar-ctrl-btn--active' : ''}`}\n onClick={() => actions.cycleCaptionMode()}\n title={\n state.captionMode === 'off'\n ? 'Captions: Off'\n : state.captionMode === 'standard'\n ? 'Captions: Standard'\n : 'Captions: Social'\n }\n aria-label=\"Cycle caption style\"\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 captionMode: 'off',\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","/**\n * Derive the CSS custom-property bag for `<JsonView>` from a Theme + Surface.\n * Mirrors the pattern in LinearDocView so JsonView re-themes consistently\n * with the rest of Squisq.\n */\n\nimport { useMemo } from 'react';\nimport {\n applySurface,\n resolveFontFamily,\n type SurfaceScheme,\n type Theme,\n} from '@bendyline/squisq/schemas';\nimport { DEFAULT_THEME } from '@bendyline/squisq/doc';\nimport { useAutoSurface } from '../hooks/useAutoSurface';\n\nexport interface JsonViewTokens {\n /** Inline style object to spread onto the root element. */\n style: React.CSSProperties;\n /** The effective theme (after surface application). */\n theme: Theme;\n}\n\nexport function useJsonViewTokens(\n theme: Theme | undefined,\n surface: SurfaceScheme | 'auto' | undefined,\n): JsonViewTokens {\n const auto = useAutoSurface(surface === 'auto');\n const effectiveSurface = surface === 'auto' ? auto : (surface ?? undefined);\n\n return useMemo(() => {\n const baseTheme = theme ?? DEFAULT_THEME;\n const finalTheme = effectiveSurface ? applySurface(baseTheme, effectiveSurface) : baseTheme;\n\n const titleFont = resolveFontFamily(finalTheme.typography.titleFont, 'system-ui, sans-serif');\n const bodyFont = resolveFontFamily(finalTheme.typography.bodyFont, 'system-ui, sans-serif');\n const monoFont = resolveFontFamily(\n finalTheme.typography.monoFont,\n 'ui-monospace, Consolas, monospace',\n );\n\n const style: React.CSSProperties = {\n ['--squisq-json-bg' as string]: finalTheme.colors.background,\n ['--squisq-json-text' as string]: finalTheme.colors.text,\n ['--squisq-json-muted' as string]: finalTheme.colors.textMuted,\n ['--squisq-json-primary' as string]: finalTheme.colors.primary,\n ['--squisq-json-accent' as string]: finalTheme.colors.secondary,\n ['--squisq-json-border' as string]: `color-mix(in srgb, ${finalTheme.colors.textMuted} 35%, transparent)`,\n ['--squisq-json-title-font' as string]: titleFont,\n ['--squisq-json-body-font' as string]: bodyFont,\n ['--squisq-json-mono-font' as string]: monoFont,\n ['--squisq-json-radius' as string]: `${finalTheme.style.borderRadius ?? 8}px`,\n };\n\n return { style, theme: finalTheme };\n }, [theme, effectiveSurface]);\n}\n","/**\n * Recursive dispatcher: looks at a schema node + value, chooses a\n * `ControlKind`, evaluates `hidden` rules, and renders the matching\n * read-only viewer.\n */\n\nimport {\n chooseControl,\n resolveFlag,\n resolveRef,\n type SquisqAnnotatedSchema,\n} from '@bendyline/squisq/jsonForm';\nimport { VIEWERS, type ViewerProps } from './viewers';\n\nexport interface RenderNodeProps {\n value: unknown;\n schema: SquisqAnnotatedSchema;\n rootSchema: SquisqAnnotatedSchema;\n rootData: unknown;\n pointer: string;\n density: 'comfortable' | 'compact';\n /**\n * When a parent group already wrote its own card chrome (e.g. a\n * card-stack item), suppress the nested group's outer title so the\n * UI doesn't double up on labels.\n */\n suppressTopGroupTitle?: boolean;\n}\n\nexport function RenderNode(props: RenderNodeProps): React.ReactElement | null {\n const resolved = resolveRef(props.schema, props.rootSchema) ?? props.schema;\n\n if (resolveFlag(resolved.squisq?.hidden, props.rootData)) return null;\n\n const kind = chooseControl(resolved);\n const Viewer = VIEWERS[kind];\n const viewerProps: ViewerProps = {\n value: props.value,\n schema: resolved,\n rootSchema: props.rootSchema,\n rootData: props.rootData,\n pointer: props.pointer,\n density: props.density,\n };\n if (kind === 'group' || kind === 'card') {\n // GroupViewer accepts an extra prop that RenderNode forwards.\n const Group = Viewer as React.ComponentType<ViewerProps & { suppressTitle?: boolean }>;\n return <Group {...viewerProps} suppressTitle={props.suppressTopGroupTitle} />;\n }\n return <Viewer {...viewerProps} />;\n}\n","/**\n * Read-only viewer renderers for `<JsonView>`. Each viewer is a small\n * pure function that takes a value + schema slice and renders inline.\n * Group / card-stack viewers recurse into `RenderNode`.\n */\n\nimport { Fragment, useMemo } from 'react';\nimport {\n arrayItemKind,\n type ControlKind,\n type SquisqAnnotatedSchema,\n} from '@bendyline/squisq/jsonForm';\nimport { parseMarkdown } from '@bendyline/squisq/markdown';\nimport { MarkdownRenderer } from '../MarkdownRenderer';\nimport { RenderNode } from './RenderNode';\n\nexport interface ViewerProps {\n value: unknown;\n schema: SquisqAnnotatedSchema;\n rootSchema: SquisqAnnotatedSchema;\n rootData: unknown;\n pointer: string;\n density: 'comfortable' | 'compact';\n}\n\n// ── Primitive viewers ──────────────────────────────────────────────\n\nexport function TextViewer({ value }: ViewerProps) {\n if (value === undefined || value === null || value === '') {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n return <span className=\"squisq-jv-value\">{String(value)}</span>;\n}\n\nexport function MultilineViewer({ value }: ViewerProps) {\n if (value === undefined || value === null || value === '') {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n return <span className=\"squisq-jv-value squisq-jv-value--multiline\">{String(value)}</span>;\n}\n\nexport function RichTextViewer({ value }: ViewerProps) {\n const nodes = useMemo(() => {\n if (typeof value !== 'string' || value === '') return null;\n try {\n const doc = parseMarkdown(value);\n return doc.children;\n } catch {\n return null;\n }\n }, [value]);\n if (!nodes) return <span className=\"squisq-jv-empty\">—</span>;\n return (\n <div className=\"squisq-jv-richtext\">\n <MarkdownRenderer nodes={nodes} />\n </div>\n );\n}\n\nexport function NumberViewer({ value }: ViewerProps) {\n if (value === undefined || value === null) {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n return <span className=\"squisq-jv-value\">{String(value)}</span>;\n}\n\nexport function BooleanViewer({ value }: ViewerProps) {\n const on = Boolean(value);\n return (\n <span className={`squisq-jv-toggle squisq-jv-toggle--${on ? 'on' : 'off'}`}>\n {on ? 'On' : 'Off'}\n </span>\n );\n}\n\nexport function EnumViewer({ value, schema }: ViewerProps) {\n if (value === undefined || value === null || value === '') {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n const labels = schema.squisq?.enumLabels;\n const display = labels && typeof value === 'string' ? (labels[value] ?? value) : String(value);\n return <span className=\"squisq-jv-value\">{display}</span>;\n}\n\nexport function ColorViewer({ value }: ViewerProps) {\n if (typeof value !== 'string' || value === '') {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n return (\n <span className=\"squisq-jv-color\">\n <span className=\"squisq-jv-color__swatch\" style={{ background: value }} aria-hidden />\n <span className=\"squisq-jv-color__hex\">{value}</span>\n </span>\n );\n}\n\nexport function DateViewer({ value, schema }: ViewerProps) {\n if (typeof value !== 'string' || value === '') {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n const fmt = schema.format;\n let display = value;\n try {\n const date = new Date(value);\n if (!Number.isNaN(date.getTime())) {\n if (fmt === 'time') {\n display = date.toLocaleTimeString();\n } else if (fmt === 'date') {\n display = date.toLocaleDateString();\n } else {\n display = date.toLocaleString();\n }\n }\n } catch {\n /* fall through to raw value */\n }\n return <span className=\"squisq-jv-value\">{display}</span>;\n}\n\n// ── Composite viewers ─────────────────────────────────────────────\n\nexport function ChipBinViewer({ value, schema }: ViewerProps) {\n if (!Array.isArray(value) || value.length === 0) {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n const itemSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;\n const labels = itemSchema?.squisq?.enumLabels;\n return (\n <div className=\"squisq-jv-chip-bin\">\n {value.map((item, i) => {\n const label =\n labels && typeof item === 'string' ? (labels[item] ?? String(item)) : String(item);\n return (\n <span key={i} className=\"squisq-jv-chip\">\n {label}\n </span>\n );\n })}\n </div>\n );\n}\n\nexport function CardStackViewer(props: ViewerProps) {\n const { value, schema, rootSchema, rootData, pointer, density } = props;\n if (!Array.isArray(value) || value.length === 0) {\n return <span className=\"squisq-jv-empty\">No items</span>;\n }\n const itemSchema = (Array.isArray(schema.items) ? schema.items[0] : schema.items) ?? {};\n const itemLabel = itemSchema.squisq?.itemLabel;\n return (\n <div className=\"squisq-jv-card-stack\">\n {value.map((item, i) => {\n const title = resolveItemTitle(itemLabel, item, i);\n return (\n <div key={i} className=\"squisq-jv-card\">\n {title ? <h4 className=\"squisq-jv-card__title\">{title}</h4> : null}\n <RenderNode\n value={item}\n schema={itemSchema}\n rootSchema={rootSchema}\n rootData={rootData}\n pointer={`${pointer}/${i}`}\n density={density}\n suppressTopGroupTitle\n />\n </div>\n );\n })}\n </div>\n );\n}\n\nfunction resolveItemTitle(\n spec: string | { fromField: string } | undefined,\n item: unknown,\n index: number,\n): string {\n if (!spec) return `Item ${index + 1}`;\n if (typeof spec === 'string') return spec;\n if (item && typeof item === 'object') {\n const v = (item as Record<string, unknown>)[spec.fromField];\n if (typeof v === 'string' && v !== '') return v;\n if (typeof v === 'number') return String(v);\n }\n return `Item ${index + 1}`;\n}\n\nexport function GroupViewer(props: ViewerProps & { suppressTitle?: boolean }) {\n const { value, schema, rootSchema, rootData, pointer, density, suppressTitle } = props;\n const title = !suppressTitle ? (schema.squisq?.label ?? schema.title) : undefined;\n const help = schema.squisq?.help ?? schema.description;\n const obj = (value && typeof value === 'object' ? (value as Record<string, unknown>) : {}) ?? {};\n const propEntries = Object.entries(schema.properties ?? {});\n\n return (\n <section className=\"squisq-jv-group\">\n {title ? <h3 className=\"squisq-jv-group__title\">{title}</h3> : null}\n {help ? <p className=\"squisq-jv-group__help\">{help}</p> : null}\n {propEntries.map(([key, propSchema]) => (\n <Fragment key={key}>\n <RowOrSection\n label={propSchema.squisq?.label ?? propSchema.title ?? key}\n help={propSchema.squisq?.help ?? propSchema.description}\n kindHint={propSchema}\n >\n <RenderNode\n value={obj[key]}\n schema={propSchema}\n rootSchema={rootSchema}\n rootData={rootData}\n pointer={`${pointer}/${key}`}\n density={density}\n suppressTopGroupTitle\n />\n </RowOrSection>\n </Fragment>\n ))}\n </section>\n );\n}\n\n/**\n * Decide whether to use the inline label/value Row layout, or to drop\n * the row chrome entirely (for nested groups, card-stacks, richtext —\n * which present their own headings).\n */\nfunction RowOrSection({\n label,\n help,\n kindHint,\n children,\n}: {\n label: string;\n help?: string;\n kindHint: SquisqAnnotatedSchema;\n children: React.ReactNode;\n}) {\n const composite = isCompositeKind(kindHint);\n if (composite) {\n return <>{children}</>;\n }\n return (\n <div className=\"squisq-jv-row\">\n <div className=\"squisq-jv-label\" title={help}>\n {label}\n </div>\n <div>{children}</div>\n </div>\n );\n}\n\nfunction isCompositeKind(schema: SquisqAnnotatedSchema): boolean {\n const t = schema.type;\n const arrayLike = t === 'array' || (Array.isArray(t) && t.includes('array'));\n if (arrayLike) {\n return arrayItemKind(schema) === 'object';\n }\n const objectLike = t === 'object' || (Array.isArray(t) && t.includes('object'));\n if (objectLike) return true;\n if (schema.oneOf || schema.anyOf) return true;\n if (schema.format === 'markdown' || schema.squisq?.control === 'richtext') return true;\n return false;\n}\n\nexport function TabsViewer(props: ViewerProps) {\n const { value, schema, rootSchema, rootData, pointer, density } = props;\n const branches = (schema.oneOf ?? schema.anyOf ?? []).slice();\n const matchedIndex = pickMatchingBranch(branches as SquisqAnnotatedSchema[], value);\n const branch = branches[matchedIndex] as SquisqAnnotatedSchema | undefined;\n if (!branch) {\n return <TextViewer {...props} />;\n }\n return (\n <div className=\"squisq-jv-tabs\">\n <span className=\"squisq-jv-tabs__discriminator\">\n {branch.squisq?.label ?? branch.title ?? `Option ${matchedIndex + 1}`}\n </span>\n <RenderNode\n value={value}\n schema={branch}\n rootSchema={rootSchema}\n rootData={rootData}\n pointer={pointer}\n density={density}\n />\n </div>\n );\n}\n\nfunction pickMatchingBranch(branches: SquisqAnnotatedSchema[], value: unknown): number {\n for (let i = 0; i < branches.length; i++) {\n if (matchesShape(branches[i], value)) return i;\n }\n return 0;\n}\n\nfunction matchesShape(schema: SquisqAnnotatedSchema, value: unknown): boolean {\n const t = schema.type;\n const target = Array.isArray(t) ? t.find((x) => x !== 'null') : t;\n if (!target) return true;\n switch (target) {\n case 'string':\n return typeof value === 'string';\n case 'number':\n case 'integer':\n return typeof value === 'number';\n case 'boolean':\n return typeof value === 'boolean';\n case 'null':\n return value === null;\n case 'array':\n return Array.isArray(value);\n case 'object':\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n default:\n return false;\n }\n}\n\n// ── Registry ──────────────────────────────────────────────────────\n\n// eslint-disable-next-line react-refresh/only-export-components\nexport const VIEWERS: Record<ControlKind, React.ComponentType<ViewerProps>> = {\n text: TextViewer,\n multiline: MultilineViewer,\n richtext: RichTextViewer,\n color: ColorViewer,\n date: DateViewer,\n time: DateViewer,\n datetime: DateViewer,\n slider: NumberViewer,\n stepper: NumberViewer,\n segmented: EnumViewer,\n radio: EnumViewer,\n combobox: EnumViewer,\n toggle: BooleanViewer,\n checkbox: BooleanViewer,\n card: GroupViewer,\n group: GroupViewer,\n 'card-stack': CardStackViewer,\n 'chip-bin': ChipBinViewer,\n tabs: TabsViewer,\n};\n","/**\n * <JsonView>\n *\n * Read-only renderer for any JSON value bound to a JSON Schema (with\n * optional Squisq UI hints). Designed to look like a polished settings\n * summary or CRM record — not a disabled form. Themable via the\n * standard Theme + SurfaceScheme props used elsewhere in Squisq.\n */\n\nimport type { SquisqAnnotatedSchema } from '@bendyline/squisq/jsonForm';\nimport type { SurfaceScheme, Theme } from '@bendyline/squisq/schemas';\nimport { useJsonViewTokens } from './useJsonViewTokens';\nimport { RenderNode } from './RenderNode';\n\nexport interface JsonViewProps {\n /** Schema describing the value's shape (with optional `squisq` UI hints). */\n schema: SquisqAnnotatedSchema;\n /** The value to display. */\n value: unknown;\n /** Optional theme. Defaults to `DEFAULT_THEME`. */\n theme?: Theme;\n /** Light/dark surface override; `'auto'` follows `prefers-color-scheme`. */\n surface?: SurfaceScheme | 'auto';\n /** Padding/gap density. Default: 'comfortable'. */\n density?: 'comfortable' | 'compact';\n /** Optional CSS class for the outer container. */\n className?: string;\n}\n\nexport function JsonView(props: JsonViewProps) {\n const { schema, value, theme, surface, density = 'comfortable', className } = props;\n const { style } = useJsonViewTokens(theme, surface);\n\n const cls =\n 'squisq-json-view' +\n (density === 'compact' ? ' squisq-json-view--compact' : '') +\n (className ? ` ${className}` : '');\n\n return (\n <div className={cls} style={style}>\n <RenderNode\n value={value}\n schema={schema}\n rootSchema={schema}\n rootData={value}\n pointer=\"\"\n density={density}\n />\n </div>\n );\n}\n"],"mappings":"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","names":["Fragment","useRef","useState","useEffect","useCallback","useMemo","isTemplateBlock","getCaptionAtTime","useEffect","useMemo","useEffect","applySurface","jsx","useMemo","jsx","jsx","jsxs","useMemo","jsx","jsxs","jsx","jsxs","useState","useEffect","jsx","jsxs","useState","useEffect","useRef","useEffect","jsx","useRef","useEffect","jsx","jsxs","jsx","jsxs","useMemo","jsx","jsx","useMemo","useState","useEffect","useRef","useCallback","useMemo","useCallback","useRef","resolveTransitionDuration","useState","useEffect","useMemo","VIEWPORT_PRESETS","DEFAULT_THEME","VIEWPORT_PRESETS","useRef","useState","useCallback","jsx","jsxs","useRef","useState","useCallback","jsx","jsxs","jsx","jsxs","useMemo","resolveFontFamily","VIEWPORT_PRESETS","DEFAULT_THEME","Fragment","sanitizeHtmlNodes","jsx","jsx","jsx","jsxs","Fragment","sanitizeHtmlNodes","jsx","jsxs","useMemo","VIEWPORT_PRESETS","DEFAULT_THEME","resolveFontFamily","jsx","jsxs","isTemplateBlock","useRef","useState","VIEWPORT_PRESETS","useMemo","useCallback","DEFAULT_THEME","applySurface","useEffect","getCaptionAtTime","Fragment","jsx","jsxs","jsx","jsxs","useRef","useState","useCallback","useEffect","jsx","jsxs","useRef","useState","useCallback","useEffect","useMemo","applySurface","resolveFontFamily","DEFAULT_THEME","useMemo","DEFAULT_THEME","applySurface","resolveFontFamily","Fragment","useMemo","Fragment","jsx","jsxs","useMemo","jsx","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/DocPlayer.tsx","../src/MediaClipLayer.tsx","../src/hooks/MediaContext.tsx","../src/hooks/useMediaSchedule.ts","../src/BlockRenderer.tsx","../src/layers/ImageLayer.tsx","../src/utils/animationUtils.ts","../src/utils/layerUtils.ts","../src/layers/TextLayer.tsx","../src/utils/fillStyle.tsx","../src/layers/ShapeLayer.tsx","../src/layers/PathLayer.tsx","../src/layers/MapLayer.tsx","../src/utils/mapTileUtils.ts","../src/layers/VideoLayer.tsx","../src/layers/TableLayer.tsx","../src/CaptionOverlay.tsx","../src/SocialCaptionOverlay.tsx","../src/hooks/useAutoSurface.ts","../src/hooks/useAudioSync.ts","../src/hooks/useDocPlayback.ts","../src/hooks/useViewportOrientation.ts","../src/hooks/useSlideSwipe.ts","../src/DocProgressBar.tsx","../src/types.ts","../src/DocControlsOverlay.tsx","../src/DocControlsSlideshow.tsx","../src/LinearDocView.tsx","../src/MarkdownRenderer.tsx","../src/InlineVideoPlayer.tsx","../src/InlineAudioPlayer.tsx","../src/DocControlsBottom.tsx","../src/DocControlsSidebar.tsx","../src/DocPlayerWithSidebar.tsx","../src/jsonView/useJsonViewTokens.ts","../src/jsonView/RenderNode.tsx","../src/jsonView/viewers.tsx","../src/jsonView/JsonView.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 controller 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 {\n isTemplateBlock,\n getCaptionAtTime,\n resolveMediaSchedule,\n getDocPlaybackDuration,\n} from '@bendyline/squisq/schemas';\nimport { MediaClipLayer } from './MediaClipLayer';\nimport type { SurfaceScheme, Theme } from '@bendyline/squisq/schemas';\nimport { applySurface } from '@bendyline/squisq/schemas';\nimport { BlockRenderer } from './BlockRenderer';\nimport { CaptionOverlay } from './CaptionOverlay';\nimport { useAutoSurface } from './hooks/useAutoSurface';\nimport { useAudioSync } from './hooks/useAudioSync';\nimport { useDocPlayback } from './hooks/useDocPlayback';\nimport { useViewportOrientation } from './hooks/useViewportOrientation';\nimport { useSlideSwipe } from './hooks/useSlideSwipe';\nimport type { AudioController } from './hooks/AudioController';\nimport {\n expandCoverBlock,\n createTemplateContext,\n markdownToDoc,\n DEFAULT_THEME,\n VIEWPORT_PRESETS,\n type ViewportConfig,\n} from '@bendyline/squisq/doc';\nimport { parseMarkdown } from '@bendyline/squisq/markdown';\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 CaptionStyle,\n CaptionMode,\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(doc: 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 doc.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 < doc.audio.segments.length; i++) {\n if (!map.has(i)) {\n const name = doc.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 /**\n * The Doc to play. Wins over `markdown` when both are provided.\n * When neither `doc` nor `markdown` is given, the player renders a\n * minimal themed empty state instead of crashing.\n */\n doc?: Doc;\n /**\n * Markdown source to play. When `doc` is absent, the markdown is parsed\n * and converted to a Doc via `markdownToDoc(parseMarkdown(markdown))`.\n * Ignored when `doc` is provided.\n */\n markdown?: string;\n /** Base path for resolving media URLs (default: `'.'`) */\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 controller (if not provided, uses default HTML5 audio) */\n audioController?: AudioController;\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 * Optional surface scheme (light / dark paper) overlaid on top of the\n * theme's colors. Passed through to the underlying LinearDocView when\n * `displayMode === 'linear'`; otherwise overlaid onto the theme that\n * renders the player's SVG blocks.\n */\n surface?: SurfaceScheme | 'auto';\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 * Whether to synthesize and show the managed cover slide from\n * `doc.startBlock`. Defaults to true for existing documents.\n */\n showCoverSlide?: boolean;\n /** Caption display style (default: 'standard').\n * 'social' shows large centered words with the active word highlighted. */\n captionStyle?: CaptionStyle;\n /**\n * Enable drag-to-swipe slide navigation in slideshow mode (default: true).\n * When enabled, press-and-drag on a slide advances/rewinds on release past a\n * threshold (or a quick flick), and snaps back otherwise. Only applies when\n * `displayMode === 'slideshow'` and not in render/headless mode.\n */\n enableSwipe?: boolean;\n}\n\n// Dev-only, browser-safe environment probe. Bundlers substitute the\n// `process.env.NODE_ENV` expression; bare browsers without a bundler have\n// no `process` at all and are treated as production (no warning noise).\nfunction isDevEnvironment(): boolean {\n try {\n return typeof process !== 'undefined' && process.env.NODE_ENV !== 'production';\n } catch {\n return false;\n }\n}\n\n// One-shot flag for the missing-stylesheet warning (module-level so the\n// warning fires at most once per page, not once per player instance).\nlet warnedMissingStyles = false;\n\n/**\n * Front-door component: resolves the `doc` / `markdown` props into a Doc\n * and renders a themed empty state when neither is provided. The playback\n * machinery lives in `DocPlayerContent` so its hook order never changes\n * when a doc appears or disappears.\n */\nexport function DocPlayer(props: DocPlayerProps) {\n const { doc, markdown } = props;\n\n // Parse markdown into a Doc only when no explicit doc is supplied.\n const markdownDoc = useMemo(\n () => (!doc && markdown !== undefined ? markdownToDoc(parseMarkdown(markdown)) : undefined),\n [doc, markdown],\n );\n\n const resolvedDoc = doc ?? markdownDoc;\n\n if (!resolvedDoc) {\n return <div className=\"doc-player doc-player--empty\" />;\n }\n\n return <DocPlayerContent {...props} doc={resolvedDoc} />;\n}\n\ninterface DocPlayerContentProps extends DocPlayerProps {\n doc: Doc;\n}\n\nfunction DocPlayerContent({\n doc,\n basePath = '.',\n renderMode = false,\n autoPlay = false,\n onEnded,\n onTimeUpdate,\n audioController: externalAudioController,\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 showCoverSlide = true,\n theme,\n surface,\n captionStyle = 'standard',\n enableSwipe = true,\n}: DocPlayerContentProps) {\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 controller is given\n const internalAudio = useAudioSync(audioRef, doc.audio, basePath);\n\n // Use external controller if provided, otherwise fall back to internal\n const audio = externalAudioController || internalAudio;\n\n // Dev-only sentinel: warn once when the package stylesheet isn't loaded.\n // The stylesheet sets `--squisq-styles-loaded: 1` on `.doc-player`; if the\n // mounted container computes an empty value, the CSS never made it in.\n useEffect(() => {\n if (warnedMissingStyles || !isDevEnvironment()) return;\n const el = containerRef.current;\n if (!el || typeof getComputedStyle !== 'function') return;\n const value = getComputedStyle(el).getPropertyValue('--squisq-styles-loaded');\n if (!value.trim()) {\n warnedMissingStyles = true;\n console.warn(\n '[squisq] @bendyline/squisq-react/styles is not loaded — import \"@bendyline/squisq-react/styles\"',\n );\n }\n }, []);\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 // Timed media clips (block.media + doc.documentMedia) resolved to absolute\n // doc-timeline coordinates. Empty for documents without the media model, so\n // <MediaClipLayer> renders nothing and the legacy audio path is unaffected.\n const mediaSchedule = useMemo(() => resolveMediaSchedule(doc), [doc]);\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 // Resolve surface (light/dark paper) and apply it to the theme before\n // handing off to downstream renderers. Orthogonal to the editorial theme.\n const autoSurface = useAutoSurface(surface === 'auto');\n const resolvedSurface = surface === 'auto' ? autoSurface : surface;\n const effectiveTheme = useMemo(() => {\n const base = theme ?? DEFAULT_THEME;\n return resolvedSurface ? applySurface(base, resolvedSurface) : base;\n }, [theme, resolvedSurface]);\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(doc, currentTime, activeViewport, renderMode, effectiveTheme);\n\n // Expand cover block (startBlock) if present - uses active viewport\n const coverBlock = useMemo((): Block | null => {\n const startBlockConfig = doc.startBlock as StartBlockConfig | undefined;\n if (!showCoverSlide) return null;\n if (!startBlockConfig) return null;\n\n const context = createTemplateContext(effectiveTheme, 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 }, [doc.startBlock, activeViewport, effectiveTheme, showCoverSlide]);\n\n // Slideshow mode treats the managed cover as a static slide before block 1.\n // It has no timeline startTime, so keep its visibility separate from audio.\n const hasManagedCover = !!coverBlock;\n const [slideshowCoverVisible, setSlideshowCoverVisible] = useState(false);\n const slideshowCoverInitKeyRef = useRef('');\n useEffect(() => {\n const initKey = `${isSlideshowMode}:${hasManagedCover}:${renderMode}`;\n if (slideshowCoverInitKeyRef.current === initKey) return;\n slideshowCoverInitKeyRef.current = initKey;\n if (isSlideshowMode && hasManagedCover && !renderMode) {\n setSlideshowCoverVisible(true);\n pause();\n } else {\n setSlideshowCoverVisible(false);\n }\n }, [isSlideshowMode, hasManagedCover, renderMode, pause]);\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 // Track whether playback has ever been initiated — prevents the cover block\n // from re-appearing when paused at currentTime === 0 (e.g., no audio source).\n const hasPlayedOnce = useRef(false);\n\n // Track when cover is showing at rest (before play)\n const atRest = !!(\n coverBlock &&\n !isSlideshowMode &&\n !isPlaying &&\n currentTime === 0 &&\n !hasPlayedOnce.current &&\n !renderMode &&\n !autoPlay\n );\n if (atRest) coverWasShowing.current = true;\n\n useEffect(() => {\n if (isPlaying && coverWasShowing.current && coverBlock && !renderMode && !isSlideshowMode) {\n coverWasShowing.current = false;\n hasPlayedOnce.current = true;\n setCoverGraceActive(true);\n // Intentionally no cleanup: if coverBlock's memoized reference changes\n // mid-grace (e.g., due to a preview re-render), clearing the timer would\n // leave coverGraceActive stuck at true because the effect body won't\n // re-run (coverWasShowing.current is now false).\n coverGraceTimer.current = setTimeout(() => setCoverGraceActive(false), 3000);\n }\n }, [isPlaying, coverBlock, renderMode, isSlideshowMode]);\n\n // Always clear the grace timer on unmount\n useEffect(() => () => clearTimeout(coverGraceTimer.current), []);\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 const showVideoCoverBlock =\n !isSlideshowMode &&\n !isLinearMode &&\n !!coverBlock &&\n (coverForced ||\n coverGraceActive ||\n (!isPlaying && currentTime === 0 && !hasPlayedOnce.current && !renderMode && !autoPlay));\n const showSlideshowCover = !!(\n isSlideshowMode &&\n !isLinearMode &&\n !renderMode &&\n coverBlock &&\n slideshowCoverVisible\n );\n const showCoverBlock = showVideoCoverBlock || showSlideshowCover;\n\n const slideshowHasCover = !!(isSlideshowMode && !renderMode && coverBlock);\n const slideshowSlideIndex = slideshowHasCover\n ? slideshowCoverVisible\n ? 0\n : currentBlockIndex + 1\n : currentBlockIndex;\n const slideshowTotalSlides = slideshowHasCover\n ? expandedBlocks.length + 1\n : expandedBlocks.length;\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 // Honor the per-clip startAt offset: before it, hold at the\n // in-point; after, advance by (blockElapsed - startAt).\n const startAt = parseFloat(video.dataset.startAt || '0');\n const targetTime = Math.min(\n clipStart + Math.max(0, blockElapsed - startAt),\n clipEnd,\n );\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 // Seek player-level scheduled videos (document-spanning clips\n // rendered by MediaClipLayer, outside any single block). Each\n // carries data-abs-start/data-abs-end/data-source-in.\n document.querySelectorAll('video[data-clip-id]').forEach((el) => {\n const video = el as HTMLVideoElement;\n const absStart = parseFloat(video.dataset.absStart || '0');\n const absEnd = parseFloat(video.dataset.absEnd || '0');\n const sourceIn = parseFloat(video.dataset.sourceIn || '0');\n video.pause();\n if (time < absStart || time >= absEnd) return;\n const targetTime = sourceIn + (time - absStart);\n video.currentTime = targetTime;\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);\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 = () => {\n // The larger of the audio/block timeline and any media that spills\n // past the last block (block-clip spillover or document-spanning\n // media), so frame capture covers the full tail.\n const mediaDuration = getDocPlaybackDuration(doc);\n if (totalDuration > 0) return Math.max(totalDuration, mediaDuration);\n return mediaDuration;\n };\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 doc.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 doc.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(doc);\n return doc.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 -- doc is a stable prop; re-registering on every doc change is unnecessary\n }, [renderMode, isDebugMode, seekTo, totalDuration, expandedBlocks, coverBlock]);\n\n // Caption mode state: cycles through off → standard → social → off\n // The captionStyle prop sets the default active style; captionsEnabledProp\n // can override the initial on/off state.\n const defaultMode: CaptionMode =\n captionsEnabledProp === false ? 'off' : captionStyle || 'standard';\n const [captionMode, setCaptionMode] = useState<CaptionMode>(defaultMode);\n\n // Keep the internal caption mode in sync when the controlling props change\n // — e.g. the editor's preview toolbar drives caption style / on-off. Keyed\n // on the derived `defaultMode` string, so it only fires on a real prop\n // change and never disturbs the in-player CC toggle for consumers (the\n // standalone player, video export) that set these props once at mount.\n useEffect(() => {\n setCaptionMode(defaultMode);\n }, [defaultMode]);\n\n // Derive captionsEnabled and active style from the mode\n const captionsEnabled = captionMode !== 'off';\n const activeCaptionStyle: CaptionStyle = captionMode === 'social' ? 'social' : 'standard';\n\n const setCaptionsEnabled = useCallback(\n (enabled: boolean) => {\n // When re-enabling, restore the prop-specified style rather than\n // always defaulting to 'standard'\n setCaptionMode(enabled ? captionStyle || 'standard' : 'off');\n onCaptionsToggle?.(enabled);\n },\n [onCaptionsToggle, captionStyle],\n );\n\n const cycleCaptionMode = useCallback(() => {\n setCaptionMode((prev) => {\n const next: CaptionMode =\n prev === 'off' ? 'standard' : prev === 'standard' ? 'social' : 'off';\n onCaptionsToggle?.(next !== 'off');\n return next;\n });\n }, [onCaptionsToggle]);\n\n const hasCaptions = doc.captions && doc.captions.phrases.length > 0;\n\n // Map segment indices to human-readable titles (from sectionHeader blocks)\n const segmentTitleMap = useMemo(() => buildSegmentTitleMap(doc), [doc]);\n\n // Build shared playback state for extracted controls\n const playbackState: PlaybackState = useMemo(\n () => ({\n isPlaying,\n currentTime,\n totalDuration,\n currentBlockIndex: slideshowSlideIndex,\n totalBlocks: slideshowTotalSlides,\n docProgress,\n hasCaptions: !!hasCaptions,\n captionsEnabled,\n captionMode,\n isFullscreen,\n currentSegmentIndex: currentSegment,\n currentSegmentName:\n segmentTitleMap.get(currentSegment) ?? doc.audio.segments[currentSegment]?.name ?? null,\n currentBlock: showSlideshowCover ? coverBlock : (currentBlock ?? null),\n currentSlideLabel: showSlideshowCover ? 'Cover' : undefined,\n currentSlideNumber:\n slideshowHasCover && !showSlideshowCover ? currentBlockIndex + 1 : undefined,\n totalSlideNumber: slideshowHasCover ? expandedBlocks.length : undefined,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- doc.audio.segments is stable within a given doc\n [\n isPlaying,\n currentTime,\n totalDuration,\n slideshowSlideIndex,\n slideshowTotalSlides,\n docProgress,\n hasCaptions,\n captionsEnabled,\n captionMode,\n isFullscreen,\n currentSegment,\n segmentTitleMap,\n currentBlock,\n currentBlockIndex,\n showSlideshowCover,\n coverBlock,\n slideshowHasCover,\n expandedBlocks.length,\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 cycleCaptionMode,\n toggleFullscreen: onFullscreenToggle,\n }),\n [toggle, restart, seekTo, setCaptionsEnabled, cycleCaptionMode, 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 (slideshowHasCover && slideshowCoverVisible) {\n const target = expandedBlocks[0];\n if (target) {\n setSlideshowCoverVisible(false);\n seekTo(target.startTime);\n pause();\n }\n return;\n }\n if (currentBlockIndex < expandedBlocks.length - 1) {\n const target = expandedBlocks[currentBlockIndex + 1];\n if (target) {\n setSlideshowCoverVisible(false);\n seekTo(target.startTime);\n pause();\n }\n }\n },\n prevSlide: () => {\n if (slideshowHasCover && !slideshowCoverVisible && currentBlockIndex <= 0) {\n setSlideshowCoverVisible(true);\n seekTo(0);\n pause();\n return;\n }\n if (currentBlockIndex > 0) {\n const target = expandedBlocks[currentBlockIndex - 1];\n if (target) {\n setSlideshowCoverVisible(false);\n seekTo(target.startTime);\n pause();\n }\n }\n },\n goToSlide: (index: number) => {\n if (slideshowHasCover) {\n if (index === 0) {\n setSlideshowCoverVisible(true);\n seekTo(0);\n pause();\n return;\n }\n const target = expandedBlocks[index - 1];\n if (target) {\n setSlideshowCoverVisible(false);\n seekTo(target.startTime);\n pause();\n }\n return;\n }\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, slideshowHasCover, slideshowCoverVisible],\n );\n\n // Drag-to-swipe navigation for slideshow mode. Inert unless in slideshow mode,\n // interactive (not headless), and not overridden off via `enableSwipe`.\n const swipeEnabled = isSlideshowMode && !isLinearMode && !renderMode && enableSwipe;\n const swipe = useSlideSwipe({\n enabled: swipeEnabled,\n containerRef,\n canGoNext: slideshowSlideIndex < slideshowTotalSlides - 1,\n canGoPrev: slideshowSlideIndex > 0,\n onNext: slideNavActions.nextSlide,\n onPrev: slideNavActions.prevSlide,\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 = isSlideshowMode ? slideshowTotalSlides : 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\n doc={doc}\n basePath={basePath}\n viewport={activeViewport}\n theme={theme}\n surface={surface}\n />\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={`doc-player${swipeEnabled ? ' doc-player--swipe' : ''}${\n swipe.phase === 'dragging' ? ' doc-player--grabbing' : ''\n }`}\n onClick={handleContainerClick}\n onPointerDown={swipe.onPointerDown}\n style={{\n position: 'relative',\n width: '100%',\n aspectRatio: `${activeViewport.width} / ${activeViewport.height}`,\n margin: '0 auto',\n overflow: 'hidden',\n // Swipe uses the grab/grabbing cursor via CSS classes; let vertical page\n // scroll through on touch while we own horizontal drags.\n cursor: renderMode || swipeEnabled ? undefined : 'pointer',\n touchAction: swipeEnabled ? 'pan-y' : undefined,\n }}\n >\n {/* Hidden audio element */}\n <audio ref={audioRef} preload=\"auto\" muted={muted} />\n\n {/* Timed media clips (per-block + document-spanning audio/video). */}\n <MediaClipLayer\n schedule={mediaSchedule}\n currentTime={currentTime}\n isPlaying={isPlaying}\n basePath={basePath}\n renderMode={renderMode}\n />\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 // Keyed by block id so each block is its own DOM subtree: React never\n // reconciles one block's layers onto another's (templates reuse layer\n // ids like `title`/`background`), which would otherwise reuse stale\n // DOM / skip entrance animations mid-transition.\n <div key={previousBlock.id} className=\"doc-player__block doc-player__block--previous\">\n <BlockRenderer\n block={previousBlock}\n blockTime={blockTime}\n basePath={basePath}\n isExiting={true}\n transition={currentBlock?.transition}\n viewport={activeViewport}\n />\n </div>\n )}\n\n {/* Current block */}\n {!showCoverBlock && currentBlock && (\n <div\n key={currentBlock.id}\n className={`doc-player__block doc-player__block--active${\n swipe.phase !== 'idle' ? ` doc-player__block--${swipe.phase}` : ''\n }`}\n style={\n swipe.phase !== 'idle' ? { transform: `translateX(${swipe.offsetPx}px)` } : undefined\n }\n >\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 -- shown during playback and in render mode when captions are enabled */}\n {hasCaptions && (renderMode ? captionsEnabled : true) && (\n <CaptionOverlay\n captions={doc.captions}\n currentTime={currentTime}\n enabled={captionsEnabled && (renderMode || isPlaying || currentTime > 0)}\n fontSize={16}\n captionStyle={activeCaptionStyle}\n theme={effectiveTheme}\n viewport={activeViewport}\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 <span style={{ color: '#666' }}>\n (progress: {(docProgress * 100).toFixed(1)}%, scriptDur: {doc.duration.toFixed(1)})\n </span>\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 {doc.audio.segments.length - 1}{' '}\n <span style={{ color: '#666' }}>\n ({doc.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(doc.captions!, currentTime);\n const debugEnabled = captionsEnabled && (isPlaying || currentTime > 0);\n return (\n <Fragment>\n <div>\n <span style={{ color: '#888' }}>captions:</span>{' '}\n {doc.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>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' }}>🔊</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 * MediaClipLayer\n *\n * Player-level audio/video elements for the media-clip schedule. Renders one\n * element per scheduled clip (audio, or document-spanning video) and keeps each\n * mounted so re-entry doesn't reload; the drive effect seeks/plays only the\n * clips active at the current time. Multiple concurrent clips (e.g. a\n * document-spanning narration plus a block clip) are independent elements and\n * the browser mixes their output.\n *\n * Audio clips play unmuted during live playback (silent in render mode, where\n * frames are captured without sound and audio is muxed offline). Video clips\n * play muted — their audio, if any, is reproduced by the export mux.\n */\n\nimport { useEffect, useRef } from 'react';\nimport type { ScheduledClip } from '@bendyline/squisq/schemas';\nimport { useMediaUrl } from './hooks/MediaContext';\nimport { useMediaSchedule } from './hooks/useMediaSchedule';\n\n/** Re-seek an element only when it drifts this far from its target (seconds). */\nconst DRIFT = 0.25;\n\nexport interface MediaClipLayerProps {\n schedule: ScheduledClip[];\n currentTime: number;\n isPlaying: boolean;\n basePath: string;\n renderMode?: boolean;\n}\n\nexport function MediaClipLayer({\n schedule,\n currentTime,\n isPlaying,\n basePath,\n renderMode = false,\n}: MediaClipLayerProps) {\n const { renderClips, activeIds } = useMediaSchedule(schedule, currentTime);\n if (renderClips.length === 0) return null;\n return (\n <div className=\"doc-player__media-clips\" aria-hidden>\n {renderClips.map((clip) => (\n <MediaClipElement\n key={clip.id}\n clip={clip}\n active={activeIds.has(clip.id)}\n currentTime={currentTime}\n isPlaying={isPlaying}\n basePath={basePath}\n renderMode={renderMode}\n />\n ))}\n </div>\n );\n}\n\ninterface MediaClipElementProps {\n clip: ScheduledClip;\n active: boolean;\n currentTime: number;\n isPlaying: boolean;\n basePath: string;\n renderMode: boolean;\n}\n\nfunction MediaClipElement({\n clip,\n active,\n currentTime,\n isPlaying,\n basePath,\n renderMode,\n}: MediaClipElementProps) {\n const ref = useRef<HTMLMediaElement | null>(null);\n const src = useMediaUrl(clip.src, basePath);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n if (!active) {\n if (!el.paused) el.pause();\n return;\n }\n const target = Math.max(0, clip.sourceIn + (currentTime - clip.absoluteStart));\n if (renderMode || Math.abs(el.currentTime - target) > DRIFT) {\n try {\n el.currentTime = target;\n } catch {\n // Seeking before metadata loads throws; the next tick retries.\n }\n }\n if (isPlaying && !renderMode) {\n const p = el.play();\n if (p) p.catch(() => {});\n } else {\n el.pause();\n }\n }, [active, currentTime, isPlaying, renderMode, clip.sourceIn, clip.absoluteStart]);\n\n const isVideo = clip.kind === 'video';\n const common = {\n ref: ref as React.RefObject<never>,\n src,\n preload: 'auto' as const,\n 'data-clip-id': clip.id,\n 'data-abs-start': clip.absoluteStart,\n 'data-abs-end': clip.absoluteEnd,\n 'data-source-in': clip.sourceIn,\n };\n\n if (isVideo) {\n // Document-spanning video renders full-bleed behind the blocks.\n return (\n <video\n {...common}\n muted\n playsInline\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n zIndex: 0,\n pointerEvents: 'none',\n }}\n />\n );\n }\n\n return (\n <audio {...common} muted={renderMode} style={{ position: 'absolute', width: 0, height: 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 // Defensive: callers (esp. preview surfaces like InlinePreviewGutter)\n // sometimes feed in template-generated layers whose `content.src` is\n // undefined while the user is still authoring the block. Treat that\n // as an empty string rather than crashing the whole React tree.\n const safePath = typeof relativePath === 'string' ? relativePath : '';\n\n // For absolute/http URLs, skip resolution entirely\n const isAbsolute =\n !safePath ||\n safePath.startsWith('http') ||\n safePath.startsWith('/') ||\n safePath.startsWith('data:') ||\n safePath.startsWith('blob:');\n\n // Memoize fallback to avoid recalculating on every render\n const fallback = useMemo(\n () => (isAbsolute ? safePath : `${basePath}/${safePath}`),\n [isAbsolute, safePath, 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(safePath).then((resolved) => {\n if (!cancelled) setUrl(resolved);\n });\n\n return () => {\n cancelled = true;\n };\n }, [needsProvider, provider, safePath, 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 * useMediaSchedule\n *\n * Pure follower of the playback clock for the media-clip model. Given the\n * resolved {@link ScheduledClip}s and the current time, it returns the clips\n * the player should mount and which of them are active right now.\n * {@link MediaClipLayer} consumes this to drive one hidden `<audio>` /\n * full-bleed `<video>` element per clip. (Annotation-authored clips all render\n * at the player level; template-produced `VideoLayer`s are a separate path and\n * are not part of the schedule.)\n *\n * It owns no clock: `currentTime`/`isPlaying` come from the existing\n * `useAudioSync` provider via `DocPlayer`. With an empty schedule it returns\n * empty lists, so documents without the new media model are unaffected.\n */\n\nimport { useMemo } from 'react';\nimport type { ScheduledClip } from '@bendyline/squisq/schemas';\n\nexport interface MediaScheduleController {\n /** Clips the player mounts (every scheduled clip). */\n renderClips: ScheduledClip[];\n /** Ids of clips whose [absoluteStart, absoluteEnd) contains currentTime. */\n activeIds: Set<string>;\n}\n\nexport function useMediaSchedule(\n schedule: ScheduledClip[],\n currentTime: number,\n): MediaScheduleController {\n const activeIds = useMemo(() => {\n const ids = new Set<string>();\n for (const c of schedule) {\n if (currentTime >= c.absoluteStart && currentTime < c.absoluteEnd) ids.add(c.id);\n }\n return ids;\n }, [schedule, currentTime]);\n return { renderClips: schedule, activeIds };\n}\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, Transition } from '@bendyline/squisq/schemas';\nimport { resolveTransitionDuration } from '@bendyline/squisq/schemas';\nimport { ImageLayer } from './layers/ImageLayer';\nimport { TextLayer } from './layers/TextLayer';\nimport { ShapeLayer } from './layers/ShapeLayer';\nimport { PathLayer } from './layers/PathLayer';\nimport { MapLayer } from './layers/MapLayer';\nimport { VideoLayer } from './layers/VideoLayer';\nimport { TableLayer } from './layers/TableLayer';\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 /** Transition to apply. Defaults to block.transition. */\n transition?: Transition;\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 transition,\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 const activeTransition = transition ?? block.transition;\n if (activeTransition && isEntering) {\n transitionClass = getTransitionClass(activeTransition.type, true, activeTransition.direction);\n transitionStyle['--transition-duration'] = `${resolveTransitionDuration(activeTransition)}s`;\n } else if (activeTransition && isExiting) {\n transitionClass = getTransitionClass(activeTransition.type, false, activeTransition.direction);\n transitionStyle['--transition-duration'] = `${resolveTransitionDuration(activeTransition)}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 'path':\n return <PathLayer 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 case 'table':\n return <TableLayer layer={layer} viewport={viewport} blockTime={blockTime} />;\n default:\n console.warn(`Unknown layer type: ${(layer as Layer).type}`);\n return null;\n }\n}\n\nexport default BlockRenderer;\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 { cssFilterForTreatment } from '@bendyline/squisq/doc';\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 // Theme-derived photographic grade + optional blur, as a CSS filter\n // string (identical in the player and in headless frame capture).\n const filter = cssFilterForTreatment(content.treatment, content.blur);\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: `${width}px`,\n height: `${height}px`,\n overflow: 'hidden',\n }}\n >\n <img\n src={src}\n alt={content.alt || ''}\n className={kbStyle.className}\n style={{\n width: `${width}px`,\n height: `${height}px`,\n objectFit: 'cover',\n objectPosition: 'center',\n display: 'block',\n pointerEvents: 'none',\n transformOrigin: 'center center',\n ...(filter ? { filter } : {}),\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: `${width}px`,\n height: `${height}px`,\n objectFit: 'cover',\n objectPosition: 'center',\n display: 'block',\n pointerEvents: 'none',\n ...(filter ? { filter } : {}),\n // Over-scan blurred imagery so the soft edges never reveal\n // the frame behind the layer.\n ...(content.blur && content.blur > 0 ? { transform: 'scale(1.06)' } : {}),\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', ...(filter ? { filter } : {}) }}\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 * 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 * 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 * Two rendering paths:\n * - Plain text (`PlainTextLayer`) — SVG `<text>` + `<tspan>` per line, one\n * style for the whole layer. Used for the common case and for\n * backward-compat with content authored before rich text.\n * - Rich text (`RichTextLayer`) — when `content.html` is set, the sanitized\n * inline/block HTML renders inside a `<foreignObject>` so bold/italic/\n * links (and, for layout textboxes, headings/lists) format individual\n * runs. `<foreignObject>` is export-safe (video rasterizes via Chromium,\n * PDF bypasses SVG) and already used by Video/Table layers.\n */\n\nimport { useMemo, type CSSProperties } from 'react';\nimport type { TextLayer as TextLayerType } from '@bendyline/squisq/schemas';\nimport { DEFAULT_DOC_FONT } from '@bendyline/squisq/schemas';\nimport {\n parseHtmlToNodes,\n sanitizeHtmlNodes,\n stringifyHtmlNodes,\n} from '@bendyline/squisq/markdown';\n// Import from the standalone marker module (not the `icons` barrel) so the\n// player bundle doesn't pull in the ~2k-entry FontAwesome catalog.\nimport {\n hasIconMarker,\n splitIconMarkers,\n stripIconMarkers,\n iconClass,\n} from '@bendyline/squisq/icon-marker';\nimport { getAnimationStyle } from '../utils/animationUtils';\nimport { resolveValue } from '../utils/layerUtils';\nimport { resolveFill, borderDashArray } from '../utils/fillStyle';\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\n/**\n * Dispatch between the plain SVG-text renderer and the rich HTML renderer\n * based on whether the layer carries `content.html`.\n */\nexport function TextLayer(props: TextLayerProps) {\n if (props.layer.content.html?.trim()) return <RichTextLayer {...props} />;\n // Body text carrying inline-icon markers (authored `{[rocket]}` etc.) can't\n // render in the SVG `<text>` path — an icon is an `<i class=\"fa-…\">` glyph,\n // not a code point we can place in a tspan. Route it through a foreignObject\n // instead, sizing the box to the wrapped content so it doesn't clip.\n if (hasIconMarker(props.layer.content.text ?? '')) return <IconTextLayer {...props} />;\n return <PlainTextLayer {...props} />;\n}\n\n/** Escape text so it's safe inside the synthesized icon HTML. */\nfunction escapeHtml(value: string): string {\n return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n}\n\n/** Turn a marker-bearing string into HTML: text runs escaped, icons as `<i>`. */\nfunction iconRunsToHtml(text: string): string {\n return splitIconMarkers(text)\n .map((run) =>\n run.type === 'icon'\n ? `<i class=\"${iconClass(run.family, run.name)}\" aria-hidden=\"true\"></i>`\n : escapeHtml(run.text).replace(/\\n/g, '<br>'),\n )\n .join('');\n}\n\n/**\n * Renders template body text that contains inline icons. Geometry mirrors\n * `PlainTextLayer`/`layerBounds` (same anchor math), but the content lives in\n * a `<foreignObject>` so `<i class=\"fa-…\">` glyphs render (export-safe: video\n * rasterizes via Chromium, which has the FA webfont embedded in the render\n * page). Height is derived from the wrapped, icon-free projection when the\n * layer has no explicit height, and overflow is visible to guard estimates.\n */\nfunction IconTextLayer({ layer, viewport, blockTime }: TextLayerProps) {\n const { content, position, animation } = layer;\n const { text, style } = content;\n\n const rawX = resolveValue(position.x, viewport.width);\n const rawY = resolveValue(position.y, viewport.height);\n const boxWidth =\n position.width != null ? resolveValue(position.width, viewport.width) : viewport.width;\n const anchor = position.anchor ?? 'top-left';\n const lineHeight = style.lineHeight || 1.4;\n const lineHeightPx = style.fontSize * lineHeight;\n const padding = style.padding ?? 0;\n\n // Estimate wrapped line count from the icon-free text so the box is tall\n // enough. Icons roughly occupy one character; the estimate need not be exact\n // because the foreignObject is `overflow: visible`.\n const plain = stripIconMarkers(text ?? '');\n const lines = plain.split('\\n').flatMap((line) => wrapText(line, style.fontSize, boxWidth));\n const boxHeight =\n position.height != null\n ? resolveValue(position.height, viewport.height)\n : Math.max(lineHeightPx, lines.length * lineHeightPx) + padding * 2;\n\n const boxX = rawX - anchorAxis(anchor, boxWidth, 'x');\n const boxY = rawY - anchorAxis(anchor, boxHeight, 'y');\n\n const animStyle = getAnimationStyle(animation, blockTime);\n const html = useMemo(() => iconRunsToHtml(text ?? ''), [text]);\n\n const verticalJustify =\n style.verticalAlign === 'top'\n ? 'flex-start'\n : style.verticalAlign === 'bottom'\n ? 'flex-end'\n : 'center';\n\n const boxStyle: CSSProperties = {\n boxSizing: 'border-box',\n width: '100%',\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: verticalJustify,\n padding,\n color: style.color,\n fontSize: `${style.fontSize}px`,\n fontFamily: style.fontFamily || DEFAULT_DOC_FONT,\n fontWeight: style.fontWeight || 'normal',\n fontStyle: style.fontStyle || 'normal',\n lineHeight,\n textAlign: style.textAlign ?? 'left',\n ...(style.shadow ? { textShadow: '0 2px 3px rgba(0,0,0,0.7)' } : {}),\n ...animStyle.style,\n };\n\n return (\n <g className={`block-layer block-layer--text ${animStyle.className}`} data-layer-id={layer.id}>\n <foreignObject\n x={boxX}\n y={boxY}\n width={boxWidth}\n height={boxHeight}\n style={{ overflow: 'visible' }}\n >\n {/* Outer box owns the flex/vertical-centering; the inner div is a\n single flex item so the rich content (text + inline `<i>` icons)\n flows inline instead of each node becoming a stacked flex item. */}\n <div\n {...({ xmlns: 'http://www.w3.org/1999/xhtml' } as Record<string, string>)}\n style={boxStyle}\n >\n <div\n style={{ width: '100%', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}\n aria-label={plain}\n dangerouslySetInnerHTML={{ __html: html }}\n />\n </div>\n </foreignObject>\n </g>\n );\n}\n\nfunction PlainTextLayer({ layer, viewport, blockTime }: TextLayerProps) {\n const { content, position, animation } = layer;\n const { text, style } = content;\n\n // Resolve position values to pixels. `position.x/y` is the layer's\n // anchor *point*; `position.anchor` says where on the box that point\n // sits (matching `layerBounds` in the editor).\n const rawX = resolveValue(position.x, viewport.width);\n const rawY = resolveValue(position.y, viewport.height);\n const boxWidth =\n position.width != null ? resolveValue(position.width, viewport.width) : undefined;\n const boxHeight =\n position.height != null ? resolveValue(position.height, viewport.height) : undefined;\n const maxWidth = boxWidth;\n\n const textAnchor = getTextAnchor(style.textAlign, position.anchor);\n const dominantBaseline = getDominantBaseline(style.verticalAlign, position.anchor);\n\n // Place the text's pivot at the box edge/centre matching the chosen\n // alignment. This is algebraically identical to the legacy point-anchor\n // behavior for existing content (where x/y were already the desired\n // pivot — e.g. templates using x:'50%' + anchor:'center'), and makes\n // box-relative H/V alignment work for layers anchored at their\n // top-left (as the template designer creates them).\n const anchor = position.anchor ?? 'top-left';\n const x = pivotX(rawX, boxWidth, anchor, textAnchor);\n const y = pivotY(rawY, boxHeight, anchor, dominantBaseline);\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 fontStyle: style.fontStyle || '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 / border box. When the layer has an explicit box\n (width + height — as the template designer always creates), the\n fill and border cover that rectangle: text is, after all, a\n rectangle. Without a box we fall back to a snug rect hugging the\n text (legacy behavior for point-anchored text). */}\n <TextBox\n layerId={layer.id}\n style={style}\n box={\n boxWidth != null && boxHeight != null\n ? {\n x: rawX - anchorAxis(anchor, boxWidth, 'x'),\n y: rawY - anchorAxis(anchor, boxHeight, 'y'),\n width: boxWidth,\n height: boxHeight,\n }\n : {\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 }\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 * Rich-text path: render `content.html` as sanitized HTML inside a\n * `<foreignObject>` so individual runs can carry their own formatting\n * (bold/italic/links, and headings/lists for layout textboxes). The box\n * geometry matches `PlainTextLayer`/`layerBounds`, so selection and\n * drag/resize stay aligned. `content.text` remains the plain projection\n * used by export/search.\n */\nfunction RichTextLayer({ layer, viewport, blockTime }: TextLayerProps) {\n const { content, position, animation } = layer;\n const { html, style } = content;\n\n const rawX = resolveValue(position.x, viewport.width);\n const rawY = resolveValue(position.y, viewport.height);\n const boxWidth =\n position.width != null ? resolveValue(position.width, viewport.width) : viewport.width;\n const boxHeight =\n position.height != null\n ? resolveValue(position.height, viewport.height)\n : style.fontSize * (style.lineHeight || 1.4) * 2;\n const anchor = position.anchor ?? 'top-left';\n const boxX = rawX - anchorAxis(anchor, boxWidth, 'x');\n const boxY = rawY - anchorAxis(anchor, boxHeight, 'y');\n\n // Re-sanitize at render time — `html` is untrusted. Memoized so the\n // per-frame `blockTime` updates don't re-parse the HTML each frame.\n const safeHtml = useMemo(\n () => stringifyHtmlNodes(sanitizeHtmlNodes(parseHtmlToNodes(html ?? ''))),\n [html],\n );\n\n const animStyle = getAnimationStyle(animation, blockTime);\n\n const verticalJustify =\n style.verticalAlign === 'middle'\n ? 'center'\n : style.verticalAlign === 'bottom'\n ? 'flex-end'\n : 'flex-start';\n\n const hasBorder = !!(style.borderColor && (style.borderWidth ?? 0) > 0);\n // Box decoration uses CSS (foreignObject is HTML); gradients/backgroundOpacity\n // are deferred to the SVG path — solid background covers the common case.\n const boxStyle: CSSProperties = {\n boxSizing: 'border-box',\n width: '100%',\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: verticalJustify,\n padding: style.padding ?? 0,\n color: style.color,\n fontSize: `${style.fontSize}px`,\n fontFamily: style.fontFamily || DEFAULT_DOC_FONT,\n fontWeight: style.fontWeight || 'normal',\n fontStyle: style.fontStyle || 'normal',\n lineHeight: style.lineHeight || 1.4,\n textAlign: style.textAlign ?? 'left',\n overflow: 'hidden',\n ...(style.background ? { background: style.background } : {}),\n ...(hasBorder\n ? {\n border: `${style.borderWidth}px ${style.borderStyle ?? 'solid'} ${style.borderColor}`,\n borderRadius: 4,\n }\n : {}),\n ...(style.shadow ? { textShadow: '0 2px 3px rgba(0,0,0,0.7)' } : {}),\n ...animStyle.style,\n };\n\n // Scoped reset so authored block elements (headings, lists, paragraphs)\n // render tightly inside the box rather than with the UA's large margins.\n const cls = `squisq-rich-text-${cssId(layer.id)}`;\n const scopedCss =\n `.${cls}{margin:0}` +\n `.${cls} p{margin:0 0 .4em}` +\n `.${cls} h1,.${cls} h2,.${cls} h3,.${cls} h4,.${cls} h5,.${cls} h6{margin:0 0 .3em;line-height:1.2}` +\n // `list-style-position: inside` keeps the bullet/number next to its text\n // — with `outside` (the default) a centered or middle-aligned list leaves\n // the marker stranded at the box's left padding, far from the text. The\n // editor wraps each item's text in a `<p>`, so flatten that to inline or\n // the block paragraph drops below the (inline) marker.\n `.${cls} ul,.${cls} ol{margin:0 0 .4em;padding-left:1.2em;list-style-position:inside}` +\n `.${cls} li{margin:0}` +\n `.${cls} li>p{display:inline;margin:0}` +\n `.${cls} *:first-child{margin-top:0}.${cls} *:last-child{margin-bottom:0}` +\n `.${cls} a{color:inherit;text-decoration:underline}`;\n\n return (\n <g className={`block-layer block-layer--text ${animStyle.className}`} data-layer-id={layer.id}>\n <foreignObject x={boxX} y={boxY} width={boxWidth} height={boxHeight}>\n {/* xmlns is required for HTML inside SVG foreignObject (see TableLayer) */}\n <div\n {...({ xmlns: 'http://www.w3.org/1999/xhtml' } as Record<string, string>)}\n style={boxStyle}\n >\n <style>{scopedCss}</style>\n <div\n className={cls}\n aria-label={content.text}\n style={{ width: '100%', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}\n dangerouslySetInnerHTML={{ __html: safeHtml }}\n />\n </div>\n </foreignObject>\n </g>\n );\n}\n\n/** Sanitize a layer id into a CSS-class-safe suffix. */\nfunction cssId(id: string): string {\n return id.replace(/[^a-zA-Z0-9_-]/g, '-');\n}\n\n/**\n * The text layer's background + border rect. Renders nothing unless a\n * fill (solid or gradient) or a visible border is configured.\n */\nfunction TextBox({\n layerId,\n style,\n box,\n}: {\n layerId: string;\n style: import('@bendyline/squisq/schemas').TextStyle;\n box: { x: number; y: number; width: number; height: number };\n}) {\n const hasFill = !!(style.background || style.backgroundGradient);\n const hasBorder = !!(style.borderColor && (style.borderWidth ?? 0) > 0);\n if (!hasFill && !hasBorder) return null;\n\n const { fill, def } = resolveFill(layerId, style.background, style.backgroundGradient);\n const dash = borderDashArray(style.borderStyle, style.borderWidth);\n return (\n <>\n {def && <defs>{def}</defs>}\n <rect\n x={box.x}\n y={box.y}\n width={box.width}\n height={box.height}\n fill={hasFill ? fill : 'none'}\n fillOpacity={hasFill ? style.backgroundOpacity : undefined}\n stroke={hasBorder ? style.borderColor : undefined}\n strokeWidth={hasBorder ? style.borderWidth : undefined}\n strokeDasharray={hasBorder ? dash : undefined}\n rx={4}\n ry={4}\n />\n </>\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 vertical alignment (or, when unset, the position anchor) to SVG\n * dominant-baseline. Explicit `verticalAlign` wins so the editor can set\n * top/middle/bottom independently of the box-placement anchor.\n */\nfunction getDominantBaseline(verticalAlign?: 'top' | 'middle' | 'bottom', anchor?: string): string {\n if (verticalAlign === 'top') return 'text-before-edge';\n if (verticalAlign === 'middle') return 'middle';\n if (verticalAlign === 'bottom') return 'text-after-edge';\n\n if (anchor?.includes('bottom')) return 'text-after-edge';\n if (anchor === 'center') return 'middle';\n return 'text-before-edge';\n}\n\n/**\n * Horizontal pivot for the text. Derives the box's left edge from the\n * anchor point (`rawX`) and the box width, then offsets to the edge/centre\n * matching `textAnchor`. Falls back to the raw point when no width is set.\n */\nfunction pivotX(\n rawX: number,\n width: number | undefined,\n anchor: string,\n textAnchor: string,\n): number {\n if (width == null) return rawX;\n const boxLeft = rawX - anchorAxis(anchor, width, 'x');\n if (textAnchor === 'middle') return boxLeft + width / 2;\n if (textAnchor === 'end') return boxLeft + width;\n return boxLeft;\n}\n\n/**\n * Vertical pivot for the text. Mirror of {@link pivotX} for the box's top\n * edge and the resolved dominant-baseline. Falls back to the raw point\n * when no height is set.\n */\nfunction pivotY(\n rawY: number,\n height: number | undefined,\n anchor: string,\n dominantBaseline: string,\n): number {\n if (height == null) return rawY;\n const boxTop = rawY - anchorAxis(anchor, height, 'y');\n if (dominantBaseline === 'middle') return boxTop + height / 2;\n if (dominantBaseline === 'text-after-edge') return boxTop + height;\n return boxTop;\n}\n\n/** Anchor-point offset along one axis, matching `layerBounds` in the editor. */\nfunction anchorAxis(anchor: string, size: number, axis: 'x' | 'y'): number {\n if (anchor === 'center') return size / 2;\n if (axis === 'x') return anchor.includes('right') ? size : 0;\n return anchor.includes('bottom') ? size : 0;\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 * Shared fill / border helpers for the SVG layer renderers (shape, path,\n * and the text-box background). Keeping the gradient `<defs>` markup and\n * the dash-pattern math in one place means rect, circle, path, and text\n * all paint fills and borders identically.\n */\n\nimport type {\n LinearGradient,\n BorderStyle,\n ShapePattern,\n ShapeFilter,\n} from '@bendyline/squisq/schemas';\n\n/**\n * SVG `stroke-dasharray` for a border style, scaled by stroke width so the\n * pattern stays proportional. `solid`/undefined → no dashes.\n */\nexport function borderDashArray(\n style: BorderStyle | undefined,\n strokeWidth: number | undefined,\n): string | undefined {\n if (!style || style === 'solid') return undefined;\n const w = Math.max(1, strokeWidth ?? 1);\n if (style === 'dotted') return `${w} ${w * 2}`;\n return `${w * 3} ${w * 2}`; // dashed\n}\n\n/**\n * Gradient line endpoints in objectBoundingBox units (0–1) for an angle in\n * degrees: 0 = top→bottom, 90 = left→right, increasing clockwise.\n */\nfunction gradientVector(angle = 0): { x1: number; y1: number; x2: number; y2: number } {\n const a = (angle * Math.PI) / 180;\n const dx = Math.sin(a);\n const dy = Math.cos(a);\n return { x1: 0.5 - dx / 2, y1: 0.5 - dy / 2, x2: 0.5 + dx / 2, y2: 0.5 + dy / 2 };\n}\n\n/** Stable id for a layer's gradient def. */\nexport function gradientDefId(layerId: string): string {\n return `squisq-grad-${layerId}`;\n}\n\n/**\n * Resolve a fill to an SVG `fill` value plus the gradient `<defs>` to\n * render (or null). When a gradient is present it wins over the solid\n * color; otherwise the color (or undefined) is returned verbatim.\n */\nexport function resolveFill(\n layerId: string,\n color: string | undefined,\n gradient: LinearGradient | undefined,\n pattern?: ShapePattern,\n): { fill: string | undefined; def: JSX.Element | null } {\n if (pattern) {\n const id = `squisq-pattern-${layerId}`;\n return { fill: `url(#${id})`, def: patternDef(id, pattern) };\n }\n if (gradient) {\n const id = gradientDefId(layerId);\n const v = gradientVector(gradient.angle);\n return {\n fill: `url(#${id})`,\n def: (\n <linearGradient id={id} x1={v.x1} y1={v.y1} x2={v.x2} y2={v.y2}>\n <stop offset=\"0%\" stopColor={gradient.from} />\n <stop offset=\"100%\" stopColor={gradient.to} />\n </linearGradient>\n ),\n };\n }\n return { fill: color, def: null };\n}\n\n/**\n * Native SVG `<pattern>` def for a repeating shape fill — fully vector,\n * identical in the player and headless frame capture.\n */\nfunction patternDef(id: string, pattern: ShapePattern): JSX.Element {\n const size = pattern.size ?? 24;\n const opacity = pattern.opacity ?? 1;\n const color = pattern.color;\n return (\n <pattern\n id={id}\n width={size}\n height={size}\n patternUnits=\"userSpaceOnUse\"\n // Diagonal tiles rotate the whole pattern; dots/grid stay axis-aligned.\n patternTransform={pattern.kind === 'diagonal' ? 'rotate(45)' : undefined}\n >\n {pattern.kind === 'dots' && (\n <circle\n cx={size / 2}\n cy={size / 2}\n r={Math.max(1, size / 12)}\n fill={color}\n opacity={opacity}\n />\n )}\n {pattern.kind === 'grid' && (\n <path\n d={`M ${size} 0 L 0 0 0 ${size}`}\n fill=\"none\"\n stroke={color}\n strokeWidth={1}\n opacity={opacity}\n />\n )}\n {pattern.kind === 'diagonal' && (\n <line x1={0} y1={0} x2={0} y2={size} stroke={color} strokeWidth={1} opacity={opacity} />\n )}\n </pattern>\n );\n}\n\n/**\n * SVG filter def for a shape's procedural filter (static film grain via\n * feTurbulence). Returns the def plus the `filter` attribute value.\n */\nexport function resolveShapeFilter(\n layerId: string,\n filter: ShapeFilter | undefined,\n): { filterAttr: string | undefined; def: JSX.Element | null } {\n if (!filter || filter.type !== 'noise') return { filterAttr: undefined, def: null };\n const id = `squisq-noise-${layerId}`;\n const opacity = filter.opacity ?? 0.05;\n return {\n filterAttr: `url(#${id})`,\n def: (\n <filter id={id} x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\">\n <feTurbulence\n type=\"fractalNoise\"\n baseFrequency={filter.baseFrequency ?? 0.8}\n numOctaves={2}\n stitchTiles=\"stitch\"\n result=\"noise\"\n />\n <feColorMatrix in=\"noise\" type=\"saturate\" values=\"0\" result=\"mono\" />\n <feComponentTransfer in=\"mono\" result=\"faded\">\n <feFuncA type=\"linear\" slope={opacity} intercept={0} />\n </feComponentTransfer>\n <feComposite in=\"faded\" in2=\"SourceGraphic\" operator=\"in\" />\n </filter>\n ),\n };\n}\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, getAnchorOffset } from '../utils/layerUtils';\nimport { resolveFill, resolveShapeFilter, borderDashArray } from '../utils/fillStyle';\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 rawX = resolveValue(position.x, viewport.width);\n const rawY = 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 // Apply anchor offset (e.g., 'center' shifts x by -width/2 and y by -height/2)\n const anchorOffset = getAnchorOffset(position.anchor, width, height);\n const x = rawX + anchorOffset.x;\n const y = rawY + anchorOffset.y;\n\n // Get animation styles\n const animStyle = getAnimationStyle(animation, blockTime);\n\n const fill = content.fill || 'none';\n // Legacy: a CSS gradient string baked into `fill` (e.g. from older docs)\n // only works as an HTML background, so rect renders it via foreignObject.\n // The structured `content.gradient` (preferred) is handled below for all\n // shapes via an SVG <linearGradient>.\n const isCSSGradient = typeof fill === 'string' && fill.includes('gradient(');\n\n if (content.shape === 'rect' && isCSSGradient && !content.gradient) {\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: `${width}px`,\n height: `${height}px`,\n background: fill,\n borderRadius: content.borderRadius ? `${content.borderRadius}px` : undefined,\n pointerEvents: 'none',\n }}\n />\n </foreignObject>\n </g>\n );\n }\n\n const { fill: fillValue, def: fillDef } = resolveFill(\n layer.id,\n fill,\n content.gradient,\n content.pattern,\n );\n const { filterAttr, def: filterDef } = resolveShapeFilter(layer.id, content.filter);\n const dash = borderDashArray(content.borderStyle, content.strokeWidth);\n\n // Common style props for native SVG shapes. `line` is stroke-only.\n const shapeProps = {\n fill: fillValue,\n fillOpacity: content.fillOpacity,\n stroke: content.stroke,\n strokeWidth: content.strokeWidth,\n strokeDasharray: dash,\n ...(filterAttr ? { filter: filterAttr } : {}),\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 {(fillDef || filterDef) && (\n <defs>\n {fillDef}\n {filterDef}\n </defs>\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 strokeDasharray={dash}\n />\n )}\n </g>\n );\n}\n\nexport default ShapeLayer;\n","/**\n * PathLayer Component\n *\n * Renders an SVG `<path>` for arbitrary curves, connectors, arrows, and the\n * drawing template's computed shapes. Used by the diagram template for edges\n * between nodes; usable by any template that needs a non-rect/circle/line\n * shape.\n *\n * The path's `d` attribute uses absolute SVG coordinates relative to the\n * block viewport (independent of the layer's `position` box, which is\n * present only so animations and clipping match the other layer types).\n *\n * Exception: when `content.shapeKind` is set (a standard named shape like\n * `diamond` / `star` / `arrow-right`), `d` is re-derived from the layer's\n * `position` box resolved against the viewport. That keeps named shapes\n * movable, resizable, and aspect-ratio-adaptive — matching how the native\n * rect/circle/line `ShapeLayer` behaves — rather than pinned to a baked\n * absolute path.\n *\n * End markers are configured via `startMarker`/`endMarker` (with the legacy\n * `arrow` flag mapping to a filled triangle). Marker geometry comes from\n * `markerPath` in core so the SSR renderer and the editor agree.\n */\n\nimport type { PathLayer as PathLayerType, MarkerStyle } from '@bendyline/squisq/schemas';\nimport { markerPath, shapePath } from '@bendyline/squisq/doc';\nimport { getAnimationStyle } from '../utils/animationUtils';\nimport { resolveValue, getAnchorOffset } from '../utils/layerUtils';\nimport { resolveFill, borderDashArray } from '../utils/fillStyle';\n\ninterface PathLayerProps {\n layer: PathLayerType;\n /** Viewport dimensions — used to resolve `%` positions for named shapes. */\n viewport: { width: number; height: number };\n /** Current time relative to block start. */\n blockTime: number;\n}\n\n/**\n * The effective `d`: a named shape (`content.shapeKind`) is re-derived\n * from the resolved `position` box so it tracks moves/resizes and adapts\n * to the viewport; everything else uses the stored absolute path.\n */\nfunction effectivePath(layer: PathLayerType, viewport: { width: number; height: number }): string {\n const { content, position } = layer;\n if (!content.shapeKind) return content.d;\n const w = position.width ? resolveValue(position.width, viewport.width) : 0;\n const h = position.height ? resolveValue(position.height, viewport.height) : 0;\n const rawX = resolveValue(position.x, viewport.width);\n const rawY = resolveValue(position.y, viewport.height);\n const anchor = getAnchorOffset(position.anchor, w, h);\n const derived = shapePath(content.shapeKind, rawX + anchor.x, rawY + anchor.y, w, h);\n // Fall back to the stored path if the kind is unknown to `shapePath`.\n return derived ?? content.d;\n}\n\n/** Resolve the effective marker for an endpoint (explicit field, else `arrow`). */\nfunction effectiveMarker(\n explicit: MarkerStyle | undefined,\n arrow: PathLayerType['content']['arrow'],\n end: 'start' | 'end',\n): MarkerStyle {\n if (explicit) return explicit;\n const wants = arrow === 'both' || arrow === end;\n return wants ? 'arrow' : 'none';\n}\n\nexport function PathLayer({ layer, viewport, blockTime }: PathLayerProps) {\n const { content, animation, id } = layer;\n const d = effectivePath(layer, viewport);\n const stroke = content.stroke ?? '#1e293b';\n const strokeWidth = content.strokeWidth ?? 2;\n const { fill, def: fillDef } = resolveFill(id, content.fill ?? 'none', content.gradient);\n // `borderStyle` (named shapes) takes precedence over a raw `dasharray`.\n const dash = content.borderStyle\n ? borderDashArray(content.borderStyle, strokeWidth)\n : content.dasharray;\n const animStyle = getAnimationStyle(animation, blockTime);\n\n const startId = `marker-start-${id}`;\n const endId = `marker-end-${id}`;\n const start = markerPath(effectiveMarker(content.startMarker, content.arrow, 'start'), 'start');\n const end = markerPath(effectiveMarker(content.endMarker, content.arrow, 'end'), 'end');\n\n return (\n <g\n className={`block-layer block-layer--path ${animStyle.className}`}\n style={animStyle.style}\n data-layer-id={id}\n >\n <defs>\n {fillDef}\n {end && <MarkerDef id={endId} dir=\"end\" d={end.d} filled={end.filled} stroke={stroke} />}\n {start && (\n <MarkerDef id={startId} dir=\"start\" d={start.d} filled={start.filled} stroke={stroke} />\n )}\n </defs>\n <path\n d={d}\n stroke={stroke}\n strokeWidth={strokeWidth}\n fill={fill}\n fillOpacity={content.fillOpacity}\n strokeDasharray={dash}\n markerStart={start ? `url(#${startId})` : undefined}\n markerEnd={end ? `url(#${endId})` : undefined}\n />\n </g>\n );\n}\n\nfunction MarkerDef({\n id,\n dir,\n d,\n filled,\n stroke,\n}: {\n id: string;\n dir: 'start' | 'end';\n d: string;\n filled: boolean;\n stroke: string;\n}) {\n return (\n <marker\n id={id}\n viewBox=\"0 0 10 10\"\n refX={dir === 'end' ? 9 : 1}\n refY={5}\n markerWidth={4}\n markerHeight={4}\n orient=\"auto-start-reverse\"\n markerUnits=\"strokeWidth\"\n >\n <path\n d={d}\n fill={filled ? stroke : 'none'}\n stroke={filled ? 'none' : stroke}\n strokeWidth={filled ? undefined : 1.5}\n />\n </marker>\n );\n}\n\nexport default PathLayer;\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({ layer, basePath, viewport, blockTime, isPlaying }: VideoLayerProps) {\n const { content, position } = layer;\n const videoRef = useRef<HTMLVideoElement>(null);\n const hasStartedRef = useRef(false);\n\n // Seconds into the block before this clip begins. Until then the video is\n // held (paused) at its in-point so a `startAt` offset reads as a delay.\n const startAt = content.startAt ?? 0;\n const gated = blockTime < startAt;\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, honoring the startAt gate.\n useEffect(() => {\n const video = videoRef.current;\n if (!video || !hasStartedRef.current) return;\n\n // Before the clip's startAt offset, hold at the in-point.\n if (gated) {\n video.pause();\n video.currentTime = content.clipStart;\n return;\n }\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, gated, content.clipStart, 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 data-start-at={startAt}\n style={{\n width: `${width}px`,\n height: `${height}px`,\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 * TableLayer Component\n *\n * Renders a data table within an SVG block using a <foreignObject> to embed\n * an HTML table. This gives us native table layout inside SVG viewports.\n *\n * The table is styled inline using the TableLayerStyle properties, which are\n * typically derived from the active theme by the dataTable template.\n */\n\nimport type { TableLayer as TableLayerType } from '@bendyline/squisq/schemas';\nimport { resolveValue, getAnchorOffset } from '../utils/layerUtils';\nimport { getAnimationStyle } from '../utils/animationUtils';\n\ninterface TableLayerProps {\n layer: TableLayerType;\n /** Viewport dimensions for percentage calculations */\n viewport: { width: number; height: number };\n /** Current time relative to block start (for animation) */\n blockTime: number;\n}\n\nexport function TableLayer({ layer, viewport, blockTime }: TableLayerProps) {\n const { content, position, animation } = layer;\n const { headers, rows, align, 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 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 // Build animation style\n const animStyle = animation ? getAnimationStyle(animation, blockTime) : {};\n\n const cellAlign = (ci: number): React.CSSProperties | undefined => {\n const a = align?.[ci];\n return a ? { textAlign: a } : undefined;\n };\n\n const borderRadius = style.borderRadius ?? 8;\n\n return (\n <foreignObject x={finalX} y={finalY} width={width} height={height} style={animStyle}>\n <div\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n {...({ xmlns: 'http://www.w3.org/1999/xhtml' } as any)}\n style={{\n width: `${width}px`,\n height: `${height}px`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '16px',\n boxSizing: 'border-box',\n }}\n >\n <table\n style={{\n width: '100%',\n borderCollapse: 'separate',\n borderSpacing: 0,\n fontSize: `${style.fontSize}px`,\n fontFamily: style.fontFamily ?? 'system-ui, sans-serif',\n overflow: 'hidden',\n borderRadius: `${borderRadius}px`,\n border: `1px solid ${style.borderColor}`,\n }}\n >\n {headers.length > 0 && (\n <thead>\n <tr>\n {headers.map((header, ci) => (\n <th\n key={ci}\n style={{\n background: style.headerBackground,\n color: style.headerColor,\n fontFamily:\n style.headerFontFamily ?? style.fontFamily ?? 'system-ui, sans-serif',\n fontWeight: 600,\n padding: '12px 16px',\n borderBottom: `2px solid ${style.borderColor}`,\n borderRight:\n ci < headers.length - 1 ? `1px solid ${style.borderColor}` : undefined,\n ...cellAlign(ci),\n }}\n >\n {header}\n </th>\n ))}\n </tr>\n </thead>\n )}\n {rows.length > 0 && (\n <tbody>\n {rows.map((row, ri) => (\n <tr key={ri}>\n {row.map((cell, ci) => (\n <td\n key={ci}\n style={{\n background: style.cellBackground,\n color: style.cellColor,\n padding: '10px 16px',\n borderBottom:\n ri < rows.length - 1 ? `1px solid ${style.borderColor}` : undefined,\n borderRight:\n ci < row.length - 1 ? `1px solid ${style.borderColor}` : undefined,\n ...cellAlign(ci),\n }}\n >\n {cell}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n )}\n </table>\n </div>\n </foreignObject>\n );\n}\n","/**\n * CaptionOverlay Component\n *\n * Displays closed captions synchronized with audio playback.\n * Supports two styles:\n *\n * - `'standard'` (default): Traditional broadcast captions — small white text\n * on a semi-transparent black badge at the top of the player.\n * - `'social'`: Social media-style (Instagram/TikTok) — large centered words\n * with the active word highlighted in the theme's primary color.\n */\n\nimport type { CaptionTrack, ViewportConfig } from '@bendyline/squisq/schemas';\nimport { getCaptionAtTime } from '@bendyline/squisq/schemas';\nimport type { Theme } from '@bendyline/squisq/schemas';\nimport type { CaptionStyle } from './types';\nimport { SocialCaptionOverlay } from './SocialCaptionOverlay';\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 for standard style (default: 16) */\n fontSize?: number;\n /** Caption display style (default: 'standard') */\n captionStyle?: CaptionStyle;\n /** Theme for social-style caption colors and fonts */\n theme?: Theme;\n /** Viewport config for social-style font scaling */\n viewport?: ViewportConfig;\n}\n\nexport function CaptionOverlay({\n captions,\n currentTime,\n enabled = true,\n fontSize = 16,\n captionStyle = 'standard',\n theme,\n viewport,\n}: CaptionOverlayProps) {\n // Delegate to social overlay when that style is active\n if (captionStyle === 'social') {\n return (\n <SocialCaptionOverlay\n captions={captions}\n currentTime={currentTime}\n enabled={enabled}\n theme={theme}\n viewport={viewport}\n />\n );\n }\n\n // Standard caption rendering\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 * SocialCaptionOverlay Component\n *\n * Social media-style captions (Instagram/TikTok): large centered words\n * showing 3-5 words at a time with the currently-spoken word highlighted\n * in the theme's primary color. Font and colors are pulled from the\n * active theme.\n *\n * Words are gathered across all caption phrases into a continuous stream,\n * then chunked into uniform groups for smooth, consistent pacing.\n *\n * Supports two timing modes:\n * 1. Precise: uses per-word timestamps from CaptionPhrase.words\n * 2. Interpolated: distributes timing evenly within each phrase\n */\n\nimport { useMemo } from 'react';\nimport type { CaptionTrack, CaptionPhrase, ViewportConfig } from '@bendyline/squisq/schemas';\nimport type { Theme } from '@bendyline/squisq/schemas';\nimport { resolveFontFamily } from '@bendyline/squisq/schemas';\n\n/** Target words per visible chunk. */\nconst TARGET_CHUNK_SIZE = 4;\nconst MIN_CHUNK_SIZE = 2;\nconst MAX_CHUNK_SIZE = 6;\n\n/** A timed word derived from phrase data. */\ninterface TimedWord {\n text: string;\n startTime: number;\n endTime: number;\n}\n\n/** A chunk of words displayed together. */\ninterface WordChunk {\n words: TimedWord[];\n startTime: number;\n endTime: number;\n}\n\n/**\n * Resolve per-word timing for a single phrase.\n * Uses precise word timestamps when available, otherwise interpolates.\n */\nfunction resolvePhraseTiming(phrase: CaptionPhrase): TimedWord[] {\n // Use precise timing when available\n if (phrase.words && phrase.words.length > 0) {\n return phrase.words.map((w) => ({\n text: w.text,\n startTime: w.startTime,\n endTime: w.endTime,\n }));\n }\n\n // Interpolate: split text into words, distribute evenly\n const rawWords = phrase.text.split(/\\s+/).filter((w) => w.length > 0);\n if (rawWords.length === 0) return [];\n\n const duration = phrase.endTime - phrase.startTime;\n const wordDuration = duration / rawWords.length;\n\n return rawWords.map((text, i) => ({\n text,\n startTime: phrase.startTime + i * wordDuration,\n endTime: phrase.startTime + (i + 1) * wordDuration,\n }));\n}\n\n/**\n * Build a continuous stream of timed words from ALL caption phrases,\n * then group into uniform display chunks. This eliminates the \"pulsy\"\n * feeling caused by per-phrase chunking with variable phrase lengths.\n */\nfunction buildWordStream(captions: CaptionTrack): { words: TimedWord[]; chunks: WordChunk[] } {\n // Gather all words across all phrases\n const allWords: TimedWord[] = [];\n for (const phrase of captions.phrases) {\n allWords.push(...resolvePhraseTiming(phrase));\n }\n\n if (allWords.length === 0) return { words: [], chunks: [] };\n\n // Determine uniform chunk size\n let chunkSize = TARGET_CHUNK_SIZE;\n if (allWords.length > chunkSize) {\n const numChunks = Math.ceil(allWords.length / chunkSize);\n chunkSize = Math.ceil(allWords.length / numChunks);\n chunkSize = Math.min(MAX_CHUNK_SIZE, Math.max(MIN_CHUNK_SIZE, chunkSize));\n } else {\n chunkSize = Math.max(MIN_CHUNK_SIZE, allWords.length);\n }\n\n // Build chunks\n const chunks: WordChunk[] = [];\n for (let i = 0; i < allWords.length; i += chunkSize) {\n const chunkWords = allWords.slice(i, i + chunkSize);\n chunks.push({\n words: chunkWords,\n startTime: chunkWords[0].startTime,\n endTime: chunkWords[chunkWords.length - 1].endTime,\n });\n }\n\n return { words: allWords, chunks };\n}\n\ninterface SocialCaptionOverlayProps {\n captions: CaptionTrack | undefined;\n currentTime: number;\n enabled?: boolean;\n theme?: Theme;\n viewport?: ViewportConfig;\n}\n\nexport function SocialCaptionOverlay({\n captions,\n currentTime,\n enabled = true,\n theme,\n viewport,\n}: SocialCaptionOverlayProps) {\n // Build the word stream once when captions change (memoized)\n const { chunks } = useMemo(\n () => (captions ? buildWordStream(captions) : { words: [], chunks: [] }),\n [captions],\n );\n\n if (!enabled || chunks.length === 0) {\n return (\n <div\n className=\"social-caption-overlay\"\n style={{\n position: 'absolute',\n bottom: '18%',\n left: 0,\n right: 0,\n zIndex: 50,\n pointerEvents: 'none',\n opacity: 0,\n transition: 'opacity 0.15s ease-in-out',\n }}\n />\n );\n }\n\n // Find the active chunk and word using binary-style search\n let activeChunk: WordChunk | null = null;\n let activeWordIndex = -1;\n\n for (const chunk of chunks) {\n if (currentTime >= chunk.startTime && currentTime < chunk.endTime) {\n activeChunk = chunk;\n break;\n }\n }\n\n // If between chunks (gap), show the nearest chunk\n if (!activeChunk) {\n for (let i = 0; i < chunks.length - 1; i++) {\n if (currentTime >= chunks[i].endTime && currentTime < chunks[i + 1].startTime) {\n // In a gap — show the chunk we just left (feels more natural)\n activeChunk = chunks[i];\n activeWordIndex = activeChunk.words.length - 1;\n break;\n }\n }\n // Past all chunks\n if (!activeChunk && chunks.length > 0 && currentTime >= chunks[chunks.length - 1].startTime) {\n activeChunk = chunks[chunks.length - 1];\n activeWordIndex = activeChunk.words.length - 1;\n }\n }\n\n if (!activeChunk) return null;\n\n // Find active word within chunk (if not already set from gap handling)\n if (activeWordIndex === -1) {\n for (let i = 0; i < activeChunk.words.length; i++) {\n const word = activeChunk.words[i];\n if (currentTime >= word.startTime && currentTime < word.endTime) {\n activeWordIndex = i;\n break;\n }\n }\n // Fallback: last word before currentTime\n if (activeWordIndex === -1) {\n for (let i = activeChunk.words.length - 1; i >= 0; i--) {\n if (currentTime >= activeChunk.words[i].startTime) {\n activeWordIndex = i;\n break;\n }\n }\n }\n // Final fallback\n if (activeWordIndex === -1) activeWordIndex = 0;\n }\n\n // Theme-derived styling\n const primaryColor = theme?.colors?.primary ?? '#5b9bd5';\n const fontFamily = theme?.typography?.titleFont\n ? resolveFontFamily(theme.typography.titleFont, '\"PT Serif\", Georgia, serif')\n : '\"PT Serif\", Georgia, serif';\n\n // Scale font to viewport — aim for ~5.5% of viewport height\n const viewportHeight = viewport?.height ?? 720;\n const baseFontSize = Math.round(viewportHeight * 0.055);\n const fontSize = Math.max(24, Math.min(72, baseFontSize));\n\n return (\n <div\n className=\"social-caption-overlay\"\n style={{\n position: 'absolute',\n bottom: '18%',\n left: 0,\n right: 0,\n zIndex: 50,\n pointerEvents: 'none',\n textAlign: 'center',\n padding: '0 8%',\n boxSizing: 'border-box',\n opacity: 1,\n transition: 'opacity 0.15s ease-in-out',\n }}\n >\n <div\n style={{\n display: 'inline-block',\n lineHeight: 1.3,\n }}\n >\n {activeChunk.words.map((word, i) => {\n const isActive = i === activeWordIndex;\n return (\n <span\n key={`${word.startTime}-${i}`}\n style={{\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight: isActive ? 800 : 600,\n color: isActive ? primaryColor : 'rgba(255, 255, 255, 0.9)',\n textShadow: '0 2px 8px rgba(0,0,0,0.7), 0 0 20px rgba(0,0,0,0.4)',\n marginRight: i < activeChunk!.words.length - 1 ? '0.3em' : undefined,\n transition: 'color 0.1s ease, font-weight 0.1s ease',\n textTransform: 'uppercase',\n }}\n >\n {word.text}\n </span>\n );\n })}\n </div>\n </div>\n );\n}\n","import { useCallback, useMemo, useSyncExternalStore } from 'react';\nimport { DARK_SURFACE, LIGHT_SURFACE, type SurfaceScheme } from '@bendyline/squisq/schemas';\n\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\nconst getServerSnapshot = () => LIGHT_SURFACE;\n\n/**\n * Live-track `prefers-color-scheme` and return a stable SurfaceScheme.\n * `enabled: false` short-circuits to LIGHT_SURFACE (callers pass `false`\n * when a static surface was provided so the hook never observes the\n * media query). The `MediaQueryList` and the `subscribe`/`getSnapshot`\n * callbacks are memoized so `useSyncExternalStore` doesn't resubscribe on\n * every parent render.\n */\nexport function useAutoSurface(enabled: boolean): SurfaceScheme {\n const mql = useMemo(\n () => (enabled && typeof window !== 'undefined' ? window.matchMedia(DARK_QUERY) : null),\n [enabled],\n );\n\n const subscribe = useCallback(\n (cb: () => void) => {\n if (!mql) return () => {};\n mql.addEventListener('change', cb);\n return () => mql.removeEventListener('change', cb);\n },\n [mql],\n );\n\n const getSnapshot = useCallback(() => (mql?.matches ? DARK_SURFACE : LIGHT_SURFACE), [mql]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\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 AudioController interface.\n * Hosts that drive audio through an external player (e.g. a native shell)\n * can supply their own AudioController to DocPlayer instead of this hook.\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { RefObject } from 'react';\nimport type { AudioTrack } from '@bendyline/squisq/schemas';\nimport type { AudioController } from './AudioController';\n\nexport function useAudioSync(\n audioRef: RefObject<HTMLAudioElement>,\n audioTrack: AudioTrack | undefined,\n basePath: string = '',\n): AudioController {\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 // In fallback mode the <audio> element is NOT the clock — there's no real\n // source (e.g. the editor preview's synthetic, empty-src track), so the\n // synthetic timer and `seekTo` own `currentTime`. A `timeupdate` fired as\n // a side effect of programmatically setting `audio.currentTime` (during a\n // seek) would otherwise clobber the just-seeked position with the empty\n // element's unreliable `currentTime`, snapping the scrubber back.\n if (fallbackMode.current) return;\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 // Don't clear `fallbackMode` here. Whether the <audio> element is really\n // the clock is decided authoritatively by the play() promise: it only\n // resolves (clearing fallback, see `play`) when a real source actually\n // plays. The 'play' event, by contrast, can fire spuriously on the\n // source-less preview element — and clearing fallback there makes the\n // synthetic timer's tick guard bail on its next frame, freezing the\n // clock and the scrubber after a seek/resume.\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 // When totalDuration is 0 (no audio segments), don't clamp — allow\n // seeking by block timing alone (used in render mode / preview).\n const clampedTime =\n totalDuration > 0 ? Math.max(0, Math.min(time, totalDuration)) : Math.max(0, time);\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 { useMemo, useCallback, useRef } from 'react';\nimport type { Doc, Block, DocBlock } from '@bendyline/squisq/schemas';\nimport type { Theme } from '@bendyline/squisq/schemas';\nimport {\n DEFAULT_THEME,\n getBlockAtTime,\n resolveBlockTransition,\n resolveTransitionDuration,\n} from '@bendyline/squisq/schemas';\nimport {\n expandDocBlocks,\n flattenRenderableBlocks,\n isTemplateBlock,\n resolvePersistentLayers,\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 // `renderMode` is retained for API/signature compatibility; block transitions\n // are now computed identically for real-time and render (export) modes.\n void renderMode;\n // Expand any template blocks into full blocks\n const blocks = useMemo(() => {\n if (!script?.blocks) {\n return [];\n }\n\n // Flatten nested block hierarchy (markdown-derived docs have children).\n // `flattenRenderableBlocks` skips the children of container templates\n // (`diagram`, `drawing`) — those are consumed by the parent's render as\n // nodes/shapes, so they must not also appear as their own slides.\n const hasChildren = script.blocks.some((b) => b.children && b.children.length > 0);\n const flatBlocks = hasChildren ? flattenRenderableBlocks(script.blocks) : script.blocks;\n\n // Check if any blocks are templates\n const hasTemplates = flatBlocks.some(isTemplateBlock);\n\n // Doc persistent layers win wholesale; docs without any inherit the\n // theme's (see resolvePersistentLayers). Passed as a narrow object so\n // the memo deps stay field-precise.\n const resolvedTheme = theme ?? DEFAULT_THEME;\n const persistentLayers = resolvePersistentLayers(\n { persistentLayers: script.persistentLayers },\n resolvedTheme,\n );\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(flatBlocks as DocBlock[], {\n audioSegments,\n viewport,\n persistentLayers,\n theme,\n // Custom (user-defined) templates inlined into the doc's\n // frontmatter — see CustomTemplates.ts. Merged onto the\n // built-in registry so blocks annotated with `{[myhero]}`\n // resolve through the user's design.\n customTemplates: script.customTemplates,\n });\n return expanded;\n }\n\n // All raw blocks — used as-is except for the theme's default transition\n // fallback (copies, never mutations: these blocks are caller-owned).\n return flatBlocks.map((block, index) => {\n const transition = resolveBlockTransition(block, resolvedTheme, index);\n return transition !== block.transition ? { ...block, transition } : block;\n });\n }, [\n script?.blocks,\n script?.audio?.segments,\n script?.persistentLayers,\n script?.customTemplates,\n viewport,\n theme,\n ]);\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 // ─── Transition tracking (synchronous — no effect lag) ──────────────\n // `isEntering` is simply \"we are within the block's entrance window\"\n // (blockTime < the transition's duration). Deriving it during render —\n // rather than flipping it in an effect a frame AFTER the block changes —\n // means a newly-active block renders WITH its entrance state on its very\n // first frame. Otherwise the block paints once fully settled and then, a\n // frame later, snaps back to the start of its entrance animation: the brief\n // \"flash then re-animate\" seen between blocks. This runs identically for\n // real-time playback and frame-seeked render (export) mode.\n //\n // The block we transitioned FROM (for the crossfade) is tracked with refs\n // updated during render — the standard \"previous value\" pattern — so the\n // outgoing block is known on the SAME frame the new block becomes active\n // (an effect would lag a frame and drop the crossfade's first frames).\n const outgoingBlockRef = useRef<Block | null>(null);\n const activeBlockIdRef = useRef<string | null>(null);\n const lastRenderedBlockRef = useRef<Block | null>(null);\n if (currentBlock && currentBlock.id !== activeBlockIdRef.current) {\n outgoingBlockRef.current = lastRenderedBlockRef.current;\n activeBlockIdRef.current = currentBlock.id;\n }\n lastRenderedBlockRef.current = currentBlock;\n\n const transitionDuration = currentBlock?.transition\n ? resolveTransitionDuration(currentBlock.transition)\n : 0;\n const isEntering = !!currentBlock && transitionDuration > 0 && blockTime < transitionDuration;\n const outgoingBlock = outgoingBlockRef.current;\n // Only crossfade a genuinely different outgoing block (guards restarts/seeks\n // where the \"previous\" resolves to the same block).\n const isExiting = isEntering && outgoingBlock != null && outgoingBlock.id !== currentBlock?.id;\n const previousBlock = isExiting ? outgoingBlock : 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,\n isEntering,\n isExiting,\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 * useSlideSwipe Hook\n *\n * Drag-to-swipe navigation for slideshow mode. Press on the current slide to\n * \"pick it up\", drag left/right, and on release either snap back (small drag)\n * or advance to the next/previous slide (large drag or a quick flick).\n *\n * The gesture only translates the *current* slide (a \"pick up & toss\" feel) —\n * the incoming slide arrives via its normal enter transition once navigation\n * commits. The current slide is already mounted and settled, so translating it\n * is a pure `transform` with no animation conflict.\n *\n * Mirrors the repo's canonical pointer-drag pattern (imageEditor/CanvasSurface):\n * ref-held drag start + `setPointerCapture` + window pointermove/up/cancel +\n * a threshold decision on release. Navigation itself is delegated to the\n * caller's `onNext`/`onPrev` (DocPlayer's `slideNavActions`).\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { RefObject } from 'react';\n\n/** Fraction of the container width a drag must cross to commit a slide change. */\nconst DISTANCE_RATIO = 0.3;\n/** A fast flick commits even below the distance threshold (px per millisecond). */\nconst FLICK_VELOCITY = 0.5;\n/** Ignore flicks shorter than this so a click's micro-jitter never navigates (px). */\nconst MIN_FLICK_DISTANCE = 12;\n/** Resistance factor applied when dragging past the first/last slide. */\nconst RUBBER_BAND = 0.35;\n/** Default settle animation duration (ms). Must match the CSS in doc-animations.css. */\nconst DEFAULT_SETTLE_MS = 260;\n\n/** The lifecycle of a swipe gesture. */\nexport type SwipePhase = 'idle' | 'dragging' | 'settling';\n\n/** Outcome of a completed drag. */\nexport type SwipeDecision = 'next' | 'prev' | 'snap';\n\nexport interface SwipeDecisionInput {\n /** Horizontal delta from drag start (px). Negative = leftward = next. */\n dx: number;\n /** Width of the slide container (px). */\n width: number;\n /** How long the drag lasted (ms). */\n elapsedMs: number;\n /** Whether a next slide exists. */\n canNext: boolean;\n /** Whether a previous slide exists. */\n canPrev: boolean;\n}\n\n/**\n * Pure decision: given a completed drag, should we advance, go back, or snap\n * back? Committing requires either crossing the distance threshold or a fast\n * flick, *and* a slide existing in that direction. Extracted from the hook so\n * the threshold logic is testable without a DOM.\n */\nexport function decideSwipe({\n dx,\n width,\n elapsedMs,\n canNext,\n canPrev,\n}: SwipeDecisionInput): SwipeDecision {\n const distanceThreshold = width > 0 ? width * DISTANCE_RATIO : Infinity;\n const velocity = elapsedMs > 0 ? Math.abs(dx) / elapsedMs : 0;\n const passesDistance = Math.abs(dx) >= distanceThreshold;\n const passesFlick = velocity >= FLICK_VELOCITY && Math.abs(dx) >= MIN_FLICK_DISTANCE;\n\n if (!passesDistance && !passesFlick) return 'snap';\n if (dx < 0) return canNext ? 'next' : 'snap'; // dragged left → next slide\n if (dx > 0) return canPrev ? 'prev' : 'snap'; // dragged right → previous slide\n return 'snap';\n}\n\nexport interface UseSlideSwipeOptions {\n /** Master gate — the gesture is inert unless enabled (slideshow mode, not headless). */\n enabled: boolean;\n /** Ref to the player container, used to measure width for the threshold. */\n containerRef: RefObject<HTMLElement>;\n /** Whether a next slide exists (`currentBlockIndex < total - 1`). */\n canGoNext: boolean;\n /** Whether a previous slide exists (`currentBlockIndex > 0`). */\n canGoPrev: boolean;\n /** Commit to the next slide. */\n onNext: () => void;\n /** Commit to the previous slide. */\n onPrev: () => void;\n /** Settle animation duration (ms); must match the CSS. Defaults to 260. */\n settleMs?: number;\n}\n\nexport interface UseSlideSwipeResult {\n /** Live horizontal offset to apply to the active slide (px). */\n offsetPx: number;\n /** Current gesture phase — drives the transform transition class. */\n phase: SwipePhase;\n /** Attach to the container's `onPointerDown`. */\n onPointerDown: (e: React.PointerEvent) => void;\n}\n\ninterface DragState {\n pointerId: number;\n startX: number;\n startTime: number;\n target: Element;\n}\n\n/**\n * Manage the swipe gesture state machine for a slideshow player.\n */\nexport function useSlideSwipe(opts: UseSlideSwipeOptions): UseSlideSwipeResult {\n const settleMs = opts.settleMs ?? DEFAULT_SETTLE_MS;\n\n const [offsetPx, setOffsetPx] = useState(0);\n const [phase, setPhase] = useState<SwipePhase>('idle');\n\n // Keep the latest options in a ref so the window listeners (bound once) never\n // read stale callbacks/flags.\n const optsRef = useRef(opts);\n optsRef.current = opts;\n\n const dragRef = useRef<DragState | null>(null);\n const phaseRef = useRef<SwipePhase>('idle');\n phaseRef.current = phase;\n const settleTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const settleRaf = useRef<number | null>(null);\n\n const clearPending = useCallback(() => {\n if (settleTimer.current != null) {\n clearTimeout(settleTimer.current);\n settleTimer.current = null;\n }\n if (settleRaf.current != null) {\n cancelAnimationFrame(settleRaf.current);\n settleRaf.current = null;\n }\n }, []);\n\n const onPointerDown = useCallback((e: React.PointerEvent) => {\n const o = optsRef.current;\n if (!o.enabled) return;\n // Don't interrupt an in-flight fling/snap.\n if (phaseRef.current === 'settling') return;\n // Only the primary mouse button initiates a drag; touch/pen always do.\n if (e.pointerType === 'mouse' && e.button !== 0) return;\n // Let interactive chrome (prev/next buttons, links) handle their own events.\n const target = e.target as Element;\n if (target.closest?.('button, a, input, textarea, select, [data-no-swipe]')) return;\n\n dragRef.current = {\n pointerId: e.pointerId,\n startX: e.clientX,\n startTime: performance.now(),\n target,\n };\n try {\n target.setPointerCapture?.(e.pointerId);\n } catch {\n // Pointer capture is best-effort; window listeners still receive events.\n }\n setPhase('dragging');\n setOffsetPx(0);\n }, []);\n\n // Bind move/up/cancel on window once. Handlers read live state from refs, so\n // they never need re-binding and never go stale.\n useEffect(() => {\n function currentWidth(): number {\n return optsRef.current.containerRef.current?.getBoundingClientRect().width ?? 0;\n }\n\n function endDrag(drag: DragState) {\n dragRef.current = null;\n try {\n drag.target.releasePointerCapture?.(drag.pointerId);\n } catch {\n // ignore — capture may already be released\n }\n }\n\n function settleTo(target: number, onArrive?: () => void) {\n // Switch on the transition class first (offset unchanged), then move to the\n // target on the next frame so the browser reliably animates the change.\n setPhase('settling');\n settleRaf.current = requestAnimationFrame(() => {\n settleRaf.current = null;\n setOffsetPx(target);\n settleTimer.current = setTimeout(() => {\n settleTimer.current = null;\n onArrive?.();\n setOffsetPx(0);\n setPhase('idle');\n }, settleMs);\n });\n }\n\n function onMove(e: PointerEvent) {\n const drag = dragRef.current;\n if (!drag || e.pointerId !== drag.pointerId) return;\n const o = optsRef.current;\n const raw = e.clientX - drag.startX;\n const blocked = (raw > 0 && !o.canGoPrev) || (raw < 0 && !o.canGoNext);\n setOffsetPx(blocked ? raw * RUBBER_BAND : raw);\n }\n\n function onUp(e: PointerEvent) {\n const drag = dragRef.current;\n if (!drag || e.pointerId !== drag.pointerId) return;\n endDrag(drag);\n const o = optsRef.current;\n const rawDx = e.clientX - drag.startX;\n const width = currentWidth();\n const elapsedMs = performance.now() - drag.startTime;\n const decision = decideSwipe({\n dx: rawDx,\n width,\n elapsedMs,\n canNext: o.canGoNext,\n canPrev: o.canGoPrev,\n });\n\n if (decision === 'snap') {\n settleTo(0);\n return;\n }\n // Fling fully off-screen in the drag direction, then commit navigation.\n // Never move the slide back toward center first (guard against over-drag).\n const distance = Math.max(width, Math.abs(rawDx));\n const target = decision === 'next' ? -distance : distance;\n settleTo(target, decision === 'next' ? o.onNext : o.onPrev);\n }\n\n function onCancel(e: PointerEvent) {\n const drag = dragRef.current;\n if (!drag || e.pointerId !== drag.pointerId) return;\n endDrag(drag);\n settleTo(0);\n }\n\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', onUp);\n window.addEventListener('pointercancel', onCancel);\n return () => {\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', onUp);\n window.removeEventListener('pointercancel', onCancel);\n };\n }, [settleMs]);\n\n // Reset all in-flight state if the gesture is disabled mid-drag (e.g. mode switch).\n useEffect(() => {\n if (!opts.enabled) {\n dragRef.current = null;\n clearPending();\n setPhase('idle');\n setOffsetPx(0);\n }\n }, [opts.enabled, clearPending]);\n\n // Cancel any pending settle on unmount.\n useEffect(() => clearPending, [clearPending]);\n\n return { offsetPx, phase, onPointerDown };\n}\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 // Fill position tracks the same timeline as the clock readout and the block\n // marker dots — elapsed playback time over the audio `totalDuration`. (We\n // deliberately don't use `state.docProgress`, which is keyed to the doc's\n // estimated `script.duration`; when that diverges from the actual playback\n // length — e.g. the editor preview's reading-time estimate — the fill would\n // desync from the clock and the dots.)\n const playProgress =\n state.totalDuration > 0 ? Math.max(0, Math.min(1, state.currentTime / state.totalDuration)) : 0;\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 No CSS `width` transition: the fill is driven by frequent JS updates\n (the synthetic fallback timer runs ~60fps; real audio fires\n `timeupdate` several times/sec), so it's already smooth. A CSS\n transition here actively breaks playback — after a discontinuous\n backward seek, the per-frame inline-width updates on resume keep\n interrupting the in-flight transition before it advances, so the\n *rendered* width latches at the seek position even though the inline\n style (and the clock) keep climbing. The result is a frozen-looking\n bar while time advances. Update width instantly instead. */}\n <div\n data-testid=\"doc-progress-fill\"\n style={{\n position: 'absolute',\n left: 0,\n width: `${playProgress * 100}%`,\n height: '6px',\n background: '#5b9bd5',\n borderRadius: '3px',\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 #5b9bd5',\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 * - `'page'` — Plain semantic HTML preview matching what the\n * `markdownDocToPlainHtml` export produces. No SquisqPlayer, no SVG\n * cards — just `<h1>`/`<p>`/`<ul>` etc. inside a sandboxed iframe.\n * Use when you want a WYSIWYG view of the simple HTML export.\n */\nexport type DisplayMode = 'video' | 'slideshow' | 'linear' | 'page';\n\n/**\n * Caption display style.\n *\n * - `'standard'` — Traditional broadcast-style captions: small white text\n * on a semi-transparent black badge at the top of the player.\n * - `'social'` — Social media-style (Instagram/TikTok): large centered words\n * showing 3-5 words at a time with the active word highlighted in the\n * theme's primary color. Font and colors pulled from the active theme.\n */\nexport type CaptionStyle = 'standard' | 'social';\n\n/**\n * Caption display mode — combines enable/disable with style selection.\n * The CC button cycles through: off → standard → social → off.\n */\nexport type CaptionMode = 'off' | 'standard' | 'social';\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 /** Current caption display mode (off, standard, social). */\n captionMode: CaptionMode;\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 /** Optional display label for non-block slides, e.g. the managed cover. */\n currentSlideLabel?: string;\n /** Optional human-facing slide number, separate from internal nav index. */\n currentSlideNumber?: number;\n /** Optional human-facing slide total, separate from internal nav total. */\n totalSlideNumber?: number;\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 /** Cycle caption mode: off → standard → social → off */\n cycleCaptionMode: () => 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 mode cycle button: off → standard → social → off */}\n {state.hasCaptions && (\n <button\n onClick={() => actions.cycleCaptionMode()}\n style={{\n background: state.captionMode !== 'off' ? 'rgba(255,255,255,0.2)' : 'none',\n border: 'none',\n color: state.captionMode !== 'off' ? 'white' : 'rgba(255,255,255,0.5)',\n cursor: 'pointer',\n padding: '8px',\n fontSize: '12px',\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n borderRadius: '4px',\n }}\n title={\n state.captionMode === 'off'\n ? 'Captions: Off (click for Standard)'\n : state.captionMode === 'standard'\n ? 'Captions: Standard (click for Social)'\n : 'Captions: Social (click to turn off)'\n }\n aria-label=\"Cycle caption style\"\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 {state.captionMode !== 'off' && (\n <span style={{ fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.5px' }}>\n {state.captionMode === 'standard' ? 'CC' : 'SM'}\n </span>\n )}\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 {\n currentBlockIndex,\n currentSlideLabel,\n currentSlideNumber,\n totalBlocks,\n totalSlideNumber,\n } = state;\n const isFirst = currentBlockIndex <= 0;\n const isLast = currentBlockIndex >= totalBlocks - 1;\n const counterText =\n totalBlocks > 0\n ? (currentSlideLabel ??\n `${currentSlideNumber ?? currentBlockIndex + 1} / ${totalSlideNumber ?? totalBlocks}`)\n : '—';\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 {counterText}\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 { useAutoSurface } from './hooks/useAutoSurface';\nimport type { Doc, Block, DocBlock } from '@bendyline/squisq/schemas';\nimport type { ViewportConfig } from '@bendyline/squisq/schemas';\nimport {\n applySurface,\n resolveFontFamily,\n type SurfaceScheme,\n type Theme,\n} from '@bendyline/squisq/schemas';\nimport { VIEWPORT_PRESETS } from '@bendyline/squisq/schemas';\nimport {\n getLayers,\n hasTemplate,\n markdownToDoc,\n DEFAULT_THEME,\n deriveTemplateInputs,\n} from '@bendyline/squisq/doc';\nimport type { RenderContext } from '@bendyline/squisq/doc';\nimport { extractPlainText, parseMarkdown } from '@bendyline/squisq/markdown';\nimport { BlockRenderer } from './BlockRenderer';\nimport { MarkdownRenderer } from './MarkdownRenderer';\n\n// ── Props ──────────────────────────────────────────────────────────\n\nexport interface LinearDocViewProps {\n /**\n * The Doc to render. Wins over `markdown` when both are provided.\n * When neither `doc` nor `markdown` is given, an empty container renders.\n */\n doc?: Doc;\n /**\n * Markdown source to render. When `doc` is absent, the markdown is parsed\n * and converted to a Doc via `markdownToDoc(parseMarkdown(markdown))`.\n * Ignored when `doc` is provided.\n */\n markdown?: string;\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 * Optional surface scheme (light / dark paper) overlaid on top of the\n * theme's colors. Orthogonal to `theme` — a theme picks editorial\n * identity, a surface picks the paper. Pass `'auto'` to follow the\n * user's OS `prefers-color-scheme`, a `SurfaceScheme` object to force a\n * specific surface, or omit to use the theme's built-in colors.\n */\n surface?: SurfaceScheme | 'auto';\n /**\n * Use tight padding + a wider content column. The default layout is\n * designed for a reading surface with breathing room (720px column,\n * 24×16px padding). Short conversational snippets like chat replies\n * benefit from a much tighter layout. Set to `true` to render with\n * minimal padding and no max-width cap so the content hugs its\n * container.\n */\n thinMargins?: boolean;\n /**\n * How images inside the doc should be sized. `'inline'` (default)\n * flows them at natural size up to the column width; `'thumbnail'`\n * constrains each image to a 100×100 box with aspect-preserving\n * containment — use for chat history and other dense surfaces where\n * full-size images would dominate the layout.\n */\n imageDisplayMode?: ImageDisplayMode;\n}\n\nexport type ImageDisplayMode = 'inline' | 'thumbnail';\n\n// ── Helpers ────────────────────────────────────────────────────────\n\n// Unknown template names we've already warned about (module-level so each\n// name warns at most once per page, not once per render).\nconst warnedUnknownTemplates = new Set<string>();\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 *\n * Blocks annotated with a template that is NOT in the registry fall back\n * to plain markdown rendering, with a one-shot dev-visible warning per\n * unknown template name.\n */\nfunction isAnnotatedBlock(block: Block): boolean {\n const annotation = block.sourceHeading?.templateAnnotation;\n if (!annotation?.template) return false;\n if (!hasTemplate(annotation.template)) {\n if (!warnedUnknownTemplates.has(annotation.template)) {\n warnedUnknownTemplates.add(annotation.template);\n console.warn(\n `[squisq] Unknown template \"${annotation.template}\" — rendering the block as plain markdown.`,\n );\n }\n return false;\n }\n return true;\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\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 ...(deriveTemplateInputs(\n annotation.template ?? 'sectionHeader',\n headingText,\n block.contents,\n {\n placeholders: true,\n },\n ) ?? {}),\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 The heading is intentionally *not* duplicated above the card —\n every template card renders its own title layer internally, so\n a separate `squisq-linear-card-label` only made the heading\n appear twice in the linear view. The card also drops its\n rounded border + drop shadow so it reads as a continuation of\n the surrounding page rather than a chrome'd preview. */}\n {isAnnotated && visualBlock && (\n <div className=\"squisq-linear-card\">\n <div\n className=\"squisq-linear-card-svg\"\n style={{\n width: '100%',\n aspectRatio: `${viewport.width} / ${viewport.height}`,\n overflow: 'hidden',\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// ── 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 markdown,\n basePath = '/',\n viewport,\n className,\n theme,\n surface,\n thinMargins = false,\n imageDisplayMode = 'inline',\n}: LinearDocViewProps) {\n const activeViewport = viewport ?? VIEWPORT_PRESETS.landscape;\n\n // Parse markdown into a Doc only when no explicit doc is supplied.\n const markdownDoc = useMemo(\n () => (!doc && markdown !== undefined ? markdownToDoc(parseMarkdown(markdown)) : undefined),\n [doc, markdown],\n );\n const resolvedDoc = doc ?? markdownDoc;\n\n const totalBlocks = useMemo(\n () => (resolvedDoc ? countAll(resolvedDoc.blocks) : 0),\n [resolvedDoc],\n );\n const autoSurface = useAutoSurface(surface === 'auto');\n const resolvedSurface: SurfaceScheme | undefined = surface === 'auto' ? autoSurface : surface;\n\n const renderContext: RenderContext = useMemo(() => {\n const baseTheme = theme ?? DEFAULT_THEME;\n const effectiveTheme = resolvedSurface ? applySurface(baseTheme, resolvedSurface) : baseTheme;\n return {\n theme: effectiveTheme,\n viewport: activeViewport,\n totalBlocks,\n // Theme atmosphere (vignette/grain/gradient persistent layers) shows\n // on the inline template cards so they match the player's look.\n persistentLayers: effectiveTheme.persistentLayers,\n };\n }, [activeViewport, totalBlocks, theme, resolvedSurface]);\n\n const activeTheme = renderContext.theme!;\n\n // Nothing to render — keep an empty (but classed) container so hosts can\n // still target/measure the view.\n if (!resolvedDoc) {\n return <div className={`squisq-linear squisq-linear--empty ${className || ''}`} />;\n }\n\n const bgColor = activeTheme.colors.background;\n const textColor = activeTheme.colors.text;\n const mutedColor = activeTheme.colors.textMuted;\n const primaryColor = activeTheme.colors.primary;\n const bodyFont = resolveFontFamily(activeTheme.typography.bodyFont, 'system-ui, sans-serif');\n const titleFont = resolveFontFamily(activeTheme.typography.titleFont, 'Georgia, serif');\n const lineHt = activeTheme.typography.lineHeight ?? 1.7;\n\n return (\n <div\n className={`squisq-linear ${className || ''}`}\n style={{\n width: '100%',\n // Thin-margins mode is the \"embedded in someone else's container\"\n // signal (chat bubble, sidebar preview). Fit to content there so\n // the host's bubble doesn't render a tall empty box when the doc\n // is short. Standalone mode keeps height:100% for full-viewport\n // scrolling.\n height: thinMargins ? 'auto' : '100%',\n overflowY: thinMargins ? 'visible' : 'auto',\n overflowX: 'hidden',\n background: bgColor,\n }}\n >\n <div\n className={`squisq-linear-content squisq-md${thinMargins ? ' squisq-linear-content--thin' : ''}${imageDisplayMode === 'thumbnail' ? ' squisq-linear-content--thumbnail-images' : ''}`}\n style={\n {\n // Thin-margins mode drops the 720px reading column + generous\n // page padding (right for standalone docs) in favor of a tight\n // layout that hugs its container (right for chat bubbles and\n // sidebar previews).\n maxWidth: thinMargins ? 'none' : '720px',\n margin: thinMargins ? '0' : '0 auto',\n padding: thinMargins ? '0' : '24px 16px',\n lineHeight: lineHt,\n fontSize: '16px',\n fontFamily: bodyFont,\n color: textColor,\n // CSS custom properties for MarkdownRenderer / nested elements\n '--squisq-linear-title-font': titleFont,\n '--squisq-linear-body-font': bodyFont,\n '--squisq-linear-text': textColor,\n '--squisq-linear-muted': mutedColor,\n '--squisq-linear-primary': primaryColor,\n '--squisq-linear-bg': bgColor,\n } as React.CSSProperties\n }\n >\n {/* Theme-aware typography and layout for document mode */}\n <style>{`\n .squisq-linear-content h1,\n .squisq-linear-content h2,\n .squisq-linear-content h3,\n .squisq-linear-content h4,\n .squisq-linear-content h5,\n .squisq-linear-content h6 {\n font-family: var(--squisq-linear-title-font);\n color: var(--squisq-linear-text);\n margin-top: 1.2em;\n margin-bottom: 0.4em;\n }\n .squisq-linear-content h1 { font-size: 2em; }\n .squisq-linear-content h2 { font-size: 1.5em; }\n .squisq-linear-content h3 { font-size: 1.25em; }\n .squisq-linear-content p {\n margin-bottom: 0.75em;\n }\n .squisq-linear-content ul,\n .squisq-linear-content ol {\n padding-left: 2em;\n margin-bottom: 0.75em;\n }\n .squisq-linear-content li {\n margin-bottom: 0.3em;\n }\n .squisq-linear-content a {\n /* Blend the theme's primary toward the body text color so\n links stay theme-flavored but always have enough contrast\n against the background. Some themes (e.g. Gezellig) pick a\n mid-tone primary that's nearly invisible on a dark page\n without this lift. */\n color: color-mix(in srgb, var(--squisq-linear-primary) 65%, var(--squisq-linear-text));\n text-decoration: underline;\n text-decoration-thickness: 1px;\n text-underline-offset: 2px;\n }\n .squisq-linear-content a:hover {\n color: var(--squisq-linear-primary);\n }\n .squisq-linear-content code {\n color: var(--squisq-linear-primary);\n font-size: 0.9em;\n padding: 0.15em 0.3em;\n border-radius: 3px;\n background: rgba(128, 128, 128, 0.15);\n }\n .squisq-linear-content pre {\n padding: 1em;\n border-radius: 6px;\n background: rgba(0, 0, 0, 0.2);\n overflow-x: auto;\n margin-bottom: 0.75em;\n }\n .squisq-linear-content pre code {\n padding: 0;\n background: none;\n }\n .squisq-linear-content blockquote {\n border-left: 3px solid var(--squisq-linear-muted);\n color: var(--squisq-linear-muted);\n padding-left: 1em;\n margin-left: 0;\n margin-bottom: 0.75em;\n }\n .squisq-linear-content hr {\n border: none;\n border-top: 1px solid var(--squisq-linear-muted);\n margin: 1.5em 0;\n }\n .squisq-linear-content img {\n max-width: 100%;\n height: auto;\n border-radius: 6px;\n margin: 0.5em 0;\n }\n .squisq-linear-content--thumbnail-images img {\n max-width: 100px;\n max-height: 100px;\n width: auto;\n height: auto;\n object-fit: contain;\n display: block;\n }\n .squisq-linear-content strong {\n font-weight: 700;\n }\n .squisq-linear-content em {\n font-style: italic;\n }\n .squisq-linear-content table {\n width: 100%;\n border-collapse: collapse;\n margin: 1em 0;\n font-size: 0.95em;\n }\n .squisq-linear-content thead th {\n background: var(--squisq-linear-primary);\n color: var(--squisq-linear-bg);\n font-family: var(--squisq-linear-title-font);\n font-weight: 600;\n padding: 10px 14px;\n text-align: left;\n }\n .squisq-linear-content tbody td {\n padding: 8px 14px;\n border-bottom: 1px solid color-mix(in srgb, var(--squisq-linear-muted) 30%, transparent);\n }\n .squisq-linear-content tbody tr:hover {\n background: color-mix(in srgb, var(--squisq-linear-primary) 8%, transparent);\n }\n `}</style>\n {resolvedDoc.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 {\n sanitizeHtmlNodes,\n sanitizeUrl,\n type HtmlPolicy,\n type HtmlElement,\n type HtmlNode,\n MarkdownBlockNode,\n MarkdownInlineNode,\n MarkdownListItem,\n MarkdownTableRow,\n MarkdownTableCell,\n} from '@bendyline/squisq/markdown';\nimport { useMediaUrl } from './hooks/MediaContext';\nimport { InlineVideoPlayer } from './InlineVideoPlayer.js';\nimport { InlineAudioPlayer } from './InlineAudioPlayer.js';\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 * Raw HTML policy. Defaults to `sanitize`, which removes unsafe tags,\n * event handlers, and executable URL schemes before rendering.\n */\n htmlPolicy?: HtmlPolicy;\n /**\n * Extra URL schemes to allow on links (e.g. a host app's internal\n * navigation scheme it intercepts on click). Executable schemes are\n * never allowed regardless. See {@link SanitizeUrlOptions}.\n */\n linkSchemes?: readonly string[];\n}\n\n/** Options threaded through the recursive renderers. */\ninterface RenderCtx {\n htmlPolicy: HtmlPolicy;\n linkSchemes?: readonly string[];\n}\n\nconst DEFAULT_CTX: RenderCtx = { htmlPolicy: 'sanitize' };\n\n// ── Inline Renderer ────────────────────────────────────────────────\n\n/** Render an array of inline nodes into React elements. */\nfunction renderInline(\n nodes: MarkdownInlineNode[],\n keyPrefix = '',\n ctx: RenderCtx = DEFAULT_CTX,\n): React.ReactNode[] {\n return nodes.map((node, i) => {\n const key = `${keyPrefix}i${i}`;\n switch (node.type) {\n case 'text': {\n // Soft breaks (newlines without two trailing spaces) become \\n in\n // text nodes. HTML collapses \\n to whitespace, which loses the visual\n // line break the author wanted, so render <br> for each newline.\n if (!node.value.includes('\\n')) {\n return <Fragment key={key}>{node.value}</Fragment>;\n }\n const parts = node.value.split('\\n');\n return (\n <Fragment key={key}>\n {parts.map((part, j) => (\n <Fragment key={j}>\n {j > 0 && <br />}\n {part}\n </Fragment>\n ))}\n </Fragment>\n );\n }\n\n case 'emphasis':\n return (\n <em key={key} className=\"squisq-md-em\">\n {renderInline(node.children, key, ctx)}\n </em>\n );\n\n case 'strong':\n return (\n <strong key={key} className=\"squisq-md-strong\">\n {renderInline(node.children, key, ctx)}\n </strong>\n );\n\n case 'delete':\n return (\n <del key={key} className=\"squisq-md-del\">\n {renderInline(node.children, key, ctx)}\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 const href = sanitizeUrl(node.url, 'link', { extraLinkSchemes: ctx.linkSchemes });\n if (!href) {\n return (\n <span key={key} className=\"squisq-md-link squisq-md-link--blocked\">\n {renderInline(node.children, key, ctx)}\n </span>\n );\n }\n return (\n <a\n key={key}\n className=\"squisq-md-link\"\n href={href}\n title={node.title ?? undefined}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {renderInline(node.children, key, ctx)}\n </a>\n );\n }\n\n case 'image':\n return (\n <MdImage key={key} src={node.url} alt={node.alt ?? ''} title={node.title ?? undefined} />\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 if (ctx.htmlPolicy === 'strip') return null;\n // Fast path: no <video>/<audio> in the subtree → use the original\n // rawHtml passthrough (preserves arbitrary HTML for custom embeds).\n if (\n ctx.htmlPolicy === 'trusted' &&\n !containsMediaTag(node.htmlChildren) &&\n !containsDangerousTag(node.htmlChildren) &&\n !hasDangerousRawHtml(node.rawHtml)\n ) {\n return (\n <span\n key={key}\n className=\"squisq-md-html-inline\"\n dangerouslySetInnerHTML={{ __html: node.rawHtml }}\n />\n );\n }\n // Otherwise reconstruct the subtree as React so <video>/<audio>\n // go through MediaContext-aware player components.\n return (\n <span key={key} className=\"squisq-md-html-inline\">\n {renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`)}\n </span>\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, ctx)}\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, ctx)}\n </span>\n );\n\n case 'mention':\n return (\n <span\n key={key}\n className=\"squisq-md-mention mention\"\n data-mention=\"true\"\n data-kind={node.targetKind}\n data-id={node.targetId}\n data-label={node.displayName}\n >\n @{node.displayName}\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(\n node: MarkdownBlockNode,\n key: string,\n ctx: RenderCtx = DEFAULT_CTX,\n): React.ReactNode {\n switch (node.type) {\n case 'paragraph':\n return (\n <p key={key} className=\"squisq-md-p\">\n {renderInline(node.children, key, ctx)}\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, ctx)}\n </Tag>\n );\n }\n\n case 'blockquote':\n return (\n <blockquote key={key} className=\"squisq-md-blockquote\">\n {renderBlocks(node.children, key, ctx)}\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}`, ctx))}\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}`, ctx))}\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, ctx);\n\n case 'htmlBlock':\n if (ctx.htmlPolicy === 'strip') return null;\n // Fast path: no <video>/<audio> → preserve the existing rawHtml\n // passthrough so arbitrary HTML embeds still survive verbatim.\n if (\n ctx.htmlPolicy === 'trusted' &&\n !containsMediaTag(node.htmlChildren) &&\n !containsDangerousTag(node.htmlChildren) &&\n !hasDangerousRawHtml(node.rawHtml)\n ) {\n return (\n <div\n key={key}\n className=\"squisq-md-html-block\"\n dangerouslySetInnerHTML={{ __html: node.rawHtml }}\n />\n );\n }\n // Otherwise reconstruct subtree as React so <video>/<audio>\n // route through the player components and resolve via MediaContext.\n return (\n <div key={key} className=\"squisq-md-html-block\">\n {renderHtmlNodes(resolveHtmlNodes(node.htmlChildren, ctx.htmlPolicy), `${key}h`)}\n </div>\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, ctx)}\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, ctx)}\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, ctx)}\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}`, ctx)}\n </dt>\n );\n }\n return (\n <dd key={`${key}dd${i}`} className=\"squisq-md-dd\">\n {renderBlocks(child.children, `${key}dd${i}`, ctx)}\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(\n item: MarkdownListItem,\n key: string,\n ctx: RenderCtx = DEFAULT_CTX,\n): 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, ctx)}\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 ctx: RenderCtx = DEFAULT_CTX,\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}`, ctx)}\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}`, ctx)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n )}\n </table>\n );\n}\n\n/** Render an array of block-level nodes. */\nfunction renderBlocks(\n nodes: MarkdownBlockNode[],\n keyPrefix = '',\n ctx: RenderCtx = DEFAULT_CTX,\n): React.ReactNode[] {\n return nodes.map((node, i) => renderBlock(node, `${keyPrefix}b${i}`, ctx));\n}\n\n// ── Image with MediaProvider resolution ───────────────────────────\n\n/** Renders an <img> that resolves its src through the MediaProvider when available. */\nfunction MdImage({ src, alt, title }: { src: string; alt: string; title?: string }) {\n const safeSrc = sanitizeUrl(src, 'media');\n const resolved = useMediaUrl(safeSrc ?? '', '.');\n if (!safeSrc) return null;\n return <img className=\"squisq-md-image\" src={resolved} alt={alt} title={title} />;\n}\n\n// ── Raw-HTML walker (intercepts <video>/<audio>) ─────────────────\n\n/** True when the htmlElement subtree contains a tag we want to swap\n * for a React component. Cheap recursive scan — lets us keep the\n * `dangerouslySetInnerHTML` fast path for everything else. */\nfunction resolveHtmlNodes(nodes: HtmlNode[], htmlPolicy: HtmlPolicy): HtmlNode[] {\n if (htmlPolicy === 'strip') return [];\n if (htmlPolicy === 'trusted') return nodes;\n return sanitizeHtmlNodes(nodes);\n}\n\nfunction containsMediaTag(nodes: HtmlNode[]): boolean {\n for (const node of nodes) {\n if (node.type !== 'htmlElement') continue;\n const tagName = node.tagName.toLowerCase();\n if (tagName === 'video' || tagName === 'audio') return true;\n if (containsMediaTag(node.children)) return true;\n }\n return false;\n}\n\n/**\n * Tags that can escape their container and affect the whole host\n * document — global styling, script execution, external/resource loads,\n * or framing. Markdown content is frequently untrusted (LLM output,\n * pasted snippets), and these tags have no business mutating the page\n * they're embedded in, so they are *always* dropped — even under the\n * `'trusted'` policy. \"Trusted\" means \"render this HTML's structure\n * verbatim,\" not \"let it restyle or script the host.\" A stray `<style>`\n * applies document-wide (CSS has no per-element scoping outside shadow\n * DOM / iframes), which is exactly how an embedded game's `<style>`\n * leaked onto the surrounding app chrome.\n */\nconst DANGEROUS_HTML_TAGS = new Set([\n 'base',\n 'embed',\n 'iframe',\n 'link',\n 'meta',\n 'object',\n 'script',\n 'style',\n 'title',\n]);\n\n/** True when the subtree contains any host-affecting tag (see\n * {@link DANGEROUS_HTML_TAGS}). Mirrors {@link containsMediaTag}: keeps\n * such content off the verbatim `dangerouslySetInnerHTML` fast path so\n * it routes through the React reconstruction, which drops the tag. */\nfunction containsDangerousTag(nodes: HtmlNode[]): boolean {\n for (const node of nodes) {\n if (node.type !== 'htmlElement') continue;\n if (DANGEROUS_HTML_TAGS.has(node.tagName.toLowerCase())) return true;\n if (containsDangerousTag(node.children)) return true;\n }\n return false;\n}\n\n/**\n * Raw-string backstop for {@link DANGEROUS_HTML_TAGS}. The structural\n * {@link containsDangerousTag} check covers the normal case, but a block\n * parsed with `parseHtml: false` carries an empty `htmlChildren` while\n * `rawHtml` still holds the markup — so the verbatim fast path scans the\n * raw string too, guaranteeing a `<style>`/`<script>` can never be\n * injected into the host document by that path no matter how the node\n * was produced. The `\\b` keeps `<styled-thing>` from matching `<style>`.\n */\nconst DANGEROUS_RAW_HTML_RE =\n /<\\s*\\/?\\s*(?:base|embed|iframe|link|meta|object|script|style|title)\\b/i;\n\nfunction hasDangerousRawHtml(rawHtml: string): boolean {\n return DANGEROUS_RAW_HTML_RE.test(rawHtml);\n}\n\n/** A pragmatic shortlist of HTML attributes the raw-HTML walker\n * passes through to React when reconstructing a non-media element.\n * Anything outside this list is silently dropped — the media-tag\n * fast path means most authors will never hit this code, so we\n * keep the surface narrow to avoid React warnings about unknown\n * attributes. */\nconst PASSTHROUGH_ATTRS: Record<string, string> = {\n // common\n class: 'className',\n id: 'id',\n title: 'title',\n style: 'style',\n // media-adjacent (used when video/audio appear inside other wrappers)\n width: 'width',\n height: 'height',\n src: 'src',\n alt: 'alt',\n loading: 'loading',\n decoding: 'decoding',\n // anchor\n href: 'href',\n target: 'target',\n rel: 'rel',\n};\n\nfunction reactPropsFromAttrs(attrs: Record<string, string>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(attrs)) {\n const propName = PASSTHROUGH_ATTRS[name];\n if (!propName) continue;\n if (propName === 'style') {\n // We can't safely parse arbitrary `style=\"...\"` strings without\n // an HTML parser; preserve as a data attribute so authors can\n // see it survived round-trip without crashing React.\n out['data-style'] = value;\n continue;\n }\n out[propName] = value;\n }\n return out;\n}\n\nfunction renderHtmlElement(el: HtmlElement, key: string): React.ReactNode {\n const tagName = el.tagName.toLowerCase();\n // Final safety net: never reconstruct a host-affecting element (e.g. a\n // <style> that would leak globally), whatever the policy. The fast path\n // is gated by containsDangerousTag, so trusted content carrying these\n // tags lands here — drop the tag and keep the rest of the subtree.\n if (DANGEROUS_HTML_TAGS.has(tagName)) return null;\n if (tagName === 'video') {\n return (\n <InlineVideoPlayer\n key={key}\n src={el.attributes.src ?? ''}\n width={el.attributes.width}\n height={el.attributes.height}\n poster={el.attributes.poster}\n // The `controls` attribute is a boolean — present means true,\n // even if its value is an empty string.\n controls={'controls' in el.attributes}\n preload={\n el.attributes.preload === 'none' ||\n el.attributes.preload === 'metadata' ||\n el.attributes.preload === 'auto'\n ? el.attributes.preload\n : undefined\n }\n />\n );\n }\n if (tagName === 'audio') {\n return (\n <InlineAudioPlayer\n key={key}\n src={el.attributes.src ?? ''}\n controls={'controls' in el.attributes}\n preload={\n el.attributes.preload === 'none' ||\n el.attributes.preload === 'metadata' ||\n el.attributes.preload === 'auto'\n ? el.attributes.preload\n : undefined\n }\n />\n );\n }\n\n const Tag = tagName as keyof JSX.IntrinsicElements;\n const props = reactPropsFromAttrs(el.attributes);\n if (el.selfClosing) {\n return <Tag key={key} {...props} />;\n }\n return (\n <Tag key={key} {...props}>\n {renderHtmlNodes(el.children, `${key}c`)}\n </Tag>\n );\n}\n\nfunction renderHtmlNodes(nodes: HtmlNode[], keyPrefix: string): React.ReactNode[] {\n return nodes.map((node, i) => {\n const key = `${keyPrefix}${i}`;\n switch (node.type) {\n case 'htmlElement':\n return renderHtmlElement(node, key);\n case 'htmlText':\n return <Fragment key={key}>{node.value}</Fragment>;\n case 'htmlComment':\n // Comments don't render in React; ignore.\n return null;\n default:\n return null;\n }\n });\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({\n nodes,\n className,\n htmlPolicy = 'sanitize',\n linkSchemes,\n}: MarkdownRendererProps) {\n if (!nodes || nodes.length === 0) return null;\n\n return (\n <div className={`squisq-md ${className || ''}`}>\n {renderBlocks(nodes, '', { htmlPolicy, linkSchemes })}\n </div>\n );\n}\n","/**\n * InlineVideoPlayer\n *\n * Small inline HTML5 video player for `<video>` tags embedded directly in\n * a markdown document — e.g. a recording dropped into the doc by the\n * recorder. Resolves the `src` through `MediaContext` (same path\n * `VideoLayer` uses) so a container-backed `audio/foo.webm` or\n * `video/foo.mp4` resolves to a blob URL instead of 404-ing on the\n * filesystem path.\n *\n * Deliberately thin: native browser controls, intrinsic aspect ratio,\n * no autoplay. The MarkdownRenderer swaps this in for a raw `<video>`\n * htmlElement; the editor-react Tiptap video node uses the same\n * component for its NodeView.\n */\nimport { useMediaUrl } from './hooks/MediaContext.js';\n\nexport interface InlineVideoPlayerProps {\n /** Source path — resolved through MediaContext when relative. */\n src: string;\n /** Base path used when no MediaProvider is in context. */\n basePath?: string;\n /** Optional explicit width (pixels or CSS length). */\n width?: number | string;\n /** Optional explicit height (pixels or CSS length). */\n height?: number | string;\n /** Optional poster image src — also resolved through MediaContext. */\n poster?: string;\n /** Whether to show native controls. Defaults to true. */\n controls?: boolean;\n /** `preload` attribute passthrough. Defaults to `'metadata'`. */\n preload?: 'none' | 'metadata' | 'auto';\n /** Extra className on the wrapper. */\n className?: string;\n}\n\nexport function InlineVideoPlayer({\n src,\n basePath = '',\n width,\n height,\n poster,\n controls = true,\n preload = 'metadata',\n className,\n}: InlineVideoPlayerProps) {\n const resolvedSrc = useMediaUrl(src, basePath);\n // Always call the hook (Rules of Hooks); only use the result if a poster\n // was actually requested. Empty input → fallback path which we discard.\n const resolvedPoster = useMediaUrl(poster ?? '', basePath);\n const posterUrl = poster ? resolvedPoster : undefined;\n\n if (!resolvedSrc) return null;\n\n return (\n <span className={`squisq-inline-video-player ${className ?? ''}`.trim()}>\n <video\n src={resolvedSrc}\n poster={posterUrl}\n controls={controls}\n preload={preload}\n width={width}\n height={height}\n playsInline\n />\n </span>\n );\n}\n\nexport default InlineVideoPlayer;\n","/**\n * InlineAudioPlayer\n *\n * Small inline HTML5 audio player for `<audio>` tags embedded directly in\n * a markdown document — e.g. a narration recording the user wants to\n * audition in the editor. Resolves the `src` through `MediaContext` so a\n * container-backed `audio/foo.webm` path returns a playable blob URL.\n *\n * Deliberately thin: native browser controls, no autoplay. The\n * MarkdownRenderer swaps this in for raw `<audio>` htmlElements; the\n * editor-react Tiptap audio node reuses it for its NodeView.\n */\nimport { useMediaUrl } from './hooks/MediaContext.js';\n\nexport interface InlineAudioPlayerProps {\n /** Source path — resolved through MediaContext when relative. */\n src: string;\n /** Base path used when no MediaProvider is in context. */\n basePath?: string;\n /** Whether to show native controls. Defaults to true. */\n controls?: boolean;\n /** `preload` attribute passthrough. Defaults to `'metadata'`. */\n preload?: 'none' | 'metadata' | 'auto';\n /** Extra className on the wrapper. */\n className?: string;\n}\n\nexport function InlineAudioPlayer({\n src,\n basePath = '',\n controls = true,\n preload = 'metadata',\n className,\n}: InlineAudioPlayerProps) {\n const resolvedSrc = useMediaUrl(src, basePath);\n\n if (!resolvedSrc) return null;\n\n return (\n <span className={`squisq-inline-audio-player ${className ?? ''}`.trim()}>\n <audio src={resolvedSrc} controls={controls} preload={preload} />\n </span>\n );\n}\n\nexport default InlineAudioPlayer;\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 mode cycle */}\n {state.hasCaptions && (\n <button\n className={`bottom-ctrl-btn ${state.captionMode !== 'off' ? 'bottom-ctrl-btn--active' : ''}`}\n onClick={() => actions.cycleCaptionMode()}\n title={\n state.captionMode === 'off'\n ? 'Captions: Off'\n : state.captionMode === 'standard'\n ? 'Captions: Standard'\n : 'Captions: Social'\n }\n aria-label=\"Cycle caption style\"\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 mode cycle */}\n {state.hasCaptions && (\n <button\n className={`sidebar-ctrl-btn ${state.captionMode !== 'off' ? 'sidebar-ctrl-btn--active' : ''}`}\n onClick={() => actions.cycleCaptionMode()}\n title={\n state.captionMode === 'off'\n ? 'Captions: Off'\n : state.captionMode === 'standard'\n ? 'Captions: Standard'\n : 'Captions: Social'\n }\n aria-label=\"Cycle caption style\"\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, Theme } from '@bendyline/squisq/schemas';\nimport type { AudioController } from './hooks/AudioController';\nimport { DocPlayer } from './DocPlayer';\nimport { DocControlsSidebar } from './DocControlsSidebar';\nimport type { PlaybackState, PlaybackActions } from './types';\n\ninterface DocPlayerWithSidebarProps {\n /** The Doc to play */\n doc: Doc;\n /** Base path for resolving media URLs (default: `'.'`) */\n basePath?: string;\n autoPlay?: boolean;\n onEnded?: () => void;\n onTimeUpdate?: (time: number) => void;\n /** Optional audio controller (if not provided, uses default HTML5 audio) */\n audioController?: AudioController;\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 * Theme for rendering. Forwarded to the inner DocPlayer so the sidebar\n * (portrait) layout matches the default (landscape) layout — without it the\n * inner player falls back to DEFAULT_THEME, whose dark text is unreadable\n * over a hero cover image.\n */\n theme?: Theme;\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 captionMode: 'off',\n currentSegmentIndex: 0,\n currentSegmentName: null,\n currentBlock: null,\n};\n\nexport function DocPlayerWithSidebar({\n doc,\n basePath,\n autoPlay = false,\n onEnded,\n onTimeUpdate,\n audioController,\n muted,\n captionsEnabled,\n isFullscreen,\n onFullscreenToggle,\n forceViewport,\n onPlayingChange,\n theme,\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 doc={doc}\n theme={theme}\n basePath={basePath}\n autoPlay={autoPlay}\n onEnded={onEnded}\n onTimeUpdate={onTimeUpdate}\n audioController={audioController}\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","/**\n * Derive the CSS custom-property bag for `<JsonView>` from a Theme + Surface.\n * Mirrors the pattern in LinearDocView so JsonView re-themes consistently\n * with the rest of Squisq.\n */\n\nimport { useMemo } from 'react';\nimport { type SurfaceScheme, type Theme } from '@bendyline/squisq/schemas';\nimport { buildJsonFormTokens, resolveJsonFormTheme } from '@bendyline/squisq/jsonForm';\nimport { useAutoSurface } from '../hooks/useAutoSurface';\n\nexport interface JsonViewTokens {\n /** Inline style object to spread onto the root element. */\n style: React.CSSProperties;\n /** The effective theme (after surface application). */\n theme: Theme;\n}\n\nexport function useJsonViewTokens(\n theme: Theme | undefined,\n surface: SurfaceScheme | 'auto' | undefined,\n): JsonViewTokens {\n const auto = useAutoSurface(surface === 'auto');\n const effectiveSurface = surface === 'auto' ? auto : (surface ?? undefined);\n\n return useMemo(() => {\n const style = buildJsonFormTokens(theme, effectiveSurface, {\n prefix: '--squisq-json',\n }) as unknown as React.CSSProperties;\n return { style, theme: resolveJsonFormTheme(theme, effectiveSurface) };\n }, [theme, effectiveSurface]);\n}\n","/**\n * Recursive dispatcher: looks at a schema node + value, chooses a\n * `ControlKind`, evaluates `hidden` rules, and renders the matching\n * read-only viewer.\n */\n\nimport {\n chooseControl,\n resolveFlag,\n resolveRef,\n type SquisqAnnotatedSchema,\n} from '@bendyline/squisq/jsonForm';\nimport { VIEWERS, type ViewerProps } from './viewers';\n\nexport interface RenderNodeProps {\n value: unknown;\n schema: SquisqAnnotatedSchema;\n rootSchema: SquisqAnnotatedSchema;\n rootData: unknown;\n pointer: string;\n density: 'comfortable' | 'compact';\n /**\n * When a parent group already wrote its own card chrome (e.g. a\n * card-stack item), suppress the nested group's outer title so the\n * UI doesn't double up on labels.\n */\n suppressTopGroupTitle?: boolean;\n}\n\nexport function RenderNode(props: RenderNodeProps): React.ReactElement | null {\n const resolved = resolveRef(props.schema, props.rootSchema) ?? props.schema;\n\n if (resolveFlag(resolved.squisq?.hidden, props.rootData)) return null;\n\n const kind = chooseControl(resolved);\n const Viewer = VIEWERS[kind];\n const viewerProps: ViewerProps = {\n value: props.value,\n schema: resolved,\n rootSchema: props.rootSchema,\n rootData: props.rootData,\n pointer: props.pointer,\n density: props.density,\n };\n if (kind === 'group' || kind === 'card') {\n // GroupViewer accepts an extra prop that RenderNode forwards.\n const Group = Viewer as React.ComponentType<ViewerProps & { suppressTitle?: boolean }>;\n return <Group {...viewerProps} suppressTitle={props.suppressTopGroupTitle} />;\n }\n return <Viewer {...viewerProps} />;\n}\n","/**\n * Read-only viewer renderers for `<JsonView>`. Each viewer is a small\n * pure function that takes a value + schema slice and renders inline.\n * Group / card-stack viewers recurse into `RenderNode`.\n */\n\nimport { Fragment, useMemo } from 'react';\nimport {\n arrayItemKind,\n type ControlKind,\n type SquisqAnnotatedSchema,\n} from '@bendyline/squisq/jsonForm';\nimport { parseMarkdown } from '@bendyline/squisq/markdown';\nimport { MarkdownRenderer } from '../MarkdownRenderer';\nimport { RenderNode } from './RenderNode';\n\nexport interface ViewerProps {\n value: unknown;\n schema: SquisqAnnotatedSchema;\n rootSchema: SquisqAnnotatedSchema;\n rootData: unknown;\n pointer: string;\n density: 'comfortable' | 'compact';\n}\n\n// ── Primitive viewers ──────────────────────────────────────────────\n\nexport function TextViewer({ value }: ViewerProps) {\n if (value === undefined || value === null || value === '') {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n return <span className=\"squisq-jv-value\">{String(value)}</span>;\n}\n\nexport function MultilineViewer({ value }: ViewerProps) {\n if (value === undefined || value === null || value === '') {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n return <span className=\"squisq-jv-value squisq-jv-value--multiline\">{String(value)}</span>;\n}\n\nexport function RichTextViewer({ value }: ViewerProps) {\n const nodes = useMemo(() => {\n if (typeof value !== 'string' || value === '') return null;\n try {\n const doc = parseMarkdown(value);\n return doc.children;\n } catch {\n return null;\n }\n }, [value]);\n if (!nodes) return <span className=\"squisq-jv-empty\">—</span>;\n return (\n <div className=\"squisq-jv-richtext\">\n <MarkdownRenderer nodes={nodes} />\n </div>\n );\n}\n\nexport function NumberViewer({ value }: ViewerProps) {\n if (value === undefined || value === null) {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n return <span className=\"squisq-jv-value\">{String(value)}</span>;\n}\n\nexport function BooleanViewer({ value }: ViewerProps) {\n const on = Boolean(value);\n return (\n <span className={`squisq-jv-toggle squisq-jv-toggle--${on ? 'on' : 'off'}`}>\n {on ? 'On' : 'Off'}\n </span>\n );\n}\n\nexport function EnumViewer({ value, schema }: ViewerProps) {\n if (value === undefined || value === null || value === '') {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n const labels = schema.squisq?.enumLabels;\n const display = labels && typeof value === 'string' ? (labels[value] ?? value) : String(value);\n return <span className=\"squisq-jv-value\">{display}</span>;\n}\n\nexport function ColorViewer({ value }: ViewerProps) {\n if (typeof value !== 'string' || value === '') {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n return (\n <span className=\"squisq-jv-color\">\n <span className=\"squisq-jv-color__swatch\" style={{ background: value }} aria-hidden />\n <span className=\"squisq-jv-color__hex\">{value}</span>\n </span>\n );\n}\n\nexport function DateViewer({ value, schema }: ViewerProps) {\n if (typeof value !== 'string' || value === '') {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n const fmt = schema.format;\n let display = value;\n try {\n const date = new Date(value);\n if (!Number.isNaN(date.getTime())) {\n if (fmt === 'time') {\n display = date.toLocaleTimeString();\n } else if (fmt === 'date') {\n display = date.toLocaleDateString();\n } else {\n display = date.toLocaleString();\n }\n }\n } catch {\n /* fall through to raw value */\n }\n return <span className=\"squisq-jv-value\">{display}</span>;\n}\n\n// ── Composite viewers ─────────────────────────────────────────────\n\nexport function ChipBinViewer({ value, schema }: ViewerProps) {\n if (!Array.isArray(value) || value.length === 0) {\n return <span className=\"squisq-jv-empty\">—</span>;\n }\n const itemSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;\n const labels = itemSchema?.squisq?.enumLabels;\n return (\n <div className=\"squisq-jv-chip-bin\">\n {value.map((item, i) => {\n const label =\n labels && typeof item === 'string' ? (labels[item] ?? String(item)) : String(item);\n return (\n <span key={i} className=\"squisq-jv-chip\">\n {label}\n </span>\n );\n })}\n </div>\n );\n}\n\nexport function CardStackViewer(props: ViewerProps) {\n const { value, schema, rootSchema, rootData, pointer, density } = props;\n if (!Array.isArray(value) || value.length === 0) {\n return <span className=\"squisq-jv-empty\">No items</span>;\n }\n const itemSchema = (Array.isArray(schema.items) ? schema.items[0] : schema.items) ?? {};\n const itemLabel = itemSchema.squisq?.itemLabel;\n return (\n <div className=\"squisq-jv-card-stack\">\n {value.map((item, i) => {\n const title = resolveItemTitle(itemLabel, item, i);\n return (\n <div key={i} className=\"squisq-jv-card\">\n {title ? <h4 className=\"squisq-jv-card__title\">{title}</h4> : null}\n <RenderNode\n value={item}\n schema={itemSchema}\n rootSchema={rootSchema}\n rootData={rootData}\n pointer={`${pointer}/${i}`}\n density={density}\n suppressTopGroupTitle\n />\n </div>\n );\n })}\n </div>\n );\n}\n\nfunction resolveItemTitle(\n spec: string | { fromField: string } | undefined,\n item: unknown,\n index: number,\n): string {\n if (!spec) return `Item ${index + 1}`;\n if (typeof spec === 'string') return spec;\n if (item && typeof item === 'object') {\n const v = (item as Record<string, unknown>)[spec.fromField];\n if (typeof v === 'string' && v !== '') return v;\n if (typeof v === 'number') return String(v);\n }\n return `Item ${index + 1}`;\n}\n\nexport function GroupViewer(props: ViewerProps & { suppressTitle?: boolean }) {\n const { value, schema, rootSchema, rootData, pointer, density, suppressTitle } = props;\n const title = !suppressTitle ? (schema.squisq?.label ?? schema.title) : undefined;\n const help = schema.squisq?.help ?? schema.description;\n const obj = (value && typeof value === 'object' ? (value as Record<string, unknown>) : {}) ?? {};\n const propEntries = Object.entries(schema.properties ?? {});\n\n return (\n <section className=\"squisq-jv-group\">\n {title ? <h3 className=\"squisq-jv-group__title\">{title}</h3> : null}\n {help ? <p className=\"squisq-jv-group__help\">{help}</p> : null}\n {propEntries.map(([key, propSchema]) => (\n <Fragment key={key}>\n <RowOrSection\n label={propSchema.squisq?.label ?? propSchema.title ?? key}\n help={propSchema.squisq?.help ?? propSchema.description}\n kindHint={propSchema}\n >\n <RenderNode\n value={obj[key]}\n schema={propSchema}\n rootSchema={rootSchema}\n rootData={rootData}\n pointer={`${pointer}/${key}`}\n density={density}\n suppressTopGroupTitle\n />\n </RowOrSection>\n </Fragment>\n ))}\n </section>\n );\n}\n\n/**\n * Decide whether to use the inline label/value Row layout, or to drop\n * the row chrome entirely (for nested groups, card-stacks, richtext —\n * which present their own headings).\n */\nfunction RowOrSection({\n label,\n help,\n kindHint,\n children,\n}: {\n label: string;\n help?: string;\n kindHint: SquisqAnnotatedSchema;\n children: React.ReactNode;\n}) {\n const composite = isCompositeKind(kindHint);\n if (composite) {\n return <>{children}</>;\n }\n return (\n <div className=\"squisq-jv-row\">\n <div className=\"squisq-jv-label\" title={help}>\n {label}\n </div>\n <div>{children}</div>\n </div>\n );\n}\n\nfunction isCompositeKind(schema: SquisqAnnotatedSchema): boolean {\n const t = schema.type;\n const arrayLike = t === 'array' || (Array.isArray(t) && t.includes('array'));\n if (arrayLike) {\n return arrayItemKind(schema) === 'object';\n }\n const objectLike = t === 'object' || (Array.isArray(t) && t.includes('object'));\n if (objectLike) return true;\n if (schema.oneOf || schema.anyOf) return true;\n if (schema.format === 'markdown' || schema.squisq?.control === 'richtext') return true;\n return false;\n}\n\nexport function TabsViewer(props: ViewerProps) {\n const { value, schema, rootSchema, rootData, pointer, density } = props;\n const branches = (schema.oneOf ?? schema.anyOf ?? []).slice();\n const matchedIndex = pickMatchingBranch(branches as SquisqAnnotatedSchema[], value);\n const branch = branches[matchedIndex] as SquisqAnnotatedSchema | undefined;\n if (!branch) {\n return <TextViewer {...props} />;\n }\n return (\n <div className=\"squisq-jv-tabs\">\n <span className=\"squisq-jv-tabs__discriminator\">\n {branch.squisq?.label ?? branch.title ?? `Option ${matchedIndex + 1}`}\n </span>\n <RenderNode\n value={value}\n schema={branch}\n rootSchema={rootSchema}\n rootData={rootData}\n pointer={pointer}\n density={density}\n />\n </div>\n );\n}\n\nfunction pickMatchingBranch(branches: SquisqAnnotatedSchema[], value: unknown): number {\n for (let i = 0; i < branches.length; i++) {\n if (matchesShape(branches[i], value)) return i;\n }\n return 0;\n}\n\nfunction matchesShape(schema: SquisqAnnotatedSchema, value: unknown): boolean {\n const t = schema.type;\n const target = Array.isArray(t) ? t.find((x) => x !== 'null') : t;\n if (!target) return true;\n switch (target) {\n case 'string':\n return typeof value === 'string';\n case 'number':\n case 'integer':\n return typeof value === 'number';\n case 'boolean':\n return typeof value === 'boolean';\n case 'null':\n return value === null;\n case 'array':\n return Array.isArray(value);\n case 'object':\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n default:\n return false;\n }\n}\n\n// ── Registry ──────────────────────────────────────────────────────\n\n// eslint-disable-next-line react-refresh/only-export-components\nexport const VIEWERS: Record<ControlKind, React.ComponentType<ViewerProps>> = {\n text: TextViewer,\n multiline: MultilineViewer,\n richtext: RichTextViewer,\n color: ColorViewer,\n date: DateViewer,\n time: DateViewer,\n datetime: DateViewer,\n slider: NumberViewer,\n stepper: NumberViewer,\n segmented: EnumViewer,\n radio: EnumViewer,\n combobox: EnumViewer,\n toggle: BooleanViewer,\n checkbox: BooleanViewer,\n card: GroupViewer,\n group: GroupViewer,\n 'card-stack': CardStackViewer,\n 'chip-bin': ChipBinViewer,\n tabs: TabsViewer,\n};\n","/**\n * <JsonView>\n *\n * Read-only renderer for any JSON value bound to a JSON Schema (with\n * optional Squisq UI hints). Designed to look like a polished settings\n * summary or CRM record — not a disabled form. Themable via the\n * standard Theme + SurfaceScheme props used elsewhere in Squisq.\n */\n\nimport type { SquisqAnnotatedSchema } from '@bendyline/squisq/jsonForm';\nimport type { SurfaceScheme, Theme } from '@bendyline/squisq/schemas';\nimport { useJsonViewTokens } from './useJsonViewTokens';\nimport { RenderNode } from './RenderNode';\n\nexport interface JsonViewProps {\n /** Schema describing the value's shape (with optional `squisq` UI hints). */\n schema: SquisqAnnotatedSchema;\n /** The value to display. */\n value: unknown;\n /** Optional theme. Defaults to `DEFAULT_THEME`. */\n theme?: Theme;\n /** Light/dark surface override; `'auto'` follows `prefers-color-scheme`. */\n surface?: SurfaceScheme | 'auto';\n /** Padding/gap density. Default: 'comfortable'. */\n density?: 'comfortable' | 'compact';\n /** Optional CSS class for the outer container. */\n className?: string;\n}\n\nexport function JsonView(props: JsonViewProps) {\n const { schema, value, theme, surface, density = 'comfortable', className } = props;\n const { style } = useJsonViewTokens(theme, surface);\n\n const cls =\n 'squisq-json-view' +\n (density === 'compact' ? ' squisq-json-view--compact' : '') +\n (className ? ` ${className}` : '');\n\n return (\n <div className={cls} style={style}>\n <RenderNode\n value={value}\n schema={schema}\n rootSchema={schema}\n rootData={value}\n pointer=\"\"\n density={density}\n />\n </div>\n );\n}\n"],"mappings":";AAwBA,SAAS,YAAAA,WAAU,UAAAC,SAAQ,YAAAC,WAAU,aAAAC,YAAW,eAAAC,cAAa,WAAAC,gBAAe;AAE5E;AAAA,EACE,mBAAAC;AAAA,EACA,oBAAAC;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;AChBP,SAAS,aAAAC,YAAW,cAAc;;;ACAlC,SAAS,eAAe,YAAY,UAAU,WAAW,eAAe;AAMjE,IAAM,eAAe,cAAoC,IAAI;AAM7D,SAAS,mBAAyC;AACvD,SAAO,WAAW,YAAY;AAChC;AAWO,SAAS,YAAY,cAAsB,UAA0B;AAC1E,QAAM,WAAW,iBAAiB;AAMlC,QAAM,WAAW,OAAO,iBAAiB,WAAW,eAAe;AAGnE,QAAM,aACJ,CAAC,YACD,SAAS,WAAW,MAAM,KAC1B,SAAS,WAAW,GAAG,KACvB,SAAS,WAAW,OAAO,KAC3B,SAAS,WAAW,OAAO;AAG7B,QAAM,WAAW;AAAA,IACf,MAAO,aAAa,WAAW,GAAG,QAAQ,IAAI,QAAQ;AAAA,IACtD,CAAC,YAAY,UAAU,QAAQ;AAAA,EACjC;AAGA,QAAM,gBAAgB,CAAC,cAAc,CAAC,CAAC;AAEvC,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,QAAQ;AAEvC,YAAU,MAAM;AACd,QAAI,CAAC,eAAe;AAClB,aAAO,QAAQ;AACf;AAAA,IACF;AAEA,QAAI,YAAY;AAChB,aAAU,WAAW,QAAQ,EAAE,KAAK,CAAC,aAAa;AAChD,UAAI,CAAC,UAAW,QAAO,QAAQ;AAAA,IACjC,CAAC;AAED,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,eAAe,UAAU,UAAU,QAAQ,CAAC;AAIhD,SAAO,gBAAgB,MAAM;AAC/B;;;ACvEA,SAAS,WAAAC,gBAAe;AAUjB,SAAS,iBACd,UACA,aACyB;AACzB,QAAM,YAAYA,SAAQ,MAAM;AAC9B,UAAM,MAAM,oBAAI,IAAY;AAC5B,eAAW,KAAK,UAAU;AACxB,UAAI,eAAe,EAAE,iBAAiB,cAAc,EAAE,YAAa,KAAI,IAAI,EAAE,EAAE;AAAA,IACjF;AACA,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,WAAW,CAAC;AAC1B,SAAO,EAAE,aAAa,UAAU,UAAU;AAC5C;;;AFKQ;AAtBR,IAAM,QAAQ;AAUP,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AACf,GAAwB;AACtB,QAAM,EAAE,aAAa,UAAU,IAAI,iBAAiB,UAAU,WAAW;AACzE,MAAI,YAAY,WAAW,EAAG,QAAO;AACrC,SACE,oBAAC,SAAI,WAAU,2BAA0B,eAAW,MACjD,sBAAY,IAAI,CAAC,SAChB;AAAA,IAAC;AAAA;AAAA,MAEC;AAAA,MACA,QAAQ,UAAU,IAAI,KAAK,EAAE;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IANK,KAAK;AAAA,EAOZ,CACD,GACH;AAEJ;AAWA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,MAAM,OAAgC,IAAI;AAChD,QAAM,MAAM,YAAY,KAAK,KAAK,QAAQ;AAE1C,EAAAC,WAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,QAAI,CAAC,QAAQ;AACX,UAAI,CAAC,GAAG,OAAQ,IAAG,MAAM;AACzB;AAAA,IACF;AACA,UAAM,SAAS,KAAK,IAAI,GAAG,KAAK,YAAY,cAAc,KAAK,cAAc;AAC7E,QAAI,cAAc,KAAK,IAAI,GAAG,cAAc,MAAM,IAAI,OAAO;AAC3D,UAAI;AACF,WAAG,cAAc;AAAA,MACnB,QAAQ;AAAA,MAER;AAAA,IACF;AACA,QAAI,aAAa,CAAC,YAAY;AAC5B,YAAM,IAAI,GAAG,KAAK;AAClB,UAAI,EAAG,GAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACzB,OAAO;AACL,SAAG,MAAM;AAAA,IACX;AAAA,EACF,GAAG,CAAC,QAAQ,aAAa,WAAW,YAAY,KAAK,UAAU,KAAK,aAAa,CAAC;AAElF,QAAM,UAAU,KAAK,SAAS;AAC9B,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,gBAAgB,KAAK;AAAA,IACrB,kBAAkB,KAAK;AAAA,IACvB,gBAAgB,KAAK;AAAA,IACrB,kBAAkB,KAAK;AAAA,EACzB;AAEA,MAAI,SAAS;AAEX,WACE;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,OAAK;AAAA,QACL,aAAW;AAAA,QACX,OAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,WAAW;AAAA,UACX,QAAQ;AAAA,UACR,eAAe;AAAA,QACjB;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SACE,oBAAC,WAAO,GAAG,QAAQ,OAAO,YAAY,OAAO,EAAE,UAAU,YAAY,OAAO,GAAG,QAAQ,EAAE,GAAG;AAEhG;;;ADpGA,SAAS,gBAAAC,qBAAoB;;;AIzB7B,SAAS,iCAAiC;;;ACA1C,SAAS,6BAA6B;;;ACFtC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACHA,SAAS,aAAa,OAAwB,WAA2B;AAC9E,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AACA,MAAI,MAAM,SAAS,GAAG,GAAG;AACvB,UAAM,UAAU,WAAW,KAAK;AAChC,WAAQ,UAAU,MAAO;AAAA,EAC3B;AACA,SAAO,WAAW,KAAK;AACzB;AAKO,SAAS,gBACd,QACA,OACA,QAC0B;AAC1B,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,SAAS,EAAE;AAAA,IACzC,KAAK;AACH,aAAO,EAAE,GAAG,CAAC,OAAO,GAAG,EAAE;AAAA,IAC3B,KAAK;AACH,aAAO,EAAE,GAAG,GAAG,GAAG,CAAC,OAAO;AAAA,IAC5B,KAAK;AACH,aAAO,EAAE,GAAG,CAAC,OAAO,GAAG,CAAC,OAAO;AAAA,IACjC,KAAK;AAAA,IACL;AACE,aAAO,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,EACxB;AACF;;;AFgCY,gBAAAC,YAAA;AAjDL,SAAS,WAAW,EAAE,OAAO,UAAU,UAAU,UAAU,GAAoB;AACpF,QAAM,EAAE,SAAS,UAAU,UAAU,IAAI;AAGzC,QAAM,IAAI,aAAa,SAAS,GAAG,SAAS,KAAK;AACjD,QAAM,IAAI,aAAa,SAAS,GAAG,SAAS,MAAM;AAClD,QAAM,QAAQ,SAAS,QAAQ,aAAa,SAAS,OAAO,SAAS,KAAK,IAAI,SAAS;AACvF,QAAM,SAAS,SAAS,SAAS,aAAa,SAAS,QAAQ,SAAS,MAAM,IAAI,SAAS;AAG3F,QAAM,SAAS,gBAAgB,SAAS,QAAQ,OAAO,MAAM;AAC7D,QAAM,SAAS,IAAI,OAAO;AAC1B,QAAM,SAAS,IAAI,OAAO;AAG1B,QAAM,MAAM,YAAY,QAAQ,KAAK,QAAQ;AAG7C,QAAM,YAAY,kBAAkB,WAAW,SAAS;AAIxD,QAAM,SAAS,sBAAsB,QAAQ,WAAW,QAAQ,IAAI;AAGpE,QAAM,sBAAsB,uBAAuB,QAAQ,GAAG;AAC9D,QAAM,UAAU,QAAQ,QAAQ;AAKhC,QAAM,gBAAgB,aAAa,wBAAwB,IAAI,UAAU,IAAI;AAI7E,MAAI,WAAW,iBAAiB,WAAW;AACzC,UAAM,SAAS,gBAAgB,SAAS;AACxC,UAAM,UAAU,kBAAkB,QAAQ,SAAS;AAEnD,WACE,gBAAAA,KAAC,OAAE,WAAU,kCAAiC,iBAAe,MAAM,IACjE,0BAAAA,KAAC,mBAAc,GAAG,QAAQ,GAAG,QAAQ,OAAc,QACjD,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,UACL,OAAO,GAAG,KAAK;AAAA,UACf,QAAQ,GAAG,MAAM;AAAA,UACjB,UAAU;AAAA,QACZ;AAAA,QAEA,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,KAAK,QAAQ,OAAO;AAAA,YACpB,WAAW,QAAQ;AAAA,YACnB,OAAO;AAAA,cACL,OAAO,GAAG,KAAK;AAAA,cACf,QAAQ,GAAG,MAAM;AAAA,cACjB,WAAW;AAAA,cACX,gBAAgB;AAAA,cAChB,SAAS;AAAA,cACT,eAAe;AAAA,cACf,iBAAiB;AAAA,cACjB,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,cAC3B,GAAG,QAAQ;AAAA,YACb;AAAA;AAAA,QACF;AAAA;AAAA,IACF,GACF,GACF;AAAA,EAEJ;AAIA,MAAI,SAAS;AACX,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,kCAAkC,UAAU,SAAS;AAAA,QAChE,OAAO,UAAU;AAAA,QACjB,iBAAe,MAAM;AAAA,QAErB,0BAAAA,KAAC,mBAAc,GAAG,QAAQ,GAAG,QAAQ,OAAc,QACjD,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,KAAK,QAAQ,OAAO;AAAA,YACpB,OAAO;AAAA,cACL,OAAO,GAAG,KAAK;AAAA,cACf,QAAQ,GAAG,MAAM;AAAA,cACjB,WAAW;AAAA,cACX,gBAAgB;AAAA,cAChB,SAAS;AAAA,cACT,eAAe;AAAA,cACf,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA;AAAA;AAAA,cAG3B,GAAI,QAAQ,QAAQ,QAAQ,OAAO,IAAI,EAAE,WAAW,cAAc,IAAI,CAAC;AAAA,YACzE;AAAA;AAAA,QACF,GACF;AAAA;AAAA,IACF;AAAA,EAEJ;AAGA,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,kCAAkC,UAAU,SAAS;AAAA,MAChE,OAAO,UAAU;AAAA,MACjB,iBAAe,MAAM;AAAA,MAErB,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,GAAG;AAAA,UACH,GAAG;AAAA,UACH;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO,EAAE,eAAe,QAAQ,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC,EAAG;AAAA;AAAA,MAChE;AAAA;AAAA,EACF;AAEJ;AAKA,SAAS,uBAAuB,KAA4C;AAC1E,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL;AACE,aAAO;AAAA,EACX;AACF;AAOA,IAAM,0BAA0B,oBAAI,IAAI,CAAC,WAAW,YAAY,YAAY,UAAU,SAAS,CAAC;AAUhG,SAAS,gBAAgB,MAA4B;AACnD,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,EAAE,GAAG,MAAM,MAAM,YAAY,cAAc,OAAO;AAAA,IAC3D,KAAK;AACH,aAAO,EAAE,GAAG,MAAM,MAAM,YAAY,cAAc,QAAQ;AAAA,IAC5D,KAAK;AACH,aAAO,EAAE,GAAG,MAAM,MAAM,YAAY,WAAW,KAAK;AAAA,IACtD,KAAK;AACH,aAAO,EAAE,GAAG,MAAM,MAAM,YAAY,WAAW,MAAM;AAAA,IACvD;AACE,aAAO;AAAA,EACX;AACF;;;AG3KA,SAAS,WAAAC,gBAAmC;AAE5C,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACgCC,SACE,OAAAC,MADF;AA/CD,SAAS,gBACd,OACA,aACoB;AACpB,MAAI,CAAC,SAAS,UAAU,QAAS,QAAO;AACxC,QAAM,IAAI,KAAK,IAAI,GAAG,eAAe,CAAC;AACtC,MAAI,UAAU,SAAU,QAAO,GAAG,CAAC,IAAI,IAAI,CAAC;AAC5C,SAAO,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC;AAC1B;AAMA,SAAS,eAAe,QAAQ,GAAuD;AACrF,QAAM,IAAK,QAAQ,KAAK,KAAM;AAC9B,QAAM,KAAK,KAAK,IAAI,CAAC;AACrB,QAAM,KAAK,KAAK,IAAI,CAAC;AACrB,SAAO,EAAE,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,EAAE;AAClF;AAGO,SAAS,cAAc,SAAyB;AACrD,SAAO,eAAe,OAAO;AAC/B;AAOO,SAAS,YACd,SACA,OACA,UACA,SACuD;AACvD,MAAI,SAAS;AACX,UAAM,KAAK,kBAAkB,OAAO;AACpC,WAAO,EAAE,MAAM,QAAQ,EAAE,KAAK,KAAK,WAAW,IAAI,OAAO,EAAE;AAAA,EAC7D;AACA,MAAI,UAAU;AACZ,UAAM,KAAK,cAAc,OAAO;AAChC,UAAM,IAAI,eAAe,SAAS,KAAK;AACvC,WAAO;AAAA,MACL,MAAM,QAAQ,EAAE;AAAA,MAChB,KACE,qBAAC,oBAAe,IAAQ,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,IAC1D;AAAA,wBAAAA,KAAC,UAAK,QAAO,MAAK,WAAW,SAAS,MAAM;AAAA,QAC5C,gBAAAA,KAAC,UAAK,QAAO,QAAO,WAAW,SAAS,IAAI;AAAA,SAC9C;AAAA,IAEJ;AAAA,EACF;AACA,SAAO,EAAE,MAAM,OAAO,KAAK,KAAK;AAClC;AAMA,SAAS,WAAW,IAAY,SAAoC;AAClE,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,QAAQ,QAAQ;AACtB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,cAAa;AAAA,MAEb,kBAAkB,QAAQ,SAAS,aAAa,eAAe;AAAA,MAE9D;AAAA,gBAAQ,SAAS,UAChB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,IAAI,OAAO;AAAA,YACX,IAAI,OAAO;AAAA,YACX,GAAG,KAAK,IAAI,GAAG,OAAO,EAAE;AAAA,YACxB,MAAM;AAAA,YACN;AAAA;AAAA,QACF;AAAA,QAED,QAAQ,SAAS,UAChB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,GAAG,KAAK,IAAI,cAAc,IAAI;AAAA,YAC9B,MAAK;AAAA,YACL,QAAQ;AAAA,YACR,aAAa;AAAA,YACb;AAAA;AAAA,QACF;AAAA,QAED,QAAQ,SAAS,cAChB,gBAAAA,KAAC,UAAK,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,MAAM,QAAQ,OAAO,aAAa,GAAG,SAAkB;AAAA;AAAA;AAAA,EAE1F;AAEJ;AAMO,SAAS,mBACd,SACA,QAC6D;AAC7D,MAAI,CAAC,UAAU,OAAO,SAAS,QAAS,QAAO,EAAE,YAAY,QAAW,KAAK,KAAK;AAClF,QAAM,KAAK,gBAAgB,OAAO;AAClC,QAAM,UAAU,OAAO,WAAW;AAClC,SAAO;AAAA,IACL,YAAY,QAAQ,EAAE;AAAA,IACtB,KACE,qBAAC,YAAO,IAAQ,GAAE,MAAK,GAAE,MAAK,OAAM,QAAO,QAAO,QAChD;AAAA,sBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,eAAe,OAAO,iBAAiB;AAAA,UACvC,YAAY;AAAA,UACZ,aAAY;AAAA,UACZ,QAAO;AAAA;AAAA,MACT;AAAA,MACA,gBAAAA,KAAC,mBAAc,IAAG,SAAQ,MAAK,YAAW,QAAO,KAAI,QAAO,QAAO;AAAA,MACnE,gBAAAA,KAAC,yBAAoB,IAAG,QAAO,QAAO,SACpC,0BAAAA,KAAC,aAAQ,MAAK,UAAS,OAAO,SAAS,WAAW,GAAG,GACvD;AAAA,MACA,gBAAAA,KAAC,iBAAY,IAAG,SAAQ,KAAI,iBAAgB,UAAS,MAAK;AAAA,OAC5D;AAAA,EAEJ;AACF;;;ADhG+C,SA+W3C,UA/W2C,OAAAC,MAgOrC,QAAAC,aAhOqC;AADxC,SAAS,UAAU,OAAuB;AAC/C,MAAI,MAAM,MAAM,QAAQ,MAAM,KAAK,EAAG,QAAO,gBAAAD,KAAC,iBAAe,GAAG,OAAO;AAKvE,MAAI,cAAc,MAAM,MAAM,QAAQ,QAAQ,EAAE,EAAG,QAAO,gBAAAA,KAAC,iBAAe,GAAG,OAAO;AACpF,SAAO,gBAAAA,KAAC,kBAAgB,GAAG,OAAO;AACpC;AAGA,SAAS,WAAW,OAAuB;AACzC,SAAO,MAAM,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM;AAChF;AAGA,SAAS,eAAe,MAAsB;AAC5C,SAAO,iBAAiB,IAAI,EACzB;AAAA,IAAI,CAAC,QACJ,IAAI,SAAS,SACT,aAAa,UAAU,IAAI,QAAQ,IAAI,IAAI,CAAC,8BAC5C,WAAW,IAAI,IAAI,EAAE,QAAQ,OAAO,MAAM;AAAA,EAChD,EACC,KAAK,EAAE;AACZ;AAUA,SAAS,cAAc,EAAE,OAAO,UAAU,UAAU,GAAmB;AACrE,QAAM,EAAE,SAAS,UAAU,UAAU,IAAI;AACzC,QAAM,EAAE,MAAM,MAAM,IAAI;AAExB,QAAM,OAAO,aAAa,SAAS,GAAG,SAAS,KAAK;AACpD,QAAM,OAAO,aAAa,SAAS,GAAG,SAAS,MAAM;AACrD,QAAM,WACJ,SAAS,SAAS,OAAO,aAAa,SAAS,OAAO,SAAS,KAAK,IAAI,SAAS;AACnF,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,aAAa,MAAM,cAAc;AACvC,QAAM,eAAe,MAAM,WAAW;AACtC,QAAM,UAAU,MAAM,WAAW;AAKjC,QAAM,QAAQ,iBAAiB,QAAQ,EAAE;AACzC,QAAM,QAAQ,MAAM,MAAM,IAAI,EAAE,QAAQ,CAAC,SAAS,SAAS,MAAM,MAAM,UAAU,QAAQ,CAAC;AAC1F,QAAM,YACJ,SAAS,UAAU,OACf,aAAa,SAAS,QAAQ,SAAS,MAAM,IAC7C,KAAK,IAAI,cAAc,MAAM,SAAS,YAAY,IAAI,UAAU;AAEtE,QAAM,OAAO,OAAO,WAAW,QAAQ,UAAU,GAAG;AACpD,QAAM,OAAO,OAAO,WAAW,QAAQ,WAAW,GAAG;AAErD,QAAM,YAAY,kBAAkB,WAAW,SAAS;AACxD,QAAM,OAAOE,SAAQ,MAAM,eAAe,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC;AAE7D,QAAM,kBACJ,MAAM,kBAAkB,QACpB,eACA,MAAM,kBAAkB,WACtB,aACA;AAER,QAAM,WAA0B;AAAA,IAC9B,WAAW;AAAA,IACX,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB;AAAA,IACA,OAAO,MAAM;AAAA,IACb,UAAU,GAAG,MAAM,QAAQ;AAAA,IAC3B,YAAY,MAAM,cAAc;AAAA,IAChC,YAAY,MAAM,cAAc;AAAA,IAChC,WAAW,MAAM,aAAa;AAAA,IAC9B;AAAA,IACA,WAAW,MAAM,aAAa;AAAA,IAC9B,GAAI,MAAM,SAAS,EAAE,YAAY,4BAA4B,IAAI,CAAC;AAAA,IAClE,GAAG,UAAU;AAAA,EACf;AAEA,SACE,gBAAAF,KAAC,OAAE,WAAW,iCAAiC,UAAU,SAAS,IAAI,iBAAe,MAAM,IACzF,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,GAAG;AAAA,MACH,GAAG;AAAA,MACH,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO,EAAE,UAAU,UAAU;AAAA,MAK7B,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACE,GAAI,EAAE,OAAO,+BAA+B;AAAA,UAC7C,OAAO;AAAA,UAEP,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,EAAE,OAAO,QAAQ,YAAY,YAAY,WAAW,aAAa;AAAA,cACxE,cAAY;AAAA,cACZ,yBAAyB,EAAE,QAAQ,KAAK;AAAA;AAAA,UAC1C;AAAA;AAAA,MACF;AAAA;AAAA,EACF,GACF;AAEJ;AAEA,SAAS,eAAe,EAAE,OAAO,UAAU,UAAU,GAAmB;AACtE,QAAM,EAAE,SAAS,UAAU,UAAU,IAAI;AACzC,QAAM,EAAE,MAAM,MAAM,IAAI;AAKxB,QAAM,OAAO,aAAa,SAAS,GAAG,SAAS,KAAK;AACpD,QAAM,OAAO,aAAa,SAAS,GAAG,SAAS,MAAM;AACrD,QAAM,WACJ,SAAS,SAAS,OAAO,aAAa,SAAS,OAAO,SAAS,KAAK,IAAI;AAC1E,QAAM,YACJ,SAAS,UAAU,OAAO,aAAa,SAAS,QAAQ,SAAS,MAAM,IAAI;AAC7E,QAAM,WAAW;AAEjB,QAAM,aAAa,cAAc,MAAM,WAAW,SAAS,MAAM;AACjE,QAAM,mBAAmB,oBAAoB,MAAM,eAAe,SAAS,MAAM;AAQjF,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,IAAI,OAAO,MAAM,UAAU,QAAQ,UAAU;AACnD,QAAM,IAAI,OAAO,MAAM,WAAW,QAAQ,gBAAgB;AAG1D,QAAM,YAAY,kBAAkB,WAAW,SAAS;AAGxD,QAAM,YAAY,QAAQ,IAAI,MAAM,IAAI;AACxC,MAAI,QAAQ,WACR,SAAS;AAAA,IACP,CAAC,KAAK,SAAS,IAAI,OAAO,SAAS,MAAM,MAAM,UAAU,QAAQ,CAAC;AAAA,IAClE,CAAC;AAAA,EACH,IACA;AAGJ,MAAI,MAAM,YAAY,MAAM,SAAS,MAAM,UAAU;AACnD,YAAQ,MAAM,MAAM,GAAG,MAAM,QAAQ;AAErC,UAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,UAAM,MAAM,SAAS,CAAC,IAAI,KAAK,QAAQ,QAAQ,EAAE,IAAI;AAAA,EACvD;AACA,QAAM,aAAa,MAAM,cAAc;AACvC,QAAM,eAAe,MAAM,WAAW;AAGtC,QAAM,aAA8C;AAAA,IAClD,UAAU,GAAG,MAAM,QAAQ;AAAA,IAC3B,YAAY,MAAM,cAAc;AAAA,IAChC,YAAY,MAAM,cAAc;AAAA,IAChC,WAAW,MAAM,aAAa;AAAA,IAC9B,MAAM,MAAM;AAAA,IACZ,GAAG,UAAU;AAAA,EACf;AAGA,QAAM,WAAW,MAAM,SAAS,UAAU,MAAM,EAAE,KAAK;AAEvD,SACE,gBAAAC,MAAC,OAAE,WAAW,iCAAiC,UAAU,SAAS,IAAI,iBAAe,MAAM,IAExF;AAAA,UAAM,UACL,gBAAAD,KAAC,UACC,0BAAAA,KAAC,YAAO,IAAI,UAAU,GAAE,QAAO,GAAE,QAAO,OAAM,QAAO,QAAO,QAC1D,0BAAAA,KAAC,kBAAa,IAAG,KAAI,IAAG,KAAI,cAAa,KAAI,YAAW,mBAAkB,GAC5E,GACF;AAAA,IAQF,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM;AAAA,QACf;AAAA,QACA,KACE,YAAY,QAAQ,aAAa,OAC7B;AAAA,UACE,GAAG,OAAO,WAAW,QAAQ,UAAU,GAAG;AAAA,UAC1C,GAAG,OAAO,WAAW,QAAQ,WAAW,GAAG;AAAA,UAC3C,OAAO;AAAA,UACP,QAAQ;AAAA,QACV,IACA;AAAA,UACE,GAAG,KAAK,MAAM,WAAW;AAAA,UACzB,GAAG,IAAI,MAAM,YAAY,MAAM,WAAW;AAAA,UAC1C,OAAO,gBAAgB,OAAO,KAAK,KAAK,MAAM,WAAW,MAAM;AAAA,UAC/D,QAAQ,MAAM,SAAS,gBAAgB,MAAM,WAAW,MAAM;AAAA,QAChE;AAAA;AAAA,IAER;AAAA,IAGA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP,QAAQ,WAAW,QAAQ,QAAQ,MAAM;AAAA,QAExC,gBAAM,IAAI,CAAC,MAAM,MAChB,gBAAAC,MAAC,WAAc,GAAM,IAAI,MAAM,IAAI,IAAI,cACpC;AAAA,kBAAQ;AAAA,UAAS;AAAA,aADR,CAEZ,CACD;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAUA,SAAS,cAAc,EAAE,OAAO,UAAU,UAAU,GAAmB;AACrE,QAAM,EAAE,SAAS,UAAU,UAAU,IAAI;AACzC,QAAM,EAAE,MAAM,MAAM,IAAI;AAExB,QAAM,OAAO,aAAa,SAAS,GAAG,SAAS,KAAK;AACpD,QAAM,OAAO,aAAa,SAAS,GAAG,SAAS,MAAM;AACrD,QAAM,WACJ,SAAS,SAAS,OAAO,aAAa,SAAS,OAAO,SAAS,KAAK,IAAI,SAAS;AACnF,QAAM,YACJ,SAAS,UAAU,OACf,aAAa,SAAS,QAAQ,SAAS,MAAM,IAC7C,MAAM,YAAY,MAAM,cAAc,OAAO;AACnD,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,OAAO,OAAO,WAAW,QAAQ,UAAU,GAAG;AACpD,QAAM,OAAO,OAAO,WAAW,QAAQ,WAAW,GAAG;AAIrD,QAAM,WAAWC;AAAA,IACf,MAAM,mBAAmB,kBAAkB,iBAAiB,QAAQ,EAAE,CAAC,CAAC;AAAA,IACxE,CAAC,IAAI;AAAA,EACP;AAEA,QAAM,YAAY,kBAAkB,WAAW,SAAS;AAExD,QAAM,kBACJ,MAAM,kBAAkB,WACpB,WACA,MAAM,kBAAkB,WACtB,aACA;AAER,QAAM,YAAY,CAAC,EAAE,MAAM,gBAAgB,MAAM,eAAe,KAAK;AAGrE,QAAM,WAA0B;AAAA,IAC9B,WAAW;AAAA,IACX,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,SAAS,MAAM,WAAW;AAAA,IAC1B,OAAO,MAAM;AAAA,IACb,UAAU,GAAG,MAAM,QAAQ;AAAA,IAC3B,YAAY,MAAM,cAAc;AAAA,IAChC,YAAY,MAAM,cAAc;AAAA,IAChC,WAAW,MAAM,aAAa;AAAA,IAC9B,YAAY,MAAM,cAAc;AAAA,IAChC,WAAW,MAAM,aAAa;AAAA,IAC9B,UAAU;AAAA,IACV,GAAI,MAAM,aAAa,EAAE,YAAY,MAAM,WAAW,IAAI,CAAC;AAAA,IAC3D,GAAI,YACA;AAAA,MACE,QAAQ,GAAG,MAAM,WAAW,MAAM,MAAM,eAAe,OAAO,IAAI,MAAM,WAAW;AAAA,MACnF,cAAc;AAAA,IAChB,IACA,CAAC;AAAA,IACL,GAAI,MAAM,SAAS,EAAE,YAAY,4BAA4B,IAAI,CAAC;AAAA,IAClE,GAAG,UAAU;AAAA,EACf;AAIA,QAAM,MAAM,oBAAoB,MAAM,MAAM,EAAE,CAAC;AAC/C,QAAM,YACJ,IAAI,GAAG,cACH,GAAG,uBACH,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,wCAM1D,GAAG,QAAQ,GAAG,sEACd,GAAG,iBACH,GAAG,kCACH,GAAG,gCAAgC,GAAG,kCACtC,GAAG;AAET,SACE,gBAAAF,KAAC,OAAE,WAAW,iCAAiC,UAAU,SAAS,IAAI,iBAAe,MAAM,IACzF,0BAAAA,KAAC,mBAAc,GAAG,MAAM,GAAG,MAAM,OAAO,UAAU,QAAQ,WAExD,0BAAAC;AAAA,IAAC;AAAA;AAAA,MACE,GAAI,EAAE,OAAO,+BAA+B;AAAA,MAC7C,OAAO;AAAA,MAEP;AAAA,wBAAAD,KAAC,WAAO,qBAAU;AAAA,QAClB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,YACX,cAAY,QAAQ;AAAA,YACpB,OAAO,EAAE,OAAO,QAAQ,YAAY,YAAY,WAAW,aAAa;AAAA,YACxE,yBAAyB,EAAE,QAAQ,SAAS;AAAA;AAAA,QAC9C;AAAA;AAAA;AAAA,EACF,GACF,GACF;AAEJ;AAGA,SAAS,MAAM,IAAoB;AACjC,SAAO,GAAG,QAAQ,mBAAmB,GAAG;AAC1C;AAMA,SAAS,QAAQ;AAAA,EACf;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,UAAU,CAAC,EAAE,MAAM,cAAc,MAAM;AAC7C,QAAM,YAAY,CAAC,EAAE,MAAM,gBAAgB,MAAM,eAAe,KAAK;AACrE,MAAI,CAAC,WAAW,CAAC,UAAW,QAAO;AAEnC,QAAM,EAAE,MAAM,IAAI,IAAI,YAAY,SAAS,MAAM,YAAY,MAAM,kBAAkB;AACrF,QAAM,OAAO,gBAAgB,MAAM,aAAa,MAAM,WAAW;AACjE,SACE,gBAAAC,MAAA,YACG;AAAA,WAAO,gBAAAD,KAAC,UAAM,eAAI;AAAA,IACnB,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,GAAG,IAAI;AAAA,QACP,GAAG,IAAI;AAAA,QACP,OAAO,IAAI;AAAA,QACX,QAAQ,IAAI;AAAA,QACZ,MAAM,UAAU,OAAO;AAAA,QACvB,aAAa,UAAU,MAAM,oBAAoB;AAAA,QACjD,QAAQ,YAAY,MAAM,cAAc;AAAA,QACxC,aAAa,YAAY,MAAM,cAAc;AAAA,QAC7C,iBAAiB,YAAY,OAAO;AAAA,QACpC,IAAI;AAAA,QACJ,IAAI;AAAA;AAAA,IACN;AAAA,KACF;AAEJ;AAKA,SAAS,cAAc,OAAqC,QAAyB;AAEnF,MAAI,UAAU,SAAU,QAAO;AAC/B,MAAI,UAAU,QAAS,QAAO;AAC9B,MAAI,UAAU,OAAQ,QAAO;AAG7B,MAAI,QAAQ,SAAS,OAAO,EAAG,QAAO;AACtC,MAAI,WAAW,SAAU,QAAO;AAChC,SAAO;AACT;AAOA,SAAS,oBAAoB,eAA6C,QAAyB;AACjG,MAAI,kBAAkB,MAAO,QAAO;AACpC,MAAI,kBAAkB,SAAU,QAAO;AACvC,MAAI,kBAAkB,SAAU,QAAO;AAEvC,MAAI,QAAQ,SAAS,QAAQ,EAAG,QAAO;AACvC,MAAI,WAAW,SAAU,QAAO;AAChC,SAAO;AACT;AAOA,SAAS,OACP,MACA,OACA,QACA,YACQ;AACR,MAAI,SAAS,KAAM,QAAO;AAC1B,QAAM,UAAU,OAAO,WAAW,QAAQ,OAAO,GAAG;AACpD,MAAI,eAAe,SAAU,QAAO,UAAU,QAAQ;AACtD,MAAI,eAAe,MAAO,QAAO,UAAU;AAC3C,SAAO;AACT;AAOA,SAAS,OACP,MACA,QACA,QACA,kBACQ;AACR,MAAI,UAAU,KAAM,QAAO;AAC3B,QAAM,SAAS,OAAO,WAAW,QAAQ,QAAQ,GAAG;AACpD,MAAI,qBAAqB,SAAU,QAAO,SAAS,SAAS;AAC5D,MAAI,qBAAqB,kBAAmB,QAAO,SAAS;AAC5D,SAAO;AACT;AAGA,SAAS,WAAW,QAAgB,MAAc,MAAyB;AACzE,MAAI,WAAW,SAAU,QAAO,OAAO;AACvC,MAAI,SAAS,IAAK,QAAO,OAAO,SAAS,OAAO,IAAI,OAAO;AAC3D,SAAO,OAAO,SAAS,QAAQ,IAAI,OAAO;AAC5C;AAKA,SAAS,gBAAgB,OAAiB,OAAqC;AAC7E,QAAM,gBAAgB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AAE5D,SAAO,gBAAgB,MAAM,WAAW;AAC1C;AAMA,SAAS,SAAS,MAAc,UAAkB,UAA4B;AAC5E,MAAI,CAAC,KAAK,KAAK,EAAG,QAAO,CAAC,EAAE;AAG5B,QAAM,eAAe,WAAW;AAChC,QAAM,eAAe,KAAK,MAAM,WAAW,YAAY;AAEvD,MAAI,gBAAgB,EAAG,QAAO,CAAC,IAAI;AAEnC,QAAM,QAAQ,KAAK,MAAM,KAAK;AAC9B,QAAM,QAAkB,CAAC;AACzB,MAAI,cAAc;AAElB,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAW,cAAc,GAAG,WAAW,IAAI,IAAI,KAAK;AAE1D,QAAI,SAAS,UAAU,cAAc;AACnC,oBAAc;AAAA,IAChB,OAAO;AAEL,UAAI,aAAa;AACf,cAAM,KAAK,WAAW;AAAA,MACxB;AAEA,UAAI,KAAK,SAAS,cAAc;AAE9B,YAAI,YAAY;AAChB,eAAO,UAAU,SAAS,cAAc;AACtC,gBAAM,KAAK,UAAU,MAAM,GAAG,YAAY,CAAC;AAC3C,sBAAY,UAAU,MAAM,YAAY;AAAA,QAC1C;AACA,sBAAc;AAAA,MAChB,OAAO;AACL,sBAAc;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAGA,MAAI,aAAa;AACf,UAAM,KAAK,WAAW;AAAA,EACxB;AAEA,SAAO,MAAM,SAAS,IAAI,QAAQ,CAAC,EAAE;AACvC;;;AEngBU,gBAAAG,MAwCF,QAAAC,aAxCE;AAhCH,SAAS,WAAW,EAAE,OAAO,UAAU,UAAU,GAAoB;AAC1E,QAAM,EAAE,SAAS,UAAU,UAAU,IAAI;AAGzC,QAAM,OAAO,aAAa,SAAS,GAAG,SAAS,KAAK;AACpD,QAAM,OAAO,aAAa,SAAS,GAAG,SAAS,MAAM;AACrD,QAAM,QAAQ,SAAS,QAAQ,aAAa,SAAS,OAAO,SAAS,KAAK,IAAI;AAC9E,QAAM,SAAS,SAAS,SAAS,aAAa,SAAS,QAAQ,SAAS,MAAM,IAAI;AAGlF,QAAM,eAAe,gBAAgB,SAAS,QAAQ,OAAO,MAAM;AACnE,QAAM,IAAI,OAAO,aAAa;AAC9B,QAAM,IAAI,OAAO,aAAa;AAG9B,QAAM,YAAY,kBAAkB,WAAW,SAAS;AAExD,QAAM,OAAO,QAAQ,QAAQ;AAK7B,QAAM,gBAAgB,OAAO,SAAS,YAAY,KAAK,SAAS,WAAW;AAE3E,MAAI,QAAQ,UAAU,UAAU,iBAAiB,CAAC,QAAQ,UAAU;AAClE,WACE,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,kCAAkC,UAAU,SAAS;AAAA,QAChE,OAAO,UAAU;AAAA,QACjB,iBAAe,MAAM;AAAA,QAErB,0BAAAA,KAAC,mBAAc,GAAM,GAAM,OAAc,QACvC,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,OAAO,GAAG,KAAK;AAAA,cACf,QAAQ,GAAG,MAAM;AAAA,cACjB,YAAY;AAAA,cACZ,cAAc,QAAQ,eAAe,GAAG,QAAQ,YAAY,OAAO;AAAA,cACnE,eAAe;AAAA,YACjB;AAAA;AAAA,QACF,GACF;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,QAAM,EAAE,MAAM,WAAW,KAAK,QAAQ,IAAI;AAAA,IACxC,MAAM;AAAA,IACN;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACA,QAAM,EAAE,YAAY,KAAK,UAAU,IAAI,mBAAmB,MAAM,IAAI,QAAQ,MAAM;AAClF,QAAM,OAAO,gBAAgB,QAAQ,aAAa,QAAQ,WAAW;AAGrE,QAAM,aAAa;AAAA,IACjB,MAAM;AAAA,IACN,aAAa,QAAQ;AAAA,IACrB,QAAQ,QAAQ;AAAA,IAChB,aAAa,QAAQ;AAAA,IACrB,iBAAiB;AAAA,IACjB,GAAI,aAAa,EAAE,QAAQ,WAAW,IAAI,CAAC;AAAA,EAC7C;AAEA,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,kCAAkC,UAAU,SAAS;AAAA,MAChE,OAAO,UAAU;AAAA,MACjB,iBAAe,MAAM;AAAA,MAEnB;AAAA,oBAAW,cACX,gBAAAA,MAAC,UACE;AAAA;AAAA,UACA;AAAA,WACH;AAAA,QAED,QAAQ,UAAU,UACjB,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,IAAI,QAAQ;AAAA,YACZ,IAAI,QAAQ;AAAA,YACX,GAAG;AAAA;AAAA,QACN;AAAA,QAGD,QAAQ,UAAU,YACjB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,IAAI,IAAI,QAAQ;AAAA,YAChB,IAAI,IAAI,SAAS;AAAA,YACjB,GAAG,KAAK,IAAI,OAAO,MAAM,IAAI;AAAA,YAC5B,GAAG;AAAA;AAAA,QACN;AAAA,QAGD,QAAQ,UAAU,UACjB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,IAAI;AAAA,YACJ,IAAI;AAAA,YACJ,IAAI,IAAI;AAAA,YACR,IAAI,IAAI;AAAA,YACR,QAAQ,QAAQ,UAAU;AAAA,YAC1B,aAAa,QAAQ,eAAe;AAAA,YACpC,iBAAiB;AAAA;AAAA,QACnB;AAAA;AAAA;AAAA,EAEJ;AAEJ;;;AC1GA,SAAS,YAAY,iBAAiB;AAiEhC,SAEU,OAAAE,MAFV,QAAAC,aAAA;AA/CN,SAAS,cAAc,OAAsB,UAAqD;AAChG,QAAM,EAAE,SAAS,SAAS,IAAI;AAC9B,MAAI,CAAC,QAAQ,UAAW,QAAO,QAAQ;AACvC,QAAM,IAAI,SAAS,QAAQ,aAAa,SAAS,OAAO,SAAS,KAAK,IAAI;AAC1E,QAAM,IAAI,SAAS,SAAS,aAAa,SAAS,QAAQ,SAAS,MAAM,IAAI;AAC7E,QAAM,OAAO,aAAa,SAAS,GAAG,SAAS,KAAK;AACpD,QAAM,OAAO,aAAa,SAAS,GAAG,SAAS,MAAM;AACrD,QAAM,SAAS,gBAAgB,SAAS,QAAQ,GAAG,CAAC;AACpD,QAAM,UAAU,UAAU,QAAQ,WAAW,OAAO,OAAO,GAAG,OAAO,OAAO,GAAG,GAAG,CAAC;AAEnF,SAAO,WAAW,QAAQ;AAC5B;AAGA,SAAS,gBACP,UACA,OACA,KACa;AACb,MAAI,SAAU,QAAO;AACrB,QAAM,QAAQ,UAAU,UAAU,UAAU;AAC5C,SAAO,QAAQ,UAAU;AAC3B;AAEO,SAAS,UAAU,EAAE,OAAO,UAAU,UAAU,GAAmB;AACxE,QAAM,EAAE,SAAS,WAAW,GAAG,IAAI;AACnC,QAAM,IAAI,cAAc,OAAO,QAAQ;AACvC,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,EAAE,MAAM,KAAK,QAAQ,IAAI,YAAY,IAAI,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ;AAEvF,QAAM,OAAO,QAAQ,cACjB,gBAAgB,QAAQ,aAAa,WAAW,IAChD,QAAQ;AACZ,QAAM,YAAY,kBAAkB,WAAW,SAAS;AAExD,QAAM,UAAU,gBAAgB,EAAE;AAClC,QAAM,QAAQ,cAAc,EAAE;AAC9B,QAAM,QAAQ,WAAW,gBAAgB,QAAQ,aAAa,QAAQ,OAAO,OAAO,GAAG,OAAO;AAC9F,QAAM,MAAM,WAAW,gBAAgB,QAAQ,WAAW,QAAQ,OAAO,KAAK,GAAG,KAAK;AAEtF,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,iCAAiC,UAAU,SAAS;AAAA,MAC/D,OAAO,UAAU;AAAA,MACjB,iBAAe;AAAA,MAEf;AAAA,wBAAAA,MAAC,UACE;AAAA;AAAA,UACA,OAAO,gBAAAD,KAAC,aAAU,IAAI,OAAO,KAAI,OAAM,GAAG,IAAI,GAAG,QAAQ,IAAI,QAAQ,QAAgB;AAAA,UACrF,SACC,gBAAAA,KAAC,aAAU,IAAI,SAAS,KAAI,SAAQ,GAAG,MAAM,GAAG,QAAQ,MAAM,QAAQ,QAAgB;AAAA,WAE1F;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,aAAa,QAAQ;AAAA,YACrB,iBAAiB;AAAA,YACjB,aAAa,QAAQ,QAAQ,OAAO,MAAM;AAAA,YAC1C,WAAW,MAAM,QAAQ,KAAK,MAAM;AAAA;AAAA,QACtC;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR,MAAM,QAAQ,QAAQ,IAAI;AAAA,MAC1B,MAAM;AAAA,MACN,aAAa;AAAA,MACb,cAAc;AAAA,MACd,QAAO;AAAA,MACP,aAAY;AAAA,MAEZ,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,MAAM,SAAS,SAAS;AAAA,UACxB,QAAQ,SAAS,SAAS;AAAA,UAC1B,aAAa,SAAS,SAAY;AAAA;AAAA,MACpC;AAAA;AAAA,EACF;AAEJ;;;ACjIA,SAAS,YAAAE,WAAU,aAAAC,kBAAiB;;;ACuB7B,IAAM,iBAAqD;AAAA,EAChE,SAAS;AAAA,IACP,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,EACX;AAAA,EACA,MAAM;AAAA,IACJ,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,EACX;AAAA,EACA,WAAW;AAAA,IACT,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,EACX;AAAA,EACA,OAAO;AAAA,IACL,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,EACX;AAAA,EACA,YAAY;AAAA,IACV,KAAK;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,EACX;AACF;AAMO,SAAS,aAAa,KAAa,KAAa,MAAwC;AAC7F,QAAM,IAAI,KAAK,IAAI,GAAG,IAAI;AAC1B,QAAM,IAAI,KAAK,OAAQ,MAAM,OAAO,MAAO,CAAC;AAC5C,QAAM,SAAU,MAAM,KAAK,KAAM;AACjC,QAAM,IAAI,KAAK,OAAQ,IAAI,KAAK,IAAI,KAAK,IAAI,MAAM,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAK,CAAC;AAChG,SAAO,EAAE,GAAG,EAAE;AAChB;AAgBO,SAAS,eACd,KACA,KACA,MACA,WAAmB,KACO;AAC1B,QAAM,IAAI,KAAK,IAAI,GAAG,IAAI;AAC1B,QAAM,SAAU,MAAM,OAAO,MAAO;AACpC,QAAM,SAAU,MAAM,KAAK,KAAM;AACjC,QAAM,SAAU,IAAI,KAAK,IAAI,KAAK,IAAI,MAAM,IAAI,IAAI,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAK;AAExF,SAAO;AAAA,IACL,IAAI,QAAQ,KAAK,MAAM,KAAK,KAAK;AAAA,IACjC,IAAI,QAAQ,KAAK,MAAM,KAAK,KAAK;AAAA,EACnC;AACF;AAKO,SAAS,oBACd,WACA,WACA,MACA,eACA,gBACA,WAAmB,KACgD;AACnE,QAAM,aAAa,aAAa,WAAW,WAAW,IAAI;AAC1D,QAAM,cAAc,eAAe,WAAW,WAAW,MAAM,QAAQ;AAGvE,QAAM,SAAS,KAAK,KAAK,gBAAgB,QAAQ,IAAI;AACrD,QAAM,SAAS,KAAK,KAAK,iBAAiB,QAAQ,IAAI;AAGtD,QAAM,SAAS,WAAW,IAAI,KAAK,MAAM,SAAS,CAAC;AACnD,QAAM,SAAS,WAAW,IAAI,KAAK,MAAM,SAAS,CAAC;AAGnD,QAAM,gBAAgB,gBAAgB,IAAI,YAAY;AACtD,QAAM,gBAAgB,iBAAiB,IAAI,YAAY;AAEvD,QAAM,QAA2E,CAAC;AAElF,WAAS,KAAK,GAAG,KAAK,QAAQ,MAAM;AAClC,aAAS,KAAK,GAAG,KAAK,QAAQ,MAAM;AAClC,YAAM,QAAQ,SAAS;AACvB,YAAM,QAAQ,SAAS;AACvB,YAAM,UAAU,iBAAiB,QAAQ,WAAW,KAAK;AACzD,YAAM,UAAU,iBAAiB,QAAQ,WAAW,KAAK;AAEzD,YAAM,KAAK,EAAE,GAAG,OAAO,GAAG,OAAO,SAAS,QAAQ,CAAC;AAAA,IACrD;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,aAAa,UAAwB,GAAW,GAAW,GAAmB;AAC5F,SAAO,SAAS,IAAI,QAAQ,OAAO,OAAO,CAAC,CAAC,EAAE,QAAQ,OAAO,OAAO,CAAC,CAAC,EAAE,QAAQ,OAAO,OAAO,CAAC,CAAC;AAClG;AAKA,eAAe,eAAe,KAAwC;AACpE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,IAAI,MAAM;AACtB,QAAI,cAAc;AAClB,QAAI,SAAS,MAAM,QAAQ,GAAG;AAC9B,QAAI,UAAU,MAAM,OAAO,IAAI,MAAM,wBAAwB,GAAG,EAAE,CAAC;AACnE,QAAI,MAAM;AAAA,EACZ,CAAC;AACH;AA6BA,eAAsB,gBAAgB,SAA6C;AACjF,QAAM,EAAE,QAAQ,MAAM,OAAO,OAAO,QAAQ,UAAU,CAAC,GAAG,kBAAkB,KAAK,IAAI;AAErF,QAAM,WAAW,eAAe,KAAK;AACrC,QAAM,WAAW,SAAS,YAAY;AACtC,QAAM,cAAc,KAAK,IAAI,MAAM,SAAS,OAAO;AAGnD,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,8BAA8B;AAGxD,MAAI,YAAY,UAAU,UAAU,YAAY;AAChD,MAAI,SAAS,GAAG,GAAG,OAAO,MAAM;AAGhC,QAAM,QAAQ,oBAAoB,OAAO,KAAK,OAAO,KAAK,aAAa,OAAO,QAAQ,QAAQ;AAG9F,QAAM,eAAe,MAAM,IAAI,OAAO,SAAS;AAC7C,UAAM,MAAM,aAAa,UAAU,KAAK,GAAG,KAAK,GAAG,WAAW;AAC9D,QAAI;AACF,YAAM,MAAM,MAAM,eAAe,GAAG;AACpC,UAAI,UAAU,KAAK,KAAK,SAAS,KAAK,SAAS,UAAU,QAAQ;AAAA,IACnE,SAAS,KAAc;AAErB,cAAQ,KAAK,qBAAqB,GAAG,IAAI,GAAG;AAAA,IAC9C;AAAA,EACF,CAAC;AAED,QAAM,QAAQ,IAAI,YAAY;AAG9B,aAAW,UAAU,SAAS;AAC5B,eAAW,KAAK,QAAQ,QAAQ,aAAa,OAAO,QAAQ,QAAQ;AAAA,EACtE;AAGA,MAAI,iBAAiB;AACnB,oBAAgB,KAAK,SAAS,aAAa,OAAO,MAAM;AAAA,EAC1D;AAEA,SAAO,OAAO,UAAU,WAAW;AACrC;AAKA,SAAS,WACP,KACA,QACA,QACA,MACA,OACA,QACA,UACM;AAEN,QAAM,aAAa,aAAa,OAAO,KAAK,OAAO,KAAK,IAAI;AAC5D,QAAM,aAAa,aAAa,OAAO,KAAK,OAAO,KAAK,IAAI;AAE5D,QAAM,eAAe,eAAe,OAAO,KAAK,OAAO,KAAK,MAAM,QAAQ;AAC1E,QAAM,eAAe,eAAe,OAAO,KAAK,OAAO,KAAK,MAAM,QAAQ;AAE1E,QAAM,MAAM,WAAW,IAAI,WAAW,KAAK,YAAY,aAAa,IAAI,aAAa;AACrF,QAAM,MAAM,WAAW,IAAI,WAAW,KAAK,YAAY,aAAa,IAAI,aAAa;AAErF,QAAM,UAAU,QAAQ,IAAI;AAC5B,QAAM,UAAU,SAAS,IAAI;AAG7B,QAAM,QAAQ,OAAO,SAAS;AAC9B,QAAM,OAAO,OAAO,QAAQ;AAE5B,MAAI,KAAK;AAET,MAAI,SAAS,OAAO;AAElB,QAAI,YAAY;AAChB,QAAI,UAAU;AACd,QAAI,IAAI,SAAS,UAAU,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK;AACnD,QAAI,OAAO,SAAS,OAAO;AAC3B,QAAI,UAAU;AACd,QAAI,KAAK;AAGT,QAAI,YAAY;AAChB,QAAI,UAAU;AACd,QAAI,IAAI,SAAS,UAAU,IAAI,GAAG,GAAG,KAAK,KAAK,CAAC;AAChD,QAAI,KAAK;AAAA,EACX,WAAW,SAAS,UAAU;AAC5B,QAAI,YAAY;AAChB,QAAI,UAAU;AACd,QAAI,IAAI,SAAS,SAAS,GAAG,GAAG,KAAK,KAAK,CAAC;AAC3C,QAAI,KAAK;AACT,QAAI,cAAc;AAClB,QAAI,YAAY;AAChB,QAAI,OAAO;AAAA,EACb,WAAW,SAAS,QAAQ;AAC1B,QAAI,YAAY;AAChB,aAAS,KAAK,SAAS,SAAS,GAAG,IAAI,CAAC;AACxC,QAAI,KAAK;AAAA,EACX;AAGA,MAAI,OAAO,OAAO;AAChB,QAAI,YAAY;AAChB,QAAI,OAAO;AACX,QAAI,YAAY;AAChB,QAAI,SAAS,OAAO,OAAO,SAAS,UAAU,EAAE;AAAA,EAClD;AAEA,MAAI,QAAQ;AACd;AAKA,SAAS,SACP,KACA,IACA,IACA,QACA,aACA,aACM;AACN,MAAI,MAAO,KAAK,KAAK,IAAK;AAC1B,QAAM,OAAO,KAAK,KAAK;AAEvB,MAAI,UAAU;AACd,MAAI,OAAO,IAAI,KAAK,WAAW;AAE/B,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,QAAI,OAAO,KAAK,KAAK,IAAI,GAAG,IAAI,aAAa,KAAK,KAAK,IAAI,GAAG,IAAI,WAAW;AAC7E,WAAO;AACP,QAAI,OAAO,KAAK,KAAK,IAAI,GAAG,IAAI,aAAa,KAAK,KAAK,IAAI,GAAG,IAAI,WAAW;AAC7E,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,IAAI,KAAK,WAAW;AAC/B,MAAI,UAAU;AAChB;AAKA,SAAS,gBACP,KACA,MACA,OACA,QACM;AACN,MAAI,KAAK;AAGT,MAAI,YAAY;AAChB,QAAM,UAAU;AAChB,MAAI,OAAO;AACX,QAAM,YAAY,IAAI,YAAY,IAAI,EAAE;AACxC,MAAI,SAAS,QAAQ,YAAY,UAAU,IAAI,GAAG,SAAS,IAAI,YAAY,UAAU,GAAG,EAAE;AAG1F,MAAI,YAAY;AAChB,MAAI,YAAY;AAChB,MAAI,SAAS,MAAM,QAAQ,UAAU,GAAG,SAAS,CAAC;AAElD,MAAI,QAAQ;AACd;;;ADlQM,SAKE,OAAAC,MALF,QAAAC,aAAA;AA/EC,SAAS,SAAS,EAAE,OAAO,UAAU,UAAU,UAAU,GAAkB;AAChF,QAAM,EAAE,SAAS,UAAU,UAAU,IAAI;AACzC,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAwB,IAAI;AAClE,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,IAAI;AAC/C,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AAGtD,QAAM,IAAI,aAAa,SAAS,GAAG,SAAS,KAAK;AACjD,QAAM,IAAI,aAAa,SAAS,GAAG,SAAS,MAAM;AAClD,QAAM,QAAQ,SAAS,QAAQ,aAAa,SAAS,OAAO,SAAS,KAAK,IAAI,SAAS;AACvF,QAAM,SAAS,SAAS,SAAS,aAAa,SAAS,QAAQ,SAAS,MAAM,IAAI,SAAS;AAG3F,QAAM,SAAS,gBAAgB,SAAS,QAAQ,OAAO,MAAM;AAC7D,QAAM,SAAS,IAAI,OAAO;AAC1B,QAAM,SAAS,IAAI,OAAO;AAG1B,EAAAC,WAAU,MAAM;AACd,QAAI,YAAY;AAEhB,QAAI,QAAQ,WAAW;AAErB,YAAM,MAAM,QAAQ,UAAU,WAAW,MAAM,IAC3C,QAAQ,YACR,GAAG,QAAQ,IAAI,QAAQ,SAAS;AACpC,qBAAe,GAAG;AAClB,mBAAa,KAAK;AAClB;AAAA,IACF;AAGA,iBAAa,IAAI;AACjB,aAAS,IAAI;AAEb,oBAAgB;AAAA,MACd,QAAQ,QAAQ;AAAA,MAChB,MAAM,QAAQ;AAAA,MACd,OAAO,QAAQ;AAAA,MACf;AAAA,MACA;AAAA,MACA,SAAS,QAAQ;AAAA,MACjB,iBAAiB,QAAQ,oBAAoB;AAAA,IAC/C,CAAC,EACE,KAAK,CAAC,YAAY;AACjB,UAAI,CAAC,WAAW;AACd,uBAAe,OAAO;AACtB,qBAAa,KAAK;AAAA,MACpB;AAAA,IACF,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,UAAI,CAAC,WAAW;AACd,gBAAQ,MAAM,0BAA0B,GAAG;AAC3C,iBAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AACzD,qBAAa,KAAK;AAAA,MACpB;AAAA,IACF,CAAC;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EAEF,GAAG;AAAA,IACD,QAAQ,OAAO;AAAA,IACf,QAAQ,OAAO;AAAA,IACf,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAGD,QAAM,YAAY,kBAAkB,WAAW,SAAS;AAGxD,MAAI,WAAW;AACb,WACE,gBAAAF;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,gCAAgC,UAAU,SAAS;AAAA,QAC9D,OAAO,UAAU;AAAA,QACjB,iBAAe,MAAM;AAAA,QAErB;AAAA,0BAAAD,KAAC,UAAK,GAAG,QAAQ,GAAG,QAAQ,OAAc,QAAgB,MAAK,WAAU;AAAA,UACzE,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,GAAG,SAAS,QAAQ;AAAA,cACpB,GAAG,SAAS,SAAS;AAAA,cACrB,YAAW;AAAA,cACX,kBAAiB;AAAA,cACjB,MAAK;AAAA,cACL,UAAS;AAAA,cACT,YAAW;AAAA,cACZ;AAAA;AAAA,UAED;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AAGA,MAAI,SAAS,CAAC,aAAa;AACzB,WACE,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,gCAAgC,UAAU,SAAS;AAAA,QAC9D,OAAO,UAAU;AAAA,QACjB,iBAAe,MAAM;AAAA,QAErB;AAAA,0BAAAD,KAAC,UAAK,GAAG,QAAQ,GAAG,QAAQ,OAAc,QAAgB,MAAK,WAAU;AAAA,UACzE,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,GAAG,SAAS,QAAQ;AAAA,cACpB,GAAG,SAAS,SAAS;AAAA,cACrB,YAAW;AAAA,cACX,kBAAiB;AAAA,cACjB,MAAK;AAAA,cACL,UAAS;AAAA,cACT,YAAW;AAAA,cACZ;AAAA;AAAA,UAED;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,gCAAgC,UAAU,SAAS;AAAA,MAC9D,OAAO,UAAU;AAAA,MACjB,iBAAe,MAAM;AAAA,MAGrB;AAAA,wBAAAD,KAAC,UACC,0BAAAA,KAAC,cAAS,IAAI,QAAQ,MAAM,EAAE,IAC5B,0BAAAA,KAAC,UAAK,GAAG,QAAQ,GAAG,QAAQ,OAAc,QAAgB,GAC5D,GACF;AAAA,QAGA,gBAAAA,KAAC,OAAE,UAAU,aAAa,MAAM,EAAE,KAChC,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,GAAG;AAAA,YACH,GAAG;AAAA,YACH;AAAA,YACA;AAAA,YACA,qBAAoB;AAAA,YACpB,OAAO,EAAE,eAAe,OAAO;AAAA;AAAA,QACjC,GACF;AAAA;AAAA;AAAA,EACF;AAEJ;;;AE9JA,SAAS,UAAAI,SAAQ,aAAAC,kBAAiB;AA6G1B,gBAAAC,YAAA;AA5FD,SAAS,WAAW,EAAE,OAAO,UAAU,UAAU,WAAW,UAAU,GAAoB;AAC/F,QAAM,EAAE,SAAS,SAAS,IAAI;AAC9B,QAAM,WAAWC,QAAyB,IAAI;AAC9C,QAAM,gBAAgBA,QAAO,KAAK;AAIlC,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,QAAQ,YAAY;AAG1B,QAAM,IAAI,aAAa,SAAS,GAAG,SAAS,KAAK;AACjD,QAAM,IAAI,aAAa,SAAS,GAAG,SAAS,MAAM;AAClD,QAAM,QAAQ,SAAS,QAAQ,aAAa,SAAS,OAAO,SAAS,KAAK,IAAI,SAAS;AACvF,QAAM,SAAS,SAAS,SAAS,aAAa,SAAS,QAAQ,SAAS,MAAM,IAAI,SAAS;AAG3F,QAAM,SAAS,gBAAgB,SAAS,QAAQ,OAAO,MAAM;AAC7D,QAAM,SAAS,IAAI,OAAO;AAC1B,QAAM,SAAS,IAAI,OAAO;AAG1B,QAAM,MAAM,YAAY,QAAQ,KAAK,QAAQ;AAG7C,QAAM,iBAAiB,YAAY,QAAQ,aAAa,IAAI,QAAQ;AACpE,QAAM,YAAY,QAAQ,YAAY,iBAAiB;AAIvD,EAAAC,WAAU,MAAM;AACd,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,MAAO;AAGZ,UAAM,cAAc,QAAQ;AAC5B,kBAAc,UAAU;AAGxB,QAAI,WAAW;AACb,YAAM,cAAc,MAAM,KAAK;AAC/B,UAAI,aAAa;AACf,oBAAY,MAAM,MAAM;AAAA,QAExB,CAAC;AAAA,MACH;AAAA,IACF;AAGA,UAAM,mBAAmB,MAAM;AAC7B,UAAI,MAAM,eAAe,QAAQ,SAAS;AACxC,cAAM,MAAM;AACZ,cAAM,cAAc,QAAQ;AAAA,MAC9B;AAAA,IACF;AAEA,UAAM,iBAAiB,cAAc,gBAAgB;AACrD,WAAO,MAAM;AACX,YAAM,oBAAoB,cAAc,gBAAgB;AACxD,YAAM,MAAM;AAAA,IACd;AAAA,EAEF,GAAG,CAAC,QAAQ,KAAK,QAAQ,WAAW,QAAQ,OAAO,CAAC;AAGpD,EAAAA,WAAU,MAAM;AACd,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,SAAS,CAAC,cAAc,QAAS;AAGtC,QAAI,OAAO;AACT,YAAM,MAAM;AACZ,YAAM,cAAc,QAAQ;AAC5B;AAAA,IACF;AAGA,QAAI,MAAM,eAAe,QAAQ,QAAS;AAE1C,QAAI,WAAW;AACb,YAAM,cAAc,MAAM,KAAK;AAC/B,UAAI,aAAa;AACf,oBAAY,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC5B;AAAA,IACF,OAAO;AACL,YAAM,MAAM;AAAA,IACd;AAAA,EACF,GAAG,CAAC,WAAW,OAAO,QAAQ,WAAW,QAAQ,OAAO,CAAC;AAEzD,SACE,gBAAAF,KAAC,OAAE,WAAU,kCAAiC,iBAAe,MAAM,IACjE,0BAAAA,KAAC,mBAAc,GAAG,QAAQ,GAAG,QAAQ,OAAc,QACjD,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,MACR,OAAK;AAAA,MACL,aAAW;AAAA,MACX,SAAQ;AAAA,MACR,mBAAiB,QAAQ;AAAA,MACzB,iBAAe,QAAQ;AAAA,MACvB,iBAAe;AAAA,MACf,OAAO;AAAA,QACL,OAAO,GAAG,KAAK;AAAA,QACf,QAAQ,GAAG,MAAM;AAAA,QACjB,WAAW,QAAQ,OAAO;AAAA,QAC1B,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,eAAe;AAAA,MACjB;AAAA;AAAA,EACF,GACF,GACF;AAEJ;;;AC5FQ,SAgBU,OAAAG,MAhBV,QAAAC,aAAA;AAxCD,SAAS,WAAW,EAAE,OAAO,UAAU,UAAU,GAAoB;AAC1E,QAAM,EAAE,SAAS,UAAU,UAAU,IAAI;AACzC,QAAM,EAAE,SAAS,MAAM,OAAO,MAAM,IAAI;AAGxC,QAAM,IAAI,aAAa,SAAS,GAAG,SAAS,KAAK;AACjD,QAAM,IAAI,aAAa,SAAS,GAAG,SAAS,MAAM;AAClD,QAAM,QAAQ,SAAS,QAAQ,aAAa,SAAS,OAAO,SAAS,KAAK,IAAI,SAAS;AACvF,QAAM,SAAS,SAAS,SAAS,aAAa,SAAS,QAAQ,SAAS,MAAM,IAAI,SAAS;AAG3F,QAAM,SAAS,gBAAgB,SAAS,QAAQ,OAAO,MAAM;AAC7D,QAAM,SAAS,IAAI,OAAO;AAC1B,QAAM,SAAS,IAAI,OAAO;AAG1B,QAAM,YAAY,YAAY,kBAAkB,WAAW,SAAS,IAAI,CAAC;AAEzE,QAAM,YAAY,CAAC,OAAgD;AACjE,UAAM,IAAI,QAAQ,EAAE;AACpB,WAAO,IAAI,EAAE,WAAW,EAAE,IAAI;AAAA,EAChC;AAEA,QAAM,eAAe,MAAM,gBAAgB;AAE3C,SACE,gBAAAD,KAAC,mBAAc,GAAG,QAAQ,GAAG,QAAQ,OAAc,QAAgB,OAAO,WACxE,0BAAAA;AAAA,IAAC;AAAA;AAAA,MAEE,GAAI,EAAE,OAAO,+BAA+B;AAAA,MAC7C,OAAO;AAAA,QACL,OAAO,GAAG,KAAK;AAAA,QACf,QAAQ,GAAG,MAAM;AAAA,QACjB,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,SAAS;AAAA,QACT,WAAW;AAAA,MACb;AAAA,MAEA,0BAAAC;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL,OAAO;AAAA,YACP,gBAAgB;AAAA,YAChB,eAAe;AAAA,YACf,UAAU,GAAG,MAAM,QAAQ;AAAA,YAC3B,YAAY,MAAM,cAAc;AAAA,YAChC,UAAU;AAAA,YACV,cAAc,GAAG,YAAY;AAAA,YAC7B,QAAQ,aAAa,MAAM,WAAW;AAAA,UACxC;AAAA,UAEC;AAAA,oBAAQ,SAAS,KAChB,gBAAAD,KAAC,WACC,0BAAAA,KAAC,QACE,kBAAQ,IAAI,CAAC,QAAQ,OACpB,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC,OAAO;AAAA,kBACL,YAAY,MAAM;AAAA,kBAClB,OAAO,MAAM;AAAA,kBACb,YACE,MAAM,oBAAoB,MAAM,cAAc;AAAA,kBAChD,YAAY;AAAA,kBACZ,SAAS;AAAA,kBACT,cAAc,aAAa,MAAM,WAAW;AAAA,kBAC5C,aACE,KAAK,QAAQ,SAAS,IAAI,aAAa,MAAM,WAAW,KAAK;AAAA,kBAC/D,GAAG,UAAU,EAAE;AAAA,gBACjB;AAAA,gBAEC;AAAA;AAAA,cAdI;AAAA,YAeP,CACD,GACH,GACF;AAAA,YAED,KAAK,SAAS,KACb,gBAAAA,KAAC,WACE,eAAK,IAAI,CAAC,KAAK,OACd,gBAAAA,KAAC,QACE,cAAI,IAAI,CAAC,MAAM,OACd,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC,OAAO;AAAA,kBACL,YAAY,MAAM;AAAA,kBAClB,OAAO,MAAM;AAAA,kBACb,SAAS;AAAA,kBACT,cACE,KAAK,KAAK,SAAS,IAAI,aAAa,MAAM,WAAW,KAAK;AAAA,kBAC5D,aACE,KAAK,IAAI,SAAS,IAAI,aAAa,MAAM,WAAW,KAAK;AAAA,kBAC3D,GAAG,UAAU,EAAE;AAAA,gBACjB;AAAA,gBAEC;AAAA;AAAA,cAZI;AAAA,YAaP,CACD,KAjBM,EAkBT,CACD,GACH;AAAA;AAAA;AAAA,MAEJ;AAAA;AAAA,EACF,GACF;AAEJ;;;AXnDI,SAaM,OAAAE,OAbN,QAAAC,aAAA;AAxDG,IAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,QAAQ;AACV;AA2BO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,YAAY;AAAA,EACZ;AAAA,EACA,WAAW;AAAA,EACX;AACF,GAAuB;AAErB,MAAI,kBAAkB;AACtB,QAAM,kBAA0C,CAAC;AACjD,QAAM,mBAAmB,cAAc,MAAM;AAC7C,MAAI,oBAAoB,YAAY;AAClC,sBAAkB,mBAAmB,iBAAiB,MAAM,MAAM,iBAAiB,SAAS;AAC5F,oBAAgB,uBAAuB,IAAI,GAAG,0BAA0B,gBAAgB,CAAC;AAAA,EAC3F,WAAW,oBAAoB,WAAW;AACxC,sBAAkB,mBAAmB,iBAAiB,MAAM,OAAO,iBAAiB,SAAS;AAC7F,oBAAgB,uBAAuB,IAAI,GAAG,0BAA0B,gBAAgB,CAAC;AAAA,EAC3F;AAGA,QAAM,SAAS,WAAW,MAAM,EAAE;AAElC,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,aAAa,eAAe;AAAA,MACvC,OAAO;AAAA,MACP,SAAS,OAAO,SAAS,KAAK,IAAI,SAAS,MAAM;AAAA,MACjD,qBAAoB;AAAA,MACpB,UAAS;AAAA,MACT,iBAAe,MAAM;AAAA,MAKrB;AAAA,wBAAAD,MAAC,UACC,0BAAAA,MAAC,cAAS,IAAI,QACZ,0BAAAA,MAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAO,SAAS,OAAO,QAAQ,SAAS,QAAQ,GACpE,GACF;AAAA,QAGA,gBAAAA,MAAC,OAAE,UAAU,QAAQ,MAAM,KACvB,iBAAM,UAAU,CAAC,GAAG,IAAI,CAAC,UACzB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA;AAAA,UALK,MAAM;AAAA,QAMb,CACD,GACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAaA,SAAS,cAAc,EAAE,OAAO,UAAU,UAAU,WAAW,UAAU,GAAuB;AAC9F,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aACE,gBAAAA,MAAC,cAAW,OAAc,UAAoB,UAAoB,WAAsB;AAAA,IAE5F,KAAK;AACH,aAAO,gBAAAA,MAAC,aAAU,OAAc,UAAoB,WAAsB;AAAA,IAC5E,KAAK;AACH,aAAO,gBAAAA,MAAC,cAAW,OAAc,UAAoB,WAAsB;AAAA,IAC7E,KAAK;AACH,aAAO,gBAAAA,MAAC,aAAU,OAAc,UAAoB,WAAsB;AAAA,IAC5E,KAAK;AACH,aACE,gBAAAA,MAAC,YAAS,OAAc,UAAoB,UAAoB,WAAsB;AAAA,IAE1F,KAAK;AACH,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,MACF;AAAA,IAEJ,KAAK;AACH,aAAO,gBAAAA,MAAC,cAAW,OAAc,UAAoB,WAAsB;AAAA,IAC7E;AACE,cAAQ,KAAK,uBAAwB,MAAgB,IAAI,EAAE;AAC3D,aAAO;AAAA,EACX;AACF;;;AY7IA,SAAS,wBAAwB;;;ACGjC,SAAS,WAAAE,gBAAe;AAGxB,SAAS,yBAAyB;AA8G5B,gBAAAC,aAAA;AA3GN,IAAM,oBAAoB;AAC1B,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AAoBvB,SAAS,oBAAoB,QAAoC;AAE/D,MAAI,OAAO,SAAS,OAAO,MAAM,SAAS,GAAG;AAC3C,WAAO,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,MAC9B,MAAM,EAAE;AAAA,MACR,WAAW,EAAE;AAAA,MACb,SAAS,EAAE;AAAA,IACb,EAAE;AAAA,EACJ;AAGA,QAAM,WAAW,OAAO,KAAK,MAAM,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AACpE,MAAI,SAAS,WAAW,EAAG,QAAO,CAAC;AAEnC,QAAM,WAAW,OAAO,UAAU,OAAO;AACzC,QAAM,eAAe,WAAW,SAAS;AAEzC,SAAO,SAAS,IAAI,CAAC,MAAM,OAAO;AAAA,IAChC;AAAA,IACA,WAAW,OAAO,YAAY,IAAI;AAAA,IAClC,SAAS,OAAO,aAAa,IAAI,KAAK;AAAA,EACxC,EAAE;AACJ;AAOA,SAAS,gBAAgB,UAAqE;AAE5F,QAAM,WAAwB,CAAC;AAC/B,aAAW,UAAU,SAAS,SAAS;AACrC,aAAS,KAAK,GAAG,oBAAoB,MAAM,CAAC;AAAA,EAC9C;AAEA,MAAI,SAAS,WAAW,EAAG,QAAO,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,EAAE;AAG1D,MAAI,YAAY;AAChB,MAAI,SAAS,SAAS,WAAW;AAC/B,UAAM,YAAY,KAAK,KAAK,SAAS,SAAS,SAAS;AACvD,gBAAY,KAAK,KAAK,SAAS,SAAS,SAAS;AACjD,gBAAY,KAAK,IAAI,gBAAgB,KAAK,IAAI,gBAAgB,SAAS,CAAC;AAAA,EAC1E,OAAO;AACL,gBAAY,KAAK,IAAI,gBAAgB,SAAS,MAAM;AAAA,EACtD;AAGA,QAAM,SAAsB,CAAC;AAC7B,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,WAAW;AACnD,UAAM,aAAa,SAAS,MAAM,GAAG,IAAI,SAAS;AAClD,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,WAAW,WAAW,CAAC,EAAE;AAAA,MACzB,SAAS,WAAW,WAAW,SAAS,CAAC,EAAE;AAAA,IAC7C,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,OAAO,UAAU,OAAO;AACnC;AAUO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AACF,GAA8B;AAE5B,QAAM,EAAE,OAAO,IAAID;AAAA,IACjB,MAAO,WAAW,gBAAgB,QAAQ,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,EAAE;AAAA,IACtE,CAAC,QAAQ;AAAA,EACX;AAEA,MAAI,CAAC,WAAW,OAAO,WAAW,GAAG;AACnC,WACE,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO;AAAA,UACL,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,eAAe;AAAA,UACf,SAAS;AAAA,UACT,YAAY;AAAA,QACd;AAAA;AAAA,IACF;AAAA,EAEJ;AAGA,MAAI,cAAgC;AACpC,MAAI,kBAAkB;AAEtB,aAAW,SAAS,QAAQ;AAC1B,QAAI,eAAe,MAAM,aAAa,cAAc,MAAM,SAAS;AACjE,oBAAc;AACd;AAAA,IACF;AAAA,EACF;AAGA,MAAI,CAAC,aAAa;AAChB,aAAS,IAAI,GAAG,IAAI,OAAO,SAAS,GAAG,KAAK;AAC1C,UAAI,eAAe,OAAO,CAAC,EAAE,WAAW,cAAc,OAAO,IAAI,CAAC,EAAE,WAAW;AAE7E,sBAAc,OAAO,CAAC;AACtB,0BAAkB,YAAY,MAAM,SAAS;AAC7C;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,eAAe,OAAO,SAAS,KAAK,eAAe,OAAO,OAAO,SAAS,CAAC,EAAE,WAAW;AAC3F,oBAAc,OAAO,OAAO,SAAS,CAAC;AACtC,wBAAkB,YAAY,MAAM,SAAS;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,CAAC,YAAa,QAAO;AAGzB,MAAI,oBAAoB,IAAI;AAC1B,aAAS,IAAI,GAAG,IAAI,YAAY,MAAM,QAAQ,KAAK;AACjD,YAAM,OAAO,YAAY,MAAM,CAAC;AAChC,UAAI,eAAe,KAAK,aAAa,cAAc,KAAK,SAAS;AAC/D,0BAAkB;AAClB;AAAA,MACF;AAAA,IACF;AAEA,QAAI,oBAAoB,IAAI;AAC1B,eAAS,IAAI,YAAY,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AACtD,YAAI,eAAe,YAAY,MAAM,CAAC,EAAE,WAAW;AACjD,4BAAkB;AAClB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,oBAAoB,GAAI,mBAAkB;AAAA,EAChD;AAGA,QAAM,eAAe,OAAO,QAAQ,WAAW;AAC/C,QAAM,aAAa,OAAO,YAAY,YAClC,kBAAkB,MAAM,WAAW,WAAW,4BAA4B,IAC1E;AAGJ,QAAM,iBAAiB,UAAU,UAAU;AAC3C,QAAM,eAAe,KAAK,MAAM,iBAAiB,KAAK;AACtD,QAAM,WAAW,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,YAAY,CAAC;AAExD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO;AAAA,QACL,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,WAAW;AAAA,QACX,SAAS;AAAA,QACT,WAAW;AAAA,QACX,SAAS;AAAA,QACT,YAAY;AAAA,MACd;AAAA,MAEA,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL,SAAS;AAAA,YACT,YAAY;AAAA,UACd;AAAA,UAEC,sBAAY,MAAM,IAAI,CAAC,MAAM,MAAM;AAClC,kBAAM,WAAW,MAAM;AACvB,mBACE,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC,OAAO;AAAA,kBACL;AAAA,kBACA,UAAU,GAAG,QAAQ;AAAA,kBACrB,YAAY,WAAW,MAAM;AAAA,kBAC7B,OAAO,WAAW,eAAe;AAAA,kBACjC,YAAY;AAAA,kBACZ,aAAa,IAAI,YAAa,MAAM,SAAS,IAAI,UAAU;AAAA,kBAC3D,YAAY;AAAA,kBACZ,eAAe;AAAA,gBACjB;AAAA,gBAEC,eAAK;AAAA;AAAA,cAZD,GAAG,KAAK,SAAS,IAAI,CAAC;AAAA,YAa7B;AAAA,UAEJ,CAAC;AAAA;AAAA,MACH;AAAA;AAAA,EACF;AAEJ;;;AD/MM,gBAAAC,aAAA;AAZC,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AAAA,EACA;AACF,GAAwB;AAEtB,MAAI,iBAAiB,UAAU;AAC7B,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,EAEJ;AAGA,QAAM,SAAS,WAAW,WAAW,iBAAiB,UAAU,WAAW,IAAI;AAC/E,QAAM,cAAc,QAAQ,QAAQ;AAEpC,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO;AAAA,QACL,UAAU;AAAA,QACV,KAAK;AAAA,QACL,MAAM;AAAA,QACN,WAAW;AAAA,QACX,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,UAAU;AAAA,QACV,OAAO;AAAA,QACP,WAAW;AAAA,QACX,SAAS,cAAc,IAAI;AAAA,QAC3B,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,WAAW;AAAA,MACb;AAAA,MAEC,yBACC,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS;AAAA,YACT,YAAY;AAAA,YACZ,cAAc;AAAA,YACd,gBAAgB;AAAA,UAClB;AAAA,UAEA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,gBACL,OAAO;AAAA,gBACP,UAAU,GAAG,QAAQ;AAAA,gBACrB,YAAY;AAAA,gBACZ,YAAY;AAAA,gBACZ,YAAY;AAAA,gBACZ,YAAY;AAAA,gBACZ,YAAY;AAAA,gBACZ,UAAU;AAAA,cACZ;AAAA,cAEC;AAAA;AAAA,UACH;AAAA;AAAA,MACF;AAAA;AAAA,EAEJ;AAEJ;;;AE5GA,SAAS,aAAa,WAAAC,UAAS,4BAA4B;AAC3D,SAAS,cAAc,qBAAyC;AAEhE,IAAM,aAAa;AACnB,IAAM,oBAAoB,MAAM;AAUzB,SAAS,eAAe,SAAiC;AAC9D,QAAM,MAAMA;AAAA,IACV,MAAO,WAAW,OAAO,WAAW,cAAc,OAAO,WAAW,UAAU,IAAI;AAAA,IAClF,CAAC,OAAO;AAAA,EACV;AAEA,QAAM,YAAY;AAAA,IAChB,CAAC,OAAmB;AAClB,UAAI,CAAC,IAAK,QAAO,MAAM;AAAA,MAAC;AACxB,UAAI,iBAAiB,UAAU,EAAE;AACjC,aAAO,MAAM,IAAI,oBAAoB,UAAU,EAAE;AAAA,IACnD;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AAEA,QAAM,cAAc,YAAY,MAAO,KAAK,UAAU,eAAe,eAAgB,CAAC,GAAG,CAAC;AAE1F,SAAO,qBAAqB,WAAW,aAAa,iBAAiB;AACvE;;;AClBA,SAAS,YAAAC,WAAU,aAAAC,YAAW,UAAAC,SAAQ,eAAAC,oBAAmB;AAKlD,SAAS,aACd,UACA,YACA,WAAmB,IACF;AACjB,QAAM,CAAC,aAAa,cAAc,IAAIH,UAAS,CAAC;AAChD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,CAAC;AACtD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAC5C,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAS,KAAK;AACtD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,CAAC;AAGpD,QAAM,gBAAgBE,QAAiB,CAAC,CAAC;AAGzC,QAAM,kBAAkBA,QAAsB,IAAI;AAClD,QAAM,sBAAsBA,QAAO,KAAK;AAGxC,QAAM,WAAWA,QAA4B,oBAAI,IAAI,CAAC;AACtD,QAAM,kBAAkBA,QAAqC,oBAAI,IAAI,CAAC;AAItE,QAAM,eAAeA,QAAO,KAAK;AAEjC,EAAAD,WAAU,MAAM;AACd,QAAI,CAAC,YAAY,UAAU;AACzB;AAAA,IACF;AAEA,QAAI,OAAO;AACX,kBAAc,UAAU,WAAW,SAAS,IAAI,CAAC,QAAQ;AACvD,YAAM,QAAQ;AACd,cAAQ,IAAI;AACZ,aAAO;AAAA,IACT,CAAC;AACD,qBAAiB,IAAI;AAAA,EACvB,GAAG,CAAC,UAAU,CAAC;AAGf,QAAM,eAAeE;AAAA,IACnB,OAAO,QAAiC;AACtC,YAAM,WAAW,WAAW,GAAG,QAAQ,IAAI,GAAG,KAAK;AAGnD,UAAI,SAAS,QAAQ,IAAI,GAAG,GAAG;AAC7B,eAAO,SAAS,QAAQ,IAAI,GAAG;AAAA,MACjC;AAGA,UAAI,gBAAgB,QAAQ,IAAI,GAAG,GAAG;AACpC,eAAO,gBAAgB,QAAQ,IAAI,GAAG;AAAA,MACxC;AAGA,YAAM,eAAe,YAAY;AAC/B,YAAI;AACF,gBAAM,WAAW,MAAM,MAAM,QAAQ;AACrC,cAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,QAAQ,SAAS,MAAM,EAAE;AAC3D,gBAAM,OAAO,MAAM,SAAS,KAAK;AACjC,gBAAM,UAAU,IAAI,gBAAgB,IAAI;AACxC,mBAAS,QAAQ,IAAI,KAAK,OAAO;AACjC,iBAAO;AAAA,QACT,QAAQ;AAEN,iBAAO;AAAA,QACT,UAAE;AACA,0BAAgB,QAAQ,OAAO,GAAG;AAAA,QACpC;AAAA,MACF,GAAG;AAEH,sBAAgB,QAAQ,IAAI,KAAK,WAAW;AAC5C,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAGA,EAAAF,WAAU,MAAM;AACd,QAAI,CAAC,YAAY,SAAU;AAG3B,eAAW,SAAS,QAAQ,CAAC,YAAY;AACvC,mBAAa,QAAQ,GAAG;AAAA,IAC1B,CAAC;AAGD,UAAM,kBAAkB,SAAS;AACjC,WAAO,MAAM;AACX,sBAAgB,QAAQ,CAAC,QAAQ;AAC/B,YAAI,gBAAgB,GAAG;AAAA,MACzB,CAAC;AACD,sBAAgB,MAAM;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,YAAY,YAAY,CAAC;AAG7B,EAAAA,WAAU,MAAM;AACd,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,MAAO;AAEZ,UAAM,mBAAmB,MAAM;AAO7B,UAAI,aAAa,QAAS;AAE1B,YAAM,eAAe,cAAc,QAAQ,cAAc,KAAK;AAC9D,YAAM,cAAc,eAAe,MAAM;AACzC,qBAAe,WAAW;AAAA,IAC5B;AAEA,UAAM,aAAa,MAAM;AAQvB,mBAAa,IAAI;AAAA,IACnB;AACA,UAAM,cAAc,MAAM,aAAa,KAAK;AAC5C,UAAM,cAAc,MAAM;AAGxB,sBAAgB,IAAI;AAAA,IACtB;AACA,UAAM,cAAc,MAAM;AAExB,UAAI,cAAc,iBAAiB,WAAW,SAAS,SAAS,GAAG;AAGjE,4BAAoB,UAAU;AAC9B,0BAAkB,CAAC,SAAS,OAAO,CAAC;AAAA,MACtC,OAAO;AACL,mBAAW,IAAI;AACf,qBAAa,KAAK;AAAA,MACpB;AAAA,IACF;AAEA,UAAM,iBAAiB,cAAc,gBAAgB;AACrD,UAAM,iBAAiB,QAAQ,UAAU;AACzC,UAAM,iBAAiB,SAAS,WAAW;AAC3C,UAAM,iBAAiB,SAAS,WAAW;AAC3C,UAAM,iBAAiB,SAAS,WAAW;AAE3C,WAAO,MAAM;AACX,YAAM,oBAAoB,cAAc,gBAAgB;AACxD,YAAM,oBAAoB,QAAQ,UAAU;AAC5C,YAAM,oBAAoB,SAAS,WAAW;AAC9C,YAAM,oBAAoB,SAAS,WAAW;AAC9C,YAAM,oBAAoB,SAAS,WAAW;AAAA,IAChD;AAAA,EACF,GAAG,CAAC,UAAU,gBAAgB,UAAU,CAAC;AAGzC,EAAAA,WAAU,MAAM;AACd,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,SAAS,CAAC,YAAY,SAAU;AAErC,UAAM,UAAU,WAAW,SAAS,cAAc;AAClD,QAAI,CAAC,QAAS;AAGd,UAAM,mBAAmB,MAAM;AAC7B,UAAI,gBAAgB,YAAY,MAAM;AACpC,cAAM,eAAe,cAAc,QAAQ,cAAc,KAAK;AAC9D,cAAM,cAAc,gBAAgB,UAAU;AAC9C,cAAM,cAAc,KAAK,IAAI,GAAG,WAAW;AAC3C,uBAAe,gBAAgB,OAAO;AACtC,wBAAgB,UAAU;AAAA,MAC5B;AAEA,UAAI,oBAAoB,SAAS;AAC/B,cAAM,KAAK,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAC3B,4BAAoB,UAAU;AAAA,MAChC;AAAA,IACF;AAIA,UAAM,aAAa,MAAM;AACzB,UAAM,gBAAgB,SAAS,QAAQ,IAAI,QAAQ,GAAG;AACtD,UAAM,eACJ,eAAe,eAAe,iBAAiB,WAAW,SAAS,QAAQ,GAAG;AAEhF,QAAI,CAAC,cAAc;AAEjB,YAAM,cAAc,YAAY;AAC9B,cAAM,UAAU,MAAM,aAAa,QAAQ,GAAG;AAE9C,cAAM,gBAAgB,MAAM;AAC1B,0BAAgB,IAAI;AACpB,2BAAiB;AACjB,gBAAM,oBAAoB,WAAW,aAAa;AAAA,QACpD;AAEA,cAAM,iBAAiB,WAAW,aAAa;AAC/C,cAAM,MAAM;AACZ,cAAM,KAAK;AAIX,cAAM,QAAQ,QAAQ;AACtB,YAAI,MAAM,cAAc,GAAG;AACzB,gBAAM,oBAAoB,WAAW,aAAa;AAClD,0BAAgB,IAAI;AACpB,2BAAiB;AAAA,QACnB;AAAA,MACF;AAEA,kBAAY;AAAA,IACd,OAAO;AAEL,uBAAiB;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,UAAU,gBAAgB,YAAY,YAAY,CAAC;AAEvD,QAAM,OAAOE,aAAY,MAAM;AAC7B,UAAM,QAAQ,SAAS;AACvB,QAAI,OAAO;AACT,UAAI,SAAS;AAEX,0BAAkB,CAAC;AACnB,mBAAW,KAAK;AAAA,MAClB;AACA,YACG,KAAK,EACL,KAAK,MAAM;AACV,qBAAa,UAAU;AAAA,MACzB,CAAC,EACA,MAAM,MAAM;AAGX,qBAAa,UAAU;AACvB,qBAAa,IAAI;AAAA,MACnB,CAAC;AAAA,IACL;AAAA,EACF,GAAG,CAAC,UAAU,OAAO,CAAC;AAEtB,QAAM,QAAQA,aAAY,MAAM;AAC9B,UAAM,QAAQ,SAAS;AACvB,QAAI,OAAO;AACT,YAAM,MAAM;AAAA,IACd;AAGA,iBAAa,KAAK;AAAA,EACpB,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,SAASA,aAAY,MAAM;AAC/B,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,MAAO;AAIZ,QAAI,CAAC,WAAW;AACd,WAAK;AAAA,IACP,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF,GAAG,CAAC,UAAU,WAAW,MAAM,KAAK,CAAC;AAErC,QAAM,SAASA;AAAA,IACb,CAAC,SAAiB;AAChB,YAAM,QAAQ,SAAS;AACvB,UAAI,CAAC,SAAS,CAAC,YAAY,SAAU;AAKrC,YAAM,cACJ,gBAAgB,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,MAAM,aAAa,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI;AAGnF,UAAI,eAAe;AACnB,UAAI,eAAe;AACnB,eAAS,IAAI,GAAG,IAAI,WAAW,SAAS,QAAQ,KAAK;AACnD,cAAM,SAAS,eAAe,WAAW,SAAS,CAAC,EAAE;AACrD,YAAI,cAAc,QAAQ;AACxB,yBAAe;AACf;AAAA,QACF;AACA,uBAAe;AAEf,YAAI,MAAM,WAAW,SAAS,SAAS,GAAG;AACxC,yBAAe;AAAA,QACjB;AAAA,MACF;AAEA,YAAM,aAAa,CAAC,MAAM;AAC1B,iBAAW,KAAK;AAGhB,UAAI,iBAAiB,gBAAgB;AAEnC,wBAAgB,UAAU;AAC1B,4BAAoB,UAAU;AAC9B,0BAAkB,YAAY;AAAA,MAChC,OAAO;AAEL,cAAM,cAAc,cAAc;AAClC,cAAM,cAAc,KAAK,IAAI,GAAG,WAAW;AAC3C,uBAAe,WAAW;AAAA,MAC5B;AAAA,IACF;AAAA,IACA,CAAC,UAAU,YAAY,gBAAgB,aAAa;AAAA,EACtD;AAEA,QAAM,gBAAgBA;AAAA,IACpB,CAAC,UAAkB;AACjB,UAAI,CAAC,YAAY,YAAY,QAAQ,KAAK,SAAS,WAAW,SAAS,QAAQ;AAC7E;AAAA,MACF;AACA,wBAAkB,KAAK;AACvB,iBAAW,KAAK;AAAA,IAClB;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AAGA,QAAM,UAAUA,aAAY,YAAY;AACtC,WAAO,CAAC;AAER,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACtD,SAAK;AAAA,EACP,GAAG,CAAC,QAAQ,IAAI,CAAC;AAKjB,EAAAF,WAAU,MAAM;AACd,QAAI,CAAC,aAAa,CAAC,aAAa,WAAW,CAAC,cAAe;AAE3D,QAAI,WAAW,YAAY,IAAI;AAC/B,QAAI;AAEJ,UAAM,OAAO,CAAC,QAAgB;AAE5B,UAAI,CAAC,aAAa,QAAS;AAE3B,YAAM,MAAM,MAAM,YAAY;AAC9B,iBAAW;AAEX,qBAAe,CAAC,SAAS;AACvB,cAAM,OAAO,OAAO;AACpB,YAAI,QAAQ,eAAe;AACzB,uBAAa,UAAU;AACvB,qBAAW,IAAI;AACf,uBAAa,KAAK;AAClB,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT,CAAC;AAED,YAAM,sBAAsB,IAAI;AAAA,IAClC;AAEA,UAAM,sBAAsB,IAAI;AAChC,WAAO,MAAM,qBAAqB,GAAG;AAAA,EACvC,GAAG,CAAC,WAAW,aAAa,CAAC;AAE7B,SAAO;AAAA;AAAA,IAEL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,aAAa;AAAA;AAAA;AAAA,IAEb,MAAM,YAAY,KAAK;AAAA,IACvB,OAAO,YAAY,MAAM;AAAA,IACzB,QAAQ,YAAY,OAAO;AAAA,IAC3B,QAAQ,OAAO,SAAiB,OAAO,IAAI;AAAA,IAC3C,eAAe,OAAO,UAAkB,cAAc,KAAK;AAAA,IAC3D;AAAA,EACF;AACF;;;ACtYA,SAAS,WAAAG,UAAS,eAAAC,cAAa,UAAAC,eAAc;AAG7C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,6BAAAC;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAgCA,SAAS,eACd,QACA,aACA,WAA2B,iBAAiB,WAC5C,aAAsB,OACtB,OACiC;AAGjC,OAAK;AAEL,QAAM,SAASH,SAAQ,MAAM;AAC3B,QAAI,CAAC,QAAQ,QAAQ;AACnB,aAAO,CAAC;AAAA,IACV;AAMA,UAAM,cAAc,OAAO,OAAO,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,SAAS,CAAC;AACjF,UAAM,aAAa,cAAc,wBAAwB,OAAO,MAAM,IAAI,OAAO;AAGjF,UAAM,eAAe,WAAW,KAAK,eAAe;AAKpD,UAAM,gBAAgB,SAAS;AAC/B,UAAM,mBAAmB;AAAA,MACvB,EAAE,kBAAkB,OAAO,iBAAiB;AAAA,MAC5C;AAAA,IACF;AAEA,QAAI,cAAc;AAEhB,YAAM,gBAAgB,OAAO,OAAO,UAAU,IAAI,CAAC,SAAS;AAAA,QAC1D,WAAW,IAAI;AAAA,QACf,UAAU,IAAI;AAAA,MAChB,EAAE;AAGF,YAAM,WAAW,gBAAgB,YAA0B;AAAA,QACzD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA;AAAA;AAAA;AAAA,QAKA,iBAAiB,OAAO;AAAA,MAC1B,CAAC;AACD,aAAO;AAAA,IACT;AAIA,WAAO,WAAW,IAAI,CAAC,OAAO,UAAU;AACtC,YAAM,aAAa,uBAAuB,OAAO,eAAe,KAAK;AACrE,aAAO,eAAe,MAAM,aAAa,EAAE,GAAG,OAAO,WAAW,IAAI;AAAA,IACtE,CAAC;AAAA,EACH,GAAG;AAAA,IACD,QAAQ;AAAA,IACR,QAAQ,OAAO;AAAA,IACf,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,EACF,CAAC;AAGD,QAAM,eAAeA,SAAQ,MAAM,eAAe,QAAQ,WAAW,GAAG,CAAC,QAAQ,WAAW,CAAC;AAE7F,QAAM,oBAAoBA;AAAA,IACxB,MAAO,eAAe,OAAO,QAAQ,YAAY,IAAI;AAAA,IACrD,CAAC,QAAQ,YAAY;AAAA,EACvB;AAGA,QAAM,YAAYA,SAAQ,MAAM;AAC9B,QAAI,CAAC,aAAc,QAAO;AAC1B,WAAO,KAAK,IAAI,GAAG,cAAc,aAAa,SAAS;AAAA,EACzD,GAAG,CAAC,cAAc,WAAW,CAAC;AAG9B,QAAM,gBAAgBA,SAAQ,MAAM;AAClC,QAAI,CAAC,gBAAgB,aAAa,aAAa,EAAG,QAAO;AACzD,WAAO,KAAK,IAAI,GAAG,YAAY,aAAa,QAAQ;AAAA,EACtD,GAAG,CAAC,cAAc,SAAS,CAAC;AAE5B,QAAM,cAAcA,SAAQ,MAAM;AAChC,QAAI,CAAC,UAAU,OAAO,aAAa,EAAG,QAAO;AAC7C,WAAO,KAAK,IAAI,GAAG,cAAc,OAAO,QAAQ;AAAA,EAClD,GAAG,CAAC,QAAQ,WAAW,CAAC;AAgBxB,QAAM,mBAAmBE,QAAqB,IAAI;AAClD,QAAM,mBAAmBA,QAAsB,IAAI;AACnD,QAAM,uBAAuBA,QAAqB,IAAI;AACtD,MAAI,gBAAgB,aAAa,OAAO,iBAAiB,SAAS;AAChE,qBAAiB,UAAU,qBAAqB;AAChD,qBAAiB,UAAU,aAAa;AAAA,EAC1C;AACA,uBAAqB,UAAU;AAE/B,QAAM,qBAAqB,cAAc,aACrCC,2BAA0B,aAAa,UAAU,IACjD;AACJ,QAAM,aAAa,CAAC,CAAC,gBAAgB,qBAAqB,KAAK,YAAY;AAC3E,QAAM,gBAAgB,iBAAiB;AAGvC,QAAM,YAAY,cAAc,iBAAiB,QAAQ,cAAc,OAAO,cAAc;AAC5F,QAAM,gBAAgB,YAAY,gBAAgB;AAGlD,QAAM,YAAYF;AAAA,IAChB,CAAC,UAAkB;AACjB,UAAI,CAAC,UAAU,QAAQ,KAAK,SAAS,OAAO,OAAQ;AAGpD,YAAM,cAAc,OAAO,KAAK;AAChC,UAAI,aAAa;AAEf,eAAO,YAAY;AAAA,MACrB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,MAAM;AAAA,EACjB;AAEA,QAAM,YAAYA,aAAY,MAAM;AAClC,QAAI,oBAAoB,OAAO,SAAS,GAAG;AACzC,aAAO,UAAU,oBAAoB,CAAC;AAAA,IACxC;AAAA,EACF,GAAG,CAAC,mBAAmB,OAAO,QAAQ,SAAS,CAAC;AAEhD,QAAM,YAAYA,aAAY,MAAM;AAClC,QAAI,oBAAoB,GAAG;AACzB,aAAO,UAAU,oBAAoB,CAAC;AAAA,IACxC;AAAA,EACF,GAAG,CAAC,mBAAmB,SAAS,CAAC;AAEjC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,EACF;AACF;;;AC7NA,SAAS,YAAAG,WAAU,aAAAC,YAAW,WAAAC,gBAAe;AAC7C;AAAA,EACE,oBAAAC;AAAA,OAGK;AAcP,SAAS,yBAAyB,OAAe,QAAqC;AACpF,QAAM,QAAQ,QAAQ;AAMtB,MAAI,QAAQ,KAAK;AACf,WAAO;AAAA,EACT,WAAW,QAAQ,MAAM;AACvB,WAAO;AAAA,EACT,OAAO;AAGL,WAAO;AAAA,EACT;AACF;AAKA,SAAS,0BAA0B,aAAkD;AACnF,UAAQ,aAAa;AAAA,IACnB,KAAK;AACH,aAAOA,kBAAiB;AAAA,IAC1B,KAAK;AACH,aAAOA,kBAAiB;AAAA,IAC1B,KAAK;AAAA,IACL;AACE,aAAOA,kBAAiB;AAAA,EAC5B;AACF;AAMO,SAAS,yBAAuD;AACrE,QAAM,CAAC,YAAY,aAAa,IAAIH,UAAS,OAAO;AAAA,IAClD,OAAO,OAAO,WAAW,cAAc,OAAO,aAAa;AAAA,IAC3D,QAAQ,OAAO,WAAW,cAAc,OAAO,cAAc;AAAA,EAC/D,EAAE;AAGF,EAAAC,WAAU,MAAM;AACd,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,eAAe,MAAM;AACzB,oBAAc;AAAA,QACZ,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,MACjB,CAAC;AAAA,IACH;AAGA,QAAI;AACJ,UAAM,kBAAkB,MAAM;AAC5B,mBAAa,SAAS;AACtB,kBAAY,WAAW,cAAc,GAAG;AAAA,IAC1C;AAEA,WAAO,iBAAiB,UAAU,eAAe;AACjD,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,eAAe;AACpD,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,QAAM,cAAcC;AAAA,IAClB,MAAM,yBAAyB,WAAW,OAAO,WAAW,MAAM;AAAA,IAClE,CAAC,WAAW,OAAO,WAAW,MAAM;AAAA,EACtC;AAGA,QAAM,WAAWA,SAAQ,MAAM,0BAA0B,WAAW,GAAG,CAAC,WAAW,CAAC;AAEpF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AChGA,SAAS,eAAAE,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AAIzD,IAAM,iBAAiB;AAEvB,IAAM,iBAAiB;AAEvB,IAAM,qBAAqB;AAE3B,IAAM,cAAc;AAEpB,IAAM,oBAAoB;AA2BnB,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsC;AACpC,QAAM,oBAAoB,QAAQ,IAAI,QAAQ,iBAAiB;AAC/D,QAAM,WAAW,YAAY,IAAI,KAAK,IAAI,EAAE,IAAI,YAAY;AAC5D,QAAM,iBAAiB,KAAK,IAAI,EAAE,KAAK;AACvC,QAAM,cAAc,YAAY,kBAAkB,KAAK,IAAI,EAAE,KAAK;AAElE,MAAI,CAAC,kBAAkB,CAAC,YAAa,QAAO;AAC5C,MAAI,KAAK,EAAG,QAAO,UAAU,SAAS;AACtC,MAAI,KAAK,EAAG,QAAO,UAAU,SAAS;AACtC,SAAO;AACT;AAsCO,SAAS,cAAc,MAAiD;AAC7E,QAAM,WAAW,KAAK,YAAY;AAElC,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,CAAC;AAC1C,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAqB,MAAM;AAIrD,QAAM,UAAUD,QAAO,IAAI;AAC3B,UAAQ,UAAU;AAElB,QAAM,UAAUA,QAAyB,IAAI;AAC7C,QAAM,WAAWA,QAAmB,MAAM;AAC1C,WAAS,UAAU;AACnB,QAAM,cAAcA,QAA6C,IAAI;AACrE,QAAM,YAAYA,QAAsB,IAAI;AAE5C,QAAM,eAAeF,aAAY,MAAM;AACrC,QAAI,YAAY,WAAW,MAAM;AAC/B,mBAAa,YAAY,OAAO;AAChC,kBAAY,UAAU;AAAA,IACxB;AACA,QAAI,UAAU,WAAW,MAAM;AAC7B,2BAAqB,UAAU,OAAO;AACtC,gBAAU,UAAU;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAgBA,aAAY,CAAC,MAA0B;AAC3D,UAAM,IAAI,QAAQ;AAClB,QAAI,CAAC,EAAE,QAAS;AAEhB,QAAI,SAAS,YAAY,WAAY;AAErC,QAAI,EAAE,gBAAgB,WAAW,EAAE,WAAW,EAAG;AAEjD,UAAM,SAAS,EAAE;AACjB,QAAI,OAAO,UAAU,qDAAqD,EAAG;AAE7E,YAAQ,UAAU;AAAA,MAChB,WAAW,EAAE;AAAA,MACb,QAAQ,EAAE;AAAA,MACV,WAAW,YAAY,IAAI;AAAA,MAC3B;AAAA,IACF;AACA,QAAI;AACF,aAAO,oBAAoB,EAAE,SAAS;AAAA,IACxC,QAAQ;AAAA,IAER;AACA,aAAS,UAAU;AACnB,gBAAY,CAAC;AAAA,EACf,GAAG,CAAC,CAAC;AAIL,EAAAC,WAAU,MAAM;AACd,aAAS,eAAuB;AAC9B,aAAO,QAAQ,QAAQ,aAAa,SAAS,sBAAsB,EAAE,SAAS;AAAA,IAChF;AAEA,aAAS,QAAQ,MAAiB;AAChC,cAAQ,UAAU;AAClB,UAAI;AACF,aAAK,OAAO,wBAAwB,KAAK,SAAS;AAAA,MACpD,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,aAAS,SAAS,QAAgB,UAAuB;AAGvD,eAAS,UAAU;AACnB,gBAAU,UAAU,sBAAsB,MAAM;AAC9C,kBAAU,UAAU;AACpB,oBAAY,MAAM;AAClB,oBAAY,UAAU,WAAW,MAAM;AACrC,sBAAY,UAAU;AACtB,qBAAW;AACX,sBAAY,CAAC;AACb,mBAAS,MAAM;AAAA,QACjB,GAAG,QAAQ;AAAA,MACb,CAAC;AAAA,IACH;AAEA,aAAS,OAAO,GAAiB;AAC/B,YAAM,OAAO,QAAQ;AACrB,UAAI,CAAC,QAAQ,EAAE,cAAc,KAAK,UAAW;AAC7C,YAAM,IAAI,QAAQ;AAClB,YAAM,MAAM,EAAE,UAAU,KAAK;AAC7B,YAAM,UAAW,MAAM,KAAK,CAAC,EAAE,aAAe,MAAM,KAAK,CAAC,EAAE;AAC5D,kBAAY,UAAU,MAAM,cAAc,GAAG;AAAA,IAC/C;AAEA,aAAS,KAAK,GAAiB;AAC7B,YAAM,OAAO,QAAQ;AACrB,UAAI,CAAC,QAAQ,EAAE,cAAc,KAAK,UAAW;AAC7C,cAAQ,IAAI;AACZ,YAAM,IAAI,QAAQ;AAClB,YAAM,QAAQ,EAAE,UAAU,KAAK;AAC/B,YAAM,QAAQ,aAAa;AAC3B,YAAM,YAAY,YAAY,IAAI,IAAI,KAAK;AAC3C,YAAM,WAAW,YAAY;AAAA,QAC3B,IAAI;AAAA,QACJ;AAAA,QACA;AAAA,QACA,SAAS,EAAE;AAAA,QACX,SAAS,EAAE;AAAA,MACb,CAAC;AAED,UAAI,aAAa,QAAQ;AACvB,iBAAS,CAAC;AACV;AAAA,MACF;AAGA,YAAM,WAAW,KAAK,IAAI,OAAO,KAAK,IAAI,KAAK,CAAC;AAChD,YAAM,SAAS,aAAa,SAAS,CAAC,WAAW;AACjD,eAAS,QAAQ,aAAa,SAAS,EAAE,SAAS,EAAE,MAAM;AAAA,IAC5D;AAEA,aAAS,SAAS,GAAiB;AACjC,YAAM,OAAO,QAAQ;AACrB,UAAI,CAAC,QAAQ,EAAE,cAAc,KAAK,UAAW;AAC7C,cAAQ,IAAI;AACZ,eAAS,CAAC;AAAA,IACZ;AAEA,WAAO,iBAAiB,eAAe,MAAM;AAC7C,WAAO,iBAAiB,aAAa,IAAI;AACzC,WAAO,iBAAiB,iBAAiB,QAAQ;AACjD,WAAO,MAAM;AACX,aAAO,oBAAoB,eAAe,MAAM;AAChD,aAAO,oBAAoB,aAAa,IAAI;AAC5C,aAAO,oBAAoB,iBAAiB,QAAQ;AAAA,IACtD;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAGb,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,KAAK,SAAS;AACjB,cAAQ,UAAU;AAClB,mBAAa;AACb,eAAS,MAAM;AACf,kBAAY,CAAC;AAAA,IACf;AAAA,EACF,GAAG,CAAC,KAAK,SAAS,YAAY,CAAC;AAG/B,EAAAA,WAAU,MAAM,cAAc,CAAC,YAAY,CAAC;AAE5C,SAAO,EAAE,UAAU,OAAO,cAAc;AAC1C;;;AtB7NA;AAAA,EACE;AAAA,EACA;AAAA,EACA,iBAAAG;AAAA,EACA,iBAAAC;AAAA,EACA,oBAAAC;AAAA,OAEK;AACP,SAAS,iBAAAC,sBAAqB;;;AuBjC9B,SAAS,UAAAC,SAAQ,YAAAC,WAAU,eAAAC,oBAAmB;;;ACgKvC,SAAS,WAAW,SAAyB;AAClD,QAAM,OAAO,KAAK,MAAM,UAAU,EAAE;AACpC,QAAM,OAAO,KAAK,MAAM,UAAU,EAAE;AACpC,SAAO,GAAG,IAAI,IAAI,KAAK,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AACpD;;;ADnFM,gBAAAC,OAmEE,QAAAC,aAnEF;AAlEC,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwB;AACtB,QAAM,iBAAiBC,QAAuB,IAAI;AAClD,QAAM,CAAC,eAAe,gBAAgB,IAAIC,UAAwB,IAAI;AAQtE,QAAM,eACJ,MAAM,gBAAgB,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,MAAM,cAAc,MAAM,aAAa,CAAC,IAAI;AAEhG,QAAM,sBAAsBC,aAAY,CAAC,MAAwB;AAC/D,UAAM,MAAM,eAAe;AAC3B,QAAI,CAAC,IAAK;AACV,UAAM,OAAO,IAAI,sBAAsB;AACvC,UAAM,IAAI,EAAE,UAAU,KAAK;AAC3B,UAAM,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,IAAI,KAAK,KAAK,CAAC;AACxD,qBAAiB,QAAQ;AAAA,EAC3B,GAAG,CAAC,CAAC;AAEL,QAAM,sBAAsBA,aAAY,MAAM;AAC5C,qBAAiB,IAAI;AAAA,EACvB,GAAG,CAAC,CAAC;AAEL,QAAM,sBAAsBA;AAAA,IAC1B,CAAC,SAAyD;AACxD,eAAS,IAAI,eAAe,SAAS,GAAG,KAAK,GAAG,KAAK;AACnD,cAAM,MAAM,eAAe,CAAC;AAC5B,YAAI,QAAQ,IAAI,WAAW;AACzB,iBAAO,EAAE,OAAO,KAAK,OAAO,EAAE;AAAA,QAChC;AAAA,MACF;AACA,aAAO,eAAe,SAAS,IAAI,EAAE,OAAO,eAAe,CAAC,GAAG,OAAO,EAAE,IAAI;AAAA,IAC9E;AAAA,IACA,CAAC,cAAc;AAAA,EACjB;AAEA,SACE,gBAAAH;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,OAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,SAAS;AAAA,QACT,YAAY;AAAA,MACd;AAAA,MACA,SAAS,CAAC,MAAM;AACd,cAAM,OAAQ,EAAE,cAA8B,sBAAsB;AACpE,cAAM,IAAI,EAAE,UAAU,KAAK;AAC3B,cAAM,WAAW,IAAI,KAAK;AAC1B,gBAAQ,OAAO,WAAW,MAAM,aAAa;AAAA,MAC/C;AAAA,MACA,aAAa;AAAA,MACb,cAAc;AAAA,MAGd;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU;AAAA,cACV,MAAM;AAAA,cACN,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,YAAY;AAAA,cACZ,cAAc;AAAA,YAChB;AAAA;AAAA,QACF;AAAA,QAYA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,OAAO;AAAA,cACL,UAAU;AAAA,cACV,MAAM;AAAA,cACN,OAAO,GAAG,eAAe,GAAG;AAAA,cAC5B,QAAQ;AAAA,cACR,YAAY;AAAA,cACZ,cAAc;AAAA,YAChB;AAAA;AAAA,QACF;AAAA,QAGC,aAAa,IAAI,CAAC,QAAQ,MACzB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,OAAO;AAAA,cACL,UAAU;AAAA,cACV,MAAM,GAAG,OAAO,QAAQ;AAAA,cACxB,WAAW;AAAA,cACX,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,cAAc;AAAA,cACd,YACE,OAAO,UAAU,MAAM,oBAAoB,YAAY;AAAA,cACzD,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,YAAY;AAAA,YACd;AAAA,YACA,OAAO,OAAO;AAAA,YACd,SAAS,CAAC,MAAM;AACd,gBAAE,gBAAgB;AAClB,sBAAQ,OAAO,OAAO,MAAM,SAAS;AAAA,YACvC;AAAA,YACA,cAAc,CAAC,MAAM;AACnB,cAAC,EAAE,cAA8B,MAAM,YAAY;AAAA,YACrD;AAAA,YACA,cAAc,CAAC,MAAM;AACnB,cAAC,EAAE,cAA8B,MAAM,YAAY;AAAA,YACrD;AAAA;AAAA,UAzBK,GAAG,OAAO,MAAM,EAAE,IAAI,CAAC;AAAA,QA0B9B,CACD;AAAA,QAGA,kBAAkB,QACjB,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU;AAAA,cACV,MAAM,GAAG,gBAAgB,GAAG;AAAA,cAC5B,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,cAAc;AAAA,cACd,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,cAAc;AAAA,cACd,YAAY;AAAA,cACZ,eAAe;AAAA,cACf,QAAQ;AAAA,YACV;AAAA,YAEA;AAAA,8BAAAD,MAAC,SAAI,OAAO,EAAE,OAAO,SAAS,UAAU,QAAQ,YAAY,YAAY,GACrE,qBAAW,gBAAgB,MAAM,aAAa,GACjD;AAAA,eACE,MAAM;AACN,sBAAM,YAAY,gBAAgB,MAAM;AACxC,sBAAM,YAAY,oBAAoB,SAAS;AAC/C,oBAAI,aAAa,eAAe;AAC9B,yBACE,gBAAAA,MAAC,SAAI,OAAO,EAAE,OAAO,yBAAyB,UAAU,QAAQ,WAAW,MAAM,GAC9E,wBAAc,UAAU,KAAK,GAChC;AAAA,gBAEJ;AACA,uBAAO;AAAA,cACT,GAAG;AAAA;AAAA;AAAA,QACL;AAAA,QAID,kBAAkB,QACjB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU;AAAA,cACV,MAAM,GAAG,gBAAgB,GAAG;AAAA,cAC5B,KAAK;AAAA,cACL,WAAW;AAAA,cACX,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,YAAY;AAAA,cACZ,eAAe;AAAA,cACf,QAAQ;AAAA,YACV;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;;;AEnJU,gBAAAK,OAmCJ,QAAAC,aAnCI;AAxCH,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA4B;AAC1B,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO;AAAA,QACL,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,OAAO;AAAA,QACP,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,KAAK;AAAA,QACL,QAAQ;AAAA,MACV;AAAA,MAGA;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,QAAQ;AAAA,YACjB,OAAO;AAAA,cACL,YAAY;AAAA,cACZ,QAAQ;AAAA,cACR,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,UAAU;AAAA,cACV,SAAS;AAAA,cACT,YAAY;AAAA,YACd;AAAA,YACA,OAAM;AAAA,YACN,cAAW;AAAA,YAEX,0BAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,yGAAwG,GAClH;AAAA;AAAA,QACF;AAAA,QAGA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,QAAQ;AAAA,YACjB,OAAO;AAAA,cACL,YAAY;AAAA,cACZ,QAAQ;AAAA,cACR,cAAc;AAAA,cACd,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,UAAU;AAAA,cACV,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,gBAAgB;AAAA,cAChB,OAAO;AAAA,cACP,QAAQ;AAAA,YACV;AAAA,YACA,cAAY,MAAM,YAAY,UAAU;AAAA,YAEvC,gBAAM,YACL,gBAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,mCAAkC,GAC5C,IAEA,gBAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,iBAAgB,GAC1B;AAAA;AAAA,QAEJ;AAAA,QAGA,gBAAAC,MAAC,UAAK,OAAO,EAAE,OAAO,SAAS,UAAU,QAAQ,UAAU,QAAQ,YAAY,YAAY,GACxF;AAAA,qBAAW,MAAM,WAAW;AAAA,UAAE;AAAA,UAAI,WAAW,MAAM,aAAa;AAAA,WACnE;AAAA,QAGA,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QAGA,gBAAAC,MAAC,UAAK,OAAO,EAAE,OAAO,yBAAyB,UAAU,QAAQ,YAAY,SAAS,GACnF;AAAA,gBAAM,oBAAoB;AAAA,UAAE;AAAA,UAAE,MAAM;AAAA,WACvC;AAAA,QAGC,MAAM,eACL,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,QAAQ,iBAAiB;AAAA,YACxC,OAAO;AAAA,cACL,YAAY,MAAM,gBAAgB,QAAQ,0BAA0B;AAAA,cACpE,QAAQ;AAAA,cACR,OAAO,MAAM,gBAAgB,QAAQ,UAAU;AAAA,cAC/C,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,UAAU;AAAA,cACV,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,KAAK;AAAA,cACL,cAAc;AAAA,YAChB;AAAA,YACA,OACE,MAAM,gBAAgB,QAClB,uCACA,MAAM,gBAAgB,aACpB,0CACA;AAAA,YAER,cAAW;AAAA,YAEX;AAAA,8BAAAD,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,uPAAsP,GAChQ;AAAA,cACC,MAAM,gBAAgB,SACrB,gBAAAA,MAAC,UAAK,OAAO,EAAE,UAAU,QAAQ,eAAe,aAAa,eAAe,QAAQ,GACjF,gBAAM,gBAAgB,aAAa,OAAO,MAC7C;AAAA;AAAA;AAAA,QAEJ;AAAA,QAID,QAAQ,oBACP,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,QAAQ;AAAA,YACjB,OAAO;AAAA,cACL,YAAY,MAAM,eAAe,0BAA0B;AAAA,cAC3D,QAAQ;AAAA,cACR,OAAO,MAAM,eAAe,UAAU;AAAA,cACtC,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,cAAc;AAAA,YAChB;AAAA,YACA,OAAO,MAAM,eAAe,wBAAwB;AAAA,YACpD,cAAY,MAAM,eAAe,oBAAoB;AAAA,YAEpD,gBAAM,eACL,gBAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,iFAAgF,GAC1F,IAEA,gBAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,kFAAiF,GAC3F;AAAA;AAAA,QAEJ;AAAA;AAAA;AAAA,EAEJ;AAEJ;;;ACxJI,SAiDM,OAAAE,OAjDN,QAAAC,cAAA;AAjBG,SAAS,qBAAqB,EAAE,OAAO,SAAS,GAA8B;AACnF,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,UAAU,qBAAqB;AACrC,QAAM,SAAS,qBAAqB,cAAc;AAClD,QAAM,cACJ,cAAc,IACT,qBACD,GAAG,sBAAsB,oBAAoB,CAAC,MAAM,oBAAoB,WAAW,KACnF;AAEN,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,eAAY;AAAA,MACZ,OAAO;AAAA,QACL,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,KAAK;AAAA,QACL,YAAY;AAAA,QACZ,cAAc;AAAA,QACd,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,sBAAsB;AAAA,MACxB;AAAA,MAGA;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,CAAC,MAAM;AACd,gBAAE,gBAAgB;AAClB,uBAAS,UAAU;AAAA,YACrB;AAAA,YACA,UAAU;AAAA,YACV,eAAY;AAAA,YACZ,cAAW;AAAA,YACX,OAAM;AAAA,YACN,OAAO;AAAA,cACL,YAAY;AAAA,cACZ,QAAQ;AAAA,cACR,OAAO,UAAU,0BAA0B;AAAA,cAC3C,QAAQ,UAAU,YAAY;AAAA,cAC9B,SAAS;AAAA,cACT,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,gBAAgB;AAAA,cAChB,cAAc;AAAA,cACd,YAAY;AAAA,YACd;AAAA,YACA,cAAc,CAAC,MAAM;AACnB,kBAAI,CAAC,QAAS,GAAE,cAAc,MAAM,aAAa;AAAA,YACnD;AAAA,YACA,cAAc,CAAC,MAAM;AACnB,gBAAE,cAAc,MAAM,aAAa;AAAA,YACrC;AAAA,YAEA,0BAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,iDAAgD,GAC1D;AAAA;AAAA,QACF;AAAA,QAGA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,OAAO;AAAA,cACL,OAAO;AAAA,cACP,UAAU;AAAA,cACV,YAAY;AAAA,cACZ,oBAAoB;AAAA,cACpB,UAAU;AAAA,cACV,WAAW;AAAA,cACX,SAAS;AAAA,cACT,eAAe;AAAA,YACjB;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QAGA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,CAAC,MAAM;AACd,gBAAE,gBAAgB;AAClB,uBAAS,UAAU;AAAA,YACrB;AAAA,YACA,UAAU;AAAA,YACV,eAAY;AAAA,YACZ,cAAW;AAAA,YACX,OAAM;AAAA,YACN,OAAO;AAAA,cACL,YAAY;AAAA,cACZ,QAAQ;AAAA,cACR,OAAO,SAAS,0BAA0B;AAAA,cAC1C,QAAQ,SAAS,YAAY;AAAA,cAC7B,SAAS;AAAA,cACT,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,gBAAgB;AAAA,cAChB,cAAc;AAAA,cACd,YAAY;AAAA,YACd;AAAA,YACA,cAAc,CAAC,MAAM;AACnB,kBAAI,CAAC,OAAQ,GAAE,cAAc,MAAM,aAAa;AAAA,YAClD;AAAA,YACA,cAAc,CAAC,MAAM;AACnB,gBAAE,cAAc,MAAM,aAAa;AAAA,YACrC;AAAA,YAEA,0BAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,iDAAgD,GAC1D;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EACF;AAEJ;;;AC3HA,SAAS,WAAAE,gBAAe;AAIxB;AAAA,EACE;AAAA,EACA,qBAAAC;AAAA,OAGK;AACP,SAAS,oBAAAC,yBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,iBAAAC;AAAA,EACA;AAAA,OACK;AAEP,SAAS,kBAAkB,qBAAqB;;;ACtBhD,SAAS,YAAAC,iBAAgB;AACzB;AAAA,EACE,qBAAAC;AAAA,EACA;AAAA,OASK;;;AC4BD,gBAAAC,aAAA;AApBC,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,UAAU;AAAA,EACV;AACF,GAA2B;AACzB,QAAM,cAAc,YAAY,KAAK,QAAQ;AAG7C,QAAM,iBAAiB,YAAY,UAAU,IAAI,QAAQ;AACzD,QAAM,YAAY,SAAS,iBAAiB;AAE5C,MAAI,CAAC,YAAa,QAAO;AAEzB,SACE,gBAAAA,MAAC,UAAK,WAAW,8BAA8B,aAAa,EAAE,GAAG,KAAK,GACpE,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAW;AAAA;AAAA,EACb,GACF;AAEJ;;;AC3BM,gBAAAC,aAAA;AAbC,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA,WAAW;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AAAA,EACV;AACF,GAA2B;AACzB,QAAM,cAAc,YAAY,KAAK,QAAQ;AAE7C,MAAI,CAAC,YAAa,QAAO;AAEzB,SACE,gBAAAA,MAAC,UAAK,WAAW,8BAA8B,aAAa,EAAE,GAAG,KAAK,GACpE,0BAAAA,MAAC,WAAM,KAAK,aAAa,UAAoB,SAAkB,GACjE;AAEJ;;;AFkCiB,gBAAAC,OAMH,QAAAC,cANG;AAlBjB,IAAM,cAAyB,EAAE,YAAY,WAAW;AAKxD,SAAS,aACP,OACA,YAAY,IACZ,MAAiB,aACE;AACnB,SAAO,MAAM,IAAI,CAAC,MAAM,MAAM;AAC5B,UAAM,MAAM,GAAG,SAAS,IAAI,CAAC;AAC7B,YAAQ,KAAK,MAAM;AAAA,MACjB,KAAK,QAAQ;AAIX,YAAI,CAAC,KAAK,MAAM,SAAS,IAAI,GAAG;AAC9B,iBAAO,gBAAAD,MAACE,WAAA,EAAoB,eAAK,SAAX,GAAiB;AAAA,QACzC;AACA,cAAM,QAAQ,KAAK,MAAM,MAAM,IAAI;AACnC,eACE,gBAAAF,MAACE,WAAA,EACE,gBAAM,IAAI,CAAC,MAAM,MAChB,gBAAAD,OAACC,WAAA,EACE;AAAA,cAAI,KAAK,gBAAAF,MAAC,QAAG;AAAA,UACb;AAAA,aAFY,CAGf,CACD,KANY,GAOf;AAAA,MAEJ;AAAA,MAEA,KAAK;AACH,eACE,gBAAAA,MAAC,QAAa,WAAU,gBACrB,uBAAa,KAAK,UAAU,KAAK,GAAG,KAD9B,GAET;AAAA,MAGJ,KAAK;AACH,eACE,gBAAAA,MAAC,YAAiB,WAAU,oBACzB,uBAAa,KAAK,UAAU,KAAK,GAAG,KAD1B,GAEb;AAAA,MAGJ,KAAK;AACH,eACE,gBAAAA,MAAC,SAAc,WAAU,iBACtB,uBAAa,KAAK,UAAU,KAAK,GAAG,KAD7B,GAEV;AAAA,MAGJ,KAAK;AACH,eACE,gBAAAA,MAAC,UAAe,WAAU,yBACvB,eAAK,SADG,GAEX;AAAA,MAGJ,KAAK,QAAQ;AACX,cAAM,OAAO,YAAY,KAAK,KAAK,QAAQ,EAAE,kBAAkB,IAAI,YAAY,CAAC;AAChF,YAAI,CAAC,MAAM;AACT,iBACE,gBAAAA,MAAC,UAAe,WAAU,0CACvB,uBAAa,KAAK,UAAU,KAAK,GAAG,KAD5B,GAEX;AAAA,QAEJ;AACA,eACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YACV;AAAA,YACA,OAAO,KAAK,SAAS;AAAA,YACrB,QAAO;AAAA,YACP,KAAI;AAAA,YAEH,uBAAa,KAAK,UAAU,KAAK,GAAG;AAAA;AAAA,UAPhC;AAAA,QAQP;AAAA,MAEJ;AAAA,MAEA,KAAK;AACH,eACE,gBAAAA,MAAC,WAAkB,KAAK,KAAK,KAAK,KAAK,KAAK,OAAO,IAAI,OAAO,KAAK,SAAS,UAA9D,GAAyE;AAAA,MAG3F,KAAK;AACH,eAAO,gBAAAA,MAAC,UAAQ,GAAK;AAAA,MAEvB,KAAK;AACH,eACE,gBAAAA,MAAC,UAAe,WAAU,yBACvB,eAAK,SADG,GAEX;AAAA,MAGJ,KAAK;AACH,YAAI,IAAI,eAAe,QAAS,QAAO;AAGvC,YACE,IAAI,eAAe,aACnB,CAAC,iBAAiB,KAAK,YAAY,KACnC,CAAC,qBAAqB,KAAK,YAAY,KACvC,CAAC,oBAAoB,KAAK,OAAO,GACjC;AACA,iBACE,gBAAAA;AAAA,YAAC;AAAA;AAAA,cAEC,WAAU;AAAA,cACV,yBAAyB,EAAE,QAAQ,KAAK,QAAQ;AAAA;AAAA,YAF3C;AAAA,UAGP;AAAA,QAEJ;AAGA,eACE,gBAAAA,MAAC,UAAe,WAAU,yBACvB,0BAAgB,iBAAiB,KAAK,cAAc,IAAI,UAAU,GAAG,GAAG,GAAG,GAAG,KADtE,GAEX;AAAA,MAGJ,KAAK;AACH,eACE,gBAAAA,MAAC,SAAc,WAAU,0BACvB,0BAAAC,OAAC,OAAE,MAAM,OAAO,KAAK,UAAU,IAAI;AAAA;AAAA,UAAE,KAAK,SAAS,KAAK;AAAA,UAAW;AAAA,WAAC,KAD5D,GAEV;AAAA,MAGJ,KAAK;AAEH,eACE,gBAAAD,MAAC,UAAe,WAAU,sBACvB,uBAAa,KAAK,UAAU,KAAK,GAAG,KAD5B,GAEX;AAAA,MAGJ,KAAK;AACH,eACE,gBAAAC,OAAC,UAAe,WAAU,uBAAsB;AAAA;AAAA,UAC5C,KAAK,OAAO,KAAK;AAAA,UAAW;AAAA,aADrB,GAEX;AAAA,MAGJ,KAAK;AACH,eACE,gBAAAD,MAAC,UAAe,WAAU,4BAA2B,kBAAgB,KAAK,MACvE,uBAAa,KAAK,UAAU,KAAK,GAAG,KAD5B,GAEX;AAAA,MAGJ,KAAK;AACH,eACE,gBAAAC;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YACV,gBAAa;AAAA,YACb,aAAW,KAAK;AAAA,YAChB,WAAS,KAAK;AAAA,YACd,cAAY,KAAK;AAAA,YAClB;AAAA;AAAA,cACG,KAAK;AAAA;AAAA;AAAA,UAPF;AAAA,QAQP;AAAA,MAGJ;AACE,eAAO;AAAA,IACX;AAAA,EACF,CAAC;AACH;AAKA,SAAS,YACP,MACA,KACA,MAAiB,aACA;AACjB,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aACE,gBAAAD,MAAC,OAAY,WAAU,eACpB,uBAAa,KAAK,UAAU,KAAK,GAAG,KAD/B,GAER;AAAA,IAGJ,KAAK,WAAW;AACd,YAAM,MAAM,IAAI,KAAK,KAAK;AAC1B,aACE,gBAAAA,MAAC,OAAc,WAAW,gCAAgC,KAAK,KAAK,IACjE,uBAAa,KAAK,UAAU,KAAK,GAAG,KAD7B,GAEV;AAAA,IAEJ;AAAA,IAEA,KAAK;AACH,aACE,gBAAAA,MAAC,gBAAqB,WAAU,wBAC7B,uBAAa,KAAK,UAAU,KAAK,GAAG,KADtB,GAEjB;AAAA,IAGJ,KAAK;AACH,UAAI,KAAK,SAAS;AAChB,eACE,gBAAAA,MAAC,QAAa,WAAU,+BAA8B,OAAO,KAAK,SAAS,QACxE,eAAK,SAAS,IAAI,CAAC,MAAM,MAAM,eAAe,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,KADlE,GAET;AAAA,MAEJ;AACA,aACE,gBAAAA,MAAC,QAAa,WAAU,+BACrB,eAAK,SAAS,IAAI,CAAC,MAAM,MAAM,eAAe,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,KADlE,GAET;AAAA,IAGJ,KAAK;AACH,aACE,gBAAAA,MAAC,SAAc,WAAU,wBACvB,0BAAAA,MAAC,UAAK,WAAW,KAAK,OAAO,YAAY,KAAK,IAAI,KAAK,QAAY,eAAK,OAAM,KADtE,GAEV;AAAA,IAGJ,KAAK;AACH,aAAO,gBAAAA,MAAC,QAAa,WAAU,kBAAf,GAA8B;AAAA,IAEhD,KAAK;AACH,aAAO,YAAY,KAAK,UAAU,KAAK,OAAO,KAAK,GAAG;AAAA,IAExD,KAAK;AACH,UAAI,IAAI,eAAe,QAAS,QAAO;AAGvC,UACE,IAAI,eAAe,aACnB,CAAC,iBAAiB,KAAK,YAAY,KACnC,CAAC,qBAAqB,KAAK,YAAY,KACvC,CAAC,oBAAoB,KAAK,OAAO,GACjC;AACA,eACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YACV,yBAAyB,EAAE,QAAQ,KAAK,QAAQ;AAAA;AAAA,UAF3C;AAAA,QAGP;AAAA,MAEJ;AAGA,aACE,gBAAAA,MAAC,SAAc,WAAU,wBACtB,0BAAgB,iBAAiB,KAAK,cAAc,IAAI,UAAU,GAAG,GAAG,GAAG,GAAG,KADvE,GAEV;AAAA,IAGJ,KAAK;AACH,aACE,gBAAAA,MAAC,SAAc,WAAU,wBACvB,0BAAAA,MAAC,UAAM,eAAK,OAAM,KADV,GAEV;AAAA,IAGJ,KAAK;AAEH,aAAO;AAAA,IAET,KAAK;AACH,aACE,gBAAAC,OAAC,SAAc,WAAU,0BAAyB,IAAI,MAAM,KAAK,UAAU,IACzE;AAAA,wBAAAD,MAAC,SAAK,eAAK,SAAS,KAAK,YAAW;AAAA,QACnC,aAAa,KAAK,UAAU,KAAK,GAAG;AAAA,WAF7B,GAGV;AAAA,IAGJ,KAAK;AACH,aACE,gBAAAC;AAAA,QAAC;AAAA;AAAA,UAEC,WAAW,2CAA2C,KAAK,IAAI;AAAA,UAC/D,kBAAgB,KAAK;AAAA,UAEpB;AAAA,iBAAK,SAAS,gBAAAD,MAAC,SAAI,WAAU,6BAA6B,eAAK,OAAM;AAAA,YACrE,aAAa,KAAK,UAAU,KAAK,GAAG;AAAA;AAAA;AAAA,QALhC;AAAA,MAMP;AAAA,IAGJ,KAAK;AACH,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC,WAAW,2CAA2C,KAAK,IAAI;AAAA,UAC/D,kBAAgB,KAAK;AAAA,UAEpB,uBAAa,KAAK,UAAU,KAAK,GAAG;AAAA;AAAA,QAJhC;AAAA,MAKP;AAAA,IAGJ,KAAK;AACH,aACE,gBAAAA,MAAC,QAAa,WAAU,gBACrB,eAAK,SAAS,IAAI,CAAC,OAAO,MAAM;AAC/B,YAAI,MAAM,SAAS,kBAAkB;AACnC,iBACE,gBAAAA,MAAC,QAAwB,WAAU,gBAChC,uBAAa,MAAM,UAAU,GAAG,GAAG,KAAK,CAAC,IAAI,GAAG,KAD1C,GAAG,GAAG,KAAK,CAAC,EAErB;AAAA,QAEJ;AACA,eACE,gBAAAA,MAAC,QAAwB,WAAU,gBAChC,uBAAa,MAAM,UAAU,GAAG,GAAG,KAAK,CAAC,IAAI,GAAG,KAD1C,GAAG,GAAG,KAAK,CAAC,EAErB;AAAA,MAEJ,CAAC,KAdM,GAeT;AAAA,IAGJ;AACE,aAAO;AAAA,EACX;AACF;AAGA,SAAS,eACP,MACA,KACA,MAAiB,aACA;AACjB,QAAM,SAAS,KAAK,YAAY,QAAQ,KAAK,YAAY;AACzD,SACE,gBAAAC,OAAC,QAAa,WAAW,eAAe,SAAS,oBAAoB,EAAE,IACpE;AAAA,cACC,gBAAAD,MAAC,WAAM,MAAK,YAAW,SAAS,CAAC,CAAC,KAAK,SAAS,UAAQ,MAAC,WAAU,sBAAqB;AAAA,IAEzF,aAAa,KAAK,UAAU,KAAK,GAAG;AAAA,OAJ9B,GAKT;AAEJ;AAGA,SAAS,YACP,MACA,OACA,KACA,MAAiB,aACA;AACjB,QAAM,CAAC,WAAW,GAAG,QAAQ,IAAI;AACjC,SACE,gBAAAC,OAAC,WAAgB,WAAU,mBACxB;AAAA,iBACC,gBAAAD,MAAC,WACC,0BAAAA,MAAC,QACE,oBAAU,SAAS,IAAI,CAAC,MAAyB,OAChD,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QACV,OAAO,QAAQ,EAAE,IAAI,EAAE,WAAW,MAAM,EAAE,EAAG,IAAI;AAAA,QAEhD,uBAAa,KAAK,UAAU,GAAG,GAAG,KAAK,EAAE,IAAI,GAAG;AAAA;AAAA,MAJ5C,GAAG,GAAG,KAAK,EAAE;AAAA,IAKpB,CACD,GACH,GACF;AAAA,IAED,SAAS,SAAS,KACjB,gBAAAA,MAAC,WACE,mBAAS,IAAI,CAAC,KAAK,OAClB,gBAAAA,MAAC,QACE,cAAI,SAAS,IAAI,CAAC,MAAyB,OAC1C,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QACV,OAAO,QAAQ,EAAE,IAAI,EAAE,WAAW,MAAM,EAAE,EAAG,IAAI;AAAA,QAEhD,uBAAa,KAAK,UAAU,GAAG,GAAG,KAAK,EAAE,IAAI,EAAE,IAAI,GAAG;AAAA;AAAA,MAJlD,GAAG,GAAG,KAAK,EAAE,IAAI,EAAE;AAAA,IAK1B,CACD,KATM,GAAG,GAAG,KAAK,EAAE,EAUtB,CACD,GACH;AAAA,OA/BQ,GAiCZ;AAEJ;AAGA,SAAS,aACP,OACA,YAAY,IACZ,MAAiB,aACE;AACnB,SAAO,MAAM,IAAI,CAAC,MAAM,MAAM,YAAY,MAAM,GAAG,SAAS,IAAI,CAAC,IAAI,GAAG,CAAC;AAC3E;AAKA,SAAS,QAAQ,EAAE,KAAK,KAAK,MAAM,GAAiD;AAClF,QAAM,UAAU,YAAY,KAAK,OAAO;AACxC,QAAM,WAAW,YAAY,WAAW,IAAI,GAAG;AAC/C,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,gBAAAA,MAAC,SAAI,WAAU,mBAAkB,KAAK,UAAU,KAAU,OAAc;AACjF;AAOA,SAAS,iBAAiB,OAAmB,YAAoC;AAC/E,MAAI,eAAe,QAAS,QAAO,CAAC;AACpC,MAAI,eAAe,UAAW,QAAO;AACrC,SAAOG,mBAAkB,KAAK;AAChC;AAEA,SAAS,iBAAiB,OAA4B;AACpD,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,cAAe;AACjC,UAAM,UAAU,KAAK,QAAQ,YAAY;AACzC,QAAI,YAAY,WAAW,YAAY,QAAS,QAAO;AACvD,QAAI,iBAAiB,KAAK,QAAQ,EAAG,QAAO;AAAA,EAC9C;AACA,SAAO;AACT;AAcA,IAAM,sBAAsB,oBAAI,IAAI;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMD,SAAS,qBAAqB,OAA4B;AACxD,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,cAAe;AACjC,QAAI,oBAAoB,IAAI,KAAK,QAAQ,YAAY,CAAC,EAAG,QAAO;AAChE,QAAI,qBAAqB,KAAK,QAAQ,EAAG,QAAO;AAAA,EAClD;AACA,SAAO;AACT;AAWA,IAAM,wBACJ;AAEF,SAAS,oBAAoB,SAA0B;AACrD,SAAO,sBAAsB,KAAK,OAAO;AAC3C;AAQA,IAAM,oBAA4C;AAAA;AAAA,EAEhD,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,KAAK;AAAA,EACL,SAAS;AAAA,EACT,UAAU;AAAA;AAAA,EAEV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AACP;AAEA,SAAS,oBAAoB,OAAwD;AACnF,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACjD,UAAM,WAAW,kBAAkB,IAAI;AACvC,QAAI,CAAC,SAAU;AACf,QAAI,aAAa,SAAS;AAIxB,UAAI,YAAY,IAAI;AACpB;AAAA,IACF;AACA,QAAI,QAAQ,IAAI;AAAA,EAClB;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,IAAiB,KAA8B;AACxE,QAAM,UAAU,GAAG,QAAQ,YAAY;AAKvC,MAAI,oBAAoB,IAAI,OAAO,EAAG,QAAO;AAC7C,MAAI,YAAY,SAAS;AACvB,WACE,gBAAAH;AAAA,MAAC;AAAA;AAAA,QAEC,KAAK,GAAG,WAAW,OAAO;AAAA,QAC1B,OAAO,GAAG,WAAW;AAAA,QACrB,QAAQ,GAAG,WAAW;AAAA,QACtB,QAAQ,GAAG,WAAW;AAAA,QAGtB,UAAU,cAAc,GAAG;AAAA,QAC3B,SACE,GAAG,WAAW,YAAY,UAC1B,GAAG,WAAW,YAAY,cAC1B,GAAG,WAAW,YAAY,SACtB,GAAG,WAAW,UACd;AAAA;AAAA,MAbD;AAAA,IAeP;AAAA,EAEJ;AACA,MAAI,YAAY,SAAS;AACvB,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC,KAAK,GAAG,WAAW,OAAO;AAAA,QAC1B,UAAU,cAAc,GAAG;AAAA,QAC3B,SACE,GAAG,WAAW,YAAY,UAC1B,GAAG,WAAW,YAAY,cAC1B,GAAG,WAAW,YAAY,SACtB,GAAG,WAAW,UACd;AAAA;AAAA,MARD;AAAA,IAUP;AAAA,EAEJ;AAEA,QAAM,MAAM;AACZ,QAAM,QAAQ,oBAAoB,GAAG,UAAU;AAC/C,MAAI,GAAG,aAAa;AAClB,WAAO,gBAAAA,MAAC,OAAe,GAAG,SAAT,GAAgB;AAAA,EACnC;AACA,SACE,gBAAAA,MAAC,OAAe,GAAG,OAChB,0BAAgB,GAAG,UAAU,GAAG,GAAG,GAAG,KAD/B,GAEV;AAEJ;AAEA,SAAS,gBAAgB,OAAmB,WAAsC;AAChF,SAAO,MAAM,IAAI,CAAC,MAAM,MAAM;AAC5B,UAAM,MAAM,GAAG,SAAS,GAAG,CAAC;AAC5B,YAAQ,KAAK,MAAM;AAAA,MACjB,KAAK;AACH,eAAO,kBAAkB,MAAM,GAAG;AAAA,MACpC,KAAK;AACH,eAAO,gBAAAA,MAACE,WAAA,EAAoB,eAAK,SAAX,GAAiB;AAAA,MACzC,KAAK;AAEH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF,CAAC;AACH;AAYO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AACF,GAA0B;AACxB,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AAEzC,SACE,gBAAAF,MAAC,SAAI,WAAW,aAAa,aAAa,EAAE,IACzC,uBAAa,OAAO,IAAI,EAAE,YAAY,YAAY,CAAC,GACtD;AAEJ;;;ADreI,SAM0C,OAAAI,OAN1C,QAAAC,cAAA;AAlGJ,IAAM,yBAAyB,oBAAI,IAAY;AAY/C,SAAS,iBAAiB,OAAuB;AAC/C,QAAM,aAAa,MAAM,eAAe;AACxC,MAAI,CAAC,YAAY,SAAU,QAAO;AAClC,MAAI,CAAC,YAAY,WAAW,QAAQ,GAAG;AACrC,QAAI,CAAC,uBAAuB,IAAI,WAAW,QAAQ,GAAG;AACpD,6BAAuB,IAAI,WAAW,QAAQ;AAC9C,cAAQ;AAAA,QACN,8BAA8B,WAAW,QAAQ;AAAA,MACnD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKA,SAAS,SAAS,QAAyB;AACzC,MAAI,QAAQ;AACZ,aAAW,KAAK,QAAQ;AACtB;AACA,QAAI,EAAE,SAAU,UAAS,SAAS,EAAE,QAAQ;AAAA,EAC9C;AACA,SAAO;AACT;AAgBA,SAAS,aAAa,EAAE,OAAO,UAAU,UAAU,eAAe,WAAW,GAAsB;AACjG,QAAM,cAAc,iBAAiB,KAAK;AAG1C,QAAM,cAAcC,SAAQ,MAAM;AAChC,QAAI,CAAC,YAAa,QAAO;AAEzB,UAAM,aAAa,MAAM,cAAe;AACxC,UAAM,cAAc,iBAAiB,MAAM,aAAc;AAGzD,UAAM,gBAAyC;AAAA,MAC7C,IAAI,MAAM;AAAA,MACV,UAAU,WAAW;AAAA,MACrB,WAAW;AAAA,MACX,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO;AAAA,MACP,GAAI;AAAA,QACF,WAAW,YAAY;AAAA,QACvB;AAAA,QACA,MAAM;AAAA,QACN;AAAA,UACE,cAAc;AAAA,QAChB;AAAA,MACF,KAAK,CAAC;AAAA,MACN,GAAG,WAAW;AAAA,MACd,GAAG,MAAM;AAAA,IACX;AAGA,UAAM,MAAqB;AAAA,MACzB,GAAG;AAAA,MACH;AAAA,IACF;AACA,UAAM,SAAS,UAAU,eAAsC,GAAG;AAElE,WAAO;AAAA,MACL,GAAG;AAAA,MACH;AAAA,MACA,UAAU,WAAW;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,OAAO,aAAa,eAAe,UAAU,CAAC;AAElD,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,iBAAe,MAAM;AAAA,MACrB,iBAAe,cAAc,MAAM,eAAe,oBAAoB,WAAW;AAAA,MAGhF;AAAA,cAAM,iBAAiB,CAAC,eAAe,gBAAAD,MAAC,oBAAiB,OAAO,CAAC,MAAM,aAAa,GAAG;AAAA,QASvF,eAAe,eACd,gBAAAA,MAAC,SAAI,WAAU,sBACb,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,OAAO;AAAA,cACP,aAAa,GAAG,SAAS,KAAK,MAAM,SAAS,MAAM;AAAA,cACnD,UAAU;AAAA,cACV,cAAc;AAAA,YAChB;AAAA,YAEA,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO;AAAA,gBACP,WAAW;AAAA,gBACX;AAAA,gBACA;AAAA;AAAA,YACF;AAAA;AAAA,QACF,GACF;AAAA,QAID,CAAC,eAAe,MAAM,YAAY,MAAM,SAAS,SAAS,KACzD,gBAAAA,MAAC,oBAAiB,OAAO,MAAM,UAAU;AAAA,QAI1C,MAAM,YAAY,MAAM,SAAS,SAAS,KACzC,gBAAAA,MAAC,SAAI,WAAU,0BACZ,gBAAM,SAAS,IAAI,CAAC,OAAO,MAC1B,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,OAAO;AAAA,YACP;AAAA,YACA;AAAA,YACA;AAAA,YACA,YAAY,aAAa,IAAI;AAAA;AAAA,UALxB,MAAM;AAAA,QAMb,CACD,GACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAgBO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,mBAAmB;AACrB,GAAuB;AACrB,QAAM,iBAAiB,YAAYG,kBAAiB;AAGpD,QAAM,cAAcD;AAAA,IAClB,MAAO,CAAC,OAAO,aAAa,SAAY,cAAc,cAAc,QAAQ,CAAC,IAAI;AAAA,IACjF,CAAC,KAAK,QAAQ;AAAA,EAChB;AACA,QAAM,cAAc,OAAO;AAE3B,QAAM,cAAcA;AAAA,IAClB,MAAO,cAAc,SAAS,YAAY,MAAM,IAAI;AAAA,IACpD,CAAC,WAAW;AAAA,EACd;AACA,QAAM,cAAc,eAAe,YAAY,MAAM;AACrD,QAAM,kBAA6C,YAAY,SAAS,cAAc;AAEtF,QAAM,gBAA+BA,SAAQ,MAAM;AACjD,UAAM,YAAY,SAASE;AAC3B,UAAM,iBAAiB,kBAAkB,aAAa,WAAW,eAAe,IAAI;AACpF,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,MACV;AAAA;AAAA;AAAA,MAGA,kBAAkB,eAAe;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,gBAAgB,aAAa,OAAO,eAAe,CAAC;AAExD,QAAM,cAAc,cAAc;AAIlC,MAAI,CAAC,aAAa;AAChB,WAAO,gBAAAJ,MAAC,SAAI,WAAW,sCAAsC,aAAa,EAAE,IAAI;AAAA,EAClF;AAEA,QAAM,UAAU,YAAY,OAAO;AACnC,QAAM,YAAY,YAAY,OAAO;AACrC,QAAM,aAAa,YAAY,OAAO;AACtC,QAAM,eAAe,YAAY,OAAO;AACxC,QAAM,WAAWK,mBAAkB,YAAY,WAAW,UAAU,uBAAuB;AAC3F,QAAM,YAAYA,mBAAkB,YAAY,WAAW,WAAW,gBAAgB;AACtF,QAAM,SAAS,YAAY,WAAW,cAAc;AAEpD,SACE,gBAAAL;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,iBAAiB,aAAa,EAAE;AAAA,MAC3C,OAAO;AAAA,QACL,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMP,QAAQ,cAAc,SAAS;AAAA,QAC/B,WAAW,cAAc,YAAY;AAAA,QACrC,WAAW;AAAA,QACX,YAAY;AAAA,MACd;AAAA,MAEA,0BAAAC;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,kCAAkC,cAAc,iCAAiC,EAAE,GAAG,qBAAqB,cAAc,6CAA6C,EAAE;AAAA,UACnL,OACE;AAAA;AAAA;AAAA;AAAA;AAAA,YAKE,UAAU,cAAc,SAAS;AAAA,YACjC,QAAQ,cAAc,MAAM;AAAA,YAC5B,SAAS,cAAc,MAAM;AAAA,YAC7B,YAAY;AAAA,YACZ,UAAU;AAAA,YACV,YAAY;AAAA,YACZ,OAAO;AAAA;AAAA,YAEP,8BAA8B;AAAA,YAC9B,6BAA6B;AAAA,YAC7B,wBAAwB;AAAA,YACxB,yBAAyB;AAAA,YACzB,2BAA2B;AAAA,YAC3B,sBAAsB;AAAA,UACxB;AAAA,UAIF;AAAA,4BAAAD,MAAC,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WA+GN;AAAA,YACD,YAAY,OAAO,IAAI,CAAC,OAAO,MAC9B,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC;AAAA,gBACA;AAAA,gBACA,UAAU;AAAA,gBACV;AAAA,gBACA,YAAY;AAAA;AAAA,cALP,MAAM;AAAA,YAMb,CACD;AAAA;AAAA;AAAA,MACH;AAAA;AAAA,EACF;AAEJ;;;A3BjPW,gBAAAM,OA45BC,QAAAC,cA55BD;AAxLX,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAOD,SAAS,qBAAqB,KAA+B;AAC3D,QAAM,MAAM,oBAAI,IAAoB;AAGpC,aAAW,SAAS,IAAI,QAAsB;AAC5C,QAAIC,iBAAgB,KAAK,KAAK,MAAM,aAAa,mBAAmB,WAAW,OAAO;AACpF,YAAM,SAAS,MAAM;AACrB,UAAI,CAAC,IAAI,IAAI,MAAM,GAAG;AACpB,YAAI,IAAI,QAAS,MAA4B,KAAK;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAGA,WAAS,IAAI,GAAG,IAAI,IAAI,MAAM,SAAS,QAAQ,KAAK;AAClD,QAAI,CAAC,IAAI,IAAI,CAAC,GAAG;AACf,YAAM,OAAO,IAAI,MAAM,SAAS,CAAC,EAAE;AACnC,UAAI,SAAS,WAAW,KAAK,SAAS,OAAO,GAAG;AAC9C,YAAI,IAAI,GAAG,cAAc;AAAA,MAC3B,WAAW,SAAS,oBAAoB,KAAK,SAAS,gBAAgB,GAAG;AACvE,YAAI,IAAI,GAAG,gBAAgB;AAAA,MAC7B,OAAO;AAEL,cAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,cAAM,SAAS,MACZ;AAAA,UAAI,CAAC,GAAG,QACP,QAAQ,KAAK,CAAC,YAAY,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC,IAAI;AAAA,QAC9E,EACC,KAAK,GAAG;AACX,YAAI,IAAI,GAAG,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAgGA,SAAS,mBAA4B;AACnC,MAAI;AACF,WAAO,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa;AAAA,EACpE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIA,IAAI,sBAAsB;AAQnB,SAAS,UAAU,OAAuB;AAC/C,QAAM,EAAE,KAAK,SAAS,IAAI;AAG1B,QAAM,cAAcC;AAAA,IAClB,MAAO,CAAC,OAAO,aAAa,SAAYC,eAAcC,eAAc,QAAQ,CAAC,IAAI;AAAA,IACjF,CAAC,KAAK,QAAQ;AAAA,EAChB;AAEA,QAAM,cAAc,OAAO;AAE3B,MAAI,CAAC,aAAa;AAChB,WAAO,gBAAAL,MAAC,SAAI,WAAU,gCAA+B;AAAA,EACvD;AAEA,SAAO,gBAAAA,MAAC,oBAAkB,GAAG,OAAO,KAAK,aAAa;AACxD;AAMA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA,WAAW;AAAA,EACX,aAAa;AAAA,EACb,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,cAAc;AAChB,GAA0B;AACxB,QAAM,kBAAkB,gBAAgB;AACxC,QAAM,eAAe,gBAAgB;AACrC,QAAM,WAAWM,QAAyB,IAAI;AAC9C,QAAM,eAAeA,QAAuB,IAAI;AAGhD,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAkC,IAAI;AAC5E,QAAM,mBAAmBD,QAAsC;AAK/D,QAAM,EAAE,UAAU,YAAY,IAAI,uBAAuB;AACzD,QAAM,iBAAiB,kBAAkB,aAAaE,kBAAiB,YAAY;AAGnF,QAAM,cAAcL,SAAQ,MAAM;AAChC,QAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,UAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;AACzD,WAAO,OAAO,IAAI,OAAO,MAAM;AAAA,EACjC,GAAG,CAAC,CAAC;AAGL,QAAM,gBAAgB,aAAa,UAAU,IAAI,OAAO,QAAQ;AAGhE,QAAM,QAAQ,2BAA2B;AAKzC,EAAAM,WAAU,MAAM;AACd,QAAI,uBAAuB,CAAC,iBAAiB,EAAG;AAChD,UAAM,KAAK,aAAa;AACxB,QAAI,CAAC,MAAM,OAAO,qBAAqB,WAAY;AACnD,UAAM,QAAQ,iBAAiB,EAAE,EAAE,iBAAiB,wBAAwB;AAC5E,QAAI,CAAC,MAAM,KAAK,GAAG;AACjB,4BAAsB;AACtB,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,EACF,IAAI;AAKJ,QAAM,gBAAgBN,SAAQ,MAAM,qBAAqB,GAAG,GAAG,CAAC,GAAG,CAAC;AAIpE,QAAM,iBAAiBG,QAAO,WAAW;AACzC,iBAAe,UAAU;AACzB,QAAM,mBAAmBA,QAAO,aAAa;AAC7C,mBAAiB,UAAU;AAC3B,QAAM,uBAAuBA,QAAO,CAAC;AAGrC,QAAM,uBAAuBI;AAAA,IAC3B,CAAC,MAAwB;AACvB,UAAI,cAAc,mBAAmB,aAAc;AACnD,YAAM,SAAS,EAAE;AAEjB,UACE,OAAO;AAAA,QACL;AAAA,MACF;AAEA;AACF,aAAO;AAEP,YAAM,YAAY,YAAY,SAAS;AACvC,qBAAe,SAAS;AACxB,mBAAa,iBAAiB,OAAO;AACrC,uBAAiB,UAAU,WAAW,MAAM,eAAe,IAAI,GAAG,GAAG;AAAA,IACvE;AAAA,IACA,CAAC,YAAY,QAAQ,WAAW,iBAAiB,YAAY;AAAA,EAC/D;AAIA,QAAM,cAAc,eAAe,YAAY,MAAM;AACrD,QAAM,kBAAkB,YAAY,SAAS,cAAc;AAC3D,QAAM,iBAAiBP,SAAQ,MAAM;AACnC,UAAM,OAAO,SAASQ;AACtB,WAAO,kBAAkBC,cAAa,MAAM,eAAe,IAAI;AAAA,EACjE,GAAG,CAAC,OAAO,eAAe,CAAC;AAG3B,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,EACV,IAAI,eAAe,KAAK,aAAa,gBAAgB,YAAY,cAAc;AAG/E,QAAM,aAAaT,SAAQ,MAAoB;AAC7C,UAAM,mBAAmB,IAAI;AAC7B,QAAI,CAAC,eAAgB,QAAO;AAC5B,QAAI,CAAC,iBAAkB,QAAO;AAE9B,UAAM,UAAU,sBAAsB,gBAAgB,GAAG,GAAG,cAAc;AAC1E,UAAM,SAAS,iBAAiB,kBAAkB,OAAO;AAEzD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,WAAW;AAAA;AAAA,MACX,UAAU;AAAA;AAAA,MACV,cAAc;AAAA,MACd;AAAA,IACF;AAAA,EACF,GAAG,CAAC,IAAI,YAAY,gBAAgB,gBAAgB,cAAc,CAAC;AAInE,QAAM,kBAAkB,CAAC,CAAC;AAC1B,QAAM,CAAC,uBAAuB,wBAAwB,IAAII,UAAS,KAAK;AACxE,QAAM,2BAA2BD,QAAO,EAAE;AAC1C,EAAAG,WAAU,MAAM;AACd,UAAM,UAAU,GAAG,eAAe,IAAI,eAAe,IAAI,UAAU;AACnE,QAAI,yBAAyB,YAAY,QAAS;AAClD,6BAAyB,UAAU;AACnC,QAAI,mBAAmB,mBAAmB,CAAC,YAAY;AACrD,+BAAyB,IAAI;AAC7B,YAAM;AAAA,IACR,OAAO;AACL,+BAAyB,KAAK;AAAA,IAChC;AAAA,EACF,GAAG,CAAC,iBAAiB,iBAAiB,YAAY,KAAK,CAAC;AAGxD,QAAM,CAAC,aAAa,cAAc,IAAIF,UAAS,KAAK;AAGpD,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAS,KAAK;AAC9D,QAAM,kBAAkBD,QAAsC;AAC9D,QAAM,kBAAkBA,QAAO,KAAK;AAGpC,QAAM,gBAAgBA,QAAO,KAAK;AAGlC,QAAM,SAAS,CAAC,EACd,cACA,CAAC,mBACD,CAAC,aACD,gBAAgB,KAChB,CAAC,cAAc,WACf,CAAC,cACD,CAAC;AAEH,MAAI,OAAQ,iBAAgB,UAAU;AAEtC,EAAAG,WAAU,MAAM;AACd,QAAI,aAAa,gBAAgB,WAAW,cAAc,CAAC,cAAc,CAAC,iBAAiB;AACzF,sBAAgB,UAAU;AAC1B,oBAAc,UAAU;AACxB,0BAAoB,IAAI;AAKxB,sBAAgB,UAAU,WAAW,MAAM,oBAAoB,KAAK,GAAG,GAAI;AAAA,IAC7E;AAAA,EACF,GAAG,CAAC,WAAW,YAAY,YAAY,eAAe,CAAC;AAGvD,EAAAA,WAAU,MAAM,MAAM,aAAa,gBAAgB,OAAO,GAAG,CAAC,CAAC;AAK/D,QAAM,sBACJ,CAAC,mBACD,CAAC,gBACD,CAAC,CAAC,eACD,eACC,oBACC,CAAC,aAAa,gBAAgB,KAAK,CAAC,cAAc,WAAW,CAAC,cAAc,CAAC;AAClF,QAAM,qBAAqB,CAAC,EAC1B,mBACA,CAAC,gBACD,CAAC,cACD,cACA;AAEF,QAAM,iBAAiB,uBAAuB;AAE9C,QAAM,oBAAoB,CAAC,EAAE,mBAAmB,CAAC,cAAc;AAC/D,QAAM,sBAAsB,oBACxB,wBACE,IACA,oBAAoB,IACtB;AACJ,QAAM,uBAAuB,oBACzB,eAAe,SAAS,IACxB,eAAe;AAInB,QAAM,gBAAgBH,QAAO,KAAK;AAClC,EAAAG,WAAU,MAAM;AACd,QAAI,gBAAgB,YAAY,CAAC,cAAc,SAAS;AACtD,oBAAc,UAAU;AACxB,WAAK;AAAA,IACP;AAAA,EACF,GAAG,CAAC,cAAc,UAAU,IAAI,CAAC;AAGjC,EAAAA,WAAU,MAAM;AACd,mBAAe,WAAW;AAAA,EAC5B,GAAG,CAAC,aAAa,YAAY,CAAC;AAG9B,EAAAA,WAAU,MAAM;AACd,QAAI,SAAS;AACX,gBAAU;AAAA,IACZ;AAAA,EACF,GAAG,CAAC,SAAS,OAAO,CAAC;AAGrB,EAAAA,WAAU,MAAM;AACd,SAAK,cAAc,gBAAgB,OAAO,WAAW,aAAa;AAChE,YAAM,IAAI;AACV,QAAE,SAAS,CAAC,SAAiB;AAC3B,eAAO,IAAI;AAKX,eAAO,IAAI,QAAc,CAAC,YAAY;AACpC,gCAAsB,MAAM;AAE1B,gBAAI,iBAAiB;AACrB,qBAAS,IAAI,eAAe,SAAS,GAAG,KAAK,GAAG,KAAK;AACnD,kBAAI,QAAQ,eAAe,CAAC,EAAE,WAAW;AACvC,iCAAiB,eAAe,CAAC,EAAE;AACnC;AAAA,cACF;AAAA,YACF;AACA,kBAAM,aAAa,OAAO,kBAAkB;AAG5C,qBAAS,cAAc,EAAE,QAAQ,CAAC,SAAS;AACzC,oBAAM,SAAU,KAAK,QAA2B;AAChD,kBAAI,CAAC,OAAQ;AAGb,kBAAI,OAAO,QAAQ,4BAA4B,GAAG;AAChD,qBAAK,cAAc,KAAK,IAAI,GAAG,SAAS;AAAA,cAC1C,WAKS,OAAO,QAAQ,8BAA8B,GAAG;AACvD,qBAAK,cAAc,KAAK,IAAI,GAAG,SAAS;AAAA,cAC1C;AAAA,YACF,CAAC;AAKD,kBAAM,eAAe,OAAO;AAC5B,kBAAM,oBAAqC,CAAC;AAC5C,kBAAM,gBAAgB,SAAS,cAAc,4BAA4B;AACzE,gBAAI,eAAe;AACjB,oBAAM,SAAS,cAAc,iBAAiB,wBAAwB;AACtE,qBAAO,QAAQ,CAAC,OAAO;AACrB,sBAAM,QAAQ;AACd,sBAAM,YAAY,WAAW,MAAM,QAAQ,aAAa,GAAG;AAC3D,sBAAM,UAAU,WAAW,MAAM,QAAQ,WAAW,GAAG;AAGvD,sBAAM,UAAU,WAAW,MAAM,QAAQ,WAAW,GAAG;AACvD,sBAAM,aAAa,KAAK;AAAA,kBACtB,YAAY,KAAK,IAAI,GAAG,eAAe,OAAO;AAAA,kBAC9C;AAAA,gBACF;AAEA,sBAAM,MAAM;AACZ,sBAAM,cAAc;AAEpB,kCAAkB;AAAA,kBAChB,IAAI,QAAc,CAAC,MAAM;AACvB,wBAAI,KAAK,IAAI,MAAM,cAAc,UAAU,IAAI,KAAK;AAClD,wBAAE;AAAA,oBACJ,OAAO;AACL,4BAAM,iBAAiB,UAAU,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1D,iCAAW,GAAG,GAAG;AAAA,oBACnB;AAAA,kBACF,CAAC;AAAA,gBACH;AAAA,cACF,CAAC;AAAA,YACH;AAKA,qBAAS,iBAAiB,qBAAqB,EAAE,QAAQ,CAAC,OAAO;AAC/D,oBAAM,QAAQ;AACd,oBAAM,WAAW,WAAW,MAAM,QAAQ,YAAY,GAAG;AACzD,oBAAM,SAAS,WAAW,MAAM,QAAQ,UAAU,GAAG;AACrD,oBAAM,WAAW,WAAW,MAAM,QAAQ,YAAY,GAAG;AACzD,oBAAM,MAAM;AACZ,kBAAI,OAAO,YAAY,QAAQ,OAAQ;AACvC,oBAAM,aAAa,YAAY,OAAO;AACtC,oBAAM,cAAc;AACpB,gCAAkB;AAAA,gBAChB,IAAI,QAAc,CAAC,MAAM;AACvB,sBAAI,KAAK,IAAI,MAAM,cAAc,UAAU,IAAI,KAAK;AAClD,sBAAE;AAAA,kBACJ,OAAO;AACL,0BAAM,iBAAiB,UAAU,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1D,+BAAW,GAAG,GAAG;AAAA,kBACnB;AAAA,gBACF,CAAC;AAAA,cACH;AAAA,YACF,CAAC;AAGD,oBAAQ,IAAI,iBAAiB,EAAE,KAAK,MAAM;AACxC,oCAAsB,MAAM,QAAQ,CAAC;AAAA,YACvC,CAAC;AAAA,UACH,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AACA,QAAE,cAAc,MAAM;AAIpB,cAAM,gBAAgB,uBAAuB,GAAG;AAChD,YAAI,gBAAgB,EAAG,QAAO,KAAK,IAAI,eAAe,aAAa;AACnE,eAAO;AAAA,MACT;AAEA,QAAE,YAAY,MACZ,eAAe,IAAI,CAAC,OAAc;AAAA,QAChC,IAAI,EAAE;AAAA,QACN,UAAW,EAAe,YAAY;AAAA,QACtC,WAAW,EAAE;AAAA,QACb,UAAU,EAAE;AAAA,MACd,EAAE;AAEJ,QAAE,mBAAmB,MACnB,IAAI,MAAM,SAAS,IAAI,CAAC,SAAS;AAAA,QAC/B,KAAK,IAAI;AAAA,QACT,MAAM,IAAI;AAAA,QACV,UAAU,IAAI;AAAA,QACd,WAAW,IAAI;AAAA,MACjB,EAAE;AAEJ,QAAE,cAAc,MACd,IAAI,UAAU,SAAS,IAAI,CAAC,OAAO;AAAA,QACjC,MAAM,EAAE;AAAA,QACR,WAAW,EAAE;AAAA,QACb,SAAS,EAAE;AAAA,MACb,EAAE,KAAK,CAAC;AAEV,QAAE,cAAc,MAAM;AACpB,cAAM,WAAW,qBAAqB,GAAG;AACzC,eAAO,IAAI,MAAM,SAAS,IAAI,CAAC,KAAK,OAAO;AAAA,UACzC,OAAO,SAAS,IAAI,CAAC,KAAK,IAAI;AAAA,UAC9B,WAAW,IAAI;AAAA,UACf,UAAU,IAAI;AAAA,QAChB,EAAE;AAAA,MACJ;AAEA,QAAE,YAAY,MAAM;AAClB,uBAAe,IAAI;AACnB,eAAO,IAAI,QAAc,CAAC,YAAY,sBAAsB,MAAM,QAAQ,CAAC,CAAC;AAAA,MAC9E;AACA,QAAE,YAAY,MAAM;AAClB,uBAAe,KAAK;AACpB,eAAO,IAAI,QAAc,CAAC,YAAY,sBAAsB,MAAM,QAAQ,CAAC,CAAC;AAAA,MAC9E;AACA,QAAE,gBAAgB,MAAM,CAAC,CAAC;AAAA,IAC5B;AACA,WAAO,MAAM;AACX,UAAI,OAAO,WAAW,aAAa;AACjC,cAAM,IAAI;AACV,eAAO,EAAE;AACT,eAAO,EAAE;AACT,eAAO,EAAE;AACT,eAAO,EAAE;AACT,eAAO,EAAE;AACT,eAAO,EAAE;AACT,eAAO,EAAE;AACT,eAAO,EAAE;AACT,eAAO,EAAE;AAAA,MACX;AAAA,IACF;AAAA,EAEF,GAAG,CAAC,YAAY,aAAa,QAAQ,eAAe,gBAAgB,UAAU,CAAC;AAK/E,QAAM,cACJ,wBAAwB,QAAQ,QAAQ,gBAAgB;AAC1D,QAAM,CAAC,aAAa,cAAc,IAAIF,UAAsB,WAAW;AAOvE,EAAAE,WAAU,MAAM;AACd,mBAAe,WAAW;AAAA,EAC5B,GAAG,CAAC,WAAW,CAAC;AAGhB,QAAM,kBAAkB,gBAAgB;AACxC,QAAM,qBAAmC,gBAAgB,WAAW,WAAW;AAE/E,QAAM,qBAAqBC;AAAA,IACzB,CAAC,YAAqB;AAGpB,qBAAe,UAAU,gBAAgB,aAAa,KAAK;AAC3D,yBAAmB,OAAO;AAAA,IAC5B;AAAA,IACA,CAAC,kBAAkB,YAAY;AAAA,EACjC;AAEA,QAAM,mBAAmBA,aAAY,MAAM;AACzC,mBAAe,CAAC,SAAS;AACvB,YAAM,OACJ,SAAS,QAAQ,aAAa,SAAS,aAAa,WAAW;AACjE,yBAAmB,SAAS,KAAK;AACjC,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,gBAAgB,CAAC;AAErB,QAAM,cAAc,IAAI,YAAY,IAAI,SAAS,QAAQ,SAAS;AAGlE,QAAM,kBAAkBP,SAAQ,MAAM,qBAAqB,GAAG,GAAG,CAAC,GAAG,CAAC;AAGtE,QAAM,gBAA+BA;AAAA,IACnC,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,mBAAmB;AAAA,MACnB,aAAa;AAAA,MACb;AAAA,MACA,aAAa,CAAC,CAAC;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA,qBAAqB;AAAA,MACrB,oBACE,gBAAgB,IAAI,cAAc,KAAK,IAAI,MAAM,SAAS,cAAc,GAAG,QAAQ;AAAA,MACrF,cAAc,qBAAqB,aAAc,gBAAgB;AAAA,MACjE,mBAAmB,qBAAqB,UAAU;AAAA,MAClD,oBACE,qBAAqB,CAAC,qBAAqB,oBAAoB,IAAI;AAAA,MACrE,kBAAkB,oBAAoB,eAAe,SAAS;AAAA,IAChE;AAAA;AAAA,IAEA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe;AAAA,IACjB;AAAA,EACF;AAGA,QAAM,kBAAmCA;AAAA,IACvC,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAkB;AAAA,IACpB;AAAA,IACA,CAAC,QAAQ,SAAS,QAAQ,oBAAoB,kBAAkB,kBAAkB;AAAA,EACpF;AAIA,QAAM,kBAAmCA;AAAA,IACvC,OAAO;AAAA,MACL,WAAW,MAAM;AACf,YAAI,qBAAqB,uBAAuB;AAC9C,gBAAM,SAAS,eAAe,CAAC;AAC/B,cAAI,QAAQ;AACV,qCAAyB,KAAK;AAC9B,mBAAO,OAAO,SAAS;AACvB,kBAAM;AAAA,UACR;AACA;AAAA,QACF;AACA,YAAI,oBAAoB,eAAe,SAAS,GAAG;AACjD,gBAAM,SAAS,eAAe,oBAAoB,CAAC;AACnD,cAAI,QAAQ;AACV,qCAAyB,KAAK;AAC9B,mBAAO,OAAO,SAAS;AACvB,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,MACA,WAAW,MAAM;AACf,YAAI,qBAAqB,CAAC,yBAAyB,qBAAqB,GAAG;AACzE,mCAAyB,IAAI;AAC7B,iBAAO,CAAC;AACR,gBAAM;AACN;AAAA,QACF;AACA,YAAI,oBAAoB,GAAG;AACzB,gBAAM,SAAS,eAAe,oBAAoB,CAAC;AACnD,cAAI,QAAQ;AACV,qCAAyB,KAAK;AAC9B,mBAAO,OAAO,SAAS;AACvB,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,MACA,WAAW,CAAC,UAAkB;AAC5B,YAAI,mBAAmB;AACrB,cAAI,UAAU,GAAG;AACf,qCAAyB,IAAI;AAC7B,mBAAO,CAAC;AACR,kBAAM;AACN;AAAA,UACF;AACA,gBAAM,SAAS,eAAe,QAAQ,CAAC;AACvC,cAAI,QAAQ;AACV,qCAAyB,KAAK;AAC9B,mBAAO,OAAO,SAAS;AACvB,kBAAM;AAAA,UACR;AACA;AAAA,QACF;AACA,YAAI,SAAS,KAAK,QAAQ,eAAe,QAAQ;AAC/C,gBAAM,SAAS,eAAe,KAAK;AACnC,cAAI,QAAQ;AACV,mBAAO,OAAO,SAAS;AACvB,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,mBAAmB,gBAAgB,QAAQ,OAAO,mBAAmB,qBAAqB;AAAA,EAC7F;AAIA,QAAM,eAAe,mBAAmB,CAAC,gBAAgB,CAAC,cAAc;AACxE,QAAM,QAAQ,cAAc;AAAA,IAC1B,SAAS;AAAA,IACT;AAAA,IACA,WAAW,sBAAsB,uBAAuB;AAAA,IACxD,WAAW,sBAAsB;AAAA,IACjC,QAAQ,gBAAgB;AAAA,IACxB,QAAQ,gBAAgB;AAAA,EAC1B,CAAC;AAGD,EAAAM,WAAU,MAAM;AACd,4BAAwB,aAAa;AAAA,EACvC,GAAG,CAAC,eAAe,qBAAqB,CAAC;AAKzC,EAAAA,WAAU,MAAM;AACd,sBAAkB,EAAE,MAAM,OAAO,GAAG,gBAAgB,CAAC;AAAA,EACvD,GAAG,CAAC,MAAM,OAAO,iBAAiB,eAAe,CAAC;AAGlD,QAAM,gBAAgBC,aAAY,CAAC,UAAyB;AAE1D,UAAM,WAAW;AACjB,QAAIR,iBAAgB,QAAQ,GAAG;AAC7B,YAAM,QAAQ;AACd,UAAI,OAAO,MAAM,UAAU,SAAU,QAAO,MAAM;AAClD,UAAI,OAAO,MAAM,SAAS,SAAU,QAAO,MAAM;AACjD,UAAI,OAAO,MAAM,UAAU,UAAU;AACnC,cAAM,YAAY,MAAM,MAAM,MAAM,IAAI,EAAE,CAAC;AAC3C,YAAI,UAAU,UAAU,GAAI,QAAO;AACnC,eAAO,UAAU,MAAM,GAAG,EAAE,IAAI;AAAA,MAClC;AACA,UAAI,OAAO,MAAM,SAAS,SAAU,QAAO,MAAM;AACjD,UAAI,OAAO,MAAM,SAAS,SAAU,QAAO,MAAM;AAAA,IACnD;AAGA,QAAI,MAAM,UAAU,MAAM,QAAQ,MAAM,MAAM,GAAG;AAC/C,YAAM,YAAY,MAAM,OAAO,KAAK,CAAC,MAAsB,EAAE,SAAS,MAAM;AAC5E,UAAI,WAAW,SAAS,MAAM;AAE5B,cAAM,YAAY,UAAU,QAAQ,KAAK,MAAM,IAAI,EAAE,CAAC;AACtD,YAAI,UAAU,UAAU,GAAI,QAAO;AACnC,eAAO,UAAU,MAAM,GAAG,EAAE,IAAI;AAAA,MAClC;AAAA,IACF;AAGA,WAAO,MAAM,GAAG,QAAQ,MAAM,GAAG,EAAE,QAAQ,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC;AAAA,EAC5E,GAAG,CAAC,CAAC;AAGL,QAAM,eAAeC,SAAQ,MAAM;AACjC,QAAI,CAAC,iBAAiB,CAAC,eAAe,OAAQ,QAAO,CAAC;AACtD,QAAI,cAAc;AAClB,WAAO,eAAe,IAAI,CAAC,OAAO,UAAU;AAC1C,YAAM,iBAAiB,MAAM,iBAAiB;AAC9C,oBAAc,MAAM;AACpB,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,UAAW,MAAM,YAAY,gBAAiB;AAAA,QAC9C,OAAO,cAAc,KAAK;AAAA,QAC1B;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,gBAAgB,eAAe,aAAa,CAAC;AAGjD,EAAAM,WAAU,MAAM;AACd,QAAI,aAAa,SAAS,GAAG;AAC3B,uBAAiB,YAAY;AAAA,IAC/B;AAAA,EACF,GAAG,CAAC,cAAc,cAAc,CAAC;AAGjC,uBAAqB,UAAU,kBAAkB,uBAAuB,eAAe;AAKvF,QAAM,gBAAgBC;AAAA,IACpB,CAAC,MAAqB;AAEpB,YAAM,WAAW,SAAS;AAC1B,UACE,aACC,SAAS,YAAY,WACpB,SAAS,YAAY,cACrB,SAAS,YAAY,WACvB;AACA;AAAA,MACF;AAGA,UAAI,aAAc;AAElB,UAAI,iBAAiB;AAEnB,gBAAQ,EAAE,KAAK;AAAA,UACb,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AACH,cAAE,eAAe;AACjB,4BAAgB,UAAU;AAC1B;AAAA,UACF,KAAK;AAAA,UACL,KAAK;AACH,cAAE,eAAe;AACjB,4BAAgB,UAAU;AAC1B;AAAA,UACF,KAAK;AACH,cAAE,eAAe;AACjB,4BAAgB,UAAU,CAAC;AAC3B;AAAA,UACF,KAAK;AACH,cAAE,eAAe;AACjB,4BAAgB,UAAU,qBAAqB,UAAU,CAAC;AAC1D;AAAA,QACJ;AAAA,MACF,OAAO;AAEL,gBAAQ,EAAE,KAAK;AAAA,UACb,KAAK;AACH,cAAE,eAAe;AACjB,mBAAO;AACP;AAAA,UACF,KAAK;AACH,mBAAO,KAAK,IAAI,eAAe,UAAU,IAAI,iBAAiB,OAAO,CAAC;AACtE;AAAA,UACF,KAAK;AACH,mBAAO,KAAK,IAAI,eAAe,UAAU,IAAI,CAAC,CAAC;AAC/C;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,iBAAiB,cAAc,QAAQ,QAAQ,eAAe;AAAA,EACjE;AAEA,EAAAD,WAAU,MAAM;AACd,QAAI,WAAY;AAChB,WAAO,iBAAiB,WAAW,aAAa;AAChD,WAAO,MAAM,OAAO,oBAAoB,WAAW,aAAa;AAAA,EAClE,GAAG,CAAC,eAAe,UAAU,CAAC;AAG9B,MAAI,cAAc;AAChB,WACE,gBAAAT;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,WAAU;AAAA,QACV,OAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,UAAU;AAAA,QACZ;AAAA,QAEA,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA,UAAU;AAAA,YACV;AAAA,YACA;AAAA;AAAA,QACF;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW,aAAa,eAAe,uBAAuB,EAAE,GAC9D,MAAM,UAAU,aAAa,0BAA0B,EACzD;AAAA,MACA,SAAS;AAAA,MACT,eAAe,MAAM;AAAA,MACrB,OAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO;AAAA,QACP,aAAa,GAAG,eAAe,KAAK,MAAM,eAAe,MAAM;AAAA,QAC/D,QAAQ;AAAA,QACR,UAAU;AAAA;AAAA;AAAA,QAGV,QAAQ,cAAc,eAAe,SAAY;AAAA,QACjD,aAAa,eAAe,UAAU;AAAA,MACxC;AAAA,MAGA;AAAA,wBAAAD,MAAC,WAAM,KAAK,UAAU,SAAQ,QAAO,OAAc;AAAA,QAGnD,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,UAAU;AAAA,YACV;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QAGA,gBAAAC,OAAC,SAAI,WAAU,wBAEZ;AAAA,4BAAkB,cACjB,gBAAAD,MAAC,SAAI,WAAU,8CACb,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,WAAW;AAAA,cACX;AAAA,cACA,YAAY;AAAA,cACZ,UAAU;AAAA;AAAA,UACZ,GACF;AAAA,UAID,CAAC,kBAAkB,iBAAiB;AAAA;AAAA;AAAA;AAAA,UAKnC,gBAAAA,MAAC,SAA2B,WAAU,iDACpC,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP;AAAA,cACA;AAAA,cACA,WAAW;AAAA,cACX,YAAY,cAAc;AAAA,cAC1B,UAAU;AAAA;AAAA,UACZ,KARQ,cAAc,EASxB;AAAA,UAID,CAAC,kBAAkB,gBAClB,gBAAAA;AAAA,YAAC;AAAA;AAAA,cAEC,WAAW,8CACT,MAAM,UAAU,SAAS,uBAAuB,MAAM,KAAK,KAAK,EAClE;AAAA,cACA,OACE,MAAM,UAAU,SAAS,EAAE,WAAW,cAAc,MAAM,QAAQ,MAAM,IAAI;AAAA,cAG9E,0BAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA,UAAU;AAAA,kBACV;AAAA;AAAA,cACF;AAAA;AAAA,YAfK,aAAa;AAAA,UAgBpB;AAAA,UAID,gBAAgB,aAAa,kBAAkB,SAC9C,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,UAAU,IAAI;AAAA,cACd;AAAA,cACA,SAAS,oBAAoB,cAAc,aAAa,cAAc;AAAA,cACtE,UAAU;AAAA,cACV,cAAc;AAAA,cACd,OAAO;AAAA,cACP,UAAU;AAAA;AAAA,UACZ;AAAA,UAID,eACC,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO;AAAA,gBACL,UAAU;AAAA,gBACV,KAAK;AAAA,gBACL,OAAO;AAAA,gBACP,SAAS;AAAA,gBACT,YAAY;AAAA,gBACZ,cAAc;AAAA,gBACd,OAAO;AAAA,gBACP,YAAY;AAAA,gBACZ,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,QAAQ;AAAA,gBACR,UAAU;AAAA,gBACV,eAAe;AAAA,gBACf,WAAW;AAAA,cACb;AAAA,cAEA;AAAA,gCAAAD,MAAC,SAAI,OAAO,EAAE,OAAO,WAAW,YAAY,QAAQ,cAAc,MAAM,GAAG,wBAE3E;AAAA,gBACA,gBAAAC,OAAC,SACC;AAAA,kCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,uBAAS;AAAA,kBAAQ;AAAA,kBACjD,gBAAAA,MAAC,UAAK,OAAO,EAAE,OAAO,UAAU,GAC5B,wBAAkC,YAAY,OAClD;AAAA,mBACF;AAAA,gBACA,gBAAAC,OAAC,SACC;AAAA,kCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,oBAAM;AAAA,kBAAO;AAAA,kBAAE,oBAAoB;AAAA,kBAAE;AAAA,kBACpE,eAAe;AAAA,kBAAQ;AAAA,kBACxB,gBAAAC,OAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG;AAAA;AAAA,oBAAE,cAAc,MAAM;AAAA,oBAAO;AAAA,qBAAC;AAAA,mBAChE;AAAA,gBACA,gBAAAA,OAAC,SACC;AAAA,kCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,mBAAK;AAAA,kBAAO;AAAA,kBAAE,YAAY,QAAQ,CAAC;AAAA,kBAAE;AAAA,kBAAI;AAAA,kBACxE,cAAc,QAAQ,CAAC;AAAA,kBAAE;AAAA,kBAAE;AAAA,kBAC5B,gBAAAC,OAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG;AAAA;AAAA,qBACjB,cAAc,KAAK,QAAQ,CAAC;AAAA,oBAAE;AAAA,oBAAe,IAAI,SAAS,QAAQ,CAAC;AAAA,oBAAE;AAAA,qBACpF;AAAA,mBACF;AAAA,gBACA,gBAAAA,OAAC,SACC;AAAA,kCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,wBAAU;AAAA,kBAAO;AAAA,kBAAE,UAAU,QAAQ,CAAC;AAAA,kBAAE;AAAA,kBAAI;AAAA,mBAC1E,cAAc,YAAY,GAAG,QAAQ,CAAC;AAAA,kBAAE;AAAA,mBAC5C;AAAA,gBACA,gBAAAC,OAAC,SACC;AAAA,kCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,sBAAQ;AAAA,kBAAO;AAAA,kBAAE;AAAA,kBAAe;AAAA,kBAC/D,IAAI,MAAM,SAAS,SAAS;AAAA,kBAAG;AAAA,kBAChC,gBAAAC,OAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG;AAAA;AAAA,oBAC5B,IAAI,MAAM,SAAS,cAAc,GAAG,QAAQ;AAAA,oBAAO;AAAA,qBACvD;AAAA,mBACF;AAAA,gBACA,gBAAAA,OAAC,SACC;AAAA,kCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,uBAAS;AAAA,kBAAQ;AAAA,kBAChD,eAAe,QAAQ,GAAG,eAAe,KAAK,IAAI,eAAe,MAAM;AAAA,kBAAI;AAAA,kBAC5E,gBAAAC,OAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG;AAAA;AAAA,oBAAE;AAAA,oBAAY;AAAA,qBAAC;AAAA,mBACjD;AAAA,gBACA,gBAAAA,OAAC,SACC;AAAA,kCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,sBAAQ;AAAA,kBAAQ;AAAA,kBAChD,gBAAAA,MAAC,UAAK,OAAO,EAAE,OAAO,YAAY,YAAY,UAAU,GACrD,sBAAY,QAAQ,MACvB;AAAA,kBACC,kBAAkB,gBAAAA,MAAC,UAAK,OAAO,EAAE,OAAO,UAAU,GAAG,sBAAQ;AAAA,mBAChE;AAAA,gBACC,gBACE,MAAM;AACL,wBAAM,cAAca,kBAAiB,IAAI,UAAW,WAAW;AAC/D,wBAAM,eAAe,oBAAoB,aAAa,cAAc;AACpE,yBACE,gBAAAZ,OAACa,WAAA,EACC;AAAA,oCAAAb,OAAC,SACC;AAAA,sCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,uBAAS;AAAA,sBAAQ;AAAA,sBAChD,IAAI,UAAU,QAAQ,UAAU;AAAA,sBAAE;AAAA,sBAAS;AAAA,sBAC5C,gBAAAC,OAAC,UAAK,OAAO,EAAE,OAAO,kBAAkB,YAAY,OAAO,GAAG;AAAA;AAAA,wBAC1D,kBAAkB,OAAO;AAAA,wBAAM;AAAA,yBACnC;AAAA,uBACF;AAAA,oBACA,gBAAAA,OAAC,SACC;AAAA,sCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,yBAAW;AAAA,sBAAQ;AAAA,sBACnD,gBAAAA,MAAC,UAAK,OAAO,EAAE,OAAO,eAAe,YAAY,UAAU,GACxD,iBAAO,YAAY,GACtB;AAAA,sBAAQ;AAAA,sBACR,gBAAAC,OAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG;AAAA;AAAA,wBACpB,OAAO,SAAS;AAAA,wBAAE;AAAA,wBAAS,OAAO,cAAc,CAAC;AAAA,wBAAE;AAAA,yBAC/D;AAAA,uBACF;AAAA,oBACA,gBAAAA,OAAC,SACC;AAAA,sCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,wBAAU;AAAA,sBAAQ;AAAA,sBAClD,gBAAAA,MAAC,UAAK,OAAO,EAAE,OAAO,cAAc,YAAY,UAAU,GACvD,wBAAc,IAAI,YAAY,KAAK,MAAM,GAAG,EAAE,CAAC,SAAS,QAC3D;AAAA,uBACF;AAAA,oBACC,eACC,gBAAAC,OAAC,SACC;AAAA,sCAAAD,MAAC,UAAK,OAAO,EAAE,OAAO,OAAO,GAAG,uBAAS;AAAA,sBAAQ;AAAA,sBACjD,gBAAAC,OAAC,UAAK,OAAO,EAAE,OAAO,UAAU,GAC7B;AAAA,oCAAY,UAAU,QAAQ,CAAC;AAAA,wBAAE;AAAA,wBAAE,YAAY,QAAQ,QAAQ,CAAC;AAAA,yBACnE;AAAA,uBACF;AAAA,qBAEJ;AAAA,gBAEJ,GAAG;AAAA;AAAA;AAAA,UACP;AAAA,WAEJ;AAAA,QAGC,CAAC,eAAe,sBACf,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,UAAU;AAAA,cACV,KAAK;AAAA,cACL,MAAM;AAAA,cACN,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,gBAAgB;AAAA,cAChB,eAAe;AAAA,cACf,KAAK;AAAA,cACL,YAAY;AAAA,cACZ,OAAO;AAAA,cACP,UAAU;AAAA,cACV,QAAQ;AAAA,YACV;AAAA,YAEA;AAAA,8BAAAD,MAAC,UAAK,OAAO,EAAE,UAAU,OAAO,GAAG,uBAAS;AAAA,cAC5C,gBAAAA,MAAC,UAAM,8BAAmB;AAAA;AAAA;AAAA,QAC5B;AAAA,QAID,CAAC,cAAc,CAAC,mBAAmB,gBAClC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,SAAS;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QAID,CAAC,cAAc,CAAC,mBAAmB,CAAC,gBAAgB,gBACnD,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,MAAM;AAAA,cACN,OAAO;AAAA,cACP,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,QAAQ;AAAA,YACV;AAAA,YAEA,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO;AAAA,gBACP,SAAS;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA;AAAA;AAAA,YACF;AAAA;AAAA,QACF;AAAA,QAID,CAAC,cAAc,mBACd,gBAAAA,MAAC,wBAAqB,OAAO,eAAe,UAAU,iBAAiB;AAAA,QAIxE,CAAC,mBAAmB,eACnB,gBAAAA,MAAC,SAAI,WAAU,4BACb,0BAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,SAAQ,OAAM,MAAK,QAAO,MACrD,0BAAgB,UACf,gBAAAA,MAAC,UAAK,GAAE,mCAAkC,IAE1C,gBAAAA,MAAC,UAAK,GAAE,iBAAgB,GAE5B,KAP6C,KAAK,IAAI,CAQxD;AAAA;AAAA;AAAA,EAEJ;AAEJ;;;A+B9wCU,gBAAAe,OAsBJ,QAAAC,cAtBI;AAjBH,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA2B;AACzB,SACE,gBAAAA,OAAC,SAAI,WAAU,uBAEb;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,SAAS,QAAQ;AAAA,QACjB,OAAM;AAAA,QACN,cAAW;AAAA,QAEX,0BAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,yGAAwG,GAClH;AAAA;AAAA,IACF;AAAA,IAGA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,SAAS,QAAQ;AAAA,QACjB,cAAY,MAAM,YAAY,UAAU;AAAA,QAEvC,gBAAM,YACL,gBAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,mCAAkC,GAC5C,IAEA,gBAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,iBAAgB,GAC1B;AAAA;AAAA,IAEJ;AAAA,IAGA,gBAAAC,OAAC,UAAK,WAAU,eACb;AAAA,iBAAW,MAAM,WAAW;AAAA,MAAE;AAAA,MAAI,WAAW,MAAM,aAAa;AAAA,OACnE;AAAA,IAGA,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IAGA,gBAAAC,OAAC,UAAK,WAAU,kBACb;AAAA,YAAM,oBAAoB;AAAA,MAAE;AAAA,MAAE,MAAM;AAAA,OACvC;AAAA,IAGC,MAAM,eACL,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,mBAAmB,MAAM,gBAAgB,QAAQ,4BAA4B,EAAE;AAAA,QAC1F,SAAS,MAAM,QAAQ,iBAAiB;AAAA,QACxC,OACE,MAAM,gBAAgB,QAClB,kBACA,MAAM,gBAAgB,aACpB,uBACA;AAAA,QAER,cAAW;AAAA,QAEX,0BAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,uPAAsP,GAChQ;AAAA;AAAA,IACF;AAAA,KAEJ;AAEJ;;;ACrEU,gBAAAE,OAsBJ,QAAAC,cAtBI;AAXH,SAAS,mBAAmB,EAAE,OAAO,QAAQ,GAA4B;AAC9E,SACE,gBAAAA,OAAC,SAAI,WAAU,wBAEb;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,SAAS,QAAQ;AAAA,QACjB,OAAM;AAAA,QACN,cAAW;AAAA,QAEX,0BAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,yGAAwG,GAClH;AAAA;AAAA,IACF;AAAA,IAGA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,SAAS,QAAQ;AAAA,QACjB,cAAY,MAAM,YAAY,UAAU;AAAA,QAEvC,gBAAM,YACL,gBAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,mCAAkC,GAC5C,IAEA,gBAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,iBAAgB,GAC1B;AAAA;AAAA,IAEJ;AAAA,IAGA,gBAAAC,OAAC,SAAI,WAAU,gBACb;AAAA,sBAAAD,MAAC,SAAK,qBAAW,MAAM,WAAW,GAAE;AAAA,MACpC,gBAAAA,MAAC,SAAI,WAAU,sBAAsB,qBAAW,MAAM,aAAa,GAAE;AAAA,OACvE;AAAA,IAGA,gBAAAC,OAAC,SAAI,WAAU,mBACZ;AAAA,YAAM,oBAAoB;AAAA,MAAE;AAAA,MAAE,MAAM;AAAA,OACvC;AAAA,IAGC,MAAM,eACL,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,oBAAoB,MAAM,gBAAgB,QAAQ,6BAA6B,EAAE;AAAA,QAC5F,SAAS,MAAM,QAAQ,iBAAiB;AAAA,QACxC,OACE,MAAM,gBAAgB,QAClB,kBACA,MAAM,gBAAgB,aACpB,uBACA;AAAA,QAER,cAAW;AAAA,QAEX,0BAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,uPAAsP,GAChQ;AAAA;AAAA,IACF;AAAA,IAID,QAAQ,oBACP,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,oBAAoB,MAAM,eAAe,6BAA6B,EAAE;AAAA,QACnF,SAAS,QAAQ;AAAA,QACjB,OAAO,MAAM,eAAe,wBAAwB;AAAA,QACpD,cAAY,MAAM,eAAe,oBAAoB;AAAA,QAEpD,gBAAM,eACL,gBAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,iFAAgF,GAC1F,IAEA,gBAAAA,MAAC,SAAI,SAAQ,aAAY,MAAK,gBAAe,OAAM,MAAK,QAAO,MAC7D,0BAAAA,MAAC,UAAK,GAAE,kFAAiF,GAC3F;AAAA;AAAA,IAEJ;AAAA,KAEJ;AAEJ;;;AC5FA,SAAS,UAAAE,SAAQ,YAAAC,WAAU,eAAAC,cAAa,aAAAC,kBAAiB;AAwGrD,SAEI,OAAAC,OAFJ,QAAAC,cAAA;AArEJ,IAAM,gBAA+B;AAAA,EACnC,WAAW;AAAA,EACX,aAAa;AAAA,EACb,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,cAAc;AAChB;AAEO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAE5B,QAAM,WAAWC,QAAsB,aAAa;AACpD,QAAM,aAAaA,QAA+B,IAAI;AACtD,QAAM,gBAAgBA,QAAO,KAAK;AAGlC,QAAM,CAAC,EAAE,OAAO,IAAIC,UAAS,CAAC;AAG9B,QAAM,oBAAoBC;AAAA,IACxB,CAAC,UAAyB;AACxB,eAAS,UAAU;AACnB,UAAI,mBAAmB,MAAM,cAAc,cAAc,SAAS;AAChE,sBAAc,UAAU,MAAM;AAC9B,wBAAgB,MAAM,SAAS;AAAA,MACjC;AAAA,IACF;AAAA,IACA,CAAC,eAAe;AAAA,EAClB;AAEA,QAAM,sBAAsBA;AAAA,IAC1B,CAAC,aAAwE;AACvE,YAAM,UAAU,CAAC,WAAW;AAC5B,iBAAW,UAAU;AAErB,UAAI,QAAS,SAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,IACnC;AAAA,IACA,CAAC;AAAA,EACH;AAGA,EAAAC,WAAU,MAAM;AACd,UAAM,WAAW,YAAY,MAAM;AACjC,cAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,IACtB,GAAG,GAAG;AACN,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,CAAC;AAEL,SACE,gBAAAJ,OAAC,SAAI,WAAU,6BACb;AAAA,oBAAAD,MAAC,SAAI,WAAU,oCACb,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc;AAAA,QACd,cAAc,CAAC;AAAA,QACf,uBAAuB;AAAA,QACvB,iBAAiB;AAAA,QACjB;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF,GACF;AAAA,IACC,WAAW,WACV,gBAAAA,MAAC,sBAAmB,OAAO,SAAS,SAAS,SAAS,WAAW,SAAS;AAAA,KAE9E;AAEJ;;;AChJA,SAAS,WAAAM,iBAAe;AAExB,SAAS,qBAAqB,4BAA4B;AAUnD,SAAS,kBACd,OACA,SACgB;AAChB,QAAM,OAAO,eAAe,YAAY,MAAM;AAC9C,QAAM,mBAAmB,YAAY,SAAS,OAAQ,WAAW;AAEjE,SAAOC,UAAQ,MAAM;AACnB,UAAM,QAAQ,oBAAoB,OAAO,kBAAkB;AAAA,MACzD,QAAQ;AAAA,IACV,CAAC;AACD,WAAO,EAAE,OAAO,OAAO,qBAAqB,OAAO,gBAAgB,EAAE;AAAA,EACvE,GAAG,CAAC,OAAO,gBAAgB,CAAC;AAC9B;;;ACzBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;;;ACLP,SAAS,YAAAC,WAAU,WAAAC,iBAAe;AAClC;AAAA,EACE;AAAA,OAGK;AACP,SAAS,iBAAAC,sBAAqB;AAiBnB,SAkNA,YAAAC,WAlNA,OAAAC,OA4DP,QAAAC,cA5DO;AAFJ,SAAS,WAAW,EAAE,MAAM,GAAgB;AACjD,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACzD,WAAO,gBAAAD,MAAC,UAAK,WAAU,mBAAkB,oBAAC;AAAA,EAC5C;AACA,SAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAmB,iBAAO,KAAK,GAAE;AAC1D;AAEO,SAAS,gBAAgB,EAAE,MAAM,GAAgB;AACtD,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACzD,WAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,oBAAC;AAAA,EAC5C;AACA,SAAO,gBAAAA,MAAC,UAAK,WAAU,8CAA8C,iBAAO,KAAK,GAAE;AACrF;AAEO,SAAS,eAAe,EAAE,MAAM,GAAgB;AACrD,QAAM,QAAQE,UAAQ,MAAM;AAC1B,QAAI,OAAO,UAAU,YAAY,UAAU,GAAI,QAAO;AACtD,QAAI;AACF,YAAM,MAAMC,eAAc,KAAK;AAC/B,aAAO,IAAI;AAAA,IACb,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AACV,MAAI,CAAC,MAAO,QAAO,gBAAAH,MAAC,UAAK,WAAU,mBAAkB,oBAAC;AACtD,SACE,gBAAAA,MAAC,SAAI,WAAU,sBACb,0BAAAA,MAAC,oBAAiB,OAAc,GAClC;AAEJ;AAEO,SAAS,aAAa,EAAE,MAAM,GAAgB;AACnD,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,oBAAC;AAAA,EAC5C;AACA,SAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAmB,iBAAO,KAAK,GAAE;AAC1D;AAEO,SAAS,cAAc,EAAE,MAAM,GAAgB;AACpD,QAAM,KAAK,QAAQ,KAAK;AACxB,SACE,gBAAAA,MAAC,UAAK,WAAW,sCAAsC,KAAK,OAAO,KAAK,IACrE,eAAK,OAAO,OACf;AAEJ;AAEO,SAAS,WAAW,EAAE,OAAO,OAAO,GAAgB;AACzD,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACzD,WAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,oBAAC;AAAA,EAC5C;AACA,QAAM,SAAS,OAAO,QAAQ;AAC9B,QAAM,UAAU,UAAU,OAAO,UAAU,WAAY,OAAO,KAAK,KAAK,QAAS,OAAO,KAAK;AAC7F,SAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAmB,mBAAQ;AACpD;AAEO,SAAS,YAAY,EAAE,MAAM,GAAgB;AAClD,MAAI,OAAO,UAAU,YAAY,UAAU,IAAI;AAC7C,WAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,oBAAC;AAAA,EAC5C;AACA,SACE,gBAAAC,OAAC,UAAK,WAAU,mBACd;AAAA,oBAAAD,MAAC,UAAK,WAAU,2BAA0B,OAAO,EAAE,YAAY,MAAM,GAAG,eAAW,MAAC;AAAA,IACpF,gBAAAA,MAAC,UAAK,WAAU,wBAAwB,iBAAM;AAAA,KAChD;AAEJ;AAEO,SAAS,WAAW,EAAE,OAAO,OAAO,GAAgB;AACzD,MAAI,OAAO,UAAU,YAAY,UAAU,IAAI;AAC7C,WAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,oBAAC;AAAA,EAC5C;AACA,QAAM,MAAM,OAAO;AACnB,MAAI,UAAU;AACd,MAAI;AACF,UAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,QAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,GAAG;AACjC,UAAI,QAAQ,QAAQ;AAClB,kBAAU,KAAK,mBAAmB;AAAA,MACpC,WAAW,QAAQ,QAAQ;AACzB,kBAAU,KAAK,mBAAmB;AAAA,MACpC,OAAO;AACL,kBAAU,KAAK,eAAe;AAAA,MAChC;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAmB,mBAAQ;AACpD;AAIO,SAAS,cAAc,EAAE,OAAO,OAAO,GAAgB;AAC5D,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AAC/C,WAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,oBAAC;AAAA,EAC5C;AACA,QAAM,aAAa,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,MAAM,CAAC,IAAI,OAAO;AAC1E,QAAM,SAAS,YAAY,QAAQ;AACnC,SACE,gBAAAA,MAAC,SAAI,WAAU,sBACZ,gBAAM,IAAI,CAAC,MAAM,MAAM;AACtB,UAAM,QACJ,UAAU,OAAO,SAAS,WAAY,OAAO,IAAI,KAAK,OAAO,IAAI,IAAK,OAAO,IAAI;AACnF,WACE,gBAAAA,MAAC,UAAa,WAAU,kBACrB,mBADQ,CAEX;AAAA,EAEJ,CAAC,GACH;AAEJ;AAEO,SAAS,gBAAgB,OAAoB;AAClD,QAAM,EAAE,OAAO,QAAQ,YAAY,UAAU,SAAS,QAAQ,IAAI;AAClE,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AAC/C,WAAO,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,sBAAQ;AAAA,EACnD;AACA,QAAM,cAAc,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,MAAM,CAAC,IAAI,OAAO,UAAU,CAAC;AACtF,QAAM,YAAY,WAAW,QAAQ;AACrC,SACE,gBAAAA,MAAC,SAAI,WAAU,wBACZ,gBAAM,IAAI,CAAC,MAAM,MAAM;AACtB,UAAM,QAAQ,iBAAiB,WAAW,MAAM,CAAC;AACjD,WACE,gBAAAC,OAAC,SAAY,WAAU,kBACpB;AAAA,cAAQ,gBAAAD,MAAC,QAAG,WAAU,yBAAyB,iBAAM,IAAQ;AAAA,MAC9D,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,UACA,SAAS,GAAG,OAAO,IAAI,CAAC;AAAA,UACxB;AAAA,UACA,uBAAqB;AAAA;AAAA,MACvB;AAAA,SAVQ,CAWV;AAAA,EAEJ,CAAC,GACH;AAEJ;AAEA,SAAS,iBACP,MACA,MACA,OACQ;AACR,MAAI,CAAC,KAAM,QAAO,QAAQ,QAAQ,CAAC;AACnC,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,UAAM,IAAK,KAAiC,KAAK,SAAS;AAC1D,QAAI,OAAO,MAAM,YAAY,MAAM,GAAI,QAAO;AAC9C,QAAI,OAAO,MAAM,SAAU,QAAO,OAAO,CAAC;AAAA,EAC5C;AACA,SAAO,QAAQ,QAAQ,CAAC;AAC1B;AAEO,SAAS,YAAY,OAAkD;AAC5E,QAAM,EAAE,OAAO,QAAQ,YAAY,UAAU,SAAS,SAAS,cAAc,IAAI;AACjF,QAAM,QAAQ,CAAC,gBAAiB,OAAO,QAAQ,SAAS,OAAO,QAAS;AACxE,QAAM,OAAO,OAAO,QAAQ,QAAQ,OAAO;AAC3C,QAAM,OAAO,SAAS,OAAO,UAAU,WAAY,QAAoC,CAAC,MAAM,CAAC;AAC/F,QAAM,cAAc,OAAO,QAAQ,OAAO,cAAc,CAAC,CAAC;AAE1D,SACE,gBAAAC,OAAC,aAAQ,WAAU,mBAChB;AAAA,YAAQ,gBAAAD,MAAC,QAAG,WAAU,0BAA0B,iBAAM,IAAQ;AAAA,IAC9D,OAAO,gBAAAA,MAAC,OAAE,WAAU,yBAAyB,gBAAK,IAAO;AAAA,IACzD,YAAY,IAAI,CAAC,CAAC,KAAK,UAAU,MAChC,gBAAAA,MAACD,WAAA,EACC,0BAAAC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,WAAW,QAAQ,SAAS,WAAW,SAAS;AAAA,QACvD,MAAM,WAAW,QAAQ,QAAQ,WAAW;AAAA,QAC5C,UAAU;AAAA,QAEV,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,IAAI,GAAG;AAAA,YACd,QAAQ;AAAA,YACR;AAAA,YACA;AAAA,YACA,SAAS,GAAG,OAAO,IAAI,GAAG;AAAA,YAC1B;AAAA,YACA,uBAAqB;AAAA;AAAA,QACvB;AAAA;AAAA,IACF,KAfa,GAgBf,CACD;AAAA,KACH;AAEJ;AAOA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,YAAY,gBAAgB,QAAQ;AAC1C,MAAI,WAAW;AACb,WAAO,gBAAAA,MAAAD,WAAA,EAAG,UAAS;AAAA,EACrB;AACA,SACE,gBAAAE,OAAC,SAAI,WAAU,iBACb;AAAA,oBAAAD,MAAC,SAAI,WAAU,mBAAkB,OAAO,MACrC,iBACH;AAAA,IACA,gBAAAA,MAAC,SAAK,UAAS;AAAA,KACjB;AAEJ;AAEA,SAAS,gBAAgB,QAAwC;AAC/D,QAAM,IAAI,OAAO;AACjB,QAAM,YAAY,MAAM,WAAY,MAAM,QAAQ,CAAC,KAAK,EAAE,SAAS,OAAO;AAC1E,MAAI,WAAW;AACb,WAAO,cAAc,MAAM,MAAM;AAAA,EACnC;AACA,QAAM,aAAa,MAAM,YAAa,MAAM,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ;AAC7E,MAAI,WAAY,QAAO;AACvB,MAAI,OAAO,SAAS,OAAO,MAAO,QAAO;AACzC,MAAI,OAAO,WAAW,cAAc,OAAO,QAAQ,YAAY,WAAY,QAAO;AAClF,SAAO;AACT;AAEO,SAAS,WAAW,OAAoB;AAC7C,QAAM,EAAE,OAAO,QAAQ,YAAY,UAAU,SAAS,QAAQ,IAAI;AAClE,QAAM,YAAY,OAAO,SAAS,OAAO,SAAS,CAAC,GAAG,MAAM;AAC5D,QAAM,eAAe,mBAAmB,UAAqC,KAAK;AAClF,QAAM,SAAS,SAAS,YAAY;AACpC,MAAI,CAAC,QAAQ;AACX,WAAO,gBAAAA,MAAC,cAAY,GAAG,OAAO;AAAA,EAChC;AACA,SACE,gBAAAC,OAAC,SAAI,WAAU,kBACb;AAAA,oBAAAD,MAAC,UAAK,WAAU,iCACb,iBAAO,QAAQ,SAAS,OAAO,SAAS,UAAU,eAAe,CAAC,IACrE;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAEA,SAAS,mBAAmB,UAAmC,OAAwB;AACrF,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,QAAI,aAAa,SAAS,CAAC,GAAG,KAAK,EAAG,QAAO;AAAA,EAC/C;AACA,SAAO;AACT;AAEA,SAAS,aAAa,QAA+B,OAAyB;AAC5E,QAAM,IAAI,OAAO;AACjB,QAAM,SAAS,MAAM,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,MAAM,MAAM,IAAI;AAChE,MAAI,CAAC,OAAQ,QAAO;AACpB,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,OAAO,UAAU;AAAA,IAC1B,KAAK;AAAA,IACL,KAAK;AACH,aAAO,OAAO,UAAU;AAAA,IAC1B,KAAK;AACH,aAAO,OAAO,UAAU;AAAA,IAC1B,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AACH,aAAO,MAAM,QAAQ,KAAK;AAAA,IAC5B,KAAK;AACH,aAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAAA,IAC5E;AACE,aAAO;AAAA,EACX;AACF;AAKO,IAAM,UAAiE;AAAA,EAC5E,MAAM;AAAA,EACN,WAAW;AAAA,EACX,UAAU;AAAA,EACV,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,WAAW;AAAA,EACX,OAAO;AAAA,EACP,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,OAAO;AAAA,EACP,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,MAAM;AACR;;;ADvSW,gBAAAI,aAAA;AAlBJ,SAAS,WAAW,OAAmD;AAC5E,QAAM,WAAW,WAAW,MAAM,QAAQ,MAAM,UAAU,KAAK,MAAM;AAErE,MAAI,YAAY,SAAS,QAAQ,QAAQ,MAAM,QAAQ,EAAG,QAAO;AAEjE,QAAM,OAAO,cAAc,QAAQ;AACnC,QAAM,SAAS,QAAQ,IAAI;AAC3B,QAAM,cAA2B;AAAA,IAC/B,OAAO,MAAM;AAAA,IACb,QAAQ;AAAA,IACR,YAAY,MAAM;AAAA,IAClB,UAAU,MAAM;AAAA,IAChB,SAAS,MAAM;AAAA,IACf,SAAS,MAAM;AAAA,EACjB;AACA,MAAI,SAAS,WAAW,SAAS,QAAQ;AAEvC,UAAM,QAAQ;AACd,WAAO,gBAAAA,MAAC,SAAO,GAAG,aAAa,eAAe,MAAM,uBAAuB;AAAA,EAC7E;AACA,SAAO,gBAAAA,MAAC,UAAQ,GAAG,aAAa;AAClC;;;AEVM,gBAAAC,aAAA;AAXC,SAAS,SAAS,OAAsB;AAC7C,QAAM,EAAE,QAAQ,OAAO,OAAO,SAAS,UAAU,eAAe,UAAU,IAAI;AAC9E,QAAM,EAAE,MAAM,IAAI,kBAAkB,OAAO,OAAO;AAElD,QAAM,MACJ,sBACC,YAAY,YAAY,+BAA+B,OACvD,YAAY,IAAI,SAAS,KAAK;AAEjC,SACE,gBAAAA,MAAC,SAAI,WAAW,KAAK,OACnB,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,SAAQ;AAAA,MACR;AAAA;AAAA,EACF,GACF;AAEJ;","names":["Fragment","useRef","useState","useEffect","useCallback","useMemo","isTemplateBlock","getCaptionAtTime","useEffect","useMemo","useEffect","applySurface","jsx","useMemo","jsx","jsx","jsxs","useMemo","jsx","jsxs","jsx","jsxs","useState","useEffect","jsx","jsxs","useState","useEffect","useRef","useEffect","jsx","useRef","useEffect","jsx","jsxs","jsx","jsxs","useMemo","jsx","jsx","useMemo","useState","useEffect","useRef","useCallback","useMemo","useCallback","useRef","resolveTransitionDuration","useState","useEffect","useMemo","VIEWPORT_PRESETS","useCallback","useEffect","useRef","useState","markdownToDoc","DEFAULT_THEME","VIEWPORT_PRESETS","parseMarkdown","useRef","useState","useCallback","jsx","jsxs","useRef","useState","useCallback","jsx","jsxs","jsx","jsxs","useMemo","resolveFontFamily","VIEWPORT_PRESETS","DEFAULT_THEME","Fragment","sanitizeHtmlNodes","jsx","jsx","jsx","jsxs","Fragment","sanitizeHtmlNodes","jsx","jsxs","useMemo","VIEWPORT_PRESETS","DEFAULT_THEME","resolveFontFamily","jsx","jsxs","isTemplateBlock","useMemo","markdownToDoc","parseMarkdown","useRef","useState","VIEWPORT_PRESETS","useEffect","useCallback","DEFAULT_THEME","applySurface","getCaptionAtTime","Fragment","jsx","jsxs","jsx","jsxs","useRef","useState","useCallback","useEffect","jsx","jsxs","useRef","useState","useCallback","useEffect","useMemo","useMemo","Fragment","useMemo","parseMarkdown","Fragment","jsx","jsxs","useMemo","parseMarkdown","jsx","jsx"]}
|