Repository navigation
fix(app): drop duplicated keydown events at the document boundary - #56376
NoJuanNobody wants to merge 2 commits into
Conversation
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 72 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
|
The following comment was made by an LLM, it may be inaccurate: |
There was a problem hiding this comment.
Requesting changes. As written, the guard doesn't work in the web or desktop build, and I couldn't find evidence that the problem it targets happens in the DOM.
How I checked: the new unit tests pass (4/4) and tsgo -b passes. bun run build in packages/app warns that perf_hooks was externalized for browser compatibility. In the bundled output the timing call becomes ({}).performance.now(), so every printable or editing keydown throws a TypeError before the guard can act. Nothing is ever filtered, and each keystroke produces an uncaught error (including in Sentry). The tests pass only because Bun provides perf_hooks.
Public API: unchanged.
Concerns:
- Broken in the browser: the
perf_hooksimport (inline). - No reproduction: #4920 is a terminal UI (TUI) issue, and I found no report of doubled keydowns in the app. A document-wide filter that runs before every other key handler is a large hammer without a reproduction. Please share one (browser or OS, Electron version, and an event log showing two
keydowns for one press). Without it, this risks fixing a problem that doesn't occur in the app. - Swallowing real input: the 6 ms window also drops scripted or synthetic keystrokes (Playwright, accessibility tools, remote desktop) (inline).
- Timing-based tests: they rely on real
Bun.sleep(10)waits around a 6 ms window, which can be flaky on slow CI.packages/app/AGENTS.mdasks forsetSystemTimefrombun:testinstead.
Related: #4920
| @@ -0,0 +1,38 @@ | |||
| import { performance } from "perf_hooks" | |||
There was a problem hiding this comment.
perf_hooks is a Node module. Vite replaces it with an empty module in the app build (bun run build warns about this), so performance is undefined at runtime. In the browser, line 28 throws TypeError: Cannot read properties of undefined (reading 'now') on every printable or editing keydown, before preventDefault can run, so the guard never filters anything and every keystroke raises an uncaught error. Use the global performance (or event.timeStamp) and drop the import.
| if (key.length !== 1 && !EDITING_KEYS.has(key)) return | ||
|
|
||
| const signature = `${key}\u0000${event.metaKey}\u0000${event.ctrlKey}\u0000${event.altKey}\u0000${event.shiftKey}` | ||
| const at = performance.now() |
There was a problem hiding this comment.
This listener runs at the document level, before every other handler, so it drops any identical printable or editing key within 6 ms with no other check. That includes keystrokes from automation and accessibility tools, remote-desktop input, and dispatched KeyboardEvents, which can legitimately arrive that fast. Since there's no evidence yet of real OS or Electron duplicates in the DOM, the downside currently outweighs the benefit. A reproduction would show whether this belongs in the app at all, and if so, what narrower condition (e.g. isTrusted, matching timeStamp) actually identifies a duplicate.
| keydown(guard, "b") | ||
| keydown(guard, "a") | ||
| expect(keydown(guard, "Backspace").defaultPrevented).toBe(false) | ||
| await Bun.sleep(10) |
There was a problem hiding this comment.
These tests time a 6 ms window with real Bun.sleep(10) waits, which is timing-sensitive and can flake on slow CI. packages/app/AGENTS.md asks to control time with setSystemTime from bun:test instead. Also, because Bun provides perf_hooks, these tests can't catch the browser breakage above.
|
This pull request has been automatically closed because it was not updated to meet our contributing guidelines within the 72-hour window. Feel free to open a new pull request that follows our guidelines. |
Companion to #56372, rooted in the same reported behavior as #4920: one physical keypress can surface as two identical key events a few milliseconds apart, and in the desktop/web app the visible victim is text insertion in the composer and any editable field.
Change
packages/app/src/shell/key-dedupe.ts— a document-level, capture-phasekeydownguard mounted once from the router root (AppInterface), the DOM counterpart of the TUI's raw-stdin guard:event.repeat, so held keys are never touched.Backspace,Delete,Enter,Tab,Escape): the event kinds whose duplication produces visible double text. IME composition events (isComposing,Process) are never touched, so non-Latin input methods are unaffected.preventDefault+stopImmediatePropagationin the capture phase, so no text insertion or other listener sees it.DOM
keydownevents carry no paste-burst path unlike the TUI's raw stdin (pastes do not emit per-character keydowns), so this guard needs no same-read discrimination.Testing
packages/app/src/shell/key-dedupe.test.tscovers the duplicate drop, editing-key behavior, window expiry, auto-repeat/IME/non-editing-key pass-through, and chained editing-key drops — all passing.bun run checkpasses.Added by the opencode bot (AI-generated).
Related: #4920