From f145a89c66f85f5fc44eb8a2f36ef13e196f097d Mon Sep 17 00:00:00 2001 From: Yiqun Xu <71995731+yiqun12@users.noreply.github.com> Date: Wed, 11 Jun 2025 04:07:33 -0700 Subject: [PATCH] feat: Implement versioned default settings system --- browser_tests/fixtures/ComfyPage.ts | 10 +++--- src/components/graph/GraphCanvas.vue | 8 +++++ src/constants/coreSettings.ts | 12 +++++++- src/schemas/apiSchema.ts | 2 ++ src/stores/settingStore.ts | 24 +++++++++++++++ src/types/settingTypes.ts | 2 ++ src/utils/versioning.ts | 37 +++++++++++++++++++++++ tests-ui/tests/store/settingStore.test.ts | 20 ++++++++++++ 8 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 src/utils/versioning.ts diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index 2f6fc0b14..52c4a44db 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -762,7 +762,7 @@ export class ComfyPage { y: 625 } }) - this.page.mouse.move(10, 10) + await this.page.mouse.move(10, 10) await this.nextFrame() } @@ -774,7 +774,7 @@ export class ComfyPage { }, button: 'right' }) - this.page.mouse.move(10, 10) + await this.page.mouse.move(10, 10) await this.nextFrame() } @@ -1072,7 +1072,9 @@ export const comfyPageFixture = base.extend<{ 'Comfy.EnableTooltips': false, 'Comfy.userId': userId, // Set tutorial completed to true to avoid loading the tutorial workflow. - 'Comfy.TutorialCompleted': true + 'Comfy.TutorialCompleted': true, + // Set installed version for consistent test behavior. + 'Comfy.InstalledVersion': '1.22.1' }) } catch (e) { console.error(e) @@ -1083,7 +1085,7 @@ export const comfyPageFixture = base.extend<{ }, comfyMouse: async ({ comfyPage }, use) => { const comfyMouse = new ComfyMouse(comfyPage) - use(comfyMouse) + await use(comfyMouse) } }) diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index e4db7b6be..00304d5fe 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -86,6 +86,7 @@ import { useSettingStore } from '@/stores/settingStore' import { useToastStore } from '@/stores/toastStore' import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore' import { useWorkspaceStore } from '@/stores/workspaceStore' +import { getCurrentVersion } from '@/utils/versioning' const emit = defineEmits<{ ready: [] @@ -300,6 +301,13 @@ onMounted(async () => { CORE_SETTINGS.forEach((setting) => { settingStore.addSetting(setting) }) + if (!settingStore.get('Comfy.InstalledVersion')) { + const currentVersion = + Object.keys(settingStore.settingValues).length > 0 + ? '0.0.1' + : getCurrentVersion() + await settingStore.set('Comfy.InstalledVersion', currentVersion) + } // @ts-expect-error fixme ts strict error await comfyApp.setup(canvasRef.value) canvasStore.canvas = comfyApp.canvas diff --git a/src/constants/coreSettings.ts b/src/constants/coreSettings.ts index ec554f632..1f30b3fc2 100644 --- a/src/constants/coreSettings.ts +++ b/src/constants/coreSettings.ts @@ -35,7 +35,10 @@ export const CORE_SETTINGS: SettingParams[] = [ name: 'Action on link release (No modifier)', type: 'combo', options: Object.values(LinkReleaseTriggerAction), - defaultValue: LinkReleaseTriggerAction.CONTEXT_MENU + defaultValue: LinkReleaseTriggerAction.CONTEXT_MENU, + defaultsByInstallVersion: { + '1.21.3': LinkReleaseTriggerAction.SEARCH_BOX + } }, { id: 'Comfy.LinkRelease.ActionShift', @@ -747,6 +750,13 @@ export const CORE_SETTINGS: SettingParams[] = [ defaultValue: false, versionAdded: '1.8.7' }, + { + id: 'Comfy.InstalledVersion', + name: 'Installed version', + type: 'hidden', + defaultValue: null, + versionAdded: '1.22.1' + }, { id: 'LiteGraph.ContextMenu.Scaling', name: 'Scale node combo widget menus (lists) when zoomed in', diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts index a6d8eee27..44ba31b1e 100644 --- a/src/schemas/apiSchema.ts +++ b/src/schemas/apiSchema.ts @@ -448,6 +448,7 @@ const zSettings = z.object({ 'Comfy.Toast.DisableReconnectingToast': z.boolean(), 'Comfy.Workflow.Persist': z.boolean(), 'Comfy.TutorialCompleted': z.boolean(), + 'Comfy.InstalledVersion': z.string().nullable(), 'Comfy.Node.AllowImageSizeDraw': z.boolean(), 'Comfy-Desktop.AutoUpdate': z.boolean(), 'Comfy-Desktop.SendStatistics': z.boolean(), @@ -471,6 +472,7 @@ const zSettings = z.object({ 'VHS.AdvancedPreviews': z.string(), /** Settings used for testing */ 'test.setting': z.any(), + 'test.versionedSetting': z.any(), 'main.sub.setting.name': z.any(), 'single.setting': z.any(), 'LiteGraph.Node.DefaultPadding': z.boolean(), diff --git a/src/stores/settingStore.ts b/src/stores/settingStore.ts index 1247e6919..18cc801a0 100644 --- a/src/stores/settingStore.ts +++ b/src/stores/settingStore.ts @@ -7,6 +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/versioning' export const getSettingInfo = (setting: SettingParams) => { const parts = setting.category || setting.id.split('.') @@ -83,6 +84,29 @@ export const useSettingStore = defineStore('setting', () => { */ function getDefaultValue(key: K): Settings[K] { const param = settingsById.value[key] + + // Check for versioned defaults based on installation version + if (param?.defaultsByInstallVersion) { + const installedVersion = get('Comfy.InstalledVersion') + + if (installedVersion) { + // Find the highest version that is <= installedVersion + const sortedVersions = Object.keys(param.defaultsByInstallVersion).sort( + (a, b) => compareVersions(a, b) + ) + + for (const version of sortedVersions.reverse()) { + if (compareVersions(installedVersion, version) >= 0) { + const versionedDefault = param.defaultsByInstallVersion[version] + return typeof versionedDefault === 'function' + ? versionedDefault() + : versionedDefault + } + } + } + } + + // Fall back to original defaultValue return typeof param?.defaultValue === 'function' ? param.defaultValue() : param?.defaultValue diff --git a/src/types/settingTypes.ts b/src/types/settingTypes.ts index 4518c30ec..92ea911a8 100644 --- a/src/types/settingTypes.ts +++ b/src/types/settingTypes.ts @@ -35,6 +35,8 @@ export interface Setting { export interface SettingParams extends FormItem { id: keyof Settings defaultValue: any | (() => any) + // Optional versioned defaults based on installation version + defaultsByInstallVersion?: Record any)> 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/versioning.ts b/src/utils/versioning.ts new file mode 100644 index 000000000..2f6104144 --- /dev/null +++ b/src/utils/versioning.ts @@ -0,0 +1,37 @@ +import { electronAPI, isElectron } from '@/utils/envUtil' + +/** + * Compare two semantic version strings. + * @param a - First version string + * @param b - Second version string + * @returns 0 if equal, 1 if a > b, -1 if a < b + */ +export function compareVersions(a: string, b: string): number { + const parseVersion = (version: string) => { + return version.split('.').map((v) => parseInt(v, 10) || 0) + } + + const versionA = parseVersion(a) + const versionB = parseVersion(b) + + for (let i = 0; i < Math.max(versionA.length, versionB.length); i++) { + const numA = versionA[i] || 0 + const numB = versionB[i] || 0 + + if (numA > numB) return 1 + if (numA < numB) return -1 + } + + return 0 +} + +/** + * Get the current ComfyUI version for version tracking + */ +export function getCurrentVersion(): string { + if (isElectron()) { + return electronAPI().getComfyUIVersion() + } + // For web version, fallback to frontend version + return __COMFYUI_FRONTEND_VERSION__ +} diff --git a/tests-ui/tests/store/settingStore.test.ts b/tests-ui/tests/store/settingStore.test.ts index a217cfeb6..f6aed8a9c 100644 --- a/tests-ui/tests/store/settingStore.test.ts +++ b/tests-ui/tests/store/settingStore.test.ts @@ -122,6 +122,26 @@ describe('useSettingStore', () => { expect(store.get('test.setting')).toBe('default') }) + it('should use versioned default based on installation version', () => { + // Set up an installed version + store.settingValues['Comfy.InstalledVersion'] = '1.25.0' + + const setting: SettingParams = { + id: 'test.versionedSetting', + name: 'test.versionedSetting', + type: 'text', + defaultValue: 'original', + defaultsByInstallVersion: { + '1.20.0': 'version_1_20', + '1.24.0': 'version_1_24' + } + } + store.addSetting(setting) + + // Should use the highest version <= installed version + expect(store.get('test.versionedSetting')).toBe('version_1_24') + }) + it('should set value and trigger onChange', async () => { const onChangeMock = vi.fn() const dispatchChangeMock = vi.mocked(app.ui.settings.dispatchChange)