From 2c64ef089e4c58f64bfa8e8984bfaa65e686f335 Mon Sep 17 00:00:00 2001 From: Hikari Date: Fri, 6 Feb 2026 22:40:47 -0800 Subject: [PATCH] fix: resolve critical config store race condition causing config loss CRITICAL BUG: The config store had a race condition in all methods that accessed the current config. The pattern: ```typescript let currentConfig = defaultConfig; config.subscribe((c) => (currentConfig = c))(); ``` This immediately unsubscribes (the `()` at the end), creating a race where sometimes it would get the value, sometimes it wouldn't. This caused: - Config appearing "lost" after permission approvals - Settings resetting to defaults randomly - Model selection not persisting FIX: - Created a proper `getCurrentConfig()` helper that unsubscribes cleanly - Replaced ALL instances of the buggy pattern (12 occurrences!) - getConfig(), updateConfig(), toggles, theme setters, font methods, etc. This should completely fix the config loss issue. --- src/lib/stores/config.ts | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/lib/stores/config.ts b/src/lib/stores/config.ts index 0c95945..a19d5bd 100644 --- a/src/lib/stores/config.ts +++ b/src/lib/stores/config.ts @@ -92,6 +92,14 @@ function createConfigStore() { const isSidebarOpen = writable(false); const saveError = writable(null); + // Internal function to get current config synchronously + function getCurrentConfig(): HikariConfig { + let currentConfig: HikariConfig = defaultConfig; + const unsubscribe = config.subscribe((c) => (currentConfig = c)); + unsubscribe(); + return currentConfig; + } + async function loadConfig() { isLoading.set(true); try { @@ -119,8 +127,7 @@ function createConfigStore() { } async function updateConfig(updates: Partial) { - let currentConfig: HikariConfig = defaultConfig; - config.subscribe((c) => (currentConfig = c))(); + const currentConfig = getCurrentConfig(); const newConfig = { ...currentConfig, ...updates }; await saveConfig(newConfig); } @@ -145,15 +152,13 @@ function createConfigStore() { updates.custom_theme_colors = customColors; } await updateConfig(updates); - let currentConfig: HikariConfig = defaultConfig; - config.subscribe((c) => (currentConfig = c))(); + const currentConfig = getCurrentConfig(); applyTheme(theme, currentConfig.custom_theme_colors); }, setCustomThemeColors: async (colors: CustomThemeColors) => { await updateConfig({ custom_theme_colors: colors }); - let currentConfig: HikariConfig = defaultConfig; - config.subscribe((c) => (currentConfig = c))(); + const currentConfig = getCurrentConfig(); if (currentConfig.theme === "custom") { applyCustomThemeColors(colors); } @@ -166,16 +171,14 @@ function createConfigStore() { }, increaseFontSize: async () => { - let currentConfig: HikariConfig = defaultConfig; - config.subscribe((c) => (currentConfig = c))(); + const currentConfig = getCurrentConfig(); const newSize = Math.min(MAX_FONT_SIZE, currentConfig.font_size + 2); await updateConfig({ font_size: newSize }); applyFontSize(newSize); }, decreaseFontSize: async () => { - let currentConfig: HikariConfig = defaultConfig; - config.subscribe((c) => (currentConfig = c))(); + const currentConfig = getCurrentConfig(); const newSize = Math.max(MIN_FONT_SIZE, currentConfig.font_size - 2); await updateConfig({ font_size: newSize }); applyFontSize(newSize); @@ -187,8 +190,7 @@ function createConfigStore() { }, addAutoGrantedTool: async (tool: string) => { - let currentConfig: HikariConfig = defaultConfig; - config.subscribe((c) => (currentConfig = c))(); + const currentConfig = getCurrentConfig(); if (!currentConfig.auto_granted_tools.includes(tool)) { const newTools = [...currentConfig.auto_granted_tools, tool]; await updateConfig({ auto_granted_tools: newTools }); @@ -196,27 +198,22 @@ function createConfigStore() { }, removeAutoGrantedTool: async (tool: string) => { - let currentConfig: HikariConfig = defaultConfig; - config.subscribe((c) => (currentConfig = c))(); + const currentConfig = getCurrentConfig(); const newTools = currentConfig.auto_granted_tools.filter((t) => t !== tool); await updateConfig({ auto_granted_tools: newTools }); }, getConfig: (): HikariConfig => { - let currentConfig: HikariConfig = defaultConfig; - config.subscribe((c) => (currentConfig = c))(); - return currentConfig; + return getCurrentConfig(); }, toggleStreamerMode: async () => { - let currentConfig: HikariConfig = defaultConfig; - config.subscribe((c) => (currentConfig = c))(); + const currentConfig = getCurrentConfig(); await updateConfig({ streamer_mode: !currentConfig.streamer_mode }); }, toggleCompactMode: async () => { - let currentConfig: HikariConfig = defaultConfig; - config.subscribe((c) => (currentConfig = c))(); + const currentConfig = getCurrentConfig(); await updateConfig({ compact_mode: !currentConfig.compact_mode }); },