feat(session): enhance delete dialog for worktrees

This commit is contained in:
Bohdan Triapitsyn
2026-01-17 22:30:30 +02:00
parent 1ca08cf2d8
commit bb9c181b72
@@ -10,7 +10,7 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import { RiCheckboxBlankLine, RiCheckboxLine } from '@remixicon/react'; import { RiCheckboxBlankLine, RiCheckboxLine, RiDeleteBinLine, RiGitBranchLine } from '@remixicon/react';
import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel'; import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel';
import { DirectoryExplorerDialog } from './DirectoryExplorerDialog'; import { DirectoryExplorerDialog } from './DirectoryExplorerDialog';
import { cn, formatPathForDisplay } from '@/lib/utils'; import { cn, formatPathForDisplay } from '@/lib/utils';
@@ -379,22 +379,45 @@ export const SessionDialogs: React.FC = () => {
: ''; : '';
const deleteDialogBody = deleteDialog ? ( const deleteDialogBody = deleteDialog ? (
<div className="space-y-2"> <div className={cn(isWorktreeDelete ? 'space-y-3' : 'space-y-2')}>
{deleteDialog.sessions.length > 0 && ( {deleteDialog.sessions.length > 0 && (
<div className="space-y-1.5 rounded-xl border border-border/40 bg-sidebar/60 p-3"> <div className={cn(
isWorktreeDelete ? 'rounded-lg bg-muted/30 p-3' : 'space-y-1.5 rounded-xl border border-border/40 bg-sidebar/60 p-3'
)}>
{isWorktreeDelete && ( {isWorktreeDelete && (
<span className="typography-meta font-medium text-foreground"> <div className="flex items-center gap-2">
{deleteDialog.sessions.length === 1 ? 'Linked session' : 'Linked sessions'} <span className="typography-meta font-medium text-foreground">
</span> {deleteDialog.sessions.length === 1 ? 'Linked session' : 'Linked sessions'}
</span>
<span className="typography-micro text-muted-foreground/70">
{deleteDialog.sessions.length}
</span>
</div>
)} )}
<ul className="space-y-0.5"> <ul className={cn(isWorktreeDelete ? 'mt-2 space-y-1' : 'space-y-0.5')}>
{deleteDialog.sessions.slice(0, 5).map((session) => ( {deleteDialog.sessions.slice(0, 5).map((session) => (
<li key={session.id} className="typography-micro text-muted-foreground/80"> <li
{session.title || 'Untitled Session'} key={session.id}
className={cn(
isWorktreeDelete
? 'flex items-center gap-2 rounded-md px-2.5 py-1.5 text-sm text-muted-foreground'
: 'typography-micro text-muted-foreground/80'
)}
>
<span className={cn(!isWorktreeDelete && 'hidden')}>
</span>
<span className="truncate">
{session.title || 'Untitled Session'}
</span>
</li> </li>
))} ))}
{deleteDialog.sessions.length > 5 && ( {deleteDialog.sessions.length > 5 && (
<li className="typography-micro text-muted-foreground/70"> <li className={cn(
isWorktreeDelete
? 'px-2.5 py-1 text-xs text-muted-foreground/70'
: 'typography-micro text-muted-foreground/70'
)}>
+{deleteDialog.sessions.length - 5} more +{deleteDialog.sessions.length - 5} more
</li> </li>
)} )}
@@ -403,8 +426,9 @@ export const SessionDialogs: React.FC = () => {
)} )}
{isWorktreeDelete ? ( {isWorktreeDelete ? (
<div className="space-y-2 rounded-xl border border-border/40 bg-sidebar/60 p-3"> <div className="space-y-2 rounded-lg bg-muted/30 p-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<RiGitBranchLine className="h-4 w-4 text-muted-foreground" />
<span className="typography-meta font-medium text-foreground">Worktree</span> <span className="typography-meta font-medium text-foreground">Worktree</span>
{targetWorktree?.label ? ( {targetWorktree?.label ? (
<span className="typography-micro text-muted-foreground/70">{targetWorktree.label}</span> <span className="typography-micro text-muted-foreground/70">{targetWorktree.label}</span>
@@ -417,44 +441,6 @@ export const SessionDialogs: React.FC = () => {
<p className="typography-micro text-warning">Uncommitted changes will be discarded.</p> <p className="typography-micro text-warning">Uncommitted changes will be discarded.</p>
)} )}
{canRemoveRemoteBranches ? (
<button
type="button"
onClick={() => {
if (removeRemoteOptionDisabled) {
return;
}
setDeleteDialogShouldRemoveRemote((prev) => !prev);
}}
disabled={removeRemoteOptionDisabled}
className={cn(
'flex w-full items-start gap-3 rounded-xl border border-border/40 bg-sidebar/70 px-3 py-2 text-left',
removeRemoteOptionDisabled
? 'cursor-not-allowed opacity-60'
: 'hover:bg-sidebar/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary'
)}
>
<span className="mt-0.5 flex size-5 items-center justify-center text-muted-foreground">
{deleteDialogShouldRemoveRemote ? (
<RiCheckboxLine className="size-4 text-primary" />
) : (
<RiCheckboxBlankLine className="size-4" />
)}
</span>
<div className="flex-1 space-y-1">
<span className="typography-meta font-medium text-foreground">Delete remote branch</span>
<p className="typography-micro text-muted-foreground/70">
{deleteDialogShouldRemoveRemote
? 'Remote branch on origin will also be removed.'
: 'Keep the remote branch intact.'}
</p>
</div>
</button>
) : (
<p className="typography-micro text-muted-foreground/70">
Remote branch information unavailable for this worktree.
</p>
)}
</div> </div>
) : ( ) : (
<div className="rounded-xl border border-border/40 bg-sidebar/60 p-3"> <div className="rounded-xl border border-border/40 bg-sidebar/60 p-3">
@@ -466,7 +452,49 @@ export const SessionDialogs: React.FC = () => {
</div> </div>
) : null; ) : null;
const deleteDialogActions = ( const deleteRemoteBranchAction = isWorktreeDelete ? (
canRemoveRemoteBranches ? (
<button
type="button"
onClick={() => {
if (removeRemoteOptionDisabled) {
return;
}
setDeleteDialogShouldRemoveRemote((prev) => !prev);
}}
disabled={removeRemoteOptionDisabled}
className={cn(
'flex items-center gap-2 rounded-md px-2 py-1 text-sm text-muted-foreground transition-colors',
removeRemoteOptionDisabled
? 'cursor-not-allowed opacity-60'
: 'hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary'
)}
>
{deleteDialogShouldRemoveRemote ? (
<RiCheckboxLine className="size-4 text-primary" />
) : (
<RiCheckboxBlankLine className="size-4" />
)}
Delete remote branch
</button>
) : (
<span className="text-xs text-muted-foreground/70">Remote branch info unavailable</span>
)
) : null;
const deleteDialogActions = isWorktreeDelete ? (
<div className="flex w-full items-center justify-between gap-3">
{deleteRemoteBranchAction}
<div className="flex items-center gap-2">
<Button variant="ghost" onClick={closeDeleteDialog} disabled={isProcessingDelete}>
Cancel
</Button>
<Button variant="destructive" onClick={handleConfirmDelete} disabled={isProcessingDelete}>
{isProcessingDelete ? 'Deleting…' : 'Delete worktree'}
</Button>
</div>
</div>
) : (
<> <>
<Button variant="ghost" onClick={closeDeleteDialog} disabled={isProcessingDelete}> <Button variant="ghost" onClick={closeDeleteDialog} disabled={isProcessingDelete}>
Cancel Cancel
@@ -474,11 +502,9 @@ export const SessionDialogs: React.FC = () => {
<Button variant="destructive" onClick={handleConfirmDelete} disabled={isProcessingDelete}> <Button variant="destructive" onClick={handleConfirmDelete} disabled={isProcessingDelete}>
{isProcessingDelete {isProcessingDelete
? 'Deleting…' ? 'Deleting…'
: isWorktreeDelete : deleteDialog?.sessions.length === 1
? 'Delete worktree' ? 'Delete session'
: deleteDialog?.sessions.length === 1 : 'Delete sessions'}
? 'Delete session'
: 'Delete sessions'}
</Button> </Button>
</> </>
); );
@@ -522,12 +548,23 @@ export const SessionDialogs: React.FC = () => {
} }
}} }}
> >
<DialogContent className="max-w-[min(520px,100vw-2rem)] space-y-2 pb-2"> <DialogContent
className={cn(
isWorktreeDelete
? 'max-w-xl max-h-[70vh] flex flex-col overflow-hidden gap-3'
: 'max-w-[min(520px,100vw-2rem)] space-y-2 pb-2'
)}
>
<DialogHeader> <DialogHeader>
<DialogTitle>{deleteDialogTitle}</DialogTitle> <DialogTitle className={cn(isWorktreeDelete && 'flex items-center gap-2')}>
{isWorktreeDelete && <RiDeleteBinLine className="h-5 w-5" />}
{deleteDialogTitle}
</DialogTitle>
{deleteDialogDescription && <DialogDescription>{deleteDialogDescription}</DialogDescription>} {deleteDialogDescription && <DialogDescription>{deleteDialogDescription}</DialogDescription>}
</DialogHeader> </DialogHeader>
{deleteDialogBody} <div className={cn(isWorktreeDelete && 'flex-1 min-h-0 overflow-y-auto space-y-2')}>
{deleteDialogBody}
</div>
<DialogFooter className="mt-2 gap-2 pt-1 pb-1">{deleteDialogActions}</DialogFooter> <DialogFooter className="mt-2 gap-2 pt-1 pb-1">{deleteDialogActions}</DialogFooter>
</DialogContent> </DialogContent>
</Dialog> </Dialog>