fix(stealth): dead keys vanishing on Windows, and stealth typing lost to a screenshot - #506
fix(stealth): dead keys vanishing on Windows, and stealth typing lost to a screenshot#506evinjohnn wants to merge 0 commit into
Conversation
|
React Doctor found 6 issues in 1 file · 6 warnings · score 91 / 100 (Great) · vs 6 warnings
|
|
| Filename | Overview |
|---|---|
| electron/main.ts | Adds screenshot-session restoration for stealth typing, but the pre-capture boolean can override an explicit stop request made during the asynchronous capture. |
| native-module/src/keyboard_hook_windows.rs | Preserves the spacing character supplied by ToUnicodeEx for dead-key results while retaining the no-translation path. |
| README.md | Updates the product heading and repository-view badge. |
Sequence Diagram
sequenceDiagram
participant User
participant App as Screenshot session
participant Hook as Stealth keyboard manager
participant Capture as Screenshot capture
App->>App: "Snapshot stealthActive = true"
App->>Hook: Stop for window hiding
App->>Capture: Await capture
User->>Hook: Request stop or leave overlay
Hook-->>User: Already inactive
Capture-->>App: Capture completes
App->>App: Restore overlay
App->>Hook: Start from stale snapshot
Hook-->>User: Keystrokes intercepted again
Reviews (2): Last reviewed commit: "docs: update Repo Views badge to 1.8M" | Re-trigger Greptile
| if (session.wasStealthTypingActive) { | ||
| this.restoreStealthTypingAfterScreenshot(); |
There was a problem hiding this comment.
Stale snapshot reactivates stealth typing
When the user stops stealth typing or leaves the overlay while screenshot capture is awaiting completion, this restoration uses the earlier wasStealthTypingActive snapshot and starts the hook again, causing subsequent keystrokes to be intercepted after the explicit stop.
Knowledge Base Used:
d9ad1e4 to
2ca5b70
Compare
|
No reviewable files after applying ignore patterns. |
Ports the two defects from the unmerged
fix/stack-review-followupsthatmainhas not already fixed by another route. That branch was never pushed and had no PR; PR #434 merged only "the low-risk subset of the #420–#424 review findings" and the rest sat on one local branch for 21 days.Its third fix —
stop()being gated onisAvailable()— is already on main (StealthKeyboardManager.stop()now guards only on!this.tap/!this.active), so it is deliberately not included here.1. A dead key no longer vanishes (
keyboard_hook_windows.rs)unicode_for_keytreated every non-positiveToUnicodeExresult as "no character". Forn == 0that is correct. Forn < 0it is not — that is a dead key, and where the layout allows it the OS has already written the spacing form of the accent to the buffer.Returning
""lost the key outright, because the only empty-chars escape hatch is AltGr-gated:A plain dead key is not AltGr, so it skipped that guard, reached
send_payloadwithchars: "", and fell through toLRESULT(1)— "Swallow: the foreground meeting app never sees this keystroke." The key therefore reached neither the overlay nor the foreground app. That silently kills every accent key on German T1, AZERTY, Spanish, Nordic and US-International layouts while stealth typing is engaged.This is not dead-key composition.
wFlags=0x4deliberately stops the kernel accumulating the dead key — composing properly would corrupt what every other app on the machine sees — so an acute accent followed byeyields two characters, not one precomposed glyph. Visible and correctable beats silently eaten.The
altgrguard is left narrow on purpose. Widening it to cover any no-text printable key is a genuine improvement, butWindowsPlatformParitypins the literal narrow form, so that change belongs with its own test update rather than smuggled in here.2. Stealth typing survives a screenshot (
main.ts)hideWindowsForScreenshot→hideMainWindow()→WindowHelper.stopStealthTyping()tears the hook down on the way into a capture; that call site's comment names "screenshot hide" explicitly.restoreWindowsAfterScreenshotput the main window, Settings and the model selector back — but never the hook. The overlay returned looking identical with stealth typing gone, so the user's next keystrokes went to the foreground meeting app.ScreenshotCaptureSessionnow recordswasStealthTypingActive, and the restore re-engages when it was set.The re-engage runs last, and the ordering is forced in both directions:
start()refuses on win32 unless the overlay is already visible, so it cannot precede the main-window restore.start()callshideAuxWindowsForStealth(), so running it before the Settings / ModelSelector restores would find those windows already hidden, do nothing, and they would then be re-shown under an engaged hook — visible windows whose input is dead because every keystroke routes to the overlay.Consequence named rather than hidden: Settings + stealth collapses to stealth-only, a valid state, rather than a window that looks usable and is not.
Validation
Build validated on macOS—tsc -p electron/tsconfig.json --noEmit, 0 errorsCovered by automated Windows branch tests—WindowsPlatformParity22/22 (it string-asserts the Rust file, including the char-translation contract)Covered by automated tests—ScreenshotCaptureFocusSafety6/6,ScreenshotCaptureRuntime6/6,StealthBlockInputFocusGuards19/19,StealthShortcutGuard6/6,StealthIpcHandlerRegistration4/4 — 63/63 across the six suites.ScreenshotCaptureFocusSafetystring-matchesmain.ts, so the comments added here were checked against itsindexOfanchors rather than assumed safe.Reviewed but not executed on WindowsRequires physical Windows verification—keyboard_hook_windows.rsis#![cfg(target_os = "windows")]and its dependencies need the Windows SDK, so it cannot be compiled from macOS. The dead-key path is reasoned from the Win32 return-value contract, not observed. Please do not merge on a green macOS run alone — this needs a Windows build and a real keyboard on a dead-key layout.🤖 Generated with Claude Code
https://claude.ai/code/session_01JJS2xjF8uDPMa4EpXey5FL