VSCode extension: add option to prevent panel from stealing focus
Problem
When using Claude Code as a VSCode extension, the panel auto-reveals and steals focus whenever it produces output. This interrupts workflow in other editor tabs — for example, if I'm typing in a file and Claude finishes a response, focus jumps to the Claude panel.
Expected behavior
The extension should have an option to not steal focus when producing output. The user should be able to check Claude's output on their own terms.
Suggested solution
Add a preserveFocus setting (e.g., claude-code.preserveFocus: true) that prevents the extension panel from auto-revealing or grabbing focus when new output arrives. VSCode's WebviewPanel API supports preserveFocus natively, so this should be straightforward to wire up.
Workarounds
Currently the only workarounds are:
- Running Claude in terminal mode (
claude-code.useTerminal: true) - Running
claudedirectly in the integrated terminal
Neither gives the full panel experience without the focus interruption.
15 Comments
Just to clarify for anyone landing here — the notification hook doesn't stop the panel from stealing focus. It gives you a notification when Claude is waiting for input, but the
WebviewPanelstill callsreveal()withoutpreserveFocus: true, so your editor focus still gets yanked every time there's output.The actual fix needs to happen in the extension itself — passing
preserveFocus: truetoWebviewPanel.reveal(). The hook is a nice complement (get notified without watching the panel), but it's not a solution to the focus-stealing issue described in this ticket.Running into this too, specifically when using View: Toggle Maximize Panel — the Claude Code webview grabs focus instead of the panel maximizing. The issue has been reported in #14995 but it seems like there was no actual fix. Hopefully addressing this issue will also fix the focus stealing from panel maximizing. Many thanks to all involved!
+1. Running Claude Code in Cursor with "ask before edits" mode. The diff tabs steal focus from the active editor, which is highly disruptive. The
preserveFocusfix would make a huge difference — please prioritize this.Related variant: when Claude Code calls MCP server tools (e.g.,
mcp__jcodemunch__search_text), each tool result opens as a new readonly editor tab (\temp\readonly\mcp__jcodemunch__search_text tool output (hpsr5d)). These tabs steal focus from whatever file you're working in.This is especially disruptive when using MCP servers for code navigation — a single task can trigger 5-10 tool calls, each one opening a new tab over your active editor. The tabs are temporary/readonly and serve no purpose after Claude processes the result.
A
preserveFocussetting would help, but ideally MCP tool output tabs shouldn't open as editor tabs at all — or at minimum there should be a setting to suppress them. The tool results are consumed by Claude, not the user.Environment: Claude Code VS Code extension, Windows 11, multiple MCP servers (jCodemunch, jDocmunch, jDatamunch)
Yes! This is very frustrating. I often press enter or escape for some unrelated reason and accidentally accept or reject claude's suggestion because of this.
This is very annoying and potentially dangerous if you're pressing enter when something pops up. Please give us the option to disable this.
That is really annoying. We still code by ourselves; Claude Code should not steal focus.
The same behavior is in PyCharm 2025.2.4. It is so annoying when i type the next prompt to the queue, the spacebar auto-confirms something. It would be good if I could disable this as well.
Confirmed in the code: the plan panel preserves focus, but the session tab doesn't.
Running multiple sessions, when one session presents a plan I get yanked to that session's tab while typing in another. I dug into the extension (v2.1.167,
extension.js):preserveFocus: true:createWebviewPanel("claudePlanPreview", title, { viewColumn, preserveFocus: true }, ...)— so the plan panel itself does not steal focus.reveal()calls in the extension are made without thepreserveFocusargument, so they default to taking focus. One of them is the callback handed to the session controller that fires when a session wants attention, which is what pulls me away.The fix looks small: pass
preserveFocus: trueto thosereveal(viewColumn, preserveFocus)calls (or gate it behind a setting). The blue/orange status dot is already enough of a signal; I'd like to decide myself when to visit a session instead of being pulled to it.Adding a multi-window use case for this. I run several Claude Code sessions at once, each in its own VS Code window (Windows 11). When I'm typing a prompt in one window and a session in another window proposes an edit, the diff opens and pulls focus away from where I'm working, so I lose my place and the edited file jumps on top of everything else. A preserveFocus option as suggested here would fix the worst of it: let the diff/output appear without raising the window or grabbing focus, so I can review on my own terms. Strong +1.
Hitting this constantly in the VS Code extension, and the impact is bigger than "annoying": it makes it impossible to do anything else while Claude is working.
The whole point of a long-running agent task is to use that time - edit a file, write notes, answer something in another window. With the panel grabbing focus on every output, that time is unusable: you get pulled back mid-keystroke, repeatedly, for the entire duration of the task.
claude-code.preserveFocusas proposed here would fix it. Auto-reveal on the first output would be a fine default; re-stealing focus on every subsequent chunk is what makes it unworkable.Env: Claude Code 2.1.217, VS Code 1.130.0.
Some findings from digging through the shipped bundle (
anthropic.claude-code-2.1.220), which I think narrow this down usefully. There are two independent focus steals, the diff one is already fixed upstream, and the remaining one is in the webview and is deliberate.The diff paths already preserve focus
Both diff-opening call sites in 2.1.220 already pass
preserveFocus: true:So "pass
preserveFocuswhen opening the diff" is done. Anyone still seeing focus jumps is hitting one of the two paths below.Steal 1 —
openFile(extension host)The MCP
openFilehandler computespreserveFocusfrom a parameter that defaults to taking focus:With
makeFrontmostdefaulting totrue,preserveFocusevaluates tofalse— focus is taken deliberately, and there's no way to opt out short of the caller passingmakeFrontmost: false.Steal 2 — the permission prompt (webview)
This is the one that survives after fixing the above, and I think it's the bigger cause of reports in this thread: it fires on the approval path, so
acceptEdits/bypassPermissionsaren't workarounds for anyone who wants to keep reviewing changes.In
webview/index.js, the permission-request component schedules this 500ms after mount:fis the primary Yes button,yis thepermissionRequestContainerdiv (tabIndex: 0).The first branch is well-behaved — it only focuses the button when the webview already has focus. The
else ifis the problem: it runs only whendocument.hasFocus()is false, which is exactly the case where the user is typing in an editor or terminal. So an Edit/Write approval pulls focus out of wherever you were working, half a second after it appears — long enough that you're mid-keystroke when it lands.The existing
isTextEntrybail doesn't help, because it inspects the webview'sdocument.activeElement. When focus is in an editor that'sbody, not an input.The choke point is
safeFocus, which every focus call in the app routes through:It guards on the panel being visible, never on it being focused — so a visible-but-unfocused panel can still pull focus at will. Same applies to the terminal-kickback dialog, which calls
safeFocusunconditionally on mount.Proposed change
A
claudeCode.preserveFocusboolean (defaultfalse, so current behaviour is unchanged), gating both steals:openFile— gate themakeFrontmostdefault soshowTextDocumentreceivespreserveFocus: true.safeFocus— add a focus guard at the choke point:The
document.hasFocus()condition is what makes this safe to apply at the choke point rather than per-call-site: focus moves the user initiated inside the panel happen while the panel has focus and still work, while every cross-window grab is suppressed. The permission prompt keeps its keyboard flow — thef.current && document.hasFocus()branch is unaffected — and only theelse iffallback goes quiet.The one behaviour worth checking is panel open: if the webview's mount effects run before VS Code's focus lands, the composer may come up unfocused. If that races in practice, dropping the
else ifbranch alone fixes the reported issue with no such risk, just less coverage of the other paths.Note the settings namespace is camelCase in
package.json(claudeCode.autosave,claudeCode.preferredLocation, …), soclaudeCode.preserveFocusrather thanclaude-code.preserveFocus.Related: tab accumulation
Separately, both diff call sites pass
preview: false, which is why diff tabs open as permanent tabs and pile up rather than reusing the preview slot. That's the complaint in #25018, #52832 and #59820, andpreview: true(or the same setting) would address it independently of the focus issue.Temporary fix
For anyone who wants this today, both conditions can be forced in the installed bundle. This is a local hack — it's overwritten by every extension update, and the minified identifiers may shift between versions, so the script below verifies each pattern is present exactly once and stops if not.
Then run Developer: Reload Window for the extension host and webview to pick it up.
To undo:
and reload, or reinstall the extension.
On Windows the extensions directory is
%USERPROFILE%\.vscode\extensionsinstead.The host edit is a single character (
!o→!0); the webview edit adds one&&document.hasFocus()clause. Confirmed working on 2.1.220 — happy to test a proper fix against a pre-release if that's useful.Note: fix produced by Claude (Opus 5.0), and verified personally.
Following up on the 2.1.220 patch above — it fixes the diff/
openFilesteal, butsafeFocusis not the only choke point, so the panel still pulls focus on some paths. Onanthropic.claude-code-2.1.220I count ~15 raw.focus()calls inwebview/index.jsthat never go throughsafeFocus. The one that reproduces most reliably is theAskUserQuestioncomponent, which focuses the first option on mount:Others on the same footing:
plus the modal component, which auto-focuses its primary button on open (
h.focus()/p.focus()/a.current?.focus()).Guarding each call site is a losing game, so the local fix below moves the guard one level down — to
HTMLElement.prototype.focusitself. When the webview document isn't focused, the focus call isn't dropped, it's deferred: the element is remembered and focused once the panel actually receives focus. That avoids the panel-open race mentioned above (composer coming up unfocused), while every cross-pane grab goes quiet. It also subsumes thesafeFocusedit — that one becomes redundant, though harmless if you already applied it.The host-side
openFileedit is still needed (different process), so it's included and is idempotent.Worth noting what I deliberately left alone:
()=>e.show()(×2, theclaudeVSCodeSidebar/claudeVSCodeSidebarSecondaryproviders) and()=>e.reveal()inextension.jsare also missingpreserveFocus, but they're wired tomakeVisible, which only fires when you click a notification button — being taken to the panel there is the point.Same caveat as before: this is a local hack, overwritten by every extension update, and the minified identifiers shift between versions, so the script verifies before touching anything and is safe to re-run.
Then run Developer: Reload Window so both the extension host and the webview pick it up.
To undo:
and reload, or reinstall the extension. On Windows the extensions directory is
%USERPROFILE%\.vscode\extensions.Confirmed on 2.1.220 (Linux, VS Code): with this in place, an
AskUserQuestionprompt arriving while I'm typing in an editor no longer moves the caret — the panel renders the question and the option gets focused only when I switch to the panel myself.Also affects the
AskUserQuestionpopup specifically: if you're typing in another editor tab when Claude shows a clarifying-question dialog, focus jumps straight to the popup and interrupts typing mid-word. Same root cause as described above (WebviewPanel.reveal()withoutpreserveFocus: true), just a different trigger than every-output-reveal. Would love to see this land.Even worse, a key press accepts the first pre-selected option before gou can even read the question.