78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { netcattyBridge } from "../infrastructure/services/netcattyBridge";
|
|
|
|
let shellCache: DiscoveredShell[] | null = null;
|
|
let shellPromise: Promise<DiscoveredShell[]> | null = null;
|
|
|
|
export function useDiscoveredShells(): DiscoveredShell[] {
|
|
const [shells, setShells] = useState<DiscoveredShell[]>(shellCache ?? []);
|
|
|
|
useEffect(() => {
|
|
if (shellCache) {
|
|
setShells(shellCache);
|
|
return;
|
|
}
|
|
|
|
const bridge = netcattyBridge.get();
|
|
if (!bridge?.discoverShells) return;
|
|
|
|
if (!shellPromise) {
|
|
shellPromise = bridge.discoverShells();
|
|
}
|
|
|
|
shellPromise.then((result) => {
|
|
shellCache = result;
|
|
setShells(result);
|
|
}).catch((err) => {
|
|
console.warn("Failed to discover shells:", err);
|
|
// Clear the failed promise so the next mount can retry
|
|
shellPromise = null;
|
|
});
|
|
}, []);
|
|
|
|
return shells;
|
|
}
|
|
|
|
/**
|
|
* Resolve a localShell setting value to shell command and args.
|
|
* The value can be a discovered shell id (e.g., "wsl-ubuntu", "pwsh")
|
|
* or a custom path/command (e.g., "/usr/local/bin/fish" or "fish").
|
|
* Returns { command, args } or null when discovery hasn't loaded yet
|
|
* and the value might be a shell ID that can't be resolved yet.
|
|
*/
|
|
export function resolveShellSetting(
|
|
localShell: string,
|
|
discoveredShells: DiscoveredShell[]
|
|
): { command: string; args?: string[] } | null {
|
|
if (!localShell) return null;
|
|
|
|
// Try to match as a discovered shell id
|
|
const shell = discoveredShells.find(s => s.id === localShell);
|
|
if (shell) {
|
|
return { command: shell.command, args: shell.args };
|
|
}
|
|
|
|
// No ID match — treat as a custom shell path/command and pass through.
|
|
// This handles both custom executables (e.g., "/usr/local/bin/fish", "pwsh-preview")
|
|
// and stale/synced IDs that no longer exist on this machine (graceful fallback
|
|
// to whatever the OS resolves the name to, or a spawn error the user can see).
|
|
return { command: localShell };
|
|
}
|
|
|
|
const DISTRO_ICONS = new Set([
|
|
"ubuntu", "debian", "kali", "alpine", "opensuse",
|
|
"fedora", "arch", "oracle", "linux",
|
|
]);
|
|
|
|
export function getShellIconPath(iconId: string): string {
|
|
if (DISTRO_ICONS.has(iconId)) {
|
|
return `/distro/${iconId}.svg`;
|
|
}
|
|
return `/shells/${iconId}.svg`;
|
|
}
|
|
|
|
/** Distro icons are monochrome black and need `dark:invert` in dark mode */
|
|
export function isMonochromeShellIcon(iconId: string): boolean {
|
|
return DISTRO_ICONS.has(iconId);
|
|
}
|