Skip to content

Conversation

@Ashwin-droid
Copy link

What does this PR do?

Video
Fixes #8038

Summary

Fixes Windows prompt behavior where Shift+Enter was treated like Enter (submitting) instead of inserting a newline, restoring the intended “newline vs submit” UX.

Details

  • Observed behavior (Windows Terminal/ConPTY): Shift+Enter can be delivered as an unmodified return key event, so it matches the textarea’s return → submit binding (same path as plain Enter).
  • Implementation: Add a Windows-only modifier fallback in packages/opencode/src/cli/cmd/tui/context/keybind.tsx:
    • Load user32.dll via bun:ffi and call GetAsyncKeyState(VK_SHIFT) (VK_SHIFT = 0x10).
    • When receiving an event with:
      • evt.name === "return"
      • evt.shift/ctrl/meta/super/hyper all false
      • GetAsyncKeyState indicates Shift is currently down (state & 0x8000)
      • then set evt.shift = true before matching keybinds.
  • Scope control (narrow by design): Only applies to return events with no other modifiers and only on process.platform === "win32", so the change is intentionally limited to the broken case.

How did you verify your code works?

  • The attached screen capture makes the corrected behavior evident end-to-end on Windows (newline insertion with Shift+Enter, submit with Enter).
  • Ran bun run typecheck (repo typecheck via the pre-push hook / turbo typecheck).

@github-actions
Copy link
Contributor

Hey! Your PR title tui: fix Shift+Enter newline on Windows doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@github-actions
Copy link
Contributor

The following comment was made by an LLM, it may be inaccurate:

Potential Duplicate Found

PR #3965: Differentiate Shift+Enter from Enter for prompt submission
#3965

This PR appears to address the same issue - differentiating between Shift+Enter (newline) and Enter (submit) in the prompt/textarea. This is directly related to the current PR #8040, which fixes the Windows-specific case where this differentiation was broken.

@Ashwin-droid Ashwin-droid changed the title tui: fix Shift+Enter newline on Windows fix(tui): fix Shift+Enter newline on Windows Jan 12, 2026
@rekram1-node
Copy link
Collaborator

@kommander possibly something opentuis houdl handle?

@kommander
Copy link
Collaborator

Interesting workaround. @Hona you think that makes sense? Could bring that in opentui.

@Hona
Copy link
Collaborator

Hona commented Jan 12, 2026

I reached out on discord to kmdr, this is an API I've used in high render throughput C# console apps.
Good option.

@mynameistito
Copy link

mynameistito commented Jan 13, 2026

Oh interesting @kommander, I was having these same issues and was about to and get Claude or GLM to come up with a fix.

I was having these issues even with the following set in my keybinds;

"keybinds": {
	"input_newline": "shift+return",
	// more binds
}

This was GLM's take on this issue where shift+return was assigned in the opencode.jsonc and when it was pressed.

Analysis

I've traced through the keybinding system and identified the root cause:

Problem Location

packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts:67-69

Root Cause

The function adds hardcoded keybindings before user-configured bindings:

return [
  { name: "return", action: "submit" },  // Line 68
  { name: "return", meta: true, action: "newline" },  // Line 69
  ...TEXTAREA_ACTIONS.flatMap((action) => mapTextareaKeybindings(keybinds, action)),
]

The hardcoded { name: "return", action: "submit" } doesn't specify modifiers, which causes it to match shift+return when it should only match plain return. When the user presses shift+return, the hardcoded "submit" binding matches first, overriding the user's configured input_newline: "shift+return".

Current Defaults

(from config.ts:663-667)

  • input_submit: "return"
  • input_newline: "shift+return,ctrl+return,alt+return,ctrl+j"

These defaults are already in the config schema but are being ignored because of the hardcoded bindings.

Minimal Fix Plan

File: packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts

Changes Required:

  1. Add "submit" and "newline" to TEXTAREA_ACTIONS array (after line 7):
    • Add "submit" to the array
    • Add "newline" to the array
  2. Remove hardcoded bindings (lines 68-69):
    • Remove { name: "return", action: "submit" }
    • Remove { name: "return", meta: true, action: "newline" }

Result:

  • Total lines changed: ~4
  • All keybindings (submit and newline) will come from config via mapTextareaKeybindings
  • User configurations in opencode.json/jsonc will be fully respected
  • Default values from config schema will be used when user doesn't configure them
  • No more hardcoded conflicts

Behavior After Fix

  • User config: "input_newline": "shift+return"shift+return inserts newline ✓
  • User config: "input_submit": "return"return submits message ✓
  • Defaults work as expected without overrides ✓
  • User can disable binding with "input_submit": "none"

Relevant Chat

@Ashwin-droid
Copy link
Author

Oh interesting @kommander, I was having these same issues and was about to and get Claude or GLM to come up with a fix.

I was having these issues even with the following set in my keybinds;

"keybinds": {
	"input_newline": "shift+return",
	// more binds
}

This was GLM's take on this issue where shift+return was assigned in the opencode.jsonc and when it was pressed.

Analysis

I've traced through the keybinding system and identified the root cause:

Problem Location

packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts:67-69

Root Cause

The function adds hardcoded keybindings before user-configured bindings:

return [
  { name: "return", action: "submit" },  // Line 68
  { name: "return", meta: true, action: "newline" },  // Line 69
  ...TEXTAREA_ACTIONS.flatMap((action) => mapTextareaKeybindings(keybinds, action)),
]

The hardcoded { name: "return", action: "submit" } doesn't specify modifiers, which causes it to match shift+return when it should only match plain return. When the user presses shift+return, the hardcoded "submit" binding matches first, overriding the user's configured input_newline: "shift+return".

Current Defaults

(from config.ts:663-667)

  • input_submit: "return"
  • input_newline: "shift+return,ctrl+return,alt+return,ctrl+j"

These defaults are already in the config schema but are being ignored because of the hardcoded bindings.

Minimal Fix Plan

File: packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts

Changes Required:

  1. Add "submit" and "newline" to TEXTAREA_ACTIONS array (after line 7):

    • Add "submit" to the array
    • Add "newline" to the array
  2. Remove hardcoded bindings (lines 68-69):

    • Remove { name: "return", action: "submit" }
    • Remove { name: "return", meta: true, action: "newline" }

Result:

  • Total lines changed: ~4
  • All keybindings (submit and newline) will come from config via mapTextareaKeybindings
  • User configurations in opencode.json/jsonc will be fully respected
  • Default values from config schema will be used when user doesn't configure them
  • No more hardcoded conflicts

Behavior After Fix

  • User config: "input_newline": "shift+return"shift+return inserts newline ✓
  • User config: "input_submit": "return"return submits message ✓
  • Defaults work as expected without overrides ✓
  • User can disable binding with "input_submit": "none"

Relevant Chat

can you show some proof of this working? cause the fix i provided does showcase the fix applied and the behaviour working as intended?

@mynameistito
Copy link

mynameistito commented Jan 13, 2026

Oh interesting @kommander, I was having these same issues and was about to and get Claude or GLM to come up with a fix.
I was having these issues even with the following set in my keybinds;

"keybinds": {
	"input_newline": "shift+return",
	// more binds
}

This was GLM's take on this issue where shift+return was assigned in the opencode.jsonc and when it was pressed.

Analysis

I've traced through the keybinding system and identified the root cause:

Problem Location

packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts:67-69

Root Cause

The function adds hardcoded keybindings before user-configured bindings:

return [
  { name: "return", action: "submit" },  // Line 68
  { name: "return", meta: true, action: "newline" },  // Line 69
  ...TEXTAREA_ACTIONS.flatMap((action) => mapTextareaKeybindings(keybinds, action)),
]

The hardcoded { name: "return", action: "submit" } doesn't specify modifiers, which causes it to match shift+return when it should only match plain return. When the user presses shift+return, the hardcoded "submit" binding matches first, overriding the user's configured input_newline: "shift+return".

Current Defaults

(from config.ts:663-667)

  • input_submit: "return"
  • input_newline: "shift+return,ctrl+return,alt+return,ctrl+j"

These defaults are already in the config schema but are being ignored because of the hardcoded bindings.

Minimal Fix Plan

File: packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts
Changes Required:

  1. Add "submit" and "newline" to TEXTAREA_ACTIONS array (after line 7):

    • Add "submit" to the array
    • Add "newline" to the array
  2. Remove hardcoded bindings (lines 68-69):

    • Remove { name: "return", action: "submit" }
    • Remove { name: "return", meta: true, action: "newline" }

Result:

  • Total lines changed: ~4
  • All keybindings (submit and newline) will come from config via mapTextareaKeybindings
  • User configurations in opencode.json/jsonc will be fully respected
  • Default values from config schema will be used when user doesn't configure them
  • No more hardcoded conflicts

Behavior After Fix

  • User config: "input_newline": "shift+return"shift+return inserts newline ✓
  • User config: "input_submit": "return"return submits message ✓
  • Defaults work as expected without overrides ✓
  • User can disable binding with "input_submit": "none"

Relevant Chat

can you show some proof of this working? cause the fix i provided does showcase the fix applied and the behaviour working as intended?

Currently, in the process of trying to see what I have to do to actually test it.

03:34 UTC: Had a bunch of issues with typecheck, then finally was able to get it working in dev, but my implementation didn't fix it. Happy to try help though should need but not I am not technical Discord: mynameistito

Edit: consolidated messages.

Note: wasn't trying to do a this way is better too btw.

@Ashwin-droid
Copy link
Author

@kommander should i close this pr and open a more general fix pr on opentui?

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.

windows: cannot type in newline with shift enter.

5 participants