@ttt-productions/file-input 0.11.19 → 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"}
|
|
@@ -13,17 +13,6 @@ const RECORDER_MIME_CANDIDATES = {
|
|
|
13
13
|
audio: ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4'],
|
|
14
14
|
video: ['video/webm;codecs=vp9,opus', 'video/webm', 'video/mp4'],
|
|
15
15
|
};
|
|
16
|
-
// TEMPORARY: the record_dialog_* console lines diagnose the Firefox recording failure, and come out
|
|
17
|
-
// as soon as it is diagnosed and fixed.
|
|
18
|
-
function logRecordDialog(event, data = {}) {
|
|
19
|
-
if (typeof console === 'undefined' || typeof console.debug !== 'function')
|
|
20
|
-
return;
|
|
21
|
-
console.debug(`record_dialog_${event} ${JSON.stringify(data)}`);
|
|
22
|
-
}
|
|
23
|
-
// TEMPORARY: part of the record_dialog_* diagnostics.
|
|
24
|
-
function errorFields(e) {
|
|
25
|
-
return e instanceof Error ? { name: e.name, message: e.message } : { thrown: String(e) };
|
|
26
|
-
}
|
|
27
16
|
function stopIfRecording(recorder) {
|
|
28
17
|
if (!recorder || recorder.state !== 'recording')
|
|
29
18
|
return;
|
|
@@ -205,24 +194,14 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
205
194
|
const wantVideo = kind === 'video';
|
|
206
195
|
const facingMode = cameraFacingMode ?? 'user';
|
|
207
196
|
const gen = acquireGenRef.current;
|
|
208
|
-
// TEMPORARY diagnostic
|
|
209
|
-
logRecordDialog('stream_request', { kind, gen });
|
|
210
197
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
211
198
|
audio: true,
|
|
212
199
|
video: wantVideo ? { facingMode } : false,
|
|
213
200
|
});
|
|
214
201
|
if (gen !== acquireGenRef.current) {
|
|
215
|
-
// TEMPORARY diagnostic
|
|
216
|
-
logRecordDialog('stream_discarded', { kind, gen, currentGen: acquireGenRef.current });
|
|
217
202
|
stream.getTracks().forEach((t) => t.stop());
|
|
218
203
|
return null;
|
|
219
204
|
}
|
|
220
|
-
// TEMPORARY diagnostic
|
|
221
|
-
logRecordDialog('stream_ready', {
|
|
222
|
-
kind,
|
|
223
|
-
gen,
|
|
224
|
-
tracks: stream.getTracks().map((t) => ({ kind: t.kind, readyState: t.readyState })),
|
|
225
|
-
});
|
|
226
205
|
recordStreamRef.current = stream;
|
|
227
206
|
liveStreamKindRef.current = kind;
|
|
228
207
|
setError(null);
|
|
@@ -233,10 +212,8 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
233
212
|
try {
|
|
234
213
|
await livePreviewRef.current.play();
|
|
235
214
|
}
|
|
236
|
-
catch
|
|
215
|
+
catch {
|
|
237
216
|
// A browser that blocks preview playback rejects; recording does not depend on it.
|
|
238
|
-
// TEMPORARY diagnostic
|
|
239
|
-
logRecordDialog('preview_play_failed', errorFields(e));
|
|
240
217
|
}
|
|
241
218
|
}
|
|
242
219
|
else if (!wantVideo) {
|
|
@@ -252,13 +229,6 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
252
229
|
await acquireStream(kind);
|
|
253
230
|
}
|
|
254
231
|
catch (e) {
|
|
255
|
-
// TEMPORARY diagnostic
|
|
256
|
-
logRecordDialog('stream_failed', {
|
|
257
|
-
kind,
|
|
258
|
-
req,
|
|
259
|
-
currentReq: previewReqRef.current,
|
|
260
|
-
...errorFields(e),
|
|
261
|
-
});
|
|
262
232
|
if (req !== previewReqRef.current)
|
|
263
233
|
return;
|
|
264
234
|
setError(e instanceof Error ? e.message : 'Camera/microphone access denied.');
|
|
@@ -267,8 +237,6 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
267
237
|
}, [teardownCapture, acquireStream]);
|
|
268
238
|
useEffect(() => {
|
|
269
239
|
if (open) {
|
|
270
|
-
// TEMPORARY diagnostic
|
|
271
|
-
logRecordDialog('open', { initialKind });
|
|
272
240
|
void startLivePreview(initialKind);
|
|
273
241
|
}
|
|
274
242
|
return () => {
|
|
@@ -284,27 +252,16 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
284
252
|
setRecordKind(kind);
|
|
285
253
|
const wantVideo = kind === 'video';
|
|
286
254
|
let stream = recordStreamRef.current;
|
|
287
|
-
// TEMPORARY diagnostic
|
|
288
|
-
logRecordDialog('start_branch', {
|
|
289
|
-
kind,
|
|
290
|
-
branch: !stream || liveStreamKindRef.current !== kind ? 'reacquire' : 'reuse',
|
|
291
|
-
liveKind: liveStreamKindRef.current,
|
|
292
|
-
trackStates: stream?.getTracks().map((t) => t.readyState) ?? [],
|
|
293
|
-
});
|
|
294
255
|
if (!stream || liveStreamKindRef.current !== kind) {
|
|
295
256
|
try {
|
|
296
257
|
stream = await acquireStream(kind);
|
|
297
258
|
}
|
|
298
259
|
catch (e) {
|
|
299
|
-
// TEMPORARY diagnostic
|
|
300
|
-
logRecordDialog('start_stream_failed', { kind, ...errorFields(e) });
|
|
301
260
|
setError(e instanceof Error ? e.message : 'Camera/microphone access denied.');
|
|
302
261
|
return;
|
|
303
262
|
}
|
|
304
263
|
}
|
|
305
264
|
if (!stream) {
|
|
306
|
-
// TEMPORARY diagnostic
|
|
307
|
-
logRecordDialog('start_no_stream', { kind });
|
|
308
265
|
return;
|
|
309
266
|
}
|
|
310
267
|
const preferredMimeType = pickRecorderMimeType(kind);
|
|
@@ -314,26 +271,16 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
314
271
|
? new MediaRecorder(stream, { mimeType: preferredMimeType })
|
|
315
272
|
: new MediaRecorder(stream);
|
|
316
273
|
}
|
|
317
|
-
catch
|
|
318
|
-
// TEMPORARY diagnostic
|
|
319
|
-
logRecordDialog('recorder_create_fallback', { preferredMimeType, ...errorFields(e) });
|
|
274
|
+
catch {
|
|
320
275
|
mr = new MediaRecorder(stream);
|
|
321
276
|
}
|
|
322
|
-
// TEMPORARY diagnostic
|
|
323
|
-
logRecordDialog('recorder_created', { preferredMimeType, mimeType: mr.mimeType });
|
|
324
277
|
mediaRecorderRef.current = mr;
|
|
325
278
|
recordChunksRef.current = [];
|
|
326
279
|
mr.ondataavailable = (e) => {
|
|
327
280
|
if (e.data && e.data.size > 0)
|
|
328
281
|
recordChunksRef.current.push(e.data);
|
|
329
282
|
};
|
|
330
|
-
mr.onerror = (e) => {
|
|
331
|
-
// TEMPORARY diagnostic
|
|
332
|
-
logRecordDialog('recorder_error', { type: e.type });
|
|
333
|
-
};
|
|
334
283
|
mr.onstop = () => {
|
|
335
|
-
// TEMPORARY diagnostic
|
|
336
|
-
logRecordDialog('recorder_stopped', { chunks: recordChunksRef.current.length });
|
|
337
284
|
const blob = new Blob(recordChunksRef.current, {
|
|
338
285
|
type: mr.mimeType || preferredMimeType || (wantVideo ? 'video/webm' : 'audio/webm'),
|
|
339
286
|
});
|
|
@@ -345,16 +292,7 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
345
292
|
setRecorderState('preview');
|
|
346
293
|
teardownCapture();
|
|
347
294
|
};
|
|
348
|
-
|
|
349
|
-
mr.start();
|
|
350
|
-
}
|
|
351
|
-
catch (e) {
|
|
352
|
-
// TEMPORARY diagnostic
|
|
353
|
-
logRecordDialog('recorder_start_failed', errorFields(e));
|
|
354
|
-
throw e;
|
|
355
|
-
}
|
|
356
|
-
// TEMPORARY diagnostic
|
|
357
|
-
logRecordDialog('recorder_started', { state: mr.state });
|
|
295
|
+
mr.start();
|
|
358
296
|
setRecorderState('recording');
|
|
359
297
|
startElapsedTimer();
|
|
360
298
|
if (maxRecordDurationSec && maxRecordDurationSec > 0) {
|
|
@@ -442,10 +380,6 @@ export function RecordDialog({ open, onOpenChange, initialKind, canPhoto, canRec
|
|
|
442
380
|
return (_jsxs(_Fragment, { children: [_jsx(Dialog, { open: open, onOpenChange: handleDialogOpenChange, children: _jsxs(DialogContent, { className: "max-w-md", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Record" }), _jsxs(DialogDescription, { children: [isIdle && 'Choose audio or video, then press Start.', isRecording && 'Recording…', isPreview && 'Review your recording. Save to upload, or Re-record to try again.'] })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [canRecVideo && (_jsx(Button, { type: "button", variant: recordKind === 'video' ? 'default' : 'secondary', "aria-pressed": recordKind === 'video', onClick: () => handleSelectKind('video'), disabled: !isIdle || acquiring, icon: _jsx(Video, { className: "icon-xs" }), children: "Video" })), canRecAudio && (_jsx(Button, { type: "button", variant: recordKind === 'audio' ? 'default' : 'secondary', "aria-pressed": recordKind === 'audio', onClick: () => handleSelectKind('audio'), disabled: !isIdle || acquiring, icon: _jsx(Mic, { className: "icon-xs" }), children: "Audio" })), canPhoto && onRequestPhoto && (_jsx(Button, { type: "button", variant: "secondary", onClick: () => {
|
|
443
381
|
onRequestPhoto();
|
|
444
382
|
onOpenChange(false);
|
|
445
|
-
}, disabled: !isIdle, icon: _jsx(Camera, { className: "icon-xs" }), children: "Photo" }))] }), recordKind === 'video' && !isPreview && (_jsxs("div", { className: "relative mt-2 rounded-md overflow-hidden bg-[hsl(var(--neutral-black))] aspect-video", children: [_jsx("video", { ref: livePreviewRef, className: "w-full h-full object-contain", playsInline: true, muted: true }), acquiring && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx(Spinner, { size: "lg", label: "Starting camera" }) }))] })), recordKind === 'audio' && !isPreview && (_jsxs("div", { className: "relative mt-2 rounded-md bg-muted p-2 text-primary", children: [_jsx("canvas", { ref: waveformCanvasRef, width: 480, height: 80, className: "w-full h-20", "aria-label": "Audio waveform" }), acquiring && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx(Spinner, { size: "lg", label: "Starting microphone" }) }))] })), error && (_jsx("p", { className: "text-sm text-destructive", role: "alert", children: error })), isRecording && (_jsxs("div", { className: "flex items-center gap-2 text-sm", "aria-live": "polite", children: [_jsx("span", { className: "inline-block h-2.5 w-2.5 rounded-full bg-[color:var(--recording-indicator)] animate-pulse", "aria-label": "Recording" }), _jsx("span", { className: "tabular-nums", children: elapsedLabel })] })), isPreview && recordPreviewUrl && (_jsx("div", { className: "mt-2", children: _jsx(MediaPreview, { url: recordPreviewUrl, type: recordKind, controls: true, priority: true, className: "w-full rounded-md" }) })), _jsxs(DialogFooter, { className: "flex-row justify-between gap-2", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => handleDialogOpenChange(false), disabled: save.pending, icon: _jsx(X, { className: "icon-xs" }), children: "Close" }), isIdle && (_jsx(Button, { type: "button", variant: "default", onClick: () => {
|
|
446
|
-
// TEMPORARY diagnostic
|
|
447
|
-
logRecordDialog('start_click', { kind: recordKind, streamStatus });
|
|
448
|
-
void start.run(recordKind);
|
|
449
|
-
}, disabled: disabled || isLoading || acquiring, pending: start.pending, icon: recordKind === 'video' ? (_jsx(Video, { className: "icon-xs" })) : (_jsx(Mic, { className: "icon-xs" })), children: "Start" })), isRecording && (_jsx(Button, { type: "button", variant: "default", onClick: stopRecording, children: "Stop" })), isPreview && (_jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: handleReRecord, disabled: save.pending, icon: _jsx(RotateCcw, { className: "icon-xs" }), children: "Re-record" }), _jsx(Button, { type: "button", variant: "default", onClick: () => void save.run(), disabled: disabled || isLoading, pending: save.pending, icon: _jsx(Check, { className: "icon-xs" }), children: "Save" })] }))] })] }) }), _jsx(AlertDialog, { open: discardConfirmOpen, onOpenChange: setDiscardConfirmOpen, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Discard recording?" }), _jsx(AlertDialogDescription, { children: "You have a recorded clip that hasn't been saved. Closing now will discard it." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Keep recording" }), _jsx(AlertDialogAction, { onClick: closeDialogImmediately, children: "Discard" })] })] }) })] }));
|
|
383
|
+
}, disabled: !isIdle, icon: _jsx(Camera, { className: "icon-xs" }), children: "Photo" }))] }), recordKind === 'video' && !isPreview && (_jsxs("div", { className: "relative mt-2 rounded-md overflow-hidden bg-[hsl(var(--neutral-black))] aspect-video", children: [_jsx("video", { ref: livePreviewRef, className: "w-full h-full object-contain", playsInline: true, muted: true }), acquiring && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx(Spinner, { size: "lg", label: "Starting camera" }) }))] })), recordKind === 'audio' && !isPreview && (_jsxs("div", { className: "relative mt-2 rounded-md bg-muted p-2 text-primary", children: [_jsx("canvas", { ref: waveformCanvasRef, width: 480, height: 80, className: "w-full h-20", "aria-label": "Audio waveform" }), acquiring && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx(Spinner, { size: "lg", label: "Starting microphone" }) }))] })), error && (_jsx("p", { className: "text-sm text-destructive", role: "alert", children: error })), isRecording && (_jsxs("div", { className: "flex items-center gap-2 text-sm", "aria-live": "polite", children: [_jsx("span", { className: "inline-block h-2.5 w-2.5 rounded-full bg-[color:var(--recording-indicator)] animate-pulse", "aria-label": "Recording" }), _jsx("span", { className: "tabular-nums", children: elapsedLabel })] })), isPreview && recordPreviewUrl && (_jsx("div", { className: "mt-2", children: _jsx(MediaPreview, { url: recordPreviewUrl, type: recordKind, controls: true, priority: true, className: "w-full rounded-md" }) })), _jsxs(DialogFooter, { className: "flex-row justify-between gap-2", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => handleDialogOpenChange(false), disabled: save.pending, icon: _jsx(X, { className: "icon-xs" }), children: "Close" }), isIdle && (_jsx(Button, { type: "button", variant: "default", onClick: () => void start.run(recordKind), disabled: disabled || isLoading || acquiring, pending: start.pending, icon: recordKind === 'video' ? (_jsx(Video, { className: "icon-xs" })) : (_jsx(Mic, { className: "icon-xs" })), children: "Start" })), isRecording && (_jsx(Button, { type: "button", variant: "default", onClick: stopRecording, children: "Stop" })), isPreview && (_jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: handleReRecord, disabled: save.pending, icon: _jsx(RotateCcw, { className: "icon-xs" }), children: "Re-record" }), _jsx(Button, { type: "button", variant: "default", onClick: () => void save.run(), disabled: disabled || isLoading, pending: save.pending, icon: _jsx(Check, { className: "icon-xs" }), children: "Save" })] }))] })] }) }), _jsx(AlertDialog, { open: discardConfirmOpen, onOpenChange: setDiscardConfirmOpen, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Discard recording?" }), _jsx(AlertDialogDescription, { children: "You have a recorded clip that hasn't been saved. Closing now will discard it." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Keep recording" }), _jsx(AlertDialogAction, { onClick: closeDialogImmediately, children: "Discard" })] })] }) })] }));
|
|
450
384
|
}
|
|
451
385
|
//# sourceMappingURL=record-dialog.js.map
|
|
@@ -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\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\n// TEMPORARY: the record_dialog_* console lines diagnose the Firefox recording failure, and come out\n// as soon as it is diagnosed and fixed.\nfunction logRecordDialog(event: string, data: Record<string, unknown> = {}): void {\n if (typeof console === 'undefined' || typeof console.debug !== 'function') return;\n console.debug(`record_dialog_${event} ${JSON.stringify(data)}`);\n}\n\n// TEMPORARY: part of the record_dialog_* diagnostics.\nfunction errorFields(e: unknown): Record<string, unknown> {\n return e instanceof Error ? { name: e.name, message: e.message } : { thrown: String(e) };\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 // TEMPORARY diagnostic\n logRecordDialog('stream_request', { kind, gen });\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: wantVideo ? { facingMode } : false,\n });\n if (gen !== acquireGenRef.current) {\n // TEMPORARY diagnostic\n logRecordDialog('stream_discarded', { kind, gen, currentGen: acquireGenRef.current });\n stream.getTracks().forEach((t) => t.stop());\n return null;\n }\n // TEMPORARY diagnostic\n logRecordDialog('stream_ready', {\n kind,\n gen,\n tracks: stream.getTracks().map((t) => ({ kind: t.kind, readyState: t.readyState })),\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 (e) {\n // A browser that blocks preview playback rejects; recording does not depend on it.\n // TEMPORARY diagnostic\n logRecordDialog('preview_play_failed', errorFields(e));\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 // TEMPORARY diagnostic\n logRecordDialog('stream_failed', {\n kind,\n req,\n currentReq: previewReqRef.current,\n ...errorFields(e),\n });\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 // TEMPORARY diagnostic\n logRecordDialog('open', { initialKind });\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 // TEMPORARY diagnostic\n logRecordDialog('start_branch', {\n kind,\n branch: !stream || liveStreamKindRef.current !== kind ? 'reacquire' : 'reuse',\n liveKind: liveStreamKindRef.current,\n trackStates: stream?.getTracks().map((t) => t.readyState) ?? [],\n });\n if (!stream || liveStreamKindRef.current !== kind) {\n try {\n stream = await acquireStream(kind);\n } catch (e) {\n // TEMPORARY diagnostic\n logRecordDialog('start_stream_failed', { kind, ...errorFields(e) });\n setError(e instanceof Error ? e.message : 'Camera/microphone access denied.');\n return;\n }\n }\n if (!stream) {\n // TEMPORARY diagnostic\n logRecordDialog('start_no_stream', { kind });\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 (e) {\n // TEMPORARY diagnostic\n logRecordDialog('recorder_create_fallback', { preferredMimeType, ...errorFields(e) });\n mr = new MediaRecorder(stream);\n }\n // TEMPORARY diagnostic\n logRecordDialog('recorder_created', { preferredMimeType, mimeType: mr.mimeType });\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.onerror = (e) => {\n // TEMPORARY diagnostic\n logRecordDialog('recorder_error', { type: e.type });\n };\n\n mr.onstop = () => {\n // TEMPORARY diagnostic\n logRecordDialog('recorder_stopped', { chunks: recordChunksRef.current.length });\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 try {\n mr.start();\n } catch (e) {\n // TEMPORARY diagnostic\n logRecordDialog('recorder_start_failed', errorFields(e));\n throw e;\n }\n // TEMPORARY diagnostic\n logRecordDialog('recorder_started', { state: mr.state });\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={() => {\n // TEMPORARY diagnostic\n logRecordDialog('start_click', { kind: recordKind, streamStatus });\n void start.run(recordKind);\n }}\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,8 +31,8 @@
|
|
|
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.
|
|
34
|
+
"@ttt-productions/ui-core": "^0.11.38",
|
|
35
|
+
"@ttt-productions/media-schemas": "^0.14.14",
|
|
36
36
|
"@ttt-productions/media-viewer": "^0.10.21",
|
|
37
37
|
"react-easy-crop": "^6.2.3"
|
|
38
38
|
},
|