@usefy/screen-recorder 0.1.3
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 +631 -0
- package/dist/index.d.mts +1092 -0
- package/dist/index.d.ts +1092 -0
- package/dist/index.js +3473 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3410 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +1267 -0
- package/package.json +80 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/ScreenRecorder.tsx","../src/constants.ts","../src/useScreenRecorder.ts","../src/hooks/useBrowserSupport.ts","../src/hooks/useDisplayMedia.ts","../src/hooks/useMediaRecorder.ts","../src/hooks/useTimer.ts","../src/hooks/useCountdown.ts","../src/utils/cn.ts","../src/components/Trigger/TriggerIcon.tsx","../src/components/Trigger/Trigger.tsx","../src/components/Controls/Timer.tsx","../src/components/Controls/RecordingControls.tsx","../src/components/Countdown/Countdown.tsx","../src/components/Preview/VideoPlayer.tsx","../src/components/Preview/PreviewModal.tsx","../src/utils/downloadBlob.ts","../src/components/Status/StatusBadge.tsx","../src/components/Status/ErrorMessage.tsx"],"sourcesContent":["// ============================================================================\n// Main Component\n// ============================================================================\n\nexport { ScreenRecorder } from \"./ScreenRecorder\";\n\n// ============================================================================\n// Main Hook\n// ============================================================================\n\nexport { useScreenRecorder } from \"./useScreenRecorder\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type {\n // State types\n RecordingState,\n RecordingResult,\n // Error types\n ScreenRecorderError,\n ScreenRecorderErrorCode,\n // Quality types\n QualityPreset,\n QualityOption,\n // Audio types\n AudioConfig,\n AudioOption,\n // UI types\n TriggerPosition,\n ThemeOption,\n RenderMode,\n // Props types\n ScreenRecorderProps,\n // Hook types\n UseScreenRecorderOptions,\n UseScreenRecorderReturn,\n // Browser support types\n BrowserSupport,\n} from \"./types\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nexport {\n QUALITY_PRESETS,\n DEFAULT_OPTIONS,\n SUPPORTED_MIME_TYPES,\n ERROR_MESSAGES,\n TIMER_WARNING_THRESHOLD,\n TIMER_CRITICAL_THRESHOLD,\n} from \"./constants\";\n\n// ============================================================================\n// Sub-hooks (for advanced usage)\n// ============================================================================\n\nexport {\n useBrowserSupport,\n checkBrowserSupport,\n getBestMimeType,\n useDisplayMedia,\n useMediaRecorder,\n useTimer,\n useCountdown,\n formatTime,\n} from \"./hooks\";\n\nexport type {\n UseDisplayMediaOptions,\n UseDisplayMediaReturn,\n UseMediaRecorderOptions,\n UseMediaRecorderReturn,\n UseTimerOptions,\n UseTimerReturn,\n UseCountdownOptions,\n UseCountdownReturn,\n} from \"./hooks\";\n\n// ============================================================================\n// Utils\n// ============================================================================\n\nexport { cn, downloadBlob, generateFilename, formatBytes } from \"./utils\";\n\n// ============================================================================\n// UI Components (for custom compositions)\n// ============================================================================\n\nexport {\n // Trigger\n Trigger,\n TriggerIcon,\n RecordingIcon,\n StopIcon,\n PauseIcon,\n PlayIcon,\n DownloadIcon,\n CloseIcon,\n RefreshIcon,\n CheckIcon,\n WarningIcon,\n // Controls\n Timer,\n RecordingControls,\n // Countdown\n Countdown,\n // Preview\n VideoPlayer,\n PreviewModal,\n // Status\n StatusBadge,\n ErrorMessage,\n} from \"./components\";\n\nexport type {\n TriggerProps,\n TimerProps,\n RecordingControlsProps,\n CountdownProps,\n VideoPlayerProps,\n VideoPlayerRef,\n PreviewModalProps,\n StatusBadgeProps,\n ErrorMessageProps,\n} from \"./components\";\n","import { forwardRef, useCallback, useEffect, useRef } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { ScreenRecorderProps } from \"./types\";\nimport { DEFAULT_OPTIONS } from \"./constants\";\nimport { useScreenRecorder } from \"./useScreenRecorder\";\nimport { cn } from \"./utils/cn\";\nimport { Trigger } from \"./components/Trigger\";\nimport { RecordingControls } from \"./components/Controls\";\nimport { Countdown } from \"./components/Countdown\";\nimport { PreviewModal } from \"./components/Preview\";\nimport { ErrorMessage } from \"./components/Status\";\nimport { generateFilename } from \"./utils/downloadBlob\";\n\n/**\n * ScreenRecorder component for capturing screen recordings\n *\n * A complete UI solution for screen recording with floating trigger,\n * recording controls, countdown, and preview modal.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <div>\n * <h1>My Application</h1>\n * <ScreenRecorder\n * onRecordingStop={(result) => {\n * console.log('Recording complete:', result);\n * }}\n * />\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Bug report integration\n * function BugReportForm() {\n * const [recording, setRecording] = useState<RecordingResult | null>(null);\n *\n * return (\n * <form>\n * <textarea placeholder=\"Describe the bug...\" />\n * <ScreenRecorder\n * position=\"bottom-left\"\n * maxDuration={60}\n * onRecordingStop={setRecording}\n * triggerContent={\n * recording ? \"✓ Recording attached\" : \"📹 Record screen\"\n * }\n * />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * }\n * ```\n */\nexport const ScreenRecorder = forwardRef<HTMLDivElement, ScreenRecorderProps>(\n (props, ref) => {\n const {\n // Recording options\n maxDuration = DEFAULT_OPTIONS.maxDuration,\n countdown = DEFAULT_OPTIONS.countdown,\n audio = DEFAULT_OPTIONS.audio,\n quality = DEFAULT_OPTIONS.quality,\n mimeType = DEFAULT_OPTIONS.mimeType,\n\n // UI options\n position = DEFAULT_OPTIONS.position,\n triggerContent,\n showPreview = DEFAULT_OPTIONS.showPreview,\n showTimer = DEFAULT_OPTIONS.showTimer,\n zIndex = DEFAULT_OPTIONS.zIndex,\n theme = DEFAULT_OPTIONS.theme,\n className,\n filename = DEFAULT_OPTIONS.filename,\n\n // Callbacks\n onRecordingStart,\n onRecordingStop,\n onPause,\n onResume,\n onDownload,\n onError,\n onPermissionDenied,\n onTick,\n\n // Advanced\n autoDownload = DEFAULT_OPTIONS.autoDownload,\n disabled = false,\n renderMode = DEFAULT_OPTIONS.renderMode,\n } = props;\n\n // Track previous state for detecting transitions\n const prevStateRef = useRef<string>(\"idle\");\n const prevElapsedRef = useRef<number>(0);\n const autoDownloadTriggeredRef = useRef<boolean>(false);\n\n // Use the main hook\n const recorder = useScreenRecorder({\n maxDuration,\n countdown,\n audio,\n quality,\n mimeType,\n onError: (error) => {\n if (error.code === \"PERMISSION_DENIED\") {\n onPermissionDenied?.();\n }\n onError?.(error);\n },\n });\n\n // Call onRecordingStart when recording actually starts\n useEffect(() => {\n if (\n recorder.state === \"recording\" &&\n prevStateRef.current !== \"recording\" &&\n prevStateRef.current !== \"paused\"\n ) {\n onRecordingStart?.();\n }\n prevStateRef.current = recorder.state;\n }, [recorder.state, onRecordingStart]);\n\n // Call onTick when elapsed changes during recording\n useEffect(() => {\n if (\n (recorder.isRecording || recorder.isPaused) &&\n recorder.elapsed !== prevElapsedRef.current\n ) {\n onTick?.(recorder.elapsed, recorder.remaining);\n prevElapsedRef.current = recorder.elapsed;\n }\n }, [\n recorder.elapsed,\n recorder.remaining,\n recorder.isRecording,\n recorder.isPaused,\n onTick,\n ]);\n\n // Handle auto-download and onRecordingStop when showPreview is false\n useEffect(() => {\n if (\n recorder.state === \"stopped\" &&\n recorder.result &&\n !showPreview &&\n !autoDownloadTriggeredRef.current\n ) {\n // Mark as triggered to prevent multiple calls\n autoDownloadTriggeredRef.current = true;\n\n // Call onRecordingStop callback\n onRecordingStop?.(recorder.result);\n\n // Auto-download if enabled\n if (autoDownload) {\n const downloadFilename =\n typeof filename === \"function\"\n ? filename(recorder.result.timestamp)\n : generateFilename(recorder.result.timestamp);\n\n recorder.download(downloadFilename);\n onDownload?.(recorder.result);\n }\n\n // Reset to idle state after a short delay\n setTimeout(() => {\n recorder.reset();\n autoDownloadTriggeredRef.current = false;\n }, 100);\n }\n }, [\n recorder.state,\n recorder.result,\n showPreview,\n autoDownload,\n filename,\n recorder,\n onRecordingStop,\n onDownload,\n ]);\n\n // Reset auto-download trigger when state changes to idle\n useEffect(() => {\n if (recorder.state === \"idle\") {\n autoDownloadTriggeredRef.current = false;\n }\n }, [recorder.state]);\n\n // Handle recording start\n const handleStart = useCallback(async () => {\n await recorder.start();\n }, [recorder]);\n\n // Handle recording stop\n const handleStop = useCallback(() => {\n recorder.stop();\n }, [recorder]);\n\n // Handle pause\n const handlePause = useCallback(() => {\n recorder.pause();\n onPause?.();\n }, [recorder, onPause]);\n\n // Handle resume\n const handleResume = useCallback(() => {\n recorder.resume();\n onResume?.();\n }, [recorder, onResume]);\n\n // Handle download\n const handleDownload = useCallback(() => {\n if (!recorder.result) return;\n\n const downloadFilename =\n typeof filename === \"function\"\n ? filename(recorder.result.timestamp)\n : generateFilename(recorder.result.timestamp);\n\n recorder.download(downloadFilename);\n onDownload?.(recorder.result);\n }, [recorder, filename, onDownload]);\n\n // Handle re-record\n const handleReRecord = useCallback(async () => {\n recorder.reset();\n // Start new recording immediately\n await recorder.start();\n }, [recorder]);\n\n // Handle preview close\n const handlePreviewClose = useCallback(() => {\n if (recorder.result) {\n onRecordingStop?.(recorder.result);\n }\n recorder.reset();\n }, [recorder, onRecordingStop]);\n\n // Handle countdown cancel\n const handleCountdownCancel = useCallback(() => {\n recorder.stop();\n }, [recorder]);\n\n // Handle error dismiss\n const handleErrorDismiss = useCallback(() => {\n recorder.reset();\n }, [recorder]);\n\n // Handle error retry\n const handleErrorRetry = useCallback(() => {\n recorder.reset();\n handleStart();\n }, [recorder, handleStart]);\n\n // Determine what to render based on state\n const renderContent = () => {\n // Browser not supported\n if (!recorder.isSupported) {\n return (\n <ErrorMessage\n error={{\n code: \"NOT_SUPPORTED\",\n message:\n \"Screen recording is not supported in your browser. Please use Chrome, Edge, or Firefox on desktop.\",\n }}\n position={position}\n zIndex={zIndex}\n />\n );\n }\n\n // Error state\n if (recorder.error && recorder.state === \"error\") {\n return (\n <ErrorMessage\n error={recorder.error}\n onRetry={handleErrorRetry}\n onDismiss={handleErrorDismiss}\n position={position}\n zIndex={zIndex}\n />\n );\n }\n\n // Countdown state\n if (recorder.isCountingDown && recorder.countdownValue != null) {\n return (\n <Countdown\n value={recorder.countdownValue}\n onCancel={handleCountdownCancel}\n zIndex={zIndex + 1}\n />\n );\n }\n\n // Recording or paused state\n if (recorder.isRecording || recorder.isPaused) {\n return (\n <RecordingControls\n elapsed={recorder.elapsed}\n elapsedFormatted={recorder.elapsedFormatted}\n remaining={recorder.remaining}\n maxDuration={maxDuration}\n isPaused={recorder.isPaused}\n isRecording={recorder.isRecording}\n onPause={handlePause}\n onResume={handleResume}\n onStop={handleStop}\n position={position}\n zIndex={zIndex}\n />\n );\n }\n\n // Stopped state with preview\n if (recorder.state === \"stopped\" && recorder.result && showPreview) {\n return (\n <PreviewModal\n result={recorder.result}\n isOpen={true}\n onDownload={handleDownload}\n onReRecord={handleReRecord}\n onClose={handlePreviewClose}\n zIndex={zIndex + 1}\n usePortal={renderMode === \"portal\"}\n theme={theme}\n />\n );\n }\n\n // Requesting state - show loading trigger\n if (recorder.state === \"requesting\") {\n return (\n <Trigger\n position={position}\n disabled={true}\n zIndex={zIndex}\n className={cn(\"opacity-75\", className)}\n >\n <span className=\"flex items-center gap-2\">\n <svg\n className=\"animate-spin h-4 w-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n <span>Selecting...</span>\n </span>\n </Trigger>\n );\n }\n\n // Idle state - show trigger\n return (\n <Trigger\n position={position}\n onClick={handleStart}\n disabled={disabled}\n zIndex={zIndex}\n className={className}\n >\n {triggerContent}\n </Trigger>\n );\n };\n\n const content = renderContent();\n\n // Wrap in container div for ref\n const containerContent = (\n <div\n ref={ref}\n className={cn(\"usefy-screen-recorder\", theme === \"dark\" && \"dark\")}\n data-state={recorder.state}\n >\n {content}\n </div>\n );\n\n // Use portal for floating UI if specified\n if (\n renderMode === \"portal\" &&\n typeof document !== \"undefined\" &&\n recorder.state !== \"stopped\" // Preview modal handles its own portal\n ) {\n return createPortal(containerContent, document.body);\n }\n\n return containerContent;\n }\n);\n\nScreenRecorder.displayName = \"ScreenRecorder\";\n","import type { QualityPreset, ScreenRecorderErrorCode } from \"./types\";\n\n// ============================================================================\n// Quality Presets\n// ============================================================================\n\n/**\n * Predefined quality presets for recording\n */\nexport const QUALITY_PRESETS: Record<\"low\" | \"medium\" | \"high\", QualityPreset> =\n {\n low: {\n videoBitsPerSecond: 1_000_000, // 1 Mbps\n frameRate: 15,\n },\n medium: {\n videoBitsPerSecond: 2_500_000, // 2.5 Mbps\n frameRate: 30,\n },\n high: {\n videoBitsPerSecond: 5_000_000, // 5 Mbps\n frameRate: 60,\n },\n };\n\n// ============================================================================\n// Default Options\n// ============================================================================\n\n/**\n * Default values for screen recorder options\n */\nexport const DEFAULT_OPTIONS = {\n /** Default maximum recording duration (5 minutes) */\n maxDuration: 300,\n /** Default countdown duration (3 seconds) */\n countdown: 3,\n /** Default audio setting */\n audio: false,\n /** Default quality preset */\n quality: \"medium\" as const,\n /** Default output format */\n format: \"webm\" as const,\n /** Default MIME type for recording */\n mimeType: \"video/webm;codecs=vp9\",\n /** Default filename */\n filename: \"screen-recording\",\n /** Default position */\n position: \"bottom-right\" as const,\n /** Default z-index */\n zIndex: 9999,\n /** Default theme */\n theme: \"system\" as const,\n /** Default render mode */\n renderMode: \"portal\" as const,\n /** Default show preview */\n showPreview: true,\n /** Default show timer */\n showTimer: true,\n /** Default auto download */\n autoDownload: false,\n} as const;\n\n// ============================================================================\n// Supported MIME Types\n// ============================================================================\n\n/**\n * MIME types to check for browser support, in order of preference\n */\nexport const SUPPORTED_MIME_TYPES = [\n \"video/webm;codecs=vp9,opus\",\n \"video/webm;codecs=vp9\",\n \"video/webm;codecs=vp8,opus\",\n \"video/webm;codecs=vp8\",\n \"video/webm\",\n \"video/mp4\",\n] as const;\n\n/**\n * Fallback MIME type if none of the preferred types are supported\n */\nexport const FALLBACK_MIME_TYPE = \"video/webm\";\n\n// ============================================================================\n// Error Messages\n// ============================================================================\n\n/**\n * Human-readable error messages for each error code\n */\nexport const ERROR_MESSAGES: Record<ScreenRecorderErrorCode, string> = {\n PERMISSION_DENIED:\n \"Screen recording permission was denied. Please allow screen sharing when prompted.\",\n NOT_SUPPORTED:\n \"Screen recording is not supported in your browser. Please use Chrome, Edge, or Firefox on desktop.\",\n MEDIA_RECORDER_ERROR:\n \"An error occurred while recording. Please try again.\",\n STREAM_ENDED:\n \"Screen sharing was stopped. The recording has been saved.\",\n NO_STREAM: \"No screen stream is available. Please try starting a new recording.\",\n ENCODING_ERROR: \"Failed to encode the video. Please try again.\",\n UNKNOWN: \"An unknown error occurred. Please try again.\",\n};\n\n// ============================================================================\n// Timer Constants\n// ============================================================================\n\n/**\n * Timer warning threshold (30 seconds remaining)\n */\nexport const TIMER_WARNING_THRESHOLD = 30;\n\n/**\n * Timer critical threshold (10 seconds remaining)\n */\nexport const TIMER_CRITICAL_THRESHOLD = 10;\n\n/**\n * Timer update interval in milliseconds\n */\nexport const TIMER_INTERVAL = 1000;\n\n// ============================================================================\n// Animation Durations (in ms)\n// ============================================================================\n\nexport const ANIMATION_DURATIONS = {\n /** Trigger button appear/disappear */\n triggerAppear: 200,\n /** Controls slide in/out */\n controlsSlide: 300,\n /** Each countdown number display */\n countdownNumber: 1000,\n /** Recording pulse animation cycle */\n recordingPulse: 1500,\n /** Modal open animation */\n modalOpen: 200,\n /** Modal close animation */\n modalClose: 150,\n} as const;\n\n// ============================================================================\n// MediaRecorder Constants\n// ============================================================================\n\n/**\n * Time slice for MediaRecorder (emit data every 1 second)\n * This helps with memory management for long recordings\n */\nexport const MEDIA_RECORDER_TIMESLICE = 1000;\n\n// ============================================================================\n// Storage Keys\n// ============================================================================\n\n/**\n * LocalStorage key prefix for screen recorder settings\n */\nexport const STORAGE_KEY_PREFIX = \"usefy_screen_recorder_\";\n\n/**\n * LocalStorage keys\n */\nexport const STORAGE_KEYS = {\n theme: `${STORAGE_KEY_PREFIX}theme`,\n quality: `${STORAGE_KEY_PREFIX}quality`,\n countdown: `${STORAGE_KEY_PREFIX}countdown`,\n} as const;\n\n// ============================================================================\n// Accessibility\n// ============================================================================\n\n/**\n * ARIA labels for screen recorder elements\n */\nexport const ARIA_LABELS = {\n triggerButton: \"Start screen recording\",\n stopButton: \"Stop recording\",\n pauseButton: \"Pause recording\",\n resumeButton: \"Resume recording\",\n downloadButton: \"Download recording\",\n reRecordButton: \"Discard and record again\",\n closePreview: \"Close preview\",\n timer: \"Recording duration\",\n countdown: \"Recording starts in\",\n previewModal: \"Recording preview\",\n videoPlayer: \"Recorded video preview\",\n} as const;\n\n// ============================================================================\n// CSS Class Names\n// ============================================================================\n\n/**\n * Base CSS class name for the component\n */\nexport const BASE_CLASS = \"usefy-screen-recorder\";\n\n/**\n * CSS class names for component parts\n */\nexport const CSS_CLASSES = {\n root: BASE_CLASS,\n trigger: `${BASE_CLASS}__trigger`,\n controls: `${BASE_CLASS}__controls`,\n timer: `${BASE_CLASS}__timer`,\n countdown: `${BASE_CLASS}__countdown`,\n preview: `${BASE_CLASS}__preview`,\n status: `${BASE_CLASS}__status`,\n error: `${BASE_CLASS}__error`,\n} as const;\n\n// ============================================================================\n// Position Styles\n// ============================================================================\n\n/**\n * Tailwind classes for each trigger position\n */\nexport const POSITION_CLASSES = {\n \"top-left\": \"top-4 left-4\",\n \"top-right\": \"top-4 right-4\",\n \"bottom-left\": \"bottom-4 left-4\",\n \"bottom-right\": \"bottom-4 right-4\",\n} as const;\n","import { useState, useCallback, useRef, useEffect } from \"react\";\nimport type {\n RecordingState,\n RecordingResult,\n ScreenRecorderError,\n UseScreenRecorderOptions,\n UseScreenRecorderReturn,\n} from \"./types\";\nimport { DEFAULT_OPTIONS } from \"./constants\";\nimport { useBrowserSupport } from \"./hooks/useBrowserSupport\";\nimport { useDisplayMedia } from \"./hooks/useDisplayMedia\";\nimport { useMediaRecorder } from \"./hooks/useMediaRecorder\";\nimport { useTimer, formatTime } from \"./hooks/useTimer\";\nimport { useCountdown } from \"./hooks/useCountdown\";\n\n/**\n * Main hook for screen recording functionality\n *\n * Combines display media capture, media recording, timer, and countdown\n * into a unified state machine for screen recording.\n *\n * @param options - Configuration options\n * @returns Screen recorder state and controls\n *\n * @example\n * ```tsx\n * function CustomRecorder() {\n * const {\n * state,\n * isRecording,\n * elapsed,\n * elapsedFormatted,\n * result,\n * start,\n * stop,\n * pause,\n * resume,\n * download,\n * reset,\n * isSupported,\n * error,\n * } = useScreenRecorder({\n * maxDuration: 120,\n * audio: true,\n * countdown: 3,\n * });\n *\n * if (!isSupported) {\n * return <p>Screen recording is not supported.</p>;\n * }\n *\n * return (\n * <div>\n * <p>State: {state}</p>\n * <p>Time: {elapsedFormatted}</p>\n * {state === 'idle' && <button onClick={start}>Start</button>}\n * {isRecording && <button onClick={stop}>Stop</button>}\n * {result && <video src={result.url} controls />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useScreenRecorder(\n options: UseScreenRecorderOptions = {}\n): UseScreenRecorderReturn {\n const {\n maxDuration = DEFAULT_OPTIONS.maxDuration,\n countdown: countdownSeconds = DEFAULT_OPTIONS.countdown,\n audio = DEFAULT_OPTIONS.audio,\n quality = DEFAULT_OPTIONS.quality,\n mimeType = DEFAULT_OPTIONS.mimeType,\n onError,\n } = options;\n\n // State\n const [state, setState] = useState<RecordingState>(\"idle\");\n const [result, setResult] = useState<RecordingResult | null>(null);\n const [error, setError] = useState<ScreenRecorderError | null>(null);\n\n // Blob URL management\n const blobUrlRef = useRef<string | null>(null);\n\n // Recording start time (for duration calculation)\n const recordingStartTimeRef = useRef<number | null>(null);\n\n // Track if we need to start recording immediately (countdown=false)\n const pendingImmediateStartRef = useRef<boolean>(false);\n\n // Browser support\n const browserSupport = useBrowserSupport();\n\n // Display media (screen capture)\n const displayMedia = useDisplayMedia({\n audio,\n onError: (err) => {\n setState(\"error\");\n setError(err);\n onError?.(err);\n },\n onStreamEnded: () => {\n // User stopped sharing via browser UI\n if (state === \"recording\" || state === \"paused\") {\n handleStop();\n }\n },\n });\n\n // Media recorder\n const mediaRecorder = useMediaRecorder(displayMedia.stream, {\n quality,\n mimeType,\n onStart: () => {\n recordingStartTimeRef.current = Date.now();\n },\n onStop: (blob) => {\n // Calculate duration\n const duration = recordingStartTimeRef.current\n ? Math.floor((Date.now() - recordingStartTimeRef.current) / 1000)\n : timer.elapsed;\n\n // Revoke previous blob URL if exists\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n }\n\n // Create new blob URL\n const url = URL.createObjectURL(blob);\n blobUrlRef.current = url;\n\n // Create result\n const recordingResult: RecordingResult = {\n blob,\n url,\n duration,\n size: blob.size,\n mimeType: blob.type || mimeType,\n timestamp: new Date(),\n hasAudio: displayMedia.hasAudio,\n };\n\n setResult(recordingResult);\n setState(\"stopped\");\n timer.stop();\n },\n onError: (err) => {\n setState(\"error\");\n setError(err);\n onError?.(err);\n },\n });\n\n // Timer\n const timer = useTimer({\n maxDuration,\n onTick: (elapsed, remaining) => {\n // Auto-stop when max duration reached\n if (remaining !== null && remaining <= 0) {\n handleStop();\n }\n },\n });\n\n // Countdown\n const countdown = useCountdown({\n initialValue: typeof countdownSeconds === \"number\" ? countdownSeconds : 3,\n onComplete: () => {\n // Start actual recording after countdown\n mediaRecorder.start();\n timer.start();\n setState(\"recording\");\n },\n });\n\n // Handle stop (internal)\n const handleStop = useCallback(() => {\n mediaRecorder.stop();\n displayMedia.stopStream();\n timer.stop();\n }, [mediaRecorder, displayMedia, timer]);\n\n // Effect to start recording when MediaRecorder becomes ready (for countdown=false case)\n useEffect(() => {\n if (\n pendingImmediateStartRef.current &&\n mediaRecorder.recorder &&\n displayMedia.stream\n ) {\n pendingImmediateStartRef.current = false;\n mediaRecorder.start();\n timer.start();\n setState(\"recording\");\n }\n }, [mediaRecorder.recorder, mediaRecorder, displayMedia.stream, timer]);\n\n // Start recording\n const start = useCallback(async () => {\n // Clear previous error\n setError(null);\n setResult(null);\n\n // Clear previous blob URL\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n blobUrlRef.current = null;\n }\n\n setState(\"requesting\");\n\n // Request screen capture\n const stream = await displayMedia.requestStream();\n\n if (!stream) {\n // Error already set by displayMedia hook\n setState(\"error\");\n return;\n }\n\n // Start countdown or recording immediately\n if (countdownSeconds !== false && countdownSeconds > 0) {\n setState(\"countdown\");\n countdown.start();\n } else {\n // No countdown - mark as pending and wait for MediaRecorder to be ready\n // MediaRecorder is created in useEffect when stream changes, so it may not be ready yet\n pendingImmediateStartRef.current = true;\n // Trigger a state that indicates we're waiting\n setState(\"countdown\"); // Temporarily use countdown state, will transition to recording\n }\n }, [displayMedia, countdownSeconds, countdown]);\n\n // Stop recording\n const stop = useCallback(() => {\n // Cancel pending start if any\n pendingImmediateStartRef.current = false;\n\n if (state === \"countdown\") {\n // Cancel countdown and stop\n countdown.cancel();\n displayMedia.stopStream();\n setState(\"idle\");\n return;\n }\n\n if (state === \"recording\" || state === \"paused\") {\n handleStop();\n }\n }, [state, countdown, displayMedia, handleStop]);\n\n // Pause recording\n const pause = useCallback(() => {\n if (state !== \"recording\") return;\n\n mediaRecorder.pause();\n timer.pause();\n setState(\"paused\");\n }, [state, mediaRecorder, timer]);\n\n // Resume recording\n const resume = useCallback(() => {\n if (state !== \"paused\") return;\n\n mediaRecorder.resume();\n timer.resume();\n setState(\"recording\");\n }, [state, mediaRecorder, timer]);\n\n // Toggle pause/resume\n const togglePause = useCallback(() => {\n if (state === \"recording\") {\n pause();\n } else if (state === \"paused\") {\n resume();\n }\n }, [state, pause, resume]);\n\n // Download recording\n const download = useCallback(\n (filename?: string) => {\n if (!result) return;\n\n const downloadFilename =\n filename || `screen-recording-${Date.now()}.webm`;\n\n const link = document.createElement(\"a\");\n link.href = result.url;\n link.download = downloadFilename.endsWith(\".webm\")\n ? downloadFilename\n : `${downloadFilename}.webm`;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n },\n [result]\n );\n\n // Reset state for new recording\n const reset = useCallback(() => {\n // Cancel pending start if any\n pendingImmediateStartRef.current = false;\n\n // Stop everything\n if (state === \"recording\" || state === \"paused\") {\n mediaRecorder.stop();\n }\n displayMedia.stopStream();\n timer.reset();\n countdown.cancel();\n\n // Clear blob URL\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n blobUrlRef.current = null;\n }\n\n // Reset state\n setState(\"idle\");\n setResult(null);\n setError(null);\n }, [state, mediaRecorder, displayMedia, timer, countdown]);\n\n // Get preview URL\n const getPreviewUrl = useCallback(() => {\n return blobUrlRef.current;\n }, []);\n\n // Revoke preview URL (cleanup)\n const revokePreviewUrl = useCallback(() => {\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n blobUrlRef.current = null;\n }\n }, []);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n }\n };\n }, []);\n\n // Computed values\n const isRecording = state === \"recording\";\n const isPaused = state === \"paused\";\n // For countdown=false, we briefly show countdown state while waiting for MediaRecorder\n // But countdownValue should be null in this case\n const isCountingDown =\n state === \"countdown\" && !pendingImmediateStartRef.current;\n const remaining =\n maxDuration != null && isFinite(maxDuration)\n ? Math.max(0, maxDuration - timer.elapsed)\n : null;\n\n return {\n // State\n state,\n isRecording,\n isPaused,\n isCountingDown,\n countdownValue: pendingImmediateStartRef.current ? null : countdown.value,\n elapsed: timer.elapsed,\n remaining,\n elapsedFormatted: timer.elapsedFormatted,\n result,\n error: error || displayMedia.error || mediaRecorder.error,\n isSupported: browserSupport.isSupported,\n\n // Actions\n start,\n stop,\n pause,\n resume,\n togglePause,\n download,\n reset,\n getPreviewUrl,\n revokePreviewUrl,\n };\n}\n","import { useState, useEffect, useCallback } from \"react\";\nimport type { BrowserSupport } from \"../types\";\nimport { SUPPORTED_MIME_TYPES } from \"../constants\";\n\n/**\n * Checks if the getDisplayMedia API is available\n */\nfunction checkDisplayMediaSupport(): boolean {\n if (typeof window === \"undefined\") return false;\n if (typeof navigator === \"undefined\") return false;\n if (!navigator.mediaDevices) return false;\n return typeof navigator.mediaDevices.getDisplayMedia === \"function\";\n}\n\n/**\n * Checks if MediaRecorder is available\n */\nfunction checkMediaRecorderSupport(): boolean {\n if (typeof window === \"undefined\") return false;\n return typeof MediaRecorder !== \"undefined\";\n}\n\n/**\n * Gets list of supported MIME types\n */\nfunction getSupportedMimeTypes(): string[] {\n if (!checkMediaRecorderSupport()) return [];\n\n return SUPPORTED_MIME_TYPES.filter((mimeType) => {\n try {\n return MediaRecorder.isTypeSupported(mimeType);\n } catch {\n return false;\n }\n });\n}\n\n/**\n * Gets the best supported MIME type\n */\nexport function getBestMimeType(): string | null {\n const supported = getSupportedMimeTypes();\n return supported.length > 0 ? supported[0] : null;\n}\n\n/**\n * Hook to detect browser support for screen recording\n *\n * @returns Browser support information\n *\n * @example\n * ```tsx\n * function RecorderComponent() {\n * const { isSupported, hasDisplayMedia, supportedMimeTypes } = useBrowserSupport();\n *\n * if (!isSupported) {\n * return <p>Screen recording is not supported</p>;\n * }\n *\n * return <button>Start Recording</button>;\n * }\n * ```\n */\nexport function useBrowserSupport(): BrowserSupport {\n const [support, setSupport] = useState<BrowserSupport>(() => ({\n isSupported: false,\n hasDisplayMedia: false,\n hasMediaRecorder: false,\n supportedMimeTypes: [],\n }));\n\n useEffect(() => {\n const hasDisplayMedia = checkDisplayMediaSupport();\n const hasMediaRecorder = checkMediaRecorderSupport();\n const supportedMimeTypes = getSupportedMimeTypes();\n\n setSupport({\n isSupported: hasDisplayMedia && hasMediaRecorder && supportedMimeTypes.length > 0,\n hasDisplayMedia,\n hasMediaRecorder,\n supportedMimeTypes,\n });\n }, []);\n\n return support;\n}\n\n/**\n * Checks browser support without React hooks (for one-time checks)\n */\nexport function checkBrowserSupport(): BrowserSupport {\n const hasDisplayMedia = checkDisplayMediaSupport();\n const hasMediaRecorder = checkMediaRecorderSupport();\n const supportedMimeTypes = getSupportedMimeTypes();\n\n return {\n isSupported: hasDisplayMedia && hasMediaRecorder && supportedMimeTypes.length > 0,\n hasDisplayMedia,\n hasMediaRecorder,\n supportedMimeTypes,\n };\n}\n","import { useState, useCallback, useRef, useEffect } from \"react\";\nimport type { ScreenRecorderError, AudioOption, AudioConfig } from \"../types\";\n\nexport interface UseDisplayMediaOptions {\n /**\n * Audio configuration\n */\n audio?: AudioOption;\n /**\n * Called when an error occurs\n */\n onError?: (error: ScreenRecorderError) => void;\n /**\n * Called when the stream ends (user stops sharing via browser)\n */\n onStreamEnded?: () => void;\n}\n\nexport interface UseDisplayMediaReturn {\n /** Current media stream (null if not capturing) */\n stream: MediaStream | null;\n /** Whether stream is active */\n isStreaming: boolean;\n /** Request screen capture from user */\n requestStream: () => Promise<MediaStream | null>;\n /** Stop the current stream */\n stopStream: () => void;\n /** Current error if any */\n error: ScreenRecorderError | null;\n /** Whether audio is being captured */\n hasAudio: boolean;\n}\n\n/**\n * Convert audio option to DisplayMediaStreamOptions\n */\nfunction getAudioConstraints(audio?: AudioOption): boolean | MediaTrackConstraints {\n if (audio === false || audio === undefined) {\n return false;\n }\n\n if (audio === true) {\n return true;\n }\n\n // AudioConfig\n const config = audio as AudioConfig;\n if (config.system || config.microphone) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Create error object from DOMException or Error\n */\nfunction createError(error: unknown): ScreenRecorderError {\n if (error instanceof DOMException) {\n if (error.name === \"NotAllowedError\" || error.name === \"PermissionDeniedError\") {\n return {\n code: \"PERMISSION_DENIED\",\n message: \"Screen recording permission was denied. Please allow screen sharing when prompted.\",\n originalError: error,\n };\n }\n if (error.name === \"NotSupportedError\") {\n return {\n code: \"NOT_SUPPORTED\",\n message: \"Screen recording is not supported in your browser.\",\n originalError: error,\n };\n }\n if (error.name === \"AbortError\") {\n return {\n code: \"PERMISSION_DENIED\",\n message: \"Screen selection was cancelled.\",\n originalError: error,\n };\n }\n }\n\n const originalError = error instanceof Error ? error : new Error(String(error));\n return {\n code: \"UNKNOWN\",\n message: originalError.message || \"An unknown error occurred.\",\n originalError,\n };\n}\n\n/**\n * Hook to manage screen capture using getDisplayMedia API\n *\n * @param options - Configuration options\n * @returns Display media state and controls\n *\n * @example\n * ```tsx\n * function ScreenCapture() {\n * const { stream, requestStream, stopStream, error } = useDisplayMedia({\n * audio: true,\n * onStreamEnded: () => console.log('User stopped sharing'),\n * });\n *\n * return (\n * <button onClick={stream ? stopStream : requestStream}>\n * {stream ? 'Stop' : 'Start'} Sharing\n * </button>\n * );\n * }\n * ```\n */\nexport function useDisplayMedia(\n options: UseDisplayMediaOptions = {}\n): UseDisplayMediaReturn {\n const { audio, onError, onStreamEnded } = options;\n\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [error, setError] = useState<ScreenRecorderError | null>(null);\n const [hasAudio, setHasAudio] = useState(false);\n\n // Use ref to track stream for cleanup\n const streamRef = useRef<MediaStream | null>(null);\n\n // Handle track ended event\n const handleTrackEnded = useCallback(() => {\n onStreamEnded?.();\n setStream(null);\n streamRef.current = null;\n }, [onStreamEnded]);\n\n // Stop current stream\n const stopStream = useCallback(() => {\n if (streamRef.current) {\n streamRef.current.getTracks().forEach((track) => {\n track.removeEventListener(\"ended\", handleTrackEnded);\n track.stop();\n });\n streamRef.current = null;\n }\n setStream(null);\n setHasAudio(false);\n }, [handleTrackEnded]);\n\n // Request screen capture\n const requestStream = useCallback(async (): Promise<MediaStream | null> => {\n // Check API availability\n if (\n typeof navigator === \"undefined\" ||\n !navigator.mediaDevices ||\n !navigator.mediaDevices.getDisplayMedia\n ) {\n const error: ScreenRecorderError = {\n code: \"NOT_SUPPORTED\",\n message: \"Screen recording is not supported in your browser.\",\n };\n setError(error);\n onError?.(error);\n return null;\n }\n\n // Stop any existing stream\n stopStream();\n setError(null);\n\n try {\n const displayMediaOptions: DisplayMediaStreamOptions = {\n video: true,\n audio: getAudioConstraints(audio),\n };\n\n const mediaStream = await navigator.mediaDevices.getDisplayMedia(\n displayMediaOptions\n );\n\n // Add ended event listener to video track\n const videoTrack = mediaStream.getVideoTracks()[0];\n if (videoTrack) {\n videoTrack.addEventListener(\"ended\", handleTrackEnded);\n }\n\n // Check if audio is captured\n const audioTracks = mediaStream.getAudioTracks();\n setHasAudio(audioTracks.length > 0);\n\n streamRef.current = mediaStream;\n setStream(mediaStream);\n return mediaStream;\n } catch (err) {\n const screenRecorderError = createError(err);\n setError(screenRecorderError);\n onError?.(screenRecorderError);\n return null;\n }\n }, [audio, stopStream, handleTrackEnded, onError]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (streamRef.current) {\n streamRef.current.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n }\n };\n }, []);\n\n return {\n stream,\n isStreaming: stream !== null,\n requestStream,\n stopStream,\n error,\n hasAudio,\n };\n}\n","import { useState, useCallback, useRef, useEffect } from \"react\";\nimport type { ScreenRecorderError, QualityOption, QualityPreset } from \"../types\";\nimport {\n QUALITY_PRESETS,\n DEFAULT_OPTIONS,\n MEDIA_RECORDER_TIMESLICE,\n} from \"../constants\";\nimport { getBestMimeType } from \"./useBrowserSupport\";\n\nexport interface UseMediaRecorderOptions {\n /**\n * Quality preset or custom configuration\n */\n quality?: QualityOption;\n /**\n * MIME type for recording\n */\n mimeType?: string;\n /**\n * Time slice for data events (ms)\n * @default 1000\n */\n timeslice?: number;\n /**\n * Called when recording data is available\n */\n onDataAvailable?: (data: Blob) => void;\n /**\n * Called when recording starts\n */\n onStart?: () => void;\n /**\n * Called when recording stops\n */\n onStop?: (blob: Blob) => void;\n /**\n * Called when an error occurs\n */\n onError?: (error: ScreenRecorderError) => void;\n}\n\nexport interface UseMediaRecorderReturn {\n /** MediaRecorder instance */\n recorder: MediaRecorder | null;\n /** Current recording state */\n state: RecordingState;\n /** Start recording */\n start: () => void;\n /** Stop recording */\n stop: () => void;\n /** Pause recording */\n pause: () => void;\n /** Resume recording */\n resume: () => void;\n /** Recorded blob (available after stop) */\n blob: Blob | null;\n /** MIME type being used */\n mimeType: string;\n /** Current error if any */\n error: ScreenRecorderError | null;\n}\n\n/**\n * Get quality configuration from option\n */\nfunction getQualityConfig(quality?: QualityOption): QualityPreset {\n if (!quality) {\n return QUALITY_PRESETS[DEFAULT_OPTIONS.quality];\n }\n\n if (typeof quality === \"string\") {\n return QUALITY_PRESETS[quality] || QUALITY_PRESETS.medium;\n }\n\n return quality;\n}\n\n/**\n * Hook to manage MediaRecorder for video recording\n *\n * @param stream - MediaStream to record\n * @param options - Configuration options\n * @returns MediaRecorder state and controls\n *\n * @example\n * ```tsx\n * function Recorder({ stream }) {\n * const { state, start, stop, pause, resume, blob } = useMediaRecorder(stream, {\n * quality: 'high',\n * onStop: (blob) => console.log('Recording complete:', blob),\n * });\n *\n * return (\n * <div>\n * <p>State: {state}</p>\n * <button onClick={start}>Start</button>\n * <button onClick={stop}>Stop</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useMediaRecorder(\n stream: MediaStream | null,\n options: UseMediaRecorderOptions = {}\n): UseMediaRecorderReturn {\n const {\n quality,\n mimeType: preferredMimeType,\n timeslice = MEDIA_RECORDER_TIMESLICE,\n onDataAvailable,\n onStart,\n onStop,\n onError,\n } = options;\n\n const [recorder, setRecorder] = useState<MediaRecorder | null>(null);\n const [state, setState] = useState<RecordingState>(\"inactive\");\n const [blob, setBlob] = useState<Blob | null>(null);\n const [error, setError] = useState<ScreenRecorderError | null>(null);\n\n // Track collected chunks\n const chunksRef = useRef<Blob[]>([]);\n\n // Determine MIME type\n const actualMimeType = preferredMimeType || getBestMimeType() || DEFAULT_OPTIONS.mimeType;\n\n // Create recorder when stream changes\n useEffect(() => {\n if (!stream) {\n setRecorder(null);\n setState(\"inactive\");\n return;\n }\n\n // Check MediaRecorder availability\n if (typeof MediaRecorder === \"undefined\") {\n const err: ScreenRecorderError = {\n code: \"NOT_SUPPORTED\",\n message: \"MediaRecorder is not supported in your browser.\",\n };\n setError(err);\n onError?.(err);\n return;\n }\n\n // Check MIME type support\n let mimeToUse = actualMimeType;\n if (!MediaRecorder.isTypeSupported(mimeToUse)) {\n const bestMime = getBestMimeType();\n if (bestMime) {\n mimeToUse = bestMime;\n } else {\n const err: ScreenRecorderError = {\n code: \"ENCODING_ERROR\",\n message: `The MIME type ${actualMimeType} is not supported.`,\n };\n setError(err);\n onError?.(err);\n return;\n }\n }\n\n const qualityConfig = getQualityConfig(quality);\n\n try {\n const mediaRecorder = new MediaRecorder(stream, {\n mimeType: mimeToUse,\n videoBitsPerSecond: qualityConfig.videoBitsPerSecond,\n });\n\n // Handle data available\n mediaRecorder.ondataavailable = (event) => {\n if (event.data && event.data.size > 0) {\n chunksRef.current.push(event.data);\n onDataAvailable?.(event.data);\n }\n };\n\n // Handle start\n mediaRecorder.onstart = () => {\n setState(\"recording\");\n chunksRef.current = [];\n setBlob(null);\n setError(null);\n onStart?.();\n };\n\n // Handle stop\n mediaRecorder.onstop = () => {\n setState(\"inactive\");\n const recordedBlob = new Blob(chunksRef.current, { type: mimeToUse });\n setBlob(recordedBlob);\n onStop?.(recordedBlob);\n };\n\n // Handle pause\n mediaRecorder.onpause = () => {\n setState(\"paused\");\n };\n\n // Handle resume\n mediaRecorder.onresume = () => {\n setState(\"recording\");\n };\n\n // Handle error\n mediaRecorder.onerror = (event) => {\n const err: ScreenRecorderError = {\n code: \"MEDIA_RECORDER_ERROR\",\n message: \"An error occurred during recording.\",\n originalError:\n event instanceof ErrorEvent ? event.error : new Error(\"MediaRecorder error\"),\n };\n setError(err);\n setState(\"inactive\");\n onError?.(err);\n };\n\n setRecorder(mediaRecorder);\n setError(null);\n } catch (err) {\n const screenRecorderError: ScreenRecorderError = {\n code: \"MEDIA_RECORDER_ERROR\",\n message: err instanceof Error ? err.message : \"Failed to create MediaRecorder.\",\n originalError: err instanceof Error ? err : undefined,\n };\n setError(screenRecorderError);\n onError?.(screenRecorderError);\n }\n\n return () => {\n // Cleanup: stop recorder if active\n if (recorder && recorder.state !== \"inactive\") {\n recorder.stop();\n }\n };\n }, [stream, actualMimeType, quality]);\n\n // Start recording\n const start = useCallback(() => {\n if (!recorder) {\n const err: ScreenRecorderError = {\n code: \"NO_STREAM\",\n message: \"No stream available. Please start screen capture first.\",\n };\n setError(err);\n onError?.(err);\n return;\n }\n\n if (recorder.state !== \"inactive\") {\n return; // Already recording\n }\n\n chunksRef.current = [];\n recorder.start(timeslice);\n }, [recorder, timeslice, onError]);\n\n // Stop recording\n const stop = useCallback(() => {\n if (!recorder || recorder.state === \"inactive\") {\n return;\n }\n recorder.stop();\n }, [recorder]);\n\n // Pause recording\n const pause = useCallback(() => {\n if (!recorder || recorder.state !== \"recording\") {\n return;\n }\n recorder.pause();\n }, [recorder]);\n\n // Resume recording\n const resume = useCallback(() => {\n if (!recorder || recorder.state !== \"paused\") {\n return;\n }\n recorder.resume();\n }, [recorder]);\n\n return {\n recorder,\n state,\n start,\n stop,\n pause,\n resume,\n blob,\n mimeType: actualMimeType,\n error,\n };\n}\n","import { useState, useCallback, useRef, useEffect } from \"react\";\nimport { TIMER_INTERVAL } from \"../constants\";\n\nexport interface UseTimerOptions {\n /**\n * Maximum duration in seconds (timer will auto-stop at this limit)\n */\n maxDuration?: number;\n /**\n * Callback fired every second with elapsed time\n */\n onTick?: (elapsed: number, remaining: number | null) => void;\n /**\n * Callback fired when max duration is reached\n */\n onComplete?: () => void;\n}\n\nexport interface UseTimerReturn {\n /** Elapsed time in seconds */\n elapsed: number;\n /** Formatted elapsed time (MM:SS) */\n elapsedFormatted: string;\n /** Remaining time if maxDuration set (null if no limit) */\n remaining: number | null;\n /** Whether timer is currently running */\n isRunning: boolean;\n /** Start the timer */\n start: () => void;\n /** Stop the timer */\n stop: () => void;\n /** Pause the timer (keeps elapsed time) */\n pause: () => void;\n /** Resume a paused timer */\n resume: () => void;\n /** Reset the timer to zero */\n reset: () => void;\n}\n\n/**\n * Format seconds as MM:SS\n */\nexport function formatTime(seconds: number): string {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins.toString().padStart(2, \"0\")}:${secs.toString().padStart(2, \"0\")}`;\n}\n\n/**\n * Hook to manage a timer for recording duration\n *\n * @param options - Configuration options\n * @returns Timer state and controls\n *\n * @example\n * ```tsx\n * function RecordingTimer() {\n * const { elapsed, elapsedFormatted, remaining, start, stop, reset, isRunning } = useTimer({\n * maxDuration: 60,\n * onTick: (elapsed) => console.log(`Recording: ${elapsed}s`),\n * onComplete: () => console.log('Max duration reached'),\n * });\n *\n * return (\n * <div>\n * <p>{elapsedFormatted}</p>\n * <button onClick={isRunning ? stop : start}>\n * {isRunning ? 'Stop' : 'Start'}\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useTimer(options: UseTimerOptions = {}): UseTimerReturn {\n const { maxDuration, onTick, onComplete } = options;\n\n const [elapsed, setElapsed] = useState(0);\n const [isRunning, setIsRunning] = useState(false);\n const [isPaused, setIsPaused] = useState(false);\n\n // Use refs to track actual elapsed time (more accurate than state updates)\n const startTimeRef = useRef<number | null>(null);\n const pausedAtRef = useRef<number>(0);\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n // Calculate remaining time (null if unlimited)\n const remaining =\n maxDuration != null && isFinite(maxDuration)\n ? Math.max(0, maxDuration - elapsed)\n : null;\n\n // Format elapsed time\n const elapsedFormatted = formatTime(elapsed);\n\n // Clear interval helper\n const clearTimerInterval = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n }, []);\n\n // Start the timer\n const start = useCallback(() => {\n if (isRunning && !isPaused) return;\n\n const now = Date.now();\n\n if (isPaused) {\n // Resume from paused state\n const pauseDuration = now - pausedAtRef.current;\n if (startTimeRef.current) {\n startTimeRef.current += pauseDuration;\n }\n setIsPaused(false);\n } else {\n // Fresh start\n startTimeRef.current = now;\n setElapsed(0);\n }\n\n setIsRunning(true);\n\n // Start interval\n clearTimerInterval();\n intervalRef.current = setInterval(() => {\n if (startTimeRef.current === null) return;\n\n const currentElapsed = Math.floor((Date.now() - startTimeRef.current) / 1000);\n setElapsed(currentElapsed);\n\n const currentRemaining =\n maxDuration != null && isFinite(maxDuration)\n ? maxDuration - currentElapsed\n : null;\n onTick?.(currentElapsed, currentRemaining);\n\n // Check if max duration reached (skip for Infinity)\n if (maxDuration != null && isFinite(maxDuration) && currentElapsed >= maxDuration) {\n clearTimerInterval();\n setIsRunning(false);\n onComplete?.();\n }\n }, TIMER_INTERVAL);\n }, [isRunning, isPaused, maxDuration, clearTimerInterval, onTick, onComplete]);\n\n // Stop the timer completely\n const stop = useCallback(() => {\n clearTimerInterval();\n setIsRunning(false);\n setIsPaused(false);\n }, [clearTimerInterval]);\n\n // Pause the timer (keeps elapsed time)\n const pause = useCallback(() => {\n if (!isRunning || isPaused) return;\n\n clearTimerInterval();\n pausedAtRef.current = Date.now();\n setIsPaused(true);\n }, [isRunning, isPaused, clearTimerInterval]);\n\n // Resume a paused timer\n const resume = useCallback(() => {\n if (!isPaused) return;\n start();\n }, [isPaused, start]);\n\n // Reset the timer to zero\n const reset = useCallback(() => {\n clearTimerInterval();\n setIsRunning(false);\n setIsPaused(false);\n setElapsed(0);\n startTimeRef.current = null;\n pausedAtRef.current = 0;\n }, [clearTimerInterval]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n clearTimerInterval();\n };\n }, [clearTimerInterval]);\n\n return {\n elapsed,\n elapsedFormatted,\n remaining,\n isRunning,\n start,\n stop,\n pause,\n resume,\n reset,\n };\n}\n","import { useState, useCallback, useRef, useEffect } from \"react\";\n\nexport interface UseCountdownOptions {\n /**\n * Initial countdown value in seconds\n * @default 3\n */\n initialValue?: number;\n /**\n * Called when countdown completes\n */\n onComplete?: () => void;\n /**\n * Called on each tick of the countdown\n */\n onTick?: (value: number) => void;\n}\n\nexport interface UseCountdownReturn {\n /** Current countdown value (null when not counting) */\n value: number | null;\n /** Whether countdown is active */\n isActive: boolean;\n /** Start the countdown */\n start: (onComplete?: () => void) => void;\n /** Cancel the countdown */\n cancel: () => void;\n}\n\n/**\n * Hook to manage a countdown timer (e.g., 3-2-1 before recording)\n *\n * @param options - Configuration options\n * @returns Countdown state and controls\n *\n * @example\n * ```tsx\n * function RecordingCountdown() {\n * const { value, isActive, start, cancel } = useCountdown({\n * initialValue: 3,\n * onComplete: () => console.log('Countdown finished!'),\n * });\n *\n * return (\n * <div>\n * {isActive && <div className=\"countdown\">{value}</div>}\n * <button onClick={() => start()}>Start Countdown</button>\n * <button onClick={cancel}>Cancel</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useCountdown(options: UseCountdownOptions = {}): UseCountdownReturn {\n const { initialValue = 3, onComplete, onTick } = options;\n\n const [value, setValue] = useState<number | null>(null);\n const [isActive, setIsActive] = useState(false);\n\n // Store callback in ref to avoid stale closures\n const onCompleteRef = useRef<(() => void) | undefined>(onComplete);\n const runtimeOnCompleteRef = useRef<(() => void) | undefined>(undefined);\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n // Update ref when onComplete changes\n useEffect(() => {\n onCompleteRef.current = onComplete;\n }, [onComplete]);\n\n // Clear interval helper\n const clearCountdownInterval = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n }, []);\n\n // Start the countdown\n const start = useCallback(\n (runtimeOnComplete?: () => void) => {\n // Store runtime callback\n runtimeOnCompleteRef.current = runtimeOnComplete;\n\n // Reset and start\n clearCountdownInterval();\n setValue(initialValue);\n setIsActive(true);\n\n // Call initial tick\n onTick?.(initialValue);\n\n // Start countdown interval\n let currentValue = initialValue;\n\n intervalRef.current = setInterval(() => {\n currentValue -= 1;\n\n if (currentValue <= 0) {\n // Countdown complete\n clearCountdownInterval();\n setValue(null);\n setIsActive(false);\n\n // Call completion callbacks\n runtimeOnCompleteRef.current?.();\n onCompleteRef.current?.();\n } else {\n // Update value\n setValue(currentValue);\n onTick?.(currentValue);\n }\n }, 1000);\n },\n [initialValue, clearCountdownInterval, onTick]\n );\n\n // Cancel the countdown\n const cancel = useCallback(() => {\n clearCountdownInterval();\n setValue(null);\n setIsActive(false);\n runtimeOnCompleteRef.current = undefined;\n }, [clearCountdownInterval]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n clearCountdownInterval();\n };\n }, [clearCountdownInterval]);\n\n return {\n value,\n isActive,\n start,\n cancel,\n };\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes with clsx\n *\n * Combines clsx's conditional class merging with tailwind-merge's\n * intelligent Tailwind class deduplication.\n *\n * @param inputs - Class values to merge\n * @returns Merged class string\n *\n * @example\n * ```ts\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4'\n * cn('bg-red-500', isActive && 'bg-blue-500') // => 'bg-blue-500' (if isActive)\n * ```\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import type { FC } from \"react\";\n\ninterface TriggerIconProps {\n className?: string;\n}\n\n/**\n * Recording trigger icon (video camera with record dot)\n */\nexport const TriggerIcon: FC<TriggerIconProps> = ({ className }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n {/* Video camera body */}\n <rect x=\"2\" y=\"6\" width=\"14\" height=\"12\" rx=\"2\" ry=\"2\" />\n {/* Camera lens/arrow */}\n <polygon points=\"22,8 16,12 22,16\" fill=\"currentColor\" stroke=\"none\" />\n {/* Record dot */}\n <circle cx=\"9\" cy=\"12\" r=\"2\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n );\n};\n\n/**\n * Recording state icon (pulsing red dot)\n */\nexport const RecordingIcon: FC<TriggerIconProps> = ({ className }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className={className}\n aria-hidden=\"true\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"8\" />\n </svg>\n );\n};\n\n/**\n * Stop icon (square)\n */\nexport const StopIcon: FC<TriggerIconProps> = ({ className }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className={className}\n aria-hidden=\"true\"\n >\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\" />\n </svg>\n );\n};\n\n/**\n * Pause icon (two vertical bars)\n */\nexport const PauseIcon: FC<TriggerIconProps> = ({ className }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className={className}\n aria-hidden=\"true\"\n >\n <rect x=\"6\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" />\n <rect x=\"14\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" />\n </svg>\n );\n};\n\n/**\n * Play/Resume icon (triangle)\n */\nexport const PlayIcon: FC<TriggerIconProps> = ({ className }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className={className}\n aria-hidden=\"true\"\n >\n <polygon points=\"8,5 19,12 8,19\" />\n </svg>\n );\n};\n\n/**\n * Download icon\n */\nexport const DownloadIcon: FC<TriggerIconProps> = ({ className }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <path d=\"M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4\" />\n <polyline points=\"7 10 12 15 17 10\" />\n <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\" />\n </svg>\n );\n};\n\n/**\n * Close/X icon\n */\nexport const CloseIcon: FC<TriggerIconProps> = ({ className }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n};\n\n/**\n * Refresh/Re-record icon\n */\nexport const RefreshIcon: FC<TriggerIconProps> = ({ className }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <polyline points=\"23 4 23 10 17 10\" />\n <path d=\"M20.49 15a9 9 0 11-2.12-9.36L23 10\" />\n </svg>\n );\n};\n\n/**\n * Check/Done icon\n */\nexport const CheckIcon: FC<TriggerIconProps> = ({ className }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n );\n};\n\n/**\n * Warning/Error icon\n */\nexport const WarningIcon: FC<TriggerIconProps> = ({ className }) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <path d=\"M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z\" />\n <line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\" />\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\" />\n </svg>\n );\n};\n","import type { FC, ReactNode } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { TriggerIcon } from \"./TriggerIcon\";\nimport type { TriggerPosition } from \"../../types\";\nimport { POSITION_CLASSES, ARIA_LABELS } from \"../../constants\";\n\nexport interface TriggerProps {\n /**\n * Position of the trigger button\n */\n position?: TriggerPosition;\n /**\n * Custom content for the trigger button\n */\n children?: ReactNode;\n /**\n * Click handler\n */\n onClick?: () => void;\n /**\n * Whether the trigger is disabled\n */\n disabled?: boolean;\n /**\n * Custom class name\n */\n className?: string;\n /**\n * Z-index for positioning\n */\n zIndex?: number;\n}\n\n/**\n * Floating trigger button to start screen recording\n */\nexport const Trigger: FC<TriggerProps> = ({\n position = \"bottom-right\",\n children,\n onClick,\n disabled = false,\n className,\n zIndex = 9999,\n}) => {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n aria-label={ARIA_LABELS.triggerButton}\n className={cn(\n // Base styles\n \"fixed flex items-center gap-2 px-4 py-2.5 rounded-full\",\n \"font-medium text-sm shadow-lg\",\n // Colors\n \"bg-red-600 text-white\",\n \"hover:bg-red-700 active:bg-red-800\",\n // Focus styles\n \"focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2\",\n // Disabled styles\n \"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-red-600\",\n // Transitions\n \"transition-all duration-200 ease-out\",\n \"hover:scale-105 active:scale-95\",\n // Position\n POSITION_CLASSES[position],\n className\n )}\n style={{ zIndex }}\n >\n {children ?? (\n <>\n <TriggerIcon className=\"w-5 h-5\" />\n <span>Record</span>\n </>\n )}\n </button>\n );\n};\n","import type { FC } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { TIMER_WARNING_THRESHOLD, TIMER_CRITICAL_THRESHOLD, ARIA_LABELS } from \"../../constants\";\n\nexport interface TimerProps {\n /**\n * Elapsed time in seconds\n */\n elapsed: number;\n /**\n * Formatted elapsed time (MM:SS)\n */\n elapsedFormatted: string;\n /**\n * Remaining time (null if no limit)\n */\n remaining?: number | null;\n /**\n * Maximum duration (for calculating remaining)\n */\n maxDuration?: number;\n /**\n * Whether recording is paused\n */\n isPaused?: boolean;\n /**\n * Custom class name\n */\n className?: string;\n}\n\n/**\n * Timer display component showing elapsed recording time\n */\nexport const Timer: FC<TimerProps> = ({\n elapsed,\n elapsedFormatted,\n remaining,\n maxDuration,\n isPaused = false,\n className,\n}) => {\n // Determine timer color based on remaining time\n const getTimerColor = () => {\n if (isPaused) {\n return \"text-amber-600 dark:text-amber-400\";\n }\n\n if (remaining != null) {\n if (remaining <= TIMER_CRITICAL_THRESHOLD) {\n return \"text-red-600 dark:text-red-400\";\n }\n if (remaining <= TIMER_WARNING_THRESHOLD) {\n return \"text-amber-600 dark:text-amber-400\";\n }\n }\n\n return \"text-gray-700 dark:text-gray-300\";\n };\n\n return (\n <div\n role=\"timer\"\n aria-live=\"polite\"\n aria-label={`${ARIA_LABELS.timer}: ${elapsedFormatted}`}\n className={cn(\n \"font-mono text-sm font-medium tabular-nums\",\n getTimerColor(),\n className\n )}\n >\n {elapsedFormatted}\n {maxDuration != null && isFinite(maxDuration) && (\n <span className=\"text-gray-400 dark:text-gray-500\">\n {\" / \"}\n {formatDuration(maxDuration)}\n </span>\n )}\n </div>\n );\n};\n\n/**\n * Format duration in seconds to MM:SS\n */\nfunction formatDuration(seconds: number): string {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins.toString().padStart(2, \"0\")}:${secs.toString().padStart(2, \"0\")}`;\n}\n","import type { FC } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Timer } from \"./Timer\";\nimport { RecordingIcon, StopIcon, PauseIcon, PlayIcon } from \"../Trigger/TriggerIcon\";\nimport { ARIA_LABELS } from \"../../constants\";\nimport type { TriggerPosition } from \"../../types\";\nimport { POSITION_CLASSES } from \"../../constants\";\n\nexport interface RecordingControlsProps {\n /**\n * Elapsed time in seconds\n */\n elapsed: number;\n /**\n * Formatted elapsed time (MM:SS)\n */\n elapsedFormatted: string;\n /**\n * Remaining time if maxDuration set\n */\n remaining?: number | null;\n /**\n * Maximum duration\n */\n maxDuration?: number;\n /**\n * Whether recording is paused\n */\n isPaused?: boolean;\n /**\n * Whether recording is active (not paused)\n */\n isRecording?: boolean;\n /**\n * Pause handler\n */\n onPause?: () => void;\n /**\n * Resume handler\n */\n onResume?: () => void;\n /**\n * Stop handler\n */\n onStop?: () => void;\n /**\n * Position of controls\n */\n position?: TriggerPosition;\n /**\n * Custom class name\n */\n className?: string;\n /**\n * Z-index for positioning\n */\n zIndex?: number;\n}\n\n/**\n * Recording controls bar with timer, pause, and stop buttons\n */\nexport const RecordingControls: FC<RecordingControlsProps> = ({\n elapsed,\n elapsedFormatted,\n remaining,\n maxDuration,\n isPaused = false,\n isRecording = true,\n onPause,\n onResume,\n onStop,\n position = \"bottom-right\",\n className,\n zIndex = 9999,\n}) => {\n return (\n <div\n className={cn(\n // Base styles\n \"fixed flex items-center gap-3 px-4 py-2.5 rounded-full\",\n \"bg-white dark:bg-gray-800 shadow-lg border border-gray-200 dark:border-gray-700\",\n // Animation\n \"animate-slide-up\",\n // Position\n POSITION_CLASSES[position],\n className\n )}\n style={{ zIndex }}\n >\n {/* Recording status indicator */}\n <div className=\"flex items-center gap-2\">\n <div\n className={cn(\n \"flex items-center gap-1.5\",\n isPaused ? \"text-amber-500\" : \"text-red-500\"\n )}\n >\n {isPaused ? (\n <>\n <PauseIcon className=\"w-3 h-3\" />\n <span className=\"text-xs font-semibold uppercase\">Paused</span>\n </>\n ) : (\n <>\n <RecordingIcon className=\"w-3 h-3 animate-pulse-record\" />\n <span className=\"text-xs font-semibold uppercase\">Rec</span>\n </>\n )}\n </div>\n </div>\n\n {/* Timer */}\n <Timer\n elapsed={elapsed}\n elapsedFormatted={elapsedFormatted}\n remaining={remaining}\n maxDuration={maxDuration}\n isPaused={isPaused}\n />\n\n {/* Control buttons */}\n <div className=\"flex items-center gap-1 ml-2\">\n {/* Pause/Resume button */}\n <button\n type=\"button\"\n onClick={isPaused ? onResume : onPause}\n aria-label={isPaused ? ARIA_LABELS.resumeButton : ARIA_LABELS.pauseButton}\n className={cn(\n \"p-2 rounded-full transition-colors\",\n \"hover:bg-gray-100 dark:hover:bg-gray-700\",\n \"focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n )}\n >\n {isPaused ? (\n <PlayIcon className=\"w-4 h-4 text-gray-600 dark:text-gray-300\" />\n ) : (\n <PauseIcon className=\"w-4 h-4 text-gray-600 dark:text-gray-300\" />\n )}\n </button>\n\n {/* Stop button */}\n <button\n type=\"button\"\n onClick={onStop}\n aria-label={ARIA_LABELS.stopButton}\n className={cn(\n \"p-2 rounded-full transition-colors\",\n \"hover:bg-red-100 dark:hover:bg-red-900/30\",\n \"focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2\"\n )}\n >\n <StopIcon className=\"w-4 h-4 text-red-600 dark:text-red-400\" />\n </button>\n </div>\n </div>\n );\n};\n","import type { FC } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { ARIA_LABELS } from \"../../constants\";\n\nexport interface CountdownProps {\n /**\n * Current countdown value\n */\n value: number;\n /**\n * Cancel handler\n */\n onCancel?: () => void;\n /**\n * Custom class name\n */\n className?: string;\n /**\n * Z-index for overlay\n */\n zIndex?: number;\n}\n\n/**\n * Fullscreen countdown overlay (3-2-1 before recording)\n */\nexport const Countdown: FC<CountdownProps> = ({\n value,\n onCancel,\n className,\n zIndex = 10000,\n}) => {\n return (\n <div\n className={cn(\n // Fullscreen overlay\n \"fixed inset-0 flex flex-col items-center justify-center\",\n \"bg-black/50 backdrop-blur-sm\",\n \"animate-fade-in\",\n className\n )}\n style={{ zIndex }}\n role=\"alert\"\n aria-live=\"assertive\"\n aria-label={`${ARIA_LABELS.countdown} ${value}`}\n >\n {/* Countdown number */}\n <div\n key={value}\n className={cn(\n \"flex items-center justify-center\",\n \"w-32 h-32 rounded-full\",\n \"bg-white/10 border-4 border-white/30\",\n \"text-white text-7xl font-bold\",\n \"animate-countdown-scale\"\n )}\n >\n {value}\n </div>\n\n {/* Message */}\n <p className=\"mt-6 text-white/80 text-lg font-medium\">\n Recording starts in...\n </p>\n\n {/* Cancel button */}\n {onCancel && (\n <button\n type=\"button\"\n onClick={onCancel}\n className={cn(\n \"mt-8 px-6 py-2 rounded-full\",\n \"bg-white/10 hover:bg-white/20\",\n \"text-white text-sm font-medium\",\n \"transition-colors duration-200\",\n \"focus:outline-none focus:ring-2 focus:ring-white/50\"\n )}\n >\n Cancel\n </button>\n )}\n </div>\n );\n};\n","import { forwardRef, useRef, useImperativeHandle, useState, useCallback, useEffect } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { PlayIcon, PauseIcon } from \"../Trigger/TriggerIcon\";\nimport { ARIA_LABELS } from \"../../constants\";\n\nexport interface VideoPlayerProps {\n /**\n * Video source URL\n */\n src: string;\n /**\n * Custom class name\n */\n className?: string;\n /**\n * Whether to show controls\n */\n showControls?: boolean;\n /**\n * Auto-play on mount\n */\n autoPlay?: boolean;\n /**\n * Known duration in seconds (fallback for WebM files where duration may be Infinity)\n */\n knownDuration?: number;\n}\n\nexport interface VideoPlayerRef {\n play: () => void;\n pause: () => void;\n togglePlay: () => void;\n}\n\n/**\n * Video player component with custom controls\n */\nexport const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(\n ({ src, className, showControls = true, autoPlay = false, knownDuration }, ref) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(knownDuration || 0);\n\n // Expose controls via ref\n useImperativeHandle(ref, () => ({\n play: () => videoRef.current?.play(),\n pause: () => videoRef.current?.pause(),\n togglePlay: () => {\n if (isPlaying) {\n videoRef.current?.pause();\n } else {\n videoRef.current?.play();\n }\n },\n }));\n\n // Handle play state changes\n const handlePlay = useCallback(() => setIsPlaying(true), []);\n const handlePause = useCallback(() => setIsPlaying(false), []);\n const handleEnded = useCallback(() => setIsPlaying(false), []);\n\n // Handle time update\n const handleTimeUpdate = useCallback(() => {\n if (videoRef.current) {\n setCurrentTime(videoRef.current.currentTime);\n\n // For WebM files, duration might become available during playback\n const videoDuration = videoRef.current.duration;\n if (isFinite(videoDuration) && videoDuration > 0) {\n setDuration(videoDuration);\n }\n }\n }, []);\n\n // Handle metadata loaded\n const handleLoadedMetadata = useCallback(() => {\n if (videoRef.current) {\n const videoDuration = videoRef.current.duration;\n // Only set if it's a valid finite number\n if (isFinite(videoDuration) && videoDuration > 0) {\n setDuration(videoDuration);\n }\n }\n }, []);\n\n // Handle duration change (WebM files may report duration later)\n const handleDurationChange = useCallback(() => {\n if (videoRef.current) {\n const videoDuration = videoRef.current.duration;\n if (isFinite(videoDuration) && videoDuration > 0) {\n setDuration(videoDuration);\n }\n }\n }, []);\n\n // Use knownDuration as fallback when video duration is not available\n useEffect(() => {\n if (knownDuration && knownDuration > 0 && (!duration || duration === 0)) {\n setDuration(knownDuration);\n }\n }, [knownDuration, duration]);\n\n // Handle seek\n const handleSeek = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const time = parseFloat(e.target.value);\n if (videoRef.current && isFinite(time)) {\n videoRef.current.currentTime = time;\n setCurrentTime(time);\n }\n }, []);\n\n // Toggle play/pause\n const togglePlay = useCallback(() => {\n if (isPlaying) {\n videoRef.current?.pause();\n } else {\n videoRef.current?.play();\n }\n }, [isPlaying]);\n\n // Format time as MM:SS\n const formatTime = (seconds: number): string => {\n if (!isFinite(seconds) || isNaN(seconds)) {\n return \"00:00\";\n }\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins.toString().padStart(2, \"0\")}:${secs.toString().padStart(2, \"0\")}`;\n };\n\n // Get effective duration for display\n const effectiveDuration = isFinite(duration) && duration > 0\n ? duration\n : knownDuration || 0;\n\n return (\n <div className={cn(\"relative bg-black rounded-lg overflow-hidden\", className)}>\n {/* Video element */}\n <video\n ref={videoRef}\n src={src}\n className=\"w-full h-auto\"\n onPlay={handlePlay}\n onPause={handlePause}\n onEnded={handleEnded}\n onTimeUpdate={handleTimeUpdate}\n onLoadedMetadata={handleLoadedMetadata}\n onDurationChange={handleDurationChange}\n autoPlay={autoPlay}\n playsInline\n aria-label={ARIA_LABELS.videoPlayer}\n />\n\n {/* Controls overlay */}\n {showControls && (\n <div\n className={cn(\n \"absolute bottom-0 left-0 right-0\",\n \"bg-gradient-to-t from-black/80 to-transparent\",\n \"p-3\"\n )}\n >\n {/* Progress bar */}\n <div className=\"flex items-center gap-2 mb-2\">\n <input\n type=\"range\"\n min={0}\n max={effectiveDuration || 100}\n value={currentTime}\n onChange={handleSeek}\n className={cn(\n \"flex-1 h-1 rounded-full appearance-none cursor-pointer\",\n \"bg-white/30\",\n \"[&::-webkit-slider-thumb]:appearance-none\",\n \"[&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3\",\n \"[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white\"\n )}\n aria-label=\"Video progress\"\n />\n </div>\n\n {/* Play button and time */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={togglePlay}\n className={cn(\n \"p-1.5 rounded-full\",\n \"hover:bg-white/20\",\n \"transition-colors duration-150\",\n \"focus:outline-none focus:ring-2 focus:ring-white/50\"\n )}\n aria-label={isPlaying ? \"Pause\" : \"Play\"}\n >\n {isPlaying ? (\n <PauseIcon className=\"w-4 h-4 text-white\" />\n ) : (\n <PlayIcon className=\"w-4 h-4 text-white\" />\n )}\n </button>\n\n <span className=\"text-white/80 text-xs font-mono tabular-nums\">\n {formatTime(currentTime)} / {formatTime(effectiveDuration)}\n </span>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n }\n);\n\nVideoPlayer.displayName = \"VideoPlayer\";\n","import type { FC } from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../../utils/cn\";\nimport { VideoPlayer } from \"./VideoPlayer\";\nimport {\n CloseIcon,\n DownloadIcon,\n RefreshIcon,\n CheckIcon,\n} from \"../Trigger/TriggerIcon\";\nimport { ARIA_LABELS } from \"../../constants\";\nimport { formatBytes } from \"../../utils/downloadBlob\";\nimport type { RecordingResult, ThemeOption } from \"../../types\";\n\nexport interface PreviewModalProps {\n /**\n * Recording result to preview\n */\n result: RecordingResult;\n /**\n * Whether the modal is open\n */\n isOpen: boolean;\n /**\n * Download handler\n */\n onDownload?: () => void;\n /**\n * Re-record handler\n */\n onReRecord?: () => void;\n /**\n * Done/Close handler\n */\n onClose?: () => void;\n /**\n * Custom class name\n */\n className?: string;\n /**\n * Z-index for modal\n */\n zIndex?: number;\n /**\n * Use portal for rendering\n */\n usePortal?: boolean;\n /**\n * Theme for the modal\n */\n theme?: ThemeOption;\n}\n\n/**\n * Preview modal for recorded video\n */\nexport const PreviewModal: FC<PreviewModalProps> = ({\n result,\n isOpen,\n onDownload,\n onReRecord,\n onClose,\n className,\n zIndex = 10000,\n usePortal = true,\n theme = \"system\",\n}) => {\n const modalRef = useRef<HTMLDivElement>(null);\n\n // Handle escape key\n useEffect(() => {\n if (!isOpen) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n onClose?.();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [isOpen, onClose]);\n\n // Focus trap\n useEffect(() => {\n if (!isOpen || !modalRef.current) return;\n\n const previousActiveElement = document.activeElement as HTMLElement;\n modalRef.current.focus();\n\n return () => {\n previousActiveElement?.focus();\n };\n }, [isOpen]);\n\n if (!isOpen) return null;\n\n // Determine if dark mode should be applied\n const isDark =\n theme === \"dark\" ||\n (theme === \"system\" &&\n typeof window !== \"undefined\" &&\n window.matchMedia?.(\"(prefers-color-scheme: dark)\").matches);\n\n const modalContent = (\n <div\n className={cn(\n // Dark mode wrapper for portal\n isDark && \"dark\",\n // Fullscreen overlay\n \"fixed inset-0 flex items-center justify-center p-4\",\n \"bg-black/60 backdrop-blur-sm\",\n \"animate-fade-in\",\n className\n )}\n style={{ zIndex }}\n onClick={(e) => {\n // Close on backdrop click\n if (e.target === e.currentTarget) {\n onClose?.();\n }\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"preview-title\"\n aria-describedby=\"preview-description\"\n >\n <div\n ref={modalRef}\n className={cn(\n // Modal container\n \"relative w-full max-w-2xl\",\n \"bg-white dark:bg-gray-800 rounded-xl shadow-2xl\",\n \"animate-slide-up\",\n \"focus:outline-none\"\n )}\n tabIndex={-1}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700\">\n <h2\n id=\"preview-title\"\n className=\"text-lg font-semibold text-gray-900 dark:text-white\"\n >\n Recording Complete\n </h2>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={ARIA_LABELS.closePreview}\n className={cn(\n \"p-2 rounded-full\",\n \"hover:bg-gray-100 dark:hover:bg-gray-700\",\n \"transition-colors duration-150\",\n \"focus:outline-none focus:ring-2 focus:ring-blue-500\"\n )}\n >\n <CloseIcon className=\"w-5 h-5 text-gray-500 dark:text-gray-400\" />\n </button>\n </div>\n\n {/* Video preview */}\n <div className=\"p-6\">\n <VideoPlayer\n src={result.url}\n className=\"w-full aspect-video\"\n showControls\n knownDuration={result.duration}\n />\n\n {/* Recording info */}\n <p\n id=\"preview-description\"\n className=\"mt-4 text-sm text-gray-500 dark:text-gray-400 text-center\"\n >\n Duration: {formatDuration(result.duration)} • Size:{\" \"}\n {formatBytes(result.size)} • {getFormatLabel(result.mimeType)}\n {result.hasAudio && \" • With audio\"}\n </p>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center justify-center gap-3 px-6 py-4 border-t border-gray-200 dark:border-gray-700\">\n {/* Download button */}\n <button\n type=\"button\"\n onClick={onDownload}\n aria-label={ARIA_LABELS.downloadButton}\n className={cn(\n \"flex items-center gap-2 px-4 py-2 rounded-lg\",\n \"bg-blue-600 hover:bg-blue-700 text-white\",\n \"font-medium text-sm\",\n \"transition-colors duration-150\",\n \"focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\",\n \"dark:focus:ring-offset-gray-800\"\n )}\n >\n <DownloadIcon className=\"w-4 h-4\" />\n Download\n </button>\n\n {/* Re-record button */}\n <button\n type=\"button\"\n onClick={onReRecord}\n aria-label={ARIA_LABELS.reRecordButton}\n className={cn(\n \"flex items-center gap-2 px-4 py-2 rounded-lg\",\n \"bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600\",\n \"text-gray-700 dark:text-gray-300\",\n \"font-medium text-sm\",\n \"transition-colors duration-150\",\n \"focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2\",\n \"dark:focus:ring-offset-gray-800\"\n )}\n >\n <RefreshIcon className=\"w-4 h-4\" />\n Re-record\n </button>\n\n {/* Done button */}\n <button\n type=\"button\"\n onClick={onClose}\n className={cn(\n \"flex items-center gap-2 px-4 py-2 rounded-lg\",\n \"bg-green-600 hover:bg-green-700 text-white\",\n \"font-medium text-sm\",\n \"transition-colors duration-150\",\n \"focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2\",\n \"dark:focus:ring-offset-gray-800\"\n )}\n >\n <CheckIcon className=\"w-4 h-4\" />\n Done\n </button>\n </div>\n </div>\n </div>\n );\n\n // Use portal for rendering outside component tree\n if (usePortal && typeof document !== \"undefined\") {\n return createPortal(modalContent, document.body);\n }\n\n return modalContent;\n};\n\n/**\n * Format duration in seconds to MM:SS\n */\nfunction formatDuration(seconds: number): string {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}m ${secs}s`;\n}\n\n/**\n * Get human-readable format label from MIME type\n */\nfunction getFormatLabel(mimeType: string): string {\n if (mimeType.includes(\"webm\")) return \"WebM\";\n if (mimeType.includes(\"mp4\")) return \"MP4\";\n return \"Video\";\n}\n","/**\n * Download a blob as a file\n *\n * @param blob - The blob to download\n * @param filename - The filename to save as\n *\n * @example\n * ```ts\n * const blob = new Blob(['hello'], { type: 'text/plain' });\n * downloadBlob(blob, 'hello.txt');\n * ```\n */\nexport function downloadBlob(blob: Blob, filename: string): void {\n const url = URL.createObjectURL(blob);\n\n try {\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = filename;\n link.style.display = \"none\";\n\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n } finally {\n // Revoke the URL after a short delay to ensure download starts\n setTimeout(() => {\n URL.revokeObjectURL(url);\n }, 100);\n }\n}\n\n/**\n * Generate a default filename for recordings\n *\n * @param timestamp - The recording timestamp\n * @param extension - The file extension (default: 'webm')\n * @returns Generated filename\n *\n * @example\n * ```ts\n * generateFilename(new Date()) // => 'screen-recording-2024-01-15-143052.webm'\n * ```\n */\nexport function generateFilename(\n timestamp: Date = new Date(),\n extension: string = \"webm\"\n): string {\n const year = timestamp.getFullYear();\n const month = String(timestamp.getMonth() + 1).padStart(2, \"0\");\n const day = String(timestamp.getDate()).padStart(2, \"0\");\n const hours = String(timestamp.getHours()).padStart(2, \"0\");\n const minutes = String(timestamp.getMinutes()).padStart(2, \"0\");\n const seconds = String(timestamp.getSeconds()).padStart(2, \"0\");\n\n return `screen-recording-${year}-${month}-${day}-${hours}${minutes}${seconds}.${extension}`;\n}\n\n/**\n * Format bytes to human-readable size\n *\n * @param bytes - Size in bytes\n * @param decimals - Number of decimal places\n * @returns Formatted size string\n *\n * @example\n * ```ts\n * formatBytes(1024) // => '1 KB'\n * formatBytes(1048576) // => '1 MB'\n * formatBytes(1536, 1) // => '1.5 KB'\n * ```\n */\nexport function formatBytes(bytes: number, decimals: number = 2): string {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\n\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n const index = Math.min(i, sizes.length - 1);\n\n return `${parseFloat((bytes / Math.pow(k, index)).toFixed(dm))} ${sizes[index]}`;\n}\n","import type { FC } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { RecordingState } from \"../../types\";\n\nexport interface StatusBadgeProps {\n /**\n * Current recording state\n */\n state: RecordingState;\n /**\n * Custom class name\n */\n className?: string;\n}\n\n/**\n * Status badge showing current recording state\n */\nexport const StatusBadge: FC<StatusBadgeProps> = ({ state, className }) => {\n const getStatusConfig = () => {\n switch (state) {\n case \"recording\":\n return {\n label: \"REC\",\n dotClass: \"bg-red-500 animate-pulse-record\",\n textClass: \"text-red-600 dark:text-red-400\",\n bgClass: \"bg-red-50 dark:bg-red-900/20\",\n };\n case \"paused\":\n return {\n label: \"PAUSED\",\n dotClass: \"bg-amber-500\",\n textClass: \"text-amber-600 dark:text-amber-400\",\n bgClass: \"bg-amber-50 dark:bg-amber-900/20\",\n };\n case \"requesting\":\n return {\n label: \"REQUESTING\",\n dotClass: \"bg-blue-500 animate-pulse\",\n textClass: \"text-blue-600 dark:text-blue-400\",\n bgClass: \"bg-blue-50 dark:bg-blue-900/20\",\n };\n case \"countdown\":\n return {\n label: \"STARTING\",\n dotClass: \"bg-amber-500 animate-pulse\",\n textClass: \"text-amber-600 dark:text-amber-400\",\n bgClass: \"bg-amber-50 dark:bg-amber-900/20\",\n };\n case \"stopped\":\n return {\n label: \"STOPPED\",\n dotClass: \"bg-gray-500\",\n textClass: \"text-gray-600 dark:text-gray-400\",\n bgClass: \"bg-gray-50 dark:bg-gray-800\",\n };\n case \"error\":\n return {\n label: \"ERROR\",\n dotClass: \"bg-red-500\",\n textClass: \"text-red-600 dark:text-red-400\",\n bgClass: \"bg-red-50 dark:bg-red-900/20\",\n };\n default:\n return {\n label: \"IDLE\",\n dotClass: \"bg-gray-400\",\n textClass: \"text-gray-500 dark:text-gray-400\",\n bgClass: \"bg-gray-50 dark:bg-gray-800\",\n };\n }\n };\n\n const config = getStatusConfig();\n\n return (\n <div\n className={cn(\n \"inline-flex items-center gap-1.5 px-2 py-1 rounded-full\",\n config.bgClass,\n className\n )}\n role=\"status\"\n aria-live=\"polite\"\n >\n <span className={cn(\"w-2 h-2 rounded-full\", config.dotClass)} />\n <span className={cn(\"text-xs font-semibold uppercase\", config.textClass)}>\n {config.label}\n </span>\n </div>\n );\n};\n","import type { FC } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { WarningIcon, CloseIcon } from \"../Trigger/TriggerIcon\";\nimport type { ScreenRecorderError } from \"../../types\";\nimport type { TriggerPosition } from \"../../types\";\nimport { POSITION_CLASSES } from \"../../constants\";\n\nexport interface ErrorMessageProps {\n /**\n * Error to display\n */\n error: ScreenRecorderError;\n /**\n * Retry handler\n */\n onRetry?: () => void;\n /**\n * Dismiss handler\n */\n onDismiss?: () => void;\n /**\n * Position of the error message\n */\n position?: TriggerPosition;\n /**\n * Custom class name\n */\n className?: string;\n /**\n * Z-index for positioning\n */\n zIndex?: number;\n}\n\n/**\n * Error message component for screen recording errors\n */\nexport const ErrorMessage: FC<ErrorMessageProps> = ({\n error,\n onRetry,\n onDismiss,\n position = \"bottom-right\",\n className,\n zIndex = 9999,\n}) => {\n return (\n <div\n className={cn(\n // Base styles\n \"fixed flex flex-col gap-3 p-4 max-w-sm rounded-lg shadow-lg\",\n \"bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800\",\n \"animate-slide-up\",\n // Position\n POSITION_CLASSES[position],\n className\n )}\n style={{ zIndex }}\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {/* Header with icon and close button */}\n <div className=\"flex items-start gap-3\">\n <WarningIcon className=\"w-5 h-5 text-red-500 dark:text-red-400 flex-shrink-0 mt-0.5\" />\n <div className=\"flex-1\">\n <h3 className=\"font-semibold text-red-800 dark:text-red-200\">\n {getErrorTitle(error.code)}\n </h3>\n <p className=\"mt-1 text-sm text-red-600 dark:text-red-300\">\n {error.message}\n </p>\n </div>\n {onDismiss && (\n <button\n type=\"button\"\n onClick={onDismiss}\n className={cn(\n \"p-1 rounded-full\",\n \"hover:bg-red-100 dark:hover:bg-red-800/50\",\n \"transition-colors duration-150\",\n \"focus:outline-none focus:ring-2 focus:ring-red-500\"\n )}\n aria-label=\"Dismiss error\"\n >\n <CloseIcon className=\"w-4 h-4 text-red-500 dark:text-red-400\" />\n </button>\n )}\n </div>\n\n {/* Retry button */}\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className={cn(\n \"w-full py-2 rounded-md\",\n \"bg-red-100 dark:bg-red-800/50 hover:bg-red-200 dark:hover:bg-red-800\",\n \"text-red-700 dark:text-red-200 font-medium text-sm\",\n \"transition-colors duration-150\",\n \"focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2\"\n )}\n >\n Try Again\n </button>\n )}\n </div>\n );\n};\n\n/**\n * Get user-friendly error title based on error code\n */\nfunction getErrorTitle(code: ScreenRecorderError[\"code\"]): string {\n switch (code) {\n case \"PERMISSION_DENIED\":\n return \"Permission Denied\";\n case \"NOT_SUPPORTED\":\n return \"Not Supported\";\n case \"MEDIA_RECORDER_ERROR\":\n return \"Recording Error\";\n case \"STREAM_ENDED\":\n return \"Screen Sharing Stopped\";\n case \"NO_STREAM\":\n return \"No Stream\";\n case \"ENCODING_ERROR\":\n return \"Encoding Error\";\n default:\n return \"Error\";\n }\n}\n"],"mappings":"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","names":["import_react","import_react_dom","import_react","import_react","error","import_react","import_react","import_react","remaining","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","formatTime","import_react","import_jsx_runtime","formatDuration","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
|