|
import { atom, map } from 'nanostores'; |
|
import { PROVIDER_LIST } from '~/utils/constants'; |
|
import type { IProviderConfig } from '~/types/model'; |
|
import type { |
|
TabVisibilityConfig, |
|
TabWindowConfig, |
|
UserTabConfig, |
|
DevTabConfig, |
|
} from '~/components/@settings/core/types'; |
|
import { DEFAULT_TAB_CONFIG } from '~/components/@settings/core/constants'; |
|
import Cookies from 'js-cookie'; |
|
import { toggleTheme } from './theme'; |
|
import { create } from 'zustand'; |
|
|
|
export interface Shortcut { |
|
key: string; |
|
ctrlKey?: boolean; |
|
shiftKey?: boolean; |
|
altKey?: boolean; |
|
metaKey?: boolean; |
|
ctrlOrMetaKey?: boolean; |
|
action: () => void; |
|
description?: string; |
|
isPreventDefault?: boolean; |
|
} |
|
|
|
export interface Shortcuts { |
|
toggleTheme: Shortcut; |
|
toggleTerminal: Shortcut; |
|
} |
|
|
|
export const URL_CONFIGURABLE_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike']; |
|
export const LOCAL_PROVIDERS = ['OpenAILike', 'LMStudio', 'Ollama']; |
|
|
|
export type ProviderSetting = Record<string, IProviderConfig>; |
|
|
|
|
|
export const shortcutsStore = map<Shortcuts>({ |
|
toggleTheme: { |
|
key: 'd', |
|
metaKey: true, |
|
altKey: true, |
|
shiftKey: true, |
|
action: () => toggleTheme(), |
|
description: 'Toggle theme', |
|
isPreventDefault: true, |
|
}, |
|
toggleTerminal: { |
|
key: '`', |
|
ctrlOrMetaKey: true, |
|
action: () => { |
|
|
|
}, |
|
description: 'Toggle terminal', |
|
isPreventDefault: true, |
|
}, |
|
}); |
|
|
|
|
|
const PROVIDER_SETTINGS_KEY = 'provider_settings'; |
|
|
|
|
|
const isBrowser = typeof window !== 'undefined'; |
|
|
|
|
|
const getInitialProviderSettings = (): ProviderSetting => { |
|
const initialSettings: ProviderSetting = {}; |
|
|
|
|
|
PROVIDER_LIST.forEach((provider) => { |
|
initialSettings[provider.name] = { |
|
...provider, |
|
settings: { |
|
|
|
enabled: !LOCAL_PROVIDERS.includes(provider.name), |
|
}, |
|
}; |
|
}); |
|
|
|
|
|
if (isBrowser) { |
|
const savedSettings = localStorage.getItem(PROVIDER_SETTINGS_KEY); |
|
|
|
if (savedSettings) { |
|
try { |
|
const parsed = JSON.parse(savedSettings); |
|
Object.entries(parsed).forEach(([key, value]) => { |
|
if (initialSettings[key]) { |
|
initialSettings[key].settings = (value as IProviderConfig).settings; |
|
} |
|
}); |
|
} catch (error) { |
|
console.error('Error parsing saved provider settings:', error); |
|
} |
|
} |
|
} |
|
|
|
return initialSettings; |
|
}; |
|
|
|
export const providersStore = map<ProviderSetting>(getInitialProviderSettings()); |
|
|
|
|
|
export const updateProviderSettings = (provider: string, settings: ProviderSetting) => { |
|
const currentSettings = providersStore.get(); |
|
|
|
|
|
const updatedProvider = { |
|
...currentSettings[provider], |
|
settings: { |
|
...currentSettings[provider].settings, |
|
...settings, |
|
}, |
|
}; |
|
|
|
|
|
providersStore.setKey(provider, updatedProvider); |
|
|
|
|
|
const allSettings = providersStore.get(); |
|
localStorage.setItem(PROVIDER_SETTINGS_KEY, JSON.stringify(allSettings)); |
|
}; |
|
|
|
export const isDebugMode = atom(false); |
|
|
|
|
|
const SETTINGS_KEYS = { |
|
LATEST_BRANCH: 'isLatestBranch', |
|
AUTO_SELECT_TEMPLATE: 'autoSelectTemplate', |
|
CONTEXT_OPTIMIZATION: 'contextOptimizationEnabled', |
|
EVENT_LOGS: 'isEventLogsEnabled', |
|
PROMPT_ID: 'promptId', |
|
DEVELOPER_MODE: 'isDeveloperMode', |
|
} as const; |
|
|
|
|
|
const getInitialSettings = () => { |
|
const getStoredBoolean = (key: string, defaultValue: boolean): boolean => { |
|
if (!isBrowser) { |
|
return defaultValue; |
|
} |
|
|
|
const stored = localStorage.getItem(key); |
|
|
|
if (stored === null) { |
|
return defaultValue; |
|
} |
|
|
|
try { |
|
return JSON.parse(stored); |
|
} catch { |
|
return defaultValue; |
|
} |
|
}; |
|
|
|
return { |
|
latestBranch: getStoredBoolean(SETTINGS_KEYS.LATEST_BRANCH, false), |
|
autoSelectTemplate: getStoredBoolean(SETTINGS_KEYS.AUTO_SELECT_TEMPLATE, true), |
|
contextOptimization: getStoredBoolean(SETTINGS_KEYS.CONTEXT_OPTIMIZATION, true), |
|
eventLogs: getStoredBoolean(SETTINGS_KEYS.EVENT_LOGS, true), |
|
promptId: isBrowser ? localStorage.getItem(SETTINGS_KEYS.PROMPT_ID) || 'default' : 'default', |
|
developerMode: getStoredBoolean(SETTINGS_KEYS.DEVELOPER_MODE, false), |
|
}; |
|
}; |
|
|
|
|
|
const initialSettings = getInitialSettings(); |
|
|
|
export const latestBranchStore = atom<boolean>(initialSettings.latestBranch); |
|
export const autoSelectStarterTemplate = atom<boolean>(initialSettings.autoSelectTemplate); |
|
export const enableContextOptimizationStore = atom<boolean>(initialSettings.contextOptimization); |
|
export const isEventLogsEnabled = atom<boolean>(initialSettings.eventLogs); |
|
export const promptStore = atom<string>(initialSettings.promptId); |
|
|
|
|
|
export const updateLatestBranch = (enabled: boolean) => { |
|
latestBranchStore.set(enabled); |
|
localStorage.setItem(SETTINGS_KEYS.LATEST_BRANCH, JSON.stringify(enabled)); |
|
}; |
|
|
|
export const updateAutoSelectTemplate = (enabled: boolean) => { |
|
autoSelectStarterTemplate.set(enabled); |
|
localStorage.setItem(SETTINGS_KEYS.AUTO_SELECT_TEMPLATE, JSON.stringify(enabled)); |
|
}; |
|
|
|
export const updateContextOptimization = (enabled: boolean) => { |
|
enableContextOptimizationStore.set(enabled); |
|
localStorage.setItem(SETTINGS_KEYS.CONTEXT_OPTIMIZATION, JSON.stringify(enabled)); |
|
}; |
|
|
|
export const updateEventLogs = (enabled: boolean) => { |
|
isEventLogsEnabled.set(enabled); |
|
localStorage.setItem(SETTINGS_KEYS.EVENT_LOGS, JSON.stringify(enabled)); |
|
}; |
|
|
|
export const updatePromptId = (id: string) => { |
|
promptStore.set(id); |
|
localStorage.setItem(SETTINGS_KEYS.PROMPT_ID, id); |
|
}; |
|
|
|
|
|
const getInitialTabConfiguration = (): TabWindowConfig => { |
|
const defaultConfig: TabWindowConfig = { |
|
userTabs: DEFAULT_TAB_CONFIG.filter((tab): tab is UserTabConfig => tab.window === 'user'), |
|
developerTabs: DEFAULT_TAB_CONFIG.filter((tab): tab is DevTabConfig => tab.window === 'developer'), |
|
}; |
|
|
|
if (!isBrowser) { |
|
return defaultConfig; |
|
} |
|
|
|
try { |
|
const saved = localStorage.getItem('bolt_tab_configuration'); |
|
|
|
if (!saved) { |
|
return defaultConfig; |
|
} |
|
|
|
const parsed = JSON.parse(saved); |
|
|
|
if (!parsed?.userTabs || !parsed?.developerTabs) { |
|
return defaultConfig; |
|
} |
|
|
|
|
|
return { |
|
userTabs: parsed.userTabs.filter((tab: TabVisibilityConfig): tab is UserTabConfig => tab.window === 'user'), |
|
developerTabs: parsed.developerTabs.filter( |
|
(tab: TabVisibilityConfig): tab is DevTabConfig => tab.window === 'developer', |
|
), |
|
}; |
|
} catch (error) { |
|
console.warn('Failed to parse tab configuration:', error); |
|
return defaultConfig; |
|
} |
|
}; |
|
|
|
|
|
|
|
export const tabConfigurationStore = map<TabWindowConfig>(getInitialTabConfiguration()); |
|
|
|
|
|
export const updateTabConfiguration = (config: TabVisibilityConfig) => { |
|
const currentConfig = tabConfigurationStore.get(); |
|
console.log('Current tab configuration before update:', currentConfig); |
|
|
|
const isUserTab = config.window === 'user'; |
|
const targetArray = isUserTab ? 'userTabs' : 'developerTabs'; |
|
|
|
|
|
const updatedTabs = currentConfig[targetArray].map((tab) => (tab.id === config.id ? { ...config } : tab)); |
|
|
|
|
|
if (!updatedTabs.find((tab) => tab.id === config.id)) { |
|
updatedTabs.push(config); |
|
} |
|
|
|
|
|
const newConfig: TabWindowConfig = { |
|
...currentConfig, |
|
[targetArray]: updatedTabs, |
|
}; |
|
|
|
console.log('New tab configuration after update:', newConfig); |
|
|
|
tabConfigurationStore.set(newConfig); |
|
Cookies.set('tabConfiguration', JSON.stringify(newConfig), { |
|
expires: 365, |
|
path: '/', |
|
sameSite: 'strict', |
|
}); |
|
}; |
|
|
|
|
|
export const resetTabConfiguration = () => { |
|
const defaultConfig: TabWindowConfig = { |
|
userTabs: DEFAULT_TAB_CONFIG.filter((tab): tab is UserTabConfig => tab.window === 'user'), |
|
developerTabs: DEFAULT_TAB_CONFIG.filter((tab): tab is DevTabConfig => tab.window === 'developer'), |
|
}; |
|
|
|
tabConfigurationStore.set(defaultConfig); |
|
localStorage.setItem('bolt_tab_configuration', JSON.stringify(defaultConfig)); |
|
}; |
|
|
|
|
|
export const developerModeStore = atom<boolean>(initialSettings.developerMode); |
|
|
|
export const setDeveloperMode = (value: boolean) => { |
|
developerModeStore.set(value); |
|
|
|
if (isBrowser) { |
|
localStorage.setItem(SETTINGS_KEYS.DEVELOPER_MODE, JSON.stringify(value)); |
|
} |
|
}; |
|
|
|
|
|
interface SettingsStore { |
|
isOpen: boolean; |
|
selectedTab: string; |
|
openSettings: () => void; |
|
closeSettings: () => void; |
|
setSelectedTab: (tab: string) => void; |
|
} |
|
|
|
export const useSettingsStore = create<SettingsStore>((set) => ({ |
|
isOpen: false, |
|
selectedTab: 'user', |
|
|
|
openSettings: () => { |
|
set({ |
|
isOpen: true, |
|
selectedTab: 'user', |
|
}); |
|
}, |
|
|
|
closeSettings: () => { |
|
set({ |
|
isOpen: false, |
|
selectedTab: 'user', |
|
}); |
|
}, |
|
|
|
setSelectedTab: (tab: string) => { |
|
set({ selectedTab: tab }); |
|
}, |
|
})); |
|
|