diff --git a/QuickShell.Raycast/src/components/discover-git-repos-view.tsx b/QuickShell.Raycast/src/components/discover-git-repos-view.tsx
index df48edec..6070c231 100644
--- a/QuickShell.Raycast/src/components/discover-git-repos-view.tsx
+++ b/QuickShell.Raycast/src/components/discover-git-repos-view.tsx
@@ -101,6 +101,7 @@ export default function DiscoverGitReposView({ onWorkspaceAdded, popOnAdd = true
}
let cancelled = false;
+ const controller = new AbortController();
const timer = setTimeout(() => {
setTargetedLoadingQuery(query);
void (async () => {
@@ -108,15 +109,15 @@ export default function DiscoverGitReposView({ onWorkspaceAdded, popOnAdd = true
const existing = await storage.getWorkspaces();
const existingDirs = new Set(existing.map((workspace) => workspace.directory.toLowerCase()));
const extraRoots = searchRootsFromWorkspaces(existing.map((workspace) => workspace.directory));
- const repos = (await discoverGitReposForQueryAsync(query, extraRoots)).filter(
+ const repos = (await discoverGitReposForQueryAsync(query, extraRoots, { signal: controller.signal })).filter(
(repo) => !existingDirs.has(repo.directory.toLowerCase()),
);
if (!cancelled) {
setTargetedSearch({ query, repos });
}
- } catch {
- if (!cancelled) {
- setTargetedSearch({ query, repos: [] });
+ } catch (error) {
+ if (!cancelled && !controller.signal.aborted) {
+ throw error;
}
} finally {
if (!cancelled) {
@@ -128,6 +129,7 @@ export default function DiscoverGitReposView({ onWorkspaceAdded, popOnAdd = true
return () => {
cancelled = true;
+ controller.abort();
clearTimeout(timer);
};
}, [data, searchText]);
diff --git a/QuickShell.Raycast/src/components/set-target-branch-form.tsx b/QuickShell.Raycast/src/components/set-target-branch-form.tsx
index c1456332..c2ef5829 100644
--- a/QuickShell.Raycast/src/components/set-target-branch-form.tsx
+++ b/QuickShell.Raycast/src/components/set-target-branch-form.tsx
@@ -21,7 +21,7 @@ export default function SetTargetBranchForm({ directory, workspaceName, blockDir
const { pop } = useNavigation();
const storage = getQuickShellStorage();
- const { data: branchChoices, isLoading } = usePromise(async () => {
+ const { data: branchChoices, isLoading, error, revalidate } = usePromise(async () => {
const branches = await listLocalBranches(directory);
const worktreeKey = await resolveWorktreeKey(directory);
const target = worktreeKey ? await storage.getBranchTarget(worktreeKey) : null;
@@ -104,15 +104,17 @@ export default function SetTargetBranchForm({ directory, workspaceName, blockDir
isLoading={isLoading}
actions={
+ {error ? : null}
}
>
+ {error ? : null}
{branchChoices && branchChoices.branches.length === 0 ? (
) : (
diff --git a/QuickShell.Raycast/src/lib/discovered-workspace-seed.ts b/QuickShell.Raycast/src/lib/discovered-workspace-seed.ts
index 4dcc2e02..1a835443 100644
--- a/QuickShell.Raycast/src/lib/discovered-workspace-seed.ts
+++ b/QuickShell.Raycast/src/lib/discovered-workspace-seed.ts
@@ -1,4 +1,5 @@
import { deriveAbbreviationFromName, deriveNameFromDirectory } from "./directory-helpers";
+import { tryGetGitRemoteUrl } from "./git-repo-discovery";
import { createStableId } from "./ids";
import type { WorkspaceSetupTask } from "./project-setup-suggestion";
import type { CompanionAppEntry, Workspace } from "./schema";
@@ -21,6 +22,7 @@ type DiscoveredWorkspaceSeed = {
export function createWorkspaceFromDiscoveredGitRepo(seed: DiscoveredWorkspaceSeed): Workspace {
const directory = seed.directory.trim();
const name = seed.name.trim() || deriveNameFromDirectory(directory);
+ const repoUrl = seed.remoteUrl ?? tryGetGitRemoteUrl(directory) ?? null;
const launches = launchRowsFromSuggestions(seed.tasks).map((row, index) => ({
id: row.id,
label: row.label,
@@ -45,7 +47,7 @@ export function createWorkspaceFromDiscoveredGitRepo(seed: DiscoveredWorkspaceSe
wtProfile: null,
command: null,
runAsAdmin: false,
- repoUrl: seed.remoteUrl ?? null,
+ repoUrl,
devServerUrl: seed.devServerUrl ?? null,
// normalizeWorkspace supplies a usable blank launch when discovery finds no tasks.
launches,
diff --git a/QuickShell.Raycast/src/lib/git-repo-discovery.ts b/QuickShell.Raycast/src/lib/git-repo-discovery.ts
index ad05cf13..04f61186 100644
--- a/QuickShell.Raycast/src/lib/git-repo-discovery.ts
+++ b/QuickShell.Raycast/src/lib/git-repo-discovery.ts
@@ -50,6 +50,7 @@ type DiscoveryOptions = {
maxScanned?: number;
query?: string;
rootDirectories?: string[];
+ signal?: AbortSignal;
};
export function discoverGitRepos(extraRoots: string[] = []): GitRepoCandidate[] {
@@ -88,9 +89,10 @@ export async function discoverGitReposAsync(
const maxRepos = options?.maxRepos ?? MAX_REPOS;
const maxScanned = options?.maxScanned ?? MAX_SCANNED;
const query = normalizeDiscoveryQuery(options?.query ?? "");
+ const signal = options?.signal;
async function worker(): Promise {
- while (results.length < maxRepos && scanned < maxScanned) {
+ while (!signal?.aborted && results.length < maxRepos && scanned < maxScanned) {
const work = queue.shift();
if (!work) {
return;
@@ -99,6 +101,7 @@ export async function discoverGitReposAsync(
continue;
}
+ if (signal?.aborted) return;
let isDirectory = false;
try {
isDirectory = (await fs.stat(work.directory)).isDirectory();
@@ -120,6 +123,7 @@ export async function discoverGitReposAsync(
return;
}
+ if (signal?.aborted) return;
let entries: Array<{ name: string; isDirectory: () => boolean }> = [];
try {
entries = await fs.readdir(work.directory, { withFileTypes: true });
@@ -128,6 +132,7 @@ export async function discoverGitReposAsync(
}
for (const entry of entries) {
+ if (signal?.aborted) return;
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name.toLowerCase())) {
continue;
}
@@ -154,7 +159,7 @@ export async function discoverGitReposAsync(
export async function discoverGitReposForQueryAsync(
searchText: string,
extraRoots: string[] = [],
- options?: { rootDirectories?: string[]; concurrency?: number; maxScanned?: number },
+ options?: { rootDirectories?: string[]; concurrency?: number; maxScanned?: number; signal?: AbortSignal },
): Promise {
const query = normalizeDiscoveryQuery(searchText);
if (!query) {
@@ -172,6 +177,7 @@ export async function discoverGitReposForQueryAsync(
maxScanned: options?.maxScanned ?? MAX_TARGETED_SCANNED,
query,
rootDirectories: options?.rootDirectories,
+ signal: options?.signal,
});
}
@@ -280,8 +286,12 @@ function repoCandidateFromPathQuery(value: string): GitRepoCandidate | null {
if (!statSync(candidate).isDirectory()) {
candidate = path.dirname(candidate);
}
- } catch {
- return null;
+ } catch (error) {
+ const code = (error as NodeJS.ErrnoException).code;
+ if (code === "ENOENT" || code === "ENOTDIR") {
+ return null;
+ }
+ throw error;
}
while (true) {