mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-24 00:09:32 +00:00
Compare commits
18 Commits
sno-lint-i
...
v1.26.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65785af348 | ||
|
|
ec4ad5ea92 | ||
|
|
e9ddf29507 | ||
|
|
fdd8564c07 | ||
|
|
d18081a54e | ||
|
|
45cc6ca2b4 | ||
|
|
c303a3f037 | ||
|
|
c90fd18ade | ||
|
|
2ed1704749 | ||
|
|
7d5a4d423e | ||
|
|
7aaa0f022e | ||
|
|
a132dad216 | ||
|
|
9dbdc6a72b | ||
|
|
7b228d693d | ||
|
|
547af0e043 | ||
|
|
4ca6220adf | ||
|
|
1e41c6dc45 | ||
|
|
5224c63bce |
@@ -502,11 +502,94 @@ echo "Workflow triggered. Waiting for PR creation..."
|
||||
## Files Generated
|
||||
- \`release-notes-${NEW_VERSION}.md\` - Comprehensive release notes
|
||||
- \`post-release-checklist.md\` - Follow-up tasks
|
||||
- \`gtm-summary-${NEW_VERSION}.md\` - Marketing team notification
|
||||
EOF
|
||||
```
|
||||
|
||||
4. **RELEASE COMPLETION**: All post-release setup completed?
|
||||
|
||||
### Step 16: Generate GTM Feature Summary
|
||||
|
||||
1. **Extract and analyze PR data:**
|
||||
```bash
|
||||
echo "📊 Checking for marketing-worthy features..."
|
||||
|
||||
# Extract all PR data inline
|
||||
PR_DATA=$(
|
||||
PR_LIST=$(git log ${BASE_TAG}..HEAD --grep="Merge pull request" --pretty=format:"%s" | grep -oE "#[0-9]+" | tr -d '#' | sort -u)
|
||||
|
||||
echo "["
|
||||
first=true
|
||||
for PR in $PR_LIST; do
|
||||
[[ "$first" == true ]] && first=false || echo ","
|
||||
gh pr view $PR --json number,title,author,body,files,labels,closedAt 2>/dev/null || continue
|
||||
done
|
||||
echo "]"
|
||||
)
|
||||
|
||||
# Save for analysis
|
||||
echo "$PR_DATA" > prs-${NEW_VERSION}.json
|
||||
```
|
||||
|
||||
2. **Analyze for GTM-worthy features:**
|
||||
```
|
||||
<task>
|
||||
Review these PRs to identify if ANY would interest a marketing/growth team.
|
||||
|
||||
Consider if a PR:
|
||||
- Changes something users directly interact with or experience
|
||||
- Makes something noticeably better, faster, or easier
|
||||
- Introduces capabilities users have been asking for
|
||||
- Has visual assets (screenshots, GIFs, videos) that could be shared
|
||||
- Tells a compelling story about improvement or innovation
|
||||
- Would make users excited if they heard about it
|
||||
|
||||
Many releases contain only technical improvements, bug fixes, or internal changes -
|
||||
that's perfectly normal. Only flag PRs that would genuinely interest end users.
|
||||
|
||||
If you find marketing-worthy PRs, note:
|
||||
- PR number, title, and author
|
||||
- Any media links from the description
|
||||
- One sentence on why it's worth showcasing
|
||||
|
||||
If nothing is marketing-worthy, just say "No marketing-worthy features in this release."
|
||||
</task>
|
||||
|
||||
PR data: [contents of prs-${NEW_VERSION}.json]
|
||||
```
|
||||
|
||||
3. **Generate GTM notification (only if needed):**
|
||||
```
|
||||
If there are marketing-worthy features, create a message for #gtm with:
|
||||
|
||||
🚀 Frontend Release v${NEW_VERSION}
|
||||
|
||||
Timeline: Available now in nightly, ~2-3 weeks for core
|
||||
|
||||
Features worth showcasing:
|
||||
[List the selected PRs with media links and authors]
|
||||
|
||||
Testing: --front-end-version ${NEW_VERSION}
|
||||
|
||||
If there are NO marketing-worthy features, generate:
|
||||
"No marketing-worthy features in v${NEW_VERSION} - mostly internal improvements and bug fixes."
|
||||
```
|
||||
|
||||
4. **Save the output:**
|
||||
```bash
|
||||
# Claude generates the GTM summary and saves it
|
||||
# Save to gtm-summary-${NEW_VERSION}.md
|
||||
|
||||
# Check if notification is needed
|
||||
if grep -q "No marketing-worthy features" gtm-summary-${NEW_VERSION}.md; then
|
||||
echo "✅ No GTM notification needed for this release"
|
||||
echo "📄 Summary saved to: gtm-summary-${NEW_VERSION}.md"
|
||||
else
|
||||
echo "📋 GTM summary saved to: gtm-summary-${NEW_VERSION}.md"
|
||||
echo "📤 Share this file in #gtm channel to notify the team"
|
||||
fi
|
||||
```
|
||||
|
||||
## Advanced Safety Features
|
||||
|
||||
### Rollback Procedures
|
||||
|
||||
@@ -13,6 +13,10 @@ module.exports = defineConfig({
|
||||
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.
|
||||
Note: For Traditional Chinese (Taiwan), use Taiwan-specific terminology and traditional characters.
|
||||
|
||||
IMPORTANT Chinese Translation Guidelines:
|
||||
- For 'zh' locale: Use ONLY Simplified Chinese characters (简体中文). Common examples: 节点 (not 節點), 画布 (not 畫布), 图像 (not 圖像), 选择 (not 選擇), 减小 (not 減小).
|
||||
- For 'zh-TW' locale: Use ONLY Traditional Chinese characters (繁體中文) with Taiwan-specific terminology.
|
||||
- NEVER mix Simplified and Traditional Chinese characters within the same locale.
|
||||
`
|
||||
});
|
||||
|
||||
@@ -50,7 +50,7 @@ export class Topbar {
|
||||
workflowName: string,
|
||||
command: 'Save' | 'Save As' | 'Export'
|
||||
) {
|
||||
await this.triggerTopbarCommand(['Workflow', command])
|
||||
await this.triggerTopbarCommand(['File', command])
|
||||
await this.getSaveDialog().fill(workflowName)
|
||||
await this.page.keyboard.press('Enter')
|
||||
|
||||
@@ -72,8 +72,8 @@ export class Topbar {
|
||||
}
|
||||
|
||||
async triggerTopbarCommand(path: string[]) {
|
||||
if (path.length < 2) {
|
||||
throw new Error('Path is too short')
|
||||
if (path.length < 1) {
|
||||
throw new Error('Path cannot be empty')
|
||||
}
|
||||
|
||||
const menu = await this.openTopbarMenu()
|
||||
@@ -85,6 +85,13 @@ export class Topbar {
|
||||
.locator('.p-tieredmenu-item')
|
||||
.filter({ has: topLevelMenuItem })
|
||||
await topLevelMenu.waitFor({ state: 'visible' })
|
||||
|
||||
// Handle top-level commands (like "New")
|
||||
if (path.length === 1) {
|
||||
await topLevelMenuItem.click()
|
||||
return
|
||||
}
|
||||
|
||||
await topLevelMenu.hover()
|
||||
|
||||
let currentMenu = topLevelMenu
|
||||
|
||||
@@ -268,10 +268,7 @@ test.describe('Group Node', () => {
|
||||
await comfyPage.setSetting('Comfy.ConfirmClear', false)
|
||||
|
||||
// Clear workflow
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand([
|
||||
'Edit',
|
||||
'Clear Workflow'
|
||||
])
|
||||
await comfyPage.executeCommand('Comfy.ClearWorkflow')
|
||||
|
||||
await comfyPage.ctrlV()
|
||||
await verifyNodeLoaded(comfyPage, 1)
|
||||
@@ -280,7 +277,7 @@ test.describe('Group Node', () => {
|
||||
test('Copies and pastes group node into a newly created blank workflow', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
await comfyPage.ctrlV()
|
||||
await verifyNodeLoaded(comfyPage, 1)
|
||||
})
|
||||
@@ -296,7 +293,7 @@ test.describe('Group Node', () => {
|
||||
test('Serializes group node after copy and paste across workflows', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
await comfyPage.ctrlV()
|
||||
const currentGraphState = await comfyPage.page.evaluate(() =>
|
||||
window['app'].graph.serialize()
|
||||
|
||||
@@ -684,7 +684,7 @@ test.describe('Load workflow', () => {
|
||||
workflowA = generateUniqueFilename()
|
||||
await comfyPage.menu.topbar.saveWorkflow(workflowA)
|
||||
workflowB = generateUniqueFilename()
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
await comfyPage.menu.topbar.saveWorkflow(workflowB)
|
||||
|
||||
// Wait for localStorage to persist the workflow paths before reloading
|
||||
|
||||
@@ -75,7 +75,7 @@ test.describe('Menu', () => {
|
||||
|
||||
test('Displays keybinding next to item', async ({ comfyPage }) => {
|
||||
await comfyPage.menu.topbar.openTopbarMenu()
|
||||
const workflowMenuItem = comfyPage.menu.topbar.getMenuItem('Workflow')
|
||||
const workflowMenuItem = comfyPage.menu.topbar.getMenuItem('File')
|
||||
await workflowMenuItem.hover()
|
||||
const exportTag = comfyPage.page.locator('.keybinding-tag', {
|
||||
hasText: 'Ctrl + s'
|
||||
|
||||
@@ -18,7 +18,7 @@ test.describe('Reroute Node', () => {
|
||||
[workflowName]: workflowName
|
||||
})
|
||||
await comfyPage.setup()
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
|
||||
// Insert the workflow
|
||||
const workflowsTab = comfyPage.menu.workflowsTab
|
||||
|
||||
@@ -63,7 +63,7 @@ test.describe('Workflow Tab Thumbnails', () => {
|
||||
test('Should show thumbnail when hovering over a non-active tab', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
const thumbnailImg = await getTabThumbnailImage(
|
||||
comfyPage,
|
||||
0,
|
||||
@@ -73,7 +73,7 @@ test.describe('Workflow Tab Thumbnails', () => {
|
||||
})
|
||||
|
||||
test('Should not show thumbnail for active tab', async ({ comfyPage }) => {
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
const thumbnailImg = await getTabThumbnailImage(
|
||||
comfyPage,
|
||||
1,
|
||||
@@ -105,7 +105,7 @@ test.describe('Workflow Tab Thumbnails', () => {
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Create a new workflow (tab 1) which will be empty
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Now we have two tabs: tab 0 (default workflow with nodes) and tab 1 (empty)
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"version": "1.26.2",
|
||||
"version": "1.26.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"version": "1.26.2",
|
||||
"version": "1.26.4",
|
||||
"license": "GPL-3.0-only",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"private": true,
|
||||
"version": "1.26.2",
|
||||
"version": "1.26.4",
|
||||
"type": "module",
|
||||
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
|
||||
"homepage": "https://comfy.org",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
>
|
||||
<div class="shortcut-info flex-grow pr-4">
|
||||
<div class="shortcut-name text-sm font-medium">
|
||||
{{ command.label || command.id }}
|
||||
{{ t(`commands.${normalizeI18nKey(command.id)}.label`) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -50,6 +50,7 @@ import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import type { ComfyCommandImpl } from '@/stores/commandStore'
|
||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
||||
@@ -90,18 +90,16 @@ const closeDialog = () => {
|
||||
const canvasStore = useCanvasStore()
|
||||
|
||||
const addNode = (nodeDef: ComfyNodeDefImpl) => {
|
||||
if (!triggerEvent) {
|
||||
console.warn('The trigger event was undefined when addNode was called.')
|
||||
return
|
||||
}
|
||||
|
||||
const node = litegraphService.addNodeOnGraph(nodeDef, {
|
||||
pos: getNewNodeLocation()
|
||||
})
|
||||
|
||||
if (disconnectOnReset) {
|
||||
if (disconnectOnReset && triggerEvent) {
|
||||
canvasStore.getCanvas().linkConnector.connectToNode(node, triggerEvent)
|
||||
} else if (!triggerEvent) {
|
||||
console.warn('The trigger event was undefined when addNode was called.')
|
||||
}
|
||||
|
||||
disconnectOnReset = false
|
||||
|
||||
// Notify changeTracker - new step should be added
|
||||
|
||||
@@ -58,11 +58,12 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted } from 'vue'
|
||||
|
||||
import HelpCenterMenuContent from '@/components/helpcenter/HelpCenterMenuContent.vue'
|
||||
import ReleaseNotificationToast from '@/components/helpcenter/ReleaseNotificationToast.vue'
|
||||
import WhatsNewPopup from '@/components/helpcenter/WhatsNewPopup.vue'
|
||||
import { useHelpCenterStore } from '@/stores/helpCenterStore'
|
||||
import { useReleaseStore } from '@/stores/releaseStore'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
|
||||
@@ -70,8 +71,9 @@ import SidebarIcon from './SidebarIcon.vue'
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const releaseStore = useReleaseStore()
|
||||
const helpCenterStore = useHelpCenterStore()
|
||||
const { shouldShowRedDot } = storeToRefs(releaseStore)
|
||||
const isHelpCenterVisible = ref(false)
|
||||
const { isVisible: isHelpCenterVisible } = storeToRefs(helpCenterStore)
|
||||
|
||||
const sidebarLocation = computed(() =>
|
||||
settingStore.get('Comfy.Sidebar.Location')
|
||||
@@ -80,11 +82,11 @@ const sidebarLocation = computed(() =>
|
||||
const sidebarSize = computed(() => settingStore.get('Comfy.Sidebar.Size'))
|
||||
|
||||
const toggleHelpCenter = () => {
|
||||
isHelpCenterVisible.value = !isHelpCenterVisible.value
|
||||
helpCenterStore.toggle()
|
||||
}
|
||||
|
||||
const closeHelpCenter = () => {
|
||||
isHelpCenterVisible.value = false
|
||||
helpCenterStore.hide()
|
||||
}
|
||||
|
||||
// Initialize release store on mount
|
||||
@@ -130,6 +132,7 @@ onMounted(async () => {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
|
||||
@@ -30,11 +30,18 @@
|
||||
/>
|
||||
<Button
|
||||
v-tooltip.bottom="$t('sideToolbar.nodeLibraryTab.resetView')"
|
||||
icon="pi pi-refresh"
|
||||
icon="pi pi-filter-slash"
|
||||
text
|
||||
severity="secondary"
|
||||
@click="resetOrganization"
|
||||
/>
|
||||
<Button
|
||||
v-tooltip.bottom="$t('menu.refresh')"
|
||||
icon="pi pi-refresh"
|
||||
text
|
||||
severity="secondary"
|
||||
@click="() => commandStore.execute('Comfy.RefreshNodeDefinitions')"
|
||||
/>
|
||||
<Popover ref="groupingPopover">
|
||||
<div class="flex flex-col gap-1 p-2">
|
||||
<Button
|
||||
@@ -139,6 +146,7 @@ import {
|
||||
DEFAULT_SORTING_ID,
|
||||
nodeOrganizationService
|
||||
} from '@/services/nodeOrganizationService'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useNodeBookmarkStore } from '@/stores/nodeBookmarkStore'
|
||||
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
|
||||
import { useNodeHelpStore } from '@/stores/workspace/nodeHelpStore'
|
||||
@@ -155,6 +163,7 @@ import NodeBookmarkTreeExplorer from './nodeLibrary/NodeBookmarkTreeExplorer.vue
|
||||
const nodeDefStore = useNodeDefStore()
|
||||
const nodeBookmarkStore = useNodeBookmarkStore()
|
||||
const nodeHelpStore = useNodeHelpStore()
|
||||
const commandStore = useCommandStore()
|
||||
const expandedKeys = ref<Record<string, boolean>>({})
|
||||
const { expandNode, toggleNodeOnEvent } = useTreeExpansion(expandedKeys)
|
||||
|
||||
|
||||
@@ -55,9 +55,30 @@
|
||||
v-bind="props.action"
|
||||
:href="item.url"
|
||||
target="_blank"
|
||||
:class="typeof item.class === 'function' ? item.class() : item.class"
|
||||
@mousedown="
|
||||
isZoomCommand(item) ? handleZoomMouseDown(item, $event) : undefined
|
||||
"
|
||||
@click="isZoomCommand(item) ? handleZoomClick($event) : undefined"
|
||||
>
|
||||
<span v-if="item.icon" class="p-menubar-item-icon" :class="item.icon" />
|
||||
<i
|
||||
v-if="hasActiveStateSiblings(item)"
|
||||
class="p-menubar-item-icon pi pi-check text-sm"
|
||||
:class="{ invisible: !item.comfyCommand?.active?.() }"
|
||||
/>
|
||||
<span
|
||||
v-else-if="
|
||||
item.icon && item.comfyCommand?.id !== 'Comfy.NewBlankWorkflow'
|
||||
"
|
||||
class="p-menubar-item-icon"
|
||||
:class="item.icon"
|
||||
/>
|
||||
<span class="p-menubar-item-label text-nowrap">{{ item.label }}</span>
|
||||
<i
|
||||
v-if="item.comfyCommand?.id === 'Comfy.NewBlankWorkflow'"
|
||||
class="ml-auto"
|
||||
:class="item.icon"
|
||||
/>
|
||||
<span
|
||||
v-if="item?.comfyCommand?.keybinding"
|
||||
class="ml-auto border border-surface rounded text-muted text-xs text-nowrap p-1 keybinding-tag"
|
||||
@@ -94,6 +115,7 @@ import { useSettingStore } from '@/stores/settingStore'
|
||||
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
|
||||
import { showNativeSystemMenu } from '@/utils/envUtil'
|
||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
||||
import { whileMouseDown } from '@/utils/mouseDownUtil'
|
||||
|
||||
const colorPaletteStore = useColorPaletteStore()
|
||||
const menuItemsStore = useMenuItemStore()
|
||||
@@ -163,16 +185,22 @@ const extraMenuItems: MenuItem[] = [
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
key: 'manage-extensions',
|
||||
label: t('menu.manageExtensions'),
|
||||
icon: 'mdi mdi-puzzle-outline',
|
||||
command: showManageExtensions
|
||||
key: 'browse-templates',
|
||||
label: t('menuLabels.Browse Templates'),
|
||||
icon: 'pi pi-folder-open',
|
||||
command: () => commandStore.execute('Comfy.BrowseTemplates')
|
||||
},
|
||||
{
|
||||
key: 'settings',
|
||||
label: t('g.settings'),
|
||||
icon: 'mdi mdi-cog-outline',
|
||||
command: () => showSettings()
|
||||
},
|
||||
{
|
||||
key: 'manage-extensions',
|
||||
label: t('menu.manageExtensions'),
|
||||
icon: 'mdi mdi-puzzle-outline',
|
||||
command: showManageExtensions
|
||||
}
|
||||
]
|
||||
|
||||
@@ -237,6 +265,36 @@ const onMenuShow = () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const isZoomCommand = (item: MenuItem) => {
|
||||
return (
|
||||
item.comfyCommand?.id === 'Comfy.Canvas.ZoomIn' ||
|
||||
item.comfyCommand?.id === 'Comfy.Canvas.ZoomOut'
|
||||
)
|
||||
}
|
||||
|
||||
const handleZoomMouseDown = (item: MenuItem, event: MouseEvent) => {
|
||||
if (item.comfyCommand) {
|
||||
whileMouseDown(
|
||||
event,
|
||||
async () => {
|
||||
await commandStore.execute(item.comfyCommand!.id)
|
||||
},
|
||||
50
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const handleZoomClick = (event: MouseEvent) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
// Prevent the menu from closing for zoom commands
|
||||
return false
|
||||
}
|
||||
|
||||
const hasActiveStateSiblings = (item: MenuItem): boolean => {
|
||||
return menuItemsStore.menuItemHasActiveStateChildren[item.parentPath]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1362,6 +1362,12 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
|
||||
return '$0.0004/$0.0016 per 1K tokens'
|
||||
} else if (model.includes('gpt-4.1')) {
|
||||
return '$0.002/$0.008 per 1K tokens'
|
||||
} else if (model.includes('gpt-5-nano')) {
|
||||
return '$0.00005/$0.0004 per 1K tokens'
|
||||
} else if (model.includes('gpt-5-mini')) {
|
||||
return '$0.00025/$0.002 per 1K tokens'
|
||||
} else if (model.includes('gpt-5')) {
|
||||
return '$0.00125/$0.01 per 1K tokens'
|
||||
}
|
||||
return 'Token-based'
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import { useWorkflowService } from '@/services/workflowService'
|
||||
import type { ComfyCommand } from '@/stores/commandStore'
|
||||
import { useExecutionStore } from '@/stores/executionStore'
|
||||
import { useCanvasStore, useTitleEditorStore } from '@/stores/graphStore'
|
||||
import { useHelpCenterStore } from '@/stores/helpCenterStore'
|
||||
import { useNodeOutputStore } from '@/stores/imagePreviewStore'
|
||||
import { useQueueSettingsStore, useQueueStore } from '@/stores/queueStore'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
@@ -278,6 +279,7 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
id: 'Comfy.Canvas.FitView',
|
||||
icon: 'pi pi-expand',
|
||||
label: 'Fit view to selected nodes',
|
||||
menubarLabel: 'Zoom to fit',
|
||||
category: 'view-controls' as const,
|
||||
function: () => {
|
||||
if (app.canvas.empty) {
|
||||
@@ -303,6 +305,7 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
id: 'Comfy.Canvas.ToggleLinkVisibility',
|
||||
icon: 'pi pi-eye',
|
||||
label: 'Canvas Toggle Link Visibility',
|
||||
menubarLabel: 'Node Links',
|
||||
versionAdded: '1.3.6',
|
||||
|
||||
function: (() => {
|
||||
@@ -324,12 +327,15 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
)
|
||||
}
|
||||
}
|
||||
})()
|
||||
})(),
|
||||
active: () =>
|
||||
useSettingStore().get('Comfy.LinkRenderMode') !== LiteGraph.HIDDEN_LINK
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Canvas.ToggleMinimap',
|
||||
icon: 'pi pi-map',
|
||||
label: 'Canvas Toggle Minimap',
|
||||
menubarLabel: 'Minimap',
|
||||
versionAdded: '1.24.1',
|
||||
function: async () => {
|
||||
const settingStore = useSettingStore()
|
||||
@@ -337,7 +343,8 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
'Comfy.Minimap.Visible',
|
||||
!settingStore.get('Comfy.Minimap.Visible')
|
||||
)
|
||||
}
|
||||
},
|
||||
active: () => useSettingStore().get('Comfy.Minimap.Visible')
|
||||
},
|
||||
{
|
||||
id: 'Comfy.QueuePrompt',
|
||||
@@ -541,21 +548,25 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
id: 'Workspace.ToggleBottomPanel',
|
||||
icon: 'pi pi-list',
|
||||
label: 'Toggle Bottom Panel',
|
||||
menubarLabel: 'Bottom Panel',
|
||||
versionAdded: '1.3.22',
|
||||
category: 'view-controls' as const,
|
||||
function: () => {
|
||||
bottomPanelStore.toggleBottomPanel()
|
||||
}
|
||||
},
|
||||
active: () => bottomPanelStore.bottomPanelVisible
|
||||
},
|
||||
{
|
||||
id: 'Workspace.ToggleFocusMode',
|
||||
icon: 'pi pi-eye',
|
||||
label: 'Toggle Focus Mode',
|
||||
menubarLabel: 'Focus Mode',
|
||||
versionAdded: '1.3.27',
|
||||
category: 'view-controls' as const,
|
||||
function: () => {
|
||||
useWorkspaceStore().toggleFocusMode()
|
||||
}
|
||||
},
|
||||
active: () => useWorkspaceStore().focusMode
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Graph.FitGroupToContents',
|
||||
@@ -815,6 +826,34 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
graph.unpackSubgraph(subgraphNode)
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.OpenManagerDialog',
|
||||
icon: 'mdi mdi-puzzle-outline',
|
||||
label: 'Manager',
|
||||
function: () => {
|
||||
dialogService.showManagerDialog()
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.ToggleHelpCenter',
|
||||
icon: 'pi pi-question-circle',
|
||||
label: 'Help Center',
|
||||
function: () => {
|
||||
useHelpCenterStore().toggle()
|
||||
},
|
||||
active: () => useHelpCenterStore().isVisible
|
||||
},
|
||||
{
|
||||
id: 'Comfy.ToggleCanvasInfo',
|
||||
icon: 'pi pi-info-circle',
|
||||
label: 'Canvas Performance',
|
||||
function: async () => {
|
||||
const settingStore = useSettingStore()
|
||||
const currentValue = settingStore.get('Comfy.Graph.CanvasInfo')
|
||||
await settingStore.set('Comfy.Graph.CanvasInfo', !currentValue)
|
||||
},
|
||||
active: () => useSettingStore().get('Comfy.Graph.CanvasInfo')
|
||||
},
|
||||
{
|
||||
id: 'Workspace.ToggleBottomPanel.Shortcuts',
|
||||
icon: 'pi pi-key',
|
||||
|
||||
@@ -19,6 +19,7 @@ export const useLitegraphSettings = () => {
|
||||
const canvasInfoEnabled = settingStore.get('Comfy.Graph.CanvasInfo')
|
||||
if (canvasStore.canvas) {
|
||||
canvasStore.canvas.show_info = canvasInfoEnabled
|
||||
canvasStore.canvas.draw(false, true)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import { useCanvasTransformSync } from '@/composables/canvas/useCanvasTransformS
|
||||
import { LGraphEventMode, LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import type { NodeId } from '@/schemas/comfyWorkflowSchema'
|
||||
import { api } from '@/scripts/api'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useCanvasStore } from '@/stores/graphStore'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
import { useWorkflowStore } from '@/stores/workflowStore'
|
||||
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
|
||||
import { adjustColor } from '@/utils/colorUtil'
|
||||
|
||||
@@ -27,6 +27,7 @@ export type MinimapOptionKey =
|
||||
export function useMinimap() {
|
||||
const settingStore = useSettingStore()
|
||||
const canvasStore = useCanvasStore()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const colorPaletteStore = useColorPaletteStore()
|
||||
|
||||
const containerRef = ref<HTMLDivElement>()
|
||||
@@ -147,7 +148,11 @@ export function useMinimap() {
|
||||
}
|
||||
|
||||
const canvas = computed(() => canvasStore.canvas)
|
||||
const graph = ref(app.canvas?.graph)
|
||||
const graph = computed(() => {
|
||||
// If we're in a subgraph, use that; otherwise use the canvas graph
|
||||
const activeSubgraph = workflowStore.activeSubgraph
|
||||
return activeSubgraph || canvas.value?.graph
|
||||
})
|
||||
|
||||
const containerStyles = computed(() => ({
|
||||
width: `${width}px`,
|
||||
@@ -627,7 +632,8 @@ export function useMinimap() {
|
||||
c.setDirty(true, true)
|
||||
}
|
||||
|
||||
let originalCallbacks: GraphCallbacks = {}
|
||||
// Map to store original callbacks per graph ID
|
||||
const originalCallbacksMap = new Map<string, GraphCallbacks>()
|
||||
|
||||
const handleGraphChanged = useThrottleFn(() => {
|
||||
needsFullRedraw.value = true
|
||||
@@ -641,11 +647,18 @@ export function useMinimap() {
|
||||
const g = graph.value
|
||||
if (!g) return
|
||||
|
||||
originalCallbacks = {
|
||||
// Check if we've already wrapped this graph's callbacks
|
||||
if (originalCallbacksMap.has(g.id)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Store the original callbacks for this graph
|
||||
const originalCallbacks: GraphCallbacks = {
|
||||
onNodeAdded: g.onNodeAdded,
|
||||
onNodeRemoved: g.onNodeRemoved,
|
||||
onConnectionChange: g.onConnectionChange
|
||||
}
|
||||
originalCallbacksMap.set(g.id, originalCallbacks)
|
||||
|
||||
g.onNodeAdded = function (node) {
|
||||
originalCallbacks.onNodeAdded?.call(this, node)
|
||||
@@ -670,15 +683,18 @@ export function useMinimap() {
|
||||
const g = graph.value
|
||||
if (!g) return
|
||||
|
||||
if (originalCallbacks.onNodeAdded !== undefined) {
|
||||
g.onNodeAdded = originalCallbacks.onNodeAdded
|
||||
}
|
||||
if (originalCallbacks.onNodeRemoved !== undefined) {
|
||||
g.onNodeRemoved = originalCallbacks.onNodeRemoved
|
||||
}
|
||||
if (originalCallbacks.onConnectionChange !== undefined) {
|
||||
g.onConnectionChange = originalCallbacks.onConnectionChange
|
||||
const originalCallbacks = originalCallbacksMap.get(g.id)
|
||||
if (!originalCallbacks) {
|
||||
throw new Error(
|
||||
'Attempted to cleanup event listeners for graph that was never set up'
|
||||
)
|
||||
}
|
||||
|
||||
g.onNodeAdded = originalCallbacks.onNodeAdded
|
||||
g.onNodeRemoved = originalCallbacks.onNodeRemoved
|
||||
g.onConnectionChange = originalCallbacks.onConnectionChange
|
||||
|
||||
originalCallbacksMap.delete(g.id)
|
||||
}
|
||||
|
||||
const init = async () => {
|
||||
@@ -751,6 +767,19 @@ export function useMinimap() {
|
||||
{ immediate: true, flush: 'post' }
|
||||
)
|
||||
|
||||
// Watch for graph changes (e.g., when navigating to/from subgraphs)
|
||||
watch(graph, (newGraph, oldGraph) => {
|
||||
if (newGraph && newGraph !== oldGraph) {
|
||||
cleanupEventListeners()
|
||||
setupEventListeners()
|
||||
needsFullRedraw.value = true
|
||||
updateFlags.value.bounds = true
|
||||
updateFlags.value.nodes = true
|
||||
updateFlags.value.connections = true
|
||||
updateMinimap()
|
||||
}
|
||||
})
|
||||
|
||||
watch(visible, async (isVisible) => {
|
||||
if (isVisible) {
|
||||
if (containerRef.value) {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
export const CORE_MENU_COMMANDS = [
|
||||
[['Workflow'], ['Comfy.NewBlankWorkflow']],
|
||||
[['Workflow'], ['Comfy.OpenWorkflow', 'Comfy.BrowseTemplates']],
|
||||
[[], ['Comfy.NewBlankWorkflow']],
|
||||
[[], []], // Separator after New
|
||||
[['File'], ['Comfy.OpenWorkflow']],
|
||||
[
|
||||
['Workflow'],
|
||||
['File'],
|
||||
[
|
||||
'Comfy.SaveWorkflow',
|
||||
'Comfy.SaveWorkflowAs',
|
||||
@@ -11,8 +12,6 @@ export const CORE_MENU_COMMANDS = [
|
||||
]
|
||||
],
|
||||
[['Edit'], ['Comfy.Undo', 'Comfy.Redo']],
|
||||
[['Edit'], ['Comfy.RefreshNodeDefinitions']],
|
||||
[['Edit'], ['Comfy.ClearWorkflow']],
|
||||
[['Edit'], ['Comfy.OpenClipspace']],
|
||||
[
|
||||
['Help'],
|
||||
|
||||
@@ -772,7 +772,8 @@ export const CORE_SETTINGS: SettingParams[] = [
|
||||
{
|
||||
id: 'LiteGraph.Canvas.LowQualityRenderingZoomThreshold',
|
||||
name: 'Low quality rendering zoom threshold',
|
||||
tooltip: 'Render low quality shapes when zoomed out',
|
||||
tooltip:
|
||||
'Zoom level threshold for performance mode. Lower values (0.1) = quality at all zoom levels. Higher values (1.0) = performance mode even when zoomed in. Performance mode simplifies rendering by hiding text labels, shadows, and details.',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 0.1,
|
||||
|
||||
@@ -3608,6 +3608,7 @@ export class LGraphCanvas
|
||||
subgraphs: []
|
||||
}
|
||||
|
||||
// NOTE: logic for traversing nested subgraphs depends on this being a set.
|
||||
const subgraphs = new Set<Subgraph>()
|
||||
|
||||
// Create serialisable objects
|
||||
@@ -3646,8 +3647,13 @@ export class LGraphCanvas
|
||||
}
|
||||
|
||||
// Add unique subgraph entries
|
||||
// TODO: Must find all nested subgraphs
|
||||
// NOTE: subgraphs is appended to mid iteration.
|
||||
for (const subgraph of subgraphs) {
|
||||
for (const node of subgraph.nodes) {
|
||||
if (node instanceof SubgraphNode) {
|
||||
subgraphs.add(node.subgraph)
|
||||
}
|
||||
}
|
||||
const cloned = subgraph.clone(true).asSerialisable()
|
||||
serialisable.subgraphs.push(cloned)
|
||||
}
|
||||
@@ -3764,12 +3770,19 @@ export class LGraphCanvas
|
||||
created.push(group)
|
||||
}
|
||||
|
||||
// Update subgraph ids with nesting
|
||||
function updateSubgraphIds(nodes: { type: string }[]) {
|
||||
for (const info of nodes) {
|
||||
const subgraph = results.subgraphs.get(info.type)
|
||||
if (!subgraph) continue
|
||||
info.type = subgraph.id
|
||||
updateSubgraphIds(subgraph.nodes)
|
||||
}
|
||||
}
|
||||
updateSubgraphIds(parsed.nodes)
|
||||
|
||||
// Nodes
|
||||
for (const info of parsed.nodes) {
|
||||
// If the subgraph was cloned, update references to use the new subgraph ID.
|
||||
const subgraph = results.subgraphs.get(info.type)
|
||||
if (subgraph) info.type = subgraph.id
|
||||
|
||||
const node = info.type == null ? null : LiteGraph.createNode(info.type)
|
||||
if (!node) {
|
||||
// failedNodes.push(info)
|
||||
|
||||
@@ -414,6 +414,18 @@ export class LLink implements LinkSegment, Serialisable<SerialisableLLink> {
|
||||
* If `input` or `output`, reroutes will not be automatically removed, and retain a connection to the input or output, respectively.
|
||||
*/
|
||||
disconnect(network: LinkNetwork, keepReroutes?: 'input' | 'output'): void {
|
||||
// Clean up the target node's input slot
|
||||
if (this.target_id !== -1) {
|
||||
const targetNode = network.getNodeById(this.target_id)
|
||||
if (targetNode) {
|
||||
const targetInput = targetNode.inputs?.[this.target_slot]
|
||||
if (targetInput && targetInput.link === this.id) {
|
||||
targetInput.link = null
|
||||
targetNode.setDirtyCanvas?.(true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const reroutes = LLink.getReroutes(network, this)
|
||||
|
||||
const lastReroute = reroutes.at(-1)
|
||||
|
||||
@@ -135,6 +135,10 @@ export class FloatingRenderLink implements RenderLink {
|
||||
return true
|
||||
}
|
||||
|
||||
canConnectToSubgraphInput(input: SubgraphInput): boolean {
|
||||
return this.toType === 'output' && input.isValidTarget(this.fromSlot)
|
||||
}
|
||||
|
||||
connectToInput(
|
||||
node: LGraphNode,
|
||||
input: INodeInputSlot,
|
||||
|
||||
@@ -681,6 +681,20 @@ export class LinkConnector {
|
||||
let targetSlot = input
|
||||
|
||||
for (const link of renderLinks) {
|
||||
// Validate the connection type before proceeding
|
||||
if (
|
||||
'canConnectToSubgraphInput' in link &&
|
||||
!link.canConnectToSubgraphInput(targetSlot)
|
||||
) {
|
||||
console.warn(
|
||||
'Invalid connection type',
|
||||
link.fromSlot.type,
|
||||
'->',
|
||||
targetSlot.type
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
link.connectToSubgraphInput(targetSlot, this.events)
|
||||
|
||||
// If we just connected to an EmptySubgraphInput, check if we should reuse the slot
|
||||
@@ -941,6 +955,14 @@ export class LinkConnector {
|
||||
)
|
||||
}
|
||||
|
||||
isSubgraphInputValidDrop(input: SubgraphInput): boolean {
|
||||
return this.renderLinks.some(
|
||||
(link) =>
|
||||
'canConnectToSubgraphInput' in link &&
|
||||
link.canConnectToSubgraphInput(input)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a reroute is a valid drop target for any of the links being connected.
|
||||
* @param reroute The reroute that would be dropped on.
|
||||
|
||||
@@ -55,6 +55,10 @@ export class MovingOutputLink extends MovingLinkBase {
|
||||
return reroute.origin_id !== this.outputNode.id
|
||||
}
|
||||
|
||||
canConnectToSubgraphInput(input: SubgraphInput): boolean {
|
||||
return input.isValidTarget(this.fromSlot)
|
||||
}
|
||||
|
||||
connectToInput(): never {
|
||||
throw new Error('MovingOutputLink cannot connect to an input.')
|
||||
}
|
||||
|
||||
@@ -58,6 +58,10 @@ export class ToOutputRenderLink implements RenderLink {
|
||||
return true
|
||||
}
|
||||
|
||||
canConnectToSubgraphInput(input: SubgraphInput): boolean {
|
||||
return input.isValidTarget(this.fromSlot)
|
||||
}
|
||||
|
||||
connectToOutput(
|
||||
node: LGraphNode,
|
||||
output: INodeOutputSlot,
|
||||
|
||||
@@ -16,7 +16,10 @@ import type {
|
||||
GraphOrSubgraph,
|
||||
Subgraph
|
||||
} from '@/lib/litegraph/src/subgraph/Subgraph'
|
||||
import type { ExportedSubgraphInstance } from '@/lib/litegraph/src/types/serialisation'
|
||||
import type {
|
||||
ExportedSubgraphInstance,
|
||||
ISerialisedNode
|
||||
} from '@/lib/litegraph/src/types/serialisation'
|
||||
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
|
||||
import type { UUID } from '@/lib/litegraph/src/utils/uuid'
|
||||
import { toConcreteWidget } from '@/lib/litegraph/src/widgets/widgetMap'
|
||||
@@ -266,10 +269,14 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
const subgraphInput = this.subgraph.inputNode.slots.find(
|
||||
(slot) => slot.name === input.name
|
||||
)
|
||||
if (!subgraphInput)
|
||||
throw new Error(
|
||||
`[SubgraphNode.configure] No subgraph input found for input ${input.name}`
|
||||
if (!subgraphInput) {
|
||||
// Skip inputs that don't exist in the subgraph definition
|
||||
// This can happen when loading workflows with dynamically added inputs
|
||||
console.warn(
|
||||
`[SubgraphNode.configure] No subgraph input found for input ${input.name}, skipping`
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
this.#addSubgraphInputListeners(subgraphInput, input)
|
||||
|
||||
@@ -536,4 +543,36 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes widget values from this SubgraphNode instance to the
|
||||
* corresponding widgets in the subgraph definition before serialization.
|
||||
* This ensures nested subgraph widget values are preserved when saving.
|
||||
*/
|
||||
override serialize(): ISerialisedNode {
|
||||
// Sync widget values to subgraph definition before serialization
|
||||
for (let i = 0; i < this.widgets.length; i++) {
|
||||
const widget = this.widgets[i]
|
||||
const input = this.inputs.find((inp) => inp.name === widget.name)
|
||||
|
||||
if (input) {
|
||||
const subgraphInput = this.subgraph.inputNode.slots.find(
|
||||
(slot) => slot.name === input.name
|
||||
)
|
||||
|
||||
if (subgraphInput) {
|
||||
// Find all widgets connected to this subgraph input
|
||||
const connectedWidgets = subgraphInput.getConnectedWidgets()
|
||||
|
||||
// Update the value of all connected widgets
|
||||
for (const connectedWidget of connectedWidgets) {
|
||||
connectedWidget.value = widget.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent serialize method
|
||||
return super.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { LLink } from '@/lib/litegraph/src/litegraph'
|
||||
import { LGraph, LGraphNode, LLink } from '@/lib/litegraph/src/litegraph'
|
||||
|
||||
import { test } from './testExtensions'
|
||||
|
||||
@@ -14,4 +14,84 @@ describe('LLink', () => {
|
||||
const link = new LLink(1, 'float', 4, 2, 5, 3)
|
||||
expect(link.serialize()).toMatchSnapshot('Basic')
|
||||
})
|
||||
|
||||
describe('disconnect', () => {
|
||||
it('should clear the target input link reference when disconnecting', () => {
|
||||
// Create a graph and nodes
|
||||
const graph = new LGraph()
|
||||
const sourceNode = new LGraphNode('Source')
|
||||
const targetNode = new LGraphNode('Target')
|
||||
|
||||
// Add nodes to graph
|
||||
graph.add(sourceNode)
|
||||
graph.add(targetNode)
|
||||
|
||||
// Add slots
|
||||
sourceNode.addOutput('out', 'number')
|
||||
targetNode.addInput('in', 'number')
|
||||
|
||||
// Connect the nodes
|
||||
const link = sourceNode.connect(0, targetNode, 0)
|
||||
expect(link).toBeDefined()
|
||||
expect(targetNode.inputs[0].link).toBe(link?.id)
|
||||
|
||||
// Mock setDirtyCanvas
|
||||
const setDirtyCanvasSpy = vi.spyOn(targetNode, 'setDirtyCanvas')
|
||||
|
||||
// Disconnect the link
|
||||
link?.disconnect(graph)
|
||||
|
||||
// Verify the target input's link reference is cleared
|
||||
expect(targetNode.inputs[0].link).toBeNull()
|
||||
|
||||
// Verify setDirtyCanvas was called
|
||||
expect(setDirtyCanvasSpy).toHaveBeenCalledWith(true, false)
|
||||
})
|
||||
|
||||
it('should handle disconnecting when target node is not found', () => {
|
||||
// Create a link with invalid target
|
||||
const graph = new LGraph()
|
||||
const link = new LLink(1, 'number', 1, 0, 999, 0) // Invalid target id
|
||||
|
||||
// Should not throw when disconnecting
|
||||
expect(() => link.disconnect(graph)).not.toThrow()
|
||||
})
|
||||
|
||||
it('should only clear link reference if it matches the current link id', () => {
|
||||
// Create a graph and nodes
|
||||
const graph = new LGraph()
|
||||
const sourceNode1 = new LGraphNode('Source1')
|
||||
const sourceNode2 = new LGraphNode('Source2')
|
||||
const targetNode = new LGraphNode('Target')
|
||||
|
||||
// Add nodes to graph
|
||||
graph.add(sourceNode1)
|
||||
graph.add(sourceNode2)
|
||||
graph.add(targetNode)
|
||||
|
||||
// Add slots
|
||||
sourceNode1.addOutput('out', 'number')
|
||||
sourceNode2.addOutput('out', 'number')
|
||||
targetNode.addInput('in', 'number')
|
||||
|
||||
// Create first connection
|
||||
const link1 = sourceNode1.connect(0, targetNode, 0)
|
||||
expect(link1).toBeDefined()
|
||||
|
||||
// Disconnect first connection
|
||||
targetNode.disconnectInput(0)
|
||||
|
||||
// Create second connection
|
||||
const link2 = sourceNode2.connect(0, targetNode, 0)
|
||||
expect(link2).toBeDefined()
|
||||
expect(targetNode.inputs[0].link).toBe(link2?.id)
|
||||
|
||||
// Try to disconnect the first link (which is already disconnected)
|
||||
// It should not affect the current connection
|
||||
link1?.disconnect(graph)
|
||||
|
||||
// The input should still have the second link
|
||||
expect(targetNode.inputs[0].link).toBe(link2?.id)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { LinkConnector } from '@/lib/litegraph/src/canvas/LinkConnector'
|
||||
import { MovingOutputLink } from '@/lib/litegraph/src/canvas/MovingOutputLink'
|
||||
import { ToOutputRenderLink } from '@/lib/litegraph/src/canvas/ToOutputRenderLink'
|
||||
import { LGraphNode, LLink } from '@/lib/litegraph/src/litegraph'
|
||||
import { NodeInputSlot } from '@/lib/litegraph/src/node/NodeInputSlot'
|
||||
|
||||
import { createTestSubgraph } from '../subgraph/fixtures/subgraphHelpers'
|
||||
|
||||
describe('LinkConnector SubgraphInput connection validation', () => {
|
||||
let connector: LinkConnector
|
||||
const mockSetConnectingLinks = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
connector = new LinkConnector(mockSetConnectingLinks)
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('MovingOutputLink validation', () => {
|
||||
it('should implement canConnectToSubgraphInput method', () => {
|
||||
const subgraph = createTestSubgraph({
|
||||
inputs: [{ name: 'number_input', type: 'number' }]
|
||||
})
|
||||
|
||||
const sourceNode = new LGraphNode('SourceNode')
|
||||
sourceNode.addOutput('number_out', 'number')
|
||||
subgraph.add(sourceNode)
|
||||
|
||||
const targetNode = new LGraphNode('TargetNode')
|
||||
targetNode.addInput('number_in', 'number')
|
||||
subgraph.add(targetNode)
|
||||
|
||||
const link = new LLink(1, 'number', sourceNode.id, 0, targetNode.id, 0)
|
||||
subgraph._links.set(link.id, link)
|
||||
|
||||
const movingLink = new MovingOutputLink(subgraph, link)
|
||||
|
||||
// Verify the method exists
|
||||
expect(typeof movingLink.canConnectToSubgraphInput).toBe('function')
|
||||
})
|
||||
|
||||
it('should validate type compatibility correctly', () => {
|
||||
const subgraph = createTestSubgraph({
|
||||
inputs: [{ name: 'number_input', type: 'number' }]
|
||||
})
|
||||
|
||||
const sourceNode = new LGraphNode('SourceNode')
|
||||
sourceNode.addOutput('number_out', 'number')
|
||||
sourceNode.addOutput('string_out', 'string')
|
||||
subgraph.add(sourceNode)
|
||||
|
||||
const targetNode = new LGraphNode('TargetNode')
|
||||
targetNode.addInput('number_in', 'number')
|
||||
targetNode.addInput('string_in', 'string')
|
||||
subgraph.add(targetNode)
|
||||
|
||||
// Create valid link (number -> number)
|
||||
const validLink = new LLink(
|
||||
1,
|
||||
'number',
|
||||
sourceNode.id,
|
||||
0,
|
||||
targetNode.id,
|
||||
0
|
||||
)
|
||||
subgraph._links.set(validLink.id, validLink)
|
||||
const validMovingLink = new MovingOutputLink(subgraph, validLink)
|
||||
|
||||
// Create invalid link (string -> number)
|
||||
const invalidLink = new LLink(
|
||||
2,
|
||||
'string',
|
||||
sourceNode.id,
|
||||
1,
|
||||
targetNode.id,
|
||||
1
|
||||
)
|
||||
subgraph._links.set(invalidLink.id, invalidLink)
|
||||
const invalidMovingLink = new MovingOutputLink(subgraph, invalidLink)
|
||||
|
||||
const numberInput = subgraph.inputs[0]
|
||||
|
||||
// Test validation
|
||||
expect(validMovingLink.canConnectToSubgraphInput(numberInput)).toBe(true)
|
||||
expect(invalidMovingLink.canConnectToSubgraphInput(numberInput)).toBe(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle wildcard types', () => {
|
||||
const subgraph = createTestSubgraph({
|
||||
inputs: [{ name: 'wildcard_input', type: '*' }]
|
||||
})
|
||||
|
||||
const sourceNode = new LGraphNode('SourceNode')
|
||||
sourceNode.addOutput('number_out', 'number')
|
||||
subgraph.add(sourceNode)
|
||||
|
||||
const targetNode = new LGraphNode('TargetNode')
|
||||
targetNode.addInput('number_in', 'number')
|
||||
subgraph.add(targetNode)
|
||||
|
||||
const link = new LLink(1, 'number', sourceNode.id, 0, targetNode.id, 0)
|
||||
subgraph._links.set(link.id, link)
|
||||
const movingLink = new MovingOutputLink(subgraph, link)
|
||||
|
||||
const wildcardInput = subgraph.inputs[0]
|
||||
|
||||
// Wildcard should accept any type
|
||||
expect(movingLink.canConnectToSubgraphInput(wildcardInput)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ToOutputRenderLink validation', () => {
|
||||
it('should implement canConnectToSubgraphInput method', () => {
|
||||
// Create a minimal valid setup
|
||||
const subgraph = createTestSubgraph()
|
||||
const node = new LGraphNode('TestNode')
|
||||
node.id = 1
|
||||
node.addInput('test_in', 'number')
|
||||
subgraph.add(node)
|
||||
|
||||
const slot = node.inputs[0] as NodeInputSlot
|
||||
const renderLink = new ToOutputRenderLink(subgraph, node, slot)
|
||||
|
||||
// Verify the method exists
|
||||
expect(typeof renderLink.canConnectToSubgraphInput).toBe('function')
|
||||
})
|
||||
})
|
||||
|
||||
describe('dropOnIoNode validation', () => {
|
||||
it('should prevent invalid connections when dropping on SubgraphInputNode', () => {
|
||||
const subgraph = createTestSubgraph({
|
||||
inputs: [{ name: 'number_input', type: 'number' }]
|
||||
})
|
||||
|
||||
const sourceNode = new LGraphNode('SourceNode')
|
||||
sourceNode.addOutput('string_out', 'string')
|
||||
subgraph.add(sourceNode)
|
||||
|
||||
const targetNode = new LGraphNode('TargetNode')
|
||||
targetNode.addInput('string_in', 'string')
|
||||
subgraph.add(targetNode)
|
||||
|
||||
// Create an invalid link (string output -> string input, but subgraph expects number)
|
||||
const link = new LLink(1, 'string', sourceNode.id, 0, targetNode.id, 0)
|
||||
subgraph._links.set(link.id, link)
|
||||
const movingLink = new MovingOutputLink(subgraph, link)
|
||||
|
||||
// Mock console.warn to verify it's called
|
||||
const consoleWarnSpy = vi
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {})
|
||||
|
||||
// Add the link to the connector
|
||||
connector.renderLinks.push(movingLink)
|
||||
connector.state.connectingTo = 'output'
|
||||
|
||||
// Create mock event
|
||||
const mockEvent = {
|
||||
canvasX: 100,
|
||||
canvasY: 100
|
||||
} as any
|
||||
|
||||
// Mock the getSlotInPosition to return the subgraph input
|
||||
const mockGetSlotInPosition = vi.fn().mockReturnValue(subgraph.inputs[0])
|
||||
subgraph.inputNode.getSlotInPosition = mockGetSlotInPosition
|
||||
|
||||
// Spy on connectToSubgraphInput to ensure it's NOT called
|
||||
const connectSpy = vi.spyOn(movingLink, 'connectToSubgraphInput')
|
||||
|
||||
// Drop on the SubgraphInputNode
|
||||
connector.dropOnIoNode(subgraph.inputNode, mockEvent)
|
||||
|
||||
// Verify that the invalid connection was skipped
|
||||
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
||||
'Invalid connection type',
|
||||
'string',
|
||||
'->',
|
||||
'number'
|
||||
)
|
||||
expect(connectSpy).not.toHaveBeenCalled()
|
||||
|
||||
consoleWarnSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('should allow valid connections when dropping on SubgraphInputNode', () => {
|
||||
const subgraph = createTestSubgraph({
|
||||
inputs: [{ name: 'number_input', type: 'number' }]
|
||||
})
|
||||
|
||||
const sourceNode = new LGraphNode('SourceNode')
|
||||
sourceNode.addOutput('number_out', 'number')
|
||||
subgraph.add(sourceNode)
|
||||
|
||||
const targetNode = new LGraphNode('TargetNode')
|
||||
targetNode.addInput('number_in', 'number')
|
||||
subgraph.add(targetNode)
|
||||
|
||||
// Create a valid link (number -> number)
|
||||
const link = new LLink(1, 'number', sourceNode.id, 0, targetNode.id, 0)
|
||||
subgraph._links.set(link.id, link)
|
||||
const movingLink = new MovingOutputLink(subgraph, link)
|
||||
|
||||
// Add the link to the connector
|
||||
connector.renderLinks.push(movingLink)
|
||||
connector.state.connectingTo = 'output'
|
||||
|
||||
// Create mock event
|
||||
const mockEvent = {
|
||||
canvasX: 100,
|
||||
canvasY: 100
|
||||
} as any
|
||||
|
||||
// Mock the getSlotInPosition to return the subgraph input
|
||||
const mockGetSlotInPosition = vi.fn().mockReturnValue(subgraph.inputs[0])
|
||||
subgraph.inputNode.getSlotInPosition = mockGetSlotInPosition
|
||||
|
||||
// Spy on connectToSubgraphInput to ensure it IS called
|
||||
const connectSpy = vi.spyOn(movingLink, 'connectToSubgraphInput')
|
||||
|
||||
// Drop on the SubgraphInputNode
|
||||
connector.dropOnIoNode(subgraph.inputNode, mockEvent)
|
||||
|
||||
// Verify that the valid connection was made
|
||||
expect(connectSpy).toHaveBeenCalledWith(
|
||||
subgraph.inputs[0],
|
||||
connector.events
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isSubgraphInputValidDrop', () => {
|
||||
it('should check if render links can connect to SubgraphInput', () => {
|
||||
const subgraph = createTestSubgraph({
|
||||
inputs: [{ name: 'number_input', type: 'number' }]
|
||||
})
|
||||
|
||||
const sourceNode = new LGraphNode('SourceNode')
|
||||
sourceNode.addOutput('number_out', 'number')
|
||||
sourceNode.addOutput('string_out', 'string')
|
||||
subgraph.add(sourceNode)
|
||||
|
||||
const targetNode = new LGraphNode('TargetNode')
|
||||
targetNode.addInput('number_in', 'number')
|
||||
targetNode.addInput('string_in', 'string')
|
||||
subgraph.add(targetNode)
|
||||
|
||||
// Create valid and invalid links
|
||||
const validLink = new LLink(
|
||||
1,
|
||||
'number',
|
||||
sourceNode.id,
|
||||
0,
|
||||
targetNode.id,
|
||||
0
|
||||
)
|
||||
const invalidLink = new LLink(
|
||||
2,
|
||||
'string',
|
||||
sourceNode.id,
|
||||
1,
|
||||
targetNode.id,
|
||||
1
|
||||
)
|
||||
subgraph._links.set(validLink.id, validLink)
|
||||
subgraph._links.set(invalidLink.id, invalidLink)
|
||||
|
||||
const validMovingLink = new MovingOutputLink(subgraph, validLink)
|
||||
const invalidMovingLink = new MovingOutputLink(subgraph, invalidLink)
|
||||
|
||||
const subgraphInput = subgraph.inputs[0]
|
||||
|
||||
// Test with only invalid link
|
||||
connector.renderLinks.length = 0
|
||||
connector.renderLinks.push(invalidMovingLink)
|
||||
expect(connector.isSubgraphInputValidDrop(subgraphInput)).toBe(false)
|
||||
|
||||
// Test with valid link
|
||||
connector.renderLinks.length = 0
|
||||
connector.renderLinks.push(validMovingLink)
|
||||
expect(connector.isSubgraphInputValidDrop(subgraphInput)).toBe(true)
|
||||
|
||||
// Test with mixed links
|
||||
connector.renderLinks.length = 0
|
||||
connector.renderLinks.push(invalidMovingLink, validMovingLink)
|
||||
expect(connector.isSubgraphInputValidDrop(subgraphInput)).toBe(true)
|
||||
})
|
||||
|
||||
it('should handle render links without canConnectToSubgraphInput method', () => {
|
||||
const subgraph = createTestSubgraph({
|
||||
inputs: [{ name: 'number_input', type: 'number' }]
|
||||
})
|
||||
|
||||
// Create a mock render link without the method
|
||||
const mockLink = {
|
||||
fromSlot: { type: 'number' }
|
||||
// No canConnectToSubgraphInput method
|
||||
} as any
|
||||
|
||||
connector.renderLinks.push(mockLink)
|
||||
|
||||
const subgraphInput = subgraph.inputs[0]
|
||||
|
||||
// Should return false as the link doesn't have the method
|
||||
expect(connector.isSubgraphInputValidDrop(subgraphInput)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -185,6 +185,9 @@
|
||||
"Comfy_OpenClipspace": {
|
||||
"label": "Clipspace"
|
||||
},
|
||||
"Comfy_OpenManagerDialog": {
|
||||
"label": "مدير"
|
||||
},
|
||||
"Comfy_OpenWorkflow": {
|
||||
"label": "فتح سير عمل"
|
||||
},
|
||||
@@ -212,6 +215,12 @@
|
||||
"Comfy_ShowSettingsDialog": {
|
||||
"label": "عرض نافذة الإعدادات"
|
||||
},
|
||||
"Comfy_ToggleCanvasInfo": {
|
||||
"label": "أداء اللوحة"
|
||||
},
|
||||
"Comfy_ToggleHelpCenter": {
|
||||
"label": "مركز المساعدة"
|
||||
},
|
||||
"Comfy_ToggleTheme": {
|
||||
"label": "تبديل النمط (فاتح/داكن)"
|
||||
},
|
||||
|
||||
@@ -749,6 +749,7 @@
|
||||
"manageExtensions": "إدارة الإضافات",
|
||||
"onChange": "عند التغيير",
|
||||
"onChangeTooltip": "سيتم وضع سير العمل في قائمة الانتظار عند إجراء تغيير",
|
||||
"queue": "لوحة الانتظار",
|
||||
"refresh": "تحديث تعريفات العقد",
|
||||
"resetView": "إعادة تعيين عرض اللوحة",
|
||||
"run": "تشغيل",
|
||||
@@ -762,11 +763,11 @@
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "حول ComfyUI",
|
||||
"Add Edit Model Step": "إضافة خطوة تعديل النموذج",
|
||||
"Bottom Panel": "لوحة سفلية",
|
||||
"Browse Templates": "تصفح القوالب",
|
||||
"Bypass/Unbypass Selected Nodes": "تجاوز/إلغاء تجاوز العقد المحددة",
|
||||
"Canvas Toggle Link Visibility": "تبديل ظهور الروابط على اللوحة",
|
||||
"Canvas Performance": "أداء اللوحة",
|
||||
"Canvas Toggle Lock": "تبديل قفل اللوحة",
|
||||
"Canvas Toggle Minimap": "تبديل الخريطة المصغرة على اللوحة",
|
||||
"Check for Updates": "التحقق من التحديثات",
|
||||
"Clear Pending Tasks": "مسح المهام المعلقة",
|
||||
"Clear Workflow": "مسح سير العمل",
|
||||
@@ -788,15 +789,20 @@
|
||||
"Exit Subgraph": "الخروج من الرسم الفرعي",
|
||||
"Export": "تصدير",
|
||||
"Export (API)": "تصدير (API)",
|
||||
"File": "ملف",
|
||||
"Fit Group To Contents": "ملائمة المجموعة للمحتويات",
|
||||
"Fit view to selected nodes": "تعديل العرض للعقد المحددة",
|
||||
"Focus Mode": "وضع التركيز",
|
||||
"Give Feedback": "تقديم ملاحظات",
|
||||
"Group Selected Nodes": "تجميع العقد المحددة",
|
||||
"Help": "مساعدة",
|
||||
"Help Center": "مركز المساعدة",
|
||||
"Increase Brush Size in MaskEditor": "زيادة حجم الفرشاة في محرر القناع",
|
||||
"Interrupt": "إيقاف مؤقت",
|
||||
"Load Default Workflow": "تحميل سير العمل الافتراضي",
|
||||
"Manage group nodes": "إدارة عقد المجموعة",
|
||||
"Manager": "المدير",
|
||||
"Minimap": "خريطة مصغرة",
|
||||
"Model Library": "مكتبة النماذج",
|
||||
"Move Selected Nodes Down": "تحريك العقد المحددة للأسفل",
|
||||
"Move Selected Nodes Left": "تحريك العقد المحددة لليسار",
|
||||
"Move Selected Nodes Right": "تحريك العقد المحددة لليمين",
|
||||
@@ -804,6 +810,8 @@
|
||||
"Mute/Unmute Selected Nodes": "كتم/إلغاء كتم العقد المحددة",
|
||||
"New": "جديد",
|
||||
"Next Opened Workflow": "سير العمل التالي المفتوح",
|
||||
"Node Library": "مكتبة العقد",
|
||||
"Node Links": "روابط العقد",
|
||||
"Open": "فتح",
|
||||
"Open 3D Viewer (Beta) for Selected Node": "فتح عارض ثلاثي الأبعاد (بيتا) للعقدة المحددة",
|
||||
"Open Custom Nodes Folder": "فتح مجلد العقد المخصصة",
|
||||
@@ -818,6 +826,7 @@
|
||||
"Pin/Unpin Selected Items": "تثبيت/إلغاء تثبيت العناصر المحددة",
|
||||
"Pin/Unpin Selected Nodes": "تثبيت/إلغاء تثبيت العقد المحددة",
|
||||
"Previous Opened Workflow": "سير العمل السابق المفتوح",
|
||||
"Queue Panel": "لوحة الانتظار",
|
||||
"Queue Prompt": "قائمة انتظار التعليمات",
|
||||
"Queue Prompt (Front)": "قائمة انتظار التعليمات (أمامي)",
|
||||
"Queue Selected Output Nodes": "قائمة انتظار عقد المخرجات المحددة",
|
||||
@@ -833,26 +842,21 @@
|
||||
"Show Keybindings Dialog": "عرض مربع حوار اختصارات لوحة المفاتيح",
|
||||
"Show Settings Dialog": "عرض نافذة الإعدادات",
|
||||
"Sign Out": "تسجيل خروج",
|
||||
"Toggle Bottom Panel": "تبديل اللوحة السفلية",
|
||||
"Toggle Essential Bottom Panel": "تبديل اللوحة السفلية الأساسية",
|
||||
"Toggle Focus Mode": "تبديل وضع التركيز",
|
||||
"Toggle Logs Bottom Panel": "تبديل لوحة السجلات السفلية",
|
||||
"Toggle Model Library Sidebar": "تبديل الشريط الجانبي لمكتبة النماذج",
|
||||
"Toggle Node Library Sidebar": "تبديل الشريط الجانبي لمكتبة العقد",
|
||||
"Toggle Queue Sidebar": "تبديل الشريط الجانبي لقائمة الانتظار",
|
||||
"Toggle Search Box": "تبديل مربع البحث",
|
||||
"Toggle Terminal Bottom Panel": "تبديل لوحة الطرفية السفلية",
|
||||
"Toggle Theme (Dark/Light)": "تبديل السمة (داكن/فاتح)",
|
||||
"Toggle View Controls Bottom Panel": "تبديل لوحة التحكم في العرض السفلية",
|
||||
"Toggle Workflows Sidebar": "تبديل الشريط الجانبي لسير العمل",
|
||||
"Toggle the Custom Nodes Manager": "تبديل مدير العقد المخصصة",
|
||||
"Toggle the Custom Nodes Manager Progress Bar": "تبديل شريط تقدم مدير العقد المخصصة",
|
||||
"Undo": "تراجع",
|
||||
"Ungroup selected group nodes": "فك تجميع عقد المجموعة المحددة",
|
||||
"Unpack the selected Subgraph": "فك تجميع الرسم البياني الفرعي المحدد",
|
||||
"Workflow": "سير العمل",
|
||||
"Workflows": "سير العمل",
|
||||
"Zoom In": "تكبير",
|
||||
"Zoom Out": "تصغير"
|
||||
"Zoom Out": "تصغير",
|
||||
"Zoom to fit": "تكبير لتناسب"
|
||||
},
|
||||
"minimap": {
|
||||
"nodeColors": "ألوان العقد",
|
||||
|
||||
@@ -185,6 +185,9 @@
|
||||
"Comfy_OpenClipspace": {
|
||||
"label": "Clipspace"
|
||||
},
|
||||
"Comfy_OpenManagerDialog": {
|
||||
"label": "Manager"
|
||||
},
|
||||
"Comfy_OpenWorkflow": {
|
||||
"label": "Open Workflow"
|
||||
},
|
||||
@@ -212,6 +215,12 @@
|
||||
"Comfy_ShowSettingsDialog": {
|
||||
"label": "Show Settings Dialog"
|
||||
},
|
||||
"Comfy_ToggleCanvasInfo": {
|
||||
"label": "Canvas Performance"
|
||||
},
|
||||
"Comfy_ToggleHelpCenter": {
|
||||
"label": "Help Center"
|
||||
},
|
||||
"Comfy_ToggleTheme": {
|
||||
"label": "Toggle Theme (Dark/Light)"
|
||||
},
|
||||
|
||||
@@ -546,7 +546,8 @@
|
||||
"light": "Light",
|
||||
"manageExtensions": "Manage Extensions",
|
||||
"settings": "Settings",
|
||||
"help": "Help"
|
||||
"help": "Help",
|
||||
"queue": "Queue Panel"
|
||||
},
|
||||
"tabMenu": {
|
||||
"duplicateTab": "Duplicate Tab",
|
||||
@@ -940,7 +941,7 @@
|
||||
"Image Layer": "Image Layer"
|
||||
},
|
||||
"menuLabels": {
|
||||
"Workflow": "Workflow",
|
||||
"File": "File",
|
||||
"Edit": "Edit",
|
||||
"Help": "Help",
|
||||
"Check for Updates": "Check for Updates",
|
||||
@@ -959,16 +960,16 @@
|
||||
"Browse Templates": "Browse Templates",
|
||||
"Add Edit Model Step": "Add Edit Model Step",
|
||||
"Delete Selected Items": "Delete Selected Items",
|
||||
"Fit view to selected nodes": "Fit view to selected nodes",
|
||||
"Zoom to fit": "Zoom to fit",
|
||||
"Move Selected Nodes Down": "Move Selected Nodes Down",
|
||||
"Move Selected Nodes Left": "Move Selected Nodes Left",
|
||||
"Move Selected Nodes Right": "Move Selected Nodes Right",
|
||||
"Move Selected Nodes Up": "Move Selected Nodes Up",
|
||||
"Reset View": "Reset View",
|
||||
"Resize Selected Nodes": "Resize Selected Nodes",
|
||||
"Canvas Toggle Link Visibility": "Canvas Toggle Link Visibility",
|
||||
"Node Links": "Node Links",
|
||||
"Canvas Toggle Lock": "Canvas Toggle Lock",
|
||||
"Canvas Toggle Minimap": "Canvas Toggle Minimap",
|
||||
"Minimap": "Minimap",
|
||||
"Pin/Unpin Selected Items": "Pin/Unpin Selected Items",
|
||||
"Bypass/Unbypass Selected Nodes": "Bypass/Unbypass Selected Nodes",
|
||||
"Collapse/Expand Selected Nodes": "Collapse/Expand Selected Nodes",
|
||||
@@ -1005,6 +1006,7 @@
|
||||
"Open Mask Editor for Selected Node": "Open Mask Editor for Selected Node",
|
||||
"New": "New",
|
||||
"Clipspace": "Clipspace",
|
||||
"Manager": "Manager",
|
||||
"Open": "Open",
|
||||
"Queue Prompt": "Queue Prompt",
|
||||
"Queue Prompt (Front)": "Queue Prompt (Front)",
|
||||
@@ -1014,6 +1016,8 @@
|
||||
"Save": "Save",
|
||||
"Save As": "Save As",
|
||||
"Show Settings Dialog": "Show Settings Dialog",
|
||||
"Canvas Performance": "Canvas Performance",
|
||||
"Help Center": "Help Center",
|
||||
"Toggle Theme (Dark/Light)": "Toggle Theme (Dark/Light)",
|
||||
"Undo": "Undo",
|
||||
"Open Sign In Dialog": "Open Sign In Dialog",
|
||||
@@ -1022,17 +1026,17 @@
|
||||
"Next Opened Workflow": "Next Opened Workflow",
|
||||
"Previous Opened Workflow": "Previous Opened Workflow",
|
||||
"Toggle Search Box": "Toggle Search Box",
|
||||
"Toggle Bottom Panel": "Toggle Bottom Panel",
|
||||
"Bottom Panel": "Bottom Panel",
|
||||
"Show Keybindings Dialog": "Show Keybindings Dialog",
|
||||
"Toggle Terminal Bottom Panel": "Toggle Terminal Bottom Panel",
|
||||
"Toggle Logs Bottom Panel": "Toggle Logs Bottom Panel",
|
||||
"Toggle Essential Bottom Panel": "Toggle Essential Bottom Panel",
|
||||
"Toggle View Controls Bottom Panel": "Toggle View Controls Bottom Panel",
|
||||
"Toggle Focus Mode": "Toggle Focus Mode",
|
||||
"Toggle Model Library Sidebar": "Toggle Model Library Sidebar",
|
||||
"Toggle Node Library Sidebar": "Toggle Node Library Sidebar",
|
||||
"Toggle Queue Sidebar": "Toggle Queue Sidebar",
|
||||
"Toggle Workflows Sidebar": "Toggle Workflows Sidebar"
|
||||
"Focus Mode": "Focus Mode",
|
||||
"Model Library": "Model Library",
|
||||
"Node Library": "Node Library",
|
||||
"Queue Panel": "Queue Panel",
|
||||
"Workflows": "Workflows"
|
||||
},
|
||||
"desktopMenu": {
|
||||
"reinstall": "Reinstall",
|
||||
|
||||
@@ -390,7 +390,7 @@
|
||||
},
|
||||
"LiteGraph_Canvas_LowQualityRenderingZoomThreshold": {
|
||||
"name": "Low quality rendering zoom threshold",
|
||||
"tooltip": "Render low quality shapes when zoomed out"
|
||||
"tooltip": "Zoom level threshold for performance mode. Lower values (0.1) = quality at all zoom levels. Higher values (1.0) = performance mode even when zoomed in. Performance mode simplifies rendering by hiding text labels, shadows, and details."
|
||||
},
|
||||
"LiteGraph_Canvas_MaximumFps": {
|
||||
"name": "Maximum FPS",
|
||||
|
||||
@@ -185,6 +185,9 @@
|
||||
"Comfy_OpenClipspace": {
|
||||
"label": "Abrir espacio de clips"
|
||||
},
|
||||
"Comfy_OpenManagerDialog": {
|
||||
"label": "Administrador"
|
||||
},
|
||||
"Comfy_OpenWorkflow": {
|
||||
"label": "Abrir Flujo de Trabajo"
|
||||
},
|
||||
@@ -212,6 +215,12 @@
|
||||
"Comfy_ShowSettingsDialog": {
|
||||
"label": "Mostrar Diálogo de Configuraciones"
|
||||
},
|
||||
"Comfy_ToggleCanvasInfo": {
|
||||
"label": "Rendimiento del lienzo"
|
||||
},
|
||||
"Comfy_ToggleHelpCenter": {
|
||||
"label": "Centro de ayuda"
|
||||
},
|
||||
"Comfy_ToggleTheme": {
|
||||
"label": "Cambiar Tema (Oscuro/Claro)"
|
||||
},
|
||||
|
||||
@@ -749,6 +749,7 @@
|
||||
"manageExtensions": "Gestionar extensiones",
|
||||
"onChange": "Al cambiar",
|
||||
"onChangeTooltip": "El flujo de trabajo se encolará una vez que se haga un cambio",
|
||||
"queue": "Panel de cola",
|
||||
"refresh": "Actualizar definiciones de nodos",
|
||||
"resetView": "Restablecer vista del lienzo",
|
||||
"run": "Ejecutar",
|
||||
@@ -762,11 +763,11 @@
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "Acerca de ComfyUI",
|
||||
"Add Edit Model Step": "Agregar paso de edición de modelo",
|
||||
"Bottom Panel": "Panel inferior",
|
||||
"Browse Templates": "Explorar plantillas",
|
||||
"Bypass/Unbypass Selected Nodes": "Evitar/No evitar nodos seleccionados",
|
||||
"Canvas Toggle Link Visibility": "Alternar visibilidad de enlace en lienzo",
|
||||
"Canvas Performance": "Rendimiento del lienzo",
|
||||
"Canvas Toggle Lock": "Alternar bloqueo en lienzo",
|
||||
"Canvas Toggle Minimap": "Lienzo: Alternar minimapa",
|
||||
"Check for Updates": "Buscar actualizaciones",
|
||||
"Clear Pending Tasks": "Borrar tareas pendientes",
|
||||
"Clear Workflow": "Borrar flujo de trabajo",
|
||||
@@ -788,15 +789,20 @@
|
||||
"Exit Subgraph": "Salir de subgrafo",
|
||||
"Export": "Exportar",
|
||||
"Export (API)": "Exportar (API)",
|
||||
"File": "Archivo",
|
||||
"Fit Group To Contents": "Ajustar grupo a contenidos",
|
||||
"Fit view to selected nodes": "Ajustar vista a los nodos seleccionados",
|
||||
"Focus Mode": "Modo de enfoque",
|
||||
"Give Feedback": "Dar retroalimentación",
|
||||
"Group Selected Nodes": "Agrupar nodos seleccionados",
|
||||
"Help": "Ayuda",
|
||||
"Help Center": "Centro de ayuda",
|
||||
"Increase Brush Size in MaskEditor": "Aumentar tamaño del pincel en MaskEditor",
|
||||
"Interrupt": "Interrumpir",
|
||||
"Load Default Workflow": "Cargar flujo de trabajo predeterminado",
|
||||
"Manage group nodes": "Gestionar nodos de grupo",
|
||||
"Manager": "Administrador",
|
||||
"Minimap": "Minimapa",
|
||||
"Model Library": "Biblioteca de modelos",
|
||||
"Move Selected Nodes Down": "Mover nodos seleccionados hacia abajo",
|
||||
"Move Selected Nodes Left": "Mover nodos seleccionados hacia la izquierda",
|
||||
"Move Selected Nodes Right": "Mover nodos seleccionados hacia la derecha",
|
||||
@@ -804,6 +810,8 @@
|
||||
"Mute/Unmute Selected Nodes": "Silenciar/Activar sonido de nodos seleccionados",
|
||||
"New": "Nuevo",
|
||||
"Next Opened Workflow": "Siguiente flujo de trabajo abierto",
|
||||
"Node Library": "Biblioteca de nodos",
|
||||
"Node Links": "Enlaces de nodos",
|
||||
"Open": "Abrir",
|
||||
"Open 3D Viewer (Beta) for Selected Node": "Abrir visor 3D (Beta) para el nodo seleccionado",
|
||||
"Open Custom Nodes Folder": "Abrir carpeta de nodos personalizados",
|
||||
@@ -818,6 +826,7 @@
|
||||
"Pin/Unpin Selected Items": "Anclar/Desanclar elementos seleccionados",
|
||||
"Pin/Unpin Selected Nodes": "Anclar/Desanclar nodos seleccionados",
|
||||
"Previous Opened Workflow": "Flujo de trabajo abierto anterior",
|
||||
"Queue Panel": "Panel de cola",
|
||||
"Queue Prompt": "Indicador de cola",
|
||||
"Queue Prompt (Front)": "Indicador de cola (Frente)",
|
||||
"Queue Selected Output Nodes": "Encolar nodos de salida seleccionados",
|
||||
@@ -833,26 +842,21 @@
|
||||
"Show Keybindings Dialog": "Mostrar diálogo de combinaciones de teclas",
|
||||
"Show Settings Dialog": "Mostrar diálogo de configuración",
|
||||
"Sign Out": "Cerrar sesión",
|
||||
"Toggle Bottom Panel": "Alternar panel inferior",
|
||||
"Toggle Essential Bottom Panel": "Alternar panel inferior esencial",
|
||||
"Toggle Focus Mode": "Alternar modo de enfoque",
|
||||
"Toggle Logs Bottom Panel": "Alternar panel inferior de registros",
|
||||
"Toggle Model Library Sidebar": "Alternar barra lateral de la biblioteca de modelos",
|
||||
"Toggle Node Library Sidebar": "Alternar barra lateral de la biblioteca de nodos",
|
||||
"Toggle Queue Sidebar": "Alternar barra lateral de la cola",
|
||||
"Toggle Search Box": "Alternar caja de búsqueda",
|
||||
"Toggle Terminal Bottom Panel": "Alternar panel inferior de terminal",
|
||||
"Toggle Theme (Dark/Light)": "Alternar tema (Oscuro/Claro)",
|
||||
"Toggle View Controls Bottom Panel": "Alternar panel inferior de controles de vista",
|
||||
"Toggle Workflows Sidebar": "Alternar barra lateral de los flujos de trabajo",
|
||||
"Toggle the Custom Nodes Manager": "Alternar el Administrador de Nodos Personalizados",
|
||||
"Toggle the Custom Nodes Manager Progress Bar": "Alternar la Barra de Progreso del Administrador de Nodos Personalizados",
|
||||
"Undo": "Deshacer",
|
||||
"Ungroup selected group nodes": "Desagrupar nodos de grupo seleccionados",
|
||||
"Unpack the selected Subgraph": "Desempaquetar el Subgrafo seleccionado",
|
||||
"Workflow": "Flujo de trabajo",
|
||||
"Workflows": "Flujos de trabajo",
|
||||
"Zoom In": "Acercar",
|
||||
"Zoom Out": "Alejar"
|
||||
"Zoom Out": "Alejar",
|
||||
"Zoom to fit": "Ajustar al tamaño"
|
||||
},
|
||||
"minimap": {
|
||||
"nodeColors": "Colores de nodos",
|
||||
|
||||
@@ -185,6 +185,9 @@
|
||||
"Comfy_OpenClipspace": {
|
||||
"label": "Espace de clip"
|
||||
},
|
||||
"Comfy_OpenManagerDialog": {
|
||||
"label": "Gestionnaire"
|
||||
},
|
||||
"Comfy_OpenWorkflow": {
|
||||
"label": "Ouvrir le flux de travail"
|
||||
},
|
||||
@@ -212,6 +215,12 @@
|
||||
"Comfy_ShowSettingsDialog": {
|
||||
"label": "Afficher la boîte de dialogue des paramètres"
|
||||
},
|
||||
"Comfy_ToggleCanvasInfo": {
|
||||
"label": "Performance du canvas"
|
||||
},
|
||||
"Comfy_ToggleHelpCenter": {
|
||||
"label": "Centre d'aide"
|
||||
},
|
||||
"Comfy_ToggleTheme": {
|
||||
"label": "Changer de thème (Sombre/Clair)"
|
||||
},
|
||||
|
||||
@@ -749,6 +749,7 @@
|
||||
"manageExtensions": "Gérer les extensions",
|
||||
"onChange": "Sur modification",
|
||||
"onChangeTooltip": "Le flux de travail sera mis en file d'attente une fois une modification effectuée",
|
||||
"queue": "Panneau de file d’attente",
|
||||
"refresh": "Actualiser les définitions des nœuds",
|
||||
"resetView": "Réinitialiser la vue du canevas",
|
||||
"run": "Exécuter",
|
||||
@@ -762,11 +763,11 @@
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "À propos de ComfyUI",
|
||||
"Add Edit Model Step": "Ajouter une étape d’édition de modèle",
|
||||
"Bottom Panel": "Panneau inférieur",
|
||||
"Browse Templates": "Parcourir les modèles",
|
||||
"Bypass/Unbypass Selected Nodes": "Contourner/Ne pas contourner les nœuds sélectionnés",
|
||||
"Canvas Toggle Link Visibility": "Basculer la visibilité du lien de la toile",
|
||||
"Canvas Performance": "Performance du canevas",
|
||||
"Canvas Toggle Lock": "Basculer le verrouillage de la toile",
|
||||
"Canvas Toggle Minimap": "Basculer la mini-carte du canevas",
|
||||
"Check for Updates": "Vérifier les mises à jour",
|
||||
"Clear Pending Tasks": "Effacer les tâches en attente",
|
||||
"Clear Workflow": "Effacer le flux de travail",
|
||||
@@ -788,15 +789,20 @@
|
||||
"Exit Subgraph": "Quitter le sous-graphe",
|
||||
"Export": "Exporter",
|
||||
"Export (API)": "Exporter (API)",
|
||||
"File": "Fichier",
|
||||
"Fit Group To Contents": "Ajuster le groupe au contenu",
|
||||
"Fit view to selected nodes": "Ajuster la vue aux nœuds sélectionnés",
|
||||
"Focus Mode": "Mode focus",
|
||||
"Give Feedback": "Donnez votre avis",
|
||||
"Group Selected Nodes": "Grouper les nœuds sélectionnés",
|
||||
"Help": "Aide",
|
||||
"Help Center": "Centre d’aide",
|
||||
"Increase Brush Size in MaskEditor": "Augmenter la taille du pinceau dans MaskEditor",
|
||||
"Interrupt": "Interrompre",
|
||||
"Load Default Workflow": "Charger le flux de travail par défaut",
|
||||
"Manage group nodes": "Gérer les nœuds de groupe",
|
||||
"Manager": "Gestionnaire",
|
||||
"Minimap": "Minicarte",
|
||||
"Model Library": "Bibliothèque de modèles",
|
||||
"Move Selected Nodes Down": "Déplacer les nœuds sélectionnés vers le bas",
|
||||
"Move Selected Nodes Left": "Déplacer les nœuds sélectionnés vers la gauche",
|
||||
"Move Selected Nodes Right": "Déplacer les nœuds sélectionnés vers la droite",
|
||||
@@ -804,6 +810,8 @@
|
||||
"Mute/Unmute Selected Nodes": "Mettre en sourdine/Activer le son des nœuds sélectionnés",
|
||||
"New": "Nouveau",
|
||||
"Next Opened Workflow": "Prochain flux de travail ouvert",
|
||||
"Node Library": "Bibliothèque de nœuds",
|
||||
"Node Links": "Liens de nœuds",
|
||||
"Open": "Ouvrir",
|
||||
"Open 3D Viewer (Beta) for Selected Node": "Ouvrir le visualiseur 3D (bêta) pour le nœud sélectionné",
|
||||
"Open Custom Nodes Folder": "Ouvrir le dossier des nœuds personnalisés",
|
||||
@@ -818,6 +826,7 @@
|
||||
"Pin/Unpin Selected Items": "Épingler/Désépingler les éléments sélectionnés",
|
||||
"Pin/Unpin Selected Nodes": "Épingler/Désépingler les nœuds sélectionnés",
|
||||
"Previous Opened Workflow": "Flux de travail ouvert précédent",
|
||||
"Queue Panel": "Panneau de file d’attente",
|
||||
"Queue Prompt": "Invite de file d'attente",
|
||||
"Queue Prompt (Front)": "Invite de file d'attente (Front)",
|
||||
"Queue Selected Output Nodes": "Mettre en file d’attente les nœuds de sortie sélectionnés",
|
||||
@@ -833,26 +842,21 @@
|
||||
"Show Keybindings Dialog": "Afficher la boîte de dialogue des raccourcis clavier",
|
||||
"Show Settings Dialog": "Afficher la boîte de dialogue des paramètres",
|
||||
"Sign Out": "Se déconnecter",
|
||||
"Toggle Bottom Panel": "Basculer le panneau inférieur",
|
||||
"Toggle Essential Bottom Panel": "Afficher/Masquer le panneau inférieur essentiel",
|
||||
"Toggle Focus Mode": "Basculer le mode focus",
|
||||
"Toggle Logs Bottom Panel": "Basculer le panneau inférieur des journaux",
|
||||
"Toggle Model Library Sidebar": "Afficher/Masquer la barre latérale de la bibliothèque de modèles",
|
||||
"Toggle Node Library Sidebar": "Afficher/Masquer la barre latérale de la bibliothèque de nœuds",
|
||||
"Toggle Queue Sidebar": "Afficher/Masquer la barre latérale de la file d’attente",
|
||||
"Toggle Search Box": "Basculer la boîte de recherche",
|
||||
"Toggle Terminal Bottom Panel": "Basculer le panneau inférieur du terminal",
|
||||
"Toggle Theme (Dark/Light)": "Basculer le thème (Sombre/Clair)",
|
||||
"Toggle View Controls Bottom Panel": "Afficher/Masquer le panneau inférieur des contrôles de vue",
|
||||
"Toggle Workflows Sidebar": "Afficher/Masquer la barre latérale des workflows",
|
||||
"Toggle the Custom Nodes Manager": "Basculer le gestionnaire de nœuds personnalisés",
|
||||
"Toggle the Custom Nodes Manager Progress Bar": "Basculer la barre de progression du gestionnaire de nœuds personnalisés",
|
||||
"Undo": "Annuler",
|
||||
"Ungroup selected group nodes": "Dégrouper les nœuds de groupe sélectionnés",
|
||||
"Unpack the selected Subgraph": "Décompresser le Subgraph sélectionné",
|
||||
"Workflow": "Flux de travail",
|
||||
"Workflows": "Flux de travail",
|
||||
"Zoom In": "Zoom avant",
|
||||
"Zoom Out": "Zoom arrière"
|
||||
"Zoom Out": "Zoom arrière",
|
||||
"Zoom to fit": "Ajuster à l’écran"
|
||||
},
|
||||
"minimap": {
|
||||
"nodeColors": "Couleurs des nœuds",
|
||||
|
||||
@@ -185,6 +185,9 @@
|
||||
"Comfy_OpenClipspace": {
|
||||
"label": "クリップスペース"
|
||||
},
|
||||
"Comfy_OpenManagerDialog": {
|
||||
"label": "マネージャー"
|
||||
},
|
||||
"Comfy_OpenWorkflow": {
|
||||
"label": "ワークフローを開く"
|
||||
},
|
||||
@@ -212,6 +215,12 @@
|
||||
"Comfy_ShowSettingsDialog": {
|
||||
"label": "設定ダイアログを表示"
|
||||
},
|
||||
"Comfy_ToggleCanvasInfo": {
|
||||
"label": "キャンバスパフォーマンス"
|
||||
},
|
||||
"Comfy_ToggleHelpCenter": {
|
||||
"label": "ヘルプセンター"
|
||||
},
|
||||
"Comfy_ToggleTheme": {
|
||||
"label": "テーマの切り替え(ダーク/ライト)"
|
||||
},
|
||||
|
||||
@@ -749,6 +749,7 @@
|
||||
"manageExtensions": "拡張機能の管理",
|
||||
"onChange": "変更時",
|
||||
"onChangeTooltip": "変更が行われるとワークフローがキューに追加されます",
|
||||
"queue": "キューパネル",
|
||||
"refresh": "ノードを更新",
|
||||
"resetView": "ビューをリセット",
|
||||
"run": "実行する",
|
||||
@@ -762,11 +763,11 @@
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "ComfyUIについて",
|
||||
"Add Edit Model Step": "モデル編集ステップを追加",
|
||||
"Bottom Panel": "下部パネル",
|
||||
"Browse Templates": "テンプレートを参照",
|
||||
"Bypass/Unbypass Selected Nodes": "選択したノードのバイパス/バイパス解除",
|
||||
"Canvas Toggle Link Visibility": "キャンバスのリンク表示を切り替え",
|
||||
"Canvas Performance": "キャンバスパフォーマンス",
|
||||
"Canvas Toggle Lock": "キャンバスのロックを切り替え",
|
||||
"Canvas Toggle Minimap": "キャンバス ミニマップの切り替え",
|
||||
"Check for Updates": "更新を確認する",
|
||||
"Clear Pending Tasks": "保留中のタスクをクリア",
|
||||
"Clear Workflow": "ワークフローをクリア",
|
||||
@@ -788,15 +789,20 @@
|
||||
"Exit Subgraph": "サブグラフを終了",
|
||||
"Export": "エクスポート",
|
||||
"Export (API)": "エクスポート (API)",
|
||||
"File": "ファイル",
|
||||
"Fit Group To Contents": "グループを内容に合わせる",
|
||||
"Fit view to selected nodes": "選択したノードにビューを合わせる",
|
||||
"Focus Mode": "フォーカスモード",
|
||||
"Give Feedback": "フィードバックを送る",
|
||||
"Group Selected Nodes": "選択したノードをグループ化",
|
||||
"Help": "ヘルプ",
|
||||
"Help Center": "ヘルプセンター",
|
||||
"Increase Brush Size in MaskEditor": "マスクエディタでブラシサイズを大きくする",
|
||||
"Interrupt": "中断",
|
||||
"Load Default Workflow": "デフォルトワークフローを読み込む",
|
||||
"Manage group nodes": "グループノードを管理",
|
||||
"Manager": "マネージャー",
|
||||
"Minimap": "ミニマップ",
|
||||
"Model Library": "モデルライブラリ",
|
||||
"Move Selected Nodes Down": "選択したノードを下へ移動",
|
||||
"Move Selected Nodes Left": "選択したノードを左へ移動",
|
||||
"Move Selected Nodes Right": "選択したノードを右へ移動",
|
||||
@@ -804,6 +810,8 @@
|
||||
"Mute/Unmute Selected Nodes": "選択したノードのミュート/ミュート解除",
|
||||
"New": "新規",
|
||||
"Next Opened Workflow": "次に開いたワークフロー",
|
||||
"Node Library": "ノードライブラリ",
|
||||
"Node Links": "ノードリンク",
|
||||
"Open": "開く",
|
||||
"Open 3D Viewer (Beta) for Selected Node": "選択したノードの3Dビューアー(ベータ)を開く",
|
||||
"Open Custom Nodes Folder": "カスタムノードフォルダを開く",
|
||||
@@ -818,6 +826,7 @@
|
||||
"Pin/Unpin Selected Items": "選択したアイテムのピン留め/ピン留め解除",
|
||||
"Pin/Unpin Selected Nodes": "選択したノードのピン留め/ピン留め解除",
|
||||
"Previous Opened Workflow": "前に開いたワークフロー",
|
||||
"Queue Panel": "キューパネル",
|
||||
"Queue Prompt": "キューのプロンプト",
|
||||
"Queue Prompt (Front)": "キューのプロンプト (前面)",
|
||||
"Queue Selected Output Nodes": "選択した出力ノードをキューに追加",
|
||||
@@ -833,26 +842,21 @@
|
||||
"Show Keybindings Dialog": "キーバインドダイアログを表示",
|
||||
"Show Settings Dialog": "設定ダイアログを表示",
|
||||
"Sign Out": "サインアウト",
|
||||
"Toggle Bottom Panel": "下部パネルの切り替え",
|
||||
"Toggle Essential Bottom Panel": "エッセンシャル下部パネルの切り替え",
|
||||
"Toggle Focus Mode": "フォーカスモードの切り替え",
|
||||
"Toggle Logs Bottom Panel": "ログパネル下部を切り替え",
|
||||
"Toggle Model Library Sidebar": "モデルライブラリサイドバーを切り替え",
|
||||
"Toggle Node Library Sidebar": "ノードライブラリサイドバーを切り替え",
|
||||
"Toggle Queue Sidebar": "キューサイドバーを切り替え",
|
||||
"Toggle Search Box": "検索ボックスの切り替え",
|
||||
"Toggle Terminal Bottom Panel": "ターミナルパネル下部を切り替え",
|
||||
"Toggle Theme (Dark/Light)": "テーマを切り替え(ダーク/ライト)",
|
||||
"Toggle View Controls Bottom Panel": "ビューコントロール下部パネルの切り替え",
|
||||
"Toggle Workflows Sidebar": "ワークフローサイドバーを切り替え",
|
||||
"Toggle the Custom Nodes Manager": "カスタムノードマネージャーを切り替え",
|
||||
"Toggle the Custom Nodes Manager Progress Bar": "カスタムノードマネージャーの進行状況バーを切り替え",
|
||||
"Undo": "元に戻す",
|
||||
"Ungroup selected group nodes": "選択したグループノードのグループ解除",
|
||||
"Unpack the selected Subgraph": "選択したサブグラフを展開",
|
||||
"Workflow": "ワークフロー",
|
||||
"Workflows": "ワークフロー",
|
||||
"Zoom In": "ズームイン",
|
||||
"Zoom Out": "ズームアウト"
|
||||
"Zoom Out": "ズームアウト",
|
||||
"Zoom to fit": "全体表示にズーム"
|
||||
},
|
||||
"minimap": {
|
||||
"nodeColors": "ノードの色",
|
||||
|
||||
@@ -185,6 +185,9 @@
|
||||
"Comfy_OpenClipspace": {
|
||||
"label": "클립스페이스"
|
||||
},
|
||||
"Comfy_OpenManagerDialog": {
|
||||
"label": "매니저"
|
||||
},
|
||||
"Comfy_OpenWorkflow": {
|
||||
"label": "워크플로 열기"
|
||||
},
|
||||
@@ -212,6 +215,12 @@
|
||||
"Comfy_ShowSettingsDialog": {
|
||||
"label": "설정 대화상자 보기"
|
||||
},
|
||||
"Comfy_ToggleCanvasInfo": {
|
||||
"label": "캔버스 성능"
|
||||
},
|
||||
"Comfy_ToggleHelpCenter": {
|
||||
"label": "도움말 센터"
|
||||
},
|
||||
"Comfy_ToggleTheme": {
|
||||
"label": "밝기 테마 전환 (어두운/밝은)"
|
||||
},
|
||||
|
||||
@@ -749,6 +749,7 @@
|
||||
"manageExtensions": "확장 프로그램 관리",
|
||||
"onChange": "변경 시",
|
||||
"onChangeTooltip": "변경이 있는 경우에만 워크플로를 실행 대기열에 추가합니다.",
|
||||
"queue": "대기열 패널",
|
||||
"refresh": "노드 정의 새로 고침",
|
||||
"resetView": "캔버스 보기 재설정",
|
||||
"run": "실행",
|
||||
@@ -762,11 +763,11 @@
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "ComfyUI에 대하여",
|
||||
"Add Edit Model Step": "모델 편집 단계 추가",
|
||||
"Bottom Panel": "하단 패널",
|
||||
"Browse Templates": "템플릿 탐색",
|
||||
"Bypass/Unbypass Selected Nodes": "선택한 노드 우회/우회 해제",
|
||||
"Canvas Toggle Link Visibility": "캔버스 토글 링크 가시성",
|
||||
"Canvas Performance": "캔버스 성능",
|
||||
"Canvas Toggle Lock": "캔버스 토글 잠금",
|
||||
"Canvas Toggle Minimap": "캔버스 미니맵 전환",
|
||||
"Check for Updates": "업데이트 확인",
|
||||
"Clear Pending Tasks": "보류 중인 작업 제거하기",
|
||||
"Clear Workflow": "워크플로 지우기",
|
||||
@@ -788,15 +789,20 @@
|
||||
"Exit Subgraph": "서브그래프 종료",
|
||||
"Export": "내보내기",
|
||||
"Export (API)": "내보내기 (API)",
|
||||
"File": "파일",
|
||||
"Fit Group To Contents": "그룹을 내용에 맞게 조정",
|
||||
"Fit view to selected nodes": "선택한 노드에 맞게 보기 조정",
|
||||
"Focus Mode": "포커스 모드",
|
||||
"Give Feedback": "피드백 제공",
|
||||
"Group Selected Nodes": "선택한 노드 그룹화",
|
||||
"Help": "도움말",
|
||||
"Help Center": "도움말 센터",
|
||||
"Increase Brush Size in MaskEditor": "마스크 편집기에서 브러시 크기 늘리기",
|
||||
"Interrupt": "중단",
|
||||
"Load Default Workflow": "기본 워크플로 불러오기",
|
||||
"Manage group nodes": "그룹 노드 관리",
|
||||
"Manager": "매니저",
|
||||
"Minimap": "미니맵",
|
||||
"Model Library": "모델 라이브러리",
|
||||
"Move Selected Nodes Down": "선택한 노드 아래로 이동",
|
||||
"Move Selected Nodes Left": "선택한 노드 왼쪽으로 이동",
|
||||
"Move Selected Nodes Right": "선택한 노드 오른쪽으로 이동",
|
||||
@@ -804,6 +810,8 @@
|
||||
"Mute/Unmute Selected Nodes": "선택한 노드 활성화/비활성화",
|
||||
"New": "새로 만들기",
|
||||
"Next Opened Workflow": "다음 열린 워크플로",
|
||||
"Node Library": "노드 라이브러리",
|
||||
"Node Links": "노드 링크",
|
||||
"Open": "열기",
|
||||
"Open 3D Viewer (Beta) for Selected Node": "선택한 노드에 대해 3D 뷰어(베타) 열기",
|
||||
"Open Custom Nodes Folder": "사용자 정의 노드 폴더 열기",
|
||||
@@ -818,6 +826,7 @@
|
||||
"Pin/Unpin Selected Items": "선택한 항목 고정/고정 해제",
|
||||
"Pin/Unpin Selected Nodes": "선택한 노드 고정/고정 해제",
|
||||
"Previous Opened Workflow": "이전 열린 워크플로",
|
||||
"Queue Panel": "대기열 패널",
|
||||
"Queue Prompt": "실행 대기열에 프롬프트 추가",
|
||||
"Queue Prompt (Front)": "실행 대기열 맨 앞에 프롬프트 추가",
|
||||
"Queue Selected Output Nodes": "선택한 출력 노드 대기열에 추가",
|
||||
@@ -833,26 +842,21 @@
|
||||
"Show Keybindings Dialog": "키 바인딩 대화상자 표시",
|
||||
"Show Settings Dialog": "설정 대화상자 표시",
|
||||
"Sign Out": "로그아웃",
|
||||
"Toggle Bottom Panel": "하단 패널 전환",
|
||||
"Toggle Essential Bottom Panel": "필수 하단 패널 전환",
|
||||
"Toggle Focus Mode": "포커스 모드 전환",
|
||||
"Toggle Logs Bottom Panel": "로그 하단 패널 전환",
|
||||
"Toggle Model Library Sidebar": "모델 라이브러리 사이드바 전환",
|
||||
"Toggle Node Library Sidebar": "노드 라이브러리 사이드바 전환",
|
||||
"Toggle Queue Sidebar": "실행 대기열 사이드바 전환",
|
||||
"Toggle Search Box": "검색 상자 전환",
|
||||
"Toggle Terminal Bottom Panel": "터미널 하단 패널 전환",
|
||||
"Toggle Theme (Dark/Light)": "테마 전환 (어두운/밝은)",
|
||||
"Toggle View Controls Bottom Panel": "뷰 컨트롤 하단 패널 전환",
|
||||
"Toggle Workflows Sidebar": "워크플로 사이드바 전환",
|
||||
"Toggle the Custom Nodes Manager": "커스텀 노드 매니저 전환",
|
||||
"Toggle the Custom Nodes Manager Progress Bar": "커스텀 노드 매니저 진행률 표시줄 전환",
|
||||
"Undo": "실행 취소",
|
||||
"Ungroup selected group nodes": "선택한 그룹 노드 그룹 해제",
|
||||
"Unpack the selected Subgraph": "선택한 서브그래프 풀기",
|
||||
"Workflow": "워크플로",
|
||||
"Workflows": "워크플로우",
|
||||
"Zoom In": "확대",
|
||||
"Zoom Out": "축소"
|
||||
"Zoom Out": "축소",
|
||||
"Zoom to fit": "화면에 맞추기"
|
||||
},
|
||||
"minimap": {
|
||||
"nodeColors": "노드 색상",
|
||||
|
||||
@@ -185,6 +185,9 @@
|
||||
"Comfy_OpenClipspace": {
|
||||
"label": "Клипспейс"
|
||||
},
|
||||
"Comfy_OpenManagerDialog": {
|
||||
"label": "Менеджер"
|
||||
},
|
||||
"Comfy_OpenWorkflow": {
|
||||
"label": "Открыть рабочий процесс"
|
||||
},
|
||||
@@ -212,6 +215,12 @@
|
||||
"Comfy_ShowSettingsDialog": {
|
||||
"label": "Показать диалог настроек"
|
||||
},
|
||||
"Comfy_ToggleCanvasInfo": {
|
||||
"label": "Производительность холста"
|
||||
},
|
||||
"Comfy_ToggleHelpCenter": {
|
||||
"label": "Центр поддержки"
|
||||
},
|
||||
"Comfy_ToggleTheme": {
|
||||
"label": "Переключить тему (Тёмная/Светлая)"
|
||||
},
|
||||
|
||||
@@ -749,6 +749,7 @@
|
||||
"manageExtensions": "Управление расширениями",
|
||||
"onChange": "При изменении",
|
||||
"onChangeTooltip": "Рабочий процесс будет поставлен в очередь после внесения изменений",
|
||||
"queue": "Панель очереди",
|
||||
"refresh": "Обновить определения нод",
|
||||
"resetView": "Сбросить вид холста",
|
||||
"run": "Запустить",
|
||||
@@ -762,11 +763,11 @@
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "О ComfyUI",
|
||||
"Add Edit Model Step": "Добавить или изменить шаг модели",
|
||||
"Bottom Panel": "Нижняя панель",
|
||||
"Browse Templates": "Просмотреть шаблоны",
|
||||
"Bypass/Unbypass Selected Nodes": "Обойти/восстановить выбранные ноды",
|
||||
"Canvas Toggle Link Visibility": "Переключение видимости ссылки на холст",
|
||||
"Canvas Performance": "Производительность холста",
|
||||
"Canvas Toggle Lock": "Переключение блокировки холста",
|
||||
"Canvas Toggle Minimap": "Показать/скрыть миникарту на холсте",
|
||||
"Check for Updates": "Проверить наличие обновлений",
|
||||
"Clear Pending Tasks": "Очистить ожидающие задачи",
|
||||
"Clear Workflow": "Очистить рабочий процесс",
|
||||
@@ -788,15 +789,20 @@
|
||||
"Exit Subgraph": "Выйти из подграфа",
|
||||
"Export": "Экспортировать",
|
||||
"Export (API)": "Экспорт (API)",
|
||||
"File": "Файл",
|
||||
"Fit Group To Contents": "Подогнать группу под содержимое",
|
||||
"Fit view to selected nodes": "Подогнать вид под выбранные ноды",
|
||||
"Focus Mode": "Режим фокуса",
|
||||
"Give Feedback": "Оставить отзыв",
|
||||
"Group Selected Nodes": "Сгруппировать выбранные ноды",
|
||||
"Help": "Помощь",
|
||||
"Help Center": "Центр поддержки",
|
||||
"Increase Brush Size in MaskEditor": "Увеличить размер кисти в MaskEditor",
|
||||
"Interrupt": "Прервать",
|
||||
"Load Default Workflow": "Загрузить стандартный рабочий процесс",
|
||||
"Manage group nodes": "Управление групповыми нодами",
|
||||
"Manager": "Менеджер",
|
||||
"Minimap": "Мини-карта",
|
||||
"Model Library": "Библиотека моделей",
|
||||
"Move Selected Nodes Down": "Переместить выбранные узлы вниз",
|
||||
"Move Selected Nodes Left": "Переместить выбранные узлы влево",
|
||||
"Move Selected Nodes Right": "Переместить выбранные узлы вправо",
|
||||
@@ -804,6 +810,8 @@
|
||||
"Mute/Unmute Selected Nodes": "Отключить/включить звук для выбранных нод",
|
||||
"New": "Новый",
|
||||
"Next Opened Workflow": "Следующий открытый рабочий процесс",
|
||||
"Node Library": "Библиотека узлов",
|
||||
"Node Links": "Связи узлов",
|
||||
"Open": "Открыть",
|
||||
"Open 3D Viewer (Beta) for Selected Node": "Открыть 3D-просмотрщик (бета) для выбранного узла",
|
||||
"Open Custom Nodes Folder": "Открыть папку пользовательских нод",
|
||||
@@ -818,6 +826,7 @@
|
||||
"Pin/Unpin Selected Items": "Закрепить/открепить выбранные элементы",
|
||||
"Pin/Unpin Selected Nodes": "Закрепить/открепить выбранные ноды",
|
||||
"Previous Opened Workflow": "Предыдущий открытый рабочий процесс",
|
||||
"Queue Panel": "Панель очереди",
|
||||
"Queue Prompt": "Запрос в очереди",
|
||||
"Queue Prompt (Front)": "Запрос в очереди (спереди)",
|
||||
"Queue Selected Output Nodes": "Добавить выбранные выходные узлы в очередь",
|
||||
@@ -833,26 +842,21 @@
|
||||
"Show Keybindings Dialog": "Показать диалог клавиш быстрого доступа",
|
||||
"Show Settings Dialog": "Показать диалог настроек",
|
||||
"Sign Out": "Выйти",
|
||||
"Toggle Bottom Panel": "Переключить нижнюю панель",
|
||||
"Toggle Essential Bottom Panel": "Показать/скрыть основную нижнюю панель",
|
||||
"Toggle Focus Mode": "Переключить режим фокуса",
|
||||
"Toggle Logs Bottom Panel": "Переключение нижней панели журналов",
|
||||
"Toggle Model Library Sidebar": "Показать/скрыть боковую панель библиотеки моделей",
|
||||
"Toggle Node Library Sidebar": "Показать/скрыть боковую панель библиотеки узлов",
|
||||
"Toggle Queue Sidebar": "Показать/скрыть боковую панель очереди",
|
||||
"Toggle Search Box": "Переключить поисковую панель",
|
||||
"Toggle Terminal Bottom Panel": "Переключение нижней панели терминала",
|
||||
"Toggle Theme (Dark/Light)": "Переключение темы (Тёмная/Светлая)",
|
||||
"Toggle View Controls Bottom Panel": "Показать/скрыть панель управления просмотром",
|
||||
"Toggle Workflows Sidebar": "Показать/скрыть боковую панель рабочих процессов",
|
||||
"Toggle the Custom Nodes Manager": "Переключить менеджер пользовательских узлов",
|
||||
"Toggle the Custom Nodes Manager Progress Bar": "Переключить индикатор выполнения менеджера пользовательских узлов",
|
||||
"Undo": "Отменить",
|
||||
"Ungroup selected group nodes": "Разгруппировать выбранные групповые ноды",
|
||||
"Unpack the selected Subgraph": "Распаковать выбранный подграф",
|
||||
"Workflow": "Рабочий процесс",
|
||||
"Workflows": "Рабочие процессы",
|
||||
"Zoom In": "Увеличить",
|
||||
"Zoom Out": "Уменьшить"
|
||||
"Zoom Out": "Уменьшить",
|
||||
"Zoom to fit": "Масштабировать по размеру"
|
||||
},
|
||||
"minimap": {
|
||||
"nodeColors": "Цвета узлов",
|
||||
|
||||
@@ -185,6 +185,9 @@
|
||||
"Comfy_OpenClipspace": {
|
||||
"label": "Clipspace"
|
||||
},
|
||||
"Comfy_OpenManagerDialog": {
|
||||
"label": "管理器"
|
||||
},
|
||||
"Comfy_OpenWorkflow": {
|
||||
"label": "開啟工作流程"
|
||||
},
|
||||
@@ -212,6 +215,12 @@
|
||||
"Comfy_ShowSettingsDialog": {
|
||||
"label": "顯示設定對話框"
|
||||
},
|
||||
"Comfy_ToggleCanvasInfo": {
|
||||
"label": "畫布效能"
|
||||
},
|
||||
"Comfy_ToggleHelpCenter": {
|
||||
"label": "說明中心"
|
||||
},
|
||||
"Comfy_ToggleTheme": {
|
||||
"label": "切換主題(深色/淺色)"
|
||||
},
|
||||
|
||||
@@ -749,6 +749,7 @@
|
||||
"manageExtensions": "管理擴充功能",
|
||||
"onChange": "變更時",
|
||||
"onChangeTooltip": "每當有變更時,工作流程會排入佇列",
|
||||
"queue": "佇列面板",
|
||||
"refresh": "重新整理節點定義",
|
||||
"resetView": "重設畫布視圖",
|
||||
"run": "執行",
|
||||
@@ -762,11 +763,11 @@
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "關於 ComfyUI",
|
||||
"Add Edit Model Step": "新增編輯模型步驟",
|
||||
"Bottom Panel": "底部面板",
|
||||
"Browse Templates": "瀏覽範本",
|
||||
"Bypass/Unbypass Selected Nodes": "繞過/取消繞過選取節點",
|
||||
"Canvas Toggle Link Visibility": "切換連結可見性",
|
||||
"Canvas Performance": "畫布效能",
|
||||
"Canvas Toggle Lock": "切換畫布鎖定",
|
||||
"Canvas Toggle Minimap": "畫布切換小地圖",
|
||||
"Check for Updates": "檢查更新",
|
||||
"Clear Pending Tasks": "清除待處理任務",
|
||||
"Clear Workflow": "清除工作流程",
|
||||
@@ -788,15 +789,20 @@
|
||||
"Exit Subgraph": "離開子圖",
|
||||
"Export": "匯出",
|
||||
"Export (API)": "匯出(API)",
|
||||
"File": "檔案",
|
||||
"Fit Group To Contents": "群組貼合內容",
|
||||
"Fit view to selected nodes": "視圖貼合選取節點",
|
||||
"Focus Mode": "專注模式",
|
||||
"Give Feedback": "提供意見回饋",
|
||||
"Group Selected Nodes": "群組選取節點",
|
||||
"Help": "說明",
|
||||
"Help Center": "說明中心",
|
||||
"Increase Brush Size in MaskEditor": "在 MaskEditor 中增大筆刷大小",
|
||||
"Interrupt": "中斷",
|
||||
"Load Default Workflow": "載入預設工作流程",
|
||||
"Manage group nodes": "管理群組節點",
|
||||
"Manager": "管理員",
|
||||
"Minimap": "縮圖地圖",
|
||||
"Model Library": "模型庫",
|
||||
"Move Selected Nodes Down": "選取節點下移",
|
||||
"Move Selected Nodes Left": "選取節點左移",
|
||||
"Move Selected Nodes Right": "選取節點右移",
|
||||
@@ -804,6 +810,8 @@
|
||||
"Mute/Unmute Selected Nodes": "靜音/取消靜音選取節點",
|
||||
"New": "新增",
|
||||
"Next Opened Workflow": "下一個已開啟的工作流程",
|
||||
"Node Library": "節點庫",
|
||||
"Node Links": "節點連結",
|
||||
"Open": "開啟",
|
||||
"Open 3D Viewer (Beta) for Selected Node": "為選取的節點開啟 3D 檢視器(Beta 版)",
|
||||
"Open Custom Nodes Folder": "開啟自訂節點資料夾",
|
||||
@@ -818,6 +826,7 @@
|
||||
"Pin/Unpin Selected Items": "釘選/取消釘選選取項目",
|
||||
"Pin/Unpin Selected Nodes": "釘選/取消釘選選取節點",
|
||||
"Previous Opened Workflow": "上一個已開啟的工作流程",
|
||||
"Queue Panel": "佇列面板",
|
||||
"Queue Prompt": "加入提示至佇列",
|
||||
"Queue Prompt (Front)": "將提示加入佇列前端",
|
||||
"Queue Selected Output Nodes": "將選取的輸出節點加入佇列",
|
||||
@@ -833,26 +842,21 @@
|
||||
"Show Keybindings Dialog": "顯示快捷鍵對話框",
|
||||
"Show Settings Dialog": "顯示設定對話框",
|
||||
"Sign Out": "登出",
|
||||
"Toggle Bottom Panel": "切換下方面板",
|
||||
"Toggle Essential Bottom Panel": "切換基本下方面板",
|
||||
"Toggle Focus Mode": "切換專注模式",
|
||||
"Toggle Logs Bottom Panel": "切換日誌下方面板",
|
||||
"Toggle Model Library Sidebar": "切換模型庫側邊欄",
|
||||
"Toggle Node Library Sidebar": "切換節點庫側邊欄",
|
||||
"Toggle Queue Sidebar": "切換佇列側邊欄",
|
||||
"Toggle Search Box": "切換搜尋框",
|
||||
"Toggle Terminal Bottom Panel": "切換終端機底部面板",
|
||||
"Toggle Theme (Dark/Light)": "切換主題(深色/淺色)",
|
||||
"Toggle View Controls Bottom Panel": "切換檢視控制下方面板",
|
||||
"Toggle Workflows Sidebar": "切換工作流程側邊欄",
|
||||
"Toggle the Custom Nodes Manager": "切換自訂節點管理器",
|
||||
"Toggle the Custom Nodes Manager Progress Bar": "切換自訂節點管理器進度條",
|
||||
"Undo": "復原",
|
||||
"Ungroup selected group nodes": "取消群組選取的群組節點",
|
||||
"Unpack the selected Subgraph": "解包所選子圖",
|
||||
"Workflow": "工作流程",
|
||||
"Workflows": "工作流程",
|
||||
"Zoom In": "放大",
|
||||
"Zoom Out": "縮小"
|
||||
"Zoom Out": "縮小",
|
||||
"Zoom to fit": "縮放至適合大小"
|
||||
},
|
||||
"minimap": {
|
||||
"nodeColors": "節點顏色",
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"label": "重启"
|
||||
},
|
||||
"Comfy_3DViewer_Open3DViewer": {
|
||||
"label": "為所選節點開啟 3D 檢視器(Beta 版)"
|
||||
"label": "为所选节点开启 3D 浏览器(Beta 版)"
|
||||
},
|
||||
"Comfy_BrowseTemplates": {
|
||||
"label": "浏览模板"
|
||||
@@ -75,7 +75,7 @@
|
||||
"label": "锁定视图"
|
||||
},
|
||||
"Comfy_Canvas_ToggleMinimap": {
|
||||
"label": "畫布切換小地圖"
|
||||
"label": "画布切换小地图"
|
||||
},
|
||||
"Comfy_Canvas_ToggleSelectedNodes_Bypass": {
|
||||
"label": "忽略/取消忽略选中节点"
|
||||
@@ -123,7 +123,7 @@
|
||||
"label": "将选区转换为子图"
|
||||
},
|
||||
"Comfy_Graph_ExitSubgraph": {
|
||||
"label": "退出子圖"
|
||||
"label": "退出子图"
|
||||
},
|
||||
"Comfy_Graph_FitGroupToContents": {
|
||||
"label": "适应节点框到内容"
|
||||
@@ -132,7 +132,7 @@
|
||||
"label": "添加框到选中节点"
|
||||
},
|
||||
"Comfy_Graph_UnpackSubgraph": {
|
||||
"label": "解開所選子圖"
|
||||
"label": "解开所选子图"
|
||||
},
|
||||
"Comfy_GroupNode_ConvertSelectedNodesToGroupNode": {
|
||||
"label": "将选中节点转换为组节点"
|
||||
@@ -171,10 +171,10 @@
|
||||
"label": "切换进度对话框"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Decrease": {
|
||||
"label": "減小 MaskEditor 中的筆刷大小"
|
||||
"label": "减小 MaskEditor 中的笔刷大小"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Increase": {
|
||||
"label": "增加 MaskEditor 畫筆大小"
|
||||
"label": "增加 MaskEditor 画笔大小"
|
||||
},
|
||||
"Comfy_MaskEditor_OpenMaskEditor": {
|
||||
"label": "打开选中节点的遮罩编辑器"
|
||||
@@ -185,6 +185,9 @@
|
||||
"Comfy_OpenClipspace": {
|
||||
"label": "打开剪贴板"
|
||||
},
|
||||
"Comfy_OpenManagerDialog": {
|
||||
"label": "管理器"
|
||||
},
|
||||
"Comfy_OpenWorkflow": {
|
||||
"label": "打开工作流"
|
||||
},
|
||||
@@ -212,6 +215,12 @@
|
||||
"Comfy_ShowSettingsDialog": {
|
||||
"label": "显示设置对话框"
|
||||
},
|
||||
"Comfy_ToggleCanvasInfo": {
|
||||
"label": "画布性能"
|
||||
},
|
||||
"Comfy_ToggleHelpCenter": {
|
||||
"label": "说明中心"
|
||||
},
|
||||
"Comfy_ToggleTheme": {
|
||||
"label": "切换主题"
|
||||
},
|
||||
@@ -246,13 +255,13 @@
|
||||
"label": "切换日志底部面板"
|
||||
},
|
||||
"Workspace_ToggleBottomPanelTab_shortcuts-essentials": {
|
||||
"label": "切換基本下方面板"
|
||||
"label": "切换基础底部面板"
|
||||
},
|
||||
"Workspace_ToggleBottomPanelTab_shortcuts-view-controls": {
|
||||
"label": "切換檢視控制底部面板"
|
||||
"label": "切换视图控制底部面板"
|
||||
},
|
||||
"Workspace_ToggleBottomPanel_Shortcuts": {
|
||||
"label": "顯示快捷鍵對話框"
|
||||
"label": "显示快捷键对话框"
|
||||
},
|
||||
"Workspace_ToggleFocusMode": {
|
||||
"label": "切换焦点模式"
|
||||
|
||||
@@ -84,9 +84,9 @@
|
||||
},
|
||||
"breadcrumbsMenu": {
|
||||
"clearWorkflow": "清除工作流程",
|
||||
"deleteWorkflow": "刪除工作流程",
|
||||
"duplicate": "複製",
|
||||
"enterNewName": "輸入新名稱"
|
||||
"deleteWorkflow": "删除工作流程",
|
||||
"duplicate": "复制",
|
||||
"enterNewName": "输入新名称"
|
||||
},
|
||||
"chatHistory": {
|
||||
"cancelEdit": "取消",
|
||||
@@ -272,7 +272,7 @@
|
||||
"category": "类别",
|
||||
"choose_file_to_upload": "选择要上传的文件",
|
||||
"clear": "清除",
|
||||
"clearFilters": "清除篩選",
|
||||
"clearFilters": "清除筛选",
|
||||
"close": "关闭",
|
||||
"color": "颜色",
|
||||
"comingSoon": "即将推出",
|
||||
@@ -297,7 +297,7 @@
|
||||
"devices": "设备",
|
||||
"disableAll": "禁用全部",
|
||||
"disabling": "禁用中",
|
||||
"dismiss": "關閉",
|
||||
"dismiss": "关闭",
|
||||
"download": "下载",
|
||||
"duplicate": "复制",
|
||||
"edit": "编辑",
|
||||
@@ -314,7 +314,7 @@
|
||||
"findIssues": "查找问题",
|
||||
"firstTimeUIMessage": "这是您第一次使用新界面。选择 \"菜单 > 使用新菜单 > 禁用\" 来恢复旧界面。",
|
||||
"frontendNewer": "前端版本 {frontendVersion} 可能與後端版本 {backendVersion} 不相容。",
|
||||
"frontendOutdated": "前端版本 {frontendVersion} 已過時。後端需要 {requiredVersion} 或更高版本。",
|
||||
"frontendOutdated": "前端版本 {frontendVersion} 已过时。后端需要 {requiredVersion} 或更高版本。",
|
||||
"goToNode": "转到节点",
|
||||
"help": "帮助",
|
||||
"icon": "图标",
|
||||
@@ -401,7 +401,7 @@
|
||||
"usageHint": "使用提示",
|
||||
"user": "用户",
|
||||
"versionMismatchWarning": "版本相容性警告",
|
||||
"versionMismatchWarningMessage": "{warning}:{detail} 請參閱 https://docs.comfy.org/installation/update_comfyui#common-update-issues 以取得更新說明。",
|
||||
"versionMismatchWarningMessage": "{warning}:{detail} 请参阅 https://docs.comfy.org/installation/update_comfyui#common-update-issues 以取得更新说明。",
|
||||
"videoFailedToLoad": "视频加载失败",
|
||||
"workflow": "工作流"
|
||||
},
|
||||
@@ -411,7 +411,7 @@
|
||||
"resetView": "重置视图",
|
||||
"selectMode": "选择模式",
|
||||
"toggleLinkVisibility": "切换连线可见性",
|
||||
"toggleMinimap": "切換小地圖",
|
||||
"toggleMinimap": "切换小地图",
|
||||
"zoomIn": "放大",
|
||||
"zoomOut": "缩小"
|
||||
},
|
||||
@@ -594,7 +594,7 @@
|
||||
"wireframe": "线框"
|
||||
},
|
||||
"model": "模型",
|
||||
"openIn3DViewer": "在 3D 檢視器中開啟",
|
||||
"openIn3DViewer": "在 3D 查看器中打开",
|
||||
"previewOutput": "预览输出",
|
||||
"removeBackgroundImage": "移除背景图片",
|
||||
"resizeNodeMatchOutput": "调整节点以匹配输出",
|
||||
@@ -611,15 +611,15 @@
|
||||
"uploadBackgroundImage": "上传背景图片",
|
||||
"uploadTexture": "上传纹理",
|
||||
"viewer": {
|
||||
"apply": "套用",
|
||||
"cameraSettings": "相機設定",
|
||||
"cameraType": "相機類型",
|
||||
"apply": "应用",
|
||||
"cameraSettings": "相机设置",
|
||||
"cameraType": "相机类型",
|
||||
"cancel": "取消",
|
||||
"exportSettings": "匯出設定",
|
||||
"lightSettings": "燈光設定",
|
||||
"modelSettings": "模型設定",
|
||||
"sceneSettings": "場景設定",
|
||||
"title": "3D 檢視器(測試版)"
|
||||
"exportSettings": "导出设置",
|
||||
"lightSettings": "灯光设置",
|
||||
"modelSettings": "模型设置",
|
||||
"sceneSettings": "场景设置",
|
||||
"title": "3D 查看器(测试版)"
|
||||
}
|
||||
},
|
||||
"loadWorkflowWarning": {
|
||||
@@ -740,7 +740,7 @@
|
||||
"disabled": "禁用",
|
||||
"disabledTooltip": "工作流将不会自动执行",
|
||||
"execute": "执行",
|
||||
"help": "說明",
|
||||
"help": "说明",
|
||||
"hideMenu": "隐藏菜单",
|
||||
"instant": "实时",
|
||||
"instantTooltip": "工作流将会在生成完成后立即执行",
|
||||
@@ -749,24 +749,25 @@
|
||||
"manageExtensions": "管理擴充功能",
|
||||
"onChange": "更改时",
|
||||
"onChangeTooltip": "一旦进行更改,工作流将添加到执行队列",
|
||||
"queue": "队列面板",
|
||||
"refresh": "刷新节点",
|
||||
"resetView": "重置视图",
|
||||
"run": "运行",
|
||||
"runWorkflow": "运行工作流程(Shift排在前面)",
|
||||
"runWorkflowFront": "运行工作流程(排在前面)",
|
||||
"settings": "設定",
|
||||
"settings": "设定",
|
||||
"showMenu": "显示菜单",
|
||||
"theme": "主題",
|
||||
"theme": "主题",
|
||||
"toggleBottomPanel": "底部面板"
|
||||
},
|
||||
"menuLabels": {
|
||||
"About ComfyUI": "关于ComfyUI",
|
||||
"Add Edit Model Step": "添加编辑模型步骤",
|
||||
"Bottom Panel": "底部面板",
|
||||
"Browse Templates": "浏览模板",
|
||||
"Bypass/Unbypass Selected Nodes": "忽略/取消忽略选定节点",
|
||||
"Canvas Toggle Link Visibility": "切换连线可见性",
|
||||
"Canvas Performance": "画布性能",
|
||||
"Canvas Toggle Lock": "切换视图锁定",
|
||||
"Canvas Toggle Minimap": "畫布切換小地圖",
|
||||
"Check for Updates": "检查更新",
|
||||
"Clear Pending Tasks": "清除待处理任务",
|
||||
"Clear Workflow": "清除工作流",
|
||||
@@ -780,23 +781,28 @@
|
||||
"Contact Support": "联系支持",
|
||||
"Convert Selection to Subgraph": "将选中内容转换为子图",
|
||||
"Convert selected nodes to group node": "将选中节点转换为组节点",
|
||||
"Decrease Brush Size in MaskEditor": "在 MaskEditor 中減小筆刷大小",
|
||||
"Decrease Brush Size in MaskEditor": "在 MaskEditor 中减小笔刷大小",
|
||||
"Delete Selected Items": "删除选定的项目",
|
||||
"Desktop User Guide": "桌面端用户指南",
|
||||
"Duplicate Current Workflow": "复制当前工作流",
|
||||
"Edit": "编辑",
|
||||
"Exit Subgraph": "退出子圖",
|
||||
"Exit Subgraph": "退出子图",
|
||||
"Export": "导出",
|
||||
"Export (API)": "导出 (API)",
|
||||
"File": "文件",
|
||||
"Fit Group To Contents": "适应组内容",
|
||||
"Fit view to selected nodes": "适应视图到选中节点",
|
||||
"Focus Mode": "专注模式",
|
||||
"Give Feedback": "提供反馈",
|
||||
"Group Selected Nodes": "将选中节点转换为组节点",
|
||||
"Help": "帮助",
|
||||
"Increase Brush Size in MaskEditor": "在 MaskEditor 中增大筆刷大小",
|
||||
"Help Center": "帮助中心",
|
||||
"Increase Brush Size in MaskEditor": "在 MaskEditor 中增大笔刷大小",
|
||||
"Interrupt": "中断",
|
||||
"Load Default Workflow": "加载默认工作流",
|
||||
"Manage group nodes": "管理组节点",
|
||||
"Manager": "管理器",
|
||||
"Minimap": "小地图",
|
||||
"Model Library": "模型库",
|
||||
"Move Selected Nodes Down": "下移所选节点",
|
||||
"Move Selected Nodes Left": "左移所选节点",
|
||||
"Move Selected Nodes Right": "右移所选节点",
|
||||
@@ -804,8 +810,10 @@
|
||||
"Mute/Unmute Selected Nodes": "静音/取消静音选定节点",
|
||||
"New": "新建",
|
||||
"Next Opened Workflow": "下一个打开的工作流",
|
||||
"Node Library": "节点库",
|
||||
"Node Links": "节点连接",
|
||||
"Open": "打开",
|
||||
"Open 3D Viewer (Beta) for Selected Node": "為所選節點開啟 3D 檢視器(Beta 版)",
|
||||
"Open 3D Viewer (Beta) for Selected Node": "为选中节点打开3D查看器(测试版)",
|
||||
"Open Custom Nodes Folder": "打开自定义节点文件夹",
|
||||
"Open DevTools": "打开开发者工具",
|
||||
"Open Inputs Folder": "打开输入文件夹",
|
||||
@@ -818,6 +826,7 @@
|
||||
"Pin/Unpin Selected Items": "固定/取消固定选定项目",
|
||||
"Pin/Unpin Selected Nodes": "固定/取消固定选定节点",
|
||||
"Previous Opened Workflow": "上一个打开的工作流",
|
||||
"Queue Panel": "队列面板",
|
||||
"Queue Prompt": "执行提示词",
|
||||
"Queue Prompt (Front)": "执行提示词 (优先执行)",
|
||||
"Queue Selected Output Nodes": "将所选输出节点加入队列",
|
||||
@@ -830,36 +839,31 @@
|
||||
"Restart": "重启",
|
||||
"Save": "保存",
|
||||
"Save As": "另存为",
|
||||
"Show Keybindings Dialog": "顯示快捷鍵對話框",
|
||||
"Show Keybindings Dialog": "显示快捷键对话框",
|
||||
"Show Settings Dialog": "显示设置对话框",
|
||||
"Sign Out": "退出登录",
|
||||
"Toggle Bottom Panel": "切换底部面板",
|
||||
"Toggle Essential Bottom Panel": "切換基本下方面板",
|
||||
"Toggle Focus Mode": "切换专注模式",
|
||||
"Toggle Essential Bottom Panel": "切换基础底部面板",
|
||||
"Toggle Logs Bottom Panel": "切换日志底部面板",
|
||||
"Toggle Model Library Sidebar": "切換模型庫側邊欄",
|
||||
"Toggle Node Library Sidebar": "切換節點庫側邊欄",
|
||||
"Toggle Queue Sidebar": "切換佇列側邊欄",
|
||||
"Toggle Search Box": "切换搜索框",
|
||||
"Toggle Terminal Bottom Panel": "切换终端底部面板",
|
||||
"Toggle Theme (Dark/Light)": "切换主题(暗/亮)",
|
||||
"Toggle View Controls Bottom Panel": "切換檢視控制下方面板",
|
||||
"Toggle Workflows Sidebar": "切換工作流程側邊欄",
|
||||
"Toggle View Controls Bottom Panel": "切换视图控制底部面板",
|
||||
"Toggle the Custom Nodes Manager": "切换自定义节点管理器",
|
||||
"Toggle the Custom Nodes Manager Progress Bar": "切换自定义节点管理器进度条",
|
||||
"Undo": "撤销",
|
||||
"Ungroup selected group nodes": "解散选中组节点",
|
||||
"Unpack the selected Subgraph": "解開所選子圖",
|
||||
"Workflow": "工作流",
|
||||
"Unpack the selected Subgraph": "解包选中子图",
|
||||
"Workflows": "工作流",
|
||||
"Zoom In": "放大画面",
|
||||
"Zoom Out": "缩小画面"
|
||||
"Zoom Out": "缩小画面",
|
||||
"Zoom to fit": "缩放以适应"
|
||||
},
|
||||
"minimap": {
|
||||
"nodeColors": "節點顏色",
|
||||
"renderBypassState": "顯示繞過狀態",
|
||||
"renderErrorState": "顯示錯誤狀態",
|
||||
"showGroups": "顯示框架/群組",
|
||||
"showLinks": "顯示連結"
|
||||
"nodeColors": "节点颜色",
|
||||
"renderBypassState": "渲染绕过状态",
|
||||
"renderErrorState": "渲染错误状态",
|
||||
"showGroups": "显示框架/分组",
|
||||
"showLinks": "显示连接"
|
||||
},
|
||||
"missingModelsDialog": {
|
||||
"doNotAskAgain": "不再显示此消息",
|
||||
@@ -1127,7 +1131,7 @@
|
||||
},
|
||||
"settingsCategories": {
|
||||
"3D": "3D",
|
||||
"3DViewer": "3D 檢視器",
|
||||
"3DViewer": "3D查看器",
|
||||
"API Nodes": "API 节点",
|
||||
"About": "关于",
|
||||
"Appearance": "外观",
|
||||
@@ -1180,18 +1184,18 @@
|
||||
"Workflow": "工作流"
|
||||
},
|
||||
"shortcuts": {
|
||||
"essentials": "基本功能",
|
||||
"keyboardShortcuts": "鍵盤快捷鍵",
|
||||
"manageShortcuts": "管理快捷鍵",
|
||||
"noKeybinding": "無快捷鍵",
|
||||
"essentials": "常用",
|
||||
"keyboardShortcuts": "键盘快捷键",
|
||||
"manageShortcuts": "管理快捷键",
|
||||
"noKeybinding": "无快捷键",
|
||||
"subcategories": {
|
||||
"node": "節點",
|
||||
"node": "节点",
|
||||
"panelControls": "面板控制",
|
||||
"queue": "佇列",
|
||||
"view": "檢視",
|
||||
"workflow": "工作流程"
|
||||
"queue": "队列",
|
||||
"view": "视图",
|
||||
"workflow": "工作流"
|
||||
},
|
||||
"viewControls": "檢視控制"
|
||||
"viewControls": "视图控制"
|
||||
},
|
||||
"sideToolbar": {
|
||||
"browseTemplates": "浏览示例模板",
|
||||
@@ -1199,10 +1203,10 @@
|
||||
"helpCenter": "帮助中心",
|
||||
"labels": {
|
||||
"models": "模型",
|
||||
"nodes": "節點",
|
||||
"queue": "佇列",
|
||||
"templates": "範本",
|
||||
"workflows": "工作流程"
|
||||
"nodes": "节点",
|
||||
"queue": "队列",
|
||||
"templates": "模板",
|
||||
"workflows": "工作流"
|
||||
},
|
||||
"logout": "登出",
|
||||
"modelLibrary": "模型库",
|
||||
@@ -1241,7 +1245,7 @@
|
||||
},
|
||||
"showFlatList": "平铺结果"
|
||||
},
|
||||
"templates": "範本",
|
||||
"templates": "模板",
|
||||
"workflowTab": {
|
||||
"confirmDelete": "您确定要删除此工作流吗?",
|
||||
"confirmDeleteTitle": "删除工作流?",
|
||||
@@ -1288,8 +1292,8 @@
|
||||
"Video": "视频生成",
|
||||
"Video API": "视频 API"
|
||||
},
|
||||
"loadingMore": "正在載入更多範本...",
|
||||
"searchPlaceholder": "搜尋範本...",
|
||||
"loadingMore": "正在加载更多模板...",
|
||||
"searchPlaceholder": "搜索模板...",
|
||||
"template": {
|
||||
"3D": {
|
||||
"3d_hunyuan3d_image_to_model": "混元3D 2.0 图生模型",
|
||||
@@ -1612,7 +1616,7 @@
|
||||
"failedToExportModel": "无法将模型导出为 {format}",
|
||||
"failedToFetchBalance": "获取余额失败:{error}",
|
||||
"failedToFetchLogs": "无法获取服务器日志",
|
||||
"failedToInitializeLoad3dViewer": "初始化 3D 檢視器失敗",
|
||||
"failedToInitializeLoad3dViewer": "初始化3D查看器失败",
|
||||
"failedToInitiateCreditPurchase": "发起积分购买失败:{error}",
|
||||
"failedToPurchaseCredits": "购买积分失败:{error}",
|
||||
"fileLoadError": "无法在 {fileName} 中找到工作流",
|
||||
@@ -1669,9 +1673,9 @@
|
||||
"required": "必填"
|
||||
},
|
||||
"versionMismatchWarning": {
|
||||
"dismiss": "關閉",
|
||||
"dismiss": "关闭",
|
||||
"frontendNewer": "前端版本 {frontendVersion} 可能與後端版本 {backendVersion} 不相容。",
|
||||
"frontendOutdated": "前端版本 {frontendVersion} 已過時。後端需要 {requiredVersion} 版或更高版本。",
|
||||
"frontendOutdated": "前端版本 {frontendVersion} 已过时。後端需要 {requiredVersion} 版或更高版本。",
|
||||
"title": "版本相容性警告",
|
||||
"updateFrontend": "更新前端"
|
||||
},
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
"tooltip": "画布背景的图像 URL。你可以在输出面板中右键点击一张图片,并选择“设为背景”来使用它。"
|
||||
},
|
||||
"Comfy_Canvas_NavigationMode": {
|
||||
"name": "畫布導航模式",
|
||||
"name": "画布导航模式",
|
||||
"options": {
|
||||
"Left-Click Pan (Legacy)": "左鍵拖曳(舊版)",
|
||||
"Standard (New)": "標準(新)"
|
||||
"Left-Click Pan (Legacy)": "左键拖曳(旧版)",
|
||||
"Standard (New)": "标准(新)"
|
||||
}
|
||||
},
|
||||
"Comfy_Canvas_SelectionToolbox": {
|
||||
@@ -120,8 +120,8 @@
|
||||
}
|
||||
},
|
||||
"Comfy_Load3D_3DViewerEnable": {
|
||||
"name": "啟用 3D 檢視器(測試版)",
|
||||
"tooltip": "為所選節點啟用 3D 檢視器(測試版)。此功能可讓您直接在全尺寸 3D 檢視器中瀏覽並互動 3D 模型。"
|
||||
"name": "启用3D查看器(测试版)",
|
||||
"tooltip": "为选定节点启用3D查看器(测试版)。此功能允许你在全尺寸3D查看器中直接可视化和交互3D模型。"
|
||||
},
|
||||
"Comfy_Load3D_BackgroundColor": {
|
||||
"name": "初始背景颜色",
|
||||
@@ -338,7 +338,7 @@
|
||||
"Disabled": "禁用",
|
||||
"Top": "顶部"
|
||||
},
|
||||
"tooltip": "選單列位置。在行動裝置上,選單始終顯示於頂端。"
|
||||
"tooltip": "选单列位置。在行动装置上,选单始终显示于顶端。"
|
||||
},
|
||||
"Comfy_Validation_Workflows": {
|
||||
"name": "校验工作流"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import _ from 'es-toolkit/compat'
|
||||
|
||||
import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems'
|
||||
import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage'
|
||||
import { useNodeCanvasImagePreview } from '@/composables/node/useNodeCanvasImagePreview'
|
||||
import { useNodeImage, useNodeVideo } from '@/composables/node/useNodeImage'
|
||||
@@ -63,6 +64,7 @@ export const useLitegraphService = () => {
|
||||
const toastStore = useToastStore()
|
||||
const widgetStore = useWidgetStore()
|
||||
const canvasStore = useCanvasStore()
|
||||
const { toggleSelectedNodesMode } = useSelectedLiteGraphItems()
|
||||
|
||||
// TODO: Dedupe `registerNodeDef`; this should remain synchronous.
|
||||
function registerSubgraphNodeDef(
|
||||
@@ -762,15 +764,8 @@ export const useLitegraphService = () => {
|
||||
options.push({
|
||||
content: 'Bypass',
|
||||
callback: () => {
|
||||
const mode =
|
||||
this.mode === LGraphEventMode.BYPASS
|
||||
? LGraphEventMode.ALWAYS
|
||||
: LGraphEventMode.BYPASS
|
||||
for (const item of app.canvas.selectedItems) {
|
||||
if (item instanceof LGraphNode) item.mode = mode
|
||||
}
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.graph.change()
|
||||
toggleSelectedNodesMode(LGraphEventMode.BYPASS)
|
||||
app.canvas.setDirty(true, true)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface ComfyCommand {
|
||||
versionAdded?: string
|
||||
confirmation?: string // If non-nullish, this command will prompt for confirmation
|
||||
source?: string
|
||||
active?: () => boolean // Getter to check if the command is active/toggled on
|
||||
category?: 'essentials' | 'view-controls' // For shortcuts panel organization
|
||||
}
|
||||
|
||||
@@ -30,6 +31,7 @@ export class ComfyCommandImpl implements ComfyCommand {
|
||||
versionAdded?: string
|
||||
confirmation?: string
|
||||
source?: string
|
||||
active?: () => boolean
|
||||
category?: 'essentials' | 'view-controls'
|
||||
|
||||
constructor(command: ComfyCommand) {
|
||||
@@ -42,6 +44,7 @@ export class ComfyCommandImpl implements ComfyCommand {
|
||||
this.versionAdded = command.versionAdded
|
||||
this.confirmation = command.confirmation
|
||||
this.source = command.source
|
||||
this.active = command.active
|
||||
this.category = command.category
|
||||
}
|
||||
|
||||
|
||||
25
src/stores/helpCenterStore.ts
Normal file
25
src/stores/helpCenterStore.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const useHelpCenterStore = defineStore('helpCenter', () => {
|
||||
const isVisible = ref(false)
|
||||
|
||||
const toggle = () => {
|
||||
isVisible.value = !isVisible.value
|
||||
}
|
||||
|
||||
const show = () => {
|
||||
isVisible.value = true
|
||||
}
|
||||
|
||||
const hide = () => {
|
||||
isVisible.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
isVisible,
|
||||
toggle,
|
||||
show,
|
||||
hide
|
||||
}
|
||||
})
|
||||
@@ -10,6 +10,7 @@ import { useCommandStore } from './commandStore'
|
||||
export const useMenuItemStore = defineStore('menuItem', () => {
|
||||
const commandStore = useCommandStore()
|
||||
const menuItems = ref<MenuItem[]>([])
|
||||
const menuItemHasActiveStateChildren = ref<Record<string, boolean>>({})
|
||||
|
||||
const registerMenuGroup = (path: string[], items: MenuItem[]) => {
|
||||
let currentLevel = menuItems.value
|
||||
@@ -45,6 +46,14 @@ export const useMenuItemStore = defineStore('menuItem', () => {
|
||||
}
|
||||
// Add the new items to the last level
|
||||
currentLevel.push(...items)
|
||||
|
||||
// Store if any of the children have active state as we will hide the icon if they do
|
||||
const parentPath = path.join('.')
|
||||
if (!menuItemHasActiveStateChildren.value[parentPath]) {
|
||||
menuItemHasActiveStateChildren.value[parentPath] = items.some(
|
||||
(item) => item.comfyCommand?.active
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const registerCommands = (path: string[], commandIds: string[]) => {
|
||||
@@ -57,7 +66,8 @@ export const useMenuItemStore = defineStore('menuItem', () => {
|
||||
label: command.menubarLabel,
|
||||
icon: command.icon,
|
||||
tooltip: command.tooltip,
|
||||
comfyCommand: command
|
||||
comfyCommand: command,
|
||||
parentPath: path.join('.')
|
||||
}) as MenuItem
|
||||
)
|
||||
registerMenuGroup(path, items)
|
||||
@@ -92,6 +102,7 @@ export const useMenuItemStore = defineStore('menuItem', () => {
|
||||
registerMenuGroup,
|
||||
registerCommands,
|
||||
loadExtensionMenuCommands,
|
||||
registerCoreMenuCommands
|
||||
registerCoreMenuCommands,
|
||||
menuItemHasActiveStateChildren
|
||||
}
|
||||
})
|
||||
|
||||
@@ -226,6 +226,14 @@ export const useReleaseStore = defineStore('release', () => {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip fetching if API nodes are disabled via argv
|
||||
if (
|
||||
systemStatsStore.systemStats?.system?.argv?.includes(
|
||||
'--disable-api-nodes'
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useQueueSidebarTab } from '@/composables/sidebarTabs/useQueueSidebarTab
|
||||
import { useWorkflowsSidebarTab } from '@/composables/sidebarTabs/useWorkflowsSidebarTab'
|
||||
import { t, te } from '@/i18n'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useMenuItemStore } from '@/stores/menuItemStore'
|
||||
import { SidebarTabExtension } from '@/types/extensionTypes'
|
||||
|
||||
export const useSidebarTabStore = defineStore('sidebarTab', () => {
|
||||
@@ -38,16 +39,34 @@ export const useSidebarTabStore = defineStore('sidebarTab', () => {
|
||||
: String(tab.tooltip)
|
||||
: undefined
|
||||
|
||||
const menubarLabelFunction = () => {
|
||||
const menubarLabelKeys: Record<string, string> = {
|
||||
queue: 'menu.queue',
|
||||
'node-library': 'sideToolbar.nodeLibrary',
|
||||
'model-library': 'sideToolbar.modelLibrary',
|
||||
workflows: 'sideToolbar.workflows'
|
||||
}
|
||||
|
||||
const key = menubarLabelKeys[tab.id]
|
||||
if (key && te(key)) {
|
||||
return t(key)
|
||||
}
|
||||
|
||||
return tab.title
|
||||
}
|
||||
|
||||
useCommandStore().registerCommand({
|
||||
id: `Workspace.ToggleSidebarTab.${tab.id}`,
|
||||
icon: typeof tab.icon === 'string' ? tab.icon : undefined,
|
||||
label: labelFunction,
|
||||
menubarLabel: menubarLabelFunction,
|
||||
tooltip: tooltipFunction,
|
||||
versionAdded: '1.3.9',
|
||||
category: 'view-controls' as const,
|
||||
function: () => {
|
||||
toggleSidebarTab(tab.id)
|
||||
},
|
||||
active: () => activeSidebarTab.value?.id === tab.id,
|
||||
source: 'System'
|
||||
})
|
||||
}
|
||||
@@ -73,6 +92,25 @@ export const useSidebarTabStore = defineStore('sidebarTab', () => {
|
||||
registerSidebarTab(useNodeLibrarySidebarTab())
|
||||
registerSidebarTab(useModelLibrarySidebarTab())
|
||||
registerSidebarTab(useWorkflowsSidebarTab())
|
||||
|
||||
const menuStore = useMenuItemStore()
|
||||
|
||||
menuStore.registerCommands(
|
||||
['View'],
|
||||
[
|
||||
'Workspace.ToggleBottomPanel',
|
||||
'Comfy.BrowseTemplates',
|
||||
'Workspace.ToggleFocusMode',
|
||||
'Comfy.ToggleCanvasInfo',
|
||||
'Comfy.Canvas.ToggleMinimap',
|
||||
'Comfy.Canvas.ToggleLinkVisibility'
|
||||
]
|
||||
)
|
||||
|
||||
menuStore.registerCommands(
|
||||
['View'],
|
||||
['Comfy.Canvas.ZoomIn', 'Comfy.Canvas.ZoomOut', 'Comfy.Canvas.FitView']
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -16,12 +16,17 @@ export const whileMouseDown = (
|
||||
callback(iteration++)
|
||||
}, interval)
|
||||
|
||||
const dispose = useEventListener(element, 'mouseup', () => {
|
||||
const dispose = () => {
|
||||
clearInterval(intervalId)
|
||||
dispose()
|
||||
})
|
||||
disposeGlobal()
|
||||
disposeLocal()
|
||||
}
|
||||
|
||||
// Listen for mouseup globally to catch cases where user drags out of element
|
||||
const disposeGlobal = useEventListener(document, 'mouseup', dispose)
|
||||
const disposeLocal = useEventListener(element, 'mouseup', dispose)
|
||||
|
||||
return {
|
||||
dispose
|
||||
dispose: dispose
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ const mockT = vi.fn((key: string) => {
|
||||
'shortcuts.subcategories.node': 'Node',
|
||||
'shortcuts.subcategories.queue': 'Queue',
|
||||
'shortcuts.subcategories.view': 'View',
|
||||
'shortcuts.subcategories.panelControls': 'Panel Controls'
|
||||
'shortcuts.subcategories.panelControls': 'Panel Controls',
|
||||
'commands.Workflow_New.label': 'New Blank Workflow'
|
||||
}
|
||||
return translations[key] || key
|
||||
})
|
||||
@@ -76,9 +77,7 @@ describe('ShortcutsList', () => {
|
||||
expect(wrapper.text()).toContain('Queue')
|
||||
|
||||
// Check that commands are rendered
|
||||
expect(wrapper.text()).toContain('New Workflow')
|
||||
expect(wrapper.text()).toContain('Add Node')
|
||||
expect(wrapper.text()).toContain('Clear Queue')
|
||||
expect(wrapper.text()).toContain('New Blank Workflow')
|
||||
})
|
||||
|
||||
it('should format keyboard shortcuts correctly', () => {
|
||||
|
||||
@@ -1512,7 +1512,10 @@ describe('useNodePricing', () => {
|
||||
{ model: 'gpt-4o', expected: '$0.0025/$0.01 per 1K tokens' },
|
||||
{ model: 'gpt-4.1-nano', expected: '$0.0001/$0.0004 per 1K tokens' },
|
||||
{ model: 'gpt-4.1-mini', expected: '$0.0004/$0.0016 per 1K tokens' },
|
||||
{ model: 'gpt-4.1', expected: '$0.002/$0.008 per 1K tokens' }
|
||||
{ model: 'gpt-4.1', expected: '$0.002/$0.008 per 1K tokens' },
|
||||
{ model: 'gpt-5-nano', expected: '$0.00005/$0.0004 per 1K tokens' },
|
||||
{ model: 'gpt-5-mini', expected: '$0.00025/$0.002 per 1K tokens' },
|
||||
{ model: 'gpt-5', expected: '$0.00125/$0.01 per 1K tokens' }
|
||||
]
|
||||
|
||||
testCases.forEach(({ model, expected }) => {
|
||||
|
||||
@@ -251,6 +251,53 @@ describe('useReleaseStore', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should skip fetching when --disable-api-nodes is present', async () => {
|
||||
mockSystemStatsStore.systemStats.system.argv = ['--disable-api-nodes']
|
||||
|
||||
await store.initialize()
|
||||
|
||||
expect(mockReleaseService.getReleases).not.toHaveBeenCalled()
|
||||
expect(store.isLoading).toBe(false)
|
||||
})
|
||||
|
||||
it('should skip fetching when --disable-api-nodes is one of multiple args', async () => {
|
||||
mockSystemStatsStore.systemStats.system.argv = [
|
||||
'--port',
|
||||
'8080',
|
||||
'--disable-api-nodes',
|
||||
'--verbose'
|
||||
]
|
||||
|
||||
await store.initialize()
|
||||
|
||||
expect(mockReleaseService.getReleases).not.toHaveBeenCalled()
|
||||
expect(store.isLoading).toBe(false)
|
||||
})
|
||||
|
||||
it('should fetch normally when --disable-api-nodes is not present', async () => {
|
||||
mockSystemStatsStore.systemStats.system.argv = [
|
||||
'--port',
|
||||
'8080',
|
||||
'--verbose'
|
||||
]
|
||||
mockReleaseService.getReleases.mockResolvedValue([mockRelease])
|
||||
|
||||
await store.initialize()
|
||||
|
||||
expect(mockReleaseService.getReleases).toHaveBeenCalled()
|
||||
expect(store.releases).toEqual([mockRelease])
|
||||
})
|
||||
|
||||
it('should fetch normally when argv is undefined', async () => {
|
||||
mockSystemStatsStore.systemStats.system.argv = undefined
|
||||
mockReleaseService.getReleases.mockResolvedValue([mockRelease])
|
||||
|
||||
await store.initialize()
|
||||
|
||||
expect(mockReleaseService.getReleases).toHaveBeenCalled()
|
||||
expect(store.releases).toEqual([mockRelease])
|
||||
})
|
||||
|
||||
it('should handle API errors gracefully', async () => {
|
||||
mockReleaseService.getReleases.mockResolvedValue(null)
|
||||
mockReleaseService.error.value = 'API Error'
|
||||
@@ -307,6 +354,63 @@ describe('useReleaseStore', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('--disable-api-nodes argument handling', () => {
|
||||
it('should skip fetchReleases when --disable-api-nodes is present', async () => {
|
||||
mockSystemStatsStore.systemStats.system.argv = ['--disable-api-nodes']
|
||||
|
||||
await store.fetchReleases()
|
||||
|
||||
expect(mockReleaseService.getReleases).not.toHaveBeenCalled()
|
||||
expect(store.isLoading).toBe(false)
|
||||
})
|
||||
|
||||
it('should skip fetchReleases when --disable-api-nodes is among other args', async () => {
|
||||
mockSystemStatsStore.systemStats.system.argv = [
|
||||
'--port',
|
||||
'8080',
|
||||
'--disable-api-nodes',
|
||||
'--verbose'
|
||||
]
|
||||
|
||||
await store.fetchReleases()
|
||||
|
||||
expect(mockReleaseService.getReleases).not.toHaveBeenCalled()
|
||||
expect(store.isLoading).toBe(false)
|
||||
})
|
||||
|
||||
it('should proceed with fetchReleases when --disable-api-nodes is not present', async () => {
|
||||
mockSystemStatsStore.systemStats.system.argv = [
|
||||
'--port',
|
||||
'8080',
|
||||
'--verbose'
|
||||
]
|
||||
mockReleaseService.getReleases.mockResolvedValue([mockRelease])
|
||||
|
||||
await store.fetchReleases()
|
||||
|
||||
expect(mockReleaseService.getReleases).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should proceed with fetchReleases when argv is null', async () => {
|
||||
mockSystemStatsStore.systemStats.system.argv = null
|
||||
mockReleaseService.getReleases.mockResolvedValue([mockRelease])
|
||||
|
||||
await store.fetchReleases()
|
||||
|
||||
expect(mockReleaseService.getReleases).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should proceed with fetchReleases when system stats are not available', async () => {
|
||||
mockSystemStatsStore.systemStats = null
|
||||
mockReleaseService.getReleases.mockResolvedValue([mockRelease])
|
||||
|
||||
await store.fetchReleases()
|
||||
|
||||
expect(mockSystemStatsStore.fetchSystemStats).toHaveBeenCalled()
|
||||
expect(mockReleaseService.getReleases).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('action handlers', () => {
|
||||
beforeEach(() => {
|
||||
store.releases = [mockRelease]
|
||||
|
||||
Reference in New Issue
Block a user