From dfa3fc13b9f7134d5545b92308819192bc79736a Mon Sep 17 00:00:00 2001 From: Johannes du Plessis Date: Mon, 15 Jun 2026 10:52:45 -0700 Subject: [PATCH] fix: enforce minimum chat width so the git panel can't squish it (#1526) The git panel can now be dragged toward full window width, but its max was reserved against window.innerWidth (ignoring the sidebar), letting the panel squeeze the chat below a usable width. Clamp the panel against the actual container width and give the chat column a matching min-width floor. Co-authored-by: open-swe[bot] --- ui/src/components/agents/AgentGitPanel.tsx | 30 ++++++++++++-------- ui/src/components/agents/AgentThreadView.tsx | 10 +++++-- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/ui/src/components/agents/AgentGitPanel.tsx b/ui/src/components/agents/AgentGitPanel.tsx index 219f6cfc..b3230c45 100644 --- a/ui/src/components/agents/AgentGitPanel.tsx +++ b/ui/src/components/agents/AgentGitPanel.tsx @@ -78,16 +78,18 @@ const COLLAPSED_STATE_FALSE = "0" const PANEL_DEFAULT_WIDTH = 420 const PANEL_MIN_WIDTH = 320 // Keep at least this much room for the chat so the panel can grow to nearly the -// full window (e.g. ~50/50 on ultrawide screens) without hiding the chat. -const PANEL_MIN_CHAT_WIDTH = 360 +// full window (e.g. ~50/50 on ultrawide screens) without squishing the chat. +// Exported so the chat column can enforce the same floor via min-width. +export const PANEL_MIN_CHAT_WIDTH = 360 -function getPanelMaxWidth(): number { +function getPanelMaxWidth(availableWidth?: number): number { if (typeof window === "undefined") return PANEL_DEFAULT_WIDTH - return Math.max(PANEL_MIN_WIDTH, window.innerWidth - PANEL_MIN_CHAT_WIDTH) + const available = availableWidth ?? window.innerWidth + return Math.max(PANEL_MIN_WIDTH, available - PANEL_MIN_CHAT_WIDTH) } -function clampPanelWidth(width: number): number { - return Math.min(getPanelMaxWidth(), Math.max(PANEL_MIN_WIDTH, width)) +function clampPanelWidth(width: number, availableWidth?: number): number { + return Math.min(getPanelMaxWidth(availableWidth), Math.max(PANEL_MIN_WIDTH, width)) } function readStoredPanelWidth(): number { @@ -190,6 +192,7 @@ export function AgentGitPanel({ thread, messages }: AgentGitPanelProps) { ) const [width, setWidthState] = useState(() => readStoredPanelWidth()) const [fullScreen, setFullScreen] = useState(false) + const panelRef = useRef(null) const setCollapsed = (next: boolean) => { setCollapsedState(next) @@ -202,18 +205,20 @@ export function AgentGitPanel({ thread, messages }: AgentGitPanelProps) { } const setWidth = useCallback((next: number) => { - const clamped = clampPanelWidth(next) + const available = panelRef.current?.parentElement?.clientWidth + const clamped = clampPanelWidth(next, available) setWidthState(clamped) window.localStorage.setItem(PANEL_STORAGE_WIDTH, String(clamped)) }, []) - // Re-clamp the stored width when the window shrinks so the panel never - // exceeds the available space (which would squeeze out the chat). + // Re-clamp against the real container width on mount and whenever the window + // resizes, so the panel can never squeeze the chat below its minimum width. useEffect(() => { if (typeof window === "undefined") return - const onResize = () => setWidth(width) - window.addEventListener("resize", onResize) - return () => window.removeEventListener("resize", onResize) + const reclamp = () => setWidth(width) + reclamp() + window.addEventListener("resize", reclamp) + return () => window.removeEventListener("resize", reclamp) }, [setWidth, width]) const [selectedTreePath, setSelectedTreePath] = useState(null) const sectionRefs = useRef>({}) @@ -305,6 +310,7 @@ export function AgentGitPanel({ thread, messages }: AgentGitPanelProps) { return (