summaryrefslogtreecommitdiff
path: root/apps/web/src/voiceover/voiceover-store.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/voiceover-store.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/voiceover-store.ts')
-rw-r--r--apps/web/src/voiceover/voiceover-store.ts41
1 files changed, 41 insertions, 0 deletions
diff --git a/apps/web/src/voiceover/voiceover-store.ts b/apps/web/src/voiceover/voiceover-store.ts
new file mode 100644
index 00000000..44ccae6c
--- /dev/null
+++ b/apps/web/src/voiceover/voiceover-store.ts
@@ -0,0 +1,41 @@
+/**
+ * Voice-over recording UI state.
+ * Transient by nature — a recording never survives a reload, so unlike most
+ * stores here this one is intentionally NOT persisted (same pattern as
+ * editor-store / properties-store).
+ */
+
+import { create } from "zustand";
+import type { MediaTime } from "@/wasm";
+
+export type VoiceOverStatus = "idle" | "recording";
+
+interface VoiceOverStore {
+ status: VoiceOverStatus;
+ startedAtMs: number | null;
+ playheadStartTime: MediaTime | null;
+ startRecording: ({ playheadTime }: { playheadTime: MediaTime }) => void;
+ stopRecording: () => void;
+}
+
+export const useVoiceOverStore = create<VoiceOverStore>()((set) => ({
+ status: "idle",
+ startedAtMs: null,
+ playheadStartTime: null,
+
+ startRecording: ({ playheadTime }) => {
+ set({
+ status: "recording",
+ startedAtMs: Date.now(),
+ playheadStartTime: playheadTime,
+ });
+ },
+
+ stopRecording: () => {
+ set({
+ status: "idle",
+ startedAtMs: null,
+ playheadStartTime: null,
+ });
+ },
+}));