Merge remote-tracking branch 'origin/main' into feat/new-workflow-templates

This commit is contained in:
Johnpaul
2025-09-05 20:22:26 +01:00
614 changed files with 37783 additions and 9017 deletions

View File

@@ -1,4 +1,5 @@
import axios from 'axios'
import { get } from 'es-toolkit/compat'
import defaultClientFeatureFlags from '@/config/clientFeatureFlags.json'
import type {
@@ -35,6 +36,7 @@ import type {
NodeId
} from '@/schemas/comfyWorkflowSchema'
import type { ComfyNodeDef } from '@/schemas/nodeDefSchema'
import { useToastStore } from '@/stores/toastStore'
import type { NodeExecutionId } from '@/types/nodeIdentification'
import { WorkflowTemplates } from '@/types/workflowTemplateTypes'
@@ -1037,6 +1039,56 @@ export class ComfyApi extends EventTarget {
return (await axios.get(this.internalURL('/folder_paths'))).data
}
/* Frees memory by unloading models and optionally freeing execution cache
* @param {Object} options - The options object
* @param {boolean} options.freeExecutionCache - If true, also frees execution cache
*/
async freeMemory(options: { freeExecutionCache: boolean }) {
try {
let mode = ''
if (options.freeExecutionCache) {
mode = '{"unload_models": true, "free_memory": true}'
} else {
mode = '{"unload_models": true}'
}
const res = await this.fetchApi(`/free`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: mode
})
if (res.status === 200) {
if (options.freeExecutionCache) {
useToastStore().add({
severity: 'success',
summary: 'Models and Execution Cache have been cleared.',
life: 3000
})
} else {
useToastStore().add({
severity: 'success',
summary: 'Models have been unloaded.',
life: 3000
})
}
} else {
useToastStore().add({
severity: 'error',
summary:
'Unloading of models failed. Installed ComfyUI may be an outdated version.',
life: 5000
})
}
} catch (error) {
useToastStore().add({
severity: 'error',
summary: 'An error occurred while trying to unload models.',
life: 5000
})
}
}
/**
* Gets the custom nodes i18n data from the server.
*
@@ -1048,21 +1100,21 @@ export class ComfyApi extends EventTarget {
/**
* Checks if the server supports a specific feature.
* @param featureName The name of the feature to check
* @param featureName The name of the feature to check (supports dot notation for nested values)
* @returns true if the feature is supported, false otherwise
*/
serverSupportsFeature(featureName: string): boolean {
return this.serverFeatureFlags[featureName] === true
return get(this.serverFeatureFlags, featureName) === true
}
/**
* Gets a server feature flag value.
* @param featureName The name of the feature to get
* @param featureName The name of the feature to get (supports dot notation for nested values)
* @param defaultValue The default value if the feature is not found
* @returns The feature value or default
*/
getServerFeature<T = unknown>(featureName: string, defaultValue?: T): T {
return (this.serverFeatureFlags[featureName] ?? defaultValue) as T
return get(this.serverFeatureFlags, featureName, defaultValue) as T
}
/**

View File

@@ -375,17 +375,3 @@ LGraphNode.prototype.addDOMWidget = function <
return widget
}
/**
* Prunes widgets that are no longer in the graph.
* @param nodes The nodes to prune widgets for.
*/
export const pruneWidgets = (nodes: LGraphNode[]) => {
const nodeSet = new Set(nodes)
const domWidgetStore = useDomWidgetStore()
for (const { widget } of domWidgetStore.widgetStates.values()) {
if (!nodeSet.has(widget.node)) {
domWidgetStore.unregisterWidget(widget.id)
}
}
}

View File

@@ -1,9 +1,9 @@
import { useChainCallback } from '@/composables/functional/useChainCallback'
import { useBooleanWidget } from '@/composables/widgets/useBooleanWidget'
import { useFloatWidget } from '@/composables/widgets/useFloatWidget'
import { useStringWidget } from '@/composables/widgets/useStringWidget'
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
import { useBooleanWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useBooleanWidget'
import { useFloatWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useFloatWidget'
import { useStringWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useStringWidget'
const StringWidget = useStringWidget()
const FloatWidget = useFloatWidget()

View File

@@ -17,6 +17,7 @@ export function clone<T>(obj: T): T {
}
/**
* @knipIgnoreUnusedButUsedByCustomNodes
* @deprecated Use `applyTextReplacements` from `@/utils/searchAndReplace` instead
* There are external callers to this function, so we need to keep it for now
*/
@@ -24,6 +25,7 @@ export function applyTextReplacements(app: ComfyApp, value: string): string {
return _applyTextReplacements(app.graph, value)
}
/** @knipIgnoreUnusedButUsedByCustomNodes */
export async function addStylesheet(
urlOrFile: string,
relativeTo?: string

View File

@@ -1,10 +1,3 @@
import { useBooleanWidget } from '@/composables/widgets/useBooleanWidget'
import { useComboWidget } from '@/composables/widgets/useComboWidget'
import { useFloatWidget } from '@/composables/widgets/useFloatWidget'
import { useImageUploadWidget } from '@/composables/widgets/useImageUploadWidget'
import { useIntWidget } from '@/composables/widgets/useIntWidget'
import { useMarkdownWidget } from '@/composables/widgets/useMarkdownWidget'
import { useStringWidget } from '@/composables/widgets/useStringWidget'
import { t } from '@/i18n'
import { type LGraphNode, isComboWidget } from '@/lib/litegraph/src/litegraph'
import type {
@@ -12,6 +5,22 @@ import type {
IComboWidget,
IStringWidget
} from '@/lib/litegraph/src/types/widgets'
import { useBooleanWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useBooleanWidget'
import { useChartWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useChartWidget'
import { useColorWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useColorWidget'
import { useComboWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useComboWidget'
import { useFileUploadWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useFileUploadWidget'
import { useFloatWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useFloatWidget'
import { useGalleriaWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useGalleriaWidget'
import { useImageCompareWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useImageCompareWidget'
import { useImageUploadWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useImageUploadWidget'
import { useIntWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useIntWidget'
import { useMarkdownWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useMarkdownWidget'
import { useMultiSelectWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useMultiSelectWidget'
import { useSelectButtonWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useSelectButtonWidget'
import { useStringWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useStringWidget'
import { useTextareaWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useTextareaWidget'
import { useTreeSelectWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useTreeSelectWidget'
import { transformInputSpecV1ToV2 } from '@/schemas/nodeDef/migration'
import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2'
import type { InputSpec } from '@/schemas/nodeDefSchema'
@@ -288,5 +297,14 @@ export const ComfyWidgets: Record<string, ComfyWidgetConstructor> = {
STRING: transformWidgetConstructorV2ToV1(useStringWidget()),
MARKDOWN: transformWidgetConstructorV2ToV1(useMarkdownWidget()),
COMBO: transformWidgetConstructorV2ToV1(useComboWidget()),
IMAGEUPLOAD: useImageUploadWidget()
IMAGEUPLOAD: useImageUploadWidget(),
FILEUPLOAD: transformWidgetConstructorV2ToV1(useFileUploadWidget()),
COLOR: transformWidgetConstructorV2ToV1(useColorWidget()),
IMAGECOMPARE: transformWidgetConstructorV2ToV1(useImageCompareWidget()),
TREESELECT: transformWidgetConstructorV2ToV1(useTreeSelectWidget()),
MULTISELECT: transformWidgetConstructorV2ToV1(useMultiSelectWidget()),
CHART: transformWidgetConstructorV2ToV1(useChartWidget()),
GALLERIA: transformWidgetConstructorV2ToV1(useGalleriaWidget()),
SELECTBUTTON: transformWidgetConstructorV2ToV1(useSelectButtonWidget()),
TEXTAREA: transformWidgetConstructorV2ToV1(useTextareaWidget())
}