diff --git a/src/components/graph/SelectionToolbox.vue b/src/components/graph/SelectionToolbox.vue
index d4cf408fcf..bbbeadef94 100644
--- a/src/components/graph/SelectionToolbox.vue
+++ b/src/components/graph/SelectionToolbox.vue
@@ -12,7 +12,6 @@
-
@@ -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'
diff --git a/src/components/graph/selectionToolbox/EditModelButton.vue b/src/components/graph/selectionToolbox/EditModelButton.vue
deleted file mode 100644
index d9f515ddd4..0000000000
--- a/src/components/graph/selectionToolbox/EditModelButton.vue
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts
index 80eeca78f7..fe6d112bba 100644
--- a/src/composables/useCoreCommands.ts
+++ b/src/composables/useCoreCommands.ts
@@ -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',
diff --git a/src/locales/ar/commands.json b/src/locales/ar/commands.json
index 760d0a1780..95b417dc1d 100644
--- a/src/locales/ar/commands.json
+++ b/src/locales/ar/commands.json
@@ -41,9 +41,6 @@
"Comfy_BrowseTemplates": {
"label": "تصفح القوالب"
},
- "Comfy_Canvas_AddEditModelStep": {
- "label": "إضافة خطوة تحرير النموذج"
- },
"Comfy_Canvas_DeleteSelectedItems": {
"label": "حذف العناصر المحددة"
},
diff --git a/src/locales/ar/main.json b/src/locales/ar/main.json
index af0549674a..8a9cc8b2cf 100644
--- a/src/locales/ar/main.json
+++ b/src/locales/ar/main.json
@@ -762,7 +762,6 @@
},
"menuLabels": {
"About ComfyUI": "حول ComfyUI",
- "Add Edit Model Step": "إضافة خطوة تعديل النموذج",
"Bottom Panel": "لوحة سفلية",
"Browse Templates": "تصفح القوالب",
"Bypass/Unbypass Selected Nodes": "تجاوز/إلغاء تجاوز العقد المحددة",
diff --git a/src/locales/en/commands.json b/src/locales/en/commands.json
index 5b0d9c445a..8508497e66 100644
--- a/src/locales/en/commands.json
+++ b/src/locales/en/commands.json
@@ -41,9 +41,6 @@
"Comfy_BrowseTemplates": {
"label": "Browse Templates"
},
- "Comfy_Canvas_AddEditModelStep": {
- "label": "Add Edit Model Step"
- },
"Comfy_Canvas_DeleteSelectedItems": {
"label": "Delete Selected Items"
},
diff --git a/src/locales/en/main.json b/src/locales/en/main.json
index b4c2537082..b6ed989a14 100644
--- a/src/locales/en/main.json
+++ b/src/locales/en/main.json
@@ -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",
diff --git a/src/locales/es/commands.json b/src/locales/es/commands.json
index 580515847e..d41962c03b 100644
--- a/src/locales/es/commands.json
+++ b/src/locales/es/commands.json
@@ -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"
},
diff --git a/src/locales/es/main.json b/src/locales/es/main.json
index e056b0afb3..ef27fc8c28 100644
--- a/src/locales/es/main.json
+++ b/src/locales/es/main.json
@@ -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",
diff --git a/src/locales/fr/commands.json b/src/locales/fr/commands.json
index 69a9f4fc32..e04e372b41 100644
--- a/src/locales/fr/commands.json
+++ b/src/locales/fr/commands.json
@@ -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"
},
diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json
index 3680db24eb..31dcbe86f6 100644
--- a/src/locales/fr/main.json
+++ b/src/locales/fr/main.json
@@ -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",
diff --git a/src/locales/ja/commands.json b/src/locales/ja/commands.json
index caaf7732d0..307ef72e56 100644
--- a/src/locales/ja/commands.json
+++ b/src/locales/ja/commands.json
@@ -41,9 +41,6 @@
"Comfy_BrowseTemplates": {
"label": "テンプレートを参照"
},
- "Comfy_Canvas_AddEditModelStep": {
- "label": "編集モデルステップを追加"
- },
"Comfy_Canvas_DeleteSelectedItems": {
"label": "選択したアイテムを削除"
},
diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json
index 34caa47a24..c3af5ee251 100644
--- a/src/locales/ja/main.json
+++ b/src/locales/ja/main.json
@@ -762,7 +762,6 @@
},
"menuLabels": {
"About ComfyUI": "ComfyUIについて",
- "Add Edit Model Step": "モデル編集ステップを追加",
"Bottom Panel": "下部パネル",
"Browse Templates": "テンプレートを参照",
"Bypass/Unbypass Selected Nodes": "選択したノードのバイパス/バイパス解除",
diff --git a/src/locales/ko/commands.json b/src/locales/ko/commands.json
index 7105cd8212..431ae7ae00 100644
--- a/src/locales/ko/commands.json
+++ b/src/locales/ko/commands.json
@@ -41,9 +41,6 @@
"Comfy_BrowseTemplates": {
"label": "템플릿 탐색"
},
- "Comfy_Canvas_AddEditModelStep": {
- "label": "모델 편집 단계 추가"
- },
"Comfy_Canvas_DeleteSelectedItems": {
"label": "선택한 항목 삭제"
},
diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json
index 672fad4fca..f11bc201f0 100644
--- a/src/locales/ko/main.json
+++ b/src/locales/ko/main.json
@@ -762,7 +762,6 @@
},
"menuLabels": {
"About ComfyUI": "ComfyUI에 대하여",
- "Add Edit Model Step": "모델 편집 단계 추가",
"Bottom Panel": "하단 패널",
"Browse Templates": "템플릿 탐색",
"Bypass/Unbypass Selected Nodes": "선택한 노드 우회/우회 해제",
diff --git a/src/locales/ru/commands.json b/src/locales/ru/commands.json
index fa3927190b..86fc88f176 100644
--- a/src/locales/ru/commands.json
+++ b/src/locales/ru/commands.json
@@ -41,9 +41,6 @@
"Comfy_BrowseTemplates": {
"label": "Просмотр шаблонов"
},
- "Comfy_Canvas_AddEditModelStep": {
- "label": "Добавить или изменить шаг модели"
- },
"Comfy_Canvas_DeleteSelectedItems": {
"label": "Удалить выбранные элементы"
},
diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json
index 3257bc1b11..04e2b2bef8 100644
--- a/src/locales/ru/main.json
+++ b/src/locales/ru/main.json
@@ -762,7 +762,6 @@
},
"menuLabels": {
"About ComfyUI": "О ComfyUI",
- "Add Edit Model Step": "Добавить или изменить шаг модели",
"Bottom Panel": "Нижняя панель",
"Browse Templates": "Просмотреть шаблоны",
"Bypass/Unbypass Selected Nodes": "Обойти/восстановить выбранные ноды",
diff --git a/src/locales/zh-TW/commands.json b/src/locales/zh-TW/commands.json
index 71d1907b6f..46fbcb30d5 100644
--- a/src/locales/zh-TW/commands.json
+++ b/src/locales/zh-TW/commands.json
@@ -41,9 +41,6 @@
"Comfy_BrowseTemplates": {
"label": "瀏覽範本"
},
- "Comfy_Canvas_AddEditModelStep": {
- "label": "新增編輯模型步驟"
- },
"Comfy_Canvas_DeleteSelectedItems": {
"label": "刪除選取項目"
},
diff --git a/src/locales/zh-TW/main.json b/src/locales/zh-TW/main.json
index b61ea22cc5..fe0d5f4168 100644
--- a/src/locales/zh-TW/main.json
+++ b/src/locales/zh-TW/main.json
@@ -762,7 +762,6 @@
},
"menuLabels": {
"About ComfyUI": "關於 ComfyUI",
- "Add Edit Model Step": "新增編輯模型步驟",
"Bottom Panel": "底部面板",
"Browse Templates": "瀏覽範本",
"Bypass/Unbypass Selected Nodes": "繞過/取消繞過選取節點",
diff --git a/src/locales/zh/commands.json b/src/locales/zh/commands.json
index a42111c547..5d319aa170 100644
--- a/src/locales/zh/commands.json
+++ b/src/locales/zh/commands.json
@@ -41,9 +41,6 @@
"Comfy_BrowseTemplates": {
"label": "浏览模板"
},
- "Comfy_Canvas_AddEditModelStep": {
- "label": "添加编辑模型步骤"
- },
"Comfy_Canvas_DeleteSelectedItems": {
"label": "删除选定的项目"
},
diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json
index 6c678c651a..19b6533e82 100644
--- a/src/locales/zh/main.json
+++ b/src/locales/zh/main.json
@@ -762,7 +762,6 @@
},
"menuLabels": {
"About ComfyUI": "关于ComfyUI",
- "Add Edit Model Step": "添加编辑模型步骤",
"Bottom Panel": "底部面板",
"Browse Templates": "浏览模板",
"Bypass/Unbypass Selected Nodes": "忽略/取消忽略选定节点",
diff --git a/src/scripts/fluxKontextEditNode.ts b/src/scripts/fluxKontextEditNode.ts
deleted file mode 100644
index fe794d43cc..0000000000
--- a/src/scripts/fluxKontextEditNode.ts
+++ /dev/null
@@ -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 } }
-) {
- 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,
- 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
-}