feat(settings): create config context and wire into App

This commit is contained in:
Mark IJbema
2026-02-12 15:12:47 +01:00
parent b26cb9ab39
commit 98c5255146
2 changed files with 91 additions and 5 deletions
+8 -5
View File
@@ -9,6 +9,7 @@ import ProfileView from "./components/ProfileView"
import { VSCodeProvider } from "./context/vscode"
import { ServerProvider, useServer } from "./context/server"
import { ProviderProvider } from "./context/provider"
import { ConfigProvider } from "./context/config"
import { SessionProvider, useSession } from "./context/session"
import { LanguageProvider } from "./context/language"
import { ChatView } from "./components/chat"
@@ -165,11 +166,13 @@ const App: Component = () => {
<LanguageBridge>
<MarkedProvider>
<ProviderProvider>
<SessionProvider>
<DataBridge>
<AppContent />
</DataBridge>
</SessionProvider>
<ConfigProvider>
<SessionProvider>
<DataBridge>
<AppContent />
</DataBridge>
</SessionProvider>
</ConfigProvider>
</ProviderProvider>
</MarkedProvider>
</LanguageBridge>
@@ -0,0 +1,83 @@
/**
* Config context
* Manages backend configuration state (permissions, agents, providers, etc.)
* and exposes an updateConfig method to apply partial updates.
*/
import { createContext, useContext, createSignal, onCleanup, ParentComponent, Accessor } from "solid-js"
import { useVSCode } from "./vscode"
import type { Config, ExtensionMessage } from "../types/messages"
interface ConfigContextValue {
config: Accessor<Config>
loading: Accessor<boolean>
updateConfig: (partial: Partial<Config>) => void
}
const ConfigContext = createContext<ConfigContextValue>()
export const ConfigProvider: ParentComponent = (props) => {
const vscode = useVSCode()
const [config, setConfig] = createSignal<Config>({})
const [loading, setLoading] = createSignal(true)
// Register handler immediately (not in onMount) so we never miss
// a configLoaded message that arrives before the DOM mount.
const unsubscribe = vscode.onMessage((message: ExtensionMessage) => {
if (message.type === "configLoaded") {
setConfig(message.config)
setLoading(false)
return
}
if (message.type === "configUpdated") {
setConfig(message.config)
return
}
})
onCleanup(unsubscribe)
// Request config in case the initial push was missed.
// Retry a few times because the extension's httpClient may
// not be ready yet when the first request arrives.
let retries = 0
const maxRetries = 5
const retryMs = 500
vscode.postMessage({ type: "requestConfig" })
const retryTimer = setInterval(() => {
retries++
if (!loading() || retries >= maxRetries) {
clearInterval(retryTimer)
return
}
vscode.postMessage({ type: "requestConfig" })
}, retryMs)
onCleanup(() => clearInterval(retryTimer))
function updateConfig(partial: Partial<Config>) {
// Optimistically update local state
setConfig((prev) => ({ ...prev, ...partial }))
// Send to extension for persistence
vscode.postMessage({ type: "updateConfig", config: partial })
}
const value: ConfigContextValue = {
config,
loading,
updateConfig,
}
return <ConfigContext.Provider value={value}>{props.children}</ConfigContext.Provider>
}
export function useConfig(): ConfigContextValue {
const context = useContext(ConfigContext)
if (!context) {
throw new Error("useConfig must be used within a ConfigProvider")
}
return context
}