From 3cb4383d9d292547322ed0236c47a28dfa511b8d Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Sun, 19 Jul 2026 03:07:54 -0600 Subject: [PATCH] Extract split-pane layout into center-pane-split.tsx Move the ~80-line isSplit render block out of agents-view.tsx into a new presentational CenterPaneSplit component. Pure refactor: the JSX is unchanged and the terminal DOM node is still portaled into the shared split terminal slot; refs and layout effects stay in the parent. agents-view.tsx: 1006 -> 933 lines. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/web/src/components/app/agents-view.tsx | 97 ++-------------- .../src/components/app/center-pane-split.tsx | 106 ++++++++++++++++++ 2 files changed, 118 insertions(+), 85 deletions(-) create mode 100644 apps/web/src/components/app/center-pane-split.tsx diff --git a/apps/web/src/components/app/agents-view.tsx b/apps/web/src/components/app/agents-view.tsx index 2375030a..1a9038db 100644 --- a/apps/web/src/components/app/agents-view.tsx +++ b/apps/web/src/components/app/agents-view.tsx @@ -8,7 +8,7 @@ import { } from "react"; import { createPortal } from "react-dom"; import { Routes, Route, useNavigate, useParams } from "react-router-dom"; -import { PanelLeftOpen, PanelRightOpen, Split } from "lucide-react"; +import { PanelLeftOpen, PanelRightOpen } from "lucide-react"; import { ChangesTab } from "@/components/app/changes-tab"; import { ChangesSettingsPopover } from "@/components/app/changes-settings-popover"; @@ -17,6 +17,7 @@ import { TAB_DRAG_MIME, } from "@/components/app/center-pane-tab-bar"; import { SplitDropZones } from "@/components/app/split-drop-zones"; +import { CenterPaneSplit } from "@/components/app/center-pane-split"; import { useAgentDiffStats } from "@/hooks/use-agent-diff-stats"; import { useSplitPane } from "@/hooks/use-split-pane"; @@ -50,11 +51,6 @@ import { } from "@/components/app/types"; import { Button } from "@/components/ui/button"; import { GlassSidebar } from "@/components/ui/glass-sidebar"; -import { - ResizableHandle, - ResizablePanel, - ResizablePanelGroup, -} from "@/components/ui/resizable"; import { uploadAgentMedia } from "@/lib/media-upload"; import { type AgentType, @@ -748,85 +744,16 @@ export function AgentsView({ onDrop={handleContentDrop} > {isSplit ? ( -
- - -
-
- - {splitState.left === "terminal" - ? "Terminal" - : "Changes"} - - {splitState.left === "changes" && !isMobile ? ( - - ) : null} -
-
- {splitState.left === "terminal" ? ( -
- ) : ( - changesElement - )} -
-
- - - -
-
- - {splitState.right === "terminal" - ? "Terminal" - : "Changes"} - - {splitState.right === "changes" && !isMobile ? ( - - ) : null} -
-
- {splitState.right === "terminal" ? ( -
- ) : ( - changesElement - )} -
-
- - - -
+ ) : ( <>
; + splitButtonRef: React.RefObject; + splitTerminalSlotRef: React.RefObject; + changesElement: React.ReactNode; + isMobile: boolean; + onLayoutChange: (layout: Record) => void; + onExitSplit: () => void; +}; + +/** + * The split-pane layout for the center pane: two resizable panels, each showing + * either the terminal (via the shared terminal slot) or the Changes tab, with an + * unsplit button anchored on the divider. Purely presentational — the terminal + * DOM node is portaled into `splitTerminalSlotRef` by the parent. + */ +export function CenterPaneSplit({ + splitState, + splitLeftRef, + splitButtonRef, + splitTerminalSlotRef, + changesElement, + isMobile, + onLayoutChange, + onExitSplit, +}: CenterPaneSplitProps): JSX.Element { + return ( +
+ + +
+
+ + {splitState.left === "terminal" ? "Terminal" : "Changes"} + + {splitState.left === "changes" && !isMobile ? ( + + ) : null} +
+
+ {splitState.left === "terminal" ? ( +
+ ) : ( + changesElement + )} +
+
+ + + +
+
+ + {splitState.right === "terminal" ? "Terminal" : "Changes"} + + {splitState.right === "changes" && !isMobile ? ( + + ) : null} +
+
+ {splitState.right === "terminal" ? ( +
+ ) : ( + changesElement + )} +
+
+ + + +
+ ); +}