diff --git a/src/components/RightSidebar/CodeReview/ReviewControls.tsx b/src/components/RightSidebar/CodeReview/ReviewControls.tsx index d7f6c2587..74aceb059 100644 --- a/src/components/RightSidebar/CodeReview/ReviewControls.tsx +++ b/src/components/RightSidebar/CodeReview/ReviewControls.tsx @@ -162,8 +162,8 @@ export const ReviewControls: React.FC = ({ } }; - const handleDirtyToggle = (e: React.ChangeEvent) => { - onFiltersChange({ ...filters, includeDirty: e.target.checked }); + const handleUncommittedToggle = (e: React.ChangeEvent) => { + onFiltersChange({ ...filters, includeUncommitted: e.target.checked }); }; const handleSetDefault = () => { @@ -202,14 +202,19 @@ export const ReviewControls: React.FC = ({ )} - - Dirty + + Uncommitted diff --git a/src/components/RightSidebar/CodeReview/ReviewPanel.tsx b/src/components/RightSidebar/CodeReview/ReviewPanel.tsx index fda0ec552..016335052 100644 --- a/src/components/RightSidebar/CodeReview/ReviewPanel.tsx +++ b/src/components/RightSidebar/CodeReview/ReviewPanel.tsx @@ -236,12 +236,12 @@ interface DiagnosticInfo { } /** - * Build git diff command based on diffBase and includeDirty flag + * Build git diff command based on diffBase and includeUncommitted flag * Shared logic between numstat (file tree) and diff (hunks) commands */ function buildGitDiffCommand( diffBase: string, - includeDirty: boolean, + includeUncommitted: boolean, pathFilter: string, command: "diff" | "numstat" ): string { @@ -258,8 +258,8 @@ function buildGitDiffCommand( cmd = `git diff ${diffBase}...HEAD${flag}${pathFilter}`; } - // Append dirty changes if requested - if (includeDirty) { + // Append uncommitted changes if requested + if (includeUncommitted) { cmd += ` && git diff HEAD${flag}${pathFilter}`; } @@ -295,9 +295,9 @@ export const ReviewPanel: React.FC = ({ // Persist diff base per workspace (falls back to global default) const [diffBase, setDiffBase] = usePersistedState(`review-diff-base:${workspaceId}`, defaultBase); - // Persist includeDirty flag per workspace - const [includeDirty, setIncludeDirty] = usePersistedState( - `review-include-dirty:${workspaceId}`, + // Persist includeUncommitted flag per workspace + const [includeUncommitted, setIncludeUncommitted] = usePersistedState( + `review-include-uncommitted:${workspaceId}`, false ); @@ -305,7 +305,7 @@ export const ReviewPanel: React.FC = ({ showReviewed: true, statusFilter: "all", diffBase: diffBase, - includeDirty: includeDirty, + includeUncommitted: includeUncommitted, }); // Load file tree - when workspace, diffBase, or refreshTrigger changes @@ -317,7 +317,7 @@ export const ReviewPanel: React.FC = ({ try { const numstatCommand = buildGitDiffCommand( filters.diffBase, - filters.includeDirty, + filters.includeUncommitted, "", // No path filter for file tree "numstat" ); @@ -352,7 +352,7 @@ export const ReviewPanel: React.FC = ({ return () => { cancelled = true; }; - }, [workspaceId, workspacePath, filters.diffBase, filters.includeDirty, refreshTrigger]); + }, [workspaceId, workspacePath, filters.diffBase, filters.includeUncommitted, refreshTrigger]); // Load diff hunks - when workspace, diffBase, selected path, or refreshTrigger changes useEffect(() => { @@ -369,7 +369,7 @@ export const ReviewPanel: React.FC = ({ const diffCommand = buildGitDiffCommand( filters.diffBase, - filters.includeDirty, + filters.includeUncommitted, pathFilter, "diff" ); @@ -435,7 +435,7 @@ export const ReviewPanel: React.FC = ({ workspaceId, workspacePath, filters.diffBase, - filters.includeDirty, + filters.includeUncommitted, selectedFilePath, refreshTrigger, ]); @@ -445,10 +445,10 @@ export const ReviewPanel: React.FC = ({ setDiffBase(filters.diffBase); }, [filters.diffBase, setDiffBase]); - // Persist includeDirty when it changes + // Persist includeUncommitted when it changes useEffect(() => { - setIncludeDirty(filters.includeDirty); - }, [filters.includeDirty, setIncludeDirty]); + setIncludeUncommitted(filters.includeUncommitted); + }, [filters.includeUncommitted, setIncludeUncommitted]); // For MVP: No review state tracking, just show all hunks const filteredHunks = hunks; diff --git a/src/components/RightSidebar/CodeReview/UntrackedStatus.tsx b/src/components/RightSidebar/CodeReview/UntrackedStatus.tsx index 0d9c88e7e..4c8867fb1 100644 --- a/src/components/RightSidebar/CodeReview/UntrackedStatus.tsx +++ b/src/components/RightSidebar/CodeReview/UntrackedStatus.tsx @@ -10,6 +10,7 @@ interface UntrackedStatusProps { workspaceId: string; workspacePath: string; refreshTrigger?: number; + onRefresh?: () => void; } const Container = styled.div` @@ -128,6 +129,7 @@ export const UntrackedStatus: React.FC = ({ workspaceId, workspacePath, refreshTrigger, + onRefresh, }) => { const [untrackedFiles, setUntrackedFiles] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -135,12 +137,17 @@ export const UntrackedStatus: React.FC = ({ const [isTracking, setIsTracking] = useState(false); const containerRef = useRef(null); const hasLoadedOnce = useRef(false); + const loadingRef = useRef(false); // Prevent concurrent loads // Load untracked files useEffect(() => { let cancelled = false; const loadUntracked = async () => { + // Prevent concurrent loads + if (loadingRef.current) return; + loadingRef.current = true; + // Only show loading on first load ever, not on subsequent refreshes if (!hasLoadedOnce.current) { setIsLoading(true); @@ -167,6 +174,7 @@ export const UntrackedStatus: React.FC = ({ } catch (err) { console.error("Failed to load untracked files:", err); } finally { + loadingRef.current = false; setIsLoading(false); } }; @@ -208,8 +216,11 @@ export const UntrackedStatus: React.FC = ({ ); if (result.success) { - setUntrackedFiles([]); + // Close tooltip first setShowTooltip(false); + // Trigger refresh - this will reload untracked files from git + // Don't clear untrackedFiles optimistically to avoid flicker + onRefresh?.(); } else { console.error("Failed to track files:", result.error); } diff --git a/src/types/review.ts b/src/types/review.ts index ebfe642d3..c6797885d 100644 --- a/src/types/review.ts +++ b/src/types/review.ts @@ -82,8 +82,8 @@ export interface ReviewFilters { filePathFilter?: string; /** Base reference to diff against (e.g., "HEAD", "main", "origin/main") */ diffBase: string; - /** Whether to include uncommitted dirty changes in the diff */ - includeDirty: boolean; + /** Whether to include uncommitted changes (staged + unstaged) in the diff */ + includeUncommitted: boolean; } /**