0031. The window draws its own title bar
- Status: Accepted
- Date: 2026-11-11
- Amends: 0030 (the window had the system’s title bar)
Context — what it has to do
Section titled “Context — what it has to do”With ADR 0030 Exhale opens in a window of its own, under the system’s title bar: on Linux a GTK header with the title and a lone close button, above a page that has its own sidebar and header. The owner tried it and asked for what current desktop apps do (OpenWhispr, Perplexity, Linear): no separate title bar. The sidebar sits on a muted frame, the page sits beside it in a rounded card, and the window buttons are part of the app: minimise, maximise and close at the top right on Windows and Linux, the traffic lights at the top left on a Mac.
Physically, a window without the system’s title bar still has to:
- Move when dragged by its top, and maximise on a double click.
- Resize from its edges.
- Minimise, maximise or restore, and close, with buttons that show which one applies.
- Look like a window: an edge on any desktop, and the Mac’s own traffic lights and full screen.
Criteria
Section titled “Criteria”| Criterion | Weight |
|---|---|
| Looks like the apps the owner pointed to, on all three systems | High |
| Moving, resizing and the buttons behave as the system’s do | High |
| Nothing new to keep up: the same Wails window, the same bridge | High |
| The browser keeps working as before | Medium |
Options
Section titled “Options”| System title bar (ADR 0030) | No system frame, the page draws it | GTK header bar / Windows custom caption | |
|---|---|---|---|
| Looks like the references | No: a bar above the app | Yes | Partly: native widgets in each system’s style |
| Code | None | A small component, a few bridge messages | Native code per system, outside what Wails offers |
| Mac | Title bar above the sidebar | Traffic lights over the sidebar (Wails “hidden inset”) | Same as the middle column |
Decision
Section titled “Decision”On Windows and Linux the window has no system frame and the page draws the title bar; on a Mac the title bar is hidden and the traffic lights stay, over the sidebar.
- Window (
internal/desktop/window):Framelesson Windows and Linux,MacTitleBarHiddenInseton a Mac. The bridge takes three more messages,{"type":"window","action":"minimise"|"maximise"|"close"}, and tells the page when the window is maximised or in full screen with anexhale:windowevent. - Moving and resizing (
web/src/lib/desktop.ts): Wails moves or resizes a window when the page sendswails:dragorwails:resize:<edge>while the button is down. Its JavaScript runtime does that for pages Wails serves; Exhale’s pages come from the local server, sodesktop.tsdoes the same in a few lines: an area markeddata-dragmoves the window (a closerdata-no-dragone, and buttons, links and fields, don’t), a double click maximises, and on Windows and Linux the 5px band along each edge resizes it. A double click asks the window, which does what the system’s title bar would: maximise on Windows and Linux; on a Mac, read “Double-click a window’s title bar to” (AppleActionOnDoubleClick) and zoom for Fill or Zoom, minimise, or do nothing. Wails 3.1 treats Fill, the default since macOS 15, as nothing, so the window reads it itself. The window only starts moving after the pointer moves 3px, so a double click is not taken for a drag. - Frame (
web/src/components/layout.tsx): at 1024px and wider, the sidebar sits on a muted frame (--frame) and the page in a rounded card that scrolls on its own. This is the same in a browser: the app looks the same wherever it opens. In the window, the card starts with a 44px title band to move it by, holding the buttons on Windows and Linux. Narrower, the frame is unchanged; in the window, the header gets the buttons (or room for the traffic lights). - Edges: Windows draws its own border and shadow around a window without a frame. Linux draws none, so the page adds a hairline, gone when maximised.
Consequences — cost we accept
Section titled “Consequences — cost we accept”- Our buttons, not the system’s. They follow the app’s look, not the GNOME or Windows theme, and Windows 11’s snap layouts menu does not open from our maximise button (Win+Z and dragging to the screen edges still work).
- No shadow on Linux. GTK gives a window without a frame no shadow; the hairline is the outline.
- The Mac layout is checked in CI only. The installers workflow opens the window on a real Mac, but the place of the traffic lights over the sidebar (a 52px band) is only checked by eye on a Mac.
- The page scrolls inside its card on wide screens, so the router resets that element’s scroll on navigation too (
scrollToTopSelectors).
Revisit when
Section titled “Revisit when”- People ask for the system’s own buttons: Wails’s
Frameless: falsebrings the title bar back, the page keeps working without it. - Wails serves its runtime to pages it does not host: drop the copy of the drag logic in
desktop.ts.