mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-06 22:21:51 +00:00
Compare commits
5 Commits
playwright
...
refactor/e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e994c2f630 | ||
|
|
711fa84e3c | ||
|
|
51874a57eb | ||
|
|
5782131a18 | ||
|
|
87bcff999c |
@@ -8,15 +8,17 @@ import Button from '@/components/ui/button/Button.vue'
|
||||
import { isPromotedWidgetView } from '@/core/graph/subgraph/promotedWidgetTypes'
|
||||
import {
|
||||
demoteWidget,
|
||||
getPromotableWidgets,
|
||||
getSourceNodeId,
|
||||
getWidgetName,
|
||||
isLinkedPromotion,
|
||||
isRecommendedWidget,
|
||||
promoteWidget,
|
||||
pruneDisconnected
|
||||
} from '@/core/graph/subgraph/promotionUtils'
|
||||
import type { WidgetItem } from '@/core/graph/subgraph/promotionUtils'
|
||||
import type { WidgetItem } from '@/core/graph/subgraph/promotionPolicy'
|
||||
import {
|
||||
getPromotableWidgets,
|
||||
isRecommendedWidget
|
||||
} from '@/core/graph/subgraph/promotionPolicy'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { SubgraphNode } from '@/lib/litegraph/src/subgraph/SubgraphNode'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
|
||||
195
src/core/graph/subgraph/promotionPolicy.test.ts
Normal file
195
src/core/graph/subgraph/promotionPolicy.test.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import { fromPartial } from '@total-typescript/shoehorn'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { CANVAS_IMAGE_PREVIEW_WIDGET } from '@/composables/node/canvasImagePreviewTypes'
|
||||
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
|
||||
|
||||
import type { WidgetItem } from './promotionPolicy'
|
||||
import {
|
||||
getPromotableWidgets,
|
||||
isPreviewPseudoWidget,
|
||||
isRecommendedWidget
|
||||
} from './promotionPolicy'
|
||||
|
||||
function widget(
|
||||
overrides: Partial<
|
||||
Pick<IBaseWidget, 'name' | 'serialize' | 'type' | 'options'>
|
||||
>
|
||||
): IBaseWidget {
|
||||
return fromPartial<IBaseWidget>({ name: 'widget', ...overrides })
|
||||
}
|
||||
|
||||
function widgetItem(
|
||||
nodeType: string,
|
||||
widgetName: string,
|
||||
overrides: Partial<IBaseWidget> = {}
|
||||
): WidgetItem {
|
||||
const node = { title: nodeType, id: 1, type: nodeType }
|
||||
const w = fromPartial<IBaseWidget>({
|
||||
name: widgetName,
|
||||
computedDisabled: false,
|
||||
...overrides
|
||||
})
|
||||
return [node, w]
|
||||
}
|
||||
|
||||
describe('isPreviewPseudoWidget', () => {
|
||||
it('returns true for $$-prefixed widget names', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(widget({ name: '$$canvas-image-preview' }))
|
||||
).toBe(true)
|
||||
expect(isPreviewPseudoWidget(widget({ name: '$$anything' }))).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for serialize:false with type "preview"', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({ name: 'videopreview', serialize: false, type: 'preview' })
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for options.serialize:false with type "preview" (VHS pattern)', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({
|
||||
name: 'videopreview',
|
||||
type: 'preview',
|
||||
options: { serialize: false }
|
||||
})
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for serialize:false with type "video"', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({ name: 'vid', serialize: false, type: 'video' })
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for serialize:false with type "audioUI"', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({ name: 'audio', serialize: false, type: 'audioUI' })
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns false for type "preview" when serialize is not false', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({ name: 'videopreview', serialize: true, type: 'preview' })
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for regular widgets', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(widget({ name: 'seed', type: 'number' }))
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for serialize:false with unknown type', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({ name: 'text', serialize: false, type: 'customtext' })
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPromotableWidgets', () => {
|
||||
it('adds virtual canvas preview widget for PreviewImage nodes', () => {
|
||||
const node = new LGraphNode('PreviewImage')
|
||||
node.type = 'PreviewImage'
|
||||
|
||||
const widgets = getPromotableWidgets(node)
|
||||
|
||||
expect(
|
||||
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('adds virtual canvas preview widget for SaveImage nodes', () => {
|
||||
const node = new LGraphNode('SaveImage')
|
||||
node.type = 'SaveImage'
|
||||
|
||||
const widgets = getPromotableWidgets(node)
|
||||
|
||||
expect(
|
||||
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('adds virtual canvas preview widget for GLSLShader nodes', () => {
|
||||
const node = new LGraphNode('GLSLShader')
|
||||
node.type = 'GLSLShader'
|
||||
|
||||
const widgets = getPromotableWidgets(node)
|
||||
|
||||
expect(
|
||||
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('does not add virtual canvas preview widget for non-image nodes', () => {
|
||||
const node = new LGraphNode('TextNode')
|
||||
node.addOutput('TEXT', 'STRING')
|
||||
|
||||
const widgets = getPromotableWidgets(node)
|
||||
|
||||
expect(
|
||||
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('does not add virtual canvas preview widget for ImageInvert nodes', () => {
|
||||
const node = new LGraphNode('ImageInvert')
|
||||
node.type = 'ImageInvert'
|
||||
|
||||
const widgets = getPromotableWidgets(node)
|
||||
|
||||
expect(
|
||||
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isRecommendedWidget', () => {
|
||||
it('returns true for widgets on recommended node types', () => {
|
||||
expect(isRecommendedWidget(widgetItem('CLIPTextEncode', 'text'))).toBe(true)
|
||||
expect(isRecommendedWidget(widgetItem('LoadImage', 'image'))).toBe(true)
|
||||
expect(isRecommendedWidget(widgetItem('SaveImage', 'filename'))).toBe(true)
|
||||
expect(isRecommendedWidget(widgetItem('PreviewImage', 'anything'))).toBe(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('returns true for seed widgets regardless of node type', () => {
|
||||
expect(isRecommendedWidget(widgetItem('KSampler', 'seed'))).toBe(true)
|
||||
expect(isRecommendedWidget(widgetItem('KSamplerAdvanced', 'seed'))).toBe(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('returns false for non-recommended node and widget combinations', () => {
|
||||
expect(isRecommendedWidget(widgetItem('KSampler', 'steps'))).toBe(false)
|
||||
expect(isRecommendedWidget(widgetItem('VAEDecode', 'samples'))).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false when widget is computedDisabled', () => {
|
||||
expect(
|
||||
isRecommendedWidget(
|
||||
widgetItem('CLIPTextEncode', 'text', { computedDisabled: true })
|
||||
)
|
||||
).toBe(false)
|
||||
expect(
|
||||
isRecommendedWidget(
|
||||
widgetItem('KSampler', 'seed', { computedDisabled: true })
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
69
src/core/graph/subgraph/promotionPolicy.ts
Normal file
69
src/core/graph/subgraph/promotionPolicy.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets.ts'
|
||||
import {
|
||||
CANVAS_IMAGE_PREVIEW_WIDGET,
|
||||
supportsVirtualCanvasImagePreview
|
||||
} from '@/composables/node/canvasImagePreviewTypes'
|
||||
|
||||
export type PartialNode = Pick<LGraphNode, 'title' | 'id' | 'type'>
|
||||
|
||||
export type WidgetItem = [PartialNode, IBaseWidget]
|
||||
|
||||
/** Known non-$$ preview widget types added by core or popular extensions. */
|
||||
const PREVIEW_WIDGET_TYPES = new Set(['preview', 'video', 'audioUI'])
|
||||
|
||||
/**
|
||||
* Returns true for pseudo-widgets that display media previews and should
|
||||
* be auto-promoted when their node is inside a subgraph.
|
||||
* Matches the core `$$` convention as well as custom-node patterns
|
||||
* (e.g. VHS `videopreview` with type `"preview"`).
|
||||
*/
|
||||
export function isPreviewPseudoWidget(widget: IBaseWidget): boolean {
|
||||
if (widget.name.startsWith('$$')) return true
|
||||
// Custom nodes may set serialize on the widget or in options
|
||||
if (widget.serialize !== false && widget.options?.serialize !== false)
|
||||
return false
|
||||
if (typeof widget.type === 'string' && PREVIEW_WIDGET_TYPES.has(widget.type))
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
const recommendedNodes = [
|
||||
'CLIPTextEncode',
|
||||
'LoadImage',
|
||||
'SaveImage',
|
||||
'PreviewImage'
|
||||
]
|
||||
const recommendedWidgetNames = ['seed']
|
||||
export function isRecommendedWidget([node, widget]: WidgetItem) {
|
||||
return (
|
||||
!widget.computedDisabled &&
|
||||
(recommendedNodes.includes(node.type) ||
|
||||
recommendedWidgetNames.includes(widget.name))
|
||||
)
|
||||
}
|
||||
|
||||
function createVirtualCanvasImagePreviewWidget(): IBaseWidget {
|
||||
return {
|
||||
name: CANVAS_IMAGE_PREVIEW_WIDGET,
|
||||
type: 'IMAGE_PREVIEW',
|
||||
options: { serialize: false },
|
||||
serialize: false,
|
||||
y: 0,
|
||||
computedDisabled: false
|
||||
}
|
||||
}
|
||||
|
||||
export function getPromotableWidgets(node: LGraphNode): IBaseWidget[] {
|
||||
const widgets = [...(node.widgets ?? [])]
|
||||
|
||||
const hasCanvasPreviewWidget = widgets.some(
|
||||
(widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET
|
||||
)
|
||||
const supportsVirtualPreview = supportsVirtualCanvasImagePreview(node)
|
||||
if (!hasCanvasPreviewWidget && supportsVirtualPreview) {
|
||||
widgets.push(createVirtualCanvasImagePreviewWidget())
|
||||
}
|
||||
|
||||
return widgets
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { fromPartial } from '@total-typescript/shoehorn'
|
||||
import { setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { CANVAS_IMAGE_PREVIEW_WIDGET } from '@/composables/node/canvasImagePreviewTypes'
|
||||
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import {
|
||||
createTestSubgraph,
|
||||
@@ -17,95 +17,12 @@ vi.mock('@/services/litegraphService', () => ({
|
||||
}))
|
||||
|
||||
import {
|
||||
CANVAS_IMAGE_PREVIEW_WIDGET,
|
||||
getPromotableWidgets,
|
||||
hasUnpromotedWidgets,
|
||||
isLinkedPromotion,
|
||||
isPreviewPseudoWidget,
|
||||
promoteRecommendedWidgets,
|
||||
pruneDisconnected
|
||||
} from './promotionUtils'
|
||||
|
||||
function widget(
|
||||
overrides: Partial<
|
||||
Pick<IBaseWidget, 'name' | 'serialize' | 'type' | 'options'>
|
||||
>
|
||||
): IBaseWidget {
|
||||
return fromPartial<IBaseWidget>({ name: 'widget', ...overrides })
|
||||
}
|
||||
|
||||
describe('isPreviewPseudoWidget', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('returns true for $$-prefixed widget names', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(widget({ name: '$$canvas-image-preview' }))
|
||||
).toBe(true)
|
||||
expect(isPreviewPseudoWidget(widget({ name: '$$anything' }))).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for serialize:false with type "preview"', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({ name: 'videopreview', serialize: false, type: 'preview' })
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for options.serialize:false with type "preview" (VHS pattern)', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({
|
||||
name: 'videopreview',
|
||||
type: 'preview',
|
||||
options: { serialize: false }
|
||||
})
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for serialize:false with type "video"', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({ name: 'vid', serialize: false, type: 'video' })
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for serialize:false with type "audioUI"', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({ name: 'audio', serialize: false, type: 'audioUI' })
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns false for type "preview" when serialize is not false', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({ name: 'videopreview', serialize: true, type: 'preview' })
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for regular widgets', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(widget({ name: 'seed', type: 'number' }))
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for serialize:false with unknown type', () => {
|
||||
expect(
|
||||
isPreviewPseudoWidget(
|
||||
widget({ name: 'text', serialize: false, type: 'customtext' })
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('pruneDisconnected', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
@@ -169,63 +86,6 @@ describe('pruneDisconnected', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPromotableWidgets', () => {
|
||||
it('adds virtual canvas preview widget for PreviewImage nodes', () => {
|
||||
const node = new LGraphNode('PreviewImage')
|
||||
node.type = 'PreviewImage'
|
||||
|
||||
const widgets = getPromotableWidgets(node)
|
||||
|
||||
expect(
|
||||
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('adds virtual canvas preview widget for SaveImage nodes', () => {
|
||||
const node = new LGraphNode('SaveImage')
|
||||
node.type = 'SaveImage'
|
||||
|
||||
const widgets = getPromotableWidgets(node)
|
||||
|
||||
expect(
|
||||
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('adds virtual canvas preview widget for GLSLShader nodes', () => {
|
||||
const node = new LGraphNode('GLSLShader')
|
||||
node.type = 'GLSLShader'
|
||||
|
||||
const widgets = getPromotableWidgets(node)
|
||||
|
||||
expect(
|
||||
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('does not add virtual canvas preview widget for non-image nodes', () => {
|
||||
const node = new LGraphNode('TextNode')
|
||||
node.addOutput('TEXT', 'STRING')
|
||||
|
||||
const widgets = getPromotableWidgets(node)
|
||||
|
||||
expect(
|
||||
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('does not add virtual canvas preview widget for ImageInvert nodes', () => {
|
||||
const node = new LGraphNode('ImageInvert')
|
||||
node.type = 'ImageInvert'
|
||||
|
||||
const widgets = getPromotableWidgets(node)
|
||||
|
||||
expect(
|
||||
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('promoteRecommendedWidgets', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import * as Sentry from '@sentry/vue'
|
||||
import type { PromotedWidgetSource } from '@/core/graph/subgraph/promotedWidgetTypes'
|
||||
import { isPromotedWidgetView } from '@/core/graph/subgraph/promotedWidgetTypes'
|
||||
import type {
|
||||
PartialNode,
|
||||
WidgetItem
|
||||
} from '@/core/graph/subgraph/promotionPolicy'
|
||||
import {
|
||||
getPromotableWidgets,
|
||||
isPreviewPseudoWidget,
|
||||
isRecommendedWidget
|
||||
} from '@/core/graph/subgraph/promotionPolicy'
|
||||
import { t } from '@/i18n'
|
||||
import type {
|
||||
IContextMenuValue,
|
||||
@@ -18,11 +27,6 @@ import { useLitegraphService } from '@/services/litegraphService'
|
||||
import { usePromotionStore } from '@/stores/promotionStore'
|
||||
import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore'
|
||||
|
||||
type PartialNode = Pick<LGraphNode, 'title' | 'id' | 'type'>
|
||||
|
||||
export type WidgetItem = [PartialNode, IBaseWidget]
|
||||
export { CANVAS_IMAGE_PREVIEW_WIDGET }
|
||||
|
||||
export function getWidgetName(w: IBaseWidget): string {
|
||||
return isPromotedWidgetView(w) ? w.sourceWidgetName : w.name
|
||||
}
|
||||
@@ -74,25 +78,6 @@ function refreshPromotedWidgetRendering(parents: SubgraphNode[]): void {
|
||||
useCanvasStore().canvas?.setDirty(true, true)
|
||||
}
|
||||
|
||||
/** Known non-$$ preview widget types added by core or popular extensions. */
|
||||
const PREVIEW_WIDGET_TYPES = new Set(['preview', 'video', 'audioUI'])
|
||||
|
||||
/**
|
||||
* Returns true for pseudo-widgets that display media previews and should
|
||||
* be auto-promoted when their node is inside a subgraph.
|
||||
* Matches the core `$$` convention as well as custom-node patterns
|
||||
* (e.g. VHS `videopreview` with type `"preview"`).
|
||||
*/
|
||||
export function isPreviewPseudoWidget(widget: IBaseWidget): boolean {
|
||||
if (widget.name.startsWith('$$')) return true
|
||||
// Custom nodes may set serialize on the widget or in options
|
||||
if (widget.serialize !== false && widget.options?.serialize !== false)
|
||||
return false
|
||||
if (typeof widget.type === 'string' && PREVIEW_WIDGET_TYPES.has(widget.type))
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
export function promoteWidget(
|
||||
node: PartialNode,
|
||||
widget: IBaseWidget,
|
||||
@@ -199,50 +184,6 @@ export function tryToggleWidgetPromotion() {
|
||||
else demoteWidget(node, widget, parents)
|
||||
}
|
||||
|
||||
const recommendedNodes = [
|
||||
'CLIPTextEncode',
|
||||
'LoadImage',
|
||||
'SaveImage',
|
||||
'PreviewImage'
|
||||
]
|
||||
const recommendedWidgetNames = ['seed']
|
||||
export function isRecommendedWidget([node, widget]: WidgetItem) {
|
||||
return (
|
||||
!widget.computedDisabled &&
|
||||
(recommendedNodes.includes(node.type) ||
|
||||
recommendedWidgetNames.includes(widget.name))
|
||||
)
|
||||
}
|
||||
|
||||
function supportsVirtualPreviewWidget(node: LGraphNode): boolean {
|
||||
return supportsVirtualCanvasImagePreview(node)
|
||||
}
|
||||
|
||||
function createVirtualCanvasImagePreviewWidget(): IBaseWidget {
|
||||
return {
|
||||
name: CANVAS_IMAGE_PREVIEW_WIDGET,
|
||||
type: 'IMAGE_PREVIEW',
|
||||
options: { serialize: false },
|
||||
serialize: false,
|
||||
y: 0,
|
||||
computedDisabled: false
|
||||
}
|
||||
}
|
||||
|
||||
export function getPromotableWidgets(node: LGraphNode): IBaseWidget[] {
|
||||
const widgets = [...(node.widgets ?? [])]
|
||||
|
||||
const hasCanvasPreviewWidget = widgets.some(
|
||||
(widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET
|
||||
)
|
||||
const supportsVirtualPreview = supportsVirtualPreviewWidget(node)
|
||||
if (!hasCanvasPreviewWidget && supportsVirtualPreview) {
|
||||
widgets.push(createVirtualCanvasImagePreviewWidget())
|
||||
}
|
||||
|
||||
return widgets
|
||||
}
|
||||
|
||||
function nodeWidgets(n: LGraphNode): WidgetItem[] {
|
||||
return getPromotableWidgets(n).map((w: IBaseWidget) => [n, w])
|
||||
}
|
||||
|
||||
@@ -6,10 +6,8 @@ import { useSubgraphOperations } from '@/composables/graph/useSubgraphOperations
|
||||
import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage'
|
||||
import { useNodeCanvasImagePreview } from '@/composables/node/useNodeCanvasImagePreview'
|
||||
import { useNodeImage, useNodeVideo } from '@/composables/node/useNodeImage'
|
||||
import {
|
||||
addWidgetPromotionOptions,
|
||||
isPreviewPseudoWidget
|
||||
} from '@/core/graph/subgraph/promotionUtils'
|
||||
import { addWidgetPromotionOptions } from '@/core/graph/subgraph/promotionUtils'
|
||||
import { isPreviewPseudoWidget } from '@/core/graph/subgraph/promotionPolicy'
|
||||
import { applyDynamicInputs } from '@/core/graph/widgets/dynamicWidgets'
|
||||
import { st, t } from '@/i18n'
|
||||
import {
|
||||
|
||||
Reference in New Issue
Block a user