diff --git a/apps/x/apps/renderer/src/components/settings-dialog.tsx b/apps/x/apps/renderer/src/components/settings-dialog.tsx index 657b66b9..248c6cc6 100644 --- a/apps/x/apps/renderer/src/components/settings-dialog.tsx +++ b/apps/x/apps/renderer/src/components/settings-dialog.tsx @@ -364,6 +364,11 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b const [testState, setTestState] = useState<{ status: "idle" | "testing" | "success" | "error"; error?: string }>({ status: "idle" }) const [configLoading, setConfigLoading] = useState(true) const [showMoreProviders, setShowMoreProviders] = useState(false) + // "Defer background tasks while a chat is running" — a top-level + // models.json flag. deferExplicit tracks whether the user (or the Ollama + // auto-enable) has ever set it, so we only auto-enable once. + const [deferBackgroundTasks, setDeferBackgroundTasks] = useState(false) + const [deferExplicit, setDeferExplicit] = useState(false) const activeConfig = providerConfigs[provider] const showApiKey = provider === "openai" || provider === "anthropic" || provider === "google" || provider === "openrouter" || provider === "aigateway" || provider === "openai-compatible" @@ -406,6 +411,8 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b path: "config/models.json", }) const parsed = JSON.parse(result.data) + setDeferBackgroundTasks(parsed?.deferBackgroundTasks === true) + setDeferExplicit(typeof parsed?.deferBackgroundTasks === "boolean") if (parsed?.provider?.flavor && parsed?.model) { const flavor = parsed.provider.flavor as LlmProviderFlavor setProvider(flavor) @@ -462,6 +469,17 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b loadCurrentConfig() }, [dialogOpen]) + const handleDeferToggle = useCallback(async (value: boolean) => { + setDeferBackgroundTasks(value) + setDeferExplicit(true) + try { + await window.ipc.invoke("models:updateConfig", { deferBackgroundTasks: value }) + window.dispatchEvent(new Event("models-config-changed")) + } catch { + toast.error("Failed to save setting") + } + }, []) + // Load models catalog useEffect(() => { if (!dialogOpen) return @@ -532,6 +550,12 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b setDefaultProvider(provider) setTestState({ status: "success" }) window.dispatchEvent(new Event('models-config-changed')) + // Local models compete with background agents for the same hardware: + // when the user connects Ollama and has never touched the defer + // flag, enable it for them (they can switch it off below). + if (provider === "ollama" && !deferExplicit && !deferBackgroundTasks) { + void handleDeferToggle(true) + } // Capability probe caveats (local models): saved, but the user should // know when the model can't do tools or has a too-small context. const warnings: string[] = result.warnings ?? [] @@ -551,7 +575,7 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b setTestState({ status: "error", error: "Connection test failed" }) toast.error("Connection test failed") } - }, [canTest, provider, activeConfig, rowboatConnected]) + }, [canTest, provider, activeConfig, rowboatConnected, deferExplicit, deferBackgroundTasks, handleDeferToggle]) const handleSetDefault = useCallback(async (prov: LlmProviderFlavor) => { const config = providerConfigs[prov] @@ -938,6 +962,17 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b )} + {/* Defer background tasks while chatting */} +
+
+
Defer background tasks while chatting
+
+ Background agents (knowledge sync, live notes, tasks) wait until your chat finishes. Recommended for local models. +
+
+ +
+ {/* Test & Save button */}