diff --git a/src/scripts/ui/settings.ts b/src/scripts/ui/settings.ts index a7a677368..1a4ae12e0 100644 --- a/src/scripts/ui/settings.ts +++ b/src/scripts/ui/settings.ts @@ -65,7 +65,9 @@ export class ComfySettingsDialog extends ComfyDialog { /** * @deprecated Use `settingStore.getDefaultValue` instead. */ - getSettingDefaultValue(id: K): Settings[K] { + getSettingDefaultValue( + id: K + ): Settings[K] | undefined { return useSettingStore().getDefaultValue(id) } diff --git a/src/stores/settingStore.ts b/src/stores/settingStore.ts index 600014881..b3a9f5bbf 100644 --- a/src/stores/settingStore.ts +++ b/src/stores/settingStore.ts @@ -7,7 +7,7 @@ import { api } from '@/scripts/api' import { app } from '@/scripts/app' import type { SettingParams } from '@/types/settingTypes' import type { TreeNode } from '@/types/treeExplorerTypes' -import { compareVersions } from '@/utils/formatUtil' +import { compareVersions, isSemVer } from '@/utils/formatUtil' export const getSettingInfo = (setting: SettingParams) => { const parts = setting.category || setting.id.split('.') @@ -21,16 +21,24 @@ export interface SettingTreeNode extends TreeNode { data?: SettingParams } -function tryMigrateDeprecatedValue(setting: SettingParams, value: any) { +function tryMigrateDeprecatedValue( + setting: SettingParams | undefined, + value: unknown +) { return setting?.migrateDeprecatedValue?.(value) ?? value } -function onChange(setting: SettingParams, newValue: any, oldValue: any) { +function onChange( + setting: SettingParams | undefined, + newValue: unknown, + oldValue: unknown +) { if (setting?.onChange) { setting.onChange(newValue, oldValue) } // Backward compatibility with old settings dialog. // Some extensions still listens event emitted by the old settings dialog. + // @ts-expect-error 'setting' is possibly 'undefined'.ts(18048) app.ui.settings.dispatchChange(setting.id, newValue, oldValue) } @@ -77,13 +85,30 @@ export const useSettingStore = defineStore('setting', () => { return _.cloneDeep(settingValues.value[key] ?? getDefaultValue(key)) } + /** + * Gets the setting params, asserting the type that is intentionally left off + * of {@link settingsById}. + * @param key The key of the setting to get. + * @returns The setting. + */ + function getSettingById( + key: K + ): SettingParams | undefined { + return settingsById.value[key] as SettingParams | undefined + } + /** * Get the default value of a setting. * @param key - The key of the setting to get. * @returns The default value of the setting. */ - function getDefaultValue(key: K): Settings[K] { - const param = settingsById.value[key] + function getDefaultValue( + key: K + ): Settings[K] | undefined { + // Assertion: settingsById is not typed. + const param = getSettingById(key) + + if (param === undefined) return const versionedDefault = getVersionedDefaultValue(key, param) @@ -91,32 +116,33 @@ export const useSettingStore = defineStore('setting', () => { return versionedDefault } - return typeof param?.defaultValue === 'function' + return typeof param.defaultValue === 'function' ? param.defaultValue() - : param?.defaultValue + : param.defaultValue } - function getVersionedDefaultValue( - key: K, - param: SettingParams - ): Settings[K] | null { + function getVersionedDefaultValue< + K extends keyof Settings, + TValue = Settings[K] + >(key: K, param: SettingParams | undefined): TValue | null { // get default versioned value, skipping if the key is 'Comfy.InstalledVersion' to prevent infinite loop - if (param?.defaultsByInstallVersion && key !== 'Comfy.InstalledVersion') { + const defaultsByInstallVersion = param?.defaultsByInstallVersion + if (defaultsByInstallVersion && key !== 'Comfy.InstalledVersion') { const installedVersion = get('Comfy.InstalledVersion') if (installedVersion) { - const sortedVersions = Object.keys(param.defaultsByInstallVersion).sort( - (a, b) => compareVersions(a, b) + const sortedVersions = Object.keys(defaultsByInstallVersion).sort( + (a, b) => compareVersions(b, a) ) - for (const version of sortedVersions.reverse()) { + for (const version of sortedVersions) { // Ensure the version is in a valid format before comparing - if (!isValidVersionFormat(version)) { + if (!isSemVer(version)) { continue } if (compareVersions(installedVersion, version) >= 0) { - const versionedDefault = param.defaultsByInstallVersion[version] + const versionedDefault = defaultsByInstallVersion[version] return typeof versionedDefault === 'function' ? versionedDefault() : versionedDefault @@ -128,12 +154,6 @@ export const useSettingStore = defineStore('setting', () => { return null } - function isValidVersionFormat( - version: string - ): version is `${number}.${number}.${number}` { - return /^\d+\.\d+\.\d+$/.test(version) - } - /** * Register a setting. * @param setting - The setting to register. diff --git a/src/types/settingTypes.ts b/src/types/settingTypes.ts index 8c2497e56..af706fb14 100644 --- a/src/types/settingTypes.ts +++ b/src/types/settingTypes.ts @@ -32,13 +32,10 @@ export interface Setting { render: () => HTMLElement } -export interface SettingParams extends FormItem { +export interface SettingParams extends FormItem { id: keyof Settings defaultValue: any | (() => any) - defaultsByInstallVersion?: Record< - `${number}.${number}.${number}`, - any | (() => any) - > + defaultsByInstallVersion?: Record<`${number}.${number}.${number}`, TValue> onChange?: (newValue: any, oldValue?: any) => void // By default category is id.split('.'). However, changing id to assign // new category has poor backward compatibility. Use this field to overwrite diff --git a/src/utils/formatUtil.ts b/src/utils/formatUtil.ts index 1ce04257b..7b7966394 100644 --- a/src/utils/formatUtil.ts +++ b/src/utils/formatUtil.ts @@ -386,8 +386,10 @@ export const downloadUrlToHfRepoUrl = (url: string): string => { } } -export const isSemVer = (version: string) => { - const regex = /^(\d+)\.(\d+)\.(\d+)$/ +export const isSemVer = ( + version: string +): version is `${number}.${number}.${number}` => { + const regex = /^\d+\.\d+\.\d+$/ return regex.test(version) }