# Settings System ## Overview ComfyUI frontend uses a comprehensive settings system for user preferences with support for dynamic defaults, version-based rollouts, and environment-aware configuration. ### Settings Architecture - Settings are defined as `SettingParams` in `src/constants/coreSettings.ts` - Registered at app startup, loaded/saved via `useSettingStore` (Pinia) - Persisted per user via backend `/settings` endpoint - If a value hasn't been set by the user, the store returns the computed default ```typescript // From src/stores/settingStore.ts:105-122 function getDefaultValue( key: K ): Settings[K] | undefined { const param = getSettingById(key) if (param === undefined) return const versionedDefault = getVersionedDefaultValue(key, param) if (versionedDefault) { return versionedDefault } return typeof param.defaultValue === 'function' ? param.defaultValue() : param.defaultValue } ``` ### Settings Registration Process Settings are registered after server values are loaded: ```typescript // From src/components/graph/GraphCanvas.vue:311-315 CORE_SETTINGS.forEach((setting) => { settingStore.addSetting(setting) }) await newUserService().initializeIfNewUser(settingStore) ``` ## Dynamic and Environment-Based Defaults ### Computed Defaults You can compute defaults dynamically using function defaults that access runtime context: ```typescript // From src/constants/coreSettings.ts:94-101 { id: 'Comfy.Sidebar.Size', // Default to small if the window is less than 1536px(2xl) wide defaultValue: () => (window.innerWidth < 1536 ? 'small' : 'normal') } ``` ```typescript // From src/constants/coreSettings.ts:306 { id: 'Comfy.Locale', defaultValue: () => navigator.language.split('-')[0] || 'en' } ``` ### Version-Based Defaults You can vary defaults by installed frontend version using `defaultsByInstallVersion`: ```typescript // From src/stores/settingStore.ts:129-150 function getVersionedDefaultValue( key: K, param: SettingParams | undefined ): TValue | null { const defaultsByInstallVersion = param?.defaultsByInstallVersion if (defaultsByInstallVersion && key !== 'Comfy.InstalledVersion') { const installedVersion = get('Comfy.InstalledVersion') if (installedVersion) { const sortedVersions = Object.keys(defaultsByInstallVersion).sort( (a, b) => compareVersions(b, a) ) for (const version of sortedVersions) { if (!isSemVer(version)) continue if (compareVersions(installedVersion, version) >= 0) { const versionedDefault = defaultsByInstallVersion[version] return typeof versionedDefault === 'function' ? versionedDefault() : versionedDefault } } } } return null } ``` Example versioned defaults from codebase: ```typescript // From src/constants/coreSettings.ts:38-40 { id: 'Comfy.Graph.LinkReleaseAction', defaultValue: LinkReleaseTriggerAction.CONTEXT_MENU, defaultsByInstallVersion: { '1.24.1': LinkReleaseTriggerAction.SEARCH_BOX } } // Another versioned default example { id: 'Comfy.Graph.LinkReleaseAction.Shift', defaultValue: LinkReleaseTriggerAction.SEARCH_BOX, defaultsByInstallVersion: { '1.24.1': LinkReleaseTriggerAction.CONTEXT_MENU } } ``` ### Real Examples from Codebase Here are actual settings showing different patterns: ```typescript // Number setting with validation { id: 'LiteGraph.Node.TooltipDelay', name: 'Tooltip Delay', type: 'number', attrs: { min: 100, max: 3000, step: 50 }, defaultValue: 500, versionAdded: '1.9.0' } // Hidden system setting for tracking { id: 'Comfy.InstalledVersion', name: 'The frontend version that was running when the user first installed ComfyUI', type: 'hidden', defaultValue: null, versionAdded: '1.24.0' } // Slider with complex tooltip { id: 'LiteGraph.Canvas.LowQualityRenderingZoomThreshold', name: 'Low quality rendering zoom threshold', tooltip: 'Zoom level threshold for performance mode. Lower values (0.1) = quality at all zoom levels. Higher values (1.0) = performance mode even when zoomed in.', type: 'slider', attrs: { min: 0.1, max: 1.0, step: 0.05 }, defaultValue: 0.5 } ``` ### New User Version Capture The initial installed version is captured for new users to ensure versioned defaults remain stable: ```typescript // From src/services/newUserService.ts:49-53 await settingStore.set( 'Comfy.InstalledVersion', __COMFYUI_FRONTEND_VERSION__ ) ``` ## Practical Patterns for Environment-Based Defaults ### Dynamic Default Patterns ```typescript // Device-based default { id: 'Comfy.Example.MobileDefault', type: 'boolean', defaultValue: () => /Mobile/i.test(navigator.userAgent) } // Environment-based default { id: 'Comfy.Example.DevMode', type: 'boolean', defaultValue: () => import.meta.env.DEV } // Window size based { id: 'Comfy.Example.CompactUI', type: 'boolean', defaultValue: () => window.innerWidth < 1024 } ``` ### Version-Based Rollout Pattern ```typescript { id: 'Comfy.Example.NewFeature', type: 'combo', options: ['legacy', 'enhanced'], defaultValue: 'legacy', defaultsByInstallVersion: { '1.25.0': 'enhanced' } } ``` ## Settings Persistence and Access ### API Interaction Values are stored per user via the backend. The store writes through API and falls back to defaults when not set: ```typescript // From src/stores/settingStore.ts:73-75 onChange(settingsById.value[key], newValue, oldValue) settingValues.value[key] = newValue await api.storeSetting(key, newValue) ``` ### Usage in Components ```typescript const settingStore = useSettingStore() // Get setting value (returns computed default if not set by user) const value = settingStore.get('Comfy.SomeSetting') // Update setting value await settingStore.set('Comfy.SomeSetting', newValue) ``` ## Advanced Settings Features ### Migration and Backward Compatibility Settings support migration from deprecated values: ```typescript // From src/stores/settingStore.ts:68-69, 172-175 const newValue = tryMigrateDeprecatedValue( settingsById.value[key], clonedValue ) // Migration happens during addSetting for existing values: if (settingValues.value[setting.id] !== undefined) { settingValues.value[setting.id] = tryMigrateDeprecatedValue( setting, settingValues.value[setting.id] ) } ``` ### onChange Callbacks Settings can define onChange callbacks that receive the setting definition, new value, and old value: ```typescript // From src/stores/settingStore.ts:73, 177 onChange(settingsById.value[key], newValue, oldValue) // During set() onChange(setting, get(setting.id), undefined) // During addSetting() ``` ### Settings UI and Categories Settings are automatically grouped for UI based on their `category` or derived from `id`: ```typescript { id: 'Comfy.Sidebar.Size', category: ['Appearance', 'Sidebar', 'Size'], // UI will group this under Appearance > Sidebar > Size } ``` ## Related Documentation - Feature flag system: `docs/FEATURE_FLAGS.md` - Settings schema for backend: `src/schemas/apiSchema.ts` (zSettings) - Server configuration (separate from user settings): `src/constants/serverConfig.ts` ## Summary - **Settings**: User preferences with dynamic/versioned defaults, persisted per user - **Environment Defaults**: Use function defaults to read runtime context (window, navigator, env) - **Version Rollouts**: Use `defaultsByInstallVersion` for gradual feature releases - **API Interaction**: Settings persist to `/settings` endpoint via `storeSetting()`