mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-17 02:47:35 +00:00
Compare commits
26 Commits
graphMutat
...
bl-invert
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f5bbe0605 | ||
|
|
a975e50f1b | ||
|
|
a17c74fa0c | ||
|
|
5e625a5002 | ||
|
|
002fac0232 | ||
|
|
7e115543fa | ||
|
|
80d75bb164 | ||
|
|
d59885839a | ||
|
|
cbb0f765b8 | ||
|
|
726a2fbbc9 | ||
|
|
553b5aa02b | ||
|
|
2ff0d951ed | ||
|
|
1f88925144 | ||
|
|
250433a91a | ||
|
|
eb664f47af | ||
|
|
bc85d4e87b | ||
|
|
7585444ce6 | ||
|
|
a886798a10 | ||
|
|
37975e4eac | ||
|
|
a41b8a6d4f | ||
|
|
b264685052 | ||
|
|
78d0ea6fa5 | ||
|
|
ea4e57b602 | ||
|
|
4789d86fe8 | ||
|
|
09e7d1040e | ||
|
|
dfa1cbba4f |
@@ -67,9 +67,9 @@ This is critical for better file inspection:
|
||||
|
||||
Use git locally for much faster analysis:
|
||||
|
||||
1. Get list of changed files: `git diff --name-only "origin/$BASE_BRANCH" > changed_files.txt`
|
||||
2. Get the full diff: `git diff "origin/$BASE_BRANCH" > pr_diff.txt`
|
||||
3. Get detailed file changes with status: `git diff --name-status "origin/$BASE_BRANCH" > file_changes.txt`
|
||||
1. Get list of changed files: `git diff --name-only "$BASE_SHA" > changed_files.txt`
|
||||
2. Get the full diff: `git diff "$BASE_SHA" > pr_diff.txt`
|
||||
3. Get detailed file changes with status: `git diff --name-status "$BASE_SHA" > file_changes.txt`
|
||||
|
||||
### Step 1.5: Create Analysis Cache
|
||||
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -13,4 +13,4 @@
|
||||
|
||||
# Generated files
|
||||
src/types/comfyRegistryTypes.ts linguist-generated=true
|
||||
src/types/generatedManagerTypes.ts linguist-generated=true
|
||||
src/workbench/extensions/manager/types/generatedManagerTypes.ts linguist-generated=true
|
||||
|
||||
109
.github/workflows/backport.yaml
vendored
109
.github/workflows/backport.yaml
vendored
@@ -4,10 +4,25 @@ on:
|
||||
pull_request_target:
|
||||
types: [closed, labeled]
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to backport'
|
||||
required: true
|
||||
type: string
|
||||
force_rerun:
|
||||
description: 'Force rerun even if backports exist'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
backport:
|
||||
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'needs-backport')
|
||||
if: >
|
||||
(github.event_name == 'pull_request_target' &&
|
||||
github.event.pull_request.merged == true &&
|
||||
contains(github.event.pull_request.labels.*.name, 'needs-backport')) ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -15,6 +30,35 @@ jobs:
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- name: Validate inputs for manual triggers
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
# Validate PR number format
|
||||
if ! [[ "${{ inputs.pr_number }}" =~ ^[0-9]+$ ]]; then
|
||||
echo "::error::Invalid PR number format. Must be a positive integer."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate PR exists and is merged
|
||||
if ! gh pr view "${{ inputs.pr_number }}" --json merged >/dev/null 2>&1; then
|
||||
echo "::error::PR #${{ inputs.pr_number }} not found or inaccessible."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MERGED=$(gh pr view "${{ inputs.pr_number }}" --json merged --jq '.merged')
|
||||
if [ "$MERGED" != "true" ]; then
|
||||
echo "::error::PR #${{ inputs.pr_number }} is not merged. Only merged PRs can be backported."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate PR has needs-backport label
|
||||
if ! gh pr view "${{ inputs.pr_number }}" --json labels --jq '.labels[].name' | grep -q "needs-backport"; then
|
||||
echo "::error::PR #${{ inputs.pr_number }} does not have 'needs-backport' label."
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -29,7 +73,7 @@ jobs:
|
||||
id: check-existing
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
||||
run: |
|
||||
# Check for existing backport PRs for this PR number
|
||||
EXISTING_BACKPORTS=$(gh pr list --state all --search "backport-${PR_NUMBER}-to" --json title,headRefName,baseRefName | jq -r '.[].headRefName')
|
||||
@@ -39,6 +83,13 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# For manual triggers with force_rerun, proceed anyway
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force_rerun }}" = "true" ]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "::warning::Force rerun requested - existing backports will be updated"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Found existing backport PRs:"
|
||||
echo "$EXISTING_BACKPORTS"
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
@@ -50,8 +101,17 @@ jobs:
|
||||
run: |
|
||||
# Extract version labels (e.g., "1.24", "1.22")
|
||||
VERSIONS=""
|
||||
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
|
||||
for label in $(echo "$LABELS" | jq -r '.[].name'); do
|
||||
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
# For manual triggers, get labels from the PR
|
||||
LABELS=$(gh pr view ${{ inputs.pr_number }} --json labels | jq -r '.labels[].name')
|
||||
else
|
||||
# For automatic triggers, extract from PR event
|
||||
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
|
||||
LABELS=$(echo "$LABELS" | jq -r '.[].name')
|
||||
fi
|
||||
|
||||
for label in $LABELS; do
|
||||
# Match version labels like "1.24" (major.minor only)
|
||||
if [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||
# Validate the branch exists before adding to list
|
||||
@@ -75,12 +135,20 @@ jobs:
|
||||
if: steps.check-existing.outputs.skip != 'true'
|
||||
id: backport
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
MERGE_COMMIT: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
||||
run: |
|
||||
FAILED=""
|
||||
SUCCESS=""
|
||||
|
||||
# Get PR data for manual triggers
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json title,mergeCommit)
|
||||
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
|
||||
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid')
|
||||
else
|
||||
PR_TITLE="${{ github.event.pull_request.title }}"
|
||||
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
|
||||
fi
|
||||
|
||||
for version in ${{ steps.versions.outputs.versions }}; do
|
||||
echo "::group::Backporting to core/${version}"
|
||||
@@ -133,10 +201,18 @@ jobs:
|
||||
if: steps.check-existing.outputs.skip != 'true' && steps.backport.outputs.success
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
||||
run: |
|
||||
# Get PR data for manual triggers
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json title,author)
|
||||
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
|
||||
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
|
||||
else
|
||||
PR_TITLE="${{ github.event.pull_request.title }}"
|
||||
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
|
||||
fi
|
||||
|
||||
for backport in ${{ steps.backport.outputs.success }}; do
|
||||
IFS=':' read -r version branch <<< "${backport}"
|
||||
|
||||
@@ -165,9 +241,16 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
|
||||
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json author,mergeCommit)
|
||||
PR_NUMBER="${{ inputs.pr_number }}"
|
||||
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
|
||||
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid')
|
||||
else
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
|
||||
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
|
||||
fi
|
||||
|
||||
for failure in ${{ steps.backport.outputs.failed }}; do
|
||||
IFS=':' read -r version reason conflicts <<< "${failure}"
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -78,8 +78,8 @@ vite.config.mts.timestamp-*.mjs
|
||||
*storybook.log
|
||||
storybook-static
|
||||
|
||||
|
||||
|
||||
# MCP Servers
|
||||
.playwright-mcp/*
|
||||
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
|
||||
@@ -9,7 +9,7 @@ module.exports = defineConfig({
|
||||
entry: 'src/locales/en',
|
||||
entryLocale: 'en',
|
||||
output: 'src/locales',
|
||||
outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'ar'],
|
||||
outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'ar', 'tr'],
|
||||
reference: `Special names to keep untranslated: flux, photomaker, clip, vae, cfg, stable audio, stable cascade, stable zero, controlnet, lora, HiDream.
|
||||
'latent' is the short form of 'latent space'.
|
||||
'mask' is in the context of image processing.
|
||||
|
||||
@@ -15,21 +15,32 @@ const config: StorybookConfig = {
|
||||
async viteFinal(config) {
|
||||
// Use dynamic import to avoid CJS deprecation warning
|
||||
const { mergeConfig } = await import('vite')
|
||||
const { default: tailwindcss } = await import('@tailwindcss/vite')
|
||||
|
||||
// Filter out any plugins that might generate import maps
|
||||
if (config.plugins) {
|
||||
config.plugins = config.plugins.filter((plugin: any) => {
|
||||
if (plugin && plugin.name && plugin.name.includes('import-map')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
config.plugins = config.plugins
|
||||
// Type guard: ensure we have valid plugin objects with names
|
||||
.filter(
|
||||
(plugin): plugin is NonNullable<typeof plugin> & { name: string } => {
|
||||
return (
|
||||
plugin !== null &&
|
||||
plugin !== undefined &&
|
||||
typeof plugin === 'object' &&
|
||||
'name' in plugin &&
|
||||
typeof plugin.name === 'string'
|
||||
)
|
||||
}
|
||||
)
|
||||
// Business logic: filter out import-map plugins
|
||||
.filter((plugin) => !plugin.name.includes('import-map'))
|
||||
}
|
||||
|
||||
return mergeConfig(config, {
|
||||
// Replace plugins entirely to avoid inheritance issues
|
||||
plugins: [
|
||||
// Only include plugins we explicitly need for Storybook
|
||||
tailwindcss(),
|
||||
Icons({
|
||||
compiler: 'vue3',
|
||||
customCollections: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { definePreset } from '@primevue/themes'
|
||||
import Aura from '@primevue/themes/aura'
|
||||
import { setup } from '@storybook/vue3'
|
||||
import type { Preview } from '@storybook/vue3-vite'
|
||||
import type { Preview, StoryContext, StoryFn } from '@storybook/vue3-vite'
|
||||
import { createPinia } from 'pinia'
|
||||
import 'primeicons/primeicons.css'
|
||||
import PrimeVue from 'primevue/config'
|
||||
@@ -9,11 +9,9 @@ import ConfirmationService from 'primevue/confirmationservice'
|
||||
import ToastService from 'primevue/toastservice'
|
||||
import Tooltip from 'primevue/tooltip'
|
||||
|
||||
import '../src/assets/css/style.css'
|
||||
import { i18n } from '../src/i18n'
|
||||
import '../src/lib/litegraph/public/css/litegraph.css'
|
||||
import { useWidgetStore } from '../src/stores/widgetStore'
|
||||
import { useColorPaletteStore } from '../src/stores/workspace/colorPaletteStore'
|
||||
import '@/assets/css/style.css'
|
||||
import { i18n } from '@/i18n'
|
||||
import '@/lib/litegraph/public/css/litegraph.css'
|
||||
|
||||
const ComfyUIPreset = definePreset(Aura, {
|
||||
semantic: {
|
||||
@@ -25,13 +23,11 @@ const ComfyUIPreset = definePreset(Aura, {
|
||||
// Setup Vue app for Storybook
|
||||
setup((app) => {
|
||||
app.directive('tooltip', Tooltip)
|
||||
|
||||
// Create Pinia instance
|
||||
const pinia = createPinia()
|
||||
|
||||
app.use(pinia)
|
||||
|
||||
// Initialize stores
|
||||
useColorPaletteStore(pinia)
|
||||
useWidgetStore(pinia)
|
||||
|
||||
app.use(i18n)
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
@@ -50,8 +46,8 @@ setup((app) => {
|
||||
app.use(ToastService)
|
||||
})
|
||||
|
||||
// Dark theme decorator
|
||||
export const withTheme = (Story: any, context: any) => {
|
||||
// Theme and dialog decorator
|
||||
export const withTheme = (Story: StoryFn, context: StoryContext) => {
|
||||
const theme = context.globals.theme || 'light'
|
||||
|
||||
// Apply theme class to document root
|
||||
@@ -63,7 +59,7 @@ export const withTheme = (Story: any, context: any) => {
|
||||
document.body.classList.remove('dark-theme')
|
||||
}
|
||||
|
||||
return Story()
|
||||
return Story(context.args, context)
|
||||
}
|
||||
|
||||
const preview: Preview = {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Page, test as base } from '@playwright/test'
|
||||
import type { Page } from '@playwright/test'
|
||||
import { test as base } from '@playwright/test'
|
||||
|
||||
export class UserSelectPage {
|
||||
constructor(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Locator, Page } from '@playwright/test'
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
export class ComfyNodeSearchFilterSelectionPanel {
|
||||
constructor(public readonly page: Page) {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Page } from '@playwright/test'
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
import { ComfyPage } from '../ComfyPage'
|
||||
import type { ComfyPage } from '../ComfyPage'
|
||||
|
||||
export class SettingDialog {
|
||||
constructor(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Locator, Page } from '@playwright/test'
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
class SidebarTab {
|
||||
constructor(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Locator, Page, expect } from '@playwright/test'
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
export class Topbar {
|
||||
private readonly menuLocator: Locator
|
||||
|
||||
@@ -12,9 +12,10 @@ export const webSocketFixture = base.extend<{
|
||||
// so we can look it up to trigger messages
|
||||
const store: Record<string, WebSocket> = ((window as any).__ws__ = {})
|
||||
window.WebSocket = class extends window.WebSocket {
|
||||
constructor() {
|
||||
// @ts-expect-error
|
||||
super(...arguments)
|
||||
constructor(
|
||||
...rest: ConstructorParameters<typeof window.WebSocket>
|
||||
) {
|
||||
super(...rest)
|
||||
store[this.url] = this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FullConfig } from '@playwright/test'
|
||||
import type { FullConfig } from '@playwright/test'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
import { backupPath } from './utils/backupUtils'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FullConfig } from '@playwright/test'
|
||||
import type { FullConfig } from '@playwright/test'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
import { restorePath } from './utils/backupUtils'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Locator, Page } from '@playwright/test'
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
export class ManageGroupNode {
|
||||
footer: Locator
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Locator, Page } from '@playwright/test'
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
import path from 'path'
|
||||
|
||||
import {
|
||||
import type {
|
||||
TemplateInfo,
|
||||
WorkflowTemplates
|
||||
} from '../../src/platform/workflow/templates/types/template'
|
||||
|
||||
@@ -29,9 +29,9 @@ test.describe('Actionbar', () => {
|
||||
|
||||
// Intercept the prompt queue endpoint
|
||||
let promptNumber = 0
|
||||
comfyPage.page.route('**/api/prompt', async (route, req) => {
|
||||
await comfyPage.page.route('**/api/prompt', async (route, req) => {
|
||||
await new Promise((r) => setTimeout(r, 100))
|
||||
route.fulfill({
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
body: JSON.stringify({
|
||||
prompt_id: promptNumber,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
import {
|
||||
ComfyPage,
|
||||
comfyExpect as expect,
|
||||
comfyPageFixture as test
|
||||
} from '../fixtures/ComfyPage'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Page, expect } from '@playwright/test'
|
||||
import type { Page } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Locator, expect } from '@playwright/test'
|
||||
import type { Locator } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import type { Keybinding } from '../../src/schemas/keyBindingSchema'
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { SettingParams } from '../../src/platform/settings/types'
|
||||
import type { SettingParams } from '../../src/platform/settings/types'
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Topbar commands', () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { ComfyPage, comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
import type { NodeReference } from '../fixtures/utils/litegraphUtils'
|
||||
|
||||
test.describe('Group Node', () => {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Locator, expect } from '@playwright/test'
|
||||
import { Position } from '@vueuse/core'
|
||||
import type { Locator } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
import type { Position } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
type ComfyPage,
|
||||
comfyPageFixture as test,
|
||||
testComfySnapToGridGridSize
|
||||
} from '../fixtures/ComfyPage'
|
||||
import { type NodeReference } from '../fixtures/utils/litegraphUtils'
|
||||
import type { NodeReference } from '../fixtures/utils/litegraphUtils'
|
||||
|
||||
test.describe('Item Interaction', () => {
|
||||
test('Can select/delete all items', async ({ comfyPage }) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { ComfyPage, comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Remote COMBO Widget', () => {
|
||||
const mockOptions = ['d', 'c', 'b', 'a']
|
||||
|
||||
@@ -160,7 +160,9 @@ test.describe.skip('Queue sidebar', () => {
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.nextFrame()
|
||||
expect(comfyPage.menu.queueTab.getGalleryImage(firstImage)).toBeVisible()
|
||||
await expect(
|
||||
comfyPage.menu.queueTab.getGalleryImage(firstImage)
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('maintains active gallery item when new tasks are added', async ({
|
||||
@@ -174,7 +176,9 @@ test.describe.skip('Queue sidebar', () => {
|
||||
const newTask = comfyPage.menu.queueTab.tasks.getByAltText(newImage)
|
||||
await newTask.waitFor({ state: 'visible' })
|
||||
// The active gallery item should still be the initial image
|
||||
expect(comfyPage.menu.queueTab.getGalleryImage(firstImage)).toBeVisible()
|
||||
await expect(
|
||||
comfyPage.menu.queueTab.getGalleryImage(firstImage)
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test.describe('Gallery navigation', () => {
|
||||
@@ -196,7 +200,9 @@ test.describe.skip('Queue sidebar', () => {
|
||||
delay: 256
|
||||
})
|
||||
await comfyPage.nextFrame()
|
||||
expect(comfyPage.menu.queueTab.getGalleryImage(end)).toBeVisible()
|
||||
await expect(
|
||||
comfyPage.menu.queueTab.getGalleryImage(end)
|
||||
).toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Page, expect } from '@playwright/test'
|
||||
import type { Page } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { SystemStats } from '../../src/schemas/apiSchema'
|
||||
import type { SystemStats } from '../../src/schemas/apiSchema'
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Version Mismatch Warnings', () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
/* Test files should not be compiled */
|
||||
"noEmit": true,
|
||||
@@ -9,13 +9,6 @@
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": [
|
||||
"*.ts",
|
||||
"*.mts",
|
||||
"*.config.js",
|
||||
"browser_tests/**/*.ts",
|
||||
"scripts/**/*.js",
|
||||
"scripts/**/*.ts",
|
||||
"tests-ui/**/*.ts",
|
||||
".storybook/**/*.ts"
|
||||
"**/*.ts",
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path'
|
||||
import { Plugin } from 'vite'
|
||||
import type { Plugin } from 'vite'
|
||||
|
||||
interface ShimResult {
|
||||
code: string
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import glob from 'fast-glob'
|
||||
import fs from 'fs-extra'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { HtmlTagDescriptor, Plugin, normalizePath } from 'vite'
|
||||
import { type HtmlTagDescriptor, type Plugin, normalizePath } from 'vite'
|
||||
|
||||
interface ImportMapSource {
|
||||
name: string
|
||||
|
||||
@@ -153,5 +153,14 @@ export default defineConfig([
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['tests-ui/**/*'],
|
||||
rules: {
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'error',
|
||||
{ disallowTypeAnnotations: false }
|
||||
]
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
@@ -22,13 +22,12 @@ const config: KnipConfig = {
|
||||
],
|
||||
ignore: [
|
||||
// Auto generated manager types
|
||||
'src/types/generatedManagerTypes.ts',
|
||||
'src/workbench/extensions/manager/types/generatedManagerTypes.ts',
|
||||
'src/types/comfyRegistryTypes.ts',
|
||||
// Used by a custom node (that should move off of this)
|
||||
'src/scripts/ui/components/splitButton.ts',
|
||||
// Staged for for use with subgraph widget promotion
|
||||
'src/lib/litegraph/src/widgets/DisconnectedWidget.ts',
|
||||
'src/core/graph/operations/types.ts'
|
||||
'src/lib/litegraph/src/widgets/DisconnectedWidget.ts'
|
||||
],
|
||||
compilers: {
|
||||
// https://github.com/webpro-nl/knip/issues/1008#issuecomment-3207756199
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"private": true,
|
||||
"version": "1.27.5",
|
||||
"version": "1.28.0",
|
||||
"type": "module",
|
||||
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
|
||||
"homepage": "https://comfy.org",
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as fs from 'fs'
|
||||
|
||||
import { comfyPageFixture as test } from '../browser_tests/fixtures/ComfyPage'
|
||||
import { CORE_MENU_COMMANDS } from '../src/constants/coreMenuCommands'
|
||||
import { DESKTOP_DIALOGS } from '../src/constants/desktopDialogs'
|
||||
import { SERVER_CONFIG_ITEMS } from '../src/constants/serverConfig'
|
||||
import type { FormItem, SettingParams } from '../src/platform/settings/types'
|
||||
import type { ComfyCommandImpl } from '../src/stores/commandStore'
|
||||
@@ -131,6 +132,23 @@ test('collect-i18n-general', async ({ comfyPage }) => {
|
||||
])
|
||||
)
|
||||
|
||||
// Desktop Dialogs
|
||||
const allDesktopDialogsLocale = Object.fromEntries(
|
||||
Object.values(DESKTOP_DIALOGS).map((dialog) => [
|
||||
normalizeI18nKey(dialog.id),
|
||||
{
|
||||
title: dialog.title,
|
||||
message: dialog.message,
|
||||
buttons: Object.fromEntries(
|
||||
dialog.buttons.map((button) => [
|
||||
normalizeI18nKey(button.label),
|
||||
button.label
|
||||
])
|
||||
)
|
||||
}
|
||||
])
|
||||
)
|
||||
|
||||
fs.writeFileSync(
|
||||
localePath,
|
||||
JSON.stringify(
|
||||
@@ -144,7 +162,8 @@ test('collect-i18n-general', async ({ comfyPage }) => {
|
||||
...allSettingCategoriesLocale
|
||||
},
|
||||
serverConfigItems: allServerConfigsLocale,
|
||||
serverConfigCategories: allServerConfigCategoriesLocale
|
||||
serverConfigCategories: allServerConfigCategoriesLocale,
|
||||
desktopDialogs: allDesktopDialogsLocale
|
||||
},
|
||||
null,
|
||||
2
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
--color-charcoal-700: #202121;
|
||||
--color-charcoal-800: #171718;
|
||||
|
||||
--color-neutral-550: #636363;
|
||||
|
||||
--color-stone-100: #444444;
|
||||
--color-stone-200: #828282;
|
||||
--color-stone-300: #bbbbbb;
|
||||
@@ -103,6 +105,10 @@
|
||||
--color-danger-100: #c02323;
|
||||
--color-danger-200: #d62952;
|
||||
|
||||
--color-coral-red-600: #973a40;
|
||||
--color-coral-red-500: #c53f49;
|
||||
--color-coral-red-400: #dd424e;
|
||||
|
||||
--color-bypass: #6a246a;
|
||||
--color-error: #962a2a;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
class="absolute inset-0"
|
||||
/>
|
||||
<img
|
||||
v-show="isImageLoaded"
|
||||
v-if="cachedSrc"
|
||||
ref="imageRef"
|
||||
:src="cachedSrc"
|
||||
:alt="alt"
|
||||
@@ -77,8 +77,8 @@ const shouldLoad = computed(() => isIntersecting.value)
|
||||
|
||||
watch(
|
||||
shouldLoad,
|
||||
async (shouldLoad) => {
|
||||
if (shouldLoad && src && !cachedSrc.value && !hasError.value) {
|
||||
async (shouldLoadVal) => {
|
||||
if (shouldLoadVal && src && !cachedSrc.value && !hasError.value) {
|
||||
try {
|
||||
const cachedMedia = await getCachedMedia(src)
|
||||
if (cachedMedia.error) {
|
||||
@@ -93,7 +93,7 @@ watch(
|
||||
console.warn('Failed to load cached media:', error)
|
||||
cachedSrc.value = src
|
||||
}
|
||||
} else if (!shouldLoad) {
|
||||
} else if (!shouldLoadVal) {
|
||||
if (cachedSrc.value?.startsWith('blob:')) {
|
||||
releaseUrl(src)
|
||||
}
|
||||
|
||||
@@ -59,14 +59,13 @@ import { useI18n } from 'vue-i18n'
|
||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||
import MissingCoreNodesMessage from '@/components/dialog/content/MissingCoreNodesMessage.vue'
|
||||
import { useMissingNodes } from '@/composables/nodePack/useMissingNodes'
|
||||
import { useManagerState } from '@/composables/useManagerState'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
import type { MissingNodeType } from '@/types/comfy'
|
||||
import { ManagerTab } from '@/types/comfyManagerTypes'
|
||||
|
||||
import PackInstallButton from './manager/button/PackInstallButton.vue'
|
||||
import PackInstallButton from '@/workbench/extensions/manager/components/manager/button/PackInstallButton.vue'
|
||||
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||
|
||||
const props = defineProps<{
|
||||
missingNodeTypes: MissingNodeType[]
|
||||
@@ -138,7 +137,7 @@ const allMissingNodesInstalled = computed(() => {
|
||||
})
|
||||
// Watch for completion and close dialog
|
||||
watch(allMissingNodesInstalled, async (allInstalled) => {
|
||||
if (allInstalled) {
|
||||
if (allInstalled && showInstallAllButton.value) {
|
||||
// Use nextTick to ensure state updates are complete
|
||||
await nextTick()
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { createPinia } from 'pinia'
|
||||
import PrimeVue from 'primevue/config'
|
||||
import Tag from 'primevue/tag'
|
||||
import Tooltip from 'primevue/tooltip'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import enMessages from '@/locales/en/main.json' with { type: 'json' }
|
||||
|
||||
import ManagerHeader from './ManagerHeader.vue'
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: 'en',
|
||||
messages: {
|
||||
en: enMessages
|
||||
}
|
||||
})
|
||||
|
||||
describe('ManagerHeader', () => {
|
||||
const createWrapper = () => {
|
||||
return mount(ManagerHeader, {
|
||||
global: {
|
||||
plugins: [createPinia(), PrimeVue, i18n],
|
||||
directives: {
|
||||
tooltip: Tooltip
|
||||
},
|
||||
components: {
|
||||
Tag
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
it('renders the component title', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
expect(wrapper.find('h2').text()).toBe(
|
||||
enMessages.manager.discoverCommunityContent
|
||||
)
|
||||
})
|
||||
|
||||
it('displays the legacy manager UI tag', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const tag = wrapper.find('[data-pc-name="tag"]')
|
||||
expect(tag.exists()).toBe(true)
|
||||
expect(tag.text()).toContain(enMessages.manager.legacyManagerUI)
|
||||
})
|
||||
|
||||
it('applies info severity to the tag', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const tag = wrapper.find('[data-pc-name="tag"]')
|
||||
expect(tag.classes()).toContain('p-tag-info')
|
||||
})
|
||||
|
||||
it('displays info icon in the tag', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const icon = wrapper.find('.pi-info-circle')
|
||||
expect(icon.exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('has cursor-help class on the tag', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const tag = wrapper.find('[data-pc-name="tag"]')
|
||||
expect(tag.classes()).toContain('cursor-help')
|
||||
})
|
||||
|
||||
it('has proper structure with flex container', () => {
|
||||
const wrapper = createWrapper()
|
||||
|
||||
const flexContainer = wrapper.find('.flex.justify-end.ml-auto.pr-4')
|
||||
expect(flexContainer.exists()).toBe(true)
|
||||
|
||||
const tag = flexContainer.find('[data-pc-name="tag"]')
|
||||
expect(tag.exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<div class="flex items-center">
|
||||
<h2 class="text-lg font-normal text-left">
|
||||
{{ $t('manager.discoverCommunityContent') }}
|
||||
</h2>
|
||||
<div class="flex justify-end ml-auto pr-4 pl-2">
|
||||
<Tag
|
||||
v-tooltip.left="$t('manager.legacyManagerUIDescription')"
|
||||
severity="info"
|
||||
icon="pi pi-info-circle"
|
||||
:value="$t('manager.legacyManagerUI')"
|
||||
class="cursor-help ml-2"
|
||||
:pt="{
|
||||
root: { class: 'text-xs' }
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Tag from 'primevue/tag'
|
||||
</script>
|
||||
@@ -96,7 +96,6 @@ import NodeSearchboxPopover from '@/components/searchbox/NodeSearchBoxPopover.vu
|
||||
import SideToolbar from '@/components/sidebar/SideToolbar.vue'
|
||||
import SecondRowWorkflowTabs from '@/components/topbar/SecondRowWorkflowTabs.vue'
|
||||
import { useChainCallback } from '@/composables/functional/useChainCallback'
|
||||
import { useCanvasInteractions } from '@/composables/graph/useCanvasInteractions'
|
||||
import { useViewportCulling } from '@/composables/graph/useViewportCulling'
|
||||
import { useVueNodeLifecycle } from '@/composables/graph/useVueNodeLifecycle'
|
||||
import { useNodeBadge } from '@/composables/node/useNodeBadge'
|
||||
@@ -118,6 +117,8 @@ import { useWorkflowAutoSave } from '@/platform/workflow/persistence/composables
|
||||
import { useWorkflowPersistence } from '@/platform/workflow/persistence/composables/useWorkflowPersistence'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { SelectedNodeIdsKey } from '@/renderer/core/canvas/injectionKeys'
|
||||
import { attachSlotLinkPreviewRenderer } from '@/renderer/core/canvas/links/slotLinkPreviewRenderer'
|
||||
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
||||
import TransformPane from '@/renderer/core/layout/transform/TransformPane.vue'
|
||||
import MiniMap from '@/renderer/extensions/minimap/MiniMap.vue'
|
||||
import VueGraphNode from '@/renderer/extensions/vueNodes/components/LGraphNode.vue'
|
||||
@@ -404,6 +405,7 @@ onMounted(async () => {
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
await comfyApp.setup(canvasRef.value)
|
||||
attachSlotLinkPreviewRenderer(comfyApp.canvas)
|
||||
canvasStore.canvas = comfyApp.canvas
|
||||
canvasStore.canvas.render_canvas_border = false
|
||||
workspaceStore.spinner = false
|
||||
|
||||
@@ -124,11 +124,11 @@ import ButtonGroup from 'primevue/buttongroup'
|
||||
import { computed, onBeforeUnmount, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useCanvasInteractions } from '@/composables/graph/useCanvasInteractions'
|
||||
import { useZoomControls } from '@/composables/useZoomControls'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
||||
import { useMinimap } from '@/renderer/extensions/minimap/composables/useMinimap'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
||||
|
||||
@@ -68,7 +68,7 @@ const onIdle = () => {
|
||||
ctor.title_mode !== LiteGraph.NO_TITLE &&
|
||||
canvas.graph_mouse[1] < node.pos[1] // If we are over a node, but not within the node then we are on its title
|
||||
) {
|
||||
return showTooltip(nodeDef.description)
|
||||
return showTooltip(nodeDef?.description)
|
||||
}
|
||||
|
||||
if (node.flags?.collapsed) return
|
||||
@@ -83,7 +83,7 @@ const onIdle = () => {
|
||||
const inputName = node.inputs[inputSlot].name
|
||||
const translatedTooltip = st(
|
||||
`nodeDefs.${normalizeI18nKey(node.type ?? '')}.inputs.${normalizeI18nKey(inputName)}.tooltip`,
|
||||
nodeDef.inputs[inputName]?.tooltip ?? ''
|
||||
nodeDef?.inputs[inputName]?.tooltip ?? ''
|
||||
)
|
||||
return showTooltip(translatedTooltip)
|
||||
}
|
||||
@@ -97,7 +97,7 @@ const onIdle = () => {
|
||||
if (outputSlot !== -1) {
|
||||
const translatedTooltip = st(
|
||||
`nodeDefs.${normalizeI18nKey(node.type ?? '')}.outputs.${outputSlot}.tooltip`,
|
||||
nodeDef.outputs[outputSlot]?.tooltip ?? ''
|
||||
nodeDef?.outputs[outputSlot]?.tooltip ?? ''
|
||||
)
|
||||
return showTooltip(translatedTooltip)
|
||||
}
|
||||
@@ -107,7 +107,7 @@ const onIdle = () => {
|
||||
if (widget && !isDOMWidget(widget)) {
|
||||
const translatedTooltip = st(
|
||||
`nodeDefs.${normalizeI18nKey(node.type ?? '')}.inputs.${normalizeI18nKey(widget.name)}.tooltip`,
|
||||
nodeDef.inputs[widget.name]?.tooltip ?? ''
|
||||
nodeDef?.inputs[widget.name]?.tooltip ?? ''
|
||||
)
|
||||
// Widget tooltip can be set dynamically, current translation collection does not support this.
|
||||
return showTooltip(widget.tooltip ?? translatedTooltip)
|
||||
|
||||
@@ -5,12 +5,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import SelectionToolbox from '@/components/graph/SelectionToolbox.vue'
|
||||
import { useCanvasInteractions } from '@/composables/graph/useCanvasInteractions'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
||||
import { useExtensionService } from '@/services/extensionService'
|
||||
|
||||
// Mock the composables and services
|
||||
vi.mock('@/composables/graph/useCanvasInteractions', () => ({
|
||||
vi.mock('@/renderer/core/canvas/useCanvasInteractions', () => ({
|
||||
useCanvasInteractions: vi.fn(() => ({
|
||||
handleWheel: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -60,9 +60,9 @@ import MaskEditorButton from '@/components/graph/selectionToolbox/MaskEditorButt
|
||||
import RefreshSelectionButton from '@/components/graph/selectionToolbox/RefreshSelectionButton.vue'
|
||||
import PublishSubgraphButton from '@/components/graph/selectionToolbox/SaveToSubgraphLibrary.vue'
|
||||
import { useSelectionToolboxPosition } from '@/composables/canvas/useSelectionToolboxPosition'
|
||||
import { useCanvasInteractions } from '@/composables/graph/useCanvasInteractions'
|
||||
import { useSelectionState } from '@/composables/graph/useSelectionState'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
||||
import { useMinimap } from '@/renderer/extensions/minimap/composables/useMinimap'
|
||||
import { useExtensionService } from '@/services/extensionService'
|
||||
import { type ComfyCommandImpl, useCommandStore } from '@/stores/commandStore'
|
||||
|
||||
@@ -142,14 +142,14 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import PuzzleIcon from '@/components/icons/PuzzleIcon.vue'
|
||||
import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment'
|
||||
import { useManagerState } from '@/composables/useManagerState'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import type { ReleaseNote } from '@/platform/updates/common/releaseService'
|
||||
import { useReleaseStore } from '@/platform/updates/common/releaseStore'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { ManagerTab } from '@/types/comfyManagerTypes'
|
||||
import { electronAPI, isElectron } from '@/utils/envUtil'
|
||||
import { formatVersionAnchor } from '@/utils/formatUtil'
|
||||
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
|
||||
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||
|
||||
// Types
|
||||
interface MenuItem {
|
||||
|
||||
@@ -82,7 +82,6 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import SubgraphBreadcrumb from '@/components/breadcrumb/SubgraphBreadcrumb.vue'
|
||||
import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue'
|
||||
import { useManagerState } from '@/composables/useManagerState'
|
||||
import SettingDialogContent from '@/platform/settings/components/SettingDialogContent.vue'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useColorPaletteService } from '@/services/colorPaletteService'
|
||||
@@ -90,10 +89,11 @@ import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
import { useMenuItemStore } from '@/stores/menuItemStore'
|
||||
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
|
||||
import { ManagerTab } from '@/types/comfyManagerTypes'
|
||||
import { showNativeSystemMenu } from '@/utils/envUtil'
|
||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
||||
import { whileMouseDown } from '@/utils/mouseDownUtil'
|
||||
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
|
||||
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||
|
||||
const colorPaletteStore = useColorPaletteStore()
|
||||
const colorPaletteService = useColorPaletteService()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { onUnmounted, ref, watch } from 'vue'
|
||||
import { computed, onUnmounted, ref, watch } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
import { useCanvasTransformSync } from '@/composables/canvas/useCanvasTransformSync'
|
||||
@@ -170,50 +170,75 @@ export function useSelectionToolboxPosition(
|
||||
}
|
||||
)
|
||||
|
||||
// Watch for dragging state
|
||||
watch(
|
||||
() => canvasStore.canvas?.state?.draggingItems,
|
||||
(dragging) => {
|
||||
if (dragging) {
|
||||
visible.value = false
|
||||
|
||||
if (moreOptionsOpen.value) {
|
||||
const currentSig = buildSelectionSignature(canvasStore)
|
||||
if (currentSig !== moreOptionsSelectionSignature) {
|
||||
moreOptionsSelectionSignature = null
|
||||
}
|
||||
moreOptionsWasOpenBeforeDrag = true
|
||||
moreOptionsOpen.value = false
|
||||
moreOptionsRestorePending.value = !!moreOptionsSelectionSignature
|
||||
if (moreOptionsRestorePending.value) {
|
||||
forceCloseMoreOptionsSignal.value++
|
||||
} else {
|
||||
moreOptionsWasOpenBeforeDrag = false
|
||||
}
|
||||
} else {
|
||||
moreOptionsRestorePending.value = false
|
||||
moreOptionsWasOpenBeforeDrag = false
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(() => {
|
||||
updateSelectionBounds()
|
||||
const selectionMatches = currentSelectionMatchesSignature(canvasStore)
|
||||
const shouldRestore =
|
||||
moreOptionsWasOpenBeforeDrag &&
|
||||
visible.value &&
|
||||
moreOptionsRestorePending.value &&
|
||||
selectionMatches
|
||||
|
||||
if (shouldRestore) {
|
||||
restoreMoreOptionsSignal.value++
|
||||
} else {
|
||||
moreOptionsRestorePending.value = false
|
||||
}
|
||||
moreOptionsWasOpenBeforeDrag = false
|
||||
})
|
||||
}
|
||||
const handleDragStateChange = (dragging: boolean) => {
|
||||
if (dragging) {
|
||||
handleDragStart()
|
||||
return
|
||||
}
|
||||
)
|
||||
|
||||
handleDragEnd()
|
||||
}
|
||||
|
||||
const handleDragStart = () => {
|
||||
visible.value = false
|
||||
|
||||
// Early return if more options wasn't open
|
||||
if (!moreOptionsOpen.value) {
|
||||
moreOptionsRestorePending.value = false
|
||||
moreOptionsWasOpenBeforeDrag = false
|
||||
return
|
||||
}
|
||||
|
||||
// Handle more options cleanup
|
||||
const currentSig = buildSelectionSignature(canvasStore)
|
||||
const selectionChanged = currentSig !== moreOptionsSelectionSignature
|
||||
|
||||
if (selectionChanged) {
|
||||
moreOptionsSelectionSignature = null
|
||||
}
|
||||
moreOptionsOpen.value = false
|
||||
moreOptionsWasOpenBeforeDrag = true
|
||||
moreOptionsRestorePending.value = !!moreOptionsSelectionSignature
|
||||
|
||||
if (moreOptionsRestorePending.value) {
|
||||
forceCloseMoreOptionsSignal.value++
|
||||
return
|
||||
}
|
||||
|
||||
moreOptionsWasOpenBeforeDrag = false
|
||||
}
|
||||
|
||||
const handleDragEnd = () => {
|
||||
requestAnimationFrame(() => {
|
||||
updateSelectionBounds()
|
||||
|
||||
const selectionMatches = currentSelectionMatchesSignature(canvasStore)
|
||||
const shouldRestore =
|
||||
moreOptionsWasOpenBeforeDrag &&
|
||||
visible.value &&
|
||||
moreOptionsRestorePending.value &&
|
||||
selectionMatches
|
||||
|
||||
// Single point of assignment for each ref
|
||||
moreOptionsRestorePending.value =
|
||||
shouldRestore && moreOptionsRestorePending.value
|
||||
moreOptionsWasOpenBeforeDrag = false
|
||||
|
||||
if (shouldRestore) {
|
||||
restoreMoreOptionsSignal.value++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Unified dragging state - combines both LiteGraph and Vue node dragging
|
||||
const isDragging = computed((): boolean => {
|
||||
const litegraphDragging = canvasStore.canvas?.state?.draggingItems ?? false
|
||||
const vueNodeDragging =
|
||||
shouldRenderVueNodes.value && layoutStore.isDraggingVueNodes.value
|
||||
return litegraphDragging || vueNodeDragging
|
||||
})
|
||||
|
||||
watch(isDragging, handleDragStateChange)
|
||||
|
||||
onUnmounted(() => {
|
||||
resetMoreOptionsState()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCanvasInteractions } from '@/composables/graph/useCanvasInteractions'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
||||
import { useNodeOutputStore } from '@/stores/imagePreviewStore'
|
||||
import { fitDimensionsToNodeWidth } from '@/utils/imageUtil'
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import { whenever } from '@vueuse/core'
|
||||
import { computed, onUnmounted, ref } from 'vue'
|
||||
|
||||
import { useNodePacks } from '@/composables/nodePack/useNodePacks'
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import type { UseNodePacksOptions } from '@/types/comfyManagerTypes'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
import type { UseNodePacksOptions } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||
|
||||
export const useInstalledPacks = (options: UseNodePacksOptions = {}) => {
|
||||
const comfyManagerStore = useComfyManagerStore()
|
||||
|
||||
@@ -5,10 +5,10 @@ import { useWorkflowPacks } from '@/composables/nodePack/useWorkflowPacks'
|
||||
import type { NodeProperty } from '@/lib/litegraph/src/LGraphNode'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import { useNodeDefStore } from '@/stores/nodeDefStore'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { collectAllNodes } from '@/utils/graphTraversalUtil'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
|
||||
/**
|
||||
* Composable to find missing NodePacks from workflow
|
||||
|
||||
@@ -2,7 +2,7 @@ import { get, useAsyncState } from '@vueuse/core'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
import { useComfyRegistryStore } from '@/stores/comfyRegistryStore'
|
||||
import type { UseNodePacksOptions } from '@/types/comfyManagerTypes'
|
||||
import type { UseNodePacksOptions } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||
|
||||
/**
|
||||
* Handles fetching node packs from the registry given a list of node pack IDs
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { compareVersions, isSemVer } from '@/utils/formatUtil'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
|
||||
export const usePackUpdateStatus = (
|
||||
nodePack: components['schemas']['Node']
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type Ref, computed } from 'vue'
|
||||
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
|
||||
type NodePack = components['schemas']['Node']
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { computed, onMounted } from 'vue'
|
||||
|
||||
import { useInstalledPacks } from '@/composables/nodePack/useInstalledPacks'
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { compareVersions, isSemVer } from '@/utils/formatUtil'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
|
||||
/**
|
||||
* Composable to find NodePacks that have updates available
|
||||
|
||||
@@ -7,9 +7,9 @@ import { app } from '@/scripts/app'
|
||||
import { useComfyRegistryStore } from '@/stores/comfyRegistryStore'
|
||||
import { useNodeDefStore } from '@/stores/nodeDefStore'
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import type { UseNodePacksOptions } from '@/types/comfyManagerTypes'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { collectAllNodes } from '@/utils/graphTraversalUtil'
|
||||
import type { UseNodePacksOptions } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||
|
||||
type WorkflowPack = {
|
||||
id:
|
||||
|
||||
@@ -5,9 +5,7 @@ import { computed, getCurrentInstance, onUnmounted, readonly, ref } from 'vue'
|
||||
import { useInstalledPacks } from '@/composables/nodePack/useInstalledPacks'
|
||||
import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment'
|
||||
import config from '@/config'
|
||||
import { useComfyManagerService } from '@/services/comfyManagerService'
|
||||
import { useComfyRegistryService } from '@/services/comfyRegistryService'
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import { useConflictDetectionStore } from '@/stores/conflictDetectionStore'
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import type { SystemStats } from '@/types'
|
||||
@@ -28,6 +26,8 @@ import {
|
||||
satisfiesVersion,
|
||||
utilCheckVersionCompatibility
|
||||
} from '@/utils/versionUtil'
|
||||
import { useComfyManagerService } from '@/workbench/extensions/manager/services/comfyManagerService'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
|
||||
/**
|
||||
* Composable for conflict detection system.
|
||||
@@ -641,7 +641,9 @@ export function useConflictDetection() {
|
||||
async function initializeConflictDetection(): Promise<void> {
|
||||
try {
|
||||
// Check if manager is new Manager before proceeding
|
||||
const { useManagerState } = await import('@/composables/useManagerState')
|
||||
const { useManagerState } = await import(
|
||||
'@/workbench/extensions/manager/composables/useManagerState'
|
||||
)
|
||||
const managerState = useManagerState()
|
||||
|
||||
if (!managerState.isNewManagerUI.value) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems'
|
||||
import { ManagerUIState, useManagerState } from '@/composables/useManagerState'
|
||||
import { useModelSelectorDialog } from '@/composables/useModelSelectorDialog'
|
||||
import {
|
||||
DEFAULT_DARK_COLOR_PALETTE,
|
||||
@@ -41,12 +40,16 @@ import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore'
|
||||
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
|
||||
import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore'
|
||||
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
||||
import { ManagerTab } from '@/types/comfyManagerTypes'
|
||||
import {
|
||||
getAllNonIoNodesInSubgraph,
|
||||
getExecutionIdsForSelectedNodes
|
||||
} from '@/utils/graphTraversalUtil'
|
||||
import { filterOutputNodes } from '@/utils/nodeFilterUtil'
|
||||
import {
|
||||
ManagerUIState,
|
||||
useManagerState
|
||||
} from '@/workbench/extensions/manager/composables/useManagerState'
|
||||
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||
|
||||
const moveSelectedNodesVersionAdded = '1.22.2'
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import { type ComputedRef, computed, unref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import { useConflictDetectionStore } from '@/stores/conflictDetectionStore'
|
||||
import type { ConflictDetail } from '@/types/conflictDetectionTypes'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
|
||||
/**
|
||||
* Extracting import failed conflicts from conflict list
|
||||
|
||||
@@ -5,9 +5,9 @@ import { computed, ref, watch } from 'vue'
|
||||
import { DEFAULT_PAGE_SIZE } from '@/constants/searchConstants'
|
||||
import { useRegistrySearchGateway } from '@/services/gateway/registrySearchGateway'
|
||||
import type { SearchAttribute } from '@/types/algoliaTypes'
|
||||
import { SortableAlgoliaField } from '@/types/comfyManagerTypes'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import type { QuerySuggestion, SearchMode } from '@/types/searchServiceTypes'
|
||||
import { SortableAlgoliaField } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||
|
||||
type RegistryNodePack = components['schemas']['Node']
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { onUnmounted, ref } from 'vue'
|
||||
|
||||
import type { LogsWsMessage } from '@/schemas/apiSchema'
|
||||
import { api } from '@/scripts/api'
|
||||
import type { components } from '@/types/generatedManagerTypes'
|
||||
import type { components } from '@/workbench/extensions/manager/types/generatedManagerTypes'
|
||||
|
||||
const LOGS_MESSAGE_TYPE = 'logs'
|
||||
const MANAGER_WS_TASK_DONE_NAME = 'cm-task-completed'
|
||||
|
||||
75
src/constants/desktopDialogs.ts
Normal file
75
src/constants/desktopDialogs.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
export interface DialogAction {
|
||||
readonly label: string
|
||||
readonly action: 'openUrl' | 'close' | 'cancel'
|
||||
readonly url?: string
|
||||
readonly severity?: 'danger' | 'primary' | 'secondary' | 'warn'
|
||||
readonly returnValue: string
|
||||
}
|
||||
|
||||
interface DesktopDialog {
|
||||
readonly title: string
|
||||
readonly message: string
|
||||
readonly buttons: DialogAction[]
|
||||
}
|
||||
|
||||
export const DESKTOP_DIALOGS = {
|
||||
/** Shown when a corrupt venv is detected. */
|
||||
reinstallVenv: {
|
||||
title: 'Reinstall ComfyUI (Fresh Start)?',
|
||||
message: `Sorry, we can't launch ComfyUI because some installed packages aren't compatible.
|
||||
|
||||
Click Reinstall to restore ComfyUI and get back up and running.
|
||||
|
||||
Please note: if you've added custom nodes, you'll need to reinstall them after this process.`,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
action: 'openUrl',
|
||||
url: 'https://docs.comfy.org',
|
||||
returnValue: 'openDocs'
|
||||
},
|
||||
{
|
||||
label: 'Reinstall',
|
||||
action: 'close',
|
||||
severity: 'danger',
|
||||
returnValue: 'resetVenv'
|
||||
}
|
||||
]
|
||||
},
|
||||
/** A dialog that is shown when an invalid dialog ID is provided. */
|
||||
invalidDialog: {
|
||||
title: 'Invalid Dialog',
|
||||
message: `Invalid dialog ID was provided.`,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Close',
|
||||
action: 'cancel',
|
||||
returnValue: 'cancel'
|
||||
}
|
||||
]
|
||||
}
|
||||
} as const satisfies { [K: string]: DesktopDialog }
|
||||
|
||||
/** The ID of a desktop dialog. */
|
||||
type DesktopDialogId = keyof typeof DESKTOP_DIALOGS
|
||||
|
||||
/**
|
||||
* Checks if {@link id} is a valid dialog ID.
|
||||
* @param id The string to check
|
||||
* @returns `true` if the ID is a valid dialog ID, otherwise `false`
|
||||
*/
|
||||
function isDialogId(id: unknown): id is DesktopDialogId {
|
||||
return typeof id === 'string' && id in DESKTOP_DIALOGS
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dialog with the given ID.
|
||||
* @param dialogId The ID of the dialog to get
|
||||
* @returns The dialog with the given ID
|
||||
*/
|
||||
export function getDialog(
|
||||
dialogId: string | string[]
|
||||
): DesktopDialog & { id: DesktopDialogId } {
|
||||
const id = isDialogId(dialogId) ? dialogId : 'invalidDialog'
|
||||
return { id, ...structuredClone(DESKTOP_DIALOGS[id]) }
|
||||
}
|
||||
@@ -1,532 +0,0 @@
|
||||
# GraphMutationService Design and Implementation
|
||||
|
||||
## Overview
|
||||
|
||||
GraphMutationService is the centralized service layer for all graph modification operations in ComfyUI Frontend. It provides a unified, command-based API for graph mutations with built-in error handling through the Result pattern, serving as the single entry point for all graph modification operations.
|
||||
|
||||
## Project Background
|
||||
|
||||
### Current System Analysis
|
||||
|
||||
ComfyUI Frontend uses the LiteGraph library for graph operations, with main components including:
|
||||
|
||||
1. **LGraph** (`src/lib/litegraph/src/LGraph.ts`)
|
||||
- Core graph management class
|
||||
- Provides basic operations like `add()`, `remove()`
|
||||
- Supports `beforeChange()`/`afterChange()` transaction mechanism
|
||||
|
||||
2. **LGraphNode** (`src/lib/litegraph/src/LGraphNode.ts`)
|
||||
- Node class containing position, connections, and other properties
|
||||
- Provides methods like `connect()`, `disconnectInput()`, `disconnectOutput()`
|
||||
|
||||
3. **ChangeTracker** (`src/scripts/changeTracker.ts`)
|
||||
- Existing undo/redo system
|
||||
- Snapshot-based history tracking
|
||||
- Supports up to 50 history states
|
||||
|
||||
**Primary Goals:**
|
||||
- Single entry point for all graph modifications via command pattern
|
||||
- Built-in validation and error handling through Result pattern
|
||||
- Transaction support for atomic operations
|
||||
- Natural undo/redo through existing ChangeTracker
|
||||
- Clean architecture for future extensibility (CRDT support ready)
|
||||
- Comprehensive error context preservation
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Command Pattern
|
||||
All operations are executed through a unified command interface:
|
||||
|
||||
```typescript
|
||||
interface GraphMutationOperation {
|
||||
type: string // Operation type identifier
|
||||
timestamp: number // For ordering and CRDT support
|
||||
origin: CommandOrigin // Source of the command
|
||||
params?: any // Operation-specific parameters
|
||||
}
|
||||
```
|
||||
|
||||
### Result Pattern
|
||||
All operations return a discriminated union Result type instead of throwing exceptions:
|
||||
|
||||
```typescript
|
||||
type Result<T, E> =
|
||||
| { success: true; data: T }
|
||||
| { success: false; error: E }
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
Custom error class with rich context:
|
||||
|
||||
```typescript
|
||||
class GraphMutationError extends Error {
|
||||
code: string
|
||||
context: Record<string, any> // Contains operation details and original error
|
||||
}
|
||||
```
|
||||
|
||||
### Interface-Based Architecture
|
||||
|
||||
The GraphMutationService follows an **interface-based design pattern** with singleton state management:
|
||||
|
||||
- **IGraphMutationService Interface**: Defines the complete contract for all graph operations
|
||||
- **GraphMutationService Class**: Implements the interface with LiteGraph integration
|
||||
- **Singleton State**: Shared clipboard and transaction state across components
|
||||
|
||||
```typescript
|
||||
interface IGraphMutationService {
|
||||
// Central command dispatcher
|
||||
applyOperation(
|
||||
operation: GraphMutationOperation
|
||||
): Promise<Result<any, GraphMutationError>>
|
||||
|
||||
// Direct operation methods (all return Result types)
|
||||
createNode(params: createNodeParams): Promise<Result<NodeId, GraphMutationError>>
|
||||
removeNode(nodeId: NodeId): Promise<Result<void, GraphMutationError>>
|
||||
// ... 40+ total operations
|
||||
|
||||
// Undo/Redo
|
||||
undo(): Promise<Result<void, GraphMutationError>>
|
||||
redo(): Promise<Result<void, GraphMutationError>>
|
||||
}
|
||||
```
|
||||
|
||||
### Core Components
|
||||
|
||||
```typescript
|
||||
// Implementation Class
|
||||
class GraphMutationService implements IGraphMutationService {
|
||||
private workflowStore = useWorkflowStore()
|
||||
private static readonly CLIPBOARD_KEY = 'litegrapheditor_clipboard'
|
||||
|
||||
// Command dispatcher
|
||||
async applyOperation(
|
||||
operation: GraphMutationOperation
|
||||
): Promise<Result<any, GraphMutationError>> {
|
||||
switch (operation.type) {
|
||||
case 'createNode':
|
||||
return await this.createNode(operation.params)
|
||||
case 'removeNode':
|
||||
return await this.removeNode(operation.params)
|
||||
// ... handle all operation types
|
||||
default:
|
||||
return {
|
||||
success: false,
|
||||
error: new GraphMutationError('Unknown operation type', {
|
||||
operation: operation.type
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All operations wrapped with error handling
|
||||
async createNode(params: createNodeParams): Promise<Result<NodeId, GraphMutationError>> {
|
||||
try {
|
||||
const graph = this.getGraph()
|
||||
graph.beforeChange()
|
||||
// ... perform operation
|
||||
graph.afterChange()
|
||||
return { success: true, data: nodeId }
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: new GraphMutationError('Failed to create node', {
|
||||
operation: 'createNode',
|
||||
params,
|
||||
cause: error
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton Hook
|
||||
export const useGraphMutationService = (): IGraphMutationService => {
|
||||
if (!graphMutationServiceInstance) {
|
||||
graphMutationServiceInstance = new GraphMutationService()
|
||||
}
|
||||
return graphMutationServiceInstance
|
||||
}
|
||||
```
|
||||
|
||||
## Implemented Operations
|
||||
|
||||
### Node Operations (8 operations)
|
||||
|
||||
| Operation | Description | Result Type |
|
||||
|-----------|-------------|-------------|
|
||||
| `createNode` | Create a new node in the graph | `Result<NodeId, GraphMutationError>` |
|
||||
| `removeNode` | Remove a node from the graph | `Result<void, GraphMutationError>` |
|
||||
| `updateNodeProperty` | Update a custom node property | `Result<void, GraphMutationError>` |
|
||||
| `updateNodeTitle` | Change the node's title | `Result<void, GraphMutationError>` |
|
||||
| `changeNodeMode` | Change execution mode (ALWAYS/BYPASS/etc) | `Result<void, GraphMutationError>` |
|
||||
| `cloneNode` | Create a copy of a node | `Result<NodeId, GraphMutationError>` |
|
||||
| `bypassNode` | Set node to bypass mode | `Result<void, GraphMutationError>` |
|
||||
| `unbypassNode` | Remove bypass mode from node | `Result<void, GraphMutationError>` |
|
||||
|
||||
### Connection Operations (3 operations)
|
||||
|
||||
| Operation | Description | Result Type |
|
||||
|-----------|-------------|-------------|
|
||||
| `connect` | Create a connection between nodes | `Result<LinkId, GraphMutationError>` |
|
||||
| `disconnect` | Disconnect a node input/output slot | `Result<boolean, GraphMutationError>` |
|
||||
| `disconnectLink` | Disconnect by link ID | `Result<void, GraphMutationError>` |
|
||||
|
||||
### Group Operations (5 operations)
|
||||
|
||||
| Operation | Description | Result Type |
|
||||
|-----------|-------------|-------------|
|
||||
| `createGroup` | Create a new node group | `Result<GroupId, GraphMutationError>` |
|
||||
| `removeGroup` | Delete a group (nodes remain) | `Result<void, GraphMutationError>` |
|
||||
| `updateGroupTitle` | Change group title | `Result<void, GraphMutationError>` |
|
||||
| `addNodesToGroup` | Add nodes to group and auto-resize | `Result<void, GraphMutationError>` |
|
||||
| `recomputeGroupNodes` | Recalculate which nodes are in group | `Result<void, GraphMutationError>` |
|
||||
|
||||
### Clipboard Operations (3 operations)
|
||||
|
||||
| Operation | Description | Result Type |
|
||||
|-----------|-------------|-------------|
|
||||
| `copyNodes` | Copy nodes to clipboard | `Result<void, GraphMutationError>` |
|
||||
| `cutNodes` | Cut nodes to clipboard | `Result<void, GraphMutationError>` |
|
||||
| `pasteNodes` | Paste nodes from clipboard | `Result<NodeId[], GraphMutationError>` |
|
||||
|
||||
### Reroute Operations (2 operations)
|
||||
|
||||
| Operation | Description | Result Type |
|
||||
|-----------|-------------|-------------|
|
||||
| `addReroute` | Add a reroute point on a connection | `Result<RerouteId, GraphMutationError>` |
|
||||
| `removeReroute` | Remove a reroute point | `Result<void, GraphMutationError>` |
|
||||
|
||||
### Subgraph Operations (10 operations)
|
||||
|
||||
| Operation | Description | Result Type |
|
||||
|-----------|-------------|-------------|
|
||||
| `createSubgraph` | Create a subgraph from selected items | `Result<{subgraph, node}, GraphMutationError>` |
|
||||
| `unpackSubgraph` | Unpack a subgraph node back into regular nodes | `Result<void, GraphMutationError>` |
|
||||
| `addSubgraphNodeInput` | Add input slot to subgraph node | `Result<number, GraphMutationError>` |
|
||||
| `addSubgraphNodeOutput` | Add output slot to subgraph node | `Result<number, GraphMutationError>` |
|
||||
| `removeSubgraphNodeInput` | Remove input slot from subgraph node | `Result<void, GraphMutationError>` |
|
||||
| `removeSubgraphNodeOutput` | Remove output slot from subgraph node | `Result<void, GraphMutationError>` |
|
||||
| `addSubgraphInput` | Add an input to a subgraph | `Result<void, GraphMutationError>` |
|
||||
| `addSubgraphOutput` | Add an output to a subgraph | `Result<void, GraphMutationError>` |
|
||||
| `removeSubgraphInput` | Remove a subgraph input | `Result<void, GraphMutationError>` |
|
||||
| `removeSubgraphOutput` | Remove a subgraph output | `Result<void, GraphMutationError>` |
|
||||
|
||||
### Graph-level Operations (1 operation)
|
||||
|
||||
| Operation | Description | Result Type |
|
||||
|-----------|-------------|-------------|
|
||||
| `clearGraph` | Clear all nodes and connections | `Result<void, GraphMutationError>` |
|
||||
|
||||
### History Operations (2 operations)
|
||||
|
||||
| Operation | Description | Result Type |
|
||||
|-----------|-------------|-------------|
|
||||
| `undo` | Undo the last operation | `Result<void, GraphMutationError>` |
|
||||
| `redo` | Redo the previously undone operation | `Result<void, GraphMutationError>` |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Command Pattern Usage
|
||||
|
||||
```typescript
|
||||
import { useGraphMutationService, CommandOrigin } from '@/core/graph/operations'
|
||||
import type { GraphMutationOperation } from '@/core/graph/operations/types'
|
||||
|
||||
const service = useGraphMutationService()
|
||||
|
||||
// Execute operations via command pattern
|
||||
const operation: GraphMutationOperation = {
|
||||
type: 'createNode',
|
||||
timestamp: Date.now(),
|
||||
origin: CommandOrigin.Local,
|
||||
params: {
|
||||
type: 'LoadImage',
|
||||
title: 'My Image Loader',
|
||||
properties: { seed: 12345 }
|
||||
}
|
||||
}
|
||||
|
||||
const result = await service.applyOperation(operation)
|
||||
|
||||
if (result.success) {
|
||||
console.log('Node created with ID:', result.data)
|
||||
} else {
|
||||
console.error('Failed:', result.error.message)
|
||||
console.error('Context:', result.error.context)
|
||||
}
|
||||
```
|
||||
|
||||
### Direct Method Usage with Result Pattern
|
||||
|
||||
```typescript
|
||||
// All methods return Result<T, GraphMutationError>
|
||||
const result = await service.createNode({
|
||||
type: 'LoadImage',
|
||||
title: 'Image Loader'
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
const nodeId = result.data
|
||||
|
||||
// Update node properties
|
||||
const updateResult = await service.updateNodeProperty({
|
||||
nodeId,
|
||||
property: 'seed',
|
||||
value: 12345
|
||||
})
|
||||
|
||||
if (!updateResult.success) {
|
||||
console.error('Update failed:', updateResult.error)
|
||||
}
|
||||
} else {
|
||||
// Access detailed error context
|
||||
const { operation, params, cause } = result.error.context
|
||||
console.error(`Operation ${operation} failed:`, cause)
|
||||
}
|
||||
```
|
||||
|
||||
### Connection Management
|
||||
|
||||
```typescript
|
||||
// Create a connection
|
||||
const connectOp: GraphMutationOperation = {
|
||||
type: 'connect',
|
||||
timestamp: Date.now(),
|
||||
origin: CommandOrigin.Local,
|
||||
params: {
|
||||
sourceNodeId: node1Id,
|
||||
sourceSlot: 0,
|
||||
targetNodeId: node2Id,
|
||||
targetSlot: 0
|
||||
}
|
||||
}
|
||||
|
||||
const result = await service.applyOperation(connectOp)
|
||||
|
||||
if (result.success) {
|
||||
const linkId = result.data
|
||||
|
||||
// Later disconnect by link ID
|
||||
const disconnectResult = await service.disconnectLink(linkId)
|
||||
|
||||
if (!disconnectResult.success) {
|
||||
console.error('Disconnect failed:', disconnectResult.error)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Group Management
|
||||
|
||||
```typescript
|
||||
// Create a group via command
|
||||
const createGroupOp: GraphMutationOperation = {
|
||||
type: 'createGroup',
|
||||
timestamp: Date.now(),
|
||||
origin: CommandOrigin.Local,
|
||||
params: {
|
||||
title: 'Image Processing',
|
||||
size: [400, 300],
|
||||
color: '#335577'
|
||||
}
|
||||
}
|
||||
|
||||
const groupResult = await service.applyOperation(createGroupOp)
|
||||
|
||||
if (groupResult.success) {
|
||||
const groupId = groupResult.data
|
||||
|
||||
// Add nodes to group
|
||||
const addNodesResult = await service.addNodesToGroup({
|
||||
groupId,
|
||||
nodeIds: [node1Id, node2Id]
|
||||
})
|
||||
|
||||
if (!addNodesResult.success) {
|
||||
console.error('Failed to add nodes:', addNodesResult.error)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Clipboard Operations
|
||||
|
||||
```typescript
|
||||
// Copy nodes
|
||||
const copyResult = await service.copyNodes([node1Id, node2Id])
|
||||
|
||||
if (copyResult.success) {
|
||||
// Paste at a different location
|
||||
const pasteResult = await service.pasteNodes()
|
||||
|
||||
if (pasteResult.success) {
|
||||
console.log('Pasted nodes:', pasteResult.data)
|
||||
} else {
|
||||
console.error('Paste failed:', pasteResult.error)
|
||||
}
|
||||
}
|
||||
|
||||
// Cut operation
|
||||
const cutResult = await service.cutNodes([node3Id])
|
||||
// Original nodes marked for deletion after paste
|
||||
```
|
||||
|
||||
### Error Context Preservation
|
||||
|
||||
```typescript
|
||||
const result = await service.updateNodeProperty({
|
||||
nodeId: 'invalid-node',
|
||||
property: 'seed',
|
||||
value: 12345
|
||||
})
|
||||
|
||||
if (!result.success) {
|
||||
// Rich error context available
|
||||
console.error('Error:', result.error.message)
|
||||
console.error('Code:', result.error.code)
|
||||
console.error('Operation:', result.error.context.operation)
|
||||
console.error('Parameters:', result.error.context.params)
|
||||
console.error('Original error:', result.error.context.cause)
|
||||
}
|
||||
```
|
||||
|
||||
### Subgraph Operations
|
||||
|
||||
```typescript
|
||||
// Create subgraph from selected items
|
||||
const subgraphOp: GraphMutationOperation = {
|
||||
type: 'createSubgraph',
|
||||
timestamp: Date.now(),
|
||||
origin: CommandOrigin.Local,
|
||||
params: {
|
||||
selectedItems: new Set([node1, node2, node3])
|
||||
}
|
||||
}
|
||||
|
||||
const result = await service.applyOperation(subgraphOp)
|
||||
|
||||
if (result.success) {
|
||||
const { subgraph, node } = result.data
|
||||
|
||||
// Add I/O to subgraph
|
||||
await service.addSubgraphInput({
|
||||
subgraphId: subgraph.id,
|
||||
name: 'image',
|
||||
type: 'IMAGE'
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### History Operations
|
||||
|
||||
```typescript
|
||||
// All operations are undoable
|
||||
const result = await service.undo()
|
||||
|
||||
if (result.success) {
|
||||
console.log('Undo successful')
|
||||
} else {
|
||||
console.error('Undo failed:', result.error.message)
|
||||
// Might fail if no history or change tracker unavailable
|
||||
}
|
||||
|
||||
// Redo
|
||||
const redoResult = await service.redo()
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Integration Points
|
||||
|
||||
1. **LiteGraph Integration**
|
||||
- Uses `app.graph` for graph access
|
||||
- Calls `beforeChange()`/`afterChange()` for all mutations
|
||||
- Integrates with existing LiteGraph node/connection APIs
|
||||
|
||||
2. **ChangeTracker Integration**
|
||||
- Maintains compatibility with existing undo/redo system
|
||||
- Transactions wrapped with `beforeChange()`/`afterChange()`
|
||||
- No longer calls `checkState()` directly (removed from new implementation)
|
||||
|
||||
3. **Error Handling**
|
||||
- All operations wrapped in try-catch blocks
|
||||
- Errors converted to GraphMutationError with context
|
||||
- Original errors preserved in context.cause
|
||||
|
||||
## Technical Decisions
|
||||
|
||||
### Why Command Pattern?
|
||||
- **Uniformity**: Single entry point for all operations
|
||||
- **Extensibility**: Easy to add new operations
|
||||
- **CRDT Ready**: Commands include timestamp and origin for future sync
|
||||
- **Testing**: Easy to test command dispatch and execution
|
||||
|
||||
### Why Result Pattern?
|
||||
- **Explicit Error Handling**: Forces consumers to handle errors
|
||||
- **No Exceptions**: Predictable control flow
|
||||
- **Rich Context**: Errors carry operation context
|
||||
- **Type Safety**: TypeScript discriminated unions
|
||||
|
||||
### Why GraphMutationError?
|
||||
- **Context Preservation**: Maintains full operation context
|
||||
- **Debugging**: Detailed information for troubleshooting
|
||||
- **Standardization**: Consistent error structure
|
||||
- **Traceability**: Links errors to specific operations
|
||||
|
||||
## Related Files
|
||||
|
||||
- **Interface Definition**: `src/core/graph/operations/IGraphMutationService.ts`
|
||||
- **Implementation**: `src/core/graph/operations/graphMutationService.ts`
|
||||
- **Types**: `src/core/graph/operations/types.ts`
|
||||
- **Error Class**: `src/core/graph/operations/GraphMutationError.ts`
|
||||
- **Tests**: `tests-ui/tests/services/graphMutationService.test.ts`
|
||||
- **LiteGraph Core**: `src/lib/litegraph/src/LGraph.ts`
|
||||
- **Node Implementation**: `src/lib/litegraph/src/LGraphNode.ts`
|
||||
- **Change Tracking**: `src/scripts/changeTracker.ts`
|
||||
|
||||
## Implementation Compatibility Notes
|
||||
|
||||
### Critical Implementation Details to Maintain:
|
||||
|
||||
1. **beforeChange/afterChange Pattern**
|
||||
- All mutations MUST be wrapped with `graph.beforeChange()` and `graph.afterChange()`
|
||||
- This enables undo/redo functionality through ChangeTracker
|
||||
- Reference: Pattern used consistently throughout service
|
||||
|
||||
2. **Node ID Management**
|
||||
- Node IDs use NodeId type from schemas
|
||||
- Custom IDs can be provided during creation (for workflow loading)
|
||||
|
||||
3. **Clipboard Implementation**
|
||||
- Uses localStorage with key 'litegrapheditor_clipboard'
|
||||
- Maintains node connections during copy/paste
|
||||
- Cut operation marks nodes for deletion after paste
|
||||
|
||||
4. **Group Management**
|
||||
- Groups auto-resize when adding nodes using `recomputeInsideNodes()`
|
||||
- Visual operations call `graph.setDirtyCanvas(true, false)`
|
||||
|
||||
5. **Error Handling**
|
||||
- All operations return Result<T, GraphMutationError>
|
||||
- Never throw exceptions from public methods
|
||||
- Preserve original error in context.cause
|
||||
|
||||
6. **Subgraph Support**
|
||||
- Uses instanceof checks for SubgraphNode detection
|
||||
- Iterates through graph._nodes to find subgraphs
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
1. Replace direct graph method calls with service operations
|
||||
2. Update error handling from try-catch to Result pattern checking
|
||||
3. Convert operation calls to use command pattern where beneficial
|
||||
4. Leverage error context for better debugging
|
||||
5. Ensure all operations maintain existing beforeChange/afterChange patterns
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Always use GraphMutationService** - Never call graph methods directly
|
||||
2. **Handle Result types** - Check success before using data
|
||||
3. **Preserve error context** - Log full error context for debugging
|
||||
4. **Command pattern ready** - Can easily add CRDT sync in future
|
||||
5. **Performance** - Result pattern and command recording have minimal overhead
|
||||
6. **Type safety** - Use TypeScript types for all operations
|
||||
@@ -1,14 +0,0 @@
|
||||
export class GraphMutationError extends Error {
|
||||
public readonly code: string
|
||||
public readonly context: Record<string, any>
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
context: Record<string, any>,
|
||||
code = 'GRAPH_MUTATION_ERROR'
|
||||
) {
|
||||
super(message)
|
||||
this.code = code
|
||||
this.context = context
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
import type { GraphMutationError } from '@/core/graph/operations/GraphMutationError'
|
||||
import type {
|
||||
AddNodeInputParams,
|
||||
AddNodeOutputParams,
|
||||
AddNodesToGroupParams,
|
||||
AddRerouteParams,
|
||||
ChangeNodeModeParams,
|
||||
ConnectParams,
|
||||
CreateGroupParams,
|
||||
CreateNodeParams,
|
||||
CreateSubgraphParams,
|
||||
CreateSubgraphResult,
|
||||
DisconnectParams,
|
||||
GraphMutationOperation,
|
||||
NodeInputSlotParams,
|
||||
OperationResultType,
|
||||
Result,
|
||||
SubgraphIndexParams,
|
||||
SubgraphNameTypeParams,
|
||||
UpdateGroupTitleParams,
|
||||
UpdateNodePropertyParams,
|
||||
UpdateNodeTitleParams
|
||||
} from '@/core/graph/operations/types'
|
||||
import type { GroupId } from '@/lib/litegraph/src/LGraphGroup'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||
import type { LinkId } from '@/lib/litegraph/src/LLink'
|
||||
import type { RerouteId } from '@/lib/litegraph/src/Reroute'
|
||||
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
|
||||
|
||||
export interface IGraphMutationService {
|
||||
applyOperation<T extends GraphMutationOperation>(
|
||||
operation: T
|
||||
): Promise<Result<OperationResultType<T>, GraphMutationError>>
|
||||
|
||||
createNode(
|
||||
params: CreateNodeParams
|
||||
): Promise<Result<NodeId, GraphMutationError>>
|
||||
|
||||
getNodeById(nodeId: NodeId): Promise<Result<LGraphNode, GraphMutationError>>
|
||||
|
||||
removeNode(nodeId: NodeId): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
updateNodeProperty(
|
||||
params: UpdateNodePropertyParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
updateNodeTitle(
|
||||
params: UpdateNodeTitleParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
changeNodeMode(
|
||||
params: ChangeNodeModeParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
cloneNode(nodeId: NodeId): Promise<Result<NodeId, GraphMutationError>>
|
||||
|
||||
connect(params: ConnectParams): Promise<Result<LinkId, GraphMutationError>>
|
||||
|
||||
disconnect(
|
||||
params: DisconnectParams
|
||||
): Promise<Result<boolean, GraphMutationError>>
|
||||
|
||||
disconnectLink(linkId: LinkId): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
createGroup(
|
||||
params: CreateGroupParams
|
||||
): Promise<Result<GroupId, GraphMutationError>>
|
||||
|
||||
removeGroup(groupId: GroupId): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
updateGroupTitle(
|
||||
params: UpdateGroupTitleParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
addNodesToGroup(
|
||||
params: AddNodesToGroupParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
recomputeGroupNodes(
|
||||
groupId: GroupId
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
addReroute(
|
||||
params: AddRerouteParams
|
||||
): Promise<Result<RerouteId, GraphMutationError>>
|
||||
|
||||
removeReroute(rerouteId: RerouteId): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
copyNodes(nodeIds: NodeId[]): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
cutNodes(nodeIds: NodeId[]): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
pasteNodes(): Promise<Result<NodeId[], GraphMutationError>>
|
||||
|
||||
addSubgraphNodeInput(
|
||||
params: AddNodeInputParams
|
||||
): Promise<Result<number, GraphMutationError>>
|
||||
|
||||
addSubgraphNodeOutput(
|
||||
params: AddNodeOutputParams
|
||||
): Promise<Result<number, GraphMutationError>>
|
||||
|
||||
removeSubgraphNodeInput(
|
||||
params: NodeInputSlotParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
removeSubgraphNodeOutput(
|
||||
params: NodeInputSlotParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
createSubgraph(
|
||||
params: CreateSubgraphParams
|
||||
): Promise<Result<CreateSubgraphResult, GraphMutationError>>
|
||||
|
||||
unpackSubgraph(
|
||||
subgraphNodeId: NodeId
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
addSubgraphInput(
|
||||
params: SubgraphNameTypeParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
addSubgraphOutput(
|
||||
params: SubgraphNameTypeParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
removeSubgraphInput(
|
||||
params: SubgraphIndexParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
removeSubgraphOutput(
|
||||
params: SubgraphIndexParams
|
||||
): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
clearGraph(): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
bypassNode(nodeId: NodeId): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
unbypassNode(nodeId: NodeId): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
undo(): Promise<Result<void, GraphMutationError>>
|
||||
|
||||
redo(): Promise<Result<void, GraphMutationError>>
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,367 +0,0 @@
|
||||
/**
|
||||
* Graph Mutation Command System - Type Definitions
|
||||
*
|
||||
* Defines command types for graph mutation operations with CRDT support.
|
||||
* Each command represents an atomic operation that can be applied, undone, and synchronized.
|
||||
*/
|
||||
import type { Subgraph } from '@/lib/litegraph/src/LGraph'
|
||||
import type { GroupId, LGraphGroup } from '@/lib/litegraph/src/LGraphGroup'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||
import type { LinkId } from '@/lib/litegraph/src/LLink'
|
||||
import type { RerouteId } from '@/lib/litegraph/src/Reroute'
|
||||
import type {
|
||||
SubgraphId,
|
||||
SubgraphNode
|
||||
} from '@/lib/litegraph/src/subgraph/SubgraphNode'
|
||||
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
|
||||
|
||||
export type Result<T, E> =
|
||||
| { success: true; data: T }
|
||||
| { success: false; error: E }
|
||||
|
||||
export interface CreateNodeParams {
|
||||
type: string
|
||||
properties?: Record<string, string | number | boolean | object>
|
||||
title?: string
|
||||
}
|
||||
|
||||
export interface UpdateNodePropertyParams {
|
||||
nodeId: NodeId
|
||||
property: string
|
||||
value: string | number | boolean | object | string[] | undefined
|
||||
}
|
||||
|
||||
export interface UpdateNodeTitleParams {
|
||||
nodeId: NodeId
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface ChangeNodeModeParams {
|
||||
nodeId: NodeId
|
||||
mode: number
|
||||
}
|
||||
|
||||
export interface ConnectParams {
|
||||
sourceNodeId: NodeId
|
||||
sourceSlot: number | string
|
||||
targetNodeId: NodeId
|
||||
targetSlot: number | string
|
||||
}
|
||||
|
||||
export interface DisconnectParams {
|
||||
nodeId: NodeId
|
||||
slot: number | string
|
||||
slotType: 'input' | 'output'
|
||||
targetNodeId?: NodeId
|
||||
}
|
||||
|
||||
export interface CreateGroupParams {
|
||||
title?: string
|
||||
size?: [number, number]
|
||||
color?: string
|
||||
fontSize?: number
|
||||
}
|
||||
|
||||
export interface UpdateGroupTitleParams {
|
||||
groupId: GroupId
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface AddNodesToGroupParams {
|
||||
groupId: GroupId
|
||||
nodeIds: NodeId[]
|
||||
}
|
||||
|
||||
export interface AddRerouteParams {
|
||||
pos: [number, number]
|
||||
linkId?: LinkId
|
||||
parentRerouteId?: RerouteId
|
||||
}
|
||||
|
||||
export interface AddNodeInputParams {
|
||||
nodeId: NodeId
|
||||
name: string
|
||||
type: string
|
||||
extra_info?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface AddNodeOutputParams {
|
||||
nodeId: NodeId
|
||||
name: string
|
||||
type: string
|
||||
extra_info?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface CreateSubgraphParams {
|
||||
selectedItems: Set<LGraphNode | LGraphGroup>
|
||||
}
|
||||
|
||||
export interface NodeInputSlotParams {
|
||||
nodeId: NodeId
|
||||
slot: number
|
||||
}
|
||||
|
||||
export interface SubgraphNameTypeParams {
|
||||
subgraphId: SubgraphId
|
||||
name: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export interface SubgraphIndexParams {
|
||||
subgraphId: SubgraphId
|
||||
index: number
|
||||
}
|
||||
|
||||
export enum CommandOrigin {
|
||||
Local = 'local'
|
||||
}
|
||||
|
||||
export type GraphMutationOperation =
|
||||
| CreateNodeCommand
|
||||
| RemoveNodeCommand
|
||||
| UpdateNodePropertyCommand
|
||||
| UpdateNodeTitleCommand
|
||||
| ChangeNodeModeCommand
|
||||
| CloneNodeCommand
|
||||
| BypassNodeCommand
|
||||
| UnbypassNodeCommand
|
||||
| ConnectCommand
|
||||
| DisconnectCommand
|
||||
| DisconnectLinkCommand
|
||||
| CreateGroupCommand
|
||||
| RemoveGroupCommand
|
||||
| UpdateGroupTitleCommand
|
||||
| AddNodesToGroupCommand
|
||||
| RecomputeGroupNodesCommand
|
||||
| AddRerouteCommand
|
||||
| RemoveRerouteCommand
|
||||
| CopyNodesCommand
|
||||
| CutNodesCommand
|
||||
| PasteNodesCommand
|
||||
| CreateSubgraphCommand
|
||||
| UnpackSubgraphCommand
|
||||
| AddSubgraphNodeInputCommand
|
||||
| AddSubgraphNodeOutputCommand
|
||||
| RemoveSubgraphNodeInputCommand
|
||||
| RemoveSubgraphNodeOutputCommand
|
||||
| AddSubgraphInputCommand
|
||||
| AddSubgraphOutputCommand
|
||||
| RemoveSubgraphInputCommand
|
||||
| RemoveSubgraphOutputCommand
|
||||
| ClearGraphCommand
|
||||
| UndoCommand
|
||||
| RedoCommand
|
||||
|
||||
interface GraphOpBase {
|
||||
/** Timestamp for ordering commands */
|
||||
timestamp: number
|
||||
/** Origin of the command */
|
||||
origin: CommandOrigin
|
||||
}
|
||||
|
||||
export interface CreateNodeCommand extends GraphOpBase {
|
||||
type: 'createNode'
|
||||
params: CreateNodeParams
|
||||
}
|
||||
|
||||
export interface RemoveNodeCommand extends GraphOpBase {
|
||||
type: 'removeNode'
|
||||
params: NodeId
|
||||
}
|
||||
|
||||
export interface UpdateNodePropertyCommand extends GraphOpBase {
|
||||
type: 'updateNodeProperty'
|
||||
params: UpdateNodePropertyParams
|
||||
}
|
||||
|
||||
export interface UpdateNodeTitleCommand extends GraphOpBase {
|
||||
type: 'updateNodeTitle'
|
||||
params: UpdateNodeTitleParams
|
||||
}
|
||||
|
||||
export interface ChangeNodeModeCommand extends GraphOpBase {
|
||||
type: 'changeNodeMode'
|
||||
params: ChangeNodeModeParams
|
||||
}
|
||||
|
||||
export interface CloneNodeCommand extends GraphOpBase {
|
||||
type: 'cloneNode'
|
||||
params: NodeId
|
||||
}
|
||||
|
||||
export interface BypassNodeCommand extends GraphOpBase {
|
||||
type: 'bypassNode'
|
||||
params: NodeId
|
||||
}
|
||||
|
||||
export interface UnbypassNodeCommand extends GraphOpBase {
|
||||
type: 'unbypassNode'
|
||||
params: NodeId
|
||||
}
|
||||
|
||||
export interface ConnectCommand extends GraphOpBase {
|
||||
type: 'connect'
|
||||
params: ConnectParams
|
||||
}
|
||||
|
||||
export interface DisconnectCommand extends GraphOpBase {
|
||||
type: 'disconnect'
|
||||
params: DisconnectParams
|
||||
}
|
||||
|
||||
export interface DisconnectLinkCommand extends GraphOpBase {
|
||||
type: 'disconnectLink'
|
||||
params: LinkId
|
||||
}
|
||||
|
||||
export interface CreateGroupCommand extends GraphOpBase {
|
||||
type: 'createGroup'
|
||||
params: CreateGroupParams
|
||||
}
|
||||
|
||||
export interface RemoveGroupCommand extends GraphOpBase {
|
||||
type: 'removeGroup'
|
||||
params: GroupId
|
||||
}
|
||||
|
||||
export interface UpdateGroupTitleCommand extends GraphOpBase {
|
||||
type: 'updateGroupTitle'
|
||||
params: UpdateGroupTitleParams
|
||||
}
|
||||
|
||||
export interface AddNodesToGroupCommand extends GraphOpBase {
|
||||
type: 'addNodesToGroup'
|
||||
params: AddNodesToGroupParams
|
||||
}
|
||||
|
||||
export interface RecomputeGroupNodesCommand extends GraphOpBase {
|
||||
type: 'recomputeGroupNodes'
|
||||
params: GroupId
|
||||
}
|
||||
|
||||
// Reroute Commands
|
||||
export interface AddRerouteCommand extends GraphOpBase {
|
||||
type: 'addReroute'
|
||||
params: AddRerouteParams
|
||||
}
|
||||
|
||||
export interface RemoveRerouteCommand extends GraphOpBase {
|
||||
type: 'removeReroute'
|
||||
params: RerouteId
|
||||
}
|
||||
|
||||
export interface CopyNodesCommand extends GraphOpBase {
|
||||
type: 'copyNodes'
|
||||
nodeIds: NodeId[]
|
||||
}
|
||||
|
||||
export interface CutNodesCommand extends GraphOpBase {
|
||||
type: 'cutNodes'
|
||||
params: NodeId[]
|
||||
}
|
||||
|
||||
export interface PasteNodesCommand extends GraphOpBase {
|
||||
type: 'pasteNodes'
|
||||
}
|
||||
|
||||
export interface CreateSubgraphCommand extends GraphOpBase {
|
||||
type: 'createSubgraph'
|
||||
params: CreateSubgraphParams
|
||||
}
|
||||
|
||||
export interface UnpackSubgraphCommand extends GraphOpBase {
|
||||
type: 'unpackSubgraph'
|
||||
params: NodeId
|
||||
}
|
||||
|
||||
export interface AddSubgraphNodeInputCommand extends GraphOpBase {
|
||||
type: 'addSubgraphNodeInput'
|
||||
params: AddNodeInputParams
|
||||
}
|
||||
|
||||
export interface AddSubgraphNodeOutputCommand extends GraphOpBase {
|
||||
type: 'addSubgraphNodeOutput'
|
||||
params: AddNodeOutputParams
|
||||
}
|
||||
|
||||
export interface RemoveSubgraphNodeInputCommand extends GraphOpBase {
|
||||
type: 'removeSubgraphNodeInput'
|
||||
params: NodeInputSlotParams
|
||||
}
|
||||
|
||||
export interface RemoveSubgraphNodeOutputCommand extends GraphOpBase {
|
||||
type: 'removeSubgraphNodeOutput'
|
||||
params: NodeInputSlotParams
|
||||
}
|
||||
|
||||
export interface AddSubgraphInputCommand extends GraphOpBase {
|
||||
type: 'addSubgraphInput'
|
||||
params: SubgraphNameTypeParams
|
||||
}
|
||||
|
||||
export interface AddSubgraphOutputCommand extends GraphOpBase {
|
||||
type: 'addSubgraphOutput'
|
||||
params: SubgraphNameTypeParams
|
||||
}
|
||||
|
||||
export interface RemoveSubgraphInputCommand extends GraphOpBase {
|
||||
type: 'removeSubgraphInput'
|
||||
params: SubgraphIndexParams
|
||||
}
|
||||
|
||||
export interface RemoveSubgraphOutputCommand extends GraphOpBase {
|
||||
type: 'removeSubgraphOutput'
|
||||
params: SubgraphIndexParams
|
||||
}
|
||||
|
||||
export interface ClearGraphCommand extends GraphOpBase {
|
||||
type: 'clearGraph'
|
||||
}
|
||||
|
||||
export interface UndoCommand extends GraphOpBase {
|
||||
type: 'undo'
|
||||
}
|
||||
|
||||
export interface RedoCommand extends GraphOpBase {
|
||||
type: 'redo'
|
||||
}
|
||||
|
||||
export type NodeIdReturnOperations = CreateNodeCommand | CloneNodeCommand
|
||||
|
||||
export type LinkIdReturnOperations = ConnectCommand
|
||||
|
||||
export type BooleanReturnOperations = DisconnectCommand
|
||||
|
||||
export type GroupIdReturnOperations = CreateGroupCommand
|
||||
|
||||
export type RerouteIdReturnOperations = AddRerouteCommand
|
||||
|
||||
export type NodeIdArrayReturnOperations = PasteNodesCommand
|
||||
|
||||
export type NumberReturnOperations =
|
||||
| AddSubgraphNodeInputCommand
|
||||
| AddSubgraphNodeOutputCommand
|
||||
|
||||
export interface CreateSubgraphResult {
|
||||
subgraph: Subgraph
|
||||
node: SubgraphNode
|
||||
}
|
||||
|
||||
export type OperationResultType<T extends GraphMutationOperation> =
|
||||
T extends NodeIdReturnOperations
|
||||
? NodeId
|
||||
: T extends LinkIdReturnOperations
|
||||
? LinkId
|
||||
: T extends BooleanReturnOperations
|
||||
? boolean
|
||||
: T extends GroupIdReturnOperations
|
||||
? GroupId
|
||||
: T extends RerouteIdReturnOperations
|
||||
? RerouteId
|
||||
: T extends NodeIdArrayReturnOperations
|
||||
? NodeId[]
|
||||
: T extends NumberReturnOperations
|
||||
? number
|
||||
: T extends CreateSubgraphCommand
|
||||
? CreateSubgraphResult
|
||||
: void
|
||||
@@ -11,16 +11,16 @@ import {
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import {
|
||||
ComfyLink,
|
||||
ComfyNode,
|
||||
ComfyWorkflowJSON
|
||||
type ComfyLink,
|
||||
type ComfyNode,
|
||||
type ComfyWorkflowJSON
|
||||
} from '@/platform/workflow/validation/schemas/workflowSchema'
|
||||
import type { ComfyNodeDef } from '@/schemas/nodeDefSchema'
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
import { useExecutionStore } from '@/stores/executionStore'
|
||||
import { useNodeDefStore } from '@/stores/nodeDefStore'
|
||||
import { useWidgetStore } from '@/stores/widgetStore'
|
||||
import { ComfyExtension } from '@/types/comfy'
|
||||
import { type ComfyExtension } from '@/types/comfy'
|
||||
import { ExecutableGroupNodeChildDTO } from '@/utils/executableGroupNodeChildDTO'
|
||||
import { GROUP } from '@/utils/executableGroupNodeDto'
|
||||
import { deserialiseAndCreate, serialise } from '@/utils/vintageClipboard'
|
||||
|
||||
@@ -9,7 +9,7 @@ import Load3dUtils from '@/extensions/core/load3d/Load3dUtils'
|
||||
import { t } from '@/i18n'
|
||||
import type { IStringWidget } from '@/lib/litegraph/src/types/widgets'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import { CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import { type CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import { api } from '@/scripts/api'
|
||||
import { ComfyApp, app } from '@/scripts/app'
|
||||
import { ComponentWidgetImpl, addWidget } from '@/scripts/domWidget'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import {
|
||||
AnimationItem,
|
||||
AnimationManagerInterface,
|
||||
EventManagerInterface
|
||||
type AnimationItem,
|
||||
type AnimationManagerInterface,
|
||||
type EventManagerInterface
|
||||
} from '@/extensions/core/load3d/interfaces'
|
||||
|
||||
export class AnimationManager implements AnimationManagerInterface {
|
||||
|
||||
@@ -2,11 +2,11 @@ import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
|
||||
import {
|
||||
CameraManagerInterface,
|
||||
CameraState,
|
||||
CameraType,
|
||||
EventManagerInterface,
|
||||
NodeStorageInterface
|
||||
type CameraManagerInterface,
|
||||
type CameraState,
|
||||
type CameraType,
|
||||
type EventManagerInterface,
|
||||
type NodeStorageInterface
|
||||
} from './interfaces'
|
||||
|
||||
export class CameraManager implements CameraManagerInterface {
|
||||
|
||||
@@ -2,9 +2,9 @@ import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
|
||||
import {
|
||||
ControlsManagerInterface,
|
||||
EventManagerInterface,
|
||||
NodeStorageInterface
|
||||
type ControlsManagerInterface,
|
||||
type EventManagerInterface,
|
||||
type NodeStorageInterface
|
||||
} from './interfaces'
|
||||
|
||||
export class ControlsManager implements ControlsManagerInterface {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { EventCallback, EventManagerInterface } from './interfaces'
|
||||
import { type EventCallback, type EventManagerInterface } from './interfaces'
|
||||
|
||||
export class EventManager implements EventManagerInterface {
|
||||
private listeners: { [key: string]: EventCallback[] } = {}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import { EventManagerInterface, LightingManagerInterface } from './interfaces'
|
||||
import {
|
||||
type EventManagerInterface,
|
||||
type LightingManagerInterface
|
||||
} from './interfaces'
|
||||
|
||||
export class LightingManager implements LightingManagerInterface {
|
||||
lights: THREE.Light[] = []
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import { type CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
|
||||
import { CameraManager } from './CameraManager'
|
||||
import { ControlsManager } from './ControlsManager'
|
||||
@@ -16,11 +16,11 @@ import { SceneManager } from './SceneManager'
|
||||
import { SceneModelManager } from './SceneModelManager'
|
||||
import { ViewHelperManager } from './ViewHelperManager'
|
||||
import {
|
||||
CameraState,
|
||||
CaptureResult,
|
||||
Load3DOptions,
|
||||
MaterialMode,
|
||||
UpDirection
|
||||
type CameraState,
|
||||
type CaptureResult,
|
||||
type Load3DOptions,
|
||||
type MaterialMode,
|
||||
type UpDirection
|
||||
} from './interfaces'
|
||||
|
||||
class Load3d {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
|
||||
import { AnimationManager } from './AnimationManager'
|
||||
import Load3d from './Load3d'
|
||||
import { Load3DOptions } from './interfaces'
|
||||
import { type Load3DOptions } from './interfaces'
|
||||
|
||||
class Load3dAnimation extends Load3d {
|
||||
private animationManager: AnimationManager
|
||||
|
||||
@@ -9,9 +9,9 @@ import { t } from '@/i18n'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
|
||||
import {
|
||||
EventManagerInterface,
|
||||
LoaderManagerInterface,
|
||||
ModelManagerInterface
|
||||
type EventManagerInterface,
|
||||
type LoaderManagerInterface,
|
||||
type ModelManagerInterface
|
||||
} from './interfaces'
|
||||
|
||||
export class LoaderManager implements LoaderManagerInterface {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
|
||||
import { NodeStorageInterface } from './interfaces'
|
||||
import { type NodeStorageInterface } from './interfaces'
|
||||
|
||||
export class NodeStorage implements NodeStorageInterface {
|
||||
private node: LGraphNode
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
|
||||
import { EventManagerInterface, PreviewManagerInterface } from './interfaces'
|
||||
import {
|
||||
type EventManagerInterface,
|
||||
type PreviewManagerInterface
|
||||
} from './interfaces'
|
||||
|
||||
export class PreviewManager implements PreviewManagerInterface {
|
||||
previewCamera: THREE.Camera
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
import { EventManagerInterface } from './interfaces'
|
||||
import { type EventManagerInterface } from './interfaces'
|
||||
|
||||
export class RecordingManager {
|
||||
private mediaRecorder: MediaRecorder | null = null
|
||||
|
||||
@@ -2,7 +2,10 @@ import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
|
||||
import Load3dUtils from './Load3dUtils'
|
||||
import { EventManagerInterface, SceneManagerInterface } from './interfaces'
|
||||
import {
|
||||
type EventManagerInterface,
|
||||
type SceneManagerInterface
|
||||
} from './interfaces'
|
||||
|
||||
export class SceneManager implements SceneManagerInterface {
|
||||
scene: THREE.Scene
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as THREE from 'three'
|
||||
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'
|
||||
import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2'
|
||||
import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry'
|
||||
import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader'
|
||||
import { type GLTF } from 'three/examples/jsm/loaders/GLTFLoader'
|
||||
import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils'
|
||||
|
||||
import { ColoredShadowMaterial } from './conditional-lines/ColoredShadowMaterial'
|
||||
@@ -11,11 +11,11 @@ import { ConditionalEdgesShader } from './conditional-lines/ConditionalEdgesShad
|
||||
import { ConditionalLineMaterial } from './conditional-lines/Lines2/ConditionalLineMaterial'
|
||||
import { ConditionalLineSegmentsGeometry } from './conditional-lines/Lines2/ConditionalLineSegmentsGeometry'
|
||||
import {
|
||||
EventManagerInterface,
|
||||
Load3DOptions,
|
||||
MaterialMode,
|
||||
ModelManagerInterface,
|
||||
UpDirection
|
||||
type EventManagerInterface,
|
||||
type Load3DOptions,
|
||||
type MaterialMode,
|
||||
type ModelManagerInterface,
|
||||
type UpDirection
|
||||
} from './interfaces'
|
||||
|
||||
export class SceneModelManager implements ModelManagerInterface {
|
||||
|
||||
@@ -2,7 +2,10 @@ import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
import { ViewHelper } from 'three/examples/jsm/helpers/ViewHelper'
|
||||
|
||||
import { NodeStorageInterface, ViewHelperManagerInterface } from './interfaces'
|
||||
import {
|
||||
type NodeStorageInterface,
|
||||
type ViewHelperManagerInterface
|
||||
} from './interfaces'
|
||||
|
||||
export class ViewHelperManager implements ViewHelperManagerInterface {
|
||||
viewHelper: ViewHelper = {} as ViewHelper
|
||||
|
||||
@@ -2,13 +2,13 @@ import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||
import { ViewHelper } from 'three/examples/jsm/helpers/ViewHelper'
|
||||
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'
|
||||
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
|
||||
import { type GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
|
||||
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'
|
||||
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
|
||||
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'
|
||||
|
||||
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import { type CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
|
||||
export type Load3DNodeType = 'Load3D' | 'Preview3D'
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ https://github.com/rgthree/rgthree-comfy/blob/main/py/display_any.py
|
||||
upstream requested in https://github.com/Kosinkadink/rfcs/blob/main/rfcs/0000-corenodes.md#preview-nodes
|
||||
*/
|
||||
import { app } from '@/scripts/app'
|
||||
import { DOMWidget } from '@/scripts/domWidget'
|
||||
import { type DOMWidget } from '@/scripts/domWidget'
|
||||
import { ComfyWidgets } from '@/scripts/widgets'
|
||||
import { useExtensionService } from '@/services/extensionService'
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { nextTick } from 'vue'
|
||||
|
||||
import Load3D from '@/components/load3d/Load3D.vue'
|
||||
import Load3DConfiguration from '@/extensions/core/load3d/Load3DConfiguration'
|
||||
import { CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import { type CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import { ComponentWidgetImpl, addWidget } from '@/scripts/domWidget'
|
||||
import { useExtensionService } from '@/services/extensionService'
|
||||
import { useLoad3dService } from '@/services/load3dService'
|
||||
|
||||
@@ -15,7 +15,7 @@ import type { ResultItemType } from '@/schemas/apiSchema'
|
||||
import type { ComfyNodeDef } from '@/schemas/nodeDefSchema'
|
||||
import type { DOMWidget } from '@/scripts/domWidget'
|
||||
import { useAudioService } from '@/services/audioService'
|
||||
import { NodeLocatorId } from '@/types'
|
||||
import { type NodeLocatorId } from '@/types'
|
||||
import { getNodeByLocatorId } from '@/utils/graphTraversalUtil'
|
||||
|
||||
import { api } from '../../scripts/api'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
ComfyNodeDef,
|
||||
InputSpec,
|
||||
type ComfyNodeDef,
|
||||
type InputSpec,
|
||||
isComboInputSpecV1
|
||||
} from '@/schemas/nodeDefSchema'
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ import ruCommands from './locales/ru/commands.json' with { type: 'json' }
|
||||
import ru from './locales/ru/main.json' with { type: 'json' }
|
||||
import ruNodes from './locales/ru/nodeDefs.json' with { type: 'json' }
|
||||
import ruSettings from './locales/ru/settings.json' with { type: 'json' }
|
||||
import trCommands from './locales/tr/commands.json' with { type: 'json' }
|
||||
import tr from './locales/tr/main.json' with { type: 'json' }
|
||||
import trNodes from './locales/tr/nodeDefs.json' with { type: 'json' }
|
||||
import trSettings from './locales/tr/settings.json' with { type: 'json' }
|
||||
import zhTWCommands from './locales/zh-TW/commands.json' with { type: 'json' }
|
||||
import zhTW from './locales/zh-TW/main.json' with { type: 'json' }
|
||||
import zhTWNodes from './locales/zh-TW/nodeDefs.json' with { type: 'json' }
|
||||
@@ -55,7 +59,8 @@ const messages = {
|
||||
ko: buildLocale(ko, koNodes, koCommands, koSettings),
|
||||
fr: buildLocale(fr, frNodes, frCommands, frSettings),
|
||||
es: buildLocale(es, esNodes, esCommands, esSettings),
|
||||
ar: buildLocale(ar, arNodes, arCommands, arSettings)
|
||||
ar: buildLocale(ar, arNodes, arCommands, arSettings),
|
||||
tr: buildLocale(tr, trNodes, trCommands, trSettings)
|
||||
}
|
||||
|
||||
export const i18n = createI18n({
|
||||
|
||||
@@ -87,6 +87,7 @@ import type { PickNevers } from './types/utility'
|
||||
import type { IBaseWidget } from './types/widgets'
|
||||
import { alignNodes, distributeNodes, getBoundaryNodes } from './utils/arrange'
|
||||
import { findFirstNode, getAllNestedItems } from './utils/collections'
|
||||
import { resolveConnectingLinkColor } from './utils/linkColors'
|
||||
import type { UUID } from './utils/uuid'
|
||||
import { BaseWidget } from './widgets/BaseWidget'
|
||||
import { toConcreteWidget } from './widgets/widgetMap'
|
||||
@@ -4716,29 +4717,20 @@ export class LGraphCanvas
|
||||
const connShape = fromSlot.shape
|
||||
const connType = fromSlot.type
|
||||
|
||||
const colour =
|
||||
connType === LiteGraph.EVENT
|
||||
? LiteGraph.EVENT_LINK_COLOR
|
||||
: LiteGraph.CONNECTING_LINK_COLOR
|
||||
const colour = resolveConnectingLinkColor(connType)
|
||||
|
||||
// the connection being dragged by the mouse
|
||||
if (this.linkRenderer) {
|
||||
this.linkRenderer.renderLinkDirect(
|
||||
this.linkRenderer.renderDraggingLink(
|
||||
ctx,
|
||||
pos,
|
||||
highlightPos,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
colour,
|
||||
fromDirection,
|
||||
dragDirection,
|
||||
{
|
||||
...this.buildLinkRenderContext(),
|
||||
linkMarkerShape: LinkMarkerShape.None
|
||||
},
|
||||
{
|
||||
disabled: false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import {
|
||||
} from './measure'
|
||||
import type { ISerialisedGroup } from './types/serialisation'
|
||||
|
||||
export type GroupId = number
|
||||
|
||||
export interface IGraphGroupFlags extends Record<string, unknown> {
|
||||
pinned?: true
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import type { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'
|
||||
import { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||
import { LLink, type ResolvedConnection } from '@/lib/litegraph/src/LLink'
|
||||
import { RecursionError } from '@/lib/litegraph/src/infrastructure/RecursionError'
|
||||
import type { ISubgraphInput } from '@/lib/litegraph/src/interfaces'
|
||||
import type {
|
||||
ISubgraphInput,
|
||||
IWidgetLocator
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
import type {
|
||||
INodeInputSlot,
|
||||
ISlotType,
|
||||
@@ -31,8 +34,6 @@ import {
|
||||
} from './ExecutableNodeDTO'
|
||||
import type { SubgraphInput } from './SubgraphInput'
|
||||
|
||||
export type SubgraphId = string
|
||||
|
||||
/**
|
||||
* An instance of a {@link Subgraph}, displayed as a node on the containing (parent) graph.
|
||||
*/
|
||||
@@ -80,9 +81,10 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
const existingInput = this.inputs.find((i) => i.name == name)
|
||||
if (existingInput) {
|
||||
const linkId = subgraphInput.linkIds[0]
|
||||
const { inputNode } = subgraph.links[linkId].resolve(subgraph)
|
||||
const { inputNode, input } = subgraph.links[linkId].resolve(subgraph)
|
||||
const widget = inputNode?.widgets?.find?.((w) => w.name == name)
|
||||
if (widget) this.#setWidget(subgraphInput, existingInput, widget)
|
||||
if (widget)
|
||||
this.#setWidget(subgraphInput, existingInput, widget, input?.widget)
|
||||
return
|
||||
}
|
||||
const input = this.addInput(name, type)
|
||||
@@ -187,13 +189,14 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
|
||||
subgraphInput.events.addEventListener(
|
||||
'input-connected',
|
||||
() => {
|
||||
(e) => {
|
||||
if (input._widget) return
|
||||
|
||||
const widget = subgraphInput._widget
|
||||
if (!widget) return
|
||||
|
||||
this.#setWidget(subgraphInput, input, widget)
|
||||
const widgetLocator = e.detail.input.widget
|
||||
this.#setWidget(subgraphInput, input, widget, widgetLocator)
|
||||
},
|
||||
{ signal }
|
||||
)
|
||||
@@ -303,7 +306,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
const widget = resolved.inputNode.getWidgetFromSlot(resolved.input)
|
||||
if (!widget) continue
|
||||
|
||||
this.#setWidget(subgraphInput, input, widget)
|
||||
this.#setWidget(subgraphInput, input, widget, resolved.input.widget)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -312,11 +315,13 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
#setWidget(
|
||||
subgraphInput: Readonly<SubgraphInput>,
|
||||
input: INodeInputSlot,
|
||||
widget: Readonly<IBaseWidget>
|
||||
widget: Readonly<IBaseWidget>,
|
||||
inputWidget: IWidgetLocator | undefined
|
||||
) {
|
||||
// Use the first matching widget
|
||||
const targetWidget = toConcreteWidget(widget, this)
|
||||
const promotedWidget = targetWidget.createCopyForNode(this)
|
||||
const promotedWidget = toConcreteWidget(widget, this).createCopyForNode(
|
||||
this
|
||||
)
|
||||
|
||||
Object.assign(promotedWidget, {
|
||||
get name() {
|
||||
@@ -374,11 +379,9 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
// NOTE: This code creates linked chains of prototypes for passing across
|
||||
// multiple levels of subgraphs. As part of this, it intentionally avoids
|
||||
// creating new objects. Have care when making changes.
|
||||
const backingInput =
|
||||
targetWidget.node.findInputSlot(widget.name, true)?.widget ?? {}
|
||||
input.widget ??= { name: subgraphInput.name }
|
||||
input.widget.name = subgraphInput.name
|
||||
Object.setPrototypeOf(input.widget, backingInput)
|
||||
if (inputWidget) Object.setPrototypeOf(input.widget, inputWidget)
|
||||
|
||||
input._widget = promotedWidget
|
||||
}
|
||||
|
||||
13
src/lib/litegraph/src/utils/linkColors.ts
Normal file
13
src/lib/litegraph/src/utils/linkColors.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { CanvasColour, ISlotType } from '../interfaces'
|
||||
import { LiteGraph } from '../litegraph'
|
||||
|
||||
/**
|
||||
* Resolve the colour used while rendering or previewing a connection of a given slot type.
|
||||
*/
|
||||
export function resolveConnectingLinkColor(
|
||||
type: ISlotType | undefined
|
||||
): CanvasColour {
|
||||
return type === LiteGraph.EVENT
|
||||
? LiteGraph.EVENT_LINK_COLOR
|
||||
: LiteGraph.CONNECTING_LINK_COLOR
|
||||
}
|
||||
@@ -24,7 +24,7 @@ Add your language code to the `outputLocales` array:
|
||||
```javascript
|
||||
module.exports = defineConfig({
|
||||
// ... existing config
|
||||
outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es'], // Add your language here
|
||||
outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'tr'], // Add your language here
|
||||
reference: `Special names to keep untranslated: flux, photomaker, clip, vae, cfg, stable audio, stable cascade, stable zero, controlnet, lora, HiDream.
|
||||
'latent' is the short form of 'latent space'.
|
||||
'mask' is in the context of image processing.
|
||||
|
||||
@@ -11,6 +11,7 @@ Our project supports multiple languages using `vue-i18n`. This allows users arou
|
||||
- ko (한국어)
|
||||
- fr (Français)
|
||||
- es (Español)
|
||||
- tr (Türkçe)
|
||||
|
||||
## How to Add a New Language
|
||||
|
||||
|
||||
@@ -1862,5 +1862,17 @@
|
||||
"showGroups": "Show Frames/Groups",
|
||||
"renderBypassState": "Render Bypass State",
|
||||
"renderErrorState": "Render Error State"
|
||||
},
|
||||
"assetBrowser": {
|
||||
"assets": "Assets",
|
||||
"browseAssets": "Browse Assets",
|
||||
"noAssetsFound": "No assets found",
|
||||
"tryAdjustingFilters": "Try adjusting your search or filters",
|
||||
"loadingModels": "Loading {type}...",
|
||||
"connectionError": "Please check your connection and try again",
|
||||
"noModelsInFolder": "No {type} available in this folder",
|
||||
"searchAssetsPlaceholder": "Search assets...",
|
||||
"allModels": "All Models",
|
||||
"unknown": "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
312
src/locales/tr/commands.json
Normal file
312
src/locales/tr/commands.json
Normal file
@@ -0,0 +1,312 @@
|
||||
{
|
||||
"Comfy-Desktop_CheckForUpdates": {
|
||||
"label": "Güncellemeleri Kontrol Et"
|
||||
},
|
||||
"Comfy-Desktop_Folders_OpenCustomNodesFolder": {
|
||||
"label": "Özel Düğümler Klasörünü Aç"
|
||||
},
|
||||
"Comfy-Desktop_Folders_OpenInputsFolder": {
|
||||
"label": "Girişler Klasörünü Aç"
|
||||
},
|
||||
"Comfy-Desktop_Folders_OpenLogsFolder": {
|
||||
"label": "Kayıtlar Klasörünü Aç"
|
||||
},
|
||||
"Comfy-Desktop_Folders_OpenModelConfig": {
|
||||
"label": "extra_model_paths.yaml dosyasını aç"
|
||||
},
|
||||
"Comfy-Desktop_Folders_OpenModelsFolder": {
|
||||
"label": "Modeller Klasörünü Aç"
|
||||
},
|
||||
"Comfy-Desktop_Folders_OpenOutputsFolder": {
|
||||
"label": "Çıktılar Klasörünü Aç"
|
||||
},
|
||||
"Comfy-Desktop_OpenDevTools": {
|
||||
"label": "Geliştirici Araçlarını Aç"
|
||||
},
|
||||
"Comfy-Desktop_OpenUserGuide": {
|
||||
"label": "Masaüstü Kullanıcı Kılavuzu"
|
||||
},
|
||||
"Comfy-Desktop_Quit": {
|
||||
"label": "Çık"
|
||||
},
|
||||
"Comfy-Desktop_Reinstall": {
|
||||
"label": "Yeniden Yükle"
|
||||
},
|
||||
"Comfy-Desktop_Restart": {
|
||||
"label": "Yeniden Başlat"
|
||||
},
|
||||
"Comfy_3DViewer_Open3DViewer": {
|
||||
"label": "Seçili Düğüm için 3D Görüntüleyiciyi (Beta) Aç"
|
||||
},
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "Şablonlara Gözat"
|
||||
},
|
||||
"Comfy_Canvas_DeleteSelectedItems": {
|
||||
"label": "Seçili Öğeleri Sil"
|
||||
},
|
||||
"Comfy_Canvas_FitView": {
|
||||
"label": "Görünümü seçili düğümlere sığdır"
|
||||
},
|
||||
"Comfy_Canvas_Lock": {
|
||||
"label": "Tuvali Kilitle"
|
||||
},
|
||||
"Comfy_Canvas_MoveSelectedNodes_Down": {
|
||||
"label": "Seçili Düğümleri Aşağı Taşı"
|
||||
},
|
||||
"Comfy_Canvas_MoveSelectedNodes_Left": {
|
||||
"label": "Seçili Düğümleri Sola Taşı"
|
||||
},
|
||||
"Comfy_Canvas_MoveSelectedNodes_Right": {
|
||||
"label": "Seçili Düğümleri Sağa Taşı"
|
||||
},
|
||||
"Comfy_Canvas_MoveSelectedNodes_Up": {
|
||||
"label": "Seçili Düğümleri Yukarı Taşı"
|
||||
},
|
||||
"Comfy_Canvas_ResetView": {
|
||||
"label": "Görünümü Sıfırla"
|
||||
},
|
||||
"Comfy_Canvas_Resize": {
|
||||
"label": "Seçili Düğümleri Yeniden Boyutlandır"
|
||||
},
|
||||
"Comfy_Canvas_ToggleLinkVisibility": {
|
||||
"label": "Tuval Bağlantı Görünürlüğünü Aç/Kapat"
|
||||
},
|
||||
"Comfy_Canvas_ToggleLock": {
|
||||
"label": "Tuval Kilidini Aç/Kapat"
|
||||
},
|
||||
"Comfy_Canvas_ToggleMinimap": {
|
||||
"label": "Mini Haritayı Aç/Kapat"
|
||||
},
|
||||
"Comfy_Canvas_ToggleSelected_Pin": {
|
||||
"label": "Seçili Öğeleri Sabitle/Sabitlemeyi Kaldır"
|
||||
},
|
||||
"Comfy_Canvas_ToggleSelectedNodes_Bypass": {
|
||||
"label": "Seçili Düğümleri Atla/Geri Al"
|
||||
},
|
||||
"Comfy_Canvas_ToggleSelectedNodes_Collapse": {
|
||||
"label": "Seçili Düğümleri Daralt/Genişlet"
|
||||
},
|
||||
"Comfy_Canvas_ToggleSelectedNodes_Mute": {
|
||||
"label": "Seçili Düğümleri Sessize Al/Sesi Aç"
|
||||
},
|
||||
"Comfy_Canvas_ToggleSelectedNodes_Pin": {
|
||||
"label": "Seçili Düğümleri Sabitle/Sabitlemeyi Kaldır"
|
||||
},
|
||||
"Comfy_Canvas_Unlock": {
|
||||
"label": "Tuvalin Kilidini Aç"
|
||||
},
|
||||
"Comfy_Canvas_ZoomIn": {
|
||||
"label": "Yakınlaştır"
|
||||
},
|
||||
"Comfy_Canvas_ZoomOut": {
|
||||
"label": "Uzaklaştır"
|
||||
},
|
||||
"Comfy_ClearPendingTasks": {
|
||||
"label": "Bekleyen Görevleri Temizle"
|
||||
},
|
||||
"Comfy_ClearWorkflow": {
|
||||
"label": "İş Akışını Temizle"
|
||||
},
|
||||
"Comfy_ContactSupport": {
|
||||
"label": "Destekle İletişime Geç"
|
||||
},
|
||||
"Comfy_Dev_ShowModelSelector": {
|
||||
"label": "Model Seçiciyi Göster (Geliştirici)"
|
||||
},
|
||||
"Comfy_DuplicateWorkflow": {
|
||||
"label": "Mevcut İş Akışını Çoğalt"
|
||||
},
|
||||
"Comfy_ExportWorkflow": {
|
||||
"label": "İş Akışını Dışa Aktar"
|
||||
},
|
||||
"Comfy_ExportWorkflowAPI": {
|
||||
"label": "İş Akışını Dışa Aktar (API Formatı)"
|
||||
},
|
||||
"Comfy_Feedback": {
|
||||
"label": "Geri Bildirim Ver"
|
||||
},
|
||||
"Comfy_Graph_ConvertToSubgraph": {
|
||||
"label": "Seçimi Alt Grafiğe Dönüştür"
|
||||
},
|
||||
"Comfy_Graph_ExitSubgraph": {
|
||||
"label": "Alt Grafikten Çık"
|
||||
},
|
||||
"Comfy_Graph_FitGroupToContents": {
|
||||
"label": "Grubu İçeriğe Sığdır"
|
||||
},
|
||||
"Comfy_Graph_GroupSelectedNodes": {
|
||||
"label": "Seçili Düğümleri Gruplandır"
|
||||
},
|
||||
"Comfy_Graph_UnpackSubgraph": {
|
||||
"label": "Seçili Alt Grafiği Aç"
|
||||
},
|
||||
"Comfy_GroupNode_ConvertSelectedNodesToGroupNode": {
|
||||
"label": "Seçili düğümleri grup düğümüne dönüştür"
|
||||
},
|
||||
"Comfy_GroupNode_ManageGroupNodes": {
|
||||
"label": "Grup düğümlerini yönet"
|
||||
},
|
||||
"Comfy_GroupNode_UngroupSelectedGroupNodes": {
|
||||
"label": "Seçili grup düğümlerinin grubunu çöz"
|
||||
},
|
||||
"Comfy_Help_AboutComfyUI": {
|
||||
"label": "ComfyUI Hakkında'yı Aç"
|
||||
},
|
||||
"Comfy_Help_OpenComfyOrgDiscord": {
|
||||
"label": "Comfy-Org Discord'unu Aç"
|
||||
},
|
||||
"Comfy_Help_OpenComfyUIDocs": {
|
||||
"label": "ComfyUI Belgelerini Aç"
|
||||
},
|
||||
"Comfy_Help_OpenComfyUIForum": {
|
||||
"label": "ComfyUI Forumunu Aç"
|
||||
},
|
||||
"Comfy_Help_OpenComfyUIIssues": {
|
||||
"label": "ComfyUI Sorunlarını Aç"
|
||||
},
|
||||
"Comfy_Interrupt": {
|
||||
"label": "Kes"
|
||||
},
|
||||
"Comfy_LoadDefaultWorkflow": {
|
||||
"label": "Varsayılan İş Akışını Yükle"
|
||||
},
|
||||
"Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": {
|
||||
"label": "Özel Düğüm Yöneticisi"
|
||||
},
|
||||
"Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": {
|
||||
"label": "Özel Düğümler (Eski)"
|
||||
},
|
||||
"Comfy_Manager_ShowLegacyManagerMenu": {
|
||||
"label": "Yönetici Menüsü (Eski)"
|
||||
},
|
||||
"Comfy_Manager_ShowMissingPacks": {
|
||||
"label": "Eksik Özel Düğümleri Yükle"
|
||||
},
|
||||
"Comfy_Manager_ShowUpdateAvailablePacks": {
|
||||
"label": "Özel Düğüm Güncellemelerini Kontrol Et"
|
||||
},
|
||||
"Comfy_Manager_ToggleManagerProgressDialog": {
|
||||
"label": "Özel Düğüm Yöneticisi İlerleme Çubuğunu Aç/Kapat"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Decrease": {
|
||||
"label": "Maske Düzenleyicide Fırça Boyutunu Azalt"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Increase": {
|
||||
"label": "Maske Düzenleyicide Fırça Boyutunu Artır"
|
||||
},
|
||||
"Comfy_MaskEditor_OpenMaskEditor": {
|
||||
"label": "Seçili Düğüm için Maske Düzenleyiciyi Aç"
|
||||
},
|
||||
"Comfy_Memory_UnloadModels": {
|
||||
"label": "Modelleri Boşalt"
|
||||
},
|
||||
"Comfy_Memory_UnloadModelsAndExecutionCache": {
|
||||
"label": "Modelleri ve Yürütme Önbelleğini Boşalt"
|
||||
},
|
||||
"Comfy_NewBlankWorkflow": {
|
||||
"label": "Yeni Boş İş Akışı"
|
||||
},
|
||||
"Comfy_OpenClipspace": {
|
||||
"label": "Clipspace"
|
||||
},
|
||||
"Comfy_OpenManagerDialog": {
|
||||
"label": "Yönetici"
|
||||
},
|
||||
"Comfy_OpenWorkflow": {
|
||||
"label": "İş Akışını Aç"
|
||||
},
|
||||
"Comfy_PublishSubgraph": {
|
||||
"label": "Alt Grafiği Yayınla"
|
||||
},
|
||||
"Comfy_QueuePrompt": {
|
||||
"label": "İstemi Kuyruğa Al"
|
||||
},
|
||||
"Comfy_QueuePromptFront": {
|
||||
"label": "İstemi Kuyruğa Al (Ön)"
|
||||
},
|
||||
"Comfy_QueueSelectedOutputNodes": {
|
||||
"label": "Seçili Çıktı Düğümlerini Kuyruğa Al"
|
||||
},
|
||||
"Comfy_Redo": {
|
||||
"label": "Yinele"
|
||||
},
|
||||
"Comfy_RefreshNodeDefinitions": {
|
||||
"label": "Düğüm Tanımlarını Yenile"
|
||||
},
|
||||
"Comfy_SaveWorkflow": {
|
||||
"label": "İş Akışını Kaydet"
|
||||
},
|
||||
"Comfy_SaveWorkflowAs": {
|
||||
"label": "İş Akışını Farklı Kaydet"
|
||||
},
|
||||
"Comfy_ShowSettingsDialog": {
|
||||
"label": "Ayarlar İletişim Kutusunu Göster"
|
||||
},
|
||||
"Comfy_ToggleCanvasInfo": {
|
||||
"label": "Tuval Performansı"
|
||||
},
|
||||
"Comfy_ToggleHelpCenter": {
|
||||
"label": "Yardım Merkezi"
|
||||
},
|
||||
"Comfy_ToggleTheme": {
|
||||
"label": "Temayı Değiştir (Karanlık/Açık)"
|
||||
},
|
||||
"Comfy_Undo": {
|
||||
"label": "Geri Al"
|
||||
},
|
||||
"Comfy_User_OpenSignInDialog": {
|
||||
"label": "Giriş Yapma İletişim Kutusunu Aç"
|
||||
},
|
||||
"Comfy_User_SignOut": {
|
||||
"label": "Çıkış Yap"
|
||||
},
|
||||
"Workspace_CloseWorkflow": {
|
||||
"label": "Mevcut İş Akışını Kapat"
|
||||
},
|
||||
"Workspace_NextOpenedWorkflow": {
|
||||
"label": "Sonraki Açılan İş Akışı"
|
||||
},
|
||||
"Workspace_PreviousOpenedWorkflow": {
|
||||
"label": "Önceki Açılan İş Akışı"
|
||||
},
|
||||
"Workspace_SearchBox_Toggle": {
|
||||
"label": "Arama Kutusunu Aç/Kapat"
|
||||
},
|
||||
"Workspace_ToggleBottomPanel": {
|
||||
"label": "Alt Paneli Aç/Kapat"
|
||||
},
|
||||
"Workspace_ToggleBottomPanel_Shortcuts": {
|
||||
"label": "Tuş Atamaları İletişim Kutusunu Göster"
|
||||
},
|
||||
"Workspace_ToggleBottomPanelTab_command-terminal": {
|
||||
"label": "Terminal Alt Panelini Aç/Kapat"
|
||||
},
|
||||
"Workspace_ToggleBottomPanelTab_logs-terminal": {
|
||||
"label": "Kayıtlar Alt Panelini Aç/Kapat"
|
||||
},
|
||||
"Workspace_ToggleBottomPanelTab_shortcuts-essentials": {
|
||||
"label": "Temel Alt Paneli Aç/Kapat"
|
||||
},
|
||||
"Workspace_ToggleBottomPanelTab_shortcuts-view-controls": {
|
||||
"label": "Görünüm Kontrolleri Alt Panelini Aç/Kapat"
|
||||
},
|
||||
"Workspace_ToggleFocusMode": {
|
||||
"label": "Odak Modunu Aç/Kapat"
|
||||
},
|
||||
"Workspace_ToggleSidebarTab_model-library": {
|
||||
"label": "Model Kütüphanesi Kenar Çubuğunu Aç/Kapat",
|
||||
"tooltip": "Model Kütüphanesi"
|
||||
},
|
||||
"Workspace_ToggleSidebarTab_node-library": {
|
||||
"label": "Düğüm Kütüphanesi Kenar Çubuğunu Aç/Kapat",
|
||||
"tooltip": "Düğüm Kütüphanesi"
|
||||
},
|
||||
"Workspace_ToggleSidebarTab_queue": {
|
||||
"label": "Kuyruk Kenar Çubuğunu Aç/Kapat",
|
||||
"tooltip": "Kuyruk"
|
||||
},
|
||||
"Workspace_ToggleSidebarTab_workflows": {
|
||||
"label": "İş Akışları Kenar Çubuğunu Aç/Kapat",
|
||||
"tooltip": "İş Akışları"
|
||||
}
|
||||
}
|
||||
1800
src/locales/tr/main.json
Normal file
1800
src/locales/tr/main.json
Normal file
File diff suppressed because it is too large
Load Diff
8653
src/locales/tr/nodeDefs.json
Normal file
8653
src/locales/tr/nodeDefs.json
Normal file
File diff suppressed because it is too large
Load Diff
424
src/locales/tr/settings.json
Normal file
424
src/locales/tr/settings.json
Normal file
@@ -0,0 +1,424 @@
|
||||
{
|
||||
"Comfy-Desktop_AutoUpdate": {
|
||||
"name": "Güncellemeleri otomatik olarak kontrol et"
|
||||
},
|
||||
"Comfy-Desktop_SendStatistics": {
|
||||
"name": "Anonim kullanım metrikleri gönder"
|
||||
},
|
||||
"Comfy-Desktop_UV_PypiInstallMirror": {
|
||||
"name": "Pypi Yükleme Yansısı",
|
||||
"tooltip": "Varsayılan pip yükleme yansısı"
|
||||
},
|
||||
"Comfy-Desktop_UV_PythonInstallMirror": {
|
||||
"name": "Python Yükleme Yansısı",
|
||||
"tooltip": "Yönetilen Python kurulumları Astral python-build-standalone projesinden indirilir. Bu değişken, Python kurulumları için farklı bir kaynak kullanmak üzere bir yansıma URL'sine ayarlanabilir. Sağlanan URL, örneğin https://github.com/astral-sh/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz'deki https://github.com/astral-sh/python-build-standalone/releases/download'ın yerini alacaktır. Dağıtımlar, file:// URL şeması kullanılarak yerel bir dizinden okunabilir."
|
||||
},
|
||||
"Comfy-Desktop_UV_TorchInstallMirror": {
|
||||
"name": "Torch Yükleme Yansısı",
|
||||
"tooltip": "Pytorch için Pip yükleme yansısı"
|
||||
},
|
||||
"Comfy-Desktop_WindowStyle": {
|
||||
"name": "Pencere Stili",
|
||||
"tooltip": "Özel: Sistem başlık çubuğunu ComfyUI'nin Üst menüsüyle değiştirin",
|
||||
"options": {
|
||||
"default": "varsayılan",
|
||||
"custom": "özel"
|
||||
}
|
||||
},
|
||||
"Comfy_Canvas_BackgroundImage": {
|
||||
"name": "Tuval arka plan resmi",
|
||||
"tooltip": "Tuval arka planı için resim URL'si. Çıktılar panelindeki bir resme sağ tıklayıp \"Arka Plan Olarak Ayarla\"yı seçerek kullanabilir veya yükleme düğmesini kullanarak kendi resminizi yükleyebilirsiniz."
|
||||
},
|
||||
"Comfy_Canvas_NavigationMode": {
|
||||
"name": "Tuval Gezinme Modu",
|
||||
"options": {
|
||||
"Standard (New)": "Standart (Yeni)",
|
||||
"Drag Navigation": "Sürükleyerek Gezinme"
|
||||
}
|
||||
},
|
||||
"Comfy_Canvas_SelectionToolbox": {
|
||||
"name": "Seçim araç kutusunu göster"
|
||||
},
|
||||
"Comfy_ConfirmClear": {
|
||||
"name": "İş akışını temizlerken onay iste"
|
||||
},
|
||||
"Comfy_DevMode": {
|
||||
"name": "Geliştirici modu seçeneklerini etkinleştir (API kaydetme, vb.)"
|
||||
},
|
||||
"Comfy_DisableFloatRounding": {
|
||||
"name": "Varsayılan ondalık sayı widget yuvarlamasını devre dışı bırak.",
|
||||
"tooltip": "(sayfanın yeniden yüklenmesini gerektirir) Arka uçtaki düğüm tarafından yuvarlama ayarlandığında yuvarlama devre dışı bırakılamaz."
|
||||
},
|
||||
"Comfy_DisableSliders": {
|
||||
"name": "Düğüm widget kaydırıcılarını devre dışı bırak"
|
||||
},
|
||||
"Comfy_DOMClippingEnabled": {
|
||||
"name": "DOM öğesi kırpmayı etkinleştir (etkinleştirmek performansı düşürebilir)"
|
||||
},
|
||||
"Comfy_EditAttention_Delta": {
|
||||
"name": "Ctrl+yukarı/aşağı hassasiyeti"
|
||||
},
|
||||
"Comfy_EnableTooltips": {
|
||||
"name": "Araç İpuçlarını Etkinleştir"
|
||||
},
|
||||
"Comfy_EnableWorkflowViewRestore": {
|
||||
"name": "İş akışlarında tuval konumunu ve yakınlaştırma seviyesini kaydet ve geri yükle"
|
||||
},
|
||||
"Comfy_FloatRoundingPrecision": {
|
||||
"name": "Ondalık sayı widget yuvarlama ondalık basamakları [0 = otomatik].",
|
||||
"tooltip": "(sayfanın yeniden yüklenmesini gerektirir)"
|
||||
},
|
||||
"Comfy_Graph_CanvasInfo": {
|
||||
"name": "Sol alt köşede tuval bilgilerini göster (fps, vb.)"
|
||||
},
|
||||
"Comfy_Graph_CanvasMenu": {
|
||||
"name": "Grafik tuval menüsünü göster"
|
||||
},
|
||||
"Comfy_Graph_CtrlShiftZoom": {
|
||||
"name": "Hızlı yakınlaştırma kısayolunu etkinleştir (Ctrl + Shift + Sürükle)"
|
||||
},
|
||||
"Comfy_Graph_LinkMarkers": {
|
||||
"name": "Bağlantı orta nokta işaretçileri",
|
||||
"options": {
|
||||
"None": "Yok",
|
||||
"Circle": "Daire",
|
||||
"Arrow": "Ok"
|
||||
}
|
||||
},
|
||||
"Comfy_Graph_ZoomSpeed": {
|
||||
"name": "Tuval yakınlaştırma hızı"
|
||||
},
|
||||
"Comfy_Group_DoubleClickTitleToEdit": {
|
||||
"name": "Düzenlemek için grup başlığına çift tıkla"
|
||||
},
|
||||
"Comfy_GroupSelectedNodes_Padding": {
|
||||
"name": "Seçili düğümleri gruplandırma dolgusu"
|
||||
},
|
||||
"Comfy_LinkRelease_Action": {
|
||||
"name": "Bağlantı bırakıldığında eylem (Değiştirici yok)",
|
||||
"options": {
|
||||
"context menu": "bağlam menüsü",
|
||||
"search box": "arama kutusu",
|
||||
"no action": "eylem yok"
|
||||
}
|
||||
},
|
||||
"Comfy_LinkRelease_ActionShift": {
|
||||
"name": "Bağlantı bırakıldığında eylem (Shift)",
|
||||
"options": {
|
||||
"context menu": "bağlam menüsü",
|
||||
"search box": "arama kutusu",
|
||||
"no action": "eylem yok"
|
||||
}
|
||||
},
|
||||
"Comfy_LinkRenderMode": {
|
||||
"name": "Bağlantı Oluşturma Modu",
|
||||
"options": {
|
||||
"Straight": "Düz",
|
||||
"Linear": "Doğrusal",
|
||||
"Spline": "Eğri",
|
||||
"Hidden": "Gizli"
|
||||
}
|
||||
},
|
||||
"Comfy_Load3D_3DViewerEnable": {
|
||||
"name": "3D Görüntüleyiciyi Etkinleştir (Beta)",
|
||||
"tooltip": "Seçili düğümler için 3D Görüntüleyiciyi (Beta) etkinleştirir. Bu özellik, 3D modelleri doğrudan tam boyutlu 3D görüntüleyici içinde görselleştirmenize ve etkileşimde bulunmanıza olanak tanır."
|
||||
},
|
||||
"Comfy_Load3D_BackgroundColor": {
|
||||
"name": "Başlangıç Arka Plan Rengi",
|
||||
"tooltip": "3D sahnenin varsayılan arka plan rengini kontrol eder. Bu ayar, yeni bir 3D widget oluşturulduğunda arka plan görünümünü belirler, ancak oluşturulduktan sonra her widget için ayrı ayrı ayarlanabilir."
|
||||
},
|
||||
"Comfy_Load3D_CameraType": {
|
||||
"name": "Başlangıç Kamera Tipi",
|
||||
"tooltip": "Yeni bir 3D widget oluşturulduğunda kameranın varsayılan olarak perspektif mi yoksa ortografik mi olacağını kontrol eder. Bu varsayılan, oluşturulduktan sonra her widget için ayrı ayrı değiştirilebilir.",
|
||||
"options": {
|
||||
"perspective": "perspektif",
|
||||
"orthographic": "ortografik"
|
||||
}
|
||||
},
|
||||
"Comfy_Load3D_LightAdjustmentIncrement": {
|
||||
"name": "Işık Ayarlama Artışı",
|
||||
"tooltip": "3D sahnelerde ışık yoğunluğunu ayarlarken artış boyutunu kontrol eder. Daha küçük bir adım değeri, aydınlatma ayarlamaları üzerinde daha ince kontrol sağlarken, daha büyük bir değer ayarlama başına daha belirgin değişikliklere neden olur."
|
||||
},
|
||||
"Comfy_Load3D_LightIntensity": {
|
||||
"name": "Başlangıç Işık Yoğunluğu",
|
||||
"tooltip": "3D sahnedeki aydınlatmanın varsayılan parlaklık seviyesini ayarlar. Bu değer, yeni bir 3D widget oluşturulduğunda ışıkların nesneleri ne kadar yoğun aydınlatacağını belirler, ancak oluşturulduktan sonra her widget için ayrı ayrı ayarlanabilir."
|
||||
},
|
||||
"Comfy_Load3D_LightIntensityMaximum": {
|
||||
"name": "Maksimum Işık Yoğunluğu",
|
||||
"tooltip": "3D sahneler için izin verilen maksimum ışık yoğunluğu değerini ayarlar. Bu, herhangi bir 3D widget'ta aydınlatma ayarlanırken ayarlanabilecek üst parlaklık sınırını tanımlar."
|
||||
},
|
||||
"Comfy_Load3D_LightIntensityMinimum": {
|
||||
"name": "Minimum Işık Yoğunluğu",
|
||||
"tooltip": "3D sahneler için izin verilen minimum ışık yoğunluğu değerini ayarlar. Bu, herhangi bir 3D widget'ta aydınlatma ayarlanırken ayarlanabilecek alt parlaklık sınırını tanımlar."
|
||||
},
|
||||
"Comfy_Load3D_ShowGrid": {
|
||||
"name": "Başlangıç Izgara Görünürlüğü",
|
||||
"tooltip": "Yeni bir 3D widget oluşturulduğunda ızgaranın varsayılan olarak görünür olup olmadığını kontrol eder. Bu varsayılan, oluşturulduktan sonra her widget için ayrı ayrı değiştirilebilir."
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "Başlangıç Önizleme Görünürlüğü",
|
||||
"tooltip": "Yeni bir 3D widget oluşturulduğunda önizleme ekranının varsayılan olarak görünür olup olmadığını kontrol eder. Bu varsayılan, oluşturulduktan sonra her widget için ayrı ayrı değiştirilebilir."
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "Dil"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushAdjustmentSpeed": {
|
||||
"name": "Fırça ayar hızı çarpanı",
|
||||
"tooltip": "Ayarlama sırasında fırça boyutunun ve sertliğinin ne kadar hızlı değiştiğini kontrol eder. Daha yüksek değerler daha hızlı değişiklikler anlamına gelir."
|
||||
},
|
||||
"Comfy_MaskEditor_UseDominantAxis": {
|
||||
"name": "Fırça ayarını baskın eksene kilitle",
|
||||
"tooltip": "Etkinleştirildiğinde, fırça ayarları yalnızca daha fazla hareket ettiğiniz yöne bağlı olarak boyutu VEYA sertliği etkileyecektir"
|
||||
},
|
||||
"Comfy_MaskEditor_UseNewEditor": {
|
||||
"name": "Yeni maske düzenleyiciyi kullan",
|
||||
"tooltip": "Yeni maske düzenleyici arayüzüne geç"
|
||||
},
|
||||
"Comfy_ModelLibrary_AutoLoadAll": {
|
||||
"name": "Tüm model klasörlerini otomatik olarak yükle",
|
||||
"tooltip": "Doğruysa, model kütüphanesini açar açmaz tüm klasörler yüklenecektir (bu, yüklenirken gecikmelere neden olabilir). Yanlışsa, kök düzeyindeki model klasörleri yalnızca üzerlerine tıkladığınızda yüklenecektir."
|
||||
},
|
||||
"Comfy_ModelLibrary_NameFormat": {
|
||||
"name": "Model kütüphanesi ağaç görünümünde hangi adın görüntüleneceği",
|
||||
"tooltip": "Model listesinde ham dosya adının (dizin veya \".safetensors\" uzantısı olmadan) basitleştirilmiş bir görünümünü oluşturmak için \"dosyaadı\"nı seçin. Yapılandırılabilir model meta veri başlığını görüntülemek için \"başlık\"ı seçin.",
|
||||
"options": {
|
||||
"filename": "dosyaadı",
|
||||
"title": "başlık"
|
||||
}
|
||||
},
|
||||
"Comfy_Node_AllowImageSizeDraw": {
|
||||
"name": "Görüntü önizlemesinin altında genişlik × yüksekliği göster"
|
||||
},
|
||||
"Comfy_Node_AutoSnapLinkToSlot": {
|
||||
"name": "Bağlantıyı otomatik olarak düğüm yuvasına yapıştır",
|
||||
"tooltip": "Bir bağlantıyı bir düğümün üzerine sürüklerken, bağlantı otomatik olarak düğüm üzerindeki uygun bir giriş yuvasına yapışır"
|
||||
},
|
||||
"Comfy_Node_BypassAllLinksOnDelete": {
|
||||
"name": "Düğümleri silerken tüm bağlantıları koru",
|
||||
"tooltip": "Bir düğümü silerken, tüm giriş ve çıkış bağlantılarını yeniden bağlamaya çalışın (silinen düğümü atlayarak)"
|
||||
},
|
||||
"Comfy_Node_DoubleClickTitleToEdit": {
|
||||
"name": "Düzenlemek için düğüm başlığına çift tıkla"
|
||||
},
|
||||
"Comfy_Node_MiddleClickRerouteNode": {
|
||||
"name": "Orta tıklama yeni bir Yeniden Yönlendirme düğümü oluşturur"
|
||||
},
|
||||
"Comfy_Node_Opacity": {
|
||||
"name": "Düğüm opaklığı"
|
||||
},
|
||||
"Comfy_Node_ShowDeprecated": {
|
||||
"name": "Aramada kullanımdan kaldırılmış düğümleri göster",
|
||||
"tooltip": "Kullanımdan kaldırılmış düğümler arayüzde varsayılan olarak gizlidir, ancak bunları kullanan mevcut iş akışlarında işlevsel kalır."
|
||||
},
|
||||
"Comfy_Node_ShowExperimental": {
|
||||
"name": "Aramada deneysel düğümleri göster",
|
||||
"tooltip": "Deneysel düğümler arayüzde bu şekilde işaretlenmiştir ve gelecekteki sürümlerde önemli değişikliklere veya kaldırılmaya tabi olabilir. Üretim iş akışlarında dikkatli kullanın"
|
||||
},
|
||||
"Comfy_Node_SnapHighlightsNode": {
|
||||
"name": "Yapıştırma düğümü vurgular",
|
||||
"tooltip": "Uygun giriş yuvasına sahip bir düğümün üzerine bir bağlantı sürüklerken, düğümü vurgulayın"
|
||||
},
|
||||
"Comfy_NodeBadge_NodeIdBadgeMode": {
|
||||
"name": "Düğüm ID rozeti modu",
|
||||
"options": {
|
||||
"None": "Yok",
|
||||
"Show all": "Tümünü göster"
|
||||
}
|
||||
},
|
||||
"Comfy_NodeBadge_NodeLifeCycleBadgeMode": {
|
||||
"name": "Düğüm yaşam döngüsü rozeti modu",
|
||||
"options": {
|
||||
"None": "Yok",
|
||||
"Show all": "Tümünü göster"
|
||||
}
|
||||
},
|
||||
"Comfy_NodeBadge_NodeSourceBadgeMode": {
|
||||
"name": "Düğüm kaynak rozeti modu",
|
||||
"options": {
|
||||
"None": "Yok",
|
||||
"Show all": "Tümünü göster",
|
||||
"Hide built-in": "Yerleşik olanı gizle"
|
||||
}
|
||||
},
|
||||
"Comfy_NodeBadge_ShowApiPricing": {
|
||||
"name": "API düğüm fiyatlandırma rozetini göster"
|
||||
},
|
||||
"Comfy_NodeSearchBoxImpl": {
|
||||
"name": "Düğüm arama kutusu uygulaması",
|
||||
"options": {
|
||||
"default": "varsayılan",
|
||||
"litegraph (legacy)": "litegraph (eski)"
|
||||
}
|
||||
},
|
||||
"Comfy_NodeSearchBoxImpl_NodePreview": {
|
||||
"name": "Düğüm önizlemesi",
|
||||
"tooltip": "Yalnızca varsayılan uygulama için geçerlidir"
|
||||
},
|
||||
"Comfy_NodeSearchBoxImpl_ShowCategory": {
|
||||
"name": "Arama sonuçlarında düğüm kategorisini göster",
|
||||
"tooltip": "Yalnızca varsayılan uygulama için geçerlidir"
|
||||
},
|
||||
"Comfy_NodeSearchBoxImpl_ShowIdName": {
|
||||
"name": "Arama sonuçlarında düğüm kimliği adını göster",
|
||||
"tooltip": "Yalnızca varsayılan uygulama için geçerlidir"
|
||||
},
|
||||
"Comfy_NodeSearchBoxImpl_ShowNodeFrequency": {
|
||||
"name": "Arama sonuçlarında düğüm sıklığını göster",
|
||||
"tooltip": "Yalnızca varsayılan uygulama için geçerlidir"
|
||||
},
|
||||
"Comfy_NodeSuggestions_number": {
|
||||
"name": "Düğüm öneri sayısı",
|
||||
"tooltip": "Yalnızca litegraph arama kutusu/bağlam menüsü için"
|
||||
},
|
||||
"Comfy_Notification_ShowVersionUpdates": {
|
||||
"name": "Sürüm güncellemelerini göster",
|
||||
"tooltip": "Yeni modeller ve önemli yeni özellikler için güncellemeleri göster."
|
||||
},
|
||||
"Comfy_Pointer_ClickBufferTime": {
|
||||
"name": "İşaretçi tıklama kayma gecikmesi",
|
||||
"tooltip": "Bir işaretçi düğmesine bastıktan sonra, bu, işaretçi hareketinin göz ardı edilebileceği maksimum süredir (milisaniye cinsinden).\n\nTıklarken işaretçi hareket ettirilirse nesnelerin istemeden dürtülmesini önlemeye yardımcı olur."
|
||||
},
|
||||
"Comfy_Pointer_ClickDrift": {
|
||||
"name": "İşaretçi tıklama kayması (maksimum mesafe)",
|
||||
"tooltip": "İşaretçi bir düğmeyi basılı tutarken bu mesafeden daha fazla hareket ederse, bu sürükleme olarak kabul edilir (tıklama yerine).\n\nTıklarken işaretçi hareket ettirilirse nesnelerin istemeden dürtülmesini önlemeye yardımcı olur."
|
||||
},
|
||||
"Comfy_Pointer_DoubleClickTime": {
|
||||
"name": "Çift tıklama aralığı (maksimum)",
|
||||
"tooltip": "Çift tıklamanın iki tıklaması arasındaki milisaniye cinsinden maksimum süre. Bu değeri artırmak, çift tıklamaların bazen kaydedilmemesi durumunda yardımcı olabilir."
|
||||
},
|
||||
"Comfy_PreviewFormat": {
|
||||
"name": "Önizleme görüntü formatı",
|
||||
"tooltip": "Görüntü widget'ında bir önizleme görüntülerken, onu hafif bir görüntüye dönüştürün, örn. webp, jpeg, webp;50, vb."
|
||||
},
|
||||
"Comfy_PromptFilename": {
|
||||
"name": "İş akışını kaydederken dosya adı iste"
|
||||
},
|
||||
"Comfy_Queue_MaxHistoryItems": {
|
||||
"name": "Kuyruk geçmişi boyutu",
|
||||
"tooltip": "Kuyruk geçmişinde gösterilen maksimum görev sayısı."
|
||||
},
|
||||
"Comfy_QueueButton_BatchCountLimit": {
|
||||
"name": "Toplu iş sayısı sınırı",
|
||||
"tooltip": "Tek bir düğme tıklamasıyla kuyruğa eklenen maksimum görev sayısı"
|
||||
},
|
||||
"Comfy_Sidebar_Location": {
|
||||
"name": "Kenar çubuğu konumu",
|
||||
"options": {
|
||||
"left": "sol",
|
||||
"right": "sağ"
|
||||
}
|
||||
},
|
||||
"Comfy_Sidebar_Size": {
|
||||
"name": "Kenar çubuğu boyutu",
|
||||
"options": {
|
||||
"normal": "normal",
|
||||
"small": "küçük"
|
||||
}
|
||||
},
|
||||
"Comfy_Sidebar_UnifiedWidth": {
|
||||
"name": "Birleşik kenar çubuğu genişliği"
|
||||
},
|
||||
"Comfy_SnapToGrid_GridSize": {
|
||||
"name": "Izgaraya yapıştırma boyutu",
|
||||
"tooltip": "Shift tuşunu basılı tutarken düğümleri sürükleyip yeniden boyutlandırırken ızgaraya hizalanacaklar, bu o ızgaranın boyutunu kontrol eder."
|
||||
},
|
||||
"Comfy_TextareaWidget_FontSize": {
|
||||
"name": "Metin alanı widget yazı tipi boyutu"
|
||||
},
|
||||
"Comfy_TextareaWidget_Spellcheck": {
|
||||
"name": "Metin alanı widget yazım denetimi"
|
||||
},
|
||||
"Comfy_TreeExplorer_ItemPadding": {
|
||||
"name": "Ağaç gezgini öğe dolgusu"
|
||||
},
|
||||
"Comfy_UseNewMenu": {
|
||||
"name": "Yeni menüyü kullan",
|
||||
"tooltip": "Menü çubuğu konumu. Mobil cihazlarda menü her zaman üstte gösterilir.",
|
||||
"options": {
|
||||
"Disabled": "Devre dışı",
|
||||
"Top": "Üst",
|
||||
"Bottom": "Alt"
|
||||
}
|
||||
},
|
||||
"Comfy_Validation_Workflows": {
|
||||
"name": "İş akışlarını doğrula"
|
||||
},
|
||||
"Comfy_VueNodes_Enabled": {
|
||||
"name": "Vue düğüm oluşturmayı etkinleştir",
|
||||
"tooltip": "Düğümleri tuval öğeleri yerine Vue bileşenleri olarak oluşturun. Deneysel özellik."
|
||||
},
|
||||
"Comfy_VueNodes_Widgets": {
|
||||
"name": "Vue widget'larını etkinleştir",
|
||||
"tooltip": "Widget'ları Vue düğümleri içinde Vue bileşenleri olarak oluşturun."
|
||||
},
|
||||
"Comfy_WidgetControlMode": {
|
||||
"name": "Widget kontrol modu",
|
||||
"tooltip": "Widget değerlerinin ne zaman güncelleneceğini (rastgele/artırma/azaltma), istem kuyruğa alınmadan önce veya sonra kontrol eder.",
|
||||
"options": {
|
||||
"before": "önce",
|
||||
"after": "sonra"
|
||||
}
|
||||
},
|
||||
"Comfy_Window_UnloadConfirmation": {
|
||||
"name": "Pencereyi kapatırken onay göster"
|
||||
},
|
||||
"Comfy_Workflow_AutoSave": {
|
||||
"name": "Otomatik Kaydet",
|
||||
"options": {
|
||||
"off": "kapalı",
|
||||
"after delay": "gecikmeden sonra"
|
||||
}
|
||||
},
|
||||
"Comfy_Workflow_AutoSaveDelay": {
|
||||
"name": "Otomatik Kaydetme Gecikmesi (ms)",
|
||||
"tooltip": "Yalnızca Otomatik Kaydetme \"gecikmeden sonra\" olarak ayarlandığında geçerlidir."
|
||||
},
|
||||
"Comfy_Workflow_ConfirmDelete": {
|
||||
"name": "İş akışlarını silerken onay göster"
|
||||
},
|
||||
"Comfy_Workflow_Persist": {
|
||||
"name": "İş akışı durumunu koru ve sayfayı (yeniden) yüklediğinde geri yükle"
|
||||
},
|
||||
"Comfy_Workflow_ShowMissingModelsWarning": {
|
||||
"name": "Eksik model uyarısını göster"
|
||||
},
|
||||
"Comfy_Workflow_ShowMissingNodesWarning": {
|
||||
"name": "Eksik düğüm uyarısını göster"
|
||||
},
|
||||
"Comfy_Workflow_SortNodeIdOnSave": {
|
||||
"name": "İş akışını kaydederken düğüm kimliklerini sırala"
|
||||
},
|
||||
"Comfy_Workflow_WorkflowTabsPosition": {
|
||||
"name": "Açılan iş akışları konumu",
|
||||
"options": {
|
||||
"Sidebar": "Kenar Çubuğu",
|
||||
"Topbar": "Üst Çubuk",
|
||||
"Topbar (2nd-row)": "Üst Çubuk (2. sıra)"
|
||||
}
|
||||
},
|
||||
"LiteGraph_Canvas_MinFontSizeForLOD": {
|
||||
"name": "Yakınlaştırma Düğümü Ayrıntı Seviyesi - yazı tipi boyutu eşiği",
|
||||
"tooltip": "Düğümlerin ne zaman düşük kaliteli LOD oluşturmaya geçeceğini kontrol eder. Ne zaman geçiş yapılacağını belirlemek için piksel cinsinden yazı tipi boyutunu kullanır. Devre dışı bırakmak için 0'a ayarlayın. 1-24 arasındaki değerler LOD için minimum yazı tipi boyutu eşiğini ayarlar - daha yüksek değerler (24 piksel) = uzaklaştırırken düğümleri daha erken basitleştirilmiş oluşturmaya geçirin, daha düşük değerler (1 piksel) = tam düğüm kalitesini daha uzun süre koruyun."
|
||||
},
|
||||
"LiteGraph_Canvas_MaximumFps": {
|
||||
"name": "Maksimum FPS",
|
||||
"tooltip": "Tuvalin saniyede oluşturmasına izin verilen maksimum kare sayısı. Akıcılık pahasına GPU kullanımını sınırlar. 0 ise, ekran yenileme hızı kullanılır. Varsayılan: 0"
|
||||
},
|
||||
"LiteGraph_ContextMenu_Scaling": {
|
||||
"name": "Yakınlaştırıldığında düğüm birleşik widget menülerini (listeleri) ölçeklendir"
|
||||
},
|
||||
"LiteGraph_Node_DefaultPadding": {
|
||||
"name": "Yeni düğümleri her zaman küçült",
|
||||
"tooltip": "Oluşturulduğunda düğümleri mümkün olan en küçük boyuta yeniden boyutlandırın. Devre dışı bırakıldığında, yeni eklenen bir düğüm widget değerlerini göstermek için biraz genişletilecektir."
|
||||
},
|
||||
"LiteGraph_Node_TooltipDelay": {
|
||||
"name": "Araç İpucu Gecikmesi"
|
||||
},
|
||||
"LiteGraph_Reroute_SplineOffset": {
|
||||
"name": "Yeniden yönlendirme eğri ofseti",
|
||||
"tooltip": "Yeniden yönlendirme merkez noktasından bezier kontrol noktası ofseti"
|
||||
},
|
||||
"pysssss_SnapToGrid": {
|
||||
"name": "Her zaman ızgaraya yapıştır"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user