[FEATURE] A tab group element for plugin panes, drawn as a native tab control on desktop
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
A plugin Pane cannot draw a tab group that works well in the desktop app.
My plugin's pane has three tabs, each with a name and a count: "Needs you 7", "Findings 4", "PRs 1". One tab is shown at a time. In the terminal I draw them from Box and Text, with the shown tab raised, and a Button per tab takes the click.
In the desktop app's Code mode, the same approach does not hold up:
- Only a
Buttontakes a click. ABoxcan have a background and a hover background, but a click on it does nothing. - A
Buttonsizes to its label: one line tall, as wide as its text. It does not stretch to fill its parent, andButtonPropshas no width, height or flex props. - A
Buttonalways draws as a native button,plainor not, so it cannot be an invisible hit area over a drawn tab.
So a desktop tab is either a small native button or a drawn panel where only the label's text takes a click. Neither looks or behaves like a tab group.
Proposed Solution
A Tabs element that the surface draws as its own tab control: a native tab bar or segmented control on desktop, and a tab line in the terminal.
<Tabs
key="inbox-tabs"
selected={tab}
tabs={[
{ id: 'needsYou', label: 'Needs you', count: 7 },
{ id: 'findings', label: 'Findings', count: 4 },
{ id: 'prs', label: 'PRs', count: 1 },
]}
onSelect={id => showTab(id)}
/>
- Each tab takes a click over its whole shape, with the surface's own hover, focus and selected states.
- A press raises
ui.press(orui.select) with the tab's id, like other elements. - The tab group keeps the focus when the selection changes, so the next click works. Today a pane can lose the focus when the clicked element leaves the drawing (#100874).
- An optional
countor badge per tab, drawn in the surface's style. - In the terminal, the arrow keys or a digit could move between tabs, as the surface decides.
Alternative Solutions
- Layout props on
Button:width,heightorflexGrow, and padding. A plugin could then size a button to a drawn tab. This is more general, but each plugin would rebuild the tab group and its selected state by hand. - A pressable
Box:onPressonBoxwould let a plugin draw any shape and take clicks over all of it. - What I do today: each tab is a native
Button, with the shown tab asvariant="primary". The labels are padded with non-breaking spaces to widen the hit area. Plain spaces draw with no size. - What I tried first: blank
plainButtons laid over a drawn tab panel. They sized to their labels instead of the panel and showed native highlights under the pointer, so the tab looked like stacked buttons.
Priority
Medium - Would be very helpful
Feature Category
Developer tools/SDK
Use Case Example
- A plugin keeps a pane open beside the conversation, with tabs for questions, findings and pull requests.
- In the desktop app, the person clicks "PRs 1" to see the one PR that needs them.
- With a tab group element, the whole tab takes the click, shows its hover and selected states, and keeps the pane's focus for the next click.
- The plugin draws one element for every surface, instead of a terminal drawing and a desktop workaround.
Additional Context
- Claude desktop app 2.31226.0, which bundles Claude Code 2.1.293, on macOS 15.7.3.
Buttonchildren (Textinside aButton) arrive in 2.1.295 and would let the count keep its color. They do not change the button's size.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗