add new style and global search
This commit is contained in:
+102
-4
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user