Skip to content

feat(app): make prompt action keybinds configurable - #55313

Open
CasualDeveloper wants to merge 3 commits into
anomalyco:devfrom
CasualDeveloper:feat/43088-prompt-keybinds
Open

CasualDeveloper wants to merge 3 commits into
anomalyco:devfrom
CasualDeveloper:feat/43088-prompt-keybinds

Conversation

@CasualDeveloper

@CasualDeveloper CasualDeveloper commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #43088

Replacement for #43128, which was closed by automated age/reaction cleanup. I have kept the branch maintained and rebased it onto dev; GitHub cannot retarget the closed PR from retired v2.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Makes prompt.submit, prompt.newline, and prompt.submit.alternate configurable in the existing App Keyboard Shortcuts settings. Desktop defaults stay Enter, Shift+Enter, and Ctrl/Cmd+Enter. Alternate delivery performs Queue/Steer opposite the follow-up preference. Touch keyboard Return keeps adding a line; its bar and hardware Cmd/Ctrl+Return send.

The Composer handles these bindings locally so suggestions, IME guards, selection replacement, native newline undo, and repeat suppression stay editor-owned. IME-owned chords stay uncancelled, including modified Enter. Bindings can be swapped or disabled; submit and Queue/Steer hints show the effective shortcuts. TUI settings are unchanged.

Legacy modifier variants are declared alongside the defaults, so Settings conflict checks and editor dispatch agree. Hover or focus a shortcut to see its alternatives. Explicit overrides match exactly.

No feature flag: customization is opt-in through existing App settings, with the default desktop and touch behavior preserved.

How did you verify your code works?

I verified fork commit a82a346ba1 against local dev base 73c95abac2, using Bun 1.4.4 and Node 26.13.0 for Playwright:

  • Root bun run check (34 workspace tasks), official App typecheck:tsc, production build, 2,611 unit cases, and 358 Happy DOM cases passed. The modified-Enter IME regression fails before the fix and passes after it.
  • App components: 267/270 passed cold; the three flakes and all Composer cases passed a 67-case targeted run. The same non-Composer suites pass on the base. Session UI passed all 209 cases cold; final touch/IME E2E passed 4/4.
  • Both shortcut E2E cases passed ten times each at five-worker concurrency without retries, including Alt+Enter, submitted multiline text, selection replacement, native undo, hints, delivery, disabling, and reset.
  • The broader built E2E run passed 86/88 cases. Both remaining multiline-paste positioning failures reproduce on the unmodified base. The standalone E2E typecheck reports 182 errors identical to the base after the feature's import-line shift; all 550 lint warnings also match.

Real-platform IME sequencing and Windows Desktop have not been manually verified.

Visual

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="72" viewBox="0 0 240 72" fill="none" role="img" aria-label="Choose your prompt shortcuts">
  <style>
    .press{animation:press 3s ease-in-out infinite}
    .send{animation:press 3s 1.5s ease-in-out infinite}
    @keyframes press{0%,35%,100%{opacity:.35}15%,25%{opacity:1}}
    @media(prefers-reduced-motion:reduce){.press,.send{animation:none}}
  </style>
  <g stroke="currentColor" stroke-width="1.5">
    <rect x="8" y="10" width="224" height="52" rx="10" opacity=".2"/>
    <rect x="18" y="20" width="56" height="30" rx="5" opacity=".55"/>
    <rect x="126" y="20" width="64" height="30" rx="5" opacity=".55"/>
    <path class="press" d="M104 26v12H86m0 0 5-5m-5 5 5 5" stroke-linecap="round" stroke-linejoin="round"/>
    <g class="send" stroke-linecap="round" stroke-linejoin="round">
      <path d="M211 46V26m-6 6 6-6 6 6"/>
    </g>
  </g>
  <g fill="currentColor" font-family="system-ui" font-size="11" text-anchor="middle">
    <text x="46" y="39">Enter</text>
    <text x="158" y="39">Ctrl+Enter</text>
  </g>
</svg>

Screenshots / recordings

Before: the upstream Settings screen has no prompt-action entries.

Before: no prompt shortcut settings

After: submit, newline, and alternate delivery appear in Shortcuts.

After: configurable prompt actions

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #43897, #54520. App/TUI configuration remains separate.

@github-actions

Copy link
Copy Markdown
Contributor

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

@CasualDeveloper
CasualDeveloper marked this pull request as ready for review October 14, 2026 05:19

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I read through the change and ran the new unit tests, the related browser-environment, settings and command tests, and typecheck in packages/app; all pass. I couldn't run the new Playwright case: Chromium won't start on this host (a system library is missing). Before/after screenshots or a short video of the Settings rows and the composer hints would still help.

Public API: unchanged. The change only adds three entries, prompt.submit, prompt.newline and prompt.submit.alternate, to the App's locally stored keyboard-shortcut settings. Nothing changes in the config schema, the SDK, the server or the TUI.

The structure looks sound. Handling these keys inside the editor and registering them as disabled commands keeps the global shortcut handler out of the way.

Concerns (details inline):

  • Default matching: when a shortcut has no saved value, matching is looser than what Settings shows and checks for conflicts. It also changes as soon as the user sets any one of the three shortcuts.
  • E2E test size: the new test is one long scenario covering everything.
  • Storybook: the Storybook additions (the ConfigurableShortcuts story, wrapping every story in SettingsProvider, and an unrelated reformatted line) aren't needed for the feature and could go in a separate PR.

Fixes #43897
Related: #54520

Comment thread packages/app/src/composer/keybinds.ts Outdated

if (override !== undefined) return matchKeybind(parseKeybind(override), event)

if (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When a shortcut has no saved value, it doesn't match what Settings shows. Settings lists enter / shift+enter / mod+enter and checks conflicts against those, but:

  • submit also fires on Alt+Enter;
  • newline fires on any Shift+Enter combination, including Ctrl/Cmd+Shift+Enter;
  • alternate fires on both Ctrl+Enter and Cmd+Enter on every platform.

The behaviour also changes once the user sets any one of the three: the cross-override check disables part of the other two defaults. For example, saving submit as enter makes Alt+Enter do nothing, because the composer now swallows unbound Enter keys.

This makes behaviour hard to predict from the Settings screen. Could the defaults be plain strings matched exactly with matchKeybind, like other commands? If the legacy variants need to stay, they could be listed in the default (e.g. enter,alt+enter) so Settings and conflict detection see them too.

.toBe(true)
}

test("prompt shortcuts rebind through Settings, preserve newline undo, and restore defaults", async ({ page }) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This single E2E case is about 100 lines. It covers rebinding through Settings, newline undo with a selection, tooltip contents, steer/queue delivery, disabling everything and restoring defaults. It's long and slow, and if one step fails the rest of the scenario doesn't run. The unit tests already cover the matching logic. Could this be cut down to the core path (rebind Enter to newline and Ctrl+Enter to submit, check both work, then reset) and drop the rest, or split it into a couple of short cases?

@CasualDeveloper
CasualDeveloper force-pushed the feat/43088-prompt-keybinds branch from ad364b9 to bb8b3f9 Compare October 14, 2026 05:44
@OLIDEA

OLIDEA commented Oct 14, 2026

Copy link
Copy Markdown

Thanks for this PR!

I'm using OpenCode Desktop on Windows, and I'd like to use Alt+Enter to insert a newline in the prompt, while keeping Enter for submission.

Currently, Shift+Enter works, but Alt+Enter doesn't.

Will this PR allow assigning Alt+Enter to prompt.newline on Windows?

@CasualDeveloper

Copy link
Copy Markdown
Contributor Author

Updated in bedf26bb2c: defaults and overrides now share one resolver, all 16 legacy Enter modifier combinations remain covered, and Settings exposes the alternatives and checks their conflicts. I split the UI keeper into two independent cases without dropping its integration coverage. Settings providers are now scoped to the Composer fixtures; I kept the safe rebinding story and removed the unrelated formatting. Before/after screenshots are in the PR.

@OLIDEA: yes, Alt+Enter for newline is covered. Save Submit as Enter first (releasing its legacy Alt+Enter alias), then assign Alt+Enter to Newline. I verified this in Chromium, not manually in Windows Desktop.

I changed #43897 to a related issue rather than closing it: shared App/TUI configuration is outside this PR.

@CasualDeveloper
CasualDeveloper force-pushed the feat/43088-prompt-keybinds branch from bedf26b to 4280845 Compare October 29, 2026 02:36
@CasualDeveloper
CasualDeveloper requested a review from a team October 29, 2026 02:36
@CasualDeveloper
CasualDeveloper force-pushed the feat/43088-prompt-keybinds branch 3 times, most recently from d37b15c to 4854139 Compare November 5, 2026 14:03
Expose submit, newline, and alternate delivery (queue/steer) in App Keyboard Shortcuts as disabled command entries, with editor-local dispatch. Preserve default bindings, suggestion acceptance, IME guards, repeat suppression, and native newline undo while supporting exact custom, swapped, or disabled bindings and effective shortcut hints.

Cover matching and dispatch with unit tests and bounded newline insertion with browser tests. Exercise live Settings rebinding, delivery, disabling, default restoration, hints, and native undo in the Composer E2E keeper, and provide a safe submit/newline rebinding story.

Closes anomalyco#43088
Declare preserved modifier variants and resolve custom chord ownership consistently for editor dispatch, shortcut hints, and Settings conflict checks. Show effective alternatives on focus or hover, and use native line-break insertion so trailing newlines survive continued typing and remain undoable.

Split the UI keeper into independent rebinding and alternate/reset flows, including Alt+Enter. Scope Settings providers to fixtures that render Composer and include before-and-after Settings media.
Track composition start and end in a pure state machine and leave IME-owned chords uncancelled, including modified Enter and Safari keyCode 229. Keep suggestion and history selection in the production controller and retain touch Return and hardware-submit behavior.

Exercise actual key dispatch across composition start and end, initialize legacy keyCode explicitly in Happy DOM, and cover all three IME ownership signals. Include the Settings fixture screenshots.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Make App prompt action shortcuts configurable

2 participants