diff options
| author | cashmere <cashmere@autistici.org> | 2026-07-27 01:21:20 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-07-27 01:21:20 +0200 |
| commit | 783e9ac8462751adc14d78b070095fd63e45bda0 (patch) | |
| tree | 9e28a0937b9155c6b791af836c973f39ced9d541 /apps/web/src/voiceover/use-recording-elapsed.ts | |
| parent | 30a36e45686db503fded68e4da5d4c465a06f978 (diff) | |
| download | opencut-783e9ac8462751adc14d78b070095fd63e45bda0.tar.gz | |
feat: voice-over recording into the timeline
MediaRecorder mic capture (defensive mimeType picking, permission/no-mic
toasts) with elapsed display in the timeline toolbar. Stop produces an
audio asset and inserts it at the playhead on an audio track as ONE
BatchCommand undo step, freeze-frame precedent. voice-over action
registered, no default keybinding. 20 tests; smoke-tested in Chromium
with a fake mic end to end.
Diffstat (limited to 'apps/web/src/voiceover/use-recording-elapsed.ts')
| -rw-r--r-- | apps/web/src/voiceover/use-recording-elapsed.ts | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/apps/web/src/voiceover/use-recording-elapsed.ts b/apps/web/src/voiceover/use-recording-elapsed.ts new file mode 100644 index 00000000..1eeb7f90 --- /dev/null +++ b/apps/web/src/voiceover/use-recording-elapsed.ts @@ -0,0 +1,26 @@ +import { useEffect, useState } from "react"; +import { useVoiceOverStore } from "@/voiceover/voiceover-store"; + +const ELAPSED_TICK_MS = 250; + +/** + * Elapsed seconds of the active voice-over recording, ticking while recording. + * Returns 0 when idle. + */ +export function useRecordingElapsedSeconds(): number { + const status = useVoiceOverStore((state) => state.status); + const startedAtMs = useVoiceOverStore((state) => state.startedAtMs); + const [elapsedSeconds, setElapsedSeconds] = useState(0); + + useEffect(() => { + if (status !== "recording" || startedAtMs === null) { + return; + } + const interval = setInterval(() => { + setElapsedSeconds((Date.now() - startedAtMs) / 1000); + }, ELAPSED_TICK_MS); + return () => clearInterval(interval); + }, [status, startedAtMs]); + + return status === "recording" ? elapsedSeconds : 0; +} |
