import { defineStore } from 'pinia' import { computed, markRaw, ref } from 'vue' import type { ComfyExtension } from '@/types/comfy' /** * These extensions are always active, even if they are disabled in the setting. */ const ALWAYS_ENABLED_EXTENSIONS: readonly string[] = [] const ALWAYS_DISABLED_EXTENSIONS: readonly string[] = [ // pysssss.Locking is replaced by pin/unpin in ComfyUI core. // https://github.com/Comfy-Org/litegraph.js/pull/117 'pysssss.Locking', // pysssss.SnapToGrid is replaced by Comfy.Graph.AlwaysSnapToGrid in ComfyUI core. // pysssss.SnapToGrid tries to write global app.shiftDown state, which is no longer // allowed since v1.3.12. // https://github.com/Comfy-Org/ComfyUI_frontend/issues/1176 'pysssss.SnapToGrid', // Favicon status is implemented in ComfyUI core in v1.20. // https://github.com/Comfy-Org/ComfyUI_frontend/pull/3880 'pysssss.FaviconStatus', 'KJNodes.browserstatus' ] export const useExtensionStore = defineStore('extension', () => { // For legacy reasons, the name uniquely identifies an extension const extensionByName = ref>({}) const extensions = computed(() => Object.values(extensionByName.value)) // Not using computed because disable extension requires reloading of the page. // Dynamically update this list won't affect extensions that are already loaded. const disabledExtensionNames = ref>(new Set()) // Disabled extension names that are currently not in the extension list. // If a node pack is disabled in the backend, we shouldn't remove the configuration // of the frontend extension disable list, in case the node pack is re-enabled. const inactiveDisabledExtensionNames = computed(() => { return Array.from(disabledExtensionNames.value).filter( (name) => !(name in extensionByName.value) ) }) const isExtensionInstalled = (name: string) => name in extensionByName.value const isExtensionEnabled = (name: string) => !disabledExtensionNames.value.has(name) const enabledExtensions = computed(() => { return extensions.value.filter((ext) => isExtensionEnabled(ext.name)) }) function isExtensionReadOnly(name: string) { return ( ALWAYS_DISABLED_EXTENSIONS.includes(name) || ALWAYS_ENABLED_EXTENSIONS.includes(name) ) } function registerExtension(extension: ComfyExtension) { if (!extension.name) { throw new Error("Extensions must have a 'name' property.") } if (extensionByName.value[extension.name]) { throw new Error(`Extension named '${extension.name}' already registered.`) } if (disabledExtensionNames.value.has(extension.name)) { console.warn(`Extension ${extension.name} is disabled.`) } extensionByName.value[extension.name] = markRaw(extension) } function loadDisabledExtensionNames(names: string[]) { disabledExtensionNames.value = new Set(names) for (const name of ALWAYS_DISABLED_EXTENSIONS) { disabledExtensionNames.value.add(name) } for (const name of ALWAYS_ENABLED_EXTENSIONS) { disabledExtensionNames.value.delete(name) } } /** * Core extensions are extensions that are defined in the core package. * See /extensions/core/index.ts for the list. */ const coreExtensionNames = ref([]) function captureCoreExtensions() { coreExtensionNames.value = extensions.value.map((ext) => ext.name) } function isCoreExtension(name: string) { return coreExtensionNames.value.includes(name) } const hasThirdPartyExtensions = computed(() => { return extensions.value.some((ext) => !isCoreExtension(ext.name)) }) return { extensions, enabledExtensions, inactiveDisabledExtensionNames, isExtensionInstalled, isExtensionEnabled, isExtensionReadOnly, registerExtension, loadDisabledExtensionNames, captureCoreExtensions, isCoreExtension, hasThirdPartyExtensions } })