Skip to content

fix(cli): ignore literal Tab input in BaseTextInput#3270

Merged
tanzhenxin merged 2 commits intoQwenLM:mainfrom
shenyankm:sheny/fix-tab-literal-insertion
Apr 15, 2026
Merged

fix(cli): ignore literal Tab input in BaseTextInput#3270
tanzhenxin merged 2 commits intoQwenLM:mainfrom
shenyankm:sheny/fix-tab-literal-insertion

Conversation

@shenyankm
Copy link
Copy Markdown
Contributor

TLDR

Fixes Tab key handling in BaseTextInput / useTextBuffer so pressing Tab as a keypress no longer inserts a literal \t into the input buffer.

Screenshots / Video Demo

The before-fix behavior is shown in the video attached to the linked issue: #3268.

After fix: pressing Tab no longer changes the buffer content unless a consumer handles Tab explicitly.

No additional video is attached to this PR. The behavior was validated manually by pressing Tab in the interactive prompt and confirming that no literal tab was inserted.

Dive Deeper

Tab is used as a control key for completion, focus switching, and other consumer-specific behavior. When no consumer intercepted Tab, the default text-buffer insertion path could treat it as normal input and insert \t.

This change makes both BaseTextInput and useTextBuffer ignore non-paste Tab keypresses by default. Consumers that need Tab behavior should continue to intercept it via onKeypress.

Reviewer Test Plan

  1. Run Qwen Code interactively.
  2. Focus an input backed by BaseTextInput.
  3. Press Tab when no completion or consumer Tab handler is active.
  4. Confirm the input buffer does not receive a literal tab character.

Testing Matrix

🍏 🪟 🐧
npm run
npx
Docker
Podman - -
Seatbelt - -

Windows validation:

  • Built locally with npm run build.
  • Bundled locally with npm run bundle.
  • Manual interactive check with node dist/cli.js.
  • Confirmed pressing Tab in the interactive prompt no longer inserts a literal tab character.
  • Environment: Windows win32 x64, Qwen Code 0.14.4.

Linked issues / bugs

Fixes #3268

- Prevent insertion of literal tab characters in the BaseTextInput component
- Require consumers to intercept Tab via onKeypress for custom behavior
- Ensure smoother handling of Tab key without affecting buffer content
Copy link
Copy Markdown
Collaborator

@tanzhenxin tanzhenxin left a comment

Choose a reason for hiding this comment

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

Review

Nice fix — the approach of guarding at both BaseTextInput and useTextBuffer layers is the right call, and the paste exception is handled correctly for the bracketed-paste path.

Issues

  1. Single-line paste regression on pasteWorkaround platforms. On Windows / Node <20, the pasteWorkaround pipeline forwards pasted content through the normal keypress path without setting paste: true (see KeypressContext.tsx flushRawBuffer — single-line data without \r is written directly to keypressStream without paste markers). This means tab characters in single-line pastes (e.g. tab-separated data from Excel) will be silently stripped by the new guard.

Verdict

COMMENT — The fix is correct for the standard path, but please verify behavior on the pasteWorkaround path before merging. Consider an approach that doesn't rely solely on the paste flag, or ensure the workaround path sets paste: true for forwarded content.

- Mark single-line raw chunks containing tabs as paste events in the pasteWorkaround path
- Keep a literal Tab key as a non-paste keypress
- Add KeypressContext coverage for literal Tab keys and single-line tab-separated raw chunks
@shenyankm
Copy link
Copy Markdown
Contributor Author

Review

Nice fix — the approach of guarding at both BaseTextInput and useTextBuffer layers is the right call, and the paste exception is handled correctly for the bracketed-paste path.

Issues

  1. Single-line paste regression on pasteWorkaround platforms. On Windows / Node <20, the pasteWorkaround pipeline forwards pasted content through the normal keypress path without setting paste: true (see KeypressContext.tsx flushRawBuffer — single-line data without \r is written directly to keypressStream without paste markers). This means tab characters in single-line pastes (e.g. tab-separated data from Excel) will be silently stripped by the new guard.

Verdict

COMMENT — The fix is correct for the standard path, but please verify behavior on the pasteWorkaround path before merging. Consider an approach that doesn't rely solely on the paste flag, or ensure the workaround path sets paste: true for forwarded content.

Thanks for the review. I addressed this based on the suggested direction: the pasteWorkaround path now sets paste: true for forwarded single-line raw chunks containing tabs.

The PR now has two parts:

  • BaseTextInput / useTextBuffer ignore non-paste literal Tab keypresses.
  • KeypressContext marks single-line tab-separated raw chunks in the pasteWorkaround path as paste events, so pasted content like A\tB\tC is preserved.

I also added tests covering both cases:

  • a literal Tab key remains a non-paste keypress
  • a single-line tab-separated raw chunk is emitted as a paste event

Validation: cd packages/cli && npx vitest run src/ui/contexts/KeypressContext.test.tsx src/ui/components/shared/text-buffer.test.ts

Result: 2 test files passed, 224 tests passed.

Manual validation on Windows with node dist/cli.js confirmed that pressing Tab does not insert a literal tab, while pasting A\tB\tC preserves the tab separators.

This covers the pasteWorkaround path because it is enabled on Windows.

QQ20260415-115732

@tanzhenxin
Copy link
Copy Markdown
Collaborator

Ran an e2e test plan against the latest commit (970eafdd) to verify the fix and guard against regressions on the earlier tab-paste behavior. All 5 cases passed on Linux (node dist/cli.js, tmux 200x50, --approval-mode yolo).

TC Scenario Result
TC1 Type hello, press Tab, type world — inspect input buffer PASS — prompt shows helloworld, Tab silently discarded
TC2 Type prompt, press Tab, press Enter — check for embedded \t in submitted content PASS — model receives clean prompt, echoes banana
TC3 Bracketed paste of 13354\t2945\t58 PASS — tabs preserved, model receives all three numbers with tab-width spacing
TC4 Multi-line bracketed paste (Name\tAge\nAlice\t30\nBob\t25) PASS — table structure preserved end-to-end
TC5 Space-separated sanity check PASS — no regression

LGTM from my side. Thanks for the quick turnaround on the follow-up commit.

@tanzhenxin tanzhenxin merged commit 679446d into QwenLM:main Apr 15, 2026
12 of 13 checks passed
@shenyankm shenyankm deleted the sheny/fix-tab-literal-insertion branch April 16, 2026 00:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tab key inserts a literal tab into BaseTextInput

2 participants