feat: add Painter Node (#8521)

## Summary
Add PainterNode widget for freehand mask drawing directly on the canvas,
with brush/eraser tools, opacity, hardness, and background color
controls.

need BE changes https://github.com/Comfy-Org/ComfyUI/pull/12294

## Screenshots (if applicable)


https://github.com/user-attachments/assets/7222063a-0e40-40bb-b72e-b42c8984beb9



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8521-feat-add-Painter-Node-2fa6d73d36508124ab2ede449a0cc67a)
by [Unito](https://www.unito.io)
This commit is contained in:
Terry Jia
2026-02-26 00:08:49 -05:00
committed by GitHub
parent 2cb4c5eff3
commit 5cfd1aa77e
11 changed files with 1243 additions and 1 deletions

View File

@@ -0,0 +1,16 @@
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2'
import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
export const usePainterWidget = (): ComfyWidgetConstructorV2 => {
return (node: LGraphNode, inputSpec: InputSpecV2): IBaseWidget => {
return node.addWidget(
'painter',
inputSpec.name,
(inputSpec.default as string) ?? '',
null,
{ serialize: true, canvasOnly: false }
) as IBaseWidget
}
}

View File

@@ -60,6 +60,9 @@ const WidgetBoundingBox = defineAsyncComponent(
const WidgetCurve = defineAsyncComponent(
() => import('@/components/curve/WidgetCurve.vue')
)
const WidgetPainter = defineAsyncComponent(
() => import('@/components/painter/WidgetPainter.vue')
)
export const FOR_TESTING = {
WidgetButton,
@@ -186,6 +189,14 @@ const coreWidgetDefinitions: Array<[string, WidgetDefinition]> = [
aliases: ['CURVE'],
essential: false
}
],
[
'painter',
{
component: WidgetPainter,
aliases: ['PAINTER'],
essential: false
}
]
]
@@ -217,7 +228,13 @@ export const shouldRenderAsVue = (widget: Partial<SafeWidgetData>): boolean => {
return !widget.options?.canvasOnly && !!widget.type
}
const EXPANDING_TYPES = ['textarea', 'markdown', 'load3D', 'curve'] as const
const EXPANDING_TYPES = [
'textarea',
'markdown',
'load3D',
'curve',
'painter'
] as const
export function shouldExpand(type: string): boolean {
const canonicalType = getCanonicalType(type)