Compare commits

...
2 Commits
Author SHA1 Message Date
Christoph Brandau 0877d5a568 feat(status-panel): add loading overlay for ongoing operations
This update introduces a loading overlay in the StatusPanel component
to indicate ongoing operations. The overlay displays a spinner and
the current operation status, enhancing user experience during
long-running tasks.

- Added a loading overlay with a spinner for busy states
- Included operation status text to inform users of current actions
- Improved visual feedback for ongoing processes in the UI
2026-07-09 15:20:52 +02:00
Christoph Brandau f79f3dc1ec feat(recent-repos): add functionality to remove repositories from recent
This update introduces a new function to remove repositories from the
recent list. The user can now manage their recent repositories more
effectively, enhancing the overall user experience.

- Implemented removal of repositories from the recent list
- Updated UI to reflect changes in the recent repositories management
2026-07-09 15:14:29 +02:00
2 changed files with 66 additions and 3 deletions
+17 -1
View File
@@ -1939,6 +1939,21 @@
if (!activeStillOpen) await openRepo(path);
}
async function removeRepoFromRecent(path: string, event?: MouseEvent) {
event?.stopPropagation();
if (isBusy) return;
recentRepoPaths = recentRepoPaths.filter((recentPath) => !sameRepoPath(recentPath, path));
persistRepoLists();
trackEvent("repository_removed_from_recent", {
recent_repositories: recentRepoPaths.length,
});
if (!repoTabs.some((tab) => sameRepoPath(tab.path, path)) && !isFavoriteRepo(path) && repoStatusCache[repoKey(path)]) {
const { [repoKey(path)]: _removed, ...rest } = repoStatusCache;
repoStatusCache = rest;
persistRepoStatusCache();
}
}
async function removeRepoFromManagement(path: string, event?: MouseEvent) {
event?.stopPropagation();
if (isBusy) return;
@@ -3516,7 +3531,7 @@
>
<Star size={14} aria-hidden="true" />
</button>
<button class="repo-row-icon" type="button" onclick={(event) => removeRepoFromManagement(repo.path, event)} disabled={isBusy} title="Remove from recent" aria-label={`Remove ${repo.name}`}>
<button class="repo-row-icon" type="button" onclick={(event) => removeRepoFromRecent(repo.path, event)} disabled={isBusy} title="Remove from recent" aria-label={`Remove ${repo.name}`}>
<X size={14} aria-hidden="true" />
</button>
</div>
@@ -3719,6 +3734,7 @@
{unstagedCount}
{hasRepository}
{isBusy}
{operation}
{status}
selectedFilePath={selectedExplorerPath}
onSelectFile={selectFileFromStatus}
+49 -2
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { Check, FileDiff, RotateCcw, Undo2 } from "@lucide/svelte";
import { Check, FileDiff, LoaderCircle, RotateCcw, Undo2 } from "@lucide/svelte";
import type { FileStatusKind, GitFileStatus, GitStatus } from "../types";
interface Props {
@@ -8,6 +8,7 @@
unstagedCount: number;
hasRepository: boolean;
isBusy: boolean;
operation: string;
status: GitStatus | null;
selectedFilePath: string;
onSelectFile: (file: GitFileStatus) => void;
@@ -25,6 +26,7 @@
unstagedCount = 0,
hasRepository = false,
isBusy = false,
operation = "",
status = null,
selectedFilePath = "",
onSelectFile = () => {},
@@ -136,7 +138,7 @@
});
</script>
<section class="panel grid grid-rows-[auto_auto_1fr] overflow-hidden" aria-label="Working tree status">
<section class="panel relative grid grid-rows-[auto_auto_1fr] overflow-hidden" aria-label="Working tree status">
<div class="section-head">
<div>
<span class="eyebrow">Working tree</span>
@@ -278,4 +280,49 @@
{/each}
</div>
{/if}
{#if isBusy && hasRepository}
<div class="status-panel-overlay" role="status" aria-live="polite">
<div class="status-panel-overlay-card">
<LoaderCircle class="spin" size={22} aria-hidden="true" />
<span>{operation || "Working"}</span>
</div>
</div>
{/if}
</section>
<style>
.status-panel-overlay {
position: absolute;
inset: 0;
z-index: 20;
display: grid;
place-items: center;
background: rgba(8, 9, 16, 0.5);
backdrop-filter: blur(3px);
animation: status-panel-overlay-in 120ms ease;
}
.status-panel-overlay-card {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 20px;
border: 1px solid var(--color-border);
border-radius: 10px;
background: var(--color-surface-raised);
color: var(--color-ink);
font-size: 13px;
font-weight: 600;
box-shadow: 0 18px 44px rgba(0, 0, 0, 0.38);
}
@keyframes status-panel-overlay-in {
from { opacity: 0; }
to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.status-panel-overlay { animation: none; }
}
</style>