From b73fe807619d415c7013b3550fe87f67087c2e72 Mon Sep 17 00:00:00 2001 From: Chenlei Hu Date: Wed, 24 Jul 2024 11:49:09 -0400 Subject: [PATCH] [Major Refactor] Use pinia store to manage setting & nodeDef (#202) * Node def store and settings tore * Fix initial values * Remove legacy setting listen * Fix searchbox test --- browser_tests/ComfyPage.ts | 3 + src/App.vue | 72 ++++++++----------- .../sidebar/SideBarThemeToggleIcon.vue | 36 ++++------ src/components/sidebar/SideToolBar.vue | 26 +++---- src/main.ts | 2 + src/scripts/app.ts | 3 + src/scripts/ui/menu/index.ts | 1 - src/scripts/ui/settings.ts | 18 +++++ src/stores/nodeDefStore.ts | 68 ++++++++++++++++++ src/stores/settingStore.ts | 41 +++++++++++ 10 files changed, 186 insertions(+), 84 deletions(-) create mode 100644 src/stores/nodeDefStore.ts create mode 100644 src/stores/settingStore.ts diff --git a/browser_tests/ComfyPage.ts b/browser_tests/ComfyPage.ts index 9fcd08893..154165f8a 100644 --- a/browser_tests/ComfyPage.ts +++ b/browser_tests/ComfyPage.ts @@ -30,6 +30,9 @@ class ComfyNodeSearchBox { await this.input.waitFor({ state: "visible" }); await this.input.fill(nodeName); await this.dropdown.waitFor({ state: "visible" }); + // Wait for some time for the auto complete list to update. + // The auto complete list is debounced and may take some time to update. + await this.page.waitForTimeout(500); await this.dropdown.locator("li").nth(0).click(); } } diff --git a/src/App.vue b/src/App.vue index 59708691d..f39fb3770 100644 --- a/src/App.vue +++ b/src/App.vue @@ -13,51 +13,43 @@ diff --git a/src/components/sidebar/SideBarThemeToggleIcon.vue b/src/components/sidebar/SideBarThemeToggleIcon.vue index efaf2834f..fdf19935d 100644 --- a/src/components/sidebar/SideBarThemeToggleIcon.vue +++ b/src/components/sidebar/SideBarThemeToggleIcon.vue @@ -8,31 +8,23 @@ diff --git a/src/components/sidebar/SideToolBar.vue b/src/components/sidebar/SideToolBar.vue index fcac4333c..88d437e7e 100644 --- a/src/components/sidebar/SideToolBar.vue +++ b/src/components/sidebar/SideToolBar.vue @@ -23,8 +23,9 @@ import SideBarThemeToggleIcon from "./SideBarThemeToggleIcon.vue"; import SideBarSettingsToggleIcon from "./SideBarSettingsToggleIcon.vue"; import NodeDetailSideBarItem from "./items/NodeDetailSideBarItem.vue"; import QueueSideBarItem from "./items/QueueSideBarItem.vue"; -import { markRaw, onMounted, onUnmounted, ref, watch } from "vue"; +import { computed, markRaw, ref, watch } from "vue"; import { useI18n } from "vue-i18n"; +import { useSettingStore } from "@/stores/settingStore"; const { t } = useI18n(); const items = ref([ @@ -49,22 +50,13 @@ watch(selectedItem, (newVal) => { emit("change", newVal !== null); }); -const onBetaMenuDisabled = () => { - selectedItem.value = null; -}; - -onMounted(() => { - document.addEventListener( - "comfy:setting:beta-menu-disabled", - onBetaMenuDisabled - ); -}); - -onUnmounted(() => { - document.removeEventListener( - "comfy:setting:beta-menu-disabled", - onBetaMenuDisabled - ); +const betaMenuEnabled = computed( + () => useSettingStore().get("Comfy.UseNewMenu") !== "Disabled" +); +watch(betaMenuEnabled, (newValue) => { + if (!newValue) { + selectedItem.value = null; + } }); diff --git a/src/main.ts b/src/main.ts index 0cde8caa8..f63b00219 100644 --- a/src/main.ts +++ b/src/main.ts @@ -45,4 +45,6 @@ comfyApp.setup().then(() => { .use(pinia) .use(i18n) .mount("#vue-app"); + + comfyApp.vueAppReady = true; }); diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 76d243351..4b8c2dcbc 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -86,6 +86,7 @@ export class ComfyApp { DraggableList, }; + vueAppReady: boolean; ui: ComfyUI; logging: ComfyLogging; extensions: ComfyExtension[]; @@ -118,6 +119,7 @@ export class ComfyApp { nodeDefs: Record; constructor() { + this.vueAppReady = false; this.ui = new ComfyUI(this); this.logging = new ComfyLogging(this); this.workflowManager = new ComfyWorkflowManager(this); @@ -1877,6 +1879,7 @@ export class ComfyApp { this.#addAfterConfigureHandler(); this.canvas = new LGraphCanvas(canvasEl, this.graph); + this.ui.settings.refreshSetting("Comfy.NodeSearchBoxImpl"); this.ctx = canvasEl.getContext("2d"); LiteGraph.release_link_on_empty_shows_menu = true; diff --git a/src/scripts/ui/menu/index.ts b/src/scripts/ui/menu/index.ts index f593de0ad..e8e26411a 100644 --- a/src/scripts/ui/menu/index.ts +++ b/src/scripts/ui/menu/index.ts @@ -182,7 +182,6 @@ export class ComfyAppMenu { app.ui.menuContainer.style.removeProperty("display"); this.element.style.display = "none"; app.ui.restoreMenuPosition(); - document.dispatchEvent(new Event("comfy:setting:beta-menu-disabled")); } window.dispatchEvent(new Event("resize")); }, diff --git a/src/scripts/ui/settings.ts b/src/scripts/ui/settings.ts index f53689a1b..f98475f18 100644 --- a/src/scripts/ui/settings.ts +++ b/src/scripts/ui/settings.ts @@ -3,11 +3,13 @@ import { api } from "../api"; import { ComfyDialog } from "./dialog"; import type { ComfyApp } from "../app"; import type { Setting, SettingParams } from "@/types/settingTypes"; +import { useSettingStore } from "@/stores/settingStore"; export class ComfySettingsDialog extends ComfyDialog { app: ComfyApp; settingsValues: any; settingsLookup: Record; + settingsParamLookup: Record; constructor(app: ComfyApp) { super(); @@ -15,6 +17,7 @@ export class ComfySettingsDialog extends ComfyDialog { this.app = app; this.settingsValues = {}; this.settingsLookup = {}; + this.settingsParamLookup = {}; this.element = $el( "dialog", { @@ -55,6 +58,14 @@ export class ComfySettingsDialog extends ComfyDialog { } #dispatchChange(id: string, value: T, oldValue?: T) { + // Keep the settingStore updated. Not using `store.set` as it would trigger + // setSettingValue again. + // `load` re-dispatch the change for any settings added before load so + // settingStore is always up to date. + if (this.app.vueAppReady) { + useSettingStore().settingValues[id] = value; + } + this.dispatchEvent( new CustomEvent(id + ".change", { detail: { @@ -99,6 +110,11 @@ export class ComfySettingsDialog extends ComfyDialog { return value ?? defaultValue; } + getSettingDefaultValue(id: string) { + const param = this.settingsParamLookup[id]; + return param?.defaultValue; + } + async setSettingValueAsync(id: string, value: any) { const json = JSON.stringify(value); localStorage["Comfy.Settings." + id] = json; // backwards compatibility for extensions keep setting in storage @@ -165,8 +181,10 @@ export class ComfySettingsDialog extends ComfyDialog { // Trigger initial setting of value if (!skipOnChange) { onChange?.(value, undefined); + this.#dispatchChange(id, value); } + this.settingsParamLookup[id] = params; this.settingsLookup[id] = { id, onChange, diff --git a/src/stores/nodeDefStore.ts b/src/stores/nodeDefStore.ts new file mode 100644 index 000000000..e5539678d --- /dev/null +++ b/src/stores/nodeDefStore.ts @@ -0,0 +1,68 @@ +import { ComfyNodeDef } from "@/types/apiTypes"; +import { defineStore } from "pinia"; + +export const SYSTEM_NODE_DEFS: ComfyNodeDef[] = [ + { + name: "PrimitiveNode", + display_name: "Primitive", + category: "utils", + input: { required: {}, optional: {} }, + output: ["*"], + output_name: ["connect to widget input"], + output_is_list: [false], + python_module: "nodes", + description: "Primitive values like numbers, strings, and booleans.", + }, + { + name: "Reroute", + display_name: "Reroute", + category: "utils", + input: { required: { "": ["*"] }, optional: {} }, + output: ["*"], + output_name: [""], + output_is_list: [false], + python_module: "nodes", + description: "Reroute the connection to another node.", + }, + { + name: "Note", + display_name: "Note", + category: "utils", + input: { required: {}, optional: {} }, + output: [], + output_name: [], + output_is_list: [], + python_module: "nodes", + description: "Node that add notes to your project", + }, +]; + +const SYSTEM_NODE_DEFS_BY_NAME = SYSTEM_NODE_DEFS.reduce((acc, nodeDef) => { + acc[nodeDef.name] = nodeDef; + return acc; +}, {}) as Record; + +interface State { + nodeDefsByName: Record; +} + +export const useNodeDefStore = defineStore("nodeDef", { + state: (): State => ({ + nodeDefsByName: SYSTEM_NODE_DEFS_BY_NAME, + }), + getters: { + nodeDefs(state) { + return Object.values(state.nodeDefsByName); + }, + }, + actions: { + addNodeDef(nodeDef: ComfyNodeDef) { + this.nodeDefsByName[nodeDef.name] = nodeDef; + }, + addNodeDefs(nodeDefs: ComfyNodeDef[]) { + for (const nodeDef of nodeDefs) { + this.addNodeDef(nodeDef); + } + }, + }, +}); diff --git a/src/stores/settingStore.ts b/src/stores/settingStore.ts new file mode 100644 index 000000000..f755aa695 --- /dev/null +++ b/src/stores/settingStore.ts @@ -0,0 +1,41 @@ +/** + * TODO: Migrate scripts/ui/settings.ts here + * + * Currently the reactive settings act as a proxy of the legacy settings. + * Every time a setting is changed, the settingStore dispatch the change to the + * legacy settings. Every time the legacy settings are changed, the legacy + * settings directly updates the settingStore.settingValues. + */ + +import { app } from "@/scripts/app"; +import { ComfySettingsDialog } from "@/scripts/ui/settings"; +import { defineStore } from "pinia"; + +interface State { + settingValues: Record; +} + +export const useSettingStore = defineStore("setting", { + state: (): State => ({ + settingValues: {}, + }), + actions: { + addSettings(settings: ComfySettingsDialog) { + for (const id in settings.settingsLookup) { + const value = settings.getSettingValue(id); + this.settingValues[id] = value; + } + }, + + set(key: string, value: any) { + this.settingValues[key] = value; + app.ui.settings.setSettingValue(key, value); + }, + + get(key: string) { + return ( + this.settingValues[key] ?? app.ui.settings.getSettingDefaultValue(key) + ); + }, + }, +});