Compare commits

...

9 Commits

Author SHA1 Message Date
Glary-Bot
71ae5020bf fix: openAtEvent falls back to showing search box when popover unset
Defends against the (rare) case where the menu callback fires before
NodeSearchBoxPopover has mounted and registered its ref. Previously a
silent no-op; now sets visible=true to ensure the search UI appears.
2026-05-12 05:28:43 +00:00
Glary-Bot
f44f93907a fix: forward right-click event so node lands at click position
Addresses review feedback:

- Add openAtEvent() action to searchBoxStore that calls
  popoverRef.showSearchBox(event), preserving the originating
  CanvasPointerEvent so getNewNodeLocation uses canvasX/canvasY
  instead of falling back to canvas center.
- Plumb the originating event from the menu callback via
  previousMenu.getFirstEvent() in useCanvasSearchBoxMenu.
- Make the prototype patch idempotent via a wrapper symbol marker
  so repeated invocations (HMR, remount) do not stack wrappers.
- Behavioral tests cover: event forwarding, fallback when no
  event, idempotency, and flag-off no-op.
2026-05-12 05:20:12 +00:00
Glary-Bot
ce1a44848d feat: experimental flag to replace canvas Add Node menu with search box
Behind 'Comfy.NodeSearchBox.ReplaceCanvasMenu' (default off), the right-click
canvas menu's 'Add Node' entry opens the V2 search box instead of the
LiteGraph category submenu. The V2 search box already exposes subgraph
blueprints, partner nodes, core nodes, and extensions, giving feature
parity with the left-panel node library.
2026-05-12 04:57:46 +00:00
Deep Mehta
bb420fe2c7 feat: add model-to-node mappings for new model directories (#12151)
## Summary

Add entries to `MODEL_NODE_MAPPINGS` so the model browser's "Use" button
correctly creates a loader node for five model directories that
currently have no mapping.

## Changes

- **What**: 5 new entries in
`src/platform/assets/mappings/modelNodeMappings.ts`:
- `background_removal` → `LoadBackgroundRemovalModel` /
`bg_removal_name` (ComfyUI v0.21+ core)
- `frame_interpolation` → `FrameInterpolationModelLoader` / `model_name`
(ComfyUI v0.21+ core)
  - `film` → `FILM VFI` / `ckpt_name` (ComfyUI-Frame-Interpolation)
- `ultralytics/bbox` → `UltralyticsDetectorProvider` / `model_name`
(ComfyUI-Impact-Pack)
- `ultralytics/segm` → `UltralyticsDetectorProvider` / `model_name`
(ComfyUI-Impact-Pack)
- **Breaking**: none

## Review Focus

- Node class names and input keys were cross-checked against the ComfyUI
v0.21.0 source and the published Impact-Pack / Frame-Interpolation node
definitions
- Both `ultralytics/bbox` and `ultralytics/segm` map to the same node
(`UltralyticsDetectorProvider`); its `model_name` combo accepts values
from both subdirectories (`bbox/...` and `segm/...`)
- `film` and `frame_interpolation` are separate directories serving
different node packs — keeping them as distinct entries rather than
collapsing under a parent

## Test plan

- [ ] In the model browser, clicking "Use" on `birefnet.safetensors`
creates a `LoadBackgroundRemovalModel` node with the model preselected
- [ ] Same for one model in each of: `frame_interpolation`, `film`,
`ultralytics/bbox`, `ultralytics/segm`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12151-feat-add-model-to-node-mappings-for-new-model-directories-35d6d73d365081ff834bf6eb610da160)
by [Unito](https://www.unito.io)
2026-05-12 02:28:51 +00:00
pythongosssss
4504256f11 test: add test for custom node i18n (#12132)
## Summary

Adds e2e test for custom node i18n

## Changes

- **What**: 
- add e2e regression for previous fix with no e2e

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12132-test-add-test-for-custom-node-i18n-35d6d73d365081f7bed2db39af38f855)
by [Unito](https://www.unito.io)
2026-05-12 01:04:38 +00:00
Comfy Org PR Bot
1290bbd359 1.45.5 (#12152)
Patch version increment to 1.45.5

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12152-1-45-5-35e6d73d365081c39aecddf42d8f7a2a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2026-05-12 00:44:22 +00:00
Yourz
7ddf71d91b fix(website): center GitHubStarBadge text in Safari (#12138)
*PR Created by the Glary-Bot Agent*

---

## Summary

The 10px star count text inside the desktop nav GitHub star badge
rendered vertically off-center in Safari/WebKit. The text was visibly
shifted upward inside the yellow badge body, while Chromium rendered it
centered correctly.

## Root cause

`NodeBadge.vue` centers its inner text span by setting `flex
items-center justify-center` on the segment, then nudging the text with
`translate-y-1` (or `translate-y-0` for the small variant). The text
span itself is an `inline-block` with no explicit `line-height`, so it
inherits the default `line-height: 1.5` (15px for a 10px font).

Safari and Chromium distribute that extra leading differently for the
`PP Formula Narrow` custom font: Safari pushes the glyph higher inside
its 15px line box, while Chromium positions it near the middle. With
only a 5px gap above/below the glyph to play with, that browser-specific
divergence is enough to make the badge look misaligned in Safari.

## Fix

Add `leading-none` to the star count text class so the inline-block's
line box equals the font size (10px) and the parent flex container's
`items-center` produces deterministic vertical centering across
browsers.

Verified at lg breakpoint (1440×900) in both WebKit and Chromium via
Playwright; the badge now renders identically and is properly centered.

## Verification

- `pnpm typecheck` (website) — clean
- `pnpm build` (website) — 51 pages built successfully
- Pre-commit hooks (stylelint, oxfmt, oxlint, eslint, typecheck,
typecheck:website) — all passed
- Visual inspection in WebKit and Chromium via Playwright

Fixes FE-648

## Screenshots

![BEFORE (Safari/WebKit): star count text displaced upward in yellow
badge](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/364b285ccc004b7d3778b6129dab4410e0bffe9db5ef124e37443414f9790d70/pr-images/1778513564903-c047c9ac-4719-479e-8442-f15ad389be02.png)

![AFTER (Safari/WebKit): star count text properly centered in yellow
badge](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/364b285ccc004b7d3778b6129dab4410e0bffe9db5ef124e37443414f9790d70/pr-images/1778513565238-a4f00af8-54d7-4131-a5fd-849105e427d6.png)

![Zoomed Safari/WebKit view showing the fixed badge with centered 85K
text](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/364b285ccc004b7d3778b6129dab4410e0bffe9db5ef124e37443414f9790d70/pr-images/1778513565587-f6aca193-5ced-45aa-b692-3340629f64be.png)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12138-fix-website-center-GitHubStarBadge-text-in-Safari-35d6d73d3650818aa0e8e0f341b60378)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
2026-05-12 00:38:49 +00:00
Christian Byrne
74caeb0b0b fix: detect V1/V2 draft storage keys in new-user check (#11728)
## Problem

`checkIsNewUser()` in `useNewUserService` only checked legacy pre-V1
localStorage keys (`workflow`, `Comfy.PreviousWorkflow`) to determine if
a user had prior workflow history. A returning user who had only ever
used the V1 or V2 draft persistence system would have neither of those
keys set, causing `isNewUser()` to return `true` and the getting-started
tab to appear in the workflow templates dialog after a settings reset.

## Solution

Extend the check to also cover:
- **V1 draft store keys**: `Comfy.Workflow.Drafts`,
`Comfy.Workflow.DraftOrder`
- **V2 draft index key**: `Comfy.Workflow.DraftIndex.v2:personal`

The `personal` scope is hardcoded for the V2 check because at the time
`checkIsNewUser()` runs, the cloud workspace ID (stored in
sessionStorage) may not be set yet. This is fine — any genuine new user
will have no personal workspace index regardless.

The original legacy keys are preserved for users who may still have them
from older installs.

## Tests

Added three new test cases covering V1 draft store keys, V1 draft order
key, and V2 draft index key.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11728-fix-detect-V1-V2-draft-storage-keys-in-new-user-check-3506d73d3650819ca4cfc8e83d95c258)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Connor Byrne <c.byrne@comfy.org>
2026-05-11 20:16:04 +00:00
Alexander Brown
ced7c93e63 testing: Improve custom checks in .coderabbit.yaml (#12141)
Update coderabbit end-to-end check logic.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12141-testing-Improve-custom-checks-in-coderabbit-yaml-35d6d73d3650818e8be2f0b7d403683b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-05-11 19:05:48 +00:00
28 changed files with 2348 additions and 21 deletions

View File

@@ -19,15 +19,26 @@ reviews:
- name: End-to-end regression coverage for fixes
mode: error
instructions: |
Use only PR metadata already available in the review context: the PR title, commit subjects in this PR, the files changed in this PR relative to the PR base (equivalent to `base...head`), and the PR description.
Do not rely on shell commands. Do not inspect reverse diffs, files changed only on the base branch, or files outside this PR. If the changed-file list or commit subjects are unavailable, mark the check inconclusive instead of guessing.
Use only PR metadata already available in the review context:
- the PR title
- commit subjects in this PR
- The files changed in this PR relative to the PR base (equivalent to `base...head`)
- the PR description.
Do not rely on shell commands.
Do not inspect reverse diffs, files changed only on the base branch, or files outside this PR.
If the changed-file list or commit subjects are unavailable, mark the check inconclusive instead of guessing.
Pass if at least one of the following is true:
1. Neither the PR title nor any commit subject in the PR uses bug-fix language such as `fix`, `fixed`, `fixes`, `fixing`, `bugfix`, or `hotfix`.
2. The PR changes at least one file under `browser_tests/`.
3. The PR description includes a concrete, non-placeholder explanation of why an end-to-end regression test was not added.
Fail if all of the following are true:
1. The PR title and/or any commit subject in the PR uses bug-fix language such as `fix`, `fixed`, `fixes`, `fixing`, `bugfix`, or `hotfix`.
2. The PR changes files under `src/` or `packages/` related to the main frontend application but the PR does not change at least one file under `browser_tests/`.
3. The PR description lacks a concrete explanation of why an end-to-end regression test was not added.
Do not fail if the changes are exclusively in `apps/website`, just documentation changes, or changes related to CI processes.
The goal is to make sure that fixes include End-to-End regression tests. Do not insist on tests when the PR is not fixing a bug.
Pass otherwise.
When failing, mention which bug-fix signal you found and ask the author to either add or update a Playwright regression test under `browser_tests/` or add a concrete explanation in the PR description of why an end-to-end regression test is not practical.
Fail otherwise. When failing, mention which bug-fix signal you found and ask the author to either add or update a Playwright regression test under `browser_tests/` or add a concrete explanation in the PR description of why an end-to-end regression test is not practical.
- name: ADR compliance for entity/litegraph changes
mode: warning
instructions: |

View File

@@ -26,7 +26,7 @@ const {
<img
src="/icons/node-left.svg"
alt=""
class="-mx-px self-stretch"
class="-mx-px h-full w-auto self-stretch"
aria-hidden="true"
/>
@@ -38,7 +38,7 @@ const {
v-if="i > 0"
src="/icons/node-union.svg"
alt=""
class="-mx-px self-stretch"
class="-mx-px h-full w-auto self-stretch"
aria-hidden="true"
/>
<span
@@ -72,7 +72,7 @@ const {
<img
src="/icons/node-right.svg"
alt=""
class="-mx-px self-stretch"
class="-mx-px h-full w-auto self-stretch"
aria-hidden="true"
/>
</div>

View File

@@ -470,6 +470,7 @@ const COLLECT_COVERAGE = process.env.COLLECT_COVERAGE === 'true'
export const comfyPageFixture = base.extend<{
initialFeatureFlags: Record<string, unknown>
initialSettings: Record<string, unknown>
comfyPage: ComfyPage
comfyMouse: ComfyMouse
comfyFiles: ComfyFiles
@@ -477,6 +478,10 @@ export const comfyPageFixture = base.extend<{
// Allows configuring feature flags for tests with before initial setup:
// `test.use({ initialFeatureFlags: { my_flag: true } })`.
initialFeatureFlags: [{}, { option: true }],
// Allows seeding user settings before initial page load:
// `test.use({ initialSettings: { 'Comfy.Locale': 'zh' } })`. Merged on top of
// the fixture's defaults so per-test values win.
initialSettings: [{}, { option: true }],
page: async ({ page, browserName }, use) => {
if (browserName !== 'chromium' || !COLLECT_COVERAGE) {
@@ -494,7 +499,11 @@ export const comfyPageFixture = base.extend<{
await mcr.add(coverage)
},
comfyPage: async ({ page, request, initialFeatureFlags }, use, testInfo) => {
comfyPage: async (
{ page, request, initialFeatureFlags, initialSettings },
use,
testInfo
) => {
const comfyPage = new ComfyPage(page, request)
const { parallelIndex } = testInfo
@@ -529,7 +538,8 @@ export const comfyPageFixture = base.extend<{
// Disable errors tab to prevent missing model detection from
// rendering error indicators on nodes during unrelated tests.
'Comfy.RightSidePanel.ShowErrorsTab': false,
...(isVueNodes && { 'Comfy.VueNodes.Enabled': true })
...(isVueNodes && { 'Comfy.VueNodes.Enabled': true }),
...initialSettings
})
} catch (e) {
console.error(e)

View File

@@ -0,0 +1,47 @@
import type { CustomNodesI18n } from '@/schemas/apiSchema'
import {
comfyExpect as expect,
comfyPageFixture as test
} from '@e2e/fixtures/ComfyPage'
const NODE_TYPE = 'DevToolsNodeWithStringInput'
const LOCALIZED_ZH = '本地化字符串输入 (ZH)'
const i18nResponse: CustomNodesI18n = {
zh: {
nodeDefs: {
[NODE_TYPE]: { display_name: LOCALIZED_ZH }
}
}
}
test.describe(
'Custom node locales loading',
{ tag: ['@ui', '@vue-nodes'] },
() => {
test.use({ initialSettings: { 'Comfy.Locale': 'zh' } })
test.beforeEach(async ({ page }) => {
await page.route('**/api/i18n', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(i18nResponse)
})
})
})
// Regression test for PR #7214 (issue #7025): custom-node i18n data was
// clobbered when a non-English locale was lazily loaded, so nodes from
// custom packs lost their translated display_name on locale switch.
test('preserves custom-node /api/i18n translation through lazy locale load', async ({
comfyPage
}) => {
await comfyPage.nodeOps.addNode(NODE_TYPE)
await expect(comfyPage.vueNodes.getNodeByTitle(LOCALIZED_ZH)).toHaveCount(
1
)
})
}
)

View File

@@ -1,6 +1,6 @@
{
"name": "@comfyorg/comfyui-frontend",
"version": "1.45.4",
"version": "1.45.5",
"private": true,
"description": "Official front-end implementation of ComfyUI",
"homepage": "https://comfy.org",

View File

@@ -146,6 +146,7 @@ import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
import { useVueNodeLifecycle } from '@/composables/graph/useVueNodeLifecycle'
import { useNodeBadge } from '@/composables/node/useNodeBadge'
import { useCanvasDrop } from '@/composables/useCanvasDrop'
import { useCanvasSearchBoxMenu } from '@/composables/useCanvasSearchBoxMenu'
import { useContextMenuTranslation } from '@/composables/useContextMenuTranslation'
import { useCopy } from '@/composables/useCopy'
import { useGlobalLitegraph } from '@/composables/useGlobalLitegraph'
@@ -459,6 +460,7 @@ useLitegraphSettings()
useNodeBadge()
useGlobalLitegraph()
useCanvasSearchBoxMenu()
useContextMenuTranslation()
useCopy()
usePaste()

View File

@@ -0,0 +1,134 @@
import { createPinia, setActivePinia } from 'pinia'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { useCanvasSearchBoxMenu } from '@/composables/useCanvasSearchBoxMenu'
import type {
ContextMenu,
IContextMenuValue
} from '@/lib/litegraph/src/litegraph'
import { LGraphCanvas } from '@/lib/litegraph/src/litegraph'
import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore'
import { createMockCanvas } from '@/utils/__tests__/litegraphTestUtils'
describe('useCanvasSearchBoxMenu', () => {
let originalGetCanvasMenuOptions: typeof LGraphCanvas.prototype.getCanvasMenuOptions
let mockCanvas: LGraphCanvas
beforeEach(() => {
setActivePinia(createPinia())
originalGetCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions
LGraphCanvas.prototype.getCanvasMenuOptions =
function (): (IContextMenuValue | null)[] {
const items: (IContextMenuValue<string> | null)[] = [
{
content: 'Add Node',
has_submenu: true,
callback: LGraphCanvas.onMenuAdd
},
{ content: 'Add Group', callback: vi.fn() }
]
return items as (IContextMenuValue | null)[]
}
mockCanvas = createMockCanvas({
constructor: { prototype: LGraphCanvas.prototype } as typeof LGraphCanvas
} as Partial<LGraphCanvas>)
})
afterEach(() => {
LGraphCanvas.prototype.getCanvasMenuOptions = originalGetCanvasMenuOptions
vi.restoreAllMocks()
})
function invokeAddNodeCallback(
addNode: IContextMenuValue,
previousMenu?: Partial<ContextMenu<unknown>>
) {
void addNode.callback?.call(
addNode as never,
undefined,
undefined,
undefined as never,
previousMenu as ContextMenu<unknown> | undefined
)
}
it('leaves the default Add Node entry untouched when the setting is off', () => {
vi.spyOn(useSettingStore(), 'get').mockImplementation((id) =>
id === 'Comfy.NodeSearchBox.ReplaceCanvasMenu' ? false : undefined
)
useCanvasSearchBoxMenu()
const items = LGraphCanvas.prototype.getCanvasMenuOptions.call(mockCanvas)
const addNode = items.find((i) => i?.content === 'Add Node')
expect(addNode?.callback).toBe(LGraphCanvas.onMenuAdd)
expect(addNode?.has_submenu).toBe(true)
})
it('forwards the original right-click event to the search box so the node lands at the click position', () => {
vi.spyOn(useSettingStore(), 'get').mockImplementation((id) =>
id === 'Comfy.NodeSearchBox.ReplaceCanvasMenu' ? true : undefined
)
const openAtEvent = vi.spyOn(useSearchBoxStore(), 'openAtEvent')
const toggleVisible = vi.spyOn(useSearchBoxStore(), 'toggleVisible')
const triggerEvent = {
canvasX: 123,
canvasY: 456
} as unknown as CanvasPointerEvent
const previousMenu = {
getFirstEvent: () => triggerEvent
} as unknown as Partial<ContextMenu<unknown>>
useCanvasSearchBoxMenu()
const items = LGraphCanvas.prototype.getCanvasMenuOptions.call(mockCanvas)
const addNode = items.find((i) => i?.content === 'Add Node')
expect(addNode).toBeTruthy()
expect(addNode?.has_submenu).toBe(false)
invokeAddNodeCallback(addNode!, previousMenu)
expect(openAtEvent).toHaveBeenCalledTimes(1)
expect(openAtEvent).toHaveBeenCalledWith(triggerEvent)
expect(toggleVisible).not.toHaveBeenCalled()
})
it('falls back to toggleVisible when no originating event is available', () => {
vi.spyOn(useSettingStore(), 'get').mockReturnValue(true)
const openAtEvent = vi.spyOn(useSearchBoxStore(), 'openAtEvent')
const toggleVisible = vi.spyOn(useSearchBoxStore(), 'toggleVisible')
useCanvasSearchBoxMenu()
const items = LGraphCanvas.prototype.getCanvasMenuOptions.call(mockCanvas)
const addNode = items.find((i) => i?.content === 'Add Node')
invokeAddNodeCallback(addNode!, undefined)
expect(openAtEvent).not.toHaveBeenCalled()
expect(toggleVisible).toHaveBeenCalledTimes(1)
})
it('preserves other canvas menu entries', () => {
vi.spyOn(useSettingStore(), 'get').mockReturnValue(true)
useCanvasSearchBoxMenu()
const items = LGraphCanvas.prototype.getCanvasMenuOptions.call(mockCanvas)
const contents = items.map((i) => i?.content)
expect(contents).toEqual(['Add Node', 'Add Group'])
})
it('is idempotent across repeated invocations (HMR, remount)', () => {
useCanvasSearchBoxMenu()
const firstPatch = LGraphCanvas.prototype.getCanvasMenuOptions
useCanvasSearchBoxMenu()
useCanvasSearchBoxMenu()
expect(LGraphCanvas.prototype.getCanvasMenuOptions).toBe(firstPatch)
})
})

View File

@@ -0,0 +1,105 @@
import { legacyMenuCompat } from '@/lib/litegraph/src/contextMenuCompat'
import type {
ContextMenu,
IContextMenuValue
} from '@/lib/litegraph/src/litegraph'
import { LGraphCanvas } from '@/lib/litegraph/src/litegraph'
import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore'
const REPLACE_SETTING_ID = 'Comfy.NodeSearchBox.ReplaceCanvasMenu'
const WRAPPER_MARK = Symbol('useCanvasSearchBoxMenu.wrapper')
/**
* When the experimental "replace canvas menu" setting is enabled, the
* right-click canvas menu's "Add Node" entry opens the Vue node search box
* (which already includes blueprints, partner nodes, core nodes, and
* extensions) instead of the legacy LiteGraph category submenu.
*
* The replacement is identified by callback identity against
* {@link LGraphCanvas.onMenuAdd} so it remains stable across the translation
* wrapper installed by {@link useContextMenuTranslation}. The original
* right-click event is forwarded via {@link ContextMenu.getFirstEvent} so the
* resulting node lands at the click position instead of canvas center.
*
* Installation is idempotent: repeated calls (e.g. HMR remounts) do not stack
* wrappers because the wrapper is tagged and detected on re-entry.
*/
export const useCanvasSearchBoxMenu = () => {
legacyMenuCompat.install(LGraphCanvas.prototype, 'getCanvasMenuOptions')
const previousGetCanvasMenuOptions =
LGraphCanvas.prototype.getCanvasMenuOptions
if (isOurWrapper(previousGetCanvasMenuOptions)) return
const wrapped: typeof previousGetCanvasMenuOptions = function (
this: LGraphCanvas,
...args
) {
const items = previousGetCanvasMenuOptions.apply(this, args)
const settingStore = useSettingStore()
if (!settingStore.get(REPLACE_SETTING_ID)) return items
return items.map((item) =>
isLegacyAddNode(item) ? buildSearchBoxAddNode(item) : item
)
}
markAsOurWrapper(wrapped)
LGraphCanvas.prototype.getCanvasMenuOptions = wrapped
legacyMenuCompat.registerWrapper(
'getCanvasMenuOptions',
wrapped,
previousGetCanvasMenuOptions,
LGraphCanvas.prototype
)
}
function isOurWrapper(fn: unknown): boolean {
return !!fn && (fn as { [WRAPPER_MARK]?: true })[WRAPPER_MARK] === true
}
function markAsOurWrapper(fn: object) {
Object.defineProperty(fn, WRAPPER_MARK, {
value: true,
enumerable: false,
configurable: false,
writable: false
})
}
function isLegacyAddNode(
item: IContextMenuValue | null
): item is IContextMenuValue {
return (
!!item &&
typeof item === 'object' &&
item.callback === LGraphCanvas.onMenuAdd
)
}
function buildSearchBoxAddNode(original: IContextMenuValue): IContextMenuValue {
return {
...original,
has_submenu: false,
submenu: undefined,
callback: (
_value?: unknown,
_options?: unknown,
_event?: MouseEvent,
previousMenu?: ContextMenu<unknown>
) => {
const triggerEvent = previousMenu?.getFirstEvent() as
| CanvasPointerEvent
| undefined
const store = useSearchBoxStore()
if (triggerEvent) {
store.openAtEvent(triggerEvent)
} else {
store.toggleVisible()
}
}
}
}

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "توليد موحد من النص إلى الصورة وتحرير دقيق لجملة واحدة بدقة تصل إلى 4K.",
"display_name": "ByteDance Seedream 4.5 & 5.0",
"inputs": {
"control_after_generate": {
"name": "التحكم بعد التوليد"
},
"model": {
"name": "النموذج"
},
"model_fail_on_partial": {
"name": "فشل عند التوليد الجزئي"
},
"model_height": {
"name": "الارتفاع"
},
"model_max_images": {
"name": "أقصى عدد للصور"
},
"model_size_preset": {
"name": "إعداد الحجم"
},
"model_width": {
"name": "العرض"
},
"prompt": {
"name": "الموجه",
"tooltip": "موجه نصي لإنشاء أو تعديل صورة."
},
"seed": {
"name": "البذرة",
"tooltip": "البذرة المستخدمة للتوليد."
},
"watermark": {
"name": "علامة مائية",
"tooltip": "هل تريد إضافة علامة \"تم الإنشاء بواسطة الذكاء الاصطناعي\" على الصورة."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "توليد فيديو باستخدام نماذج ByteDance عبر API استنادًا إلى النص الموجه",
"display_name": "ByteDance نص إلى فيديو",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "توليد الصور عبر Flux.2 [pro] أو Flux.2 [max] من موجه وصور مرجعية اختيارية.",
"display_name": "Flux.2 Image",
"inputs": {
"control_after_generate": {
"name": "التحكم بعد التوليد"
},
"model": {
"name": "النموذج"
},
"model_height": {
"name": "الارتفاع"
},
"model_width": {
"name": "العرض"
},
"prompt": {
"name": "الموجه",
"tooltip": "موجه لتوليد أو تعديل الصورة"
},
"seed": {
"name": "البذرة",
"tooltip": "البذرة العشوائية المستخدمة لإنشاء الضوضاء."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "ينشئ الصور بشكل متزامن بناءً على النص والوَضوح.",
"display_name": "Flux.2 [max] صورة",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "تعديل صورة موجودة بناءً على موجه نصي",
"display_name": "تعديل صورة Grok",
"inputs": {
"control_after_generate": {
"name": "التحكم بعد التوليد"
},
"model": {
"name": "النموذج"
},
"model_aspect_ratio": {
"name": "نسبة العرض إلى الارتفاع"
},
"model_number_of_images": {
"name": "عدد الصور"
},
"model_resolution": {
"name": "الدقة"
},
"prompt": {
"name": "الموجه",
"tooltip": "الموجه النصي المستخدم لتوليد الصورة"
},
"seed": {
"name": "البذرة",
"tooltip": "البذرة لتحديد ما إذا كان يجب إعادة تشغيل العقدة؛ النتائج الفعلية غير حتمية بغض النظر عن البذرة."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "توليد صور باستخدام Grok بناءً على مطالبة نصية",
"display_name": "صورة Grok",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "توليد الصور عبر نقطة نهاية GPT Image من OpenAI.",
"display_name": "OpenAI GPT Image 2",
"inputs": {
"control_after_generate": {
"name": "التحكم بعد التوليد"
},
"model": {
"name": "النموذج"
},
"model_background": {
"name": "الخلفية"
},
"model_custom_height": {
"name": "ارتفاع مخصص"
},
"model_custom_width": {
"name": "عرض مخصص"
},
"model_quality": {
"name": "الجودة"
},
"model_size": {
"name": "الحجم"
},
"n": {
"name": "عدد الصور",
"tooltip": "كم عدد الصور التي سيتم توليدها"
},
"prompt": {
"name": "الموجه",
"tooltip": "موجه نصي لـ GPT Image"
},
"seed": {
"name": "البذرة",
"tooltip": "لم يتم تطبيقها بعد في الخلفية"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "يقوم بتحميل وإعداد ملفات الإدخال (نص، pdf، إلخ) لتضمينها كمدخلات لعقدة OpenAI Chat. سيتم قراءة الملفات بواسطة نموذج OpenAI عند إنشاء الرد. 🛈 تلميح: يمكن ربطها مع عقد OpenAI Input File الأخرى.",
"display_name": "OpenAI ChatGPT Input Files",

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"display_name": "ByteDance Seedream 4.5 & 5.0",
"description": "Unified text-to-image generation and precise single-sentence editing at up to 4K resolution.",
"inputs": {
"prompt": {
"name": "prompt",
"tooltip": "Text prompt for creating or editing an image."
},
"model": {
"name": "model"
},
"seed": {
"name": "seed",
"tooltip": "Seed to use for generation."
},
"watermark": {
"name": "watermark",
"tooltip": "Whether to add an \"AI generated\" watermark to the image."
},
"control_after_generate": {
"name": "control after generate"
},
"model_fail_on_partial": {
"name": "fail_on_partial"
},
"model_height": {
"name": "height"
},
"model_max_images": {
"name": "max_images"
},
"model_size_preset": {
"name": "size_preset"
},
"model_width": {
"name": "width"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"display_name": "ByteDance Text to Video",
"description": "Generate video using ByteDance models via api based on prompt",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"display_name": "Flux.2 Image",
"description": "Generate images via Flux.2 [pro] or Flux.2 [max] from a prompt and optional reference images.",
"inputs": {
"prompt": {
"name": "prompt",
"tooltip": "Prompt for the image generation or edit"
},
"model": {
"name": "model"
},
"seed": {
"name": "seed",
"tooltip": "The random seed used for creating the noise."
},
"control_after_generate": {
"name": "control after generate"
},
"model_height": {
"name": "height"
},
"model_width": {
"name": "width"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"display_name": "Flux.2 [max] Image",
"description": "Generates images synchronously based on prompt and resolution.",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"display_name": "Grok Image Edit",
"description": "Modify an existing image based on a text prompt",
"inputs": {
"prompt": {
"name": "prompt",
"tooltip": "The text prompt used to generate the image"
},
"model": {
"name": "model"
},
"seed": {
"name": "seed",
"tooltip": "Seed to determine if node should re-run; actual results are nondeterministic regardless of seed."
},
"control_after_generate": {
"name": "control after generate"
},
"model_aspect_ratio": {
"name": "aspect_ratio"
},
"model_number_of_images": {
"name": "number_of_images"
},
"model_resolution": {
"name": "resolution"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"display_name": "Grok Image",
"description": "Generate images using Grok based on a text prompt",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"display_name": "OpenAI GPT Image 2",
"description": "Generates images via OpenAI's GPT Image endpoint.",
"inputs": {
"prompt": {
"name": "prompt",
"tooltip": "Text prompt for GPT Image"
},
"model": {
"name": "model"
},
"n": {
"name": "n",
"tooltip": "How many images to generate"
},
"seed": {
"name": "seed",
"tooltip": "not implemented yet in backend"
},
"control_after_generate": {
"name": "control after generate"
},
"model_background": {
"name": "background"
},
"model_custom_height": {
"name": "custom_height"
},
"model_custom_width": {
"name": "custom_width"
},
"model_quality": {
"name": "quality"
},
"model_size": {
"name": "size"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"display_name": "OpenAI ChatGPT Input Files",
"description": "Loads and prepares input files (text, pdf, etc.) to include as inputs for the OpenAI Chat Node. The files will be read by the OpenAI model when generating a response. 🛈 TIP: Can be chained together with other OpenAI Input File nodes.",

View File

@@ -328,6 +328,10 @@
"name": "Show node frequency in search results",
"tooltip": "Only applies to v1 (legacy)"
},
"Comfy_NodeSearchBox_ReplaceCanvasMenu": {
"name": "Replace canvas right-click \"Add Node\" with search box",
"tooltip": "When enabled, the right-click canvas menu opens the node search box instead of the LiteGraph category submenu. The search box includes blueprints, partner nodes, core nodes, and extensions."
},
"Comfy_NodeSuggestions_number": {
"name": "Number of nodes suggestions",
"tooltip": "Only for litegraph searchbox/context menu"

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "Generación unificada de texto a imagen y edición precisa de una sola frase hasta una resolución de 4K.",
"display_name": "ByteDance Seedream 4.5 & 5.0",
"inputs": {
"control_after_generate": {
"name": "control después de generar"
},
"model": {
"name": "modelo"
},
"model_fail_on_partial": {
"name": "fallar_en_parcial"
},
"model_height": {
"name": "altura"
},
"model_max_images": {
"name": "imágenes_máximas"
},
"model_size_preset": {
"name": "preajuste_de_tamaño"
},
"model_width": {
"name": "ancho"
},
"prompt": {
"name": "prompt",
"tooltip": "Indicador de texto para crear o editar una imagen."
},
"seed": {
"name": "semilla",
"tooltip": "Semilla a utilizar para la generación."
},
"watermark": {
"name": "marca de agua",
"tooltip": "Indica si se añade una marca de agua de \"Generado por IA\" a la imagen."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "Generar video usando modelos de ByteDance mediante API basado en prompt",
"display_name": "ByteDance Texto a Video",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "Genera imágenes mediante Flux.2 [pro] o Flux.2 [max] a partir de un prompt y, opcionalmente, imágenes de referencia.",
"display_name": "Flux.2 Image",
"inputs": {
"control_after_generate": {
"name": "control después de generar"
},
"model": {
"name": "modelo"
},
"model_height": {
"name": "altura"
},
"model_width": {
"name": "ancho"
},
"prompt": {
"name": "prompt",
"tooltip": "Prompt para la generación o edición de la imagen"
},
"seed": {
"name": "semilla",
"tooltip": "La semilla aleatoria utilizada para crear el ruido."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "Genera imágenes de forma sincrónica según el prompt y la resolución.",
"display_name": "Flux.2 [max] Imagen",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "Modifica una imagen existente en base a un prompt de texto",
"display_name": "Grok Image Edit",
"inputs": {
"control_after_generate": {
"name": "control después de generar"
},
"model": {
"name": "modelo"
},
"model_aspect_ratio": {
"name": "relación_de_aspecto"
},
"model_number_of_images": {
"name": "número_de_imágenes"
},
"model_resolution": {
"name": "resolución"
},
"prompt": {
"name": "prompt",
"tooltip": "El prompt de texto utilizado para generar la imagen"
},
"seed": {
"name": "semilla",
"tooltip": "Semilla para determinar si el nodo debe volver a ejecutarse; los resultados reales son no deterministas independientemente de la semilla."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "Genera imágenes usando Grok a partir de una indicación de texto",
"display_name": "Imagen Grok",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "Genera imágenes a través del endpoint GPT Image de OpenAI.",
"display_name": "OpenAI GPT Image 2",
"inputs": {
"control_after_generate": {
"name": "control después de generar"
},
"model": {
"name": "modelo"
},
"model_background": {
"name": "fondo"
},
"model_custom_height": {
"name": "altura_personalizada"
},
"model_custom_width": {
"name": "ancho_personalizado"
},
"model_quality": {
"name": "calidad"
},
"model_size": {
"name": "tamaño"
},
"n": {
"name": "n",
"tooltip": "Cuántas imágenes generar"
},
"prompt": {
"name": "prompt",
"tooltip": "Prompt de texto para GPT Image"
},
"seed": {
"name": "semilla",
"tooltip": "aún no implementado en el backend"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "Carga y prepara archivos de entrada (texto, pdf, etc.) para incluirlos como entradas para el Nodo de Chat de OpenAI. Los archivos serán leídos por el modelo de OpenAI al generar una respuesta. 🛈 CONSEJO: Se puede encadenar con otros nodos de Archivos de Entrada de OpenAI.",
"display_name": "Archivos de Entrada de OpenAI ChatGPT",

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "تولید تصویر از متن و ویرایش دقیق جمله‌ای با وضوح تا ۴K به صورت یکپارچه.",
"display_name": "ByteDance Seedream ۴.۵ و ۵.۰",
"inputs": {
"control_after_generate": {
"name": "control after generate"
},
"model": {
"name": "model"
},
"model_fail_on_partial": {
"name": "fail_on_partial"
},
"model_height": {
"name": "height"
},
"model_max_images": {
"name": "max_images"
},
"model_size_preset": {
"name": "size_preset"
},
"model_width": {
"name": "width"
},
"prompt": {
"name": "prompt",
"tooltip": "پرامپت متنی برای ایجاد یا ویرایش تصویر."
},
"seed": {
"name": "seed",
"tooltip": "Seed مورد استفاده برای تولید."
},
"watermark": {
"name": "watermark",
"tooltip": "آیا واترمارک «تولید شده توسط هوش مصنوعی» به تصویر اضافه شود یا خیر."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "تولید ویدیو با استفاده از مدل‌های ByteDance از طریق API بر اساس پرامپت",
"display_name": "تبدیل متن به ویدیو ByteDance",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "تولید تصویر با استفاده از Flux.2 [pro] یا Flux.2 [max] از طریق پرامپت و تصاویر مرجع اختیاری.",
"display_name": "Flux.2 Image",
"inputs": {
"control_after_generate": {
"name": "control after generate"
},
"model": {
"name": "model"
},
"model_height": {
"name": "height"
},
"model_width": {
"name": "width"
},
"prompt": {
"name": "prompt",
"tooltip": "پرامپت برای تولید یا ویرایش تصویر"
},
"seed": {
"name": "seed",
"tooltip": "Seed تصادفی برای ایجاد نویز."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "تولید تصویر به صورت همزمان بر اساس پرامپت و وضوح تصویر.",
"display_name": "Flux.2 [max] Image",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "ویرایش یک تصویر موجود بر اساس پرامپت متنی",
"display_name": "Grok Image Edit",
"inputs": {
"control_after_generate": {
"name": "control after generate"
},
"model": {
"name": "model"
},
"model_aspect_ratio": {
"name": "aspect_ratio"
},
"model_number_of_images": {
"name": "number_of_images"
},
"model_resolution": {
"name": "resolution"
},
"prompt": {
"name": "prompt",
"tooltip": "پرامپت متنی برای تولید تصویر"
},
"seed": {
"name": "seed",
"tooltip": "Seed برای تعیین اجرای مجدد node؛ نتایج واقعی صرف‌نظر از seed غیرقطعی هستند."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "تولید تصویر با استفاده از Grok بر اساس یک متن راهنما",
"display_name": "تصویر Grok",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "تولید تصویر از طریق سرویس GPT Image شرکت OpenAI.",
"display_name": "OpenAI GPT Image ۲",
"inputs": {
"control_after_generate": {
"name": "control after generate"
},
"model": {
"name": "model"
},
"model_background": {
"name": "background"
},
"model_custom_height": {
"name": "custom_height"
},
"model_custom_width": {
"name": "custom_width"
},
"model_quality": {
"name": "quality"
},
"model_size": {
"name": "size"
},
"n": {
"name": "n",
"tooltip": "تعداد تصاویری که باید تولید شود"
},
"prompt": {
"name": "prompt",
"tooltip": "پرامپت متنی برای GPT Image"
},
"seed": {
"name": "seed",
"tooltip": "در backend هنوز پیاده‌سازی نشده است"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "بارگذاری و آماده‌سازی فایل‌های ورودی (متن، PDF و غیره) برای استفاده به عنوان ورودی در Node چت OpenAI. این فایل‌ها هنگام تولید پاسخ توسط مدل OpenAI خوانده می‌شوند. 🛈 نکته: می‌توان این node را با سایر nodeهای فایل ورودی OpenAI زنجیره کرد.",
"display_name": "فایل‌های ورودی OpenAI ChatGPT",

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "Génération d'image à partir de texte unifiée et édition précise d'une seule phrase jusqu'à une résolution 4K.",
"display_name": "ByteDance Seedream 4.5 & 5.0",
"inputs": {
"control_after_generate": {
"name": "contrôle après génération"
},
"model": {
"name": "modèle"
},
"model_fail_on_partial": {
"name": "échec_sur_partiel"
},
"model_height": {
"name": "hauteur"
},
"model_max_images": {
"name": "images_max"
},
"model_size_preset": {
"name": "préréglage_taille"
},
"model_width": {
"name": "largeur"
},
"prompt": {
"name": "prompt",
"tooltip": "Invite textuelle pour créer ou éditer une image."
},
"seed": {
"name": "graine",
"tooltip": "Graine à utiliser pour la génération."
},
"watermark": {
"name": "filigrane",
"tooltip": "Ajouter ou non un filigrane « Généré par IA » à limage."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "Générer une vidéo en utilisant les modèles ByteDance via l'API basée sur l'invite",
"display_name": "ByteDance Texte vers Vidéo",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "Générez des images via Flux.2 [pro] ou Flux.2 [max] à partir dun prompt et dimages de référence optionnelles.",
"display_name": "Flux.2 Image",
"inputs": {
"control_after_generate": {
"name": "contrôle après génération"
},
"model": {
"name": "modèle"
},
"model_height": {
"name": "hauteur"
},
"model_width": {
"name": "largeur"
},
"prompt": {
"name": "prompt",
"tooltip": "Prompt pour la génération ou lédition dimage"
},
"seed": {
"name": "graine",
"tooltip": "La graine aléatoire utilisée pour créer le bruit."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "Génère des images de manière synchrone à partir d'une invite et d'une résolution.",
"display_name": "Flux.2 [max] Image",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "Modifiez une image existante à partir dun prompt textuel",
"display_name": "Grok Image Edit",
"inputs": {
"control_after_generate": {
"name": "contrôle après génération"
},
"model": {
"name": "modèle"
},
"model_aspect_ratio": {
"name": "ratio_daspect"
},
"model_number_of_images": {
"name": "nombre_dimages"
},
"model_resolution": {
"name": "résolution"
},
"prompt": {
"name": "prompt",
"tooltip": "Le prompt textuel utilisé pour générer limage"
},
"seed": {
"name": "graine",
"tooltip": "Graine pour déterminer si le nœud doit être relancé ; les résultats réels sont non déterministes quel que soit la graine."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "Générez des images avec Grok à partir d'une invite textuelle",
"display_name": "Grok Image",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "Génère des images via lAPI GPT Image dOpenAI.",
"display_name": "OpenAI GPT Image 2",
"inputs": {
"control_after_generate": {
"name": "contrôle après génération"
},
"model": {
"name": "modèle"
},
"model_background": {
"name": "arrière-plan"
},
"model_custom_height": {
"name": "hauteur_personnalisée"
},
"model_custom_width": {
"name": "largeur_personnalisée"
},
"model_quality": {
"name": "qualité"
},
"model_size": {
"name": "taille"
},
"n": {
"name": "n",
"tooltip": "Combien dimages générer"
},
"prompt": {
"name": "prompt",
"tooltip": "Prompt textuel pour GPT Image"
},
"seed": {
"name": "graine",
"tooltip": "non implémenté côté serveur pour le moment"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "Charge et prépare les fichiers d'entrée (texte, pdf, etc.) à inclure comme entrées pour le nœud de chat OpenAI. Les fichiers seront lus par le modèle OpenAI lors de la génération d'une réponse. 🛈 ASTUCE : Peut être chaîné avec d'autres nœuds de fichiers d'entrée OpenAI.",
"display_name": "Fichiers d'entrée OpenAI ChatGPT",

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "統合されたテキストから画像生成と、最大4K解像度での正確な単一文編集。",
"display_name": "ByteDance Seedream 4.5 & 5.0",
"inputs": {
"control_after_generate": {
"name": "生成後のコントロール"
},
"model": {
"name": "モデル"
},
"model_fail_on_partial": {
"name": "部分的な失敗時に停止"
},
"model_height": {
"name": "高さ"
},
"model_max_images": {
"name": "最大画像数"
},
"model_size_preset": {
"name": "サイズプリセット"
},
"model_width": {
"name": "幅"
},
"prompt": {
"name": "プロンプト",
"tooltip": "画像の作成または編集のためのテキストプロンプト。"
},
"seed": {
"name": "シード",
"tooltip": "生成に使用するシード。"
},
"watermark": {
"name": "ウォーターマーク",
"tooltip": "画像に「AI生成」のウォーターマークを追加するかどうか。"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "プロンプトに基づきAPI経由でByteDanceモデルを使用して動画を生成",
"display_name": "ByteDance テキストから動画へ",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "Flux.2 [pro] または Flux.2 [max] を使い、プロンプトとオプションの参照画像から画像を生成します。",
"display_name": "Flux.2 Image",
"inputs": {
"control_after_generate": {
"name": "生成後のコントロール"
},
"model": {
"name": "モデル"
},
"model_height": {
"name": "高さ"
},
"model_width": {
"name": "幅"
},
"prompt": {
"name": "プロンプト",
"tooltip": "画像生成または編集のためのプロンプト"
},
"seed": {
"name": "シード",
"tooltip": "ノイズ生成に使用されるランダムシード。"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "プロンプトと解像度に基づいて同期的に画像を生成します。",
"display_name": "Flux.2 [max] 画像",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "既存の画像をテキストプロンプトに基づいて編集します",
"display_name": "Grok Image Edit",
"inputs": {
"control_after_generate": {
"name": "生成後のコントロール"
},
"model": {
"name": "モデル"
},
"model_aspect_ratio": {
"name": "アスペクト比"
},
"model_number_of_images": {
"name": "画像数"
},
"model_resolution": {
"name": "解像度"
},
"prompt": {
"name": "プロンプト",
"tooltip": "画像生成に使用されるテキストプロンプト"
},
"seed": {
"name": "シード",
"tooltip": "ノードを再実行するかどうかを決定するシード;実際の結果はシードに関係なく非決定的です。"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "テキストプロンプトに基づいてGrokで画像を生成します",
"display_name": "Grok画像生成",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "OpenAIのGPT Imageエンドポイントを使って画像を生成します。",
"display_name": "OpenAI GPT Image 2",
"inputs": {
"control_after_generate": {
"name": "生成後のコントロール"
},
"model": {
"name": "モデル"
},
"model_background": {
"name": "背景"
},
"model_custom_height": {
"name": "カスタム高さ"
},
"model_custom_width": {
"name": "カスタム幅"
},
"model_quality": {
"name": "品質"
},
"model_size": {
"name": "サイズ"
},
"n": {
"name": "n",
"tooltip": "生成する画像の数"
},
"prompt": {
"name": "プロンプト",
"tooltip": "GPT Image用のテキストプロンプト"
},
"seed": {
"name": "シード",
"tooltip": "バックエンドではまだ未実装"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "OpenAIチャットードの入力として含める入力ファイルテキスト、PDFなどを読み込み、準備します。ファイルはOpenAIモデルによって応答生成時に読み込まれます。🛈 ヒント: 他のOpenAI入力ファイルードと連結できます。",
"display_name": "OpenAI ChatGPT 入力ファイル",

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "최대 4K 해상도에서 통합 텍스트-이미지 생성 및 정밀한 단일 문장 편집.",
"display_name": "ByteDance Seedream 4.5 & 5.0",
"inputs": {
"control_after_generate": {
"name": "생성 후 제어"
},
"model": {
"name": "모델"
},
"model_fail_on_partial": {
"name": "부분 실패 시 중단"
},
"model_height": {
"name": "높이"
},
"model_max_images": {
"name": "최대 이미지 수"
},
"model_size_preset": {
"name": "사이즈 프리셋"
},
"model_width": {
"name": "너비"
},
"prompt": {
"name": "프롬프트",
"tooltip": "이미지를 생성하거나 편집하기 위한 텍스트 프롬프트입니다."
},
"seed": {
"name": "시드",
"tooltip": "생성에 사용할 시드입니다."
},
"watermark": {
"name": "워터마크",
"tooltip": "이미지에 \"AI 생성됨\" 워터마크를 추가할지 여부입니다."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "프롬프트를 기반으로 API를 통해 ByteDance 모델을 사용하여 비디오 생성",
"display_name": "ByteDance 텍스트-비디오",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "프롬프트와 선택적 참조 이미지를 통해 Flux.2 [pro] 또는 Flux.2 [max]로 이미지 생성.",
"display_name": "Flux.2 Image",
"inputs": {
"control_after_generate": {
"name": "생성 후 제어"
},
"model": {
"name": "모델"
},
"model_height": {
"name": "높이"
},
"model_width": {
"name": "너비"
},
"prompt": {
"name": "프롬프트",
"tooltip": "이미지 생성 또는 편집을 위한 프롬프트"
},
"seed": {
"name": "시드",
"tooltip": "노이즈 생성을 위한 랜덤 시드입니다."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "프롬프트와 해상도에 따라 이미지를 동기적으로 생성합니다.",
"display_name": "Flux.2 [max] 이미지",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "텍스트 프롬프트를 기반으로 기존 이미지를 수정합니다.",
"display_name": "Grok 이미지 편집",
"inputs": {
"control_after_generate": {
"name": "생성 후 제어"
},
"model": {
"name": "모델"
},
"model_aspect_ratio": {
"name": "종횡비"
},
"model_number_of_images": {
"name": "이미지 개수"
},
"model_resolution": {
"name": "해상도"
},
"prompt": {
"name": "프롬프트",
"tooltip": "이미지 생성을 위해 사용되는 텍스트 프롬프트입니다."
},
"seed": {
"name": "시드",
"tooltip": "노드가 다시 실행되어야 하는지 결정하는 시드입니다. 실제 결과는 시드와 관계없이 비결정적입니다."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "텍스트 프롬프트를 기반으로 Grok을 사용해 이미지를 생성합니다",
"display_name": "Grok 이미지",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "OpenAI의 GPT 이미지 엔드포인트를 통해 이미지를 생성합니다.",
"display_name": "OpenAI GPT 이미지 2",
"inputs": {
"control_after_generate": {
"name": "생성 후 제어"
},
"model": {
"name": "모델"
},
"model_background": {
"name": "배경"
},
"model_custom_height": {
"name": "사용자 지정 높이"
},
"model_custom_width": {
"name": "사용자 지정 너비"
},
"model_quality": {
"name": "품질"
},
"model_size": {
"name": "사이즈"
},
"n": {
"name": "개수",
"tooltip": "생성할 이미지 수"
},
"prompt": {
"name": "프롬프트",
"tooltip": "GPT 이미지용 텍스트 프롬프트"
},
"seed": {
"name": "시드",
"tooltip": "백엔드에서 아직 구현되지 않음"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "OpenAI 채팅 노드에 대한 입력으로 포함할 입력 파일(텍스트, PDF 등)을 로드하고 준비합니다. 파일은 OpenAI 모델이 응답을 생성할 때 읽힙니다. 🛈 팁: 다른 OpenAI 입력 파일 노드와 함께 연결하여 사용할 수 있습니다.",
"display_name": "OpenAI ChatGPT 입력 파일",

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "Geração unificada de texto para imagem e edição precisa de frases únicas em até 4K de resolução.",
"display_name": "ByteDance Seedream 4.5 & 5.0",
"inputs": {
"control_after_generate": {
"name": "controle após gerar"
},
"model": {
"name": "modelo"
},
"model_fail_on_partial": {
"name": "falhar_em_parcial"
},
"model_height": {
"name": "altura"
},
"model_max_images": {
"name": "máx_imagens"
},
"model_size_preset": {
"name": "predefinição_de_tamanho"
},
"model_width": {
"name": "largura"
},
"prompt": {
"name": "prompt",
"tooltip": "Prompt de texto para criar ou editar uma imagem."
},
"seed": {
"name": "semente",
"tooltip": "Semente utilizada para a geração."
},
"watermark": {
"name": "marca d'água",
"tooltip": "Se deve adicionar uma marca d'água \"Gerado por IA\" à imagem."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "Gere vídeo usando modelos ByteDance via API com base no prompt",
"display_name": "ByteDance Texto para Vídeo",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "Gere imagens via Flux.2 [pro] ou Flux.2 [max] a partir de um prompt e imagens de referência opcionais.",
"display_name": "Flux.2 Image",
"inputs": {
"control_after_generate": {
"name": "controle após gerar"
},
"model": {
"name": "modelo"
},
"model_height": {
"name": "altura"
},
"model_width": {
"name": "largura"
},
"prompt": {
"name": "prompt",
"tooltip": "Prompt para a geração ou edição da imagem"
},
"seed": {
"name": "semente",
"tooltip": "A semente aleatória usada para criar o ruído."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "Gera imagens de forma síncrona com base no prompt e na resolução.",
"display_name": "Flux.2 [max] Imagem",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "Modifique uma imagem existente com base em um prompt de texto",
"display_name": "Grok Image Edit",
"inputs": {
"control_after_generate": {
"name": "controle após gerar"
},
"model": {
"name": "modelo"
},
"model_aspect_ratio": {
"name": "proporção"
},
"model_number_of_images": {
"name": "número_de_imagens"
},
"model_resolution": {
"name": "resolução"
},
"prompt": {
"name": "prompt",
"tooltip": "O prompt de texto usado para gerar a imagem"
},
"seed": {
"name": "semente",
"tooltip": "Semente para determinar se o nó deve ser executado novamente; os resultados reais são não determinísticos independentemente da semente."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "Gere imagens usando Grok com base em um prompt de texto",
"display_name": "Grok Image",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "Gera imagens via endpoint GPT Image da OpenAI.",
"display_name": "OpenAI GPT Image 2",
"inputs": {
"control_after_generate": {
"name": "controle após gerar"
},
"model": {
"name": "modelo"
},
"model_background": {
"name": "fundo"
},
"model_custom_height": {
"name": "altura_personalizada"
},
"model_custom_width": {
"name": "largura_personalizada"
},
"model_quality": {
"name": "qualidade"
},
"model_size": {
"name": "tamanho"
},
"n": {
"name": "n",
"tooltip": "Quantas imagens gerar"
},
"prompt": {
"name": "prompt",
"tooltip": "Prompt de texto para o GPT Image"
},
"seed": {
"name": "semente",
"tooltip": "ainda não implementado no backend"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "Carrega e prepara arquivos de entrada (texto, pdf, etc.) para incluir como entradas no Nó de Chat da OpenAI. Os arquivos serão lidos pelo modelo OpenAI ao gerar uma resposta. 🛈 DICA: Pode ser encadeado com outros nós de Arquivo de Entrada OpenAI.",
"display_name": "Arquivos de Entrada do OpenAI ChatGPT",

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "Унифицированная генерация изображений по тексту и точное редактирование по одному предложению с разрешением до 4K.",
"display_name": "ByteDance Seedream 4.5 & 5.0",
"inputs": {
"control_after_generate": {
"name": "control after generate"
},
"model": {
"name": "model"
},
"model_fail_on_partial": {
"name": "fail_on_partial"
},
"model_height": {
"name": "height"
},
"model_max_images": {
"name": "max_images"
},
"model_size_preset": {
"name": "size_preset"
},
"model_width": {
"name": "width"
},
"prompt": {
"name": "prompt",
"tooltip": "Текстовый запрос для создания или редактирования изображения."
},
"seed": {
"name": "seed",
"tooltip": "Сид для генерации."
},
"watermark": {
"name": "watermark",
"tooltip": "Добавлять ли водяной знак «AI generated» на изображение."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "Создать видео с использованием моделей ByteDance через API на основе промпта",
"display_name": "ByteDance Текст в Видео",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "Генерация изображений с помощью Flux.2 [pro] или Flux.2 [max] по текстовому запросу и, при необходимости, референсным изображениям.",
"display_name": "Flux.2 Image",
"inputs": {
"control_after_generate": {
"name": "control after generate"
},
"model": {
"name": "model"
},
"model_height": {
"name": "height"
},
"model_width": {
"name": "width"
},
"prompt": {
"name": "prompt",
"tooltip": "Запрос для генерации или редактирования изображения"
},
"seed": {
"name": "seed",
"tooltip": "Случайный сид, используемый для создания шума."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "Генерирует изображения синхронно на основе запроса и разрешения.",
"display_name": "Flux.2 [max] Image",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "Изменение существующего изображения на основе текстового запроса",
"display_name": "Grok Image Edit",
"inputs": {
"control_after_generate": {
"name": "control after generate"
},
"model": {
"name": "model"
},
"model_aspect_ratio": {
"name": "aspect_ratio"
},
"model_number_of_images": {
"name": "number_of_images"
},
"model_resolution": {
"name": "resolution"
},
"prompt": {
"name": "prompt",
"tooltip": "Текстовый запрос, используемый для генерации изображения"
},
"seed": {
"name": "seed",
"tooltip": "Сид для определения необходимости повторного запуска узла; фактические результаты не детерминированы независимо от сида."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "Генерировать изображения с помощью Grok на основе текстового запроса",
"display_name": "Grok Image",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "Генерирует изображения через конечную точку OpenAI GPT Image.",
"display_name": "OpenAI GPT Image 2",
"inputs": {
"control_after_generate": {
"name": "control after generate"
},
"model": {
"name": "model"
},
"model_background": {
"name": "background"
},
"model_custom_height": {
"name": "custom_height"
},
"model_custom_width": {
"name": "custom_width"
},
"model_quality": {
"name": "quality"
},
"model_size": {
"name": "size"
},
"n": {
"name": "n",
"tooltip": "Сколько изображений сгенерировать"
},
"prompt": {
"name": "prompt",
"tooltip": "Текстовый запрос для GPT Image"
},
"seed": {
"name": "seed",
"tooltip": "еще не реализовано на сервере"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "Загружает и подготавливает входные файлы (текст, pdf и т.д.) для включения в качестве входных данных для узла Чат OpenAI. Файлы будут прочитаны моделью OpenAI при генерации ответа. 🛈 СОВЕТ: Может быть объединен в цепочку с другими узлами Входных файлов OpenAI.",
"display_name": "Файлы ввода OpenAI ChatGPT",

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "Birleştirilmiş metinden-görüntüye üretim ve 4K çözünürlüğe kadar hassas tek cümle düzenleme.",
"display_name": "ByteDance Seedream 4.5 & 5.0",
"inputs": {
"control_after_generate": {
"name": "oluşturduktan sonra kontrol"
},
"model": {
"name": "model"
},
"model_fail_on_partial": {
"name": "kısmi başarısızlıkta dur"
},
"model_height": {
"name": "yükseklik"
},
"model_max_images": {
"name": "maksimum_görüntü"
},
"model_size_preset": {
"name": "boyut_önayarı"
},
"model_width": {
"name": "genişlik"
},
"prompt": {
"name": "istem",
"tooltip": "Bir görüntü oluşturmak veya düzenlemek için metin istemi."
},
"seed": {
"name": "tohum",
"tooltip": "Üretim için kullanılacak tohum."
},
"watermark": {
"name": "filigran",
"tooltip": "Görüntüye \"Yapay Zeka ile oluşturuldu\" filigranı eklenip eklenmeyeceği."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "Prompt'a dayalı olarak api üzerinden ByteDance modellerini kullanarak video oluştur",
"display_name": "ByteDance Metinden Videoya",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "Bir istem ve isteğe bağlı referans görüntülerden Flux.2 [pro] veya Flux.2 [max] ile görüntüler oluşturun.",
"display_name": "Flux.2 Görüntü",
"inputs": {
"control_after_generate": {
"name": "oluşturduktan sonra kontrol"
},
"model": {
"name": "model"
},
"model_height": {
"name": "yükseklik"
},
"model_width": {
"name": "genişlik"
},
"prompt": {
"name": "istem",
"tooltip": "Görüntü oluşturma veya düzenleme için istem"
},
"seed": {
"name": "tohum",
"tooltip": "Gürültü oluşturmak için kullanılan rastgele tohum."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "İstem ve çözünürlüğe göre senkron bir şekilde görseller üretir.",
"display_name": "Flux.2 [max] Görsel",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "Mevcut bir görüntüyü bir metin istemine göre değiştirin",
"display_name": "Grok Görüntü Düzenleme",
"inputs": {
"control_after_generate": {
"name": "oluşturduktan sonra kontrol"
},
"model": {
"name": "model"
},
"model_aspect_ratio": {
"name": "en-boy_oranı"
},
"model_number_of_images": {
"name": "görüntü_sayısı"
},
"model_resolution": {
"name": "çözünürlük"
},
"prompt": {
"name": "istem",
"tooltip": "Görüntü oluşturmak için kullanılan metin istemi"
},
"seed": {
"name": "tohum",
"tooltip": "Düğümün tekrar çalıştırılıp çalıştırılmayacağını belirlemek için tohum; gerçek sonuçlar tohumdan bağımsız olarak belirlenemezdir."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "Bir metin istemine göre Grok ile görüntüler oluştur",
"display_name": "Grok Görüntü",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "OpenAI'nin GPT Görüntü uç noktası ile görüntüler oluşturur.",
"display_name": "OpenAI GPT Görüntü 2",
"inputs": {
"control_after_generate": {
"name": "oluşturduktan sonra kontrol"
},
"model": {
"name": "model"
},
"model_background": {
"name": "arka_plan"
},
"model_custom_height": {
"name": "özel_yükseklik"
},
"model_custom_width": {
"name": "özel_genişlik"
},
"model_quality": {
"name": "kalite"
},
"model_size": {
"name": "boyut"
},
"n": {
"name": "n",
"tooltip": "Kaç görüntü oluşturulacak"
},
"prompt": {
"name": "istem",
"tooltip": "GPT Görüntü için metin istemi"
},
"seed": {
"name": "tohum",
"tooltip": "Henüz arka uçta uygulanmadı"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "OpenAI Sohbet Düğümü için girdi olarak eklemek üzere girdi dosyalarını (metin, pdf vb.) yükler ve hazırlar. Dosyalar, yanıt oluşturulurken OpenAI modeli tarafından okunacaktır. 🛈 İPUCU: Diğer OpenAI Girdi Dosyası düğümleriyle zincirlenebilir.",
"display_name": "OpenAI ChatGPT Girdi Dosyaları",

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "統一的文字轉圖與精確單句編輯,最高支援 4K 解析度。",
"display_name": "ByteDance Seedream 4.5 & 5.0",
"inputs": {
"control_after_generate": {
"name": "生成後控制"
},
"model": {
"name": "模型"
},
"model_fail_on_partial": {
"name": "部分失敗時終止"
},
"model_height": {
"name": "高度"
},
"model_max_images": {
"name": "最大圖像數"
},
"model_size_preset": {
"name": "尺寸預設"
},
"model_width": {
"name": "寬度"
},
"prompt": {
"name": "提示詞",
"tooltip": "用於創建或編輯圖像的文字提示。"
},
"seed": {
"name": "種子",
"tooltip": "用於生成的隨機種子。"
},
"watermark": {
"name": "浮水印",
"tooltip": "是否在圖像上添加「AI 生成」浮水印。"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "透過 API 使用字節跳動模型根據提示生成影片",
"display_name": "字節跳動文字轉影片",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "透過 Flux.2 [pro] 或 Flux.2 [max],根據提示詞與可選參考圖像生成圖像。",
"display_name": "Flux.2 圖像",
"inputs": {
"control_after_generate": {
"name": "生成後控制"
},
"model": {
"name": "模型"
},
"model_height": {
"name": "高度"
},
"model_width": {
"name": "寬度"
},
"prompt": {
"name": "提示詞",
"tooltip": "用於圖像生成或編輯的提示詞"
},
"seed": {
"name": "種子",
"tooltip": "用於產生雜訊的隨機種子。"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "根據提示詞與解析度同步生成圖像。",
"display_name": "Flux.2 [max] 圖像",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "根據文字提示修改現有圖像",
"display_name": "Grok 圖像編輯",
"inputs": {
"control_after_generate": {
"name": "生成後控制"
},
"model": {
"name": "模型"
},
"model_aspect_ratio": {
"name": "長寬比"
},
"model_number_of_images": {
"name": "圖像數量"
},
"model_resolution": {
"name": "解析度"
},
"prompt": {
"name": "提示詞",
"tooltip": "用於生成圖像的文字提示"
},
"seed": {
"name": "種子",
"tooltip": "決定節點是否重新執行的種子;實際結果無論種子如何皆為非確定性。"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "根據文字提示使用 Grok 生成圖像",
"display_name": "Grok 圖像",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "透過 OpenAI 的 GPT Image 端點生成圖像。",
"display_name": "OpenAI GPT 圖像 2",
"inputs": {
"control_after_generate": {
"name": "生成後控制"
},
"model": {
"name": "模型"
},
"model_background": {
"name": "背景"
},
"model_custom_height": {
"name": "自訂高度"
},
"model_custom_width": {
"name": "自訂寬度"
},
"model_quality": {
"name": "品質"
},
"model_size": {
"name": "尺寸"
},
"n": {
"name": "數量",
"tooltip": "要生成多少張圖像"
},
"prompt": {
"name": "提示詞",
"tooltip": "GPT Image 的文字提示"
},
"seed": {
"name": "種子",
"tooltip": "後端尚未實作"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "載入並準備輸入文件文字、pdf 等)以作為 OpenAI 聊天節點的輸入。生成回應時OpenAI 模型將讀取這些文件。🛈 提示:可以與其他 OpenAI 輸入文件節點鏈接使用。",
"display_name": "OpenAI ChatGPT 輸入文件",

View File

@@ -953,6 +953,50 @@
}
}
},
"ByteDanceSeedreamNodeV2": {
"description": "统一的文本生成图像与精确的单句编辑最高支持4K分辨率。",
"display_name": "ByteDance Seedream 4.5 & 5.0",
"inputs": {
"control_after_generate": {
"name": "生成后控制"
},
"model": {
"name": "模型"
},
"model_fail_on_partial": {
"name": "部分失败时终止"
},
"model_height": {
"name": "高度"
},
"model_max_images": {
"name": "最大图像数"
},
"model_size_preset": {
"name": "尺寸预设"
},
"model_width": {
"name": "宽度"
},
"prompt": {
"name": "提示词",
"tooltip": "用于创建或编辑图像的文本提示。"
},
"seed": {
"name": "种子",
"tooltip": "用于生成的种子。"
},
"watermark": {
"name": "水印",
"tooltip": "是否在图像上添加“AI生成”水印。"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"ByteDanceTextToVideoNode": {
"description": "通过API基于提示使用字节跳动模型生成视频",
"display_name": "字节跳动文生视频",
@@ -3432,6 +3476,37 @@
}
}
},
"Flux2ImageNode": {
"description": "通过Flux.2 [pro]或Flux.2 [max],根据提示词和可选参考图像生成图像。",
"display_name": "Flux.2 图像",
"inputs": {
"control_after_generate": {
"name": "生成后控制"
},
"model": {
"name": "模型"
},
"model_height": {
"name": "高度"
},
"model_width": {
"name": "宽度"
},
"prompt": {
"name": "提示词",
"tooltip": "用于图像生成或编辑的提示词"
},
"seed": {
"name": "种子",
"tooltip": "用于生成噪声的随机种子。"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Flux2MaxImageNode": {
"description": "根据提示词和分辨率同步生成图像。",
"display_name": "Flux.2 [max] 图像",
@@ -4455,6 +4530,40 @@
}
}
},
"GrokImageEditNodeV2": {
"description": "根据文本提示修改已有图像",
"display_name": "Grok 图像编辑",
"inputs": {
"control_after_generate": {
"name": "生成后控制"
},
"model": {
"name": "模型"
},
"model_aspect_ratio": {
"name": "宽高比"
},
"model_number_of_images": {
"name": "图像数量"
},
"model_resolution": {
"name": "分辨率"
},
"prompt": {
"name": "提示词",
"tooltip": "用于生成图像的文本提示"
},
"seed": {
"name": "种子",
"tooltip": "用于决定节点是否重新运行的种子;无论种子如何,实际结果都是非确定性的。"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"GrokImageNode": {
"description": "使用 Grok 根据文本提示生成图像",
"display_name": "Grok 图像",
@@ -11965,6 +12074,50 @@
}
}
},
"OpenAIGPTImageNodeV2": {
"description": "通过OpenAI的GPT Image端点生成图像。",
"display_name": "OpenAI GPT 图像 2",
"inputs": {
"control_after_generate": {
"name": "生成后控制"
},
"model": {
"name": "模型"
},
"model_background": {
"name": "背景"
},
"model_custom_height": {
"name": "自定义高度"
},
"model_custom_width": {
"name": "自定义宽度"
},
"model_quality": {
"name": "质量"
},
"model_size": {
"name": "尺寸"
},
"n": {
"name": "数量",
"tooltip": "要生成的图像数量"
},
"prompt": {
"name": "提示词",
"tooltip": "用于GPT Image的文本提示"
},
"seed": {
"name": "种子",
"tooltip": "后端尚未实现"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"OpenAIInputFiles": {
"description": "加载并准备输入文件文本、PDF等作为OpenAI聊天节点的输入。生成响应时OpenAI模型将读取这些文件。🛈 提示可与其他OpenAI输入文件节点链式连接。",
"display_name": "OpenAI ChatGPT Input Files",

View File

@@ -197,5 +197,18 @@ export const MODEL_NODE_MAPPINGS: ReadonlyArray<
['mediapipe', 'LivePortraitLoadMediaPipeCropper', ''],
// ---- Superprompt text enhancement ----
['superprompt-v1', 'Superprompt', '']
['superprompt-v1', 'Superprompt', ''],
// ---- ComfyUI core background removal (v0.21+) ----
['background_removal', 'LoadBackgroundRemovalModel', 'bg_removal_name'],
// ---- ComfyUI core frame interpolation (v0.21+) ----
['frame_interpolation', 'FrameInterpolationModelLoader', 'model_name'],
// ---- FILM frame interpolation (ComfyUI-Frame-Interpolation) ----
['film', 'FILM VFI', 'ckpt_name'],
// ---- Ultralytics YOLO detectors (ComfyUI-Impact-Pack) ----
['ultralytics/bbox', 'UltralyticsDetectorProvider', 'model_name'],
['ultralytics/segm', 'UltralyticsDetectorProvider', 'model_name']
] as const satisfies ReadonlyArray<readonly [string, string, string]>

View File

@@ -38,6 +38,17 @@ export const CORE_SETTINGS: SettingParams[] = [
options: ['default', 'v1 (legacy)', 'litegraph (legacy)'],
defaultValue: 'default'
},
{
id: 'Comfy.NodeSearchBox.ReplaceCanvasMenu',
category: ['Comfy', 'Node Search Box', 'ReplaceCanvasMenu'],
experimental: true,
name: 'Replace canvas right-click "Add Node" with search box',
tooltip:
'When enabled, the right-click canvas menu opens the node search box instead of the LiteGraph category submenu. The search box includes blueprints, partner nodes, core nodes, and extensions.',
type: 'boolean',
defaultValue: false,
versionAdded: '1.46.0'
},
{
id: 'Comfy.LinkRelease.Action',
category: ['LiteGraph', 'LinkRelease', 'Action'],

View File

@@ -344,6 +344,7 @@ const zSettings = z.object({
'Comfy.NodeSearchBoxImpl.ShowCategory': z.boolean(),
'Comfy.NodeSearchBoxImpl.ShowIdName': z.boolean(),
'Comfy.NodeSearchBoxImpl.ShowNodeFrequency': z.boolean(),
'Comfy.NodeSearchBox.ReplaceCanvasMenu': z.boolean(),
'Comfy.NodeSuggestions.number': z.number(),
'Comfy.Node.BypassAllLinksOnDelete': z.boolean(),
'Comfy.Node.Opacity': z.number(),

View File

@@ -26,9 +26,6 @@ vi.mock('@/platform/settings/settingStore', () => ({
useSettingStore: () => mockSettingStore
}))
//@ts-expect-error Define global for the test
global.__COMFYUI_FRONTEND_VERSION__ = '1.24.0'
import { useNewUserService } from '@/services/useNewUserService'
describe('useNewUserService', () => {
@@ -120,6 +117,73 @@ describe('useNewUserService', () => {
expect(service.isNewUser()).toBe(false)
})
it('should identify existing user when V1 draft store keys exist', async () => {
mockSettingStore.settingValues = {}
mockSettingStore.get.mockReturnValue(undefined)
mockLocalStorage.getItem.mockImplementation((key: string) => {
if (key === 'Comfy.Workflow.Drafts') return '{}'
return null
})
await service.initializeIfNewUser()
expect(service.isNewUser()).toBe(false)
})
it('should identify existing user when V1 draft order key exists', async () => {
mockSettingStore.settingValues = {}
mockSettingStore.get.mockReturnValue(undefined)
mockLocalStorage.getItem.mockImplementation((key: string) => {
if (key === 'Comfy.Workflow.DraftOrder') return '[]'
return null
})
await service.initializeIfNewUser()
expect(service.isNewUser()).toBe(false)
})
it('should identify existing user when V2 draft index has entries', async () => {
mockSettingStore.settingValues = {}
mockSettingStore.get.mockReturnValue(undefined)
mockLocalStorage.getItem.mockImplementation((key: string) => {
if (key === 'Comfy.Workflow.DraftIndex.v2:personal')
return '{"v":2,"updatedAt":1,"order":["abc"],"entries":{"abc":{"path":"workflows/Untitled.json","name":"Untitled","isTemporary":true,"updatedAt":1}}}'
return null
})
await service.initializeIfNewUser()
expect(service.isNewUser()).toBe(false)
})
it('should identify new user when V2 draft index exists but is empty', async () => {
mockSettingStore.settingValues = {}
mockSettingStore.get.mockReturnValue(undefined)
mockLocalStorage.getItem.mockImplementation((key: string) => {
if (key === 'Comfy.Workflow.DraftIndex.v2:personal')
return '{"v":2,"updatedAt":1,"order":[],"entries":{}}'
return null
})
await service.initializeIfNewUser()
expect(service.isNewUser()).toBe(true)
})
it('should identify new user when V2 draft index is malformed', async () => {
mockSettingStore.settingValues = {}
mockSettingStore.get.mockReturnValue(undefined)
mockLocalStorage.getItem.mockImplementation((key: string) => {
if (key === 'Comfy.Workflow.DraftIndex.v2:personal') return 'not json'
return null
})
await service.initializeIfNewUser()
expect(service.isNewUser()).toBe(true)
})
it('should identify new user when tutorial is explicitly false', async () => {
mockSettingStore.settingValues = { 'Comfy.TutorialCompleted': false }
mockSettingStore.get.mockImplementation((key: string) => {

View File

@@ -2,6 +2,24 @@ import { ref, shallowRef } from 'vue'
import { createSharedComposable } from '@vueuse/core'
import { useSettingStore } from '@/platform/settings/settingStore'
function hasV2DraftHistory(raw: string | null): boolean {
if (!raw) return false
try {
const parsed = JSON.parse(raw) as {
order?: unknown
entries?: unknown
}
const orderLength = Array.isArray(parsed.order) ? parsed.order.length : 0
const entriesCount =
parsed.entries && typeof parsed.entries === 'object'
? Object.keys(parsed.entries as Record<string, unknown>).length
: 0
return orderLength > 0 || entriesCount > 0
} catch {
return false
}
}
function _useNewUserService() {
const settingStore = useSettingStore()
const pendingCallbacks = shallowRef<Array<() => Promise<void>>>([])
@@ -18,12 +36,32 @@ function _useNewUserService() {
const isNewUserSettings =
Object.keys(settingStore.settingValues).length === 0 ||
!settingStore.get('Comfy.TutorialCompleted')
const hasNoWorkflow = !localStorage.getItem('workflow')
const hasNoPreviousWorkflow = !localStorage.getItem(
'Comfy.PreviousWorkflow'
// Legacy keys (pre-V1 and V1 persistence)
const hasNoLegacyWorkflow =
!localStorage.getItem('workflow') &&
!localStorage.getItem('Comfy.PreviousWorkflow')
// V1 draft store keys
const hasNoV1Drafts =
!localStorage.getItem('Comfy.Workflow.Drafts') &&
!localStorage.getItem('Comfy.Workflow.DraftOrder')
// V2 draft index key (scoped to personal workspace; cloud workspace id
// comes from sessionStorage which may not be set yet at this point).
// Check for actual draft history rather than key existence: an empty
// index is written by `migrateV1toV2()` for genuine new users during
// startup, so key presence alone is not evidence of prior usage.
const hasNoV2DraftIndex = !hasV2DraftHistory(
localStorage.getItem('Comfy.Workflow.DraftIndex.v2:personal')
)
return isNewUserSettings && hasNoWorkflow && hasNoPreviousWorkflow
return (
isNewUserSettings &&
hasNoLegacyWorkflow &&
hasNoV1Drafts &&
hasNoV2DraftIndex
)
}
async function registerInitCallback(callback: () => Promise<void>) {

View File

@@ -3,6 +3,7 @@ import { setActivePinia } from 'pinia'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type NodeSearchBoxPopover from '@/components/searchbox/NodeSearchBoxPopover.vue'
import type { CanvasPointerEvent } from '@/lib/litegraph/src/litegraph'
import type { useSettingStore } from '@/platform/settings/settingStore'
import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore'
@@ -135,4 +136,43 @@ describe('useSearchBoxStore', () => {
expect(store.visible).toBe(false)
})
})
describe('openAtEvent', () => {
const event = {
canvasX: 123,
canvasY: 456
} as unknown as CanvasPointerEvent
it('forwards the event to the popover when one is registered', () => {
vi.mocked(mockSettingStore.get).mockReturnValue('default')
const store = useSearchBoxStore()
const mockPopover = createMockPopover()
store.setPopoverRef(mockPopover)
store.openAtEvent(event)
expect(vi.mocked(mockPopover.showSearchBox)).toHaveBeenCalledWith(event)
expect(store.visible).toBe(false)
})
it('falls back to showing the new search box when no popover is registered', () => {
vi.mocked(mockSettingStore.get).mockReturnValue('default')
const store = useSearchBoxStore()
store.setPopoverRef(null)
store.openAtEvent(event)
expect(store.visible).toBe(true)
})
it('does nothing when the legacy litegraph search box is selected and no popover is registered', () => {
vi.mocked(mockSettingStore.get).mockReturnValue('litegraph (legacy)')
const store = useSearchBoxStore()
store.setPopoverRef(null)
store.openAtEvent(event)
expect(store.visible).toBe(false)
})
})
})

View File

@@ -45,11 +45,22 @@ export const useSearchBoxStore = defineStore('searchBox', () => {
)
}
function openAtEvent(event: CanvasPointerEvent) {
if (popoverRef.value) {
popoverRef.value.showSearchBox(event)
return
}
if (newSearchBoxEnabled.value) {
visible.value = true
}
}
return {
useSearchBoxV2,
newSearchBoxEnabled,
setPopoverRef,
toggleVisible,
openAtEvent,
visible
}
})