Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/components/RightSidebar/CodeReview/ReviewControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ export const ReviewControls: React.FC<ReviewControlsProps> = ({
}
};

const handleDirtyToggle = (e: React.ChangeEvent<HTMLInputElement>) => {
onFiltersChange({ ...filters, includeDirty: e.target.checked });
const handleUncommittedToggle = (e: React.ChangeEvent<HTMLInputElement>) => {
onFiltersChange({ ...filters, includeUncommitted: e.target.checked });
};

const handleSetDefault = () => {
Expand Down Expand Up @@ -202,14 +202,19 @@ export const ReviewControls: React.FC<ReviewControlsProps> = ({
)}

<CheckboxLabel>
<input type="checkbox" checked={filters.includeDirty} onChange={handleDirtyToggle} />
Dirty
<input
type="checkbox"
checked={filters.includeUncommitted}
onChange={handleUncommittedToggle}
/>
Uncommitted
</CheckboxLabel>

<UntrackedStatus
workspaceId={workspaceId}
workspacePath={workspacePath}
refreshTrigger={refreshTrigger}
onRefresh={onRefresh}
/>

<Separator />
Expand Down
30 changes: 15 additions & 15 deletions src/components/RightSidebar/CodeReview/ReviewPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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}`;
}

Expand Down Expand Up @@ -295,17 +295,17 @@ export const ReviewPanel: React.FC<ReviewPanelProps> = ({
// 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
);

const [filters, setFilters] = useState<ReviewFiltersType>({
showReviewed: true,
statusFilter: "all",
diffBase: diffBase,
includeDirty: includeDirty,
includeUncommitted: includeUncommitted,
});

// Load file tree - when workspace, diffBase, or refreshTrigger changes
Expand All @@ -317,7 +317,7 @@ export const ReviewPanel: React.FC<ReviewPanelProps> = ({
try {
const numstatCommand = buildGitDiffCommand(
filters.diffBase,
filters.includeDirty,
filters.includeUncommitted,
"", // No path filter for file tree
"numstat"
);
Expand Down Expand Up @@ -352,7 +352,7 @@ export const ReviewPanel: React.FC<ReviewPanelProps> = ({
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(() => {
Expand All @@ -369,7 +369,7 @@ export const ReviewPanel: React.FC<ReviewPanelProps> = ({

const diffCommand = buildGitDiffCommand(
filters.diffBase,
filters.includeDirty,
filters.includeUncommitted,
pathFilter,
"diff"
);
Expand Down Expand Up @@ -435,7 +435,7 @@ export const ReviewPanel: React.FC<ReviewPanelProps> = ({
workspaceId,
workspacePath,
filters.diffBase,
filters.includeDirty,
filters.includeUncommitted,
selectedFilePath,
refreshTrigger,
]);
Expand All @@ -445,10 +445,10 @@ export const ReviewPanel: React.FC<ReviewPanelProps> = ({
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;
Expand Down
13 changes: 12 additions & 1 deletion src/components/RightSidebar/CodeReview/UntrackedStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface UntrackedStatusProps {
workspaceId: string;
workspacePath: string;
refreshTrigger?: number;
onRefresh?: () => void;
}

const Container = styled.div`
Expand Down Expand Up @@ -128,19 +129,25 @@ export const UntrackedStatus: React.FC<UntrackedStatusProps> = ({
workspaceId,
workspacePath,
refreshTrigger,
onRefresh,
}) => {
const [untrackedFiles, setUntrackedFiles] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [showTooltip, setShowTooltip] = useState(false);
const [isTracking, setIsTracking] = useState(false);
const containerRef = useRef<HTMLDivElement>(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);
Expand All @@ -167,6 +174,7 @@ export const UntrackedStatus: React.FC<UntrackedStatusProps> = ({
} catch (err) {
console.error("Failed to load untracked files:", err);
} finally {
loadingRef.current = false;
setIsLoading(false);
}
};
Expand Down Expand Up @@ -208,8 +216,11 @@ export const UntrackedStatus: React.FC<UntrackedStatusProps> = ({
);

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);
}
Expand Down
4 changes: 2 additions & 2 deletions src/types/review.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down