From 777f189ecd1dffd503ce81625dbb22c87ef6791f Mon Sep 17 00:00:00 2001 From: Robert Ramalhoto Date: Wed, 1 Apr 2026 17:39:07 +0100 Subject: [PATCH 1/4] fix(whispering): terminal paste and HTTP plugin version mismatch - Detect terminal windows on Linux via xprop WM_CLASS and send Ctrl+Shift+V instead of Ctrl+V for paste operations - Update tauri-plugin-http from 2.5.4 to 2.5.7 to fix fetch_cancel_body command not found error that broke Groq cloud transcription --- apps/whispering/src-tauri/Cargo.lock | 191 ++++++++++++++------------- apps/whispering/src-tauri/Cargo.toml | 2 +- apps/whispering/src-tauri/src/lib.rs | 64 ++++++++- 3 files changed, 155 insertions(+), 102 deletions(-) diff --git a/apps/whispering/src-tauri/Cargo.lock b/apps/whispering/src-tauri/Cargo.lock index 9e65da7a6e..c5974ad8bb 100644 --- a/apps/whispering/src-tauri/Cargo.lock +++ b/apps/whispering/src-tauri/Cargo.lock @@ -2365,9 +2365,9 @@ dependencies = [ [[package]] name = "ico" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" dependencies = [ "byteorder", "png 0.17.16", @@ -2635,10 +2635,12 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -3222,17 +3224,10 @@ checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.10.0", "block2 0.6.2", - "libc", "objc2 0.6.3", - "objc2-cloud-kit", - "objc2-core-data", "objc2-core-foundation", "objc2-core-graphics", - "objc2-core-image", - "objc2-core-text", - "objc2-core-video", "objc2-foundation 0.3.2", - "objc2-quartz-core 0.3.2", ] [[package]] @@ -3289,7 +3284,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "bitflags 2.10.0", "objc2 0.6.3", "objc2-foundation 0.3.2", ] @@ -3350,19 +3344,6 @@ dependencies = [ "objc2-core-graphics", ] -[[package]] -name = "objc2-core-video" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" -dependencies = [ - "bitflags 2.10.0", - "objc2 0.6.3", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-io-surface", -] - [[package]] name = "objc2-encode" version = "4.1.0" @@ -3414,16 +3395,6 @@ dependencies = [ "objc2-core-foundation", ] -[[package]] -name = "objc2-javascript-core" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1e6550c4caed348956ce3370c9ffeca70bb1dbed4fa96112e7c6170e074586" -dependencies = [ - "objc2 0.6.3", - "objc2-core-foundation", -] - [[package]] name = "objc2-metal" version = "0.2.2" @@ -3473,17 +3444,6 @@ dependencies = [ "objc2-foundation 0.3.2", ] -[[package]] -name = "objc2-security" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" -dependencies = [ - "bitflags 2.10.0", - "objc2 0.6.3", - "objc2-core-foundation", -] - [[package]] name = "objc2-ui-kit" version = "0.3.2" @@ -3527,8 +3487,6 @@ dependencies = [ "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.2", - "objc2-javascript-core", - "objc2-security", ] [[package]] @@ -4537,11 +4495,45 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.4.2", "web-sys", "webpki-roots", ] +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams 0.5.0", + "web-sys", +] + [[package]] name = "rfd" version = "0.15.4" @@ -5459,9 +5451,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.9.4" +version = "2.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15524fc7959bfcaa051ba6d0b3fb1ef18e978de2176c7c6acb977f7fd14d35c7" +checksum = "da77cc00fb9028caf5b5d4650f75e31f1ef3693459dfca7f7e506d1ecef0ba2d" dependencies = [ "anyhow", "bytes", @@ -5489,7 +5481,7 @@ dependencies = [ "percent-encoding", "plist", "raw-window-handle", - "reqwest", + "reqwest 0.13.2", "serde", "serde_json", "serde_repr", @@ -5512,9 +5504,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.5.3" +version = "2.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17fcb8819fd16463512a12f531d44826ce566f486d7ccd211c9c8cebdaec4e08" +checksum = "4bbc990d1dbf57a8e1c7fa2327f2a614d8b757805603c1b9ba5c81bade09fd4d" dependencies = [ "anyhow", "cargo_toml", @@ -5534,9 +5526,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.5.2" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa9844cefcf99554a16e0a278156ae73b0d8680bbc0e2ad1e4287aadd8489cf" +checksum = "d4a24476afd977c5d5d169f72425868613d82747916dd29e0a357c84c4bd6d29" dependencies = [ "base64 0.22.1", "brotli", @@ -5561,9 +5553,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.5.2" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3764a12f886d8245e66b7ee9b43ccc47883399be2019a61d80cf0f4117446fde" +checksum = "d39b349a98dadaffebb73f0a40dcd1f23c999211e5a2e744403db384d0c33de7" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -5600,7 +5592,7 @@ dependencies = [ "log", "os_info", "rand 0.9.2", - "reqwest", + "reqwest 0.12.24", "serde", "serde_json", "sys-locale", @@ -5659,9 +5651,9 @@ dependencies = [ [[package]] name = "tauri-plugin-fs" -version = "2.4.4" +version = "2.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47df422695255ecbe7bac7012440eddaeefd026656171eac9559f5243d3230d9" +checksum = "ed390cc669f937afeb8b28032ce837bac8ea023d975a2e207375ec05afaf1804" dependencies = [ "anyhow", "dunce", @@ -5696,16 +5688,16 @@ dependencies = [ [[package]] name = "tauri-plugin-http" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00685aceab12643cf024f712ab0448ba8fcadf86f2391d49d2e5aa732aacc70" +checksum = "d8f069451c4e87e7e2636b7f065a4c52866c4ce5e60e2d53fa1038edb6d184dc" dependencies = [ "bytes", "cookie_store", "data-url", "http", "regex", - "reqwest", + "reqwest 0.12.24", "schemars 0.8.22", "serde", "serde_json", @@ -5876,7 +5868,7 @@ dependencies = [ "minisign-verify", "osakit", "percent-encoding", - "reqwest", + "reqwest 0.12.24", "semver", "serde", "serde_json", @@ -5894,9 +5886,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.9.2" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f766fe9f3d1efc4b59b17e7a891ad5ed195fa8d23582abb02e6c9a01137892" +checksum = "2826d79a3297ed08cd6ea7f412644ef58e32969504bc4fbd8d7dbeabc4445ea2" dependencies = [ "cookie", "dpi", @@ -5919,9 +5911,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.9.2" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7950f3bde6bcca6655bc5e76d3d6ec587ceb81032851ab4ddbe1f508bdea2729" +checksum = "e11ea2e6f801d275fdd890d6c9603736012742a1c33b96d0db788c9cdebf7f9e" dependencies = [ "gtk", "http", @@ -5929,7 +5921,6 @@ dependencies = [ "log", "objc2 0.6.3", "objc2-app-kit", - "objc2-foundation 0.3.2", "once_cell", "percent-encoding", "raw-window-handle", @@ -5946,9 +5937,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a423c51176eb3616ee9b516a9fa67fed5f0e78baaba680e44eb5dd2cc37490" +checksum = "219a1f983a2af3653f75b5747f76733b0da7ff03069c7a41901a5eb3ace4557d" dependencies = [ "anyhow", "brotli", @@ -6325,9 +6316,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "bitflags 2.10.0", "bytes", @@ -6714,9 +6705,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -6727,22 +6718,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" dependencies = [ - "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6750,9 +6738,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", @@ -6763,9 +6751,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -6783,6 +6771,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wayland-backend" version = "0.3.11" @@ -6858,9 +6859,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" dependencies = [ "js-sys", "wasm-bindgen", @@ -6878,9 +6879,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -6902,9 +6903,9 @@ dependencies = [ [[package]] name = "webkit2gtk-sys" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" dependencies = [ "bitflags 1.3.2", "cairo-sys-rs", @@ -7602,9 +7603,9 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wry" -version = "0.53.5" +version = "0.54.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728b7d4c8ec8d81cab295e0b5b8a4c263c0d41a785fb8f8c4df284e5411140a2" +checksum = "bb26159b420aa77684589a744ae9a9461a95395b848764ad12290a14d960a11a" dependencies = [ "base64 0.22.1", "block2 0.6.2", diff --git a/apps/whispering/src-tauri/Cargo.toml b/apps/whispering/src-tauri/Cargo.toml index 81f7c15d0c..89681d8f1e 100644 --- a/apps/whispering/src-tauri/Cargo.toml +++ b/apps/whispering/src-tauri/Cargo.toml @@ -32,7 +32,7 @@ tauri-plugin-opener = "2" tauri-plugin-clipboard-manager = "2" tauri-plugin-dialog = "2" tauri-plugin-fs = "2" -tauri-plugin-http = "2" +tauri-plugin-http = "2.5.7" tauri-plugin-notification = "2" tauri-plugin-os = "2" tauri-plugin-process = "2" diff --git a/apps/whispering/src-tauri/src/lib.rs b/apps/whispering/src-tauri/src/lib.rs index dba53fa830..3945d83472 100644 --- a/apps/whispering/src-tauri/src/lib.rs +++ b/apps/whispering/src-tauri/src/lib.rs @@ -225,24 +225,76 @@ async fn write_text(app: tauri::AppHandle, text: String) -> Result<(), String> { // Use virtual key codes for V to work with any keyboard layout #[cfg(target_os = "macos")] - let (modifier, v_key) = (Key::Meta, Key::Other(9)); // Virtual key code for V on macOS + let (modifier, v_key, use_shift) = (Key::Meta, Key::Other(9), false); // Virtual key code for V on macOS #[cfg(target_os = "windows")] - let (modifier, v_key) = (Key::Control, Key::Other(0x56)); // VK_V on Windows + let (modifier, v_key, use_shift) = (Key::Control, Key::Other(0x56), false); // VK_V on Windows #[cfg(target_os = "linux")] - let (modifier, v_key) = (Key::Control, Key::Unicode('v')); // Fallback for Linux - - // Press modifier + V + let (modifier, v_key, use_shift) = { + // Terminals use Ctrl+Shift+V instead of Ctrl+V. Detect by checking the + // focused window's WM_CLASS via xdotool. + let is_terminal = std::process::Command::new("sh") + .args(["-c", "xprop -id $(xdotool getactivewindow) WM_CLASS 2>/dev/null"]) + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|class| { + let class = class.trim().to_lowercase(); + [ + "gnome-terminal", + "konsole", + "xfce4-terminal", + "mate-terminal", + "terminator", + "tilix", + "alacritty", + "kitty", + "wezterm", + "foot", + "st", + "urxvt", + "xterm", + "sakura", + "guake", + "yakuake", + "tilda", + "hyper", + "termux", + "rio", + "ghostty", + "contour", + "blackbox", + ] + .iter() + .any(|t| class.contains(t)) + || class.contains("terminal") + || class.contains("term") + }) + .unwrap_or(false); + (Key::Control, Key::Unicode('v'), is_terminal) + }; + + // Press modifier (+ Shift for terminals) + V enigo .key(modifier, Direction::Press) .map_err(|e| format!("Failed to press modifier key: {}", e))?; + if use_shift { + enigo + .key(Key::Shift, Direction::Press) + .map_err(|e| format!("Failed to press Shift key: {}", e))?; + } enigo .key(v_key, Direction::Press) .map_err(|e| format!("Failed to press V key: {}", e))?; - // Release V + modifier (in reverse order for proper cleanup) + // Release V (+ Shift) + modifier (in reverse order for proper cleanup) enigo .key(v_key, Direction::Release) .map_err(|e| format!("Failed to release V key: {}", e))?; + if use_shift { + enigo + .key(Key::Shift, Direction::Release) + .map_err(|e| format!("Failed to release Shift key: {}", e))?; + } enigo .key(modifier, Direction::Release) .map_err(|e| format!("Failed to release modifier key: {}", e))?; From bf9a533626b250f8e7df6119b10c613421d8e841 Mon Sep 17 00:00:00 2001 From: Robert Ramalhoto Date: Thu, 2 Apr 2026 00:35:20 +0100 Subject: [PATCH 2/4] docs: add project memory system, git workflow, and session handoff - Created docs/git-workflow.md with fork remotes and conflict targets - Bootstrapped memory/ with active context, task list, changelog, decisions, tech context - Added learnings and error logs from session - Created session handoff document --- docs/git-workflow.md | 40 +++++ memory/active_context.md | 25 +++ memory/changelog.md | 32 ++++ memory/changelog_summary.md | 16 ++ memory/decisions.md | 42 +++++ memory/learnings/ERRORS.md | 39 +++++ memory/learnings/LEARNINGS.md | 48 ++++++ memory/project_brief.md | 22 +++ .../2026-04-02_0030_session_handoff.md | 158 ++++++++++++++++++ memory/task_list.md | 20 +++ memory/tech_context.md | 41 +++++ 11 files changed, 483 insertions(+) create mode 100644 docs/git-workflow.md create mode 100644 memory/active_context.md create mode 100644 memory/changelog.md create mode 100644 memory/changelog_summary.md create mode 100644 memory/decisions.md create mode 100644 memory/learnings/ERRORS.md create mode 100644 memory/learnings/LEARNINGS.md create mode 100644 memory/project_brief.md create mode 100644 memory/session_handoff/2026-04-02_0030_whispering-linux-fixes/2026-04-02_0030_session_handoff.md create mode 100644 memory/task_list.md create mode 100644 memory/tech_context.md diff --git a/docs/git-workflow.md b/docs/git-workflow.md new file mode 100644 index 0000000000..a19e389727 --- /dev/null +++ b/docs/git-workflow.md @@ -0,0 +1,40 @@ +# Git Workflow + +This repo is a fork of [EpicenterHQ/epicenter](https://github.com/EpicenterHQ/epicenter) with custom enhancements for Linux terminal paste support and dependency fixes. + +| Remote | URL | Purpose | +|--------|-----|---------| +| `origin` | `https://github.com/rob88pt/epicenter.git` | Your fork -- push changes here | +| `upstream` | `https://github.com/EpicenterHQ/epicenter.git` | Original repo -- pull updates from here | + +## Pushing Your Changes +```bash +git add && git commit -m "message" && git push origin main +``` + +## Pulling Upstream Updates +```bash +git fetch upstream && git merge upstream/main +``` + +## Viewing Upstream Changes Before Merging +```bash +git fetch upstream +git log upstream/main --oneline --not main +git diff main upstream/main +``` + +## Tagging a Release +```bash +git tag -a v1.0.0 -m "Description" && git push origin v1.0.0 +``` + +## Likely Conflict Files + +These files contain our custom changes and are most likely to conflict when syncing upstream: + +- `apps/whispering/src-tauri/src/lib.rs` -- terminal paste detection (Ctrl+Shift+V for terminals on Linux) +- `apps/whispering/src-tauri/Cargo.toml` -- pinned `tauri-plugin-http` version to 2.5.7 +- `apps/whispering/src-tauri/Cargo.lock` -- dependency lock changes from the plugin update + +When resolving conflicts in these files, prioritize keeping our terminal detection logic and HTTP plugin version while incorporating upstream feature changes. diff --git a/memory/active_context.md b/memory/active_context.md new file mode 100644 index 0000000000..cea7eb177d --- /dev/null +++ b/memory/active_context.md @@ -0,0 +1,25 @@ +# Active Context + +## Current Focus +Whispering desktop app -- fixing Linux-specific issues and maintaining a fork with patches. + +## Recent Changes +- [2026-04-01] Forked EpicenterHQ/epicenter to rob88pt/epicenter +- [2026-04-01] Fixed terminal paste: detect terminal windows via `xprop WM_CLASS` and send Ctrl+Shift+V instead of Ctrl+V +- [2026-04-01] Fixed Groq transcription: updated `tauri-plugin-http` from 2.5.4 to 2.5.7 to fix `fetch_cancel_body` command not found +- [2026-04-01] Built and installed patched `.deb` locally +- [2026-04-01] Opened PR EpicenterHQ/epicenter#1575 + +## Next Steps +- [ ] Verify terminal paste works with the installed `.deb` release build +- [ ] Monitor PR #1575 for upstream review/merge +- [ ] Sync fork when upstream merges the PR or releases a new version + +## Blockers / Open Questions +- The xprop-based terminal detection hasn't been tested in the release `.deb` yet (only confirmed in dev build) +- VAD (voice-activated detection) mode shows "Failed to get recording stream" errors -- may be a separate issue + +## Session Notes +- User runs Linux Mint with GNOME Terminal +- xdotool v3.20160805 does not have `getwindowclassname` -- had to use `xprop` instead +- Dev build uses `http_localhost_1420` for localstorage origin vs `tauri_localhost_0` in production -- settings don't transfer between dev/prod diff --git a/memory/changelog.md b/memory/changelog.md new file mode 100644 index 0000000000..b74ca46e92 --- /dev/null +++ b/memory/changelog.md @@ -0,0 +1,32 @@ +# Changelog + +## [2026-04-01] - Terminal Paste Fix and HTTP Plugin Update + +### Added +- Terminal window detection on Linux via `xprop WM_CLASS` in `write_text` command +- List of 23 known terminal emulator class names for matching +- Fallback matching on "terminal" and "term" substrings in WM_CLASS + +### Changed +- `tauri-plugin-http` pinned from `"2"` to `"2.5.7"` in Cargo.toml +- Cargo.lock updated with tauri-plugin-http 2.5.7 and transitive dependency updates (tauri 2.9.4 -> 2.10.3, wry 0.53.5 -> 0.54.2, etc.) + +### Fixed +- Clipboard paste now sends Ctrl+Shift+V in terminal emulators on Linux (was Ctrl+V which terminals ignore) +- Groq cloud transcription fixed -- `fetch_cancel_body` command was missing because JS plugin (2.5.7) was ahead of Rust plugin (2.5.4) + +### Decisions +- Used `xprop -id $(xdotool getactivewindow) WM_CLASS` instead of `xdotool getwindowclassname` because xdotool v3.20160805 (shipped with Ubuntu/Mint) doesn't have `getwindowclassname` +- Pinned exact version `2.5.7` rather than `"2"` to prevent future version mismatches +- Put detection logic in `#[cfg(target_os = "linux")]` block -- macOS and Windows unaffected + +### Problems & Solutions +- `xdotool getwindowclassname` not found -> used `xprop` via shell subprocess instead +- `http.fetch_cancel_body not allowed` -> root cause was JS plugin v2.5.7 calling a command that Rust plugin v2.5.4 didn't have; fixed by updating Rust side +- Dev build localstorage uses different origin than production (`http_localhost_1420` vs `tauri_localhost_0`) -> settings/API keys don't transfer; user must re-enter +- Missing system deps for Tauri build: libssl-dev, libclang-dev, libasound2-dev, libvulkan-dev, glslc, and various GTK/WebKit libs + +### Files Affected +- `apps/whispering/src-tauri/src/lib.rs` - terminal detection and Ctrl+Shift+V logic +- `apps/whispering/src-tauri/Cargo.toml` - pinned tauri-plugin-http version +- `apps/whispering/src-tauri/Cargo.lock` - dependency updates diff --git a/memory/changelog_summary.md b/memory/changelog_summary.md new file mode 100644 index 0000000000..3dcfa07426 --- /dev/null +++ b/memory/changelog_summary.md @@ -0,0 +1,16 @@ +# Changelog Summary + +## Current State +Forked from EpicenterHQ/epicenter. Two fixes applied: Linux terminal paste (Ctrl+Shift+V detection) and tauri-plugin-http version mismatch (2.5.4 -> 2.5.7). PR #1575 open upstream. Patched `.deb` installed locally, pending terminal paste verification. + +## Major Milestones +- **[2026-04]** Forked repo, fixed terminal paste and Groq transcription, opened upstream PR + +## Key Decisions +- Use `xprop` for WM_CLASS detection instead of newer xdotool commands (see [[decisions.md]] ADR-001) +- Pin `tauri-plugin-http = "2.5.7"` explicitly to prevent JS/Rust version drift + +## Recent Focus +- Getting Whispering app building and running on Linux Mint +- Fixing clipboard paste for terminal usage +- Fixing broken Groq cloud transcription due to plugin version mismatch diff --git a/memory/decisions.md b/memory/decisions.md new file mode 100644 index 0000000000..0b982393f6 --- /dev/null +++ b/memory/decisions.md @@ -0,0 +1,42 @@ +# Architecture Decision Records + +## ADR-001: Use xprop for Terminal Window Detection +- **Date:** 2026-04-01 +- **Status:** accepted + +### Context +Whispering sends Ctrl+V to paste transcribed text, but terminals on Linux use Ctrl+Shift+V. Need to detect whether the focused window is a terminal at paste time. + +### Decision +Use `xprop -id $(xdotool getactivewindow) WM_CLASS` to get the focused window's class, then check against a list of known terminal emulator names. + +### Rationale +- `xdotool getwindowclassname` would be simpler but doesn't exist in xdotool v3.20160805 shipped with Ubuntu 24.04 / Linux Mint +- `xprop` is universally available on X11 systems +- Checking WM_CLASS against a list plus "terminal"/"term" substring fallback covers most emulators +- Runs as a shell subprocess which adds ~5ms latency -- acceptable since it runs once per paste + +### Consequences +- Positive: Works on all common Linux distros without extra dependencies +- Positive: Fallback matching catches most terminals not in the explicit list +- Negative: Won't work on pure Wayland (no X11) -- would need a different approach +- Negative: Shell subprocess per paste has minor overhead + +## ADR-002: Pin tauri-plugin-http Version +- **Date:** 2026-04-01 +- **Status:** accepted + +### Context +The JS `@tauri-apps/plugin-http` (v2.5.7) called `fetch_cancel_body` but the Rust `tauri-plugin-http` (v2.5.4 via `"2"` semver) didn't have that command, causing all HTTP requests to fail silently. + +### Decision +Pin `tauri-plugin-http = "2.5.7"` explicitly in Cargo.toml instead of using `"2"`. + +### Rationale +- The semver `"2"` resolved to 2.5.4 due to Cargo.lock pinning, while bun installed JS plugin 2.5.7 +- Explicit version prevents future mismatches between JS and Rust sides +- Both sides should always be on the same minor version + +### Consequences +- Positive: Prevents silent HTTP failures from version drift +- Negative: Requires manual version bumps when updating the JS plugin diff --git a/memory/learnings/ERRORS.md b/memory/learnings/ERRORS.md new file mode 100644 index 0000000000..f8eddc2978 --- /dev/null +++ b/memory/learnings/ERRORS.md @@ -0,0 +1,39 @@ +# Error Log + +Specific errors, their causes, and resolutions. + +--- + +### ERR-001: xdotool: Unknown command: getwindowclassname +- **Logged:** 2026-04-01 +- **Area:** backend +- **Priority:** high +- **Error:** `xdotool: Unknown command: getwindowclassname` +- **Cause:** xdotool v3.20160805 (Ubuntu 24.04/Mint default) doesn't include `getwindowclassname`. This command was added in a later version. +- **Fix:** Replace with `xprop -id $(xdotool getactivewindow) WM_CLASS 2>/dev/null` which works on all X11 systems. +- **Prevention:** Don't use newer xdotool commands without checking version compatibility. Prefer xprop for window property queries. +- **Status:** resolved + +--- + +### ERR-002: http.fetch_cancel_body not allowed. Command not found +- **Logged:** 2026-04-01 +- **Area:** config +- **Priority:** critical +- **Error:** `Unhandled Promise Rejection: http.fetch_cancel_body not allowed. Command not found` +- **Cause:** JS `@tauri-apps/plugin-http` v2.5.7 calls `fetch_cancel_body` command, but Rust `tauri-plugin-http` v2.5.4 doesn't define it. The command was added in a later Rust plugin version. +- **Fix:** Update `tauri-plugin-http` in Cargo.toml from `"2"` to `"2.5.7"` and run `cargo update -p tauri-plugin-http`. +- **Prevention:** When updating JS Tauri plugins via bun, always check and update the corresponding Rust crate version in Cargo.toml to match. +- **Status:** resolved + +--- + +### ERR-003: Missing system libraries for Tauri build on Linux Mint +- **Logged:** 2026-04-01 +- **Area:** infra +- **Priority:** medium +- **Error:** Multiple: `pkg-config exited with status code 1` (alsa), `Unable to find libclang`, `Could NOT find Vulkan` +- **Cause:** Fresh Linux Mint install missing development headers for ALSA, libclang, Vulkan, OpenSSL, GTK, WebKit. +- **Fix:** `sudo apt install libssl-dev libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf libsoup-3.0-dev libjavascriptcoregtk-4.1-dev libasound2-dev libclang-dev libvulkan-dev glslc` +- **Prevention:** See `memory/tech_context.md` for full setup command. Note: use `libayatana-appindicator3-dev` not `libappindicator3-dev` on newer Ubuntu/Mint (conflicts with ayatana variant). +- **Status:** resolved diff --git a/memory/learnings/LEARNINGS.md b/memory/learnings/LEARNINGS.md new file mode 100644 index 0000000000..09d368f2a5 --- /dev/null +++ b/memory/learnings/LEARNINGS.md @@ -0,0 +1,48 @@ +# Learnings Log + +Discoveries, patterns, and non-obvious solutions. Recurring patterns get promoted to CLAUDE.md. + +--- + +### LRN-001: xdotool version differences across distros +- **Logged:** 2026-04-01 +- **Area:** backend +- **Priority:** high +- **Pattern-Key:** linux.xdotool_version_compat +- **Recurrence-Count:** 1 +- **First-Seen:** 2026-04-01 +- **Last-Seen:** 2026-04-01 +- **Context:** xdotool v3.20160805 (shipped with Ubuntu 24.04/Mint) does not have `getwindowclassname`. Newer versions do. Code that uses xdotool must account for older versions. +- **Resolution:** Use `xprop -id $(xdotool getactivewindow) WM_CLASS` as a portable alternative. Works on all X11 systems regardless of xdotool version. +- **See Also:** ERR-001 +- **Status:** pending + +--- + +### LRN-002: Tauri JS/Rust plugin version sync is critical +- **Logged:** 2026-04-01 +- **Area:** config +- **Priority:** critical +- **Pattern-Key:** tauri.plugin_version_sync +- **Recurrence-Count:** 1 +- **First-Seen:** 2026-04-01 +- **Last-Seen:** 2026-04-01 +- **Context:** `bun install` updates JS plugins to latest semver-compatible version, but `cargo build` uses Cargo.lock which may pin an older Rust version. The JS side can call commands that don't exist in the older Rust side, causing silent failures. +- **Resolution:** Pin exact versions in Cargo.toml to match the JS plugin version. Check both `package.json` and `Cargo.lock` versions when debugging Tauri plugin errors. +- **See Also:** ERR-002 +- **Status:** pending + +--- + +### LRN-003: Tauri dev vs production use different localstorage origins +- **Logged:** 2026-04-01 +- **Area:** config +- **Priority:** medium +- **Pattern-Key:** tauri.dev_prod_storage_origin +- **Recurrence-Count:** 1 +- **First-Seen:** 2026-04-01 +- **Last-Seen:** 2026-04-01 +- **Context:** Dev mode uses `http_localhost_1420` origin, production uses `tauri_localhost_0`. WebView localstorage (API keys, settings) is scoped to origin, so copying the localstorage file between dev/prod doesn't work -- different filenames. +- **Resolution:** Re-enter settings manually in dev mode, or rename the localstorage file to match the target origin. +- **See Also:** -- +- **Status:** pending diff --git a/memory/project_brief.md b/memory/project_brief.md new file mode 100644 index 0000000000..67acbd0b67 --- /dev/null +++ b/memory/project_brief.md @@ -0,0 +1,22 @@ +# Project Brief + +## Overview +Fork of EpicenterHQ/epicenter focused on the Whispering desktop app -- a speech-to-text tool that records audio, transcribes via Groq cloud API, and pastes text at the cursor. This fork adds Linux-specific fixes. + +## Goals +- Fix clipboard paste in terminal emulators on Linux +- Keep Tauri plugin versions in sync to prevent silent failures +- Maintain compatibility with upstream for easy syncing + +## Scope +- Whispering app (`apps/whispering/`) only -- other monorepo packages are unchanged +- Linux-specific fixes in the Rust backend (`src-tauri/`) +- No frontend/UI changes + +## Target Users / Audience +Linux users who use Whispering for voice dictation in terminal environments. + +## Success Criteria +- Transcribed text pastes correctly in both terminal and non-terminal apps +- Groq cloud transcription works without errors +- Upstream PR merged or fork stays in sync with upstream releases diff --git a/memory/session_handoff/2026-04-02_0030_whispering-linux-fixes/2026-04-02_0030_session_handoff.md b/memory/session_handoff/2026-04-02_0030_whispering-linux-fixes/2026-04-02_0030_session_handoff.md new file mode 100644 index 0000000000..a523a9e252 --- /dev/null +++ b/memory/session_handoff/2026-04-02_0030_whispering-linux-fixes/2026-04-02_0030_session_handoff.md @@ -0,0 +1,158 @@ +# Epicenter (Whispering) - Session Handoff + +**Date**: 2026-04-01 to 2026-04-02 +**Location**: `/home/robert/Dev/epicenter` +**Session Goal**: Get the Whispering speech-to-text app working on Linux Mint, specifically fixing clipboard paste in terminals (Ctrl+Shift+V) and broken Groq cloud transcription. + +--- + +## Chronological Narrative + +### 1. Orientation & Setup +- **The Challenge**: User wanted to use the Whispering app from the epicenter monorepo on Linux Mint +- **Discovery**: The repo is a monorepo; Whispering lives in `apps/whispering/` as a Tauri (Svelte + Rust) desktop app +- **Decision**: Installed from the GitHub release `.deb` first, then moved to building from source to fix issues + +### 2. Building from Source +- **The Problem**: Multiple missing system dependencies for Tauri on a fresh Linux Mint install +- **The Solution**: Installed in stages as errors appeared: Rust toolchain, libssl-dev, libclang-dev, libasound2-dev, libvulkan-dev, glslc, GTK/WebKit headers +- **Discovery**: `libappindicator3-dev` conflicts with `libayatana-appindicator3-dev` on modern Ubuntu/Mint -- must use the ayatana variant +- **Key Learning**: Full dependency list documented in `memory/tech_context.md` + +### 3. Fixing Groq Transcription +- **The Problem**: After recording, transcription silently failed. Console showed: `Unhandled Promise Rejection: http.fetch_cancel_body not allowed. Command not found` +- **Investigation**: The JS `@tauri-apps/plugin-http` (v2.5.7 installed by bun) called `fetch_cancel_body`, but the Rust `tauri-plugin-http` (v2.5.4 pinned in Cargo.lock) didn't have that command +- **The Solution**: Pinned `tauri-plugin-http = "2.5.7"` in Cargo.toml, ran `cargo update -p tauri-plugin-http` +- **Dead End**: Initially tried adding explicit HTTP permissions (`http:allow-fetch-cancel` etc.) to capabilities/default.json -- this was wrong because `http:default` already includes all fetch permissions. The real issue was a version mismatch, not a permissions gap. + +### 4. Fixing Terminal Paste +- **The Problem**: Whispering sends Ctrl+V to paste text, but terminals on Linux need Ctrl+Shift+V +- **The Solution**: Added terminal detection in `write_text` (lib.rs) -- checks WM_CLASS of the focused window against a list of known terminal emulators +- **Dead End**: First used `xdotool getwindowclassname` which doesn't exist in xdotool v3.20160805 (Ubuntu 24.04/Mint default). Switched to `xprop -id $(xdotool getactivewindow) WM_CLASS` + +### 5. Dev/Prod Settings Migration +- **The Problem**: User wanted settings (API keys, model config) from the installed version in the dev build +- **Discovery**: Dev mode uses `http_localhost_1420` localstorage origin, production uses `tauri_localhost_0`. Copying the localstorage file doesn't work because the filenames differ by origin. +- **Resolution**: User re-entered API key manually in dev app settings + +### 6. Fork, PR, and Release Build +- **Decision**: Forked to `rob88pt/epicenter`, created branch `fix/terminal-paste-and-http-plugin` +- **PR**: EpicenterHQ/epicenter#1575 opened with both fixes +- **Release**: Built patched `.deb` installer and installed it locally + +### 7. Memory System Bootstrap +- **Decision**: Set up full project memory system with all core files, learnings, and error logs + +--- + +## Current Technical State + +- **Terminal Paste**: `write_text` in `lib.rs` detects terminal windows via `xprop WM_CLASS` on Linux. Sends Ctrl+Shift+V for terminals, Ctrl+V for everything else. macOS/Windows unchanged. +- **HTTP Plugin**: `tauri-plugin-http` pinned to 2.5.7 in Cargo.toml, matching the JS plugin version. Groq transcription confirmed working in dev build. +- **Installed Version**: Patched `.deb` installed locally at `/usr/bin/whispering`. Includes both fixes but terminal paste not yet verified in release build. + +--- + +## Files Changed + +### Created +| File | Purpose | +|------|---------| +| `docs/git-workflow.md` | Fork workflow, remotes, sync commands, conflict file list | +| `memory/active_context.md` | Current focus and session state | +| `memory/task_list.md` | Persistent task backlog | +| `memory/changelog.md` | Detailed change history | +| `memory/changelog_summary.md` | Lean summary for quick reads | +| `memory/decisions.md` | ADRs for xprop detection and HTTP version pinning | +| `memory/project_brief.md` | Project goals and scope | +| `memory/tech_context.md` | Stack, setup commands, build instructions | +| `memory/learnings/LEARNINGS.md` | 3 learnings logged | +| `memory/learnings/ERRORS.md` | 3 errors logged with resolutions | + +### Modified +| File | What Changed | +|------|-------------| +| `apps/whispering/src-tauri/src/lib.rs` | Added terminal detection via xprop, Ctrl+Shift+V for terminals | +| `apps/whispering/src-tauri/Cargo.toml` | Pinned `tauri-plugin-http = "2.5.7"` | +| `apps/whispering/src-tauri/Cargo.lock` | Updated tauri-plugin-http and transitive deps | + +### Key Reference Files +| File | Why It Matters | +|------|---------------| +| `apps/whispering/src-tauri/src/lib.rs:210-295` | `write_text` command -- clipboard sandwich + terminal detection | +| `apps/whispering/src-tauri/capabilities/default.json` | Tauri permission config for HTTP, clipboard, etc. | +| `apps/whispering/src/lib/services/transcription/cloud/groq.ts` | Groq API transcription service | +| `apps/whispering/src/lib/services/http/tauri-fetch.ts` | Custom fetch that routes through Tauri HTTP plugin | + +--- + +## Problems Faced & Solutions + +| Problem | Solution | +|---------|----------| +| Missing system deps for Tauri build (libssl, libclang, ALSA, Vulkan) | Installed all dev packages -- see `memory/tech_context.md` | +| `libappindicator3-dev` conflicts with ayatana variant | Use `libayatana-appindicator3-dev` instead | +| `http.fetch_cancel_body not allowed` breaking Groq transcription | JS plugin v2.5.7 ahead of Rust v2.5.4 -- updated Rust to 2.5.7 | +| `xdotool getwindowclassname` not found | xdotool v3.20160805 lacks it -- used `xprop` instead | +| Dev/prod localstorage origins differ | Can't copy settings between builds -- re-enter manually | +| `pkill` killed both old and new process | Launch without killing first, or use `fuser -k` on the port | + +--- + +## Next Session Action Plan + +1. **Verify terminal paste in release build** -- launch installed Whispering, record, and paste into GNOME Terminal +2. **Monitor PR #1575** -- check for upstream review comments or merge +3. **Investigate VAD errors** -- "Failed to get recording stream" appears when voice-activated capture mode tries to start +4. **Sync fork** -- after upstream merges or releases, pull changes + +--- + +## Roadmap + +### Completed (This Session) +- [x] Fork EpicenterHQ/epicenter to rob88pt/epicenter +- [x] Fix terminal paste (Ctrl+Shift+V detection via xprop) +- [x] Fix Groq transcription (tauri-plugin-http version mismatch) +- [x] Build and install patched `.deb` +- [x] Open upstream PR #1575 +- [x] Set up project memory system +- [x] Create git-workflow.md + +### Unchanged Backlog (Pre-existing) +- [ ] Verify terminal paste in release build +- [ ] Monitor upstream PR review +- [ ] Investigate VAD recording stream errors +- [ ] Sync fork with upstream after merge/release + +### New Items Added (NEEDS CONFIRMATION) +- [ ] **ASSUMED**: May need Wayland support for terminal detection (xprop is X11 only) + +--- + +## Quick Commands +```bash +cd /home/robert/Dev/epicenter + +# Dev mode +bun run --filter @epicenter/whispering dev + +# Build release .deb +bun run --filter @epicenter/whispering build +cd apps/whispering && bun x @tauri-apps/cli build + +# Install .deb +sudo dpkg -i apps/whispering/src-tauri/target/release/bundle/deb/Whispering_7.11.0_amd64.deb + +# Check fork status +git remote -v +git fetch upstream +git log upstream/main --oneline --not main + +# Check PR +gh pr view 1575 --repo EpicenterHQ/epicenter +``` + +## Dependencies Added +- `tauri-plugin-http` updated 2.5.4 -> 2.5.7 (Rust side, to match JS plugin) +- System packages: libssl-dev, libgtk-3-dev, libwebkit2gtk-4.1-dev, libayatana-appindicator3-dev, librsvg2-dev, patchelf, libsoup-3.0-dev, libjavascriptcoregtk-4.1-dev, libasound2-dev, libclang-dev, libvulkan-dev, glslc diff --git a/memory/task_list.md b/memory/task_list.md new file mode 100644 index 0000000000..7e26afc71a --- /dev/null +++ b/memory/task_list.md @@ -0,0 +1,20 @@ +# Task List + +## In Progress +- [/] Verify terminal paste fix in release `.deb` build (added: 2026-04-01) + - Dev build confirmed working for transcription + - xprop detection code compiled into release build + - `.deb` installed, needs user testing + +## Backlog +- [ ] Monitor upstream PR EpicenterHQ/epicenter#1575 for review (added: 2026-04-01) +- [ ] Investigate VAD "Failed to get recording stream" error (added: 2026-04-01) +- [ ] Sync fork with upstream after PR merge or new release (added: 2026-04-01) + +## Done +- [x] Fork EpicenterHQ/epicenter to rob88pt/epicenter (added: 2026-04-01, done: 2026-04-01) +- [x] Fix terminal paste -- Ctrl+Shift+V for Linux terminals (added: 2026-04-01, done: 2026-04-01) +- [x] Fix Groq transcription -- update tauri-plugin-http to 2.5.7 (added: 2026-04-01, done: 2026-04-01) +- [x] Build release `.deb` installer with all fixes (added: 2026-04-01, done: 2026-04-01) +- [x] Open PR to upstream (added: 2026-04-01, done: 2026-04-01) +- [x] Install system dependencies for Tauri build on Linux Mint (added: 2026-04-01, done: 2026-04-01) diff --git a/memory/tech_context.md b/memory/tech_context.md new file mode 100644 index 0000000000..989e8263b3 --- /dev/null +++ b/memory/tech_context.md @@ -0,0 +1,41 @@ +# Technical Context + +## Stack +- **Frontend:** Svelte 5 + TypeScript + Vite +- **Backend:** Rust (Tauri v2) +- **Transcription:** Groq cloud API (also supports local Whisper/Moonshine/Parakeet) +- **Audio:** cpal (Rust), ffmpeg (compression) +- **Keyboard simulation:** enigo 0.5.0 +- **Key Dependencies:** tauri-plugin-http 2.5.7, tauri-plugin-clipboard-manager 2, groq-sdk (JS) + +## Setup +```bash +# Install system deps (Ubuntu/Mint) +sudo apt install libssl-dev libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf libsoup-3.0-dev libjavascriptcoregtk-4.1-dev libasound2-dev libclang-dev libvulkan-dev glslc xdotool xprop + +# Install Rust +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +rustup default stable + +# Install JS deps +bun install + +# Dev mode +bun run --filter @epicenter/whispering dev + +# Build release .deb +bun run --filter @epicenter/whispering build +cd apps/whispering && bun x @tauri-apps/cli build +# Output: apps/whispering/src-tauri/target/release/bundle/deb/Whispering_7.11.0_amd64.deb +``` + +## Environment +- **Bun:** package manager and runtime (not npm/yarn/node) +- **Rust:** stable toolchain via rustup +- **Required env vars:** GROQ_API_KEY (set in app settings UI, not env) + +## Build & Deploy +- Dev: `bun run --filter @epicenter/whispering dev` +- Build frontend: `bun run --filter @epicenter/whispering build` +- Build installer: `cd apps/whispering && bun x @tauri-apps/cli build` +- Install: `sudo dpkg -i apps/whispering/src-tauri/target/release/bundle/deb/Whispering_7.11.0_amd64.deb` From 003e46402bf314a6b8516494e265a5792255589f Mon Sep 17 00:00:00 2001 From: Robert Ramalhoto Date: Thu, 2 Apr 2026 02:31:24 +0100 Subject: [PATCH 3/4] fix(whispering): use xdotool for paste on Linux, fix terminal detection Replace enigo with xdotool for key simulation on Linux: - Use --clearmodifiers to prevent stuck modifier interference - Refocus target window and dismiss menus before pasting - Increase clipboard restore delay for heavy apps (Electron) Fix terminal detection false positive where "st" (suckless terminal) matched "STRING" in xprop output prefix. Now parses only the class values after "=" and uses exact quoted matching for short names. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/whispering/src-tauri/src/lib.rs | 135 ++++++++++++++++++--------- 1 file changed, 92 insertions(+), 43 deletions(-) diff --git a/apps/whispering/src-tauri/src/lib.rs b/apps/whispering/src-tauri/src/lib.rs index 3945d83472..859d444af8 100644 --- a/apps/whispering/src-tauri/src/lib.rs +++ b/apps/whispering/src-tauri/src/lib.rs @@ -220,25 +220,39 @@ async fn write_text(app: tauri::AppHandle, text: String) -> Result<(), String> { // Small delay to ensure clipboard is updated tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; - // 3. Simulate paste operation using virtual key codes (layout-independent) - let mut enigo = Enigo::new(&Settings::default()).map_err(|e| e.to_string())?; - - // Use virtual key codes for V to work with any keyboard layout - #[cfg(target_os = "macos")] - let (modifier, v_key, use_shift) = (Key::Meta, Key::Other(9), false); // Virtual key code for V on macOS - #[cfg(target_os = "windows")] - let (modifier, v_key, use_shift) = (Key::Control, Key::Other(0x56), false); // VK_V on Windows + // 3. Simulate paste operation #[cfg(target_os = "linux")] - let (modifier, v_key, use_shift) = { - // Terminals use Ctrl+Shift+V instead of Ctrl+V. Detect by checking the - // focused window's WM_CLASS via xdotool. - let is_terminal = std::process::Command::new("sh") - .args(["-c", "xprop -id $(xdotool getactivewindow) WM_CLASS 2>/dev/null"]) + { + // On Linux, use xdotool instead of enigo for reliable key simulation. + // xdotool --clearmodifiers prevents stuck modifier keys from interfering + // and --window targets the correct window regardless of focus changes. + let target_window = std::process::Command::new("xdotool") + .args(["getactivewindow"]) .output() .ok() .and_then(|o| String::from_utf8(o.stdout).ok()) - .map(|class| { - let class = class.trim().to_lowercase(); + .map(|s| s.trim().to_string()); + + // Detect terminals by WM_CLASS — they need Ctrl+Shift+V instead of Ctrl+V. + // xprop output format: WM_CLASS(STRING) = "instance", "class" + // We extract only the quoted values to avoid false matches on the + // "STRING" prefix (e.g. "st" matching "STRING"). + let is_terminal = target_window + .as_ref() + .and_then(|id| { + std::process::Command::new("xprop") + .args(["-id", id, "WM_CLASS"]) + .output() + .ok() + }) + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|raw| { + // Extract the values after "=" and check only those + let values = raw + .split('=') + .nth(1) + .unwrap_or("") + .to_lowercase(); [ "gnome-terminal", "konsole", @@ -250,6 +264,7 @@ async fn write_text(app: tauri::AppHandle, text: String) -> Result<(), String> { "kitty", "wezterm", "foot", + "st-256color", "st", "urxvt", "xterm", @@ -265,42 +280,76 @@ async fn write_text(app: tauri::AppHandle, text: String) -> Result<(), String> { "blackbox", ] .iter() - .any(|t| class.contains(t)) - || class.contains("terminal") - || class.contains("term") + .any(|t| { + // For short names like "st", match exact quoted values + // to avoid substring false positives + if t.len() <= 3 { + values.contains(&format!("\"{}\"", t)) + } else { + values.contains(*t) + } + }) + || values.contains("terminal") + || values.contains("term") }) .unwrap_or(false); - (Key::Control, Key::Unicode('v'), is_terminal) - }; - // Press modifier (+ Shift for terminals) + V - enigo - .key(modifier, Direction::Press) - .map_err(|e| format!("Failed to press modifier key: {}", e))?; - if use_shift { - enigo - .key(Key::Shift, Direction::Press) - .map_err(|e| format!("Failed to press Shift key: {}", e))?; + let paste_key = if is_terminal { + "ctrl+shift+v" + } else { + "ctrl+v" + }; + + info!( + "write_text: target_window={:?}, is_terminal={}, paste_key={}", + target_window, is_terminal, paste_key + ); + + // Refocus the target window in case focus shifted during clipboard operations. + // Also dismiss any active menus (Alt-based shortcuts can activate menu bars). + if let Some(ref id) = target_window { + let _ = std::process::Command::new("xdotool") + .args(["windowactivate", "--sync", id]) + .output(); + let _ = std::process::Command::new("xdotool") + .args(["key", "--clearmodifiers", "Escape"]) + .output(); + // Small delay for the app to process the Escape and restore focus + std::thread::sleep(std::time::Duration::from_millis(50)); + } + + std::process::Command::new("xdotool") + .args(["key", "--clearmodifiers", paste_key]) + .output() + .map_err(|e| format!("Failed to simulate paste via xdotool: {}", e))?; } - enigo - .key(v_key, Direction::Press) - .map_err(|e| format!("Failed to press V key: {}", e))?; - // Release V (+ Shift) + modifier (in reverse order for proper cleanup) - enigo - .key(v_key, Direction::Release) - .map_err(|e| format!("Failed to release V key: {}", e))?; - if use_shift { + #[cfg(not(target_os = "linux"))] + { + let mut enigo = Enigo::new(&Settings::default()).map_err(|e| e.to_string())?; + + #[cfg(target_os = "macos")] + let (modifier, v_key) = (Key::Meta, Key::Other(9)); + #[cfg(target_os = "windows")] + let (modifier, v_key) = (Key::Control, Key::Other(0x56)); + + enigo + .key(modifier, Direction::Press) + .map_err(|e| format!("Failed to press modifier key: {}", e))?; enigo - .key(Key::Shift, Direction::Release) - .map_err(|e| format!("Failed to release Shift key: {}", e))?; + .key(v_key, Direction::Press) + .map_err(|e| format!("Failed to press V key: {}", e))?; + enigo + .key(v_key, Direction::Release) + .map_err(|e| format!("Failed to release V key: {}", e))?; + enigo + .key(modifier, Direction::Release) + .map_err(|e| format!("Failed to release modifier key: {}", e))?; } - enigo - .key(modifier, Direction::Release) - .map_err(|e| format!("Failed to release modifier key: {}", e))?; - // Small delay to ensure paste completes - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + // Delay before restoring clipboard — heavy apps like VS Code (Electron) need + // more time to process the paste event and read the clipboard content. + tokio::time::sleep(tokio::time::Duration::from_millis(300)).await; // 4. Restore original clipboard content if let Some(content) = original_clipboard { From 60f4fa38e544b54ca3bf59b4bc6793bb822e4744 Mon Sep 17 00:00:00 2001 From: Robert Ramalhoto Date: Thu, 2 Apr 2026 02:44:53 +0100 Subject: [PATCH 4/4] =?UTF-8?q?docs:=20session=20handoff=20=E2=80=94=20ter?= =?UTF-8?q?minal=20paste=20debugging=20findings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated memory files with "st" matching "STRING" bug discovery - Added learnings: xprop parsing, Alt key menus, log-first debugging - Created session handoff with minimal fix instructions for next session Co-Authored-By: Claude Opus 4.6 (1M context) --- memory/active_context.md | 27 ++- memory/changelog.md | 29 +++ memory/changelog_summary.md | 12 +- memory/learnings/ERRORS.md | 12 + memory/learnings/LEARNINGS.md | 45 ++++ ...2026-04-02_0242_terminal-paste-fix-plan.md | 30 +++ .../2026-04-02_0242_session_handoff.md | 206 ++++++++++++++++++ memory/task_list.md | 10 +- 8 files changed, 348 insertions(+), 23 deletions(-) create mode 100644 memory/plans/2026-04-02_0242_terminal-paste-fix-plan.md create mode 100644 memory/session_handoff/2026-04-02_0242_terminal-paste-debugging/2026-04-02_0242_session_handoff.md diff --git a/memory/active_context.md b/memory/active_context.md index cea7eb177d..434d6eb266 100644 --- a/memory/active_context.md +++ b/memory/active_context.md @@ -1,25 +1,24 @@ # Active Context ## Current Focus -Whispering desktop app -- fixing Linux-specific issues and maintaining a fork with patches. +Whispering desktop app — debugging and fixing Linux paste issues. Need to start fresh from upstream and apply a clean, minimal terminal fix. ## Recent Changes -- [2026-04-01] Forked EpicenterHQ/epicenter to rob88pt/epicenter -- [2026-04-01] Fixed terminal paste: detect terminal windows via `xprop WM_CLASS` and send Ctrl+Shift+V instead of Ctrl+V -- [2026-04-01] Fixed Groq transcription: updated `tauri-plugin-http` from 2.5.4 to 2.5.7 to fix `fetch_cancel_body` command not found -- [2026-04-01] Built and installed patched `.deb` locally -- [2026-04-01] Opened PR EpicenterHQ/epicenter#1575 +- [2026-04-02] Discovered the "st" matching bug: `"st"` (suckless terminal) in the terminal list was matching `STRING` in xprop output prefix `WM_CLASS(STRING) = ...`, causing EVERY window to be detected as terminal → Ctrl+Shift+V sent everywhere +- [2026-04-02] Replaced enigo with xdotool for Linux paste (more reliable with `--clearmodifiers`) +- [2026-04-02] Added Escape key workaround and increased clipboard restore delay — these were unnecessary, caused by Alt+D shortcut activating VS Code menu bar +- [2026-04-02] Pushed changes to `fix/terminal-paste-and-http-plugin` branch but plan to start fresh ## Next Steps -- [ ] Verify terminal paste works with the installed `.deb` release build -- [ ] Monitor PR #1575 for upstream review/merge -- [ ] Sync fork when upstream merges the PR or releases a new version +- [ ] Reset fork to upstream/main (clean slate) +- [ ] Apply ONLY the terminal detection fix (minimal change) +- [ ] Test thoroughly in terminal, VS Code, Chrome, text editor, LibreOffice before adding more changes +- [ ] Keep changes minimal to avoid merge conflicts with upstream ## Blockers / Open Questions -- The xprop-based terminal detection hasn't been tested in the release `.deb` yet (only confirmed in dev build) -- VAD (voice-activated detection) mode shows "Failed to get recording stream" errors -- may be a separate issue +- Alt-based shortcuts (e.g. Alt+D) activate menu bars in apps like VS Code — this is expected OS behavior, NOT a bug in our code. User should use non-Alt shortcuts for push-to-talk if using VS Code. +- The "2.0.0 update" notification in Whispering was a false alarm — upstream latest is still v7.11.0 ## Session Notes -- User runs Linux Mint with GNOME Terminal -- xdotool v3.20160805 does not have `getwindowclassname` -- had to use `xprop` instead -- Dev build uses `http_localhost_1420` for localstorage origin vs `tauri_localhost_0` in production -- settings don't transfer between dev/prod +- User prefers minimal changes to avoid merge conflicts with upstream +- Ghost-chasing: spent time debugging paste issues that were actually caused by the `"st"` matching bug and the Alt+D shortcut, not by enigo, timing, or focus issues diff --git a/memory/changelog.md b/memory/changelog.md index b74ca46e92..6aa294b659 100644 --- a/memory/changelog.md +++ b/memory/changelog.md @@ -1,5 +1,34 @@ # Changelog +## [2026-04-02] - Terminal Detection Bug Fix and Paste Rework + +### Fixed +- **Critical bug**: `"st"` (suckless terminal) in the terminal emulator list was matching `STRING` in xprop output prefix `WM_CLASS(STRING) = ...`, causing every window to be falsely detected as terminal. Ctrl+Shift+V was sent to all apps. + +### Changed +- Replaced enigo with xdotool for Linux key simulation — uses `--clearmodifiers` to prevent modifier interference +- Terminal detection now parses only the values after `=` in xprop output, not the full prefix +- Short terminal names (≤3 chars like "st") now use exact quoted matching (`"st"`) to avoid substring false positives +- Added `windowactivate --sync` to refocus target window before paste +- Added Escape key press before paste to dismiss Alt-activated menus (workaround) +- Increased clipboard restore delay from 100ms to 300ms + +### Discarded (unnecessary, should be reverted) +- The Escape key workaround — VS Code issue was caused by user's Alt+D shortcut activating the menu bar, not a code bug +- The 300ms delay — VS Code paste failed because of menu focus, not timing +- The windowactivate call — focus wasn't the core problem + +### Files Affected +- `apps/whispering/src-tauri/src/lib.rs` — write_text command reworked for Linux + +### Plan for Next Session +Reset fork to upstream/main and apply ONLY the essential terminal fix: +1. Parse xprop values after `=` (not the full prefix) +2. Use exact quoted matching for short terminal names +3. Keep the change minimal — one small diff on top of upstream + +--- + ## [2026-04-01] - Terminal Paste Fix and HTTP Plugin Update ### Added diff --git a/memory/changelog_summary.md b/memory/changelog_summary.md index 3dcfa07426..489e07c590 100644 --- a/memory/changelog_summary.md +++ b/memory/changelog_summary.md @@ -1,16 +1,18 @@ # Changelog Summary ## Current State -Forked from EpicenterHQ/epicenter. Two fixes applied: Linux terminal paste (Ctrl+Shift+V detection) and tauri-plugin-http version mismatch (2.5.4 -> 2.5.7). PR #1575 open upstream. Patched `.deb` installed locally, pending terminal paste verification. +Fork has extra changes on `fix/terminal-paste-and-http-plugin` branch that should be discarded. Plan: reset to upstream/main and apply only the essential terminal detection fix (parse xprop values correctly, exact match for short names like "st"). ## Major Milestones -- **[2026-04]** Forked repo, fixed terminal paste and Groq transcription, opened upstream PR +- **[2026-04-01]** Forked repo, fixed terminal paste and Groq transcription, opened upstream PR #1575 +- **[2026-04-02]** Found and fixed critical "st" matching bug in terminal detection. Discovered several unnecessary changes made while debugging — plan to start fresh. ## Key Decisions - Use `xprop` for WM_CLASS detection instead of newer xdotool commands (see [[decisions.md]] ADR-001) - Pin `tauri-plugin-http = "2.5.7"` explicitly to prevent JS/Rust version drift +- Keep changes minimal to avoid merge conflicts with upstream +- Alt-based shortcuts (Alt+D) activate menu bars in VS Code — not a code bug, user should use non-Alt shortcuts ## Recent Focus -- Getting Whispering app building and running on Linux Mint -- Fixing clipboard paste for terminal usage -- Fixing broken Groq cloud transcription due to plugin version mismatch +- Debugging paste issues across different Linux apps +- Isolating the real bug ("st" matching "STRING") from symptoms (menu activation from Alt shortcuts) diff --git a/memory/learnings/ERRORS.md b/memory/learnings/ERRORS.md index f8eddc2978..faceb91436 100644 --- a/memory/learnings/ERRORS.md +++ b/memory/learnings/ERRORS.md @@ -37,3 +37,15 @@ Specific errors, their causes, and resolutions. - **Fix:** `sudo apt install libssl-dev libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf libsoup-3.0-dev libjavascriptcoregtk-4.1-dev libasound2-dev libclang-dev libvulkan-dev glslc` - **Prevention:** See `memory/tech_context.md` for full setup command. Note: use `libayatana-appindicator3-dev` not `libappindicator3-dev` on newer Ubuntu/Mint (conflicts with ayatana variant). - **Status:** resolved + +--- + +### ERR-004: "st" in terminal list matches "STRING" in xprop output +- **Logged:** 2026-04-02 +- **Area:** backend +- **Priority:** critical +- **Error:** Every window falsely detected as terminal — Ctrl+Shift+V sent to all apps. LibreOffice shows Paste Special dialog, VS Code toggles Markdown preview, Chrome pastes as plain text (masking the issue). +- **Cause:** `xprop` output format is `WM_CLASS(STRING) = "instance", "class"`. The terminal detection code checked if the ENTIRE output line contained any terminal name. `"st"` (suckless terminal) matched the `ST` in `STRING`. +- **Fix:** Parse only the values after `=` in xprop output. For short names (≤3 chars like "st"), use exact quoted matching: `values.contains("\"st\"")` instead of `values.contains("st")`. +- **Prevention:** When matching against structured command output, always parse the relevant values first — never match against the entire output including format prefixes. Test with diverse apps (not just terminal and Chrome). +- **Status:** resolved diff --git a/memory/learnings/LEARNINGS.md b/memory/learnings/LEARNINGS.md index 09d368f2a5..5c166e54bd 100644 --- a/memory/learnings/LEARNINGS.md +++ b/memory/learnings/LEARNINGS.md @@ -46,3 +46,48 @@ Discoveries, patterns, and non-obvious solutions. Recurring patterns get promote - **Resolution:** Re-enter settings manually in dev mode, or rename the localstorage file to match the target origin. - **See Also:** -- - **Status:** pending + +--- + +### LRN-004: xprop output format causes substring matching bugs +- **Logged:** 2026-04-02 +- **Area:** backend +- **Priority:** critical +- **Pattern-Key:** linux.xprop_output_parsing +- **Recurrence-Count:** 1 +- **First-Seen:** 2026-04-02 +- **Last-Seen:** 2026-04-02 +- **Context:** `xprop -id WM_CLASS` outputs `WM_CLASS(STRING) = "instance", "class"`. The word `STRING` in the prefix contains `st`, which matched the suckless terminal entry `"st"` in the terminal list. Every window was falsely detected as a terminal. +- **Resolution:** Parse only the values after `=` (not the full output line). For short names (≤3 chars), use exact quoted matching: check for `"st"` not just `st` as a substring. +- **See Also:** LRN-001 +- **Status:** pending + +--- + +### LRN-005: Alt keyboard shortcuts activate menu bars in Linux apps +- **Logged:** 2026-04-02 +- **Area:** backend +- **Priority:** medium +- **Pattern-Key:** linux.alt_key_menu_activation +- **Recurrence-Count:** 1 +- **First-Seen:** 2026-04-02 +- **Last-Seen:** 2026-04-02 +- **Context:** Using Alt+D as a push-to-talk shortcut causes VS Code (and other apps) to activate the File menu bar when Alt is released. This steals focus from the editor, making subsequent paste operations fail. Not a code bug — standard OS/DE behavior. +- **Resolution:** Use non-Alt shortcuts for push-to-talk when using VS Code or other apps with Alt-activated menus. Ctrl+Shift combos are safer. +- **See Also:** -- +- **Status:** pending + +--- + +### LRN-006: Iterate with logging, don't speculate on root causes +- **Logged:** 2026-04-02 +- **Area:** process +- **Priority:** high +- **Pattern-Key:** debugging.log_first_speculate_later +- **Recurrence-Count:** 1 +- **First-Seen:** 2026-04-02 +- **Last-Seen:** 2026-04-02 +- **Context:** Spent significant time speculating about stuck modifier keys, enigo vs xdotool, timing issues, and focus stealing — all wrong. The actual bug was the "st" matching "STRING" in xprop output, found in seconds once we added logging and checked. +- **Resolution:** Add logging first, reproduce, read logs. Don't stack changes based on theories. Apply one fix at a time and test. +- **See Also:** -- +- **Status:** pending diff --git a/memory/plans/2026-04-02_0242_terminal-paste-fix-plan.md b/memory/plans/2026-04-02_0242_terminal-paste-fix-plan.md new file mode 100644 index 0000000000..0a6d85f469 --- /dev/null +++ b/memory/plans/2026-04-02_0242_terminal-paste-fix-plan.md @@ -0,0 +1,30 @@ +# Fix: Terminal detection falsely matches every window + +## Context + +The terminal paste fix from the last session broke paste for non-terminal apps. The root cause was **`"st"` (suckless terminal) in the terminal name list matching `STRING` in the xprop output prefix `WM_CLASS(STRING) = ...`**, causing every window to be detected as a terminal. + +This meant Ctrl+Shift+V was sent everywhere — appearing to work in Chrome (plain-text paste) and terminals, but breaking VS Code (toggles Markdown preview), LibreOffice (Paste Special dialog), and other apps. + +## Changes already applied (lib.rs) + +Two fixes in `write_text` on Linux: + +1. **Replaced enigo with xdotool** for key simulation — uses `--clearmodifiers` to prevent modifier interference, and `windowactivate --sync` to refocus the target window before pasting + +2. **Fixed terminal detection parsing** — now extracts only the values after `=` in xprop output (not the `WM_CLASS(STRING)` prefix), and uses exact quoted matching for short names like `"st"` to avoid substring false positives + +3. **Added debug logging** — logs target window ID, is_terminal, and paste_key to `whispering.log` + +## Verification (dev build running now) + +Test push-to-talk paste in: +1. GNOME Terminal → should use ctrl+shift+v +2. Chrome → should use ctrl+v +3. VS Code → should use ctrl+v (NOT toggle markdown preview) +4. LibreOffice Writer → should use ctrl+v (NOT Paste Special) + +## After verification + +- Build release `.deb` and install +- Commit and update PR #1575 diff --git a/memory/session_handoff/2026-04-02_0242_terminal-paste-debugging/2026-04-02_0242_session_handoff.md b/memory/session_handoff/2026-04-02_0242_terminal-paste-debugging/2026-04-02_0242_session_handoff.md new file mode 100644 index 0000000000..6b72213bb8 --- /dev/null +++ b/memory/session_handoff/2026-04-02_0242_terminal-paste-debugging/2026-04-02_0242_session_handoff.md @@ -0,0 +1,206 @@ +# Epicenter (Whispering) - Session Handoff + +**Date**: 2026-04-02 +**Location**: `/home/robert/Dev/epicenter` +**Session Goal**: Debug why terminal paste fix from previous session broke paste for non-terminal apps. Identify root cause and determine the minimal fix needed. + +--- + +## Chronological Narrative + +### 1. Context Restoration +- **Starting state**: Previous session added terminal detection (xprop WM_CLASS) to `write_text` in lib.rs. Terminal paste (Ctrl+Shift+V) worked, but non-terminal apps stopped pasting. +- **User report**: "now it only works on the terminal but not on other apps" + +### 2. Initial Investigation (Speculation Phase — Dead End) +- **Theory 1 — Always-on-top stealing focus**: User had `ui.alwaysOnTop` enabled. Disabling it helped some apps but not all. This was a red herring — the real issue was elsewhere. +- **Theory 2 — Stuck Shift modifier from push-to-talk**: Hypothesized that `Alt+Shift+D` shortcut left Shift stuck. Wrong — user's actual shortcut was `Alt+D` (no Shift). +- **Key mistake**: Speculated instead of adding logging. Multiple theories and code changes were applied without verifying each one. + +### 3. Root Cause Found — "st" Matching Bug +- **The Bug**: Added `log::info!` to `write_text`. Logs showed `is_terminal=true` for VS Code (window `77594814`). +- **Verified**: `xprop -id 77594814 WM_CLASS` returns `"code", "Code"` — clearly not a terminal. +- **Root Cause**: The terminal name list included `"st"` (suckless terminal). The detection code checked if the ENTIRE xprop output line contained any terminal name. xprop output format is `WM_CLASS(STRING) = "code", "Code"`. The word `STRING` contains `st`, so `class.contains("st")` returned true for **every window**. +- **This was the only real bug.** Everything else was a consequence of it. + +### 4. Changes Applied (Some Necessary, Some Not) +Applied in this order: + +1. **Replaced enigo with xdotool for Linux paste** — Uses `xdotool key --clearmodifiers` instead of enigo's `XTestFakeKeyEvent`. More robust but NOT strictly necessary to fix the bug. + +2. **Fixed xprop output parsing** — Parse only values after `=`, not the `WM_CLASS(STRING)` prefix. For short names (≤3 chars like "st"), use exact quoted matching: `values.contains("\"st\"")`. **This is the essential fix.** + +3. **Added `windowactivate --sync`** — Refocuses target window before paste. Unnecessary for the core bug. + +4. **Added Escape key press** — Dismisses menus activated by Alt-based shortcuts. Workaround for user's `Alt+D` shortcut activating VS Code's File menu. Not a code bug. + +5. **Increased clipboard restore delay** — From 100ms to 300ms. Was trying to fix VS Code paste, but the real issue was menu activation from Alt+D. + +6. **Added debug logging** — `log::info!` in write_text showing target_window, is_terminal, paste_key. Useful for diagnostics. + +### 5. VS Code Paste Still Fails +- After fixing the "st" bug, paste worked in Chrome, text editor, and terminal. +- VS Code still failed — but this was because user's `Alt+D` push-to-talk shortcut activates VS Code's menu bar on Alt release, stealing focus from the editor. +- This is standard OS/DE behavior, not a code bug. User needs a non-Alt shortcut for VS Code. + +### 6. Decision to Start Fresh +- User recognized too many changes were stacked chasing symptoms. +- **Plan**: Reset fork to upstream/main, apply ONLY the essential terminal detection fix, test everything before adding more. +- Current changes pushed to `fix/terminal-paste-and-http-plugin` branch for reference. + +--- + +## Current Technical State + +- **Branch `fix/terminal-paste-and-http-plugin`**: Has all changes from both sessions (HTTP fix, terminal fix, xdotool switch, workarounds). Pushed to `origin`. **Should be discarded in favor of a clean fix.** +- **PR #1575**: Open on EpicenterHQ/epicenter. Contains original terminal fix + HTTP version pin + current session's changes. Needs to be updated with clean fix. +- **Upstream**: No new commits on `upstream/main` since last sync. The "2.0.0 update" notification in the app was a false alarm. +- **Installed `.deb`**: Still the previous session's build (with the "st" bug). Needs rebuild after clean fix. + +--- + +## Files Changed + +### Modified +| File | What Changed | +|------|-------------| +| `apps/whispering/src-tauri/src/lib.rs` | Replaced enigo with xdotool on Linux, fixed xprop parsing, added logging, workarounds | +| `memory/active_context.md` | Updated with current state and plan | +| `memory/changelog.md` | Added 2026-04-02 entry | +| `memory/changelog_summary.md` | Updated current state | +| `memory/task_list.md` | Updated progress | +| `memory/learnings/LEARNINGS.md` | Added LRN-004 (xprop parsing), LRN-005 (Alt key menus), LRN-006 (logging before speculating) | +| `memory/learnings/ERRORS.md` | Added ERR-004 ("st" matching "STRING") | + +### Created +| File | Purpose | +|------|---------| +| `memory/plans/2026-04-02_0242_terminal-paste-fix-plan.md` | Plan file from this session | + +### Key Reference Files +| File | Why It Matters | +|------|---------------| +| `apps/whispering/src-tauri/src/lib.rs:210-355` | `write_text` command — all paste logic lives here | +| `apps/whispering/src/lib/commands.ts` | Command definitions including push-to-talk trigger config | +| `apps/whispering/src/routes/(app)/_layout-utils/alwaysOnTop.svelte.ts` | Reactive always-on-top behavior during recording | +| `apps/whispering/src/lib/query/delivery.ts` | Delivery flow: clipboard + cursor + enter after transcription | + +--- + +## Problems Faced & Solutions + +| Problem | Solution | +|---------|----------| +| `is_terminal=true` for every window | `"st"` (suckless terminal) matched `STRING` in xprop prefix. Fix: parse only values after `=`, exact quoted match for short names | +| `xdotool key --window` didn't work | XSendEvent is ignored by most apps. Removed `--window`, use `windowactivate` instead | +| VS Code paste fails with Alt+D shortcut | Alt release activates VS Code menu bar, stealing focus. Not a code bug — use non-Alt shortcuts | +| Speculated too long without data | Added `log::info!` — found root cause in seconds from log output | + +--- + +## Next Session Action Plan + +1. **Reset fork to upstream/main** — `git checkout main && git reset --hard upstream/main && git push --force origin main` +2. **Create a new clean branch** — `git checkout -b fix/terminal-paste-v2` +3. **Apply ONLY the essential terminal detection fix** — The minimal change to `lib.rs`: + - Keep using enigo (original upstream approach) + - Just fix the xprop parsing: extract values after `=`, exact quoted match for short names + - Keep the `#[cfg(target_os = "linux")]` structure from upstream +4. **Test thoroughly** — Terminal, VS Code (with non-Alt shortcut), Chrome, text editor, LibreOffice +5. **Build `.deb`** — Only after all tests pass +6. **Update PR #1575** — With the clean, minimal fix + +### The Minimal Fix (Apply This) + +The ONLY change needed to upstream's terminal detection in `lib.rs` `write_text`: + +```rust +// BEFORE (upstream, buggy): +.map(|class| { + let class = class.trim().to_lowercase(); + ["gnome-terminal", ..., "st", ...] + .iter() + .any(|t| class.contains(t)) + || class.contains("terminal") + || class.contains("term") +}) + +// AFTER (fixed): +.map(|raw| { + // Extract only values after "=" to avoid matching "STRING" prefix + let values = raw.split('=').nth(1).unwrap_or("").to_lowercase(); + ["gnome-terminal", ..., "st-256color", "st", ...] + .iter() + .any(|t| { + if t.len() <= 3 { + values.contains(&format!("\"{}\"", t)) + } else { + values.contains(*t) + } + }) + || values.contains("terminal") + || values.contains("term") +}) +``` + +That's it. No enigo→xdotool switch. No Escape workaround. No delay change. No windowactivate. Just fix the parsing. + +--- + +## Roadmap + +### Completed (This Session) +- [x] Identified "st" matching "STRING" bug as root cause +- [x] Verified detection returns correct results after fix +- [x] Confirmed xdotool paste works on this system +- [x] Tested paste in Chrome, text editor, terminal (all work) +- [x] Documented all findings and updated memory + +### Unchanged Backlog (Pre-existing) +- [ ] Monitor upstream PR #1575 for review +- [ ] Investigate VAD "Failed to get recording stream" error +- [ ] Sync fork with upstream after PR merge or new release + +### New Items Added (NEEDS CONFIRMATION) +- [ ] **NEW**: Reset fork to upstream/main and apply clean minimal fix +- [ ] **NEW**: Rebuild and install `.deb` after clean fix +- [ ] **ASSUMED**: User should change push-to-talk shortcut from Alt+D to a non-Alt combo for VS Code compatibility + +--- + +## Quick Commands +```bash +cd /home/robert/Dev/epicenter + +# Reset fork to upstream (DESTRUCTIVE — confirm first) +git checkout main +git fetch upstream +git reset --hard upstream/main +git push --force origin main + +# Create clean fix branch +git checkout -b fix/terminal-paste-v2 + +# Apply the HTTP version pin fix +# Edit apps/whispering/src-tauri/Cargo.toml: tauri-plugin-http = "2.5.7" +# Then: cd apps/whispering/src-tauri && cargo update -p tauri-plugin-http + +# Dev build +bun run --filter @epicenter/whispering dev + +# Build release .deb +cd apps/whispering && bun x @tauri-apps/cli build + +# Install .deb +sudo dpkg -i src-tauri/target/release/bundle/deb/Whispering_*.deb + +# Check logs after testing +tail -20 ~/.local/share/com.bradenwong.whispering/logs/whispering.log +tail -20 ~/.local/share/com.bradenwong.whispering.dev/logs/whispering.log + +# Check PR status +gh pr view 1575 --repo EpicenterHQ/epicenter +``` + +## Dependencies Added +- None (xdotool and xprop were already system dependencies) diff --git a/memory/task_list.md b/memory/task_list.md index 7e26afc71a..c00213ee1f 100644 --- a/memory/task_list.md +++ b/memory/task_list.md @@ -1,10 +1,11 @@ # Task List ## In Progress -- [/] Verify terminal paste fix in release `.deb` build (added: 2026-04-01) - - Dev build confirmed working for transcription - - xprop detection code compiled into release build - - `.deb` installed, needs user testing +- [/] Apply clean terminal detection fix on upstream (added: 2026-04-02) + - Reset fork to upstream/main + - Apply minimal fix: parse xprop values after `=`, exact match for short names + - Test in terminal, VS Code, Chrome, text editor, LibreOffice + - Build and install `.deb` ## Backlog - [ ] Monitor upstream PR EpicenterHQ/epicenter#1575 for review (added: 2026-04-01) @@ -18,3 +19,4 @@ - [x] Build release `.deb` installer with all fixes (added: 2026-04-01, done: 2026-04-01) - [x] Open PR to upstream (added: 2026-04-01, done: 2026-04-01) - [x] Install system dependencies for Tauri build on Linux Mint (added: 2026-04-01, done: 2026-04-01) +- [x] Find "st" matching bug in terminal detection (added: 2026-04-02, done: 2026-04-02)