feat(startup): wire startup repo path from CLI to UI

The app now supports opening a repository at startup by
reading a --repo argument and passing the path to the UI.
A new startup repository state is managed in the backend and
exposed via a tauri command and event, enabling the frontend
to auto-open the repository when ready. The UI adds a small
startup flow with retry handling to ensure the path is
consumed once available.
- Reads --repo or --repo=PATH and resolves relative paths
- Signals UI to open startup repo via open-startup-repository
- Frontend retries opening the repo until it succeeds
This commit is contained in:
2026-08-16 15:54:54 +02:00
parent 25d7f27d7a
commit 993179c25e
2 changed files with 126 additions and 3 deletions
+40
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import { onDestroy, onMount, tick } from "svelte";
import { getVersion } from "@tauri-apps/api/app";
import { listen } from "@tauri-apps/api/event";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { open as openDialog } from "@tauri-apps/plugin-dialog";
import { check, type DownloadEvent, type Update } from "@tauri-apps/plugin-updater";
@@ -275,6 +276,10 @@
// ── State ──────────────────────────────────────────────────────────────────
let repoPath = "";
let startupReady = false;
let pendingStartupRepoPath = "";
let startupRepositoryRetryTimer: ReturnType<typeof setTimeout> | undefined;
let unlistenStartupRepository: (() => void) | undefined;
let activeRepoPath = "";
let activeView: AppView = "management";
let repoTabs: RepoTab[] = [];
@@ -543,6 +548,14 @@
themeMediaQuery = window.matchMedia("(prefers-color-scheme: light)");
themeMediaQuery.addEventListener("change", handleSystemThemeChange);
void runStartupSequence();
void listen("open-startup-repository", () => {
void receiveStartupRepository();
}).then((unlisten) => {
if (appShuttingDown) unlisten();
else unlistenStartupRepository = unlisten;
}).catch(() => {
// Browser preview has no Tauri event system.
});
void refreshDetectedExternalTools();
void getVersion().then((version) => { appVersion = version; }).catch(() => { appVersion = ""; });
window.addEventListener("beforeunload", handleAppShutdown);
@@ -565,7 +578,9 @@
window.removeEventListener("beforeunload", handleAppShutdown);
window.removeEventListener("pagehide", handleAppShutdown);
unlistenCloseRequested?.();
unlistenStartupRepository?.();
if (cloneDialogErrorTimer) clearTimeout(cloneDialogErrorTimer);
if (startupRepositoryRetryTimer) clearTimeout(startupRepositoryRetryTimer);
Object.values(errorAutoHideStates).forEach((state) => {
if (state?.timer) clearTimeout(state.timer);
});
@@ -642,6 +657,8 @@
if (remainingSplashTime > 0) await wait(remainingSplashTime);
await closeStartupSplashscreen();
startupReady = true;
await receiveStartupRepository();
startBackgroundTimers();
// Remote access can take seconds (offline networks, SSH negotiation,
// credential helpers). It must never hold the startup screen hostage.
@@ -650,6 +667,29 @@
}
}
async function receiveStartupRepository() {
try {
const path = await tracedInvoke<string | null>("take_startup_repository");
if (path) pendingStartupRepoPath = path;
} catch {
return;
}
if (!startupReady || !pendingStartupRepoPath) return;
if (isBusy) {
if (!startupRepositoryRetryTimer) {
startupRepositoryRetryTimer = setTimeout(() => {
startupRepositoryRetryTimer = undefined;
void receiveStartupRepository();
}, 100);
}
return;
}
const path = pendingStartupRepoPath;
pendingStartupRepoPath = "";
await openRepo(path);
}
async function fetchOpenRepositoriesDuringStartup() {
const paths = uniqueRepoPaths(repoTabs.map((tab) => tab.path));
if (paths.length === 0 || backgroundFetchInFlight) return;