From 842f8d5f90a91407f8d7cc3615dbc26e8b79fc66 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Thu, 26 Feb 2026 16:02:27 -0800 Subject: [PATCH] Add auto-scroll, file dialog, and transcript editing - Auto-scroll transcript to active segment during playback with smart pause when user manually scrolls (resumes after 3s) - Replace prompt() with native Tauri file dialog for audio/video import with file type filters - Add inline transcript editing via double-click with Enter to save, Esc to cancel, preserving original text for change tracking - Show "edited" badge on modified segments Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 10 ++ package.json | 1 + src-tauri/Cargo.lock | 67 ++++++++++ src-tauri/Cargo.toml | 1 + src-tauri/capabilities/default.json | 3 +- src-tauri/src/lib.rs | 1 + src/lib/components/TranscriptEditor.svelte | 148 ++++++++++++++++++--- src/routes/+page.svelte | 11 +- 8 files changed, 223 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9cd7eed..35f3abb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2.6.0", "@tauri-apps/plugin-opener": "^2", "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0", @@ -1219,6 +1220,15 @@ "node": ">= 10" } }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.6.0.tgz", + "integrity": "sha512-q4Uq3eY87TdcYzXACiYSPhmpBA76shgmQswGkSVio4C82Sz2W4iehe9TnKYwbq7weHiL88Yw19XZm7v28+Micg==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.8.0" + } + }, "node_modules/@tauri-apps/plugin-opener": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.3.tgz", diff --git a/package.json b/package.json index b33626b..543d205 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "license": "MIT", "dependencies": { "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2.6.0", "@tauri-apps/plugin-opener": "^2", "@tiptap/core": "^3.20.0", "@tiptap/pm": "^3.20.0", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 582c00f..dc597cd 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -712,6 +712,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ "bitflags 2.11.0", + "block2", + "libc", "objc2", ] @@ -2983,6 +2985,30 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + [[package]] name = "rusqlite" version = "0.31.0" @@ -3683,6 +3709,46 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-dialog" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9204b425d9be8d12aa60c2a83a289cf7d1caae40f57f336ed1155b3a5c0e359b" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed390cc669f937afeb8b28032ce837bac8ea023d975a2e207375ec05afaf1804" +dependencies = [ + "anyhow", + "dunce", + "glob", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.18", + "toml 0.9.12+spec-1.1.0", + "url", +] + [[package]] name = "tauri-plugin-opener" version = "2.5.3" @@ -4297,6 +4363,7 @@ dependencies = [ "serde_json", "tauri", "tauri-build", + "tauri-plugin-dialog", "tauri-plugin-opener", "thiserror 1.0.69", "uuid", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 016a0ca..bc0d0ee 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -22,3 +22,4 @@ rusqlite = { version = "0.31", features = ["bundled"] } uuid = { version = "1", features = ["v4", "serde"] } thiserror = "1" chrono = { version = "0.4", features = ["serde"] } +tauri-plugin-dialog = "2.6.0" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 4cdbf49..778bfb5 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -5,6 +5,7 @@ "windows": ["main"], "permissions": [ "core:default", - "opener:default" + "opener:default", + "dialog:default" ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index bc3a42a..a463111 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -10,6 +10,7 @@ use commands::transcribe::transcribe_file; pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) + .plugin(tauri_plugin_dialog::init()) .invoke_handler(tauri::generate_handler![ create_project, get_project, diff --git a/src/lib/components/TranscriptEditor.svelte b/src/lib/components/TranscriptEditor.svelte index 392cd24..a49dc29 100644 --- a/src/lib/components/TranscriptEditor.svelte +++ b/src/lib/components/TranscriptEditor.svelte @@ -1,6 +1,6 @@ -
+
{#if $segments.length === 0}

No transcript yet

@@ -55,6 +121,7 @@
{formatTimestamp(segment.start_ms)}
-
- {#each segment.words as word (word.id)} - handleWordClick(word)} - role="button" - tabindex="0" - onkeydown={(e) => { if (e.key === 'Enter') handleWordClick(word); }} - >{word.word} - {:else} - {segment.text} - {/each} -
+ {#if editingSegmentId === segment.id} +
+ + Enter to save, Esc to cancel +
+ {:else} + +
startEditing(segment)}> + {#each segment.words as word (word.id)} + handleWordClick(word)} + role="button" + tabindex="0" + onkeydown={(e) => { if (e.key === 'Enter') handleWordClick(word); }} + >{word.word} + {:else} + {segment.text} + {/each} + {#if segment.is_edited} + edited + {/if} +
+ {/if}
{/each} {/if} @@ -151,4 +234,37 @@ .segment-plain-text { color: #ccc; } + .segment-edit { + padding-left: 0.75rem; + } + .edit-textarea { + width: 100%; + min-height: 3rem; + background: #1a1a2e; + color: #e0e0e0; + border: 1px solid #e94560; + border-radius: 4px; + padding: 0.5rem; + font-family: inherit; + font-size: inherit; + line-height: 1.6; + resize: vertical; + } + .edit-textarea:focus { + outline: none; + border-color: #ff6b81; + } + .edit-hint { + font-size: 0.7rem; + color: #666; + } + .edited-badge { + font-size: 0.65rem; + color: #e94560; + background: rgba(233, 69, 96, 0.15); + padding: 0.1rem 0.3rem; + border-radius: 3px; + margin-left: 0.5rem; + vertical-align: middle; + } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 5fc9384..d16de6a 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,5 +1,6 @@