diff --git a/apps/desktop/src/app/shell/wslg-window-controls.test.tsx b/apps/desktop/src/app/shell/wslg-window-controls.test.tsx index 5897187a5bc..8c5bc3035b6 100644 --- a/apps/desktop/src/app/shell/wslg-window-controls.test.tsx +++ b/apps/desktop/src/app/shell/wslg-window-controls.test.tsx @@ -72,7 +72,7 @@ describe('WslgWindowControls', () => { expect(screen.queryByLabelText('Window controls')).toBeNull() }) - it('prevents pointer activation from stealing renderer focus', () => { + it('stops pointerdown propagation without cancelling the click', () => { desktopWindow.hermesDesktop = { windowControls } as unknown as Window['hermesDesktop'] renderControls() const event = new MouseEvent('pointerdown', { bubbles: true, cancelable: true }) @@ -81,7 +81,10 @@ describe('WslgWindowControls', () => { fireEvent(button, event) fireEvent.click(button) - expect(event.defaultPrevented).toBe(true) + // preventDefault on pointerdown kills the synthesized click under WSLg's + // RAIL compositor, so the button must NOT cancel the default — only stop + // propagation so the drag region doesn't swallow the press. + expect(event.defaultPrevented).toBe(false) expect(windowControls.toggleMaximize).toHaveBeenCalledOnce() }) diff --git a/apps/desktop/src/app/shell/wslg-window-controls.tsx b/apps/desktop/src/app/shell/wslg-window-controls.tsx index a623533bd1e..676c172c44a 100644 --- a/apps/desktop/src/app/shell/wslg-window-controls.tsx +++ b/apps/desktop/src/app/shell/wslg-window-controls.tsx @@ -21,7 +21,13 @@ interface WslgWindowControlsProps { const buttonClass = 'grid h-full w-[46px] place-items-center border-0 bg-transparent p-0 text-muted-foreground transition-colors duration-75 select-none [-webkit-app-region:no-drag] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring hover:bg-white/10 hover:text-foreground active:bg-white/15' -const preserveRendererFocus = (event: PointerEvent) => event.preventDefault() +// Match the native titlebar tools: stopPropagation (NOT preventDefault) on +// pointerdown. preventDefault on pointerdown suppresses the synthesized click +// under WSLg's XWayland/RAIL compositor, so the buttons render but never fire. +// stopPropagation keeps the drag region from swallowing the press while leaving +// the click intact; keyboard-focus reassertion after maximize is handled on the +// main side in performWindowControl (win.focus()). +const stopTitlebarDrag = (event: PointerEvent) => event.stopPropagation() export function WslgWindowControls({ isFullscreen, isMaximized }: WslgWindowControlsProps) { const location = useLocation() @@ -43,7 +49,7 @@ export function WslgWindowControls({ isFullscreen, isMaximized }: WslgWindowCont aria-label="Minimize window" className={buttonClass} onClick={controls.minimize} - onPointerDown={preserveRendererFocus} + onPointerDown={stopTitlebarDrag} type="button" > @@ -52,7 +58,7 @@ export function WslgWindowControls({ isFullscreen, isMaximized }: WslgWindowCont aria-label={isMaximized ? 'Restore window' : 'Maximize window'} className={buttonClass} onClick={controls.toggleMaximize} - onPointerDown={preserveRendererFocus} + onPointerDown={stopTitlebarDrag} type="button" > @@ -64,7 +70,7 @@ export function WslgWindowControls({ isFullscreen, isMaximized }: WslgWindowCont 'hover:bg-[#c42b1c] hover:text-white active:bg-[#b3271a] active:text-white dark:hover:bg-[#c42b1c]' )} onClick={controls.close} - onPointerDown={preserveRendererFocus} + onPointerDown={stopTitlebarDrag} type="button" >