summaryrefslogtreecommitdiff
path: root/apps/web/src/voiceover/use-recording-elapsed.ts
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-07-27 01:21:20 +0200
committercashmere <cashmere@autistici.org>2026-07-27 01:21:20 +0200
commit783e9ac8462751adc14d78b070095fd63e45bda0 (patch)
tree9e28a0937b9155c6b791af836c973f39ced9d541 /apps/web/src/voiceover/use-recording-elapsed.ts
parent30a36e45686db503fded68e4da5d4c465a06f978 (diff)
downloadopencut-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.ts26
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;
+}