@@ -132,6 +132,7 @@ import {
132132 pendingPrMessageAtom ,
133133 pendingReviewMessageAtom ,
134134 pendingUserQuestionsAtom ,
135+ planDisplayModeAtom ,
135136 planEditRefetchTriggerAtomFamily ,
136137 planSidebarOpenAtomFamily ,
137138 QUESTIONS_SKIPPED_MESSAGE ,
@@ -145,6 +146,7 @@ import {
145146 undoStackAtom ,
146147 workspaceDiffCacheAtomFamily ,
147148 type AgentMode ,
149+ type PlanDisplayMode ,
148150 type SelectedCommit
149151} from "../atoms"
150152import { BUILTIN_SLASH_COMMANDS } from "../commands"
@@ -3075,11 +3077,11 @@ const ChatViewInner = memo(function ChatViewInner({
30753077 // Handle pending "Build plan" from sidebar
30763078 useEffect ( ( ) => {
30773079 // Only trigger if this is the target sub-chat and we're active
3078- if ( pendingBuildPlanSubChatId === subChatId && isActive && ! isSplitPane ) {
3080+ if ( pendingBuildPlanSubChatId === subChatId && isActive ) {
30793081 setPendingBuildPlanSubChatId ( null ) // Clear immediately to prevent double-trigger
30803082 handleApprovePlan ( )
30813083 }
3082- } , [ pendingBuildPlanSubChatId , subChatId , isActive , isSplitPane , setPendingBuildPlanSubChatId , handleApprovePlan ] )
3084+ } , [ pendingBuildPlanSubChatId , subChatId , isActive , setPendingBuildPlanSubChatId , handleApprovePlan ] )
30833085
30843086 // Detect PR URLs in assistant messages and store them
30853087 // Initialize with existing PR URL to prevent duplicate toast on re-mount
@@ -4710,12 +4712,20 @@ export function ChatView({
47104712 [ activeSubChatIdForPlan ] ,
47114713 )
47124714 const [ isPlanSidebarOpen , setIsPlanSidebarOpen ] = useAtom ( planSidebarAtom )
4715+ const [ planDisplayMode , setPlanDisplayMode ] = useAtom ( planDisplayModeAtom )
47134716 const currentPlanPathAtom = useMemo (
47144717 ( ) => currentPlanPathAtomFamily ( activeSubChatIdForPlan || "" ) ,
47154718 [ activeSubChatIdForPlan ] ,
47164719 )
47174720 const [ currentPlanPath , setCurrentPlanPath ] = useAtom ( currentPlanPathAtom )
47184721
4722+ // Effective plan display mode: force center-peek in split view, otherwise use user preference
4723+ // Computed early because mutual exclusion logic needs it
4724+ const isSplitViewForPlan = useAgentSubChatStore (
4725+ useShallow ( ( state ) => state . splitPaneIds . length >= 2 && state . splitPaneIds . includes ( state . activeSubChatId ) )
4726+ )
4727+ const effectivePlanDisplayMode : PlanDisplayMode = isSplitViewForPlan ? "center-peek" : planDisplayMode
4728+
47194729 // File viewer sidebar state - per-chat open file path
47204730 const fileViewerAtom = useMemo (
47214731 ( ) => fileViewerOpenAtomFamily ( chatId ) ,
@@ -4736,16 +4746,16 @@ export function ChatView({
47364746 const toggleTerminalHotkey = useResolvedHotkeyDisplay ( "toggle-terminal" )
47374747
47384748 // Close plan sidebar when switching to a sub-chat that has no plan
4749+ // Skip in split view — clicking between panes shouldn't close the plan dialog
47394750 const prevSubChatIdRef = useRef ( activeSubChatIdForPlan )
47404751 useEffect ( ( ) => {
47414752 if ( prevSubChatIdRef . current !== activeSubChatIdForPlan ) {
4742- // Sub-chat changed - if new one has no plan path, close sidebar
4743- if ( ! currentPlanPath ) {
4753+ if ( ! currentPlanPath && ! isSplitViewForPlan ) {
47444754 setIsPlanSidebarOpen ( false )
47454755 }
47464756 prevSubChatIdRef . current = activeSubChatIdForPlan
47474757 }
4748- } , [ activeSubChatIdForPlan , currentPlanPath , setIsPlanSidebarOpen ] )
4758+ } , [ activeSubChatIdForPlan , currentPlanPath , isSplitViewForPlan , setIsPlanSidebarOpen ] )
47494759 const setPendingBuildPlanSubChatId = useSetAtom ( pendingBuildPlanSubChatIdAtom )
47504760
47514761 // Read plan edit refetch trigger from atom (set by ChatViewInner when Edit completes)
@@ -4813,14 +4823,16 @@ export function ChatView({
48134823 // Track previous states to detect opens/closes
48144824 const prevSidebarStatesRef = useRef ( {
48154825 details : isDetailsSidebarOpen ,
4816- plan : isPlanSidebarOpen && ! ! currentPlanPath ,
4826+ plan : isPlanSidebarOpen && ! ! currentPlanPath && effectivePlanDisplayMode === "side-peek" ,
48174827 terminal : isTerminalSidebarOpen ,
48184828 } )
48194829
48204830 useEffect ( ( ) => {
48214831 const prev = prevSidebarStatesRef . current
48224832 const auto = autoClosedStateRef . current
4823- const isPlanOpen = isPlanSidebarOpen && ! ! currentPlanPath
4833+ // Only treat plan as a physical sidebar conflict when in side-peek mode
4834+ // In center-peek (dialog) mode, plan floats above everything — no conflict
4835+ const isPlanOpen = isPlanSidebarOpen && ! ! currentPlanPath && effectivePlanDisplayMode === "side-peek"
48244836
48254837 // Detect state changes
48264838 const detailsJustOpened = isDetailsSidebarOpen && ! prev . details
@@ -4885,6 +4897,7 @@ export function ChatView({
48854897 isDetailsSidebarOpen ,
48864898 isPlanSidebarOpen ,
48874899 currentPlanPath ,
4900+ effectivePlanDisplayMode ,
48884901 isTerminalSidebarOpen ,
48894902 terminalDisplayMode ,
48904903 setIsDetailsSidebarOpen ,
@@ -5102,6 +5115,12 @@ export function ChatView({
51025115 } ) )
51035116 )
51045117
5118+ // isSplitView alias using local splitPaneIds (for JSX rendering)
5119+ const isSplitView = splitPaneIds . length >= 2 && splitPaneIds . includes ( activeSubChatId )
5120+ const handlePlanDisplayModeChange = useCallback ( ( mode : PlanDisplayMode ) => {
5121+ setPlanDisplayMode ( mode )
5122+ } , [ setPlanDisplayMode ] )
5123+
51055124 // Clear sub-chat "unseen changes" indicator when sub-chat becomes active
51065125 useEffect ( ( ) => {
51075126 if ( ! activeSubChatId ) return
@@ -7259,9 +7278,8 @@ Make sure to preserve all functionality from both branches when resolving confli
72597278 ) }
72607279 </ div >
72617280
7262- { /* Plan Sidebar - shows plan files on the right (leftmost right sidebar) */ }
7263- { /* Only show when we have an active sub-chat with a plan */ }
7264- { ! isMobileFullscreen && activeSubChatIdForPlan && (
7281+ { /* Plan Sidebar - side-peek mode (ResizableSidebar) */ }
7282+ { ! isMobileFullscreen && activeSubChatIdForPlan && effectivePlanDisplayMode === "side-peek" && (
72657283 < ResizableSidebar
72667284 isOpen = { isPlanSidebarOpen && ! ! currentPlanPath }
72677285 onClose = { ( ) => setIsPlanSidebarOpen ( false ) }
@@ -7283,9 +7301,30 @@ Make sure to preserve all functionality from both branches when resolving confli
72837301 onBuildPlan = { handleApprovePlanFromSidebar }
72847302 refetchTrigger = { planEditRefetchTrigger }
72857303 mode = { currentMode }
7304+ displayMode = "side-peek"
7305+ onDisplayModeChange = { handlePlanDisplayModeChange }
72867306 />
72877307 </ ResizableSidebar >
72887308 ) }
7309+ { /* Plan Sidebar - center-peek mode (Dialog overlay) */ }
7310+ { activeSubChatIdForPlan && effectivePlanDisplayMode === "center-peek" && isPlanSidebarOpen && ! ! currentPlanPath && (
7311+ < DiffCenterPeekDialog
7312+ isOpen = { true }
7313+ onClose = { ( ) => setIsPlanSidebarOpen ( false ) }
7314+ >
7315+ < AgentPlanSidebar
7316+ chatId = { activeSubChatIdForPlan }
7317+ planPath = { currentPlanPath }
7318+ onClose = { ( ) => setIsPlanSidebarOpen ( false ) }
7319+ onBuildPlan = { handleApprovePlanFromSidebar }
7320+ refetchTrigger = { planEditRefetchTrigger }
7321+ mode = { currentMode }
7322+ displayMode = "center-peek"
7323+ onDisplayModeChange = { handlePlanDisplayModeChange }
7324+ isSplitView = { isSplitView }
7325+ />
7326+ </ DiffCenterPeekDialog >
7327+ ) }
72897328
72907329 { /* Diff View - hidden on mobile fullscreen and when diff is not available */ }
72917330 { /* Supports three display modes: side-peek (sidebar), center-peek (dialog), full-page */ }
@@ -7497,7 +7536,7 @@ Make sure to preserve all functionality from both branches when resolving confli
74977536 onBuildPlan = { handleApprovePlanFromSidebar }
74987537 planRefetchTrigger = { planEditRefetchTrigger }
74997538 activeSubChatId = { activeSubChatIdForPlan }
7500- isPlanSidebarOpen = { isPlanSidebarOpen && ! ! currentPlanPath }
7539+ isPlanSidebarOpen = { isPlanSidebarOpen && ! ! currentPlanPath && effectivePlanDisplayMode === "side-peek" }
75017540 isTerminalSidebarOpen = { isTerminalSidebarOpen }
75027541 isDiffSidebarOpen = { isDiffSidebarOpen }
75037542 diffDisplayMode = { diffDisplayMode }
0 commit comments