-
Notifications
You must be signed in to change notification settings - Fork 4.9k
feat(web): add a rebindable thread.interrupt keybinding #5669
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5841395
4eb8ddd
25860a9
0b759f7
b00ab8c
0c3b692
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,9 +39,14 @@ import { | |
| detectComposerTrigger, | ||
| expandCollapsedComposerCursor, | ||
| replaceTextRange, | ||
| shouldInterruptRunningThreadFromKeybinding, | ||
| shouldSubmitComposerOnEnter, | ||
| } from "../../composer-logic"; | ||
| import { deriveComposerSendState, readFileAsDataUrl } from "../ChatView.logic"; | ||
| import { | ||
| deriveComposerSendState, | ||
| OPEN_FLOATING_LAYER_SELECTOR, | ||
| readFileAsDataUrl, | ||
| } from "../ChatView.logic"; | ||
| import { | ||
| dataTransferHasComposerMention, | ||
| makeComposerMentionDragHandlers, | ||
|
|
@@ -2275,6 +2280,52 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) | |
| terminalOpen, | ||
| ]); | ||
|
|
||
| // Interrupting the running turn is a keybinding rather than a hard-coded key | ||
| // so it shows up in Settings and can be rebound. It only claims the shortcut | ||
| // while a turn is running; otherwise the key falls through untouched. | ||
| // | ||
| // Bubble phase, unlike the capture-phase composer.stash listener above: | ||
| // stash must beat the browser's save dialog, while interrupt must lose to | ||
| // any open overlay that dismisses on the same key. By the time the event | ||
| // bubbles to window every overlay handler has run, and a dismiss marks the | ||
| // event consumed via preventDefault. | ||
| useEffect(() => { | ||
| const handler = (event: globalThis.KeyboardEvent) => { | ||
| // Interrupt is one-shot: key auto-repeat would spray concurrent | ||
| // interrupt requests that race each other for the same turn. | ||
| if (event.repeat) return; | ||
| // An overlay (dialog, menu, select, command palette) already consumed | ||
| // this press to dismiss itself. | ||
| if (event.defaultPrevented) return; | ||
| const command = resolveShortcutCommand(event, keybindings, { | ||
| context: { | ||
| terminalFocus: getTerminalFocusOwner() !== null, | ||
| terminalOpen, | ||
| modelPickerOpen: isComposerModelPickerOpen, | ||
| }, | ||
| }); | ||
| if ( | ||
| !shouldInterruptRunningThreadFromKeybinding({ command, isRunning: phase === "running" }) | ||
| ) { | ||
| return; | ||
| } | ||
| // An open floating layer owns the keyboard even when its dismiss | ||
| // handler has not run yet — window bubble listeners fire in | ||
| // registration order, so a later-mounted overlay (the expanded image | ||
| // viewer) marks the event only after this handler has already seen it. | ||
| // Checking the DOM instead of the event answers "is an overlay open" | ||
| // regardless of listener ordering; the model picker is excluded via | ||
| // the default binding's when clause. | ||
| if (isCommandPaletteOpen() || document.querySelector(OPEN_FLOATING_LAYER_SELECTOR) !== null) { | ||
| return; | ||
| } | ||
| event.preventDefault(); | ||
|
macroscopeapp[bot] marked this conversation as resolved.
|
||
| onInterrupt(); | ||
| }; | ||
| window.addEventListener("keydown", handler); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 High The 🤖 Copy this AI Prompt to have your agent fix this:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Valid. Fixed in b00ab8c: ChatView's handler now resolves the shortcut before the type-to-focus branch, and type-to-focus only claims keys that resolve to no binding. An explicit binding on a bare printable key wins consistently — for every command, not just
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I'm unable to act on this request because you do not have permissions within this repository. |
||
| return () => window.removeEventListener("keydown", handler); | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| }, [isComposerModelPickerOpen, keybindings, onInterrupt, phase, terminalOpen]); | ||
|
|
||
| // ------------------------------------------------------------------ | ||
| // Callbacks: images | ||
| // ------------------------------------------------------------------ | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.