Skip to content

Comments

feat(desktop): add discard button to unstaged file rows#1698

Open
supungbab wants to merge 1 commit intosuperset-sh:mainfrom
supungbab:feat/unstaged-discard-button
Open

feat(desktop): add discard button to unstaged file rows#1698
supungbab wants to merge 1 commit intosuperset-sh:mainfrom
supungbab:feat/unstaged-discard-button

Conversation

@supungbab
Copy link

@supungbab supungbab commented Feb 23, 2026

feat(desktop): add discard button to unstaged file rows

Summary

  • Add a hover discard button (undo/trash icon) to the left of the stage (+) button on unstaged file rows
  • Improves accessibility — previously discard was only available via right-click context menu
  • Untracked/added files show trash icon, modified files show undo icon
  • Clicking triggers the existing confirmation dialog before discarding

View

image

Test plan

  • Hover over an unstaged modified file → verify undo icon appears left of the + button
  • Hover over an unstaged untracked file → verify trash icon appears left of the + button
  • Click discard button → verify confirmation dialog appears and discard works
  • Hover over a staged file → verify no discard button is shown

Summary by CodeRabbit

  • New Features
    • Added discard functionality with an action button and context menu option for file items
    • Discard button displays dynamic icons based on file status (trash icon for deletions, undo icon for reversions)
    • Included confirmation dialog to prevent accidental discards

Add a hover discard button next to the stage button for unstaged files,
improving accessibility over the previous context-menu-only approach.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 23, 2026

📝 Walkthrough

Walkthrough

The FileItem component now supports discarding file changes through a new optional callback prop. The component renders a discard action button and context menu option that trigger a confirmation dialog, with visual indicators (trash or undo icon) varying by file status.

Changes

Cohort / File(s) Summary
Discard Action Support
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx
Added optional onDiscard callback to FileItemProps. Updated hasAction logic to enable UI when discard is available. Added discard button with conditional icon (trash/undo based on status) and context menu option, both connected to discard confirmation dialog.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

A discard button hops into view,
With icons that change as the file status grew,
Trash and undo dance side by side,
Confirming each change with whisker'd pride! 🐰✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a discard button to unstaged file rows, which matches the primary feature in the changeset.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering all key aspects including summary, visual reference, and detailed test plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx (1)

285-288: ⚠️ Potential issue | 🟠 Major

Discard confirmation dialog unreachable when worktreePath is absent.

fileContent — which contains the discard button — is returned by both code paths, but the AlertDialog is only rendered in the second return (lines 345–374), gated behind the worktreePath check. If onDiscard is provided without worktreePath, clicking the discard button calls setShowDiscardDialog(true), but the AlertDialog is never mounted, so the dialog never appears and onDiscard is never invoked.

The fix is to extract the dialog so it is always rendered alongside fileContent:

🐛 Proposed fix — decouple the AlertDialog from the `worktreePath` branch
+	const discardDialog = onDiscard ? (
+		<AlertDialog open={showDiscardDialog} onOpenChange={setShowDiscardDialog}>
+			<AlertDialogContent className="max-w-[340px] gap-0 p-0">
+				<AlertDialogHeader className="px-4 pt-4 pb-2">
+					<AlertDialogTitle className="font-medium">
+						{discardDialogTitle}
+					</AlertDialogTitle>
+					<AlertDialogDescription>
+						{discardDialogDescription}
+					</AlertDialogDescription>
+				</AlertDialogHeader>
+				<AlertDialogFooter className="px-4 pb-4 pt-2 flex-row justify-end gap-2">
+					<Button
+						variant="ghost"
+						size="sm"
+						className="h-7 px-3 text-xs"
+						onClick={() => setShowDiscardDialog(false)}
+					>
+						Cancel
+					</Button>
+					<Button
+						variant="destructive"
+						size="sm"
+						className="h-7 px-3 text-xs"
+						onClick={handleConfirmDiscard}
+					>
+						{isDeleteAction ? "Delete" : "Discard"}
+					</Button>
+				</AlertDialogFooter>
+			</AlertDialogContent>
+		</AlertDialog>
+	) : null;

	if (!worktreePath) {
-		return fileContent;
+		return (
+			<>
+				{fileContent}
+				{discardDialog}
+			</>
+		);
	}

	return (
		<>
			<ContextMenu>
				<ContextMenuTrigger asChild>{fileContent}</ContextMenuTrigger>
				<ContextMenuContent className="w-48">
					{/* … existing menu items … */}
				</ContextMenuContent>
			</ContextMenu>

-			<AlertDialog open={showDiscardDialog} onOpenChange={setShowDiscardDialog}>
-				{/* … dialog content … */}
-			</AlertDialog>
+			{discardDialog}
		</>
	);

Also applies to: 345-374

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx`
around lines 285 - 288, The discard confirmation AlertDialog is only rendered
inside the worktreePath branch so when worktreePath is falsy the discard button
(part of fileContent) can toggle setShowDiscardDialog(true) but the dialog never
mounts; move/extract the AlertDialog out of the worktreePath conditional so it
is always rendered alongside fileContent, keep the existing state
(setShowDiscardDialog, showDiscardDialog) and confirmation handler (calling
onDiscard) intact, and if the dialog logic needs worktreePath use a guarded
check inside the dialog confirm handler to call onDiscard with worktreePath (or
call onDiscard without it when absent) so confirming always invokes onDiscard
regardless of the outer branch.
🧹 Nitpick comments (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx (1)

149-156: handleConfirmDiscard / handleDiscardClick missing useCallback.

handleClick and handleDoubleClick (lines 114–139) are memoised with useCallback, but these two new handlers are plain closures recreated on every render. handleConfirmDiscard closes over the onDiscard prop, so it's worth stabilising for consistency and to avoid stale-closure edge cases.

♻️ Memoize both handlers
-	const handleDiscardClick = () => {
+	const handleDiscardClick = useCallback(() => {
		setShowDiscardDialog(true);
-	};
+	}, []);

-	const handleConfirmDiscard = () => {
+	const handleConfirmDiscard = useCallback(() => {
		setShowDiscardDialog(false);
		onDiscard?.();
-	};
+	}, [onDiscard]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx`
around lines 149 - 156, The two handlers handleDiscardClick and
handleConfirmDiscard should be wrapped with React.useCallback to stabilize their
identities and avoid stale closures: memoize handleDiscardClick (which calls
setShowDiscardDialog(true)) and memoize handleConfirmDiscard (which calls
setShowDiscardDialog(false) and invokes onDiscard?.()) using useCallback with
appropriate dependency arrays (e.g., handleDiscardClick depends on
setShowDiscardDialog, handleConfirmDiscard depends on setShowDiscardDialog and
onDiscard) so they match the memoization style used for
handleClick/handleDoubleClick.
ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 795d18f and da7e7fc.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx`:
- Around line 285-288: The discard confirmation AlertDialog is only rendered
inside the worktreePath branch so when worktreePath is falsy the discard button
(part of fileContent) can toggle setShowDiscardDialog(true) but the dialog never
mounts; move/extract the AlertDialog out of the worktreePath conditional so it
is always rendered alongside fileContent, keep the existing state
(setShowDiscardDialog, showDiscardDialog) and confirmation handler (calling
onDiscard) intact, and if the dialog logic needs worktreePath use a guarded
check inside the dialog confirm handler to call onDiscard with worktreePath (or
call onDiscard without it when absent) so confirming always invokes onDiscard
regardless of the outer branch.

---

Nitpick comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsx`:
- Around line 149-156: The two handlers handleDiscardClick and
handleConfirmDiscard should be wrapped with React.useCallback to stabilize their
identities and avoid stale closures: memoize handleDiscardClick (which calls
setShowDiscardDialog(true)) and memoize handleConfirmDiscard (which calls
setShowDiscardDialog(false) and invokes onDiscard?.()) using useCallback with
appropriate dependency arrays (e.g., handleDiscardClick depends on
setShowDiscardDialog, handleConfirmDiscard depends on setShowDiscardDialog and
onDiscard) so they match the memoization style used for
handleClick/handleDoubleClick.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant