feat(web): remove sidebar and panel max-width limits#985
Conversation
Make the resize handle max width optional so the web sidebar and right-side detail/preview panel can be resized beyond their previous fixed maximums.
🦋 Changeset detectedLatest commit: eac41eb The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f7695d9f9a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| :min="SIDEBAR_MIN" | ||
| :max="SIDEBAR_MAX" | ||
| @update:width="sessionColWidth = $event" |
There was a problem hiding this comment.
Keep the sidebar resize handle reachable after restoring wide widths
If a user widens the sidebar on a large display and later opens the app in a narrower window, the persisted sessionColWidth is restored without any upper bound. Because .app has overflow: hidden and the sidebar resize handle sits after the var(--side-w) grid track, a restored width larger than the viewport pushes both the handle and the collapse button off-screen, leaving the conversation inaccessible with no in-UI way to shrink the sidebar. This needs a viewport-aware cap or another reachable recovery path for the sidebar even if fixed 420px limits are removed.
Useful? React with 👍 / 👎.
Cap the restored sidebar width at a viewport-aware maximum (viewport width minus the conversation pane minimum) so a width saved on a wide display cannot push the resize handle or collapse button off-screen on a narrower window. The cap updates on resize.
Apply the same viewport-aware maximum to the right-side detail/preview panel and extract the viewport tracking and width clamping into a shared composable used by both panels.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 26e1cd5bf2
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| :default-width="SIDEBAR_DEFAULT" | ||
| :min="SIDEBAR_MIN" | ||
| :max="SIDEBAR_MAX" | ||
| :max="sidebarMax" |
There was a problem hiding this comment.
Make resize caps react to viewport changes
When the desktop window grows after the sidebar handle has mounted, this viewport-derived max value updates in App.vue, but ResizeHandle.vue only calls useResizable({ max: props.max }) once during setup and useResizable destructures that initial number. As a result, opening on a narrow window and then widening it still leaves the sidebar clamped to the old smaller maximum until the handle is remounted, so the new viewport-aware cap does not actually let users drag to the newly available width.
Useful? React with 👍 / 👎.
| const sidebarMax = computed(() => | ||
| panelMaxWidth(viewportWidth.value, SIDEBAR_MIN, CONVERSATION_MIN), | ||
| ); |
There was a problem hiding this comment.
Reserve space for the preview panel too
When the right-side detail/file panel is open, sidebarMax still reserves only CONVERSATION_MIN from the full viewport and does not account for the preview panel's current/minimum width. For example, on a 1440px desktop viewport the sidebar can be dragged to 1120px while the preview panel remains at its 320px minimum, leaving the conversation column at 0px; this violates the intended invariant that resizing keeps the conversation pane usable and the controls reachable whenever both side panels are visible.
Useful? React with 👍 / 👎.
Make the resize handle read its max width reactively so a viewport-derived cap keeps working as the window grows after mount. Also have the sidebar reserve the preview panel's minimum width whenever the right-side panel is open, so the conversation column can never be squeezed to zero.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fa2ccb3165
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const sideWidth = computed(() => | ||
| sidebarCollapsed.value ? SIDEBAR_COLLAPSED_WIDTH : sessionColWidth.value, | ||
| sidebarCollapsed.value | ||
| ? SIDEBAR_COLLAPSED_WIDTH | ||
| : clampPanelWidth(sessionColWidth.value, SIDEBAR_MIN, sidebarMax.value), | ||
| ); |
There was a problem hiding this comment.
Clamp the sidebar content width too
When a saved/sidebar width is above the new viewport-derived cap, this only clamps sideWidth for the grid track; sessionColWidth remains the larger value and is still passed to Sidebar, whose .col is rendered at that width. On a narrower window or after opening the right panel, the grid column shrinks but the sidebar header/list are laid out at the stale larger width, so controls like the collapse button can be clipped or covered by the conversation pane instead of staying reachable.
Useful? React with 👍 / 👎.
|
|
||
| // True while a file preview or any detail panel claims the right-side slot, so | ||
| // the sidebar reserves room for it and the conversation can never be squeezed. | ||
| const previewOpen = computed(() => detailTarget.value !== null || previewTarget.value !== null); |
There was a problem hiding this comment.
Ignore stale preview targets when reserving panel space
Including previewTarget keeps the sidebar in the “right panel open” max-width mode even after the panel is no longer visible: for example, open a file preview, switch to a thinking/diff panel, then close that panel. openThinkingPanel/openDiffDetail do not clear the previous file preview target, so detailTarget becomes null but previewTarget remains set, capping the sidebar as if 320px were still reserved for a hidden right panel.
Useful? React with 👍 / 👎.
Render the Sidebar content at the clamped width so controls stay reachable when the saved width exceeds the viewport cap. Also stop reserving space for a hidden right panel by keying the sidebar preview-open check off detailTarget instead of the stale previewTarget.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 455423346e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| function clamp(value: number): number { | ||
| if (!Number.isFinite(value)) return defaultWidth; | ||
| return Math.min(max, Math.max(min, Math.round(value))); | ||
| return Math.min(toValue(max), Math.max(min, Math.round(value))); |
There was a problem hiding this comment.
Clamp stored width before starting a drag
When max drops after the handle has mounted (for example, a wide saved sidebar/preview width is rendered clamped after the window narrows or a detail panel opens), the internal width.value can remain at the old larger value because this clamp only runs when setWidth is called. onPointerDown then captures that stale value as startWidth, so the first drag has to cover the invisible delta before the handle moves; users can appear unable to shrink the now-clamped panel until they release and start a second drag. Sync the internal width when the cap changes, or clamp it before assigning startWidth.
Useful? React with 👍 / 👎.
When the saved width exceeds the current cap (after the window narrows or a side panel opens), start the drag from the clamped width so the handle responds immediately instead of first covering an invisible delta.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f98d168be4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // room for it and the conversation can never be squeezed. Keyed off detailTarget | ||
| // (the real occupant) rather than previewTarget, which can stay set after the | ||
| // panel is hidden. | ||
| const previewOpen = computed(() => detailTarget.value !== null); |
There was a problem hiding this comment.
Reserve sidebar width only for visible side panels
On desktop, when the user runs bare /btw to close an open side chat, handleCommand calls client.closeSideChat() directly and leaves detailTarget === 'btw'. sidePanelVisible then becomes false because client.sideChatVisible is false, but this computed still stays true, so useSidebarLayout keeps subtracting PREVIEW_MIN from the sidebar maximum after the right panel is gone. That leaves the session sidebar capped at viewport - 640 until some other action clears detailTarget; base this reservation on the actually visible panel state or clear detailTarget on the /btw close path.
Useful? React with 👍 / 👎.
The bare /btw close path called client.closeSideChat() directly, which hid the panel but left detailTarget set to 'btw', so the sidebar kept reserving room for a hidden right panel. Route it through the detail-layer close which clears detailTarget.
|
Codex Review: Didn't find any major issues. 🎉 Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Related Issue
No related issue — see the problem below.
Problem
The web sidebar was capped at a fixed 420px and the right-side detail / file-preview panel at a static viewport-derived cap that did not react to window resizing. On wide monitors neither panel could be dragged wider. Removing those caps outright is unsafe though: a width saved on a large display and restored on a narrow window would push the resize handle and collapse button off-screen, leaving the conversation inaccessible with no in-UI way to shrink the panel.
What changed
Replaced the fixed / static caps with a shared, viewport-aware maximum for both panels. Each panel can now be dragged up to nearly full screen (leaving a minimum for the conversation pane), while the cap updates on window resize and the rendered width is clamped so a restored width or a shrinking window can never push a resize handle off-screen. The viewport tracking and width clamping live in one shared composable so the same logic is not duplicated between the two panels. Default widths are unchanged.
Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.