diff --git a/apps/desktop/src/components/ui/pane-tab.test.tsx b/apps/desktop/src/components/ui/pane-tab.test.tsx
index c36f03fb8ada..60943b85b848 100644
--- a/apps/desktop/src/components/ui/pane-tab.test.tsx
+++ b/apps/desktop/src/components/ui/pane-tab.test.tsx
@@ -90,3 +90,54 @@ describe('PaneTab close gestures', () => {
expect(onPointerDown).toHaveBeenCalledTimes(1)
})
})
+
+describe('PaneTab hover close button', () => {
+ it('renders a close button when onClose is set on a horizontal tab', () => {
+ const onClose = vi.fn()
+ render(
+
+ tab
+
+ )
+
+ expect(screen.getByRole('button', { name: 'Close tab' })).toBeTruthy()
+ })
+
+ it('clicking the close button calls onClose and stops propagation', () => {
+ const onClose = vi.fn()
+ const onTabPointerDown = vi.fn()
+ render(
+
+ tab
+
+ )
+
+ const closeBtn = screen.getByRole('button', { name: 'Close tab' })
+ fireEvent.pointerDown(closeBtn)
+ fireEvent.click(closeBtn)
+ expect(onClose).toHaveBeenCalledTimes(1)
+ // The tab's own pointerdown handler must NOT fire — the X is a leaf action.
+ expect(onTabPointerDown).not.toHaveBeenCalled()
+ })
+
+ it('does not render a close button on vertical tabs', () => {
+ const onClose = vi.fn()
+ render(
+
+ tab
+
+ )
+
+ expect(screen.queryByRole('button', { name: 'Close tab' })).toBeNull()
+ })
+
+ it('does not render a close button without onClose', () => {
+ render(
+
+ tab
+
+ )
+
+ expect(screen.queryByRole('button', { name: 'Close tab' })).toBeNull()
+ })
+})
diff --git a/apps/desktop/src/components/ui/pane-tab.tsx b/apps/desktop/src/components/ui/pane-tab.tsx
index da8baae2aec9..3e3b4851ec20 100644
--- a/apps/desktop/src/components/ui/pane-tab.tsx
+++ b/apps/desktop/src/components/ui/pane-tab.tsx
@@ -1,5 +1,6 @@
import * as React from 'react'
+import { Codicon } from '@/components/ui/codicon'
import { cn } from '@/lib/utils'
/** Inset bottom stroke for a horizontal tab strip — titlebar color, cut by the active tab. */
@@ -27,8 +28,8 @@ const TAB_IDLE =
interface PaneTabProps extends React.ComponentProps<'div'> {
active?: boolean
dirty?: boolean
- /** Close gesture, no hover X (too easy to hit on small tabs): middle-click,
- * or ⌘-click as the trackpad-friendly Mac equivalent. */
+ /** Close gesture: hover X on horizontal tabs, plus middle-click and ⌘-click
+ * (the trackpad-friendly Mac equivalent). */
onClose?: () => void
/** Vertical rail form (collapsed sidebar zones). */
vertical?: boolean
@@ -127,7 +128,35 @@ export const PaneTab = React.forwardRef(function P
{...props}
>
{children}
- {dirty && (
+ {onClose && !vertical && (
+
+ )}
+ {dirty && !(onClose && !vertical) && (