@ttt-productions/file-input 0.11.18 → 0.11.20
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-dialog.d.ts","sourceRoot":"","sources":["../../../src/react/components/record-dialog.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"record-dialog.d.ts","sourceRoot":"","sources":["../../../src/react/components/record-dialog.tsx"],"names":[],"mappings":"AAiFA,wBAAgB,eAAe,CAAC,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAGpE;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC;IAC1C,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,CACV,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,OAAO,GAAG,OAAO,KAC5B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AASD,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,oBAAoB,EACpB,QAAgB,EAChB,SAAiB,EACjB,UAAU,EACV,cAAc,GACf,EAAE,iBAAiB,+BAijBnB"}
|
|
@@ -72,7 +72,7 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
72
72
|
const [elapsedMs, setElapsedMs] = useState(0);
|
|
73
73
|
const [discardConfirmOpen, setDiscardConfirmOpen] = useState(false);
|
|
74
74
|
const [error, setError] = useState(null);
|
|
75
|
-
const [
|
|
75
|
+
const [streamStatus, setStreamStatus] = useState('acquiring');
|
|
76
76
|
const mediaRecorderRef = useRef(null);
|
|
77
77
|
const recordStreamRef = useRef(null);
|
|
78
78
|
const liveStreamKindRef = useRef(null);
|
|
@@ -205,6 +205,7 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
205
205
|
recordStreamRef.current = stream;
|
|
206
206
|
liveStreamKindRef.current = kind;
|
|
207
207
|
setError(null);
|
|
208
|
+
setStreamStatus('ready');
|
|
208
209
|
if (wantVideo && livePreviewRef.current) {
|
|
209
210
|
livePreviewRef.current.srcObject = stream;
|
|
210
211
|
livePreviewRef.current.muted = true;
|
|
@@ -223,16 +224,15 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
223
224
|
const startLivePreview = useCallback(async (kind) => {
|
|
224
225
|
teardownCapture();
|
|
225
226
|
const req = ++previewReqRef.current;
|
|
226
|
-
|
|
227
|
+
setStreamStatus('acquiring');
|
|
227
228
|
try {
|
|
228
229
|
await acquireStream(kind);
|
|
229
230
|
}
|
|
230
231
|
catch (e) {
|
|
232
|
+
if (req !== previewReqRef.current)
|
|
233
|
+
return;
|
|
231
234
|
setError(e instanceof Error ? e.message : 'Camera/microphone access denied.');
|
|
232
|
-
|
|
233
|
-
finally {
|
|
234
|
-
if (req === previewReqRef.current)
|
|
235
|
-
setAcquiring(false);
|
|
235
|
+
setStreamStatus('failed');
|
|
236
236
|
}
|
|
237
237
|
}, [teardownCapture, acquireStream]);
|
|
238
238
|
useEffect(() => {
|
|
@@ -242,6 +242,7 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
242
242
|
return () => {
|
|
243
243
|
teardownCapture();
|
|
244
244
|
revokeLastRecordUrl();
|
|
245
|
+
setStreamStatus('acquiring');
|
|
245
246
|
};
|
|
246
247
|
}, [open, initialKind, startLivePreview, teardownCapture, revokeLastRecordUrl]);
|
|
247
248
|
const startRecording = useCallback(async (kind) => {
|
|
@@ -260,8 +261,9 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
260
261
|
return;
|
|
261
262
|
}
|
|
262
263
|
}
|
|
263
|
-
if (!stream)
|
|
264
|
+
if (!stream) {
|
|
264
265
|
return;
|
|
266
|
+
}
|
|
265
267
|
const preferredMimeType = pickRecorderMimeType(kind);
|
|
266
268
|
let mr;
|
|
267
269
|
try {
|
|
@@ -371,6 +373,7 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
371
373
|
const isRecording = recorderState === 'recording';
|
|
372
374
|
const isPreview = recorderState === 'preview';
|
|
373
375
|
const isIdle = recorderState === 'idle';
|
|
376
|
+
const acquiring = streamStatus === 'acquiring';
|
|
374
377
|
const elapsedLabel = maxRecordDurationSec
|
|
375
378
|
? `${formatDuration(elapsedMs)} / ${formatDuration(maxRecordDurationSec * 1000)}`
|
|
376
379
|
: formatDuration(elapsedMs);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-dialog.js","sourceRoot":"","sources":["../../../src/react/components/record-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { Camera, Video, Mic, X, RotateCcw, Check } from 'lucide-react';\nimport { startWaveformLoop } from '@ttt-productions/media-viewer';\nimport { MediaPreview } from '@ttt-productions/media-viewer/react';\nimport { ensureFileWithContentType } from '../../lib/infer-content-type.js';\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n Spinner,\n useAsyncAction,\n} from '@ttt-productions/ui-core/react';\nimport { releaseObjectUrl } from '../../lib/validate-media-duration.js';\n\ntype RecorderState = 'idle' | 'recording' | 'preview';\n\n// An explicit supported container keeps mr.mimeType deterministic; Safari has no webm and falls\n// through to mp4.\nconst RECORDER_MIME_CANDIDATES: Record<'audio' | 'video', readonly string[]> = {\n audio: ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4'],\n video: ['video/webm;codecs=vp9,opus', 'video/webm', 'video/mp4'],\n};\n\nfunction stopIfRecording(recorder: MediaRecorder | null): void {\n if (!recorder || recorder.state !== 'recording') return;\n try {\n recorder.stop();\n } catch {\n // A recorder that stopped between the state check and this call throws; it has stopped.\n }\n}\n\nfunction pickRecorderMimeType(kind: 'audio' | 'video'): string | undefined {\n if (typeof MediaRecorder === 'undefined' || typeof MediaRecorder.isTypeSupported !== 'function') {\n return undefined;\n }\n for (const candidate of RECORDER_MIME_CANDIDATES[kind]) {\n try {\n if (MediaRecorder.isTypeSupported(candidate)) return candidate;\n } catch {\n // A browser that cannot parse a candidate throws; it counts as unsupported.\n }\n }\n return undefined;\n}\n\n// mp4 is not in the ambiguous-extension map, so an audio recording is named .m4a whatever\n// container type the recorder reports.\nfunction recordingFileName(mime: string, kind: 'audio' | 'video'): string {\n const base = mime.split(';')[0].trim().toLowerCase();\n const ext =\n base === 'audio/mp4' || base === 'video/mp4'\n ? kind === 'audio'\n ? 'm4a'\n : 'mp4'\n : base === 'audio/ogg' || base === 'video/ogg'\n ? 'ogg'\n : 'webm';\n return `recording.${ext}`;\n}\n\n// A MediaRecorder blob runs a little longer than start() to stop() (muxer flush, frame-end\n// duration, late timers) and both duration gates are exact, so recording stops before the cap.\nconst AUTO_STOP_SAFETY_MARGIN_MS = 750;\n\nexport function autoStopDelayMs(maxRecordDurationSec: number): number {\n const capMs = Math.floor(maxRecordDurationSec * 1000);\n return Math.max(capMs - AUTO_STOP_SAFETY_MARGIN_MS, Math.ceil(capMs / 2));\n}\n\ninterface RecordDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n initialKind: 'audio' | 'video';\n canPhoto: boolean;\n canRecVideo: boolean;\n canRecAudio: boolean;\n cameraFacingMode?: 'user' | 'environment';\n maxRecordDurationSec?: number;\n disabled?: boolean;\n isLoading?: boolean;\n onRecorded: (\n file: File,\n previewUrl: string,\n recordedKind: 'audio' | 'video',\n ) => void | Promise<void>;\n onRequestPhoto?: () => void;\n}\n\nfunction formatDuration(ms: number): string {\n const totalSec = Math.max(0, Math.floor(ms / 1000));\n const m = Math.floor(totalSec / 60);\n const s = totalSec % 60;\n return `${m}:${s.toString().padStart(2, '0')}`;\n}\n\nexport function RecordDialog({\n open,\n onOpenChange,\n initialKind,\n canPhoto,\n canRecVideo,\n canRecAudio,\n cameraFacingMode,\n maxRecordDurationSec,\n disabled = false,\n isLoading = false,\n onRecorded,\n onRequestPhoto,\n}: RecordDialogProps) {\n const [recordKind, setRecordKind] = useState<'audio' | 'video'>(initialKind);\n const [recorderState, setRecorderState] = useState<RecorderState>('idle');\n const [recordPreviewUrl, setRecordPreviewUrl] = useState<string | null>(null);\n const [recordedFile, setRecordedFile] = useState<File | null>(null);\n const [elapsedMs, setElapsedMs] = useState(0);\n const [discardConfirmOpen, setDiscardConfirmOpen] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [acquiring, setAcquiring] = useState(false);\n\n const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n const recordStreamRef = useRef<MediaStream | null>(null);\n const liveStreamKindRef = useRef<'audio' | 'video' | null>(null);\n const recordChunksRef = useRef<BlobPart[]>([]);\n const lastRecordUrlRef = useRef<string | null>(null);\n const livePreviewRef = useRef<HTMLVideoElement | null>(null);\n const autoStopTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // An acquisition that resolves after a teardown belongs to nobody and is never attached.\n const acquireGenRef = useRef(0);\n const previewReqRef = useRef(0);\n\n const startedAtRef = useRef<number>(0);\n const elapsedIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const audioContextRef = useRef<AudioContext | null>(null);\n const audioAnalyserRef = useRef<AnalyserNode | null>(null);\n const audioSourceRef = useRef<MediaStreamAudioSourceNode | null>(null);\n const waveformStopRef = useRef<(() => void) | null>(null);\n const waveformCanvasRef = useRef<HTMLCanvasElement | null>(null);\n\n useEffect(() => {\n if (open) {\n setRecordKind(initialKind);\n setRecorderState('idle');\n setRecordedFile(null);\n setRecordPreviewUrl(null);\n setElapsedMs(0);\n setDiscardConfirmOpen(false);\n setError(null);\n }\n }, [open, initialKind]);\n\n const revokeLastRecordUrl = useCallback(() => {\n if (!lastRecordUrlRef.current) return;\n releaseObjectUrl(lastRecordUrlRef.current);\n lastRecordUrlRef.current = null;\n }, []);\n\n const makeRecordUrl = useCallback(\n (blob: Blob) => {\n revokeLastRecordUrl();\n const url = URL.createObjectURL(blob);\n lastRecordUrlRef.current = url;\n return url;\n },\n [revokeLastRecordUrl],\n );\n\n const stopElapsedTimer = useCallback(() => {\n if (elapsedIntervalRef.current) {\n clearInterval(elapsedIntervalRef.current);\n elapsedIntervalRef.current = null;\n }\n }, []);\n\n const startElapsedTimer = useCallback(() => {\n stopElapsedTimer();\n startedAtRef.current = Date.now();\n setElapsedMs(0);\n elapsedIntervalRef.current = setInterval(() => {\n setElapsedMs(Date.now() - startedAtRef.current);\n }, 100);\n }, [stopElapsedTimer]);\n\n const stopWaveform = useCallback(() => {\n waveformStopRef.current?.();\n waveformStopRef.current = null;\n try {\n audioSourceRef.current?.disconnect();\n } catch {\n // A node that is already disconnected throws, and there is nothing left to tear down.\n }\n audioSourceRef.current = null;\n audioAnalyserRef.current = null;\n const ctx = audioContextRef.current;\n audioContextRef.current = null;\n if (ctx && ctx.state !== 'closed') {\n // Closing a context the browser is already closing rejects; the context is released either way.\n ctx.close().catch(() => {});\n }\n }, []);\n\n const startWaveform = useCallback(\n (stream: MediaStream) => {\n stopWaveform();\n try {\n // lib.dom does not declare Safari's prefixed webkitAudioContext.\n const prefixed = window as Window & { webkitAudioContext?: typeof AudioContext };\n const AudioCtx: typeof AudioContext | undefined =\n window.AudioContext || prefixed.webkitAudioContext;\n if (!AudioCtx) return;\n\n const ctx = new AudioCtx();\n const source = ctx.createMediaStreamSource(stream);\n const analyser = ctx.createAnalyser();\n analyser.fftSize = 256;\n source.connect(analyser);\n\n audioContextRef.current = ctx;\n audioSourceRef.current = source;\n audioAnalyserRef.current = analyser;\n\n waveformStopRef.current = startWaveformLoop({\n analyser,\n getCanvas: () => waveformCanvasRef.current,\n mode: 'line',\n });\n } catch {\n // The waveform is decoration: a browser that refuses an audio graph records without one.\n }\n },\n [stopWaveform],\n );\n\n const teardownCapture = useCallback(() => {\n acquireGenRef.current += 1;\n if (autoStopTimeoutRef.current) {\n clearTimeout(autoStopTimeoutRef.current);\n autoStopTimeoutRef.current = null;\n }\n mediaRecorderRef.current = null;\n\n const s = recordStreamRef.current;\n recordStreamRef.current = null;\n liveStreamKindRef.current = null;\n if (s) s.getTracks().forEach((t) => t.stop());\n\n if (livePreviewRef.current) {\n livePreviewRef.current.srcObject = null;\n }\n\n recordChunksRef.current = [];\n\n stopElapsedTimer();\n stopWaveform();\n }, [stopElapsedTimer, stopWaveform]);\n\n const acquireStream = useCallback(\n async (kind: 'audio' | 'video') => {\n const wantVideo = kind === 'video';\n const facingMode = cameraFacingMode ?? 'user';\n const gen = acquireGenRef.current;\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: wantVideo ? { facingMode } : false,\n });\n if (gen !== acquireGenRef.current) {\n stream.getTracks().forEach((t) => t.stop());\n return null;\n }\n recordStreamRef.current = stream;\n liveStreamKindRef.current = kind;\n setError(null);\n if (wantVideo && livePreviewRef.current) {\n livePreviewRef.current.srcObject = stream;\n livePreviewRef.current.muted = true;\n try {\n await livePreviewRef.current.play();\n } catch {\n // A browser that blocks preview playback rejects; recording does not depend on it.\n }\n } else if (!wantVideo) {\n startWaveform(stream);\n }\n return stream;\n },\n [cameraFacingMode, startWaveform],\n );\n\n const startLivePreview = useCallback(\n async (kind: 'audio' | 'video') => {\n teardownCapture();\n const req = ++previewReqRef.current;\n setAcquiring(true);\n try {\n await acquireStream(kind);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Camera/microphone access denied.');\n } finally {\n if (req === previewReqRef.current) setAcquiring(false);\n }\n },\n [teardownCapture, acquireStream],\n );\n\n useEffect(() => {\n if (open) {\n void startLivePreview(initialKind);\n }\n return () => {\n teardownCapture();\n revokeLastRecordUrl();\n };\n }, [open, initialKind, startLivePreview, teardownCapture, revokeLastRecordUrl]);\n\n const startRecording = useCallback(\n async (kind: 'audio' | 'video') => {\n revokeLastRecordUrl();\n setRecordPreviewUrl(null);\n setRecordedFile(null);\n setRecordKind(kind);\n\n const wantVideo = kind === 'video';\n\n let stream = recordStreamRef.current;\n if (!stream || liveStreamKindRef.current !== kind) {\n try {\n stream = await acquireStream(kind);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Camera/microphone access denied.');\n return;\n }\n }\n if (!stream) return;\n\n const preferredMimeType = pickRecorderMimeType(kind);\n let mr: MediaRecorder;\n try {\n mr = preferredMimeType\n ? new MediaRecorder(stream, { mimeType: preferredMimeType })\n : new MediaRecorder(stream);\n } catch {\n mr = new MediaRecorder(stream);\n }\n mediaRecorderRef.current = mr;\n recordChunksRef.current = [];\n\n mr.ondataavailable = (e) => {\n if (e.data && e.data.size > 0) recordChunksRef.current.push(e.data);\n };\n\n mr.onstop = () => {\n const blob = new Blob(recordChunksRef.current, {\n type: mr.mimeType || preferredMimeType || (wantVideo ? 'video/webm' : 'audio/webm'),\n });\n\n const rawFile = new File([blob], recordingFileName(blob.type, kind), { type: blob.type });\n const file = ensureFileWithContentType(rawFile, wantVideo ? 'video' : 'audio');\n const url = makeRecordUrl(file);\n\n setRecordedFile(file);\n setRecordPreviewUrl(url);\n setRecorderState('preview');\n\n teardownCapture();\n };\n\n mr.start();\n setRecorderState('recording');\n startElapsedTimer();\n\n if (maxRecordDurationSec && maxRecordDurationSec > 0) {\n autoStopTimeoutRef.current = setTimeout(\n () => stopIfRecording(mediaRecorderRef.current),\n autoStopDelayMs(maxRecordDurationSec),\n );\n }\n },\n [\n acquireStream,\n maxRecordDurationSec,\n makeRecordUrl,\n revokeLastRecordUrl,\n teardownCapture,\n startElapsedTimer,\n ],\n );\n\n const stopRecording = useCallback(() => {\n const mr = mediaRecorderRef.current;\n if (mr && mr.state === 'recording') {\n try {\n mr.stop();\n } catch {\n teardownCapture();\n setRecorderState('idle');\n }\n } else {\n teardownCapture();\n setRecorderState('idle');\n }\n }, [teardownCapture]);\n\n const handleReRecord = useCallback(() => {\n revokeLastRecordUrl();\n setRecordPreviewUrl(null);\n setRecordedFile(null);\n setElapsedMs(0);\n setError(null);\n setRecorderState('idle');\n void startLivePreview(recordKind);\n }, [revokeLastRecordUrl, startLivePreview, recordKind]);\n\n const handleSelectKind = useCallback(\n (kind: 'audio' | 'video') => {\n setRecordKind(kind);\n setError(null);\n void startLivePreview(kind);\n },\n [startLivePreview],\n );\n\n const reportError = useCallback((e: unknown) => {\n setError(e instanceof Error ? e.message : 'Something went wrong with the recording.');\n }, []);\n\n const start = useAsyncAction((kind: 'audio' | 'video') => startRecording(kind), {\n onError: reportError,\n });\n\n const save = useAsyncAction(\n async () => {\n if (!recordedFile || !recordPreviewUrl) return;\n await onRecorded(recordedFile, recordPreviewUrl, recordKind);\n onOpenChange(false);\n },\n { onError: reportError },\n );\n\n const closeDialogImmediately = useCallback(() => {\n stopIfRecording(mediaRecorderRef.current);\n teardownCapture();\n revokeLastRecordUrl();\n setRecordedFile(null);\n setRecordPreviewUrl(null);\n setElapsedMs(0);\n setRecorderState('idle');\n setDiscardConfirmOpen(false);\n onOpenChange(false);\n }, [teardownCapture, revokeLastRecordUrl, onOpenChange]);\n\n const handleDialogOpenChange = useCallback(\n (v: boolean) => {\n if (v) return;\n if (save.pending) return;\n if (recorderState === 'preview') {\n setDiscardConfirmOpen(true);\n return;\n }\n closeDialogImmediately();\n },\n [recorderState, save.pending, closeDialogImmediately],\n );\n\n const isRecording = recorderState === 'recording';\n const isPreview = recorderState === 'preview';\n const isIdle = recorderState === 'idle';\n\n const elapsedLabel = maxRecordDurationSec\n ? `${formatDuration(elapsedMs)} / ${formatDuration(maxRecordDurationSec * 1000)}`\n : formatDuration(elapsedMs);\n\n return (\n <>\n <Dialog open={open} onOpenChange={handleDialogOpenChange}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>Record</DialogTitle>\n <DialogDescription>\n {isIdle && 'Choose audio or video, then press Start.'}\n {isRecording && 'Recording…'}\n {isPreview && 'Review your recording. Save to upload, or Re-record to try again.'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"flex flex-wrap gap-2\">\n {canRecVideo && (\n <Button\n type=\"button\"\n variant={recordKind === 'video' ? 'default' : 'secondary'}\n aria-pressed={recordKind === 'video'}\n onClick={() => handleSelectKind('video')}\n disabled={!isIdle || acquiring}\n icon={<Video className=\"icon-xs\" />}\n >\n Video\n </Button>\n )}\n {canRecAudio && (\n <Button\n type=\"button\"\n variant={recordKind === 'audio' ? 'default' : 'secondary'}\n aria-pressed={recordKind === 'audio'}\n onClick={() => handleSelectKind('audio')}\n disabled={!isIdle || acquiring}\n icon={<Mic className=\"icon-xs\" />}\n >\n Audio\n </Button>\n )}\n {canPhoto && onRequestPhoto && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={() => {\n onRequestPhoto();\n onOpenChange(false);\n }}\n disabled={!isIdle}\n icon={<Camera className=\"icon-xs\" />}\n >\n Photo\n </Button>\n )}\n </div>\n\n {recordKind === 'video' && !isPreview && (\n <div className=\"relative mt-2 rounded-md overflow-hidden bg-[hsl(var(--neutral-black))] aspect-video\">\n <video\n ref={livePreviewRef}\n className=\"w-full h-full object-contain\"\n playsInline\n muted\n />\n {acquiring && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <Spinner size=\"lg\" label=\"Starting camera\" />\n </div>\n )}\n </div>\n )}\n\n {recordKind === 'audio' && !isPreview && (\n <div className=\"relative mt-2 rounded-md bg-muted p-2 text-primary\">\n <canvas\n ref={waveformCanvasRef}\n width={480}\n height={80}\n className=\"w-full h-20\"\n aria-label=\"Audio waveform\"\n />\n {acquiring && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <Spinner size=\"lg\" label=\"Starting microphone\" />\n </div>\n )}\n </div>\n )}\n\n {error && (\n <p className=\"text-sm text-destructive\" role=\"alert\">\n {error}\n </p>\n )}\n\n {isRecording && (\n <div className=\"flex items-center gap-2 text-sm\" aria-live=\"polite\">\n <span\n className=\"inline-block h-2.5 w-2.5 rounded-full bg-[color:var(--recording-indicator)] animate-pulse\"\n aria-label=\"Recording\"\n />\n <span className=\"tabular-nums\">{elapsedLabel}</span>\n </div>\n )}\n\n {isPreview && recordPreviewUrl && (\n <div className=\"mt-2\">\n <MediaPreview\n url={recordPreviewUrl}\n type={recordKind}\n controls\n priority\n className=\"w-full rounded-md\"\n />\n </div>\n )}\n\n <DialogFooter className=\"flex-row justify-between gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleDialogOpenChange(false)}\n disabled={save.pending}\n icon={<X className=\"icon-xs\" />}\n >\n Close\n </Button>\n\n {isIdle && (\n <Button\n type=\"button\"\n variant=\"default\"\n onClick={() => void start.run(recordKind)}\n disabled={disabled || isLoading || acquiring}\n pending={start.pending}\n icon={\n recordKind === 'video' ? (\n <Video className=\"icon-xs\" />\n ) : (\n <Mic className=\"icon-xs\" />\n )\n }\n >\n Start\n </Button>\n )}\n\n {isRecording && (\n <Button type=\"button\" variant=\"default\" onClick={stopRecording}>\n Stop\n </Button>\n )}\n\n {isPreview && (\n <div className=\"flex gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={handleReRecord}\n disabled={save.pending}\n icon={<RotateCcw className=\"icon-xs\" />}\n >\n Re-record\n </Button>\n <Button\n type=\"button\"\n variant=\"default\"\n onClick={() => void save.run()}\n disabled={disabled || isLoading}\n pending={save.pending}\n icon={<Check className=\"icon-xs\" />}\n >\n Save\n </Button>\n </div>\n )}\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n <AlertDialog open={discardConfirmOpen} onOpenChange={setDiscardConfirmOpen}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Discard recording?</AlertDialogTitle>\n <AlertDialogDescription>\n You have a recorded clip that hasn't been saved. Closing now will discard it.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Keep recording</AlertDialogCancel>\n <AlertDialogAction onClick={closeDialogImmediately}>Discard</AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"record-dialog.js","sourceRoot":"","sources":["../../../src/react/components/record-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { Camera, Video, Mic, X, RotateCcw, Check } from 'lucide-react';\nimport { startWaveformLoop } from '@ttt-productions/media-viewer';\nimport { MediaPreview } from '@ttt-productions/media-viewer/react';\nimport { ensureFileWithContentType } from '../../lib/infer-content-type.js';\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n Spinner,\n useAsyncAction,\n} from '@ttt-productions/ui-core/react';\nimport { releaseObjectUrl } from '../../lib/validate-media-duration.js';\n\ntype RecorderState = 'idle' | 'recording' | 'preview';\n\ntype StreamStatus = 'acquiring' | 'ready' | 'failed';\n\n// An explicit supported container keeps mr.mimeType deterministic; Safari has no webm and falls\n// through to mp4.\nconst RECORDER_MIME_CANDIDATES: Record<'audio' | 'video', readonly string[]> = {\n audio: ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4'],\n video: ['video/webm;codecs=vp9,opus', 'video/webm', 'video/mp4'],\n};\n\nfunction stopIfRecording(recorder: MediaRecorder | null): void {\n if (!recorder || recorder.state !== 'recording') return;\n try {\n recorder.stop();\n } catch {\n // A recorder that stopped between the state check and this call throws; it has stopped.\n }\n}\n\nfunction pickRecorderMimeType(kind: 'audio' | 'video'): string | undefined {\n if (typeof MediaRecorder === 'undefined' || typeof MediaRecorder.isTypeSupported !== 'function') {\n return undefined;\n }\n for (const candidate of RECORDER_MIME_CANDIDATES[kind]) {\n try {\n if (MediaRecorder.isTypeSupported(candidate)) return candidate;\n } catch {\n // A browser that cannot parse a candidate throws; it counts as unsupported.\n }\n }\n return undefined;\n}\n\n// mp4 is not in the ambiguous-extension map, so an audio recording is named .m4a whatever\n// container type the recorder reports.\nfunction recordingFileName(mime: string, kind: 'audio' | 'video'): string {\n const base = mime.split(';')[0].trim().toLowerCase();\n const ext =\n base === 'audio/mp4' || base === 'video/mp4'\n ? kind === 'audio'\n ? 'm4a'\n : 'mp4'\n : base === 'audio/ogg' || base === 'video/ogg'\n ? 'ogg'\n : 'webm';\n return `recording.${ext}`;\n}\n\n// A MediaRecorder blob runs a little longer than start() to stop() (muxer flush, frame-end\n// duration, late timers) and both duration gates are exact, so recording stops before the cap.\nconst AUTO_STOP_SAFETY_MARGIN_MS = 750;\n\nexport function autoStopDelayMs(maxRecordDurationSec: number): number {\n const capMs = Math.floor(maxRecordDurationSec * 1000);\n return Math.max(capMs - AUTO_STOP_SAFETY_MARGIN_MS, Math.ceil(capMs / 2));\n}\n\ninterface RecordDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n initialKind: 'audio' | 'video';\n canPhoto: boolean;\n canRecVideo: boolean;\n canRecAudio: boolean;\n cameraFacingMode?: 'user' | 'environment';\n maxRecordDurationSec?: number;\n disabled?: boolean;\n isLoading?: boolean;\n onRecorded: (\n file: File,\n previewUrl: string,\n recordedKind: 'audio' | 'video',\n ) => void | Promise<void>;\n onRequestPhoto?: () => void;\n}\n\nfunction formatDuration(ms: number): string {\n const totalSec = Math.max(0, Math.floor(ms / 1000));\n const m = Math.floor(totalSec / 60);\n const s = totalSec % 60;\n return `${m}:${s.toString().padStart(2, '0')}`;\n}\n\nexport function RecordDialog({\n open,\n onOpenChange,\n initialKind,\n canPhoto,\n canRecVideo,\n canRecAudio,\n cameraFacingMode,\n maxRecordDurationSec,\n disabled = false,\n isLoading = false,\n onRecorded,\n onRequestPhoto,\n}: RecordDialogProps) {\n const [recordKind, setRecordKind] = useState<'audio' | 'video'>(initialKind);\n const [recorderState, setRecorderState] = useState<RecorderState>('idle');\n const [recordPreviewUrl, setRecordPreviewUrl] = useState<string | null>(null);\n const [recordedFile, setRecordedFile] = useState<File | null>(null);\n const [elapsedMs, setElapsedMs] = useState(0);\n const [discardConfirmOpen, setDiscardConfirmOpen] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [streamStatus, setStreamStatus] = useState<StreamStatus>('acquiring');\n\n const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n const recordStreamRef = useRef<MediaStream | null>(null);\n const liveStreamKindRef = useRef<'audio' | 'video' | null>(null);\n const recordChunksRef = useRef<BlobPart[]>([]);\n const lastRecordUrlRef = useRef<string | null>(null);\n const livePreviewRef = useRef<HTMLVideoElement | null>(null);\n const autoStopTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // An acquisition that resolves after a teardown belongs to nobody and is never attached.\n const acquireGenRef = useRef(0);\n const previewReqRef = useRef(0);\n\n const startedAtRef = useRef<number>(0);\n const elapsedIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const audioContextRef = useRef<AudioContext | null>(null);\n const audioAnalyserRef = useRef<AnalyserNode | null>(null);\n const audioSourceRef = useRef<MediaStreamAudioSourceNode | null>(null);\n const waveformStopRef = useRef<(() => void) | null>(null);\n const waveformCanvasRef = useRef<HTMLCanvasElement | null>(null);\n\n useEffect(() => {\n if (open) {\n setRecordKind(initialKind);\n setRecorderState('idle');\n setRecordedFile(null);\n setRecordPreviewUrl(null);\n setElapsedMs(0);\n setDiscardConfirmOpen(false);\n setError(null);\n }\n }, [open, initialKind]);\n\n const revokeLastRecordUrl = useCallback(() => {\n if (!lastRecordUrlRef.current) return;\n releaseObjectUrl(lastRecordUrlRef.current);\n lastRecordUrlRef.current = null;\n }, []);\n\n const makeRecordUrl = useCallback(\n (blob: Blob) => {\n revokeLastRecordUrl();\n const url = URL.createObjectURL(blob);\n lastRecordUrlRef.current = url;\n return url;\n },\n [revokeLastRecordUrl],\n );\n\n const stopElapsedTimer = useCallback(() => {\n if (elapsedIntervalRef.current) {\n clearInterval(elapsedIntervalRef.current);\n elapsedIntervalRef.current = null;\n }\n }, []);\n\n const startElapsedTimer = useCallback(() => {\n stopElapsedTimer();\n startedAtRef.current = Date.now();\n setElapsedMs(0);\n elapsedIntervalRef.current = setInterval(() => {\n setElapsedMs(Date.now() - startedAtRef.current);\n }, 100);\n }, [stopElapsedTimer]);\n\n const stopWaveform = useCallback(() => {\n waveformStopRef.current?.();\n waveformStopRef.current = null;\n try {\n audioSourceRef.current?.disconnect();\n } catch {\n // A node that is already disconnected throws, and there is nothing left to tear down.\n }\n audioSourceRef.current = null;\n audioAnalyserRef.current = null;\n const ctx = audioContextRef.current;\n audioContextRef.current = null;\n if (ctx && ctx.state !== 'closed') {\n // Closing a context the browser is already closing rejects; the context is released either way.\n ctx.close().catch(() => {});\n }\n }, []);\n\n const startWaveform = useCallback(\n (stream: MediaStream) => {\n stopWaveform();\n try {\n // lib.dom does not declare Safari's prefixed webkitAudioContext.\n const prefixed = window as Window & { webkitAudioContext?: typeof AudioContext };\n const AudioCtx: typeof AudioContext | undefined =\n window.AudioContext || prefixed.webkitAudioContext;\n if (!AudioCtx) return;\n\n const ctx = new AudioCtx();\n const source = ctx.createMediaStreamSource(stream);\n const analyser = ctx.createAnalyser();\n analyser.fftSize = 256;\n source.connect(analyser);\n\n audioContextRef.current = ctx;\n audioSourceRef.current = source;\n audioAnalyserRef.current = analyser;\n\n waveformStopRef.current = startWaveformLoop({\n analyser,\n getCanvas: () => waveformCanvasRef.current,\n mode: 'line',\n });\n } catch {\n // The waveform is decoration: a browser that refuses an audio graph records without one.\n }\n },\n [stopWaveform],\n );\n\n const teardownCapture = useCallback(() => {\n acquireGenRef.current += 1;\n if (autoStopTimeoutRef.current) {\n clearTimeout(autoStopTimeoutRef.current);\n autoStopTimeoutRef.current = null;\n }\n mediaRecorderRef.current = null;\n\n const s = recordStreamRef.current;\n recordStreamRef.current = null;\n liveStreamKindRef.current = null;\n if (s) s.getTracks().forEach((t) => t.stop());\n\n if (livePreviewRef.current) {\n livePreviewRef.current.srcObject = null;\n }\n\n recordChunksRef.current = [];\n\n stopElapsedTimer();\n stopWaveform();\n }, [stopElapsedTimer, stopWaveform]);\n\n const acquireStream = useCallback(\n async (kind: 'audio' | 'video') => {\n const wantVideo = kind === 'video';\n const facingMode = cameraFacingMode ?? 'user';\n const gen = acquireGenRef.current;\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: wantVideo ? { facingMode } : false,\n });\n if (gen !== acquireGenRef.current) {\n stream.getTracks().forEach((t) => t.stop());\n return null;\n }\n recordStreamRef.current = stream;\n liveStreamKindRef.current = kind;\n setError(null);\n setStreamStatus('ready');\n if (wantVideo && livePreviewRef.current) {\n livePreviewRef.current.srcObject = stream;\n livePreviewRef.current.muted = true;\n try {\n await livePreviewRef.current.play();\n } catch {\n // A browser that blocks preview playback rejects; recording does not depend on it.\n }\n } else if (!wantVideo) {\n startWaveform(stream);\n }\n return stream;\n },\n [cameraFacingMode, startWaveform],\n );\n\n const startLivePreview = useCallback(\n async (kind: 'audio' | 'video') => {\n teardownCapture();\n const req = ++previewReqRef.current;\n setStreamStatus('acquiring');\n try {\n await acquireStream(kind);\n } catch (e) {\n if (req !== previewReqRef.current) return;\n setError(e instanceof Error ? e.message : 'Camera/microphone access denied.');\n setStreamStatus('failed');\n }\n },\n [teardownCapture, acquireStream],\n );\n\n useEffect(() => {\n if (open) {\n void startLivePreview(initialKind);\n }\n return () => {\n teardownCapture();\n revokeLastRecordUrl();\n setStreamStatus('acquiring');\n };\n }, [open, initialKind, startLivePreview, teardownCapture, revokeLastRecordUrl]);\n\n const startRecording = useCallback(\n async (kind: 'audio' | 'video') => {\n revokeLastRecordUrl();\n setRecordPreviewUrl(null);\n setRecordedFile(null);\n setRecordKind(kind);\n\n const wantVideo = kind === 'video';\n\n let stream = recordStreamRef.current;\n if (!stream || liveStreamKindRef.current !== kind) {\n try {\n stream = await acquireStream(kind);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Camera/microphone access denied.');\n return;\n }\n }\n if (!stream) {\n return;\n }\n\n const preferredMimeType = pickRecorderMimeType(kind);\n let mr: MediaRecorder;\n try {\n mr = preferredMimeType\n ? new MediaRecorder(stream, { mimeType: preferredMimeType })\n : new MediaRecorder(stream);\n } catch {\n mr = new MediaRecorder(stream);\n }\n mediaRecorderRef.current = mr;\n recordChunksRef.current = [];\n\n mr.ondataavailable = (e) => {\n if (e.data && e.data.size > 0) recordChunksRef.current.push(e.data);\n };\n\n mr.onstop = () => {\n const blob = new Blob(recordChunksRef.current, {\n type: mr.mimeType || preferredMimeType || (wantVideo ? 'video/webm' : 'audio/webm'),\n });\n\n const rawFile = new File([blob], recordingFileName(blob.type, kind), { type: blob.type });\n const file = ensureFileWithContentType(rawFile, wantVideo ? 'video' : 'audio');\n const url = makeRecordUrl(file);\n\n setRecordedFile(file);\n setRecordPreviewUrl(url);\n setRecorderState('preview');\n\n teardownCapture();\n };\n\n mr.start();\n setRecorderState('recording');\n startElapsedTimer();\n\n if (maxRecordDurationSec && maxRecordDurationSec > 0) {\n autoStopTimeoutRef.current = setTimeout(\n () => stopIfRecording(mediaRecorderRef.current),\n autoStopDelayMs(maxRecordDurationSec),\n );\n }\n },\n [\n acquireStream,\n maxRecordDurationSec,\n makeRecordUrl,\n revokeLastRecordUrl,\n teardownCapture,\n startElapsedTimer,\n ],\n );\n\n const stopRecording = useCallback(() => {\n const mr = mediaRecorderRef.current;\n if (mr && mr.state === 'recording') {\n try {\n mr.stop();\n } catch {\n teardownCapture();\n setRecorderState('idle');\n }\n } else {\n teardownCapture();\n setRecorderState('idle');\n }\n }, [teardownCapture]);\n\n const handleReRecord = useCallback(() => {\n revokeLastRecordUrl();\n setRecordPreviewUrl(null);\n setRecordedFile(null);\n setElapsedMs(0);\n setError(null);\n setRecorderState('idle');\n void startLivePreview(recordKind);\n }, [revokeLastRecordUrl, startLivePreview, recordKind]);\n\n const handleSelectKind = useCallback(\n (kind: 'audio' | 'video') => {\n setRecordKind(kind);\n setError(null);\n void startLivePreview(kind);\n },\n [startLivePreview],\n );\n\n const reportError = useCallback((e: unknown) => {\n setError(e instanceof Error ? e.message : 'Something went wrong with the recording.');\n }, []);\n\n const start = useAsyncAction((kind: 'audio' | 'video') => startRecording(kind), {\n onError: reportError,\n });\n\n const save = useAsyncAction(\n async () => {\n if (!recordedFile || !recordPreviewUrl) return;\n await onRecorded(recordedFile, recordPreviewUrl, recordKind);\n onOpenChange(false);\n },\n { onError: reportError },\n );\n\n const closeDialogImmediately = useCallback(() => {\n stopIfRecording(mediaRecorderRef.current);\n teardownCapture();\n revokeLastRecordUrl();\n setRecordedFile(null);\n setRecordPreviewUrl(null);\n setElapsedMs(0);\n setRecorderState('idle');\n setDiscardConfirmOpen(false);\n onOpenChange(false);\n }, [teardownCapture, revokeLastRecordUrl, onOpenChange]);\n\n const handleDialogOpenChange = useCallback(\n (v: boolean) => {\n if (v) return;\n if (save.pending) return;\n if (recorderState === 'preview') {\n setDiscardConfirmOpen(true);\n return;\n }\n closeDialogImmediately();\n },\n [recorderState, save.pending, closeDialogImmediately],\n );\n\n const isRecording = recorderState === 'recording';\n const isPreview = recorderState === 'preview';\n const isIdle = recorderState === 'idle';\n const acquiring = streamStatus === 'acquiring';\n\n const elapsedLabel = maxRecordDurationSec\n ? `${formatDuration(elapsedMs)} / ${formatDuration(maxRecordDurationSec * 1000)}`\n : formatDuration(elapsedMs);\n\n return (\n <>\n <Dialog open={open} onOpenChange={handleDialogOpenChange}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>Record</DialogTitle>\n <DialogDescription>\n {isIdle && 'Choose audio or video, then press Start.'}\n {isRecording && 'Recording…'}\n {isPreview && 'Review your recording. Save to upload, or Re-record to try again.'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"flex flex-wrap gap-2\">\n {canRecVideo && (\n <Button\n type=\"button\"\n variant={recordKind === 'video' ? 'default' : 'secondary'}\n aria-pressed={recordKind === 'video'}\n onClick={() => handleSelectKind('video')}\n disabled={!isIdle || acquiring}\n icon={<Video className=\"icon-xs\" />}\n >\n Video\n </Button>\n )}\n {canRecAudio && (\n <Button\n type=\"button\"\n variant={recordKind === 'audio' ? 'default' : 'secondary'}\n aria-pressed={recordKind === 'audio'}\n onClick={() => handleSelectKind('audio')}\n disabled={!isIdle || acquiring}\n icon={<Mic className=\"icon-xs\" />}\n >\n Audio\n </Button>\n )}\n {canPhoto && onRequestPhoto && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={() => {\n onRequestPhoto();\n onOpenChange(false);\n }}\n disabled={!isIdle}\n icon={<Camera className=\"icon-xs\" />}\n >\n Photo\n </Button>\n )}\n </div>\n\n {recordKind === 'video' && !isPreview && (\n <div className=\"relative mt-2 rounded-md overflow-hidden bg-[hsl(var(--neutral-black))] aspect-video\">\n <video\n ref={livePreviewRef}\n className=\"w-full h-full object-contain\"\n playsInline\n muted\n />\n {acquiring && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <Spinner size=\"lg\" label=\"Starting camera\" />\n </div>\n )}\n </div>\n )}\n\n {recordKind === 'audio' && !isPreview && (\n <div className=\"relative mt-2 rounded-md bg-muted p-2 text-primary\">\n <canvas\n ref={waveformCanvasRef}\n width={480}\n height={80}\n className=\"w-full h-20\"\n aria-label=\"Audio waveform\"\n />\n {acquiring && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <Spinner size=\"lg\" label=\"Starting microphone\" />\n </div>\n )}\n </div>\n )}\n\n {error && (\n <p className=\"text-sm text-destructive\" role=\"alert\">\n {error}\n </p>\n )}\n\n {isRecording && (\n <div className=\"flex items-center gap-2 text-sm\" aria-live=\"polite\">\n <span\n className=\"inline-block h-2.5 w-2.5 rounded-full bg-[color:var(--recording-indicator)] animate-pulse\"\n aria-label=\"Recording\"\n />\n <span className=\"tabular-nums\">{elapsedLabel}</span>\n </div>\n )}\n\n {isPreview && recordPreviewUrl && (\n <div className=\"mt-2\">\n <MediaPreview\n url={recordPreviewUrl}\n type={recordKind}\n controls\n priority\n className=\"w-full rounded-md\"\n />\n </div>\n )}\n\n <DialogFooter className=\"flex-row justify-between gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleDialogOpenChange(false)}\n disabled={save.pending}\n icon={<X className=\"icon-xs\" />}\n >\n Close\n </Button>\n\n {isIdle && (\n <Button\n type=\"button\"\n variant=\"default\"\n onClick={() => void start.run(recordKind)}\n disabled={disabled || isLoading || acquiring}\n pending={start.pending}\n icon={\n recordKind === 'video' ? (\n <Video className=\"icon-xs\" />\n ) : (\n <Mic className=\"icon-xs\" />\n )\n }\n >\n Start\n </Button>\n )}\n\n {isRecording && (\n <Button type=\"button\" variant=\"default\" onClick={stopRecording}>\n Stop\n </Button>\n )}\n\n {isPreview && (\n <div className=\"flex gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={handleReRecord}\n disabled={save.pending}\n icon={<RotateCcw className=\"icon-xs\" />}\n >\n Re-record\n </Button>\n <Button\n type=\"button\"\n variant=\"default\"\n onClick={() => void save.run()}\n disabled={disabled || isLoading}\n pending={save.pending}\n icon={<Check className=\"icon-xs\" />}\n >\n Save\n </Button>\n </div>\n )}\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n <AlertDialog open={discardConfirmOpen} onOpenChange={setDiscardConfirmOpen}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Discard recording?</AlertDialogTitle>\n <AlertDialogDescription>\n You have a recorded clip that hasn't been saved. Closing now will discard it.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Keep recording</AlertDialogCancel>\n <AlertDialogAction onClick={closeDialogImmediately}>Discard</AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </>\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttt-productions/file-input",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.20",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/ttt-productions/ttt-packages.git",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"prepublishOnly": "npm run clean && npm run build"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ttt-productions/ui-core": "^0.11.
|
|
35
|
-
"@ttt-productions/media-schemas": "^0.14.
|
|
36
|
-
"@ttt-productions/media-viewer": "^0.10.
|
|
34
|
+
"@ttt-productions/ui-core": "^0.11.38",
|
|
35
|
+
"@ttt-productions/media-schemas": "^0.14.14",
|
|
36
|
+
"@ttt-productions/media-viewer": "^0.10.21",
|
|
37
37
|
"react-easy-crop": "^6.2.3"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|