feat(ui): enhance branch delete confirmation flow

This update introduces a comprehensive and visually refined dialog component for confirming the deletion of branches. The logic now correctly distinguishes between deleting local and remote branches, providing tailored warnings and context to the user. Corresponding global styles were updated to implement the new layout and visual fidelity for this specific dialog type.

- Implements structured display for branch name and location
- Improves distinction between local and remote delete contexts
- Updates backdrop filter CSS properties for better compatibility
This commit is contained in:
Christoph Brandau
2026-07-23 00:33:17 +02:00
parent 6aed0b933e
commit 27c8b40dc6
2 changed files with 121 additions and 20 deletions
+93 -3
View File
@@ -37,7 +37,7 @@
--app-select-option-bg: #171d2b; --app-select-option-bg: #171d2b;
--app-panel-highlight: linear-gradient(180deg, rgba(255,255,255,0.038), transparent 70%); --app-panel-highlight: linear-gradient(180deg, rgba(255,255,255,0.038), transparent 70%);
--app-panel-shadow: 0 18px 48px rgba(0,0,0,0.28), inset 0 1px 0 rgba(255,255,255,0.045); --app-panel-shadow: 0 18px 48px rgba(0,0,0,0.28), inset 0 1px 0 rgba(255,255,255,0.045);
--app-dialog-backdrop: #070a10; --app-dialog-backdrop: rgba(4, 7, 12, 0.58);
--app-dialog-bg: #101622; --app-dialog-bg: #101622;
--app-dialog-chrome: #141b29; --app-dialog-chrome: #141b29;
--app-dialog-shadow: 0 24px 68px rgba(0, 0, 0, 0.54), 0 2px 12px rgba(0,0,0,0.32); --app-dialog-shadow: 0 24px 68px rgba(0, 0, 0, 0.54), 0 2px 12px rgba(0,0,0,0.32);
@@ -103,7 +103,7 @@
--app-select-option-bg: #ffffff; --app-select-option-bg: #ffffff;
--app-panel-highlight: linear-gradient(180deg, rgba(255,255,255,0.72), transparent 70%); --app-panel-highlight: linear-gradient(180deg, rgba(255,255,255,0.72), transparent 70%);
--app-panel-shadow: 0 18px 48px rgba(28, 44, 74, 0.12), inset 0 1px 0 rgba(255,255,255,0.86); --app-panel-shadow: 0 18px 48px rgba(28, 44, 74, 0.12), inset 0 1px 0 rgba(255,255,255,0.86);
--app-dialog-backdrop: rgba(232, 238, 248, 0.96); --app-dialog-backdrop: rgba(222, 229, 241, 0.56);
--app-dialog-bg: #ffffff; --app-dialog-bg: #ffffff;
--app-dialog-chrome: #f4f7fc; --app-dialog-chrome: #f4f7fc;
--app-dialog-shadow: 0 24px 72px rgba(28, 44, 74, 0.2), 0 2px 12px rgba(28,44,74,0.1); --app-dialog-shadow: 0 24px 72px rgba(28, 44, 74, 0.2), 0 2px 12px rgba(28,44,74,0.1);
@@ -3057,6 +3057,8 @@
padding: 24px; padding: 24px;
background-color: var(--app-dialog-backdrop); background-color: var(--app-dialog-backdrop);
background-image: none; background-image: none;
-webkit-backdrop-filter: blur(10px) saturate(0.78);
backdrop-filter: blur(10px) saturate(0.78);
} }
.app-chrome-backdrop { .app-chrome-backdrop {
@@ -3249,6 +3251,16 @@
max-height: calc(100vh - 32px); max-height: calc(100vh - 32px);
overflow: auto; overflow: auto;
} }
.branch-delete-dialog {
display: grid;
grid-template-rows: auto auto auto;
width: min(500px, calc(100vw - 32px));
height: auto;
max-height: calc(100vh - 32px);
border-color: rgba(255, 90, 103, 0.22);
box-shadow: var(--app-dialog-shadow), 0 0 0 1px rgba(255, 90, 103, 0.04);
overflow: auto;
}
.ai-settings-dialog { .ai-settings-dialog {
display: block; display: block;
width: min(560px, calc(100vw - 32px)); width: min(560px, calc(100vw - 32px));
@@ -3753,7 +3765,30 @@
display: grid; display: grid;
grid-template-columns: auto minmax(0, 1fr); grid-template-columns: auto minmax(0, 1fr);
gap: 14px; gap: 14px;
padding: 18px 16px 16px; padding: 20px 18px 18px;
}
.branch-delete-header {
padding: 15px 16px;
background:
linear-gradient(90deg, rgba(255, 90, 103, 0.08), transparent 42%),
var(--app-dialog-chrome);
}
.branch-delete-heading { display: flex; align-items: center; gap: 11px; }
.branch-delete-heading-icon {
display: grid;
place-items: center;
width: 32px;
height: 32px;
flex: 0 0 auto;
border: 1px solid rgba(255, 90, 103, 0.28);
border-radius: 8px;
color: #ff9aa4;
background: rgba(255, 90, 103, 0.09);
}
.branch-delete-heading-icon.force {
border-color: rgba(255, 174, 92, 0.34);
color: #ffc07a;
background: rgba(255, 151, 61, 0.1);
} }
.discard-warning-icon { .discard-warning-icon {
display: grid; display: grid;
@@ -3794,6 +3829,61 @@
align-items: center; align-items: center;
gap: 6px; gap: 6px;
} }
.branch-delete-lead { color: var(--color-ink); font-weight: 600; }
.branch-delete-target {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
padding: 10px 11px;
border: 1px solid var(--color-border-subtle);
border-radius: 8px;
background: rgba(0, 0, 0, 0.16);
box-shadow: inset 3px 0 0 rgba(255, 90, 103, 0.48);
}
.branch-delete-target-icon {
display: grid;
place-items: center;
width: 28px;
height: 28px;
flex: 0 0 auto;
border-radius: 7px;
color: var(--color-accent);
background: rgba(77, 182, 214, 0.1);
}
.branch-delete-target-copy { display: grid; gap: 2px; min-width: 0; flex: 1; }
.branch-delete-target-copy code {
overflow: hidden;
color: var(--color-ink);
font-family: var(--font-mono);
font-size: 12px;
font-weight: 700;
text-overflow: ellipsis;
white-space: nowrap;
}
.branch-delete-target-copy > span { color: var(--color-ink-faint); font-size: 10.5px; }
.branch-delete-scope {
flex: 0 0 auto;
padding: 3px 7px;
border: 1px solid rgba(77, 182, 214, 0.22);
border-radius: 999px;
color: #8ed8ee;
background: rgba(77, 182, 214, 0.08);
font-size: 9.5px;
font-weight: 800;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.branch-delete-scope.remote { border-color: rgba(167, 126, 255, 0.25); color: #c2a9ff; background: rgba(132, 88, 232, 0.1); }
.branch-delete-body .discard-warning-text {
padding: 9px 10px;
border-left: 2px solid rgba(255, 90, 103, 0.55);
color: #f2aeb5;
background: rgba(255, 90, 103, 0.055);
font-size: 11.5px;
font-weight: 600;
}
.branch-delete-confirm { min-width: 116px; }
.discard-target-list { .discard-target-list {
display: grid; display: grid;
gap: 4px; gap: 4px;
@@ -20,15 +20,22 @@
let title = $derived(branch.remote ? "Delete remote branch?" : force ? "Force delete branch?" : "Delete branch?"); let title = $derived(branch.remote ? "Delete remote branch?" : force ? "Force delete branch?" : "Delete branch?");
let remoteParts = $derived(branch.remote ? branch.name.split(/\/(.+)/) : []); let remoteParts = $derived(branch.remote ? branch.name.split(/\/(.+)/) : []);
let branchName = $derived(branch.remote ? remoteParts[1] || branch.name : branch.name);
let branchLocation = $derived(branch.remote ? remoteParts[0] || "Remote" : "Local repository");
</script> </script>
<div class="dialog-backdrop" role="presentation"> <div class="dialog-backdrop" role="presentation">
<div class="dialog branch-delete-dialog" role="dialog" aria-modal="true" aria-label={title}> <div class="dialog branch-delete-dialog" role="dialog" aria-modal="true" aria-labelledby="branch-delete-title">
<header class="dialog-header"> <header class="dialog-header branch-delete-header">
<div> <div class="branch-delete-heading">
<span class:force class="branch-delete-heading-icon" aria-hidden="true">
<Trash2 size={16} />
</span>
<div>
<span class="eyebrow">{branch.remote ? "Remote branch" : force ? "Force delete" : "Delete branch"}</span> <span class="eyebrow">{branch.remote ? "Remote branch" : force ? "Force delete" : "Delete branch"}</span>
<p class="dialog-title">{title}</p> <p class="dialog-title" id="branch-delete-title">{title}</p>
</div>
</div> </div>
<button class="btn-sm dialog-close" type="button" onclick={onClose} disabled={isBusy} aria-label="Close"> <button class="btn-sm dialog-close" type="button" onclick={onClose} disabled={isBusy} aria-label="Close">
<X size={16} aria-hidden="true" /> <X size={16} aria-hidden="true" />
@@ -36,31 +43,35 @@
</header> </header>
<div class="branch-delete-body"> <div class="branch-delete-body">
<div class="discard-warning-icon" aria-hidden="true"> <div class:force class="discard-warning-icon branch-delete-warning-icon" aria-hidden="true">
<AlertTriangle size={22} /> <AlertTriangle size={22} />
</div> </div>
<div class="discard-confirm-copy"> <div class="discard-confirm-copy">
<p> <p class="branch-delete-lead">
{#if branch.remote} {#if branch.remote}
Delete this branch from the remote server? This branch will be removed from the shared remote repository.
{:else if force} {:else if force}
This branch is not fully merged. Force deleting removes the branch pointer even if some commits are only reachable from this branch. This branch is not fully merged. Some commits may only exist here.
{:else} {:else}
Delete this local branch from the repository? This branch will be removed from your local repository.
{/if} {/if}
</p> </p>
<code class="discard-target" title={branch.name}> <div class="branch-delete-target" title={branch.name}>
<GitBranch size={13} aria-hidden="true" /> <span class="branch-delete-target-icon" aria-hidden="true"><GitBranch size={16} /></span>
{branch.name} <span class="branch-delete-target-copy">
</code> <code>{branchName}</code>
<span>{branchLocation}</span>
</span>
<span class:remote={branch.remote} class="branch-delete-scope">{branch.remote ? "Remote" : "Local"}</span>
</div>
<p class="discard-warning-text"> <p class="discard-warning-text">
{#if branch.remote} {#if branch.remote}
This affects everyone using <strong>{remoteParts[0] || "the remote"}</strong>. Your local commits and local branches are kept. This affects everyone using <strong>{remoteParts[0] || "the remote"}</strong>. Your local branch is kept.
{:else if force} {:else if force}
Make sure you no longer need the unique commits on this branch. Force deletion can make unmerged commits difficult to recover.
{:else} {:else}
Git will refuse if the branch is not fully merged. Git will stop the deletion if the branch contains unmerged commits.
{/if} {/if}
</p> </p>
</div> </div>
@@ -68,7 +79,7 @@
<footer class="discard-confirm-actions"> <footer class="discard-confirm-actions">
<button class="btn-secondary" type="button" onclick={onClose} disabled={isBusy}>Cancel</button> <button class="btn-secondary" type="button" onclick={onClose} disabled={isBusy}>Cancel</button>
<button class="btn-danger" type="button" onclick={onConfirm} disabled={isBusy}> <button class="btn-danger branch-delete-confirm" type="button" onclick={onConfirm} disabled={isBusy}>
{#if isBusy} {#if isBusy}
<LoaderCircle class="spin" size={15} aria-hidden="true" /> <LoaderCircle class="spin" size={15} aria-hidden="true" />
{:else} {:else}