Compare commits

...

13 Commits

Author SHA1 Message Date
Chenlei Hu
0aedf9c694 1.18.8 (#3775) 2025-05-05 21:21:03 -04:00
Chenlei Hu
98abb20e8d [API Nodes] Add cost badge for api nodes (#3768) 2025-05-05 21:19:27 -04:00
Chenlei Hu
508f13b66d Update blog post link (#3767) 2025-05-05 21:19:12 -04:00
Chenlei Hu
9f2aecf052 [API Nodes] Apply yellow color to api nodes by default (#3766) 2025-05-05 21:19:02 -04:00
Christian Byrne
1215a23393 [API Node] Sort API templates by localized display name (#3765)
Co-authored-by: github-actions <github-actions@github.com>
2025-05-05 21:18:58 -04:00
Chenlei Hu
51fc93ef34 [Bug] Remove default dialog close button on news dialog (#3758) 2025-05-05 21:18:40 -04:00
Chenlei Hu
8c5ccb0346 [API Nodes] Add API Nodes new feature dialog (#3755)
Co-authored-by: github-actions <github-actions@github.com>
2025-05-05 21:18:28 -04:00
filtered
e9383fc6b2 1.18.7 (#3748) 2025-05-04 08:26:24 +10:00
filtered
0825ea2f5d Revert "1.18.7" (#3747) 2025-05-04 08:24:53 +10:00
filtered
32ac59f0f4 1.18.7 (#3745) 2025-05-04 08:10:38 +10:00
filtered
8feb5af8fa v1.18.7
Backport of major patches
2025-05-04 05:54:08 +10:00
Chenlei Hu
2ac6159939 [Bug] Register dom widget when only node is added to graph (#3732) 2025-05-04 05:54:08 +10:00
filtered
1071242359 [Desktop] Fix Server-Config panel does not load (#3743) 2025-05-04 05:54:08 +10:00
21 changed files with 509 additions and 13 deletions

View File

@@ -2,8 +2,8 @@ name: Create Release Draft
on:
pull_request:
types: [ closed ]
branches: [ main, core/* ]
types: [closed]
branches: [main, core/*, desktop/*]
paths:
- 'package.json'

View File

@@ -270,6 +270,7 @@ export class ComfyPage {
localStorage.clear()
sessionStorage.clear()
localStorage.setItem('Comfy.userId', id)
localStorage.setItem('api-nodes-news-seen', 'true')
}, this.id)
}
await this.goto()
@@ -924,6 +925,12 @@ export class ComfyPage {
return window['app'].canvas.ds.convertOffsetToCanvas(pos)
}, pos)
}
/** Get number of DOM widgets on the canvas. */
async getDOMWidgetCount() {
return await this.page.locator('.dom-widget').count()
}
async getNodeRefById(id: NodeId) {
return new NodeReference(id, this)
}

View File

@@ -31,4 +31,20 @@ test.describe('DOM Widget', () => {
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('focus-mode-on.png')
})
// No DOM widget should be created by creation of interim LGraphNode objects.
test('Copy node with DOM widget by dragging + alt', async ({ comfyPage }) => {
const initialCount = await comfyPage.getDOMWidgetCount()
// TextEncodeNode1
await comfyPage.page.mouse.move(618, 191)
await comfyPage.page.keyboard.down('Alt')
await comfyPage.page.mouse.down()
await comfyPage.page.mouse.move(100, 100)
await comfyPage.page.mouse.up()
await comfyPage.page.keyboard.up('Alt')
const finalCount = await comfyPage.getDOMWidgetCount()
expect(finalCount).toBe(initialCount + 1)
})
})

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@comfyorg/comfyui-frontend",
"version": "1.18.6",
"version": "1.18.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@comfyorg/comfyui-frontend",
"version": "1.18.6",
"version": "1.18.8",
"license": "GPL-3.0-only",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",

View File

@@ -1,7 +1,7 @@
{
"name": "@comfyorg/comfyui-frontend",
"private": true,
"version": "1.18.6",
"version": "1.18.8",
"type": "module",
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
"homepage": "https://comfy.org",

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,69 @@
<template>
<div class="flex flex-col gap-12 p-2 w-96">
<img src="@/assets/images/api-nodes-news.webp" alt="API Nodes News" />
<div class="flex flex-col gap-2 justify-center items-center">
<div class="text-xl">
{{ $t('apiNodesNews.introducing') }}
<span class="text-amber-500">API NODES</span>
</div>
<div class="text-muted">{{ $t('apiNodesNews.subtitle') }}</div>
</div>
<div class="flex flex-col gap-4">
<div
v-for="(step, index) in steps"
:key="index"
class="grid grid-cols-[auto_1fr] gap-2 items-center"
>
<Tag class="w-8 h-8" :value="index + 1" rounded />
<div class="flex flex-col gap-2">
<div>{{ step.title }}</div>
<div v-if="step.subtitle" class="text-muted">
{{ step.subtitle }}
</div>
</div>
</div>
</div>
<div class="flex flex-row justify-between">
<Button label="Learn More" text @click="handleLearnMore" />
<Button label="Close" @click="onClose" />
</div>
</div>
</template>
<script setup lang="ts">
import Button from 'primevue/button'
import Tag from 'primevue/tag'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const steps: {
title: string
subtitle?: string
}[] = [
{
title: t('apiNodesNews.steps.step1.title'),
subtitle: t('apiNodesNews.steps.step1.subtitle')
},
{
title: t('apiNodesNews.steps.step2.title'),
subtitle: t('apiNodesNews.steps.step2.subtitle')
},
{
title: t('apiNodesNews.steps.step3.title')
},
{
title: t('apiNodesNews.steps.step4.title')
}
]
const { onClose } = defineProps<{
onClose: () => void
}>()
const handleLearnMore = () => {
window.open('https://blog.comfy.org/p/comfyui-native-api-nodes', '_blank')
}
</script>

View File

@@ -5,9 +5,11 @@
</template>
<script setup lang="ts">
import type { LGraphNode } from '@comfyorg/litegraph'
import { BadgePosition } from '@comfyorg/litegraph'
import { LGraphBadge } from '@comfyorg/litegraph'
import {
BadgePosition,
LGraphBadge,
type LGraphNode
} from '@comfyorg/litegraph'
import _ from 'lodash'
import { computed, onMounted, watch } from 'vue'
@@ -83,6 +85,29 @@ onMounted(() => {
})
node.badges.push(() => badge.value)
if (node.constructor.nodeData?.api_node) {
const creditsBadge = computed(() => {
return new LGraphBadge({
text: '',
iconOptions: {
unicode: '\ue96b',
fontFamily: 'PrimeIcons',
color: '#FABC25',
bgColor: '#353535',
fontSize: 8
},
fgColor:
colorPaletteStore.completedActivePalette.colors.litegraph_base
.BADGE_FG_COLOR,
bgColor:
colorPaletteStore.completedActivePalette.colors.litegraph_base
.BADGE_BG_COLOR
})
})
node.badges.push(() => creditsBadge.value)
}
}
})
})

View File

@@ -132,7 +132,8 @@ export function useSettingUI(
creditsPanel,
userPanel,
keybindingPanel,
extensionPanel
extensionPanel,
...(isElectron() ? [serverConfigPanel] : [])
].filter((panel) => panel.component)
)

View File

@@ -531,6 +531,32 @@
"wan2_1_fun_inp": "Wan 2.1 Inpainting",
"wan2_1_fun_control": "Wan 2.1 ControlNet"
},
"Image API": {
"api_openai_image_1_t2i": "OpenAI Image-1 Text to Image",
"api_openai_image_1_i2i": "OpenAI Image-1 Image to Image",
"api_openai_image_1_inpaint": "OpenAI Image-1 Inpaint",
"api_openai_image_1_multi_inputs": "OpenAI Image-1 Multi Inputs",
"api-openai-dall-e-2-t2i": "Dall-E 2 Text to Image",
"api-openai-dall-e-2-inpaint": "Dall-E 2 Inpaint",
"api-openai-dall-e-3-t2i": "Dall-E 3 Text to Image",
"api_bfl_flux_pro_t2i": "BFL Flux[Pro] Text to Image",
"api_stability_sd3_t2i": "Stability SD3 Text to Image",
"api_ideogram_v3_t2i": "Ideogram V3 Text to Image",
"api_luma_photon_i2i": "Luma Photon Image to Image",
"api_luma_photon_style_ref": "Luma Photon Style Reference",
"api_recraft_image_gen_with_color_control": "Recraft Color Control Image Generation",
"api_recraft_image_gen_with_style_control": "Recraft Style Control Image Generation",
"api_recraft_vector_gen": "Recraft Vector Generation"
},
"Video API": {
"api_luma_i2v": "Luma Image to Video",
"api_kling_i2v": "Kling Image to Video",
"api_veo2_i2v": "Veo2 Image to Video",
"api_hailuo_minimax_i2v": "MiniMax Image to Video",
"api_pika_scene": "Pika Scenes: Images to Video",
"api_pixverse_template_i2v": "PixVerse Templates: Image to Video",
"api_pixverse_t2v": "PixVerse Text to Video"
},
"Image": {
"sd3_5_simple_example": "SD3.5 Simple",
"sd3_5_large_canny_controlnet_example": "SD3.5 Large Canny ControlNet",
@@ -1182,5 +1208,25 @@
"provider": "Sign-in Provider",
"notSet": "Not set",
"updatePassword": "Update Password"
},
"apiNodesNews": {
"introducing": "Introducing",
"subtitle": "All External Models now available in ComfyUI",
"steps": {
"step1": {
"title": "Login/Create an account:",
"subtitle": "Settings > User > Login"
},
"step2": {
"title": "Purchase credits:",
"subtitle": "Settings > Credits > Buy Credits"
},
"step3": {
"title": "Locate new API Nodes under 'API Node' section and add to the canvas"
},
"step4": {
"title": "Run!"
}
}
}
}

View File

@@ -4,6 +4,26 @@
"title": "Nodo(s) de API",
"totalCost": "Costo total"
},
"apiNodesNews": {
"introducing": "Presentamos",
"steps": {
"step1": {
"subtitle": "Configuración > Usuario > Iniciar sesión",
"title": "Inicia sesión/Crea una cuenta:"
},
"step2": {
"subtitle": "Configuración > Créditos > Comprar créditos",
"title": "Compra créditos:"
},
"step3": {
"title": "Ubica los nuevos nodos API en la sección 'API Node' y agrégalos al lienzo"
},
"step4": {
"title": "¡Ejecuta!"
}
},
"subtitle": "Todos los modelos externos ahora disponibles en ComfyUI"
},
"apiNodesSignInDialog": {
"message": "Este flujo de trabajo contiene nodos de API, que requieren que inicies sesión en tu cuenta para poder ejecutar.",
"title": "Se requiere iniciar sesión para usar los nodos de API"
@@ -1090,6 +1110,23 @@
"sdxl_simple_example": "SDXL Simple",
"sdxlturbo_example": "SDXL Turbo"
},
"Image API": {
"api-openai-dall-e-2-inpaint": "Dall-E 2 Rellenar",
"api-openai-dall-e-2-t2i": "Dall-E 2 Texto a Imagen",
"api-openai-dall-e-3-t2i": "Dall-E 3 Texto a Imagen",
"api_bfl_flux_pro_t2i": "BFL Flux[Pro] Texto a Imagen",
"api_ideogram_v3_t2i": "Ideogram V3 Texto a Imagen",
"api_luma_photon_i2i": "Luma Photon Imagen a Imagen",
"api_luma_photon_style_ref": "Luma Photon Referencia de Estilo",
"api_openai_image_1_i2i": "OpenAI Image-1 Imagen a Imagen",
"api_openai_image_1_inpaint": "OpenAI Image-1 Rellenar",
"api_openai_image_1_multi_inputs": "OpenAI Image-1 Múltiples Entradas",
"api_openai_image_1_t2i": "OpenAI Image-1 Texto a Imagen",
"api_recraft_image_gen_with_color_control": "Recraft Generación de Imagen con Control de Color",
"api_recraft_image_gen_with_style_control": "Recraft Generación de Imagen con Control de Estilo",
"api_recraft_vector_gen": "Recraft Generación de Vectores",
"api_stability_sd3_t2i": "Stability SD3 Texto a Imagen"
},
"Upscaling": {
"esrgan_example": "ESRGAN",
"hiresfix_esrgan_workflow": "Flujo de Trabajo HiresFix ESRGAN",
@@ -1107,6 +1144,15 @@
"txt_to_image_to_video": "SVD Texto a Imagen a Video",
"wan2_1_fun_control": "Wan 2.1 ControlNet",
"wan2_1_fun_inp": "Wan 2.1 Relleno"
},
"Video API": {
"api_hailuo_minimax_i2v": "MiniMax Imagen a Video",
"api_kling_i2v": "Kling Imagen a Video",
"api_luma_i2v": "Luma Imagen a Video",
"api_pika_scene": "Pika Escenas: Imágenes a Video",
"api_pixverse_t2v": "PixVerse Texto a Video",
"api_pixverse_template_i2v": "PixVerse Plantillas: Imagen a Video",
"api_veo2_i2v": "Veo2 Imagen a Video"
}
},
"title": "Comienza con una Plantilla"

View File

@@ -4,6 +4,26 @@
"title": "Nœud(s) API",
"totalCost": "Coût total"
},
"apiNodesNews": {
"introducing": "Présentation",
"steps": {
"step1": {
"subtitle": "Paramètres > Utilisateur > Connexion",
"title": "Connectez-vous / Créez un compte :"
},
"step2": {
"subtitle": "Paramètres > Crédits > Acheter des crédits",
"title": "Achetez des crédits :"
},
"step3": {
"title": "Trouvez les nouveaux nœuds API dans la section 'API Node' et ajoutez-les à la toile"
},
"step4": {
"title": "Lancez !"
}
},
"subtitle": "Tous les modèles externes sont désormais disponibles dans ComfyUI"
},
"apiNodesSignInDialog": {
"message": "Ce flux de travail contient des nœuds API, qui nécessitent que vous soyez connecté à votre compte pour pouvoir fonctionner.",
"title": "Connexion requise pour utiliser les nœuds API"
@@ -1090,6 +1110,23 @@
"sdxl_simple_example": "SDXL Simple",
"sdxlturbo_example": "SDXL Turbo"
},
"Image API": {
"api-openai-dall-e-2-inpaint": "Dall-E 2 Inpainting",
"api-openai-dall-e-2-t2i": "Dall-E 2 Texte vers Image",
"api-openai-dall-e-3-t2i": "Dall-E 3 Texte vers Image",
"api_bfl_flux_pro_t2i": "BFL Flux[Pro] Texte vers Image",
"api_ideogram_v3_t2i": "Ideogram V3 Texte vers Image",
"api_luma_photon_i2i": "Luma Photon Image vers Image",
"api_luma_photon_style_ref": "Luma Photon Référence de Style",
"api_openai_image_1_i2i": "OpenAI Image-1 Image vers Image",
"api_openai_image_1_inpaint": "OpenAI Image-1 Inpainting",
"api_openai_image_1_multi_inputs": "OpenAI Image-1 Entrées Multiples",
"api_openai_image_1_t2i": "OpenAI Image-1 Texte vers Image",
"api_recraft_image_gen_with_color_control": "Recraft Génération dImage avec Contrôle des Couleurs",
"api_recraft_image_gen_with_style_control": "Recraft Génération dImage avec Contrôle du Style",
"api_recraft_vector_gen": "Recraft Génération de Vecteur",
"api_stability_sd3_t2i": "Stability SD3 Texte vers Image"
},
"Upscaling": {
"esrgan_example": "ESRGAN",
"hiresfix_esrgan_workflow": "Flux de Travail ESRGAN HiresFix",
@@ -1107,6 +1144,15 @@
"txt_to_image_to_video": "Texte à Image à Vidéo",
"wan2_1_fun_control": "Wan 2.1 ControlNet",
"wan2_1_fun_inp": "Wan 2.1 Inpainting"
},
"Video API": {
"api_hailuo_minimax_i2v": "MiniMax Image vers Vidéo",
"api_kling_i2v": "Kling Image vers Vidéo",
"api_luma_i2v": "Luma Image vers Vidéo",
"api_pika_scene": "Pika Scènes : Images vers Vidéo",
"api_pixverse_t2v": "PixVerse Texte vers Vidéo",
"api_pixverse_template_i2v": "PixVerse Modèles : Image vers Vidéo",
"api_veo2_i2v": "Veo2 Image vers Vidéo"
}
},
"title": "Commencez avec un modèle"

View File

@@ -4,6 +4,26 @@
"title": "APIード",
"totalCost": "合計コスト"
},
"apiNodesNews": {
"introducing": "紹介",
"steps": {
"step1": {
"subtitle": "設定 > ユーザー > ログイン",
"title": "ログイン/アカウント作成:"
},
"step2": {
"subtitle": "設定 > クレジット > クレジットを購入",
"title": "クレジットを購入:"
},
"step3": {
"title": "「APIード」セクションで新しいAPIードを見つけてキャンバスに追加"
},
"step4": {
"title": "実行!"
}
},
"subtitle": "すべての外部モデルがComfyUIで利用可能になりました"
},
"apiNodesSignInDialog": {
"message": "このワークフローにはAPIードが含まれており、実行するためにはアカウントにサインインする必要があります。",
"title": "APIードを使用するためにはサインインが必要です"
@@ -1090,6 +1110,23 @@
"sdxl_simple_example": "SDXLシンプル",
"sdxlturbo_example": "SDXLターボ"
},
"Image API": {
"api-openai-dall-e-2-inpaint": "Dall-E 2 インペイント",
"api-openai-dall-e-2-t2i": "Dall-E 2 テキストから画像へ",
"api-openai-dall-e-3-t2i": "Dall-E 3 テキストから画像へ",
"api_bfl_flux_pro_t2i": "BFL Flux[Pro] テキストから画像へ",
"api_ideogram_v3_t2i": "Ideogram V3 テキストから画像へ",
"api_luma_photon_i2i": "Luma Photon 画像から画像へ",
"api_luma_photon_style_ref": "Luma Photon スタイル参照",
"api_openai_image_1_i2i": "OpenAI Image-1 画像から画像へ",
"api_openai_image_1_inpaint": "OpenAI Image-1 インペイント",
"api_openai_image_1_multi_inputs": "OpenAI Image-1 複数入力",
"api_openai_image_1_t2i": "OpenAI Image-1 テキストから画像へ",
"api_recraft_image_gen_with_color_control": "Recraft カラーコントロール画像生成",
"api_recraft_image_gen_with_style_control": "Recraft スタイルコントロール画像生成",
"api_recraft_vector_gen": "Recraft ベクター生成",
"api_stability_sd3_t2i": "Stability SD3 テキストから画像へ"
},
"Upscaling": {
"esrgan_example": "ESRGAN",
"hiresfix_esrgan_workflow": "HiresFix ESRGANワークフロー",
@@ -1107,6 +1144,15 @@
"txt_to_image_to_video": "テキストから画像へ、画像からビデオへ",
"wan2_1_fun_control": "Wan 2.1 ControlNet",
"wan2_1_fun_inp": "Wan 2.1 インペインティング"
},
"Video API": {
"api_hailuo_minimax_i2v": "MiniMax 画像から動画へ",
"api_kling_i2v": "Kling 画像から動画へ",
"api_luma_i2v": "Luma 画像から動画へ",
"api_pika_scene": "Pika シーン: 画像から動画へ",
"api_pixverse_t2v": "PixVerse テキストから動画へ",
"api_pixverse_template_i2v": "PixVerse テンプレート: 画像から動画へ",
"api_veo2_i2v": "Veo2 画像から動画へ"
}
},
"title": "テンプレートを利用して開始"

View File

@@ -4,6 +4,26 @@
"title": "API 노드(들)",
"totalCost": "총 비용"
},
"apiNodesNews": {
"introducing": "소개합니다",
"steps": {
"step1": {
"subtitle": "설정 > 사용자 > 로그인",
"title": "로그인/계정 생성:"
},
"step2": {
"subtitle": "설정 > 크레딧 > 크레딧 구매",
"title": "크레딧 구매:"
},
"step3": {
"title": "'API Node' 섹션에서 새로운 API 노드를 찾아 캔버스에 추가하세요"
},
"step4": {
"title": "실행!"
}
},
"subtitle": "모든 외부 모델이 이제 ComfyUI에서 사용 가능합니다"
},
"apiNodesSignInDialog": {
"message": "이 워크플로우에는 API 노드가 포함되어 있으며, 실행하려면 계정에 로그인해야 합니다.",
"title": "API 노드 사용에 필요한 로그인"
@@ -1090,6 +1110,23 @@
"sdxl_simple_example": "간단한 SDXL 예제",
"sdxlturbo_example": "SDXL 터보"
},
"Image API": {
"api-openai-dall-e-2-inpaint": "Dall-E 2 인페인트",
"api-openai-dall-e-2-t2i": "Dall-E 2 텍스트 투 이미지",
"api-openai-dall-e-3-t2i": "Dall-E 3 텍스트 투 이미지",
"api_bfl_flux_pro_t2i": "BFL Flux[Pro] 텍스트 투 이미지",
"api_ideogram_v3_t2i": "Ideogram V3 텍스트 투 이미지",
"api_luma_photon_i2i": "Luma Photon 이미지 투 이미지",
"api_luma_photon_style_ref": "Luma Photon 스타일 참조",
"api_openai_image_1_i2i": "OpenAI Image-1 이미지 투 이미지",
"api_openai_image_1_inpaint": "OpenAI Image-1 인페인트",
"api_openai_image_1_multi_inputs": "OpenAI Image-1 멀티 입력",
"api_openai_image_1_t2i": "OpenAI Image-1 텍스트 투 이미지",
"api_recraft_image_gen_with_color_control": "Recraft 색상 제어 이미지 생성",
"api_recraft_image_gen_with_style_control": "Recraft 스타일 제어 이미지 생성",
"api_recraft_vector_gen": "Recraft 벡터 생성",
"api_stability_sd3_t2i": "Stability SD3 텍스트 투 이미지"
},
"Upscaling": {
"esrgan_example": "ESRGAN",
"hiresfix_esrgan_workflow": "HiresFix ESRGAN 워크플로우",
@@ -1107,6 +1144,15 @@
"txt_to_image_to_video": "텍스트 -> 이미지 -> 동영상",
"wan2_1_fun_control": "Wan 2.1 컨트롤넷",
"wan2_1_fun_inp": "Wan 2.1 인페인트"
},
"Video API": {
"api_hailuo_minimax_i2v": "MiniMax 이미지 투 비디오",
"api_kling_i2v": "Kling 이미지 투 비디오",
"api_luma_i2v": "Luma 이미지 투 비디오",
"api_pika_scene": "Pika 장면: 이미지 투 비디오",
"api_pixverse_t2v": "PixVerse 텍스트 투 비디오",
"api_pixverse_template_i2v": "PixVerse 템플릿: 이미지 투 비디오",
"api_veo2_i2v": "Veo2 이미지 투 비디오"
}
},
"title": "템플릿으로 시작하기"

View File

@@ -4,6 +4,26 @@
"title": "API Node(s)",
"totalCost": "Общая стоимость"
},
"apiNodesNews": {
"introducing": "Представляем",
"steps": {
"step1": {
"subtitle": "Настройки > Пользователь > Войти",
"title": "Войти/Создать аккаунт:"
},
"step2": {
"subtitle": "Настройки > Кредиты > Купить кредиты",
"title": "Купить кредиты:"
},
"step3": {
"title": "Найдите новые API-узлы в разделе 'API Node' и добавьте их на холст"
},
"step4": {
"title": "Запустить!"
}
},
"subtitle": "Все внешние модели теперь доступны в ComfyUI"
},
"apiNodesSignInDialog": {
"message": "Этот рабочий процесс содержит API Nodes, которые требуют входа в вашу учетную запись для выполнения.",
"title": "Требуется вход для использования API Nodes"
@@ -1090,6 +1110,23 @@
"sdxl_simple_example": "SDXL Простой",
"sdxlturbo_example": "SDXL Turbo"
},
"Image API": {
"api-openai-dall-e-2-inpaint": "Dall-E 2: дорисовка",
"api-openai-dall-e-2-t2i": "Dall-E 2: текст в изображение",
"api-openai-dall-e-3-t2i": "Dall-E 3: текст в изображение",
"api_bfl_flux_pro_t2i": "BFL Flux[Pro]: текст в изображение",
"api_ideogram_v3_t2i": "Ideogram V3: текст в изображение",
"api_luma_photon_i2i": "Luma Photon: изображение в изображение",
"api_luma_photon_style_ref": "Luma Photon: стиль по образцу",
"api_openai_image_1_i2i": "OpenAI Image-1: изображение в изображение",
"api_openai_image_1_inpaint": "OpenAI Image-1: дорисовка",
"api_openai_image_1_multi_inputs": "OpenAI Image-1: несколько входов",
"api_openai_image_1_t2i": "OpenAI Image-1: текст в изображение",
"api_recraft_image_gen_with_color_control": "Recraft: генерация изображения с управлением цветом",
"api_recraft_image_gen_with_style_control": "Recraft: генерация изображения с управлением стилем",
"api_recraft_vector_gen": "Recraft: генерация векторного изображения",
"api_stability_sd3_t2i": "Stability SD3: текст в изображение"
},
"Upscaling": {
"esrgan_example": "ESRGAN",
"hiresfix_esrgan_workflow": "HiresFix ESRGAN Workflow",
@@ -1107,6 +1144,15 @@
"txt_to_image_to_video": "Текст в изображение в видео",
"wan2_1_fun_control": "Wan 2.1 ControlNet",
"wan2_1_fun_inp": "Wan 2.1 Inpainting"
},
"Video API": {
"api_hailuo_minimax_i2v": "MiniMax: изображение в видео",
"api_kling_i2v": "Kling: изображение в видео",
"api_luma_i2v": "Luma: изображение в видео",
"api_pika_scene": "Pika Scenes: изображения в видео",
"api_pixverse_t2v": "PixVerse: текст в видео",
"api_pixverse_template_i2v": "PixVerse Templates: изображение в видео",
"api_veo2_i2v": "Veo2: изображение в видео"
}
},
"title": "Начните с шаблона"

View File

@@ -4,6 +4,26 @@
"title": "API节点",
"totalCost": "总成本"
},
"apiNodesNews": {
"introducing": "介绍",
"steps": {
"step1": {
"subtitle": "设置 > 用户 > 登录",
"title": "登录/创建账户:"
},
"step2": {
"subtitle": "设置 > 积分 > 购买积分",
"title": "购买积分:"
},
"step3": {
"title": "在“API 节点”部分找到新的 API 节点并添加到画布"
},
"step4": {
"title": "运行!"
}
},
"subtitle": "所有外部模型现已在 ComfyUI 中可用"
},
"apiNodesSignInDialog": {
"message": "此工作流包含API节点需要您登录账户才能运行。",
"title": "使用API节点需要登录"
@@ -1090,6 +1110,23 @@
"sdxl_simple_example": "SDXL简单",
"sdxlturbo_example": "SDXL Turbo"
},
"Image API": {
"api-openai-dall-e-2-inpaint": "Dall-E 2 局部修复",
"api-openai-dall-e-2-t2i": "Dall-E 2 文生图",
"api-openai-dall-e-3-t2i": "Dall-E 3 文生图",
"api_bfl_flux_pro_t2i": "BFL Flux[Pro] 文生图",
"api_ideogram_v3_t2i": "Ideogram V3 文生图",
"api_luma_photon_i2i": "Luma Photon 图生图",
"api_luma_photon_style_ref": "Luma Photon 风格参考",
"api_openai_image_1_i2i": "OpenAI Image-1 图生图",
"api_openai_image_1_inpaint": "OpenAI Image-1 局部修复",
"api_openai_image_1_multi_inputs": "OpenAI Image-1 多输入",
"api_openai_image_1_t2i": "OpenAI Image-1 文生图",
"api_recraft_image_gen_with_color_control": "Recraft 颜色控制图像生成",
"api_recraft_image_gen_with_style_control": "Recraft 风格控制图像生成",
"api_recraft_vector_gen": "Recraft 矢量生成",
"api_stability_sd3_t2i": "Stability SD3 文生图"
},
"Upscaling": {
"esrgan_example": "ESRGAN",
"hiresfix_esrgan_workflow": "HiresFix ESRGAN工作流",
@@ -1107,6 +1144,15 @@
"txt_to_image_to_video": "文本到图像到视频",
"wan2_1_fun_control": "Wan 2.1 ControlNet",
"wan2_1_fun_inp": "Wan 2.1 图像修复"
},
"Video API": {
"api_hailuo_minimax_i2v": "MiniMax 图生视频",
"api_kling_i2v": "Kling 图生视频",
"api_luma_i2v": "Luma 图生视频",
"api_pika_scene": "Pika 场景:图转视频",
"api_pixverse_t2v": "PixVerse 文转视频",
"api_pixverse_template_i2v": "PixVerse 模板:图转视频",
"api_veo2_i2v": "Veo2 图生视频"
}
},
"title": "从模板开始"

View File

@@ -267,6 +267,15 @@ export const addWidget = <W extends BaseDOMWidget<object | string>>(
widget: W
) => {
node.addCustomWidget(widget)
if (node.graph) {
useDomWidgetStore().registerWidget(widget)
}
node.onAdded = useChainCallback(node.onAdded, () => {
useDomWidgetStore().registerWidget(widget)
})
node.onRemoved = useChainCallback(node.onRemoved, () => {
widget.onRemove?.()
})
@@ -275,8 +284,6 @@ export const addWidget = <W extends BaseDOMWidget<object | string>>(
widget.options.beforeResize?.call(widget, node)
widget.options.afterResize?.call(widget, node)
})
useDomWidgetStore().registerWidget(widget)
}
LGraphNode.prototype.addDOMWidget = function <

View File

@@ -1,3 +1,4 @@
import ApiNodesNewsContent from '@/components/dialog/content/ApiNodesNewsContent.vue'
import ApiNodesSignInContent from '@/components/dialog/content/ApiNodesSignInContent.vue'
import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationDialogContent.vue'
import ErrorDialogContent from '@/components/dialog/content/ErrorDialogContent.vue'
@@ -379,6 +380,32 @@ export const useDialogService = () => {
})
}
/**
* Shows a dialog for the API nodes news.
* TODO: Remove the news dialog on next major feature release.
*/
function showApiNodesNewsDialog() {
if (localStorage.getItem('api-nodes-news-seen') === 'true') {
return
}
return dialogStore.showDialog({
key: 'api-nodes-news',
component: ApiNodesNewsContent,
props: {
onClose: () => {
dialogStore.closeDialog({ key: 'api-nodes-news' })
localStorage.setItem('api-nodes-news-seen', 'true')
}
},
dialogComponentProps: {
closable: false,
modal: false,
position: 'bottomright'
}
})
}
return {
showLoadWorkflowWarning,
showMissingModelsWarning,
@@ -394,6 +421,7 @@ export const useDialogService = () => {
showSignInDialog,
showTopUpCreditsDialog,
showUpdatePasswordDialog,
showApiNodesNewsDialog,
prompt,
confirm
}

View File

@@ -1,5 +1,6 @@
import {
type IContextMenuValue,
LGraphCanvas,
LGraphEventMode,
LGraphNode,
LiteGraph,
@@ -79,6 +80,13 @@ export const useLitegraphService = () => {
this.#addOutputs(ComfyNode.nodeData.outputs)
this.#setInitialSize()
this.serialize_widgets = true
// Mark API Nodes yellow by default to distinguish with other nodes.
if (ComfyNode.nodeData.api_node) {
this.color = LGraphCanvas.node_colors.yellow.color
this.bgcolor = LGraphCanvas.node_colors.yellow.bgcolor
}
void extensionService.invokeExtensionsAsync('nodeCreated', this)
}

View File

@@ -27,10 +27,20 @@ export const useWorkflowTemplatesStore = defineStore(
const isLoaded = ref(false)
/**
* Sort a list of templates in alphabetical order by name.
* Sort a list of templates in alphabetical order by localized display name.
*/
const sortTemplateList = (templates: TemplateInfo[]) =>
templates.sort((a, b) => a.name.localeCompare(b.name))
templates.sort((a, b) => {
const aName = st(
`templateWorkflows.name.${normalizeI18nKey(a.name)}`,
a.title ?? a.name
)
const bName = st(
`templateWorkflows.name.${normalizeI18nKey(b.name)}`,
b.name
)
return aName.localeCompare(bName)
})
/**
* Sort any template categories (grouped templates) that should be sorted.

View File

@@ -42,6 +42,7 @@ import { StatusWsMessageStatus } from '@/schemas/apiSchema'
import { api } from '@/scripts/api'
import { app } from '@/scripts/app'
import { setupAutoQueueHandler } from '@/services/autoQueueService'
import { useDialogService } from '@/services/dialogService'
import { useKeybindingService } from '@/services/keybindingService'
import { useCommandStore } from '@/stores/commandStore'
import { useExecutionStore } from '@/stores/executionStore'
@@ -241,6 +242,8 @@ const onGraphReady = () => {
// Explicitly initialize nodeSearchService to avoid indexing delay when
// node search is triggered
useNodeDefStore().nodeSearchService.searchNode('')
useDialogService().showApiNodesNewsDialog()
},
{ timeout: 1000 }
)