Skip to content

Commit 2425e32

Browse files
huchenleigithub-actions
andauthored
Partial execute to selected output nodes (#3818)
Co-authored-by: github-actions <[email protected]>
1 parent b6466c4 commit 2425e32

File tree

21 files changed

+351
-12
lines changed

21 files changed

+351
-12
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{
2+
"id": "1a95532f-c8aa-4c9d-a7f6-f928ba2d4862",
3+
"revision": 0,
4+
"last_node_id": 4,
5+
"last_link_id": 3,
6+
"nodes": [
7+
{
8+
"id": 4,
9+
"type": "PreviewAny",
10+
"pos": [946.2566528320312, 598.4373168945312],
11+
"size": [140, 76],
12+
"flags": {},
13+
"order": 2,
14+
"mode": 0,
15+
"inputs": [
16+
{
17+
"name": "source",
18+
"type": "*",
19+
"link": 3
20+
}
21+
],
22+
"outputs": [],
23+
"properties": {
24+
"Node name for S&R": "PreviewAny"
25+
},
26+
"widgets_values": []
27+
},
28+
{
29+
"id": 1,
30+
"type": "PreviewAny",
31+
"pos": [951.0236206054688, 421.3861083984375],
32+
"size": [140, 76],
33+
"flags": {},
34+
"order": 1,
35+
"mode": 0,
36+
"inputs": [
37+
{
38+
"name": "source",
39+
"type": "*",
40+
"link": 2
41+
}
42+
],
43+
"outputs": [],
44+
"properties": {
45+
"Node name for S&R": "PreviewAny"
46+
},
47+
"widgets_values": []
48+
},
49+
{
50+
"id": 3,
51+
"type": "PrimitiveString",
52+
"pos": [575.1760864257812, 504.5214538574219],
53+
"size": [270, 58],
54+
"flags": {},
55+
"order": 0,
56+
"mode": 0,
57+
"inputs": [],
58+
"outputs": [
59+
{
60+
"name": "STRING",
61+
"type": "STRING",
62+
"links": [2, 3]
63+
}
64+
],
65+
"properties": {
66+
"Node name for S&R": "PrimitiveString"
67+
},
68+
"widgets_values": ["foo"]
69+
}
70+
],
71+
"links": [
72+
[2, 3, 0, 1, 0, "*"],
73+
[3, 3, 0, 4, 0, "*"]
74+
],
75+
"groups": [],
76+
"config": {},
77+
"extra": {
78+
"frontendVersion": "1.19.1",
79+
"ds": {
80+
"offset": [400, 400],
81+
"scale": 1
82+
}
83+
},
84+
"version": 0.4
85+
}

browser_tests/tests/execution.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,27 @@ test.describe('Execution', () => {
1818
)
1919
})
2020
})
21+
22+
test.describe('Execute to selected output nodes', () => {
23+
test('Execute to selected output nodes', async ({ comfyPage }) => {
24+
await comfyPage.loadWorkflow('execution/partial_execution')
25+
const input = await comfyPage.getNodeRefById(3)
26+
const output1 = await comfyPage.getNodeRefById(1)
27+
const output2 = await comfyPage.getNodeRefById(4)
28+
expect(await (await input.getWidget(0)).getValue()).toBe('foo')
29+
expect(await (await output1.getWidget(0)).getValue()).toBe('')
30+
expect(await (await output2.getWidget(0)).getValue()).toBe('')
31+
32+
await output1.click('title')
33+
34+
await comfyPage.executeCommand('Comfy.QueueSelectedOutputNodes')
35+
// @note: Wait for the execution to finish. We might want to move to a more
36+
// reliable way to wait for the execution to finish. Workflow in this test
37+
// is simple enough that this is fine for now.
38+
await comfyPage.page.waitForTimeout(200)
39+
40+
expect(await (await input.getWidget(0)).getValue()).toBe('foo')
41+
expect(await (await output1.getWidget(0)).getValue()).toBe('foo')
42+
expect(await (await output2.getWidget(0)).getValue()).toBe('')
43+
})
44+
})

src/components/graph/SelectionToolbox.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
content: 'p-0 flex flex-row'
77
}"
88
>
9+
<ExecuteButton v-show="nodeSelected" />
910
<ColorPickerButton v-show="nodeSelected || groupSelected" />
1011
<Button
1112
v-show="nodeSelected"
@@ -74,6 +75,7 @@ import Panel from 'primevue/panel'
7475
import { computed } from 'vue'
7576
7677
import ColorPickerButton from '@/components/graph/selectionToolbox/ColorPickerButton.vue'
78+
import ExecuteButton from '@/components/graph/selectionToolbox/ExecuteButton.vue'
7779
import { useRefreshableSelection } from '@/composables/useRefreshableSelection'
7880
import { st, t } from '@/i18n'
7981
import { useExtensionService } from '@/services/extensionService'
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<template>
2+
<Button
3+
v-tooltip.top="{
4+
value: isDisabled
5+
? t('selectionToolbox.executeButton.disabledTooltip')
6+
: t('selectionToolbox.executeButton.tooltip'),
7+
showDelay: 1000
8+
}"
9+
:severity="isDisabled ? 'secondary' : 'success'"
10+
text
11+
:disabled="isDisabled"
12+
@mouseenter="() => handleMouseEnter()"
13+
@mouseleave="() => handleMouseLeave()"
14+
@click="handleClick"
15+
>
16+
<i-lucide:play />
17+
</Button>
18+
</template>
19+
20+
<script setup lang="ts">
21+
import type { LGraphNode } from '@comfyorg/litegraph'
22+
import Button from 'primevue/button'
23+
import { computed, ref } from 'vue'
24+
import { useI18n } from 'vue-i18n'
25+
26+
import { useCommandStore } from '@/stores/commandStore'
27+
import { useCanvasStore } from '@/stores/graphStore'
28+
import { isLGraphNode } from '@/utils/litegraphUtil'
29+
30+
const { t } = useI18n()
31+
const canvasStore = useCanvasStore()
32+
const commandStore = useCommandStore()
33+
34+
const canvas = canvasStore.getCanvas()
35+
const buttonHovered = ref(false)
36+
const selectedOutputNodes = computed(
37+
() =>
38+
canvasStore.selectedItems.filter(
39+
(item) => isLGraphNode(item) && item.constructor.nodeData.output_node
40+
) as LGraphNode[]
41+
)
42+
43+
const isDisabled = computed(() => selectedOutputNodes.value.length === 0)
44+
45+
function outputNodeStokeStyle(this: LGraphNode) {
46+
if (
47+
this.selected &&
48+
this.constructor.nodeData.output_node &&
49+
buttonHovered.value
50+
) {
51+
return { color: 'orange', lineWidth: 2, padding: 10 }
52+
}
53+
}
54+
55+
const handleMouseEnter = () => {
56+
buttonHovered.value = true
57+
for (const node of selectedOutputNodes.value) {
58+
node.strokeStyles['outputNode'] = outputNodeStokeStyle
59+
}
60+
canvas.setDirty(true)
61+
}
62+
63+
const handleMouseLeave = () => {
64+
buttonHovered.value = false
65+
canvas.setDirty(true)
66+
}
67+
68+
const handleClick = async () => {
69+
await commandStore.execute('Comfy.QueueSelectedOutputNodes')
70+
}
71+
</script>

src/composables/useCoreCommands.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,28 @@ export function useCoreCommands(): ComfyCommand[] {
312312
await app.queuePrompt(-1, batchCount)
313313
}
314314
},
315+
{
316+
id: 'Comfy.QueueSelectedOutputNodes',
317+
icon: 'pi pi-play',
318+
label: 'Queue Selected Output Nodes',
319+
versionAdded: '1.19.6',
320+
function: async () => {
321+
const batchCount = useQueueSettingsStore().batchCount
322+
const queueNodeIds = getSelectedNodes()
323+
.filter((node) => node.constructor.nodeData.output_node)
324+
.map((node) => node.id)
325+
if (queueNodeIds.length === 0) {
326+
toastStore.add({
327+
severity: 'error',
328+
summary: t('toastMessages.nothingToQueue'),
329+
detail: t('toastMessages.pleaseSelectOutputNodes'),
330+
life: 3000
331+
})
332+
return
333+
}
334+
await app.queuePrompt(0, batchCount, queueNodeIds)
335+
}
336+
},
315337
{
316338
id: 'Comfy.ShowSettingsDialog',
317339
icon: 'pi pi-cog',

src/locales/en/commands.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@
155155
"Comfy_QueuePromptFront": {
156156
"label": "Queue Prompt (Front)"
157157
},
158+
"Comfy_QueueSelectedOutputNodes": {
159+
"label": "Queue Selected Output Nodes"
160+
},
158161
"Comfy_Redo": {
159162
"label": "Redo"
160163
},

src/locales/en/main.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,7 @@
803803
"Open": "Open",
804804
"Queue Prompt": "Queue Prompt",
805805
"Queue Prompt (Front)": "Queue Prompt (Front)",
806+
"Queue Selected Output Nodes": "Queue Selected Output Nodes",
806807
"Redo": "Redo",
807808
"Refresh Node Definitions": "Refresh Node Definitions",
808809
"Save": "Save",
@@ -1197,6 +1198,8 @@
11971198
"resizeNodeMatchOutput": "Resize Node to match output"
11981199
},
11991200
"toastMessages": {
1201+
"nothingToQueue": "Nothing to queue",
1202+
"pleaseSelectOutputNodes": "Please select output nodes",
12001203
"no3dScene": "No 3D scene to apply texture",
12011204
"failedToApplyTexture": "Failed to apply texture",
12021205
"no3dSceneToExport": "No 3D scene to export",
@@ -1343,5 +1346,11 @@
13431346
"title": "Run!"
13441347
}
13451348
}
1349+
},
1350+
"selectionToolbox": {
1351+
"executeButton": {
1352+
"tooltip": "Execute to selected output nodes (Highlighted with orange border)",
1353+
"disabledTooltip": "No output nodes selected"
1354+
}
13461355
}
13471356
}

src/locales/es/commands.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@
155155
"Comfy_QueuePromptFront": {
156156
"label": "Prompt de Cola (Frente)"
157157
},
158+
"Comfy_QueueSelectedOutputNodes": {
159+
"label": "Encolar nodos de salida seleccionados"
160+
},
158161
"Comfy_Redo": {
159162
"label": "Rehacer"
160163
},

src/locales/es/main.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@
695695
"Previous Opened Workflow": "Flujo de trabajo abierto anterior",
696696
"Queue Prompt": "Indicador de cola",
697697
"Queue Prompt (Front)": "Indicador de cola (Frente)",
698+
"Queue Selected Output Nodes": "Encolar nodos de salida seleccionados",
698699
"Quit": "Salir",
699700
"Redo": "Rehacer",
700701
"Refresh Node Definitions": "Actualizar definiciones de nodo",
@@ -801,6 +802,12 @@
801802
},
802803
"title": "Tu dispositivo no es compatible"
803804
},
805+
"selectionToolbox": {
806+
"executeButton": {
807+
"disabledTooltip": "No hay nodos de salida seleccionados",
808+
"tooltip": "Ejecutar en los nodos de salida seleccionados (resaltados con borde naranja)"
809+
}
810+
},
804811
"serverConfig": {
805812
"modifiedConfigs": "Has modificado las siguientes configuraciones del servidor. Reinicia para aplicar los cambios.",
806813
"restart": "Reiniciar",
@@ -1297,8 +1304,10 @@
12971304
"noTemplatesToExport": "No hay plantillas para exportar",
12981305
"nodeDefinitionsUpdated": "Definiciones de nodos actualizadas",
12991306
"nothingToGroup": "Nada para agrupar",
1307+
"nothingToQueue": "Nada para poner en cola",
13001308
"pendingTasksDeleted": "Tareas pendientes eliminadas",
13011309
"pleaseSelectNodesToGroup": "Por favor, seleccione los nodos (u otros grupos) para crear un grupo para",
1310+
"pleaseSelectOutputNodes": "Por favor, selecciona los nodos de salida",
13021311
"unableToGetModelFilePath": "No se puede obtener la ruta del archivo del modelo",
13031312
"unauthorizedDomain": "Tu dominio {domain} no está autorizado para usar este servicio. Por favor, contacta a {email} para agregar tu dominio a la lista blanca.",
13041313
"updateRequested": "Actualización solicitada",

src/locales/fr/commands.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@
155155
"Comfy_QueuePromptFront": {
156156
"label": "Invite de file d'attente (avant)"
157157
},
158+
"Comfy_QueueSelectedOutputNodes": {
159+
"label": "Mettre en file d’attente les nœuds de sortie sélectionnés"
160+
},
158161
"Comfy_Redo": {
159162
"label": "Refaire"
160163
},

0 commit comments

Comments
 (0)