fix(windows): window-controls hover height matches titlebar (#1632)

* feat(windows): custom title bar with renderer-drawn window controls

Replace the default Windows native title bar with a custom renderer-drawn
titlebar to match the macOS experience:

- Set titleBarStyle:'hidden' on win32 to remove the OS chrome
- Add min/max/close buttons (Fluent-style SVG) fixed to the top-right corner,
  rendered last in DOM order so they're never blocked by -webkit-app-region:drag
- Route close through IPC (window:close-requested) so the terminal-running
  confirmation guard stays active; minimize/maximize via window:minimize and
  window:maximize IPC channels
- Add maximize state sync (window:maximize-changed) so the restore icon shows
  correctly
- Add Orca logo + ··· application-menu button on the left in place of the bare
  pl-2 spacer; ··· calls Menu.getApplicationMenu().popup() replicating Alt-key
  reveal
- Add window-controls-titlebar-spacer to reserve 138px on the right of the
  full-width titlebar so content isn't obscured by the overlay

* fix(windows): titlebar logo color and window-controls overlap

- Logo light-mode invert: :not(.dark) selector was matching in dark mode
  because .dark lives on <html> (:root), not on a descendant. Fixed to
  :root:not(.dark) so the invert(1) filter only fires in light mode.

- Overlap with right-side controls: introduce --window-controls-width CSS
  var (138px on Windows, 0px elsewhere) on the root div so every affected
  surface reads a single source of truth:
  - Workspace-view floating sidebar toggle (absolute top-0): right now uses
    var(--window-controls-width) instead of hardcoded 0
  - window-controls-titlebar-spacer width uses the var
  - Right sidebar header: new .right-sidebar-header-inset class adds
    padding-right: var(--window-controls-width) to both the top-activity-bar
    and side-layout header variants so the close button is never hidden

* fix(windows): window-controls hover must not bleed below titlebar

The .window-controls container and .window-controls-btn were both 42px
tall while the titlebar is 36px. The hover background filled the full
button height, painting 6px of highlight into the content area below
the titlebar. Shrink both to 36px to match.

* fix(windows): side activity bar icons obscured by window-controls overlay

In side-bar activity-bar mode the icon strip is flush against the right
window edge. The fixed-position window-controls overlay (138×36px)
covers the top-right corner, making the topmost icons unreachable.

Add --window-controls-height (36px on Windows, 0px elsewhere) CSS var
and apply it as padding-top on the side activity bar via
.side-activity-bar-windows-inset — same pattern used by
--window-controls-width for the horizontal dimension.

---------

Co-authored-by: Neil Parker <nwparker@anthropic.com>
This commit is contained in:
Neil 2026-05-09 16:20:02 -07:00 committed by GitHub
parent d345aeda65
commit 09ca40e4ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 15 additions and 3 deletions

View File

@ -990,7 +990,10 @@ function App(): React.JSX.Element {
// Why: consumed by anything that needs to avoid the fixed-position
// window-controls overlay on Windows (floating sidebar toggle, right
// sidebar header, etc.) without hardcoding 138px in multiple places.
'--window-controls-width': isWindows ? '138px' : '0px'
'--window-controls-width': isWindows ? '138px' : '0px',
// Why: consumed by the side-position activity bar to push icons below
// the fixed-position window-controls overlay on Windows.
'--window-controls-height': isWindows ? '36px' : '0px'
} as React.CSSProperties
}
>

View File

@ -406,7 +406,7 @@
}
/* Why: small identity anchor on Windows where the native window chrome is
hidden. Sized to sit comfortably in the 42px titlebar with a little
hidden. Sized to sit comfortably in the 36px titlebar with a little
horizontal breathing room. The SVG fill is white; light mode inverts it to
black so it reads on the light titlebar background. */
.titlebar-logo {
@ -478,6 +478,15 @@
padding-right: var(--window-controls-width, 0px);
}
/* Why: in side-bar activity-bar mode the icon strip sits flush against the
right window edge. The fixed-position window-controls overlay (height 36px,
width 138px) covers the top-right corner, so the topmost sidebar icons are
unreachable without this offset. --window-controls-height is 0px on
non-Windows so this rule is a no-op there. */
.side-activity-bar-windows-inset {
padding-top: var(--window-controls-height, 0px);
}
.sidebar-toggle {
-webkit-app-region: no-drag;
background: none;

View File

@ -346,7 +346,7 @@ function RightSidebarInner(): React.JSX.Element {
{activityBarPosition === 'side' && (
<ContextMenu>
<ContextMenuTrigger asChild>
<div className="flex flex-col items-center w-10 min-w-[40px] bg-sidebar border-l border-border">
<div className="flex flex-col items-center w-10 min-w-[40px] bg-sidebar border-l border-border side-activity-bar-windows-inset">
<TooltipProvider delayDuration={400}>{activityBarIcons}</TooltipProvider>
</div>
</ContextMenuTrigger>