import { useEffect, useMemo, useRef, useState } from "react"; import { getModelLibraryState, pickRuntimeModelFiles, pickRuntimeModelsDir, queryRuntimeGpu, scanRuntimeModels, searchHuggingFaceModels, } from "../api"; import { chooseModel, createRuntimeConfigPatch, dedupeModels, getLocalRuntimeStartReason, pathName, resolveDraftModelSelection, toModelFile, } from "../runtime-settings-form"; import { createRuntimeDraftFieldState, type ModelLibraryState, type HuggingFaceModelResult, type RuntimeConfig, type RuntimeDraftFieldKey, type RuntimeDraftFieldMeta, type RuntimeDraftFieldState, type RuntimeGpuInfo, type RuntimeLifecycleResult, type RuntimeModelFile, type RuntimeSnapshot, } from "../types"; import "./RuntimeSettingsDrawer.css"; const CTX_PRESETS = [2048, 4096, 8192, 16384, 32768, 65536]; const REMOTE_PRESET_TEMPLATES = [ { id: "openai", label: "OpenAI", provider: "open_ai", baseUrl: "https://api.openai.com/v1" }, { id: "openrouter", label: "OpenRouter", provider: "open_router", baseUrl: "https://openrouter.ai/api/v1" }, { id: "anthropic", label: "Anthropic", provider: "anthropic", baseUrl: "https://api.anthropic.com/v1" }, ] as const; interface Props { open: boolean; snapshot: RuntimeSnapshot; busy: boolean; activeLifecycleCommand?: RuntimeSnapshot["active_command"]; lastLifecycleResult?: RuntimeLifecycleResult; lifecycleBusy?: boolean; applyImpact?: "immediate" | "restart_required" | "save_before_apply"; onClose: () => void; onSave: (patch: Partial) => Promise; onStart: () => Promise; onStop: () => Promise; draftConfig?: RuntimeConfig; savedBaseline?: RuntimeConfig; draftDirty?: boolean; draftFields?: RuntimeDraftFieldState; onUpdateDraft?: (patch: Partial) => void; onUpdateDraftFieldMeta?: (field: RuntimeDraftFieldKey, patch: Partial) => void; onResetDraft?: () => void; } function valuesEqual(left: unknown, right: unknown): boolean { return JSON.stringify(left) === JSON.stringify(right); } function maskSecret(value: string): string { if (!value.trim()) return "Not configured"; return `${"*".repeat(Math.max(4, Math.min(12, value.trim().length)))} (${value.trim().length} chars)`; } function formatReferenceValue(field: keyof RuntimeConfig, value: RuntimeConfig[keyof RuntimeConfig]): string { switch (field) { case "connection_mode": return value === "remote" ? "Remote API" : "Local API"; case "start_policy": return value === "on_app_launch" ? "Start on app launch" : "Start manually"; case "server_url": return String(value || "Not configured"); case "api_key": return maskSecret(String(value || "")); case "model_path": return value ? pathName(String(value)) : "No model selected"; case "models_dir": return String(value || "No folder selected"); case "addr": return `http://${String(value || "127.0.0.1:8080")}`; case "gpu_layers": return `${String(value)} GPU layers`; case "ctx_size": return `${String(value)} context tokens`; case "selected_model_id": return String(value || "No managed model selected"); case "hugging_face_token": return maskSecret(String(value || "")); default: return Array.isArray(value) ? `${value.length} configured` : String(value || "-"); } } export default function RuntimeSettingsDrawer({ open, snapshot, busy, activeLifecycleCommand = snapshot.active_command, lastLifecycleResult = snapshot.last_command_result, lifecycleBusy = false, applyImpact = "immediate", onClose, onSave, onStart, onStop, draftConfig: controlledDraftConfig, savedBaseline: controlledSavedBaseline, draftDirty: controlledDraftDirty, draftFields: controlledDraftFields, onUpdateDraft, onUpdateDraftFieldMeta, onResetDraft, }: Props) { const draftConfig = controlledDraftConfig ?? snapshot.config; const savedBaseline = controlledSavedBaseline ?? snapshot.config; const draftFields = controlledDraftFields ?? createRuntimeDraftFieldState(); const draftDirty = controlledDraftDirty ?? !valuesEqual(draftConfig, savedBaseline); const draftConfigRef = useRef(draftConfig); const requestIdsRef = useRef>({}); const [scannedModels, setScannedModels] = useState([]); const [gpuInfo, setGpuInfo] = useState(null); const [gpuError, setGpuError] = useState(""); const [localError, setLocalError] = useState(""); const [scanning, setScanning] = useState(false); const [hfQuery, setHfQuery] = useState(""); const [hfResults, setHfResults] = useState([]); const [hfLoading, setHfLoading] = useState(false); const [libraryState, setLibraryState] = useState({ entries: [], managed_dir: "" }); const [confirmDiscard, setConfirmDiscard] = useState(false); useEffect(() => { draftConfigRef.current = draftConfig; }, [draftConfig]); const updateDraft = (patch: Partial) => { onUpdateDraft?.(patch); }; const updateDraftFieldMeta = (field: RuntimeDraftFieldKey, patch: Partial) => { onUpdateDraftFieldMeta?.(field, patch); }; const beginRequest = (key: string) => { const nextId = (requestIdsRef.current[key] ?? 0) + 1; requestIdsRef.current[key] = nextId; return nextId; }; const isLatestRequest = (key: string, requestId: number) => requestIdsRef.current[key] === requestId; const extraModels = useMemo( () => draftConfig.extra_model_paths.map(toModelFile), [draftConfig.extra_model_paths], ); const allModels = useMemo(() => { const merged = dedupeModels([...scannedModels, ...extraModels]); if (draftConfig.model_path && !merged.some((model) => model.path === draftConfig.model_path)) { return [toModelFile(draftConfig.model_path), ...merged]; } return merged; }, [draftConfig.model_path, extraModels, scannedModels]); const dirtyFieldCount = useMemo(() => { const trackedFields: Array = [ "connection_mode", "server_url", "api_key", "models_dir", "model_path", "addr", "ctx_size", "gpu_layers", "start_policy", "hugging_face_token", ]; return trackedFields.filter((field) => !valuesEqual(draftConfig[field], savedBaseline[field])).length; }, [draftConfig, savedBaseline]); const hasInvalidDraft = Object.values(draftFields).some((meta) => meta?.invalid); const hasPendingDraft = Object.values(draftFields).some((meta) => meta?.pending); const hasBlockingDraftState = hasInvalidDraft || hasPendingDraft; const hasPendingModeChange = draftConfig.connection_mode !== savedBaseline.connection_mode; const restartRequiredWhileRunning = draftDirty && draftConfig.connection_mode === "local" && snapshot.process_running && applyImpact === "restart_required"; const maxLayers = gpuInfo?.recommended_layers ?? 200; const startReason = getLocalRuntimeStartReason({ mode: draftConfig.connection_mode, binaryPath: snapshot.binary_path, selectedModel: draftConfig.model_path || "", }); const isFieldDirty = (field: keyof RuntimeConfig) => !valuesEqual(draftConfig[field], savedBaseline[field]); const fieldMeta = (field: RuntimeDraftFieldKey) => draftFields[field] ?? { invalid: false, pending: false, message: null }; const showLifecycleResult = lastLifecycleResult.command !== "idle" && !lifecycleBusy; const applyGuidance = !draftDirty || draftConfig.connection_mode !== "local" ? null : hasPendingModeChange || applyImpact === "save_before_apply" ? "Stop the local API or save first before applying this change." : applyImpact === "restart_required" ? "Restart required to apply saved changes." : "Applies immediately after save."; const saveLabel = activeLifecycleCommand === "save" ? "Saving changes..." : "Save changes"; const tertiaryLabel = hasPendingModeChange ? "Save mode before applying" : activeLifecycleCommand === "start" ? "Starting local API..." : activeLifecycleCommand === "stop" ? "Stopping local API..." : restartRequiredWhileRunning ? "Restart required to apply saved changes" : snapshot.process_running ? "Stop local API" : "Save and start local API"; const renderFieldState = (field: RuntimeDraftFieldKey) => { const meta = fieldMeta(field); const dirty = isFieldDirty(field); if (!dirty && !meta.invalid && !meta.pending) { return null; } return (
{dirty && Draft} {meta.invalid && Invalid} {meta.pending && Pending} {dirty && ( <> Live now {formatReferenceValue(field, savedBaseline[field])} )}
{meta.message &&
{meta.message}
}
); }; const applyResolvedModelState = (models: RuntimeModelFile[]) => { const currentDraft = draftConfigRef.current; const resolved = resolveDraftModelSelection(currentDraft.model_path || "", models, currentDraft.extra_model_paths.map(toModelFile)); updateDraft({ model_path: resolved.nextModel || null, selected_model_id: resolved.invalid || resolved.nextModel === currentDraft.model_path ? currentDraft.selected_model_id : null, }); updateDraftFieldMeta("model_path", { invalid: resolved.invalid, pending: false, message: resolved.message, }); }; const loadModelLibrary = async () => { const requestId = beginRequest("library"); try { const state = await getModelLibraryState(); if (!isLatestRequest("library", requestId)) return; setLibraryState(state); } catch (error) { if (!isLatestRequest("library", requestId)) return; setLocalError(String(error)); } }; const loadGpuInfo = async () => { const requestId = beginRequest("gpu"); try { const info = await queryRuntimeGpu(); if (!isLatestRequest("gpu", requestId)) return; setGpuInfo(info); setGpuError(""); if (info && draftConfigRef.current.gpu_layers === 0) { updateDraft({ gpu_layers: info.recommended_layers }); } } catch (error) { if (!isLatestRequest("gpu", requestId)) return; setGpuError(String(error)); } }; const handleRescan = async (dir = draftConfigRef.current.models_dir || "") => { if (!dir.trim()) return; const requestId = beginRequest("model-selection"); setScanning(true); setLocalError(""); updateDraftFieldMeta("models_dir", { pending: true, invalid: false, message: null }); updateDraftFieldMeta("model_path", { pending: true }); try { const models = await scanRuntimeModels(dir.trim()); if (!isLatestRequest("model-selection", requestId)) return; setScannedModels(models); applyResolvedModelState(models); updateDraftFieldMeta("models_dir", { pending: false, invalid: false, message: null }); } catch (error) { if (!isLatestRequest("model-selection", requestId)) return; setLocalError(String(error)); setScannedModels([]); updateDraftFieldMeta("models_dir", { pending: false }); updateDraftFieldMeta("model_path", { pending: false }); } finally { if (isLatestRequest("model-selection", requestId)) { setScanning(false); } } }; const handlePickDir = async () => { const requestId = beginRequest("model-selection"); setLocalError(""); updateDraftFieldMeta("models_dir", { pending: true, invalid: false, message: null }); updateDraftFieldMeta("model_path", { pending: true }); try { const result = await pickRuntimeModelsDir(); if (!isLatestRequest("model-selection", requestId) || !result) return; updateDraft({ models_dir: result.dir }); setScannedModels(result.models); applyResolvedModelState(result.models); updateDraftFieldMeta("models_dir", { pending: false, invalid: false, message: null }); } catch (error) { if (!isLatestRequest("model-selection", requestId)) return; setLocalError(String(error)); updateDraftFieldMeta("models_dir", { pending: false }); updateDraftFieldMeta("model_path", { pending: false }); } }; const handlePickFiles = async () => { const requestId = beginRequest("model-selection"); setLocalError(""); updateDraftFieldMeta("model_path", { pending: true }); try { const picked = await pickRuntimeModelFiles(); if (!isLatestRequest("model-selection", requestId) || picked.length === 0) { updateDraftFieldMeta("model_path", { pending: false }); return; } const currentDraft = draftConfigRef.current; const nextExtra = dedupeModels([...currentDraft.extra_model_paths.map(toModelFile), ...picked]); const nextModel = currentDraft.model_path || nextExtra[0]?.path || ""; updateDraft({ extra_model_paths: nextExtra.map((model) => model.path), model_path: nextModel || null, selected_model_id: currentDraft.model_path ? currentDraft.selected_model_id : null, }); updateDraftFieldMeta("model_path", { pending: false, invalid: false, message: null }); } catch (error) { if (!isLatestRequest("model-selection", requestId)) return; setLocalError(String(error)); updateDraftFieldMeta("model_path", { pending: false }); } }; const handleHubSearch = async () => { if (!hfQuery.trim()) { setHfResults([]); return; } const requestId = beginRequest("hf-search"); setLocalError(""); setHfLoading(true); try { const results = await searchHuggingFaceModels(hfQuery.trim(), draftConfig.hugging_face_token.trim()); if (!isLatestRequest("hf-search", requestId)) return; setHfResults(results); } catch (error) { if (!isLatestRequest("hf-search", requestId)) return; setLocalError(String(error)); setHfResults([]); } finally { if (isLatestRequest("hf-search", requestId)) { setHfLoading(false); } } }; const handleSave = async () => { setLocalError(""); try { await onSave(buildPatch()); setConfirmDiscard(false); } catch (error) { setLocalError(String(error)); } }; const handleSaveAndStart = async () => { setLocalError(""); if (snapshot.process_running) { try { await onStop(); } catch (error) { setLocalError(String(error)); } return; } try { const next = await onSave(buildPatch()); if (next.config.connection_mode !== "local") return; if (!next.config.model_path?.trim()) return; if (next.last_error?.code === "restart_required") return; await onStart(); } catch (error) { setLocalError(String(error)); } }; useEffect(() => { if (!open) return; setConfirmDiscard(false); setLocalError(""); setHfResults([]); void loadModelLibrary(); void loadGpuInfo(); if (draftConfig.connection_mode === "local" && draftConfig.models_dir?.trim()) { void handleRescan(draftConfig.models_dir); } }, [open]); if (!open) return null; const buildPatch = (): RuntimeConfig => ({ ...createRuntimeConfigPatch( savedBaseline, { mode: draftConfig.connection_mode, startPolicy: draftConfig.start_policy, serverUrl: draftConfig.server_url, apiKey: draftConfig.api_key, modelsDir: draftConfig.models_dir || "", selectedModel: draftConfig.model_path || "", selectedModelId: draftConfig.selected_model_id, addr: draftConfig.addr, gpuLayers: draftConfig.gpu_layers, ctxSize: draftConfig.ctx_size, extraModels, }, maxLayers, ), hugging_face_token: draftConfig.hugging_face_token.trim(), remote_presets: draftConfig.remote_presets?.length ? draftConfig.remote_presets : REMOTE_PRESET_TEMPLATES.map((preset) => ({ id: preset.id, label: preset.label, provider: preset.provider, base_url: preset.baseUrl, default_model: "", })), }); return (
Current status
{snapshot.status === "ready" ? "Local API ready" : snapshot.config.connection_mode === "remote" ? "Remote API mode" : "Local API setup"}
Endpoint
{snapshot.config.connection_mode === "remote" ? snapshot.config.server_url || "Not configured" : `http://${snapshot.config.addr}`}
Saved model
{savedBaseline.model_path ? pathName(savedBaseline.model_path) : "No model saved"}
Connection mode
{isFieldDirty("connection_mode") && (
Draft Live now {formatReferenceValue("connection_mode", savedBaseline.connection_mode)}
)}
{draftConfig.connection_mode === "remote" ? (
Provider presets
{REMOTE_PRESET_TEMPLATES.map((preset) => ( ))}
updateDraft({ server_url: event.target.value })} placeholder="https://api.example.com" type="text" /> {renderFieldState("server_url")} updateDraft({ api_key: event.target.value })} placeholder="sk-..." type="password" /> {renderFieldState("api_key")}
) : ( <>
Managed endpoint
http://{draftConfig.addr.trim() || snapshot.config.addr}
Binary discovery
{snapshot.binary_path || "Binary not found yet"}
Managed library
App-managed downloads live outside your scanned folders so they survive app updates.
{libraryState.managed_dir || "Loading managed storage path..."}
{libraryState.entries.length > 0 ? (
{libraryState.entries.map((entry) => ( ))}
) : (
No managed downloads yet.
)}
Models folder
{draftConfig.models_dir || "Choose a folder or add individual files"}
{renderFieldState("models_dir")} {renderFieldState("model_path")} {extraModels.length > 0 && (
{extraModels.map((model) => ( ))}
)}
Discover from Hugging Face
Search public GGUF repos, preview verified files, and keep your local library as the primary runtime source.
updateDraft({ hugging_face_token: event.target.value })} placeholder="hf_..." type="password" />
setHfQuery(event.target.value)} onInput={(event) => setHfQuery((event.target as HTMLInputElement).value)} placeholder="phi-4, mistral, qwen..." type="text" />
{hfResults.length > 0 ? (
{hfResults.map((result) => (
{result.repo_id}
{result.author || "Unknown author"}
{result.verified_files.map((file) => (
{file.path} {file.quantization || "gguf"} {file.size ? ` · ${Math.round(file.size / (1024 * 1024))} MiB` : ""}
))}
))}
) : (
{hfLoading ? "Looking up verified GGUF files..." : "Search for a model family to preview compatible local installs."}
)}
updateDraft({ addr: event.target.value })} placeholder="127.0.0.1:8080" type="text" />
updateDraft({ ctx_size: Math.max(512, parseInt(event.target.value, 10) || 4096) })} type="number" />
GPU layers
{gpuInfo ? `${gpuInfo.name} · recommended ${gpuInfo.recommended_layers}` : gpuError || "GPU detection unavailable"}
{gpuInfo && ( )}
updateDraft({ gpu_layers: Math.max(0, Math.min(maxLayers, parseInt(event.target.value, 10) || 0)) })} type="number" />
{CTX_PRESETS.map((preset) => ( ))}
)} {(localError || snapshot.last_error) && (
{localError || snapshot.last_error?.message || "Couldn't save your draft. Review the highlighted fields and try again — your unsaved changes are still here."}
)} {showLifecycleResult && (
{lastLifecycleResult.message}
)} {applyGuidance && (
{applyGuidance}
)} {!snapshot.process_running && draftConfig.connection_mode === "local" && startReason && (
{startReason}
)}
{draftConfig.connection_mode === "local" && ( )}
{confirmDiscard && (
Revert to saved: Replace all unsaved edits with the last saved values.
)} ); }