Repository navigation
feat(app): keep narrow desktop windows in the desktop layout - #58894
kitlangton wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
The code looks sound. I couldn't see it working here: Chromium won't launch in this environment (missing system libraries), so the e2e and component specs didn't run, and the Electron minimum-size change needs the real desktop app. The window-state and session-panel-width unit tests pass, and typecheck:tsc is clean in app and desktop. I read through the room and hysteresis logic, how the sidebar auto-hide drives the peek and the toggle command, the sheet's Escape handling next to the peek's, and the move from width-only media queries to isMobileLayout. I found nothing blocking.
Public API: one behaviour change for GUI extensions. In @opencode/gui-extensions/sdk, Layout.narrow() changes from "viewport under 768px" to "touch screen without hover, under 768px". The type is unchanged, but an extension that used narrow() to adapt to a small desktop window will now get false there. @opencode/ui also gains a ./mobile-layout export, which is additive and internal to the workspace. Config, the server API, the CLI and the client SDK are unchanged.
A few width-only styles are left, so narrow mouse windows still get them: the md: padding in the timeline and session route, and @media (max-width: 767px) in session-ui/src/pierre/index.ts. The pierre rule looks like a deliberate width rule. Please check the padding ones are meant to stay.
The stills and clips you mentioned will help here, since none of this could be checked on screen.
Related: #50459
e74f7f8 to
fa68fc1
Compare
There was a problem hiding this comment.
The new commit only touches patch-sticky-header.spec.ts. Under 768px it now emulates touch, like the other phone-layout specs, so the 390px case still exercises the phone layout. The wrapper is small and easy to follow, and I have nothing new to raise. I couldn't run the spec here because headless Chromium won't start in this environment. The public API impact is unchanged from my previous review.
There was a problem hiding this comment.
The new commit makes sense. The sheet decision now uses the shell's full row width, is made while the panel is open and is kept while it closes. That matches how the sidebar gives way: with the side panel open, the sidebar only stays docked if the content still has the docking width, so re-docking on close shouldn't turn a docked panel into a sheet. The peek's focus return and toggle-hover fix look right. I checked this by reading the code: typecheck:tsc in app is clean and the session-panel-width unit tests pass. I couldn't run the e2e specs here because headless Chromium won't launch. The public API impact is unchanged from earlier.
One small thing in the new test, inline.
| // Records whether the side panel ever floats as a sheet, on any frame, through an open and a close. | ||
| await page.evaluate(() => { | ||
| const sample = () => { | ||
| if (document.querySelector('[data-slot="session-side-panel-presence"].z-20')) |
There was a problem hiding this comment.
This sampler finds the sheet by its Tailwind class (.z-20). If that styling changes, the selector never matches and the "never became a sheet" check passes even when the bug is back. A dedicated attribute on the presence element (e.g. data-sheet set from sideSheet()) would keep this test checking the right thing.
There was a problem hiding this comment.
The new commit fixes the test selector point: the sampler now uses a data-sheet attribute, set under the same condition as the sheet styling, so the "never became a sheet" check can't pass by accident. typecheck:tsc in app is clean. I couldn't run the e2e spec here because headless Chromium won't launch. The public API impact is unchanged.
What it does
A desktop window narrower than 768px switched to the phone layout: bottom navigation, no titlebar, no hover, mobile density. Now a narrow window keeps the desktop layout and makes room the way native apps do. The phone layout is only for touch screens (
(hover: none) and (pointer: coarse)under 768px).How it works
isMobileLayoutin@opencode/ui/mobile-layoutis now the one place that decides the phone layout; the screen-width checks that used to make that call read it instead. Room math lives inpackages/app/src/shell/room.ts.Based on
betabecause the sidebar peek, titlebar and shell code this builds on (#58244, #58517, #58520) only exists there.Visual
Risk
UI only, desktop app and web. A narrow browser tab with a mouse now keeps the desktop layout too, so phone-only affordances no longer appear there. A touch device under 768px still gets the phone layout. RTL uses logical properties and a mirrored sheet transform; I checked it in the code, not on screen. Full-screen and real-Electron window resizing weren't exercised; the proof comes from a headless browser.
Proof
Today (left) and this PR (right) at 1200, 900, 760, 600 and 480px, side panel open. Headless Chromium with a mouse, mocked server.
A 700px window, real time: the sidebar floats on its toggle and Escape closes it, it floats again and a click outside closes it, then the side panel opens as a sheet and Escape closes it.
narrow-overlay.mp4
A resize sweep from 1300 to 480px and back with the side panel open (Playwright's recorder, about 25 fps):
narrow-resize.mp4
bun typecheckin app, ui, session-ui, gui-extensions and desktop;bun run typecheck:tscin app, ui and desktop: clean.bun script/lint-changed.ts origin/beta: 0 problems.bun testsession-panel-width.test.tsandwindow-state.test.ts: pass.tabs.spec.ts: a 700px mouse window keeps the desktop layout, the sidebar floats on demand and the side panel becomes a sheet. Pass.beta(macOS Ctrl/Meta shortcut cases, two mobile-session cases, quarantined tests).beta's head (home.spec.ts:184,timeline-stream.spec.ts:2059,composer.spec.ts:268).Added by the opencode bot (AI-generated).
Related: #50459
Public API: changes the meaning of the GUI extension SDK's Layout.narrow() from "viewport under 768px" to "touch screen under 768px" (type-compatible)