add new style and global search

This commit is contained in:
Christoph Brandau
2026-06-29 17:24:08 +02:00
parent 9861fa2446
commit b0491d1479
17 changed files with 1805 additions and 70 deletions
+102 -4
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import { onDestroy, onMount } from "svelte";
import { AlertCircle, Check, GitBranch, GitMerge, LoaderCircle } from "@lucide/svelte";
import { open as openDialog } from "@tauri-apps/plugin-dialog";
import { AlertCircle, Check, FolderOpen, GitBranch, GitMerge, LoaderCircle } from "@lucide/svelte";
import TitleBar from "./lib/TitleBar.svelte";
import BranchPanel from "./lib/components/BranchPanel.svelte";
@@ -10,6 +11,7 @@
import CredentialDialog from "./lib/components/CredentialDialog.svelte";
import ExplorerPanel from "./lib/components/ExplorerPanel.svelte";
import FileHistoryPanel from "./lib/components/FileHistoryPanel.svelte";
import GlobalSearchDialog from "./lib/components/GlobalSearchDialog.svelte";
import HistoryPanel from "./lib/components/HistoryPanel.svelte";
import ResolveDialog from "./lib/components/ResolveDialog.svelte";
import StatusPanel from "./lib/components/StatusPanel.svelte";
@@ -18,6 +20,7 @@
checkoutBranch,
commit,
compareCommits,
cancelCodeSearch,
diffFileAgainstWorkingTree,
getStatus,
listBranches,
@@ -34,6 +37,7 @@
restoreFileFromCommit,
restoreFiles,
restoreToCommit,
searchCodeIntroductions,
stageFiles,
unstageFiles,
} from "./lib/git";
@@ -49,6 +53,7 @@
GitDiffFile,
GitFileStatus,
GitRepositoryFile,
GitSearchHit,
GitStatus,
PreparedResolution,
} from "./lib/types";
@@ -74,6 +79,11 @@
let comparison: GitCommitComparison | null = null;
let compareDialogOpen = false;
let selectedDiffPath = "";
let globalSearchOpen = false;
let globalSearchResults: GitSearchHit[] = [];
let globalSearchBusy = false;
let globalSearchError = "";
let globalSearchId = "";
let resolveDialogOpen = false;
let conflictTarget = "";
let conflict: ConflictFile | null = null;
@@ -122,7 +132,7 @@
}
async function autoRefreshTick() {
if (!autoRefreshEnabled || !activeRepoPath || isBusy || autoRefreshInFlight || resolveDialogOpen || compareDialogOpen) return;
if (!autoRefreshEnabled || !activeRepoPath || isBusy || autoRefreshInFlight || resolveDialogOpen || compareDialogOpen || globalSearchOpen) return;
autoRefreshInFlight = true;
try {
const nextStatus = await getStatus(activeRepoPath);
@@ -220,9 +230,10 @@
// ── Repository operations ──────────────────────────────────────────────────
async function openRepo() {
const path = repoPath.trim();
async function openRepo(pathOverride?: string) {
const path = (pathOverride ?? repoPath).trim();
if (!path) { errorMessage = "Enter a repository path."; return; }
repoPath = path;
await runOperation("Opening repository", async () => {
const nextStatus = await openRepository(path);
@@ -232,6 +243,8 @@
expandedExplorerPaths = new Set(); expandedCommitHashes = new Set();
fileHistory = []; compareFrom = ""; compareTo = "";
comparison = null; compareDialogOpen = false; selectedDiffPath = "";
if (globalSearchBusy) void cancelGlobalSearch();
globalSearchResults = []; globalSearchOpen = false; globalSearchError = "";
resolveDialogOpen = false; conflictTarget = ""; conflict = null;
preparedResolutions = {};
await refreshBranchList(activeRepoPath);
@@ -240,6 +253,23 @@
});
}
async function chooseRepositoryFolder() {
if (isBusy) return;
try {
const selected = await openDialog({
title: "Repository folder auswaehlen",
directory: true,
multiple: false,
defaultPath: repoPath.trim() || activeRepoPath || undefined,
});
if (typeof selected !== "string") return;
repoPath = selected;
await openRepo(selected);
} catch (error) {
errorMessage = errorToMessage(error);
}
}
async function refreshRepo() {
if (!activeRepoPath) { await openRepo(); return; }
await runOperation("Refreshing", async () => {
@@ -474,6 +504,48 @@
selectedDiffPath = file.path;
}
async function runGlobalSearch(query: string, caseSensitive: boolean, limit: number) {
if (!activeRepoPath || globalSearchBusy) return;
const searchId = `search-${Date.now()}-${Math.random().toString(36).slice(2)}`;
globalSearchId = searchId;
globalSearchBusy = true;
globalSearchError = "";
globalSearchResults = [];
try {
const results = await searchCodeIntroductions(activeRepoPath, query, caseSensitive, limit, searchId);
if (globalSearchId === searchId) {
globalSearchResults = results;
}
} catch (error) {
if (globalSearchId === searchId) {
const message = errorToMessage(error);
globalSearchError = message.includes("abgebrochen") ? "Suche wurde abgebrochen." : message;
}
} finally {
if (globalSearchId === searchId) {
globalSearchBusy = false;
globalSearchId = "";
}
}
}
async function cancelGlobalSearch() {
if (!globalSearchId) return;
const searchId = globalSearchId;
globalSearchError = "Abbruch wird angefordert...";
try {
await cancelCodeSearch(searchId);
} catch (error) {
globalSearchError = errorToMessage(error);
}
}
function closeGlobalSearchDialog() {
if (globalSearchBusy) void cancelGlobalSearch();
globalSearchOpen = false;
}
// ── Conflict resolution ────────────────────────────────────────────────────
async function loadConflict(file: string) {
@@ -543,6 +615,7 @@
function handleWindowKeydown(event: KeyboardEvent) {
if (event.key === "Escape" && compareDialogOpen) compareDialogOpen = false;
else if (event.key === "Escape" && globalSearchOpen) closeGlobalSearchDialog();
}
</script>
@@ -566,6 +639,7 @@
onPull={pullRepo}
onPush={pushRepo}
onRefresh={refreshRepo}
onSearch={() => { globalSearchOpen = true; }}
onToggleAutoRefresh={toggleAutoRefresh}
/>
@@ -583,6 +657,17 @@
placeholder="/path/to/repository"
disabled={isBusy}
/>
<button
class="btn-secondary repo-browse"
type="button"
onclick={chooseRepositoryFolder}
disabled={isBusy}
title="Repository-Ordner auswaehlen"
aria-label="Repository-Ordner auswaehlen"
>
<FolderOpen size={16} aria-hidden="true" />
Browse
</button>
<button class="btn-primary" type="submit" disabled={isBusy || repoPath.trim().length === 0}>
{#if operation === "Opening repository"}
<LoaderCircle class="spin" size={16} aria-hidden="true" />
@@ -743,6 +828,19 @@
/>
{/if}
{#if globalSearchOpen}
<GlobalSearchDialog
{hasRepository}
{isBusy}
isSearching={globalSearchBusy}
error={globalSearchError}
results={globalSearchResults}
onClose={closeGlobalSearchDialog}
onSearch={runGlobalSearch}
onCancel={cancelGlobalSearch}
/>
{/if}
<!-- Credential dialog for push/pull -->
{#if credDialogOpen && credDialogAction}
<CredentialDialog