diff --git a/apps/web/src/core/managers/__tests__/transcript-store.test.ts b/apps/web/src/core/managers/__tests__/transcript-store.test.ts index 26e5795d..79657cb5 100644 --- a/apps/web/src/core/managers/__tests__/transcript-store.test.ts +++ b/apps/web/src/core/managers/__tests__/transcript-store.test.ts @@ -1,6 +1,13 @@ -import { describe, expect, test } from "bun:test"; +import { describe, expect, test, mock } from "bun:test"; import type { EditorCore } from "@/core"; import type { TranscriptDocument } from "@/transcript-editor/types"; + +const deleteTranscript = mock(() => Promise.resolve()); +const saveTranscript = mock(() => Promise.resolve()); +mock.module("@/services/storage/service", () => ({ + storageService: { deleteTranscript, saveTranscript }, +})); + import { TranscriptStore } from "../transcript-store"; // persist:false keeps the store from touching storageService / the editor, so a @@ -9,6 +16,15 @@ function makeStore(): TranscriptStore { return new TranscriptStore({} as unknown as EditorCore); } +function makeStoreWithProject(projectId: string | null): TranscriptStore { + return new TranscriptStore({ + project: { + getActiveOrNull: () => + projectId ? { metadata: { id: projectId } } : null, + }, + } as unknown as EditorCore); +} + const doc: TranscriptDocument = { segments: [], deletedRanges: [] }; describe("TranscriptStore", () => { @@ -32,4 +48,26 @@ describe("TranscriptStore", () => { expect(calls).toBe(1); expect(store.getDoc()).toBeNull(); }); + + test("clearDoc nulls the document and notifies", () => { + const store = makeStoreWithProject(null); + let calls = 0; + store.subscribe(() => { + calls++; + }); + store.setDoc(doc, { persist: false }); + expect(store.getDoc()).toBe(doc); + store.clearDoc(); + expect(store.getDoc()).toBeNull(); + expect(calls).toBe(2); // setDoc + clearDoc + }); + + test("clearDoc deletes the persisted transcript for the active project", () => { + deleteTranscript.mockClear(); + const store = makeStoreWithProject("proj-1"); + store.setDoc(doc, { persist: false }); + store.clearDoc(); + expect(deleteTranscript).toHaveBeenCalledTimes(1); + expect(deleteTranscript).toHaveBeenCalledWith({ projectId: "proj-1" }); + }); }); diff --git a/apps/web/src/core/managers/transcript-store.ts b/apps/web/src/core/managers/transcript-store.ts index 23f521c8..3b6230b0 100644 --- a/apps/web/src/core/managers/transcript-store.ts +++ b/apps/web/src/core/managers/transcript-store.ts @@ -41,6 +41,20 @@ export class TranscriptStore { this.notify(); } + /** + * Discard the active transcript AND remove its persisted copy, so a refresh + * doesn't reload it. (setDoc(null) only clears memory — it deliberately never + * touches storage, since undo/redo round-trips through it.) + */ + clearDoc(): void { + this.doc = null; + const projectId = this.editor.project.getActiveOrNull()?.metadata.id; + if (projectId) { + void storageService.deleteTranscript({ projectId }); + } + this.notify(); + } + subscribe(listener: () => void): () => void { this.listeners.add(listener); return () => this.listeners.delete(listener); diff --git a/apps/web/src/transcript-editor/transcript-panel.tsx b/apps/web/src/transcript-editor/transcript-panel.tsx index e9768fa6..27a9a5bd 100644 --- a/apps/web/src/transcript-editor/transcript-panel.tsx +++ b/apps/web/src/transcript-editor/transcript-panel.tsx @@ -468,6 +468,23 @@ export function TranscriptPanel() { } }, [activeWordId]); + const clearTranscript = useCallback(() => { + if ( + !window.confirm( + "Clear this transcript? You can regenerate it, but any transcript text edits will be lost.", + ) + ) { + return; + } + editor.transcript.clearDoc(); + setStatus({ kind: "idle" }); + setSelection(new Set()); + setLastSelectedId(null); + setSearchOpen(false); + setSearchQuery(""); + setEditingWordId(null); + }, [editor]); + return (