Compare commits

..

33 Commits

Author SHA1 Message Date
bymyself
ee8ded10b8 fix: flatten slot context menu to avoid submenu overflow clipping
Amp-Thread-ID: https://ampcode.com/threads/T-019c3a65-ccea-72d8-855c-0d55837a3d13
2026-02-08 12:57:32 -08:00
bymyself
c1e6d87c3d feat: add 'Rename slot' to slot context menu
Amp-Thread-ID: https://ampcode.com/threads/T-019c3056-108e-7248-9c30-7d236197edcc
2026-02-06 01:34:30 -08:00
bymyself
76373a6eea feat: slot context menu 'Connect to...' and auto-pan during link drag
Add right-click context menu on slot dots with 'Connect to...' submenu
listing compatible existing nodes. Uses Vue/PrimeVue ContextMenu pattern
matching NodeContextMenu.vue. Finds compatible nodes via
LiteGraph.isValidConnection, filters wildcards/bypassed/connected inputs,
sorts by Y position, caps at 15 results.

Add auto-panning when dragging links near canvas edges. Integrated into
useSlotLinkInteraction via useAutoPan composable. Velocity-based rAF
panning that recomputes canvas coordinates after each offset change.

Amp-Thread-ID: https://ampcode.com/threads/T-019c3056-108e-7248-9c30-7d236197edcc
2026-02-06 01:22:35 -08:00
Johnpaul Chiwetelu
d05e4eac58 fix: include subfolder in asset download URL for audio/video files (#8684)
## Summary

- `getAssetUrl()` was constructing `/view` URLs without the `subfolder`
query parameter, causing backend to return "file not found" for assets
stored in subfolders (common for audio/video outputs)
- Preview/playback was unaffected because it uses `preview_url` from
`ResultItemImpl.url` which correctly includes `subfolder`
- Fixed `getAssetUrl()` to include `subfolder` from
`asset.user_metadata` when present
- Simplified download functions to prefer `preview_url` (already
correct) with `getAssetUrl` as fallback

## Test plan

- [ ] Generate audio/video output (e.g. via SaveAudio node) that saves
to a subfolder
- [ ] Right-click the asset in the assets sidebar and click Download —
should download successfully
- [ ] Select multiple audio/video assets and use bulk download — should
download all
- [ ] Verify image downloads still work as before
- [ ] Verify cloud environment downloads still work (uses `preview_url`
path)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
  * Added support for organizing and downloading assets from subfolders.

* **Refactor**
* Improved asset URL generation and download handling for better
reliability and performance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-06 05:06:11 +01:00
Comfy Org PR Bot
7f509cc018 1.39.8 (#8678)
Patch version increment to 1.39.8

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8678-1-39-8-2ff6d73d36508144b359cdd15822f6fb)
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-02-05 19:30:45 -08:00
Christian Byrne
23c8757447 fix: increase active node border weight from 2 to 3 (#8654)
## Summary

Increase stroke/outline weight for active node states to improve
visibility during workflow execution (COM-7770).

## Changes

- **What**: Increased border/stroke weight from 2 to 3 for active nodes
in both Vue Nodes and LiteGraph renderers
  - Vue Nodes: `outline-2` → `outline-3` in `LGraphNode.vue`
- LiteGraph: `lineWidth: 3` for `running` stroke (was default 1) and
`executionError` stroke (was 2) in `litegraphService.ts`
  - Updated test assertion to match

## Review Focus

Minimal visual change. The `executionError` lineWidth was also bumped
from 2 → 3 so error states remain at least as prominent as running
states.

> **Note:**
[#8603](https://github.com/Comfy-Org/ComfyUI_frontend/pull/8603) (by
@AustinMroz) also modifies `LGraphNode.vue` with a larger restructuring
(bottom buttons, badges, resize handle). The two PRs do not conflict —
#8603 does not touch the outline/border state classes or
`litegraphService.ts`, so both changes merge cleanly.

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: github-actions <github-actions@github.com>
2026-02-05 17:44:19 -08:00
Johnpaul Chiwetelu
7d3d00858a feat: add download button to audio preview player (#8628)
## Summary
- Adds a download icon button to the `AudioPreviewPlayer` widget for
PreviewAudio and SaveAudio nodes
- Reuses the existing `downloadFile` utility (same as video download)
- Button appears inline next to volume/options controls, matching the
player's existing UI style

## Test plan
- [x] Add a PreviewAudio or SaveAudio node, run a workflow that produces
audio output
- [x] Verify the download icon appears in the audio player controls
- [x] Click the download button and confirm the audio file downloads
correctly
- [x] Verify the button does not appear when no audio is loaded


https://github.com/user-attachments/assets/7fb2df16-82a6-40aa-a938-aed57032e30b

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8628-feat-add-download-button-to-audio-preview-player-2fe6d73d365081e3997fc45d3bb8cffc)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-02-06 01:35:32 +01:00
Christian Byrne
478cfc0b5e feat: replace puzzle icon with extensions-blocks icon for manager button (#8644)
## Summary

Replace the manager button puzzle icon with a custom extensions-blocks
SVG icon and add a "Manage Extensions" text label to the top bar button.

## Changes

- **What**: Swap `icon-[lucide--puzzle]` →
`icon-[comfy--extensions-blocks]` in TopMenuSection, ComfyMenuButton,
and ManagerDialog. Add visible "Manage Extensions" label (hidden below
md). Align tooltip with new label text.

## Review Focus

- Visual appearance of the new icon at different sizes
- Button layout with text label at md+ breakpoints
- Red dot notification positioning with wider button

Fixes COM-12161

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8644-feat-replace-puzzle-icon-with-extensions-blocks-icon-for-manager-button-2fe6d73d3650815c8867efc5a0842ef7)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-02-05 16:33:26 -08:00
Johnpaul Chiwetelu
90a701dd67 Road to No Explicit Any Part 11 (#8565)
## Summary

This PR removes `any` types from widgets, services, stores, and test
files, replacing them with proper TypeScript types.

### Key Changes

#### Type Safety Improvements
- Replaced `any` with `unknown`, explicit types, or proper interfaces
across widgets and services
- Added proper type imports (TgpuRoot, Point, StyleValue, etc.)
- Created typed interfaces (NumericWidgetOptions, TestWindow,
ImportFailureDetail, etc.)
- Fixed function return types to be non-nullable where appropriate
- Added type guards and null checks instead of non-null assertions
- Used `ComponentProps` from vue-component-type-helpers for component
testing

#### Widget System
- Added index signature to IWidgetOptions for Record compatibility
- Centralized disabled logic in WidgetInputNumberInput
- Moved template type assertions to computed properties
- Fixed ComboWidget getOptionLabel type assertions
- Improved remote widget type handling with runtime checks

#### Services & Stores
- Fixed getOrCreateViewer to return non-nullable values
- Updated addNodeOnGraph to use specific options type `{ pos?: Point }`
- Added proper type assertions for settings store retrieval
- Fixed executionIdToCurrentId return type (string | undefined)

#### Test Infrastructure
- Exported GraphOrSubgraph from litegraph barrel to avoid circular
dependencies
- Updated test fixtures with proper TypeScript types (TestInfo,
LGraphNode)
- Replaced loose Record types with ComponentProps in tests
- Added proper error handling in WebSocket fixture

#### Code Organization
- Created shared i18n-types module for locale data types
- Made ImportFailureDetail non-exported (internal use only)
- Added @public JSDoc tag to ElectronWindow type
- Fixed console.log usage in scripts to use allowed methods

### Files Changed

**Widgets & Components:**
-
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
-
src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue
-
src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue
- src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.vue
-
src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts
- src/lib/litegraph/src/widgets/ComboWidget.ts
- src/lib/litegraph/src/types/widgets.ts
- src/components/common/LazyImage.vue
- src/components/load3d/Load3dViewerContent.vue

**Services & Stores:**
- src/services/litegraphService.ts
- src/services/load3dService.ts
- src/services/colorPaletteService.ts
- src/stores/maskEditorStore.ts
- src/stores/nodeDefStore.ts
- src/platform/settings/settingStore.ts
- src/platform/workflow/management/stores/workflowStore.ts

**Composables & Utils:**
- src/composables/node/useWatchWidget.ts
- src/composables/useCanvasDrop.ts
- src/utils/widgetPropFilter.ts
- src/utils/queueDisplay.ts
- src/utils/envUtil.ts

**Test Files:**
- browser_tests/fixtures/ComfyPage.ts
- browser_tests/fixtures/ws.ts
- browser_tests/tests/actionbar.spec.ts
-
src/workbench/extensions/manager/components/manager/skeleton/PackCardGridSkeleton.test.ts
- src/lib/litegraph/src/subgraph/subgraphUtils.test.ts
- src/components/rightSidePanel/shared.test.ts
- src/platform/cloud/subscription/composables/useSubscription.test.ts
-
src/platform/workflow/persistence/composables/useWorkflowPersistence.test.ts

**Scripts & Types:**
- scripts/i18n-types.ts (new shared module)
- scripts/diff-i18n.ts
- scripts/check-unused-i18n-keys.ts
- src/workbench/extensions/manager/types/conflictDetectionTypes.ts
- src/types/algoliaTypes.ts
- src/types/simplifiedWidget.ts

**Infrastructure:**
- src/lib/litegraph/src/litegraph.ts (added GraphOrSubgraph export)
- src/lib/litegraph/src/infrastructure/CustomEventTarget.ts
- src/platform/assets/services/assetService.ts

**Stories:**
- apps/desktop-ui/src/views/InstallView.stories.ts
- src/components/queue/job/JobDetailsPopover.stories.ts

**Extension Manager:**
- src/workbench/extensions/manager/composables/useConflictDetection.ts
- src/workbench/extensions/manager/composables/useManagerQueue.ts
- src/workbench/extensions/manager/services/comfyManagerService.ts
- src/workbench/extensions/manager/utils/conflictMessageUtil.ts

### Testing

- [x] All TypeScript type checking passes (`pnpm typecheck`)
- [x] ESLint passes without errors (`pnpm lint`)
- [x] Format checks pass (`pnpm format:check`)
- [x] Knip (unused exports) passes (`pnpm knip`)
- [x] Pre-commit and pre-push hooks pass

Part of the "Road to No Explicit Any" initiative.

### Previous PRs in this series:
- Part 2: #7401
- Part 3: #7935
- Part 4: #7970
- Part 5: #8064
- Part 6: #8083
- Part 7: #8092
- Part 8 Group 1: #8253
- Part 8 Group 2: #8258
- Part 8 Group 3: #8304
- Part 8 Group 4: #8314
- Part 8 Group 5: #8329
- Part 8 Group 6: #8344
- Part 8 Group 7: #8459
- Part 8 Group 8: #8496
- Part 9: #8498
- Part 10: #8499

---------

Co-authored-by: Comfy Org PR Bot <snomiao+comfy-pr@gmail.com>
Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2026-02-05 16:29:28 -08:00
Johnpaul Chiwetelu
7f81e1afac ci: filter snapshot update job to only run @screenshot tagged tests (#8629)
## Summary
- Adds `--grep @screenshot` to the Playwright command in the
update-snapshots CI workflow
- Skips ~146 non-screenshot tests that don't produce any snapshot files,
reducing CI time and resource usage

## Details
All tests that call `toHaveScreenshot` are already tagged with
`@screenshot` (either at the `test.describe` or individual `test`
level). The snapshot update job was previously running every test
unnecessarily.

The `--grep` CLI flag is ANDed with each project's existing
`grep`/`grepInvert` settings, so all projects continue to work
correctly:
- `chromium`: `@screenshot` AND NOT `@mobile`
- `chromium-2x`: `@screenshot` AND `@2x`
- `mobile-chrome`: `@screenshot` AND `@mobile`

## Test plan
- [x] Trigger the update-snapshots workflow on a PR with the "New
Browser Test Expectations" label and verify only screenshot-tagged tests
run
- [x] Verify snapshot files are still correctly updated
2026-02-05 15:18:21 -08:00
AustinMroz
e26283e754 Revert delay of layout initialization (#8619)
#7591 added a one tick delay to layout initialization in an attempt to
resolve some layouting discrepancies. However, it appears to have
reintroduced node scaling issues and introduced a new bug that prevents
cloning nodes with alt+drag in vue.

Alternatives methods of resolving the original issue are being
investigated, but this change was causing more harm than good.

The prior PR included other changes (like a testing fix). Those changes
remain beneficial and do not need to be reverted.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8619-Revert-delay-of-layout-initialization-2fe6d73d365081fc9111c9457ea9752d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-02-05 09:17:41 -08:00
Jin Yi
1ca6e57ac4 fix: skip node replacement API call when feature is disabled (#8618) 2026-02-05 16:37:18 +09:00
Johnpaul Chiwetelu
3adecc4ded fix: prevent duplicate context menu items by using content-based comparison (#8602)
## Summary
- Switches from reference-based to content-based duplicate detection for
context menu items
- Fixes cases where extensions create duplicate menu items (different
objects with same content)
- Improves removal detection accuracy by comparing content strings
instead of object references

## Details
The previous implementation compared menu items by object reference,
which would miss duplicates when extensions added new objects with the
same content. This change:
- Creates Sets of content strings from menu items for efficient
duplicate detection
- Filters additions by checking if their content already exists
- Provides accurate count of removed items through content comparison

## Test plan
- [x] Unit tests pass (`pnpm test:unit
src/lib/litegraph/src/contextMenuCompat.test.ts`)
- [x] TypeScript compilation succeeds (`pnpm typecheck`)
- [x] Linting passes (`pnpm lint`)
- [x] Pre-commit hooks pass

## Before
<img width="633" height="1316" alt="Screenshot 2026-02-04 045422"
src="https://github.com/user-attachments/assets/972871e2-1fd6-45a4-bb6c-9ce73ce7aed7"
/>


## After
<img width="531" height="854" alt="Screenshot 2026-02-04 045918"
src="https://github.com/user-attachments/assets/977bed37-dfb8-41d7-b659-88c477ff8a02"
/>

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved context menu compatibility: more accurate detection of
added/removed menu items and clearer warnings when items are removed.
* **Refactor**
* Updated numeric input formatting to use locale-aware formatting logic,
preserving grouping and precision behavior.
* **Tests**
* Added a test ensuring legacy menu extraction handles items with
undefined content correctly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8602-fix-prevent-duplicate-context-menu-items-by-using-content-based-comparison-2fd6d73d36508197aa74c3409c7425fa)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Terry Jia <terryjia88@gmail.com>
2026-02-04 20:06:58 -08:00
Comfy Org PR Bot
7404756b6d 1.39.7 (#8614)
Patch version increment to 1.39.7

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8614-1-39-7-2fe6d73d365081449fa7cc9143adf0fa)
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-02-04 18:38:15 -08:00
Christian Byrne
7c6e2d2c7a fix: disable CodeRabbit high_level_summary to stop PR description auto-updates (#8615)
## Summary

Disables the `high_level_summary` feature in CodeRabbit's configuration.
This feature automatically modifies the PR description every time a
commit is pushed, which was causing frustration for developers.

## Changes

- Set `high_level_summary: false` in `.coderabbit.yaml`

## Context

Discussed in #frontend-code-reviews Slack channel. The team agreed that
having PR descriptions automatically modified with every commit push is
not desirable behavior.

---

Fixes
https://www.notion.so/comfy-org/Ops-Disable-high_level_summary-in-ComfyUI_frontend-coderabbit-yaml-2fd6d73d3650812b9eb8d6680fa11932

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8615-fix-disable-CodeRabbit-high_level_summary-to-stop-PR-description-auto-updates-2fe6d73d36508112ac16f5df51845fcd)
by [Unito](https://www.unito.io)
2026-02-04 18:37:44 -08:00
pythongosssss
6feb2022a4 Add support for search aliases on subgraphs (#8608)
## Summary

- add commands for setting search aliases and description when in
subgraph
- in future we can add these fields to the dialog when publishing a
subgraph
- map workflow extra metadata on save/load from from/to subgraph node to
allow access via `canvas.subgraph.extra`

## Changes

**What**: 
- new core commands for Comfy.Subgraph.SetSearchAliases &
Comfy.Subgraph.SetDescription to be called when in a subgraph context
- update Publish command to allow command metadata arg for name
- update test executeCommand to allow passing metadata arg

## Review Focus

- When saving a subgraph, the outer workflow "wrapper" is created at the
point of publishing. So unlike a normal workflow `extra` property that
is available at any point, for a subgraph this is not accessible.
To workaround this, the `extra` property that exists on the inner
subgraph node is copied to the top level on save, and restored on load
so extra properties can be set via `canvas.subgraph.extra`.
- I have kept the existing naming format matching `BlueprintDescription`
for `BlueprintSearchAliases` but i'm not sure if the description was
ever used before

## Screenshots


https://github.com/user-attachments/assets/4d4df9c1-2281-4589-aa56-ab07cdecd353

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8608-Add-support-for-search-aliases-on-subgraphs-2fd6d73d365081d083caebd6befcacdd)
by [Unito](https://www.unito.io)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Set subgraph search aliases (comma-separated) and descriptions;
aliases enable discovery by alternative names.
  * Publish subgraphs using a provided name.
* Node definitions now support search aliases so nodes can be found by
alternate names.
  * UI strings added for entering descriptions and search aliases.

* **Tests**
* Added end-to-end and unit tests covering aliases, descriptions, search
behavior, publishing, and persistence.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-04 12:52:30 -08:00
Alexander Brown
b8287f6c2f Update manifest.json to remove color properties (#8612)
Removed background and theme colors from manifest.



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Removed custom color theme settings from the app's configuration. The
app's display mode and all other properties remain unchanged.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-04 12:25:28 -08:00
Luke Mino-Altherr
036675bb49 fix: Safari compatibility issues in Secrets panel dialog (#8610)
## Summary
Fixes multiple Safari-specific issues in the Secrets panel dialog:

1. **Dropdown not opening** - Safari has issues with click events on
portaled content inside dialogs. Added `disablePortal` prop to
`SelectContent` to render content inline.
2. **Close button focused on open** - Added `autofocus` to
`SelectTrigger` so focus goes to the first form field.
3. **Buttons not focusable** - Safari doesn't focus buttons on click by
default. Added `tabindex="0"` to Cancel/Save buttons.

## Changes
- `SelectContent.vue`: Added `disablePortal` prop with explanatory
comment linking to upstream issue
- `SecretFormDialog.vue`: Applied Safari workarounds

## References
- https://github.com/chakra-ui/ark/issues/1782 (Portal issue in Safari)
- https://mayank.co/blog/safari-focus/ (Button focus behavior)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added option to disable portal rendering in select components for
greater control over component behavior.

* **Bug Fixes**
* Improved keyboard accessibility in form dialogs with enhanced focus
management, auto-focus on select triggers, and optimized tab navigation
for action buttons.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Amp <amp@ampcode.com>
2026-02-04 19:58:40 +00:00
Terry Jia
507500a9d7 fix: use Intl.NumberFormat instead of vue-i18n n() for number formatting (#8600)
## Summary

vue-i18n's n() function does not support passing
Intl.NumberFormatOptions directly as the second argument. When widgets
without precision defined (e.g., KJNodes' CreateShapeImageOnPath) were
rendered, the n() function threw a SyntaxError in parseNumberArgs.

Replace n() with native Intl.NumberFormat which properly supports
NumberFormatOptions while still using the i18n locale for localization.

## Screenshots (if applicable)
before

https://github.com/user-attachments/assets/eb48379b-fe45-4f1c-a674-b92130f0fcac

after

https://github.com/user-attachments/assets/1abcd2da-ad8b-4432-831a-2a7e91f375a5



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Enhanced number input widgets with improved locale-aware number
formatting, ensuring proper decimal and group separator display based on
user locale settings.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8600-fix-use-Intl-NumberFormat-instead-of-vue-i18n-n-for-number-formatting-2fd6d73d365081b78509e87d516c6067)
by [Unito](https://www.unito.io)
2026-02-03 20:57:49 -05:00
Comfy Org PR Bot
6499eda004 1.39.6 (#8595)
Patch version increment to 1.39.6

**Base branch:** `main`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
  * Version bumped to 1.39.6.

* **New Features**
* Added multilingual UI strings for secrets management, asset import
errors, app-mode prompts, and HitPaw tools.
* New node types for image/video/audio workflows and expanded export
formats (GLB/FBX/OBJ) for 3D/model outputs.

* **Bug Fixes / Removals**
* Removed the "Open 3D Viewer (Beta) for Selected Node" menu entry and
related 3D viewer settings.
* Added setting to enable automatic node replacement when mappings
exist.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8595-1-39-6-2fd6d73d3650818cba9cffca313909e8)
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-02-03 17:27:52 -08:00
Christian Byrne
a7e4a86ec8 fix: correct #visibleReroutes to _visibleReroutes typo (#8597)
Fixes build failure on main caused by merge conflict typo.

- `#visibleReroutes` → `_visibleReroutes` on line 5644 of
LGraphCanvas.ts

Fixes
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/21653103679/job/62422017295

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
  * Internal code style adjustment with no user-facing impact.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8597-fix-correct-visibleReroutes-to-_visibleReroutes-typo-2fd6d73d365081ce9202d070f80fc5c3)
by [Unito](https://www.unito.io)
2026-02-03 16:54:00 -08:00
Christian Byrne
ffc0bf00ef fix: make SubgraphNode.graph nullable to allow proper cleanup (#8180)
## Summary

Fix `SubgraphNode.graph` property to match `LGraphNode` lifecycle
contract. Previously declared as `override readonly graph` via
constructor parameter promotion, which prevented `LGraph.remove()` from
setting `node.graph = null`.

## Changes

- Remove `readonly` from `SubgraphNode.graph` constructor parameter
- Add `override graph: GraphOrSubgraph | null` as class property  
- Add `NullGraphError` guard in `rootGraph` getter with node ID for
debugging
- Add null guards in `ExecutableNodeDTO.resolveInput` and
`imagePreviewStore.revokeSubgraphPreviews`
- Add test verifying `rootGraph` throws after node removal

## Testing

- Existing subgraph tests pass
- New test confirms `NullGraphError` is thrown when accessing
`rootGraph` on removed node

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-02-03 16:18:48 -08:00
Christian Byrne
66e776774a feat: add default keybindings for toggle mode and assets panel (#8593)
## Summary
Add default keyboard shortcuts for mode toggle and assets panel access.

## Changes
- **Ctrl+Shift+A**: Toggle between Simple Mode and Graph Mode
(`Comfy.ToggleLinear`)
- **A**: Open/toggle assets panel (`Workspace.ToggleSidebarTab.assets`)
- Show keybinding in ModeToggle button tooltip (e.g. "Simple Mode
(Ctrl+Shift+A)")

## Keybinding Rationale
- `A` follows the existing pattern: `w` (workflows), `n` (node-lib), `m`
(model-lib)
- `Ctrl+Shift+A` chosen because:
  - Ctrl+Shift is the standard modifier pattern for toggle commands
  - "A" mnemonic for "App mode"
  - Does not conflict with existing keybindings

## Testing
- Verified typecheck passes
- Verified lint passes

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Tooltips now show dynamic keyboard shortcut suffixes for mode and tab
controls.
  * Added keyboard shortcut: A — toggles the assets sidebar.
  * Added keyboard shortcut: Ctrl+Shift+A — toggles linear mode.

* **Localization**
* Added a localized shortcut suffix template so displayed shortcuts
respect translations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8593-feat-add-default-keybindings-for-toggle-mode-and-assets-panel-2fc6d73d36508172bd6ed3378f43de55)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-02-03 16:06:33 -08:00
Christian Byrne
f98232c272 fix: suppress link rendering during slot sync after graph reconfigure (#8367)
## Description

Fixes link alignment issues after undo/redo operations in Vue Nodes 2.0.
When multiple connections exist from the same output to different nodes,
performing an undo would cause the connections to become misaligned with
their inputs.

## Root Cause

When undo triggers `loadGraphData`, the graph is reconfigured and Vue
node components are destroyed and recreated. The new slot elements mount
and schedule RAF-batched position syncs via `scheduleSlotLayoutSync`.
However, links are drawn **before** the RAF batch completes, causing
`getSlotPosition()` to return stale/missing positions.

## Solution

- Export a new `flushPendingSlotLayoutSyncs()` function from
`useSlotElementTracking.ts`
- Create a `useGraphConfigureSlotSync` composable that flushes pending
syncs after graph configuration
- Integrate the flush into `addAfterConfigureHandler` in `app.ts`,
called after `onAfterGraphConfigured`
- Force canvas redraw after flushing to render links with correct
positions

## Testing

- Added unit tests for `flushPendingSlotLayoutSyncs`
- Added unit tests for `useGraphConfigureSlotSync` composable
- Manual verification: connections now align correctly after undo/redo
operations

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8367-fix-flush-pending-slot-layout-syncs-after-graph-configure-2f66d73d365081a3a564fac96c44a048)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-02-03 15:56:31 -08:00
Christian Byrne
278d491030 refactor: move ellipsis and punctuation into i18n translation strings (#8573)
## Summary

Move ellipsis and punctuation characters into i18n translation strings
for proper internationalization support.

## Changes

- Add 12 new translation keys with punctuation included:
- Placeholder keys with trailing ellipsis (e.g.,
`searchNodesPlaceholder: "Search Nodes..."`)
  - `downloadWithSize` with interpolation: `"Download ({size})"`
  - `completedWithCheckmark`: `"Completed ✓"`
- Prompt keys with colons (e.g., `enterNewNamePrompt: "Enter new
name:"`)
- Update 20 files to use new translation keys instead of string
concatenation

## Review Focus

This eliminates string concatenation patterns like `$t('key') + '...'`
that break proper internationalization, since different languages may
use different punctuation or may not need ellipsis/colons in the same
contexts.

Fixes #7333


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Standardized localization across the app: unified search placeholders
and input hints; updated dialog prompt texts for renaming,
saving/exporting, and related prompts.
* **New Features**
  * Download buttons now show file size via localized text.
  * Completed status displays a localized label with a checkmark.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8573-refactor-move-ellipsis-and-punctuation-into-i18n-translation-strings-2fc6d73d365081828ad3f257bcac7799)
by [Unito](https://www.unito.io)
2026-02-03 15:50:18 -08:00
Christian Byrne
4b43eb5fff refactor: use isActiveJobState instead of duplicated cancellableStates (#8572)
## Summary

Eliminates code duplication by replacing local `cancellableStates`
arrays with the centralized `isActiveJobState` utility function.

## Changes

- Remove duplicated `cancellableStates: JobState[] = ['pending',
'initialization', 'running']` from:
  - `src/composables/queue/useJobActions.ts`
  - `src/storybook/mocks/useJobActions.ts`
- Import and use `isActiveJobState` from `src/utils/queueUtil.ts`
instead

## Testing

- Typecheck passes
- Lint passes
- Related queue tests pass

Fixes #7947

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8572-refactor-use-isActiveJobState-instead-of-duplicated-cancellableStates-2fc6d73d365081f89decfc869fa952a0)
by [Unito](https://www.unito.io)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
  * Internal code improvements to simplify job state management logic.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-03 15:49:11 -08:00
Christian Byrne
67107d6f56 fix: use consistent chevron icon in Vue node dropdown widgets (#8576)
## Summary

Fixes styling inconsistency where Vue node select widget chevrons look
different between dropdown implementations.

## Changes

- **What**: Added `#dropdownicon` slot to `WidgetSelectDefault.vue`
using Lucide chevron icon with
`text-component-node-foreground-secondary` styling, matching
`FormDropdownInput.vue`

## Review Focus

Both dropdown implementations now use identical chevron icons with
consistent sizing (`size-4`) and color tokens.

Fixes #COM-11645


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Style**
* Updated select/dropdown appearance: replaced the previous icon
rendering with a customizable dropdown icon slot, improving visual
consistency and allowing a custom icon to display in select controls
across the UI. This change affects only presentation—no behavior or data
handling was altered.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8576-fix-use-consistent-chevron-icon-in-Vue-node-dropdown-widgets-2fc6d73d3650814e923bcbee83fae498)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-02-03 15:48:25 -08:00
Christian Byrne
32da91ea32 perf: code-split xterm bundle and gate terminal features for cloud (#8528)
## Summary

Code-splits the ~400KB xterm bundle and excludes terminal features from
cloud distribution where they are not needed.

### Changes

- **bottomPanelStore.ts**: Gate terminal tab registration behind
`__DISTRIBUTION__ !== 'cloud'` check with dynamic import, enabling
tree-shaking
- **keybindingService.ts**: Skip logs-terminal keybinding registration
for cloud distribution
- **vite.config.mts**: Add `vendor-xterm` code splitting group

### Bundle Impact

| Distribution | xterm in bundle | Terminal tabs |
|--------------|-----------------|---------------|
| cloud |  No (~400KB saved) | None |
| localhost |  Yes (vendor-xterm chunk) | Logs terminal |
| desktop |  Yes (vendor-xterm chunk) | Logs + Command terminal |

### Verification Script

Run locally to verify xterm exclusion works correctly:

```bash
#!/bin/bash
set -e

echo "=== Verifying xterm bundle exclusion ==="
echo

# Clear Nx cache to ensure fresh builds
pnpm nx reset 2>/dev/null

# Build cloud distribution
echo "Building CLOUD distribution..."
rm -rf dist
DISTRIBUTION=cloud pnpm build --mode production 2>/dev/null

echo "Cloud build - checking for xterm:"
if grep -r "xterm" dist/assets/*.js >/dev/null 2>&1; then
  echo "   FAIL: xterm found in cloud bundle"
  grep -l "xterm" dist/assets/*.js | head -5
else
  echo "   PASS: xterm NOT in cloud bundle"
fi
echo

# Build localhost distribution
echo "Building LOCALHOST distribution..."
pnpm nx reset 2>/dev/null
rm -rf dist
DISTRIBUTION=localhost pnpm build --mode production 2>/dev/null

echo "Localhost build - checking for xterm:"
if grep -r "xterm" dist/assets/*.js >/dev/null 2>&1; then
  echo "   PASS: xterm found in localhost bundle"
  echo "  Files containing xterm:"
  grep -l "xterm" dist/assets/*.js | while read f; do
    size=$(wc -c < "$f")
    echo "    $(basename $f) ($(numfmt --to=iec $size))"
  done
else
  echo "   FAIL: xterm NOT in localhost bundle"
fi
echo

echo "=== Verification complete ==="
```

**Note**: Nx cache must be reset between builds with different
`DISTRIBUTION` values or it may return stale results.

## Test Plan

- [x] Quality gates pass (typecheck, lint, format, tests)
- [x] Cloud build verified: xterm NOT present
- [x] Localhost build verified: xterm present as vendor-xterm chunk

Fixes COM-14129

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8528-perf-code-split-xterm-bundle-and-gate-terminal-features-for-cloud-2fa6d73d365081a093ecc74ca7ce6e7c)
by [Unito](https://www.unito.io)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Terminal tab declarations adjusted (no user-visible behavior change).
* Bottom panel tabs now load asynchronously; panel toggle falls back to
shortcuts when terminal tabs aren’t loaded.

* **Chores**
* Terminal tabs and related keybindings suppressed in cloud deployments.
  * Core commands wired to a new utility to support widget promotion.

* **Tests**
* E2E tests updated to handle async terminal-tab loading and minor test
cleanup.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-02-03 15:15:37 -08:00
Christian Byrne
cfc24e3567 docs: audit and update litegraph documentation (#8588)
## Summary

Audits and updates litegraph documentation files that came from the old
litegraph.js repo merged via git subtree (per #8341 discussion).

## Changes

- **Delete** `CONTRIBUTING.md` - completely outdated, referenced
original jagenjo/litegraph.js from 2020
- **Fix** `API.md` - remove Subgraph from "Removed public interfaces"
(actively used in 25+ files)
- **Update** `README.md` - replace standalone releasing instructions
with note about embedded subtree workflow
- **Fix** `AGENTS.md` and `__fixtures__/README.md` - correct import path
typos (`./fixtures/` → `./__fixtures__/`)

Fixes #8347

cc @DrJKL

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Documentation

* Updated release information to clarify that the library is embedded
via git subtree and managed through the parent repository's release
process
* Removed contribution guidelines documentation file
* Updated API documentation by removing Subgraph from the list of
removed public interfaces
* Updated documentation examples to align with current project structure

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8588-docs-audit-and-update-litegraph-documentation-2fc6d73d365081fca335c78ce8493d3e)
by [Unito](https://www.unito.io)
2026-02-03 15:07:43 -08:00
Christian Byrne
eb1d08e9fe feat: enable CodeRabbit reviews on draft PRs (#8587)
Enable CodeRabbit to automatically review draft pull requests.

## Changes
- Added `reviews.auto_review.drafts: true` to `.coderabbit.yaml`

This allows getting early feedback from CodeRabbit while PRs are still
in draft status.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Enabled automatic review drafting to streamline the review process.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8587-feat-enable-CodeRabbit-reviews-on-draft-PRs-2fc6d73d365081ea8f1bcae4a2a8277b)
by [Unito](https://www.unito.io)
2026-02-03 13:41:29 -08:00
Christian Byrne
a350b59361 feat: Add educational hint to favorites empty state (#8559)
## Summary

Adds an educational hint to the favorites empty state to teach users how
to favorite inputs.

## Changes

- Add new i18n key `favoritesNoneHint` with text "In the Parameters tab,
click ⋮ on any input to add it here"
- Update empty state template to show hint below the existing
description
- Hint is hidden during search to avoid redundant messaging

## Before/After

**Before:** "Inputs you favorite will show up here"

**After:**
- "Inputs you favorite will show up here"
- "In the Parameters tab, click ⋮ on any input to add it here*

## Context

Per Slack discussion, uses in-app education only (no external docs link)
to ensure text matches the user's specific version of the feature.

Fixes #2fa6d73d

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8559-feat-Add-educational-hint-to-favorites-empty-state-2fc6d73d365081f5827dfafcc1ece95c)
by [Unito](https://www.unito.io)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Improved empty state in the Parameters panel: message is now two
paragraphs and shows an additional hint (with an icon) when not
searching.
* **Localization**
* Added a new localized hint that explains how to add inputs to
favorites, shown in the Parameters panel empty state.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe>
2026-02-03 13:24:52 -08:00
Christian Byrne
1031af0ad4 feat: update user-facing 'Simple Mode' terminology to 'App Mode' (#8590)
## Summary
Updates all user-facing instances of 'Simple Mode' / 'Linear Mode' to
'App Mode' as discussed in PR #8562.

## Changes
- Updated command label in `useCoreCommands.ts`
- Updated `src/locales/en/commands.json`
- Updated all 12 locale `main.json` files with:
  - `linearMode.linearMode` → "App Mode" (translated)
  - `linearMode.beta` → "App Mode in Beta - Feedback" (translated)
  - `Toggle Simple Mode` command → `Toggle App Mode` (translated)

## Languages Updated
en, zh, zh-TW, ja, ko, fr, es, pt-BR, ru, tr, ar, fa

## Notes
Internal variable names (`linearMode`, `canvasStore.linearMode`) remain
unchanged to avoid breaking changes.

Fixes #8577

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Style**
* Renamed application terminology from "Simple Mode" to "App Mode" in
user interface labels, mode descriptions, and mode toggle command.
Updates applied consistently across all 12 supported language
translations to ensure consistent terminology throughout the
application.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8590-feat-update-user-facing-Simple-Mode-terminology-to-App-Mode-2fc6d73d36508160a19af70b228b0a7e)
by [Unito](https://www.unito.io)
2026-02-03 13:06:25 -08:00
Christian Byrne
4d8254740d refactor: replace type catch-all with generic ComponentAttrs in showLayoutDialog (#8571)
## Summary

Replaces `Record<string, unknown>` catch-all with properly typed generic
props in `showLayoutDialog`, improving type safety.

## Changes

- Made `showLayoutDialog` generic over component type `C extends
Component`
- Changed props type from `{ onClose: () => void } & Record<string,
unknown>` to `ComponentAttrs<C> & { onClose: () => void }`

This matches existing patterns used elsewhere in `dialogService.ts`
(e.g., `showLoadWorkflowWarning`, `showMissingModelsWarning`).

Fixes #8102

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Dialog handling updated to infer props per component, resulting in
more consistent and reliable dialog behavior and fewer runtime prop
mismatches when opening dialogs. Developers will have clearer, safer
dialog configuration, while end users should experience steadier dialog
interactions. No visible UI changes are expected.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8571-fix-replace-type-catch-all-with-generic-ComponentAttrs-in-showLayoutDialog-2fc6d73d36508104bf60d27c9b94af69)
by [Unito](https://www.unito.io)
2026-02-03 13:05:13 -08:00
205 changed files with 7556 additions and 2683 deletions

View File

@@ -1,3 +1,7 @@
issue_enrichment:
auto_enrich:
enabled: true
reviews:
high_level_summary: false
auto_review:
drafts: true

View File

@@ -109,7 +109,7 @@ jobs:
# Run sharded tests with snapshot updates (browsers pre-installed in container)
- name: Update snapshots (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
id: playwright-tests
run: pnpm exec playwright test --update-snapshots --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
run: pnpm exec playwright test --update-snapshots --grep @screenshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
continue-on-error: true
- name: Stage changed snapshot files

View File

@@ -1,6 +1,8 @@
// eslint-disable-next-line storybook/no-renderer-packages
import type { Meta, StoryObj } from '@storybook/vue3'
import type { ElectronAPI } from '@comfyorg/comfyui-electron-types'
import { nextTick, provide } from 'vue'
import type { ElectronWindow } from '@/utils/envUtil'
import { createMemoryHistory, createRouter } from 'vue-router'
import InstallView from './InstallView.vue'
@@ -42,16 +44,21 @@ const meta: Meta<typeof InstallView> = {
const router = createMockRouter()
// Mock electron API
;(window as any).electronAPI = {
;(window as ElectronWindow).electronAPI = {
getPlatform: () => 'darwin',
Config: {
getDetectedGpu: () => Promise.resolve('mps')
},
Events: {
trackEvent: (_eventName: string, _data?: any) => {}
trackEvent: (
_eventName: string,
_data?: Record<string, unknown>
) => {}
},
installComfyUI: (_options: any) => {},
changeTheme: (_theme: any) => {},
installComfyUI: (
_options: Parameters<ElectronAPI['installComfyUI']>[0]
) => {},
changeTheme: (_theme: Parameters<ElectronAPI['changeTheme']>[0]) => {},
getSystemPaths: () =>
Promise.resolve({
defaultInstallPath: '/Users/username/ComfyUI'
@@ -240,8 +247,8 @@ export const DesktopSettings: Story = {
export const WindowsPlatform: Story = {
render: () => {
// Override the platform to Windows
;(window as any).electronAPI.getPlatform = () => 'win32'
;(window as any).electronAPI.Config.getDetectedGpu = () =>
;(window as ElectronWindow).electronAPI.getPlatform = () => 'win32'
;(window as ElectronWindow).electronAPI.Config.getDetectedGpu = () =>
Promise.resolve('nvidia')
return {
@@ -259,8 +266,8 @@ export const MacOSPlatform: Story = {
name: 'macOS Platform',
render: () => {
// Override the platform to macOS
;(window as any).electronAPI.getPlatform = () => 'darwin'
;(window as any).electronAPI.Config.getDetectedGpu = () =>
;(window as ElectronWindow).electronAPI.getPlatform = () => 'darwin'
;(window as ElectronWindow).electronAPI.Config.getDetectedGpu = () =>
Promise.resolve('mps')
return {
@@ -327,7 +334,7 @@ export const ManualInstall: Story = {
export const ErrorState: Story = {
render: () => {
// Override validation to return an error
;(window as any).electronAPI.validateInstallPath = () =>
;(window as ElectronWindow).electronAPI.validateInstallPath = () =>
Promise.resolve({
isValid: false,
exists: false,
@@ -375,7 +382,7 @@ export const ErrorState: Story = {
export const WarningState: Story = {
render: () => {
// Override validation to return a warning about non-default drive
;(window as any).electronAPI.validateInstallPath = () =>
;(window as ElectronWindow).electronAPI.validateInstallPath = () =>
Promise.resolve({
isValid: true,
exists: false,

View File

@@ -46,7 +46,7 @@ class ComfyPropertiesPanel {
constructor(readonly page: Page) {
this.root = page.getByTestId(TestIds.propertiesPanel.root)
this.panelTitle = this.root.locator('h3')
this.searchBox = this.root.getByPlaceholder('Search...')
this.searchBox = this.root.getByPlaceholder(/^Search/)
}
}

View File

@@ -80,4 +80,11 @@ export class ComfyNodeSearchBox {
async removeFilter(index: number) {
await this.filterChips.nth(index).locator('.p-chip-remove-icon').click()
}
/**
* Returns a locator for a search result containing the specified text.
*/
findResult(text: string): Locator {
return this.dropdown.locator('li').filter({ hasText: text })
}
}

View File

@@ -5,10 +5,18 @@ import type { KeyCombo } from '../../../src/platform/keybindings/types'
export class CommandHelper {
constructor(private readonly page: Page) {}
async executeCommand(commandId: string): Promise<void> {
await this.page.evaluate((id: string) => {
return window.app!.extensionManager.command.execute(id)
}, commandId)
async executeCommand(
commandId: string,
metadata?: Record<string, unknown>
): Promise<void> {
await this.page.evaluate(
({ commandId, metadata }) => {
return window['app'].extensionManager.command.execute(commandId, {
metadata
})
},
{ commandId, metadata }
)
}
async registerCommand(

View File

@@ -1,5 +1,9 @@
import { test as base } from '@playwright/test'
interface TestWindow extends Window {
__ws__?: Record<string, WebSocket>
}
export const webSocketFixture = base.extend<{
ws: { trigger(data: unknown, url?: string): Promise<void> }
}>({

View File

@@ -113,21 +113,45 @@ test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
expect(hasModifiers).toBeTruthy()
})
test('should maintain panel state when switching to terminal', async ({
test('should maintain panel state when switching between panels', async ({
comfyPage
}) => {
const { bottomPanel } = comfyPage
// Open shortcuts panel first
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.root).toBeVisible()
await bottomPanel.toggleButton.click()
await expect(bottomPanel.root).toBeVisible()
await bottomPanel.keyboardShortcutsButton.click()
await expect(
comfyPage.page.locator('[id*="tab_shortcuts-essentials"]')
).toBeVisible()
// Try to open terminal panel - may show terminal OR close shortcuts
// depending on whether terminal tabs have loaded (async loading)
await bottomPanel.toggleButton.click()
// Check if terminal tabs loaded (Logs tab visible) or fell back to shortcuts toggle
const logsTab = comfyPage.page.getByRole('tab', { name: /Logs/i })
const hasTerminalTabs = await logsTab.isVisible().catch(() => false)
if (hasTerminalTabs) {
// Terminal panel is visible - verify we can switch back to shortcuts
await expect(bottomPanel.root).toBeVisible()
// Switch back to shortcuts
await bottomPanel.keyboardShortcutsButton.click()
// Should show shortcuts content again
await expect(
comfyPage.page.locator('[id*="tab_shortcuts-essentials"]')
).toBeVisible()
} else {
// Terminal tabs not loaded - button toggled shortcuts off, reopen for verification
await bottomPanel.keyboardShortcutsButton.click()
await expect(bottomPanel.root).toBeVisible()
await expect(
comfyPage.page.locator('[id*="tab_shortcuts-essentials"]')
).toBeVisible()
}
})
test('should handle keyboard navigation', async ({ comfyPage }) => {

View File

@@ -107,7 +107,6 @@ test.describe('Menu', { tag: '@ui' }, () => {
// Checkmark should be invisible initially (panel is hidden)
await expect(checkmark).toHaveClass(/invisible/)
// Click Bottom Panel to toggle it on
await bottomPanelItem.click()
// Verify menu is still visible after clicking

View File

@@ -0,0 +1,109 @@
import { expect } from '@playwright/test'
import type { ComfyPage } from '../fixtures/ComfyPage'
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
async function createSubgraphAndNavigateInto(comfyPage: ComfyPage) {
await comfyPage.workflow.loadWorkflow('default')
await comfyPage.nextFrame()
const ksampler = await comfyPage.nodeOps.getNodeRefById('3')
await ksampler.click('title')
await ksampler.convertToSubgraph()
await comfyPage.nextFrame()
const subgraphNodes =
await comfyPage.nodeOps.getNodeRefsByTitle('New Subgraph')
expect(subgraphNodes.length).toBe(1)
const subgraphNode = subgraphNodes[0]
await subgraphNode.navigateIntoSubgraph()
return subgraphNode
}
async function exitSubgraphAndPublish(
comfyPage: ComfyPage,
subgraphNode: Awaited<ReturnType<typeof createSubgraphAndNavigateInto>>,
blueprintName: string
) {
await comfyPage.page.keyboard.press('Escape')
await comfyPage.nextFrame()
await subgraphNode.click('title')
await comfyPage.command.executeCommand('Comfy.PublishSubgraph', {
name: blueprintName
})
await expect(comfyPage.visibleToasts).toHaveCount(1, { timeout: 5000 })
await comfyPage.toast.closeToasts(1)
}
async function searchAndExpectResult(
comfyPage: ComfyPage,
searchTerm: string,
expectedResult: string
) {
await comfyPage.command.executeCommand('Workspace.SearchBox.Toggle')
await expect(comfyPage.searchBox.input).toHaveCount(1)
await comfyPage.searchBox.input.fill(searchTerm)
await expect(comfyPage.searchBox.findResult(expectedResult)).toBeVisible({
timeout: 10000
})
}
test.describe('Subgraph Search Aliases', { tag: ['@subgraph'] }, () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default')
})
test('Can set search aliases on subgraph and find via search', async ({
comfyPage
}) => {
const subgraphNode = await createSubgraphAndNavigateInto(comfyPage)
await comfyPage.command.executeCommand('Comfy.Subgraph.SetSearchAliases', {
aliases: 'qwerty,unicorn'
})
const blueprintName = `test-aliases-${Date.now()}`
await exitSubgraphAndPublish(comfyPage, subgraphNode, blueprintName)
await searchAndExpectResult(comfyPage, 'unicorn', blueprintName)
})
test('Can set description on subgraph', async ({ comfyPage }) => {
await createSubgraphAndNavigateInto(comfyPage)
await comfyPage.command.executeCommand('Comfy.Subgraph.SetDescription', {
description: 'This is a test description'
})
// Verify the description was set on the subgraph's extra
const description = await comfyPage.page.evaluate(() => {
const subgraph = window['app']!.canvas.subgraph
return (subgraph?.extra as Record<string, unknown>)?.BlueprintDescription
})
expect(description).toBe('This is a test description')
})
test('Search aliases persist after publish and reload', async ({
comfyPage
}) => {
const subgraphNode = await createSubgraphAndNavigateInto(comfyPage)
await comfyPage.command.executeCommand('Comfy.Subgraph.SetSearchAliases', {
aliases: 'dragon, fire breather'
})
const blueprintName = `test-persist-${Date.now()}`
await exitSubgraphAndPublish(comfyPage, subgraphNode, blueprintName)
// Reload the page to ensure aliases are persisted
await comfyPage.page.reload()
await comfyPage.page.waitForFunction(
() => window['app'] && window['app'].extensionManager
)
await comfyPage.nextFrame()
await searchAndExpectResult(comfyPage, 'dragon', blueprintName)
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -10,7 +10,5 @@
"type": "image/svg+xml"
}
],
"display": "standalone",
"background_color": "#172dd7",
"theme_color": "#f0ff41"
"display": "standalone"
}

View File

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

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16"><path d="M6.66667 14V5.33333C6.66667 5.15652 6.59643 4.98695 6.4714 4.86193C6.34638 4.7369 6.17681 4.66667 6 4.66667H2.66667C2.48986 4.66667 2.32029 4.7369 2.19526 4.86193C2.07024 4.98695 2 5.15652 2 5.33333V13.3333C2 13.5101 2.07024 13.6797 2.19526 13.8047C2.32029 13.9298 2.48986 14 2.66667 14H10.6667C10.8435 14 11.013 13.9298 11.1381 13.8047C11.2631 13.6797 11.3333 13.5101 11.3333 13.3333V10C11.3333 9.82319 11.2631 9.65362 11.1381 9.5286C11.013 9.40357 10.8435 9.33333 10.6667 9.33333H2M10 2H13.3333C13.7015 2 14 2.29848 14 2.66667V6C14 6.36819 13.7015 6.66667 13.3333 6.66667H10C9.63181 6.66667 9.33333 6.36819 9.33333 6V2.66667C9.33333 2.29848 9.63181 2 10 2Z" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 837 B

View File

@@ -2,10 +2,7 @@
import { execSync } from 'child_process'
import * as fs from 'fs'
import { globSync } from 'glob'
interface LocaleData {
[key: string]: any
}
import type { LocaleData } from './i18n-types'
// Configuration
const SOURCE_PATTERNS = ['src/**/*.{js,ts,vue}', '!src/locales/**/*']
@@ -45,7 +42,7 @@ function getStagedLocaleFiles(): string[] {
}
// Extract all keys from a nested object
function extractKeys(obj: any, prefix = ''): string[] {
function extractKeys(obj: LocaleData, prefix = ''): string[] {
const keys: string[] = []
for (const [key, value] of Object.entries(obj)) {
@@ -166,17 +163,17 @@ async function checkNewUnusedKeys() {
// Report results
if (unusedNewKeys.length > 0) {
console.log('\n⚠ Warning: Found unused NEW i18n keys:\n')
console.warn('\n⚠ Warning: Found unused NEW i18n keys:\n')
for (const key of unusedNewKeys.sort()) {
console.log(` - ${key}`)
console.warn(` - ${key}`)
}
console.log(`\n✨ Total unused new keys: ${unusedNewKeys.length}`)
console.log(
console.warn(`\n✨ Total unused new keys: ${unusedNewKeys.length}`)
console.warn(
'\nThese keys were added but are not used anywhere in the codebase.'
)
console.log('Consider using them or removing them in a future update.')
console.warn('Consider using them or removing them in a future update.')
// Changed from process.exit(1) to process.exit(0) for warning only
process.exit(0)

View File

@@ -7,6 +7,7 @@ import {
writeFileSync
} from 'fs'
import { dirname, join } from 'path'
import type { LocaleData } from './i18n-types'
// Ensure directories exist
function ensureDir(dir: string) {
@@ -41,8 +42,8 @@ function getAllJsonFiles(dir: string): string[] {
}
// Find additions in new object compared to base
function findAdditions(base: any, updated: any): Record<string, any> {
const additions: Record<string, any> = {}
function findAdditions(base: LocaleData, updated: LocaleData): LocaleData {
const additions: LocaleData = {}
for (const key in updated) {
if (!(key in base)) {
@@ -74,7 +75,7 @@ function capture(srcLocaleDir: string, tempBaseDir: string) {
ensureDir(dirname(targetPath))
writeFileSync(targetPath, readFileSync(file, 'utf8'))
}
console.log('Captured current locale files to temp/base/')
console.warn('Captured current locale files to temp/base/')
}
// Diff command
@@ -94,7 +95,7 @@ function diff(srcLocaleDir: string, tempBaseDir: string, tempDiffDir: string) {
if (Object.keys(additions).length > 0) {
ensureDir(dirname(diffPath))
writeFileSync(diffPath, JSON.stringify(additions, null, 2))
console.log(`Wrote diff to ${diffPath}`)
console.warn(`Wrote diff to ${diffPath}`)
}
}
}
@@ -116,9 +117,9 @@ switch (command) {
// Remove temp directory recursively
if (existsSync('temp')) {
rmSync('temp', { recursive: true, force: true })
console.log('Removed temp directory')
console.warn('Removed temp directory')
}
break
default:
console.log('Please specify either "capture" or "diff" command')
console.error('Please specify either "capture" or "diff" command')
}

5
scripts/i18n-types.ts Normal file
View File

@@ -0,0 +1,5 @@
/**
* Shared types for i18n-related scripts
*/
export type LocaleData = { [key: string]: string | LocaleData }

View File

@@ -19,12 +19,14 @@
<Button
v-tooltip.bottom="customNodesManagerTooltipConfig"
variant="secondary"
size="icon"
:aria-label="t('menu.customNodesManager')"
:aria-label="t('menu.manageExtensions')"
class="relative"
@click="openCustomNodeManager"
>
<i class="icon-[lucide--puzzle] size-4" />
<i class="icon-[comfy--extensions-blocks] size-4" />
<span class="not-md:hidden">
{{ t('menu.manageExtensions') }}
</span>
<span
v-if="shouldShowRedDot"
class="absolute top-0.5 right-1 size-2 rounded-full bg-red-500"
@@ -220,7 +222,7 @@ const queueHistoryTooltipConfig = computed(() =>
buildTooltipConfig(t('sideToolbar.queueProgressOverlay.viewJobHistory'))
)
const customNodesManagerTooltipConfig = computed(() =>
buildTooltipConfig(t('menu.customNodesManager'))
buildTooltipConfig(t('menu.manageExtensions'))
)
const queueContextMenu = ref<InstanceType<typeof ContextMenu> | null>(null)
const queueContextMenuItems = computed<MenuItem[]>(() => [

View File

@@ -22,7 +22,7 @@
@click="triggerDownload"
>
<i class="pi pi-download" />
{{ $t('g.download') + ' (' + fileSize + ')' }}
{{ $t('g.downloadWithSize', { size: fileSize }) }}
</Button>
<Button
v-if="(status === null || status === 'error') && !!props.url"

View File

@@ -27,7 +27,7 @@
:title="props.url"
@click="download.triggerBrowserDownload"
>
{{ $t('g.download') + ' (' + fileSize + ')' }}
{{ $t('g.downloadWithSize', { size: fileSize }) }}
</Button>
</div>
<div>

View File

@@ -38,6 +38,7 @@
<script setup lang="ts">
import Skeleton from 'primevue/skeleton'
import { computed, onUnmounted, ref, watch } from 'vue'
import type { StyleValue } from 'vue'
import { useIntersectionObserver } from '@/composables/useIntersectionObserver'
import { useMediaCache } from '@/services/mediaCacheService'
@@ -55,7 +56,7 @@ const {
alt?: string
containerClass?: ClassValue
imageClass?: ClassValue
imageStyle?: Record<string, any>
imageStyle?: StyleValue
rootMargin?: string
}>()

View File

@@ -3,7 +3,9 @@
<template #header>
<SearchBox
v-model="filters['global'].value"
:placeholder="$t('g.searchKeybindings') + '...'"
:placeholder="
$t('g.searchPlaceholder', { subject: $t('g.keybindings') })
"
/>
</template>

View File

@@ -43,6 +43,7 @@
</Transition>
</div>
<NodeContextMenu />
<SlotContextMenu />
</template>
<script setup lang="ts">
@@ -70,6 +71,7 @@ import { useCommandStore } from '@/stores/commandStore'
import type { ComfyCommandImpl } from '@/stores/commandStore'
import NodeContextMenu from './NodeContextMenu.vue'
import SlotContextMenu from '@/renderer/extensions/vueNodes/components/SlotContextMenu.vue'
import FrameNodes from './selectionToolbox/FrameNodes.vue'
import NodeOptionsButton from './selectionToolbox/NodeOptionsButton.vue'
import VerticalDivider from './selectionToolbox/VerticalDivider.vue'

View File

@@ -131,7 +131,7 @@ const { isDragging, dragMessage, handleDragOver, handleDragLeave, handleDrop } =
onModelDrop: async (file) => {
await viewer.handleModelDrop(file)
},
disabled: viewer.isPreview.value || isStandaloneMode
disabled: viewer.isPreview.value || !!isStandaloneMode
})
onMounted(async () => {

View File

@@ -158,7 +158,7 @@ export const Queued: Story = {
prompt_id: 'p1'
}
}
} as any
}
return { args: { ...args, jobId } }
},
@@ -217,7 +217,7 @@ export const QueuedParallel: Story = {
prompt_id: 'p2'
}
}
} as any
}
return { args: { ...args, jobId } }
},
@@ -258,7 +258,7 @@ export const Running: Story = {
prompt_id: 'p1'
}
}
} as any
}
return { args: { ...args, jobId } }
},
@@ -303,7 +303,7 @@ export const QueuedZeroAheadSingleRunning: Story = {
prompt_id: 'p1'
}
}
} as any
}
return { args: { ...args, jobId } }
},
@@ -360,7 +360,7 @@ export const QueuedZeroAheadMultiRunning: Story = {
prompt_id: 'p2'
}
}
} as any
}
return { args: { ...args, jobId } }
},

View File

@@ -131,11 +131,28 @@ onBeforeUnmount(() => {
>
<template #empty>
<div class="text-sm text-muted-foreground px-4 text-center py-10">
{{
isSearching
? t('rightSidePanel.noneSearchDesc')
: t('rightSidePanel.favoritesNoneDesc')
}}
<p>
{{
isSearching
? t('rightSidePanel.noneSearchDesc')
: t('rightSidePanel.favoritesNoneDesc')
}}
</p>
<i18n-t
v-if="!isSearching"
keypath="rightSidePanel.favoritesNoneHint"
tag="p"
class="mt-2 text-xs"
>
<template #moreIcon>
<span
aria-hidden="true"
class="inline-flex size-5 items-center justify-center rounded-md bg-secondary-background-hover text-secondary-foreground align-middle"
>
<i class="icon-[lucide--more-vertical] text-sm" />
</span>
</template>
</i18n-t>
</div>
</template>
</SectionWidgets>

View File

@@ -46,7 +46,7 @@ const isFavorited = computed(() =>
async function handleRename() {
const newLabel = await dialogService.prompt({
title: t('g.rename'),
message: t('g.enterNewName') + ':',
message: t('g.enterNewNamePrompt'),
defaultValue: widget.label,
placeholder: widget.name
})

View File

@@ -138,7 +138,6 @@ describe('flatAndCategorizeSelectedItems', () => {
expect(result.nodes).toEqual([testNode1])
expect(result.groups).toEqual([testGroup1, testGroup2])
expect(result.nodeToParentGroup.get(testNode1)).toBe(testGroup2)
expect(result.nodeToParentGroup.has(testGroup2 as any)).toBe(false)
})
it('should handle mixed selection of nodes and groups', () => {

View File

@@ -118,7 +118,9 @@ const suggestions = ref<ComfyNodeDefImpl[]>([])
const hoveredSuggestion = ref<ComfyNodeDefImpl | null>(null)
const currentQuery = ref('')
const placeholder = computed(() => {
return filters.length === 0 ? t('g.searchNodes') + '...' : ''
return filters.length === 0
? t('g.searchPlaceholder', { subject: t('g.nodes') })
: ''
})
const nodeDefStore = useNodeDefStore()

View File

@@ -230,7 +230,7 @@ const extraMenuItems = computed(() => [
{
key: 'manage-extensions',
label: t('menu.manageExtensions'),
icon: 'icon-[lucide--puzzle]',
icon: 'icon-[comfy--extensions-blocks]',
command: showManageExtensions
}
])

View File

@@ -1,10 +1,22 @@
<script setup lang="ts">
import { computed } from 'vue'
import Button from '@/components/ui/button/Button.vue'
import { t } from '@/i18n'
import { useKeybindingStore } from '@/platform/keybindings/keybindingStore'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import { useCommandStore } from '@/stores/commandStore'
const canvasStore = useCanvasStore()
const keybindingStore = useKeybindingStore()
const keybindingSuffix = computed(() => {
const shortcut = keybindingStore
.getKeybindingByCommandId('Comfy.ToggleLinear')
?.combo.toString()
return shortcut ? t('g.shortcutSuffix', { shortcut }) : ''
})
function toggleLinearMode() {
useCommandStore().execute('Comfy.ToggleLinear', {
metadata: { source: 'button' }
@@ -18,7 +30,7 @@ function toggleLinearMode() {
>
<Button
v-tooltip="{
value: t('linearMode.linearMode'),
value: t('linearMode.linearMode') + keybindingSuffix,
showDelay: 300,
hideDelay: 300
}"
@@ -30,7 +42,7 @@ function toggleLinearMode() {
</Button>
<Button
v-tooltip="{
value: t('linearMode.graphMode'),
value: t('linearMode.graphMode') + keybindingSuffix,
showDelay: 300,
hideDelay: 300
}"

View File

@@ -66,6 +66,7 @@ import SidebarBottomPanelToggleButton from '@/components/sidebar/SidebarBottomPa
import SidebarSettingsButton from '@/components/sidebar/SidebarSettingsButton.vue'
import SidebarShortcutsToggleButton from '@/components/sidebar/SidebarShortcutsToggleButton.vue'
import { useFeatureFlags } from '@/composables/useFeatureFlags'
import { t } from '@/i18n'
import { isCloud } from '@/platform/distribution/types'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useTelemetry } from '@/platform/telemetry'
@@ -151,10 +152,10 @@ const onTabClick = async (item: SidebarTabExtension) => {
const keybindingStore = useKeybindingStore()
const getTabTooltipSuffix = (tab: SidebarTabExtension) => {
const keybinding = keybindingStore.getKeybindingByCommandId(
`Workspace.ToggleSidebarTab.${tab.id}`
)
return keybinding ? ` (${keybinding.combo.toString()})` : ''
const shortcut = keybindingStore
.getKeybindingByCommandId(`Workspace.ToggleSidebarTab.${tab.id}`)
?.combo.toString()
return shortcut ? t('g.shortcutSuffix', { shortcut }) : ''
}
const isOverflowing = ref(false)

View File

@@ -25,7 +25,11 @@
<SearchBox
ref="searchBoxRef"
v-model:model-value="searchQuery"
:placeholder="$t('g.searchModels') + '...'"
:placeholder="
$t('g.searchPlaceholder', {
subject: $t('sideToolbar.labels.models')
})
"
@search="handleSearch"
/>
</div>

View File

@@ -91,7 +91,7 @@
v-model:model-value="searchQuery"
data-testid="node-library-search"
class="node-lib-search-box"
:placeholder="$t('g.searchNodes') + '...'"
:placeholder="$t('g.searchPlaceholder', { subject: $t('g.nodes') })"
filter-icon="pi pi-filter"
:filters
@search="handleSearch"

View File

@@ -22,7 +22,9 @@
ref="searchBoxRef"
v-model:model-value="searchQuery"
class="workflows-search-box"
:placeholder="$t('g.searchWorkflows') + '...'"
:placeholder="
$t('g.searchPlaceholder', { subject: $t('g.workflow') })
"
@search="handleSearch"
/>
</div>

View File

@@ -20,9 +20,18 @@ defineOptions({
const {
position = 'popper',
// Safari has issues with click events on portaled content inside dialogs.
// Set disablePortal to true when using Select inside a Dialog on Safari.
// See: https://github.com/chakra-ui/ark/issues/1782
disablePortal = false,
class: className,
...restProps
} = defineProps<SelectContentProps & { class?: HTMLAttributes['class'] }>()
} = defineProps<
SelectContentProps & {
class?: HTMLAttributes['class']
disablePortal?: boolean
}
>()
const emits = defineEmits<SelectContentEmits>()
const delegatedProps = computed(() => ({
@@ -34,7 +43,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<SelectPortal>
<SelectPortal :disabled="disablePortal">
<SelectContent
v-bind="{ ...forwarded, ...$attrs }"
:class="

View File

@@ -1,27 +1,27 @@
import { markRaw } from 'vue'
import { useI18n } from 'vue-i18n'
import CommandTerminal from '@/components/bottomPanel/tabs/terminal/CommandTerminal.vue'
import LogsTerminal from '@/components/bottomPanel/tabs/terminal/LogsTerminal.vue'
import CommandTerminal from '@/components/bottomPanel/tabs/terminal/CommandTerminal.vue'
import type { BottomPanelExtension } from '@/types/extensionTypes'
export const useLogsTerminalTab = (): BottomPanelExtension => {
export function useLogsTerminalTab(): BottomPanelExtension {
const { t } = useI18n()
return {
id: 'logs-terminal',
title: t('g.logs'), // For command labels (collected by i18n workflow)
titleKey: 'g.logs', // For dynamic translation in UI
title: t('g.logs'),
titleKey: 'g.logs',
component: markRaw(LogsTerminal),
type: 'vue'
}
}
export const useCommandTerminalTab = (): BottomPanelExtension => {
export function useCommandTerminalTab(): BottomPanelExtension {
const { t } = useI18n()
return {
id: 'command-terminal',
title: t('g.terminal'), // For command labels (collected by i18n workflow)
titleKey: 'g.terminal', // For dynamic translation in UI
title: t('g.terminal'),
titleKey: 'g.terminal',
component: markRaw(CommandTerminal),
type: 'vue'
}

View File

@@ -52,7 +52,7 @@ export interface SafeWidgetData {
isDOMWidget?: boolean
label?: string
nodeType?: string
options?: IWidgetOptions<unknown>
options?: IWidgetOptions
spec?: InputSpec
slotMetadata?: WidgetSlotMetadata
}
@@ -145,7 +145,7 @@ interface SharedWidgetEnhancements {
/** Widget label */
label?: string
/** Widget options */
options?: Record<string, any>
options?: IWidgetOptions
}
/**
@@ -170,7 +170,7 @@ export function getSharedWidgetEnhancements(
? 'ring ring-component-node-widget-advanced'
: undefined,
label: widget.label,
options: widget.options
options: widget.options as IWidgetOptions
}
}
@@ -432,7 +432,7 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
} else {
// Not during workflow loading - initialize layout immediately
// This handles individual node additions during normal operation
requestAnimationFrame(initializeVueNodeLayout)
initializeVueNodeLayout()
}
// Call original callback if provided

View File

@@ -47,7 +47,7 @@ export const useComputedWithWidgetWatch = (
const { widgetNames, triggerCanvasRedraw = false } = options
// Create a reactive trigger based on widget values
const widgetValues = ref<Record<string, any>>({})
const widgetValues = ref<Record<string, unknown>>({})
// Initialize widget observers
if (node.widgets) {
@@ -56,7 +56,7 @@ export const useComputedWithWidgetWatch = (
: node.widgets
// Initialize current values
const currentValues: Record<string, any> = {}
const currentValues: Record<string, unknown> = {}
widgetsToObserve.forEach((widget) => {
currentValues[widget.name] = widget.value
})

View File

@@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n'
import { useErrorHandling } from '@/composables/useErrorHandling'
import type { JobListItem } from '@/composables/queue/useJobList'
import { useJobMenu } from '@/composables/queue/useJobMenu'
import type { JobState } from '@/types/queue'
import { isActiveJobState } from '@/utils/queueUtil'
export type JobAction = {
icon: string
@@ -13,8 +13,6 @@ export type JobAction = {
variant: 'destructive' | 'secondary' | 'textonly'
}
const CANCELLABLE_STATES: JobState[] = ['pending', 'initialization', 'running']
export function useJobActions(
job: MaybeRefOrGetter<JobListItem | null | undefined>
) {
@@ -36,10 +34,7 @@ export function useJobActions(
return false
}
return (
currentJob.showClear !== false &&
CANCELLABLE_STATES.includes(currentJob.state)
)
return currentJob.showClear !== false && isActiveJobState(currentJob.state)
})
const runCancelJob = wrapWithErrorHandlingAsync(async () => {

View File

@@ -190,7 +190,7 @@ export function useJobMenu(
if (settingStore.get('Comfy.PromptFilename')) {
const input = await useDialogService().prompt({
title: t('workflowService.exportWorkflow'),
message: t('workflowService.enterFilename') + ':',
message: t('workflowService.enterFilenamePrompt'),
defaultValue: filename
})
if (!input) return

View File

@@ -2,7 +2,7 @@ import type { Ref } from 'vue'
import { useSharedCanvasPositionConversion } from '@/composables/element/useCanvasPositionConversion'
import { usePragmaticDroppable } from '@/composables/usePragmaticDragAndDrop'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { LGraphNode, Point } from '@/lib/litegraph/src/litegraph'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
import { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore'
@@ -33,7 +33,7 @@ export const useCanvasDrop = (canvasRef: Ref<HTMLCanvasElement | null>) => {
if (node.data instanceof ComfyNodeDefImpl) {
const nodeDef = node.data
const pos = [...basePos]
const pos: Point = [...basePos]
// Add an offset on y to make sure after adding the node, the cursor
// is on the node (top left corner)
pos[1] += LiteGraph.NODE_TITLE_HEIGHT

View File

@@ -68,8 +68,11 @@ vi.mock('@/platform/workflow/core/services/workflowService', () => ({
useWorkflowService: vi.fn(() => ({}))
}))
const mockDialogService = vi.hoisted(() => ({
prompt: vi.fn()
}))
vi.mock('@/services/dialogService', () => ({
useDialogService: vi.fn(() => ({}))
useDialogService: vi.fn(() => mockDialogService)
}))
vi.mock('@/services/litegraphService', () => ({
@@ -84,14 +87,31 @@ vi.mock('@/stores/toastStore', () => ({
useToastStore: vi.fn(() => ({}))
}))
const mockChangeTracker = vi.hoisted(() => ({
checkState: vi.fn()
}))
const mockWorkflowStore = vi.hoisted(() => ({
activeWorkflow: {
changeTracker: mockChangeTracker
}
}))
vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({
useWorkflowStore: vi.fn(() => ({}))
useWorkflowStore: vi.fn(() => mockWorkflowStore)
}))
vi.mock('@/stores/subgraphStore', () => ({
useSubgraphStore: vi.fn(() => ({}))
}))
vi.mock('@/renderer/core/canvas/canvasStore', () => ({
useCanvasStore: vi.fn(() => ({
getCanvas: () => app.canvas
})),
useTitleEditorStore: vi.fn(() => ({
titleEditorTarget: null
}))
}))
vi.mock('@/stores/workspace/colorPaletteStore', () => ({
useColorPaletteStore: vi.fn(() => ({}))
}))
@@ -155,11 +175,12 @@ describe('useCoreCommands', () => {
findNodeById: vi.fn(),
getNodeById: vi.fn(),
setDirtyCanvas: vi.fn(),
sendActionToCanvas: vi.fn()
sendActionToCanvas: vi.fn(),
extra: {} as Record<string, unknown>
} as Partial<typeof app.canvas.subgraph> as typeof app.canvas.subgraph
}
const mockSubgraph = createMockSubgraph()
const mockSubgraph = createMockSubgraph()!
function createMockSettingStore(
getReturnValue: boolean
@@ -270,4 +291,138 @@ describe('useCoreCommands', () => {
expect(api.dispatchCustomEvent).not.toHaveBeenCalled()
})
})
describe('Subgraph metadata commands', () => {
beforeEach(() => {
mockSubgraph.extra = {}
vi.clearAllMocks()
})
describe('SetDescription command', () => {
it('should do nothing when not in subgraph', async () => {
app.canvas.subgraph = undefined
const commands = useCoreCommands()
const setDescCommand = commands.find(
(cmd) => cmd.id === 'Comfy.Subgraph.SetDescription'
)!
await setDescCommand.function()
expect(mockDialogService.prompt).not.toHaveBeenCalled()
})
it('should set description on subgraph.extra', async () => {
app.canvas.subgraph = mockSubgraph
mockDialogService.prompt.mockResolvedValue('Test description')
const commands = useCoreCommands()
const setDescCommand = commands.find(
(cmd) => cmd.id === 'Comfy.Subgraph.SetDescription'
)!
await setDescCommand.function()
expect(mockDialogService.prompt).toHaveBeenCalled()
expect(mockSubgraph.extra.BlueprintDescription).toBe('Test description')
expect(mockChangeTracker.checkState).toHaveBeenCalled()
})
it('should not set description when user cancels', async () => {
app.canvas.subgraph = mockSubgraph
mockDialogService.prompt.mockResolvedValue(null)
const commands = useCoreCommands()
const setDescCommand = commands.find(
(cmd) => cmd.id === 'Comfy.Subgraph.SetDescription'
)!
await setDescCommand.function()
expect(mockSubgraph.extra.BlueprintDescription).toBeUndefined()
expect(mockChangeTracker.checkState).not.toHaveBeenCalled()
})
})
describe('SetSearchAliases command', () => {
it('should do nothing when not in subgraph', async () => {
app.canvas.subgraph = undefined
const commands = useCoreCommands()
const setAliasesCommand = commands.find(
(cmd) => cmd.id === 'Comfy.Subgraph.SetSearchAliases'
)!
await setAliasesCommand.function()
expect(mockDialogService.prompt).not.toHaveBeenCalled()
})
it('should set search aliases on subgraph.extra', async () => {
app.canvas.subgraph = mockSubgraph
mockDialogService.prompt.mockResolvedValue('alias1, alias2, alias3')
const commands = useCoreCommands()
const setAliasesCommand = commands.find(
(cmd) => cmd.id === 'Comfy.Subgraph.SetSearchAliases'
)!
await setAliasesCommand.function()
expect(mockDialogService.prompt).toHaveBeenCalled()
expect(mockSubgraph.extra.BlueprintSearchAliases).toEqual([
'alias1',
'alias2',
'alias3'
])
expect(mockChangeTracker.checkState).toHaveBeenCalled()
})
it('should trim whitespace and filter empty strings', async () => {
app.canvas.subgraph = mockSubgraph
mockDialogService.prompt.mockResolvedValue(' alias1 , , alias2 , ')
const commands = useCoreCommands()
const setAliasesCommand = commands.find(
(cmd) => cmd.id === 'Comfy.Subgraph.SetSearchAliases'
)!
await setAliasesCommand.function()
expect(mockSubgraph.extra.BlueprintSearchAliases).toEqual([
'alias1',
'alias2'
])
})
it('should set undefined when empty input', async () => {
app.canvas.subgraph = mockSubgraph
mockDialogService.prompt.mockResolvedValue('')
const commands = useCoreCommands()
const setAliasesCommand = commands.find(
(cmd) => cmd.id === 'Comfy.Subgraph.SetSearchAliases'
)!
await setAliasesCommand.function()
expect(mockSubgraph.extra.BlueprintSearchAliases).toBeUndefined()
})
it('should not set aliases when user cancels', async () => {
app.canvas.subgraph = mockSubgraph
mockDialogService.prompt.mockResolvedValue(null)
const commands = useCoreCommands()
const setAliasesCommand = commands.find(
(cmd) => cmd.id === 'Comfy.Subgraph.SetSearchAliases'
)!
await setAliasesCommand.function()
expect(mockSubgraph.extra.BlueprintSearchAliases).toBeUndefined()
expect(mockChangeTracker.checkState).not.toHaveBeenCalled()
})
})
})
})

View File

@@ -8,6 +8,7 @@ import {
DEFAULT_DARK_COLOR_PALETTE,
DEFAULT_LIGHT_COLOR_PALETTE
} from '@/constants/coreColorPalettes'
import { tryToggleWidgetPromotion } from '@/core/graph/subgraph/proxyWidgetUtils'
import { t } from '@/i18n'
import {
@@ -170,8 +171,9 @@ export function useCoreCommands(): ComfyCommand[] {
icon: 'pi pi-save',
label: 'Publish Subgraph',
menubarLabel: 'Publish',
function: async () => {
await useSubgraphStore().publishSubgraph()
function: async (metadata?: Record<string, unknown>) => {
const name = metadata?.name as string | undefined
await useSubgraphStore().publishSubgraph(name)
}
},
{
@@ -198,7 +200,7 @@ export function useCoreCommands(): ComfyCommand[] {
const newName = await dialogService.prompt({
title: t('g.rename'),
message: t('workflowService.enterFilename') + ':',
message: t('workflowService.enterFilenamePrompt'),
defaultValue: workflow.filename
})
if (!newName || newName === workflow.filename) return
@@ -1094,6 +1096,75 @@ export function useCoreCommands(): ComfyCommand[] {
)
}
},
{
id: 'Comfy.Subgraph.SetDescription',
icon: 'pi pi-pencil',
label: 'Set Subgraph Description',
versionAdded: '1.39.7',
function: async (metadata?: Record<string, unknown>) => {
const canvas = canvasStore.getCanvas()
const subgraph = canvas.subgraph
if (!subgraph) return
const extra = (subgraph.extra ??= {}) as Record<string, unknown>
const currentDescription = (extra.BlueprintDescription as string) ?? ''
let description: string | null | undefined
const rawDescription = metadata?.description
if (rawDescription != null) {
description =
typeof rawDescription === 'string'
? rawDescription
: String(rawDescription)
}
description ??= await dialogService.prompt({
title: t('g.description'),
message: t('subgraphStore.enterDescription'),
defaultValue: currentDescription
})
if (description === null) return
extra.BlueprintDescription = description.trim() || undefined
workflowStore.activeWorkflow?.changeTracker?.checkState()
}
},
{
id: 'Comfy.Subgraph.SetSearchAliases',
icon: 'pi pi-search',
label: 'Set Subgraph Search Aliases',
versionAdded: '1.39.7',
function: async (metadata?: Record<string, unknown>) => {
const canvas = canvasStore.getCanvas()
const subgraph = canvas.subgraph
if (!subgraph) return
const parseAliases = (value: unknown): string[] =>
(Array.isArray(value) ? value.map(String) : String(value).split(','))
.map((s) => s.trim())
.filter(Boolean)
const extra = (subgraph.extra ??= {}) as Record<string, unknown>
let aliases: string[]
const rawAliases = metadata?.aliases
if (rawAliases == null) {
const input = await dialogService.prompt({
title: t('subgraphStore.searchAliases'),
message: t('subgraphStore.enterSearchAliases'),
defaultValue: parseAliases(extra.BlueprintSearchAliases ?? '').join(
', '
)
})
if (input === null) return
aliases = parseAliases(input)
} else {
aliases = parseAliases(rawAliases)
}
extra.BlueprintSearchAliases = aliases.length > 0 ? aliases : undefined
workflowStore.activeWorkflow?.changeTracker?.checkState()
}
},
{
id: 'Comfy.Dev.ShowModelSelector',
icon: 'pi pi-box',
@@ -1233,7 +1304,7 @@ export function useCoreCommands(): ComfyCommand[] {
{
id: 'Comfy.ToggleLinear',
icon: 'pi pi-database',
label: 'Toggle Simple Mode',
label: 'Toggle App Mode',
function: (metadata?: Record<string, unknown>) => {
const source =
typeof metadata?.source === 'string' ? metadata.source : 'keybind'

View File

@@ -31,7 +31,7 @@ function setupSubgraph(
const subgraph = createTestSubgraph()
const subgraphNode = createTestSubgraphNode(subgraph)
subgraphNode._internalConfigureAfterSlots()
const graph = subgraphNode.graph
const graph = subgraphNode.graph!
graph.add(subgraphNode)
const innerNodes = []
for (let i = 0; i < innerNodeCount; i++) {

View File

@@ -34,7 +34,7 @@ import { LGraph } from '@/lib/litegraph/src/LGraph'
import {
createTestSubgraph,
createTestSubgraphNode
} from './fixtures/subgraphHelpers'
} from './__fixtures__/subgraphHelpers'
function createTestSetup() {
const subgraph = createTestSubgraph()

View File

@@ -191,7 +191,6 @@ if (canvas.state.hoveringOver & CanvasItem.ResizeSe) element.style.cursor = 'se-
All are unused and incomplete. Have bugs beyond just typescript typing, and are (currently) not worth maintaining. If any of these features are desired down the track, they can be reimplemented.
- Live mode
- Subgraph
- `dragged_node`
## LiteGraph

View File

@@ -1,9 +0,0 @@
# Contribution Rules
There are some simple rules that everyone should follow:
### Do not commit files from build folder
> I usually have horrible merge conflicts when I upload the build version that take me too much time to solve, but I want to keep the build version in the repo, so I guess it would be better if only one of us does the built, which would be me.
> https://github.com/jagenjo/litegraph.js/pull/155#issuecomment-656602861
> Those files will be updated by owner.

View File

@@ -144,18 +144,7 @@ Litegraph has no runtime dependencies. The build tooling has been tested on Node
## Releasing
Use GitHub actions to release normal versions.
1. Run the `Release a New Version` action, selecting the version increment type
1. Merge the resolution PR
1. A GitHub release is automatically published on merge
### Pre-release
The action directly translates `Version increment type` to the pnpm version command. `Pre-release ID (suffix)` is the option for the `--preid` argument.
e.g. Use `prerelease` increment type to automatically bump the patch version and create a pre-release version. Subsequent runs of prerelease will update the prerelease version only.
Use `patch` when ready to remove the pre-release suffix.
This library is embedded via git subtree in ComfyUI_frontend. Releases are managed through the parent repository's release process.
## Contributors

View File

@@ -5639,6 +5639,12 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
this.renderedPaths.clear()
if (this.links_render_mode === LinkRenderType.HIDDEN_LINK) return
// Skip link rendering while waiting for slot positions to sync after reconfigure
if (LiteGraph.vueNodesMode && layoutStore.pendingSlotSync) {
this._visibleReroutes.clear()
return
}
const { graph, subgraph } = this
if (!graph) throw new NullGraphError()

View File

@@ -195,6 +195,42 @@ describe('contextMenuCompat', () => {
expect.any(Error)
)
})
it('should handle multiple items with undefined content correctly', () => {
// Setup base method with items that have undefined content
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
return [
{ content: undefined, title: 'Separator 1' },
{ content: undefined, title: 'Separator 2' },
{ content: 'Item 1', callback: () => {} }
]
}
legacyMenuCompat.install(LGraphCanvas.prototype, 'getCanvasMenuOptions')
// Monkey-patch to add an item with undefined content
const original = LGraphCanvas.prototype.getCanvasMenuOptions
LGraphCanvas.prototype.getCanvasMenuOptions =
function (): (IContextMenuValue | null)[] {
const items = original.apply(this)
items.push({ content: undefined, title: 'Separator 3' })
return items
}
// Extract legacy items
const legacyItems = legacyMenuCompat.extractLegacyItems(
'getCanvasMenuOptions',
mockCanvas
)
// Should extract only the newly added item with undefined content
// (not collapse with existing undefined content items)
expect(legacyItems).toHaveLength(1)
expect(legacyItems[0]).toMatchObject({
content: undefined,
title: 'Separator 3'
})
})
})
describe('integration', () => {

View File

@@ -152,19 +152,51 @@ class LegacyMenuCompat {
const patchedItems = methodToCall.apply(context, args) as
| (IContextMenuValue | null)[]
| undefined
if (!patchedItems) return []
if (!patchedItems) {
return []
}
// Use content-based diff to detect additions (not reference-based)
// Create composite keys from multiple properties to handle undefined content
const createItemKey = (item: IContextMenuValue): string => {
const parts = [
item.content ?? '',
item.title ?? '',
item.className ?? '',
item.property ?? '',
item.type ?? ''
]
return parts.join('|')
}
// Use set-based diff to detect additions by reference
const originalSet = new Set<IContextMenuValue | null>(originalItems)
const addedItems = patchedItems.filter((item) => !originalSet.has(item))
const originalKeys = new Set(
originalItems
.filter(
(item): item is IContextMenuValue =>
item !== null && typeof item === 'object' && 'content' in item
)
.map(createItemKey)
)
const addedItems = patchedItems.filter((item) => {
if (item === null) return false
if (typeof item !== 'object' || !('content' in item)) return false
return !originalKeys.has(createItemKey(item))
})
// Warn if items were removed (patched has fewer original items than expected)
const retainedOriginalCount = patchedItems.filter((item) =>
originalSet.has(item)
const patchedKeys = new Set(
patchedItems
.filter(
(item): item is IContextMenuValue =>
item !== null && typeof item === 'object' && 'content' in item
)
.map(createItemKey)
)
const removedCount = [...originalKeys].filter(
(key) => !patchedKeys.has(key)
).length
if (retainedOriginalCount < originalItems.length) {
if (removedCount > 0) {
console.warn(
`[Context Menu Compat] Monkey patch for ${methodName} removed ${originalItems.length - retainedOriginalCount} original menu item(s). ` +
`[Context Menu Compat] Monkey patch for ${methodName} removed ${removedCount} original menu item(s). ` +
`This may cause unexpected behavior.`
)
}

View File

@@ -2,7 +2,7 @@ import type { NeverNever, PickNevers } from '@/lib/litegraph/src/types/utility'
type EventListeners<T> = {
readonly [K in keyof T]:
| ((this: EventTarget, ev: CustomEvent<T[K]>) => any)
| ((this: EventTarget, ev: CustomEvent<T[K]>) => unknown)
| EventListenerObject
| null
}

View File

@@ -194,6 +194,10 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
}
}
if (!subgraphNode.graph)
throw new NullGraphError(
`SubgraphNode ${subgraphNode.id} has no graph during input resolution`
)
const outerLink = subgraphNode.graph.getLink(linkId)
if (!outerLink)
throw new InvalidLinkError(

View File

@@ -45,7 +45,7 @@ describe.skip('SubgraphConversion', () => {
it('Should keep interior nodes and links', () => {
const subgraph = createTestSubgraph()
const subgraphNode = createTestSubgraphNode(subgraph)
const graph = subgraphNode.graph
const graph = subgraphNode.graph!
graph.add(subgraphNode)
const node1 = createNode(subgraph, [], ['number'])
@@ -63,7 +63,7 @@ describe.skip('SubgraphConversion', () => {
outputs: [{ name: 'value', type: 'number' }]
})
const subgraphNode = createTestSubgraphNode(subgraph)
const graph = subgraphNode.graph
const graph = subgraphNode.graph!
graph.add(subgraphNode)
const innerNode1 = createNode(subgraph, [], ['number'])
@@ -86,7 +86,7 @@ describe.skip('SubgraphConversion', () => {
outputs: [{ name: 'value', type: 'number' }]
})
const subgraphNode = createTestSubgraphNode(subgraph)
const graph = subgraphNode.graph
const graph = subgraphNode.graph!
graph.add(subgraphNode)
const inner = createNode(subgraph, [], ['number'])
@@ -117,7 +117,7 @@ describe.skip('SubgraphConversion', () => {
]
})
const subgraphNode = createTestSubgraphNode(subgraph)
const graph = subgraphNode.graph
const graph = subgraphNode.graph!
graph.add(subgraphNode)
const inner = createNode(subgraph, [], ['number', 'number'])
@@ -159,7 +159,7 @@ describe.skip('SubgraphConversion', () => {
]
})
const subgraphNode = createTestSubgraphNode(subgraph)
const graph = subgraphNode.graph
const graph = subgraphNode.graph!
graph.add(subgraphNode)
const inner1 = createNode(subgraph, ['number', 'number'])

View File

@@ -54,11 +54,23 @@ describe.skip('SubgraphNode Construction', () => {
it('should maintain reference to root graph', () => {
const subgraph = createTestSubgraph()
const subgraphNode = createTestSubgraphNode(subgraph)
const parentGraph = subgraphNode.graph
const parentGraph = subgraphNode.graph!
expect(subgraphNode.rootGraph).toBe(parentGraph.rootGraph)
})
it('should throw NullGraphError when accessing rootGraph after removal', () => {
const subgraph = createTestSubgraph()
const subgraphNode = createTestSubgraphNode(subgraph)
const parentGraph = subgraphNode.graph!
parentGraph.add(subgraphNode)
parentGraph.remove(subgraphNode)
expect(() => subgraphNode.rootGraph).toThrow()
expect(subgraphNode.graph).toBeNull()
})
subgraphTest(
'should synchronize slots with subgraph definition',
({ subgraphWithNode }) => {

View File

@@ -5,6 +5,7 @@ import { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import type { DrawTitleBoxOptions } from '@/lib/litegraph/src/LGraphNode'
import { LLink } from '@/lib/litegraph/src/LLink'
import type { ResolvedConnection } from '@/lib/litegraph/src/LLink'
import { NullGraphError } from '@/lib/litegraph/src/infrastructure/NullGraphError'
import { RecursionError } from '@/lib/litegraph/src/infrastructure/RecursionError'
import type {
ISubgraphInput,
@@ -47,8 +48,11 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
override readonly type: UUID
override readonly isVirtualNode = true as const
override graph: GraphOrSubgraph | null
get rootGraph(): LGraph {
if (!this.graph)
throw new NullGraphError(`SubgraphNode ${this.id} has no graph`)
return this.graph.rootGraph
}
@@ -67,12 +71,13 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
constructor(
/** The (sub)graph that contains this subgraph instance. */
override readonly graph: GraphOrSubgraph,
graph: GraphOrSubgraph,
/** The definition of this subgraph; how its nodes are configured, etc. */
readonly subgraph: Subgraph,
instanceData: ExportedSubgraphInstance
) {
super(subgraph.name, subgraph.id)
this.graph = graph
// Update this node when the subgraph input / output slots are changed
const subgraphEvents = this.subgraph.events
@@ -496,7 +501,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
const subgraphInstanceIdPath = [...subgraphNodePath, this.id]
// Store the subgraph node DTO
const parentSubgraphNode = this.graph.rootGraph
const parentSubgraphNode = this.rootGraph
.resolveSubgraphIdPath(subgraphNodePath)
.at(-1)
const subgraphNodeDto = new ExecutableNodeDTO(

View File

@@ -18,8 +18,8 @@ A subgraph in LiteGraph is a graph-within-a-graph that can be reused as a single
import {
createTestSubgraph,
assertSubgraphStructure
} from './fixtures/subgraphHelpers'
import { subgraphTest } from './fixtures/subgraphFixtures'
} from './__fixtures__/subgraphHelpers'
import { subgraphTest } from './__fixtures__/subgraphFixtures'
// Option 1: Create a subgraph manually
it('should do something', () => {

View File

@@ -68,7 +68,7 @@ describe.skip('subgraphUtils', () => {
describe.skip('findUsedSubgraphIds', () => {
it('should handle graph with no subgraphs', () => {
const graph = new LGraph()
const registry = new Map<UUID, any>()
const registry = new Map<UUID, LGraph>()
const result = findUsedSubgraphIds(graph, registry)
expect(result.size).toBe(0)
@@ -87,7 +87,7 @@ describe.skip('subgraphUtils', () => {
const node2 = createTestSubgraphNode(subgraph2)
subgraph1.add(node2)
const registry = new Map<UUID, any>([
const registry = new Map<UUID, LGraph>([
[subgraph1.id, subgraph1],
[subgraph2.id, subgraph2]
])
@@ -115,7 +115,7 @@ describe.skip('subgraphUtils', () => {
const node3 = createTestSubgraphNode(subgraph1, { id: 3 })
subgraph2.add(node3)
const registry = new Map<UUID, any>([
const registry = new Map<UUID, LGraph>([
[subgraph1.id, subgraph1],
[subgraph2.id, subgraph2]
])
@@ -139,7 +139,7 @@ describe.skip('subgraphUtils', () => {
rootGraph.add(node2)
// Only register subgraph1
const registry = new Map<UUID, any>([[subgraph1.id, subgraph1]])
const registry = new Map<UUID, LGraph>([[subgraph1.id, subgraph1]])
const result = findUsedSubgraphIds(rootGraph, registry)
expect(result.size).toBe(2)

View File

@@ -37,6 +37,14 @@ export interface IWidgetOptions<TValues = unknown[]> {
getOptionLabel?: (value?: string | null) => string
callback?: IWidget['callback']
iconClass?: string
// Vue widget options
disabled?: boolean
useGrouping?: boolean
placeholder?: string
showThumbnails?: boolean
showItemNavigators?: boolean
hidden?: boolean
}
interface IWidgetSliderOptions extends IWidgetOptions<number[]> {

View File

@@ -35,11 +35,10 @@ export class ComboWidget
override get _displayValue() {
if (this.computedDisabled) return ''
if (this.options.getOptionLabel) {
const getOptionLabel = this.options.getOptionLabel
if (getOptionLabel) {
try {
return this.options.getOptionLabel(
this.value ? String(this.value) : null
)
return getOptionLabel(this.value ? String(this.value) : null)
} catch (e) {
console.error('Failed to map value:', e)
return this.value ? String(this.value) : ''
@@ -155,9 +154,12 @@ export class ComboWidget
}
const menu = new LiteGraph.ContextMenu([], menuOptions)
const getOptionLabel = this.options.getOptionLabel
for (const value of values_list) {
try {
const label = this.options.getOptionLabel(String(value))
const label = getOptionLabel
? getOptionLabel(String(value))
: String(value)
menu.addItem(label, value, menuOptions)
} catch (err) {
console.error('Failed to map value:', err)

View File

@@ -35,9 +35,6 @@
"Comfy-Desktop_Restart": {
"label": "إعادة التشغيل"
},
"Comfy_3DViewer_Open3DViewer": {
"label": "فتح عارض ثلاثي الأبعاد (بيتا) للعقدة المحددة"
},
"Comfy_BrowseModelAssets": {
"label": "تجريبي: تصفح أصول النماذج"
},
@@ -266,6 +263,12 @@
"Comfy_ShowSettingsDialog": {
"label": "عرض نافذة الإعدادات"
},
"Comfy_Subgraph_SetDescription": {
"label": "تعيين وصف الرسم البياني الفرعي"
},
"Comfy_Subgraph_SetSearchAliases": {
"label": "تعيين الأسماء المستعارة للبحث في الرسم البياني الفرعي"
},
"Comfy_ToggleAssetAPI": {
"label": "تجريبي: تمكين AssetAPI"
},
@@ -311,12 +314,6 @@
"Workspace_ToggleBottomPanel": {
"label": "تبديل اللوحة السفلية"
},
"Workspace_ToggleBottomPanelTab_command-terminal": {
"label": "تبديل لوحة الطرفية السفلية"
},
"Workspace_ToggleBottomPanelTab_logs-terminal": {
"label": "تبديل لوحة السجلات السفلية"
},
"Workspace_ToggleBottomPanelTab_shortcuts-essentials": {
"label": "تبديل اللوحة السفلية الأساسية"
},

View File

@@ -16,6 +16,8 @@
"assetBrowser": {
"allCategory": "جميع {category}",
"allModels": "جميع النماذج",
"apiKeyHint": "تستورد نماذج خاصة أو محمية؟ {link}.",
"apiKeyHintLink": "أضف مفاتيح API الخاصة بك في الإعدادات",
"ariaLabel": {
"assetCard": "{name} - أصل {type}",
"loadingAsset": "جاري تحميل الأصل"
@@ -51,13 +53,34 @@
"canImport": "لا توجد نماذج مستوردة بعد. انقر على \"استيراد نموذج\" لإضافة نموذجك الخاص.",
"restricted": "النماذج الشخصية متاحة فقط لمستوى Creator وما فوق."
},
"errorAccessForbidden": "تم رفض الوصول إلى هذا المورد.",
"errorConnectionRefused": "تعذر الاتصال بالمصدر. يرجى المحاولة لاحقًا.",
"errorDownloadCancelled": "تم إلغاء التنزيل.",
"errorFileTooLarge": "الملف يتجاوز الحد الأقصى المسموح به للحجم",
"errorFormatNotAllowed": "يسمح فقط بصيغة SafeTensor",
"errorHttpError": "حدث خطأ أثناء جلب البيانات الوصفية.",
"errorInternalError": "حدث خطأ غير متوقع. يرجى المحاولة مرة أخرى.",
"errorInvalidHost": "تعذر حل اسم مضيف عنوان URL للمصدر.",
"errorInvalidUrl": "يرجى إدخال عنوان URL.",
"errorInvalidUrlFormat": "تنسيق عنوان URL غير صالح. يرجى التحقق والمحاولة مرة أخرى.",
"errorMetadataFetchFailed": "فشل في جلب معلومات الملف من المصدر.",
"errorModelTypeNotSupported": "نوع النموذج هذا غير مدعوم",
"errorNetworkError": "حدث خطأ في الشبكة. يرجى التحقق من الاتصال والمحاولة مرة أخرى.",
"errorNetworkTimeout": "انتهت مهلة الطلب. يرجى المحاولة مرة أخرى.",
"errorRateLimited": "عدد كبير جدًا من الطلبات. يرجى المحاولة بعد بضع دقائق.",
"errorRequestCancelled": "تم إلغاء الطلب.",
"errorResourceNotFound": "لم يتم العثور على الملف. يرجى التحقق من عنوان URL والمحاولة مرة أخرى.",
"errorServiceUnavailable": "الخدمة غير متوفرة مؤقتًا. يرجى المحاولة لاحقًا.",
"errorSourceServerError": "الخادم المصدر يواجه مشاكل. يرجى المحاولة لاحقًا.",
"errorUnauthorized": "يرجى تسجيل الدخول للمتابعة.",
"errorUnauthorizedSource": "هذا المورد يتطلب المصادقة. يرجى إضافة رمز API الخاص بك في الإعدادات.",
"errorUnknown": "حدث خطأ غير متوقع",
"errorUnsafePickleScan": "اكتشف CivitAI رمزًا غير آمن محتملًا في هذا الملف",
"errorUnsafeVirusScan": "اكتشف CivitAI برمجيات خبيثة أو محتوى مشبوه في هذا الملف",
"errorUnsupportedSource": "هذا العنوان غير مدعوم. فقط عناوين Hugging Face وCivitai مسموحة.",
"errorUploadFailed": "فشل في استيراد الأصل. يرجى المحاولة مرة أخرى.",
"errorUserTokenAccessDenied": "رمز API الخاص بك لا يملك صلاحية الوصول إلى هذا المورد. يرجى التحقق من أذونات الرمز.",
"errorUserTokenInvalid": "رمز API المخزن غير صالح أو منتهي الصلاحية. يرجى تحديث الرمز في الإعدادات.",
"failedToCreateNode": "فشل إنشاء العقدة. يرجى المحاولة مرة أخرى أو التحقق من وحدة التحكم للحصول على التفاصيل.",
"fileFormats": "تنسيقات الملفات",
"fileName": "اسم الملف",
@@ -581,6 +604,13 @@
"COMFY_MATCHTYPE_V3": "COMFY_MATCHTYPE_V3",
"CONDITIONING": "تكييف",
"CONTROL_NET": "ControlNet",
"FILE_3D": "ملف ثلاثي الأبعاد",
"FILE_3D_FBX": "ملف FBX ثلاثي الأبعاد",
"FILE_3D_GLB": "ملف GLB ثلاثي الأبعاد",
"FILE_3D_GLTF": "ملف GLTF ثلاثي الأبعاد",
"FILE_3D_OBJ": "ملف OBJ ثلاثي الأبعاد",
"FILE_3D_STL": "ملف STL ثلاثي الأبعاد",
"FILE_3D_USDZ": "ملف USDZ ثلاثي الأبعاد",
"FLOAT": "رقم عشري",
"FLOATS": "أرقام عشرية",
"GEMINI_INPUT_FILES": "ملفات إدخال جيميني",
@@ -721,6 +751,7 @@
"commandProhibited": "الأمر {command} محظور. يرجى التواصل مع المسؤول لمزيد من المعلومات.",
"community": "المجتمع",
"completed": "اكتمل",
"completedWithCheckmark": "اكتمل ✓",
"confirm": "تأكيد",
"confirmed": "تم التأكيد",
"content": "محتوى",
@@ -756,6 +787,7 @@
"download": "تنزيل",
"downloadImage": "تنزيل الصورة",
"downloadVideo": "تنزيل الفيديو",
"downloadWithSize": "تنزيل ({size})",
"downloading": "جارٍ التحميل",
"dropYourFileOr": "أسقط ملفك أو",
"duplicate": "تكرار",
@@ -771,12 +803,14 @@
"enabling": "جارٍ التمكين",
"enterBaseName": "أدخل الاسم الأساسي",
"enterNewName": "أدخل الاسم الجديد",
"enterNewNamePrompt": "أدخل اسمًا جديدًا:",
"error": "خطأ",
"errorLoadingImage": "حدث خطأ أثناء تحميل الصورة",
"errorLoadingVideo": "حدث خطأ أثناء تحميل الفيديو",
"experimental": "تجريبي",
"export": "تصدير",
"extensionName": "اسم الامتداد",
"extensions": "الملحقات",
"failed": "فشل",
"failedToCopyJobId": "فشل نسخ معرف المهمة",
"failedToDownloadImage": "فشل في تنزيل الصورة",
@@ -815,6 +849,7 @@
"jobIdCopied": "تم نسخ معرف المهمة إلى الحافظة",
"keybinding": "اختصار لوحة المفاتيح",
"keybindingAlreadyExists": "الاختصار موجود بالفعل في",
"keybindings": "اختصارات لوحة المفاتيح",
"learnMore": "اعرف المزيد",
"listening": "جاري الاستماع...",
"liveSamplingPreview": "معاينة أخذ العينات المباشرة",
@@ -926,6 +961,7 @@
"selectedFile": "الملف المحدد",
"setAsBackground": "تعيين كخلفية",
"settings": "الإعدادات",
"shortcutSuffix": " ({shortcut})",
"showLeftPanel": "إظهار اللوحة اليسرى",
"showReport": "عرض التقرير",
"showRightPanel": "إظهار اللوحة اليمنى",
@@ -1027,8 +1063,12 @@
},
"imageCrop": {
"cropPreviewAlt": "معاينة الاقتصاص",
"custom": "مخصص",
"loading": "جارٍ التحميل...",
"noInputImage": "لا توجد صورة إدخال متصلة"
"lockRatio": "تثبيت النسبة",
"noInputImage": "لا توجد صورة إدخال متصلة",
"ratio": "النسبة",
"unlockRatio": "إلغاء تثبيت النسبة"
},
"importFailed": {
"copyError": "خطأ في النسخ",
@@ -1152,14 +1192,21 @@
"helpFix": "المساعدة في الإصلاح"
},
"linearMode": {
"beta": "تجريبي - أرسل ملاحظاتك",
"beta": "وضع التطبيق تجريبي - أرسل ملاحظاتك",
"downloadAll": "تنزيل الكل",
"dragAndDropImage": "اسحب وأسقط صورة",
"graphMode": "وضع الرسم البياني",
"linearMode": "الوضع البسيط",
"linearMode": "وضع التطبيق",
"rerun": "تشغيل مجدد",
"reuseParameters": "إعادة استخدام المعلمات",
"runCount": "عدد مرات التشغيل:"
"runCount": "عدد مرات التشغيل:",
"welcome": {
"intro": "عرض مبسط يخفي مخطط العقد حتى تتمكن من التركيز على الإبداع.",
"layout": "على اليسار، سترى الصور والفيديوهات والمخرجات التي تم إنشاؤها. على اليمين، فقط عناصر التحكم التي تحتاجها. كل ما هو معقد يبقى بعيدًا عن الأنظار.",
"sharing": "المشاركة سهلة: أنشئ سير العمل الخاص بك، افتح وضع التطبيق، انقر بزر الماوس الأيمن على علامة التبويب، ثم صدّر. عندما يفتح الآخرون ملفك، سيتم تشغيله مباشرة في هذا العرض النظيف. يمكنك مشاركة سير عمل قوي كأداة بسيطة دون الحاجة لفهم مخططات العقد.",
"title": "مرحبًا بك في وضع التطبيق",
"widget": "إذا كنت تريد التحكم في الإعدادات الظاهرة، حوّل العقد العليا إلى مخطط فرعي، ثم استخدم ترقية عناصر التحكم في الأدوات أعلاه لاختيار ما يتم عرضه."
}
},
"load3d": {
"applyingTexture": "جارٍ تطبيق الخامة...",
@@ -1610,7 +1657,6 @@
"Node Library": "مكتبة العقد",
"Node Links": "روابط العقد",
"Open": "فتح",
"Open 3D Viewer (Beta) for Selected Node": "فتح عارض ثلاثي الأبعاد (بيتا) للعقدة المحددة",
"Open Color Picker in MaskEditor": "فتح منتقي الألوان في محرر القناع",
"Open Custom Nodes Folder": "فتح مجلد العقد المخصصة",
"Open DevTools": "فتح أدوات المطور",
@@ -1640,16 +1686,16 @@
"Rotate Right in MaskEditor": "تدوير لليمين في محرر القناع",
"Save": "حفظ",
"Save As": "حفظ باسم",
"Set Subgraph Description": "تعيين وصف المخطط الفرعي",
"Set Subgraph Search Aliases": "تعيين الأسماء المستعارة للبحث في المخطط الفرعي",
"Show Keybindings Dialog": "عرض مربع حوار اختصارات لوحة المفاتيح",
"Show Model Selector (Dev)": "إظهار منتقي النماذج (للمطورين)",
"Show Settings Dialog": "عرض نافذة الإعدادات",
"Sign Out": "تسجيل خروج",
"Toggle App Mode": "تبديل وضع التطبيق",
"Toggle Essential Bottom Panel": "تبديل لوحة العناصر الأساسية السفلية",
"Toggle Logs Bottom Panel": "تبديل لوحة السجلات السفلية",
"Toggle Queue Panel V2": "تبديل لوحة قائمة الانتظار V2",
"Toggle Search Box": "تبديل مربع البحث",
"Toggle Simple Mode": "تبديل وضع البسيط",
"Toggle Terminal Bottom Panel": "تبديل لوحة الطرفية السفلية",
"Toggle Theme (Dark/Light)": "تبديل السمة (داكن/فاتح)",
"Toggle View Controls Bottom Panel": "تبديل لوحة عناصر التحكم في العرض السفلية",
"Toggle promotion of hovered widget": "تبديل ترقية عنصر واجهة المستخدم المحدد",
@@ -1707,6 +1753,7 @@
"ByteDance": "بايت دانس",
"Gemini": "جيميني",
"Grok": "Grok",
"HitPaw": "HitPaw",
"Ideogram": "إيديوغرام",
"Kling": "Kling",
"LTXV": "LTXV",
@@ -1928,6 +1975,7 @@
"favorites": "المدخلات المفضلة",
"favoritesNone": "لا توجد مدخلات مفضلة",
"favoritesNoneDesc": "ستظهر المدخلات التي تضعها في المفضلة هنا",
"favoritesNoneHint": "في علامة تبويب المعلمات، انقر على {moreIcon} بجانب أي إدخال لإضافته هنا",
"favoritesNoneTooltip": "قم بوضع نجمة على الأدوات للوصول السريع إليها دون اختيار العقد",
"globalSettings": {
"canvas": "اللوحة",
@@ -1973,6 +2021,36 @@
"togglePanel": "تبديل لوحة الخصائص",
"workflowOverview": "نظرة عامة على سير العمل"
},
"secrets": {
"addSecret": "إضافة سر",
"createdAt": "تم الإنشاء في {date}",
"deleteConfirmMessage": "هل أنت متأكد أنك تريد حذف \"{name}\"؟ لا يمكن التراجع عن هذا الإجراء.",
"deleteConfirmTitle": "حذف السر",
"description": "يتم تشفير الأسرار وتُستخدم للبيانات الحساسة مثل مفاتيح API.",
"descriptionUsage": "قم بتخزين رموزك هنا لتمكين تنزيل النماذج الخاصة والمحمية من المزودين المدعومين.",
"editSecret": "تعديل السر",
"errors": {
"duplicateName": "يوجد سر بهذا الاسم بالفعل",
"duplicateProvider": "يوجد سر لهذا المزود بالفعل",
"nameRequired": "الاسم مطلوب",
"nameTooLong": "يجب ألا يزيد الاسم عن ٢٥٥ حرفًا",
"providerRequired": "المزود مطلوب",
"secretValueRequired": "قيمة السر مطلوبة"
},
"lastUsed": "آخر استخدام في {date}",
"modelProviders": "مزودو النماذج",
"name": "الاسم",
"namePlaceholder": "مثال: مفتاح API الخاص بي",
"noSecrets": "لا توجد أسرار مخزنة. أضف أول مفتاح API للبدء.",
"provider": "المزود",
"providerHint": "اختياري. اختيار مزود يتيح استخدام الرمز تلقائيًا.",
"secretValue": "قيمة السر",
"secretValueHint": "سيتم تشفير هذه القيمة ولا يمكن عرضها مرة أخرى.",
"secretValueHintEdit": "اترك الحقل فارغًا للاحتفاظ بالقيمة الحالية.",
"secretValuePlaceholder": "أدخل مفتاح API الخاص بك",
"secretValuePlaceholderEdit": "أدخل قيمة جديدة للتغيير",
"title": "مفاتيح API والأسرار"
},
"selectionToolbox": {
"Bypass Group Nodes": "تجاوز عقد المجموعة",
"Set Group Nodes to Always": "تعيين عقد المجموعة إلى دائمًا",
@@ -2190,6 +2268,7 @@
"Reroute": "إعادة التوجيه",
"RerouteBeta": "إعادة توجيه بيتا",
"Scene": "المشهد",
"Secrets": "الأسرار",
"Server": "الخادم",
"Server-Config": "إعدادات الخادم",
"Settings": "الإعدادات",
@@ -2343,9 +2422,12 @@
},
"subgraphStore": {
"blueprintName": "اسم المخطط الفرعي",
"blueprintNamePrompt": "اسم المخطط الفرعي:",
"cannotDeleteGlobal": "لا يمكن حذف المخططات المثبتة",
"confirmDelete": "سيؤدي هذا الإجراء إلى إزالة المخطط نهائيًا من مكتبتك",
"confirmDeleteTitle": "حذف المخطط؟",
"enterDescription": "أدخل وصفًا",
"enterSearchAliases": "أدخل الأسماء المستعارة للبحث (مفصولة بفواصل)",
"hidden": "معاملات مخفية / متداخلة",
"hideAll": "إخفاء الكل",
"loadFailure": "فشل تحميل مخططات الرسم البياني الفرعي",
@@ -2356,6 +2438,7 @@
"publishSuccess": "تم الحفظ في مكتبة العقد",
"publishSuccessMessage": "يمكنك العثور على مخطط الرسم البياني الفرعي الخاص بك في مكتبة العقد ضمن \"مخططات الرسم البياني الفرعي\"",
"saveBlueprint": "احفظ المخطط الفرعي في المكتبة",
"searchAliases": "بحث عن الأسماء المستعارة",
"showAll": "إظهار الكل",
"showRecommended": "إظهار العناصر الموصى بها",
"shown": "معروض على العقدة"
@@ -2702,6 +2785,7 @@
},
"workflowService": {
"enterFilename": "أدخل اسم الملف",
"enterFilenamePrompt": "أدخل اسم الملف:",
"exportWorkflow": "تصدير سير العمل",
"saveWorkflow": "حفظ سير العمل"
},

View File

@@ -2018,6 +2018,20 @@
}
}
},
"CreateList": {
"display_name": "إنشاء قائمة",
"inputs": {
"inputs": {
"name": "المدخلات"
}
},
"outputs": {
"0": {
"name": "قائمة",
"tooltip": null
}
}
},
"CreateVideo": {
"description": "إنشاء فيديو من الصور.",
"display_name": "إنشاء فيديو",
@@ -2205,6 +2219,23 @@
}
}
},
"EmptyAceStep1_5LatentAudio": {
"display_name": "Empty Ace Step 1.5 Latent Audio",
"inputs": {
"batch_size": {
"name": "حجم الدفعة",
"tooltip": "عدد صور latent في الدفعة."
},
"seconds": {
"name": "ثواني"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"EmptyAceStepLatentAudio": {
"display_name": "خطوة الصوت الكامن الفارغ",
"inputs": {
@@ -3554,6 +3585,50 @@
}
}
},
"HitPawGeneralImageEnhance": {
"description": "تكبير الصور منخفضة الدقة إلى دقة فائقة، إزالة الشوائب والضوضاء. الحد الأقصى للإخراج: ٣٢ ميغابيكسل.",
"display_name": "تحسين الصورة العام من HitPaw",
"inputs": {
"auto_downscale": {
"name": "تصغير تلقائي",
"tooltip": "تصغير الصورة المدخلة تلقائياً إذا تجاوز الإخراج الحد المسموح."
},
"image": {
"name": "الصورة"
},
"model": {
"name": "النموذج"
},
"upscale_factor": {
"name": "عامل التكبير"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"HitPawVideoEnhance": {
"description": "تكبير مقاطع الفيديو منخفضة الدقة إلى دقة عالية، إزالة الشوائب والضوضاء. الأسعار لكل ثانية من الفيديو.",
"display_name": "تحسين الفيديو العام من HitPaw",
"inputs": {
"model": {
"name": "النموذج"
},
"model_resolution": {
"name": "الدقة"
},
"video": {
"name": "الفيديو"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Hunyuan3Dv2Conditioning": {
"display_name": "Hunyuan3Dv2التكييف",
"inputs": {
@@ -6357,7 +6432,7 @@
"Load3D": {
"display_name": "تحميل ثلاثي الأبعاد",
"inputs": {
"clear": {},
"clear": "مسح",
"height": {
"name": "الارتفاع"
},
@@ -6367,8 +6442,8 @@
"model_file": {
"name": "ملف النموذج"
},
"upload 3d model": {},
"upload extra resources": {},
"upload 3d model": "رفع نموذج ثلاثي الأبعاد",
"upload extra resources": "رفع موارد إضافية",
"width": {
"name": "العرض"
}
@@ -7252,12 +7327,17 @@
"name": "rig_task_id"
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
},
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyImageToModelNode": {
"display_name": "Meshy: من صورة إلى نموذج",
@@ -7303,16 +7383,18 @@
"name": "symmetry_mode"
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyMultiImageToModelNode": {
"display_name": "Meshy: من صور متعددة إلى نموذج",
@@ -7358,16 +7440,18 @@
"name": "symmetry_mode"
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyRefineNode": {
"description": "تحسين نموذج أولي تم إنشاؤه مسبقًا.",
@@ -7392,16 +7476,18 @@
"tooltip": "أدخل نصًا لتوجيه عملية الإكساء. الحد الأقصى ٦٠٠ حرف. لا يمكن استخدامه مع 'texture_image' في نفس الوقت."
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyRigModelNode": {
"description": "يوفر شخصية مجهزة بالحركة بصيغ قياسية. التجهيز التلقائي غير مناسب حاليًا للنماذج غير المكسوة، أو الأصول غير البشرية، أو الأصول البشرية ذات البنية غير الواضحة للأطراف والجسم.",
@@ -7419,16 +7505,18 @@
"tooltip": "صورة الإكساء الأساسية (UV-unwrapped) للنموذج."
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "rig_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyTextToModelNode": {
"display_name": "Meshy: تحويل النص إلى نموذج",
@@ -7467,16 +7555,18 @@
"name": "symmetry_mode"
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyTextureNode": {
"display_name": "Meshy: نموذج النسيج",
@@ -7503,16 +7593,18 @@
"tooltip": "صف نمط النسيج المطلوب للكائن باستخدام النص. الحد الأقصى ٦٠٠ حرف. لا يمكن استخدامه مع 'image_style' في نفس الوقت."
}
},
"outputs": {
"0": {
"name": "ملف النموذج",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "معرّف مهمة Meshy",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MinimaxHailuoVideoNode": {
"description": "ينشئ مقاطع فيديو من المُوجِّه، مع إطار أول اختياري باستخدام نموذج MiniMax Hailuo-02 الجديد.",
@@ -10709,11 +10801,9 @@
"camera_info": {
"name": "معلومات الكاميرا"
},
"image": {
"name": "الصورة"
},
"model_file": {
"name": "ملف النموذج"
"name": "ملف النموذج",
"tooltip": "ملف النموذج ثلاثي الأبعاد أو مسار الملف"
}
}
},
@@ -11354,6 +11444,23 @@
}
}
},
"ReferenceTimbreAudio": {
"description": "تحدد هذه العقدة الصوت المرجعي للـ timbre (لـ ace step 1.5)",
"display_name": "ReferenceTimbreAudio",
"inputs": {
"conditioning": {
"name": "تهيئة"
},
"latent": {
"name": "latent"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"RegexExtract": {
"display_name": "استخراج التعبير النمطي",
"inputs": {
@@ -11682,12 +11789,13 @@
"name": "البذرة"
}
},
"outputs": {
"0": {
"name": "مسار_النموذج_ثلاثي_الأبعاد",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Gen2": {
"description": "توليد أصول ثلاثية الأبعاد باستخدام واجهة برمجة تطبيقات رودين",
@@ -11709,12 +11817,13 @@
"name": "وضعية_TAP"
}
},
"outputs": {
"0": {
"name": "مسار النموذج ثلاثي الأبعاد",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Regular": {
"description": "توليد أصول ثلاثية الأبعاد باستخدام واجهة برمجة تطبيقات رودين",
@@ -11733,12 +11842,13 @@
"name": "البذرة"
}
},
"outputs": {
"0": {
"name": "مسار النموذج ثلاثي الأبعاد",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Sketch": {
"description": "توليد أصول ثلاثية الأبعاد باستخدام واجهة برمجة تطبيقات رودين",
@@ -11751,12 +11861,13 @@
"name": "البذرة"
}
},
"outputs": {
"0": {
"name": "مسار النموذج ثلاثي الأبعاد",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Smooth": {
"description": "توليد أصول ثلاثية الأبعاد باستخدام واجهة برمجة تطبيقات رودين",
@@ -11775,12 +11886,13 @@
"name": "البذرة"
}
},
"outputs": {
"0": {
"name": "مسار النموذج ثلاثي الأبعاد",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"RunwayFirstLastFrameNode": {
"description": "قم برفع الإطارات الرئيسية الأولى والأخيرة، واكتب موجهًا، وقم بتوليد فيديو. قد تستفيد التحولات الأكثر تعقيدًا، مثل الحالات التي يختلف فيها الإطار الأخير تمامًا عن الإطار الأول، من المدة الأطول البالغة 10 ثوانٍ. سيمنح هذا التوليد مزيدًا من الوقت للانتقال بسلاسة بين المدخلين. قبل البدء، راجع أفضل الممارسات هذه لضمان أن اختياراتك للمدخلات ستؤدي إلى نجاح التوليد: https://help.runwayml.com/hc/en-us/articles/34170748696595-Creating-with-Keyframes-on-Gen-3.",
@@ -12507,11 +12619,9 @@
"filename_prefix": {
"name": "بادئة اسم الملف"
},
"image": {
"name": "الصورة"
},
"mesh": {
"name": "الشبكة"
"name": "الشبكة",
"tooltip": "شبكة أو ملف GLB للحفظ"
}
}
},
@@ -13764,12 +13874,17 @@
"tooltip": "البذرة تتحكم فيما إذا كان يجب إعادة تشغيل العقدة؛ النتائج غير حتمية بغض النظر عن البذرة."
}
},
"outputs": {
"0": {
"name": "ملف النموذج",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
},
{
"name": "OBJ",
"tooltip": null
}
}
]
},
"TencentTextToModelNode": {
"display_name": "Hunyuan3D: من نص إلى نموذج (احترافي)",
@@ -13799,12 +13914,17 @@
"tooltip": "البذرة تتحكم فيما إذا كان يجب إعادة تشغيل العقدة؛ النتائج غير حتمية بغض النظر عن البذرة."
}
},
"outputs": {
"0": {
"name": "ملف النموذج",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
},
{
"name": "OBJ",
"tooltip": null
}
}
]
},
"TextEncodeAceStepAudio": {
"display_name": "TextEncodeAceStepAudio",
@@ -13828,6 +13948,62 @@
}
}
},
"TextEncodeAceStepAudio1_5": {
"display_name": "TextEncodeAceStepAudio1.5",
"inputs": {
"bpm": {
"name": "الإيقاع (BPM)"
},
"cfg_scale": {
"name": "cfg_scale"
},
"clip": {
"name": "clip"
},
"control_after_generate": {
"name": "التحكم بعد التوليد"
},
"duration": {
"name": "المدة"
},
"generate_audio_codes": {
"name": "توليد رموز الصوت",
"tooltip": "تفعيل نموذج اللغة الكبير (LLM) الذي يولد رموز الصوت. قد يكون بطيئًا لكنه سيزيد من جودة الصوت الناتج. قم بإيقاف هذا الخيار إذا كنت تقدم مرجعًا صوتيًا للنموذج."
},
"keyscale": {
"name": "المقام الموسيقي"
},
"language": {
"name": "اللغة"
},
"lyrics": {
"name": "كلمات الأغنية"
},
"seed": {
"name": "البذرة"
},
"tags": {
"name": "الوسوم"
},
"temperature": {
"name": "درجة الحرارة"
},
"timesignature": {
"name": "توقيع الإيقاع"
},
"top_k": {
"name": "top_k"
},
"top_p": {
"name": "top_p"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"TextEncodeHunyuanVideo_ImageToVideo": {
"display_name": "ترميز النص لفيديو Hunyuan - من صورة إلى فيديو",
"inputs": {
@@ -14383,16 +14559,14 @@
"name": "بذرة النسيج"
}
},
"outputs": {
"0": {
"name": "ملف النموذج",
"tooltip": null
},
"1": {
"name": "model task_id",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoMultiviewToModelNode": {
"display_name": "Tripo: متعدد المناظر إلى نموذج",
@@ -14444,16 +14618,14 @@
"name": "بذرة_الملمس"
}
},
"outputs": {
"0": {
"name": "ملف_النموذج",
"tooltip": null
},
"1": {
"name": "معرف_مهمة_النموذج",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoRefineNode": {
"description": "تحسين نموذج مسود تم إنشاؤه بواسطة نماذج Tripo الإصدار 1.4 فقط.",
@@ -14464,16 +14636,14 @@
"tooltip": "يجب أن يكون نموذج Tripo الإصدار 1.4"
}
},
"outputs": {
"0": {
"name": "ملف_النموذج",
"tooltip": null
},
"1": {
"name": "معرف_مهمة_النموذج",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoRetargetNode": {
"display_name": "Tripo: إعادة توجيه النموذج المجهز",
@@ -14485,16 +14655,14 @@
"name": "معرف_مهمة_النموذج_الأصلي"
}
},
"outputs": {
"0": {
"name": "ملف_النموذج",
"tooltip": null
},
"1": {
"name": "معرف_مهمة_إعادة_التوجيه",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoRigNode": {
"display_name": "Tripo: تجهيز النموذج",
@@ -14503,16 +14671,14 @@
"name": "معرف_مهمة_النموذج_الأصلي"
}
},
"outputs": {
"0": {
"name": "ملف_النموذج",
"tooltip": null
},
"1": {
"name": "معرف_مهمة_التجهيز",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoTextToModelNode": {
"display_name": "Tripo: النص إلى نموذج",
@@ -14557,16 +14723,14 @@
"name": "بذرة_الملمس"
}
},
"outputs": {
"0": {
"name": "ملف_النموذج",
"tooltip": null
},
"1": {
"name": "معرف_مهمة_النموذج",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoTextureNode": {
"display_name": "Tripo: نموذج الملمس",
@@ -14590,16 +14754,14 @@
"name": "بذرة_الملمس"
}
},
"outputs": {
"0": {
"name": "ملف_النموذج",
"tooltip": null
},
"1": {
"name": "معرف_مهمة_النموذج",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TruncateText": {
"display_name": "اقتطاع النص",
@@ -14768,6 +14930,28 @@
}
}
},
"VAEDecodeAudioTiled": {
"display_name": "فك ترميز الصوت بواسطة VAE (مجزأ)",
"inputs": {
"overlap": {
"name": "تداخل"
},
"samples": {
"name": "عينات"
},
"tile_size": {
"name": "حجم التجزئة"
},
"vae": {
"name": "vae"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"VAEDecodeHunyuan3D": {
"display_name": "فك ترميز VAE Hunyuan3D",
"inputs": {

View File

@@ -151,51 +151,6 @@
},
"tooltip": "يتحكم في مظهر ووضوح الروابط بين العقد على اللوحة."
},
"Comfy_Load3D_3DViewerEnable": {
"name": "تمكين عارض ثلاثي الأبعاد (تجريبي)",
"tooltip": "تمكين عارض ثلاثي الأبعاد (تجريبي) للعقد المحددة. تتيح هذه الميزة عرض النماذج ثلاثية الأبعاد والتفاعل معها مباشرة داخل العارض ثلاثي الأبعاد بحجمه الكامل."
},
"Comfy_Load3D_BackgroundColor": {
"name": "لون الخلفية الابتدائي",
"tooltip": "يحدد لون الخلفية الافتراضي للمشهد ثلاثي الأبعاد. يمكن تعديل هذا اللون لكل عنصر ثلاثي الأبعاد بعد الإنشاء."
},
"Comfy_Load3D_CameraType": {
"name": "نوع الكاميرا الابتدائي",
"options": {
"orthographic": "متعامد",
"perspective": "منظور"
},
"tooltip": "يحدد ما إذا كانت الكاميرا منظور أو متعامدة بشكل افتراضي عند إنشاء عنصر ثلاثي الأبعاد جديد. يمكن تعديل هذا الإعداد لكل عنصر بعد الإنشاء."
},
"Comfy_Load3D_LightAdjustmentIncrement": {
"name": "زيادة تعديل الضوء",
"tooltip": "يتحكم في حجم الخطوة عند تعديل شدة الإضاءة في المشاهد ثلاثية الأبعاد. قيمة أصغر تسمح بتحكم أدق، وأكبر قيمة تعطي تغييرات أكثر وضوحًا."
},
"Comfy_Load3D_LightIntensity": {
"name": "شدة الإضاءة الابتدائية",
"tooltip": "يحدد مستوى سطوع الإضاءة الافتراضي في المشهد ثلاثي الأبعاد. يمكن تعديله لكل عنصر بعد الإنشاء."
},
"Comfy_Load3D_LightIntensityMaximum": {
"name": "أقصى شدة إضاءة",
"tooltip": "يحدد الحد الأقصى المسموح به لشدة الإضاءة في المشاهد ثلاثية الأبعاد."
},
"Comfy_Load3D_LightIntensityMinimum": {
"name": "أدنى شدة إضاءة",
"tooltip": "يحدد الحد الأدنى المسموح به لشدة الإضاءة في المشاهد ثلاثية الأبعاد."
},
"Comfy_Load3D_PLYEngine": {
"name": "محرك PLY",
"options": {
"fastply": "fastply",
"sparkjs": "sparkjs",
"threejs": "threejs"
},
"tooltip": "اختر المحرك لتحميل ملفات PLY. \"threejs\" يستخدم PLYLoader الأصلي من Three.js (الأفضل لملفات PLY الشبكية). \"fastply\" يستخدم محمل محسن لملفات PLY السحابية النقطية بنسق ASCII. \"sparkjs\" يستخدم Spark.js لملفات PLY الخاصة بتوزيع Gaussian ثلاثي الأبعاد."
},
"Comfy_Load3D_ShowGrid": {
"name": "رؤية الشبكة الابتدائية",
"tooltip": "يتحكم في ظهور الشبكة بشكل افتراضي عند إنشاء عنصر ثلاثي الأبعاد جديد."
},
"Comfy_Locale": {
"name": "اللغة"
},
@@ -244,6 +199,10 @@
"Comfy_NodeBadge_ShowApiPricing": {
"name": "عرض شارة تسعير عقدة API"
},
"Comfy_NodeReplacement_Enabled": {
"name": "تفعيل الاستبدال التلقائي للعقد",
"tooltip": "عند التفعيل، يمكن استبدال العقد المفقودة تلقائيًا بنظيراتها الأحدث إذا كان هناك مخطط استبدال متوفر."
},
"Comfy_NodeSearchBoxImpl": {
"name": "تنفيذ مربع بحث العقدة",
"options": {

View File

@@ -35,9 +35,6 @@
"Comfy-Desktop_Restart": {
"label": "Restart"
},
"Comfy_3DViewer_Open3DViewer": {
"label": "Open 3D Viewer (Beta) for Selected Node"
},
"Comfy_BrowseModelAssets": {
"label": "Experimental: Browse Model Assets"
},
@@ -266,6 +263,12 @@
"Comfy_ShowSettingsDialog": {
"label": "Show Settings Dialog"
},
"Comfy_Subgraph_SetDescription": {
"label": "Set Subgraph Description"
},
"Comfy_Subgraph_SetSearchAliases": {
"label": "Set Subgraph Search Aliases"
},
"Comfy_ToggleAssetAPI": {
"label": "Experimental: Enable AssetAPI"
},
@@ -276,7 +279,7 @@
"label": "Help Center"
},
"Comfy_ToggleLinear": {
"label": "Toggle Simple Mode"
"label": "Toggle App Mode"
},
"Comfy_ToggleQPOV2": {
"label": "Toggle Queue Panel V2"
@@ -314,12 +317,6 @@
"Workspace_ToggleBottomPanel_Shortcuts": {
"label": "Show Keybindings Dialog"
},
"Workspace_ToggleBottomPanelTab_command-terminal": {
"label": "Toggle Terminal Bottom Panel"
},
"Workspace_ToggleBottomPanelTab_logs-terminal": {
"label": "Toggle Logs Bottom Panel"
},
"Workspace_ToggleBottomPanelTab_shortcuts-essentials": {
"label": "Toggle Essential Bottom Panel"
},

View File

@@ -1,5 +1,6 @@
{
"g": {
"shortcutSuffix": " ({shortcut})",
"user": "User",
"you": "You",
"currentUser": "Current user",
@@ -9,6 +10,7 @@
"download": "Download",
"downloadImage": "Download image",
"downloadVideo": "Download video",
"downloadAudio": "Download audio",
"editOrMaskImage": "Edit or mask image",
"editImage": "Edit image",
"decrement": "Decrement",
@@ -136,7 +138,8 @@
"searchKeybindings": "Search Keybindings",
"searchExtensions": "Search Extensions",
"search": "Search",
"searchPlaceholder": "Search...",
"searchPlaceholder": "Search {subject}...",
"downloadWithSize": "Download ({size})",
"noResultsFound": "No Results Found",
"noResults": "No Results",
"searchFailedMessage": "We couldn't find any settings matching your search. Try adjusting your search terms.",
@@ -152,6 +155,8 @@
"custom": "Custom",
"command": "Command",
"keybinding": "Keybinding",
"keybindings": "Keybindings",
"extensions": "Extensions",
"upload": "Upload",
"export": "Export",
"workflow": "Workflow",
@@ -192,6 +197,7 @@
"missing": "Missing",
"inProgress": "In progress",
"completed": "Completed",
"completedWithCheckmark": "Completed ✓",
"downloading": "Downloading",
"interrupted": "Interrupted",
"queued": "Queued",
@@ -255,6 +261,7 @@
"batchRename": "Batch rename",
"enterBaseName": "Enter base name",
"enterNewName": "Enter new name",
"enterNewNamePrompt": "Enter new name:",
"selectItemsToRename": "Select items to rename",
"nothingToRename": "Nothing to rename",
"moreWorkflows": "More workflows",
@@ -1003,6 +1010,7 @@
"workflowService": {
"exportWorkflow": "Export Workflow",
"enterFilename": "Enter the filename",
"enterFilenamePrompt": "Enter the filename:",
"saveWorkflow": "Save workflow"
},
"subgraphStore": {
@@ -1012,6 +1020,7 @@
"overwriteBlueprintTitle": "Overwrite existing blueprint?",
"overwriteBlueprint": "Saving will overwrite the current blueprint with your changes",
"blueprintName": "Subgraph name",
"blueprintNamePrompt": "Subgraph name:",
"promoteOutsideSubgraph": "Can't promote widget when not in subgraph",
"publish": "Publish Subgraph",
"publishSuccess": "Saved to Nodes Library",
@@ -1022,7 +1031,10 @@
"hidden": "Hidden / nested parameters",
"hideAll": "Hide all",
"showRecommended": "Show recommended widgets",
"cannotDeleteGlobal": "Cannot delete installed blueprints"
"cannotDeleteGlobal": "Cannot delete installed blueprints",
"enterDescription": "Enter a description",
"searchAliases": "Search Aliases",
"enterSearchAliases": "Enter search aliases (comma separated)"
},
"electronFileDownload": {
"inProgress": "In Progress",
@@ -1163,7 +1175,6 @@
"Quit": "Quit",
"Reinstall": "Reinstall",
"Restart": "Restart",
"Open 3D Viewer (Beta) for Selected Node": "Open 3D Viewer (Beta) for Selected Node",
"Experimental: Browse Model Assets": "Experimental: Browse Model Assets",
"Browse Templates": "Browse Templates",
"Delete Selected Items": "Delete Selected Items",
@@ -1240,10 +1251,12 @@
"Save": "Save",
"Save As": "Save As",
"Show Settings Dialog": "Show Settings Dialog",
"Set Subgraph Description": "Set Subgraph Description",
"Set Subgraph Search Aliases": "Set Subgraph Search Aliases",
"Experimental: Enable AssetAPI": "Experimental: Enable AssetAPI",
"Canvas Performance": "Canvas Performance",
"Help Center": "Help Center",
"Toggle Simple Mode": "Toggle Simple Mode",
"Toggle App Mode": "Toggle App Mode",
"Toggle Queue Panel V2": "Toggle Queue Panel V2",
"Toggle Theme (Dark/Light)": "Toggle Theme (Dark/Light)",
"Undo": "Undo",
@@ -1256,8 +1269,6 @@
"Toggle Search Box": "Toggle Search Box",
"Bottom Panel": "Bottom Panel",
"Show Keybindings Dialog": "Show Keybindings Dialog",
"Toggle Terminal Bottom Panel": "Toggle Terminal Bottom Panel",
"Toggle Logs Bottom Panel": "Toggle Logs Bottom Panel",
"Toggle Essential Bottom Panel": "Toggle Essential Bottom Panel",
"Toggle View Controls Bottom Panel": "Toggle View Controls Bottom Panel",
"Focus Mode": "Focus Mode",
@@ -1521,6 +1532,7 @@
"video_models": "video_models",
"gligen": "gligen",
"Grok": "Grok",
"HitPaw": "HitPaw",
"sd": "sd",
"Ideogram": "Ideogram",
"postprocessing": "postprocessing",
@@ -1589,6 +1601,13 @@
"COMFY_MATCHTYPE_V3": "COMFY_MATCHTYPE_V3",
"CONDITIONING": "CONDITIONING",
"CONTROL_NET": "CONTROL_NET",
"FILE_3D": "FILE_3D",
"FILE_3D_FBX": "FILE_3D_FBX",
"FILE_3D_GLB": "FILE_3D_GLB",
"FILE_3D_GLTF": "FILE_3D_GLTF",
"FILE_3D_OBJ": "FILE_3D_OBJ",
"FILE_3D_STL": "FILE_3D_STL",
"FILE_3D_USDZ": "FILE_3D_USDZ",
"FLOAT": "FLOAT",
"FLOATS": "FLOATS",
"GEMINI_INPUT_FILES": "GEMINI_INPUT_FILES",
@@ -2757,8 +2776,8 @@
"message": "Switch back to Nodes 2.0 anytime from the main menu."
},
"linearMode": {
"linearMode": "Simple Mode",
"beta": "Simple Mode in Beta - Feedback",
"linearMode": "App Mode",
"beta": "App Mode in Beta - Feedback",
"graphMode": "Graph Mode",
"dragAndDropImage": "Click to browse or drag an image",
"runCount": "Run count:",
@@ -2837,6 +2856,7 @@
"groupSettings": "Group Settings",
"groups": "Groups",
"favoritesNoneDesc": "Inputs you favorite will show up here",
"favoritesNoneHint": "In the Parameters tab, click {moreIcon} on any input to add it here",
"noneSearchDesc": "No items match your search",
"nodesNoneDesc": "NO NODES",
"fallbackGroupTitle": "Group",

View File

@@ -2020,6 +2020,20 @@
}
}
},
"CreateList": {
"display_name": "Create List",
"inputs": {
"inputs": {
"name": "inputs"
}
},
"outputs": {
"0": {
"name": "list",
"tooltip": null
}
}
},
"CreateVideo": {
"display_name": "Create Video",
"description": "Create a video from images.",
@@ -2209,8 +2223,25 @@
}
}
},
"EmptyAceStep1_5LatentAudio": {
"display_name": "Empty Ace Step 1.5 Latent Audio",
"inputs": {
"seconds": {
"name": "seconds"
},
"batch_size": {
"name": "batch_size",
"tooltip": "The number of latent images in the batch."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"EmptyAceStepLatentAudio": {
"display_name": "EmptyAceStepLatentAudio",
"display_name": "Empty Ace Step 1.0 Latent Audio",
"inputs": {
"seconds": {
"name": "seconds"
@@ -3561,6 +3592,50 @@
}
}
},
"HitPawGeneralImageEnhance": {
"display_name": "HitPaw General Image Enhance",
"description": "Upscale low-resolution images to super-resolution, eliminate artifacts and noise. Maximum output: 32 megapixels.",
"inputs": {
"model": {
"name": "model"
},
"image": {
"name": "image"
},
"upscale_factor": {
"name": "upscale_factor"
},
"auto_downscale": {
"name": "auto_downscale",
"tooltip": "Automatically downscale input image if output would exceed the limit."
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"HitPawVideoEnhance": {
"display_name": "HitPaw Video Enhance",
"description": "Upscale low-resolution videos to high resolution, eliminate artifacts and noise. Prices shown are per second of video.",
"inputs": {
"model": {
"name": "model"
},
"video": {
"name": "video"
},
"model_resolution": {
"name": "resolution"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Hunyuan3Dv2Conditioning": {
"display_name": "Hunyuan3Dv2Conditioning",
"inputs": {
@@ -6043,6 +6118,10 @@
"5": {
"name": "recording_video",
"tooltip": null
},
"6": {
"name": "model_3d",
"tooltip": null
}
}
},
@@ -7274,6 +7353,14 @@
"0": {
"name": "model_file",
"tooltip": null
},
"1": {
"name": "GLB",
"tooltip": null
},
"2": {
"name": "FBX",
"tooltip": null
}
}
},
@@ -7329,6 +7416,14 @@
"1": {
"name": "meshy_task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
},
"3": {
"name": "FBX",
"tooltip": null
}
}
},
@@ -7384,6 +7479,14 @@
"1": {
"name": "meshy_task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
},
"3": {
"name": "FBX",
"tooltip": null
}
}
},
@@ -7418,6 +7521,14 @@
"1": {
"name": "meshy_task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
},
"3": {
"name": "FBX",
"tooltip": null
}
}
},
@@ -7445,6 +7556,14 @@
"1": {
"name": "rig_task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
},
"3": {
"name": "FBX",
"tooltip": null
}
}
},
@@ -7493,6 +7612,14 @@
"1": {
"name": "meshy_task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
},
"3": {
"name": "FBX",
"tooltip": null
}
}
},
@@ -7529,6 +7656,14 @@
"1": {
"name": "meshy_task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
},
"3": {
"name": "FBX",
"tooltip": null
}
}
},
@@ -10722,16 +10857,14 @@
"display_name": "Preview 3D & Animation",
"inputs": {
"model_file": {
"name": "model_file"
"name": "model_file",
"tooltip": "3D model file or path string"
},
"camera_info": {
"name": "camera_info"
},
"bg_image": {
"name": "bg_image"
},
"image": {
"name": "image"
}
}
},
@@ -11372,6 +11505,23 @@
}
}
},
"ReferenceTimbreAudio": {
"display_name": "Reference Audio",
"description": "This node sets the reference audio for ace step 1.5",
"inputs": {
"conditioning": {
"name": "conditioning"
},
"latent": {
"name": "latent"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"RegexExtract": {
"display_name": "Regex Extract",
"inputs": {
@@ -11704,6 +11854,10 @@
"0": {
"name": "3D Model Path",
"tooltip": null
},
"1": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -11731,6 +11885,10 @@
"0": {
"name": "3D Model Path",
"tooltip": null
},
"1": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -11755,6 +11913,10 @@
"0": {
"name": "3D Model Path",
"tooltip": null
},
"1": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -11773,6 +11935,10 @@
"0": {
"name": "3D Model Path",
"tooltip": null
},
"1": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -11797,6 +11963,10 @@
"0": {
"name": "3D Model Path",
"tooltip": null
},
"1": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -12389,16 +12559,14 @@
}
},
"SaveGLB": {
"display_name": "SaveGLB",
"display_name": "Save 3D Model",
"inputs": {
"mesh": {
"name": "mesh"
"name": "mesh",
"tooltip": "Mesh or 3D file to save"
},
"filename_prefix": {
"name": "filename_prefix"
},
"image": {
"name": "image"
}
}
},
@@ -13797,6 +13965,14 @@
"0": {
"name": "model_file",
"tooltip": null
},
"1": {
"name": "GLB",
"tooltip": null
},
"2": {
"name": "OBJ",
"tooltip": null
}
}
},
@@ -13832,6 +14008,14 @@
"0": {
"name": "model_file",
"tooltip": null
},
"1": {
"name": "GLB",
"tooltip": null
},
"2": {
"name": "OBJ",
"tooltip": null
}
}
},
@@ -13857,6 +14041,62 @@
}
}
},
"TextEncodeAceStepAudio1_5": {
"display_name": "TextEncodeAceStepAudio1.5",
"inputs": {
"clip": {
"name": "clip"
},
"tags": {
"name": "tags"
},
"lyrics": {
"name": "lyrics"
},
"seed": {
"name": "seed"
},
"bpm": {
"name": "bpm"
},
"duration": {
"name": "duration"
},
"timesignature": {
"name": "timesignature"
},
"language": {
"name": "language"
},
"keyscale": {
"name": "keyscale"
},
"generate_audio_codes": {
"name": "generate_audio_codes",
"tooltip": "Enable the LLM that generates audio codes. This can be slow but will increase the quality of the generated audio. Turn this off if you are giving the model an audio reference."
},
"cfg_scale": {
"name": "cfg_scale"
},
"temperature": {
"name": "temperature"
},
"top_p": {
"name": "top_p"
},
"top_k": {
"name": "top_k"
},
"control_after_generate": {
"name": "control after generate"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"TextEncodeHunyuanVideo_ImageToVideo": {
"display_name": "TextEncodeHunyuanVideo_ImageToVideo",
"inputs": {
@@ -14420,6 +14660,10 @@
"1": {
"name": "model task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -14481,6 +14725,10 @@
"1": {
"name": "model task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -14501,6 +14749,10 @@
"1": {
"name": "model task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -14522,6 +14774,10 @@
"1": {
"name": "retarget task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -14540,6 +14796,10 @@
"1": {
"name": "rig task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -14594,6 +14854,10 @@
"1": {
"name": "model task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -14627,6 +14891,10 @@
"1": {
"name": "model task_id",
"tooltip": null
},
"2": {
"name": "GLB",
"tooltip": null
}
}
},
@@ -14822,6 +15090,28 @@
}
}
},
"VAEDecodeAudioTiled": {
"display_name": "VAE Decode Audio (Tiled)",
"inputs": {
"samples": {
"name": "samples"
},
"vae": {
"name": "vae"
},
"tile_size": {
"name": "tile_size"
},
"overlap": {
"name": "overlap"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"VAEDecodeHunyuan3D": {
"display_name": "VAEDecodeHunyuan3D",
"inputs": {

View File

@@ -151,51 +151,6 @@
"Hidden": "Hidden"
}
},
"Comfy_Load3D_3DViewerEnable": {
"name": "Enable 3D Viewer (Beta)",
"tooltip": "Enables the 3D Viewer (Beta) for selected nodes. This feature allows you to visualize and interact with 3D models directly within the full size 3d viewer."
},
"Comfy_Load3D_BackgroundColor": {
"name": "Initial Background Color",
"tooltip": "Controls the default background color of the 3D scene. This setting determines the background appearance when a new 3D widget is created, but can be adjusted individually for each widget after creation."
},
"Comfy_Load3D_CameraType": {
"name": "Initial Camera Type",
"tooltip": "Controls whether the camera is perspective or orthographic by default when a new 3D widget is created. This default can still be toggled individually for each widget after creation.",
"options": {
"perspective": "perspective",
"orthographic": "orthographic"
}
},
"Comfy_Load3D_LightAdjustmentIncrement": {
"name": "Light Adjustment Increment",
"tooltip": "Controls the increment size when adjusting light intensity in 3D scenes. A smaller step value allows for finer control over lighting adjustments, while a larger value results in more noticeable changes per adjustment."
},
"Comfy_Load3D_LightIntensity": {
"name": "Initial Light Intensity",
"tooltip": "Sets the default brightness level of lighting in the 3D scene. This value determines how intensely lights illuminate objects when a new 3D widget is created, but can be adjusted individually for each widget after creation."
},
"Comfy_Load3D_LightIntensityMaximum": {
"name": "Light Intensity Maximum",
"tooltip": "Sets the maximum allowable light intensity value for 3D scenes. This defines the upper brightness limit that can be set when adjusting lighting in any 3D widget."
},
"Comfy_Load3D_LightIntensityMinimum": {
"name": "Light Intensity Minimum",
"tooltip": "Sets the minimum allowable light intensity value for 3D scenes. This defines the lower brightness limit that can be set when adjusting lighting in any 3D widget."
},
"Comfy_Load3D_PLYEngine": {
"name": "PLY Engine",
"tooltip": "Select the engine for loading PLY files. \"threejs\" uses the native Three.js PLYLoader (best for mesh PLY files). \"fastply\" uses an optimized loader for ASCII point cloud PLY files. \"sparkjs\" uses Spark.js for 3D Gaussian Splatting PLY files.",
"options": {
"threejs": "threejs",
"fastply": "fastply",
"sparkjs": "sparkjs"
}
},
"Comfy_Load3D_ShowGrid": {
"name": "Initial Grid Visibility",
"tooltip": "Controls whether the grid is visible by default when a new 3D widget is created. This default can still be toggled individually for each widget after creation."
},
"Comfy_Locale": {
"name": "Language"
},
@@ -280,6 +235,10 @@
"Comfy_NodeBadge_ShowApiPricing": {
"name": "Show API node pricing badge"
},
"Comfy_NodeReplacement_Enabled": {
"name": "Enable automatic node replacement",
"tooltip": "When enabled, missing nodes can be automatically replaced with their newer equivalents if a replacement mapping exists."
},
"Comfy_NodeSearchBoxImpl": {
"name": "Node search box implementation",
"options": {

View File

@@ -35,9 +35,6 @@
"Comfy-Desktop_Restart": {
"label": "Reiniciar"
},
"Comfy_3DViewer_Open3DViewer": {
"label": "Abrir visor 3D (Beta) para el nodo seleccionado"
},
"Comfy_BrowseModelAssets": {
"label": "Experimental: Explorar recursos de modelos"
},
@@ -266,6 +263,12 @@
"Comfy_ShowSettingsDialog": {
"label": "Mostrar Diálogo de Configuraciones"
},
"Comfy_Subgraph_SetDescription": {
"label": "Establecer descripción del subgrafo"
},
"Comfy_Subgraph_SetSearchAliases": {
"label": "Establecer alias de búsqueda del subgrafo"
},
"Comfy_ToggleAssetAPI": {
"label": "Experimental: Habilitar AssetAPI"
},
@@ -311,12 +314,6 @@
"Workspace_ToggleBottomPanel": {
"label": "Alternar Panel Inferior"
},
"Workspace_ToggleBottomPanelTab_command-terminal": {
"label": "Alternar Panel Inferior de Terminal"
},
"Workspace_ToggleBottomPanelTab_logs-terminal": {
"label": "Alternar Panel Inferior de Registros"
},
"Workspace_ToggleBottomPanelTab_shortcuts-essentials": {
"label": "Alternar panel inferior esencial"
},

View File

@@ -16,6 +16,8 @@
"assetBrowser": {
"allCategory": "Todo {category}",
"allModels": "Todos los modelos",
"apiKeyHint": "¿Importas modelos privados o restringidos? {link}.",
"apiKeyHintLink": "Agrega tus claves de API en Configuración",
"ariaLabel": {
"assetCard": "Recurso {name} - {type}",
"loadingAsset": "Cargando recurso"
@@ -51,13 +53,34 @@
"canImport": "Aún no hay modelos importados. Haz clic en \"Importar modelo\" para añadir el tuyo.",
"restricted": "Los modelos personales solo están disponibles en el nivel Creador o superior."
},
"errorAccessForbidden": "Acceso prohibido a este recurso.",
"errorConnectionRefused": "No se puede conectar a la fuente. Por favor, inténtalo de nuevo más tarde.",
"errorDownloadCancelled": "La descarga fue cancelada.",
"errorFileTooLarge": "El archivo excede el tamaño máximo permitido",
"errorFormatNotAllowed": "Solo se permite el formato SafeTensor",
"errorHttpError": "Ocurrió un error al obtener los metadatos.",
"errorInternalError": "Ocurrió un error inesperado. Por favor, inténtalo de nuevo.",
"errorInvalidHost": "No se pudo resolver el nombre de host de la URL de origen.",
"errorInvalidUrl": "Por favor, proporciona una URL.",
"errorInvalidUrlFormat": "El formato de la URL no es válido. Por favor, revisa e inténtalo de nuevo.",
"errorMetadataFetchFailed": "No se pudo obtener la información del archivo desde la fuente.",
"errorModelTypeNotSupported": "Este tipo de modelo no es compatible",
"errorNetworkError": "Ocurrió un error de red. Por favor, revisa tu conexión e inténtalo de nuevo.",
"errorNetworkTimeout": "La solicitud ha expirado. Por favor, inténtalo de nuevo.",
"errorRateLimited": "Demasiadas solicitudes. Por favor, inténtalo de nuevo en unos minutos.",
"errorRequestCancelled": "La solicitud fue cancelada.",
"errorResourceNotFound": "No se encontró el archivo. Por favor, revisa la URL e inténtalo de nuevo.",
"errorServiceUnavailable": "Servicio temporalmente no disponible. Por favor, inténtalo de nuevo más tarde.",
"errorSourceServerError": "El servidor de origen está experimentando problemas. Por favor, inténtalo de nuevo más tarde.",
"errorUnauthorized": "Por favor, inicia sesión para continuar.",
"errorUnauthorizedSource": "Este recurso requiere autenticación. Añade tu token de API en la configuración.",
"errorUnknown": "Ocurrió un error inesperado",
"errorUnsafePickleScan": "CivitAI detectó código potencialmente inseguro en este archivo",
"errorUnsafeVirusScan": "CivitAI detectó malware o contenido sospechoso en este archivo",
"errorUnsupportedSource": "Esta URL no es compatible. Solo se permiten URLs de Hugging Face y Civitai.",
"errorUploadFailed": "No se pudo importar el activo. Por favor, inténtalo de nuevo.",
"errorUserTokenAccessDenied": "Tu token de API no tiene acceso a este recurso. Por favor, revisa los permisos de tu token.",
"errorUserTokenInvalid": "Tu token de API almacenado no es válido o ha expirado. Por favor, actualiza tu token en la configuración.",
"failedToCreateNode": "No se pudo crear el nodo. Inténtalo de nuevo o revisa la consola para más detalles.",
"fileFormats": "Formatos de archivo",
"fileName": "Nombre del archivo",
@@ -581,6 +604,13 @@
"COMFY_MATCHTYPE_V3": "COMFY_MATCHTYPE_V3",
"CONDITIONING": "ACONDICIONAMIENTO",
"CONTROL_NET": "RED_DE_CONTROL",
"FILE_3D": "ARCHIVO_3D",
"FILE_3D_FBX": "ARCHIVO_3D_FBX",
"FILE_3D_GLB": "ARCHIVO_3D_GLB",
"FILE_3D_GLTF": "ARCHIVO_3D_GLTF",
"FILE_3D_OBJ": "ARCHIVO_3D_OBJ",
"FILE_3D_STL": "ARCHIVO_3D_STL",
"FILE_3D_USDZ": "ARCHIVO_3D_USDZ",
"FLOAT": "FLOTANTE",
"FLOATS": "FLOTANTES",
"GEMINI_INPUT_FILES": "ARCHIVOS_ENTRADA_GEMINI",
@@ -721,6 +751,7 @@
"commandProhibited": "El comando {command} está prohibido. Contacta a un administrador para más información.",
"community": "Comunidad",
"completed": "Completado",
"completedWithCheckmark": "Completado ✓",
"confirm": "Confirmar",
"confirmed": "Confirmado",
"content": "contenido",
@@ -756,6 +787,7 @@
"download": "Descargar",
"downloadImage": "Descargar imagen",
"downloadVideo": "Descargar video",
"downloadWithSize": "Descargar ({size})",
"downloading": "Descargando",
"dropYourFileOr": "Suelta tu archivo o",
"duplicate": "Duplicar",
@@ -771,12 +803,14 @@
"enabling": "Habilitando",
"enterBaseName": "Introduce el nombre base",
"enterNewName": "Introduce el nuevo nombre",
"enterNewNamePrompt": "Introduce un nuevo nombre:",
"error": "Error",
"errorLoadingImage": "Error al cargar imagen",
"errorLoadingVideo": "Error al cargar video",
"experimental": "BETA",
"export": "Exportar",
"extensionName": "Nombre de la extensión",
"extensions": "Extensiones",
"failed": "Fallido",
"failedToCopyJobId": "Error al copiar el ID de trabajo",
"failedToDownloadImage": "Falló la descarga de imagen",
@@ -815,6 +849,7 @@
"jobIdCopied": "ID de trabajo copiado al portapapeles",
"keybinding": "Combinación de teclas",
"keybindingAlreadyExists": "La combinación de teclas ya existe en",
"keybindings": "Atajos de teclado",
"learnMore": "Aprende más",
"listening": "Escuchando...",
"liveSamplingPreview": "Vista previa de muestreo en vivo",
@@ -926,6 +961,7 @@
"selectedFile": "Archivo seleccionado",
"setAsBackground": "Establecer como fondo",
"settings": "Configuraciones",
"shortcutSuffix": " ({shortcut})",
"showLeftPanel": "Mostrar panel izquierdo",
"showReport": "Mostrar informe",
"showRightPanel": "Mostrar panel derecho",
@@ -1027,8 +1063,12 @@
},
"imageCrop": {
"cropPreviewAlt": "Vista previa del recorte",
"custom": "Personalizado",
"loading": "Cargando...",
"noInputImage": "No hay imagen de entrada conectada"
"lockRatio": "Bloquear relación de aspecto",
"noInputImage": "No hay imagen de entrada conectada",
"ratio": "Relación",
"unlockRatio": "Desbloquear relación de aspecto"
},
"importFailed": {
"copyError": "Error al copiar",
@@ -1152,14 +1192,21 @@
"helpFix": "Ayuda a Solucionar Esto"
},
"linearMode": {
"beta": "Beta - Enviar comentarios",
"beta": "Modo App Beta - Enviar comentarios",
"downloadAll": "Descargar todo",
"dragAndDropImage": "Arrastra y suelta una imagen",
"graphMode": "Modo gráfico",
"linearMode": "Modo simple",
"linearMode": "Modo App",
"rerun": "Volver a ejecutar",
"reuseParameters": "Reutilizar parámetros",
"runCount": "Número de ejecuciones:"
"runCount": "Número de ejecuciones:",
"welcome": {
"intro": "Una vista simplificada que oculta el grafo de nodos para que puedas concentrarte en crear.",
"layout": "A la izquierda, verás tus imágenes, videos y resultados generados. A la derecha, solo los controles necesarios. Todo lo complejo queda fuera de la vista.",
"sharing": "Compartir es fácil: crea tu flujo de trabajo, abre el Modo App, haz clic derecho en la pestaña y exporta. Cuando otros abran tu archivo, se lanzará directamente en esta vista limpia. Puedes compartir flujos de trabajo potentes como herramientas simples sin que nadie tenga que entender grafos de nodos.",
"title": "Bienvenido al Modo App",
"widget": "Si quieres controlar qué ajustes aparecen, convierte tus nodos principales en un subgrafo y luego usa la promoción de widgets en la barra de herramientas sobre él para elegir qué se expone."
}
},
"load3d": {
"applyingTexture": "Aplicando textura...",
@@ -1610,7 +1657,6 @@
"Node Library": "Biblioteca de Nodos",
"Node Links": "Enlaces de nodos",
"Open": "Abrir",
"Open 3D Viewer (Beta) for Selected Node": "Abrir Visor 3D (Beta) para Nodo Seleccionado",
"Open Color Picker in MaskEditor": "Abrir selector de color en MaskEditor",
"Open Custom Nodes Folder": "Abrir carpeta de nodos personalizados",
"Open DevTools": "Abrir herramientas de desarrollo",
@@ -1640,16 +1686,16 @@
"Rotate Right in MaskEditor": "Girar a la derecha en el editor de máscaras",
"Save": "Guardar",
"Save As": "Guardar como",
"Set Subgraph Description": "Establecer descripción del subgrafo",
"Set Subgraph Search Aliases": "Establecer alias de búsqueda del subgrafo",
"Show Keybindings Dialog": "Mostrar diálogo de combinaciones de teclas",
"Show Model Selector (Dev)": "Mostrar selector de modelo (Desarrollo)",
"Show Settings Dialog": "Mostrar diálogo de configuración",
"Sign Out": "Cerrar sesión",
"Toggle App Mode": "Alternar modo App",
"Toggle Essential Bottom Panel": "Alternar panel inferior esencial",
"Toggle Logs Bottom Panel": "Alternar panel inferior de registros",
"Toggle Queue Panel V2": "Alternar panel de cola V2",
"Toggle Search Box": "Alternar caja de búsqueda",
"Toggle Simple Mode": "Alternar modo simple",
"Toggle Terminal Bottom Panel": "Alternar panel inferior de terminal",
"Toggle Theme (Dark/Light)": "Alternar tema (Oscuro/Claro)",
"Toggle View Controls Bottom Panel": "Alternar panel inferior de controles de vista",
"Toggle promotion of hovered widget": "Alternar promoción del widget sobre el que se pasa el cursor",
@@ -1707,6 +1753,7 @@
"ByteDance": "ByteDance",
"Gemini": "Gemini",
"Grok": "Grok",
"HitPaw": "HitPaw",
"Ideogram": "Ideogram",
"Kling": "Kling",
"LTXV": "LTXV",
@@ -1928,6 +1975,7 @@
"favorites": "ENTRADAS FAVORITAS",
"favoritesNone": "SIN ENTRADAS FAVORITAS",
"favoritesNoneDesc": "Las entradas que marques como favoritas aparecerán aquí",
"favoritesNoneHint": "En la pestaña Parámetros, haz clic en {moreIcon} en cualquier entrada para añadirla aquí",
"favoritesNoneTooltip": "Marca widgets con estrella para acceder rápidamente sin seleccionar nodos",
"globalSettings": {
"canvas": "LIENZO",
@@ -1973,6 +2021,36 @@
"togglePanel": "Mostrar/ocultar panel de propiedades",
"workflowOverview": "Resumen del flujo de trabajo"
},
"secrets": {
"addSecret": "Agregar secreto",
"createdAt": "Creado el {date}",
"deleteConfirmMessage": "¿Estás seguro de que deseas eliminar \"{name}\"? Esta acción no se puede deshacer.",
"deleteConfirmTitle": "Eliminar secreto",
"description": "Los secretos están cifrados y se utilizan para datos sensibles como claves API.",
"descriptionUsage": "Guarda tus tokens aquí para habilitar la descarga de modelos privados y restringidos de proveedores compatibles.",
"editSecret": "Editar secreto",
"errors": {
"duplicateName": "Ya existe un secreto con este nombre",
"duplicateProvider": "Ya existe un secreto para este proveedor",
"nameRequired": "El nombre es obligatorio",
"nameTooLong": "El nombre debe tener 255 caracteres o menos",
"providerRequired": "El proveedor es obligatorio",
"secretValueRequired": "El valor secreto es obligatorio"
},
"lastUsed": "Último uso el {date}",
"modelProviders": "Proveedores de modelos",
"name": "Nombre",
"namePlaceholder": "p. ej., Mi clave API",
"noSecrets": "No hay secretos guardados. Agrega tu primera clave API para comenzar.",
"provider": "Proveedor",
"providerHint": "Opcional. Seleccionar un proveedor habilita el uso automático del token.",
"secretValue": "Valor secreto",
"secretValueHint": "Este valor será cifrado y no podrá ser visto nuevamente.",
"secretValueHintEdit": "Déjalo en blanco para mantener el valor actual.",
"secretValuePlaceholder": "Introduce tu clave API",
"secretValuePlaceholderEdit": "Introduce un nuevo valor para cambiarlo",
"title": "Claves API y Secretos"
},
"selectionToolbox": {
"Bypass Group Nodes": "Omitir nodos de grupo",
"Set Group Nodes to Always": "Establecer nodos de grupo en Siempre",
@@ -2190,6 +2268,7 @@
"Reroute": "Reenrutar",
"RerouteBeta": "Reroute Beta",
"Scene": "Escena",
"Secrets": "Credenciales",
"Server": "Servidor",
"Server-Config": "Configuración del Servidor",
"Settings": "Configuraciones",
@@ -2343,9 +2422,12 @@
},
"subgraphStore": {
"blueprintName": "Nombre del subgrafo",
"blueprintNamePrompt": "Nombre del subgrafo:",
"cannotDeleteGlobal": "No se pueden eliminar los blueprints instalados",
"confirmDelete": "Esta acción eliminará permanentemente el subgrafo de tu biblioteca",
"confirmDeleteTitle": "¿Eliminar subgrafo?",
"enterDescription": "Introduce una descripción",
"enterSearchAliases": "Introduce alias de búsqueda (separados por comas)",
"hidden": "Parámetros ocultos/anidados",
"hideAll": "Ocultar todo",
"loadFailure": "No se pudieron cargar los subgrafos",
@@ -2356,6 +2438,7 @@
"publishSuccess": "Guardado en la biblioteca de nodos",
"publishSuccessMessage": "Puedes encontrar tu subgrafo en la biblioteca de nodos bajo \"Subgraph Blueprints\"",
"saveBlueprint": "Guardar subgrafo en la biblioteca",
"searchAliases": "Buscar alias",
"showAll": "Mostrar todo",
"showRecommended": "Mostrar widgets recomendados",
"shown": "Mostrado en el nodo"
@@ -2702,6 +2785,7 @@
},
"workflowService": {
"enterFilename": "Introduzca el nombre del archivo",
"enterFilenamePrompt": "Introduce el nombre del archivo:",
"exportWorkflow": "Exportar flujo de trabajo",
"saveWorkflow": "Guardar flujo de trabajo"
},

View File

@@ -2018,6 +2018,20 @@
}
}
},
"CreateList": {
"display_name": "Crear lista",
"inputs": {
"inputs": {
"name": "entradas"
}
},
"outputs": {
"0": {
"name": "lista",
"tooltip": null
}
}
},
"CreateVideo": {
"description": "Crea un video a partir de imágenes.",
"display_name": "Crear video",
@@ -2205,6 +2219,23 @@
}
}
},
"EmptyAceStep1_5LatentAudio": {
"display_name": "Empty Ace Step 1.5 Latent Audio",
"inputs": {
"batch_size": {
"name": "tamaño de lote",
"tooltip": "El número de imágenes latentes en el lote."
},
"seconds": {
"name": "segundos"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"EmptyAceStepLatentAudio": {
"display_name": "EmptyAceStepLatentAudio",
"inputs": {
@@ -3554,6 +3585,50 @@
}
}
},
"HitPawGeneralImageEnhance": {
"description": "Aumenta la resolución de imágenes de baja calidad a superresolución, elimina artefactos y ruido. Salida máxima: 32 megapíxeles.",
"display_name": "HitPaw General Image Enhance",
"inputs": {
"auto_downscale": {
"name": "reducción automática",
"tooltip": "Reduce automáticamente la imagen de entrada si la salida supera el límite."
},
"image": {
"name": "imagen"
},
"model": {
"name": "modelo"
},
"upscale_factor": {
"name": "factor de escalado"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"HitPawVideoEnhance": {
"description": "Aumenta la resolución de videos de baja calidad a alta resolución, elimina artefactos y ruido. Los precios mostrados son por segundo de video.",
"display_name": "HitPaw Video Enhance",
"inputs": {
"model": {
"name": "modelo"
},
"model_resolution": {
"name": "resolución"
},
"video": {
"name": "video"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Hunyuan3Dv2Conditioning": {
"display_name": "Hunyuan3Dv2Conditioning",
"inputs": {
@@ -6367,8 +6442,12 @@
"model_file": {
"name": "archivo_modelo"
},
"upload 3d model": {},
"upload extra resources": {},
"upload 3d model": {
"es": "Subir modelo 3D"
},
"upload extra resources": {
"es": "Subir recursos adicionales"
},
"width": {
"name": "ancho"
}
@@ -7252,12 +7331,17 @@
"name": "rig_task_id"
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
},
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyImageToModelNode": {
"display_name": "Meshy: Imagen a Modelo",
@@ -7303,16 +7387,18 @@
"name": "modo de simetría"
}
},
"outputs": {
"0": {
"name": "archivo_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyMultiImageToModelNode": {
"display_name": "Meshy: Multi-Imagen a Modelo",
@@ -7358,16 +7444,18 @@
"name": "modo de simetría"
}
},
"outputs": {
"0": {
"name": "archivo_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyRefineNode": {
"description": "Refina un modelo borrador previamente creado.",
@@ -7392,16 +7480,18 @@
"tooltip": "Proporcione un texto para guiar el proceso de texturizado. Máximo 600 caracteres. No se puede usar al mismo tiempo que 'texture_image'."
}
},
"outputs": {
"0": {
"name": "archivo_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyRigModelNode": {
"description": "Proporciona un personaje riggeado en formatos estándar. El auto-rigging actualmente no es adecuado para mallas sin textura, activos no humanoides o activos humanoides con estructura de extremidades y cuerpo poco clara.",
@@ -7419,16 +7509,18 @@
"tooltip": "Imagen de textura de color base UV-desplegada del modelo."
}
},
"outputs": {
"0": {
"name": "archivo_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "rig_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyTextToModelNode": {
"display_name": "Meshy: Texto a Modelo",
@@ -7467,16 +7559,18 @@
"name": "modo_simetría"
}
},
"outputs": {
"0": {
"name": "archivo_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyTextureNode": {
"display_name": "Meshy: Modelo de Textura",
@@ -7503,16 +7597,18 @@
"tooltip": "Describe el estilo de textura deseado para el objeto usando texto. Máximo 600 caracteres. No se puede usar al mismo tiempo que 'image_style'."
}
},
"outputs": {
"0": {
"name": "archivo_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MinimaxHailuoVideoNode": {
"description": "Genera videos a partir de un prompt, con opción de usar un fotograma inicial utilizando el nuevo modelo MiniMax Hailuo-02.",
@@ -10709,11 +10805,9 @@
"camera_info": {
"name": "camera_info"
},
"image": {
"name": "imagen"
},
"model_file": {
"name": "archivo_modelo"
"name": "archivo_modelo",
"tooltip": "Archivo de modelo 3D o ruta del archivo"
}
}
},
@@ -11354,6 +11448,23 @@
}
}
},
"ReferenceTimbreAudio": {
"description": "Este nodo establece el audio de referencia para el timbre (para ace step 1.5)",
"display_name": "ReferenceTimbreAudio",
"inputs": {
"conditioning": {
"name": "condicionamiento"
},
"latent": {
"name": "latente"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"RegexExtract": {
"display_name": "Extracción Regex",
"inputs": {
@@ -11682,12 +11793,13 @@
"name": "Semilla"
}
},
"outputs": {
"0": {
"name": "Ruta Modelo 3D",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Gen2": {
"description": "Generar activos 3D usando la API de Rodin",
@@ -11709,12 +11821,13 @@
"name": "TAPose"
}
},
"outputs": {
"0": {
"name": "Ruta Modelo 3D",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Regular": {
"description": "Generar activos 3D usando la API de Rodin",
@@ -11733,12 +11846,13 @@
"name": "Semilla"
}
},
"outputs": {
"0": {
"name": "Ruta Modelo 3D",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Sketch": {
"description": "Generar activos 3D usando la API de Rodin",
@@ -11751,12 +11865,13 @@
"name": "Semilla"
}
},
"outputs": {
"0": {
"name": "Ruta del modelo 3D",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Smooth": {
"description": "Generar activos 3D usando la API de Rodin",
@@ -11775,12 +11890,13 @@
"name": "Semilla"
}
},
"outputs": {
"0": {
"name": "Ruta del modelo 3D",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"RunwayFirstLastFrameNode": {
"description": "Sube los primeros y últimos fotogramas clave, redacta un prompt y genera un video. Las transiciones más complejas, como casos donde el último fotograma es completamente diferente del primero, pueden beneficiarse de la duración más larga de 10s. Esto le daría a la generación más tiempo para transicionar suavemente entre las dos entradas. Antes de comenzar, revisa estas mejores prácticas para asegurar que tus selecciones de entrada preparen tu generación para el éxito: https://help.runwayml.com/hc/en-us/articles/34170748696595-Creating-with-Keyframes-on-Gen-3.",
@@ -12507,11 +12623,9 @@
"filename_prefix": {
"name": "prefijo_nombre_archivo"
},
"image": {
"name": "imagen"
},
"mesh": {
"name": "malla"
"name": "malla",
"tooltip": "Malla o archivo GLB para guardar"
}
}
},
@@ -13764,12 +13878,17 @@
"tooltip": "La semilla controla si el nodo debe volver a ejecutarse; los resultados son no deterministas independientemente de la semilla."
}
},
"outputs": {
"0": {
"name": "archivo_modelo",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
},
{
"name": "OBJ",
"tooltip": null
}
}
]
},
"TencentTextToModelNode": {
"display_name": "Hunyuan3D: Texto a Modelo (Pro)",
@@ -13799,12 +13918,17 @@
"tooltip": "La semilla controla si el nodo debe volver a ejecutarse; los resultados son no deterministas independientemente de la semilla."
}
},
"outputs": {
"0": {
"name": "archivo_modelo",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
},
{
"name": "OBJ",
"tooltip": null
}
}
]
},
"TextEncodeAceStepAudio": {
"display_name": "TextEncodeAceStepAudio",
@@ -13828,6 +13952,62 @@
}
}
},
"TextEncodeAceStepAudio1_5": {
"display_name": "TextEncodeAceStepAudio1.5",
"inputs": {
"bpm": {
"name": "bpm"
},
"cfg_scale": {
"name": "cfg_scale"
},
"clip": {
"name": "clip"
},
"control_after_generate": {
"name": "controlar después de generar"
},
"duration": {
"name": "duración"
},
"generate_audio_codes": {
"name": "generar_códigos_de_audio",
"tooltip": "Activa el LLM que genera códigos de audio. Esto puede ser lento, pero aumentará la calidad del audio generado. Desactívalo si proporcionas una referencia de audio al modelo."
},
"keyscale": {
"name": "escala tonal"
},
"language": {
"name": "idioma"
},
"lyrics": {
"name": "letra"
},
"seed": {
"name": "semilla"
},
"tags": {
"name": "etiquetas"
},
"temperature": {
"name": "temperatura"
},
"timesignature": {
"name": "compás"
},
"top_k": {
"name": "top_k"
},
"top_p": {
"name": "top_p"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"TextEncodeHunyuanVideo_ImageToVideo": {
"display_name": "TextEncodeHunyuanVideo_ImagenAVideo",
"inputs": {
@@ -14383,16 +14563,14 @@
"name": "semilla_textura"
}
},
"outputs": {
"0": {
"name": "archivo_de_modelo",
"tooltip": null
},
"1": {
"name": "ID_de_tarea_del_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoMultiviewToModelNode": {
"display_name": "Tripo: Multivista a Modelo",
@@ -14444,16 +14622,14 @@
"name": "semilla_de_textura"
}
},
"outputs": {
"0": {
"name": "archivo_de_modelo",
"tooltip": null
},
"1": {
"name": "ID_de_tarea_del_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoRefineNode": {
"description": "Refina un modelo borrador creado únicamente por modelos Tripo v1.4.",
@@ -14464,16 +14640,14 @@
"tooltip": "Debe ser un modelo Tripo v1.4"
}
},
"outputs": {
"0": {
"name": "archivo_de_modelo",
"tooltip": null
},
"1": {
"name": "ID_de_tarea_del_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoRetargetNode": {
"display_name": "Tripo: Redireccionar modelo con rig",
@@ -14485,16 +14659,14 @@
"name": "ID_de_tarea_del_modelo_original"
}
},
"outputs": {
"0": {
"name": "archivo_de_modelo",
"tooltip": null
},
"1": {
"name": "ID_de_tarea_de_redireccionamiento",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoRigNode": {
"display_name": "Tripo: Modelo con rig",
@@ -14503,16 +14675,14 @@
"name": "ID_de_tarea_del_modelo_original"
}
},
"outputs": {
"0": {
"name": "archivo_de_modelo",
"tooltip": null
},
"1": {
"name": "ID_de_tarea_de_rig",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoTextToModelNode": {
"display_name": "Tripo: Texto a Modelo",
@@ -14557,16 +14727,14 @@
"name": "semilla_de_textura"
}
},
"outputs": {
"0": {
"name": "archivo_del_modelo",
"tooltip": null
},
"1": {
"name": "tarea_del_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoTextureNode": {
"display_name": "Tripo: Modelo de textura",
@@ -14590,16 +14758,14 @@
"name": "semilla_de_textura"
}
},
"outputs": {
"0": {
"name": "archivo_del_modelo",
"tooltip": null
},
"1": {
"name": "tarea_del_modelo",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TruncateText": {
"display_name": "Truncar texto",
@@ -14768,6 +14934,28 @@
}
}
},
"VAEDecodeAudioTiled": {
"display_name": "VAE Decodificar audio (en mosaico)",
"inputs": {
"overlap": {
"name": "superposición"
},
"samples": {
"name": "muestras"
},
"tile_size": {
"name": "tamaño_de_mosaico"
},
"vae": {
"name": "vae"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"VAEDecodeHunyuan3D": {
"display_name": "VAEDecodeHunyuan3D",
"inputs": {

View File

@@ -151,51 +151,6 @@
},
"tooltip": "Controla la apariencia y visibilidad de los enlaces de conexión entre nodos en el lienzo."
},
"Comfy_Load3D_3DViewerEnable": {
"name": "Habilitar visor 3D (Beta)",
"tooltip": "Activa el visor 3D (Beta) para los nodos seleccionados. Esta función te permite visualizar e interactuar con modelos 3D directamente dentro del visor 3D a tamaño completo."
},
"Comfy_Load3D_BackgroundColor": {
"name": "Color de fondo inicial",
"tooltip": "Controla el color de fondo predeterminado de la escena 3D. Esta configuración determina la apariencia del fondo cuando se crea un nuevo widget 3D, pero puede ajustarse individualmente para cada widget después de su creación."
},
"Comfy_Load3D_CameraType": {
"name": "Tipo de Cámara",
"options": {
"orthographic": "ortográfica",
"perspective": "perspectiva"
},
"tooltip": "Controla si la cámara es perspectiva u ortográfica por defecto cuando se crea un nuevo widget 3D. Este valor predeterminado aún puede ser alternado individualmente para cada widget después de su creación."
},
"Comfy_Load3D_LightAdjustmentIncrement": {
"name": "Incremento de ajuste de luz",
"tooltip": "Controla el tamaño del incremento al ajustar la intensidad de la luz en escenas 3D. Un valor de paso más pequeño permite un control más preciso sobre los ajustes de iluminación, mientras que un valor más grande resulta en cambios más notorios por cada ajuste."
},
"Comfy_Load3D_LightIntensity": {
"name": "Intensidad Inicial de la Luz",
"tooltip": "Establece el nivel de brillo predeterminado de la iluminación en la escena 3D. Este valor determina cuán intensamente las luces iluminan los objetos cuando se crea un nuevo widget 3D, pero puede ajustarse individualmente para cada widget después de la creación."
},
"Comfy_Load3D_LightIntensityMaximum": {
"name": "Intensidad Máxima de Luz",
"tooltip": "Establece el valor máximo permitido de intensidad de luz para escenas 3D. Esto define el límite superior de brillo que se puede ajustar al modificar la iluminación en cualquier widget 3D."
},
"Comfy_Load3D_LightIntensityMinimum": {
"name": "Intensidad de luz mínima",
"tooltip": "Establece el valor mínimo permitido de intensidad de luz para escenas 3D. Esto define el límite inferior de brillo que se puede ajustar al modificar la iluminación en cualquier widget 3D."
},
"Comfy_Load3D_PLYEngine": {
"name": "Motor PLY",
"options": {
"fastply": "fastply",
"sparkjs": "sparkjs",
"threejs": "threejs"
},
"tooltip": "Selecciona el motor para cargar archivos PLY. \"threejs\" utiliza el cargador nativo Three.js PLYLoader (mejor para archivos PLY de malla). \"fastply\" utiliza un cargador optimizado para archivos PLY de nube de puntos ASCII. \"sparkjs\" utiliza Spark.js para archivos PLY de Gaussian Splatting 3D."
},
"Comfy_Load3D_ShowGrid": {
"name": "Mostrar Cuadrícula",
"tooltip": "Cambiar para mostrar cuadrícula por defecto"
},
"Comfy_Locale": {
"name": "Idioma"
},
@@ -244,6 +199,10 @@
"Comfy_NodeBadge_ShowApiPricing": {
"name": "Mostrar insignia de precios de nodo API"
},
"Comfy_NodeReplacement_Enabled": {
"name": "Habilitar reemplazo automático de nodos",
"tooltip": "Cuando está habilitado, los nodos faltantes pueden ser reemplazados automáticamente por sus equivalentes más nuevos si existe un mapeo de reemplazo."
},
"Comfy_NodeSearchBoxImpl": {
"name": "Implementación de la caja de búsqueda de nodos",
"options": {

View File

@@ -35,9 +35,6 @@
"Comfy-Desktop_Restart": {
"label": "راه‌اندازی مجدد"
},
"Comfy_3DViewer_Open3DViewer": {
"label": "باز کردن ۳D Viewer (بتا) برای node انتخاب‌شده"
},
"Comfy_BrowseModelAssets": {
"label": "آزمایشی: مرور Model Assets"
},
@@ -266,6 +263,12 @@
"Comfy_ShowSettingsDialog": {
"label": "نمایش پنجره تنظیمات"
},
"Comfy_Subgraph_SetDescription": {
"label": "تنظیم توضیحات زیرگراف"
},
"Comfy_Subgraph_SetSearchAliases": {
"label": "تنظیم نام‌های جایگزین جستجوی زیرگراف"
},
"Comfy_ToggleAssetAPI": {
"label": "آزمایشی: فعال‌سازی AssetAPI"
},
@@ -311,12 +314,6 @@
"Workspace_ToggleBottomPanel": {
"label": "تغییر پنل پایین"
},
"Workspace_ToggleBottomPanelTab_command-terminal": {
"label": "تغییر پنل ترمینال پایین"
},
"Workspace_ToggleBottomPanelTab_logs-terminal": {
"label": "تغییر پنل گزارش‌ها پایین"
},
"Workspace_ToggleBottomPanelTab_shortcuts-essentials": {
"label": "تغییر پنل ضروریات پایین"
},

View File

@@ -16,6 +16,8 @@
"assetBrowser": {
"allCategory": "همه {category}",
"allModels": "همه مدل‌ها",
"apiKeyHint": "مدل‌های خصوصی یا محدود وارد می‌کنید؟ {link}.",
"apiKeyHintLink": "کلیدهای API خود را در تنظیمات اضافه کنید",
"ariaLabel": {
"assetCard": "{name} - دارایی {type}",
"loadingAsset": "در حال بارگذاری دارایی"
@@ -51,13 +53,34 @@
"canImport": "هنوز مدلی وارد نشده است. برای افزودن مدل خود، روی «وارد کردن مدل» کلیک کنید.",
"restricted": "مدل‌های شخصی فقط برای سطح Creator و بالاتر در دسترس هستند."
},
"errorAccessForbidden": "دسترسی به این منبع مجاز نیست.",
"errorConnectionRefused": "اتصال به منبع برقرار نشد. لطفاً بعداً دوباره تلاش کنید.",
"errorDownloadCancelled": "دانلود لغو شد.",
"errorFileTooLarge": "فایل از حداکثر اندازه مجاز بزرگ‌تر است",
"errorFormatNotAllowed": "فقط فرمت SafeTensor مجاز است",
"errorHttpError": "هنگام دریافت اطلاعات خطایی رخ داد.",
"errorInternalError": "یک خطای غیرمنتظره رخ داد. لطفاً دوباره تلاش کنید.",
"errorInvalidHost": "نام میزبان URL منبع قابل شناسایی نیست.",
"errorInvalidUrl": "لطفاً یک URL وارد کنید.",
"errorInvalidUrlFormat": "فرمت URL نامعتبر است. لطفاً بررسی و دوباره تلاش کنید.",
"errorMetadataFetchFailed": "دریافت اطلاعات فایل از منبع با شکست مواجه شد.",
"errorModelTypeNotSupported": "این نوع مدل پشتیبانی نمی‌شود",
"errorNetworkError": "یک خطای شبکه رخ داد. لطفاً اتصال خود را بررسی و دوباره تلاش کنید.",
"errorNetworkTimeout": "درخواست زمان‌بر شد. لطفاً دوباره تلاش کنید.",
"errorRateLimited": "درخواست‌های بیش از حد. لطفاً چند دقیقه دیگر دوباره تلاش کنید.",
"errorRequestCancelled": "درخواست لغو شد.",
"errorResourceNotFound": "فایل پیدا نشد. لطفاً URL را بررسی و دوباره تلاش کنید.",
"errorServiceUnavailable": "سرویس موقتاً در دسترس نیست. لطفاً بعداً دوباره تلاش کنید.",
"errorSourceServerError": "سرور منبع با مشکل مواجه است. لطفاً بعداً دوباره تلاش کنید.",
"errorUnauthorized": "لطفاً برای ادامه وارد شوید.",
"errorUnauthorizedSource": "این منبع نیاز به احراز هویت دارد. لطفاً توکن API خود را در تنظیمات وارد کنید.",
"errorUnknown": "خطای غیرمنتظره‌ای رخ داد",
"errorUnsafePickleScan": "CivitAI کد بالقوه ناامن را در این فایل شناسایی کرد",
"errorUnsafeVirusScan": "CivitAI بدافزار یا محتوای مشکوک را در این فایل شناسایی کرد",
"errorUnsupportedSource": "این URL پشتیبانی نمی‌شود. فقط URLهای Hugging Face و Civitai مجاز هستند.",
"errorUploadFailed": "وارد کردن دارایی ناموفق بود. لطفاً دوباره تلاش کنید.",
"errorUserTokenAccessDenied": "توکن API شما به این منبع دسترسی ندارد. لطفاً مجوزهای توکن خود را بررسی کنید.",
"errorUserTokenInvalid": "توکن API ذخیره‌شده شما نامعتبر یا منقضی شده است. لطفاً توکن خود را در تنظیمات به‌روزرسانی کنید.",
"failedToCreateNode": "ایجاد node ناموفق بود. لطفاً دوباره تلاش کنید یا کنسول را بررسی کنید.",
"fileFormats": "فرمت‌های فایل",
"fileName": "نام فایل",
@@ -581,6 +604,13 @@
"COMFY_MATCHTYPE_V3": "Comfy MatchType V3",
"CONDITIONING": "شرط‌گذاری",
"CONTROL_NET": "controlnet",
"FILE_3D": "FILE_3D",
"FILE_3D_FBX": "FILE_3D_FBX",
"FILE_3D_GLB": "FILE_3D_GLB",
"FILE_3D_GLTF": "FILE_3D_GLTF",
"FILE_3D_OBJ": "FILE_3D_OBJ",
"FILE_3D_STL": "FILE_3D_STL",
"FILE_3D_USDZ": "FILE_3D_USDZ",
"FLOAT": "عدد اعشاری",
"FLOATS": "اعداد اعشاری",
"GEMINI_INPUT_FILES": "فایل‌های ورودی Gemini",
@@ -721,6 +751,7 @@
"commandProhibited": "دستور {command} مجاز نیست. برای اطلاعات بیشتر با مدیر تماس بگیرید.",
"community": "انجمن",
"completed": "تکمیل شده",
"completedWithCheckmark": "تکمیل شد ✓",
"confirm": "تأیید",
"confirmed": "تأیید شد",
"content": "محتوا",
@@ -756,6 +787,7 @@
"download": "دانلود",
"downloadImage": "دانلود تصویر",
"downloadVideo": "دانلود ویدیو",
"downloadWithSize": "دانلود ({size})",
"downloading": "در حال دانلود",
"dropYourFileOr": "فایل خود را رها کنید یا",
"duplicate": "تکراری",
@@ -771,12 +803,14 @@
"enabling": "در حال فعال‌سازی {id}",
"enterBaseName": "نام پایه را وارد کنید",
"enterNewName": "نام جدید را وارد کنید",
"enterNewNamePrompt": "نام جدید را وارد کنید:",
"error": "خطا",
"errorLoadingImage": "خطا در بارگذاری تصویر",
"errorLoadingVideo": "خطا در بارگذاری ویدیو",
"experimental": "آزمایشی",
"export": "خروجی گرفتن",
"extensionName": "نام افزونه",
"extensions": "افزونه‌ها",
"failed": "ناموفق",
"failedToCopyJobId": "کپی شناسه وظیفه ناموفق بود",
"failedToDownloadImage": "دانلود تصویر ناموفق بود",
@@ -815,6 +849,7 @@
"jobIdCopied": "شناسه وظیفه در کلیپ‌بورد کپی شد",
"keybinding": "کلید میانبر",
"keybindingAlreadyExists": "کلید میانبر قبلاً وجود دارد در",
"keybindings": "کلیدهای میانبر",
"learnMore": "اطلاعات بیشتر",
"listening": "در حال گوش دادن...",
"liveSamplingPreview": "پیش‌نمایش زنده نمونه‌گیری",
@@ -926,6 +961,7 @@
"selectedFile": "فایل انتخاب‌شده",
"setAsBackground": "تنظیم به عنوان پس‌زمینه",
"settings": "تنظیمات",
"shortcutSuffix": " ({shortcut})",
"showLeftPanel": "نمایش پنل چپ",
"showReport": "نمایش گزارش",
"showRightPanel": "نمایش پنل راست",
@@ -1027,8 +1063,12 @@
},
"imageCrop": {
"cropPreviewAlt": "پیش‌نمایش برش",
"custom": "سفارشی",
"loading": "در حال بارگذاری...",
"noInputImage": "هیچ تصویر ورودی متصل نیست"
"lockRatio": "قفل نسبت تصویر",
"noInputImage": "هیچ تصویر ورودی متصل نیست",
"ratio": "نسبت",
"unlockRatio": "باز کردن قفل نسبت تصویر"
},
"importFailed": {
"copyError": "خطا در کپی",
@@ -1152,14 +1192,21 @@
"helpFix": "کمک به رفع این مشکل"
},
"linearMode": {
"beta": "بتا - ارسال بازخورد",
"beta": "حالت برنامه بتا - ارسال بازخورد",
"downloadAll": "دانلود همه",
"dragAndDropImage": "تصویر را بکشید و رها کنید",
"graphMode": "حالت گراف",
"linearMode": "حالت ساده",
"linearMode": "حالت برنامه",
"rerun": "اجرای مجدد",
"reuseParameters": "استفاده مجدد از پارامترها",
"runCount": "تعداد اجرا: "
"runCount": "تعداد اجرا: ",
"welcome": {
"intro": "نمایی ساده‌شده که گراف node را پنهان می‌کند تا بتوانید بر خلق تمرکز کنید.",
"layout": "در سمت چپ، تصاویر، ویدیوها و خروجی‌های تولیدشده خود را می‌بینید. در سمت راست، فقط کنترل‌های مورد نیاز شما قرار دارند. همه چیزهای پیچیده از دید پنهان می‌مانند.",
"sharing": "اشتراک‌گذاری آسان است: workflow خود را بسازید، حالت App را باز کنید، روی تب راست‌کلیک کنید و خروجی بگیرید. وقتی دیگران فایل شما را باز می‌کنند، مستقیماً وارد این نمای ساده می‌شوند. می‌توانید workflowهای قدرتمند را به ابزارهای ساده تبدیل و به اشتراک بگذارید بدون اینکه کسی نیاز به درک گراف node داشته باشد.",
"title": "به حالت App خوش آمدید",
"widget": "اگر می‌خواهید کنترل کنید کدام تنظیمات نمایش داده شوند، nodeهای سطح بالای خود را به یک subgraph تبدیل کنید، سپس با استفاده از ابزارک promotion در جعبه‌ابزار بالای آن، موارد قابل نمایش را انتخاب کنید."
}
},
"load3d": {
"applyingTexture": "در حال اعمال تکسچر...",
@@ -1610,7 +1657,6 @@
"Node Library": "کتابخانه Node",
"Node Links": "پیوندهای Node",
"Open": "باز کردن",
"Open 3D Viewer (Beta) for Selected Node": "باز کردن نمایشگر سه‌بعدی (بتا) برای Node انتخاب‌شده",
"Open Color Picker in MaskEditor": "باز کردن انتخاب‌گر رنگ در MaskEditor",
"Open Custom Nodes Folder": "باز کردن پوشه Custom Nodes",
"Open DevTools": "باز کردن DevTools",
@@ -1640,16 +1686,16 @@
"Rotate Right in MaskEditor": "چرخش به راست در MaskEditor",
"Save": "ذخیره",
"Save As": "ذخیره به عنوان",
"Set Subgraph Description": "تنظیم توضیح زیرگراف",
"Set Subgraph Search Aliases": "تنظیم نام‌های مستعار جستجوی زیرگراف",
"Show Keybindings Dialog": "نمایش پنجره کلیدهای میانبر",
"Show Model Selector (Dev)": "نمایش انتخاب‌گر مدل (توسعه‌دهنده)",
"Show Settings Dialog": "نمایش پنجره تنظیمات",
"Sign Out": "خروج از حساب",
"Toggle App Mode": "تغییر حالت برنامه",
"Toggle Essential Bottom Panel": "تغییر وضعیت پنل ضروری پایین",
"Toggle Logs Bottom Panel": "تغییر وضعیت پنل لاگ پایین",
"Toggle Queue Panel V2": "تغییر وضعیت پنل صف V2",
"Toggle Search Box": "تغییر وضعیت جعبه جستجو",
"Toggle Simple Mode": "تغییر حالت ساده",
"Toggle Terminal Bottom Panel": "تغییر وضعیت پنل ترمینال پایین",
"Toggle Theme (Dark/Light)": "تغییر وضعیت تم (تاریک/روشن)",
"Toggle View Controls Bottom Panel": "تغییر وضعیت کنترل‌های نمای پایین",
"Toggle promotion of hovered widget": "تغییر وضعیت ارتقاء ابزارک زیر نشانگر",
@@ -1707,6 +1753,7 @@
"ByteDance": "ByteDance",
"Gemini": "Gemini",
"Grok": "Grok",
"HitPaw": "HitPaw",
"Ideogram": "Ideogram",
"Kling": "Kling",
"LTXV": "LTXV",
@@ -1928,6 +1975,7 @@
"favorites": "ورودی‌های علاقه‌مندی",
"favoritesNone": "هیچ ورودی علاقه‌مندی وجود ندارد",
"favoritesNoneDesc": "ورودی‌هایی که به علاقه‌مندی‌ها اضافه کنید اینجا نمایش داده می‌شوند",
"favoritesNoneHint": "در تب پارامترها، روی {moreIcon} هر ورودی کلیک کنید تا اینجا اضافه شود",
"favoritesNoneTooltip": "برای دسترسی سریع، ویجت‌ها را ستاره‌دار کنید تا بدون انتخاب nodeها به آن‌ها دسترسی داشته باشید",
"globalSettings": {
"canvas": "canvas",
@@ -1973,6 +2021,36 @@
"togglePanel": "نمایش/پنهان کردن پنل ویژگی‌ها",
"workflowOverview": "نمای کلی workflow"
},
"secrets": {
"addSecret": "افزودن کلید محرمانه",
"createdAt": "ایجاد شده در {date}",
"deleteConfirmMessage": "آیا مطمئن هستید که می‌خواهید «{name}» را حذف کنید؟ این عملیات قابل بازگشت نیست.",
"deleteConfirmTitle": "حذف کلید محرمانه",
"description": "اسرار به صورت رمزگذاری‌شده ذخیره می‌شوند و برای داده‌های حساس مانند کلیدهای API استفاده می‌شوند.",
"descriptionUsage": "توکن‌های خود را اینجا ذخیره کنید تا دانلود مدل‌های خصوصی و محدود از ارائه‌دهندگان پشتیبانی‌شده فعال شود.",
"editSecret": "ویرایش کلید محرمانه",
"errors": {
"duplicateName": "کلیدی با این نام قبلاً وجود دارد",
"duplicateProvider": "کلیدی برای این ارائه‌دهنده قبلاً وجود دارد",
"nameRequired": "وارد کردن نام الزامی است",
"nameTooLong": "نام باید حداکثر ۲۵۵ کاراکتر باشد",
"providerRequired": "وارد کردن ارائه‌دهنده الزامی است",
"secretValueRequired": "وارد کردن مقدار کلید محرمانه الزامی است"
},
"lastUsed": "آخرین استفاده {date}",
"modelProviders": "ارائه‌دهندگان مدل",
"name": "نام",
"namePlaceholder": "مثلاً، کلید API من",
"noSecrets": "هیچ کلید محرمانه‌ای ذخیره نشده است. اولین کلید API خود را برای شروع اضافه کنید.",
"provider": "ارائه‌دهنده",
"providerHint": "اختیاری. انتخاب ارائه‌دهنده باعث فعال‌سازی استفاده خودکار از توکن می‌شود.",
"secretValue": "مقدار کلید محرمانه",
"secretValueHint": "این مقدار رمزگذاری می‌شود و دیگر قابل مشاهده نخواهد بود.",
"secretValueHintEdit": "برای حفظ مقدار فعلی، این قسمت را خالی بگذارید.",
"secretValuePlaceholder": "کلید API خود را وارد کنید",
"secretValuePlaceholderEdit": "برای تغییر مقدار جدید را وارد کنید",
"title": "کلیدها و اسرار API"
},
"selectionToolbox": {
"Bypass Group Nodes": "عبور از نودهای گروه",
"Set Group Nodes to Always": "تنظیم نودهای گروه روی همیشه",
@@ -2190,6 +2268,7 @@
"Reroute": "مسیر مجدد",
"RerouteBeta": "مسیر مجدد (بتا)",
"Scene": "صحنه",
"Secrets": "اسرار",
"Server": "سرور",
"Server-Config": "پیکربندی سرور",
"Settings": "تنظیمات",
@@ -2354,9 +2433,12 @@
},
"subgraphStore": {
"blueprintName": "نام زیرگراف",
"blueprintNamePrompt": "نام Subgraph:",
"cannotDeleteGlobal": "امکان حذف blueprints نصب‌شده وجود ندارد",
"confirmDelete": "این عمل باعث حذف دائمی بلوپرینت از کتابخانه شما می‌شود",
"confirmDeleteTitle": "حذف بلوپرینت؟",
"enterDescription": "توضیحی وارد کنید",
"enterSearchAliases": "نام‌های مستعار جستجو را وارد کنید (با ویرگول جدا کنید)",
"hidden": "پارامترهای مخفی / تو در تو",
"hideAll": "مخفی‌سازی همه",
"loadFailure": "بارگذاری بلوپرینت‌های زیرگراف ناموفق بود",
@@ -2367,6 +2449,7 @@
"publishSuccess": "در کتابخانه گره‌ها ذخیره شد",
"publishSuccessMessage": "می‌توانید بلوپرینت زیرگراف خود را در کتابخانه گره‌ها در بخش \"بلوپرینت‌های زیرگراف\" پیدا کنید",
"saveBlueprint": "ذخیره زیرگراف در کتابخانه",
"searchAliases": "جستجوی نام‌های مستعار",
"showAll": "نمایش همه",
"showRecommended": "نمایش ویجت‌های پیشنهادی",
"shown": "نمایش روی گره"
@@ -2713,6 +2796,7 @@
},
"workflowService": {
"enterFilename": "نام فایل را وارد کنید",
"enterFilenamePrompt": "نام فایل را وارد کنید:",
"exportWorkflow": "خروجی گرفتن از workflow",
"saveWorkflow": "ذخیره workflow"
},

View File

@@ -2020,6 +2020,20 @@
}
}
},
"CreateList": {
"display_name": "ایجاد لیست",
"inputs": {
"inputs": {
"name": "ورودی‌ها"
}
},
"outputs": {
"0": {
"name": "لیست",
"tooltip": null
}
}
},
"CreateVideo": {
"description": "ایجاد ویدیو از تصاویر.",
"display_name": "ایجاد ویدیو",
@@ -2207,6 +2221,23 @@
}
}
},
"EmptyAceStep1_5LatentAudio": {
"display_name": "Empty Ace Step 1.5 Latent Audio",
"inputs": {
"batch_size": {
"name": "اندازه دسته",
"tooltip": "تعداد تصاویر latent در هر دسته."
},
"seconds": {
"name": "ثانیه"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"EmptyAceStepLatentAudio": {
"display_name": "EmptyAceStepLatentAudio",
"inputs": {
@@ -3559,6 +3590,50 @@
}
}
},
"HitPawGeneralImageEnhance": {
"description": "ارتقاء تصاویر با وضوح پایین به وضوح بسیار بالا، حذف نویز و آرتیفکت‌ها. حداکثر خروجی: ۳۲ مگاپیکسل.",
"display_name": "افزایش کیفیت تصویر عمومی HitPaw",
"inputs": {
"auto_downscale": {
"name": "کاهش خودکار مقیاس",
"tooltip": "در صورت تجاوز خروجی از حد مجاز، تصویر ورودی به طور خودکار کوچک می‌شود."
},
"image": {
"name": "تصویر"
},
"model": {
"name": "مدل"
},
"upscale_factor": {
"name": "ضریب بزرگ‌نمایی"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"HitPawVideoEnhance": {
"description": "ارتقاء ویدیوهای با وضوح پایین به وضوح بالا، حذف نویز و آرتیفکت‌ها. قیمت‌ها به ازای هر ثانیه ویدیو محاسبه می‌شود.",
"display_name": "افزایش کیفیت ویدیو HitPaw",
"inputs": {
"model": {
"name": "مدل"
},
"model_resolution": {
"name": "وضوح"
},
"video": {
"name": "ویدیو"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"Hunyuan3Dv2Conditioning": {
"display_name": "Hunyuan3Dv2Conditioning",
"inputs": {
@@ -6366,7 +6441,9 @@
"Load3D": {
"display_name": "بارگذاری ۳بعدی و انیمیشن",
"inputs": {
"clear": {},
"clear": {
"": "پاک‌سازی"
},
"height": {
"name": "ارتفاع"
},
@@ -6376,38 +6453,28 @@
"model_file": {
"name": "فایل مدل"
},
"upload 3d model": {},
"upload extra resources": {},
"upload 3d model": {
"": "بارگذاری مدل سه‌بعدی"
},
"upload extra resources": {
"": "بارگذاری منابع اضافی"
},
"width": {
"name": "عرض"
}
},
"outputs": {
"0": {
"name": "تصویر",
"tooltip": null
},
"1": {
"name": "ماسک",
"tooltip": null
},
"2": {
"name": "مسیر مش",
"tooltip": null
},
"3": {
"name": "نرمال",
"tooltip": null
},
"4": {
"name": "اطلاعات دوربین",
"tooltip": null
},
"5": {
"name": "ویدئوی ضبط‌شده",
"outputs": [
null,
null,
null,
null,
null,
null,
{
"name": "مدل_۳بعدی",
"tooltip": null
}
}
]
},
"LoadAudio": {
"display_name": "بارگذاری صوت",
@@ -7268,12 +7335,17 @@
"name": "rig_task_id"
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
},
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyImageToModelNode": {
"display_name": "Meshy: تصویر به مدل",
@@ -7319,16 +7391,18 @@
"name": "symmetry_mode"
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyMultiImageToModelNode": {
"display_name": "Meshy: چند تصویر به مدل",
@@ -7374,16 +7448,18 @@
"name": "symmetry_mode"
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyRefineNode": {
"description": "یک مدل پیش‌نویس ایجادشده قبلی را بهبود دهید.",
@@ -7408,16 +7484,18 @@
"tooltip": "یک متن راهنما برای هدایت فرایند بافت‌دهی وارد کنید. حداکثر ۶۰۰ کاراکتر. نمی‌توان همزمان با 'texture_image' استفاده کرد."
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyRigModelNode": {
"description": "یک کاراکتر ریگ‌شده در فرمت‌های استاندارد ارائه می‌دهد. ریگ خودکار در حال حاضر برای مش‌های بدون بافت، دارایی‌های غیرانسان‌نما یا دارایی‌های انسان‌نمایی که ساختار اندام و بدن آن‌ها نامشخص است، مناسب نیست.",
@@ -7435,16 +7513,18 @@
"tooltip": "تصویر بافت رنگ پایه مدل با UV باز شده."
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "rig_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyTextToModelNode": {
"display_name": "Meshy: متن به مدل",
@@ -7483,16 +7563,18 @@
"name": "symmetry_mode"
}
},
"outputs": {
"0": {
"name": "model_file",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MeshyTextureNode": {
"display_name": "Meshy: مدل بافت",
@@ -7519,16 +7601,18 @@
"tooltip": "سبک بافت مورد نظر شیء را با استفاده از متن توصیف کنید. حداکثر ۶۰۰ کاراکتر. نمی‌توان همزمان با 'image_style' استفاده کرد."
}
},
"outputs": {
"0": {
"name": "فایل مدل",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
},
"1": {
"name": "meshy_task_id",
{
"name": "FBX",
"tooltip": null
}
}
]
},
"MinimaxHailuoVideoNode": {
"description": "تولید ویدئو از طریق پرامپت، با امکان استفاده از فریم ابتدایی با مدل جدید MiniMax Hailuo-02.",
@@ -10725,11 +10809,9 @@
"camera_info": {
"name": "اطلاعات دوربین"
},
"image": {
"name": "تصویر"
},
"model_file": {
"name": "فایل مدل"
"name": "فایل مدل",
"tooltip": "فایل مدل سه‌بعدی یا مسیر فایل"
}
}
},
@@ -11370,6 +11452,23 @@
}
}
},
"ReferenceTimbreAudio": {
"description": "این نود صدای مرجع برای timbre را تنظیم می‌کند (برای ace step 1.5)",
"display_name": "ReferenceTimbreAudio",
"inputs": {
"conditioning": {
"name": "شرایط‌دهی"
},
"latent": {
"name": "latent"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"RegexExtract": {
"display_name": "استخراج با Regex",
"inputs": {
@@ -11698,12 +11797,13 @@
"name": "Seed"
}
},
"outputs": {
"0": {
"name": "مسیر مدل سه‌بعدی",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Gen2": {
"description": "تولید دارایی‌های سه‌بعدی با استفاده از Rodin API",
@@ -11725,12 +11825,13 @@
"name": "TAPose"
}
},
"outputs": {
"0": {
"name": "مسیر مدل سه‌بعدی",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Regular": {
"description": "تولید دارایی‌های سه‌بعدی با استفاده از Rodin API",
@@ -11749,12 +11850,13 @@
"name": "Seed"
}
},
"outputs": {
"0": {
"name": "مسیر مدل سه‌بعدی",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Sketch": {
"description": "تولید دارایی‌های سه‌بعدی با استفاده از Rodin API",
@@ -11767,12 +11869,13 @@
"name": "Seed"
}
},
"outputs": {
"0": {
"name": "مسیر مدل سه‌بعدی",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"Rodin3D_Smooth": {
"description": "تولید دارایی‌های سه‌بعدی با استفاده از Rodin API",
@@ -11791,12 +11894,13 @@
"name": "Seed"
}
},
"outputs": {
"0": {
"name": "مسیر مدل سه‌بعدی",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"RunwayFirstLastFrameNode": {
"description": "اولین و آخرین فریم کلیدی را بارگذاری کنید، یک پرامپت بنویسید و ویدیو تولید کنید. انتقال‌های پیچیده‌تر، مانند زمانی که فریم آخر کاملاً با فریم اول متفاوت است، ممکن است از مدت زمان طولانی‌تر ۱۰ ثانیه‌ای بهره‌مند شوند. این کار به تولید اجازه می‌دهد تا زمان بیشتری برای انتقال روان بین دو ورودی داشته باشد. پیش از شروع، این نکات کلیدی را مرور کنید تا مطمئن شوید انتخاب‌های ورودی شما باعث موفقیت تولید خواهد شد: https://help.runwayml.com/hc/en-us/articles/34170748696595-Creating-with-Keyframes-on-Gen-3.",
@@ -12528,11 +12632,9 @@
"filename_prefix": {
"name": "filename_prefix"
},
"image": {
"name": "image"
},
"mesh": {
"name": "mesh"
"name": "mesh",
"tooltip": "مش یا فایل GLB برای ذخیره"
}
}
},
@@ -13791,12 +13893,17 @@
"tooltip": "seed تعیین می‌کند که node باید دوباره اجرا شود یا خیر؛ نتایج صرف‌نظر از seed غیرقطعی هستند."
}
},
"outputs": {
"0": {
"name": "فایل مدل",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
},
{
"name": "OBJ",
"tooltip": null
}
}
]
},
"TencentTextToModelNode": {
"display_name": "Hunyuan3D: تبدیل متن به مدل (پیشرفته)",
@@ -13826,12 +13933,17 @@
"tooltip": "seed تعیین می‌کند که node باید دوباره اجرا شود یا خیر؛ نتایج صرف‌نظر از seed غیرقطعی هستند."
}
},
"outputs": {
"0": {
"name": "فایل مدل",
"outputs": [
null,
{
"name": "GLB",
"tooltip": null
},
{
"name": "OBJ",
"tooltip": null
}
}
]
},
"TextEncodeAceStepAudio": {
"display_name": "TextEncodeAceStepAudio",
@@ -13855,6 +13967,62 @@
}
}
},
"TextEncodeAceStepAudio1_5": {
"display_name": "TextEncodeAceStepAudio1.5",
"inputs": {
"bpm": {
"name": "ضرب در دقیقه (BPM)"
},
"cfg_scale": {
"name": "cfg_scale"
},
"clip": {
"name": "clip"
},
"control_after_generate": {
"name": "کنترل پس از تولید"
},
"duration": {
"name": "مدت زمان"
},
"generate_audio_codes": {
"name": "تولید کدهای صوتی",
"tooltip": "فعال‌سازی LLM برای تولید کدهای صوتی. این کار ممکن است کند باشد اما کیفیت صدای تولیدشده را افزایش می‌دهد. اگر به مدل یک مرجع صوتی می‌دهید، این گزینه را غیرفعال کنید."
},
"keyscale": {
"name": "گام"
},
"language": {
"name": "زبان"
},
"lyrics": {
"name": "متن ترانه"
},
"seed": {
"name": "بذر"
},
"tags": {
"name": "برچسب‌ها"
},
"temperature": {
"name": "temperature"
},
"timesignature": {
"name": "امضای زمانی"
},
"top_k": {
"name": "top_k"
},
"top_p": {
"name": "top_p"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"TextEncodeHunyuanVideo_ImageToVideo": {
"display_name": "TextEncodeHunyuanVideo_ImageToVideo",
"inputs": {
@@ -14410,16 +14578,14 @@
"name": "بذر تکسچر"
}
},
"outputs": {
"0": {
"name": "فایل مدل",
"tooltip": null
},
"1": {
"name": "شناسه وظیفه مدل",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoMultiviewToModelNode": {
"display_name": "Tripo: چندنما به مدل",
@@ -14471,16 +14637,14 @@
"name": "بذر تکسچر"
}
},
"outputs": {
"0": {
"name": "فایل مدل",
"tooltip": null
},
"1": {
"name": "شناسه وظیفه مدل",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoRefineNode": {
"description": "بهبود یک مدل پیش‌نویس که فقط توسط مدل‌های Tripo نسخه ۱.۴ ایجاد شده است.",
@@ -14491,16 +14655,14 @@
"tooltip": "باید یک مدل Tripo نسخه ۱.۴ باشد"
}
},
"outputs": {
"0": {
"name": "فایل مدل",
"tooltip": null
},
"1": {
"name": "شناسه وظیفه مدل",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoRetargetNode": {
"display_name": "Tripo: هدف‌گذاری مجدد مدل ریگ‌شده",
@@ -14512,16 +14674,14 @@
"name": "شناسه وظیفه مدل اصلی"
}
},
"outputs": {
"0": {
"name": "فایل مدل",
"tooltip": null
},
"1": {
"name": "شناسه وظیفه هدف‌گذاری مجدد",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoRigNode": {
"display_name": "Tripo: ریگ مدل",
@@ -14530,16 +14690,14 @@
"name": "شناسه وظیفه مدل اصلی"
}
},
"outputs": {
"0": {
"name": "فایل مدل",
"tooltip": null
},
"1": {
"name": "شناسه وظیفه ریگ",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoTextToModelNode": {
"display_name": "Tripo: متن به مدل",
@@ -14584,16 +14742,14 @@
"name": "بذر تکسچر"
}
},
"outputs": {
"0": {
"name": "فایل مدل",
"tooltip": null
},
"1": {
"name": "شناسه وظیفه مدل",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TripoTextureNode": {
"display_name": "Tripo: مدل بافت",
@@ -14617,16 +14773,14 @@
"name": "بذر بافت"
}
},
"outputs": {
"0": {
"name": "فایل مدل",
"tooltip": null
},
"1": {
"name": "شناسه وظیفه مدل",
"outputs": [
null,
null,
{
"name": "GLB",
"tooltip": null
}
}
]
},
"TruncateText": {
"display_name": "کوتاه‌سازی متن",
@@ -14795,6 +14949,28 @@
}
}
},
"VAEDecodeAudioTiled": {
"display_name": "رمزگشایی VAE صوتی (کاشی‌بندی‌شده)",
"inputs": {
"overlap": {
"name": "همپوشانی"
},
"samples": {
"name": "نمونه‌ها"
},
"tile_size": {
"name": "اندازه کاشی"
},
"vae": {
"name": "vae"
}
},
"outputs": {
"0": {
"tooltip": null
}
}
},
"VAEDecodeHunyuan3D": {
"display_name": "VAEDecodeHunyuan3D",
"inputs": {

View File

@@ -151,51 +151,6 @@
},
"tooltip": "ظاهر و نمایش پیوندهای ارتباطی بین nodeها روی canvas را کنترل می‌کند."
},
"Comfy_Load3D_3DViewerEnable": {
"name": "فعال‌سازی نمایشگر سه‌بعدی (بتا)",
"tooltip": "نمایشگر سه‌بعدی (بتا) را برای nodeهای انتخاب‌شده فعال می‌کند. این قابلیت به شما امکان می‌دهد مدل‌های سه‌بعدی را مستقیماً در نمایشگر سه‌بعدی با اندازه کامل مشاهده و تعامل کنید."
},
"Comfy_Load3D_BackgroundColor": {
"name": "رنگ پس‌زمینه اولیه",
"tooltip": "رنگ پیش‌فرض پس‌زمینه صحنه سه‌بعدی را کنترل می‌کند. این تنظیم ظاهر پس‌زمینه را هنگام ایجاد یک ابزارک سه‌بعدی جدید تعیین می‌کند، اما پس از ایجاد برای هر ابزارک به صورت جداگانه قابل تغییر است."
},
"Comfy_Load3D_CameraType": {
"name": "نوع دوربین اولیه",
"options": {
"orthographic": "اورتوگرافیک",
"perspective": "پرسپکتیو"
},
"tooltip": "تعیین می‌کند که دوربین به طور پیش‌فرض پرسپکتیو باشد یا اورتوگرافیک هنگام ایجاد یک ابزارک سه‌بعدی جدید. این پیش‌فرض برای هر ابزارک به صورت جداگانه قابل تغییر است."
},
"Comfy_Load3D_LightAdjustmentIncrement": {
"name": "گام تنظیم شدت نور",
"tooltip": "اندازه گام هنگام تنظیم شدت نور در صحنه‌های سه‌بعدی را کنترل می‌کند. مقدار گام کوچکتر امکان کنترل دقیق‌تر نور را فراهم می‌کند، در حالی که مقدار بزرگ‌تر تغییرات محسوس‌تری ایجاد می‌کند."
},
"Comfy_Load3D_LightIntensity": {
"name": "شدت نور اولیه",
"tooltip": "سطح روشنایی پیش‌فرض نور در صحنه سه‌بعدی را تعیین می‌کند. این مقدار مشخص می‌کند که نورها هنگام ایجاد یک ابزارک سه‌بعدی جدید با چه شدتی به اشیاء تابیده شوند، اما برای هر ابزارک به صورت جداگانه قابل تغییر است."
},
"Comfy_Load3D_LightIntensityMaximum": {
"name": "حداکثر شدت نور",
"tooltip": "حداکثر مقدار مجاز شدت نور برای صحنه‌های سه‌بعدی را تعیین می‌کند. این مقدار، حد بالای روشنایی قابل تنظیم در هر ابزارک سه‌بعدی را مشخص می‌کند."
},
"Comfy_Load3D_LightIntensityMinimum": {
"name": "حداقل شدت نور",
"tooltip": "حداقل مقدار مجاز شدت نور برای صحنه‌های سه‌بعدی را تعیین می‌کند. این مقدار، حد پایین روشنایی قابل تنظیم در هر ابزارک سه‌بعدی را مشخص می‌کند."
},
"Comfy_Load3D_PLYEngine": {
"name": "موتور PLY",
"options": {
"fastply": "fastply",
"sparkjs": "sparkjs",
"threejs": "threejs"
},
"tooltip": "موتور بارگذاری فایل‌های PLY را انتخاب کنید. \"threejs\" از PLYLoader بومی Three.js (مناسب برای فایل‌های مش PLY) استفاده می‌کند. \"fastply\" برای فایل‌های point cloud PLY به صورت ASCII بهینه شده است. \"sparkjs\" از Spark.js برای فایل‌های ۳بعدی Gaussian Splatting PLY استفاده می‌کند."
},
"Comfy_Load3D_ShowGrid": {
"name": "نمایش اولیه شبکه",
"tooltip": "تعیین می‌کند که شبکه (Grid) به طور پیش‌فرض هنگام ایجاد یک ابزارک سه‌بعدی جدید قابل مشاهده باشد یا خیر. این پیش‌فرض برای هر ابزارک به صورت جداگانه قابل تغییر است."
},
"Comfy_Locale": {
"name": "زبان"
},
@@ -244,6 +199,10 @@
"Comfy_NodeBadge_ShowApiPricing": {
"name": "نمایش نشان قیمت‌گذاری API نود"
},
"Comfy_NodeReplacement_Enabled": {
"name": "فعال‌سازی جایگزینی خودکار node",
"tooltip": "در صورت فعال بودن، nodeهای مفقود می‌توانند به‌طور خودکار با معادل‌های جدیدتر خود جایگزین شوند اگر نگاشت جایگزینی وجود داشته باشد."
},
"Comfy_NodeSearchBoxImpl": {
"name": "پیاده‌سازی جعبه جستجوی نود",
"options": {

Some files were not shown because too many files have changed in this diff Show More