Compare commits
37 Commits
fix/codera
...
remove-cac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e04225dd49 | ||
|
|
ffda940e5a | ||
|
|
84f77e7675 | ||
|
|
adf81fcd73 | ||
|
|
c85a15547b | ||
|
|
c602dce375 | ||
|
|
34b1799b21 | ||
|
|
bacb5570c8 | ||
|
|
8b53d5c807 | ||
|
|
39ce4a23cc | ||
|
|
ef477d0381 | ||
|
|
37c6ddfcd9 | ||
|
|
55c42ee484 | ||
|
|
b04db536a1 | ||
|
|
975d6a360d | ||
|
|
7e137d880b | ||
|
|
8db6fb7733 | ||
|
|
7c2c59b9fb | ||
|
|
2f7f3c4e56 | ||
|
|
4c00d39ade | ||
|
|
f1fc5fa9b3 | ||
|
|
c111fb7758 | ||
|
|
65655ba35f | ||
|
|
852d77159e | ||
|
|
b61029b9da | ||
|
|
0a62ea0b2c | ||
|
|
cccc0944a0 | ||
|
|
aadec87bff | ||
|
|
f5088d6cfb | ||
|
|
fd7ce3a852 | ||
|
|
08a2f8ae15 | ||
|
|
7b9f24f515 | ||
|
|
faed80e99a | ||
|
|
b129d64c5d | ||
|
|
4c9b83a224 | ||
|
|
e973efb44a | ||
|
|
9ecb100d11 |
150
.claude/skills/backport-management/SKILL.md
Normal file
@@ -0,0 +1,150 @@
|
||||
---
|
||||
name: backport-management
|
||||
description: Manages cherry-pick backports across stable release branches. Discovers candidates from Slack/git, analyzes dependencies, resolves conflicts via worktree, and logs results. Use when asked to backport, cherry-pick to stable, manage release branches, do stable branch maintenance, or run a backport session.
|
||||
---
|
||||
|
||||
# Backport Management
|
||||
|
||||
Cherry-pick backport management for Comfy-Org/ComfyUI_frontend stable release branches.
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **Discover** — Collect candidates from Slack bot + git log gap (`reference/discovery.md`)
|
||||
2. **Analyze** — Categorize MUST/SHOULD/SKIP, check deps (`reference/analysis.md`)
|
||||
3. **Plan** — Order by dependency (leaf fixes first), group into waves per branch
|
||||
4. **Execute** — Label-driven automation → worktree fallback for conflicts (`reference/execution.md`)
|
||||
5. **Verify** — After each wave, verify branch integrity before proceeding
|
||||
6. **Log & Report** — Generate session report with mermaid diagram (`reference/logging.md`)
|
||||
|
||||
## System Context
|
||||
|
||||
| Item | Value |
|
||||
| -------------- | ------------------------------------------------- |
|
||||
| Repo | `~/ComfyUI_frontend` (Comfy-Org/ComfyUI_frontend) |
|
||||
| Merge strategy | Squash merge (`gh pr merge --squash --admin`) |
|
||||
| Automation | `pr-backport.yaml` GitHub Action (label-driven) |
|
||||
| Tracking dir | `~/temp/backport-session/` |
|
||||
|
||||
## Branch Scope Rules
|
||||
|
||||
**Critical: Match PRs to the correct target branches.**
|
||||
|
||||
| Branch prefix | Scope | Example |
|
||||
| ------------- | ------------------------------ | ----------------------------------------- |
|
||||
| `cloud/*` | Cloud-hosted ComfyUI only | App mode, cloud auth, cloud-specific UI |
|
||||
| `core/*` | Local/self-hosted ComfyUI only | Core editor, local workflows, node system |
|
||||
|
||||
**⚠️ NEVER backport cloud-only PRs to `core/*` branches.** Cloud-only changes (app mode, cloud auth, cloud billing UI, cloud-specific API calls) are irrelevant to local users and waste effort. Before backporting any PR to a `core/*` branch, check:
|
||||
|
||||
- Does the PR title/description mention "app mode", "cloud", or cloud-specific features?
|
||||
- Does the PR only touch files like `appModeStore.ts`, cloud auth, or cloud-specific components?
|
||||
- If yes → skip for `core/*` branches (may still apply to `cloud/*` branches)
|
||||
|
||||
## ⚠️ Gotchas (Learn from Past Sessions)
|
||||
|
||||
### Use `gh api` for Labels — NOT `gh pr edit`
|
||||
|
||||
`gh pr edit --add-label` triggers Projects Classic deprecation errors. Always use:
|
||||
|
||||
```bash
|
||||
gh api repos/Comfy-Org/ComfyUI_frontend/issues/$PR/labels \
|
||||
-f "labels[]=needs-backport" -f "labels[]=TARGET_BRANCH"
|
||||
```
|
||||
|
||||
### Automation Over-Reports Conflicts
|
||||
|
||||
The `pr-backport.yaml` action reports more conflicts than reality. `git cherry-pick -m 1` with git auto-merge handles many cases the automation can't. Always attempt manual cherry-pick before skipping.
|
||||
|
||||
### Never Skip Based on Conflict File Count
|
||||
|
||||
12 or 27 conflicting files can be trivial (snapshots, new files). **Categorize conflicts first**, then decide. See Conflict Triage below.
|
||||
|
||||
## Conflict Triage
|
||||
|
||||
**Always categorize before deciding to skip. High conflict count ≠ hard conflicts.**
|
||||
|
||||
| Type | Symptom | Resolution |
|
||||
| ---------------------------- | ------------------------------------ | --------------------------------------------------------------- |
|
||||
| **Binary snapshots (PNGs)** | `.png` files in conflict list | `git checkout --theirs $FILE && git add $FILE` — always trivial |
|
||||
| **Modify/delete (new file)** | PR introduces files not on target | `git add $FILE` — keep the new file |
|
||||
| **Modify/delete (removed)** | Target removed files the PR modifies | `git rm $FILE` — file no longer relevant |
|
||||
| **Content conflicts** | Marker-based (`<<<<<<<`) | Accept theirs via python regex (see below) |
|
||||
| **Add/add** | Both sides added same file | Accept theirs, verify no logic conflict |
|
||||
| **Locale/JSON files** | i18n key additions | Accept theirs, validate JSON after |
|
||||
|
||||
```python
|
||||
# Accept theirs for content conflicts
|
||||
import re
|
||||
pattern = r'<<<<<<< HEAD\n(.*?)=======\n(.*?)>>>>>>> [^\n]+\n?'
|
||||
content = re.sub(pattern, r'\2', content, flags=re.DOTALL)
|
||||
```
|
||||
|
||||
### Escalation Triggers (Flag for Human)
|
||||
|
||||
- **Package.json/lockfile changes** → skip on stable (transitive dep regression risk)
|
||||
- **Core type definition changes** → requires human judgment
|
||||
- **Business logic conflicts** (not just imports/exports) → requires domain knowledge
|
||||
- **Admin-merged conflict resolutions** → get human review of the resolution before continuing the wave
|
||||
|
||||
## Auto-Skip Categories
|
||||
|
||||
Skip these without discussion:
|
||||
|
||||
- **Dep refresh PRs** — Risk of transitive dep regressions on stable. Cherry-pick individual CVE fixes instead.
|
||||
- **CI/tooling changes** — Not user-facing
|
||||
- **Test-only / lint rule changes** — Not user-facing
|
||||
- **Revert pairs** — If PR A reverted by PR B, skip both. If fixed version (PR C) exists, backport only C.
|
||||
- **Features not on target branch** — e.g., Painter, GLSLShader, appModeStore on core/1.40
|
||||
- **Cloud-only PRs on core/\* branches** — App mode, cloud auth, cloud billing. These only affect cloud-hosted ComfyUI.
|
||||
|
||||
## Wave Verification
|
||||
|
||||
After merging each wave of PRs to a target branch, verify branch integrity before proceeding:
|
||||
|
||||
```bash
|
||||
# Fetch latest state of target branch
|
||||
git fetch origin TARGET_BRANCH
|
||||
|
||||
# Quick smoke check: does the branch build?
|
||||
git worktree add /tmp/verify-TARGET origin/TARGET_BRANCH
|
||||
cd /tmp/verify-TARGET
|
||||
source ~/.nvm/nvm.sh && nvm use 24 && pnpm install && pnpm typecheck
|
||||
git worktree remove /tmp/verify-TARGET --force
|
||||
```
|
||||
|
||||
If typecheck fails, stop and investigate before continuing. A broken branch after wave N means all subsequent waves will compound the problem.
|
||||
|
||||
## Continuous Backporting Recommendation
|
||||
|
||||
Large backport sessions (50+ PRs) are expensive and error-prone. Prefer continuous backporting:
|
||||
|
||||
- Backport bug fixes as they merge to main (same day or next day)
|
||||
- Use the automation labels immediately after merge
|
||||
- Reserve session-style bulk backporting for catching up after gaps
|
||||
- When a release branch is created, immediately start the continuous process
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Label-Driven Automation (default path)
|
||||
|
||||
```bash
|
||||
gh api repos/Comfy-Org/ComfyUI_frontend/issues/$PR/labels \
|
||||
-f "labels[]=needs-backport" -f "labels[]=TARGET_BRANCH"
|
||||
# Wait 3 min, check: gh pr list --base TARGET_BRANCH --state open
|
||||
```
|
||||
|
||||
### Manual Worktree Cherry-Pick (conflict fallback)
|
||||
|
||||
```bash
|
||||
git worktree add /tmp/backport-$BRANCH origin/$BRANCH
|
||||
cd /tmp/backport-$BRANCH
|
||||
git checkout -b backport-$PR-to-$BRANCH origin/$BRANCH
|
||||
git cherry-pick -m 1 $MERGE_SHA
|
||||
# Resolve conflicts, push, create PR, merge
|
||||
```
|
||||
|
||||
### PR Title Convention
|
||||
|
||||
```
|
||||
[backport TARGET_BRANCH] Original Title (#ORIGINAL_PR)
|
||||
```
|
||||
68
.claude/skills/backport-management/reference/analysis.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Analysis & Decision Framework
|
||||
|
||||
## Categorization
|
||||
|
||||
| Category | Criteria | Action |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| **MUST** | User-facing bug, crash, data corruption, security. Clear breakage that users will hit. | Backport (with deps if needed) |
|
||||
| **SHOULD** | UX improvement, minor bug, small dep chain. No user-visible breakage if skipped, but improves experience. | Backport if clean cherry-pick; defer if conflict resolution is non-trivial |
|
||||
| **SKIP** | CI/tooling, test-only, lint rules, cosmetic, dep refresh | Skip with documented reason |
|
||||
| **NEEDS DISCUSSION** | Large dep chain, unclear risk/benefit, touches core types | Flag for human |
|
||||
|
||||
### MUST vs SHOULD Decision Guide
|
||||
|
||||
When unsure, ask: "If a user on this stable branch reports this issue, would we consider it a bug?"
|
||||
|
||||
- **Yes** → MUST. The fix addresses broken behavior.
|
||||
- **No, but it's noticeably better** → SHOULD. The fix is a quality-of-life improvement.
|
||||
- **No, and it's cosmetic or internal** → SKIP.
|
||||
|
||||
For SHOULD items with conflicts: if conflict resolution requires more than trivial accept-theirs patterns (content conflicts in business logic, not just imports), downgrade to SKIP or escalate to NEEDS DISCUSSION.
|
||||
|
||||
## Branch Scope Filtering
|
||||
|
||||
**Before categorizing, filter by branch scope:**
|
||||
|
||||
| Target branch | Skip if PR is... |
|
||||
| ------------- | ------------------------------------------------------------------- |
|
||||
| `core/*` | Cloud-only (app mode, cloud auth, cloud billing, cloud-specific UI) |
|
||||
| `cloud/*` | Local-only features not present on cloud branch |
|
||||
|
||||
Cloud-only PRs backported to `core/*` are wasted effort — `core/*` branches serve local/self-hosted users who never see cloud features. Check PR titles, descriptions, and files changed for cloud-specific indicators.
|
||||
|
||||
## Features Not on Stable Branches
|
||||
|
||||
Check before backporting — these don't exist on older branches:
|
||||
|
||||
- **Painter** (`src/extensions/core/painter.ts`) — not on core/1.40
|
||||
- **GLSLShader** — not on core/1.40
|
||||
- **App builder** — check per branch
|
||||
- **appModeStore.ts** — not on core/1.40
|
||||
|
||||
## Dep Refresh PRs
|
||||
|
||||
Always SKIP on stable branches. Risk of transitive dependency regressions outweighs audit cleanup benefit. If a specific CVE fix is needed, cherry-pick that individual fix instead.
|
||||
|
||||
## Revert Pairs
|
||||
|
||||
If PR A is reverted by PR B:
|
||||
|
||||
- Skip BOTH A and B
|
||||
- If a fixed version exists (PR C), backport only C
|
||||
|
||||
## Dependency Analysis
|
||||
|
||||
```bash
|
||||
# Find other PRs that touched the same files
|
||||
gh pr view $PR --json files --jq '.files[].path' | while read f; do
|
||||
git log --oneline origin/TARGET..$MERGE_SHA -- "$f"
|
||||
done
|
||||
```
|
||||
|
||||
## Human Review Checkpoint
|
||||
|
||||
Present decisions.md before execution. Include:
|
||||
|
||||
1. All MUST/SHOULD/SKIP categorizations with rationale
|
||||
2. Questions for human (feature existence, scope, deps)
|
||||
3. Estimated effort per branch
|
||||
42
.claude/skills/backport-management/reference/discovery.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Discovery — Candidate Collection
|
||||
|
||||
## Source 1: Slack Backport-Checker Bot
|
||||
|
||||
Use `slackdump` skill to export `#frontend-releases` channel (C09K9TPU2G7):
|
||||
|
||||
```bash
|
||||
slackdump export -o ~/slack-exports/frontend-releases.zip C09K9TPU2G7
|
||||
```
|
||||
|
||||
Parse bot messages for PRs flagged "Might need backport" per release version.
|
||||
|
||||
## Source 2: Git Log Gap Analysis
|
||||
|
||||
```bash
|
||||
# Count gap
|
||||
git log --oneline origin/TARGET..origin/main | wc -l
|
||||
|
||||
# List gap commits
|
||||
git log --oneline origin/TARGET..origin/main
|
||||
|
||||
# Check if a PR is already on target
|
||||
git log --oneline origin/TARGET --grep="#PR_NUMBER"
|
||||
|
||||
# Check for existing backport PRs
|
||||
gh pr list --base TARGET --state all --search "backport PR_NUMBER"
|
||||
```
|
||||
|
||||
## Source 3: GitHub PR Details
|
||||
|
||||
```bash
|
||||
# Get merge commit SHA
|
||||
gh pr view $PR --json mergeCommit,title --jq '"Title: \(.title)\nMerge: \(.mergeCommit.oid)"'
|
||||
|
||||
# Get files changed
|
||||
gh pr view $PR --json files --jq '.files[].path'
|
||||
```
|
||||
|
||||
## Output: candidate_list.md
|
||||
|
||||
Table per target branch:
|
||||
| PR# | Title | Category | Flagged by Bot? | Decision |
|
||||
150
.claude/skills/backport-management/reference/execution.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# Execution Workflow
|
||||
|
||||
## Per-Branch Execution Order
|
||||
|
||||
1. Smallest gap first (validation run)
|
||||
2. Medium gap next (quick win)
|
||||
3. Largest gap last (main effort)
|
||||
|
||||
## Step 1: Label-Driven Automation (Batch)
|
||||
|
||||
```bash
|
||||
# Add labels to all candidates for a target branch
|
||||
for pr in $PR_LIST; do
|
||||
gh api repos/Comfy-Org/ComfyUI_frontend/issues/$pr/labels \
|
||||
-f "labels[]=needs-backport" -f "labels[]=TARGET_BRANCH" --silent
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Wait 3 minutes for automation
|
||||
sleep 180
|
||||
|
||||
# Check which got auto-PRs
|
||||
gh pr list --base TARGET_BRANCH --state open --limit 50 --json number,title
|
||||
```
|
||||
|
||||
## Step 2: Review & Merge Clean Auto-PRs
|
||||
|
||||
```bash
|
||||
for pr in $AUTO_PRS; do
|
||||
# Check size
|
||||
gh pr view $pr --json title,additions,deletions,changedFiles \
|
||||
--jq '"Files: \(.changedFiles), +\(.additions)/-\(.deletions)"'
|
||||
# Admin merge
|
||||
gh pr merge $pr --squash --admin
|
||||
sleep 3
|
||||
done
|
||||
```
|
||||
|
||||
## Step 3: Manual Worktree for Conflicts
|
||||
|
||||
```bash
|
||||
git fetch origin TARGET_BRANCH
|
||||
git worktree add /tmp/backport-TARGET origin/TARGET_BRANCH
|
||||
cd /tmp/backport-TARGET
|
||||
|
||||
for PR in ${CONFLICT_PRS[@]}; do
|
||||
# Refresh target ref so each branch is based on current HEAD
|
||||
git fetch origin TARGET_BRANCH
|
||||
git checkout origin/TARGET_BRANCH
|
||||
|
||||
git checkout -b backport-$PR-to-TARGET origin/TARGET_BRANCH
|
||||
git cherry-pick -m 1 $MERGE_SHA
|
||||
|
||||
# If conflict — NEVER skip based on file count alone!
|
||||
# Categorize conflicts first: binary PNGs, modify/delete, content, add/add
|
||||
# See SKILL.md Conflict Triage table for resolution per type.
|
||||
|
||||
# Resolve all conflicts, then:
|
||||
git add .
|
||||
GIT_EDITOR=true git cherry-pick --continue
|
||||
|
||||
git push origin backport-$PR-to-TARGET
|
||||
NEW_PR=$(gh pr create --base TARGET_BRANCH --head backport-$PR-to-TARGET \
|
||||
--title "[backport TARGET] TITLE (#$PR)" \
|
||||
--body "Backport of #$PR..." | grep -oP '\d+$')
|
||||
gh pr merge $NEW_PR --squash --admin
|
||||
sleep 3
|
||||
done
|
||||
|
||||
# Cleanup
|
||||
cd -
|
||||
git worktree remove /tmp/backport-TARGET --force
|
||||
```
|
||||
|
||||
**⚠️ Human review for conflict resolutions:** When admin-merging a PR where you manually resolved conflicts (especially content conflicts beyond trivial accept-theirs), pause and present the resolution diff to the human for review before merging. Trivial resolutions (binary snapshots, modify/delete, locale key additions) can proceed without review.
|
||||
|
||||
## Step 4: Wave Verification
|
||||
|
||||
After completing all PRs in a wave for a target branch:
|
||||
|
||||
```bash
|
||||
git fetch origin TARGET_BRANCH
|
||||
git worktree add /tmp/verify-TARGET origin/TARGET_BRANCH
|
||||
cd /tmp/verify-TARGET
|
||||
source ~/.nvm/nvm.sh && nvm use 24 && pnpm install && pnpm typecheck
|
||||
git worktree remove /tmp/verify-TARGET --force
|
||||
```
|
||||
|
||||
If verification fails, stop and fix before proceeding to the next wave. Do not compound problems across waves.
|
||||
|
||||
## Conflict Resolution Patterns
|
||||
|
||||
### 1. Content Conflicts (accept theirs)
|
||||
|
||||
```python
|
||||
import re
|
||||
pattern = r'<<<<<<< HEAD\n(.*?)=======\n(.*?)>>>>>>> [^\n]+\n?'
|
||||
content = re.sub(pattern, r'\2', content, flags=re.DOTALL)
|
||||
```
|
||||
|
||||
### 2. Modify/Delete (two cases!)
|
||||
|
||||
```bash
|
||||
# Case A: PR introduces NEW files not on target → keep them
|
||||
git add $FILE
|
||||
|
||||
# Case B: Target REMOVED files the PR modifies → drop them
|
||||
git rm $FILE
|
||||
```
|
||||
|
||||
### 3. Binary Files (snapshots)
|
||||
|
||||
```bash
|
||||
git checkout --theirs $FILE && git add $FILE
|
||||
```
|
||||
|
||||
### 4. Locale Files
|
||||
|
||||
Usually adding new i18n keys — accept theirs, validate JSON:
|
||||
|
||||
```bash
|
||||
python3 -c "import json; json.load(open('src/locales/en/main.json'))" && echo "Valid"
|
||||
```
|
||||
|
||||
## Merge Conflicts After Other Merges
|
||||
|
||||
When merging multiple PRs to the same branch, later PRs may conflict with earlier merges:
|
||||
|
||||
```bash
|
||||
git fetch origin TARGET_BRANCH
|
||||
git rebase origin/TARGET_BRANCH
|
||||
# Resolve new conflicts
|
||||
git push --force origin backport-$PR-to-TARGET
|
||||
sleep 20 # Wait for GitHub to recompute merge state
|
||||
gh pr merge $PR --squash --admin
|
||||
```
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
1. **Automation reports more conflicts than reality** — `cherry-pick -m 1` with git auto-merge handles many "conflicts" the automation can't
|
||||
2. **Never skip based on conflict file count** — 12 or 27 conflicts can be trivial (snapshots, new files). Categorize first: binary PNGs, modify/delete, content, add/add.
|
||||
3. **Modify/delete goes BOTH ways** — if the PR introduces new files (not on target), `git add` them. If target deleted files the PR modifies, `git rm`.
|
||||
4. **Binary snapshot PNGs** — always `git checkout --theirs && git add`. Never skip a PR just because it has many snapshot conflicts.
|
||||
5. **Batch label additions need 2s delay** between API calls to avoid rate limits
|
||||
6. **Merging 6+ PRs rapidly** can cause later PRs to become unmergeable — wait 20-30s for GitHub to recompute merge state
|
||||
7. **appModeStore.ts, painter files, GLSLShader files** don't exist on core/1.40 — `git rm` these
|
||||
8. **Always validate JSON** after resolving locale file conflicts
|
||||
9. **Dep refresh PRs** — skip on stable branches. Risk of transitive dep regressions outweighs audit cleanup. Cherry-pick individual CVE fixes instead.
|
||||
10. **Verify after each wave** — run `pnpm typecheck` on the target branch after merging a batch. Catching breakage early prevents compounding errors.
|
||||
11. **Cloud-only PRs don't belong on core/\* branches** — app mode, cloud auth, and cloud-specific UI changes are irrelevant to local users. Always check PR scope against branch scope before backporting.
|
||||
96
.claude/skills/backport-management/reference/logging.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Logging & Session Reports
|
||||
|
||||
## During Execution
|
||||
|
||||
Maintain `execution-log.md` with per-branch tables:
|
||||
|
||||
```markdown
|
||||
| PR# | Title | Status | Backport PR | Notes |
|
||||
| ----- | ----- | --------------------------------- | ----------- | ------- |
|
||||
| #XXXX | Title | ✅ Merged / ⏭️ Skip / ⏸️ Deferred | #YYYY | Details |
|
||||
```
|
||||
|
||||
## Wave Verification Log
|
||||
|
||||
Track verification results per wave:
|
||||
|
||||
```markdown
|
||||
## Wave N Verification — TARGET_BRANCH
|
||||
|
||||
- PRs merged: #A, #B, #C
|
||||
- Typecheck: ✅ Pass / ❌ Fail
|
||||
- Issues found: (if any)
|
||||
- Human review needed: (list any non-trivial conflict resolutions)
|
||||
```
|
||||
|
||||
## Session Report Template
|
||||
|
||||
```markdown
|
||||
# Backport Session Report
|
||||
|
||||
## Summary
|
||||
|
||||
| Branch | Candidates | Merged | Skipped | Deferred | Rate |
|
||||
| ------ | ---------- | ------ | ------- | -------- | ---- |
|
||||
|
||||
## Deferred Items (Needs Human)
|
||||
|
||||
| PR# | Title | Branch | Issue |
|
||||
|
||||
## Conflict Resolutions Requiring Review
|
||||
|
||||
| PR# | Branch | Conflict Type | Resolution Summary |
|
||||
|
||||
## Automation Performance
|
||||
|
||||
| Metric | Value |
|
||||
| --------------------------- | ----- |
|
||||
| Auto success rate | X% |
|
||||
| Manual resolution rate | X% |
|
||||
| Overall clean rate | X% |
|
||||
| Wave verification pass rate | X% |
|
||||
|
||||
## Process Recommendations
|
||||
|
||||
- Were there clusters of related PRs that should have been backported together?
|
||||
- Any PRs that should have been backported sooner (continuous backporting candidates)?
|
||||
- Feature branches that need tracking for future sessions?
|
||||
```
|
||||
|
||||
## Final Deliverable: Visual Summary
|
||||
|
||||
At session end, generate a **mermaid diagram** showing all backported PRs organized by target branch and category (MUST/SHOULD), plus a summary table. Present this to the user as the final output.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph branch1["☁️ cloud/X.XX — N PRs"]
|
||||
C1["#XXXX title"]
|
||||
C2["#XXXX title"]
|
||||
end
|
||||
|
||||
subgraph branch2must["🔴 core/X.XX MUST — N PRs"]
|
||||
M1["#XXXX title"]
|
||||
end
|
||||
|
||||
subgraph branch2should["🟡 core/X.XX SHOULD — N PRs"]
|
||||
S1["#XXXX-#XXXX N auto-merged"]
|
||||
S2["#XXXX-#XXXX N manual picks"]
|
||||
end
|
||||
|
||||
classDef cloudStyle fill:#1a3a5c,stroke:#4da6ff,color:#e0f0ff
|
||||
classDef coreStyle fill:#1a4a2e,stroke:#4dff88,color:#e0ffe8
|
||||
classDef mustStyle fill:#5c1a1a,stroke:#ff4d4d,color:#ffe0e0
|
||||
classDef shouldStyle fill:#4a3a1a,stroke:#ffcc4d,color:#fff5e0
|
||||
```
|
||||
|
||||
Use the `mermaid` tool to render this diagram and present it alongside the summary table as the session's final deliverable.
|
||||
|
||||
## Files to Track
|
||||
|
||||
- `candidate_list.md` — all candidates per branch
|
||||
- `decisions.md` — MUST/SHOULD/SKIP with rationale
|
||||
- `wave-plan.md` — execution order
|
||||
- `execution-log.md` — real-time status
|
||||
- `backport-session-report.md` — final summary
|
||||
|
||||
All in `~/temp/backport-session/`.
|
||||
25
.github/workflows/ci-tests-storybook.yaml
vendored
@@ -4,6 +4,8 @@ name: 'CI: Tests Storybook'
|
||||
on:
|
||||
workflow_dispatch: # Allow manual triggering
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
# Post starting comment for non-forked PRs
|
||||
@@ -138,6 +140,29 @@ jobs:
|
||||
"${{ github.head_ref }}" \
|
||||
"completed"
|
||||
|
||||
# Deploy Storybook to production URL on main branch push
|
||||
deploy-production:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
|
||||
- name: Build Storybook
|
||||
run: pnpm build-storybook
|
||||
|
||||
- name: Deploy to Cloudflare Pages (production)
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
run: |
|
||||
npx wrangler@^4.0.0 pages deploy storybook-static \
|
||||
--project-name=comfy-storybook \
|
||||
--branch=main
|
||||
|
||||
# Update comment with Chromatic URLs for version-bump branches
|
||||
update-comment-with-chromatic:
|
||||
needs: [chromatic-deployment, deploy-and-comment]
|
||||
|
||||
1
.gitignore
vendored
@@ -26,6 +26,7 @@ dist-ssr
|
||||
.claude/*.local.json
|
||||
.claude/*.local.md
|
||||
.claude/*.local.txt
|
||||
.claude/worktrees
|
||||
CLAUDE.local.md
|
||||
|
||||
# Editor directories and files
|
||||
|
||||
47
browser_tests/assets/nodes/load_image_with_ksampler.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"last_node_id": 2,
|
||||
"last_link_id": 0,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "LoadImage",
|
||||
"pos": [50, 50],
|
||||
"size": [315, 314],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{ "name": "IMAGE", "type": "IMAGE", "links": null },
|
||||
{ "name": "MASK", "type": "MASK", "links": null }
|
||||
],
|
||||
"properties": { "Node name for S&R": "LoadImage" },
|
||||
"widgets_values": ["example.png", "image"]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "KSampler",
|
||||
"pos": [500, 50],
|
||||
"size": [315, 262],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{ "name": "model", "type": "MODEL", "link": null },
|
||||
{ "name": "positive", "type": "CONDITIONING", "link": null },
|
||||
{ "name": "negative", "type": "CONDITIONING", "link": null },
|
||||
{ "name": "latent_image", "type": "LATENT", "link": null }
|
||||
],
|
||||
"outputs": [{ "name": "LATENT", "type": "LATENT", "links": null }],
|
||||
"properties": { "Node name for S&R": "KSampler" },
|
||||
"widgets_values": [0, "randomize", 20, 8, "euler", "normal", 1]
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": { "offset": [0, 0], "scale": 1 }
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -333,7 +333,7 @@ test.describe('Settings', () => {
|
||||
await editKeybindingButton.click()
|
||||
|
||||
// Set new keybinding
|
||||
const input = comfyPage.page.getByPlaceholder('Press keys for new binding')
|
||||
const input = comfyPage.page.getByPlaceholder('Enter your keybind')
|
||||
await input.press('Alt+n')
|
||||
|
||||
const requestPromise = comfyPage.page.waitForRequest(
|
||||
@@ -345,7 +345,7 @@ test.describe('Settings', () => {
|
||||
|
||||
// Save keybinding
|
||||
const saveButton = comfyPage.page
|
||||
.getByLabel('New Blank Workflow')
|
||||
.getByLabel('Modify keybinding')
|
||||
.getByText('Save')
|
||||
await saveButton.click()
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
58
browser_tests/tests/imagePastePriority.spec.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.describe(
|
||||
'Image paste priority over stale node metadata',
|
||||
{ tag: ['@node'] },
|
||||
() => {
|
||||
test('Should not paste copied node when a LoadImage node is selected and clipboard has stale node metadata', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.workflow.loadWorkflow('nodes/load_image_with_ksampler')
|
||||
|
||||
const initialCount = await comfyPage.nodeOps.getGraphNodesCount()
|
||||
expect(initialCount).toBe(2)
|
||||
|
||||
// Copy the KSampler node (puts data-metadata in clipboard)
|
||||
const ksamplerNodes =
|
||||
await comfyPage.nodeOps.getNodeRefsByType('KSampler')
|
||||
await ksamplerNodes[0].copy()
|
||||
|
||||
// Select the LoadImage node
|
||||
const loadImageNodes =
|
||||
await comfyPage.nodeOps.getNodeRefsByType('LoadImage')
|
||||
await loadImageNodes[0].click('title')
|
||||
|
||||
// Simulate pasting when clipboard has stale node metadata (text/html
|
||||
// with data-metadata) but no image file items. This replicates the bug
|
||||
// scenario: user copied a node, then copied a web image (which replaces
|
||||
// clipboard files but may leave stale text/html with node metadata).
|
||||
await comfyPage.page.evaluate(() => {
|
||||
const nodeData = { nodes: [{ type: 'KSampler', id: 99 }] }
|
||||
const base64 = btoa(JSON.stringify(nodeData))
|
||||
const html =
|
||||
'<meta charset="utf-8"><div><span data-metadata="' +
|
||||
base64 +
|
||||
'"></span></div><span style="white-space:pre-wrap;">Text</span>'
|
||||
|
||||
const dataTransfer = new DataTransfer()
|
||||
dataTransfer.setData('text/html', html)
|
||||
|
||||
const event = new ClipboardEvent('paste', {
|
||||
clipboardData: dataTransfer,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
})
|
||||
document.dispatchEvent(event)
|
||||
})
|
||||
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Node count should remain the same — stale node metadata should NOT
|
||||
// be deserialized when a media node is selected.
|
||||
const finalCount = await comfyPage.nodeOps.getGraphNodesCount()
|
||||
expect(finalCount).toBe(initialCount)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
91
docs/architecture/change-tracker.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Change Tracker (Undo/Redo System)
|
||||
|
||||
The `ChangeTracker` class (`src/scripts/changeTracker.ts`) manages undo/redo
|
||||
history by comparing serialized graph snapshots.
|
||||
|
||||
## How It Works
|
||||
|
||||
`checkState()` is the core method. It:
|
||||
|
||||
1. Serializes the current graph via `app.rootGraph.serialize()`
|
||||
2. Deep-compares the result against the last known `activeState`
|
||||
3. If different, pushes `activeState` onto `undoQueue` and replaces it
|
||||
|
||||
**It is not reactive.** Changes to the graph (widget values, node positions,
|
||||
links, etc.) are only captured when `checkState()` is explicitly triggered.
|
||||
|
||||
## Automatic Triggers
|
||||
|
||||
These are set up once in `ChangeTracker.init()`:
|
||||
|
||||
| Trigger | Event / Hook | What It Catches |
|
||||
| ----------------------------------- | -------------------------------------------------- | --------------------------------------------------- |
|
||||
| Keyboard (non-modifier, non-repeat) | `window` `keydown` | Shortcuts, typing in canvas |
|
||||
| Modifier key release | `window` `keyup` | Releasing Ctrl/Shift/Alt/Meta |
|
||||
| Mouse click | `window` `mouseup` | General clicks on native DOM |
|
||||
| Canvas mouse up | `LGraphCanvas.processMouseUp` override | LiteGraph canvas interactions |
|
||||
| Number/string dialog | `LGraphCanvas.prompt` override | Dialog popups for editing widgets |
|
||||
| Context menu close | `LiteGraph.ContextMenu.close` override | COMBO widget menus in LiteGraph |
|
||||
| Active input element | `bindInput` (change/input/blur on focused element) | Native HTML input edits |
|
||||
| Prompt queued | `api` `promptQueued` event | Dynamic widget changes on queue |
|
||||
| Graph cleared | `api` `graphCleared` event | Full graph clear |
|
||||
| Transaction end | `litegraph:canvas` `after-change` event | Batched operations via `beforeChange`/`afterChange` |
|
||||
|
||||
## When You Must Call `checkState()` Manually
|
||||
|
||||
The automatic triggers above are designed around LiteGraph's native DOM
|
||||
rendering. They **do not cover**:
|
||||
|
||||
- **Vue-rendered widgets** — Vue handles events internally without triggering
|
||||
native DOM events that the tracker listens to (e.g., `mouseup` on a Vue
|
||||
dropdown doesn't bubble the same way as a native LiteGraph widget click)
|
||||
- **Programmatic graph mutations** — Any code that modifies the graph outside
|
||||
of user interaction (e.g., applying a template, pasting nodes, aligning)
|
||||
- **Async operations** — File uploads, API calls that change widget values
|
||||
after the initial user gesture
|
||||
|
||||
### Pattern for Manual Calls
|
||||
|
||||
```typescript
|
||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||
|
||||
// After mutating the graph:
|
||||
useWorkflowStore().activeWorkflow?.changeTracker?.checkState()
|
||||
```
|
||||
|
||||
### Existing Manual Call Sites
|
||||
|
||||
These locations already call `checkState()` explicitly:
|
||||
|
||||
- `WidgetSelectDropdown.vue` — After dropdown selection and file upload
|
||||
- `ColorPickerButton.vue` — After changing node colors
|
||||
- `NodeSearchBoxPopover.vue` — After adding a node from search
|
||||
- `useAppSetDefaultView.ts` — After setting default view
|
||||
- `useSelectionOperations.ts` — After align, copy, paste, duplicate, group
|
||||
- `useSelectedNodeActions.ts` — After pin, bypass, collapse
|
||||
- `useGroupMenuOptions.ts` — After group operations
|
||||
- `useSubgraphOperations.ts` — After subgraph enter/exit
|
||||
- `useCanvasRefresh.ts` — After canvas refresh
|
||||
- `useCoreCommands.ts` — After metadata/subgraph commands
|
||||
- `workflowService.ts` — After workflow service operations
|
||||
|
||||
## Transaction Guards
|
||||
|
||||
For operations that make multiple changes that should be a single undo entry:
|
||||
|
||||
```typescript
|
||||
changeTracker.beforeChange()
|
||||
// ... multiple graph mutations ...
|
||||
changeTracker.afterChange() // calls checkState() when nesting count hits 0
|
||||
```
|
||||
|
||||
The `litegraph:canvas` custom event also supports this with `before-change` /
|
||||
`after-change` sub-types.
|
||||
|
||||
## Key Invariants
|
||||
|
||||
- `checkState()` is a no-op during `loadGraphData` (guarded by
|
||||
`isLoadingGraph`) to prevent cross-workflow corruption
|
||||
- `checkState()` is a no-op when `changeCount > 0` (inside a transaction)
|
||||
- `undoQueue` is capped at 50 entries (`MAX_HISTORY`)
|
||||
- `graphEqual` ignores node order and `ds` (pan/zoom) when comparing
|
||||
1
global.d.ts
vendored
@@ -35,6 +35,7 @@ interface Window {
|
||||
mixpanel_token?: string
|
||||
posthog_project_token?: string
|
||||
posthog_api_host?: string
|
||||
posthog_config?: Record<string, unknown>
|
||||
require_whitelist?: boolean
|
||||
subscription_required?: boolean
|
||||
max_upload_size?: number
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"version": "1.42.2",
|
||||
"version": "1.42.3",
|
||||
"private": true,
|
||||
"description": "Official front-end implementation of ComfyUI",
|
||||
"homepage": "https://comfy.org",
|
||||
|
||||
@@ -25,15 +25,13 @@
|
||||
class: {
|
||||
'p-3 rounded-lg': true,
|
||||
'pointer-events-none':
|
||||
bottomPanelStore.bottomPanelTabs.length === 1
|
||||
},
|
||||
style: {
|
||||
color: 'var(--fg-color)',
|
||||
backgroundColor:
|
||||
bottomPanelStore.bottomPanelTabs.length === 1,
|
||||
'bg-secondary-background text-secondary-foreground':
|
||||
x.context.active &&
|
||||
bottomPanelStore.bottomPanelTabs.length > 1,
|
||||
'text-muted-foreground':
|
||||
!x.context.active ||
|
||||
bottomPanelStore.bottomPanelTabs.length === 1
|
||||
? ''
|
||||
: 'var(--bg-color)'
|
||||
bottomPanelStore.bottomPanelTabs.length <= 1
|
||||
}
|
||||
})
|
||||
"
|
||||
@@ -127,4 +125,8 @@ const closeBottomPanel = () => {
|
||||
:deep(.p-tablist-active-bar) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.p-tab-active) {
|
||||
color: inherit;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -43,6 +43,9 @@ export function useAppSetDefaultView() {
|
||||
const extra = (app.rootGraph.extra ??= {})
|
||||
extra.linearMode = openAsApp
|
||||
workflow.changeTracker?.checkState()
|
||||
useTelemetry()?.trackDefaultViewSet({
|
||||
default_view: openAsApp ? 'app' : 'graph'
|
||||
})
|
||||
closeDialog()
|
||||
showAppliedDialog(openAsApp)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<!-- Node -->
|
||||
<div
|
||||
v-if="item.value.type === 'node'"
|
||||
v-bind="$attrs"
|
||||
:class="cn(ROW_CLASS, isSelected && 'bg-comfy-input')"
|
||||
:style="rowStyle"
|
||||
draggable="true"
|
||||
@@ -48,6 +49,7 @@
|
||||
<!-- Folder -->
|
||||
<div
|
||||
v-else
|
||||
v-bind="$attrs"
|
||||
:class="cn(ROW_CLASS, isSelected && 'bg-comfy-input')"
|
||||
:style="rowStyle"
|
||||
@click.stop="handleClick($event, handleToggle, handleSelect)"
|
||||
@@ -98,6 +100,10 @@ import { InjectKeyContextMenuNode } from '@/types/treeExplorerTypes'
|
||||
import type { RenderedTreeExplorerNode } from '@/types/treeExplorerTypes'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false
|
||||
})
|
||||
|
||||
const ROW_CLASS =
|
||||
'group/tree-node flex cursor-pointer select-none items-center gap-3 overflow-hidden py-2 outline-none hover:bg-comfy-input mx-2 rounded'
|
||||
|
||||
|
||||
@@ -59,7 +59,15 @@
|
||||
:pt="{ bodyCell: 'p-1 min-h-8' }"
|
||||
>
|
||||
<template #body="slotProps">
|
||||
<div class="truncate" :title="slotProps.data.id">
|
||||
<div
|
||||
class="flex items-center gap-1.5 truncate"
|
||||
:title="slotProps.data.id"
|
||||
>
|
||||
<i
|
||||
v-if="slotProps.data.keybinding?.combo.isBrowserReserved"
|
||||
v-tooltip="$t('g.browserReservedKeybindingTooltip')"
|
||||
class="icon-[lucide--triangle-alert] shrink-0 text-warning-background"
|
||||
/>
|
||||
{{ slotProps.data.label }}
|
||||
</div>
|
||||
</template>
|
||||
@@ -93,44 +101,6 @@
|
||||
</Column>
|
||||
</DataTable>
|
||||
|
||||
<Dialog
|
||||
v-model:visible="editDialogVisible"
|
||||
class="min-w-96"
|
||||
modal
|
||||
:header="currentEditingCommand?.label"
|
||||
@hide="cancelEdit"
|
||||
>
|
||||
<div>
|
||||
<InputText
|
||||
ref="keybindingInput"
|
||||
class="mb-2 text-center"
|
||||
:model-value="newBindingKeyCombo?.toString() ?? ''"
|
||||
:placeholder="$t('g.pressKeysForNewBinding')"
|
||||
autocomplete="off"
|
||||
fluid
|
||||
@keydown.stop.prevent="captureKeybinding"
|
||||
/>
|
||||
<Message v-if="existingKeybindingOnCombo" severity="warn">
|
||||
{{ $t('g.keybindingAlreadyExists') }}
|
||||
<Tag
|
||||
severity="secondary"
|
||||
:value="existingKeybindingOnCombo.commandId"
|
||||
/>
|
||||
</Message>
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button
|
||||
:variant="existingKeybindingOnCombo ? 'destructive' : 'primary'"
|
||||
autofocus
|
||||
@click="saveKeybinding"
|
||||
>
|
||||
<i
|
||||
:class="existingKeybindingOnCombo ? 'pi pi-pencil' : 'pi pi-check'"
|
||||
/>
|
||||
{{ existingKeybindingOnCombo ? $t('g.overwrite') : $t('g.save') }}
|
||||
</Button>
|
||||
</template>
|
||||
</Dialog>
|
||||
<Button
|
||||
v-tooltip="$t('g.resetAllKeybindingsTooltip')"
|
||||
class="mt-4 w-full"
|
||||
@@ -147,18 +117,14 @@
|
||||
import { FilterMatchMode } from '@primevue/core/api'
|
||||
import Column from 'primevue/column'
|
||||
import DataTable from 'primevue/datatable'
|
||||
import Dialog from 'primevue/dialog'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Message from 'primevue/message'
|
||||
import Tag from 'primevue/tag'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import { computed, ref, watchEffect } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import SearchInput from '@/components/ui/search-input/SearchInput.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { KeyComboImpl } from '@/platform/keybindings/keyCombo'
|
||||
import { KeybindingImpl } from '@/platform/keybindings/keybinding'
|
||||
import { useEditKeybindingDialog } from '@/composables/useEditKeybindingDialog'
|
||||
import type { KeybindingImpl } from '@/platform/keybindings/keybinding'
|
||||
import { useKeybindingService } from '@/platform/keybindings/keybindingService'
|
||||
import { useKeybindingStore } from '@/platform/keybindings/keybindingStore'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
@@ -195,50 +161,16 @@ const commandsData = computed<ICommandData[]>(() => {
|
||||
})
|
||||
|
||||
const selectedCommandData = ref<ICommandData | null>(null)
|
||||
const editDialogVisible = ref(false)
|
||||
const newBindingKeyCombo = ref<KeyComboImpl | null>(null)
|
||||
const currentEditingCommand = ref<ICommandData | null>(null)
|
||||
const keybindingInput = ref<InstanceType<typeof InputText> | null>(null)
|
||||
|
||||
const existingKeybindingOnCombo = computed<KeybindingImpl | null>(() => {
|
||||
if (!currentEditingCommand.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
// If the new keybinding is the same as the current editing command, then don't show the error
|
||||
if (
|
||||
currentEditingCommand.value.keybinding?.combo?.equals(
|
||||
newBindingKeyCombo.value
|
||||
)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!newBindingKeyCombo.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
return keybindingStore.getKeybinding(newBindingKeyCombo.value)
|
||||
})
|
||||
const editKeybindingDialog = useEditKeybindingDialog()
|
||||
|
||||
function editKeybinding(commandData: ICommandData) {
|
||||
currentEditingCommand.value = commandData
|
||||
newBindingKeyCombo.value = commandData.keybinding
|
||||
? commandData.keybinding.combo
|
||||
: null
|
||||
editDialogVisible.value = true
|
||||
editKeybindingDialog.show({
|
||||
commandId: commandData.id,
|
||||
commandLabel: commandData.label,
|
||||
currentCombo: commandData.keybinding?.combo ?? null
|
||||
})
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (editDialogVisible.value) {
|
||||
// nextTick doesn't work here, so we use a timeout instead
|
||||
setTimeout(() => {
|
||||
// @ts-expect-error - $el is an internal property of the InputText component
|
||||
keybindingInput.value?.$el?.focus()
|
||||
}, 300)
|
||||
}
|
||||
})
|
||||
|
||||
async function removeKeybinding(commandData: ICommandData) {
|
||||
if (commandData.keybinding) {
|
||||
keybindingStore.unsetKeybinding(commandData.keybinding)
|
||||
@@ -246,40 +178,6 @@ async function removeKeybinding(commandData: ICommandData) {
|
||||
}
|
||||
}
|
||||
|
||||
async function captureKeybinding(event: KeyboardEvent) {
|
||||
// Allow the use of keyboard shortcuts when adding keyboard shortcuts
|
||||
if (!event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
cancelEdit()
|
||||
return
|
||||
case 'Enter':
|
||||
await saveKeybinding()
|
||||
return
|
||||
}
|
||||
}
|
||||
const keyCombo = KeyComboImpl.fromEvent(event)
|
||||
newBindingKeyCombo.value = keyCombo
|
||||
}
|
||||
|
||||
function cancelEdit() {
|
||||
editDialogVisible.value = false
|
||||
currentEditingCommand.value = null
|
||||
newBindingKeyCombo.value = null
|
||||
}
|
||||
|
||||
async function saveKeybinding() {
|
||||
const commandId = currentEditingCommand.value?.id
|
||||
const combo = newBindingKeyCombo.value
|
||||
cancelEdit()
|
||||
if (!combo || commandId == undefined) return
|
||||
|
||||
const updated = keybindingStore.updateKeybindingOnCommand(
|
||||
new KeybindingImpl({ commandId, combo })
|
||||
)
|
||||
if (updated) await keybindingService.persistUserKeybindings()
|
||||
}
|
||||
|
||||
async function resetKeybinding(commandData: ICommandData) {
|
||||
if (keybindingStore.resetKeybindingForCommand(commandData.id)) {
|
||||
await keybindingService.persistUserKeybindings()
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="flex w-96 flex-col border-t border-border-default px-4">
|
||||
<p class="mb-4 text-sm text-muted-foreground">
|
||||
{{ $t('g.setAKeybindingForTheFollowing') }}
|
||||
</p>
|
||||
<div class="mb-4 text-sm text-base-foreground">
|
||||
{{ commandLabel }}
|
||||
</div>
|
||||
|
||||
<input
|
||||
class="text-foreground mb-4 w-full rounded-sm border border-border-default bg-secondary-background px-3 py-2 text-center shadow-none focus:outline-none"
|
||||
:value="dialogState.newCombo?.toString() ?? ''"
|
||||
:placeholder="$t('g.enterYourKeybind')"
|
||||
:aria-label="$t('g.enterYourKeybind')"
|
||||
autocomplete="off"
|
||||
autofocus
|
||||
@keydown.stop.prevent="captureKeybinding"
|
||||
/>
|
||||
<div class="min-h-12">
|
||||
<p
|
||||
v-if="dialogState.newCombo?.isBrowserReserved"
|
||||
class="m-0 text-sm text-destructive-background"
|
||||
>
|
||||
{{ $t('g.browserReservedKeybinding') }}
|
||||
</p>
|
||||
<p
|
||||
v-else-if="existingKeybindingOnCombo"
|
||||
class="m-0 text-sm text-destructive-background"
|
||||
>
|
||||
{{ $t('g.keybindingAlreadyExists') }}
|
||||
{{ existingKeybindingOnCombo.commandId }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { KeybindingImpl } from '@/platform/keybindings/keybinding'
|
||||
import { KeyComboImpl } from '@/platform/keybindings/keyCombo'
|
||||
|
||||
import type { EditKeybindingDialogState } from '@/composables/useEditKeybindingDialog'
|
||||
|
||||
const { dialogState, onUpdateCombo, existingKeybindingOnCombo } = defineProps<{
|
||||
dialogState: EditKeybindingDialogState
|
||||
commandLabel: string
|
||||
onUpdateCombo: (combo: KeyComboImpl) => void
|
||||
existingKeybindingOnCombo: KeybindingImpl | null
|
||||
}>()
|
||||
|
||||
function captureKeybinding(event: KeyboardEvent) {
|
||||
if (!event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {
|
||||
if (event.key === 'Escape') return
|
||||
}
|
||||
onUpdateCombo(KeyComboImpl.fromEvent(event))
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="flex w-full justify-end gap-2 px-4 py-2">
|
||||
<Button
|
||||
variant="textonly"
|
||||
size="md"
|
||||
class="text-muted-foreground"
|
||||
@click="handleCancel"
|
||||
>
|
||||
{{ $t('g.cancel') }}
|
||||
</Button>
|
||||
<Button
|
||||
:variant="
|
||||
existingKeybindingOnCombo
|
||||
? 'destructive'
|
||||
: dialogState.newCombo?.isBrowserReserved
|
||||
? 'secondary'
|
||||
: 'primary'
|
||||
"
|
||||
size="md"
|
||||
:disabled="!dialogState.newCombo"
|
||||
class="px-4 py-2"
|
||||
@click="handleSave"
|
||||
>
|
||||
{{
|
||||
existingKeybindingOnCombo
|
||||
? $t('g.overwrite')
|
||||
: dialogState.newCombo?.isBrowserReserved
|
||||
? $t('g.saveAnyway')
|
||||
: $t('g.save')
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Reactive } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { KeybindingImpl } from '@/platform/keybindings/keybinding'
|
||||
import { useKeybindingService } from '@/platform/keybindings/keybindingService'
|
||||
import { useKeybindingStore } from '@/platform/keybindings/keybindingStore'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
|
||||
import type { EditKeybindingDialogState } from '@/composables/useEditKeybindingDialog'
|
||||
import { DIALOG_KEY } from '@/composables/useEditKeybindingDialog'
|
||||
|
||||
const { dialogState, existingKeybindingOnCombo } = defineProps<{
|
||||
dialogState: Reactive<EditKeybindingDialogState>
|
||||
existingKeybindingOnCombo: KeybindingImpl | null
|
||||
}>()
|
||||
|
||||
const keybindingStore = useKeybindingStore()
|
||||
const keybindingService = useKeybindingService()
|
||||
const dialogStore = useDialogStore()
|
||||
|
||||
function handleCancel() {
|
||||
dialogStore.closeDialog({ key: DIALOG_KEY })
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
const combo = dialogState.newCombo
|
||||
const commandId = dialogState.commandId
|
||||
if (!combo || !commandId) return
|
||||
|
||||
dialogStore.closeDialog({ key: DIALOG_KEY })
|
||||
|
||||
const updated = keybindingStore.updateKeybindingOnCommand(
|
||||
new KeybindingImpl({ commandId, combo })
|
||||
)
|
||||
if (updated) await keybindingService.persistUserKeybindings()
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex w-full items-center gap-2 p-4">
|
||||
<p class="m-0 font-semibold">{{ $t('g.modifyKeybinding') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
153
src/components/graph/CanvasModeSelector.test.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import CanvasModeSelector from '@/components/graph/CanvasModeSelector.vue'
|
||||
|
||||
const mockExecute = vi.fn()
|
||||
const mockGetCommand = vi.fn().mockReturnValue({
|
||||
keybinding: {
|
||||
combo: {
|
||||
getKeySequences: () => ['V']
|
||||
}
|
||||
}
|
||||
})
|
||||
const mockFormatKeySequence = vi.fn().mockReturnValue('V')
|
||||
|
||||
vi.mock('@/stores/commandStore', () => ({
|
||||
useCommandStore: () => ({
|
||||
execute: mockExecute,
|
||||
getCommand: mockGetCommand,
|
||||
formatKeySequence: mockFormatKeySequence
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/renderer/core/canvas/canvasStore', () => ({
|
||||
useCanvasStore: () => ({
|
||||
canvas: { read_only: false }
|
||||
})
|
||||
}))
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: 'en',
|
||||
messages: {
|
||||
en: {
|
||||
graphCanvasMenu: {
|
||||
select: 'Select',
|
||||
hand: 'Hand',
|
||||
canvasMode: 'Canvas Mode'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const mockPopoverHide = vi.fn()
|
||||
|
||||
function createWrapper() {
|
||||
return mount(CanvasModeSelector, {
|
||||
global: {
|
||||
plugins: [i18n],
|
||||
stubs: {
|
||||
Popover: {
|
||||
template: '<div><slot /></div>',
|
||||
methods: {
|
||||
toggle: vi.fn(),
|
||||
hide: mockPopoverHide
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
describe('CanvasModeSelector', () => {
|
||||
it('should render menu with menuitemradio roles and aria-checked', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const menu = wrapper.find('[role="menu"]')
|
||||
expect(menu.exists()).toBe(true)
|
||||
|
||||
const menuItems = wrapper.findAll('[role="menuitemradio"]')
|
||||
expect(menuItems).toHaveLength(2)
|
||||
|
||||
// Select mode is active (read_only: false), so select is checked
|
||||
expect(menuItems[0].attributes('aria-checked')).toBe('true')
|
||||
expect(menuItems[1].attributes('aria-checked')).toBe('false')
|
||||
})
|
||||
|
||||
it('should render menu items as buttons with aria-labels', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const menuItems = wrapper.findAll('[role="menuitemradio"]')
|
||||
menuItems.forEach((btn) => {
|
||||
expect(btn.element.tagName).toBe('BUTTON')
|
||||
expect(btn.attributes('type')).toBe('button')
|
||||
})
|
||||
expect(menuItems[0].attributes('aria-label')).toBe('Select')
|
||||
expect(menuItems[1].attributes('aria-label')).toBe('Hand')
|
||||
})
|
||||
|
||||
it('should use roving tabindex based on active mode', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const menuItems = wrapper.findAll('[role="menuitemradio"]')
|
||||
// Select is active (read_only: false) → tabindex 0
|
||||
expect(menuItems[0].attributes('tabindex')).toBe('0')
|
||||
// Hand is inactive → tabindex -1
|
||||
expect(menuItems[1].attributes('tabindex')).toBe('-1')
|
||||
})
|
||||
|
||||
it('should mark icons as aria-hidden', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const icons = wrapper.findAll('[role="menuitemradio"] i')
|
||||
icons.forEach((icon) => {
|
||||
expect(icon.attributes('aria-hidden')).toBe('true')
|
||||
})
|
||||
})
|
||||
|
||||
it('should expose trigger button with aria-haspopup and aria-expanded', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const trigger = wrapper.find('[aria-haspopup="menu"]')
|
||||
expect(trigger.exists()).toBe(true)
|
||||
expect(trigger.attributes('aria-label')).toBe('Canvas Mode')
|
||||
expect(trigger.attributes('aria-expanded')).toBe('false')
|
||||
})
|
||||
|
||||
it('should call focus on next item when ArrowDown is pressed', async () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const menuItems = wrapper.findAll('[role="menuitemradio"]')
|
||||
const secondItemEl = menuItems[1].element as HTMLElement
|
||||
const focusSpy = vi.spyOn(secondItemEl, 'focus')
|
||||
|
||||
await menuItems[0].trigger('keydown', { key: 'ArrowDown' })
|
||||
expect(focusSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should call focus on previous item when ArrowUp is pressed', async () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const menuItems = wrapper.findAll('[role="menuitemradio"]')
|
||||
const firstItemEl = menuItems[0].element as HTMLElement
|
||||
const focusSpy = vi.spyOn(firstItemEl, 'focus')
|
||||
|
||||
await menuItems[1].trigger('keydown', { key: 'ArrowUp' })
|
||||
expect(focusSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should close popover on Escape and restore focus to trigger', async () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const menuItems = wrapper.findAll('[role="menuitemradio"]')
|
||||
const trigger = wrapper.find('[aria-haspopup="menu"]')
|
||||
const triggerEl = trigger.element as HTMLElement
|
||||
const focusSpy = vi.spyOn(triggerEl, 'focus')
|
||||
|
||||
await menuItems[0].trigger('keydown', { key: 'Escape' })
|
||||
expect(mockPopoverHide).toHaveBeenCalled()
|
||||
expect(focusSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -4,15 +4,21 @@
|
||||
variant="secondary"
|
||||
class="group h-8 rounded-none! bg-comfy-menu-bg p-0 transition-none! hover:rounded-lg! hover:bg-interface-button-hover-surface!"
|
||||
:style="buttonStyles"
|
||||
:aria-label="$t('graphCanvasMenu.canvasMode')"
|
||||
aria-haspopup="menu"
|
||||
:aria-expanded="isOpen"
|
||||
@click="toggle"
|
||||
>
|
||||
<div class="flex items-center gap-1 pr-0.5">
|
||||
<div
|
||||
class="rounded-lg bg-interface-panel-selected-surface p-2 group-hover:bg-interface-button-hover-surface"
|
||||
>
|
||||
<i :class="currentModeIcon" class="block size-4" />
|
||||
<i :class="currentModeIcon" class="block size-4" aria-hidden="true" />
|
||||
</div>
|
||||
<i class="icon-[lucide--chevron-down] block size-4 pr-1.5" />
|
||||
<i
|
||||
class="icon-[lucide--chevron-down] block size-4 pr-1.5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
@@ -24,31 +30,54 @@
|
||||
:close-on-escape="true"
|
||||
unstyled
|
||||
:pt="popoverPt"
|
||||
@show="onPopoverShow"
|
||||
@hide="onPopoverHide"
|
||||
>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div
|
||||
class="flex cursor-pointer items-center justify-between px-3 py-2 text-sm hover:bg-node-component-surface-hovered"
|
||||
<div
|
||||
ref="menuRef"
|
||||
class="flex flex-col gap-1"
|
||||
role="menu"
|
||||
:aria-label="$t('graphCanvasMenu.canvasMode')"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
role="menuitemradio"
|
||||
:aria-checked="!isCanvasReadOnly"
|
||||
:tabindex="!isCanvasReadOnly ? 0 : -1"
|
||||
class="flex w-full cursor-pointer items-center justify-between rounded-sm border-none bg-transparent px-3 py-2 text-sm text-text-primary outline-none hover:bg-node-component-surface-hovered focus-visible:bg-node-component-surface-hovered"
|
||||
:aria-label="$t('graphCanvasMenu.select')"
|
||||
@click="setMode('select')"
|
||||
@keydown.arrow-down.prevent="focusNextItem"
|
||||
@keydown.arrow-up.prevent="focusPrevItem"
|
||||
@keydown.escape.prevent="closeAndRestoreFocus"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="icon-[lucide--mouse-pointer-2] size-4" />
|
||||
<i class="icon-[lucide--mouse-pointer-2] size-4" aria-hidden="true" />
|
||||
<span>{{ $t('graphCanvasMenu.select') }}</span>
|
||||
</div>
|
||||
<span class="text-[9px] text-text-primary">{{
|
||||
unlockCommandText
|
||||
}}</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="flex cursor-pointer items-center justify-between rounded-sm px-3 py-2 text-sm hover:bg-node-component-surface-hovered"
|
||||
<button
|
||||
type="button"
|
||||
role="menuitemradio"
|
||||
:aria-checked="isCanvasReadOnly"
|
||||
:tabindex="isCanvasReadOnly ? 0 : -1"
|
||||
class="flex w-full cursor-pointer items-center justify-between rounded-sm border-none bg-transparent px-3 py-2 text-sm text-text-primary outline-none hover:bg-node-component-surface-hovered focus-visible:bg-node-component-surface-hovered"
|
||||
:aria-label="$t('graphCanvasMenu.hand')"
|
||||
@click="setMode('hand')"
|
||||
@keydown.arrow-down.prevent="focusNextItem"
|
||||
@keydown.arrow-up.prevent="focusPrevItem"
|
||||
@keydown.escape.prevent="closeAndRestoreFocus"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="icon-[lucide--hand] size-4" />
|
||||
<i class="icon-[lucide--hand] size-4" aria-hidden="true" />
|
||||
<span>{{ $t('graphCanvasMenu.hand') }}</span>
|
||||
</div>
|
||||
<span class="text-[9px] text-text-primary">{{ lockCommandText }}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</Popover>
|
||||
</template>
|
||||
@@ -56,7 +85,7 @@
|
||||
<script setup lang="ts">
|
||||
import Popover from 'primevue/popover'
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
@@ -69,6 +98,8 @@ interface Props {
|
||||
defineProps<Props>()
|
||||
const buttonRef = ref<ComponentPublicInstance | null>(null)
|
||||
const popover = ref<InstanceType<typeof Popover>>()
|
||||
const menuRef = ref<HTMLElement | null>(null)
|
||||
const isOpen = ref(false)
|
||||
const commandStore = useCommandStore()
|
||||
const canvasStore = useCanvasStore()
|
||||
|
||||
@@ -106,6 +137,43 @@ const setMode = (mode: 'select' | 'hand') => {
|
||||
popover.value?.hide()
|
||||
}
|
||||
|
||||
async function onPopoverShow() {
|
||||
isOpen.value = true
|
||||
await nextTick()
|
||||
const checkedItem = menuRef.value?.querySelector<HTMLElement>(
|
||||
'[aria-checked="true"]'
|
||||
)
|
||||
checkedItem?.focus()
|
||||
}
|
||||
|
||||
function onPopoverHide() {
|
||||
isOpen.value = false
|
||||
}
|
||||
|
||||
function closeAndRestoreFocus() {
|
||||
popover.value?.hide()
|
||||
const el = buttonRef.value?.$el || buttonRef.value
|
||||
;(el as HTMLElement)?.focus()
|
||||
}
|
||||
|
||||
function focusNextItem(event: KeyboardEvent) {
|
||||
const items = getMenuItems(event)
|
||||
const index = items.indexOf(event.target as HTMLElement)
|
||||
items[(index + 1) % items.length]?.focus()
|
||||
}
|
||||
|
||||
function focusPrevItem(event: KeyboardEvent) {
|
||||
const items = getMenuItems(event)
|
||||
const index = items.indexOf(event.target as HTMLElement)
|
||||
items[(index - 1 + items.length) % items.length]?.focus()
|
||||
}
|
||||
|
||||
function getMenuItems(event: KeyboardEvent): HTMLElement[] {
|
||||
const menu = (event.target as HTMLElement).closest('[role="menu"]')
|
||||
if (!menu) return []
|
||||
return Array.from(menu.querySelectorAll('[role="menuitemradio"]'))
|
||||
}
|
||||
|
||||
const popoverPt = computed(() => ({
|
||||
root: {
|
||||
class: 'absolute z-50 -translate-y-2'
|
||||
|
||||
@@ -538,7 +538,7 @@ onMounted(async () => {
|
||||
|
||||
// Restore saved workflow and workflow tabs state
|
||||
await workflowPersistence.initializeWorkflow()
|
||||
workflowPersistence.restoreWorkflowTabsState()
|
||||
await workflowPersistence.restoreWorkflowTabsState()
|
||||
|
||||
const sharedWorkflowLoadStatus =
|
||||
await workflowPersistence.loadSharedWorkflowFromUrlIfPresent()
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
></div>
|
||||
|
||||
<ButtonGroup
|
||||
role="toolbar"
|
||||
:aria-label="t('graphCanvasMenu.canvasToolbar')"
|
||||
class="absolute right-0 bottom-0 z-1200 flex-row gap-1 border border-interface-stroke bg-comfy-menu-bg p-2"
|
||||
:style="{
|
||||
...stringifiedMinimapStyles.buttonGroupStyles
|
||||
@@ -30,7 +32,7 @@
|
||||
class="size-8 bg-comfy-menu-bg p-0 hover:bg-interface-button-hover-surface!"
|
||||
@click="() => commandStore.execute('Comfy.Canvas.FitView')"
|
||||
>
|
||||
<i class="icon-[lucide--focus] size-4" />
|
||||
<i class="icon-[lucide--focus] size-4" aria-hidden="true" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@@ -44,7 +46,7 @@
|
||||
>
|
||||
<span class="inline-flex items-center gap-1 px-2 text-xs">
|
||||
<span>{{ canvasStore.appScalePercentage }}%</span>
|
||||
<i class="icon-[lucide--chevron-down] size-4" />
|
||||
<i class="icon-[lucide--chevron-down] size-4" aria-hidden="true" />
|
||||
</span>
|
||||
</Button>
|
||||
|
||||
@@ -59,7 +61,7 @@
|
||||
:class="minimapButtonClass"
|
||||
@click="onMinimapToggleClick"
|
||||
>
|
||||
<i class="icon-[lucide--map] size-4" />
|
||||
<i class="icon-[lucide--map] size-4" aria-hidden="true" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@@ -78,7 +80,7 @@
|
||||
:style="stringifiedMinimapStyles.buttonStyles"
|
||||
@click="onLinkVisibilityToggleClick"
|
||||
>
|
||||
<i class="icon-[lucide--route-off] size-4" />
|
||||
<i class="icon-[lucide--route-off] size-4" aria-hidden="true" />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import { SubgraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useMissingModelStore } from '@/platform/missingModel/missingModelStore'
|
||||
import { useExecutionErrorStore } from '@/stores/executionErrorStore'
|
||||
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
|
||||
import type { RightSidePanelTab } from '@/stores/workspace/rightSidePanelStore'
|
||||
@@ -36,6 +37,7 @@ import TabErrors from './errors/TabErrors.vue'
|
||||
|
||||
const canvasStore = useCanvasStore()
|
||||
const executionErrorStore = useExecutionErrorStore()
|
||||
const missingModelStore = useMissingModelStore()
|
||||
const rightSidePanelStore = useRightSidePanelStore()
|
||||
const settingStore = useSettingStore()
|
||||
const { t } = useI18n()
|
||||
@@ -43,6 +45,8 @@ const { t } = useI18n()
|
||||
const { hasAnyError, allErrorExecutionIds, activeMissingNodeGraphIds } =
|
||||
storeToRefs(executionErrorStore)
|
||||
|
||||
const { activeMissingModelGraphIds } = storeToRefs(missingModelStore)
|
||||
|
||||
const { findParentGroup } = useGraphHierarchy()
|
||||
|
||||
const { selectedItems: directlySelectedItems } = storeToRefs(canvasStore)
|
||||
@@ -118,12 +122,21 @@ const hasMissingNodeSelected = computed(
|
||||
)
|
||||
)
|
||||
|
||||
const hasMissingModelSelected = computed(
|
||||
() =>
|
||||
hasSelection.value &&
|
||||
selectedNodes.value.some((node) =>
|
||||
activeMissingModelGraphIds.value.has(String(node.id))
|
||||
)
|
||||
)
|
||||
|
||||
const hasRelevantErrors = computed(() => {
|
||||
if (!hasSelection.value) return hasAnyError.value
|
||||
return (
|
||||
hasDirectNodeError.value ||
|
||||
hasContainerInternalError.value ||
|
||||
hasMissingNodeSelected.value
|
||||
hasMissingNodeSelected.value ||
|
||||
hasMissingModelSelected.value
|
||||
)
|
||||
})
|
||||
|
||||
@@ -314,7 +327,11 @@ function handleTitleCancel() {
|
||||
:value="tab.value"
|
||||
>
|
||||
{{ tab.label() }}
|
||||
<i v-if="tab.icon" :class="cn(tab.icon, 'size-4')" />
|
||||
<i
|
||||
v-if="tab.icon"
|
||||
aria-hidden="true"
|
||||
:class="cn(tab.icon, 'size-4')"
|
||||
/>
|
||||
</Tab>
|
||||
</TabList>
|
||||
</nav>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Scrollable content -->
|
||||
<div class="min-w-0 flex-1 overflow-y-auto">
|
||||
<div class="min-w-0 flex-1 overflow-y-auto" aria-live="polite">
|
||||
<TransitionGroup tag="div" name="list-scale" class="relative">
|
||||
<div
|
||||
v-if="filteredGroups.length === 0"
|
||||
@@ -32,11 +32,7 @@
|
||||
:key="group.title"
|
||||
:collapse="isSectionCollapsed(group.title) && !isSearching"
|
||||
class="border-b border-interface-stroke"
|
||||
:size="
|
||||
group.type === 'missing_node' || group.type === 'swap_nodes'
|
||||
? 'lg'
|
||||
: 'default'
|
||||
"
|
||||
:size="getGroupSize(group)"
|
||||
@update:collapse="setSectionCollapsed(group.title, $event)"
|
||||
>
|
||||
<template #label>
|
||||
@@ -130,6 +126,14 @@
|
||||
@copy-to-clipboard="copyToClipboard"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Missing Models -->
|
||||
<MissingModelCard
|
||||
v-else-if="group.type === 'missing_model'"
|
||||
:missing-model-groups="missingModelGroups"
|
||||
:show-node-id-badge="showNodeIdBadge"
|
||||
@locate-model="handleLocateModel"
|
||||
/>
|
||||
</PropertiesAccordionItem>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
@@ -187,12 +191,14 @@ import FormSearchInput from '@/renderer/extensions/vueNodes/widgets/components/f
|
||||
import ErrorNodeCard from './ErrorNodeCard.vue'
|
||||
import MissingNodeCard from './MissingNodeCard.vue'
|
||||
import SwapNodesCard from '@/platform/nodeReplacement/components/SwapNodesCard.vue'
|
||||
import MissingModelCard from '@/platform/missingModel/components/MissingModelCard.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import DotSpinner from '@/components/common/DotSpinner.vue'
|
||||
import { usePackInstall } from '@/workbench/extensions/manager/composables/nodePack/usePackInstall'
|
||||
import { useMissingNodes } from '@/workbench/extensions/manager/composables/nodePack/useMissingNodes'
|
||||
import { useErrorGroups } from './useErrorGroups'
|
||||
import type { SwapNodeGroup } from './useErrorGroups'
|
||||
import type { ErrorGroup } from './types'
|
||||
import { useNodeReplacement } from '@/platform/nodeReplacement/useNodeReplacement'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -211,6 +217,15 @@ const { replaceGroup, replaceAllGroups } = useNodeReplacement()
|
||||
const searchQuery = ref('')
|
||||
const isSearching = computed(() => searchQuery.value.trim() !== '')
|
||||
|
||||
const fullSizeGroupTypes = new Set([
|
||||
'missing_node',
|
||||
'swap_nodes',
|
||||
'missing_model'
|
||||
])
|
||||
function getGroupSize(group: ErrorGroup) {
|
||||
return fullSizeGroupTypes.has(group.type) ? 'lg' : 'default'
|
||||
}
|
||||
|
||||
const showNodeIdBadge = computed(
|
||||
() =>
|
||||
(settingStore.get('Comfy.NodeBadge.NodeIdBadgeMode') as NodeBadgeMode) !==
|
||||
@@ -226,6 +241,7 @@ const {
|
||||
errorNodeCache,
|
||||
missingNodeCache,
|
||||
missingPackGroups,
|
||||
missingModelGroups,
|
||||
swapNodeGroups
|
||||
} = useErrorGroups(searchQuery, t)
|
||||
|
||||
@@ -283,6 +299,10 @@ function handleLocateMissingNode(nodeId: string) {
|
||||
focusNode(nodeId, missingNodeCache.value)
|
||||
}
|
||||
|
||||
function handleLocateModel(nodeId: string) {
|
||||
focusNode(nodeId)
|
||||
}
|
||||
|
||||
function handleOpenManagerInfo(packId: string) {
|
||||
const isKnownToRegistry = missingNodePacks.value.some((p) => p.id === packId)
|
||||
if (isKnownToRegistry) {
|
||||
|
||||
@@ -23,3 +23,4 @@ export type ErrorGroup =
|
||||
}
|
||||
| { type: 'missing_node'; title: string; priority: number }
|
||||
| { type: 'swap_nodes'; title: string; priority: number }
|
||||
| { type: 'missing_model'; title: string; priority: number }
|
||||
|
||||
@@ -47,6 +47,13 @@ vi.mock('@/utils/executableGroupNodeDto', () => ({
|
||||
isGroupNode: vi.fn(() => false)
|
||||
}))
|
||||
|
||||
vi.mock(
|
||||
'@/platform/missingModel/composables/useMissingModelInteractions',
|
||||
() => ({
|
||||
clearMissingModelState: vi.fn()
|
||||
})
|
||||
)
|
||||
|
||||
import { useExecutionErrorStore } from '@/stores/executionErrorStore'
|
||||
import { useErrorGroups } from './useErrorGroups'
|
||||
|
||||
@@ -520,4 +527,115 @@ describe('useErrorGroups', () => {
|
||||
expect(typeof groups.collapseState).toBe('object')
|
||||
})
|
||||
})
|
||||
|
||||
describe('missingModelGroups', () => {
|
||||
function makeModel(
|
||||
name: string,
|
||||
opts: {
|
||||
nodeId?: string | number
|
||||
widgetName?: string
|
||||
directory?: string
|
||||
isAssetSupported?: boolean
|
||||
} = {}
|
||||
) {
|
||||
return {
|
||||
name,
|
||||
nodeId: opts.nodeId ?? '1',
|
||||
nodeType: 'CheckpointLoaderSimple',
|
||||
widgetName: opts.widgetName ?? 'ckpt_name',
|
||||
isAssetSupported: opts.isAssetSupported ?? false,
|
||||
isMissing: true as const,
|
||||
directory: opts.directory
|
||||
}
|
||||
}
|
||||
|
||||
it('returns empty array when no missing models', () => {
|
||||
const { groups } = createErrorGroups()
|
||||
expect(groups.missingModelGroups.value).toEqual([])
|
||||
})
|
||||
|
||||
it('groups asset-supported models by directory', async () => {
|
||||
const { store, groups } = createErrorGroups()
|
||||
store.surfaceMissingModels([
|
||||
makeModel('model_a.safetensors', {
|
||||
directory: 'checkpoints',
|
||||
isAssetSupported: true
|
||||
}),
|
||||
makeModel('model_b.safetensors', {
|
||||
nodeId: '2',
|
||||
directory: 'checkpoints',
|
||||
isAssetSupported: true
|
||||
}),
|
||||
makeModel('lora_a.safetensors', {
|
||||
nodeId: '3',
|
||||
directory: 'loras',
|
||||
isAssetSupported: true
|
||||
})
|
||||
])
|
||||
await nextTick()
|
||||
|
||||
expect(groups.missingModelGroups.value).toHaveLength(2)
|
||||
const ckptGroup = groups.missingModelGroups.value.find(
|
||||
(g) => g.directory === 'checkpoints'
|
||||
)
|
||||
expect(ckptGroup?.models).toHaveLength(2)
|
||||
expect(ckptGroup?.isAssetSupported).toBe(true)
|
||||
})
|
||||
|
||||
it('puts unsupported models in a separate group', async () => {
|
||||
const { store, groups } = createErrorGroups()
|
||||
store.surfaceMissingModels([
|
||||
makeModel('model_a.safetensors', {
|
||||
directory: 'checkpoints',
|
||||
isAssetSupported: true
|
||||
}),
|
||||
makeModel('custom_model.safetensors', {
|
||||
nodeId: '2',
|
||||
isAssetSupported: false
|
||||
})
|
||||
])
|
||||
await nextTick()
|
||||
|
||||
expect(groups.missingModelGroups.value).toHaveLength(2)
|
||||
const unsupported = groups.missingModelGroups.value.find(
|
||||
(g) => !g.isAssetSupported
|
||||
)
|
||||
expect(unsupported?.models).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('merges same-named models into one view model with multiple referencingNodes', async () => {
|
||||
const { store, groups } = createErrorGroups()
|
||||
store.surfaceMissingModels([
|
||||
makeModel('shared_model.safetensors', {
|
||||
nodeId: '1',
|
||||
widgetName: 'ckpt_name',
|
||||
directory: 'checkpoints',
|
||||
isAssetSupported: true
|
||||
}),
|
||||
makeModel('shared_model.safetensors', {
|
||||
nodeId: '2',
|
||||
widgetName: 'ckpt_name',
|
||||
directory: 'checkpoints',
|
||||
isAssetSupported: true
|
||||
})
|
||||
])
|
||||
await nextTick()
|
||||
|
||||
expect(groups.missingModelGroups.value).toHaveLength(1)
|
||||
const model = groups.missingModelGroups.value[0].models[0]
|
||||
expect(model.name).toBe('shared_model.safetensors')
|
||||
expect(model.referencingNodes).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('includes missing_model group in allErrorGroups', async () => {
|
||||
const { store, groups } = createErrorGroups()
|
||||
store.surfaceMissingModels([makeModel('model_a.safetensors')])
|
||||
await nextTick()
|
||||
|
||||
const modelGroup = groups.allErrorGroups.value.find(
|
||||
(g) => g.type === 'missing_model'
|
||||
)
|
||||
expect(modelGroup).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { MaybeRefOrGetter } from 'vue'
|
||||
import Fuse from 'fuse.js'
|
||||
import type { IFuseOptions } from 'fuse.js'
|
||||
|
||||
import { useMissingModelStore } from '@/platform/missingModel/missingModelStore'
|
||||
import { useExecutionErrorStore } from '@/stores/executionErrorStore'
|
||||
import { useComfyRegistryStore } from '@/stores/comfyRegistryStore'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
@@ -23,6 +24,11 @@ import { st } from '@/i18n'
|
||||
import type { MissingNodeType } from '@/types/comfy'
|
||||
import type { ErrorCardData, ErrorGroup, ErrorItem } from './types'
|
||||
import type { NodeExecutionId } from '@/types/nodeIdentification'
|
||||
import type {
|
||||
MissingModelCandidate,
|
||||
MissingModelGroup
|
||||
} from '@/platform/missingModel/types'
|
||||
import { groupCandidatesByName } from '@/platform/missingModel/missingModelScan'
|
||||
import {
|
||||
isNodeExecutionId,
|
||||
compareExecutionId
|
||||
@@ -39,6 +45,9 @@ const KNOWN_PROMPT_ERROR_TYPES = new Set([
|
||||
/** Sentinel: distinguishes "fetch in-flight" from "fetch done, pack not found (null)". */
|
||||
const RESOLVING = '__RESOLVING__'
|
||||
|
||||
/** Sentinel key for grouping non-asset-supported missing models. */
|
||||
const UNSUPPORTED = Symbol('unsupported')
|
||||
|
||||
export interface MissingPackGroup {
|
||||
packId: string | null
|
||||
nodeTypes: MissingNodeType[]
|
||||
@@ -231,6 +240,7 @@ export function useErrorGroups(
|
||||
t: (key: string) => string
|
||||
) {
|
||||
const executionErrorStore = useExecutionErrorStore()
|
||||
const missingModelStore = useMissingModelStore()
|
||||
const canvasStore = useCanvasStore()
|
||||
const { inferPackFromNodeName } = useComfyRegistryStore()
|
||||
const collapseState = reactive<Record<string, boolean>>({})
|
||||
@@ -559,6 +569,60 @@ export function useErrorGroups(
|
||||
return groups.sort((a, b) => a.priority - b.priority)
|
||||
}
|
||||
|
||||
/** Groups missing models. Asset-supported models group by directory; others go into a separate group.
|
||||
* Within each group, candidates with the same model name are merged into a single view model. */
|
||||
const missingModelGroups = computed<MissingModelGroup[]>(() => {
|
||||
const candidates = missingModelStore.missingModelCandidates
|
||||
if (!candidates?.length) return []
|
||||
|
||||
type GroupKey = string | null | typeof UNSUPPORTED
|
||||
const map = new Map<
|
||||
GroupKey,
|
||||
{ candidates: MissingModelCandidate[]; isAssetSupported: boolean }
|
||||
>()
|
||||
|
||||
for (const c of candidates) {
|
||||
const groupKey: GroupKey = c.isAssetSupported
|
||||
? c.directory || null
|
||||
: UNSUPPORTED
|
||||
|
||||
const existing = map.get(groupKey)
|
||||
if (existing) {
|
||||
existing.candidates.push(c)
|
||||
} else {
|
||||
map.set(groupKey, {
|
||||
candidates: [c],
|
||||
isAssetSupported: c.isAssetSupported
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(map.entries())
|
||||
.sort(([dirA], [dirB]) => {
|
||||
if (dirA === UNSUPPORTED) return 1
|
||||
if (dirB === UNSUPPORTED) return -1
|
||||
if (dirA === null) return 1
|
||||
if (dirB === null) return -1
|
||||
return dirA.localeCompare(dirB)
|
||||
})
|
||||
.map(([key, { candidates: groupCandidates, isAssetSupported }]) => ({
|
||||
directory: typeof key === 'string' ? key : null,
|
||||
models: groupCandidatesByName(groupCandidates),
|
||||
isAssetSupported
|
||||
}))
|
||||
})
|
||||
|
||||
function buildMissingModelGroups(): ErrorGroup[] {
|
||||
if (!missingModelGroups.value.length) return []
|
||||
return [
|
||||
{
|
||||
type: 'missing_model' as const,
|
||||
title: `${t('rightSidePanel.missingModels.missingModelsTitle')} (${missingModelGroups.value.reduce((count, group) => count + group.models.length, 0)})`,
|
||||
priority: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const allErrorGroups = computed<ErrorGroup[]>(() => {
|
||||
const groupsMap = new Map<string, GroupEntry>()
|
||||
|
||||
@@ -566,7 +630,11 @@ export function useErrorGroups(
|
||||
processNodeErrors(groupsMap)
|
||||
processExecutionError(groupsMap)
|
||||
|
||||
return [...buildMissingNodeGroups(), ...toSortedGroups(groupsMap)]
|
||||
return [
|
||||
...buildMissingNodeGroups(),
|
||||
...buildMissingModelGroups(),
|
||||
...toSortedGroups(groupsMap)
|
||||
]
|
||||
})
|
||||
|
||||
const tabErrorGroups = computed<ErrorGroup[]>(() => {
|
||||
@@ -580,7 +648,11 @@ export function useErrorGroups(
|
||||
? toSortedGroups(regroupByErrorMessage(groupsMap))
|
||||
: toSortedGroups(groupsMap)
|
||||
|
||||
return [...buildMissingNodeGroups(), ...executionGroups]
|
||||
return [
|
||||
...buildMissingNodeGroups(),
|
||||
...buildMissingModelGroups(),
|
||||
...executionGroups
|
||||
]
|
||||
})
|
||||
|
||||
const filteredGroups = computed<ErrorGroup[]>(() => {
|
||||
@@ -615,6 +687,7 @@ export function useErrorGroups(
|
||||
missingNodeCache,
|
||||
groupedErrorMessages,
|
||||
missingPackGroups,
|
||||
missingModelGroups,
|
||||
swapNodeGroups
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,15 @@
|
||||
}"
|
||||
@click="onLogoMenuClick($event)"
|
||||
>
|
||||
<div class="flex items-center gap-0.5">
|
||||
<div class="grid place-items-center-safe gap-0.5">
|
||||
<i
|
||||
class="col-span-full row-span-full icon-[lucide--chevron-down] size-3 translate-x-4 text-muted-foreground"
|
||||
/>
|
||||
<ComfyLogo
|
||||
alt="ComfyUI Logo"
|
||||
class="comfyui-logo h-[18px] w-[18px]"
|
||||
class="comfyui-logo col-span-full row-span-full size-4.5"
|
||||
mode="fill"
|
||||
/>
|
||||
<i class="icon-[lucide--chevron-down] size-3 text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -48,13 +48,24 @@
|
||||
</div>
|
||||
</div>
|
||||
<HelpCenterPopups :is-small="isSmall" />
|
||||
<Suspense v-if="NightlySurveyController">
|
||||
<component :is="NightlySurveyController" />
|
||||
</Suspense>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useResizeObserver } from '@vueuse/core'
|
||||
import { debounce } from 'es-toolkit/compat'
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import {
|
||||
computed,
|
||||
defineAsyncComponent,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
watch
|
||||
} from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import HelpCenterPopups from '@/components/helpcenter/HelpCenterPopups.vue'
|
||||
@@ -62,7 +73,7 @@ import ComfyMenuButton from '@/components/sidebar/ComfyMenuButton.vue'
|
||||
import SidebarBottomPanelToggleButton from '@/components/sidebar/SidebarBottomPanelToggleButton.vue'
|
||||
import SidebarSettingsButton from '@/components/sidebar/SidebarSettingsButton.vue'
|
||||
import SidebarShortcutsToggleButton from '@/components/sidebar/SidebarShortcutsToggleButton.vue'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { isCloud, isDesktop, isNightly } from '@/platform/distribution/types'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
@@ -78,6 +89,13 @@ import SidebarIcon from './SidebarIcon.vue'
|
||||
import SidebarLogoutIcon from './SidebarLogoutIcon.vue'
|
||||
import SidebarTemplatesButton from './SidebarTemplatesButton.vue'
|
||||
|
||||
const NightlySurveyController =
|
||||
isNightly && !isCloud && !isDesktop
|
||||
? defineAsyncComponent(
|
||||
() => import('@/platform/surveys/NightlySurveyController.vue')
|
||||
)
|
||||
: undefined
|
||||
|
||||
const { t } = useI18n()
|
||||
const workspaceStore = useWorkspaceStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
@@ -90,6 +90,7 @@ import AssetsListItem from '@/platform/assets/components/AssetsListItem.vue'
|
||||
import type { OutputStackListItem } from '@/platform/assets/composables/useOutputStacks'
|
||||
import { getOutputAssetMetadata } from '@/platform/assets/schemas/assetMetadataSchema'
|
||||
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
||||
import { getAssetDisplayName } from '@/platform/assets/utils/assetMetadataUtils'
|
||||
import { iconForMediaType } from '@/platform/assets/utils/mediaIconUtil'
|
||||
import { useAssetsStore } from '@/stores/assetsStore'
|
||||
import {
|
||||
@@ -135,10 +136,6 @@ const listGridStyle = {
|
||||
gap: '0.5rem'
|
||||
}
|
||||
|
||||
function getAssetDisplayName(asset: AssetItem): string {
|
||||
return asset.display_name || asset.name
|
||||
}
|
||||
|
||||
function getAssetPrimaryText(asset: AssetItem): string {
|
||||
return truncateFilename(getAssetDisplayName(asset))
|
||||
}
|
||||
|
||||
@@ -236,6 +236,7 @@ import { useOutputStacks } from '@/platform/assets/composables/useOutputStacks'
|
||||
import type { OutputAssetMetadata } from '@/platform/assets/schemas/assetMetadataSchema'
|
||||
import { getOutputAssetMetadata } from '@/platform/assets/schemas/assetMetadataSchema'
|
||||
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
||||
import { getAssetDisplayName } from '@/platform/assets/utils/assetMetadataUtils'
|
||||
import type { MediaKind } from '@/platform/assets/schemas/mediaAssetSchema'
|
||||
import { resolveOutputAssetItems } from '@/platform/assets/utils/outputAssetUtil'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
@@ -569,7 +570,7 @@ const handleZoomClick = (asset: AssetItem) => {
|
||||
const dialogStore = useDialogStore()
|
||||
dialogStore.showDialog({
|
||||
key: 'asset-3d-viewer',
|
||||
title: asset.display_name || asset.name,
|
||||
title: getAssetDisplayName(asset),
|
||||
component: Load3dViewerContent,
|
||||
props: {
|
||||
modelUrl: asset.preview_url || ''
|
||||
|
||||
@@ -64,6 +64,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot name="prepend" />
|
||||
<SelectScrollUpButton />
|
||||
<SelectViewport
|
||||
:class="
|
||||
|
||||
@@ -324,7 +324,8 @@ function safeWidgetMapper(
|
||||
}
|
||||
: (extractWidgetDisplayOptions(effectiveWidget) ?? options),
|
||||
slotMetadata: slotInfo,
|
||||
slotName: name !== widget.name ? widget.name : undefined
|
||||
slotName: name !== widget.name ? widget.name : undefined,
|
||||
tooltip: widget.tooltip
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
|
||||
@@ -924,7 +924,8 @@ export function useBrushDrawing(initialSettings?: {
|
||||
}
|
||||
|
||||
// Calculate target spacing based on step size percentage
|
||||
const stepPercentage = store.brushSettings.stepSize / 100
|
||||
const stepPercentage =
|
||||
Math.pow(100, store.brushSettings.stepSize / 100) / 100
|
||||
const targetSpacing = Math.max(
|
||||
1.0,
|
||||
store.brushSettings.size * stepPercentage
|
||||
@@ -1483,7 +1484,8 @@ export function useBrushDrawing(initialSettings?: {
|
||||
const dist = Math.hypot(p2.x - p1.x, p2.y - p1.y)
|
||||
|
||||
// Calculate target spacing based on stepSize
|
||||
const stepPercentage = store.brushSettings.stepSize / 100
|
||||
const stepPercentage =
|
||||
Math.pow(100, store.brushSettings.stepSize / 100) / 100
|
||||
const stepSize = Math.max(
|
||||
1.0,
|
||||
store.brushSettings.size * stepPercentage
|
||||
|
||||
@@ -59,10 +59,7 @@ function mkFileUrl(props: { ref: ImageRef; preview?: boolean }): string {
|
||||
}
|
||||
|
||||
const pathPlusQueryParams = api.apiURL(
|
||||
'/view?' +
|
||||
params.toString() +
|
||||
app.getPreviewFormatParam() +
|
||||
app.getRandParam()
|
||||
'/view?' + params.toString() + app.getPreviewFormatParam()
|
||||
)
|
||||
const imageElement = new Image()
|
||||
imageElement.crossOrigin = 'anonymous'
|
||||
|
||||
@@ -17,7 +17,7 @@ type MockTask = {
|
||||
executionEndTimestamp?: number
|
||||
previewOutput?: {
|
||||
isImage: boolean
|
||||
urlWithTimestamp: string
|
||||
url: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ describe(useQueueNotificationBanners, () => {
|
||||
if (previewUrl) {
|
||||
task.previewOutput = {
|
||||
isImage,
|
||||
urlWithTimestamp: previewUrl
|
||||
url: previewUrl
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ export const useQueueNotificationBanners = () => {
|
||||
completedCount++
|
||||
const preview = task.previewOutput
|
||||
if (preview?.isImage) {
|
||||
imagePreviews.push(preview.urlWithTimestamp)
|
||||
imagePreviews.push(preview.url)
|
||||
}
|
||||
} else if (state === 'failed') {
|
||||
failedCount++
|
||||
|
||||
60
src/composables/useEditKeybindingDialog.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { computed, reactive } from 'vue'
|
||||
|
||||
import EditKeybindingContent from '@/components/dialog/content/setting/keybinding/EditKeybindingContent.vue'
|
||||
import EditKeybindingFooter from '@/components/dialog/content/setting/keybinding/EditKeybindingFooter.vue'
|
||||
import EditKeybindingHeader from '@/components/dialog/content/setting/keybinding/EditKeybindingHeader.vue'
|
||||
import type { KeyComboImpl } from '@/platform/keybindings/keyCombo'
|
||||
import { useKeybindingStore } from '@/platform/keybindings/keybindingStore'
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
|
||||
export const DIALOG_KEY = 'edit-keybinding'
|
||||
|
||||
export interface EditKeybindingDialogState {
|
||||
commandId: string
|
||||
newCombo: KeyComboImpl | null
|
||||
currentCombo: KeyComboImpl | null
|
||||
}
|
||||
|
||||
export function useEditKeybindingDialog() {
|
||||
const { showSmallLayoutDialog } = useDialogService()
|
||||
const keybindingStore = useKeybindingStore()
|
||||
|
||||
function show(options: {
|
||||
commandId: string
|
||||
commandLabel: string
|
||||
currentCombo: KeyComboImpl | null
|
||||
}) {
|
||||
const dialogState = reactive<EditKeybindingDialogState>({
|
||||
commandId: options.commandId,
|
||||
newCombo: options.currentCombo,
|
||||
currentCombo: options.currentCombo
|
||||
})
|
||||
|
||||
const existingKeybindingOnCombo = computed(() => {
|
||||
if (!dialogState.newCombo) return null
|
||||
if (dialogState.currentCombo?.equals(dialogState.newCombo)) return null
|
||||
return keybindingStore.getKeybinding(dialogState.newCombo)
|
||||
})
|
||||
|
||||
function onUpdateCombo(combo: KeyComboImpl) {
|
||||
dialogState.newCombo = combo
|
||||
}
|
||||
|
||||
showSmallLayoutDialog({
|
||||
key: DIALOG_KEY,
|
||||
headerComponent: EditKeybindingHeader,
|
||||
footerComponent: EditKeybindingFooter,
|
||||
component: EditKeybindingContent,
|
||||
props: {
|
||||
dialogState,
|
||||
onUpdateCombo,
|
||||
commandLabel: options.commandLabel,
|
||||
existingKeybindingOnCombo
|
||||
},
|
||||
headerProps: {},
|
||||
footerProps: { dialogState, existingKeybindingOnCombo }
|
||||
})
|
||||
}
|
||||
|
||||
return { show }
|
||||
}
|
||||
@@ -357,7 +357,8 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize viewer in standalone mode (for asset preview)
|
||||
* Initialize viewer in standalone mode (for asset preview).
|
||||
* Creates the Load3d instance once; subsequent calls reuse it.
|
||||
*/
|
||||
const initializeStandaloneViewer = async (
|
||||
containerRef: HTMLElement,
|
||||
@@ -366,6 +367,11 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
|
||||
if (!containerRef) return
|
||||
|
||||
try {
|
||||
if (load3d) {
|
||||
await loadStandaloneModel(modelUrl)
|
||||
return
|
||||
}
|
||||
|
||||
isStandaloneMode.value = true
|
||||
|
||||
load3d = new Load3d(containerRef, {
|
||||
@@ -392,6 +398,23 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
|
||||
setupAnimationEvents()
|
||||
} catch (error) {
|
||||
console.error('Error initializing standalone 3D viewer:', error)
|
||||
useToastStore().addAlert(t('toastMessages.failedToLoadModel'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a new model into an existing standalone viewer,
|
||||
* reusing the same WebGLRenderer.
|
||||
*/
|
||||
const loadStandaloneModel = async (modelUrl: string) => {
|
||||
if (!load3d) return
|
||||
|
||||
try {
|
||||
await load3d.loadModel(modelUrl)
|
||||
isSplatModel.value = load3d.isSplatModel()
|
||||
isPlyModel.value = load3d.isPlyModel()
|
||||
} catch (error) {
|
||||
console.error('Error loading model in standalone viewer:', error)
|
||||
useToastStore().addAlert('Failed to load 3D model')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,6 +595,34 @@ describe('usePaste', () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should skip node metadata paste when a media node is selected', async () => {
|
||||
const mockNode = createMockLGraphNode({
|
||||
is_selected: true,
|
||||
pasteFile: vi.fn(),
|
||||
pasteFiles: vi.fn()
|
||||
})
|
||||
mockCanvas.current_node = mockNode
|
||||
vi.mocked(isImageNode).mockReturnValue(true)
|
||||
|
||||
usePaste()
|
||||
|
||||
const nodeData = { nodes: [{ type: 'KSampler' }] }
|
||||
const encoded = btoa(JSON.stringify(nodeData))
|
||||
const html = `<div data-metadata="${encoded}"></div>`
|
||||
|
||||
const dataTransfer = new DataTransfer()
|
||||
dataTransfer.setData('text/html', html)
|
||||
dataTransfer.setData('text/plain', 'some text')
|
||||
|
||||
const event = new ClipboardEvent('paste', { clipboardData: dataTransfer })
|
||||
document.dispatchEvent(event)
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(mockCanvas._deserializeItems).not.toHaveBeenCalled()
|
||||
expect(mockCanvas.pasteFromClipboard).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('cloneDataTransfer', () => {
|
||||
|
||||
@@ -229,7 +229,10 @@ export const usePaste = () => {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (pasteClipboardItems(data)) return
|
||||
|
||||
const isMediaNodeSelected =
|
||||
isImageNodeSelected || isVideoNodeSelected || isAudioNodeSelected
|
||||
if (!isMediaNodeSelected && pasteClipboardItems(data)) return
|
||||
|
||||
// No image found. Look for node data
|
||||
data = data.getData('text/plain')
|
||||
|
||||
35
src/core/graph/widgets/dynamicTypes.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { transformInputSpecV1ToV2 } from '@/schemas/nodeDef/migration'
|
||||
import { zAutogrowOptions, zMatchTypeOptions } from '@/schemas/nodeDefSchema'
|
||||
import type { InputSpec } from '@/schemas/nodeDefSchema'
|
||||
import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
|
||||
const dynamicTypeResolvers: Record<
|
||||
string,
|
||||
(inputSpec: InputSpecV2) => string[]
|
||||
> = {
|
||||
COMFY_AUTOGROW_V3: resolveAutogrowType,
|
||||
COMFY_MATCHTYPE_V3: (input) =>
|
||||
zMatchTypeOptions
|
||||
.safeParse(input)
|
||||
.data?.template?.allowed_types?.split(',') ?? []
|
||||
}
|
||||
|
||||
export function resolveInputType(input: InputSpecV2): string[] {
|
||||
return input.type in dynamicTypeResolvers
|
||||
? dynamicTypeResolvers[input.type](input)
|
||||
: input.type.split(',')
|
||||
}
|
||||
|
||||
function resolveAutogrowType(rawSpec: InputSpecV2): string[] {
|
||||
const { input } = zAutogrowOptions.safeParse(rawSpec).data?.template ?? {}
|
||||
|
||||
const inputTypes: (Record<string, InputSpec> | undefined)[] = [
|
||||
input?.required,
|
||||
input?.optional
|
||||
]
|
||||
return inputTypes.flatMap((inputType) =>
|
||||
Object.entries(inputType ?? {}).flatMap(([name, v]) =>
|
||||
resolveInputType(transformInputSpecV1ToV2(v, { name }))
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -27,7 +27,7 @@ function addDynamicCombo(node: LGraphNode, inputs: DynamicInputs) {
|
||||
`${namePrefix}.${depth}.${inputIndex}`,
|
||||
Array.isArray(input)
|
||||
? ['COMFY_DYNAMICCOMBO_V3', { options: getSpec(input, depth + 1) }]
|
||||
: [input, {}]
|
||||
: [input, { tooltip: `${groupIndex}` }]
|
||||
])
|
||||
return {
|
||||
key: `${groupIndex}`,
|
||||
@@ -106,6 +106,13 @@ describe('Dynamic Combos', () => {
|
||||
expect(node.inputs[1].name).toBe('0.0.0.0')
|
||||
expect(node.inputs[3].name).toBe('2.2.0.0')
|
||||
})
|
||||
test('Dynamically added widgets have tooltips', () => {
|
||||
const node = testNode()
|
||||
addDynamicCombo(node, [['INT'], ['STRING']])
|
||||
expect.soft(node.widgets[1].tooltip).toBe('0')
|
||||
node.widgets[0].value = '1'
|
||||
expect.soft(node.widgets[1].tooltip).toBe('1')
|
||||
})
|
||||
})
|
||||
describe('Autogrow', () => {
|
||||
const inputsSpec = { required: { image: ['IMAGE', {}] } }
|
||||
|
||||
@@ -16,7 +16,8 @@ import type { ComboInputSpec, InputSpec } from '@/schemas/nodeDefSchema'
|
||||
import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import {
|
||||
zAutogrowOptions,
|
||||
zDynamicComboInputSpec
|
||||
zDynamicComboInputSpec,
|
||||
zMatchTypeOptions
|
||||
} from '@/schemas/nodeDefSchema'
|
||||
import { useLitegraphService } from '@/services/litegraphService'
|
||||
import { app } from '@/scripts/app'
|
||||
@@ -215,6 +216,7 @@ export function applyDynamicInputs(
|
||||
dynamicInputs[inputSpec.type](node, inputSpec)
|
||||
return true
|
||||
}
|
||||
|
||||
function spliceInputs(
|
||||
node: LGraphNode,
|
||||
startIndex: number,
|
||||
@@ -329,11 +331,10 @@ function withComfyMatchType(node: LGraphNode): asserts node is MatchTypeNode {
|
||||
function applyMatchType(node: LGraphNode, inputSpec: InputSpecV2) {
|
||||
const { addNodeInput } = useLitegraphService()
|
||||
const name = inputSpec.name
|
||||
const { allowed_types, template_id } = (
|
||||
inputSpec as InputSpecV2 & {
|
||||
template: { allowed_types: string; template_id: string }
|
||||
}
|
||||
).template
|
||||
const matchTypeSpec = zMatchTypeOptions.safeParse(inputSpec).data
|
||||
if (!matchTypeSpec) return
|
||||
|
||||
const { allowed_types, template_id } = matchTypeSpec.template
|
||||
const typedSpec = { ...inputSpec, type: allowed_types }
|
||||
addNodeInput(node, typedSpec)
|
||||
withComfyMatchType(node)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||
import type { NodeOutputWith } from '@/schemas/apiSchema'
|
||||
import { api } from '@/scripts/api'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useExtensionService } from '@/services/extensionService'
|
||||
|
||||
type ImageCompareOutput = NodeOutputWith<{
|
||||
@@ -24,11 +23,10 @@ useExtensionService().registerExtension({
|
||||
onExecuted?.call(this, output)
|
||||
|
||||
const { a_images: aImages, b_images: bImages } = output
|
||||
const rand = app.getRandParam()
|
||||
|
||||
const toUrl = (record: Record<string, string>) => {
|
||||
const params = new URLSearchParams(record)
|
||||
return api.apiURL(`/view?${params}${rand}`)
|
||||
return api.apiURL(`/view?${params}`)
|
||||
}
|
||||
|
||||
const beforeImages =
|
||||
|
||||
@@ -2,7 +2,6 @@ import type Load3d from '@/extensions/core/load3d/Load3d'
|
||||
import { t } from '@/i18n'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import { api } from '@/scripts/api'
|
||||
import { app } from '@/scripts/app'
|
||||
|
||||
class Load3dUtils {
|
||||
static async generateThumbnailIfNeeded(
|
||||
@@ -133,8 +132,7 @@ class Load3dUtils {
|
||||
const params = [
|
||||
'filename=' + encodeURIComponent(filename),
|
||||
'type=' + type,
|
||||
'subfolder=' + subfolder,
|
||||
app.getRandParam().substring(1)
|
||||
'subfolder=' + subfolder
|
||||
].join('&')
|
||||
|
||||
return `/view?${params}`
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "إعادة تسمية جماعية",
|
||||
"beta": "نسخة تجريبية",
|
||||
"bookmark": "حفظ في المكتبة",
|
||||
"browserReservedKeybinding": "هذا الاختصار محجوز من قبل بعض المتصفحات وقد يؤدي إلى نتائج غير متوقعة.",
|
||||
"browserReservedKeybindingTooltip": "هذا الاختصار يتعارض مع اختصارات المتصفح المحجوزة",
|
||||
"calculatingDimensions": "جارٍ حساب الأبعاد",
|
||||
"cancel": "إلغاء",
|
||||
"cancelled": "أُلغي",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "نسخ",
|
||||
"copyAll": "نسخ الكل",
|
||||
"copyJobId": "نسخ معرف المهمة",
|
||||
"copySystemInfo": "نسخ معلومات النظام",
|
||||
"copyToClipboard": "نسخ إلى الحافظة",
|
||||
"copyURL": "نسخ الرابط",
|
||||
"core": "النواة",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "أدخل الاسم الجديد",
|
||||
"enterNewNamePrompt": "أدخل اسمًا جديدًا:",
|
||||
"enterSubgraph": "دخول الرسم البياني الفرعي",
|
||||
"enterYourKeybind": "أدخل اختصارك",
|
||||
"error": "خطأ",
|
||||
"errorLoadingImage": "حدث خطأ أثناء تحميل الصورة",
|
||||
"errorLoadingVideo": "حدث خطأ أثناء تحميل الفيديو",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "تم رفض إذن الميكروفون",
|
||||
"migrate": "ترحيل",
|
||||
"missing": "مفقود",
|
||||
"modifyKeybinding": "تعديل اختصار لوحة المفاتيح",
|
||||
"more": "المزيد",
|
||||
"moreOptions": "خيارات إضافية",
|
||||
"moreWorkflows": "المزيد من سير العمل",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "الاسم",
|
||||
"newFolder": "مجلد جديد",
|
||||
"next": "التالي",
|
||||
"nextImage": "الصورة التالية",
|
||||
"nightly": "NIGHTLY",
|
||||
"no": "لا",
|
||||
"noAudioRecorded": "لم يتم تسجيل أي صوت",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "تشغيل التسجيل",
|
||||
"playbackSpeed": "سرعة التشغيل",
|
||||
"playing": "جاري التشغيل",
|
||||
"pressKeysForNewBinding": "اضغط على المفاتيح لربط جديد",
|
||||
"preview": "معاينة",
|
||||
"previousImage": "الصورة السابقة",
|
||||
"profile": "الملف الشخصي",
|
||||
"progressCountOf": "من",
|
||||
"queued": "في قائمة الانتظار",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "تم العثور على {count} نتيجة",
|
||||
"running": "يعمل",
|
||||
"save": "حفظ",
|
||||
"saveAnyway": "احفظ على أي حال",
|
||||
"saving": "جارٍ الحفظ",
|
||||
"scrollLeft": "التمرير لليسار",
|
||||
"scrollRight": "التمرير لليمين",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "حدد العناصر لعمل نسخة",
|
||||
"selectItemsToRename": "حدد العناصر لإعادة التسمية",
|
||||
"selectedFile": "الملف المحدد",
|
||||
"setAKeybindingForTheFollowing": "تعيين اختصار لوحة المفاتيح لما يلي:",
|
||||
"setAsBackground": "تعيين كخلفية",
|
||||
"settings": "الإعدادات",
|
||||
"shortcutSuffix": " ({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "إيقاف التسجيل",
|
||||
"submit": "إرسال",
|
||||
"success": "نجاح",
|
||||
"switchToGridView": "التبديل إلى عرض الشبكة",
|
||||
"switchToSingleView": "التبديل إلى العرض الفردي",
|
||||
"systemInfo": "معلومات النظام",
|
||||
"terminal": "الطرفية",
|
||||
"title": "العنوان",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "أنت"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "وضع اللوحة",
|
||||
"canvasToolbar": "شريط أدوات اللوحة",
|
||||
"fitView": "ملائمة العرض",
|
||||
"focusMode": "وضع التركيز",
|
||||
"hand": "يد",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "اسحب وأسقط صورة",
|
||||
"emptyWorkflowExplanation": "سير العمل الخاص بك فارغ. تحتاج إلى بعض العقد أولاً لبدء بناء التطبيق.",
|
||||
"enterNodeGraph": "دخول مخطط العقد",
|
||||
"error": {
|
||||
"getHelp": "للمساعدة، راجع {0} أو {1} أو {2} مع تقرير الخطأ المنسوخ.",
|
||||
"github": "إرسال مشكلة على GitHub",
|
||||
"goto": "عرض الأخطاء في المخطط",
|
||||
"guide": "دليل استكشاف الأخطاء",
|
||||
"header": "حدث خطأ في هذا التطبيق",
|
||||
"log": "سجلات الأخطاء",
|
||||
"mobileFixable": "تحقق من {0} للأخطاء",
|
||||
"promptShow": "عرض تقرير الخطأ",
|
||||
"promptVisitGraph": "اعرض مخطط العُقد لرؤية الخطأ بالكامل.",
|
||||
"requiresGraph": "حدث خطأ أثناء التوليد. قد يكون ذلك بسبب مدخلات مخفية غير صالحة، أو موارد مفقودة، أو مشاكل في إعداد سير العمل.",
|
||||
"support": "الاتصال بالدعم"
|
||||
},
|
||||
"giveFeedback": "إعطاء ملاحظات",
|
||||
"graphMode": "وضع الرسم البياني",
|
||||
"hasCreditCost": "يتطلب أرصدة إضافية",
|
||||
"linearMode": "وضع التطبيق",
|
||||
"loadTemplate": "تحميل قالب",
|
||||
"mobileControls": "تعديل وتشغيل",
|
||||
"mobileNoWorkflow": "لم يتم بناء سير العمل هذا لوضع التطبيق. جرب سير عمل آخر.",
|
||||
"queue": {
|
||||
"clear": "مسح قائمة الانتظار",
|
||||
"clickToClear": "انقر لمسح قائمة الانتظار"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "تشغيل مجدد",
|
||||
"reuseParameters": "إعادة استخدام المعلمات",
|
||||
"runCount": "عدد مرات التشغيل:",
|
||||
"viewGraph": "عرض مخطط العُقد",
|
||||
"viewJob": "عرض المهمة",
|
||||
"welcome": {
|
||||
"buildApp": "إنشاء تطبيق",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "المثبتة",
|
||||
"missingNodes": "عقد مفقودة",
|
||||
"notInstalled": "غير مثبت",
|
||||
"unresolvedNodes": "العُقد غير المحلولة",
|
||||
"updatesAvailable": "تحديثات متوفرة"
|
||||
},
|
||||
"nightlyVersion": "ليلي",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "إلغاء التثبيت",
|
||||
"uninstallSelected": "إلغاء تثبيت المحدد",
|
||||
"uninstalling": "جاري إلغاء التثبيت",
|
||||
"unresolvedNodes": {
|
||||
"message": "العُقد التالية غير مثبتة ولم يتم العثور عليها في السجل.",
|
||||
"title": "العُقد المفقودة غير المحلولة"
|
||||
},
|
||||
"update": "تحديث",
|
||||
"updateAll": "تحديث الكل",
|
||||
"updateSelected": "تحديث المحدد",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "رودان",
|
||||
"Runway": "رن واي",
|
||||
"Sora": "سورا",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "لا توجد مدخلات",
|
||||
"inputsNoneTooltip": "العقدة ليس لديها مدخلات",
|
||||
"locateNode": "تحديد موقع العقدة على اللوحة",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "هذا النموذج موجود بالفعل في \"{category}\"",
|
||||
"assetLoadTimeout": "انتهت مهلة اكتشاف النموذج. حاول إعادة تحميل سير العمل.",
|
||||
"cancelSelection": "إلغاء الاختيار",
|
||||
"clearUrl": "مسح الرابط",
|
||||
"collapseNodes": "إخفاء العُقد المرجعية",
|
||||
"confirmSelection": "تأكيد الاختيار",
|
||||
"copyModelName": "نسخ اسم النموذج",
|
||||
"customNodeDownloadDisabled": "بيئة السحابة لا تدعم استيراد النماذج للعُقد المخصصة في هذا القسم. يرجى استخدام عُقد التحميل القياسية أو استبدالها بنموذج من المكتبة أدناه.",
|
||||
"expandNodes": "عرض العُقد المرجعية",
|
||||
"import": "استيراد",
|
||||
"importAnyway": "استيراد على أي حال",
|
||||
"importFailed": "فشل الاستيراد",
|
||||
"importNotSupported": "الاستيراد غير مدعوم",
|
||||
"imported": "تم الاستيراد",
|
||||
"importing": "جارٍ الاستيراد...",
|
||||
"locateNode": "تحديد موقع العُقدة على اللوحة",
|
||||
"metadataFetchFailed": "فشل في جلب البيانات الوصفية. يرجى التحقق من الرابط والمحاولة مرة أخرى.",
|
||||
"missingModelsTitle": "النماذج المفقودة",
|
||||
"or": "أو",
|
||||
"typeMismatch": "يبدو أن هذا النموذج هو \"{detectedType}\". هل أنت متأكد؟",
|
||||
"unknownCategory": "غير معروف",
|
||||
"unsupportedUrl": "يتم دعم روابط Civitai و Hugging Face فقط.",
|
||||
"urlPlaceholder": "الصق رابط النموذج (Civitai أو Hugging Face)",
|
||||
"useFromLibrary": "استخدم من المكتبة",
|
||||
"usingFromLibrary": "يتم الاستخدام من المكتبة"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "تطبيق التغييرات",
|
||||
"cloudMessage": "يتطلب سير العمل هذا عقدًا مخصصة غير متوفرة بعد على Comfy Cloud.",
|
||||
@@ -3238,6 +3298,7 @@
|
||||
"interrupted": "تم إيقاف التنفيذ",
|
||||
"legacyMaskEditorDeprecated": "محرر القناع القديم لم يعد مدعوماً وسيتم إزالته قريباً.",
|
||||
"migrateToLitegraphReroute": "سيتم إزالة عقد إعادة التوجيه في الإصدارات المستقبلية. انقر للترحيل إلى إعادة التوجيه الأصلية في Litegraph.",
|
||||
"missingModelVerificationFailed": "فشل التحقق من النماذج المفقودة. قد لا تظهر بعض النماذج في علامة تبويب الأخطاء.",
|
||||
"modelLoadedSuccessfully": "تم تحميل النموذج ثلاثي الأبعاد بنجاح",
|
||||
"no3dScene": "لا يوجد مشهد ثلاثي الأبعاد لتطبيق الخامة",
|
||||
"no3dSceneToExport": "لا يوجد مشهد ثلاثي الأبعاد للتصدير",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "يُمكّن تحسين KV Cache لصور المرجع على نماذج عائلة Flux.",
|
||||
"display_name": "Flux KV Cache",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "النموذج",
|
||||
"tooltip": "النموذج الذي سيتم تفعيل KV Cache عليه."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "النموذج المعدّل مع تفعيل KV Cache."
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "تعيد هذه العقدة ضبط حجم الصورة إلى حجم أكثر ملاءمة لـ flux kontext.",
|
||||
"display_name": "FluxKontextImageScale",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "توليد الصور من أوصاف نصية باستخدام Reve.",
|
||||
"display_name": "إنشاء صورة Reve",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "التحكم بعد التوليد"
|
||||
},
|
||||
"model": {
|
||||
"name": "النموذج",
|
||||
"tooltip": "إصدار النموذج المستخدم في التوليد."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "نسبة الأبعاد"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "اختبار التحجيم الزمني"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "الوصف النصي",
|
||||
"tooltip": "الوصف النصي للصورة المطلوبة. الحد الأقصى ٢٥٦٠ حرفًا."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "إزالة الخلفية",
|
||||
"tooltip": "إزالة الخلفية من الصورة المولدة. قد يضيف تكلفة إضافية."
|
||||
},
|
||||
"seed": {
|
||||
"name": "البذرة",
|
||||
"tooltip": "تتحكم البذرة فيما إذا كان يجب إعادة تشغيل العقدة؛ النتائج غير حتمية بغض النظر عن البذرة."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "تكبير",
|
||||
"tooltip": "تكبير الصورة المولدة. قد يضيف تكلفة إضافية."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "تعديل الصور باستخدام تعليمات لغة طبيعية مع Reve.",
|
||||
"display_name": "تعديل صورة Reve",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "التحكم بعد التوليد"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "تعليمات التعديل",
|
||||
"tooltip": "الوصف النصي لكيفية تعديل الصورة. الحد الأقصى ٢٥٦٠ حرفًا."
|
||||
},
|
||||
"image": {
|
||||
"name": "الصورة",
|
||||
"tooltip": "الصورة المراد تعديلها."
|
||||
},
|
||||
"model": {
|
||||
"name": "النموذج",
|
||||
"tooltip": "إصدار النموذج المستخدم في التعديل."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "نسبة الأبعاد"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "اختبار التحجيم الزمني"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "إزالة الخلفية",
|
||||
"tooltip": "إزالة الخلفية من الصورة المولدة. قد يضيف تكلفة إضافية."
|
||||
},
|
||||
"seed": {
|
||||
"name": "البذرة",
|
||||
"tooltip": "تتحكم البذرة فيما إذا كان يجب إعادة تشغيل العقدة؛ النتائج غير حتمية بغض النظر عن البذرة."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "تكبير",
|
||||
"tooltip": "تكبير الصورة المولدة. قد يضيف تكلفة إضافية."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "دمج الصور المرجعية مع الأوصاف النصية لإنشاء صور جديدة باستخدام Reve.",
|
||||
"display_name": "ريمكس صورة Reve",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "التحكم بعد التوليد"
|
||||
},
|
||||
"model": {
|
||||
"name": "النموذج",
|
||||
"tooltip": "إصدار النموذج المستخدم في الريمكس."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "نسبة الأبعاد"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "اختبار التحجيم الزمني"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "الوصف النصي",
|
||||
"tooltip": "الوصف النصي للصورة المطلوبة. يمكن أن يتضمن وسوم XML للصور للإشارة إلى صور محددة حسب الفهرس، مثل <img>0</img>، <img>1</img>، إلخ."
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "الصور المرجعية"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "إزالة الخلفية",
|
||||
"tooltip": "إزالة الخلفية من الصورة المولدة. قد يضيف تكلفة إضافية."
|
||||
},
|
||||
"seed": {
|
||||
"name": "البذرة",
|
||||
"tooltip": "تتحكم البذرة فيما إذا كان يجب إعادة تشغيل العقدة؛ النتائج غير حتمية بغض النظر عن البذرة."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "تكبير",
|
||||
"tooltip": "تكبير الصورة المولدة. قد يضيف تكلفة إضافية."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "توليد أصول ثلاثية الأبعاد باستخدام واجهة برمجة تطبيقات رودين",
|
||||
"display_name": "رودين 3D توليد - توليد التفاصيل",
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
"videoPreview": "Video preview - Use arrow keys to navigate between videos",
|
||||
"galleryImage": "Gallery image",
|
||||
"galleryThumbnail": "Gallery thumbnail",
|
||||
"previousImage": "Previous image",
|
||||
"nextImage": "Next image",
|
||||
"switchToGridView": "Switch to grid view",
|
||||
"switchToSingleView": "Switch to single view",
|
||||
"errorLoadingImage": "Error loading image",
|
||||
"errorLoadingVideo": "Error loading video",
|
||||
"failedToDownloadImage": "Failed to download image",
|
||||
@@ -106,6 +110,7 @@
|
||||
"delete": "Delete",
|
||||
"rename": "Rename",
|
||||
"save": "Save",
|
||||
"saveAnyway": "Save Anyway",
|
||||
"saving": "Saving",
|
||||
"no": "No",
|
||||
"cancel": "Cancel",
|
||||
@@ -116,7 +121,6 @@
|
||||
"showRightPanel": "Show right panel",
|
||||
"hideRightPanel": "Hide right panel",
|
||||
"or": "or",
|
||||
"pressKeysForNewBinding": "Press keys for new binding",
|
||||
"defaultBanner": "default banner",
|
||||
"enableOrDisablePack": "Enable or disable pack",
|
||||
"openManager": "Open Manager",
|
||||
@@ -261,6 +265,11 @@
|
||||
"multiSelectDropdown": "Multi-select dropdown",
|
||||
"singleSelectDropdown": "Single-select dropdown",
|
||||
"progressCountOf": "of",
|
||||
"modifyKeybinding": "Modify keybinding",
|
||||
"setAKeybindingForTheFollowing": "Set a keybinding for the following:",
|
||||
"enterYourKeybind": "Enter your keybind",
|
||||
"browserReservedKeybinding": "This shortcut is reserved by some browsers and may have unexpected results.",
|
||||
"browserReservedKeybindingTooltip": "This shortcut conflicts with browser-reserved shortcuts",
|
||||
"keybindingAlreadyExists": "Keybinding already exists on",
|
||||
"commandProhibited": "Command {command} is prohibited. Contact an administrator for more information.",
|
||||
"startRecording": "Start Recording",
|
||||
@@ -1052,6 +1061,8 @@
|
||||
"logoProviderSeparator": " & "
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "Canvas Mode",
|
||||
"canvasToolbar": "Canvas Toolbar",
|
||||
"zoomIn": "Zoom In",
|
||||
"zoomOut": "Zoom Out",
|
||||
"resetView": "Reset View",
|
||||
@@ -1621,6 +1632,7 @@
|
||||
"unet": "unet",
|
||||
"sigmas": "sigmas",
|
||||
"BFL": "BFL",
|
||||
"": "",
|
||||
"Gemini": "Gemini",
|
||||
"video_models": "video_models",
|
||||
"gligen": "gligen",
|
||||
@@ -1655,6 +1667,7 @@
|
||||
"primitive": "primitive",
|
||||
"Recraft": "Recraft",
|
||||
"edit_models": "edit_models",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "Rodin",
|
||||
"Runway": "Runway",
|
||||
"animation": "animation",
|
||||
@@ -1668,7 +1681,6 @@
|
||||
"style_model": "style_model",
|
||||
"Tencent": "Tencent",
|
||||
"textgen": "textgen",
|
||||
"": "",
|
||||
"Topaz": "Topaz",
|
||||
"Tripo": "Tripo",
|
||||
"Veo": "Veo",
|
||||
@@ -1955,6 +1967,7 @@
|
||||
"exportSuccess": "Successfully exported model as {format}",
|
||||
"fileLoadError": "Unable to find workflow in {fileName}",
|
||||
"dropFileError": "Unable to process dropped item: {error}",
|
||||
"missingModelVerificationFailed": "Failed to verify missing models. Some models may not be shown in the Errors tab.",
|
||||
"interrupted": "Execution has been interrupted",
|
||||
"pendingTasksDeleted": "Pending tasks deleted",
|
||||
"nothingToGroup": "Nothing to group",
|
||||
@@ -3386,6 +3399,33 @@
|
||||
"viewInManager": "View in Manager",
|
||||
"collapse": "Collapse",
|
||||
"expand": "Expand"
|
||||
},
|
||||
"missingModels": {
|
||||
"urlPlaceholder": "Paste Model URL (Civitai or Hugging Face)",
|
||||
"or": "OR",
|
||||
"useFromLibrary": "Use from Library",
|
||||
"usingFromLibrary": "Using from Library",
|
||||
"unsupportedUrl": "Only Civitai and Hugging Face URLs are supported.",
|
||||
"metadataFetchFailed": "Failed to retrieve metadata. Please check the link and try again.",
|
||||
"import": "Import",
|
||||
"importing": "Importing...",
|
||||
"imported": "Imported",
|
||||
"importFailed": "Import failed",
|
||||
"typeMismatch": "This model seems to be a \"{detectedType}\". Are you sure?",
|
||||
"importAnyway": "Import Anyway",
|
||||
"alreadyExistsInCategory": "This model already exists in \"{category}\"",
|
||||
"customNodeDownloadDisabled": "Cloud environment does not support model imports for custom nodes in this section. Please use standard loader nodes or substitute with a model from the library below.",
|
||||
"importNotSupported": "Import Not Supported",
|
||||
"copyModelName": "Copy model name",
|
||||
"confirmSelection": "Confirm selection",
|
||||
"locateNode": "Locate node on canvas",
|
||||
"cancelSelection": "Cancel selection",
|
||||
"clearUrl": "Clear URL",
|
||||
"expandNodes": "Show referencing nodes",
|
||||
"collapseNodes": "Hide referencing nodes",
|
||||
"unknownCategory": "Unknown",
|
||||
"missingModelsTitle": "Missing Models",
|
||||
"assetLoadTimeout": "Model detection timed out. Try reloading the workflow."
|
||||
}
|
||||
},
|
||||
"errorOverlay": {
|
||||
|
||||
@@ -3319,6 +3319,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"display_name": "Flux KV Cache",
|
||||
"description": "Enables KV Cache optimization for reference images on Flux family models.",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "The model to use KV Cache on."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "The patched model with KV Cache enabled."
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxProExpandNode": {
|
||||
"display_name": "Flux.1 Expand Image",
|
||||
"description": "Outpaints image based on prompt.",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"display_name": "Reve Image Create",
|
||||
"description": "Generate images from text descriptions using Reve.",
|
||||
"inputs": {
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "Text description of the desired image. Maximum 2560 characters."
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Model version to use for generation."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Upscale the generated image. May add additional cost."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Remove the background from the generated image. May add additional cost."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "Seed controls whether the node should re-run; results are non-deterministic regardless of seed."
|
||||
},
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"display_name": "Reve Image Edit",
|
||||
"description": "Edit images using natural language instructions with Reve.",
|
||||
"inputs": {
|
||||
"image": {
|
||||
"name": "image",
|
||||
"tooltip": "The image to edit."
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "edit_instruction",
|
||||
"tooltip": "Text description of how to edit the image. Maximum 2560 characters."
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Model version to use for editing."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Upscale the generated image. May add additional cost."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Remove the background from the generated image. May add additional cost."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "Seed controls whether the node should re-run; results are non-deterministic regardless of seed."
|
||||
},
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"display_name": "Reve Image Remix",
|
||||
"description": "Combine reference images with text prompts to create new images using Reve.",
|
||||
"inputs": {
|
||||
"reference_images": {
|
||||
"name": "reference_images"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "Text description of the desired image. May include XML img tags to reference specific images by index, e.g. <img>0</img>, <img>1</img>, etc."
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Model version to use for remixing."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Upscale the generated image. May add additional cost."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Remove the background from the generated image. May add additional cost."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "Seed controls whether the node should re-run; results are non-deterministic regardless of seed."
|
||||
},
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"display_name": "Rodin 3D Generate - Detail Generate",
|
||||
"description": "Generate 3D Assets using Rodin API",
|
||||
|
||||
@@ -348,8 +348,8 @@
|
||||
"tooltip": "The maximum number of tasks that show in the queue history."
|
||||
},
|
||||
"Comfy_Queue_QPOV2": {
|
||||
"name": "Use the unified job queue in the Assets side panel",
|
||||
"tooltip": "Replaces the floating job queue panel with an equivalent job queue embedded in the Assets side panel. You can disable this to return to the floating panel layout."
|
||||
"name": "Docked job history/queue panel",
|
||||
"tooltip": "Replaces the floating job queue panel with an equivalent job queue embedded in the job history side panel. You can disable this to return to the floating panel layout."
|
||||
},
|
||||
"Comfy_QueueButton_BatchCountLimit": {
|
||||
"name": "Batch count limit",
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "Renombrar en lote",
|
||||
"beta": "BETA",
|
||||
"bookmark": "Guardar en Biblioteca",
|
||||
"browserReservedKeybinding": "Este atajo está reservado por algunos navegadores y puede tener resultados inesperados.",
|
||||
"browserReservedKeybindingTooltip": "Este atajo entra en conflicto con los atajos reservados del navegador",
|
||||
"calculatingDimensions": "Calculando dimensiones",
|
||||
"cancel": "Cancelar",
|
||||
"cancelled": "Cancelado",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "Copiar",
|
||||
"copyAll": "Copiar todo",
|
||||
"copyJobId": "Copiar ID de trabajo",
|
||||
"copySystemInfo": "Copiar información del sistema",
|
||||
"copyToClipboard": "Copiar al portapapeles",
|
||||
"copyURL": "Copiar URL",
|
||||
"core": "Núcleo",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "Introduce el nuevo nombre",
|
||||
"enterNewNamePrompt": "Introduce un nuevo nombre:",
|
||||
"enterSubgraph": "Entrar en subgrafo",
|
||||
"enterYourKeybind": "Introduce tu atajo de teclado",
|
||||
"error": "Error",
|
||||
"errorLoadingImage": "Error al cargar imagen",
|
||||
"errorLoadingVideo": "Error al cargar video",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "Permiso de micrófono denegado",
|
||||
"migrate": "Migrar",
|
||||
"missing": "Faltante",
|
||||
"modifyKeybinding": "Modificar atajo de teclado",
|
||||
"more": "Más",
|
||||
"moreOptions": "Más Opciones",
|
||||
"moreWorkflows": "Más flujos de trabajo",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "Nombre",
|
||||
"newFolder": "Nueva carpeta",
|
||||
"next": "Siguiente",
|
||||
"nextImage": "Imagen siguiente",
|
||||
"nightly": "NIGHTLY",
|
||||
"no": "No",
|
||||
"noAudioRecorded": "No se grabó audio",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "Reproducir grabación",
|
||||
"playbackSpeed": "Velocidad de reproducción",
|
||||
"playing": "Reproduciendo",
|
||||
"pressKeysForNewBinding": "Presiona teclas para nueva asignación",
|
||||
"preview": "VISTA PREVIA",
|
||||
"previousImage": "Imagen anterior",
|
||||
"profile": "Perfil",
|
||||
"progressCountOf": "de",
|
||||
"queued": "En cola",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "Encontrados {count} resultados",
|
||||
"running": "En ejecución",
|
||||
"save": "Guardar",
|
||||
"saveAnyway": "Guardar de todos modos",
|
||||
"saving": "Guardando",
|
||||
"scrollLeft": "Desplazar a la izquierda",
|
||||
"scrollRight": "Desplazar a la derecha",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "Selecciona elementos para duplicar",
|
||||
"selectItemsToRename": "Selecciona elementos para renombrar",
|
||||
"selectedFile": "Archivo seleccionado",
|
||||
"setAKeybindingForTheFollowing": "Establecer un atajo de teclado para lo siguiente:",
|
||||
"setAsBackground": "Establecer como fondo",
|
||||
"settings": "Configuraciones",
|
||||
"shortcutSuffix": " ({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "Detener grabación",
|
||||
"submit": "Enviar",
|
||||
"success": "Éxito",
|
||||
"switchToGridView": "Cambiar a vista de cuadrícula",
|
||||
"switchToSingleView": "Cambiar a vista individual",
|
||||
"systemInfo": "Información del sistema",
|
||||
"terminal": "Terminal",
|
||||
"title": "Título",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "Tú"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "Modo lienzo",
|
||||
"canvasToolbar": "Barra de herramientas del lienzo",
|
||||
"fitView": "Ajustar vista",
|
||||
"focusMode": "Modo de Enfoque",
|
||||
"hand": "Mano",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "Arrastra y suelta una imagen",
|
||||
"emptyWorkflowExplanation": "Tu flujo de trabajo está vacío. Necesitas algunos nodos primero para empezar a construir una aplicación.",
|
||||
"enterNodeGraph": "Entrar al grafo de nodos",
|
||||
"error": {
|
||||
"getHelp": "Para obtener ayuda, consulta nuestro {0}, {1} o {2} con el error copiado.",
|
||||
"github": "enviar un issue en GitHub",
|
||||
"goto": "Mostrar errores en el grafo",
|
||||
"guide": "guía de solución de problemas",
|
||||
"header": "Esta aplicación encontró un error",
|
||||
"log": "Registros de errores",
|
||||
"mobileFixable": "Revisa {0} para ver los errores",
|
||||
"promptShow": "Mostrar informe de error",
|
||||
"promptVisitGraph": "Ve el grafo de nodos para ver el error completo.",
|
||||
"requiresGraph": "Algo salió mal durante la generación. Esto puede deberse a entradas ocultas no válidas, recursos faltantes o problemas de configuración del flujo de trabajo.",
|
||||
"support": "contacta a nuestro soporte"
|
||||
},
|
||||
"giveFeedback": "Enviar comentarios",
|
||||
"graphMode": "Modo gráfico",
|
||||
"hasCreditCost": "Requiere créditos adicionales",
|
||||
"linearMode": "Modo App",
|
||||
"loadTemplate": "Cargar una plantilla",
|
||||
"mobileControls": "Editar y ejecutar",
|
||||
"mobileNoWorkflow": "Este flujo de trabajo no ha sido creado para el modo de aplicación. Prueba con otro.",
|
||||
"queue": {
|
||||
"clear": "Limpiar cola",
|
||||
"clickToClear": "Haz clic para limpiar la cola"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "Volver a ejecutar",
|
||||
"reuseParameters": "Reutilizar parámetros",
|
||||
"runCount": "Número de ejecuciones:",
|
||||
"viewGraph": "Ver grafo de nodos",
|
||||
"viewJob": "Ver tarea",
|
||||
"welcome": {
|
||||
"buildApp": "Crear aplicación",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "INSTALADO",
|
||||
"missingNodes": "Nodos faltantes",
|
||||
"notInstalled": "No instalado",
|
||||
"unresolvedNodes": "Nodos no resueltos",
|
||||
"updatesAvailable": "Actualizaciones disponibles"
|
||||
},
|
||||
"nightlyVersion": "Nocturna",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "Desinstalar",
|
||||
"uninstallSelected": "Desinstalar Seleccionado",
|
||||
"uninstalling": "Desinstalando",
|
||||
"unresolvedNodes": {
|
||||
"message": "Los siguientes nodos no están instalados y no se pudieron encontrar en el registro.",
|
||||
"title": "Nodos faltantes no resueltos"
|
||||
},
|
||||
"update": "Actualizar",
|
||||
"updateAll": "Actualizar Todos",
|
||||
"updateSelected": "Actualizar Seleccionados",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "Rodin",
|
||||
"Runway": "Runway",
|
||||
"Sora": "Sora",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "SIN ENTRADAS",
|
||||
"inputsNoneTooltip": "El nodo no tiene entradas",
|
||||
"locateNode": "Localizar nodo en el lienzo",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "Este modelo ya existe en \"{category}\"",
|
||||
"assetLoadTimeout": "El tiempo de detección del modelo se agotó. Intenta recargar el flujo de trabajo.",
|
||||
"cancelSelection": "Cancelar selección",
|
||||
"clearUrl": "Borrar URL",
|
||||
"collapseNodes": "Ocultar nodos de referencia",
|
||||
"confirmSelection": "Confirmar selección",
|
||||
"copyModelName": "Copiar nombre del modelo",
|
||||
"customNodeDownloadDisabled": "El entorno en la nube no admite la importación de modelos para nodos personalizados en esta sección. Por favor, utiliza nodos estándar de carga o sustituye con un modelo de la biblioteca de abajo.",
|
||||
"expandNodes": "Mostrar nodos de referencia",
|
||||
"import": "Importar",
|
||||
"importAnyway": "Importar de todos modos",
|
||||
"importFailed": "Error al importar",
|
||||
"importNotSupported": "Importación no soportada",
|
||||
"imported": "Importado",
|
||||
"importing": "Importando...",
|
||||
"locateNode": "Localizar nodo en el lienzo",
|
||||
"metadataFetchFailed": "No se pudo obtener los metadatos. Por favor, revisa el enlace e inténtalo de nuevo.",
|
||||
"missingModelsTitle": "Modelos faltantes",
|
||||
"or": "O",
|
||||
"typeMismatch": "Este modelo parece ser un \"{detectedType}\". ¿Estás seguro?",
|
||||
"unknownCategory": "Desconocido",
|
||||
"unsupportedUrl": "Solo se admiten URLs de Civitai y Hugging Face.",
|
||||
"urlPlaceholder": "Pega la URL del modelo (Civitai o Hugging Face)",
|
||||
"useFromLibrary": "Usar de la biblioteca",
|
||||
"usingFromLibrary": "Usando de la biblioteca"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "Aplicar cambios",
|
||||
"cloudMessage": "Este flujo de trabajo requiere nodos personalizados que aún no están disponibles en Comfy Cloud.",
|
||||
@@ -3238,6 +3298,7 @@
|
||||
"interrupted": "La ejecución ha sido interrumpida",
|
||||
"legacyMaskEditorDeprecated": "El editor de máscaras heredado está obsoleto y se eliminará pronto.",
|
||||
"migrateToLitegraphReroute": "Los nodos de reroute se eliminarán en futuras versiones. Haz clic para migrar a reroute nativo de litegraph.",
|
||||
"missingModelVerificationFailed": "No se pudo verificar los modelos faltantes. Algunos modelos pueden no aparecer en la pestaña de Errores.",
|
||||
"modelLoadedSuccessfully": "Modelo 3D cargado exitosamente",
|
||||
"no3dScene": "No hay escena 3D para aplicar textura",
|
||||
"no3dSceneToExport": "No hay escena 3D para exportar",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "Activa la optimización de KV Cache para imágenes de referencia en modelos de la familia Flux.",
|
||||
"display_name": "Flux KV Cache",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "modelo",
|
||||
"tooltip": "El modelo en el que se usará KV Cache."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "El modelo modificado con KV Cache activado."
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "Este nodo redimensiona la imagen a una más óptima para flux kontext.",
|
||||
"display_name": "EscalaImagenFluxKontext",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "Genera imágenes a partir de descripciones de texto usando Reve.",
|
||||
"display_name": "Reve Crear Imagen",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control después de generar"
|
||||
},
|
||||
"model": {
|
||||
"name": "modelo",
|
||||
"tooltip": "Versión del modelo a utilizar para la generación."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "relación_de_aspecto"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "escalado_en_tiempo_de_prueba"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "Descripción en texto de la imagen deseada. Máximo 2560 caracteres."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "eliminar_fondo",
|
||||
"tooltip": "Eliminar el fondo de la imagen generada. Puede tener un costo adicional."
|
||||
},
|
||||
"seed": {
|
||||
"name": "semilla",
|
||||
"tooltip": "La semilla controla si el nodo debe ejecutarse de nuevo; los resultados son no deterministas independientemente de la semilla."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "escalar",
|
||||
"tooltip": "Aumentar la resolución de la imagen generada. Puede tener un costo adicional."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "Edita imágenes usando instrucciones en lenguaje natural con Reve.",
|
||||
"display_name": "Reve Editar Imagen",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control después de generar"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "instrucción_de_edición",
|
||||
"tooltip": "Descripción en texto de cómo editar la imagen. Máximo 2560 caracteres."
|
||||
},
|
||||
"image": {
|
||||
"name": "imagen",
|
||||
"tooltip": "La imagen a editar."
|
||||
},
|
||||
"model": {
|
||||
"name": "modelo",
|
||||
"tooltip": "Versión del modelo a utilizar para la edición."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "relación_de_aspecto"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "escalado_en_tiempo_de_prueba"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "eliminar_fondo",
|
||||
"tooltip": "Eliminar el fondo de la imagen generada. Puede tener un costo adicional."
|
||||
},
|
||||
"seed": {
|
||||
"name": "semilla",
|
||||
"tooltip": "La semilla controla si el nodo debe ejecutarse de nuevo; los resultados son no deterministas independientemente de la semilla."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "escalar",
|
||||
"tooltip": "Aumentar la resolución de la imagen generada. Puede tener un costo adicional."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "Combina imágenes de referencia con prompts de texto para crear nuevas imágenes usando Reve.",
|
||||
"display_name": "Reve Mezclar Imagen",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control después de generar"
|
||||
},
|
||||
"model": {
|
||||
"name": "modelo",
|
||||
"tooltip": "Versión del modelo a utilizar para mezclar."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "relación_de_aspecto"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "escalado_en_tiempo_de_prueba"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "Descripción en texto de la imagen deseada. Puede incluir etiquetas XML img para referenciar imágenes específicas por índice, por ejemplo <img>0</img>, <img>1</img>, etc."
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "imágenes_de_referencia"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "eliminar_fondo",
|
||||
"tooltip": "Eliminar el fondo de la imagen generada. Puede tener un costo adicional."
|
||||
},
|
||||
"seed": {
|
||||
"name": "semilla",
|
||||
"tooltip": "La semilla controla si el nodo debe ejecutarse de nuevo; los resultados son no deterministas independientemente de la semilla."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "escalar",
|
||||
"tooltip": "Aumentar la resolución de la imagen generada. Puede tener un costo adicional."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "Generar activos 3D usando la API de Rodin",
|
||||
"display_name": "Rodin 3D Generar - Generar Detalle",
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "تغییر نام گروهی",
|
||||
"beta": "آزمایشی",
|
||||
"bookmark": "ذخیره در کتابخانه",
|
||||
"browserReservedKeybinding": "این میانبر توسط برخی مرورگرها رزرو شده و ممکن است نتایج غیرمنتظرهای داشته باشد.",
|
||||
"browserReservedKeybindingTooltip": "این میانبر با میانبرهای رزرو شده مرورگر تداخل دارد",
|
||||
"calculatingDimensions": "در حال محاسبه ابعاد",
|
||||
"cancel": "لغو",
|
||||
"cancelled": "لغو شده",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "کپی",
|
||||
"copyAll": "کپی همه",
|
||||
"copyJobId": "کپی شناسه وظیفه",
|
||||
"copySystemInfo": "کپی اطلاعات سیستم",
|
||||
"copyToClipboard": "کپی در کلیپبورد",
|
||||
"copyURL": "کپی آدرس",
|
||||
"core": "هسته",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "نام جدید را وارد کنید",
|
||||
"enterNewNamePrompt": "نام جدید را وارد کنید:",
|
||||
"enterSubgraph": "ورود به زیرگراف",
|
||||
"enterYourKeybind": "کلید میانبر خود را وارد کنید",
|
||||
"error": "خطا",
|
||||
"errorLoadingImage": "خطا در بارگذاری تصویر",
|
||||
"errorLoadingVideo": "خطا در بارگذاری ویدیو",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "دسترسی میکروفون رد شد",
|
||||
"migrate": "مهاجرت",
|
||||
"missing": "ناقص",
|
||||
"modifyKeybinding": "تغییر کلید میانبر",
|
||||
"more": "بیشتر",
|
||||
"moreOptions": "گزینههای بیشتر",
|
||||
"moreWorkflows": "workflowهای بیشتر",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "نام",
|
||||
"newFolder": "پوشه جدید",
|
||||
"next": "بعدی",
|
||||
"nextImage": "تصویر بعدی",
|
||||
"nightly": "نسخه شبانه",
|
||||
"no": "خیر",
|
||||
"noAudioRecorded": "هیچ صدایی ضبط نشد",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "پخش ضبط",
|
||||
"playbackSpeed": "سرعت پخش",
|
||||
"playing": "در حال پخش",
|
||||
"pressKeysForNewBinding": "کلیدهای میانبر جدید را فشار دهید",
|
||||
"preview": "پیشنمایش",
|
||||
"previousImage": "تصویر قبلی",
|
||||
"profile": "پروفایل",
|
||||
"progressCountOf": "از",
|
||||
"queued": "در صف",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "{count} نتیجه یافت شد",
|
||||
"running": "در حال اجرا",
|
||||
"save": "ذخیره",
|
||||
"saveAnyway": "ذخیره کن",
|
||||
"saving": "در حال ذخیره",
|
||||
"scrollLeft": "اسکرول به چپ",
|
||||
"scrollRight": "اسکرول به راست",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "مواردی برای تکرار انتخاب کنید",
|
||||
"selectItemsToRename": "مواردی برای تغییر نام انتخاب کنید",
|
||||
"selectedFile": "فایل انتخابشده",
|
||||
"setAKeybindingForTheFollowing": "کلید میانبر را برای مورد زیر تنظیم کنید:",
|
||||
"setAsBackground": "تنظیم به عنوان پسزمینه",
|
||||
"settings": "تنظیمات",
|
||||
"shortcutSuffix": " ({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "پایان ضبط",
|
||||
"submit": "ارسال",
|
||||
"success": "موفقیتآمیز",
|
||||
"switchToGridView": "تغییر به نمای شبکهای",
|
||||
"switchToSingleView": "تغییر به نمای تکی",
|
||||
"systemInfo": "اطلاعات سیستم",
|
||||
"terminal": "ترمینال",
|
||||
"title": "عنوان",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "شما"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "حالت بوم",
|
||||
"canvasToolbar": "نوار ابزار بوم",
|
||||
"fitView": "تطبیق با نما",
|
||||
"focusMode": "حالت تمرکز",
|
||||
"hand": "دست",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "تصویر را بکشید و رها کنید",
|
||||
"emptyWorkflowExplanation": "جریان کاری شما خالی است. ابتدا باید چند node اضافه کنید تا بتوانید یک برنامه بسازید.",
|
||||
"enterNodeGraph": "ورود به گراف node",
|
||||
"error": {
|
||||
"getHelp": "برای دریافت راهنما، {0}، {1} یا {2} را با خطای کپیشده مشاهده کنید.",
|
||||
"github": "ارسال issue در GitHub",
|
||||
"goto": "نمایش خطاها در گراف",
|
||||
"guide": "راهنمای رفع اشکال",
|
||||
"header": "این اپلیکیشن با خطا مواجه شد",
|
||||
"log": "گزارشهای خطا",
|
||||
"mobileFixable": "برای مشاهده خطاها به {0} مراجعه کنید",
|
||||
"promptShow": "نمایش گزارش خطا",
|
||||
"promptVisitGraph": "برای مشاهده خطای کامل، گراف نود را ببینید.",
|
||||
"requiresGraph": "در طول تولید مشکلی پیش آمد. ممکن است به دلیل ورودیهای مخفی نامعتبر، منابع گمشده یا مشکلات پیکربندی workflow باشد.",
|
||||
"support": "تماس با پشتیبانی ما"
|
||||
},
|
||||
"giveFeedback": "ارسال بازخورد",
|
||||
"graphMode": "حالت گراف",
|
||||
"hasCreditCost": "نیازمند اعتبار اضافی",
|
||||
"linearMode": "حالت برنامه",
|
||||
"loadTemplate": "بارگذاری قالب",
|
||||
"mobileControls": "ویرایش و اجرا",
|
||||
"mobileNoWorkflow": "این workflow برای حالت اپلیکیشن ساخته نشده است. مورد دیگری را امتحان کنید.",
|
||||
"queue": {
|
||||
"clear": "پاکسازی صف",
|
||||
"clickToClear": "برای پاکسازی صف کلیک کنید"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "اجرای مجدد",
|
||||
"reuseParameters": "استفاده مجدد از پارامترها",
|
||||
"runCount": "تعداد اجرا: ",
|
||||
"viewGraph": "مشاهده گراف نود",
|
||||
"viewJob": "مشاهده وظیفه",
|
||||
"welcome": {
|
||||
"buildApp": "ساخت اپلیکیشن",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "نصب شده",
|
||||
"missingNodes": "Nodeهای مفقود",
|
||||
"notInstalled": "نصب نشده",
|
||||
"unresolvedNodes": "نودهای حلنشده",
|
||||
"updatesAvailable": "بهروزرسانیهای موجود"
|
||||
},
|
||||
"nightlyVersion": "نسخه nightly",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "حذف نصب",
|
||||
"uninstallSelected": "حذف نصب انتخابشدهها",
|
||||
"uninstalling": "در حال حذف نصب {id}",
|
||||
"unresolvedNodes": {
|
||||
"message": "نودهای زیر نصب نشدهاند و در رجیستری یافت نشدند.",
|
||||
"title": "نودهای گمشده حلنشده"
|
||||
},
|
||||
"update": "بهروزرسانی",
|
||||
"updateAll": "بهروزرسانی همه",
|
||||
"updateSelected": "بهروزرسانی انتخابشدهها",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "Rodin",
|
||||
"Runway": "Runway",
|
||||
"Sora": "Sora",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "بدون ورودی",
|
||||
"inputsNoneTooltip": "این نود ورودی ندارد",
|
||||
"locateNode": "یافتن node در canvas",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "این مدل قبلاً در «{category}» وجود دارد",
|
||||
"assetLoadTimeout": "شناسایی مدل زمانبر شد. لطفاً workflow را مجدداً بارگذاری کنید.",
|
||||
"cancelSelection": "لغو انتخاب",
|
||||
"clearUrl": "پاک کردن آدرس",
|
||||
"collapseNodes": "مخفی کردن nodeهای مرتبط",
|
||||
"confirmSelection": "تأیید انتخاب",
|
||||
"copyModelName": "کپی نام مدل",
|
||||
"customNodeDownloadDisabled": "محیط ابری از وارد کردن مدل برای custom nodeها در این بخش پشتیبانی نمیکند. لطفاً از nodeهای بارگذار استاندارد استفاده کنید یا مدلی از کتابخانه زیر جایگزین نمایید.",
|
||||
"expandNodes": "نمایش nodeهای مرتبط",
|
||||
"import": "وارد کردن",
|
||||
"importAnyway": "به هر حال وارد کن",
|
||||
"importFailed": "وارد کردن انجام نشد",
|
||||
"importNotSupported": "وارد کردن پشتیبانی نمیشود",
|
||||
"imported": "وارد شد",
|
||||
"importing": "در حال وارد کردن...",
|
||||
"locateNode": "یافتن node در بوم",
|
||||
"metadataFetchFailed": "دریافت اطلاعات مدل انجام نشد. لطفاً لینک را بررسی و دوباره تلاش کنید.",
|
||||
"missingModelsTitle": "مدلهای مفقود شده",
|
||||
"or": "یا",
|
||||
"typeMismatch": "به نظر میرسد این مدل از نوع \"{detectedType}\" باشد. مطمئن هستید؟",
|
||||
"unknownCategory": "نامشخص",
|
||||
"unsupportedUrl": "فقط آدرسهای Civitai و Hugging Face پشتیبانی میشوند.",
|
||||
"urlPlaceholder": "آدرس مدل را وارد کنید (Civitai یا Hugging Face)",
|
||||
"useFromLibrary": "استفاده از کتابخانه",
|
||||
"usingFromLibrary": "در حال استفاده از کتابخانه"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "اعمال تغییرات",
|
||||
"cloudMessage": "این workflow به nodeهای سفارشی نیاز دارد که هنوز در Comfy Cloud موجود نیستند.",
|
||||
@@ -3250,6 +3310,7 @@
|
||||
"interrupted": "اجرا متوقف شد",
|
||||
"legacyMaskEditorDeprecated": "ویرایشگر mask قدیمی منسوخ شده و بهزودی حذف خواهد شد.",
|
||||
"migrateToLitegraphReroute": "nodeهای reroute در نسخههای آینده حذف خواهند شد. برای مهاجرت به reroute بومی litegraph کلیک کنید.",
|
||||
"missingModelVerificationFailed": "تأیید مدلهای مفقود شده انجام نشد. برخی مدلها ممکن است در برگه خطاها نمایش داده نشوند.",
|
||||
"modelLoadedSuccessfully": "مدل سهبعدی با موفقیت بارگذاری شد",
|
||||
"no3dScene": "صحنه سهبعدی برای اعمال بافت وجود ندارد",
|
||||
"no3dSceneToExport": "صحنه سهبعدی برای صادرات وجود ندارد",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "بهینهسازی KV Cache را برای تصاویر مرجع در مدلهای خانواده Flux فعال میکند.",
|
||||
"display_name": "Flux KV Cache",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "مدل",
|
||||
"tooltip": "مدلی که قرار است KV Cache روی آن فعال شود."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "مدل اصلاحشده با KV Cache فعال."
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "این نود تصویر را به اندازهای تغییر میدهد که برای flux kontext بهینهتر باشد.",
|
||||
"display_name": "FluxKontextImageScale",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "تولید تصویر از توضیحات متنی با استفاده از Reve.",
|
||||
"display_name": "ایجاد تصویر با Reve",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "کنترل پس از تولید"
|
||||
},
|
||||
"model": {
|
||||
"name": "مدل",
|
||||
"tooltip": "نسخه مدل مورد استفاده برای تولید."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "نسبت ابعاد"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "مقیاسدهی زمان تست"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "پرامپت",
|
||||
"tooltip": "توضیح متنی تصویر مورد نظر. حداکثر ۲۵۶۰ کاراکتر."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "حذف پسزمینه",
|
||||
"tooltip": "حذف پسزمینه از تصویر تولید شده. ممکن است هزینه اضافی داشته باشد."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "seed تعیین میکند که node باید دوباره اجرا شود یا خیر؛ نتایج صرفنظر از seed غیرقطعی هستند."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "افزایش وضوح",
|
||||
"tooltip": "افزایش وضوح تصویر تولید شده. ممکن است هزینه اضافی داشته باشد."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "ویرایش تصاویر با استفاده از دستور زبان طبیعی در Reve.",
|
||||
"display_name": "ویرایش تصویر با Reve",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "کنترل پس از تولید"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "دستور ویرایش",
|
||||
"tooltip": "توضیح متنی نحوه ویرایش تصویر. حداکثر ۲۵۶۰ کاراکتر."
|
||||
},
|
||||
"image": {
|
||||
"name": "تصویر",
|
||||
"tooltip": "تصویری که باید ویرایش شود."
|
||||
},
|
||||
"model": {
|
||||
"name": "مدل",
|
||||
"tooltip": "نسخه مدل مورد استفاده برای ویرایش."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "نسبت ابعاد"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "مقیاسدهی زمان تست"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "حذف پسزمینه",
|
||||
"tooltip": "حذف پسزمینه از تصویر تولید شده. ممکن است هزینه اضافی داشته باشد."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "seed تعیین میکند که node باید دوباره اجرا شود یا خیر؛ نتایج صرفنظر از seed غیرقطعی هستند."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "افزایش وضوح",
|
||||
"tooltip": "افزایش وضوح تصویر تولید شده. ممکن است هزینه اضافی داشته باشد."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "ترکیب تصاویر مرجع با پرامپت متنی برای ایجاد تصاویر جدید با استفاده از Reve.",
|
||||
"display_name": "ترکیب تصویر با Reve",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "کنترل پس از تولید"
|
||||
},
|
||||
"model": {
|
||||
"name": "مدل",
|
||||
"tooltip": "نسخه مدل مورد استفاده برای ترکیب."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "نسبت ابعاد"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "مقیاسدهی زمان تست"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "پرامپت",
|
||||
"tooltip": "توضیح متنی تصویر مورد نظر. میتوانید از تگ XML img برای ارجاع به تصاویر خاص با شماره اندیس استفاده کنید، مانند <img>۰</img>، <img>۱</img> و غیره."
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "تصاویر مرجع"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "حذف پسزمینه",
|
||||
"tooltip": "حذف پسزمینه از تصویر تولید شده. ممکن است هزینه اضافی داشته باشد."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "seed تعیین میکند که node باید دوباره اجرا شود یا خیر؛ نتایج صرفنظر از seed غیرقطعی هستند."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "افزایش وضوح",
|
||||
"tooltip": "افزایش وضوح تصویر تولید شده. ممکن است هزینه اضافی داشته باشد."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "تولید داراییهای سهبعدی با استفاده از Rodin API",
|
||||
"display_name": "Rodin 3D Generate - تولید جزئیات",
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "Renommer en lot",
|
||||
"beta": "BÊTA",
|
||||
"bookmark": "Enregistrer dans la bibliothèque",
|
||||
"browserReservedKeybinding": "Ce raccourci est réservé par certains navigateurs et peut entraîner des résultats inattendus.",
|
||||
"browserReservedKeybindingTooltip": "Ce raccourci est en conflit avec des raccourcis réservés du navigateur",
|
||||
"calculatingDimensions": "Calcul des dimensions",
|
||||
"cancel": "Annuler",
|
||||
"cancelled": "Annulé",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "Copier",
|
||||
"copyAll": "Tout copier",
|
||||
"copyJobId": "Copier l'ID du travail",
|
||||
"copySystemInfo": "Copier les informations système",
|
||||
"copyToClipboard": "Copier dans le presse-papiers",
|
||||
"copyURL": "Copier l’URL",
|
||||
"core": "Noyau",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "Entrez le nouveau nom",
|
||||
"enterNewNamePrompt": "Entrez le nouveau nom :",
|
||||
"enterSubgraph": "Entrer dans le sous-graphe",
|
||||
"enterYourKeybind": "Saisissez votre raccourci",
|
||||
"error": "Erreur",
|
||||
"errorLoadingImage": "Erreur lors du chargement de l'image",
|
||||
"errorLoadingVideo": "Erreur lors du chargement de la vidéo",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "Permission du microphone refusée",
|
||||
"migrate": "Migrer",
|
||||
"missing": "Manquant",
|
||||
"modifyKeybinding": "Modifier le raccourci clavier",
|
||||
"more": "Plus",
|
||||
"moreOptions": "Plus d'options",
|
||||
"moreWorkflows": "Plus de workflows",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "Nom",
|
||||
"newFolder": "Nouveau dossier",
|
||||
"next": "Suivant",
|
||||
"nextImage": "Image suivante",
|
||||
"nightly": "NIGHTLY",
|
||||
"no": "Non",
|
||||
"noAudioRecorded": "Aucun audio enregistré",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "Lire l'enregistrement",
|
||||
"playbackSpeed": "Vitesse de lecture",
|
||||
"playing": "Lecture en cours",
|
||||
"pressKeysForNewBinding": "Appuyez sur les touches pour une nouvelle liaison",
|
||||
"preview": "APERÇU",
|
||||
"previousImage": "Image précédente",
|
||||
"profile": "Profil",
|
||||
"progressCountOf": "sur",
|
||||
"queued": "En file d’attente",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "{count} Résultats Trouvés",
|
||||
"running": "En cours",
|
||||
"save": "Enregistrer",
|
||||
"saveAnyway": "Enregistrer quand même",
|
||||
"saving": "Enregistrement",
|
||||
"scrollLeft": "Faire défiler à gauche",
|
||||
"scrollRight": "Faire défiler à droite",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "Sélectionnez les éléments à dupliquer",
|
||||
"selectItemsToRename": "Sélectionnez les éléments à renommer",
|
||||
"selectedFile": "Fichier sélectionné",
|
||||
"setAKeybindingForTheFollowing": "Définir un raccourci clavier pour :",
|
||||
"setAsBackground": "Définir comme arrière-plan",
|
||||
"settings": "Paramètres",
|
||||
"shortcutSuffix": " ({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "Arrêter l’enregistrement",
|
||||
"submit": "Soumettre",
|
||||
"success": "Succès",
|
||||
"switchToGridView": "Passer à la vue grille",
|
||||
"switchToSingleView": "Passer à la vue unique",
|
||||
"systemInfo": "Informations système",
|
||||
"terminal": "Terminal",
|
||||
"title": "Titre",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "Vous"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "Mode canevas",
|
||||
"canvasToolbar": "Barre d’outils du canevas",
|
||||
"fitView": "Adapter la vue",
|
||||
"focusMode": "Mode focus",
|
||||
"hand": "Main",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "Glissez-déposez une image",
|
||||
"emptyWorkflowExplanation": "Votre workflow est vide. Vous devez d'abord ajouter des nodes pour commencer à créer une application.",
|
||||
"enterNodeGraph": "Entrer dans le graphique de nœuds",
|
||||
"error": {
|
||||
"getHelp": "Pour obtenir de l’aide, consultez notre {0}, {1} ou {2} avec l’erreur copiée.",
|
||||
"github": "soumettre un ticket GitHub",
|
||||
"goto": "Afficher les erreurs dans le graphe",
|
||||
"guide": "guide de dépannage",
|
||||
"header": "Une erreur est survenue dans cette application",
|
||||
"log": "Journaux d’erreurs",
|
||||
"mobileFixable": "Vérifiez {0} pour les erreurs",
|
||||
"promptShow": "Afficher le rapport d’erreur",
|
||||
"promptVisitGraph": "Consultez le graphe des nœuds pour voir l’erreur complète.",
|
||||
"requiresGraph": "Une erreur s’est produite lors de la génération. Cela peut être dû à des entrées cachées invalides, des ressources manquantes ou des problèmes de configuration du workflow.",
|
||||
"support": "contacter notre support"
|
||||
},
|
||||
"giveFeedback": "Donner un avis",
|
||||
"graphMode": "Mode graphique",
|
||||
"hasCreditCost": "Nécessite des crédits supplémentaires",
|
||||
"linearMode": "Mode App",
|
||||
"loadTemplate": "Charger un modèle",
|
||||
"mobileControls": "Éditer & Exécuter",
|
||||
"mobileNoWorkflow": "Ce workflow n’a pas été conçu pour le mode application. Essayez-en un autre.",
|
||||
"queue": {
|
||||
"clear": "Vider la file d'attente",
|
||||
"clickToClear": "Cliquez pour vider la file d'attente"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "Relancer",
|
||||
"reuseParameters": "Réutiliser les paramètres",
|
||||
"runCount": "Nombre d’exécutions :",
|
||||
"viewGraph": "Voir le graphe des nœuds",
|
||||
"viewJob": "Voir la tâche",
|
||||
"welcome": {
|
||||
"buildApp": "Créer une application",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "INSTALLÉ",
|
||||
"missingNodes": "Nœuds manquants",
|
||||
"notInstalled": "Non installé",
|
||||
"unresolvedNodes": "Nœuds non résolus",
|
||||
"updatesAvailable": "Mises à jour disponibles"
|
||||
},
|
||||
"nightlyVersion": "Nocturne",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "Désinstaller",
|
||||
"uninstallSelected": "Désinstaller sélectionné",
|
||||
"uninstalling": "Désinstallation",
|
||||
"unresolvedNodes": {
|
||||
"message": "Les nœuds suivants ne sont pas installés et n'ont pas pu être trouvés dans le registre.",
|
||||
"title": "Nœuds manquants non résolus"
|
||||
},
|
||||
"update": "Mettre à jour",
|
||||
"updateAll": "Tout mettre à jour",
|
||||
"updateSelected": "Mettre à jour la sélection",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "Rodin",
|
||||
"Runway": "Runway",
|
||||
"Sora": "Sora",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "AUCUNE ENTRÉE",
|
||||
"inputsNoneTooltip": "Le nœud n’a pas d’entrées",
|
||||
"locateNode": "Localiser le nœud sur le canevas",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "Ce modèle existe déjà dans « {category} »",
|
||||
"assetLoadTimeout": "Le délai de détection du modèle est dépassé. Essayez de recharger le workflow.",
|
||||
"cancelSelection": "Annuler la sélection",
|
||||
"clearUrl": "Effacer l’URL",
|
||||
"collapseNodes": "Masquer les nœuds référencés",
|
||||
"confirmSelection": "Confirmer la sélection",
|
||||
"copyModelName": "Copier le nom du modèle",
|
||||
"customNodeDownloadDisabled": "L’environnement cloud ne prend pas en charge l’importation de modèles pour les nœuds personnalisés dans cette section. Veuillez utiliser des nœuds de chargement standard ou remplacer par un modèle de la bibliothèque ci-dessous.",
|
||||
"expandNodes": "Afficher les nœuds référencés",
|
||||
"import": "Importer",
|
||||
"importAnyway": "Importer quand même",
|
||||
"importFailed": "Échec de l’importation",
|
||||
"importNotSupported": "Importation non prise en charge",
|
||||
"imported": "Importé",
|
||||
"importing": "Importation...",
|
||||
"locateNode": "Localiser le nœud sur le canevas",
|
||||
"metadataFetchFailed": "Échec de la récupération des métadonnées. Veuillez vérifier le lien et réessayer.",
|
||||
"missingModelsTitle": "Modèles manquants",
|
||||
"or": "OU",
|
||||
"typeMismatch": "Ce modèle semble être un(e) « {detectedType} ». Êtes-vous sûr ?",
|
||||
"unknownCategory": "Inconnu",
|
||||
"unsupportedUrl": "Seules les URL Civitai et Hugging Face sont prises en charge.",
|
||||
"urlPlaceholder": "Collez l’URL du modèle (Civitai ou Hugging Face)",
|
||||
"useFromLibrary": "Utiliser depuis la bibliothèque",
|
||||
"usingFromLibrary": "Utilisation depuis la bibliothèque"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "Appliquer les modifications",
|
||||
"cloudMessage": "Ce workflow nécessite des nœuds personnalisés qui ne sont pas encore disponibles sur Comfy Cloud.",
|
||||
@@ -3238,6 +3298,7 @@
|
||||
"interrupted": "L'exécution a été interrompue",
|
||||
"legacyMaskEditorDeprecated": "L’éditeur de masque hérité est obsolète et sera bientôt supprimé.",
|
||||
"migrateToLitegraphReroute": "Les nœuds de reroute seront supprimés dans les futures versions. Cliquez pour migrer vers le reroute natif de litegraph.",
|
||||
"missingModelVerificationFailed": "Échec de la vérification des modèles manquants. Certains modèles peuvent ne pas apparaître dans l’onglet Erreurs.",
|
||||
"modelLoadedSuccessfully": "Modèle 3D chargé avec succès",
|
||||
"no3dScene": "Aucune scène 3D pour appliquer la texture",
|
||||
"no3dSceneToExport": "Aucune scène 3D à exporter",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "Active l’optimisation KV Cache pour les images de référence sur les modèles de la famille Flux.",
|
||||
"display_name": "Flux KV Cache",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "modèle",
|
||||
"tooltip": "Le modèle sur lequel activer le KV Cache."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "Le modèle modifié avec le KV Cache activé."
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "Ce nœud redimensionne l'image pour une optimisation avec flux kontext.",
|
||||
"display_name": "Échelle d'image FluxKontext",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "Générez des images à partir de descriptions textuelles avec Reve.",
|
||||
"display_name": "Reve Création d’Image",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "contrôle après génération"
|
||||
},
|
||||
"model": {
|
||||
"name": "modèle",
|
||||
"tooltip": "Version du modèle à utiliser pour la génération."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "rapport d’aspect"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "mise à l’échelle à l’exécution"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "Description textuelle de l’image souhaitée. Maximum 2560 caractères."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "supprimer l’arrière-plan",
|
||||
"tooltip": "Supprimer l’arrière-plan de l’image générée. Peut entraîner un coût supplémentaire."
|
||||
},
|
||||
"seed": {
|
||||
"name": "graine",
|
||||
"tooltip": "La graine contrôle si le nœud doit être relancé ; les résultats restent non déterministes quel que soit la graine."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "agrandir",
|
||||
"tooltip": "Agrandir l’image générée. Peut entraîner un coût supplémentaire."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "Modifiez des images à l’aide d’instructions en langage naturel avec Reve.",
|
||||
"display_name": "Reve Édition d’Image",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "contrôle après génération"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "instruction d’édition",
|
||||
"tooltip": "Description textuelle de la modification à apporter à l’image. Maximum 2560 caractères."
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"tooltip": "L’image à modifier."
|
||||
},
|
||||
"model": {
|
||||
"name": "modèle",
|
||||
"tooltip": "Version du modèle à utiliser pour l’édition."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "rapport d’aspect"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "mise à l’échelle à l’exécution"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "supprimer l’arrière-plan",
|
||||
"tooltip": "Supprimer l’arrière-plan de l’image générée. Peut entraîner un coût supplémentaire."
|
||||
},
|
||||
"seed": {
|
||||
"name": "graine",
|
||||
"tooltip": "La graine contrôle si le nœud doit être relancé ; les résultats restent non déterministes quel que soit la graine."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "agrandir",
|
||||
"tooltip": "Agrandir l’image générée. Peut entraîner un coût supplémentaire."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "Combinez des images de référence avec des prompts textuels pour créer de nouvelles images avec Reve.",
|
||||
"display_name": "Reve Remix d’Image",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "contrôle après génération"
|
||||
},
|
||||
"model": {
|
||||
"name": "modèle",
|
||||
"tooltip": "Version du modèle à utiliser pour le remix."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "rapport d’aspect"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "mise à l’échelle à l’exécution"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "Description textuelle de l’image souhaitée. Peut inclure des balises XML img pour référencer des images spécifiques par index, par exemple <img>0</img>, <img>1</img>, etc."
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "images de référence"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "supprimer l’arrière-plan",
|
||||
"tooltip": "Supprimer l’arrière-plan de l’image générée. Peut entraîner un coût supplémentaire."
|
||||
},
|
||||
"seed": {
|
||||
"name": "graine",
|
||||
"tooltip": "La graine contrôle si le nœud doit être relancé ; les résultats restent non déterministes quel que soit la graine."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "agrandir",
|
||||
"tooltip": "Agrandir l’image générée. Peut entraîner un coût supplémentaire."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "Générer des actifs 3D en utilisant l'API Rodin",
|
||||
"display_name": "Rodin 3D Générer - Générer Détails",
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "一括リネーム",
|
||||
"beta": "ベータ版",
|
||||
"bookmark": "ライブラリに保存",
|
||||
"browserReservedKeybinding": "このショートカットは一部のブラウザで予約されており、予期しない動作を引き起こす可能性があります。",
|
||||
"browserReservedKeybindingTooltip": "このショートカットはブラウザの予約済みショートカットと競合しています",
|
||||
"calculatingDimensions": "寸法を計算中",
|
||||
"cancel": "キャンセル",
|
||||
"cancelled": "キャンセル済み",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "コピー",
|
||||
"copyAll": "すべてコピー",
|
||||
"copyJobId": "ジョブIDをコピー",
|
||||
"copySystemInfo": "システム情報をコピー",
|
||||
"copyToClipboard": "クリップボードにコピー",
|
||||
"copyURL": "URLをコピー",
|
||||
"core": "コア",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "新しい名前を入力",
|
||||
"enterNewNamePrompt": "新しい名前を入力してください:",
|
||||
"enterSubgraph": "サブグラフに入る",
|
||||
"enterYourKeybind": "キーバインドを入力してください",
|
||||
"error": "エラー",
|
||||
"errorLoadingImage": "画像の読み込みエラー",
|
||||
"errorLoadingVideo": "ビデオの読み込みエラー",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "マイクの許可が拒否されました",
|
||||
"migrate": "移行する",
|
||||
"missing": "不足している",
|
||||
"modifyKeybinding": "キーバインドを変更",
|
||||
"more": "もっと見る",
|
||||
"moreOptions": "その他のオプション",
|
||||
"moreWorkflows": "さらに多くのワークフロー",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "名前",
|
||||
"newFolder": "新しいフォルダー",
|
||||
"next": "次へ",
|
||||
"nextImage": "次の画像",
|
||||
"nightly": "NIGHTLY",
|
||||
"no": "いいえ",
|
||||
"noAudioRecorded": "音声が録音されていません",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "録音を再生",
|
||||
"playbackSpeed": "再生速度",
|
||||
"playing": "再生中",
|
||||
"pressKeysForNewBinding": "新しいバインドのキーを押してください",
|
||||
"preview": "プレビュー",
|
||||
"previousImage": "前の画像",
|
||||
"profile": "プロフィール",
|
||||
"progressCountOf": "の",
|
||||
"queued": "キュー中",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "{count}件の結果が見つかりました",
|
||||
"running": "実行中",
|
||||
"save": "保存",
|
||||
"saveAnyway": "強制的に保存",
|
||||
"saving": "保存中",
|
||||
"scrollLeft": "左にスクロール",
|
||||
"scrollRight": "右にスクロール",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "複製する項目を選択",
|
||||
"selectItemsToRename": "リネームする項目を選択",
|
||||
"selectedFile": "選択されたファイル",
|
||||
"setAKeybindingForTheFollowing": "次の操作のキーバインドを設定:",
|
||||
"setAsBackground": "背景として設定",
|
||||
"settings": "設定",
|
||||
"shortcutSuffix": "({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "録音停止",
|
||||
"submit": "送信",
|
||||
"success": "成功",
|
||||
"switchToGridView": "グリッドビューに切り替え",
|
||||
"switchToSingleView": "シングルビューに切り替え",
|
||||
"systemInfo": "システム情報",
|
||||
"terminal": "ターミナル",
|
||||
"title": "タイトル",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "あなた"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "キャンバスモード",
|
||||
"canvasToolbar": "キャンバスツールバー",
|
||||
"fitView": "ビューに合わせる",
|
||||
"focusMode": "フォーカスモード",
|
||||
"hand": "パンビュー",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "画像をドラッグ&ドロップ",
|
||||
"emptyWorkflowExplanation": "ワークフローが空です。アプリを作成するには、まずノードを追加してください。",
|
||||
"enterNodeGraph": "ノードグラフに入る",
|
||||
"error": {
|
||||
"getHelp": "ヘルプが必要な場合は、コピーしたエラーを使って{0}、{1}、または{2}をご覧ください。",
|
||||
"github": "GitHub イシューを提出",
|
||||
"goto": "グラフでエラーを表示",
|
||||
"guide": "トラブルシューティングガイド",
|
||||
"header": "このアプリでエラーが発生しました",
|
||||
"log": "エラーログ",
|
||||
"mobileFixable": "{0}でエラーを確認してください",
|
||||
"promptShow": "エラーレポートを表示",
|
||||
"promptVisitGraph": "ノードグラフを表示して、エラーの詳細を確認してください。",
|
||||
"requiresGraph": "生成中に問題が発生しました。無効な非表示入力、リソースの不足、またはワークフロー設定の問題が原因の可能性があります。",
|
||||
"support": "サポートに連絡"
|
||||
},
|
||||
"giveFeedback": "フィードバックを送る",
|
||||
"graphMode": "グラフモード",
|
||||
"hasCreditCost": "追加クレジットが必要です",
|
||||
"linearMode": "アプリモード",
|
||||
"loadTemplate": "テンプレートを読み込む",
|
||||
"mobileControls": "編集と実行",
|
||||
"mobileNoWorkflow": "このワークフローはアプリモード用に作成されていません。別のものをお試しください。",
|
||||
"queue": {
|
||||
"clear": "キューをクリア",
|
||||
"clickToClear": "クリックしてキューをクリア"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "再実行",
|
||||
"reuseParameters": "パラメータを再利用",
|
||||
"runCount": "実行回数:",
|
||||
"viewGraph": "ノードグラフを表示",
|
||||
"viewJob": "ジョブを表示",
|
||||
"welcome": {
|
||||
"buildApp": "アプリを作成",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "インストール済み",
|
||||
"missingNodes": "不足しているノード",
|
||||
"notInstalled": "未インストール",
|
||||
"unresolvedNodes": "未解決ノード",
|
||||
"updatesAvailable": "アップデートあり"
|
||||
},
|
||||
"nightlyVersion": "ナイトリー",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "アンインストール",
|
||||
"uninstallSelected": "選択したものをアンインストール",
|
||||
"uninstalling": "アンインストール中",
|
||||
"unresolvedNodes": {
|
||||
"message": "以下のノードがインストールされておらず、レジストリで見つかりませんでした。",
|
||||
"title": "未解決の欠落ノード"
|
||||
},
|
||||
"update": "更新",
|
||||
"updateAll": "すべて更新",
|
||||
"updateSelected": "選択したものを更新",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "Rodin",
|
||||
"Runway": "Runway",
|
||||
"Sora": "Sora",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "入力なし",
|
||||
"inputsNoneTooltip": "このノードには入力がありません",
|
||||
"locateNode": "キャンバス上でノードを探す",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "このモデルはすでに「{category}」に存在します",
|
||||
"assetLoadTimeout": "モデルの検出がタイムアウトしました。ワークフローを再読み込みしてください。",
|
||||
"cancelSelection": "選択をキャンセル",
|
||||
"clearUrl": "URLをクリア",
|
||||
"collapseNodes": "参照ノードを非表示",
|
||||
"confirmSelection": "選択を確定",
|
||||
"copyModelName": "モデル名をコピー",
|
||||
"customNodeDownloadDisabled": "クラウド環境ではこのセクションのカスタムノード用モデルのインポートはサポートされていません。標準のローダーノードを使用するか、下記のライブラリからモデルを代用してください。",
|
||||
"expandNodes": "参照ノードを表示",
|
||||
"import": "インポート",
|
||||
"importAnyway": "強制的にインポート",
|
||||
"importFailed": "インポートに失敗しました",
|
||||
"importNotSupported": "インポートはサポートされていません",
|
||||
"imported": "インポート完了",
|
||||
"importing": "インポート中...",
|
||||
"locateNode": "キャンバス上でノードを表示",
|
||||
"metadataFetchFailed": "メタデータの取得に失敗しました。リンクを確認して再試行してください。",
|
||||
"missingModelsTitle": "不足しているモデル",
|
||||
"or": "または",
|
||||
"typeMismatch": "このモデルは「{detectedType}」のようです。本当に続行しますか?",
|
||||
"unknownCategory": "不明",
|
||||
"unsupportedUrl": "Civitai と Hugging Face のURLのみサポートされています。",
|
||||
"urlPlaceholder": "モデルのURLを貼り付け(Civitai または Hugging Face)",
|
||||
"useFromLibrary": "ライブラリから使用",
|
||||
"usingFromLibrary": "ライブラリから使用中"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "変更を適用",
|
||||
"cloudMessage": "このワークフローにはComfy Cloudでまだ利用できないカスタムノードが必要です。",
|
||||
@@ -3238,6 +3298,7 @@
|
||||
"interrupted": "実行が中断されました",
|
||||
"legacyMaskEditorDeprecated": "従来のマスクエディタは非推奨となり、まもなく削除されます。",
|
||||
"migrateToLitegraphReroute": "将来のバージョンではRerouteノードが削除されます。litegraph-native rerouteに移行するにはクリックしてください。",
|
||||
"missingModelVerificationFailed": "不足しているモデルの検証に失敗しました。一部のモデルはエラータブに表示されない場合があります。",
|
||||
"modelLoadedSuccessfully": "3Dモデルが正常に読み込まれました",
|
||||
"no3dScene": "テクスチャを適用する3Dシーンがありません",
|
||||
"no3dSceneToExport": "エクスポートする3Dシーンがありません",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "Fluxファミリーモデルの参照画像に対してKVキャッシュ最適化を有効にします。",
|
||||
"display_name": "Flux KVキャッシュ",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "モデル",
|
||||
"tooltip": "KVキャッシュを使用するモデル。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "KVキャッシュが有効になったパッチ済みモデル。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "このノードは、Flux Kontextに最適なサイズに画像をリサイズします。",
|
||||
"display_name": "FluxKontextImageScale",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "Reveを使用してテキスト説明から画像を生成します。",
|
||||
"display_name": "Reve画像生成",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "生成後のコントロール"
|
||||
},
|
||||
"model": {
|
||||
"name": "モデル",
|
||||
"tooltip": "生成に使用するモデルバージョン。"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "アスペクト比"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "テスト時スケーリング"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "プロンプト",
|
||||
"tooltip": "生成したい画像のテキスト説明。最大2560文字。"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "背景を削除",
|
||||
"tooltip": "生成された画像から背景を削除します。追加料金が発生する場合があります。"
|
||||
},
|
||||
"seed": {
|
||||
"name": "シード",
|
||||
"tooltip": "シードはノードの再実行を制御しますが、シードに関わらず結果は非決定的です。"
|
||||
},
|
||||
"upscale": {
|
||||
"name": "アップスケール",
|
||||
"tooltip": "生成された画像をアップスケールします。追加料金が発生する場合があります。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "Reveを使って自然言語指示で画像を編集します。",
|
||||
"display_name": "Reve画像編集",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "生成後のコントロール"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "編集指示",
|
||||
"tooltip": "画像の編集方法のテキスト説明。最大2560文字。"
|
||||
},
|
||||
"image": {
|
||||
"name": "画像",
|
||||
"tooltip": "編集する画像。"
|
||||
},
|
||||
"model": {
|
||||
"name": "モデル",
|
||||
"tooltip": "編集に使用するモデルバージョン。"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "アスペクト比"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "テスト時スケーリング"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "背景を削除",
|
||||
"tooltip": "生成された画像から背景を削除します。追加料金が発生する場合があります。"
|
||||
},
|
||||
"seed": {
|
||||
"name": "シード",
|
||||
"tooltip": "シードはノードの再実行を制御しますが、シードに関わらず結果は非決定的です。"
|
||||
},
|
||||
"upscale": {
|
||||
"name": "アップスケール",
|
||||
"tooltip": "生成された画像をアップスケールします。追加料金が発生する場合があります。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "参照画像とテキストプロンプトを組み合わせてReveで新しい画像を作成します。",
|
||||
"display_name": "Reve画像リミックス",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "生成後のコントロール"
|
||||
},
|
||||
"model": {
|
||||
"name": "モデル",
|
||||
"tooltip": "リミックスに使用するモデルバージョン。"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "アスペクト比"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "テスト時スケーリング"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "プロンプト",
|
||||
"tooltip": "生成したい画像のテキスト説明。特定の画像をインデックスで参照するXML imgタグ(例:<img>0</img>, <img>1</img>など)を含めることができます。"
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "参照画像"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "背景を削除",
|
||||
"tooltip": "生成された画像から背景を削除します。追加料金が発生する場合があります。"
|
||||
},
|
||||
"seed": {
|
||||
"name": "シード",
|
||||
"tooltip": "シードはノードの再実行を制御しますが、シードに関わらず結果は非決定的です。"
|
||||
},
|
||||
"upscale": {
|
||||
"name": "アップスケール",
|
||||
"tooltip": "生成された画像をアップスケールします。追加料金が発生する場合があります。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "Rodin APIを使用して3Dアセットを生成",
|
||||
"display_name": "Rodin 3D生成 - 詳細生成",
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "일괄 이름 변경",
|
||||
"beta": "베타",
|
||||
"bookmark": "라이브러리에 저장",
|
||||
"browserReservedKeybinding": "이 단축키는 일부 브라우저에서 예약되어 있어 예기치 않은 결과가 발생할 수 있습니다.",
|
||||
"browserReservedKeybindingTooltip": "이 단축키는 브라우저 예약 단축키와 충돌합니다",
|
||||
"calculatingDimensions": "크기 계산 중",
|
||||
"cancel": "취소",
|
||||
"cancelled": "취소됨",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "복사",
|
||||
"copyAll": "모두 복사",
|
||||
"copyJobId": "작업 ID 복사",
|
||||
"copySystemInfo": "시스템 정보 복사",
|
||||
"copyToClipboard": "클립보드에 복사",
|
||||
"copyURL": "URL 복사",
|
||||
"core": "코어",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "새 이름 입력",
|
||||
"enterNewNamePrompt": "새 이름을 입력하세요:",
|
||||
"enterSubgraph": "서브그래프 진입",
|
||||
"enterYourKeybind": "단축키를 입력하세요",
|
||||
"error": "오류",
|
||||
"errorLoadingImage": "이미지 로드 오류",
|
||||
"errorLoadingVideo": "비디오 로드 오류",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "마이크 권한이 거부되었습니다",
|
||||
"migrate": "이전(migrate)",
|
||||
"missing": "누락됨",
|
||||
"modifyKeybinding": "단축키 수정",
|
||||
"more": "더보기",
|
||||
"moreOptions": "추가 옵션",
|
||||
"moreWorkflows": "더 많은 워크플로",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "이름",
|
||||
"newFolder": "새 폴더",
|
||||
"next": "다음",
|
||||
"nextImage": "다음 이미지",
|
||||
"nightly": "NIGHTLY",
|
||||
"no": "아니오",
|
||||
"noAudioRecorded": "녹음된 오디오가 없습니다",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "녹음 재생",
|
||||
"playbackSpeed": "재생 속도",
|
||||
"playing": "재생 중",
|
||||
"pressKeysForNewBinding": "새 바인딩을 위한 키 입력",
|
||||
"preview": "미리보기",
|
||||
"previousImage": "이전 이미지",
|
||||
"profile": "프로필",
|
||||
"progressCountOf": "중",
|
||||
"queued": "대기 중",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "{count} 개의 결과를 찾았습니다",
|
||||
"running": "실행 중",
|
||||
"save": "저장",
|
||||
"saveAnyway": "그래도 저장",
|
||||
"saving": "저장 중",
|
||||
"scrollLeft": "왼쪽으로 스크롤",
|
||||
"scrollRight": "오른쪽으로 스크롤",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "복제할 항목 선택",
|
||||
"selectItemsToRename": "이름을 변경할 항목 선택",
|
||||
"selectedFile": "선택된 파일",
|
||||
"setAKeybindingForTheFollowing": "다음에 대한 단축키를 설정하세요:",
|
||||
"setAsBackground": "배경으로 설정",
|
||||
"settings": "설정",
|
||||
"shortcutSuffix": " ({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "녹음 중지",
|
||||
"submit": "제출",
|
||||
"success": "성공",
|
||||
"switchToGridView": "그리드 보기로 전환",
|
||||
"switchToSingleView": "단일 보기로 전환",
|
||||
"systemInfo": "시스템 정보",
|
||||
"terminal": "터미널",
|
||||
"title": "제목",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "당신"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "캔버스 모드",
|
||||
"canvasToolbar": "캔버스 툴바",
|
||||
"fitView": "보기 맞춤",
|
||||
"focusMode": "집중 모드",
|
||||
"hand": "손",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "이미지를 드래그 앤 드롭하세요",
|
||||
"emptyWorkflowExplanation": "워크플로우가 비어 있습니다. 앱을 만들려면 먼저 노드를 추가해야 합니다.",
|
||||
"enterNodeGraph": "노드 그래프로 진입",
|
||||
"error": {
|
||||
"getHelp": "도움이 필요하다면 복사된 오류와 함께 {0}, {1}, 또는 {2}를 확인하세요.",
|
||||
"github": "GitHub 이슈 제출",
|
||||
"goto": "그래프에서 오류 보기",
|
||||
"guide": "문제 해결 가이드",
|
||||
"header": "앱에서 오류가 발생했습니다",
|
||||
"log": "오류 로그",
|
||||
"mobileFixable": "{0}에서 오류를 확인하세요",
|
||||
"promptShow": "오류 보고서 보기",
|
||||
"promptVisitGraph": "전체 오류를 보려면 노드 그래프를 확인하세요.",
|
||||
"requiresGraph": "생성 중 문제가 발생했습니다. 잘못된 숨겨진 입력, 누락된 리소스 또는 워크플로우 구성 문제일 수 있습니다.",
|
||||
"support": "고객 지원 문의"
|
||||
},
|
||||
"giveFeedback": "피드백 보내기",
|
||||
"graphMode": "그래프 모드",
|
||||
"hasCreditCost": "추가 크레딧 필요",
|
||||
"linearMode": "앱 모드",
|
||||
"loadTemplate": "템플릿 불러오기",
|
||||
"mobileControls": "편집 및 실행",
|
||||
"mobileNoWorkflow": "이 워크플로우는 앱 모드에 맞게 제작되지 않았습니다. 다른 워크플로우를 시도해 보세요.",
|
||||
"queue": {
|
||||
"clear": "대기열 비우기",
|
||||
"clickToClear": "클릭하여 대기열 비우기"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "다시 실행",
|
||||
"reuseParameters": "파라미터 재사용",
|
||||
"runCount": "실행 횟수:",
|
||||
"viewGraph": "노드 그래프 보기",
|
||||
"viewJob": "작업 보기",
|
||||
"welcome": {
|
||||
"buildApp": "앱 만들기",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "설치됨",
|
||||
"missingNodes": "누락된 노드",
|
||||
"notInstalled": "미설치",
|
||||
"unresolvedNodes": "해결되지 않은 노드",
|
||||
"updatesAvailable": "업데이트 가능"
|
||||
},
|
||||
"nightlyVersion": "최신 테스트 버전(nightly)",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "제거",
|
||||
"uninstallSelected": "선택 항목 제거",
|
||||
"uninstalling": "제거 중",
|
||||
"unresolvedNodes": {
|
||||
"message": "다음 노드가 설치되어 있지 않으며 레지스트리에서 찾을 수 없습니다.",
|
||||
"title": "해결되지 않은 누락된 노드"
|
||||
},
|
||||
"update": "업데이트",
|
||||
"updateAll": "모두 업데이트",
|
||||
"updateSelected": "선택 항목 업데이트",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "Rodin",
|
||||
"Runway": "Runway",
|
||||
"Sora": "Sora",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "입력 없음",
|
||||
"inputsNoneTooltip": "노드에 입력이 없습니다",
|
||||
"locateNode": "캔버스에서 노드 찾기",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "이 모델은 이미 \"{category}\"에 존재합니다",
|
||||
"assetLoadTimeout": "모델 감지 시간이 초과되었습니다. 워크플로우를 다시 불러와 보세요.",
|
||||
"cancelSelection": "선택 취소",
|
||||
"clearUrl": "URL 지우기",
|
||||
"collapseNodes": "참조 노드 숨기기",
|
||||
"confirmSelection": "선택 확인",
|
||||
"copyModelName": "모델 이름 복사",
|
||||
"customNodeDownloadDisabled": "클라우드 환경에서는 이 섹션의 커스텀 노드 모델 가져오기를 지원하지 않습니다. 표준 로더 노드를 사용하거나 아래 라이브러리의 모델로 대체해 주세요.",
|
||||
"expandNodes": "참조 노드 보기",
|
||||
"import": "가져오기",
|
||||
"importAnyway": "그래도 가져오기",
|
||||
"importFailed": "가져오기 실패",
|
||||
"importNotSupported": "가져오기 지원되지 않음",
|
||||
"imported": "가져오기 완료",
|
||||
"importing": "가져오는 중...",
|
||||
"locateNode": "캔버스에서 노드 위치 찾기",
|
||||
"metadataFetchFailed": "메타데이터를 가져오지 못했습니다. 링크를 확인하고 다시 시도하세요.",
|
||||
"missingModelsTitle": "누락된 모델",
|
||||
"or": "또는",
|
||||
"typeMismatch": "이 모델은 \"{detectedType}\"로 보입니다. 계속하시겠습니까?",
|
||||
"unknownCategory": "알 수 없음",
|
||||
"unsupportedUrl": "Civitai와 Hugging Face URL만 지원됩니다.",
|
||||
"urlPlaceholder": "모델 URL 붙여넣기 (Civitai 또는 Hugging Face)",
|
||||
"useFromLibrary": "라이브러리에서 사용",
|
||||
"usingFromLibrary": "라이브러리에서 사용 중"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "변경 사항 적용",
|
||||
"cloudMessage": "이 워크플로우에는 Comfy Cloud에서 아직 사용할 수 없는 커스텀 노드가 필요합니다.",
|
||||
@@ -3238,6 +3298,7 @@
|
||||
"interrupted": "실행이 중단되었습니다",
|
||||
"legacyMaskEditorDeprecated": "레거시 마스크 에디터는 더 이상 지원되지 않으며 곧 제거될 예정입니다.",
|
||||
"migrateToLitegraphReroute": "향후 버전에서는 Reroute 노드가 제거됩니다. LiteGraph 에서 자체 제공하는 경유점으로 변환하려면 클릭하세요.",
|
||||
"missingModelVerificationFailed": "누락된 모델 검증에 실패했습니다. 일부 모델이 오류 탭에 표시되지 않을 수 있습니다.",
|
||||
"modelLoadedSuccessfully": "3D 모델이 성공적으로 로드됨",
|
||||
"no3dScene": "텍스처를 적용할 3D 장면이 없습니다",
|
||||
"no3dSceneToExport": "내보낼 3D 장면이 없습니다",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "Flux 계열 모델에서 참조 이미지에 대한 KV 캐시 최적화를 활성화합니다.",
|
||||
"display_name": "Flux KV 캐시",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "모델",
|
||||
"tooltip": "KV 캐시를 사용할 모델입니다."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "KV 캐시가 활성화된 패치된 모델입니다."
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "이 노드는 이미지를 Flux Kontext에 더 최적화된 크기로 조정합니다.",
|
||||
"display_name": "Flux Kontext 이미지 스케일",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "Reve를 사용하여 텍스트 설명으로부터 이미지를 생성합니다.",
|
||||
"display_name": "Reve 이미지 생성",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "생성 후 제어"
|
||||
},
|
||||
"model": {
|
||||
"name": "모델",
|
||||
"tooltip": "생성에 사용할 모델 버전입니다."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "종횡비"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "테스트 타임 스케일링"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "프롬프트",
|
||||
"tooltip": "원하는 이미지에 대한 텍스트 설명입니다. 최대 2560자까지 입력할 수 있습니다."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "배경 제거",
|
||||
"tooltip": "생성된 이미지에서 배경을 제거합니다. 추가 비용이 발생할 수 있습니다."
|
||||
},
|
||||
"seed": {
|
||||
"name": "시드",
|
||||
"tooltip": "시드는 노드가 다시 실행될지 여부를 제어합니다. 시드와 관계없이 결과는 비결정적입니다."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "업스케일",
|
||||
"tooltip": "생성된 이미지를 업스케일합니다. 추가 비용이 발생할 수 있습니다."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "Reve를 사용하여 자연어 지시로 이미지를 편집합니다.",
|
||||
"display_name": "Reve 이미지 편집",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "생성 후 제어"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "편집 지시",
|
||||
"tooltip": "이미지를 어떻게 편집할지에 대한 텍스트 설명입니다. 최대 2560자까지 입력할 수 있습니다."
|
||||
},
|
||||
"image": {
|
||||
"name": "이미지",
|
||||
"tooltip": "편집할 이미지입니다."
|
||||
},
|
||||
"model": {
|
||||
"name": "모델",
|
||||
"tooltip": "편집에 사용할 모델 버전입니다."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "종횡비"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "테스트 타임 스케일링"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "배경 제거",
|
||||
"tooltip": "생성된 이미지에서 배경을 제거합니다. 추가 비용이 발생할 수 있습니다."
|
||||
},
|
||||
"seed": {
|
||||
"name": "시드",
|
||||
"tooltip": "시드는 노드가 다시 실행될지 여부를 제어합니다. 시드와 관계없이 결과는 비결정적입니다."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "업스케일",
|
||||
"tooltip": "생성된 이미지를 업스케일합니다. 추가 비용이 발생할 수 있습니다."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "참고 이미지를 텍스트 프롬프트와 결합하여 Reve로 새로운 이미지를 만듭니다.",
|
||||
"display_name": "Reve 이미지 리믹스",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "생성 후 제어"
|
||||
},
|
||||
"model": {
|
||||
"name": "모델",
|
||||
"tooltip": "리믹스에 사용할 모델 버전입니다."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "종횡비"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "테스트 타임 스케일링"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "프롬프트",
|
||||
"tooltip": "원하는 이미지에 대한 텍스트 설명입니다. 특정 이미지를 인덱스로 참조하려면 XML img 태그를 사용할 수 있습니다. 예: <img>0</img>, <img>1</img> 등."
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "참고 이미지"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "배경 제거",
|
||||
"tooltip": "생성된 이미지에서 배경을 제거합니다. 추가 비용이 발생할 수 있습니다."
|
||||
},
|
||||
"seed": {
|
||||
"name": "시드",
|
||||
"tooltip": "시드는 노드가 다시 실행될지 여부를 제어합니다. 시드와 관계없이 결과는 비결정적입니다."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "업스케일",
|
||||
"tooltip": "생성된 이미지를 업스케일합니다. 추가 비용이 발생할 수 있습니다."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "Rodin API를 사용하여 3D 에셋 생성",
|
||||
"display_name": "Rodin 3D 생성 - 디테일 생성",
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "Renomear em lote",
|
||||
"beta": "BETA",
|
||||
"bookmark": "Salvar na biblioteca",
|
||||
"browserReservedKeybinding": "Este atalho é reservado por alguns navegadores e pode ter resultados inesperados.",
|
||||
"browserReservedKeybindingTooltip": "Este atalho entra em conflito com atalhos reservados do navegador",
|
||||
"calculatingDimensions": "Calculando dimensões",
|
||||
"cancel": "Cancelar",
|
||||
"cancelled": "Cancelado",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "Copiar",
|
||||
"copyAll": "Copiar tudo",
|
||||
"copyJobId": "Copiar ID da tarefa",
|
||||
"copySystemInfo": "Copiar informações do sistema",
|
||||
"copyToClipboard": "Copiar para a área de transferência",
|
||||
"copyURL": "Copiar URL",
|
||||
"core": "Núcleo",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "Digite o novo nome",
|
||||
"enterNewNamePrompt": "Digite o novo nome:",
|
||||
"enterSubgraph": "Entrar no Subgrafo",
|
||||
"enterYourKeybind": "Digite seu atalho",
|
||||
"error": "Erro",
|
||||
"errorLoadingImage": "Erro ao carregar imagem",
|
||||
"errorLoadingVideo": "Erro ao carregar vídeo",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "Permissão do microfone negada",
|
||||
"migrate": "Migrar",
|
||||
"missing": "Faltando",
|
||||
"modifyKeybinding": "Modificar atalho",
|
||||
"more": "Mais",
|
||||
"moreOptions": "Mais opções",
|
||||
"moreWorkflows": "Mais fluxos de trabalho",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "Nome",
|
||||
"newFolder": "Nova pasta",
|
||||
"next": "Próximo",
|
||||
"nextImage": "Próxima imagem",
|
||||
"nightly": "NIGHTLY",
|
||||
"no": "Não",
|
||||
"noAudioRecorded": "Nenhum áudio gravado",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "Reproduzir gravação",
|
||||
"playbackSpeed": "Velocidade de reprodução",
|
||||
"playing": "Reproduzindo",
|
||||
"pressKeysForNewBinding": "Pressione as teclas para novo atalho",
|
||||
"preview": "PRÉVIA",
|
||||
"previousImage": "Imagem anterior",
|
||||
"profile": "Perfil",
|
||||
"progressCountOf": "de",
|
||||
"queued": "Na fila",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "{count} resultados encontrados",
|
||||
"running": "Executando",
|
||||
"save": "Salvar",
|
||||
"saveAnyway": "Salvar mesmo assim",
|
||||
"saving": "Salvando",
|
||||
"scrollLeft": "Rolar para a esquerda",
|
||||
"scrollRight": "Rolar para a direita",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "Selecione itens para duplicar",
|
||||
"selectItemsToRename": "Selecione itens para renomear",
|
||||
"selectedFile": "Arquivo selecionado",
|
||||
"setAKeybindingForTheFollowing": "Definir um atalho para o seguinte:",
|
||||
"setAsBackground": "Definir como plano de fundo",
|
||||
"settings": "Configurações",
|
||||
"shortcutSuffix": " ({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "Parar gravação",
|
||||
"submit": "Enviar",
|
||||
"success": "Sucesso",
|
||||
"switchToGridView": "Alternar para visualização em grade",
|
||||
"switchToSingleView": "Alternar para visualização única",
|
||||
"systemInfo": "Informações do sistema",
|
||||
"terminal": "Terminal",
|
||||
"title": "Título",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "Você"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "Modo de tela",
|
||||
"canvasToolbar": "Barra de ferramentas da tela",
|
||||
"fitView": "Ajustar à Tela",
|
||||
"focusMode": "Modo de Foco",
|
||||
"hand": "Mão",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "Arraste e solte uma imagem",
|
||||
"emptyWorkflowExplanation": "Seu fluxo de trabalho está vazio. Você precisa adicionar alguns nós primeiro para começar a construir um app.",
|
||||
"enterNodeGraph": "Entrar no grafo de nós",
|
||||
"error": {
|
||||
"getHelp": "Para obter ajuda, veja nosso {0}, {1} ou {2} com o erro copiado.",
|
||||
"github": "enviar um problema no GitHub",
|
||||
"goto": "Mostrar erros no grafo",
|
||||
"guide": "guia de solução de problemas",
|
||||
"header": "Este aplicativo encontrou um erro",
|
||||
"log": "Logs de erro",
|
||||
"mobileFixable": "Verifique {0} para erros",
|
||||
"promptShow": "Mostrar relatório de erro",
|
||||
"promptVisitGraph": "Veja o grafo de nós para ver o erro completo.",
|
||||
"requiresGraph": "Algo deu errado durante a geração. Isso pode ser devido a entradas ocultas inválidas, recursos ausentes ou problemas de configuração do fluxo de trabalho.",
|
||||
"support": "contate nosso suporte"
|
||||
},
|
||||
"giveFeedback": "Enviar feedback",
|
||||
"graphMode": "Modo Gráfico",
|
||||
"hasCreditCost": "Requer créditos adicionais",
|
||||
"linearMode": "Modo App",
|
||||
"loadTemplate": "Carregar um modelo",
|
||||
"mobileControls": "Editar e Executar",
|
||||
"mobileNoWorkflow": "Este fluxo de trabalho não foi criado para o modo aplicativo. Tente outro.",
|
||||
"queue": {
|
||||
"clear": "Limpar fila",
|
||||
"clickToClear": "Clique para limpar a fila"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "Executar novamente",
|
||||
"reuseParameters": "Reutilizar parâmetros",
|
||||
"runCount": "Número de execuções:",
|
||||
"viewGraph": "Ver grafo de nós",
|
||||
"viewJob": "Ver tarefa",
|
||||
"welcome": {
|
||||
"buildApp": "Criar aplicativo",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "INSTALADO",
|
||||
"missingNodes": "Nós Ausentes",
|
||||
"notInstalled": "Não Instalado",
|
||||
"unresolvedNodes": "Nós não resolvidos",
|
||||
"updatesAvailable": "Atualizações Disponíveis"
|
||||
},
|
||||
"nightlyVersion": "Noturna",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "Desinstalar",
|
||||
"uninstallSelected": "Desinstalar Selecionados",
|
||||
"uninstalling": "Desinstalando {id}",
|
||||
"unresolvedNodes": {
|
||||
"message": "Os seguintes nós não estão instalados e não foram encontrados no registro.",
|
||||
"title": "Nós ausentes não resolvidos"
|
||||
},
|
||||
"update": "Atualizar",
|
||||
"updateAll": "Atualizar Todos",
|
||||
"updateSelected": "Atualizar Selecionados",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "Rodin",
|
||||
"Runway": "Runway",
|
||||
"Sora": "Sora",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "SEM ENTRADAS",
|
||||
"inputsNoneTooltip": "O nó não possui entradas",
|
||||
"locateNode": "Localizar nó no canvas",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "Este modelo já existe em \"{category}\"",
|
||||
"assetLoadTimeout": "Tempo esgotado na detecção do modelo. Tente recarregar o fluxo de trabalho.",
|
||||
"cancelSelection": "Cancelar seleção",
|
||||
"clearUrl": "Limpar URL",
|
||||
"collapseNodes": "Ocultar nós de referência",
|
||||
"confirmSelection": "Confirmar seleção",
|
||||
"copyModelName": "Copiar nome do modelo",
|
||||
"customNodeDownloadDisabled": "O ambiente em nuvem não suporta importação de modelos para nós personalizados nesta seção. Utilize nós de carregamento padrão ou substitua por um modelo da biblioteca abaixo.",
|
||||
"expandNodes": "Mostrar nós de referência",
|
||||
"import": "Importar",
|
||||
"importAnyway": "Importar mesmo assim",
|
||||
"importFailed": "Falha na importação",
|
||||
"importNotSupported": "Importação não suportada",
|
||||
"imported": "Importado",
|
||||
"importing": "Importando...",
|
||||
"locateNode": "Localizar nó no canvas",
|
||||
"metadataFetchFailed": "Falha ao obter metadados. Verifique o link e tente novamente.",
|
||||
"missingModelsTitle": "Modelos Ausentes",
|
||||
"or": "OU",
|
||||
"typeMismatch": "Este modelo parece ser um \"{detectedType}\". Tem certeza?",
|
||||
"unknownCategory": "Desconhecido",
|
||||
"unsupportedUrl": "Apenas URLs do Civitai e Hugging Face são suportadas.",
|
||||
"urlPlaceholder": "Cole a URL do modelo (Civitai ou Hugging Face)",
|
||||
"useFromLibrary": "Usar da Biblioteca",
|
||||
"usingFromLibrary": "Usando da Biblioteca"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "Aplicar alterações",
|
||||
"cloudMessage": "Este fluxo de trabalho requer nós personalizados que ainda não estão disponíveis no Comfy Cloud.",
|
||||
@@ -3250,6 +3310,7 @@
|
||||
"interrupted": "Execução interrompida",
|
||||
"legacyMaskEditorDeprecated": "O editor de máscara legado está obsoleto e será removido em breve.",
|
||||
"migrateToLitegraphReroute": "Nós de redirecionamento serão removidos em versões futuras. Clique para migrar para o redirecionamento nativo do litegraph.",
|
||||
"missingModelVerificationFailed": "Falha ao verificar modelos ausentes. Alguns modelos podem não aparecer na aba de Erros.",
|
||||
"modelLoadedSuccessfully": "Modelo 3D carregado com sucesso",
|
||||
"no3dScene": "Nenhuma cena 3D para aplicar textura",
|
||||
"no3dSceneToExport": "Nenhuma cena 3D para exportar",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "Ativa a otimização KV Cache para imagens de referência em modelos da família Flux.",
|
||||
"display_name": "Flux KV Cache",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "modelo",
|
||||
"tooltip": "O modelo para aplicar o KV Cache."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "O modelo modificado com KV Cache ativado."
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "Este nó redimensiona a imagem para um tamanho mais ideal para flux kontext.",
|
||||
"display_name": "FluxKontextImageScale",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "Gere imagens a partir de descrições de texto usando o Reve.",
|
||||
"display_name": "Reve Image Create",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Versão do modelo a ser usada para geração."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "Descrição em texto da imagem desejada. Máximo de 2560 caracteres."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Remover o fundo da imagem gerada. Pode gerar custo adicional."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "O seed controla se o nó deve ser executado novamente; os resultados são não determinísticos independentemente do seed."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Aumentar a resolução da imagem gerada. Pode gerar custo adicional."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "Edite imagens usando instruções em linguagem natural com o Reve.",
|
||||
"display_name": "Reve Image Edit",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "edit_instruction",
|
||||
"tooltip": "Descrição em texto de como editar a imagem. Máximo de 2560 caracteres."
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"tooltip": "A imagem a ser editada."
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Versão do modelo a ser usada para edição."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Remover o fundo da imagem gerada. Pode gerar custo adicional."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "O seed controla se o nó deve ser executado novamente; os resultados são não determinísticos independentemente do seed."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Aumentar a resolução da imagem gerada. Pode gerar custo adicional."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "Combine imagens de referência com prompts de texto para criar novas imagens usando o Reve.",
|
||||
"display_name": "Reve Image Remix",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Versão do modelo a ser usada para remix."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "Descrição em texto da imagem desejada. Pode incluir tags XML img para referenciar imagens específicas pelo índice, por exemplo, <img>0</img>, <img>1</img>, etc."
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "reference_images"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Remover o fundo da imagem gerada. Pode gerar custo adicional."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "O seed controla se o nó deve ser executado novamente; os resultados são não determinísticos independentemente do seed."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Aumentar a resolução da imagem gerada. Pode gerar custo adicional."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "Gerar ativos 3D usando a API Rodin",
|
||||
"display_name": "Rodin 3D Gerar - Geração Detalhada",
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "Пакетное переименование",
|
||||
"beta": "БЕТА",
|
||||
"bookmark": "Сохранить в библиотеку",
|
||||
"browserReservedKeybinding": "Это сочетание клавиш зарезервировано некоторыми браузерами и может привести к неожиданным результатам.",
|
||||
"browserReservedKeybindingTooltip": "Это сочетание конфликтует с зарезервированными сочетаниями браузера",
|
||||
"calculatingDimensions": "Расчёт размеров",
|
||||
"cancel": "Отмена",
|
||||
"cancelled": "Отменено",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "Копировать",
|
||||
"copyAll": "Скопировать всё",
|
||||
"copyJobId": "Копировать ID задания",
|
||||
"copySystemInfo": "Скопировать информацию о системе",
|
||||
"copyToClipboard": "Скопировать в буфер обмена",
|
||||
"copyURL": "Скопировать URL",
|
||||
"core": "Ядро",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "Введите новое имя",
|
||||
"enterNewNamePrompt": "Введите новое имя:",
|
||||
"enterSubgraph": "Войти в подграф",
|
||||
"enterYourKeybind": "Введите ваше сочетание клавиш",
|
||||
"error": "Ошибка",
|
||||
"errorLoadingImage": "Ошибка загрузки изображения",
|
||||
"errorLoadingVideo": "Ошибка загрузки видео",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "Доступ к микрофону запрещён",
|
||||
"migrate": "Мигрировать",
|
||||
"missing": "Отсутствует",
|
||||
"modifyKeybinding": "Изменить сочетание клавиш",
|
||||
"more": "Больше",
|
||||
"moreOptions": "Больше опций",
|
||||
"moreWorkflows": "Больше рабочих процессов",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "Имя",
|
||||
"newFolder": "Новая папка",
|
||||
"next": "Далее",
|
||||
"nextImage": "Следующее изображение",
|
||||
"nightly": "NIGHTLY",
|
||||
"no": "Нет",
|
||||
"noAudioRecorded": "Аудио не записано",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "Воспроизвести запись",
|
||||
"playbackSpeed": "Скорость воспроизведения",
|
||||
"playing": "Воспроизводится",
|
||||
"pressKeysForNewBinding": "Нажмите клавиши для новой привязки",
|
||||
"preview": "ПРЕДПРОСМОТР",
|
||||
"previousImage": "Предыдущее изображение",
|
||||
"profile": "Профиль",
|
||||
"progressCountOf": "из",
|
||||
"queued": "В очереди",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "Найдено {count} результатов",
|
||||
"running": "Выполняется",
|
||||
"save": "Сохранить",
|
||||
"saveAnyway": "Сохранить в любом случае",
|
||||
"saving": "Сохранение",
|
||||
"scrollLeft": "Прокрутить влево",
|
||||
"scrollRight": "Прокрутить вправо",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "Выберите элементы для дублирования",
|
||||
"selectItemsToRename": "Выберите элементы для переименования",
|
||||
"selectedFile": "Выбранный файл",
|
||||
"setAKeybindingForTheFollowing": "Установите сочетание клавиш для следующего:",
|
||||
"setAsBackground": "Установить как фон",
|
||||
"settings": "Настройки",
|
||||
"shortcutSuffix": " ({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "Остановить запись",
|
||||
"submit": "Отправить",
|
||||
"success": "Успех",
|
||||
"switchToGridView": "Переключиться на вид сетки",
|
||||
"switchToSingleView": "Переключиться на одиночный вид",
|
||||
"systemInfo": "Информация о системе",
|
||||
"terminal": "Терминал",
|
||||
"title": "Заголовок",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "Вы"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "Режим холста",
|
||||
"canvasToolbar": "Панель инструментов холста",
|
||||
"fitView": "Подгонять под выделенные",
|
||||
"focusMode": "Режим фокуса",
|
||||
"hand": "Рука",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "Перетащите изображение",
|
||||
"emptyWorkflowExplanation": "Ваш рабочий процесс пуст. Сначала добавьте несколько узлов, чтобы начать создавать приложение.",
|
||||
"enterNodeGraph": "Войти в граф узлов",
|
||||
"error": {
|
||||
"getHelp": "Для получения помощи ознакомьтесь с нашим {0}, {1} или {2} с копией ошибки.",
|
||||
"github": "отправить issue на GitHub",
|
||||
"goto": "Показать ошибки в графе",
|
||||
"guide": "руководство по устранению неполадок",
|
||||
"header": "В приложении произошла ошибка",
|
||||
"log": "Журналы ошибок",
|
||||
"mobileFixable": "Проверьте {0} на наличие ошибок",
|
||||
"promptShow": "Показать отчёт об ошибке",
|
||||
"promptVisitGraph": "Просмотрите граф узлов, чтобы увидеть полную ошибку.",
|
||||
"requiresGraph": "Что-то пошло не так во время генерации. Возможные причины: неверные скрытые входные данные, отсутствующие ресурсы или ошибки конфигурации рабочего процесса.",
|
||||
"support": "связаться с поддержкой"
|
||||
},
|
||||
"giveFeedback": "Оставить отзыв",
|
||||
"graphMode": "Графовый режим",
|
||||
"hasCreditCost": "Требуются дополнительные кредиты",
|
||||
"linearMode": "Режим приложения",
|
||||
"loadTemplate": "Загрузить шаблон",
|
||||
"mobileControls": "Редактировать и запустить",
|
||||
"mobileNoWorkflow": "Для этого режима приложения рабочий процесс не создан. Попробуйте другой.",
|
||||
"queue": {
|
||||
"clear": "Очистить очередь",
|
||||
"clickToClear": "Нажмите, чтобы очистить очередь"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "Перезапустить",
|
||||
"reuseParameters": "Повторно использовать параметры",
|
||||
"runCount": "Количество запусков:",
|
||||
"viewGraph": "Просмотреть граф узлов",
|
||||
"viewJob": "Просмотреть задачу",
|
||||
"welcome": {
|
||||
"buildApp": "Создать приложение",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "УСТАНОВЛЕНО",
|
||||
"missingNodes": "Отсутствующие узлы",
|
||||
"notInstalled": "Не установлено",
|
||||
"unresolvedNodes": "Неразрешённые узлы",
|
||||
"updatesAvailable": "Доступны обновления"
|
||||
},
|
||||
"nightlyVersion": "Ночная",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "Удалить",
|
||||
"uninstallSelected": "Удалить выбранное",
|
||||
"uninstalling": "Удаление",
|
||||
"unresolvedNodes": {
|
||||
"message": "Следующие узлы не установлены и не найдены в реестре.",
|
||||
"title": "Неразрешённые отсутствующие узлы"
|
||||
},
|
||||
"update": "Обновить",
|
||||
"updateAll": "Обновить всё",
|
||||
"updateSelected": "Обновить выбранное",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "Rodin",
|
||||
"Runway": "Runway",
|
||||
"Sora": "Sora",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "НЕТ ВХОДОВ",
|
||||
"inputsNoneTooltip": "Узел не имеет входов",
|
||||
"locateNode": "Найти узел на холсте",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "Эта модель уже существует в «{category}»",
|
||||
"assetLoadTimeout": "Время ожидания обнаружения модели истекло. Попробуйте перезагрузить рабочий процесс.",
|
||||
"cancelSelection": "Отменить выбор",
|
||||
"clearUrl": "Очистить URL",
|
||||
"collapseNodes": "Скрыть ссылающиеся узлы",
|
||||
"confirmSelection": "Подтвердить выбор",
|
||||
"copyModelName": "Скопировать имя модели",
|
||||
"customNodeDownloadDisabled": "В облачной среде импорт моделей для пользовательских узлов в этом разделе не поддерживается. Пожалуйста, используйте стандартные загрузочные узлы или выберите модель из библиотеки ниже.",
|
||||
"expandNodes": "Показать ссылающиеся узлы",
|
||||
"import": "Импортировать",
|
||||
"importAnyway": "Импортировать в любом случае",
|
||||
"importFailed": "Ошибка импорта",
|
||||
"importNotSupported": "Импорт не поддерживается",
|
||||
"imported": "Импортировано",
|
||||
"importing": "Импортируется...",
|
||||
"locateNode": "Найти узел на холсте",
|
||||
"metadataFetchFailed": "Не удалось получить метаданные. Проверьте ссылку и попробуйте снова.",
|
||||
"missingModelsTitle": "Отсутствующие модели",
|
||||
"or": "ИЛИ",
|
||||
"typeMismatch": "Похоже, эта модель — «{detectedType}». Вы уверены?",
|
||||
"unknownCategory": "Неизвестно",
|
||||
"unsupportedUrl": "Поддерживаются только URL Civitai и Hugging Face.",
|
||||
"urlPlaceholder": "Вставьте URL модели (Civitai или Hugging Face)",
|
||||
"useFromLibrary": "Использовать из библиотеки",
|
||||
"usingFromLibrary": "Используется из библиотеки"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "Применить изменения",
|
||||
"cloudMessage": "Для этого рабочего процесса требуются пользовательские узлы, которые ещё недоступны в Comfy Cloud.",
|
||||
@@ -3238,6 +3298,7 @@
|
||||
"interrupted": "Выполнение было прервано",
|
||||
"legacyMaskEditorDeprecated": "Устаревший редактор масок будет скоро удалён.",
|
||||
"migrateToLitegraphReroute": "Узлы перенаправления будут удалены в будущих версиях. Нажмите, чтобы перейти на litegraph-native reroute.",
|
||||
"missingModelVerificationFailed": "Не удалось проверить отсутствующие модели. Некоторые модели могут не отображаться на вкладке Ошибки.",
|
||||
"modelLoadedSuccessfully": "3D-модель успешно загружена",
|
||||
"no3dScene": "Нет 3D сцены для применения текстуры",
|
||||
"no3dSceneToExport": "Нет 3D сцены для экспорта",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "Включает оптимизацию KV Cache для эталонных изображений в моделях семейства Flux.",
|
||||
"display_name": "Flux KV Cache",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Модель, для которой будет использоваться KV Cache."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "Патченная модель с включённым KV Cache."
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "Этот узел изменяет размер изображения до более оптимального для flux kontext.",
|
||||
"display_name": "FluxKontextImageScale",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "Генерируйте изображения по текстовым описаниям с помощью Reve.",
|
||||
"display_name": "Reve Создание изображения",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Версия модели для генерации."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "Текстовое описание желаемого изображения. Максимум 2560 символов."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Удалить фон с сгенерированного изображения. Может потребоваться дополнительная оплата."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "Seed определяет, должен ли узел запускаться повторно; результаты не являются детерминированными независимо от seed."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Увеличить разрешение сгенерированного изображения. Может потребоваться дополнительная оплата."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "Редактируйте изображения с помощью естественно-языковых инструкций в Reve.",
|
||||
"display_name": "Reve Редактирование изображения",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "edit_instruction",
|
||||
"tooltip": "Текстовое описание того, как редактировать изображение. Максимум 2560 символов."
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"tooltip": "Изображение для редактирования."
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Версия модели для редактирования."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Удалить фон с сгенерированного изображения. Может потребоваться дополнительная оплата."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "Seed определяет, должен ли узел запускаться повторно; результаты не являются детерминированными независимо от seed."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Увеличить разрешение сгенерированного изображения. Может потребоваться дополнительная оплата."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "Комбинируйте референсные изображения с текстовыми подсказками для создания новых изображений с помощью Reve.",
|
||||
"display_name": "Reve Ремикс изображения",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Версия модели для ремикса."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "Текстовое описание желаемого изображения. Можно использовать XML-теги img для ссылки на определённые изображения по индексу, например <img>0</img>, <img>1</img> и т.д."
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "reference_images"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Удалить фон с сгенерированного изображения. Может потребоваться дополнительная оплата."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "Seed определяет, должен ли узел запускаться повторно; результаты не являются детерминированными независимо от seed."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Увеличить разрешение сгенерированного изображения. Может потребоваться дополнительная оплата."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "Создание 3D-объектов с помощью Rodin API",
|
||||
"display_name": "Rodin 3D Generate - Детальная генерация",
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "Toplu Yeniden Adlandır",
|
||||
"beta": "BETA",
|
||||
"bookmark": "Kütüphaneye Kaydet",
|
||||
"browserReservedKeybinding": "Bu kısayol bazı tarayıcılar tarafından ayrılmıştır ve beklenmeyen sonuçlara yol açabilir.",
|
||||
"browserReservedKeybindingTooltip": "Bu kısayol, tarayıcıya ayrılmış kısayollarla çakışıyor",
|
||||
"calculatingDimensions": "Boyutlar hesaplanıyor",
|
||||
"cancel": "İptal",
|
||||
"cancelled": "İptal Edildi",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "Kopyala",
|
||||
"copyAll": "Tümünü Kopyala",
|
||||
"copyJobId": "İş Kimliğini Kopyala",
|
||||
"copySystemInfo": "Sistem Bilgilerini Kopyala",
|
||||
"copyToClipboard": "Panoya Kopyala",
|
||||
"copyURL": "URL'yi Kopyala",
|
||||
"core": "Çekirdek",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "Yeni adı girin",
|
||||
"enterNewNamePrompt": "Yeni adı girin:",
|
||||
"enterSubgraph": "Alt Grafiğe Gir",
|
||||
"enterYourKeybind": "Kısayol tuşunuzu girin",
|
||||
"error": "Hata",
|
||||
"errorLoadingImage": "Görüntü yüklenirken hata",
|
||||
"errorLoadingVideo": "Video yüklenirken hata",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "Mikrofon izni reddedildi",
|
||||
"migrate": "Taşı",
|
||||
"missing": "Eksik",
|
||||
"modifyKeybinding": "Kısayol tuşunu değiştir",
|
||||
"more": "Daha Fazla",
|
||||
"moreOptions": "Daha Fazla Seçenek",
|
||||
"moreWorkflows": "Daha fazla iş akışı",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "Ad",
|
||||
"newFolder": "Yeni Klasör",
|
||||
"next": "İleri",
|
||||
"nextImage": "Sonraki görsel",
|
||||
"nightly": "NIGHTLY",
|
||||
"no": "Hayır",
|
||||
"noAudioRecorded": "Ses kaydedilmedi",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "Kaydı Oynat",
|
||||
"playbackSpeed": "Oynatma Hızı",
|
||||
"playing": "Oynatılıyor",
|
||||
"pressKeysForNewBinding": "Yeni bağlama için tuşlara basın",
|
||||
"preview": "ÖNİZLEME",
|
||||
"previousImage": "Önceki görsel",
|
||||
"profile": "Profil",
|
||||
"progressCountOf": "/",
|
||||
"queued": "Kuyrukta",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "{count} Sonuç Bulundu",
|
||||
"running": "Çalışıyor",
|
||||
"save": "Kaydet",
|
||||
"saveAnyway": "Yine de Kaydet",
|
||||
"saving": "Kaydediliyor",
|
||||
"scrollLeft": "Sola Kaydır",
|
||||
"scrollRight": "Sağa Kaydır",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "Çoğaltılacak öğeleri seçin",
|
||||
"selectItemsToRename": "Yeniden adlandırılacak öğeleri seçin",
|
||||
"selectedFile": "Seçilen dosya",
|
||||
"setAKeybindingForTheFollowing": "Aşağıdakiler için bir kısayol tuşu ayarla:",
|
||||
"setAsBackground": "Arka Plan Olarak Ayarla",
|
||||
"settings": "Ayarlar",
|
||||
"shortcutSuffix": " ({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "Kaydı Durdur",
|
||||
"submit": "Gönder",
|
||||
"success": "Başarılı",
|
||||
"switchToGridView": "Izgara görünümüne geç",
|
||||
"switchToSingleView": "Tekli görünüme geç",
|
||||
"systemInfo": "Sistem Bilgisi",
|
||||
"terminal": "Terminal",
|
||||
"title": "Başlık",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "Sen"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "Tuval Modu",
|
||||
"canvasToolbar": "Tuval Araç Çubuğu",
|
||||
"fitView": "Görünüme Sığdır",
|
||||
"focusMode": "Odak Modu",
|
||||
"hand": "El",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "Bir görseli sürükleyip bırakın",
|
||||
"emptyWorkflowExplanation": "Çalışma akışınız boş. Bir uygulama oluşturmaya başlamak için önce bazı düğümler eklemelisiniz.",
|
||||
"enterNodeGraph": "Düğüm grafiğine gir",
|
||||
"error": {
|
||||
"getHelp": "Yardım için, kopyalanan hatayla birlikte {0}, {1} veya {2} adresini ziyaret edin.",
|
||||
"github": "GitHub sorunu gönder",
|
||||
"goto": "Grafikte hataları göster",
|
||||
"guide": "sorun giderme rehberi",
|
||||
"header": "Uygulamada bir hata oluştu",
|
||||
"log": "Hata Kayıtları",
|
||||
"mobileFixable": "Hatalar için {0} kontrol edin",
|
||||
"promptShow": "Hata raporunu göster",
|
||||
"promptVisitGraph": "Tüm hatayı görmek için düğüm grafiğini görüntüleyin.",
|
||||
"requiresGraph": "Oluşturma sırasında bir şeyler ters gitti. Bu, geçersiz gizli girdiler, eksik kaynaklar veya iş akışı yapılandırma sorunlarından kaynaklanıyor olabilir.",
|
||||
"support": "destek ekibimizle iletişime geçin"
|
||||
},
|
||||
"giveFeedback": "Geri bildirim ver",
|
||||
"graphMode": "Grafik Modu",
|
||||
"hasCreditCost": "Ekstra kredi gerektirir",
|
||||
"linearMode": "Uygulama Modu",
|
||||
"loadTemplate": "Şablon yükle",
|
||||
"mobileControls": "Düzenle ve Çalıştır",
|
||||
"mobileNoWorkflow": "Bu iş akışı uygulama modu için oluşturulmamış. Farklı bir tane deneyin.",
|
||||
"queue": {
|
||||
"clear": "Kuyruğu temizle",
|
||||
"clickToClear": "Kuyruğu temizlemek için tıklayın"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "Tekrar Çalıştır",
|
||||
"reuseParameters": "Parametreleri Yeniden Kullan",
|
||||
"runCount": "Çalıştırma sayısı:",
|
||||
"viewGraph": "Düğüm grafiğini görüntüle",
|
||||
"viewJob": "İşi Görüntüle",
|
||||
"welcome": {
|
||||
"buildApp": "Uygulama oluştur",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "YÜKLÜ",
|
||||
"missingNodes": "Eksik Düğümler",
|
||||
"notInstalled": "Yüklü Değil",
|
||||
"unresolvedNodes": "Çözülemeyen Düğümler",
|
||||
"updatesAvailable": "Güncellemeler Mevcut"
|
||||
},
|
||||
"nightlyVersion": "Gecelik",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "Kaldır",
|
||||
"uninstallSelected": "Seçilenleri Kaldır",
|
||||
"uninstalling": "{id} kaldırılıyor",
|
||||
"unresolvedNodes": {
|
||||
"message": "Aşağıdaki düğümler yüklü değil ve kayıt defterinde bulunamadı.",
|
||||
"title": "Çözülemeyen Eksik Düğümler"
|
||||
},
|
||||
"update": "Güncelle",
|
||||
"updateAll": "Tümünü Güncelle",
|
||||
"updateSelected": "Seçilenleri Güncelle",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "Rodin",
|
||||
"Runway": "Runway",
|
||||
"Sora": "Sora",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "GİRİŞ YOK",
|
||||
"inputsNoneTooltip": "Düğümün girişi yok",
|
||||
"locateNode": "Düğümü tuvalde bul",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "Bu model zaten \"{category}\" içinde mevcut",
|
||||
"assetLoadTimeout": "Model algılama zaman aşımına uğradı. Lütfen iş akışını yeniden yüklemeyi deneyin.",
|
||||
"cancelSelection": "Seçimi iptal et",
|
||||
"clearUrl": "URL'yi temizle",
|
||||
"collapseNodes": "Referans veren node'ları gizle",
|
||||
"confirmSelection": "Seçimi onayla",
|
||||
"copyModelName": "Model adını kopyala",
|
||||
"customNodeDownloadDisabled": "Bulut ortamı, bu bölümde özel node'lar için model içe aktarmayı desteklemiyor. Lütfen standart yükleyici node'larını kullanın veya aşağıdaki kütüphaneden bir model ile değiştirin.",
|
||||
"expandNodes": "Referans veren node'ları göster",
|
||||
"import": "İçe aktar",
|
||||
"importAnyway": "Yine de içe aktar",
|
||||
"importFailed": "İçe aktarılamadı",
|
||||
"importNotSupported": "İçe Aktarma Desteklenmiyor",
|
||||
"imported": "İçe aktarıldı",
|
||||
"importing": "İçe aktarılıyor...",
|
||||
"locateNode": "Node'u tuvalde bul",
|
||||
"metadataFetchFailed": "Meta veriler alınamadı. Lütfen bağlantıyı kontrol edip tekrar deneyin.",
|
||||
"missingModelsTitle": "Eksik Modeller",
|
||||
"or": "VEYA",
|
||||
"typeMismatch": "Bu model \"{detectedType}\" gibi görünüyor. Emin misiniz?",
|
||||
"unknownCategory": "Bilinmeyen",
|
||||
"unsupportedUrl": "Sadece Civitai ve Hugging Face URL'leri desteklenmektedir.",
|
||||
"urlPlaceholder": "Model URL'sini yapıştırın (Civitai veya Hugging Face)",
|
||||
"useFromLibrary": "Kütüphaneden kullan",
|
||||
"usingFromLibrary": "Kütüphaneden kullanılıyor"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "Değişiklikleri Uygula",
|
||||
"cloudMessage": "Bu iş akışı, Comfy Cloud'da henüz mevcut olmayan özel düğümler gerektiriyor.",
|
||||
@@ -3238,6 +3298,7 @@
|
||||
"interrupted": "Yürütme kesintiye uğradı",
|
||||
"legacyMaskEditorDeprecated": "Klasik maske düzenleyici kullanımdan kaldırıldı ve yakında kaldırılacak.",
|
||||
"migrateToLitegraphReroute": "Yeniden yönlendirme düğümleri gelecekteki sürümlerde kaldırılacaktır. Litegraph yerel yeniden yönlendirmeye geçmek için tıklayın.",
|
||||
"missingModelVerificationFailed": "Eksik modeller doğrulanamadı. Bazı modeller Hatalar sekmesinde gösterilmeyebilir.",
|
||||
"modelLoadedSuccessfully": "3B model başarıyla yüklendi",
|
||||
"no3dScene": "Doku uygulanacak 3D sahne yok",
|
||||
"no3dSceneToExport": "Dışa aktarılacak 3D sahne yok",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "Flux ailesi modellerinde referans görselleri için KV Cache optimizasyonunu etkinleştirir.",
|
||||
"display_name": "Flux KV Cache",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "KV Cache uygulanacak model."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "KV Cache etkinleştirilmiş yamalı model."
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "Bu düğüm, görüntüyü flux kontext için daha uygun bir boyuta yeniden boyutlandırır.",
|
||||
"display_name": "FluxKontext Görüntü Ölçeği",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "Reve kullanarak metin açıklamalarından görseller oluşturun.",
|
||||
"display_name": "Reve Görsel Oluştur",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "oluşturduktan sonra kontrol et"
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Oluşturma için kullanılacak model sürümü."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "en_boy_oranı"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_zamanı_ölçekleme"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "İstenen görselin metin açıklaması. En fazla 2560 karakter."
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Oluşturulan görselin arka planını kaldır. Ek maliyet oluşturabilir."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "Seed, düğümün tekrar çalıştırılıp çalıştırılmayacağını kontrol eder; seed ne olursa olsun sonuçlar deterministik değildir."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Oluşturulan görseli büyüt. Ek maliyet oluşturabilir."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "Reve ile doğal dil talimatları kullanarak görselleri düzenleyin.",
|
||||
"display_name": "Reve Görsel Düzenle",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "oluşturduktan sonra kontrol et"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "düzenleme_talimatı",
|
||||
"tooltip": "Görselin nasıl düzenleneceğine dair metin açıklaması. En fazla 2560 karakter."
|
||||
},
|
||||
"image": {
|
||||
"name": "görsel",
|
||||
"tooltip": "Düzenlenecek görsel."
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Düzenleme için kullanılacak model sürümü."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "en_boy_oranı"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_zamanı_ölçekleme"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Oluşturulan görselin arka planını kaldır. Ek maliyet oluşturabilir."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "Seed, düğümün tekrar çalıştırılıp çalıştırılmayacağını kontrol eder; seed ne olursa olsun sonuçlar deterministik değildir."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Oluşturulan görseli büyüt. Ek maliyet oluşturabilir."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "Referans görselleri ve metin istemlerini birleştirerek Reve ile yeni görseller oluşturun.",
|
||||
"display_name": "Reve Görsel Remix",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "oluşturduktan sonra kontrol et"
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "Remix için kullanılacak model sürümü."
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "en_boy_oranı"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_zamanı_ölçekleme"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "İstenen görselin metin açıklaması. Belirli görselleri indeks ile referans göstermek için XML img etiketleri ekleyebilirsiniz, örn. <img>0</img>, <img>1</img> vb."
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "referans_görseller"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "Oluşturulan görselin arka planını kaldır. Ek maliyet oluşturabilir."
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "Seed, düğümün tekrar çalıştırılıp çalıştırılmayacağını kontrol eder; seed ne olursa olsun sonuçlar deterministik değildir."
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "Oluşturulan görseli büyüt. Ek maliyet oluşturabilir."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "Rodin API kullanarak 3D Varlıklar Oluştur",
|
||||
"display_name": "Rodin 3D Oluştur - Detay Oluştur",
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "批次重新命名",
|
||||
"beta": "測試版",
|
||||
"bookmark": "儲存至程式庫",
|
||||
"browserReservedKeybinding": "此快捷鍵已被部分瀏覽器保留,可能會產生非預期結果。",
|
||||
"browserReservedKeybindingTooltip": "此快捷鍵與瀏覽器保留的快捷鍵衝突",
|
||||
"calculatingDimensions": "計算尺寸中",
|
||||
"cancel": "取消",
|
||||
"cancelled": "已取消",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "複製",
|
||||
"copyAll": "全部複製",
|
||||
"copyJobId": "複製工作 ID",
|
||||
"copySystemInfo": "複製系統資訊",
|
||||
"copyToClipboard": "複製到剪貼簿",
|
||||
"copyURL": "複製網址",
|
||||
"core": "核心",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "輸入新名稱",
|
||||
"enterNewNamePrompt": "輸入新名稱:",
|
||||
"enterSubgraph": "進入子圖",
|
||||
"enterYourKeybind": "輸入您的快捷鍵",
|
||||
"error": "錯誤",
|
||||
"errorLoadingImage": "載入圖片時發生錯誤",
|
||||
"errorLoadingVideo": "載入影片時發生錯誤",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "麥克風權限被拒絕",
|
||||
"migrate": "遷移",
|
||||
"missing": "缺少",
|
||||
"modifyKeybinding": "修改快捷鍵",
|
||||
"more": "更多",
|
||||
"moreOptions": "更多選項",
|
||||
"moreWorkflows": "更多工作流程",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "名稱",
|
||||
"newFolder": "新資料夾",
|
||||
"next": "下一步",
|
||||
"nextImage": "下一張圖片",
|
||||
"nightly": "NIGHTLY",
|
||||
"no": "否",
|
||||
"noAudioRecorded": "沒有錄製到音訊",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "播放錄製",
|
||||
"playbackSpeed": "播放速度",
|
||||
"playing": "播放中",
|
||||
"pressKeysForNewBinding": "按下按鍵設定新綁定",
|
||||
"preview": "預覽",
|
||||
"previousImage": "上一張圖片",
|
||||
"profile": "個人檔案",
|
||||
"progressCountOf": "共",
|
||||
"queued": "已排隊",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "找到 {count} 筆結果",
|
||||
"running": "執行中",
|
||||
"save": "儲存",
|
||||
"saveAnyway": "仍要儲存",
|
||||
"saving": "儲存中",
|
||||
"scrollLeft": "向左捲動",
|
||||
"scrollRight": "向右捲動",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "請選擇要複製的項目",
|
||||
"selectItemsToRename": "請選擇要重新命名的項目",
|
||||
"selectedFile": "已選取的檔案",
|
||||
"setAKeybindingForTheFollowing": "為以下項目設定快捷鍵:",
|
||||
"setAsBackground": "設為背景",
|
||||
"settings": "設定",
|
||||
"shortcutSuffix": "({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "停止錄音",
|
||||
"submit": "提交",
|
||||
"success": "成功",
|
||||
"switchToGridView": "切換至網格檢視",
|
||||
"switchToSingleView": "切換至單一檢視",
|
||||
"systemInfo": "系統資訊",
|
||||
"terminal": "終端機",
|
||||
"title": "標題",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "你"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "畫布模式",
|
||||
"canvasToolbar": "畫布工具列",
|
||||
"fitView": "適合視窗",
|
||||
"focusMode": "專注模式",
|
||||
"hand": "手形",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "拖曳圖片到此",
|
||||
"emptyWorkflowExplanation": "您的工作流程目前是空的。您需要先新增一些節點,才能開始建立應用程式。",
|
||||
"enterNodeGraph": "進入節點圖",
|
||||
"error": {
|
||||
"getHelp": "如需協助,請參閱我們的 {0}、{1} 或 {2} 並附上複製的錯誤資訊。",
|
||||
"github": "提交 GitHub 問題",
|
||||
"goto": "在圖中顯示錯誤",
|
||||
"guide": "疑難排解指南",
|
||||
"header": "此應用程式發生錯誤",
|
||||
"log": "錯誤日誌",
|
||||
"mobileFixable": "請檢查 {0} 以獲取錯誤資訊",
|
||||
"promptShow": "顯示錯誤報告",
|
||||
"promptVisitGraph": "檢視節點圖以查看完整錯誤。",
|
||||
"requiresGraph": "產生過程中發生錯誤。可能原因包括無效的隱藏輸入、缺少資源或工作流程設定問題。",
|
||||
"support": "聯絡我們的支援"
|
||||
},
|
||||
"giveFeedback": "提供回饋",
|
||||
"graphMode": "圖形模式",
|
||||
"hasCreditCost": "需要額外點數",
|
||||
"linearMode": "App 模式",
|
||||
"loadTemplate": "載入範本",
|
||||
"mobileControls": "編輯與執行",
|
||||
"mobileNoWorkflow": "此工作流程尚未為應用程式模式建立。請嘗試其他工作流程。",
|
||||
"queue": {
|
||||
"clear": "清除佇列",
|
||||
"clickToClear": "點擊以清除佇列"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "重新執行",
|
||||
"reuseParameters": "重用參數",
|
||||
"runCount": "執行次數:",
|
||||
"viewGraph": "檢視節點圖",
|
||||
"viewJob": "檢視任務",
|
||||
"welcome": {
|
||||
"buildApp": "建立應用",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "已安裝",
|
||||
"missingNodes": "缺少節點",
|
||||
"notInstalled": "未安裝",
|
||||
"unresolvedNodes": "未解決的節點",
|
||||
"updatesAvailable": "有可用更新"
|
||||
},
|
||||
"nightlyVersion": "每夜建置版",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "解除安裝",
|
||||
"uninstallSelected": "解除安裝所選項目",
|
||||
"uninstalling": "正在解除安裝",
|
||||
"unresolvedNodes": {
|
||||
"message": "以下節點尚未安裝,且在註冊表中找不到。",
|
||||
"title": "未解決的缺失節點"
|
||||
},
|
||||
"update": "更新",
|
||||
"updateAll": "全部更新",
|
||||
"updateSelected": "更新所選項目",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "羅丹",
|
||||
"Runway": "跑道",
|
||||
"Sora": "蒼穹",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "無輸入",
|
||||
"inputsNoneTooltip": "此節點沒有輸入",
|
||||
"locateNode": "在畫布上定位節點",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "此模型已存在於「{category}」中",
|
||||
"assetLoadTimeout": "模型偵測逾時。請嘗試重新載入工作流程。",
|
||||
"cancelSelection": "取消選擇",
|
||||
"clearUrl": "清除網址",
|
||||
"collapseNodes": "隱藏參照節點",
|
||||
"confirmSelection": "確認選擇",
|
||||
"copyModelName": "複製模型名稱",
|
||||
"customNodeDownloadDisabled": "雲端環境不支援此區段自訂節點的模型匯入。請使用標準載入節點或以下資料庫中的模型替代。",
|
||||
"expandNodes": "顯示參照節點",
|
||||
"import": "匯入",
|
||||
"importAnyway": "仍要匯入",
|
||||
"importFailed": "匯入失敗",
|
||||
"importNotSupported": "不支援匯入",
|
||||
"imported": "已匯入",
|
||||
"importing": "匯入中...",
|
||||
"locateNode": "在畫布上定位節點",
|
||||
"metadataFetchFailed": "取得中繼資料失敗。請檢查連結並重試。",
|
||||
"missingModelsTitle": "缺少的模型",
|
||||
"or": "或",
|
||||
"typeMismatch": "此模型似乎是「{detectedType}」。您確定嗎?",
|
||||
"unknownCategory": "未知",
|
||||
"unsupportedUrl": "僅支援 Civitai 與 Hugging Face 網址。",
|
||||
"urlPlaceholder": "貼上模型網址(Civitai 或 Hugging Face)",
|
||||
"useFromLibrary": "從資料庫使用",
|
||||
"usingFromLibrary": "已從資料庫使用"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "套用變更",
|
||||
"cloudMessage": "此工作流程需要 Comfy Cloud 尚未提供的自訂節點。",
|
||||
@@ -3238,6 +3298,7 @@
|
||||
"interrupted": "執行已被中斷",
|
||||
"legacyMaskEditorDeprecated": "舊版遮罩編輯器即將淘汰並很快移除。",
|
||||
"migrateToLitegraphReroute": "重導節點將於未來版本移除。點擊以遷移至 litegraph 原生重導。",
|
||||
"missingModelVerificationFailed": "驗證缺少的模型失敗。部分模型可能不會顯示在錯誤標籤頁中。",
|
||||
"modelLoadedSuccessfully": "3D 模型載入成功",
|
||||
"no3dScene": "沒有 3D 場景可套用材質",
|
||||
"no3dSceneToExport": "沒有 3D 場景可匯出",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "為 Flux 系列模型的參考圖像啟用 KV 快取最佳化。",
|
||||
"display_name": "Flux KV 快取",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "模型",
|
||||
"tooltip": "要啟用 KV 快取的模型。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "已啟用 KV 快取的修補模型。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "此節點將圖像調整為更適合flux kontext的尺寸。",
|
||||
"display_name": "FluxKontextImageScale",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "使用 Reve 根據文字描述生成圖像。",
|
||||
"display_name": "Reve 圖像生成",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "生成後控制"
|
||||
},
|
||||
"model": {
|
||||
"name": "模型",
|
||||
"tooltip": "用於生成的模型版本。"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "長寬比"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "測試時縮放"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "提示詞",
|
||||
"tooltip": "欲生成圖像的文字描述。最多 2560 字元。"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "去背",
|
||||
"tooltip": "移除生成圖像的背景。可能會產生額外費用。"
|
||||
},
|
||||
"seed": {
|
||||
"name": "種子",
|
||||
"tooltip": "種子控制此節點是否重新執行;無論種子為何,結果皆為非確定性。"
|
||||
},
|
||||
"upscale": {
|
||||
"name": "放大",
|
||||
"tooltip": "對生成的圖像進行放大。可能會產生額外費用。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "使用 Reve 以自然語言指令編輯圖像。",
|
||||
"display_name": "Reve 圖像編輯",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "生成後控制"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "編輯指令",
|
||||
"tooltip": "描述如何編輯圖像的文字說明。最多 2560 字元。"
|
||||
},
|
||||
"image": {
|
||||
"name": "圖像",
|
||||
"tooltip": "要編輯的圖像。"
|
||||
},
|
||||
"model": {
|
||||
"name": "模型",
|
||||
"tooltip": "用於編輯的模型版本。"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "長寬比"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "測試時縮放"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "去背",
|
||||
"tooltip": "移除生成圖像的背景。可能會產生額外費用。"
|
||||
},
|
||||
"seed": {
|
||||
"name": "種子",
|
||||
"tooltip": "種子控制此節點是否重新執行;無論種子為何,結果皆為非確定性。"
|
||||
},
|
||||
"upscale": {
|
||||
"name": "放大",
|
||||
"tooltip": "對生成的圖像進行放大。可能會產生額外費用。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "結合參考圖像與文字提示,使用 Reve 創建新圖像。",
|
||||
"display_name": "Reve 圖像混合",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "生成後控制"
|
||||
},
|
||||
"model": {
|
||||
"name": "模型",
|
||||
"tooltip": "用於混合的模型版本。"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "長寬比"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "測試時縮放"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "提示詞",
|
||||
"tooltip": "欲生成圖像的文字描述。可包含 XML img 標籤以索引特定圖像,例如 <img>0</img>、<img>1</img> 等。"
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "參考圖像"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "去背",
|
||||
"tooltip": "移除生成圖像的背景。可能會產生額外費用。"
|
||||
},
|
||||
"seed": {
|
||||
"name": "種子",
|
||||
"tooltip": "種子控制此節點是否重新執行;無論種子為何,結果皆為非確定性。"
|
||||
},
|
||||
"upscale": {
|
||||
"name": "放大",
|
||||
"tooltip": "對生成的圖像進行放大。可能會產生額外費用。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "使用 Rodin API 生成 3D 資源",
|
||||
"display_name": "Rodin 3D 生成 - 細節生成",
|
||||
|
||||
@@ -936,6 +936,8 @@
|
||||
"batchRename": "批量重命名",
|
||||
"beta": "测试版",
|
||||
"bookmark": "保存到库",
|
||||
"browserReservedKeybinding": "此快捷键被部分浏览器保留,可能导致意外结果。",
|
||||
"browserReservedKeybindingTooltip": "此快捷键与浏览器保留的快捷键冲突",
|
||||
"calculatingDimensions": "正在计算尺寸",
|
||||
"cancel": "取消",
|
||||
"cancelled": "已取消",
|
||||
@@ -970,6 +972,7 @@
|
||||
"copy": "复制",
|
||||
"copyAll": "全部复制",
|
||||
"copyJobId": "复制队列 ID",
|
||||
"copySystemInfo": "复制系统信息",
|
||||
"copyToClipboard": "复制到剪贴板",
|
||||
"copyURL": "复制链接",
|
||||
"core": "核心",
|
||||
@@ -1016,6 +1019,7 @@
|
||||
"enterNewName": "输入新名称",
|
||||
"enterNewNamePrompt": "输入新名称:",
|
||||
"enterSubgraph": "进入子图",
|
||||
"enterYourKeybind": "输入你的快捷键",
|
||||
"error": "错误",
|
||||
"errorLoadingImage": "图片加载出错",
|
||||
"errorLoadingVideo": "视频加载出错",
|
||||
@@ -1085,6 +1089,7 @@
|
||||
"micPermissionDenied": "麦克风权限被拒绝",
|
||||
"migrate": "迁移",
|
||||
"missing": "缺失",
|
||||
"modifyKeybinding": "修改快捷键",
|
||||
"more": "更多",
|
||||
"moreOptions": "更多选项",
|
||||
"moreWorkflows": "更多工作流",
|
||||
@@ -1093,6 +1098,7 @@
|
||||
"name": "名称",
|
||||
"newFolder": "新文件夹",
|
||||
"next": "下一个",
|
||||
"nextImage": "下一张图像",
|
||||
"nightly": "NIGHTLY",
|
||||
"no": "否",
|
||||
"noAudioRecorded": "未录制音频",
|
||||
@@ -1126,8 +1132,8 @@
|
||||
"playRecording": "播放录音",
|
||||
"playbackSpeed": "播放速度",
|
||||
"playing": "播放中",
|
||||
"pressKeysForNewBinding": "按下按键设置新绑定",
|
||||
"preview": "预览",
|
||||
"previousImage": "上一张图像",
|
||||
"profile": "档案",
|
||||
"progressCountOf": "共",
|
||||
"queued": "已执行",
|
||||
@@ -1167,6 +1173,7 @@
|
||||
"resultsCount": "找到 {count} 个结果",
|
||||
"running": "正在运行",
|
||||
"save": "保存",
|
||||
"saveAnyway": "仍然保存",
|
||||
"saving": "正在保存",
|
||||
"scrollLeft": "向左滚动",
|
||||
"scrollRight": "向右滚动",
|
||||
@@ -1185,6 +1192,7 @@
|
||||
"selectItemsToDuplicate": "选择复制",
|
||||
"selectItemsToRename": "选择重命名",
|
||||
"selectedFile": "已选文件",
|
||||
"setAKeybindingForTheFollowing": "为以下操作设置快捷键:",
|
||||
"setAsBackground": "设为背景",
|
||||
"settings": "设置",
|
||||
"shortcutSuffix": "({shortcut})",
|
||||
@@ -1200,6 +1208,8 @@
|
||||
"stopRecording": "停止录音",
|
||||
"submit": "提交",
|
||||
"success": "成功",
|
||||
"switchToGridView": "切换到网格视图",
|
||||
"switchToSingleView": "切换到单图视图",
|
||||
"systemInfo": "系统信息",
|
||||
"terminal": "终端",
|
||||
"title": "标题",
|
||||
@@ -1229,6 +1239,8 @@
|
||||
"you": "你"
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
"canvasMode": "画布模式",
|
||||
"canvasToolbar": "画布工具栏",
|
||||
"fitView": "适应视图",
|
||||
"focusMode": "专注模式",
|
||||
"hand": "拖拽",
|
||||
@@ -1465,12 +1477,26 @@
|
||||
"dragAndDropImage": "拖拽图片到此处",
|
||||
"emptyWorkflowExplanation": "你的工作流为空。你需要先添加一些节点,才能开始构建应用。",
|
||||
"enterNodeGraph": "进入节点图",
|
||||
"error": {
|
||||
"getHelp": "如需帮助,请查看我们的 {0}、{1} 或 {2},并附上复制的错误信息。",
|
||||
"github": "提交 GitHub 问题",
|
||||
"goto": "在图中显示错误",
|
||||
"guide": "故障排查指南",
|
||||
"header": "应用遇到错误",
|
||||
"log": "错误日志",
|
||||
"mobileFixable": "请检查 {0} 以获取错误信息",
|
||||
"promptShow": "显示错误报告",
|
||||
"promptVisitGraph": "查看节点图以获取完整错误信息。",
|
||||
"requiresGraph": "生成过程中出现问题。可能由于无效的隐藏输入、缺失资源或工作流配置问题导致。",
|
||||
"support": "联系我们的支持团队"
|
||||
},
|
||||
"giveFeedback": "提供反馈",
|
||||
"graphMode": "图形模式",
|
||||
"hasCreditCost": "需要额外积分",
|
||||
"linearMode": "App 模式",
|
||||
"loadTemplate": "加载模板",
|
||||
"mobileControls": "编辑与运行",
|
||||
"mobileNoWorkflow": "此工作流未为应用模式构建。请尝试其他工作流。",
|
||||
"queue": {
|
||||
"clear": "清空队列",
|
||||
"clickToClear": "点击清空队列"
|
||||
@@ -1478,6 +1504,7 @@
|
||||
"rerun": "重新运行",
|
||||
"reuseParameters": "复用参数",
|
||||
"runCount": "运行次数:",
|
||||
"viewGraph": "查看节点图",
|
||||
"viewJob": "查看任务",
|
||||
"welcome": {
|
||||
"buildApp": "构建应用",
|
||||
@@ -1686,6 +1713,7 @@
|
||||
"installedSection": "已安装",
|
||||
"missingNodes": "缺失节点",
|
||||
"notInstalled": "未安装",
|
||||
"unresolvedNodes": "未解析节点",
|
||||
"updatesAvailable": "有可用更新"
|
||||
},
|
||||
"nightlyVersion": "每夜",
|
||||
@@ -1732,6 +1760,10 @@
|
||||
"uninstall": "卸载",
|
||||
"uninstallSelected": "卸载所选",
|
||||
"uninstalling": "正在卸载",
|
||||
"unresolvedNodes": {
|
||||
"message": "以下节点未安装,且在注册表中未找到。",
|
||||
"title": "未解析的缺失节点"
|
||||
},
|
||||
"update": "更新",
|
||||
"updateAll": "全部更新",
|
||||
"updateSelected": "更新所选",
|
||||
@@ -2080,6 +2112,7 @@
|
||||
"OpenAI": "OpenAI",
|
||||
"PixVerse": "PixVerse",
|
||||
"Recraft": "Recraft",
|
||||
"Reve": "Reve",
|
||||
"Rodin": "Rodin",
|
||||
"Runway": "Runway",
|
||||
"Sora": "Sora",
|
||||
@@ -2379,6 +2412,33 @@
|
||||
"inputsNone": "无输入",
|
||||
"inputsNoneTooltip": "节点没有输入",
|
||||
"locateNode": "在画布上定位节点",
|
||||
"missingModels": {
|
||||
"alreadyExistsInCategory": "该模型已存在于“{category}”中",
|
||||
"assetLoadTimeout": "模型检测超时。请尝试重新加载工作流。",
|
||||
"cancelSelection": "取消选择",
|
||||
"clearUrl": "清除链接",
|
||||
"collapseNodes": "隐藏引用节点",
|
||||
"confirmSelection": "确认选择",
|
||||
"copyModelName": "复制模型名称",
|
||||
"customNodeDownloadDisabled": "云环境不支持在此部分为自定义节点导入模型。请使用标准加载节点或在下方库中选择模型替代。",
|
||||
"expandNodes": "显示引用节点",
|
||||
"import": "导入",
|
||||
"importAnyway": "仍然导入",
|
||||
"importFailed": "导入失败",
|
||||
"importNotSupported": "不支持导入",
|
||||
"imported": "已导入",
|
||||
"importing": "正在导入...",
|
||||
"locateNode": "在画布上定位节点",
|
||||
"metadataFetchFailed": "获取元数据失败。请检查链接后重试。",
|
||||
"missingModelsTitle": "缺失模型",
|
||||
"or": "或",
|
||||
"typeMismatch": "该模型似乎是“{detectedType}”。你确定要继续吗?",
|
||||
"unknownCategory": "未知",
|
||||
"unsupportedUrl": "仅支持 Civitai 和 Hugging Face 链接。",
|
||||
"urlPlaceholder": "粘贴模型链接(Civitai 或 Hugging Face)",
|
||||
"useFromLibrary": "从库中使用",
|
||||
"usingFromLibrary": "正在从库中使用"
|
||||
},
|
||||
"missingNodePacks": {
|
||||
"applyChanges": "应用更改",
|
||||
"cloudMessage": "此工作流需要 Comfy Cloud 上尚未提供的自定义节点。",
|
||||
@@ -3250,6 +3310,7 @@
|
||||
"interrupted": "执行已被中断",
|
||||
"legacyMaskEditorDeprecated": "旧版遮罩编辑器已弃用,即将删除。",
|
||||
"migrateToLitegraphReroute": "将来的版本中将删除重定向节点。点击以迁移到litegraph-native重定向。",
|
||||
"missingModelVerificationFailed": "验证缺失模型失败。部分模型可能不会在错误标签页中显示。",
|
||||
"modelLoadedSuccessfully": "3D模型加载成功",
|
||||
"no3dScene": "没有3D场景可以应用纹理",
|
||||
"no3dSceneToExport": "没有3D场景可以导出",
|
||||
|
||||
@@ -3207,6 +3207,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKVCache": {
|
||||
"description": "为 Flux 系列模型的参考图像启用 KV 缓存优化。",
|
||||
"display_name": "Flux KV 缓存",
|
||||
"inputs": {
|
||||
"model": {
|
||||
"name": "模型",
|
||||
"tooltip": "要在其上启用 KV 缓存的模型。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": "已启用 KV 缓存的模型。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FluxKontextImageScale": {
|
||||
"description": "将图像调整为更适合 Flux Kontext 的尺寸。",
|
||||
"display_name": "图像缩放为FluxKontext",
|
||||
@@ -12855,6 +12870,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageCreateNode": {
|
||||
"description": "使用 Reve 根据文本描述生成图像。",
|
||||
"display_name": "Reve 图像生成",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "用于生成的模型版本。"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "期望图像的文本描述。最多 2560 个字符。"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "移除生成图像的背景。可能会产生额外费用。"
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "seed 控制节点是否重新运行;无论 seed 如何,结果都是非确定性的。"
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "对生成的图像进行放大。可能会产生额外费用。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageEditNode": {
|
||||
"description": "使用 Reve 通过自然语言指令编辑图像。",
|
||||
"display_name": "Reve 图像编辑",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"edit_instruction": {
|
||||
"name": "edit_instruction",
|
||||
"tooltip": "关于如何编辑图像的文本描述。最多 2560 个字符。"
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"tooltip": "要编辑的图像。"
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "用于编辑的模型版本。"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "移除生成图像的背景。可能会产生额外费用。"
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "seed 控制节点是否重新运行;无论 seed 如何,结果都是非确定性的。"
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "对生成的图像进行放大。可能会产生额外费用。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReveImageRemixNode": {
|
||||
"description": "结合参考图像和文本提示,使用 Reve 创建新图像。",
|
||||
"display_name": "Reve 图像混合",
|
||||
"inputs": {
|
||||
"control_after_generate": {
|
||||
"name": "control after generate"
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"tooltip": "用于混合的模型版本。"
|
||||
},
|
||||
"model_aspect_ratio": {
|
||||
"name": "aspect_ratio"
|
||||
},
|
||||
"model_test_time_scaling": {
|
||||
"name": "test_time_scaling"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "prompt",
|
||||
"tooltip": "期望图像的文本描述。可包含 XML img 标签,通过索引引用特定图像,例如 <img>0</img>、<img>1</img> 等。"
|
||||
},
|
||||
"reference_images": {
|
||||
"name": "reference_images"
|
||||
},
|
||||
"remove_background": {
|
||||
"name": "remove_background",
|
||||
"tooltip": "移除生成图像的背景。可能会产生额外费用。"
|
||||
},
|
||||
"seed": {
|
||||
"name": "seed",
|
||||
"tooltip": "seed 控制节点是否重新运行;无论 seed 如何,结果都是非确定性的。"
|
||||
},
|
||||
"upscale": {
|
||||
"name": "upscale",
|
||||
"tooltip": "对生成的图像进行放大。可能会产生额外费用。"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"tooltip": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rodin3D_Detail": {
|
||||
"description": "使用Rodin API生成3D资源",
|
||||
"display_name": "Rodin 3D生成 - 细节生成",
|
||||
|
||||
@@ -186,7 +186,7 @@ const tooltipDelay = computed<number>(() =>
|
||||
|
||||
const { isLoading, error } = useImage({
|
||||
src: asset.preview_url ?? '',
|
||||
alt: asset.display_name || asset.name
|
||||
alt: displayName.value
|
||||
})
|
||||
|
||||
function handleSelect() {
|
||||
|
||||