From dda50075f2f12c3e4d3773bc94a202c79391f0a5 Mon Sep 17 00:00:00 2001 From: preston176 Date: Tue, 9 Jun 2026 11:19:23 +0300 Subject: [PATCH] feat(transcript): add Clear button; actually delete persisted transcript MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing the video left the transcript behind: setDoc(null) only clears the in-memory doc and never deletes the persisted copy, so a refresh reloaded it — and with a doc present the model picker (EmptyState) stays hidden, so there was no way to re-pick a model and re-transcribe. - TranscriptStore.clearDoc(): nulls the doc AND deletes the persisted transcript for the active project (setDoc(null) is left untouched — undo/redo rides it) - transcript panel: a 'Clear' button in the toolbar resets status to idle and clears selection/search, returning to the model picker + Generate - tests: clearDoc nulls+notifies, and deletes the persisted transcript --- .../__tests__/transcript-store.test.ts | 40 ++++++++++++++++++- .../web/src/core/managers/transcript-store.ts | 14 +++++++ .../transcript-editor/transcript-panel.tsx | 25 ++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) 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 (
@@ -495,6 +512,14 @@ export function TranscriptPanel() { Remove {fillerCount} filler{fillerCount === 1 ? "" : "s"} )} +