[BUG] Desktop: first click on a plugin pane button only moves focus; the button needs a second click

Status Open
Reported on v2.1.289
Maintainer reply None cached
Activity 7 comments · opened Oct 4, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

In the Claude Code desktop app (Code tab), a plugin pane opened with $.ui.open({ id, title, rows }) draws Buttons from a ui.render hook.

When the pane does not hold the keyboard focus (e.g. after any turn, when the focus goes back to the prompt), the first click on a button raises ui.focus onto that button but no ui.press. Only a second click presses it. Every button in the pane behaves this way. Built-in panes don't have this problem.

Opening the pane with focus: true makes the first click work, but only until the next turn takes the focus back to the prompt.

  • Claude Code desktop app (Code tab), Windows 11 Pro 10.0.26200.
  • Suggestion: have a pointer click on an unfocused pane both move the focus and raise ui.press, or expose the input kind (pointer vs keyboard) in UiFocusOrigin.

What Should Happen?

The ButtonProps docs say "a click, a hotkey, ... raises ui.press". A pointer click should press the button whether or not the pane holds the focus, as it does in built-in panes.

Error Messages/Logs

# Hook events logged by the plugin (ui.focus / ui.press hooks on that pane)

# Pane without the focus, one click:
ui.focus  element=rc4  origin=person
(no ui.press)

# Second click:
ui.press  element=rc4   -> onPress runs (2ms)

# Pane freshly opened with open({ focus: true }), one click:
ui.focus  element=r4   origin=person
ui.press  element=r4    -> onPress runs (2ms)

Steps to Reproduce

  1. A plugin with a pane: on('ui.render', { component: 'Pane', requestId: 'x' }, ...) returning a <Button onPress={...} />, opened with $.ui.open({ id: 'x', title: 'x', rows: 30 }).
  2. Add hooks that log ui.focus (matcher { requestId: 'x' }) and ui.press (matcher { plugin: '<name>' }).
  3. Open the pane, then send any prompt so a turn runs (the focus goes back to the prompt).
  4. Click the button once: only ui.focus is raised, nothing happens.
  5. Click it again: ui.press is raised and onPress runs.

A plugin can't work around it: UiFocusOrigin is { kind: 'person' } for a click, Tab and arrows alike, so pressing a button from ui.focus would also fire it on Tab (dangerous for a destructive button).

Claude Model

_No response_

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.289 (Claude Code)

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

PowerShell

Additional Information

  • Claude Code desktop app (Code tab), Windows 11 Pro 10.0.26200.
  • Suggestion: have a pointer click on an unfocused pane both move the focus and raise ui.press, or expose the input kind (pointer vs keyboard) in UiFocusOrigin.

<img width="870" height="449" alt="Image" src="https://github.com/user-attachments/assets/af7c6abb-3436-427c-af21-e1abe7bccbfa" />
<img width="406" height="119" alt="Image" src="https://github.com/user-attachments/assets/f53cc4e3-736b-4ee0-b46d-ebb1c0a811e7" />
<img width="899" height="686" alt="Image" src="https://github.com/user-attachments/assets/4f3f9e5b-cbc6-4d7c-9f00-2f8fffd03001" />

View original on GitHub ↗

3 Comments

kyungkoo · 8 days ago

Same on macOS.

  • Claude desktop app 2.26454.0, Code tab, Claude Code 2.1.289 (session) / 2.1.292 installed
  • macOS 26.7.1 (25G241)
  • Pane opened from a slash command ($.ui.open({ id, title }), no focus)

Every button in the pane needs two clicks, including a "back" button drawn after the first press already focused the pane. Logged from ui.focus / ui.press hooks in a separate mod:

86745ms focus Pane/list element=item:1 origin={"kind":"person"}
87820ms press my-mod/Pane element=item:1 surface=desktop
89159ms focus Pane/list element=back origin={"kind":"person"}
90256ms press my-mod/Pane element=back surface=desktop

The first click of each pair raises only ui.focus; the second raises ui.press and onPress runs normally. Possibly related: #99211.

jessecoyle · 7 days ago

Also on macOS: Claude desktop app 2.19675.0, Code tab, Claude Code 2.1.286 (session), macOS 26.6.2 (25G83).

Logging ui.focus, ui.press and each Pane draw (with isFocused) points at the click being lost to a re-render:

  1. Every person-origin ring move redraws the pane in the same millisecond, even when the pane already holds the keyboard:

``
16:05:52.928 draw Pane focused=true
16:05:54.934 draw Pane focused=true ← click 1
16:05:54.934 focus element=view-board origin=person
16:05:55.779 press element=view-board ← click 2
`
If that redraw replaces the Button's DOM node between mousedown and click, the click never lands. That would also explain why a Button **already holding the ring presses in one click** (moving it there first with
$.ui.focus({ requestId, key })` works): no ring move, no redraw.

  1. So it's per element, not per pane: isFocused stays true throughout, and every Button other than the ring's holder still needs two clicks.
  2. Separately, the pane drops the keyboard when the focused element unmounts. ~15 ms after a press that swaps the view, the draw shows focused=false. That is why a "back" button drawn by the first press looks like it needs two clicks too.
bernardorubin · 6 days ago

Same on macOS (26.5.2), desktop app Code tab, Claude Code 2.1.293. Two things to add:

  • The Button shows its hover style while the pane is unfocused, so it looks clickable, but the click only moves the focus.
  • A plugin can't tell a click from Tab here either way: by the time ui.focus reaches the hooks, the pane is already focused ($.ui.panes() reports isFocused: true and the pane has redrawn with isFocused: true), so "was the pane focused before this move?" can't be read from the hook.

Log from the hooks (ui.focus / ui.press / ui.toast and the pane's ui.render), pane unfocused, two clicks on the same Button:

04:43:55.868  ui.render  Pane isFocused=true
04:43:55.869  ui.focus   element=copy-<draft> origin=person   (1st click, no ui.press)
04:43:55.871  $.ui.panes() inside that ui.focus hook: isFocused=true
04:43:58.130  ui.press   element=copy-<draft>                  (2nd click)
04:43:58.259  ui.toast   "Copied ... draft for Slack"

A pointer-vs-keyboard field on UiFocusOrigin, or a click that both focuses and presses, would fix it.

Showing cached comments. Read the full discussion on GitHub ↗