mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-01 13:59:54 +00:00
[refactor] Remove obsolete Kontext Edit Button (#5108)
* feat: Remove obsolete Kontext Edit Button Removes the 'Kontext Edit Button' and its associated code, as it has been made obsolete by the new 'Subgraphs + Partial Execution' feature. Fixes #5093 * Update locales [skip ci] --------- Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -12,7 +12,6 @@
|
||||
<ColorPickerButton />
|
||||
<BypassButton />
|
||||
<PinButton />
|
||||
<EditModelButton />
|
||||
<Load3DViewerButton />
|
||||
<MaskEditorButton />
|
||||
<ConvertToSubgraphButton />
|
||||
@@ -35,7 +34,6 @@ import BypassButton from '@/components/graph/selectionToolbox/BypassButton.vue'
|
||||
import ColorPickerButton from '@/components/graph/selectionToolbox/ColorPickerButton.vue'
|
||||
import ConvertToSubgraphButton from '@/components/graph/selectionToolbox/ConvertToSubgraphButton.vue'
|
||||
import DeleteButton from '@/components/graph/selectionToolbox/DeleteButton.vue'
|
||||
import EditModelButton from '@/components/graph/selectionToolbox/EditModelButton.vue'
|
||||
import ExecuteButton from '@/components/graph/selectionToolbox/ExecuteButton.vue'
|
||||
import ExtensionCommandButton from '@/components/graph/selectionToolbox/ExtensionCommandButton.vue'
|
||||
import HelpButton from '@/components/graph/selectionToolbox/HelpButton.vue'
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<template>
|
||||
<Button
|
||||
v-show="isImageOutputSelected"
|
||||
v-tooltip.top="{
|
||||
value: t('commands.Comfy_Canvas_AddEditModelStep.label'),
|
||||
showDelay: 1000
|
||||
}"
|
||||
severity="secondary"
|
||||
text
|
||||
icon="pi pi-pen-to-square"
|
||||
@click="() => commandStore.execute('Comfy.Canvas.AddEditModelStep')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useCanvasStore } from '@/stores/graphStore'
|
||||
import { isImageNode, isLGraphNode } from '@/utils/litegraphUtil'
|
||||
|
||||
const { t } = useI18n()
|
||||
const commandStore = useCommandStore()
|
||||
const canvasStore = useCanvasStore()
|
||||
|
||||
const isImageOutputOrEditModelNode = (node: unknown) =>
|
||||
isLGraphNode(node) &&
|
||||
(isImageNode(node) || node.type === 'workflow>FLUX.1 Kontext Image Edit')
|
||||
|
||||
const isImageOutputSelected = computed(
|
||||
() =>
|
||||
canvasStore.selectedItems.length === 1 &&
|
||||
isImageOutputOrEditModelNode(canvasStore.selectedItems[0])
|
||||
)
|
||||
</script>
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
import { Point } from '@/lib/litegraph/src/litegraph'
|
||||
import { api } from '@/scripts/api'
|
||||
import { app } from '@/scripts/app'
|
||||
import { addFluxKontextGroupNode } from '@/scripts/fluxKontextEditNode'
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
import { useLitegraphService } from '@/services/litegraphService'
|
||||
import { useWorkflowService } from '@/services/workflowService'
|
||||
@@ -775,17 +774,6 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
versionAdded: moveSelectedNodesVersionAdded,
|
||||
function: () => moveSelectedNodes(([x, y], gridSize) => [x + gridSize, y])
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Canvas.AddEditModelStep',
|
||||
icon: 'pi pi-pen-to-square',
|
||||
label: 'Add Edit Model Step',
|
||||
versionAdded: '1.23.3',
|
||||
function: async () => {
|
||||
const node = app.canvas.selectedItems.values().next().value
|
||||
if (!(node instanceof LGraphNode)) return
|
||||
await addFluxKontextGroupNode(node)
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Graph.ConvertToSubgraph',
|
||||
icon: 'pi pi-sitemap',
|
||||
|
||||
@@ -41,9 +41,6 @@
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "تصفح القوالب"
|
||||
},
|
||||
"Comfy_Canvas_AddEditModelStep": {
|
||||
"label": "إضافة خطوة تحرير النموذج"
|
||||
},
|
||||
"Comfy_Canvas_DeleteSelectedItems": {
|
||||
"label": "حذف العناصر المحددة"
|
||||
},
|
||||
|
||||
@@ -762,7 +762,6 @@
|
||||
},
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "حول ComfyUI",
|
||||
"Add Edit Model Step": "إضافة خطوة تعديل النموذج",
|
||||
"Bottom Panel": "لوحة سفلية",
|
||||
"Browse Templates": "تصفح القوالب",
|
||||
"Bypass/Unbypass Selected Nodes": "تجاوز/إلغاء تجاوز العقد المحددة",
|
||||
|
||||
@@ -41,9 +41,6 @@
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "Browse Templates"
|
||||
},
|
||||
"Comfy_Canvas_AddEditModelStep": {
|
||||
"label": "Add Edit Model Step"
|
||||
},
|
||||
"Comfy_Canvas_DeleteSelectedItems": {
|
||||
"label": "Delete Selected Items"
|
||||
},
|
||||
|
||||
@@ -958,7 +958,6 @@
|
||||
"Restart": "Restart",
|
||||
"Open 3D Viewer (Beta) for Selected Node": "Open 3D Viewer (Beta) for Selected Node",
|
||||
"Browse Templates": "Browse Templates",
|
||||
"Add Edit Model Step": "Add Edit Model Step",
|
||||
"Delete Selected Items": "Delete Selected Items",
|
||||
"Zoom to fit": "Zoom to fit",
|
||||
"Move Selected Nodes Down": "Move Selected Nodes Down",
|
||||
|
||||
@@ -41,9 +41,6 @@
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "Explorar plantillas"
|
||||
},
|
||||
"Comfy_Canvas_AddEditModelStep": {
|
||||
"label": "Agregar paso de edición de modelo"
|
||||
},
|
||||
"Comfy_Canvas_DeleteSelectedItems": {
|
||||
"label": "Eliminar elementos seleccionados"
|
||||
},
|
||||
|
||||
@@ -762,7 +762,6 @@
|
||||
},
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "Acerca de ComfyUI",
|
||||
"Add Edit Model Step": "Agregar paso de edición de modelo",
|
||||
"Bottom Panel": "Panel inferior",
|
||||
"Browse Templates": "Explorar plantillas",
|
||||
"Bypass/Unbypass Selected Nodes": "Evitar/No evitar nodos seleccionados",
|
||||
|
||||
@@ -41,9 +41,6 @@
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "Parcourir les modèles"
|
||||
},
|
||||
"Comfy_Canvas_AddEditModelStep": {
|
||||
"label": "Ajouter/Modifier une étape de modèle"
|
||||
},
|
||||
"Comfy_Canvas_DeleteSelectedItems": {
|
||||
"label": "Supprimer les éléments sélectionnés"
|
||||
},
|
||||
|
||||
@@ -762,7 +762,6 @@
|
||||
},
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "À propos de ComfyUI",
|
||||
"Add Edit Model Step": "Ajouter une étape d’édition de modèle",
|
||||
"Bottom Panel": "Panneau inférieur",
|
||||
"Browse Templates": "Parcourir les modèles",
|
||||
"Bypass/Unbypass Selected Nodes": "Contourner/Ne pas contourner les nœuds sélectionnés",
|
||||
|
||||
@@ -41,9 +41,6 @@
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "テンプレートを参照"
|
||||
},
|
||||
"Comfy_Canvas_AddEditModelStep": {
|
||||
"label": "編集モデルステップを追加"
|
||||
},
|
||||
"Comfy_Canvas_DeleteSelectedItems": {
|
||||
"label": "選択したアイテムを削除"
|
||||
},
|
||||
|
||||
@@ -762,7 +762,6 @@
|
||||
},
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "ComfyUIについて",
|
||||
"Add Edit Model Step": "モデル編集ステップを追加",
|
||||
"Bottom Panel": "下部パネル",
|
||||
"Browse Templates": "テンプレートを参照",
|
||||
"Bypass/Unbypass Selected Nodes": "選択したノードのバイパス/バイパス解除",
|
||||
|
||||
@@ -41,9 +41,6 @@
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "템플릿 탐색"
|
||||
},
|
||||
"Comfy_Canvas_AddEditModelStep": {
|
||||
"label": "모델 편집 단계 추가"
|
||||
},
|
||||
"Comfy_Canvas_DeleteSelectedItems": {
|
||||
"label": "선택한 항목 삭제"
|
||||
},
|
||||
|
||||
@@ -762,7 +762,6 @@
|
||||
},
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "ComfyUI에 대하여",
|
||||
"Add Edit Model Step": "모델 편집 단계 추가",
|
||||
"Bottom Panel": "하단 패널",
|
||||
"Browse Templates": "템플릿 탐색",
|
||||
"Bypass/Unbypass Selected Nodes": "선택한 노드 우회/우회 해제",
|
||||
|
||||
@@ -41,9 +41,6 @@
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "Просмотр шаблонов"
|
||||
},
|
||||
"Comfy_Canvas_AddEditModelStep": {
|
||||
"label": "Добавить или изменить шаг модели"
|
||||
},
|
||||
"Comfy_Canvas_DeleteSelectedItems": {
|
||||
"label": "Удалить выбранные элементы"
|
||||
},
|
||||
|
||||
@@ -762,7 +762,6 @@
|
||||
},
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "О ComfyUI",
|
||||
"Add Edit Model Step": "Добавить или изменить шаг модели",
|
||||
"Bottom Panel": "Нижняя панель",
|
||||
"Browse Templates": "Просмотреть шаблоны",
|
||||
"Bypass/Unbypass Selected Nodes": "Обойти/восстановить выбранные ноды",
|
||||
|
||||
@@ -41,9 +41,6 @@
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "瀏覽範本"
|
||||
},
|
||||
"Comfy_Canvas_AddEditModelStep": {
|
||||
"label": "新增編輯模型步驟"
|
||||
},
|
||||
"Comfy_Canvas_DeleteSelectedItems": {
|
||||
"label": "刪除選取項目"
|
||||
},
|
||||
|
||||
@@ -762,7 +762,6 @@
|
||||
},
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "關於 ComfyUI",
|
||||
"Add Edit Model Step": "新增編輯模型步驟",
|
||||
"Bottom Panel": "底部面板",
|
||||
"Browse Templates": "瀏覽範本",
|
||||
"Bypass/Unbypass Selected Nodes": "繞過/取消繞過選取節點",
|
||||
|
||||
@@ -41,9 +41,6 @@
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "浏览模板"
|
||||
},
|
||||
"Comfy_Canvas_AddEditModelStep": {
|
||||
"label": "添加编辑模型步骤"
|
||||
},
|
||||
"Comfy_Canvas_DeleteSelectedItems": {
|
||||
"label": "删除选定的项目"
|
||||
},
|
||||
|
||||
@@ -762,7 +762,6 @@
|
||||
},
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "关于ComfyUI",
|
||||
"Add Edit Model Step": "添加编辑模型步骤",
|
||||
"Bottom Panel": "底部面板",
|
||||
"Browse Templates": "浏览模板",
|
||||
"Bypass/Unbypass Selected Nodes": "忽略/取消忽略选定节点",
|
||||
|
||||
@@ -1,693 +0,0 @@
|
||||
import _ from 'es-toolkit/compat'
|
||||
|
||||
import {
|
||||
type INodeOutputSlot,
|
||||
type LGraph,
|
||||
type LGraphNode,
|
||||
LLink,
|
||||
LiteGraph,
|
||||
type Point
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
|
||||
import { parseFilePath } from '@/utils/formatUtil'
|
||||
|
||||
import { app } from './app'
|
||||
|
||||
const fluxKontextGroupNode = {
|
||||
nodes: [
|
||||
{
|
||||
id: -1,
|
||||
type: 'Reroute',
|
||||
pos: [2354.87890625, -127.23468780517578],
|
||||
size: [75, 26],
|
||||
flags: {},
|
||||
order: 20,
|
||||
mode: 0,
|
||||
inputs: [{ name: '', type: '*', link: null }],
|
||||
outputs: [{ name: '', type: '*', links: null }],
|
||||
properties: { showOutputText: false, horizontal: false },
|
||||
index: 0
|
||||
},
|
||||
{
|
||||
id: -1,
|
||||
type: 'ReferenceLatent',
|
||||
pos: [2730, -220],
|
||||
size: [197.712890625, 46],
|
||||
flags: {},
|
||||
order: 22,
|
||||
mode: 0,
|
||||
inputs: [
|
||||
{
|
||||
localized_name: 'conditioning',
|
||||
name: 'conditioning',
|
||||
type: 'CONDITIONING',
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'latent',
|
||||
name: 'latent',
|
||||
shape: 7,
|
||||
type: 'LATENT',
|
||||
link: null
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
localized_name: 'CONDITIONING',
|
||||
name: 'CONDITIONING',
|
||||
type: 'CONDITIONING',
|
||||
links: []
|
||||
}
|
||||
],
|
||||
properties: {
|
||||
'Node name for S&R': 'ReferenceLatent',
|
||||
cnr_id: 'comfy-core',
|
||||
ver: '0.3.38'
|
||||
},
|
||||
index: 1
|
||||
},
|
||||
{
|
||||
id: -1,
|
||||
type: 'VAEDecode',
|
||||
pos: [3270, -110],
|
||||
size: [210, 46],
|
||||
flags: {},
|
||||
order: 25,
|
||||
mode: 0,
|
||||
inputs: [
|
||||
{
|
||||
localized_name: 'samples',
|
||||
name: 'samples',
|
||||
type: 'LATENT',
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'vae',
|
||||
name: 'vae',
|
||||
type: 'VAE',
|
||||
link: null
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
localized_name: 'IMAGE',
|
||||
name: 'IMAGE',
|
||||
type: 'IMAGE',
|
||||
slot_index: 0,
|
||||
links: []
|
||||
}
|
||||
],
|
||||
properties: {
|
||||
'Node name for S&R': 'VAEDecode',
|
||||
cnr_id: 'comfy-core',
|
||||
ver: '0.3.38'
|
||||
},
|
||||
index: 2
|
||||
},
|
||||
{
|
||||
id: -1,
|
||||
type: 'KSampler',
|
||||
pos: [2930, -110],
|
||||
size: [315, 262],
|
||||
flags: {},
|
||||
order: 24,
|
||||
mode: 0,
|
||||
inputs: [
|
||||
{
|
||||
localized_name: 'model',
|
||||
name: 'model',
|
||||
type: 'MODEL',
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'positive',
|
||||
name: 'positive',
|
||||
type: 'CONDITIONING',
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'negative',
|
||||
name: 'negative',
|
||||
type: 'CONDITIONING',
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'latent_image',
|
||||
name: 'latent_image',
|
||||
type: 'LATENT',
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'seed',
|
||||
name: 'seed',
|
||||
type: 'INT',
|
||||
widget: { name: 'seed' },
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'steps',
|
||||
name: 'steps',
|
||||
type: 'INT',
|
||||
widget: { name: 'steps' },
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'cfg',
|
||||
name: 'cfg',
|
||||
type: 'FLOAT',
|
||||
widget: { name: 'cfg' },
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'sampler_name',
|
||||
name: 'sampler_name',
|
||||
type: 'COMBO',
|
||||
widget: { name: 'sampler_name' },
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'scheduler',
|
||||
name: 'scheduler',
|
||||
type: 'COMBO',
|
||||
widget: { name: 'scheduler' },
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'denoise',
|
||||
name: 'denoise',
|
||||
type: 'FLOAT',
|
||||
widget: { name: 'denoise' },
|
||||
link: null
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
localized_name: 'LATENT',
|
||||
name: 'LATENT',
|
||||
type: 'LATENT',
|
||||
slot_index: 0,
|
||||
links: []
|
||||
}
|
||||
],
|
||||
properties: {
|
||||
'Node name for S&R': 'KSampler',
|
||||
cnr_id: 'comfy-core',
|
||||
ver: '0.3.38'
|
||||
},
|
||||
widgets_values: [972054013131369, 'fixed', 20, 1, 'euler', 'simple', 1],
|
||||
index: 3
|
||||
},
|
||||
{
|
||||
id: -1,
|
||||
type: 'FluxGuidance',
|
||||
pos: [2940, -220],
|
||||
size: [211.60000610351562, 58],
|
||||
flags: {},
|
||||
order: 23,
|
||||
mode: 0,
|
||||
inputs: [
|
||||
{
|
||||
localized_name: 'conditioning',
|
||||
name: 'conditioning',
|
||||
type: 'CONDITIONING',
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'guidance',
|
||||
name: 'guidance',
|
||||
type: 'FLOAT',
|
||||
widget: { name: 'guidance' },
|
||||
link: null
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
localized_name: 'CONDITIONING',
|
||||
name: 'CONDITIONING',
|
||||
type: 'CONDITIONING',
|
||||
slot_index: 0,
|
||||
links: []
|
||||
}
|
||||
],
|
||||
properties: {
|
||||
'Node name for S&R': 'FluxGuidance',
|
||||
cnr_id: 'comfy-core',
|
||||
ver: '0.3.38'
|
||||
},
|
||||
widgets_values: [2.5],
|
||||
index: 4
|
||||
},
|
||||
{
|
||||
id: -1,
|
||||
type: 'SaveImage',
|
||||
pos: [3490, -110],
|
||||
size: [985.3012084960938, 1060.3828125],
|
||||
flags: {},
|
||||
order: 26,
|
||||
mode: 0,
|
||||
inputs: [
|
||||
{
|
||||
localized_name: 'images',
|
||||
name: 'images',
|
||||
type: 'IMAGE',
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'filename_prefix',
|
||||
name: 'filename_prefix',
|
||||
type: 'STRING',
|
||||
widget: { name: 'filename_prefix' },
|
||||
link: null
|
||||
}
|
||||
],
|
||||
outputs: [],
|
||||
properties: { cnr_id: 'comfy-core', ver: '0.3.38' },
|
||||
widgets_values: ['ComfyUI'],
|
||||
index: 5
|
||||
},
|
||||
{
|
||||
id: -1,
|
||||
type: 'CLIPTextEncode',
|
||||
pos: [2500, -110],
|
||||
size: [422.84503173828125, 164.31304931640625],
|
||||
flags: {},
|
||||
order: 12,
|
||||
mode: 0,
|
||||
inputs: [
|
||||
{
|
||||
localized_name: 'clip',
|
||||
name: 'clip',
|
||||
type: 'CLIP',
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'text',
|
||||
name: 'text',
|
||||
type: 'STRING',
|
||||
widget: { name: 'text' },
|
||||
link: null
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
localized_name: 'CONDITIONING',
|
||||
name: 'CONDITIONING',
|
||||
type: 'CONDITIONING',
|
||||
slot_index: 0,
|
||||
links: []
|
||||
}
|
||||
],
|
||||
title: 'CLIP Text Encode (Positive Prompt)',
|
||||
properties: {
|
||||
'Node name for S&R': 'CLIPTextEncode',
|
||||
cnr_id: 'comfy-core',
|
||||
ver: '0.3.38'
|
||||
},
|
||||
widgets_values: ['there is a bright light'],
|
||||
color: '#232',
|
||||
bgcolor: '#353',
|
||||
index: 6
|
||||
},
|
||||
{
|
||||
id: -1,
|
||||
type: 'CLIPTextEncode',
|
||||
pos: [2504.1435546875, 97.9598617553711],
|
||||
size: [422.84503173828125, 164.31304931640625],
|
||||
flags: { collapsed: true },
|
||||
order: 13,
|
||||
mode: 0,
|
||||
inputs: [
|
||||
{
|
||||
localized_name: 'clip',
|
||||
name: 'clip',
|
||||
type: 'CLIP',
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'text',
|
||||
name: 'text',
|
||||
type: 'STRING',
|
||||
widget: { name: 'text' },
|
||||
link: null
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
localized_name: 'CONDITIONING',
|
||||
name: 'CONDITIONING',
|
||||
type: 'CONDITIONING',
|
||||
slot_index: 0,
|
||||
links: []
|
||||
}
|
||||
],
|
||||
title: 'CLIP Text Encode (Negative Prompt)',
|
||||
properties: {
|
||||
'Node name for S&R': 'CLIPTextEncode',
|
||||
cnr_id: 'comfy-core',
|
||||
ver: '0.3.38'
|
||||
},
|
||||
widgets_values: [''],
|
||||
color: '#322',
|
||||
bgcolor: '#533',
|
||||
index: 7
|
||||
},
|
||||
{
|
||||
id: -1,
|
||||
type: 'UNETLoader',
|
||||
pos: [2630, -370],
|
||||
size: [270, 82],
|
||||
flags: {},
|
||||
order: 6,
|
||||
mode: 0,
|
||||
inputs: [
|
||||
{
|
||||
localized_name: 'unet_name',
|
||||
name: 'unet_name',
|
||||
type: 'COMBO',
|
||||
widget: { name: 'unet_name' },
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'weight_dtype',
|
||||
name: 'weight_dtype',
|
||||
type: 'COMBO',
|
||||
widget: { name: 'weight_dtype' },
|
||||
link: null
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
localized_name: 'MODEL',
|
||||
name: 'MODEL',
|
||||
type: 'MODEL',
|
||||
links: []
|
||||
}
|
||||
],
|
||||
properties: {
|
||||
'Node name for S&R': 'UNETLoader',
|
||||
cnr_id: 'comfy-core',
|
||||
ver: '0.3.38'
|
||||
},
|
||||
widgets_values: ['flux1-kontext-dev.safetensors', 'default'],
|
||||
color: '#223',
|
||||
bgcolor: '#335',
|
||||
index: 8
|
||||
},
|
||||
{
|
||||
id: -1,
|
||||
type: 'DualCLIPLoader',
|
||||
pos: [2100, -290],
|
||||
size: [337.76861572265625, 130],
|
||||
flags: {},
|
||||
order: 8,
|
||||
mode: 0,
|
||||
inputs: [
|
||||
{
|
||||
localized_name: 'clip_name1',
|
||||
name: 'clip_name1',
|
||||
type: 'COMBO',
|
||||
widget: { name: 'clip_name1' },
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'clip_name2',
|
||||
name: 'clip_name2',
|
||||
type: 'COMBO',
|
||||
widget: { name: 'clip_name2' },
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'type',
|
||||
name: 'type',
|
||||
type: 'COMBO',
|
||||
widget: { name: 'type' },
|
||||
link: null
|
||||
},
|
||||
{
|
||||
localized_name: 'device',
|
||||
name: 'device',
|
||||
shape: 7,
|
||||
type: 'COMBO',
|
||||
widget: { name: 'device' },
|
||||
link: null
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
localized_name: 'CLIP',
|
||||
name: 'CLIP',
|
||||
type: 'CLIP',
|
||||
links: []
|
||||
}
|
||||
],
|
||||
properties: {
|
||||
'Node name for S&R': 'DualCLIPLoader',
|
||||
cnr_id: 'comfy-core',
|
||||
ver: '0.3.38'
|
||||
},
|
||||
widgets_values: [
|
||||
'clip_l.safetensors',
|
||||
't5xxl_fp8_e4m3fn_scaled.safetensors',
|
||||
'flux',
|
||||
'default'
|
||||
],
|
||||
color: '#223',
|
||||
bgcolor: '#335',
|
||||
index: 9
|
||||
},
|
||||
{
|
||||
id: -1,
|
||||
type: 'VAELoader',
|
||||
pos: [2960, -370],
|
||||
size: [270, 58],
|
||||
flags: {},
|
||||
order: 7,
|
||||
mode: 0,
|
||||
inputs: [
|
||||
{
|
||||
localized_name: 'vae_name',
|
||||
name: 'vae_name',
|
||||
type: 'COMBO',
|
||||
widget: { name: 'vae_name' },
|
||||
link: null
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
localized_name: 'VAE',
|
||||
name: 'VAE',
|
||||
type: 'VAE',
|
||||
links: []
|
||||
}
|
||||
],
|
||||
properties: {
|
||||
'Node name for S&R': 'VAELoader',
|
||||
cnr_id: 'comfy-core',
|
||||
ver: '0.3.38'
|
||||
},
|
||||
widgets_values: ['ae.safetensors'],
|
||||
color: '#223',
|
||||
bgcolor: '#335',
|
||||
index: 10
|
||||
}
|
||||
],
|
||||
links: [
|
||||
[6, 0, 1, 0, 72, 'CONDITIONING'],
|
||||
[0, 0, 1, 1, 66, '*'],
|
||||
[3, 0, 2, 0, 69, 'LATENT'],
|
||||
[10, 0, 2, 1, 76, 'VAE'],
|
||||
[8, 0, 3, 0, 74, 'MODEL'],
|
||||
[4, 0, 3, 1, 70, 'CONDITIONING'],
|
||||
[7, 0, 3, 2, 73, 'CONDITIONING'],
|
||||
[0, 0, 3, 3, 66, '*'],
|
||||
[1, 0, 4, 0, 67, 'CONDITIONING'],
|
||||
[2, 0, 5, 0, 68, 'IMAGE'],
|
||||
[9, 0, 6, 0, 75, 'CLIP'],
|
||||
[9, 0, 7, 0, 75, 'CLIP']
|
||||
],
|
||||
external: [],
|
||||
config: {
|
||||
'0': {},
|
||||
'1': {},
|
||||
'2': { output: { '0': { visible: true } } },
|
||||
'3': {
|
||||
output: { '0': { visible: true } },
|
||||
input: {
|
||||
denoise: { visible: false },
|
||||
cfg: { visible: false }
|
||||
}
|
||||
},
|
||||
'4': {},
|
||||
'5': {},
|
||||
'6': {},
|
||||
'7': { input: { text: { visible: false } } },
|
||||
'8': { input: { weight_dtype: { visible: false } } },
|
||||
'9': { input: { type: { visible: false }, device: { visible: false } } },
|
||||
'10': {}
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureGraphHasFluxKontextGroupNode(
|
||||
graph: LGraph & { extra: { groupNodes?: Record<string, any> } }
|
||||
) {
|
||||
graph.extra ??= {}
|
||||
graph.extra.groupNodes ??= {}
|
||||
if (graph.extra.groupNodes['FLUX.1 Kontext Image Edit']) return
|
||||
|
||||
graph.extra.groupNodes['FLUX.1 Kontext Image Edit'] =
|
||||
structuredClone(fluxKontextGroupNode)
|
||||
|
||||
// Lazy import to avoid circular dependency issues
|
||||
const { GroupNodeConfig } = await import('@/extensions/core/groupNode')
|
||||
await GroupNodeConfig.registerFromWorkflow(
|
||||
{
|
||||
'FLUX.1 Kontext Image Edit':
|
||||
graph.extra.groupNodes['FLUX.1 Kontext Image Edit']
|
||||
},
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
export async function addFluxKontextGroupNode(fromNode: LGraphNode) {
|
||||
const { canvas } = app
|
||||
const { graph } = canvas
|
||||
if (!graph) throw new TypeError('Graph is not initialized')
|
||||
await ensureGraphHasFluxKontextGroupNode(graph)
|
||||
|
||||
const node = LiteGraph.createNode('workflow>FLUX.1 Kontext Image Edit')
|
||||
if (!node) throw new TypeError('Failed to create node')
|
||||
|
||||
const pos = getPosToRightOfNode(fromNode)
|
||||
|
||||
graph.add(node)
|
||||
node.pos = pos
|
||||
app.canvas.processSelect(node, undefined)
|
||||
|
||||
connectPreviousLatent(fromNode, node)
|
||||
|
||||
const symb = Object.getOwnPropertySymbols(node)[0]
|
||||
// @ts-expect-error It's there -- promise.
|
||||
node[symb].populateWidgets()
|
||||
|
||||
setWidgetValues(node)
|
||||
}
|
||||
|
||||
function setWidgetValues(node: LGraphNode) {
|
||||
const seedInput = node.widgets?.find((x) => x.name === 'seed')
|
||||
if (!seedInput) throw new TypeError('Seed input not found')
|
||||
seedInput.value = Math.floor(Math.random() * 1_125_899_906_842_624)
|
||||
|
||||
const firstClip = node.widgets?.find((x) => x.name === 'clip_name1')
|
||||
setPreferredValue('t5xxl_fp8_e4m3fn_scaled.safetensors', 't5xxl', firstClip)
|
||||
|
||||
const secondClip = node.widgets?.find((x) => x.name === 'clip_name2')
|
||||
setPreferredValue('clip_l.safetensors', 'clip_l', secondClip)
|
||||
|
||||
const unet = node.widgets?.find((x) => x.name === 'unet_name')
|
||||
setPreferredValue('flux1-dev-kontext_fp8_scaled.safetensors', 'kontext', unet)
|
||||
|
||||
const vae = node.widgets?.find((x) => x.name === 'vae_name')
|
||||
setPreferredValue('ae.safetensors', 'ae.s', vae)
|
||||
}
|
||||
|
||||
function setPreferredValue(
|
||||
preferred: string,
|
||||
match: string,
|
||||
widget: IBaseWidget | undefined
|
||||
): void {
|
||||
if (!widget) throw new TypeError('Widget not found')
|
||||
|
||||
const { values } = widget.options
|
||||
if (!Array.isArray(values)) return
|
||||
|
||||
// Match against filename portion only
|
||||
const mapped = values.map((x) => parseFilePath(x).filename)
|
||||
const value =
|
||||
mapped.find((x) => x === preferred) ??
|
||||
mapped.find((x) => x.includes?.(match))
|
||||
widget.value = value ?? preferred
|
||||
}
|
||||
|
||||
function getPosToRightOfNode(fromNode: LGraphNode) {
|
||||
const nodes = app.canvas.graph?.nodes
|
||||
if (!nodes) throw new TypeError('Could not get graph nodes')
|
||||
|
||||
const pos = [
|
||||
fromNode.pos[0] + fromNode.size[0] + 100,
|
||||
fromNode.pos[1]
|
||||
] satisfies Point
|
||||
|
||||
while (nodes.find((x) => isPointTooClose(x.pos, pos))) {
|
||||
pos[0] += 20
|
||||
pos[1] += 20
|
||||
}
|
||||
|
||||
return pos
|
||||
}
|
||||
|
||||
function connectPreviousLatent(fromNode: LGraphNode, toEditNode: LGraphNode) {
|
||||
const { canvas } = app
|
||||
const { graph } = canvas
|
||||
if (!graph) throw new TypeError('Graph is not initialized')
|
||||
|
||||
const l = findNearestOutputOfType([fromNode], 'LATENT')
|
||||
if (!l) {
|
||||
const imageOutput = findNearestOutputOfType([fromNode], 'IMAGE')
|
||||
if (!imageOutput) throw new TypeError('No image output found')
|
||||
|
||||
const vaeEncode = LiteGraph.createNode('VAEEncode')
|
||||
if (!vaeEncode) throw new TypeError('Failed to create node')
|
||||
|
||||
const { node: imageNode, index: imageIndex } = imageOutput
|
||||
graph.add(vaeEncode)
|
||||
vaeEncode.pos = getPosToRightOfNode(fromNode)
|
||||
vaeEncode.pos[1] -= 200
|
||||
|
||||
vaeEncode.connect(0, toEditNode, 0)
|
||||
imageNode.connect(imageIndex, vaeEncode, 0)
|
||||
return
|
||||
}
|
||||
|
||||
const { node, index } = l
|
||||
|
||||
node.connect(index, toEditNode, 0)
|
||||
}
|
||||
|
||||
function getInputNodes(node: LGraphNode): LGraphNode[] {
|
||||
return node.inputs
|
||||
.map((x) => LLink.resolve(x.link, app.graph)?.outputNode)
|
||||
.filter((x) => !!x)
|
||||
}
|
||||
|
||||
function getOutputOfType(
|
||||
node: LGraphNode,
|
||||
type: string
|
||||
): {
|
||||
output: INodeOutputSlot
|
||||
index: number
|
||||
} {
|
||||
const index = node.outputs.findIndex((x) => x.type === type)
|
||||
const output = node.outputs[index]
|
||||
return { output, index }
|
||||
}
|
||||
|
||||
function findNearestOutputOfType(
|
||||
nodes: Iterable<LGraphNode>,
|
||||
type: string = 'LATENT',
|
||||
depth: number = 0
|
||||
): { node: LGraphNode; index: number } | undefined {
|
||||
for (const node of nodes) {
|
||||
const { output, index } = getOutputOfType(node, type)
|
||||
if (output) return { node, index }
|
||||
}
|
||||
|
||||
if (depth < 3) {
|
||||
const closestNodes = new Set([...nodes].flatMap((x) => getInputNodes(x)))
|
||||
const res = findNearestOutputOfType(closestNodes, type, depth + 1)
|
||||
if (res) return res
|
||||
}
|
||||
}
|
||||
|
||||
function isPointTooClose(a: Point, b: Point, precision: number = 5) {
|
||||
return Math.abs(a[0] - b[0]) < precision && Math.abs(a[1] - b[1]) < precision
|
||||
}
|
||||
Reference in New Issue
Block a user