@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.
@@ -0,0 +1,1092 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, FC } from 'react';
3
+ import { ClassValue } from 'clsx';
4
+
5
+ /**
6
+ * Possible states of the screen recording process
7
+ */
8
+ type RecordingState$1 = "idle" | "requesting" | "countdown" | "recording" | "paused" | "stopped" | "error";
9
+ /**
10
+ * Error codes for screen recording errors
11
+ */
12
+ type ScreenRecorderErrorCode = "PERMISSION_DENIED" | "NOT_SUPPORTED" | "MEDIA_RECORDER_ERROR" | "STREAM_ENDED" | "NO_STREAM" | "ENCODING_ERROR" | "UNKNOWN";
13
+ /**
14
+ * Error object for screen recording errors
15
+ */
16
+ interface ScreenRecorderError {
17
+ /** Error code for programmatic handling */
18
+ code: ScreenRecorderErrorCode;
19
+ /** Human-readable error message */
20
+ message: string;
21
+ /** Original error if available */
22
+ originalError?: Error;
23
+ }
24
+ /**
25
+ * Result of a completed recording
26
+ */
27
+ interface RecordingResult {
28
+ /** Recorded video blob */
29
+ blob: Blob;
30
+ /** Blob URL for preview/playback */
31
+ url: string;
32
+ /** Duration in seconds */
33
+ duration: number;
34
+ /** File size in bytes */
35
+ size: number;
36
+ /** MIME type */
37
+ mimeType: string;
38
+ /** Recording timestamp */
39
+ timestamp: Date;
40
+ /** Whether audio was included */
41
+ hasAudio: boolean;
42
+ }
43
+ /**
44
+ * Quality preset configuration
45
+ */
46
+ interface QualityPreset {
47
+ /** Video bitrate in bits per second */
48
+ videoBitsPerSecond: number;
49
+ /** Video width (optional, uses source resolution if not specified) */
50
+ width?: number;
51
+ /** Video height (optional, uses source resolution if not specified) */
52
+ height?: number;
53
+ /** Frame rate */
54
+ frameRate?: number;
55
+ }
56
+ /**
57
+ * Quality option - can be a preset name or custom configuration
58
+ */
59
+ type QualityOption = "low" | "medium" | "high" | QualityPreset;
60
+ /**
61
+ * Audio configuration options
62
+ */
63
+ interface AudioConfig {
64
+ /** Capture system/tab audio */
65
+ system?: boolean;
66
+ /** Capture microphone input */
67
+ microphone?: boolean;
68
+ }
69
+ /**
70
+ * Audio option - can be boolean or detailed configuration
71
+ */
72
+ type AudioOption = boolean | AudioConfig;
73
+ /**
74
+ * Position of the floating trigger button
75
+ */
76
+ type TriggerPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
77
+ /**
78
+ * Theme setting for the component
79
+ */
80
+ type ThemeOption = "light" | "dark" | "system";
81
+ /**
82
+ * Render mode for floating UI elements
83
+ */
84
+ type RenderMode = "portal" | "inline";
85
+ /**
86
+ * Props for the ScreenRecorder component
87
+ */
88
+ interface ScreenRecorderProps {
89
+ /**
90
+ * Maximum recording duration in seconds
91
+ * Set to Infinity for unlimited duration
92
+ * @default 300 (5 minutes)
93
+ */
94
+ maxDuration?: number;
95
+ /**
96
+ * Countdown seconds before recording starts
97
+ * Set to false to disable countdown
98
+ * @default 3
99
+ */
100
+ countdown?: number | false;
101
+ /**
102
+ * Include audio in recording
103
+ * @default false
104
+ */
105
+ audio?: AudioOption;
106
+ /**
107
+ * Video quality settings
108
+ * @default 'medium'
109
+ */
110
+ quality?: QualityOption;
111
+ /**
112
+ * Output format
113
+ * @default 'webm'
114
+ */
115
+ format?: "webm" | "mp4";
116
+ /**
117
+ * MIME type for MediaRecorder
118
+ * @default 'video/webm;codecs=vp9'
119
+ */
120
+ mimeType?: string;
121
+ /**
122
+ * Position of the floating trigger button
123
+ * @default 'bottom-right'
124
+ */
125
+ position?: TriggerPosition;
126
+ /**
127
+ * Custom trigger button content
128
+ */
129
+ triggerContent?: ReactNode;
130
+ /**
131
+ * Show preview modal after recording
132
+ * @default true
133
+ */
134
+ showPreview?: boolean;
135
+ /**
136
+ * Show timer during recording
137
+ * @default true
138
+ */
139
+ showTimer?: boolean;
140
+ /**
141
+ * Z-index for floating elements
142
+ * @default 9999
143
+ */
144
+ zIndex?: number;
145
+ /**
146
+ * Theme override
147
+ * @default 'system'
148
+ */
149
+ theme?: ThemeOption;
150
+ /**
151
+ * Custom class name
152
+ */
153
+ className?: string;
154
+ /**
155
+ * Default filename for download (without extension)
156
+ * Can be a string or a function that receives the timestamp
157
+ * @default 'screen-recording'
158
+ */
159
+ filename?: string | ((timestamp: Date) => string);
160
+ /**
161
+ * Called when recording starts
162
+ */
163
+ onRecordingStart?: () => void;
164
+ /**
165
+ * Called when recording stops with the result
166
+ */
167
+ onRecordingStop?: (result: RecordingResult) => void;
168
+ /**
169
+ * Called when recording is paused
170
+ */
171
+ onPause?: () => void;
172
+ /**
173
+ * Called when recording is resumed
174
+ */
175
+ onResume?: () => void;
176
+ /**
177
+ * Called when user downloads the recording
178
+ */
179
+ onDownload?: (result: RecordingResult) => void;
180
+ /**
181
+ * Called when an error occurs
182
+ */
183
+ onError?: (error: ScreenRecorderError) => void;
184
+ /**
185
+ * Called when user denies screen share permission
186
+ */
187
+ onPermissionDenied?: () => void;
188
+ /**
189
+ * Called on each recording tick (every second)
190
+ */
191
+ onTick?: (elapsed: number, remaining: number | null) => void;
192
+ /**
193
+ * Auto-download after recording stops
194
+ * @default false
195
+ */
196
+ autoDownload?: boolean;
197
+ /**
198
+ * Disable the component
199
+ */
200
+ disabled?: boolean;
201
+ /**
202
+ * Render mode for floating UI
203
+ * @default 'portal'
204
+ */
205
+ renderMode?: RenderMode;
206
+ }
207
+ /**
208
+ * Options for the useScreenRecorder hook
209
+ */
210
+ interface UseScreenRecorderOptions {
211
+ /**
212
+ * Maximum recording duration in seconds
213
+ * Set to Infinity for unlimited duration
214
+ * @default 300 (5 minutes)
215
+ */
216
+ maxDuration?: number;
217
+ /**
218
+ * Countdown seconds before recording starts
219
+ * Set to false to disable countdown
220
+ * @default 3
221
+ */
222
+ countdown?: number | false;
223
+ /**
224
+ * Include audio in recording
225
+ * @default false
226
+ */
227
+ audio?: AudioOption;
228
+ /**
229
+ * Video quality settings
230
+ * @default 'medium'
231
+ */
232
+ quality?: QualityOption;
233
+ /**
234
+ * MIME type for MediaRecorder
235
+ * @default 'video/webm;codecs=vp9'
236
+ */
237
+ mimeType?: string;
238
+ /**
239
+ * Called when an error occurs
240
+ */
241
+ onError?: (error: ScreenRecorderError) => void;
242
+ }
243
+ /**
244
+ * Return type for the useScreenRecorder hook
245
+ */
246
+ interface UseScreenRecorderReturn {
247
+ /** Current recording state */
248
+ state: RecordingState$1;
249
+ /** Whether currently recording */
250
+ isRecording: boolean;
251
+ /** Whether recording is paused */
252
+ isPaused: boolean;
253
+ /** Whether in countdown phase */
254
+ isCountingDown: boolean;
255
+ /** Current countdown value (null when not counting) */
256
+ countdownValue: number | null;
257
+ /** Elapsed recording time in seconds */
258
+ elapsed: number;
259
+ /** Remaining time if maxDuration set (null if no limit) */
260
+ remaining: number | null;
261
+ /** Formatted elapsed time (MM:SS) */
262
+ elapsedFormatted: string;
263
+ /** Recording result after stop (null until recording completes) */
264
+ result: RecordingResult | null;
265
+ /** Current error if any */
266
+ error: ScreenRecorderError | null;
267
+ /** Whether browser supports screen recording */
268
+ isSupported: boolean;
269
+ /** Start screen recording (opens browser picker) */
270
+ start: () => Promise<void>;
271
+ /** Stop recording */
272
+ stop: () => void;
273
+ /** Pause recording */
274
+ pause: () => void;
275
+ /** Resume paused recording */
276
+ resume: () => void;
277
+ /** Toggle pause/resume */
278
+ togglePause: () => void;
279
+ /** Download the recording */
280
+ download: (filename?: string) => void;
281
+ /** Reset state for new recording */
282
+ reset: () => void;
283
+ /** Get blob URL for preview */
284
+ getPreviewUrl: () => string | null;
285
+ /** Revoke blob URL (cleanup) */
286
+ revokePreviewUrl: () => void;
287
+ }
288
+ /**
289
+ * Browser support detection result
290
+ */
291
+ interface BrowserSupport {
292
+ /** Whether screen recording is fully supported */
293
+ isSupported: boolean;
294
+ /** Whether getDisplayMedia is available */
295
+ hasDisplayMedia: boolean;
296
+ /** Whether MediaRecorder is available */
297
+ hasMediaRecorder: boolean;
298
+ /** List of supported MIME types */
299
+ supportedMimeTypes: string[];
300
+ }
301
+
302
+ /**
303
+ * ScreenRecorder component for capturing screen recordings
304
+ *
305
+ * A complete UI solution for screen recording with floating trigger,
306
+ * recording controls, countdown, and preview modal.
307
+ *
308
+ * @example
309
+ * ```tsx
310
+ * function App() {
311
+ * return (
312
+ * <div>
313
+ * <h1>My Application</h1>
314
+ * <ScreenRecorder
315
+ * onRecordingStop={(result) => {
316
+ * console.log('Recording complete:', result);
317
+ * }}
318
+ * />
319
+ * </div>
320
+ * );
321
+ * }
322
+ * ```
323
+ *
324
+ * @example
325
+ * ```tsx
326
+ * // Bug report integration
327
+ * function BugReportForm() {
328
+ * const [recording, setRecording] = useState<RecordingResult | null>(null);
329
+ *
330
+ * return (
331
+ * <form>
332
+ * <textarea placeholder="Describe the bug..." />
333
+ * <ScreenRecorder
334
+ * position="bottom-left"
335
+ * maxDuration={60}
336
+ * onRecordingStop={setRecording}
337
+ * triggerContent={
338
+ * recording ? "✓ Recording attached" : "📹 Record screen"
339
+ * }
340
+ * />
341
+ * <button type="submit">Submit</button>
342
+ * </form>
343
+ * );
344
+ * }
345
+ * ```
346
+ */
347
+ declare const ScreenRecorder: react.ForwardRefExoticComponent<ScreenRecorderProps & react.RefAttributes<HTMLDivElement>>;
348
+
349
+ /**
350
+ * Main hook for screen recording functionality
351
+ *
352
+ * Combines display media capture, media recording, timer, and countdown
353
+ * into a unified state machine for screen recording.
354
+ *
355
+ * @param options - Configuration options
356
+ * @returns Screen recorder state and controls
357
+ *
358
+ * @example
359
+ * ```tsx
360
+ * function CustomRecorder() {
361
+ * const {
362
+ * state,
363
+ * isRecording,
364
+ * elapsed,
365
+ * elapsedFormatted,
366
+ * result,
367
+ * start,
368
+ * stop,
369
+ * pause,
370
+ * resume,
371
+ * download,
372
+ * reset,
373
+ * isSupported,
374
+ * error,
375
+ * } = useScreenRecorder({
376
+ * maxDuration: 120,
377
+ * audio: true,
378
+ * countdown: 3,
379
+ * });
380
+ *
381
+ * if (!isSupported) {
382
+ * return <p>Screen recording is not supported.</p>;
383
+ * }
384
+ *
385
+ * return (
386
+ * <div>
387
+ * <p>State: {state}</p>
388
+ * <p>Time: {elapsedFormatted}</p>
389
+ * {state === 'idle' && <button onClick={start}>Start</button>}
390
+ * {isRecording && <button onClick={stop}>Stop</button>}
391
+ * {result && <video src={result.url} controls />}
392
+ * </div>
393
+ * );
394
+ * }
395
+ * ```
396
+ */
397
+ declare function useScreenRecorder(options?: UseScreenRecorderOptions): UseScreenRecorderReturn;
398
+
399
+ /**
400
+ * Predefined quality presets for recording
401
+ */
402
+ declare const QUALITY_PRESETS: Record<"low" | "medium" | "high", QualityPreset>;
403
+ /**
404
+ * Default values for screen recorder options
405
+ */
406
+ declare const DEFAULT_OPTIONS: {
407
+ /** Default maximum recording duration (5 minutes) */
408
+ readonly maxDuration: 300;
409
+ /** Default countdown duration (3 seconds) */
410
+ readonly countdown: 3;
411
+ /** Default audio setting */
412
+ readonly audio: false;
413
+ /** Default quality preset */
414
+ readonly quality: "medium";
415
+ /** Default output format */
416
+ readonly format: "webm";
417
+ /** Default MIME type for recording */
418
+ readonly mimeType: "video/webm;codecs=vp9";
419
+ /** Default filename */
420
+ readonly filename: "screen-recording";
421
+ /** Default position */
422
+ readonly position: "bottom-right";
423
+ /** Default z-index */
424
+ readonly zIndex: 9999;
425
+ /** Default theme */
426
+ readonly theme: "system";
427
+ /** Default render mode */
428
+ readonly renderMode: "portal";
429
+ /** Default show preview */
430
+ readonly showPreview: true;
431
+ /** Default show timer */
432
+ readonly showTimer: true;
433
+ /** Default auto download */
434
+ readonly autoDownload: false;
435
+ };
436
+ /**
437
+ * MIME types to check for browser support, in order of preference
438
+ */
439
+ declare const SUPPORTED_MIME_TYPES: readonly ["video/webm;codecs=vp9,opus", "video/webm;codecs=vp9", "video/webm;codecs=vp8,opus", "video/webm;codecs=vp8", "video/webm", "video/mp4"];
440
+ /**
441
+ * Human-readable error messages for each error code
442
+ */
443
+ declare const ERROR_MESSAGES: Record<ScreenRecorderErrorCode, string>;
444
+ /**
445
+ * Timer warning threshold (30 seconds remaining)
446
+ */
447
+ declare const TIMER_WARNING_THRESHOLD = 30;
448
+ /**
449
+ * Timer critical threshold (10 seconds remaining)
450
+ */
451
+ declare const TIMER_CRITICAL_THRESHOLD = 10;
452
+
453
+ /**
454
+ * Gets the best supported MIME type
455
+ */
456
+ declare function getBestMimeType(): string | null;
457
+ /**
458
+ * Hook to detect browser support for screen recording
459
+ *
460
+ * @returns Browser support information
461
+ *
462
+ * @example
463
+ * ```tsx
464
+ * function RecorderComponent() {
465
+ * const { isSupported, hasDisplayMedia, supportedMimeTypes } = useBrowserSupport();
466
+ *
467
+ * if (!isSupported) {
468
+ * return <p>Screen recording is not supported</p>;
469
+ * }
470
+ *
471
+ * return <button>Start Recording</button>;
472
+ * }
473
+ * ```
474
+ */
475
+ declare function useBrowserSupport(): BrowserSupport;
476
+ /**
477
+ * Checks browser support without React hooks (for one-time checks)
478
+ */
479
+ declare function checkBrowserSupport(): BrowserSupport;
480
+
481
+ interface UseDisplayMediaOptions {
482
+ /**
483
+ * Audio configuration
484
+ */
485
+ audio?: AudioOption;
486
+ /**
487
+ * Called when an error occurs
488
+ */
489
+ onError?: (error: ScreenRecorderError) => void;
490
+ /**
491
+ * Called when the stream ends (user stops sharing via browser)
492
+ */
493
+ onStreamEnded?: () => void;
494
+ }
495
+ interface UseDisplayMediaReturn {
496
+ /** Current media stream (null if not capturing) */
497
+ stream: MediaStream | null;
498
+ /** Whether stream is active */
499
+ isStreaming: boolean;
500
+ /** Request screen capture from user */
501
+ requestStream: () => Promise<MediaStream | null>;
502
+ /** Stop the current stream */
503
+ stopStream: () => void;
504
+ /** Current error if any */
505
+ error: ScreenRecorderError | null;
506
+ /** Whether audio is being captured */
507
+ hasAudio: boolean;
508
+ }
509
+ /**
510
+ * Hook to manage screen capture using getDisplayMedia API
511
+ *
512
+ * @param options - Configuration options
513
+ * @returns Display media state and controls
514
+ *
515
+ * @example
516
+ * ```tsx
517
+ * function ScreenCapture() {
518
+ * const { stream, requestStream, stopStream, error } = useDisplayMedia({
519
+ * audio: true,
520
+ * onStreamEnded: () => console.log('User stopped sharing'),
521
+ * });
522
+ *
523
+ * return (
524
+ * <button onClick={stream ? stopStream : requestStream}>
525
+ * {stream ? 'Stop' : 'Start'} Sharing
526
+ * </button>
527
+ * );
528
+ * }
529
+ * ```
530
+ */
531
+ declare function useDisplayMedia(options?: UseDisplayMediaOptions): UseDisplayMediaReturn;
532
+
533
+ interface UseMediaRecorderOptions {
534
+ /**
535
+ * Quality preset or custom configuration
536
+ */
537
+ quality?: QualityOption;
538
+ /**
539
+ * MIME type for recording
540
+ */
541
+ mimeType?: string;
542
+ /**
543
+ * Time slice for data events (ms)
544
+ * @default 1000
545
+ */
546
+ timeslice?: number;
547
+ /**
548
+ * Called when recording data is available
549
+ */
550
+ onDataAvailable?: (data: Blob) => void;
551
+ /**
552
+ * Called when recording starts
553
+ */
554
+ onStart?: () => void;
555
+ /**
556
+ * Called when recording stops
557
+ */
558
+ onStop?: (blob: Blob) => void;
559
+ /**
560
+ * Called when an error occurs
561
+ */
562
+ onError?: (error: ScreenRecorderError) => void;
563
+ }
564
+ interface UseMediaRecorderReturn {
565
+ /** MediaRecorder instance */
566
+ recorder: MediaRecorder | null;
567
+ /** Current recording state */
568
+ state: RecordingState;
569
+ /** Start recording */
570
+ start: () => void;
571
+ /** Stop recording */
572
+ stop: () => void;
573
+ /** Pause recording */
574
+ pause: () => void;
575
+ /** Resume recording */
576
+ resume: () => void;
577
+ /** Recorded blob (available after stop) */
578
+ blob: Blob | null;
579
+ /** MIME type being used */
580
+ mimeType: string;
581
+ /** Current error if any */
582
+ error: ScreenRecorderError | null;
583
+ }
584
+ /**
585
+ * Hook to manage MediaRecorder for video recording
586
+ *
587
+ * @param stream - MediaStream to record
588
+ * @param options - Configuration options
589
+ * @returns MediaRecorder state and controls
590
+ *
591
+ * @example
592
+ * ```tsx
593
+ * function Recorder({ stream }) {
594
+ * const { state, start, stop, pause, resume, blob } = useMediaRecorder(stream, {
595
+ * quality: 'high',
596
+ * onStop: (blob) => console.log('Recording complete:', blob),
597
+ * });
598
+ *
599
+ * return (
600
+ * <div>
601
+ * <p>State: {state}</p>
602
+ * <button onClick={start}>Start</button>
603
+ * <button onClick={stop}>Stop</button>
604
+ * </div>
605
+ * );
606
+ * }
607
+ * ```
608
+ */
609
+ declare function useMediaRecorder(stream: MediaStream | null, options?: UseMediaRecorderOptions): UseMediaRecorderReturn;
610
+
611
+ interface UseTimerOptions {
612
+ /**
613
+ * Maximum duration in seconds (timer will auto-stop at this limit)
614
+ */
615
+ maxDuration?: number;
616
+ /**
617
+ * Callback fired every second with elapsed time
618
+ */
619
+ onTick?: (elapsed: number, remaining: number | null) => void;
620
+ /**
621
+ * Callback fired when max duration is reached
622
+ */
623
+ onComplete?: () => void;
624
+ }
625
+ interface UseTimerReturn {
626
+ /** Elapsed time in seconds */
627
+ elapsed: number;
628
+ /** Formatted elapsed time (MM:SS) */
629
+ elapsedFormatted: string;
630
+ /** Remaining time if maxDuration set (null if no limit) */
631
+ remaining: number | null;
632
+ /** Whether timer is currently running */
633
+ isRunning: boolean;
634
+ /** Start the timer */
635
+ start: () => void;
636
+ /** Stop the timer */
637
+ stop: () => void;
638
+ /** Pause the timer (keeps elapsed time) */
639
+ pause: () => void;
640
+ /** Resume a paused timer */
641
+ resume: () => void;
642
+ /** Reset the timer to zero */
643
+ reset: () => void;
644
+ }
645
+ /**
646
+ * Format seconds as MM:SS
647
+ */
648
+ declare function formatTime(seconds: number): string;
649
+ /**
650
+ * Hook to manage a timer for recording duration
651
+ *
652
+ * @param options - Configuration options
653
+ * @returns Timer state and controls
654
+ *
655
+ * @example
656
+ * ```tsx
657
+ * function RecordingTimer() {
658
+ * const { elapsed, elapsedFormatted, remaining, start, stop, reset, isRunning } = useTimer({
659
+ * maxDuration: 60,
660
+ * onTick: (elapsed) => console.log(`Recording: ${elapsed}s`),
661
+ * onComplete: () => console.log('Max duration reached'),
662
+ * });
663
+ *
664
+ * return (
665
+ * <div>
666
+ * <p>{elapsedFormatted}</p>
667
+ * <button onClick={isRunning ? stop : start}>
668
+ * {isRunning ? 'Stop' : 'Start'}
669
+ * </button>
670
+ * </div>
671
+ * );
672
+ * }
673
+ * ```
674
+ */
675
+ declare function useTimer(options?: UseTimerOptions): UseTimerReturn;
676
+
677
+ interface UseCountdownOptions {
678
+ /**
679
+ * Initial countdown value in seconds
680
+ * @default 3
681
+ */
682
+ initialValue?: number;
683
+ /**
684
+ * Called when countdown completes
685
+ */
686
+ onComplete?: () => void;
687
+ /**
688
+ * Called on each tick of the countdown
689
+ */
690
+ onTick?: (value: number) => void;
691
+ }
692
+ interface UseCountdownReturn {
693
+ /** Current countdown value (null when not counting) */
694
+ value: number | null;
695
+ /** Whether countdown is active */
696
+ isActive: boolean;
697
+ /** Start the countdown */
698
+ start: (onComplete?: () => void) => void;
699
+ /** Cancel the countdown */
700
+ cancel: () => void;
701
+ }
702
+ /**
703
+ * Hook to manage a countdown timer (e.g., 3-2-1 before recording)
704
+ *
705
+ * @param options - Configuration options
706
+ * @returns Countdown state and controls
707
+ *
708
+ * @example
709
+ * ```tsx
710
+ * function RecordingCountdown() {
711
+ * const { value, isActive, start, cancel } = useCountdown({
712
+ * initialValue: 3,
713
+ * onComplete: () => console.log('Countdown finished!'),
714
+ * });
715
+ *
716
+ * return (
717
+ * <div>
718
+ * {isActive && <div className="countdown">{value}</div>}
719
+ * <button onClick={() => start()}>Start Countdown</button>
720
+ * <button onClick={cancel}>Cancel</button>
721
+ * </div>
722
+ * );
723
+ * }
724
+ * ```
725
+ */
726
+ declare function useCountdown(options?: UseCountdownOptions): UseCountdownReturn;
727
+
728
+ /**
729
+ * Utility function to merge Tailwind CSS classes with clsx
730
+ *
731
+ * Combines clsx's conditional class merging with tailwind-merge's
732
+ * intelligent Tailwind class deduplication.
733
+ *
734
+ * @param inputs - Class values to merge
735
+ * @returns Merged class string
736
+ *
737
+ * @example
738
+ * ```ts
739
+ * cn('px-2 py-1', 'px-4') // => 'py-1 px-4'
740
+ * cn('bg-red-500', isActive && 'bg-blue-500') // => 'bg-blue-500' (if isActive)
741
+ * ```
742
+ */
743
+ declare function cn(...inputs: ClassValue[]): string;
744
+
745
+ /**
746
+ * Download a blob as a file
747
+ *
748
+ * @param blob - The blob to download
749
+ * @param filename - The filename to save as
750
+ *
751
+ * @example
752
+ * ```ts
753
+ * const blob = new Blob(['hello'], { type: 'text/plain' });
754
+ * downloadBlob(blob, 'hello.txt');
755
+ * ```
756
+ */
757
+ declare function downloadBlob(blob: Blob, filename: string): void;
758
+ /**
759
+ * Generate a default filename for recordings
760
+ *
761
+ * @param timestamp - The recording timestamp
762
+ * @param extension - The file extension (default: 'webm')
763
+ * @returns Generated filename
764
+ *
765
+ * @example
766
+ * ```ts
767
+ * generateFilename(new Date()) // => 'screen-recording-2024-01-15-143052.webm'
768
+ * ```
769
+ */
770
+ declare function generateFilename(timestamp?: Date, extension?: string): string;
771
+ /**
772
+ * Format bytes to human-readable size
773
+ *
774
+ * @param bytes - Size in bytes
775
+ * @param decimals - Number of decimal places
776
+ * @returns Formatted size string
777
+ *
778
+ * @example
779
+ * ```ts
780
+ * formatBytes(1024) // => '1 KB'
781
+ * formatBytes(1048576) // => '1 MB'
782
+ * formatBytes(1536, 1) // => '1.5 KB'
783
+ * ```
784
+ */
785
+ declare function formatBytes(bytes: number, decimals?: number): string;
786
+
787
+ interface TriggerProps {
788
+ /**
789
+ * Position of the trigger button
790
+ */
791
+ position?: TriggerPosition;
792
+ /**
793
+ * Custom content for the trigger button
794
+ */
795
+ children?: ReactNode;
796
+ /**
797
+ * Click handler
798
+ */
799
+ onClick?: () => void;
800
+ /**
801
+ * Whether the trigger is disabled
802
+ */
803
+ disabled?: boolean;
804
+ /**
805
+ * Custom class name
806
+ */
807
+ className?: string;
808
+ /**
809
+ * Z-index for positioning
810
+ */
811
+ zIndex?: number;
812
+ }
813
+ /**
814
+ * Floating trigger button to start screen recording
815
+ */
816
+ declare const Trigger: FC<TriggerProps>;
817
+
818
+ interface TriggerIconProps {
819
+ className?: string;
820
+ }
821
+ /**
822
+ * Recording trigger icon (video camera with record dot)
823
+ */
824
+ declare const TriggerIcon: FC<TriggerIconProps>;
825
+ /**
826
+ * Recording state icon (pulsing red dot)
827
+ */
828
+ declare const RecordingIcon: FC<TriggerIconProps>;
829
+ /**
830
+ * Stop icon (square)
831
+ */
832
+ declare const StopIcon: FC<TriggerIconProps>;
833
+ /**
834
+ * Pause icon (two vertical bars)
835
+ */
836
+ declare const PauseIcon: FC<TriggerIconProps>;
837
+ /**
838
+ * Play/Resume icon (triangle)
839
+ */
840
+ declare const PlayIcon: FC<TriggerIconProps>;
841
+ /**
842
+ * Download icon
843
+ */
844
+ declare const DownloadIcon: FC<TriggerIconProps>;
845
+ /**
846
+ * Close/X icon
847
+ */
848
+ declare const CloseIcon: FC<TriggerIconProps>;
849
+ /**
850
+ * Refresh/Re-record icon
851
+ */
852
+ declare const RefreshIcon: FC<TriggerIconProps>;
853
+ /**
854
+ * Check/Done icon
855
+ */
856
+ declare const CheckIcon: FC<TriggerIconProps>;
857
+ /**
858
+ * Warning/Error icon
859
+ */
860
+ declare const WarningIcon: FC<TriggerIconProps>;
861
+
862
+ interface TimerProps {
863
+ /**
864
+ * Elapsed time in seconds
865
+ */
866
+ elapsed: number;
867
+ /**
868
+ * Formatted elapsed time (MM:SS)
869
+ */
870
+ elapsedFormatted: string;
871
+ /**
872
+ * Remaining time (null if no limit)
873
+ */
874
+ remaining?: number | null;
875
+ /**
876
+ * Maximum duration (for calculating remaining)
877
+ */
878
+ maxDuration?: number;
879
+ /**
880
+ * Whether recording is paused
881
+ */
882
+ isPaused?: boolean;
883
+ /**
884
+ * Custom class name
885
+ */
886
+ className?: string;
887
+ }
888
+ /**
889
+ * Timer display component showing elapsed recording time
890
+ */
891
+ declare const Timer: FC<TimerProps>;
892
+
893
+ interface RecordingControlsProps {
894
+ /**
895
+ * Elapsed time in seconds
896
+ */
897
+ elapsed: number;
898
+ /**
899
+ * Formatted elapsed time (MM:SS)
900
+ */
901
+ elapsedFormatted: string;
902
+ /**
903
+ * Remaining time if maxDuration set
904
+ */
905
+ remaining?: number | null;
906
+ /**
907
+ * Maximum duration
908
+ */
909
+ maxDuration?: number;
910
+ /**
911
+ * Whether recording is paused
912
+ */
913
+ isPaused?: boolean;
914
+ /**
915
+ * Whether recording is active (not paused)
916
+ */
917
+ isRecording?: boolean;
918
+ /**
919
+ * Pause handler
920
+ */
921
+ onPause?: () => void;
922
+ /**
923
+ * Resume handler
924
+ */
925
+ onResume?: () => void;
926
+ /**
927
+ * Stop handler
928
+ */
929
+ onStop?: () => void;
930
+ /**
931
+ * Position of controls
932
+ */
933
+ position?: TriggerPosition;
934
+ /**
935
+ * Custom class name
936
+ */
937
+ className?: string;
938
+ /**
939
+ * Z-index for positioning
940
+ */
941
+ zIndex?: number;
942
+ }
943
+ /**
944
+ * Recording controls bar with timer, pause, and stop buttons
945
+ */
946
+ declare const RecordingControls: FC<RecordingControlsProps>;
947
+
948
+ interface CountdownProps {
949
+ /**
950
+ * Current countdown value
951
+ */
952
+ value: number;
953
+ /**
954
+ * Cancel handler
955
+ */
956
+ onCancel?: () => void;
957
+ /**
958
+ * Custom class name
959
+ */
960
+ className?: string;
961
+ /**
962
+ * Z-index for overlay
963
+ */
964
+ zIndex?: number;
965
+ }
966
+ /**
967
+ * Fullscreen countdown overlay (3-2-1 before recording)
968
+ */
969
+ declare const Countdown: FC<CountdownProps>;
970
+
971
+ interface VideoPlayerProps {
972
+ /**
973
+ * Video source URL
974
+ */
975
+ src: string;
976
+ /**
977
+ * Custom class name
978
+ */
979
+ className?: string;
980
+ /**
981
+ * Whether to show controls
982
+ */
983
+ showControls?: boolean;
984
+ /**
985
+ * Auto-play on mount
986
+ */
987
+ autoPlay?: boolean;
988
+ /**
989
+ * Known duration in seconds (fallback for WebM files where duration may be Infinity)
990
+ */
991
+ knownDuration?: number;
992
+ }
993
+ interface VideoPlayerRef {
994
+ play: () => void;
995
+ pause: () => void;
996
+ togglePlay: () => void;
997
+ }
998
+ /**
999
+ * Video player component with custom controls
1000
+ */
1001
+ declare const VideoPlayer: react.ForwardRefExoticComponent<VideoPlayerProps & react.RefAttributes<VideoPlayerRef>>;
1002
+
1003
+ interface PreviewModalProps {
1004
+ /**
1005
+ * Recording result to preview
1006
+ */
1007
+ result: RecordingResult;
1008
+ /**
1009
+ * Whether the modal is open
1010
+ */
1011
+ isOpen: boolean;
1012
+ /**
1013
+ * Download handler
1014
+ */
1015
+ onDownload?: () => void;
1016
+ /**
1017
+ * Re-record handler
1018
+ */
1019
+ onReRecord?: () => void;
1020
+ /**
1021
+ * Done/Close handler
1022
+ */
1023
+ onClose?: () => void;
1024
+ /**
1025
+ * Custom class name
1026
+ */
1027
+ className?: string;
1028
+ /**
1029
+ * Z-index for modal
1030
+ */
1031
+ zIndex?: number;
1032
+ /**
1033
+ * Use portal for rendering
1034
+ */
1035
+ usePortal?: boolean;
1036
+ /**
1037
+ * Theme for the modal
1038
+ */
1039
+ theme?: ThemeOption;
1040
+ }
1041
+ /**
1042
+ * Preview modal for recorded video
1043
+ */
1044
+ declare const PreviewModal: FC<PreviewModalProps>;
1045
+
1046
+ interface StatusBadgeProps {
1047
+ /**
1048
+ * Current recording state
1049
+ */
1050
+ state: RecordingState$1;
1051
+ /**
1052
+ * Custom class name
1053
+ */
1054
+ className?: string;
1055
+ }
1056
+ /**
1057
+ * Status badge showing current recording state
1058
+ */
1059
+ declare const StatusBadge: FC<StatusBadgeProps>;
1060
+
1061
+ interface ErrorMessageProps {
1062
+ /**
1063
+ * Error to display
1064
+ */
1065
+ error: ScreenRecorderError;
1066
+ /**
1067
+ * Retry handler
1068
+ */
1069
+ onRetry?: () => void;
1070
+ /**
1071
+ * Dismiss handler
1072
+ */
1073
+ onDismiss?: () => void;
1074
+ /**
1075
+ * Position of the error message
1076
+ */
1077
+ position?: TriggerPosition;
1078
+ /**
1079
+ * Custom class name
1080
+ */
1081
+ className?: string;
1082
+ /**
1083
+ * Z-index for positioning
1084
+ */
1085
+ zIndex?: number;
1086
+ }
1087
+ /**
1088
+ * Error message component for screen recording errors
1089
+ */
1090
+ declare const ErrorMessage: FC<ErrorMessageProps>;
1091
+
1092
+ export { type AudioConfig, type AudioOption, type BrowserSupport, CheckIcon, CloseIcon, Countdown, type CountdownProps, DEFAULT_OPTIONS, DownloadIcon, ERROR_MESSAGES, ErrorMessage, type ErrorMessageProps, PauseIcon, PlayIcon, PreviewModal, type PreviewModalProps, QUALITY_PRESETS, type QualityOption, type QualityPreset, RecordingControls, type RecordingControlsProps, RecordingIcon, type RecordingResult, type RecordingState$1 as RecordingState, RefreshIcon, type RenderMode, SUPPORTED_MIME_TYPES, ScreenRecorder, type ScreenRecorderError, type ScreenRecorderErrorCode, type ScreenRecorderProps, StatusBadge, type StatusBadgeProps, StopIcon, TIMER_CRITICAL_THRESHOLD, TIMER_WARNING_THRESHOLD, type ThemeOption, Timer, type TimerProps, Trigger, TriggerIcon, type TriggerPosition, type TriggerProps, type UseCountdownOptions, type UseCountdownReturn, type UseDisplayMediaOptions, type UseDisplayMediaReturn, type UseMediaRecorderOptions, type UseMediaRecorderReturn, type UseScreenRecorderOptions, type UseScreenRecorderReturn, type UseTimerOptions, type UseTimerReturn, VideoPlayer, type VideoPlayerProps, type VideoPlayerRef, WarningIcon, checkBrowserSupport, cn, downloadBlob, formatBytes, formatTime, generateFilename, getBestMimeType, useBrowserSupport, useCountdown, useDisplayMedia, useMediaRecorder, useScreenRecorder, useTimer };