> =
+ Symbol('assetKind')
diff --git a/src/renderer/extensions/vueNodes/widgets/components/layout/WidgetLayoutField.vue b/src/renderer/extensions/vueNodes/widgets/components/layout/WidgetLayoutField.vue
index e29e09437..22653dd40 100644
--- a/src/renderer/extensions/vueNodes/widgets/components/layout/WidgetLayoutField.vue
+++ b/src/renderer/extensions/vueNodes/widgets/components/layout/WidgetLayoutField.vue
@@ -17,7 +17,7 @@ defineProps<{
{{ widget.label || widget.name }}
diff --git a/src/renderer/extensions/vueNodes/widgets/components/layout/index.ts b/src/renderer/extensions/vueNodes/widgets/components/layout/index.ts
index 2ad3a4858..3fe46f52e 100644
--- a/src/renderer/extensions/vueNodes/widgets/components/layout/index.ts
+++ b/src/renderer/extensions/vueNodes/widgets/components/layout/index.ts
@@ -2,15 +2,13 @@ import { cn } from '@/utils/tailwindUtil'
export const WidgetInputBaseClass = cn([
// Background
- 'bg-zinc-500/10',
+ 'bg-node-component-widget-input-surface',
+ 'text-node-component-widget-input',
// Outline
'border-none',
- 'outline',
- 'outline-1',
- 'outline-offset-[-1px]',
- 'outline-zinc-300/10',
+ 'outline outline-offset-[-1px] outline-zinc-300/10',
// Rounded
- '!rounded-lg',
+ 'rounded-lg',
// Hover
'hover:outline-blue-500/80'
])
diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useBooleanWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useBooleanWidget.ts
index b2e0fbd01..242afe54a 100644
--- a/src/renderer/extensions/vueNodes/widgets/composables/useBooleanWidget.ts
+++ b/src/renderer/extensions/vueNodes/widgets/composables/useBooleanWidget.ts
@@ -1,8 +1,6 @@
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
-import {
- type InputSpec,
- isBooleanInputSpec
-} from '@/schemas/nodeDef/nodeDefSchemaV2'
+import { isBooleanInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
+import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
export const useBooleanWidget = () => {
diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useChartWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useChartWidget.ts
index 4c94155a6..3096ac3b8 100644
--- a/src/renderer/extensions/vueNodes/widgets/composables/useChartWidget.ts
+++ b/src/renderer/extensions/vueNodes/widgets/composables/useChartWidget.ts
@@ -1,9 +1,9 @@
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { IChartWidget } from '@/lib/litegraph/src/types/widgets'
-import {
- type ChartInputSpec,
- type InputSpec as InputSpecV2,
- isChartInputSpec
+import { isChartInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
+import type {
+ ChartInputSpec,
+ InputSpec as InputSpecV2
} from '@/schemas/nodeDef/nodeDefSchemaV2'
import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useChatHistoryWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useChatHistoryWidget.ts
index 53fe6ce6e..2c5700045 100644
--- a/src/renderer/extensions/vueNodes/widgets/composables/useChatHistoryWidget.ts
+++ b/src/renderer/extensions/vueNodes/widgets/composables/useChatHistoryWidget.ts
@@ -3,11 +3,8 @@ import { ref } from 'vue'
import ChatHistoryWidget from '@/components/graph/widgets/ChatHistoryWidget.vue'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
-import {
- ComponentWidgetImpl,
- type ComponentWidgetStandardProps,
- addWidget
-} from '@/scripts/domWidget'
+import { ComponentWidgetImpl, addWidget } from '@/scripts/domWidget'
+import type { ComponentWidgetStandardProps } from '@/scripts/domWidget'
import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
type ChatHistoryCustomProps = Omit<
diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.ts
index 59705458c..43441530d 100644
--- a/src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.ts
+++ b/src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.ts
@@ -6,23 +6,22 @@ import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { isAssetWidget, isComboWidget } from '@/lib/litegraph/src/litegraph'
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
import { useAssetBrowserDialog } from '@/platform/assets/composables/useAssetBrowserDialog'
+import {
+ assetFilenameSchema,
+ assetItemSchema
+} from '@/platform/assets/schemas/assetSchema'
import { assetService } from '@/platform/assets/services/assetService'
import { useSettingStore } from '@/platform/settings/settingStore'
import { transformInputSpecV2ToV1 } from '@/schemas/nodeDef/migration'
-import type { ComboInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
-import {
- type InputSpec,
- isComboInputSpec
+import { isComboInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
+import type {
+ ComboInputSpec,
+ InputSpec
} from '@/schemas/nodeDef/nodeDefSchemaV2'
-import {
- type BaseDOMWidget,
- ComponentWidgetImpl,
- addWidget
-} from '@/scripts/domWidget'
-import {
- type ComfyWidgetConstructorV2,
- addValueControlWidgets
-} from '@/scripts/widgets'
+import { ComponentWidgetImpl, addWidget } from '@/scripts/domWidget'
+import type { BaseDOMWidget } from '@/scripts/domWidget'
+import { addValueControlWidgets } from '@/scripts/widgets'
+import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
import { useRemoteWidget } from './useRemoteWidget'
@@ -62,13 +61,9 @@ const addComboWidget = (
): IBaseWidget => {
const settingStore = useSettingStore()
const isUsingAssetAPI = settingStore.get('Comfy.Assets.UseAssetAPI')
- const isEligible = assetService.isAssetBrowserEligible(
- inputSpec.name,
- node.comfyClass || ''
- )
+ const isEligible = assetService.isAssetBrowserEligible(node.comfyClass)
if (isUsingAssetAPI && isEligible) {
- // Get the default value for the button text (currently selected model)
const currentValue = getDefaultValue(inputSpec)
const displayLabel = currentValue ?? t('widgets.selectModel')
@@ -86,11 +81,40 @@ const addComboWidget = (
nodeType: node.comfyClass || '',
inputName: inputSpec.name,
currentValue: widget.value,
- onAssetSelected: (filename: string) => {
+ onAssetSelected: (asset) => {
+ const validatedAsset = assetItemSchema.safeParse(asset)
+
+ if (!validatedAsset.success) {
+ console.error(
+ 'Invalid asset item:',
+ validatedAsset.error.errors,
+ 'Received:',
+ asset
+ )
+ return
+ }
+
+ const filename = validatedAsset.data.user_metadata?.filename
+ const validatedFilename = assetFilenameSchema.safeParse(filename)
+
+ if (!validatedFilename.success) {
+ console.error(
+ 'Invalid asset filename:',
+ validatedFilename.error.errors,
+ 'for asset:',
+ validatedAsset.data.id
+ )
+ return
+ }
+
const oldValue = widget.value
- widget.value = filename
- // Using onWidgetChanged prevents a callback race where asset selection could reopen the dialog
- node.onWidgetChanged?.(widget.name, filename, oldValue, widget)
+ widget.value = validatedFilename.data
+ node.onWidgetChanged?.(
+ widget.name,
+ validatedFilename.data,
+ oldValue,
+ widget
+ )
}
})
}
diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.ts
index ac0f9e5e9..3b3d8f95a 100644
--- a/src/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.ts
+++ b/src/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.ts
@@ -3,10 +3,8 @@ import { clamp } from 'es-toolkit/compat'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { INumericWidget } from '@/lib/litegraph/src/types/widgets'
import { useSettingStore } from '@/platform/settings/settingStore'
-import {
- type InputSpec,
- isFloatInputSpec
-} from '@/schemas/nodeDef/nodeDefSchemaV2'
+import { isFloatInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
+import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
function onFloatValueChange(this: INumericWidget, v: number) {
diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
index d6e89af18..49024d61f 100644
--- a/src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
+++ b/src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
@@ -1,9 +1,5 @@
-import {
- BaseWidget,
- type CanvasPointer,
- type LGraphNode,
- LiteGraph
-} from '@/lib/litegraph/src/litegraph'
+import { BaseWidget, LiteGraph } from '@/lib/litegraph/src/litegraph'
+import type { CanvasPointer, LGraphNode } from '@/lib/litegraph/src/litegraph'
import type {
IBaseWidget,
IWidgetOptions
@@ -19,7 +15,8 @@ import { is_all_same_aspect_ratio } from '@/utils/imageUtil'
const renderPreview = (
ctx: CanvasRenderingContext2D,
node: LGraphNode,
- shiftY: number
+ shiftY: number,
+ computedHeight: number | undefined
) => {
const canvas = useCanvasStore().getCanvas()
const mouse = canvas.graph_mouse
@@ -46,7 +43,7 @@ const renderPreview = (
const allowImageSizeDraw = settingStore.get('Comfy.Node.AllowImageSizeDraw')
const IMAGE_TEXT_SIZE_TEXT_HEIGHT = allowImageSizeDraw ? 15 : 0
const dw = node.size[0]
- const dh = node.size[1] - shiftY - IMAGE_TEXT_SIZE_TEXT_HEIGHT
+ const dh = computedHeight ? computedHeight - IMAGE_TEXT_SIZE_TEXT_HEIGHT : 0
if (imageIndex == null) {
// No image selected; draw thumbnails of all
@@ -260,7 +257,7 @@ class ImagePreviewWidget extends BaseWidget {
}
override drawWidget(ctx: CanvasRenderingContext2D): void {
- renderPreview(ctx, this.node, this.y)
+ renderPreview(ctx, this.node, this.y, this.computedHeight)
}
override onPointerDown(pointer: CanvasPointer, node: LGraphNode): boolean {
diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.ts
index 21cc0512f..ef2973837 100644
--- a/src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.ts
+++ b/src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.ts
@@ -2,14 +2,10 @@ import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { INumericWidget } from '@/lib/litegraph/src/types/widgets'
import { useSettingStore } from '@/platform/settings/settingStore'
import { transformInputSpecV2ToV1 } from '@/schemas/nodeDef/migration'
-import {
- type InputSpec,
- isIntInputSpec
-} from '@/schemas/nodeDef/nodeDefSchemaV2'
-import {
- type ComfyWidgetConstructorV2,
- addValueControlWidget
-} from '@/scripts/widgets'
+import { isIntInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
+import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
+import { addValueControlWidget } from '@/scripts/widgets'
+import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
function onValueChange(this: INumericWidget, v: number) {
// For integers, always round to the nearest step
diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useProgressTextWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useProgressTextWidget.ts
index dd3f1d0c2..2e7f35f86 100644
--- a/src/renderer/extensions/vueNodes/widgets/composables/useProgressTextWidget.ts
+++ b/src/renderer/extensions/vueNodes/widgets/composables/useProgressTextWidget.ts
@@ -3,11 +3,8 @@ import { ref } from 'vue'
import TextPreviewWidget from '@/components/graph/widgets/TextPreviewWidget.vue'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
-import {
- ComponentWidgetImpl,
- type ComponentWidgetStandardProps,
- addWidget
-} from '@/scripts/domWidget'
+import { ComponentWidgetImpl, addWidget } from '@/scripts/domWidget'
+import type { ComponentWidgetStandardProps } from '@/scripts/domWidget'
import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
type TextPreviewCustomProps = Omit<
diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts
index 41db11808..bfe6f71ef 100644
--- a/src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts
+++ b/src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts
@@ -1,8 +1,7 @@
import axios from 'axios'
import { useChainCallback } from '@/composables/functional/useChainCallback'
-import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
-import type { IWidget } from '@/lib/litegraph/src/litegraph'
+import type { IWidget, LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { RemoteWidgetConfig } from '@/schemas/nodeDefSchema'
import { api } from '@/scripts/api'
diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useStringWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useStringWidget.ts
index c449295c4..1ace7e9e2 100644
--- a/src/renderer/extensions/vueNodes/widgets/composables/useStringWidget.ts
+++ b/src/renderer/extensions/vueNodes/widgets/composables/useStringWidget.ts
@@ -1,9 +1,7 @@
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { useSettingStore } from '@/platform/settings/settingStore'
-import {
- type InputSpec,
- isStringInputSpec
-} from '@/schemas/nodeDef/nodeDefSchemaV2'
+import { isStringInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
+import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
import { app } from '@/scripts/app'
import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
diff --git a/src/router.ts b/src/router.ts
index f28b46030..cc51f7715 100644
--- a/src/router.ts
+++ b/src/router.ts
@@ -1,4 +1,3 @@
-import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
import {
createRouter,
createWebHashHistory,
@@ -13,18 +12,6 @@ import { isElectron } from './utils/envUtil'
const isFileProtocol = window.location.protocol === 'file:'
const basePath = isElectron() ? '/' : window.location.pathname
-const guardElectronAccess = (
- _to: RouteLocationNormalized,
- _from: RouteLocationNormalized,
- next: NavigationGuardNext
-) => {
- if (isElectron()) {
- next()
- } else {
- next('/')
- }
-}
-
const router = createRouter({
history: isFileProtocol
? createWebHashHistory()
@@ -55,72 +42,6 @@ const router = createRouter({
path: 'user-select',
name: 'UserSelectView',
component: () => import('@/views/UserSelectView.vue')
- },
- {
- path: 'server-start',
- name: 'ServerStartView',
- component: () => import('@/views/ServerStartView.vue'),
- beforeEnter: guardElectronAccess
- },
- {
- path: 'install',
- name: 'InstallView',
- component: () => import('@/views/InstallView.vue'),
- beforeEnter: guardElectronAccess
- },
- {
- path: 'welcome',
- name: 'WelcomeView',
- component: () => import('@/views/WelcomeView.vue'),
- beforeEnter: guardElectronAccess
- },
- {
- path: 'not-supported',
- name: 'NotSupportedView',
- component: () => import('@/views/NotSupportedView.vue'),
- beforeEnter: guardElectronAccess
- },
- {
- path: 'download-git',
- name: 'DownloadGitView',
- component: () => import('@/views/DownloadGitView.vue'),
- beforeEnter: guardElectronAccess
- },
- {
- path: 'manual-configuration',
- name: 'ManualConfigurationView',
- component: () => import('@/views/ManualConfigurationView.vue'),
- beforeEnter: guardElectronAccess
- },
- {
- path: '/metrics-consent',
- name: 'MetricsConsentView',
- component: () => import('@/views/MetricsConsentView.vue'),
- beforeEnter: guardElectronAccess
- },
- {
- path: 'desktop-start',
- name: 'DesktopStartView',
- component: () => import('@/views/DesktopStartView.vue'),
- beforeEnter: guardElectronAccess
- },
- {
- path: 'maintenance',
- name: 'MaintenanceView',
- component: () => import('@/views/MaintenanceView.vue'),
- beforeEnter: guardElectronAccess
- },
- {
- path: 'desktop-update',
- name: 'DesktopUpdateView',
- component: () => import('@/views/DesktopUpdateView.vue'),
- beforeEnter: guardElectronAccess
- },
- {
- path: 'desktop-dialog/:dialogId',
- name: 'DesktopDialogView',
- component: () => import('@/views/DesktopDialogView.vue'),
- beforeEnter: guardElectronAccess
}
]
}
diff --git a/src/scripts/app.ts b/src/scripts/app.ts
index 49423436d..8522aeb2c 100644
--- a/src/scripts/app.ts
+++ b/src/scripts/app.ts
@@ -1,13 +1,15 @@
+import { useResizeObserver } from '@vueuse/core'
import _ from 'es-toolkit/compat'
import type { ToastMessageOptions } from 'primevue/toast'
-import { reactive } from 'vue'
+import { reactive, unref } from 'vue'
+import { shallowRef } from 'vue'
import { useCanvasPositionConversion } from '@/composables/element/useCanvasPositionConversion'
+import { registerProxyWidgets } from '@/core/graph/subgraph/proxyWidget'
import { st, t } from '@/i18n'
import {
LGraph,
LGraphCanvas,
- LGraphEventMode,
LGraphNode,
LiteGraph
} from '@/lib/litegraph/src/litegraph'
@@ -58,12 +60,10 @@ import { useModelStore } from '@/stores/modelStore'
import { SYSTEM_NODE_DEFS, useNodeDefStore } from '@/stores/nodeDefStore'
import { useSubgraphStore } from '@/stores/subgraphStore'
import { useWidgetStore } from '@/stores/widgetStore'
-import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
import { useWorkspaceStore } from '@/stores/workspaceStore'
import type { ComfyExtension, MissingNodeType } from '@/types/comfy'
import { type ExtensionManager } from '@/types/extensionTypes'
import type { NodeExecutionId } from '@/types/nodeIdentification'
-import { type ColorAdjustOptions, adjustColor } from '@/utils/colorUtil'
import { graphToPrompt } from '@/utils/executionUtil'
import { forEachNode } from '@/utils/graphTraversalUtil'
import {
@@ -129,7 +129,7 @@ export class ComfyApp {
/**
* List of entries to queue
*/
- #queueItems: {
+ private queueItems: {
number: number
batchCount: number
queueNodeIds?: NodeExecutionId[]
@@ -137,7 +137,7 @@ export class ComfyApp {
/**
* If the queue is currently being processed
*/
- #processingQueue: boolean = false
+ private processingQueue: boolean = false
/**
* Content Clipboard
@@ -176,12 +176,15 @@ export class ComfyApp {
// @ts-expect-error fixme ts strict error
canvas: LGraphCanvas
dragOverNode: LGraphNode | null = null
- // @ts-expect-error fixme ts strict error
- canvasEl: HTMLCanvasElement
+ readonly canvasElRef = shallowRef
()
+ get canvasEl() {
+ // TODO: Fix possibly undefined reference
+ return unref(this.canvasElRef)!
+ }
- #configuringGraphLevel: number = 0
+ private configuringGraphLevel: number = 0
get configuringGraph() {
- return this.#configuringGraphLevel > 0
+ return this.configuringGraphLevel > 0
}
// @ts-expect-error fixme ts strict error
ctx: CanvasRenderingContext2D
@@ -191,11 +194,10 @@ export class ComfyApp {
bodyBottom: HTMLElement
canvasContainer: HTMLElement
menu: ComfyAppMenu
- bypassBgColor: string
// Set by Comfy.Clipspace extension
openClipspace: () => void = () => {}
- #positionConversion?: {
+ private positionConversion?: {
clientPosToCanvasPos: (pos: Vector2) => Vector2
canvasPosToClientPos: (pos: Vector2) => Vector2
}
@@ -296,7 +298,6 @@ export class ComfyApp {
this.canvasContainer = $el('div.graph-canvas-container')
this.menu = new ComfyAppMenu(this)
- this.bypassBgColor = '#FF00FF'
/**
* Stores the execution output data for each node
@@ -517,7 +518,7 @@ export class ComfyApp {
/**
* Adds a handler allowing drag+drop of files onto the window to load workflows
*/
- #addDropHandler() {
+ private addDropHandler() {
// Get prompt from dropped PNG or json
document.addEventListener('drop', async (event) => {
try {
@@ -593,7 +594,7 @@ export class ComfyApp {
/**
* Handle keypress
*/
- #addProcessKeyHandler() {
+ private addProcessKeyHandler() {
const origProcessKey = LGraphCanvas.prototype.processKey
LGraphCanvas.prototype.processKey = function (e: KeyboardEvent) {
if (!this.graph) return
@@ -635,61 +636,14 @@ export class ComfyApp {
}
// Fall through to Litegraph defaults
- // @ts-expect-error fixme ts strict error
- return origProcessKey.apply(this, arguments)
- }
- }
-
- #addDrawNodeHandler() {
- const origDrawNode = LGraphCanvas.prototype.drawNode
- LGraphCanvas.prototype.drawNode = function (node) {
- const editor_alpha = this.editor_alpha
- const old_color = node.color
- const old_bgcolor = node.bgcolor
-
- if (node.mode === LGraphEventMode.NEVER) {
- this.editor_alpha = 0.4
- }
-
- let bgColor: string
- if (node.mode === LGraphEventMode.BYPASS) {
- bgColor = app.bypassBgColor
- this.editor_alpha = 0.2
- } else {
- bgColor = old_bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR
- }
-
- const adjustments: ColorAdjustOptions = {}
-
- const opacity = useSettingStore().get('Comfy.Node.Opacity')
- if (opacity) adjustments.opacity = opacity
-
- if (useColorPaletteStore().completedActivePalette.light_theme) {
- if (old_bgcolor) adjustments.lightness = 0.5
-
- // Lighten title bar of colored nodes on light theme
- if (old_color) {
- node.color = adjustColor(old_color, { lightness: 0.5 })
- }
- }
-
- node.bgcolor = adjustColor(bgColor, adjustments)
-
- // @ts-expect-error fixme ts strict error
- const res = origDrawNode.apply(this, arguments)
-
- this.editor_alpha = editor_alpha
- node.color = old_color
- node.bgcolor = old_bgcolor
-
- return res
+ return origProcessKey.apply(this, [e])
}
}
/**
* Handles updates from the API socket
*/
- #addApiUpdateHandlers() {
+ private addApiUpdateHandlers() {
api.addEventListener('status', ({ detail }) => {
this.ui.setStatus(detail)
})
@@ -763,15 +717,15 @@ export class ComfyApp {
}
/** Flag that the graph is configuring to prevent nodes from running checks while its still loading */
- #addConfigureHandler() {
+ private addConfigureHandler() {
const app = this
const configure = LGraph.prototype.configure
LGraph.prototype.configure = function (...args) {
- app.#configuringGraphLevel++
+ app.configuringGraphLevel++
try {
return configure.apply(this, args)
} finally {
- app.#configuringGraphLevel--
+ app.configuringGraphLevel--
}
}
}
@@ -808,16 +762,15 @@ export class ComfyApp {
// @ts-expect-error fixme ts strict error
this.canvasContainer = document.getElementById('graph-canvas-container')
- this.canvasEl = canvasEl
- this.resizeCanvas()
+ this.canvasElRef.value = canvasEl
await useWorkspaceStore().workflow.syncWorkflows()
await useSubgraphStore().fetchSubgraphs()
await useExtensionService().loadExtensions()
- this.#addProcessKeyHandler()
- this.#addConfigureHandler()
- this.#addApiUpdateHandlers()
+ this.addProcessKeyHandler()
+ this.addConfigureHandler()
+ this.addApiUpdateHandlers()
const graph = new LGraph()
@@ -879,43 +832,41 @@ export class ComfyApp {
}
}
)
+ registerProxyWidgets(this.canvas)
this.graph.start()
// Ensure the canvas fills the window
- this.resizeCanvas()
- window.addEventListener('resize', () => this.resizeCanvas())
- const ro = new ResizeObserver(() => this.resizeCanvas())
- ro.observe(this.bodyTop)
- ro.observe(this.bodyLeft)
- ro.observe(this.bodyRight)
- ro.observe(this.bodyBottom)
+ useResizeObserver(this.canvasElRef, ([canvasEl]) => {
+ if (canvasEl.target instanceof HTMLCanvasElement) {
+ this.resizeCanvas(canvasEl.target)
+ }
+ })
await useExtensionService().invokeExtensionsAsync('init')
await this.registerNodes()
- this.#addDrawNodeHandler()
- this.#addDropHandler()
+ this.addDropHandler()
await useExtensionService().invokeExtensionsAsync('setup')
- this.#positionConversion = useCanvasPositionConversion(
+ this.positionConversion = useCanvasPositionConversion(
this.canvasContainer,
this.canvas
)
}
- resizeCanvas() {
+ private resizeCanvas(canvas: HTMLCanvasElement) {
// Limit minimal scale to 1, see https://github.com/comfyanonymous/ComfyUI/pull/845
const scale = Math.max(window.devicePixelRatio, 1)
// Clear fixed width and height while calculating rect so it uses 100% instead
- this.canvasEl.height = this.canvasEl.width = NaN
- const { width, height } = this.canvasEl.getBoundingClientRect()
- this.canvasEl.width = Math.round(width * scale)
- this.canvasEl.height = Math.round(height * scale)
+ canvas.height = canvas.width = NaN
+ const { width, height } = canvas.getBoundingClientRect()
+ canvas.width = Math.round(width * scale)
+ canvas.height = Math.round(height * scale)
// @ts-expect-error fixme ts strict error
- this.canvasEl.getContext('2d').scale(scale, scale)
+ canvas.getContext('2d').scale(scale, scale)
this.canvas?.draw(true, true)
}
@@ -963,7 +914,7 @@ export class ComfyApp {
nodeDefStore.updateNodeDefs(nodeDefArray)
}
- async #getNodeDefs(): Promise> {
+ async getNodeDefs(): Promise> {
const translateNodeDef = (def: ComfyNodeDefV1): ComfyNodeDefV1 => ({
...def,
display_name: st(
@@ -987,7 +938,7 @@ export class ComfyApp {
*/
async registerNodes() {
// Load node definitions from the backend
- const defs = await this.#getNodeDefs()
+ const defs = await this.getNodeDefs()
await this.registerNodesFromDefs(defs)
await useExtensionService().invokeExtensionsAsync('registerCustomNodes')
if (this.vueAppReady) {
@@ -1055,14 +1006,14 @@ export class ComfyApp {
localStorage.setItem('litegrapheditor_clipboard', old)
}
- #showMissingNodesError(missingNodeTypes: MissingNodeType[]) {
+ private showMissingNodesError(missingNodeTypes: MissingNodeType[]) {
if (useSettingStore().get('Comfy.Workflow.ShowMissingNodesWarning')) {
useDialogService().showLoadWorkflowWarning({ missingNodeTypes })
}
}
// @ts-expect-error fixme ts strict error
- #showMissingModelsError(missingModels, paths) {
+ private showMissingModelsError(missingModels, paths) {
if (useSettingStore().get('Comfy.Workflow.ShowMissingModelsWarning')) {
useDialogService().showMissingModelsWarning({
missingModels,
@@ -1295,11 +1246,11 @@ export class ComfyApp {
}
if (missingNodeTypes.length && showMissingNodesDialog) {
- this.#showMissingNodesError(missingNodeTypes)
+ this.showMissingNodesError(missingNodeTypes)
}
if (missingModels.length && showMissingModelsDialog) {
const paths = await api.getFolderPaths()
- this.#showMissingModelsError(missingModels, paths)
+ this.showMissingModelsError(missingModels, paths)
}
await useExtensionService().invokeExtensionsAsync(
'afterConfigureGraph',
@@ -1325,14 +1276,14 @@ export class ComfyApp {
batchCount: number = 1,
queueNodeIds?: NodeExecutionId[]
): Promise {
- this.#queueItems.push({ number, batchCount, queueNodeIds })
+ this.queueItems.push({ number, batchCount, queueNodeIds })
// Only have one action process the items so each one gets a unique seed correctly
- if (this.#processingQueue) {
+ if (this.processingQueue) {
return false
}
- this.#processingQueue = true
+ this.processingQueue = true
const executionStore = useExecutionStore()
executionStore.lastNodeErrors = null
@@ -1340,8 +1291,8 @@ export class ComfyApp {
let comfyOrgApiKey = useApiKeyAuthStore().getApiKey()
try {
- while (this.#queueItems.length) {
- const { number, batchCount, queueNodeIds } = this.#queueItems.pop()!
+ while (this.queueItems.length) {
+ const { number, batchCount, queueNodeIds } = this.queueItems.pop()!
for (let i = 0; i < batchCount; i++) {
// Allow widgets to run callbacks before a prompt has been queued
@@ -1406,7 +1357,7 @@ export class ComfyApp {
}
}
} finally {
- this.#processingQueue = false
+ this.processingQueue = false
}
api.dispatchCustomEvent('promptQueued', { number, batchCount })
return !executionStore.lastNodeErrors
@@ -1610,7 +1561,7 @@ export class ComfyApp {
(n) => !LiteGraph.registered_node_types[n.class_type]
)
if (missingNodeTypes.length) {
- this.#showMissingNodesError(missingNodeTypes.map((t) => t.class_type))
+ this.showMissingNodesError(missingNodeTypes.map((t) => t.class_type))
return
}
@@ -1729,7 +1680,7 @@ export class ComfyApp {
useToastStore().add(requestToastMessage)
}
- const defs = await this.#getNodeDefs()
+ const defs = await this.getNodeDefs()
for (const nodeId in defs) {
this.registerNodeDef(nodeId, defs[nodeId])
}
@@ -1805,17 +1756,17 @@ export class ComfyApp {
}
clientPosToCanvasPos(pos: Vector2): Vector2 {
- if (!this.#positionConversion) {
+ if (!this.positionConversion) {
throw new Error('clientPosToCanvasPos called before setup')
}
- return this.#positionConversion.clientPosToCanvasPos(pos)
+ return this.positionConversion.clientPosToCanvasPos(pos)
}
canvasPosToClientPos(pos: Vector2): Vector2 {
- if (!this.#positionConversion) {
+ if (!this.positionConversion) {
throw new Error('canvasPosToClientPos called before setup')
}
- return this.#positionConversion.canvasPosToClientPos(pos)
+ return this.positionConversion.canvasPosToClientPos(pos)
}
}
diff --git a/src/scripts/domWidget.ts b/src/scripts/domWidget.ts
index a94ddb2ac..a550a4145 100644
--- a/src/scripts/domWidget.ts
+++ b/src/scripts/domWidget.ts
@@ -173,6 +173,18 @@ abstract class BaseDOMWidgetImpl
)
ctx.fill()
ctx.fillStyle = originalFillStyle
+ } else if (this.promoted && this.isVisible()) {
+ ctx.save()
+ const adjustedMargin = this.margin - 1
+ ctx.beginPath()
+ ctx.strokeStyle = LiteGraph.WIDGET_PROMOTED_OUTLINE_COLOR
+ ctx.strokeRect(
+ adjustedMargin,
+ y + adjustedMargin,
+ widget_width - adjustedMargin * 2,
+ (this.computedHeight ?? widget_height) - 2 * adjustedMargin
+ )
+ ctx.restore()
}
this.options.onDraw?.(this)
}
diff --git a/src/scripts/ui/draggableList.ts b/src/scripts/ui/draggableList.ts
index 5f5db27fa..895d153b7 100644
--- a/src/scripts/ui/draggableList.ts
+++ b/src/scripts/ui/draggableList.ts
@@ -23,11 +23,8 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
-import { $el } from '../ui'
-
-$el('style', {
- parent: document.head,
- textContent: `
+const styleElement = document.createElement('style')
+styleElement.textContent = `
.draggable-item {
position: relative;
will-change: transform;
@@ -40,7 +37,7 @@ $el('style', {
z-index: 10;
}
`
-})
+document.head.append(styleElement)
export class DraggableList extends EventTarget {
listContainer
diff --git a/src/scripts/ui/menu/menu.css b/src/scripts/ui/menu/menu.css
index 33589de9c..31404df5a 100644
--- a/src/scripts/ui/menu/menu.css
+++ b/src/scripts/ui/menu/menu.css
@@ -17,7 +17,7 @@
}
.comfyui-button:active {
- box-shadow: inset 1px 1px 10px rgba(0, 0, 0, 0.5);
+ box-shadow: inset 1px 1px 10px rgb(0 0 0 / 0.5);
}
.comfyui-button:disabled {
@@ -39,13 +39,13 @@
/* Popup */
.comfyui-popup {
position: absolute;
- left: var(--left);
- right: var(--right);
top: var(--top);
+ right: var(--right);
bottom: var(--bottom);
+ left: var(--left);
z-index: 2000;
max-height: calc(100vh - var(--limit) - 10px);
- box-shadow: 3px 3px 5px 0 rgba(0, 0, 0, 0.3);
+ box-shadow: 3px 3px 5px 0 rgb(0 0 0 / 0.3);
}
.comfyui-popup:not(.open) {
diff --git a/src/scripts/widgets.ts b/src/scripts/widgets.ts
index e61edb509..dd75081af 100644
--- a/src/scripts/widgets.ts
+++ b/src/scripts/widgets.ts
@@ -1,4 +1,3 @@
-import '@/core/graph/subgraph/proxyWidget'
import { t } from '@/i18n'
import { type LGraphNode, isComboWidget } from '@/lib/litegraph/src/litegraph'
import type {
diff --git a/src/services/colorPaletteService.ts b/src/services/colorPaletteService.ts
index cf32ee3b9..bfbfeb420 100644
--- a/src/services/colorPaletteService.ts
+++ b/src/services/colorPaletteService.ts
@@ -2,16 +2,25 @@ import { toRaw } from 'vue'
import { fromZodError } from 'zod-validation-error'
import { useErrorHandling } from '@/composables/useErrorHandling'
-import { LGraphCanvas } from '@/lib/litegraph/src/litegraph'
-import { LiteGraph } from '@/lib/litegraph/src/litegraph'
+import { LGraphCanvas, LiteGraph } from '@/lib/litegraph/src/litegraph'
import { useSettingStore } from '@/platform/settings/settingStore'
-import type { Colors } from '@/schemas/colorPaletteSchema'
-import { type Palette, paletteSchema } from '@/schemas/colorPaletteSchema'
+import { paletteSchema } from '@/schemas/colorPaletteSchema'
+import type { Colors, Palette } from '@/schemas/colorPaletteSchema'
import { app } from '@/scripts/app'
import { downloadBlob, uploadFile } from '@/scripts/utils'
import { useNodeDefStore } from '@/stores/nodeDefStore'
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
+const THEME_PROPERTY_MAP = {
+ NODE_BOX_OUTLINE_COLOR: 'node-component-border',
+ NODE_DEFAULT_BGCOLOR: 'node-component-surface',
+ NODE_DEFAULT_BOXCOLOR: 'node-component-header-icon',
+ NODE_DEFAULT_COLOR: 'node-component-header-surface',
+ NODE_TITLE_COLOR: 'node-component-header',
+ WIDGET_BGCOLOR: 'node-component-widget-input-surface',
+ WIDGET_TEXT_COLOR: 'node-component-widget-input'
+} as const satisfies Partial>
+
export const useColorPaletteService = () => {
const colorPaletteStore = useColorPaletteStore()
const settingStore = useSettingStore()
@@ -78,15 +87,46 @@ export const useColorPaletteService = () => {
Object.assign(LGraphCanvas.link_type_colors, types, linkColorPalette)
}
+ function validThemeProp(
+ propertyMaybe: unknown
+ ): propertyMaybe is keyof typeof THEME_PROPERTY_MAP {
+ return (
+ (propertyMaybe as keyof typeof THEME_PROPERTY_MAP) in THEME_PROPERTY_MAP
+ )
+ }
+
+ function loadLitegraphForVueNodes(
+ palette: Colors['litegraph_base'],
+ colorPaletteId: string
+ ) {
+ if (!palette) return
+ const rootStyle = document.getElementById('vue-app')?.style
+ if (!rootStyle) return
+
+ for (const themeVar of Object.keys(THEME_PROPERTY_MAP)) {
+ if (!validThemeProp(themeVar)) {
+ continue
+ }
+ const cssVar = THEME_PROPERTY_MAP[themeVar]
+ if (colorPaletteId === 'dark' || colorPaletteId === 'light') {
+ rootStyle.removeProperty(`--${cssVar}`)
+ continue
+ }
+ const valueMaybe = palette[themeVar]
+ if (valueMaybe) {
+ rootStyle.setProperty(`--${cssVar}`, valueMaybe)
+ } else {
+ rootStyle.removeProperty(`--${cssVar}`)
+ }
+ }
+ }
+
/**
* Loads the LiteGraph color palette.
*
* @param liteGraphColorPalette - The palette to set.
*/
const loadLiteGraphColorPalette = (palette: Colors['litegraph_base']) => {
- // Sets special case colors
- app.bypassBgColor = palette.NODE_BYPASS_BGCOLOR
-
// Sets the colors of the LiteGraph objects
app.canvas.node_title_color = palette.NODE_TITLE_COLOR
app.canvas.default_link_color = palette.LINK_COLOR
@@ -123,20 +163,19 @@ export const useColorPaletteService = () => {
* @param comfyColorPalette - The palette to set.
*/
const loadComfyColorPalette = (comfyColorPalette: Colors['comfy_base']) => {
- if (comfyColorPalette) {
- const rootStyle = document.documentElement.style
- for (const [key, value] of Object.entries(comfyColorPalette)) {
- rootStyle.setProperty('--' + key, value)
- }
- const backgroundImage = settingStore.get('Comfy.Canvas.BackgroundImage')
- if (backgroundImage) {
- rootStyle.setProperty(
- '--bg-img',
- `url('${backgroundImage}') no-repeat center /cover`
- )
- } else {
- rootStyle.removeProperty('--bg-img')
- }
+ if (!comfyColorPalette) return
+ const rootStyle = document.documentElement.style
+ for (const [key, value] of Object.entries(comfyColorPalette)) {
+ rootStyle.setProperty('--' + key, value)
+ }
+ const backgroundImage = settingStore.get('Comfy.Canvas.BackgroundImage')
+ if (backgroundImage) {
+ rootStyle.setProperty(
+ '--bg-img',
+ `url('${backgroundImage}') no-repeat center /cover`
+ )
+ } else {
+ rootStyle.removeProperty('--bg-img')
}
}
@@ -154,6 +193,10 @@ export const useColorPaletteService = () => {
const completedPalette = colorPaletteStore.completePalette(colorPalette)
loadLinkColorPalette(completedPalette.colors.node_slot)
loadLiteGraphColorPalette(completedPalette.colors.litegraph_base)
+ loadLitegraphForVueNodes(
+ completedPalette.colors.litegraph_base,
+ colorPaletteId
+ )
loadComfyColorPalette(completedPalette.colors.comfy_base)
app.canvas.setDirty(true, true)
diff --git a/src/services/customerEventsService.ts b/src/services/customerEventsService.ts
index 32eb9d6df..0359f4c3a 100644
--- a/src/services/customerEventsService.ts
+++ b/src/services/customerEventsService.ts
@@ -5,8 +5,7 @@ import { useI18n } from 'vue-i18n'
import { COMFY_API_BASE_URL } from '@/config/comfyApi'
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
-import type { operations } from '@/types/comfyRegistryTypes'
-import type { components } from '@/types/comfyRegistryTypes'
+import type { components, operations } from '@/types/comfyRegistryTypes'
import { isAbortError } from '@/utils/typeGuardUtil'
export enum EventType {
diff --git a/src/services/dialogService.ts b/src/services/dialogService.ts
index 65917f273..07316b01d 100644
--- a/src/services/dialogService.ts
+++ b/src/services/dialogService.ts
@@ -15,10 +15,10 @@ import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.
import { t } from '@/i18n'
import SettingDialogContent from '@/platform/settings/components/SettingDialogContent.vue'
import type { ExecutionErrorWsMessage } from '@/schemas/apiSchema'
-import {
- type DialogComponentProps,
- type ShowDialogOptions,
- useDialogStore
+import { useDialogStore } from '@/stores/dialogStore'
+import type {
+ DialogComponentProps,
+ ShowDialogOptions
} from '@/stores/dialogStore'
import ManagerProgressDialogContent from '@/workbench/extensions/manager/components/ManagerProgressDialogContent.vue'
import ManagerProgressFooter from '@/workbench/extensions/manager/components/ManagerProgressFooter.vue'
@@ -121,8 +121,7 @@ export const useDialogService = () => {
pt: {
pcCloseButton: {
root: {
- class:
- 'bg-gray-500 dark-theme:bg-neutral-700 w-9 h-9 p-1.5 rounded-full text-white'
+ class: 'bg-dialog-surface w-9 h-9 p-1.5 rounded-full text-white'
}
},
header: { class: 'py-0! px-6 m-0! h-[68px]' },
@@ -469,7 +468,7 @@ export const useDialogService = () => {
pcCloseButton: {
root: {
class:
- '!w-7 !h-7 !border-none !outline-none !p-2 !m-1.5 bg-gray-500 dark-theme:bg-neutral-700 text-white'
+ '!w-7 !h-7 !border-none !outline-none !p-2 !m-1.5 bg-dialog-surface text-white'
}
}
},
diff --git a/src/services/litegraphService.ts b/src/services/litegraphService.ts
index 2149bb378..b731e978b 100644
--- a/src/services/litegraphService.ts
+++ b/src/services/litegraphService.ts
@@ -4,20 +4,24 @@ import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteG
import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage'
import { useNodeCanvasImagePreview } from '@/composables/node/useNodeCanvasImagePreview'
import { useNodeImage, useNodeVideo } from '@/composables/node/useNodeImage'
+import { addWidgetPromotionOptions } from '@/core/graph/subgraph/proxyWidgetUtils'
+import { showSubgraphNodeDialog } from '@/core/graph/subgraph/useSubgraphNodeDialog'
import { st, t } from '@/i18n'
import {
- type IContextMenuValue,
LGraphBadge,
LGraphCanvas,
LGraphEventMode,
LGraphNode,
LiteGraph,
- type Point,
RenderShape,
- type Subgraph,
SubgraphNode,
createBounds
} from '@/lib/litegraph/src/litegraph'
+import type {
+ IContextMenuValue,
+ Point,
+ Subgraph
+} from '@/lib/litegraph/src/litegraph'
import type {
ExportedSubgraphInstance,
ISerialisableNodeInput,
@@ -741,7 +745,7 @@ export const useLitegraphService = () => {
]
}
- node.prototype.getExtraMenuOptions = function (_, options) {
+ node.prototype.getExtraMenuOptions = function (canvas, options) {
if (this.imgs) {
// If this node has images then we add an open in new tab item
let img
@@ -788,7 +792,7 @@ export const useLitegraphService = () => {
content: 'Bypass',
callback: () => {
toggleSelectedNodesMode(LGraphEventMode.BYPASS)
- app.canvas.setDirty(true, true)
+ canvas.setDirty(true, true)
}
})
@@ -824,18 +828,88 @@ export const useLitegraphService = () => {
}
}
if (this instanceof SubgraphNode) {
- options.unshift({
- content: 'Unpack Subgraph',
- callback: () => {
- useNodeOutputStore().revokeSubgraphPreviews(this)
- this.graph.unpackSubgraph(this)
+ options.unshift(
+ {
+ content: 'Edit Subgraph Widgets',
+ callback: () => {
+ showSubgraphNodeDialog()
+ }
+ },
+ {
+ content: 'Unpack Subgraph',
+ callback: () => {
+ useNodeOutputStore().revokeSubgraphPreviews(this)
+ this.graph.unpackSubgraph(this)
+ }
}
- })
+ )
+ }
+ if (this.graph && !this.graph.isRootGraph) {
+ const [x, y] = canvas.canvas_mouse
+ const overWidget = this.getWidgetOnPos(x, y, true)
+ if (overWidget) {
+ addWidgetPromotionOptions(options, overWidget, this)
+ }
}
return []
}
}
+ function updatePreviews(node: LGraphNode, callback?: () => void) {
+ try {
+ unsafeUpdatePreviews.call(node, callback)
+ } catch (error) {
+ console.error('Error drawing node background', error)
+ }
+ }
+ function unsafeUpdatePreviews(this: LGraphNode, callback?: () => void) {
+ if (this.flags.collapsed) return
+
+ const nodeOutputStore = useNodeOutputStore()
+ const { showAnimatedPreview, removeAnimatedPreview } =
+ useNodeAnimatedImage()
+ const { showCanvasImagePreview, removeCanvasImagePreview } =
+ useNodeCanvasImagePreview()
+
+ const output = nodeOutputStore.getNodeOutputs(this)
+ const preview = nodeOutputStore.getNodePreviews(this)
+
+ const isNewOutput = output && this.images !== output.images
+ const isNewPreview = preview && this.preview !== preview
+
+ if (isNewPreview) this.preview = preview
+ if (isNewOutput) this.images = output.images
+
+ if (isNewOutput || isNewPreview) {
+ this.animatedImages = output?.animated?.find(Boolean)
+
+ const isAnimatedWebp =
+ this.animatedImages &&
+ output?.images?.some((img) => img.filename?.includes('webp'))
+ const isAnimatedPng =
+ this.animatedImages &&
+ output?.images?.some((img) => img.filename?.includes('png'))
+ const isVideo =
+ (this.animatedImages && !isAnimatedWebp && !isAnimatedPng) ||
+ isVideoNode(this)
+ if (isVideo) {
+ useNodeVideo(this, callback).showPreview()
+ } else {
+ useNodeImage(this, callback).showPreview()
+ }
+ }
+
+ // Nothing to do
+ if (!this.imgs?.length) return
+
+ if (this.animatedImages) {
+ removeCanvasImagePreview(this)
+ showAnimatedPreview(this)
+ } else {
+ removeAnimatedPreview(this)
+ showCanvasImagePreview(this)
+ }
+ }
/**
* Adds Custom drawing logic for nodes
@@ -851,62 +925,8 @@ export const useLitegraphService = () => {
'node.setSizeForImage is deprecated. Now it has no effect. Please remove the call to it.'
)
}
-
- function unsafeDrawBackground(this: LGraphNode) {
- if (this.flags.collapsed) return
-
- const nodeOutputStore = useNodeOutputStore()
- const { showAnimatedPreview, removeAnimatedPreview } =
- useNodeAnimatedImage()
- const { showCanvasImagePreview, removeCanvasImagePreview } =
- useNodeCanvasImagePreview()
-
- const output = nodeOutputStore.getNodeOutputs(this)
- const preview = nodeOutputStore.getNodePreviews(this)
-
- const isNewOutput = output && this.images !== output.images
- const isNewPreview = preview && this.preview !== preview
-
- if (isNewPreview) this.preview = preview
- if (isNewOutput) this.images = output.images
-
- if (isNewOutput || isNewPreview) {
- this.animatedImages = output?.animated?.find(Boolean)
-
- const isAnimatedWebp =
- this.animatedImages &&
- output?.images?.some((img) => img.filename?.includes('webp'))
- const isAnimatedPng =
- this.animatedImages &&
- output?.images?.some((img) => img.filename?.includes('png'))
- const isVideo =
- (this.animatedImages && !isAnimatedWebp && !isAnimatedPng) ||
- isVideoNode(this)
- if (isVideo) {
- useNodeVideo(this).showPreview()
- } else {
- useNodeImage(this).showPreview()
- }
- }
-
- // Nothing to do
- if (!this.imgs?.length) return
-
- if (this.animatedImages) {
- removeCanvasImagePreview(this)
- showAnimatedPreview(this)
- } else {
- removeAnimatedPreview(this)
- showCanvasImagePreview(this)
- }
- }
-
node.prototype.onDrawBackground = function () {
- try {
- unsafeDrawBackground.call(this)
- } catch (error) {
- console.error('Error drawing node background', error)
- }
+ updatePreviews(this)
}
}
@@ -1036,6 +1056,7 @@ export const useLitegraphService = () => {
getCanvasCenter,
goToNode,
resetView,
- fitView
+ fitView,
+ updatePreviews
}
}
diff --git a/src/stores/commandStore.ts b/src/stores/commandStore.ts
index 7ef99335d..d3da5d85f 100644
--- a/src/stores/commandStore.ts
+++ b/src/stores/commandStore.ts
@@ -4,7 +4,8 @@ import { computed, ref } from 'vue'
import { useErrorHandling } from '@/composables/useErrorHandling'
import type { ComfyExtension } from '@/types/comfy'
-import { type KeybindingImpl, useKeybindingStore } from './keybindingStore'
+import { useKeybindingStore } from './keybindingStore'
+import type { KeybindingImpl } from './keybindingStore'
export interface ComfyCommand {
id: string
diff --git a/src/stores/dialogStore.ts b/src/stores/dialogStore.ts
index f74e44c7d..d2bac3675 100644
--- a/src/stores/dialogStore.ts
+++ b/src/stores/dialogStore.ts
@@ -3,7 +3,8 @@
import { merge } from 'es-toolkit/compat'
import { defineStore } from 'pinia'
import type { DialogPassThroughOptions } from 'primevue/dialog'
-import { type Component, markRaw, ref } from 'vue'
+import { markRaw, ref } from 'vue'
+import type { Component } from 'vue'
import type GlobalDialog from '@/components/dialog/GlobalDialog.vue'
diff --git a/src/stores/domWidgetStore.ts b/src/stores/domWidgetStore.ts
index 6ad25d6c0..82c0292d3 100644
--- a/src/stores/domWidgetStore.ts
+++ b/src/stores/domWidgetStore.ts
@@ -2,7 +2,8 @@
* Stores all DOM widgets that are used in the canvas.
*/
import { defineStore } from 'pinia'
-import { type Raw, computed, markRaw, ref } from 'vue'
+import { computed, markRaw, ref } from 'vue'
+import type { Raw } from 'vue'
import type { PositionConfig } from '@/composables/element/useAbsolutePosition'
import type { BaseDOMWidget } from '@/scripts/domWidget'
diff --git a/src/stores/electronDownloadStore.ts b/src/stores/electronDownloadStore.ts
index 3364cc37d..82b7db729 100644
--- a/src/stores/electronDownloadStore.ts
+++ b/src/stores/electronDownloadStore.ts
@@ -1,7 +1,5 @@
-import {
- type DownloadState,
- DownloadStatus
-} from '@comfyorg/comfyui-electron-types'
+import { DownloadStatus } from '@comfyorg/comfyui-electron-types'
+import type { DownloadState } from '@comfyorg/comfyui-electron-types'
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
diff --git a/src/stores/firebaseAuthStore.ts b/src/stores/firebaseAuthStore.ts
index 698efff3e..602de5ec6 100644
--- a/src/stores/firebaseAuthStore.ts
+++ b/src/stores/firebaseAuthStore.ts
@@ -1,11 +1,8 @@
import { FirebaseError } from 'firebase/app'
import {
- type Auth,
AuthErrorCodes,
GithubAuthProvider,
GoogleAuthProvider,
- type User,
- type UserCredential,
browserLocalPersistence,
createUserWithEmailAndPassword,
deleteUser,
@@ -17,6 +14,7 @@ import {
signOut,
updatePassword
} from 'firebase/auth'
+import type { Auth, User, UserCredential } from 'firebase/auth'
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import { useFirebaseAuth } from 'vuefire'
diff --git a/src/stores/imagePreviewStore.ts b/src/stores/imagePreviewStore.ts
index aebc23810..848eb0ae7 100644
--- a/src/stores/imagePreviewStore.ts
+++ b/src/stores/imagePreviewStore.ts
@@ -3,7 +3,6 @@ import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { LGraphNode, SubgraphNode } from '@/lib/litegraph/src/litegraph'
-import { Subgraph } from '@/lib/litegraph/src/litegraph'
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
import type {
ExecutedWsMessage,
@@ -38,7 +37,7 @@ interface SetOutputOptions {
}
export const useNodeOutputStore = defineStore('nodeOutput', () => {
- const { nodeIdToNodeLocatorId } = useWorkflowStore()
+ const { nodeIdToNodeLocatorId, nodeToNodeLocatorId } = useWorkflowStore()
const { executionIdToNodeLocatorId } = useExecutionStore()
const scheduledRevoke: Record void }> = {}
@@ -63,11 +62,11 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
function getNodeOutputs(
node: LGraphNode
): ExecutedWsMessage['output'] | undefined {
- return app.nodeOutputs[nodeIdToNodeLocatorId(node.id)]
+ return app.nodeOutputs[nodeToNodeLocatorId(node)]
}
function getNodePreviews(node: LGraphNode): string[] | undefined {
- return app.nodePreviewImages[nodeIdToNodeLocatorId(node.id)]
+ return app.nodePreviewImages[nodeToNodeLocatorId(node)]
}
/**
@@ -161,10 +160,7 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
) {
if (!filenames || !node) return
- const locatorId =
- node.graph instanceof Subgraph
- ? nodeIdToNodeLocatorId(node.id, node.graph ?? undefined)
- : `${node.id}`
+ const locatorId = nodeToNodeLocatorId(node)
if (!locatorId) return
if (typeof filenames === 'string') {
setOutputsByLocatorId(
diff --git a/src/stores/modelToNodeStore.ts b/src/stores/modelToNodeStore.ts
index 4f7925294..436dac26d 100644
--- a/src/stores/modelToNodeStore.ts
+++ b/src/stores/modelToNodeStore.ts
@@ -29,6 +29,7 @@ export const useModelToNodeStore = defineStore('modelToNode', () => {
return new Set(
Object.values(modelToNodeMap.value)
.flat()
+ .filter((provider) => !!provider.nodeDef)
.map((provider) => provider.nodeDef.name)
)
})
@@ -38,6 +39,8 @@ export const useModelToNodeStore = defineStore('modelToNode', () => {
const lookup: Record = {}
for (const [category, providers] of Object.entries(modelToNodeMap.value)) {
for (const provider of providers) {
+ // Extension nodes may not be installed
+ if (!provider.nodeDef) continue
// Only store the first category for each node type (matches current assetService behavior)
if (!lookup[provider.nodeDef.name]) {
lookup[provider.nodeDef.name] = category
@@ -98,6 +101,7 @@ export const useModelToNodeStore = defineStore('modelToNode', () => {
nodeProvider: ModelNodeProvider
) {
registerDefaults()
+ if (!nodeProvider.nodeDef) return
if (!modelToNodeMap.value[modelType]) {
modelToNodeMap.value[modelType] = []
}
@@ -131,10 +135,24 @@ export const useModelToNodeStore = defineStore('modelToNode', () => {
quickRegister('loras', 'LoraLoaderModelOnly', 'lora_name')
quickRegister('vae', 'VAELoader', 'vae_name')
quickRegister('controlnet', 'ControlNetLoader', 'control_net_name')
- quickRegister('unet', 'UNETLoader', 'unet_name')
+ quickRegister('diffusion_models', 'UNETLoader', 'unet_name')
quickRegister('upscale_models', 'UpscaleModelLoader', 'model_name')
- quickRegister('style_models', 'StyleModelLoader', 'style_model')
+ quickRegister('style_models', 'StyleModelLoader', 'style_model_name')
quickRegister('gligen', 'GLIGENLoader', 'gligen_name')
+ quickRegister('clip_vision', 'CLIPVisionLoader', 'clip_name')
+ quickRegister('text_encoders', 'CLIPLoader', 'clip_name')
+ quickRegister('audio_encoders', 'AudioEncoderLoader', 'audio_encoder_name')
+ quickRegister('model_patches', 'ModelPatchLoader', 'name')
+ quickRegister(
+ 'animatediff_models',
+ 'ADE_LoadAnimateDiffModel',
+ 'model_name'
+ )
+ quickRegister(
+ 'animatediff_motion_lora',
+ 'ADE_AnimateDiffLoRALoader',
+ 'name'
+ )
}
return {
diff --git a/src/stores/nodeBookmarkStore.ts b/src/stores/nodeBookmarkStore.ts
index 55ffebf23..7ddabdd77 100644
--- a/src/stores/nodeBookmarkStore.ts
+++ b/src/stores/nodeBookmarkStore.ts
@@ -6,10 +6,12 @@ import { useSettingStore } from '@/platform/settings/settingStore'
import type { BookmarkCustomization } from '@/schemas/apiSchema'
import type { TreeNode } from '@/types/treeExplorerTypes'
-import { useNodeDefStore } from './nodeDefStore'
+import {
+ buildNodeDefTree,
+ createDummyFolderNodeDef,
+ useNodeDefStore
+} from './nodeDefStore'
import type { ComfyNodeDefImpl } from './nodeDefStore'
-import { createDummyFolderNodeDef } from './nodeDefStore'
-import { buildNodeDefTree } from './nodeDefStore'
const BOOKMARK_SETTING_ID = 'Comfy.NodeLibrary.Bookmarks.V2'
diff --git a/src/stores/nodeDefStore.ts b/src/stores/nodeDefStore.ts
index 3f4047bb6..25e82a93c 100644
--- a/src/stores/nodeDefStore.ts
+++ b/src/stores/nodeDefStore.ts
@@ -17,11 +17,8 @@ import type {
} from '@/schemas/nodeDefSchema'
import { NodeSearchService } from '@/services/nodeSearchService'
import { useSubgraphStore } from '@/stores/subgraphStore'
-import {
- type NodeSource,
- NodeSourceType,
- getNodeSource
-} from '@/types/nodeSource'
+import { NodeSourceType, getNodeSource } from '@/types/nodeSource'
+import type { NodeSource } from '@/types/nodeSource'
import type { TreeNode } from '@/types/treeExplorerTypes'
import type { FuseSearchable, SearchAuxScore } from '@/utils/fuseUtil'
import { buildTree } from '@/utils/treeUtil'
diff --git a/src/stores/widgetStore.ts b/src/stores/widgetStore.ts
index be4f3efc8..178e576ab 100644
--- a/src/stores/widgetStore.ts
+++ b/src/stores/widgetStore.ts
@@ -2,10 +2,8 @@ import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2'
-import {
- type InputSpec as InputSpecV1,
- getInputSpecType
-} from '@/schemas/nodeDefSchema'
+import { getInputSpecType } from '@/schemas/nodeDefSchema'
+import type { InputSpec as InputSpecV1 } from '@/schemas/nodeDefSchema'
import type { ComfyWidgetConstructor } from '@/scripts/widgets'
import { ComfyWidgets } from '@/scripts/widgets'
diff --git a/src/stores/workspace/sidebarTabStore.ts b/src/stores/workspace/sidebarTabStore.ts
index fec3df08b..4dfb2c9f5 100644
--- a/src/stores/workspace/sidebarTabStore.ts
+++ b/src/stores/workspace/sidebarTabStore.ts
@@ -5,6 +5,7 @@ import { useModelLibrarySidebarTab } from '@/composables/sidebarTabs/useModelLib
import { useNodeLibrarySidebarTab } from '@/composables/sidebarTabs/useNodeLibrarySidebarTab'
import { useQueueSidebarTab } from '@/composables/sidebarTabs/useQueueSidebarTab'
import { t, te } from '@/i18n'
+import { useSettingStore } from '@/platform/settings/settingStore'
import { useWorkflowsSidebarTab } from '@/platform/workflow/management/composables/useWorkflowsSidebarTab'
import { useCommandStore } from '@/stores/commandStore'
import { useMenuItemStore } from '@/stores/menuItemStore'
@@ -63,7 +64,20 @@ export const useSidebarTabStore = defineStore('sidebarTab', () => {
tooltip: tooltipFunction,
versionAdded: '1.3.9',
category: 'view-controls' as const,
- function: () => {
+ function: async () => {
+ const settingStore = useSettingStore()
+ const commandStore = useCommandStore()
+
+ if (
+ tab.id === 'model-library' &&
+ settingStore.get('Comfy.Assets.UseAssetAPI')
+ ) {
+ await commandStore.commands
+ .find((cmd) => cmd.id === 'Comfy.BrowseModelAssets')
+ ?.function?.()
+ return
+ }
+
toggleSidebarTab(tab.id)
},
active: () => activeSidebarTab.value?.id === tab.id,
diff --git a/src/types/litegraph-augmentation.d.ts b/src/types/litegraph-augmentation.d.ts
index 0f5dee17d..f6b4aaf42 100644
--- a/src/types/litegraph-augmentation.d.ts
+++ b/src/types/litegraph-augmentation.d.ts
@@ -1,8 +1,9 @@
import '@/lib/litegraph/src/litegraph'
-import type { LLink, Size } from '@/lib/litegraph/src/litegraph'
import type {
ExecutableLGraphNode,
- ExecutionId
+ ExecutionId,
+ LLink,
+ Size
} from '@/lib/litegraph/src/litegraph'
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
diff --git a/src/utils/executableGroupNodeChildDTO.ts b/src/utils/executableGroupNodeChildDTO.ts
index d5b0ac5e5..b129f2d10 100644
--- a/src/utils/executableGroupNodeChildDTO.ts
+++ b/src/utils/executableGroupNodeChildDTO.ts
@@ -1,11 +1,11 @@
import type { GroupNodeHandler } from '@/extensions/core/groupNode'
-import {
- type ExecutableLGraphNode,
- ExecutableNodeDTO,
- type ExecutionId,
- type LGraphNode,
- type NodeId,
- type SubgraphNode
+import { ExecutableNodeDTO } from '@/lib/litegraph/src/litegraph'
+import type {
+ ExecutableLGraphNode,
+ ExecutionId,
+ LGraphNode,
+ NodeId,
+ SubgraphNode
} from '@/lib/litegraph/src/litegraph'
export class ExecutableGroupNodeChildDTO extends ExecutableNodeDTO {
diff --git a/src/utils/executableGroupNodeDto.ts b/src/utils/executableGroupNodeDto.ts
index 89570ae9e..5c23827e2 100644
--- a/src/utils/executableGroupNodeDto.ts
+++ b/src/utils/executableGroupNodeDto.ts
@@ -1,9 +1,11 @@
import {
- type ExecutableLGraphNode,
ExecutableNodeDTO,
- type ISlotType,
- LGraphEventMode,
- type LGraphNode
+ LGraphEventMode
+} from '@/lib/litegraph/src/litegraph'
+import type {
+ ExecutableLGraphNode,
+ ISlotType,
+ LGraphNode
} from '@/lib/litegraph/src/litegraph'
export const GROUP = Symbol()
diff --git a/src/utils/hostWhitelist.ts b/src/utils/hostWhitelist.ts
index 3fb1ae2c3..694626ca7 100644
--- a/src/utils/hostWhitelist.ts
+++ b/src/utils/hostWhitelist.ts
@@ -42,6 +42,7 @@ export function isHostWhitelisted(rawHost: string): boolean {
if (isLocalhostLabel(host)) return true
if (isIPv4Loopback(host)) return true
if (isIPv6Loopback(host)) return true
+ if (isComfyOrgHost(host)) return true
const normalizedList = HOST_WHITELIST.map(normalizeHost)
return normalizedList.includes(host)
}
@@ -89,3 +90,9 @@ function isIPv6Loopback(h: string): boolean {
// Require that at least one group was actually compressed: i.e., leftCount + rightCount ≤ 6.
return leftCount + rightCount <= 6
}
+
+const COMFY_ORG_HOST = /\.comfy\.org$/
+
+function isComfyOrgHost(h: string): boolean {
+ return COMFY_ORG_HOST.test(h)
+}
diff --git a/src/utils/litegraphUtil.ts b/src/utils/litegraphUtil.ts
index 1c6796905..29a597dc8 100644
--- a/src/utils/litegraphUtil.ts
+++ b/src/utils/litegraphUtil.ts
@@ -1,10 +1,10 @@
import _ from 'es-toolkit/compat'
import type { ColorOption, LGraph } from '@/lib/litegraph/src/litegraph'
-import { Reroute } from '@/lib/litegraph/src/litegraph'
import {
LGraphGroup,
LGraphNode,
+ Reroute,
isColorable
} from '@/lib/litegraph/src/litegraph'
import type {
diff --git a/src/utils/markdownRendererUtil.ts b/src/utils/markdownRendererUtil.ts
index 8b7c94632..3222be340 100644
--- a/src/utils/markdownRendererUtil.ts
+++ b/src/utils/markdownRendererUtil.ts
@@ -1,4 +1,4 @@
-import DOMPurify from 'dompurify'
+import { default as DOMPurify } from 'dompurify'
import { Renderer, marked } from 'marked'
const ALLOWED_TAGS = ['video', 'source']
diff --git a/src/utils/rafBatch.ts b/src/utils/rafBatch.ts
new file mode 100644
index 000000000..a8756ef24
--- /dev/null
+++ b/src/utils/rafBatch.ts
@@ -0,0 +1,29 @@
+export function createRafBatch(run: () => void) {
+ let rafId: number | null = null
+
+ const schedule = () => {
+ if (rafId != null) return
+ rafId = requestAnimationFrame(() => {
+ rafId = null
+ run()
+ })
+ }
+
+ const cancel = () => {
+ if (rafId != null) {
+ cancelAnimationFrame(rafId)
+ rafId = null
+ }
+ }
+
+ const flush = () => {
+ if (rafId == null) return
+ cancelAnimationFrame(rafId)
+ rafId = null
+ run()
+ }
+
+ const isScheduled = () => rafId != null
+
+ return { schedule, cancel, flush, isScheduled }
+}
diff --git a/src/utils/typeGuardUtil.ts b/src/utils/typeGuardUtil.ts
index 40e3a4183..3cefc0d38 100644
--- a/src/utils/typeGuardUtil.ts
+++ b/src/utils/typeGuardUtil.ts
@@ -1,7 +1,10 @@
import type { PrimitiveNode } from '@/extensions/core/widgetInputs'
-import type { LGraph, LGraphNode } from '@/lib/litegraph/src/litegraph'
-import type { INodeSlot } from '@/lib/litegraph/src/litegraph'
-import type { Subgraph } from '@/lib/litegraph/src/litegraph'
+import type {
+ INodeSlot,
+ LGraph,
+ LGraphNode,
+ Subgraph
+} from '@/lib/litegraph/src/litegraph'
export function isPrimitiveNode(
node: LGraphNode
diff --git a/src/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue b/src/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue
index 5b6c15582..bce7cb901 100644
--- a/src/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue
+++ b/src/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue
@@ -239,6 +239,6 @@ const toggleExtensionsPanel = () => {
diff --git a/src/workbench/extensions/manager/components/manager/PackVersionBadge.vue b/src/workbench/extensions/manager/components/manager/PackVersionBadge.vue
index 551468861..440a886af 100644
--- a/src/workbench/extensions/manager/components/manager/PackVersionBadge.vue
+++ b/src/workbench/extensions/manager/components/manager/PackVersionBadge.vue
@@ -6,7 +6,7 @@
"
class="inline-flex items-center gap-1 rounded-2xl text-xs py-1"
:class="{
- 'bg-gray-100 dark-theme:bg-neutral-700 px-1.5': fill,
+ 'bg-dialog-surface px-1.5': fill,
'cursor-pointer': !isDisabled,
'cursor-not-allowed opacity-60': isDisabled
}"
diff --git a/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue b/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue
index 7164544df..c7def90b9 100644
--- a/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue
+++ b/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue
@@ -33,8 +33,10 @@ import type { ButtonSize } from '@/types/buttonTypes'
import type { components } from '@/types/comfyRegistryTypes'
import { useConflictDetection } from '@/workbench/extensions/manager/composables/useConflictDetection'
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
-import type { ConflictDetectionResult } from '@/workbench/extensions/manager/types/conflictDetectionTypes'
-import type { ConflictDetail } from '@/workbench/extensions/manager/types/conflictDetectionTypes'
+import type {
+ ConflictDetail,
+ ConflictDetectionResult
+} from '@/workbench/extensions/manager/types/conflictDetectionTypes'
import type { components as ManagerComponents } from '@/workbench/extensions/manager/types/generatedManagerTypes'
type NodePack = components['schemas']['Node']
diff --git a/src/workbench/extensions/manager/components/manager/infoPanel/tabs/DescriptionTabPanel.vue b/src/workbench/extensions/manager/components/manager/infoPanel/tabs/DescriptionTabPanel.vue
index 915ddcf37..a1355f5e3 100644
--- a/src/workbench/extensions/manager/components/manager/infoPanel/tabs/DescriptionTabPanel.vue
+++ b/src/workbench/extensions/manager/components/manager/infoPanel/tabs/DescriptionTabPanel.vue
@@ -28,9 +28,8 @@ import { useI18n } from 'vue-i18n'
import type { components } from '@/types/comfyRegistryTypes'
import { isValidUrl } from '@/utils/formatUtil'
-import InfoTextSection, {
- type TextSection
-} from '@/workbench/extensions/manager/components/manager/infoPanel/InfoTextSection.vue'
+import InfoTextSection from '@/workbench/extensions/manager/components/manager/infoPanel/InfoTextSection.vue'
+import type { TextSection } from '@/workbench/extensions/manager/components/manager/infoPanel/InfoTextSection.vue'
const { t } = useI18n()
diff --git a/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue b/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue
index da2f67338..266a55e7e 100644
--- a/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue
+++ b/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue
@@ -82,10 +82,12 @@ import PackCardFooter from '@/workbench/extensions/manager/components/manager/pa
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
import {
IsInstallingKey,
- type MergedNodePack,
- type RegistryPack,
isMergedNodePack
} from '@/workbench/extensions/manager/types/comfyManagerTypes'
+import type {
+ MergedNodePack,
+ RegistryPack
+} from '@/workbench/extensions/manager/types/comfyManagerTypes'
const { nodePack, isSelected = false } = defineProps<{
nodePack: MergedNodePack | RegistryPack
diff --git a/src/workbench/extensions/manager/components/manager/registrySearchBar/RegistrySearchBar.vue b/src/workbench/extensions/manager/components/manager/registrySearchBar/RegistrySearchBar.vue
index 0c6602169..990e4773f 100644
--- a/src/workbench/extensions/manager/components/manager/registrySearchBar/RegistrySearchBar.vue
+++ b/src/workbench/extensions/manager/components/manager/registrySearchBar/RegistrySearchBar.vue
@@ -78,10 +78,8 @@ import PackUpdateButton from '@/workbench/extensions/manager/components/manager/
import SearchFilterDropdown from '@/workbench/extensions/manager/components/manager/registrySearchBar/SearchFilterDropdown.vue'
import { useMissingNodes } from '@/workbench/extensions/manager/composables/nodePack/useMissingNodes'
import { useUpdateAvailableNodes } from '@/workbench/extensions/manager/composables/nodePack/useUpdateAvailableNodes'
-import {
- type SearchOption,
- SortableAlgoliaField
-} from '@/workbench/extensions/manager/types/comfyManagerTypes'
+import { SortableAlgoliaField } from '@/workbench/extensions/manager/types/comfyManagerTypes'
+import type { SearchOption } from '@/workbench/extensions/manager/types/comfyManagerTypes'
const { searchResults, sortOptions } = defineProps<{
searchResults?: components['schemas']['Node'][]
diff --git a/src/workbench/extensions/manager/composables/nodePack/usePacksSelection.ts b/src/workbench/extensions/manager/composables/nodePack/usePacksSelection.ts
index a5d382767..760d2ba5a 100644
--- a/src/workbench/extensions/manager/composables/nodePack/usePacksSelection.ts
+++ b/src/workbench/extensions/manager/composables/nodePack/usePacksSelection.ts
@@ -1,4 +1,5 @@
-import { type Ref, computed } from 'vue'
+import { computed } from 'vue'
+import type { Ref } from 'vue'
import type { components } from '@/types/comfyRegistryTypes'
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
diff --git a/src/workbench/extensions/manager/composables/nodePack/usePacksStatus.ts b/src/workbench/extensions/manager/composables/nodePack/usePacksStatus.ts
index bd1bff3da..82a3934b4 100644
--- a/src/workbench/extensions/manager/composables/nodePack/usePacksStatus.ts
+++ b/src/workbench/extensions/manager/composables/nodePack/usePacksStatus.ts
@@ -1,4 +1,5 @@
-import { type Ref, computed } from 'vue'
+import { computed } from 'vue'
+import type { Ref } from 'vue'
import type { components } from '@/types/comfyRegistryTypes'
import { useConflictDetectionStore } from '@/workbench/extensions/manager/stores/conflictDetectionStore'
diff --git a/src/workbench/extensions/manager/composables/useImportFailedDetection.ts b/src/workbench/extensions/manager/composables/useImportFailedDetection.ts
index 9b1fd9b0e..affd47c3a 100644
--- a/src/workbench/extensions/manager/composables/useImportFailedDetection.ts
+++ b/src/workbench/extensions/manager/composables/useImportFailedDetection.ts
@@ -1,4 +1,5 @@
-import { type ComputedRef, computed, unref } from 'vue'
+import { computed, unref } from 'vue'
+import type { ComputedRef } from 'vue'
import { useI18n } from 'vue-i18n'
import { useDialogService } from '@/services/dialogService'
diff --git a/tests-ui/README.md b/tests-ui/README.md
index 9270478df..4bb2d25bf 100644
--- a/tests-ui/README.md
+++ b/tests-ui/README.md
@@ -37,9 +37,6 @@ pnpm test:unit
# Run unit tests in watch mode
pnpm test:unit:dev
-
-# Run component tests with browser-native environment
-pnpm test:component
```
Refer to the specific guides for more detailed information on each testing type.
\ No newline at end of file
diff --git a/tests-ui/platform/assets/components/AssetBrowserModal.test.ts b/tests-ui/platform/assets/components/AssetBrowserModal.test.ts
index 003f6e4a2..238b5483e 100644
--- a/tests-ui/platform/assets/components/AssetBrowserModal.test.ts
+++ b/tests-ui/platform/assets/components/AssetBrowserModal.test.ts
@@ -1,10 +1,8 @@
import { mount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import { describe, expect, it, vi } from 'vitest'
-import { nextTick } from 'vue'
import AssetBrowserModal from '@/platform/assets/components/AssetBrowserModal.vue'
-import type { AssetDisplayItem } from '@/platform/assets/composables/useAssetBrowser'
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
// Mock @/i18n for useAssetBrowser and AssetFilterBar
@@ -57,6 +55,9 @@ vi.mock('@/components/widget/layout/BaseModalLayout.vue', () => ({
+
+
+
@@ -72,6 +73,9 @@ vi.mock('@/components/widget/panel/LeftSidePanel.vue', () => ({
emits: ['update:modelValue'],
template: `
+
+
+