diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index f5ea5bb1eba..70bbc296288 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -2112,12 +2112,13 @@ function ChatViewContent(props: ChatViewProps) { worktreePath: activeThread?.worktreePath ?? null, }) : null; + const gitStatusCwd = activeThread?.worktreePath ?? gitCwd; const gitStatusQuery = useEnvironmentQuery( - gitCwd === null + gitStatusCwd === null ? null : vcsEnvironment.status({ environmentId, - input: { cwd: gitCwd }, + input: { cwd: gitStatusCwd }, }), ); const keybindings = useAtomValue(primaryServerKeybindingsAtom); @@ -2151,6 +2152,9 @@ function ChatViewContent(props: ChatViewProps) { terminalUiLaunchContext?.threadId === activeThreadId ? terminalUiLaunchContext : null; // Default true while loading to avoid toolbar flicker. const isGitRepo = gitStatusQuery.data?.isRepo ?? true; + const initialDiffPanelGitScope = + gitStatusQuery.data?.hasWorkingTreeChanges === true ? "unstaged" : "branch"; + const diffPanelGitStatusResolutionKey = gitStatusQuery.data ? "resolved" : "pending"; const terminalShortcutLabelOptions = useMemo( () => ({ context: { @@ -4968,7 +4972,12 @@ function ChatViewContent(props: ChatViewProps) { /> ) : activeRightPanelSurface?.kind === "diff" ? ( - + ) : activeRightPanelSurface?.kind === "plan" ? ( ("stacked"); const [wordWrap, setWordWrap] = useState(settings.wordWrap); const [diffIgnoreWhitespace, setDiffIgnoreWhitespace] = useState(settings.diffIgnoreWhitespace); @@ -199,9 +205,6 @@ export default function DiffPanel({ mode = "inline", composerDraftTarget }: Diff strict: false, select: (params) => resolveThreadRouteRef(params), }); - const diffSelection = useDiffPanelStore((state) => - selectThreadDiffPanelSelection(state.byThreadKey, routeThreadRef), - ); const activeThreadId = routeThreadRef?.threadId ?? null; const activeThread = useThread(routeThreadRef); const activeProjectId = activeThread?.projectId ?? null; @@ -229,6 +232,13 @@ export default function DiffPanel({ mode = "inline", composerDraftTarget }: Diff }) : null, ); + const diffSelection = useDiffPanelStore((state) => + selectThreadDiffPanelSelection( + state.byThreadKey, + routeThreadRef, + initialGitScope === "unstaged", + ), + ); const isGitRepo = gitStatusQuery.data?.isRepo ?? true; const { turnDiffSummaries, inferredCheckpointTurnCountByTurnId } = useTurnDiffSummaries(activeThread); diff --git a/apps/web/src/diffPanelStore.test.ts b/apps/web/src/diffPanelStore.test.ts index 64846e8e9f1..607b7c8d580 100644 --- a/apps/web/src/diffPanelStore.test.ts +++ b/apps/web/src/diffPanelStore.test.ts @@ -9,12 +9,26 @@ const THREAD_REF = scopeThreadRef(EnvironmentId.make("environment-1"), ThreadId. describe("diffPanelStore", () => { beforeEach(() => useDiffPanelStore.setState({ byThreadKey: {}, branchBaseRefByThreadKey: {} })); - it("defaults each thread to branch changes with automatic base selection", () => { + it("defaults each thread to branch changes when the working tree is clean", () => { expect( selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF), ).toEqual({ kind: "branch", baseRef: null }); }); + it("defaults each thread to working changes when the working tree is dirty", () => { + expect( + selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF, true), + ).toEqual({ kind: "unstaged" }); + }); + + it("preserves an explicit scope selection when the working tree state changes", () => { + useDiffPanelStore.getState().selectGitScope(THREAD_REF, "branch"); + + expect( + selectThreadDiffPanelSelection(useDiffPanelStore.getState().byThreadKey, THREAD_REF, true), + ).toEqual({ kind: "branch", baseRef: null }); + }); + it("clears incompatible selection fields when changing scopes", () => { const store = useDiffPanelStore.getState(); store.selectTurn(THREAD_REF, TurnId.make("turn-1"), "src/app.ts"); diff --git a/apps/web/src/diffPanelStore.ts b/apps/web/src/diffPanelStore.ts index c946b286d1b..56b5ad23fec 100644 --- a/apps/web/src/diffPanelStore.ts +++ b/apps/web/src/diffPanelStore.ts @@ -11,6 +11,7 @@ export type DiffPanelSelection = | { kind: "turn"; turnId: TurnId; filePath: string | null; revealRequestId: number }; const DEFAULT_SELECTION: DiffPanelSelection = { kind: "branch", baseRef: null }; +const DEFAULT_WORKING_TREE_SELECTION: DiffPanelSelection = { kind: "unstaged" }; interface DiffPanelStoreState { byThreadKey: Record; @@ -133,7 +134,11 @@ export const useDiffPanelStore = create()( export function selectThreadDiffPanelSelection( byThreadKey: Record, ref: ScopedThreadRef | null | undefined, + hasWorkingTreeChanges = false, ): DiffPanelSelection { if (!ref) return DEFAULT_SELECTION; - return byThreadKey[scopedThreadKey(ref)] ?? DEFAULT_SELECTION; + return ( + byThreadKey[scopedThreadKey(ref)] ?? + (hasWorkingTreeChanges ? DEFAULT_WORKING_TREE_SELECTION : DEFAULT_SELECTION) + ); }