fix: duplicate/interleaved terminal text while typing on macOS - #1
Merged
Conversation
Three independent causes were stacking to produce duplicated lines while typing in the embedded terminal, macOS-only: - spawn_pty's reader thread only checked its session `gen` when tearing down (to skip pty-exit for a superseded session), not on every `pty-output` emit. A webview reload reusing a session id left the orphaned reader still emitting into whatever tab now owns that id. Now every emit checks `gen` and the orphan stops as soon as it's superseded. - WKWebView's inline "writing suggestions" (Safari/WebKit 18+, macOS Sequoia) were silently re-emitting the whole line typed so far as a single onData chunk mid-typing, which the shell echoed back as a duplicate. Confirmed with a debug write_pty log showing the exact bulk re-send. Fixed via `writingsuggestions="false"` on xterm.js's hidden input textarea (`autocorrect="off"` also set as related hardening — a different WebKit feature, not sufficient on its own). - xtermjs/xterm.js#5894: on dead-key keyboard layouts (Spanish included), WKWebView duplicates the dead-key char and drops the following key. Upstream issue, no native fix yet — ported the reporter's addon-based workaround (src/xtermDeadKeyAddon.ts). Also bumps the app version to 0.8.2.
CI runs cargo check --locked, which refuses to touch the lock file — the afkode package entry was still pinned at 0.8.1 after reverting an unrelated notify-rust downgrade I'd made locally to work around an outdated rustc, causing both the macOS and Windows check jobs to fail.
ohernandezdev
added a commit
that referenced
this pull request
Jul 26, 2026
Replaces the old copy-or-paste right-click with a real menu (Copy, Paste, Copy Command Output, Select All, Find, Scroll to Bottom, Clear Screen, Stop Process, Show in File Explorer when the click lands on a detected path, New Tab, Close Tab) — right-click no longer silently pastes the clipboard. Verified live against an isolated test build (separate app identifier, CDP + Playwright) rather than just by reading the code: - Shift+Tab / keyboard-synthesized clicks can no longer open the file preview panel (activate() now requires a real pointer click). - Escape now actually closes the preview: xterm.js intercepts Escape internally and stops its propagation before it reaches app-level listeners while the terminal still has focus, so opening the panel now moves focus onto it (matching the existing confirm-dialog pattern). - The close button and outside-click both release preview state immediately (title/body/path), so a stale file from another tab can't linger behind the next open. - The panel's z-index is raised above every other in-page overlay so its close button can never be covered and unclickable. Also: pasting into a Claude Code session now feeds the raw Ctrl+V keystroke through to the pty when the clipboard holds no text, so Claude Code's own native clipboard-image handling runs (shows "[Image #1]") instead of AFKode substituting a temp-file path. Non-Claude CLIs keep the path-substitution fallback. Version bumped to 0.8.18 per repo convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHgSVKPsaWRfuJMo1rc2AN
ohernandezdev
added a commit
that referenced
this pull request
Jul 26, 2026
Replaces the old copy-or-paste right-click with a real menu (Copy, Paste, Copy Command Output, Select All, Find, Scroll to Bottom, Clear Screen, Stop Process, Show in File Explorer when the click lands on a detected path, New Tab, Close Tab) — right-click no longer silently pastes the clipboard. Verified live against an isolated test build (separate app identifier, CDP + Playwright) rather than just by reading the code: - Shift+Tab / keyboard-synthesized clicks can no longer open the file preview panel (activate() now requires a real pointer click). - Escape now actually closes the preview: xterm.js intercepts Escape internally and stops its propagation before it reaches app-level listeners while the terminal still has focus, so opening the panel now moves focus onto it (matching the existing confirm-dialog pattern). - The close button and outside-click both release preview state immediately (title/body/path), so a stale file from another tab can't linger behind the next open. - The panel's z-index is raised above every other in-page overlay so its close button can never be covered and unclickable. Also: pasting into a Claude Code session now feeds the raw Ctrl+V keystroke through to the pty when the clipboard holds no text, so Claude Code's own native clipboard-image handling runs (shows "[Image #1]") instead of AFKode substituting a temp-file path. Non-Claude CLIs keep the path-substitution fallback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHgSVKPsaWRfuJMo1rc2AN
ohernandezdev
added a commit
that referenced
this pull request
Jul 26, 2026
#8) * fix(preview): close on Escape, guard keyboard-synthesized clicks, raise z-index; feed Ctrl+V through for claude's native image paste Escape and outside-click now share one closeFilePreview() path that also clears previewPath/previewText/DOM so a stale file never lingers behind the next open. File links only activate on a real pointer click (event .detail === 0 marks a keyboard-synthesized click), and the panel now sits above every other overlay so its close button can't be covered. `claude` gets the raw Ctrl+V keystroke instead of AFKode's own clipboard-image substitution, since Claude Code already reads the OS clipboard itself on that keystroke. * feat(terminal): real right-click context menu; claude native image paste Replaces the old copy-or-paste right-click with a real menu (Copy, Paste, Copy Command Output, Select All, Find, Scroll to Bottom, Clear Screen, Stop Process, Show in File Explorer when the click lands on a detected path, New Tab, Close Tab) — right-click no longer silently pastes the clipboard. Verified live against an isolated test build (separate app identifier, CDP + Playwright) rather than just by reading the code: - Shift+Tab / keyboard-synthesized clicks can no longer open the file preview panel (activate() now requires a real pointer click). - Escape now actually closes the preview: xterm.js intercepts Escape internally and stops its propagation before it reaches app-level listeners while the terminal still has focus, so opening the panel now moves focus onto it (matching the existing confirm-dialog pattern). - The close button and outside-click both release preview state immediately (title/body/path), so a stale file from another tab can't linger behind the next open. - The panel's z-index is raised above every other in-page overlay so its close button can never be covered and unclickable. Also: pasting into a Claude Code session now feeds the raw Ctrl+V keystroke through to the pty when the clipboard holds no text, so Claude Code's own native clipboard-image handling runs (shows "[Image #1]") instead of AFKode substituting a temp-file path. Non-Claude CLIs keep the path-substitution fallback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHgSVKPsaWRfuJMo1rc2AN --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three independent, stacking causes were producing duplicated/interleaved
lines while typing in the embedded terminal, macOS-only (never on Windows):
spawn_pty's reader only checked itssession
genwhen tearing down (to skip emittingpty-exitfor asuperseded session), not on every
pty-outputemit. A webview reloadreusing a session id left the orphaned reader still emitting into
whatever tab now owns that id — now every emit checks
genand theorphan stops as soon as it's superseded.
Sequoia): silently re-emitted the whole line typed so far as a single
onDatachunk mid-typing, which the shell echoed back as a duplicate.Confirmed with a debug
write_ptylog capturing the exact bulk re-send.Fixed via
writingsuggestions="false"on xterm.js's hidden inputtextarea (
autocorrect="off"also set — a different WebKit feature,not sufficient on its own).
included), WKWebView duplicates the dead-key char and drops the
following key. No native upstream fix yet — ported the reporter's
addon-based workaround (
src/xtermDeadKeyAddon.ts), removable oncexterm.js fixes it natively.
Also bumps the app version to 0.8.2.
Test plan
cargo check/tauri build --debugandtauri build(release) both compile cleanwrite_pty/pty-outputlog,confirmed the bulk-resend mechanism
release build, no recurrence after the writing-suggestions fix
🤖 Generated with Claude Code