diff --git a/packages/ui/src/components/layout/ContextPanel.tsx b/packages/ui/src/components/layout/ContextPanel.tsx
index 8df72d98..93cbf102 100644
--- a/packages/ui/src/components/layout/ContextPanel.tsx
+++ b/packages/ui/src/components/layout/ContextPanel.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import { RiCloseLine, RiFullscreenExitLine, RiFullscreenLine } from '@remixicon/react';
import { Button } from '@/components/ui/button';
-import { DiffView, FilesView } from '@/components/views';
+import { DiffView, FilesView, PlanView } from '@/components/views';
import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory';
import { cn } from '@/lib/utils';
import { useFilesViewTabsStore } from '@/stores/useFilesViewTabsStore';
@@ -145,7 +145,7 @@ export const ContextPanel: React.FC = () => {
const activeFilePath = useFilesViewTabsStore((state) => (directoryKey ? (state.byRoot[directoryKey]?.selectedPath ?? null) : null));
- const panelTitle = panelState?.mode === 'diff' ? 'Diff' : panelState?.mode === 'file' ? 'File' : panelState?.mode === 'context' ? 'Context' : 'Panel';
+ const panelTitle = panelState?.mode === 'diff' ? 'Diff' : panelState?.mode === 'file' ? 'File' : panelState?.mode === 'context' ? 'Context' : panelState?.mode === 'plan' ? 'Plan' : 'Panel';
const effectivePath = panelState?.mode === 'file' ? (activeFilePath ?? panelState?.targetPath ?? null) : panelState?.mode === 'context' ? null : (panelState?.targetPath ?? null);
const pathLabel = getRelativePathLabel(effectivePath, effectiveDirectory);
@@ -155,7 +155,9 @@ export const ContextPanel: React.FC = () => {
?
: panelState?.mode === 'context'
?
- : null;
+ : panelState?.mode === 'plan'
+ ?
+ : null;
const header = (
diff --git a/packages/ui/src/components/layout/Header.tsx b/packages/ui/src/components/layout/Header.tsx
index 5929a237..2eae3616 100644
--- a/packages/ui/src/components/layout/Header.tsx
+++ b/packages/ui/src/components/layout/Header.tsx
@@ -142,6 +142,7 @@ export const Header: React.FC = () => {
const toggleBottomTerminal = useUIStore((state) => state.toggleBottomTerminal);
const toggleRightSidebar = useUIStore((state) => state.toggleRightSidebar);
const openContextOverview = useUIStore((state) => state.openContextOverview);
+ const openContextPlan = useUIStore((state) => state.openContextPlan);
const closeContextPanel = useUIStore((state) => state.closeContextPanel);
const contextPanelByDirectory = useUIStore((state) => state.contextPanelByDirectory);
const setSettingsDialogOpen = useUIStore((state) => state.setSettingsDialogOpen);
@@ -917,6 +918,30 @@ export const Header: React.FC = () => {
return Boolean(panelState?.isOpen && panelState.mode === 'context');
}, [contextPanelByDirectory, openDirectory]);
+ const handleOpenContextPlan = React.useCallback(() => {
+ const directory = normalize(openDirectory || '');
+ if (!directory) {
+ return;
+ }
+
+ const panelState = contextPanelByDirectory[directory];
+ if (panelState?.isOpen && panelState.mode === 'plan') {
+ closeContextPanel(directory);
+ return;
+ }
+
+ openContextPlan(directory);
+ }, [closeContextPanel, contextPanelByDirectory, openContextPlan, openDirectory]);
+
+ const isContextPlanActive = React.useMemo(() => {
+ const directory = normalize(openDirectory || '');
+ if (!directory) {
+ return false;
+ }
+ const panelState = contextPanelByDirectory[directory];
+ return Boolean(panelState?.isOpen && panelState.mode === 'plan');
+ }, [contextPanelByDirectory, openDirectory]);
+
const headerIconButtonClass = 'app-region-no-drag inline-flex h-9 w-9 items-center justify-center gap-2 p-2 rounded-md typography-ui-label font-medium text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:pointer-events-none disabled:opacity-50 hover:text-foreground hover:bg-interactive-hover transition-colors';
const desktopPaddingClass = React.useMemo(() => {
@@ -1644,6 +1669,23 @@ export const Header: React.FC = () => {
percentIconClassName="h-5 w-5"
/>
)}
+ {showPlanTab && (
+
+
+
+
+
+ Plan
+
+
+ )}
void;
openContextFile: (directory: string, filePath: string) => void;
openContextOverview: (directory: string) => void;
+ openContextPlan: (directory: string) => void;
closeContextPanel: (directory: string) => void;
toggleContextPanelExpanded: (directory: string) => void;
setContextPanelWidth: (directory: string, width: number) => void;
@@ -549,6 +550,29 @@ export const useUIStore = create()(
});
},
+ openContextPlan: (directory) => {
+ const normalizedDirectory = normalizeDirectoryPath((directory || '').trim());
+ if (!normalizedDirectory) {
+ return;
+ }
+
+ set((state) => {
+ const prev = state.contextPanelByDirectory[normalizedDirectory];
+ const current = touchContextPanelState(prev);
+ const byDirectory = {
+ ...state.contextPanelByDirectory,
+ [normalizedDirectory]: {
+ ...current,
+ isOpen: true,
+ mode: 'plan' as const,
+ targetPath: null,
+ },
+ };
+
+ return { contextPanelByDirectory: clampContextPanelRoots(byDirectory, 20) };
+ });
+ },
+
closeContextPanel: (directory) => {
const normalizedDirectory = normalizeDirectoryPath((directory || '').trim());
if (!normalizedDirectory) {