Skip to content

Fix plan composer layout by preventing flex overflow in chat/sidebar#706

Merged
juliusmarminge merged 7 commits intomainfrom
t3code/fix-plan-composer-layout
Mar 9, 2026
Merged

Fix plan composer layout by preventing flex overflow in chat/sidebar#706
juliusmarminge merged 7 commits intomainfrom
t3code/fix-plan-composer-layout

Conversation

@juliusmarminge
Copy link
Copy Markdown
Member

@juliusmarminge juliusmarminge commented Mar 9, 2026

Before

image image

After

CleanShot.2026-03-09.at.11.00.55.mp4

Summary

  • add min-w-0 to the main chat/sidebar flex container in ChatView so the plan sidebar and composer can shrink correctly without horizontal overflow
  • move BranchToolbar into the chat column so it participates in the intended column layout with the composer/content area
  • extract SidebarInset base classes into sidebar.logic.ts and include min-w-0 in the shared base classname
  • add a focused unit test to assert the sidebar inset base classname retains min-w-0 for shrink behavior safety

Testing

  • apps/web/src/components/ui/sidebar.test.tsx: verifies SIDEBAR_INSET_BASE_CLASSNAME contains min-w-0
  • Layout sanity check from code changes: chat/sidebar root flex now uses min-w-0 and SidebarInset base class includes min-w-0
  • Not run: full project checks (bun lint, bun typecheck, bun run test)

Note

Fix flex overflow in chat composer and sidebar to prevent layout breakage

  • Adds a responsive compact mode to the chat composer footer in ChatView.tsx: when the composer width drops below 620px (or 720px with wide actions), secondary controls collapse into an overflow ellipsis menu via the new CompactComposerControlsMenu component.
  • Introduces shouldUseCompactComposerFooter in composerFooterLayout.ts with a ResizeObserver to recompute layout on width changes.
  • Adds min-w-0 to the main content containers in ChatView.tsx and sidebar.tsx so flex children can shrink without forcing horizontal overflow.
  • Prevents the inline diff sidebar from resizing to a width that would push the composer below a computed minimum based on its controls in _chat.$threadId.tsx.

Macroscope summarized 5d6a99a.


Note

Low Risk
UI-only layout changes (flex sizing, resize observers, and sidebar resize constraints) that could cause minor regressions in responsiveness across breakpoints, but no security or data-handling impact.

Overview
Prevents chat layout overflow by adding min-w-0 to key flex containers (ChatView main row and SidebarInset) and moving BranchToolbar into the chat column so it participates in the intended layout.

Adds a responsive compact composer footer: ChatView now measures composer width (with ResizeObserver) and, below a breakpoint, collapses secondary controls into a new CompactComposerControlsMenu and constrains the ProviderModelPicker label via a new compact prop; breakpoints and logic are extracted to shouldUseCompactComposerFooter with unit tests.

Hardens inline diff sidebar resizing by rejecting sidebar widths that would force the composer below a computed minimum width, using new data-chat-composer-* hooks to measure footer gaps and right-side action widths.

Written by Cursor Bugbot for commit 9bbb600. This will update automatically on new commits. Configure here.

@github-actions github-actions bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Mar 9, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 9, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 28c68f81-4bca-4cf8-96f3-6fa183dd85e8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch t3code/fix-plan-composer-layout

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Duplicated reasoning label map across two components
    • Extracted the duplicated reasoningLabelByOption map and getDefaultReasoningEffort("codex") call into file-level constants REASONING_LABEL_BY_OPTION and DEFAULT_CODEX_REASONING_EFFORT, shared by both CompactComposerControlsMenu and CodexTraitsPicker.

Create PR

Or push these changes by commenting:

@cursor push 23b302207c
Preview (23b302207c)
diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx
--- a/apps/web/src/components/ChatView.tsx
+++ b/apps/web/src/components/ChatView.tsx
@@ -5723,6 +5723,15 @@
   );
 });
 
+const REASONING_LABEL_BY_OPTION: Record<CodexReasoningEffort, string> = {
+  low: "Low",
+  medium: "Medium",
+  high: "High",
+  xhigh: "Extra High",
+};
+
+const DEFAULT_CODEX_REASONING_EFFORT = getDefaultReasoningEffort("codex");
+
 const CompactComposerControlsMenu = memo(function CompactComposerControlsMenu(props: {
   activePlan: boolean;
   interactionMode: ProviderInteractionMode;
@@ -5738,14 +5747,6 @@
   onTogglePlanSidebar: () => void;
   onToggleRuntimeMode: () => void;
 }) {
-  const defaultReasoningEffort = getDefaultReasoningEffort("codex");
-  const reasoningLabelByOption: Record<CodexReasoningEffort, string> = {
-    low: "Low",
-    medium: "Medium",
-    high: "High",
-    xhigh: "Extra High",
-  };
-
   return (
     <Menu>
       <MenuTrigger
@@ -5776,8 +5777,8 @@
               >
                 {props.reasoningOptions.map((effort) => (
                   <MenuRadioItem key={effort} value={effort}>
-                    {reasoningLabelByOption[effort]}
-                    {effort === defaultReasoningEffort ? " (default)" : ""}
+                    {REASONING_LABEL_BY_OPTION[effort]}
+                    {effort === DEFAULT_CODEX_REASONING_EFFORT ? " (default)" : ""}
                   </MenuRadioItem>
                 ))}
               </MenuRadioGroup>
@@ -5847,15 +5848,8 @@
   onFastModeChange: (enabled: boolean) => void;
 }) {
   const [isMenuOpen, setIsMenuOpen] = useState(false);
-  const defaultReasoningEffort = getDefaultReasoningEffort("codex");
-  const reasoningLabelByOption: Record<CodexReasoningEffort, string> = {
-    low: "Low",
-    medium: "Medium",
-    high: "High",
-    xhigh: "Extra High",
-  };
   const triggerLabel = [
-    reasoningLabelByOption[props.effort],
+    REASONING_LABEL_BY_OPTION[props.effort],
     ...(props.fastModeEnabled ? ["Fast"] : []),
   ]
     .filter(Boolean)
@@ -5894,8 +5888,8 @@
           >
             {props.options.map((effort) => (
               <MenuRadioItem key={effort} value={effort}>
-                {reasoningLabelByOption[effort]}
-                {effort === defaultReasoningEffort ? " (default)" : ""}
+                {REASONING_LABEL_BY_OPTION[effort]}
+                {effort === DEFAULT_CODEX_REASONING_EFFORT ? " (default)" : ""}
               </MenuRadioItem>
             ))}
           </MenuRadioGroup>

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Inconsistent width measurement between initial and observer callbacks
    • Changed the ResizeObserver callback to use composerForm.clientWidth instead of entry.contentRect.width, making both measurement points consistent.

Create PR

Or push these changes by commenting:

@cursor push 580eb4b5a8
Preview (580eb4b5a8)
diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx
--- a/apps/web/src/components/ChatView.tsx
+++ b/apps/web/src/components/ChatView.tsx
@@ -1928,7 +1928,7 @@
       const [entry] = entries;
       if (!entry) return;
 
-      const nextCompact = shouldUseCompactComposerFooter(entry.contentRect.width, {
+      const nextCompact = shouldUseCompactComposerFooter(composerForm.clientWidth, {
         hasWideActions: composerFooterHasWideActions,
       });
       setIsComposerFooterCompact((previous) => (previous === nextCompact ? previous : nextCompact));

juliusmarminge and others added 4 commits March 9, 2026 11:04
- add `min-w-0` to chat/flex containers to prevent composer/sidebar overflow
- move `BranchToolbar` into the chat column so layout stays aligned with plan sidebar
- extract sidebar inset base classes to shared logic and add a regression test for `min-w-0`
- add shared footer layout breakpoint helper with tests
- switch chat composer footer to compact menu when width is constrained
- preserve plan sidebar toggle behavior while consolidating footer actions
- Replace `SIDEBAR_INSET_BASE_CLASSNAME` with an inline class string in `sidebar.tsx`
- Delete the now-unused `sidebar.logic.ts` helper and its focused unit test
@juliusmarminge juliusmarminge force-pushed the t3code/fix-plan-composer-layout branch from b4d1a69 to c714e8d Compare March 9, 2026 18:11
- Measure composer width from the form element in initial and resize paths
- Avoid relying on ResizeObserver contentRect width for compact footer layout decisions
- Add composer footer/action data attributes for targeted width measurement
- Enforce a minimum composer width before allowing inline diff sidebar
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Inline arrow function defeats memo on compact menu
    • Extracted the inline arrow function into a stable toggleRuntimeMode useCallback, consistent with how toggleInteractionMode and togglePlanSidebar are handled, so the memo on CompactComposerControlsMenu can properly skip re-renders.

Create PR

Or push these changes by commenting:

@cursor push 457d6c4f14
Preview (457d6c4f14)
diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx
--- a/apps/web/src/components/ChatView.tsx
+++ b/apps/web/src/components/ChatView.tsx
@@ -1675,6 +1675,11 @@
   const toggleInteractionMode = useCallback(() => {
     handleInteractionModeChange(interactionMode === "plan" ? "default" : "plan");
   }, [handleInteractionModeChange, interactionMode]);
+  const toggleRuntimeMode = useCallback(() => {
+    void handleRuntimeModeChange(
+      runtimeMode === "full-access" ? "approval-required" : "full-access",
+    );
+  }, [handleRuntimeModeChange, runtimeMode]);
   const togglePlanSidebar = useCallback(() => {
     setPlanSidebarOpen((open) => {
       if (open) {
@@ -3745,11 +3750,7 @@
                       onCodexFastModeChange={onCodexFastModeChange}
                       onToggleInteractionMode={toggleInteractionMode}
                       onTogglePlanSidebar={togglePlanSidebar}
-                      onToggleRuntimeMode={() =>
-                        void handleRuntimeModeChange(
-                          runtimeMode === "full-access" ? "approval-required" : "full-access",
-                        )
-                      }
+                      onToggleRuntimeMode={toggleRuntimeMode}
                     />
                   ) : (
                     <>

@juliusmarminge
Copy link
Copy Markdown
Member Author

@cursor push 457d6c4

@juliusmarminge juliusmarminge merged commit 7a3d4d1 into main Mar 9, 2026
4 checks passed
@juliusmarminge juliusmarminge deleted the t3code/fix-plan-composer-layout branch March 9, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants