import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { logError } from "@/logger"; import { detectBinary } from "@/utils/detectBinary"; import { Notice } from "obsidian"; import React from "react"; interface Props { binaryName: string; placeholder: string; initialPath: string; /** Optional hint surfaced when auto-detect finds nothing. */ notFoundHint?: string; /** Validate & persist on Apply. Returns null on success, error message on failure. */ onSave: (path: string) => Promise; /** When true, a successful auto-detect immediately invokes `onSave`. */ persistOnAutoDetect?: boolean; /** * Custom detector. Used when the backend has a richer install lookup than * a generic `which`/`where` PATH search — e.g. Claude knows about * `~/.local/bin/claude`, Volta, asdf, NVM. Falls back to * {@link detectBinary} when omitted. */ detect?: () => Promise; } /** * Shared "binary path" setting row used by every Agent Mode backend that * spawns a local executable. Owns the Input + Auto-detect + Apply UX with * busy/error state. Callers parameterize the binary name, placeholder, and * persistence callback; the surrounding `` (title/description) * stays in the backend-specific panel. */ export const BinaryPathSetting: React.FC = ({ binaryName, placeholder, initialPath, notFoundHint, onSave, persistOnAutoDetect = false, detect, }) => { const [pathInput, setPathInput] = React.useState(initialPath); const [error, setError] = React.useState(null); const [busy, setBusy] = React.useState(false); React.useEffect(() => { setPathInput(initialPath); }, [initialPath]); const apply = React.useCallback(async (): Promise => { const trimmed = pathInput.trim(); if (!trimmed) { setError("Path is required."); return; } const err = await onSave(trimmed); if (err) { setError(err); return; } setError(null); }, [pathInput, onSave]); const autoDetect = React.useCallback(async (): Promise => { if (busy) return; setBusy(true); setError(null); try { const found = detect ? await detect() : await detectBinary(binaryName); if (!found) { setError( notFoundHint ?? `${binaryName} not found on PATH. Install it or paste a custom path manually.` ); return; } setPathInput(found); if (persistOnAutoDetect) { const err = await onSave(found); if (err) { setError(err); return; } } new Notice(`Found ${binaryName} at ${found}`); } catch (e) { logError(`[AgentMode] auto-detect ${binaryName} failed`, e); setError(e instanceof Error ? e.message : String(e)); } finally { setBusy(false); } }, [binaryName, busy, notFoundHint, onSave, persistOnAutoDetect, detect]); return (
setPathInput(e.target.value)} className="tw-flex-1" />
{error &&
{error}
}
); };