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)
+ );
+ },
+ },
+});