ADR 0033: Internal-dock work panel (no native window expansion)
- Status: Accepted
- Date: 2026-07-30
- Related: 01-ui-ia · 08-component-spec §5 · 09-interaction-patterns §8 · 01-ipc-protocol · decision D163
- Supersedes in part: ADR 0032
Context
ADR 0032 made the work panel a docked flex column but, to keep the chat width stable, reserved native window width equal to the panel's committed width. Main expanded the OS window (within the display work area) by that width and positioned the native browser WebContentsView in the extended region. The user-visible result is that opening the panel grows the whole application window — it reads as the panel "expanding out separately" rather than occupying space inside the existing window.
ChatGPT and WorkBuddy keep the window fixed and let the side panel take space from inside the client area, pushing the conversation left. That is the requested behavior.
Investigation showed the window expansion was only a room-making mechanism:
- The terminal tab is renderer-based (
@xterm/xterm); it needs no native view. - Only the browser tab uses a native
WebContentsView, and it is already positioned from the renderer-measured panel rect viabrowserSetBounds(seeBrowserPane.setBoundsandBrowserTab). The view composites above renderer content, so it must be told where to sit — but that rect is measured wherever the panel visually is, independent of whether the window grew.
Therefore the native width reservation is unnecessary for correctness: if the panel becomes an in-flow column of a fixed window, the measured rect already lands inside the window, and the browser view follows it without any window expansion.
Decision
- The work panel is a fixed-width in-flow flex column inside the fixed client area. Opening it reflows
MainChatto the left; it never expands the OS window. - The renderer always requests a native reservation width of
0(api.setWorkPanelReservation(0)). Main no longer changes window bounds for the panel. Thewindow/setWorkPanelReservationIPC is retained as a stable seam; Main returns an empty reservation ({ requested: 0, reserved: 0 }). - The native browser
WebContentsViewcontinues to be positioned from the renderer-measured panel rect viabrowserSetBounds. No window expansion is required for it to sit correctly. - The default committed width is 420px (the established baseline), within the unchanged
364..720pxclamp. (Superseded by decision D167: the default is 280px inside a244..720pxclamp; every other clause here stands.) - Native window-edge resize changes
MainChatonly, now by plain reflow (the panel is internal and stays at its committed width).
Consequences
- The OS window size is stable across open / collapse / divider commit; only
MainChatreflows. This matches ChatGPT / WorkBuddy. - On small windows
MainChatcan be squeezed below its 360px readability target when the panel is open at a wide width — the same trade-off ChatGPT accepts, and acceptable here. - Geometry logic is simpler: the reservation machinery is retained but inert, so the renderer→Main IPC surface and its tests stay valid.
- Persisted normal bounds already exclude reservation width; with reservation always
0they are simply the user's window size, which is the desired relaunch behavior. - The exit animation and native view detach-before-exit logic are unchanged.
Alternatives
Keep ADR 0032 (reserve native width, expand the window)
Rejected: expanding the whole window to make room for the panel is exactly the "expands out separately" behavior the user wants to remove. It also makes the panel feel disconnected from the in-window layout.
Clip the native view with a separate overlay window
Rejected: adds a second window, focus/occlusion complexity, and a different failure mode, for no benefit over measuring the in-flow rect.
References
docs/adr/0032-reserve-native-width-for-the-docked-work-panel.md(superseded in part)apps/desktop/src/App.tsx(reservation target set to0)apps/desktop/src/lib/work-panel-resize.ts(WORK_PANEL_DEFAULT_WIDTH)apps/desktop/src/components/workpanel/BrowserTab.tsx(browserSetBoundsfrom measured rect)apps/desktop/electron/main/browser-view.ts(BrowserPane.setBounds)docs/spec/06-delivery/04-e2e-test-plan.md(E2E-056)docs/spec/08-meta/decisions-log.md(D163)