Skip to content

fix(app): restore terminal keyboard input to PTY - #732

Merged
Astro-Han merged 2 commits into
devfrom
claude/i696-terminal-input
May 18, 2026
Merged

fix(app): restore terminal keyboard input to PTY#732
Astro-Han merged 2 commits into
devfrom
claude/i696-terminal-input

Conversation

@Astro-Han

@Astro-Han Astro-Han commented May 18, 2026

Copy link
Copy Markdown
Owner

Summary

Rewires terminal.tsx's Ghostty key handler with the correct boolean contract so keystrokes reach the PTY again, and adds a typed wrapper that makes the inversion impossible to write.

Why

Ghostty's customKeyEventHandler returns true to BLOCK default handling, opposite of xterm.js. packages/app/src/components/terminal.tsx:408 returned true for every non-toggle keystroke, so Ghostty silently dropped the key before onData fired and nothing reached the PTY. Cursor and focus ring kept rendering, matching the reporter's "looks focused but no input" description. The bug was inherited from the initial OpenCode import (bde4c3927, upstream PR #5081) where the boolean semantics were almost certainly copy-pasted from xterm.js docs.

The fix introduces packages/app/src/utils/terminal-key-handler.ts, a ten-line wrapper that exposes a "block" | "passthrough" enum. Writing return true is rejected by TypeScript, and missing returns are rejected by the union — the upstream-class bug becomes structurally impossible. The handler is rewritten with intercept-first / passthrough-default ordering, and the toggle branch is corrected to return block (previously returned the wrong value, letting Ghostty leak a Ctrl+` escape sequence to the PTY while the toggle command fired in parallel).

Related Issue

Closes #696.

Human Review Status

Pending.

Review Focus

The wrapper is the structural defense; please scrutinise the contract in terminal-key-handler.ts and confirm the rewritten handler in terminal.tsx covers the same three branches (Ctrl+Shift+C, toggle keybind, default passthrough) with the right enum value at each return point.

Risk Notes

Renderer-only change; no IPC, preload, native menu, updater, packaging, or signing surfaces are touched. Behaviour parity between dev:web and Electron renderer holds, so manual dev:desktop was skipped intentionally in favour of E2E plus visual screenshots. The third branch fix (toggle returning block) removes a previously-leaked escape sequence to the PTY but this was never user-visible.

How To Verify

bun --cwd packages/app run typecheck: ok (tsgo -b clean)
bun --cwd packages/app run test:unit: 1172 passed
bun --cwd packages/opencode test test/config/e2e-smoke-tagging.test.ts: 2 passed
eslint on changed files: 0 errors
E2E e2e/terminal/terminal-input.spec.ts (with fix): 1 passed in 7.5s
E2E e2e/terminal/terminal-input.spec.ts (with fix reverted to broken behaviour locally): times out at the exact terminalHas poll, reproducing the user-reported symptom

Screenshots or Recordings

01-echo-output 02-toggle-hidden

Checklist

  • Human review status is stated above as pending, approved, or not required
  • I linked the related issue, or stated why there is no issue
  • This PR has exactly one type label (bug, enhancement, task, or documentation), at least one primary routing label (app, ui, platform, harness, or ci), and exactly one priority label (P0 to P3), or I requested maintainer labeling
  • I described the review focus and any meaningful risks
  • I listed the relevant verification steps and the key result for each
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope
  • I manually checked visible UI or copy changes when needed, with screenshots or recordings
  • I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant
  • I reviewed the final diff for unrelated changes and suspicious dependency changes
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

Ghostty's `customKeyEventHandler` uses `true` to BLOCK default handling — opposite of xterm.js. terminal.tsx returned `true` for every non-toggle keystroke, so Ghostty silently dropped the key before invoking `onData`, preventing any input from reaching the PTY (#696). Cursor and focus ring still rendered, which is exactly what the reporter described.

Introduce a typed wrapper `@/utils/terminal-key-handler` that exposes a `"block" | "passthrough"` enum, so the boolean contract is impossible to invert. The wrapper compresses the cross-library quirk into one ten-line file: future authors get a compile error if they try `return true`, and missing returns are rejected by the union type. The same change rewrites the handler with intercept-first / passthrough-default ordering and fixes the toggle branch (which previously returned `false`, letting Ghostty leak a Ctrl+\` escape sequence to the PTY while the toggle command fired).

The bug was present since the initial OpenCode import (`bde4c3927`), inherited from upstream PR #5081 where the boolean semantics were likely copy-pasted from xterm.js docs.

Add an E2E regression spec that types via `runTerminal` and asserts the echoed token reaches the rendered buffer. Confirmed locally that reverting the wrapper return to `"block"` for the default branch makes the spec time out at the exact assertion the user-reported symptom maps to.
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Astro-Han has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 45 minutes and 8 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 77ea8172-e1de-4f6f-a5ef-2ca513cfe150

📥 Commits

Reviewing files that changed from the base of the PR and between 2ddedf9 and 9583e8e.

📒 Files selected for processing (3)
  • packages/app/e2e/terminal/terminal-input.spec.ts
  • packages/app/src/components/terminal.tsx
  • packages/app/src/utils/terminal-key-handler.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/i696-terminal-input

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added app Application behavior and product flows ui Design system and user interface P2 Medium priority labels May 18, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested priority: P2 (includes user-path files (packages/app/src/components/terminal.tsx, packages/app/src/utils/terminal-key-handler.ts)).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

@Astro-Han Astro-Han added the bug Something isn't working label May 18, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a typed wrapper, attachKeyHandler, for Ghostty's custom key event handler to prevent boolean inversion bugs and adds an E2E regression test for terminal input. Feedback includes improving E2E test stability by avoiding .first() locators and optimizing performance by caching parsed keybind configurations instead of re-parsing them on every keystroke.

Comment thread packages/app/e2e/terminal/terminal-input.spec.ts
Comment thread packages/app/src/components/terminal.tsx
@Astro-Han Astro-Han added P1 High priority and removed P2 Medium priority labels May 18, 2026
@github-actions

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown

Perf delta summary

Comparator: pass

Profile / Scenario interaction median interaction worst long task max tbt frame gap p95 frame gap max jank count cls status
default / homepage-cold 24 -> 24 (0) 40 -> 64 (+24) 66 -> 71 (+5) 16 -> 21 (+5) 16.8 -> 16.8 (0) 116.6 -> 116.6 (0) 3 -> 4 (+1) 0 -> 0 (0) pass
default / long-session-input-lag 48 -> 40 (-8) 48 -> 48 (0) 0 -> 0 (0) 0 -> 0 (0) 16.7 -> 16.8 (+0.1) 16.8 -> 16.8 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-streaming-long 40 -> 48 (+8) 56 -> 64 (+8) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.7 (-0.1) 16.8 -> 33.3 (+16.5) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-call-expand 16 -> 16 (0) 24 -> 16 (-8) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.8 (0) 16.8 -> 16.8 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-default-open-heavy-bash 24 -> 24 (0) 24 -> 40 (+16) 62 -> 61 (-1) 13 -> 11 (-2) 33.3 -> 49.9 (+16.6) 166.6 -> 100.1 (-66.5) 1 -> 2 (+1) 0 -> 0 (0) pass
default / terminal-side-panel-open 56 -> 40 (-16) 56 -> 48 (-8) 0 -> 0 (0) 0 -> 0 (0) 33.4 -> 33.3 (-0.1) 33.4 -> 33.3 (-0.1) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-scroll-reading 24 -> 24 (0) 32 -> 40 (+8) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.7 (-0.1) 16.8 -> 16.7 (-0.1) 0 -> 0 (0) 0.505 -> 0.505 (0) warn: cls

@Astro-Han
Astro-Han force-pushed the claude/i696-terminal-input branch from 642a9fb to 9583e8e Compare May 18, 2026 09:22
@Astro-Han
Astro-Han merged commit 18b9429 into dev May 18, 2026
26 checks passed
@Astro-Han
Astro-Han deleted the claude/i696-terminal-input branch May 18, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Application behavior and product flows bug Something isn't working P1 High priority ui Design system and user interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Right-side Terminal panel does not accept keyboard input

1 participant