diff --git a/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/workflow-avif-chromium-linux.png b/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/workflow-avif-chromium-linux.png index 12e526ce63..0dbcabedaa 100644 Binary files a/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/workflow-avif-chromium-linux.png and b/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/workflow-avif-chromium-linux.png differ diff --git a/src/components/common/DotSpinner.vue b/src/components/common/DotSpinner.vue new file mode 100644 index 0000000000..548737bdf5 --- /dev/null +++ b/src/components/common/DotSpinner.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/src/components/dialog/content/LoadWorkflowWarning.vue b/src/components/dialog/content/LoadWorkflowWarning.vue index 41d422d20c..523e3a0f23 100644 --- a/src/components/dialog/content/LoadWorkflowWarning.vue +++ b/src/components/dialog/content/LoadWorkflowWarning.vue @@ -31,28 +31,44 @@ -
+
-
diff --git a/src/components/dialog/content/manager/ManagerHeader.test.ts b/src/components/dialog/content/manager/ManagerHeader.test.ts new file mode 100644 index 0000000000..291020d1f8 --- /dev/null +++ b/src/components/dialog/content/manager/ManagerHeader.test.ts @@ -0,0 +1,82 @@ +import { mount } from '@vue/test-utils' +import { createPinia } from 'pinia' +import PrimeVue from 'primevue/config' +import Tag from 'primevue/tag' +import Tooltip from 'primevue/tooltip' +import { describe, expect, it } from 'vitest' +import { createI18n } from 'vue-i18n' + +import enMessages from '@/locales/en/main.json' + +import ManagerHeader from './ManagerHeader.vue' + +const i18n = createI18n({ + legacy: false, + locale: 'en', + messages: { + en: enMessages + } +}) + +describe('ManagerHeader', () => { + const createWrapper = () => { + return mount(ManagerHeader, { + global: { + plugins: [createPinia(), PrimeVue, i18n], + directives: { + tooltip: Tooltip + }, + components: { + Tag + } + } + }) + } + + it('renders the component title', () => { + const wrapper = createWrapper() + + expect(wrapper.find('h2').text()).toBe( + enMessages.manager.discoverCommunityContent + ) + }) + + it('displays the legacy manager UI tag', () => { + const wrapper = createWrapper() + + const tag = wrapper.find('[data-pc-name="tag"]') + expect(tag.exists()).toBe(true) + expect(tag.text()).toContain(enMessages.manager.legacyManagerUI) + }) + + it('applies info severity to the tag', () => { + const wrapper = createWrapper() + + const tag = wrapper.find('[data-pc-name="tag"]') + expect(tag.classes()).toContain('p-tag-info') + }) + + it('displays info icon in the tag', () => { + const wrapper = createWrapper() + + const icon = wrapper.find('.pi-info-circle') + expect(icon.exists()).toBe(true) + }) + + it('has cursor-help class on the tag', () => { + const wrapper = createWrapper() + + const tag = wrapper.find('[data-pc-name="tag"]') + expect(tag.classes()).toContain('cursor-help') + }) + + it('has proper structure with flex container', () => { + const wrapper = createWrapper() + + const flexContainer = wrapper.find('.flex.justify-end.ml-auto.pr-4') + expect(flexContainer.exists()).toBe(true) + + const tag = flexContainer.find('[data-pc-name="tag"]') + expect(tag.exists()).toBe(true) + }) +}) diff --git a/src/components/dialog/content/manager/ManagerHeader.vue b/src/components/dialog/content/manager/ManagerHeader.vue index f6177c87b8..a4fbdfaa35 100644 --- a/src/components/dialog/content/manager/ManagerHeader.vue +++ b/src/components/dialog/content/manager/ManagerHeader.vue @@ -4,6 +4,22 @@

{{ $t('manager.discoverCommunityContent') }}

+
+ +
+ + diff --git a/src/components/dialog/content/manager/button/PackInstallButton.vue b/src/components/dialog/content/manager/button/PackInstallButton.vue index 7bba01ef0a..2f9751d0fd 100644 --- a/src/components/dialog/content/manager/button/PackInstallButton.vue +++ b/src/components/dialog/content/manager/button/PackInstallButton.vue @@ -10,7 +10,6 @@ :loading="isInstalling" :loading-message="$t('g.installing')" @action="installAllPacks" - @click="onClick" /> @@ -37,10 +36,6 @@ const { nodePacks, variant, label } = defineProps<{ const isInstalling = inject(IsInstallingKey, ref(false)) -const onClick = (): void => { - isInstalling.value = true -} - const managerStore = useComfyManagerStore() const createPayload = (installItem: NodePack) => { @@ -65,8 +60,6 @@ const installPack = (item: NodePack) => const installAllPacks = async () => { if (!nodePacks?.length) return - isInstalling.value = true - const uninstalledPacks = nodePacks.filter( (pack) => !managerStore.isPackInstalled(pack.id) ) diff --git a/src/components/dialog/content/manager/packCard/PackCard.vue b/src/components/dialog/content/manager/packCard/PackCard.vue index 08caeb29cb..ee2cef92de 100644 --- a/src/components/dialog/content/manager/packCard/PackCard.vue +++ b/src/components/dialog/content/manager/packCard/PackCard.vue @@ -84,10 +84,9 @@ diff --git a/src/components/topbar/CommandMenubar.vue b/src/components/topbar/CommandMenubar.vue index 41a8b0ba0c..95289105e4 100644 --- a/src/components/topbar/CommandMenubar.vue +++ b/src/components/topbar/CommandMenubar.vue @@ -107,9 +107,12 @@ import SubgraphBreadcrumb from '@/components/breadcrumb/SubgraphBreadcrumb.vue' import SettingDialogContent from '@/components/dialog/content/SettingDialogContent.vue' import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue' import { useDialogService } from '@/services/dialogService' -import { useAboutPanelStore } from '@/stores/aboutPanelStore' import { useCommandStore } from '@/stores/commandStore' import { useDialogStore } from '@/stores/dialogStore' +import { + ManagerUIState, + useManagerStateStore +} from '@/stores/managerStateStore' import { useMenuItemStore } from '@/stores/menuItemStore' import { useSettingStore } from '@/stores/settingStore' import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore' @@ -121,7 +124,6 @@ const colorPaletteStore = useColorPaletteStore() const menuItemsStore = useMenuItemStore() const commandStore = useCommandStore() const dialogStore = useDialogStore() -const aboutPanelStore = useAboutPanelStore() const settingStore = useSettingStore() const { t } = useI18n() @@ -157,23 +159,28 @@ const showSettings = (defaultPanel?: string) => { }) } -// Temporary duplicated from LoadWorkflowWarning.vue -// Determines if ComfyUI-Manager is installed by checking for its badge in the about panel -// This allows us to conditionally show the Manager button only when the extension is available -// TODO: Remove this check when Manager functionality is fully migrated into core -const isManagerInstalled = computed(() => { - return aboutPanelStore.badges.some( - (badge) => - badge.label.includes('ComfyUI-Manager') || - badge.url.includes('ComfyUI-Manager') - ) -}) +const managerStateStore = useManagerStateStore() + +const showManageExtensions = async () => { + const state = await managerStateStore.getManagerUIState() + + switch (state) { + case ManagerUIState.DISABLED: + showSettings('extension') + break + + case ManagerUIState.LEGACY_UI: + try { + await commandStore.execute('Comfy.Manager.Menu.ToggleVisibility') + } catch { + // If legacy command doesn't exist, fall back to extensions panel + showSettings('extension') + } + break -const showManageExtensions = () => { - if (isManagerInstalled.value) { - useDialogService().showManagerDialog() - } else { - showSettings('extension') + case ManagerUIState.NEW_UI: + useDialogService().showManagerDialog() + break } } diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index 9162af2c54..fb8e122d2a 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -19,10 +19,15 @@ import { useDialogService } from '@/services/dialogService' import { useLitegraphService } from '@/services/litegraphService' import { useWorkflowService } from '@/services/workflowService' import type { ComfyCommand } from '@/stores/commandStore' +import { useCommandStore } 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 { + ManagerUIState, + useManagerStateStore +} from '@/stores/managerStateStore' import { useQueueSettingsStore, useQueueStore } from '@/stores/queueStore' import { useSettingStore } from '@/stores/settingStore' import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore' @@ -32,6 +37,7 @@ import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore' import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore' import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore' import { useWorkspaceStore } from '@/stores/workspaceStore' +import { ManagerTab } from '@/types/comfyManagerTypes' import { getAllNonIoNodesInSubgraph, getExecutionIdsForSelectedNodes @@ -710,12 +716,99 @@ export function useCoreCommands(): ComfyCommand[] { } }, { - id: 'Comfy.Manager.CustomNodesManager', - icon: 'pi pi-puzzle', - label: 'Toggle the Custom Nodes Manager', + id: 'Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu', + icon: 'pi pi-objects-column', + label: 'Custom Nodes Manager', versionAdded: '1.12.10', - function: () => { - dialogService.toggleManagerDialog() + function: async () => { + const managerStore = useManagerStateStore() + const state = await managerStore.getManagerUIState() + + switch (state) { + case ManagerUIState.DISABLED: + dialogService.showSettingsDialog('extension') + break + + case ManagerUIState.LEGACY_UI: + useCommandStore() + .execute('Comfy.Manager.Menu.ToggleVisibility') + .catch(() => { + // If legacy command doesn't exist, fall back to extensions panel + dialogService.showSettingsDialog('extension') + }) + break + + case ManagerUIState.NEW_UI: + dialogService.showManagerDialog() + break + } + } + }, + { + id: 'Comfy.Manager.ShowUpdateAvailablePacks', + icon: 'pi pi-sync', + label: 'Check for Custom Node Updates', + versionAdded: '1.17.0', + function: async () => { + const managerStore = useManagerStateStore() + const state = await managerStore.getManagerUIState() + + switch (state) { + case ManagerUIState.DISABLED: + dialogService.showSettingsDialog('extension') + break + + case ManagerUIState.LEGACY_UI: + try { + await useCommandStore().execute( + 'Comfy.Manager.Menu.ToggleVisibility' + ) + } catch { + // If legacy command doesn't exist, fall back to extensions panel + dialogService.showSettingsDialog('extension') + } + break + + case ManagerUIState.NEW_UI: + dialogService.showManagerDialog({ + initialTab: ManagerTab.UpdateAvailable + }) + break + } + } + }, + { + id: 'Comfy.Manager.ShowMissingPacks', + icon: 'pi pi-exclamation-circle', + label: 'Install Missing Custom Nodes', + versionAdded: '1.17.0', + function: async () => { + const managerStore = useManagerStateStore() + const state = await managerStore.getManagerUIState() + + switch (state) { + case ManagerUIState.DISABLED: + // When manager is disabled, open the extensions panel in settings + dialogService.showSettingsDialog('extension') + break + + case ManagerUIState.LEGACY_UI: + try { + await useCommandStore().execute( + 'Comfy.Manager.Menu.ToggleVisibility' + ) + } catch { + // If legacy command doesn't exist, fall back to extensions panel + dialogService.showSettingsDialog('extension') + } + break + + case ManagerUIState.NEW_UI: + dialogService.showManagerDialog({ + initialTab: ManagerTab.Missing + }) + break + } } }, { @@ -878,6 +971,84 @@ export function useCoreCommands(): ComfyCommand[] { navigationStore.navigationStack.at(-2) ?? canvas.graph.rootGraph ) } + }, + { + id: 'Comfy.Manager.CustomNodesManager.ShowLegacyCustomNodesMenu', + icon: 'pi pi-bars', + label: 'Custom Nodes (Legacy)', + versionAdded: '1.16.4', + function: async () => { + try { + await useCommandStore().execute( + 'Comfy.Manager.CustomNodesManager.ToggleVisibility' + ) + } catch (error) { + useToastStore().add({ + severity: 'error', + summary: t('g.error'), + detail: t('manager.legacyMenuNotAvailable'), + life: 3000 + }) + } + } + }, + { + id: 'Comfy.Manager.ShowLegacyManagerMenu', + icon: 'mdi mdi-puzzle', + label: 'Manager Menu (Legacy)', + versionAdded: '1.16.4', + function: async () => { + try { + await useCommandStore().execute('Comfy.Manager.Menu.ToggleVisibility') + } catch (error) { + useToastStore().add({ + severity: 'error', + summary: t('g.error'), + detail: t('manager.legacyMenuNotAvailable'), + life: 3000 + }) + } + } + }, + { + id: 'Comfy.Memory.UnloadModels', + icon: 'mdi mdi-vacuum-outline', + label: 'Unload Models', + versionAdded: '1.16.4', + function: async () => { + if (!useSettingStore().get('Comfy.Memory.AllowManualUnload')) { + useToastStore().add({ + severity: 'error', + summary: t('g.error'), + detail: t('g.commandProhibited', { + command: 'Comfy.Memory.UnloadModels' + }), + life: 3000 + }) + return + } + await api.freeMemory({ freeExecutionCache: false }) + } + }, + { + id: 'Comfy.Memory.UnloadModelsAndExecutionCache', + icon: 'mdi mdi-vacuum-outline', + label: 'Unload Models and Execution Cache', + versionAdded: '1.16.4', + function: async () => { + if (!useSettingStore().get('Comfy.Memory.AllowManualUnload')) { + useToastStore().add({ + severity: 'error', + summary: t('g.error'), + detail: t('g.commandProhibited', { + command: 'Comfy.Memory.UnloadModelsAndExecutionCache' + }), + life: 3000 + }) + return + } + await api.freeMemory({ freeExecutionCache: true }) + } } ] diff --git a/src/composables/useFeatureFlags.ts b/src/composables/useFeatureFlags.ts new file mode 100644 index 0000000000..a578eb8bf1 --- /dev/null +++ b/src/composables/useFeatureFlags.ts @@ -0,0 +1,40 @@ +import { computed, reactive, readonly } from 'vue' + +import { api } from '@/scripts/api' + +/** + * Known server feature flags (top-level, not extensions) + */ +export enum ServerFeatureFlag { + SUPPORTS_PREVIEW_METADATA = 'supports_preview_metadata', + MAX_UPLOAD_SIZE = 'max_upload_size', + MANAGER_SUPPORTS_V4 = 'extension.manager.supports_v4' +} + +/** + * Composable for reactive access to feature flags + */ +export function useFeatureFlags() { + // Create reactive state that tracks server feature flags + const flags = reactive({ + get supportsPreviewMetadata() { + return api.getServerFeature(ServerFeatureFlag.SUPPORTS_PREVIEW_METADATA) + }, + get maxUploadSize() { + return api.getServerFeature(ServerFeatureFlag.MAX_UPLOAD_SIZE) + }, + get supportsManagerV4() { + return api.getServerFeature(ServerFeatureFlag.MANAGER_SUPPORTS_V4) + } + }) + + // Create a reactive computed for any feature flag + const featureFlag = (featurePath: string, defaultValue?: T) => { + return computed(() => api.getServerFeature(featurePath, defaultValue)) + } + + return { + flags: readonly(flags), + featureFlag + } +} diff --git a/src/config/clientFeatureFlags.json b/src/config/clientFeatureFlags.json index 84a233ccfe..e3fae66286 100644 --- a/src/config/clientFeatureFlags.json +++ b/src/config/clientFeatureFlags.json @@ -1,3 +1,4 @@ { - "supports_preview_metadata": true + "supports_preview_metadata": true, + "supports_manager_v4_ui": true } diff --git a/src/constants/coreMenuCommands.ts b/src/constants/coreMenuCommands.ts index 94668668a9..c24acf9c0d 100644 --- a/src/constants/coreMenuCommands.ts +++ b/src/constants/coreMenuCommands.ts @@ -13,6 +13,14 @@ export const CORE_MENU_COMMANDS = [ ], [['Edit'], ['Comfy.Undo', 'Comfy.Redo']], [['Edit'], ['Comfy.OpenClipspace']], + [ + ['Manager'], + [ + 'Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu', + 'Comfy.Manager.ShowMissingPacks', + 'Comfy.Manager.ShowUpdateAvailablePacks' + ] + ], [ ['Help'], [ diff --git a/src/constants/coreSettings.ts b/src/constants/coreSettings.ts index d91c03cd56..b293d5e1f3 100644 --- a/src/constants/coreSettings.ts +++ b/src/constants/coreSettings.ts @@ -13,6 +13,13 @@ import type { SettingParams } from '@/types/settingTypes' * when they are no longer needed. */ export const CORE_SETTINGS: SettingParams[] = [ + { + id: 'Comfy.Memory.AllowManualUnload', + name: 'Allow manual unload of models and execution cache via user command', + type: 'hidden', + defaultValue: true, + versionAdded: '1.18.0' + }, { id: 'Comfy.Validation.Workflows', name: 'Validate workflows', diff --git a/src/locales/en/commands.json b/src/locales/en/commands.json index 2945d7ba24..471f34c24d 100644 --- a/src/locales/en/commands.json +++ b/src/locales/en/commands.json @@ -164,8 +164,20 @@ "Comfy_LoadDefaultWorkflow": { "label": "Load Default Workflow" }, - "Comfy_Manager_CustomNodesManager": { - "label": "Toggle the Custom Nodes Manager" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "Custom Nodes Manager" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "Custom Nodes (Legacy)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "Manager Menu (Legacy)" + }, + "Comfy_Manager_ShowMissingPacks": { + "label": "Install Missing Custom Nodes" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "Check for Custom Node Updates" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Toggle the Custom Nodes Manager Progress Bar" @@ -179,6 +191,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "Open Mask Editor for Selected Node" }, + "Comfy_Memory_UnloadModels": { + "label": "Unload Models" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "Unload Models and Execution Cache" + }, "Comfy_NewBlankWorkflow": { "label": "New Blank Workflow" }, diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 7bac7e521d..bae711055e 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -25,7 +25,6 @@ "confirmed": "Confirmed", "reset": "Reset", "resetAll": "Reset All", - "clearFilters": "Clear Filters", "resetAllKeybindingsTooltip": "Reset all keybindings to default", "customizeFolder": "Customize Folder", "icon": "Icon", @@ -99,12 +98,6 @@ "nodes": "Nodes", "community": "Community", "all": "All", - "versionMismatchWarning": "Version Compatibility Warning", - "versionMismatchWarningMessage": "{warning}: {detail} Visit https://docs.comfy.org/installation/update_comfyui#common-update-issues for update instructions.", - "frontendOutdated": "Frontend version {frontendVersion} is outdated. Backend requires {requiredVersion} or higher.", - "frontendNewer": "Frontend version {frontendVersion} may not be compatible with backend version {backendVersion}.", - "updateFrontend": "Update Frontend", - "dismiss": "Dismiss", "update": "Update", "updated": "Updated", "resultsCount": "Found {count} Results", @@ -141,15 +134,18 @@ "releaseTitle": "{package} {version} Release", "progressCountOf": "of", "keybindingAlreadyExists": "Keybinding already exists on", + "commandProhibited": "Command {command} is prohibited. Contact an administrator for more information.", "startRecording": "Start Recording", "stopRecording": "Stop Recording", "micPermissionDenied": "Microphone permission denied", "noAudioRecorded": "No audio recorded", - "nodesRunning": "nodes running", - "duplicate": "Duplicate" + "nodesRunning": "nodes running" }, "manager": { "title": "Custom Nodes Manager", + "legacyMenuNotAvailable": "Legacy manager menu is not available, defaulting to the new manager menu.", + "legacyManagerUI": "Use Legacy UI", + "legacyManagerUIDescription": "To use the legacy Manager UI, start ComfyUI with --enable-manager-legacy-ui", "failed": "Failed ({count})", "noNodesFound": "No nodes found", "noNodesFoundDescription": "The pack's nodes either could not be parsed, or the pack is a frontend extension only and doesn't have any nodes.", @@ -159,6 +155,12 @@ "inWorkflow": "In Workflow", "infoPanelEmpty": "Click an item to see the info", "restartToApplyChanges": "To apply changes, please restart ComfyUI", + "clickToFinishSetup": "Click", + "toFinishSetup": "to finish setup", + "applyChanges": "Apply Changes", + "restartingBackend": "Restarting backend to apply changes...", + "extensionsSuccessfullyInstalled": "Extension(s) successfully installed and are ready to use!", + "installingDependencies": "Installing dependencies...", "loadingVersions": "Loading versions...", "selectVersion": "Select Version", "downloads": "Downloads", @@ -432,19 +434,12 @@ "restart": "Restart" }, "sideToolbar": { + "themeToggle": "Toggle Theme", "helpCenter": "Help Center", "logout": "Logout", "queue": "Queue", "nodeLibrary": "Node Library", "workflows": "Workflows", - "templates": "Templates", - "labels": { - "queue": "Queue", - "nodes": "Nodes", - "models": "Models", - "workflows": "Workflows", - "templates": "Templates" - }, "browseTemplates": "Browse example templates", "openWorkflow": "Open workflow in local file system", "newBlankWorkflow": "Create a new blank workflow", @@ -540,14 +535,7 @@ "clipspace": "Open Clipspace", "resetView": "Reset canvas view", "clear": "Clear workflow", - "toggleBottomPanel": "Toggle Bottom Panel", - "theme": "Theme", - "dark": "Dark", - "light": "Light", - "manageExtensions": "Manage Extensions", - "settings": "Settings", - "help": "Help", - "queue": "Queue Panel" + "toggleBottomPanel": "Toggle Bottom Panel" }, "tabMenu": { "duplicateTab": "Duplicate Tab", @@ -560,8 +548,6 @@ }, "templateWorkflows": { "title": "Get Started with a Template", - "loadingMore": "Loading more templates...", - "searchPlaceholder": "Search templates...", "category": { "ComfyUI Examples": "ComfyUI Examples", "Custom Nodes": "Custom Nodes", @@ -891,8 +877,7 @@ "fitView": "Fit View", "selectMode": "Select Mode", "panMode": "Pan Mode", - "toggleLinkVisibility": "Toggle Link Visibility", - "toggleMinimap": "Toggle Minimap" + "toggleLinkVisibility": "Toggle Link Visibility" }, "groupNode": { "create": "Create group node", @@ -941,8 +926,9 @@ "Image Layer": "Image Layer" }, "menuLabels": { - "File": "File", + "Workflow": "Workflow", "Edit": "Edit", + "Manager": "Manager", "Help": "Help", "Check for Updates": "Check for Updates", "Open Custom Nodes Folder": "Open Custom Nodes Folder", @@ -956,20 +942,18 @@ "Quit": "Quit", "Reinstall": "Reinstall", "Restart": "Restart", - "Open 3D Viewer (Beta) for Selected Node": "Open 3D Viewer (Beta) for Selected Node", "Browse Templates": "Browse Templates", "Add Edit Model Step": "Add Edit Model Step", "Delete Selected Items": "Delete Selected Items", - "Zoom to fit": "Zoom to fit", + "Fit view to selected nodes": "Fit view to selected nodes", "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", - "Node Links": "Node Links", + "Canvas Toggle Link Visibility": "Canvas Toggle Link Visibility", "Canvas Toggle Lock": "Canvas Toggle Lock", - "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", @@ -985,10 +969,8 @@ "Export (API)": "Export (API)", "Give Feedback": "Give Feedback", "Convert Selection to Subgraph": "Convert Selection to Subgraph", - "Exit Subgraph": "Exit Subgraph", "Fit Group To Contents": "Fit Group To Contents", "Group Selected Nodes": "Group Selected Nodes", - "Unpack the selected Subgraph": "Unpack the selected Subgraph", "Convert selected nodes to group node": "Convert selected nodes to group node", "Manage group nodes": "Manage group nodes", "Ungroup selected group nodes": "Ungroup selected group nodes", @@ -999,14 +981,17 @@ "ComfyUI Issues": "ComfyUI Issues", "Interrupt": "Interrupt", "Load Default Workflow": "Load Default Workflow", - "Toggle the Custom Nodes Manager": "Toggle the Custom Nodes Manager", + "Custom Nodes Manager": "Custom Nodes Manager", + "Custom Nodes (Legacy)": "Custom Nodes (Legacy)", + "Manager Menu (Legacy)": "Manager Menu (Legacy)", + "Install Missing Custom Nodes": "Install Missing Custom Nodes", + "Check for Custom Node Updates": "Check for Custom Node Updates", "Toggle the Custom Nodes Manager Progress Bar": "Toggle the Custom Nodes Manager Progress Bar", - "Decrease Brush Size in MaskEditor": "Decrease Brush Size in MaskEditor", - "Increase Brush Size in MaskEditor": "Increase Brush Size in MaskEditor", "Open Mask Editor for Selected Node": "Open Mask Editor for Selected Node", + "Unload Models": "Unload Models", + "Unload Models and Execution Cache": "Unload Models and Execution Cache", "New": "New", "Clipspace": "Clipspace", - "Manager": "Manager", "Open": "Open", "Queue Prompt": "Queue Prompt", "Queue Prompt (Front)": "Queue Prompt (Front)", @@ -1016,8 +1001,6 @@ "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", @@ -1026,17 +1009,14 @@ "Next Opened Workflow": "Next Opened Workflow", "Previous Opened Workflow": "Previous Opened Workflow", "Toggle Search Box": "Toggle Search Box", - "Bottom Panel": "Bottom Panel", - "Show Keybindings Dialog": "Show Keybindings Dialog", + "Toggle Bottom Panel": "Toggle Bottom Panel", "Toggle Terminal Bottom Panel": "Toggle Terminal Bottom Panel", "Toggle Logs Bottom Panel": "Toggle Logs Bottom Panel", - "Toggle Essential Bottom Panel": "Toggle Essential Bottom Panel", - "Toggle View Controls Bottom Panel": "Toggle View Controls Bottom Panel", - "Focus Mode": "Focus Mode", - "Model Library": "Model Library", - "Node Library": "Node Library", - "Queue Panel": "Queue Panel", - "Workflows": "Workflows" + "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" }, "desktopMenu": { "reinstall": "Reinstall", @@ -1095,8 +1075,7 @@ "User": "User", "Credits": "Credits", "API Nodes": "API Nodes", - "Notification Preferences": "Notification Preferences", - "3DViewer": "3DViewer" + "Notification Preferences": "Notification Preferences" }, "serverConfigItems": { "listen": { @@ -1380,13 +1359,6 @@ "outdatedVersionGeneric": "Some nodes require a newer version of ComfyUI. Please update to use all nodes.", "coreNodesFromVersion": "Requires ComfyUI {version}:" }, - "versionMismatchWarning": { - "title": "Version Compatibility Warning", - "frontendOutdated": "Frontend version {frontendVersion} is outdated. Backend requires version {requiredVersion} or higher.", - "frontendNewer": "Frontend version {frontendVersion} may not be compatible with backend version {backendVersion}.", - "updateFrontend": "Update Frontend", - "dismiss": "Dismiss" - }, "errorDialog": { "defaultTitle": "An error occurred", "loadWorkflowTitle": "Loading aborted due to error reloading workflow data", @@ -1448,31 +1420,12 @@ "depth": "Depth", "lineart": "Lineart" }, - "upDirections": { - "original": "Original" - }, "startRecording": "Start Recording", "stopRecording": "Stop Recording", "exportRecording": "Export Recording", "clearRecording": "Clear Recording", "resizeNodeMatchOutput": "Resize Node to match output", - "loadingBackgroundImage": "Loading Background Image", - "cameraType": { - "perspective": "Perspective", - "orthographic": "Orthographic" - }, - "viewer": { - "title": "3D Viewer (Beta)", - "apply": "Apply", - "cancel": "Cancel", - "cameraType": "Camera Type", - "sceneSettings": "Scene Settings", - "cameraSettings": "Camera Settings", - "lightSettings": "Light Settings", - "exportSettings": "Export Settings", - "modelSettings": "Model Settings" - }, - "openIn3DViewer": "Open in 3D Viewer" + "loadingBackgroundImage": "Loading Background Image" }, "toastMessages": { "nothingToQueue": "Nothing to queue", @@ -1510,8 +1463,7 @@ "useApiKeyTip": "Tip: Can't access normal login? Use the Comfy API Key option.", "nothingSelected": "Nothing selected", "cannotCreateSubgraph": "Cannot create subgraph", - "failedToConvertToSubgraph": "Failed to convert items to subgraph", - "failedToInitializeLoad3dViewer": "Failed to initialize 3D Viewer" + "failedToConvertToSubgraph": "Failed to convert items to subgraph" }, "auth": { "apiKey": { @@ -1664,32 +1616,5 @@ "whatsNewPopup": { "learnMore": "Learn more", "noReleaseNotes": "No release notes available." - }, - "breadcrumbsMenu": { - "duplicate": "Duplicate", - "clearWorkflow": "Clear Workflow", - "deleteWorkflow": "Delete Workflow", - "enterNewName": "Enter new name" - }, - "shortcuts": { - "essentials": "Essential", - "viewControls": "View Controls", - "manageShortcuts": "Manage Shortcuts", - "noKeybinding": "No keybinding", - "keyboardShortcuts": "Keyboard Shortcuts", - "subcategories": { - "workflow": "Workflow", - "node": "Node", - "queue": "Queue", - "view": "View", - "panelControls": "Panel Controls" - } - }, - "minimap": { - "nodeColors": "Node Colors", - "showLinks": "Show Links", - "showGroups": "Show Frames/Groups", - "renderBypassState": "Render Bypass State", - "renderErrorState": "Render Error State" } } \ No newline at end of file diff --git a/src/locales/es/commands.json b/src/locales/es/commands.json index 55e903e228..c7cbe158b4 100644 --- a/src/locales/es/commands.json +++ b/src/locales/es/commands.json @@ -164,8 +164,20 @@ "Comfy_LoadDefaultWorkflow": { "label": "Cargar flujo de trabajo predeterminado" }, - "Comfy_Manager_CustomNodesManager": { - "label": "Administrador de nodos personalizados" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "Nodos personalizados (Beta)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "Nodos personalizados (heredados)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "Menú del administrador (heredado)" + }, + "Comfy_Manager_ShowMissingPacks": { + "label": "Instalar faltantes" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "Buscar actualizaciones" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Alternar diálogo de progreso del administrador" @@ -179,6 +191,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "Abrir editor de máscara para el nodo seleccionado" }, + "Comfy_Memory_UnloadModels": { + "label": "Descargar modelos" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "Descargar modelos y caché de ejecución" + }, "Comfy_NewBlankWorkflow": { "label": "Nuevo flujo de trabajo en blanco" }, diff --git a/src/locales/es/main.json b/src/locales/es/main.json index 131c85f7a6..e07bec10e6 100644 --- a/src/locales/es/main.json +++ b/src/locales/es/main.json @@ -272,11 +272,11 @@ "category": "Categoría", "choose_file_to_upload": "elige archivo para subir", "clear": "Limpiar", - "clearFilters": "Borrar filtros", "close": "Cerrar", "color": "Color", "comingSoon": "Próximamente", "command": "Comando", + "commandProhibited": "El comando {command} está prohibido. Contacta a un administrador para más información.", "community": "Comunidad", "completed": "Completado", "confirm": "Confirmar", @@ -299,7 +299,6 @@ "disabling": "Deshabilitando", "dismiss": "Descartar", "download": "Descargar", - "duplicate": "Duplicar", "edit": "Editar", "empty": "Vacío", "enableAll": "Habilitar todo", @@ -570,10 +569,6 @@ "applyingTexture": "Aplicando textura...", "backgroundColor": "Color de fondo", "camera": "Cámara", - "cameraType": { - "orthographic": "Ortográfica", - "perspective": "Perspectiva" - }, "clearRecording": "Borrar grabación", "edgeThreshold": "Umbral de borde", "export": "Exportar", @@ -594,7 +589,6 @@ "wireframe": "Malla" }, "model": "Modelo", - "openIn3DViewer": "Abrir en el visor 3D", "previewOutput": "Vista previa de salida", "removeBackgroundImage": "Eliminar imagen de fondo", "resizeNodeMatchOutput": "Redimensionar nodo para coincidir con la salida", @@ -605,22 +599,8 @@ "switchCamera": "Cambiar cámara", "switchingMaterialMode": "Cambiando modo de material...", "upDirection": "Dirección hacia arriba", - "upDirections": { - "original": "Original" - }, "uploadBackgroundImage": "Subir imagen de fondo", - "uploadTexture": "Subir textura", - "viewer": { - "apply": "Aplicar", - "cameraSettings": "Configuración de la cámara", - "cameraType": "Tipo de cámara", - "cancel": "Cancelar", - "exportSettings": "Configuración de exportación", - "lightSettings": "Configuración de la luz", - "modelSettings": "Configuración del modelo", - "sceneSettings": "Configuración de la escena", - "title": "Visor 3D (Beta)" - } + "uploadTexture": "Subir textura" }, "loadWorkflowWarning": { "coreNodesFromVersion": "Requiere ComfyUI {version}:", @@ -667,6 +647,9 @@ "installationQueue": "Cola de Instalación", "lastUpdated": "Última Actualización", "latestVersion": "Última", + "legacyManagerUI": "Usar UI antigua", + "legacyManagerUIDescription": "Para usar la UI antigua del Manager, inicia ComfyUI con --enable-manager-legacy-ui", + "legacyMenuNotAvailable": "El menú del administrador antiguo no está disponible en esta versión de ComfyUI. Por favor, utiliza el nuevo menú del administrador en su lugar.", "license": "Licencia", "loadingVersions": "Cargando versiones...", "nightlyVersion": "Nocturna", @@ -749,7 +732,6 @@ "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", @@ -763,11 +745,12 @@ "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 Performance": "Rendimiento del lienzo", + "Canvas Toggle Link Visibility": "Alternar visibilidad de enlace en lienzo", "Canvas Toggle Lock": "Alternar bloqueo en lienzo", + "Canvas Toggle Minimap": "Lienzo: Alternar minimapa", + "Check for Custom Node Updates": "Buscar actualizaciones de nodos personalizados", "Check for Updates": "Buscar actualizaciones", "Clear Pending Tasks": "Borrar tareas pendientes", "Clear Workflow": "Borrar flujo de trabajo", @@ -781,28 +764,27 @@ "Contact Support": "Contactar soporte", "Convert Selection to Subgraph": "Convertir selección en subgrafo", "Convert selected nodes to group node": "Convertir nodos seleccionados en nodo de grupo", + "Custom Nodes (Legacy)": "Nodos personalizados (heredado)", + "Custom Nodes Manager": "Administrador de Nodos Personalizados", "Decrease Brush Size in MaskEditor": "Disminuir tamaño del pincel en MaskEditor", "Delete Selected Items": "Eliminar elementos seleccionados", "Desktop User Guide": "Guía de usuario de escritorio", "Duplicate Current Workflow": "Duplicar flujo de trabajo actual", "Edit": "Editar", - "Exit Subgraph": "Salir de subgrafo", "Export": "Exportar", "Export (API)": "Exportar (API)", - "File": "Archivo", "Fit Group To Contents": "Ajustar grupo a contenidos", - "Focus Mode": "Modo de enfoque", + "Fit view to selected nodes": "Ajustar vista a los nodos seleccionados", "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", + "Install Missing Custom Nodes": "Instalar nodos personalizados faltantes", "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", + "Manager Menu (Legacy)": "Menú de gestión (heredado)", "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", @@ -810,10 +792,7 @@ "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", "Open DevTools": "Abrir DevTools", "Open Inputs Folder": "Abrir carpeta de entradas", @@ -826,7 +805,6 @@ "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", @@ -839,31 +817,26 @@ "Restart": "Reiniciar", "Save": "Guardar", "Save As": "Guardar como", - "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 Essential Bottom Panel": "Alternar panel inferior esencial", + "Toggle Bottom Panel": "Alternar panel inferior", + "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 the Custom Nodes Manager": "Alternar el Administrador de Nodos Personalizados", + "Toggle Workflows Sidebar": "Alternar barra lateral de los flujos de trabajo", "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", - "Workflows": "Flujos de trabajo", + "Unload Models": "Descargar modelos", + "Unload Models and Execution Cache": "Descargar modelos y caché de ejecución", + "Workflow": "Flujo de trabajo", "Zoom In": "Acercar", - "Zoom Out": "Alejar", - "Zoom to fit": "Ajustar al tamaño" - }, - "minimap": { - "nodeColors": "Colores de nodos", - "renderBypassState": "Mostrar estado de omisión", - "renderErrorState": "Mostrar estado de error", - "showGroups": "Mostrar marcos/grupos", - "showLinks": "Mostrar enlaces" + "Zoom Out": "Alejar" }, "missingModelsDialog": { "doNotAskAgain": "No mostrar esto de nuevo", @@ -1131,7 +1104,6 @@ }, "settingsCategories": { "3D": "3D", - "3DViewer": "Visor 3D", "API Nodes": "Nodos API", "About": "Acerca de", "Appearance": "Apariencia", @@ -1183,31 +1155,10 @@ "Window": "Ventana", "Workflow": "Flujo de Trabajo" }, - "shortcuts": { - "essentials": "Esenciales", - "keyboardShortcuts": "Atajos de teclado", - "manageShortcuts": "Gestionar atajos", - "noKeybinding": "Sin asignación de tecla", - "subcategories": { - "node": "Nodo", - "panelControls": "Controles del panel", - "queue": "Cola", - "view": "Vista", - "workflow": "Flujo de trabajo" - }, - "viewControls": "Controles de vista" - }, "sideToolbar": { "browseTemplates": "Explorar plantillas de ejemplo", "downloads": "Descargas", "helpCenter": "Centro de ayuda", - "labels": { - "models": "Modelos", - "nodes": "Nodos", - "queue": "Cola", - "templates": "Plantillas", - "workflows": "Flujos de trabajo" - }, "logout": "Cerrar sesión", "modelLibrary": "Biblioteca de modelos", "newBlankWorkflow": "Crear un nuevo flujo de trabajo en blanco", @@ -1245,7 +1196,6 @@ }, "showFlatList": "Mostrar lista plana" }, - "templates": "Plantillas", "workflowTab": { "confirmDelete": "¿Estás seguro de que quieres eliminar este flujo de trabajo?", "confirmDeleteTitle": "¿Eliminar flujo de trabajo?", @@ -1292,8 +1242,6 @@ "Video": "Video", "Video API": "API de Video" }, - "loadingMore": "Cargando más plantillas...", - "searchPlaceholder": "Buscar plantillas...", "template": { "3D": { "3d_hunyuan3d_image_to_model": "Hunyuan3D 2.0", @@ -1616,7 +1564,6 @@ "failedToExportModel": "Error al exportar modelo como {format}", "failedToFetchBalance": "No se pudo obtener el saldo: {error}", "failedToFetchLogs": "Error al obtener los registros del servidor", - "failedToInitializeLoad3dViewer": "No se pudo inicializar el visor 3D", "failedToInitiateCreditPurchase": "No se pudo iniciar la compra de créditos: {error}", "failedToPurchaseCredits": "No se pudo comprar créditos: {error}", "fileLoadError": "No se puede encontrar el flujo de trabajo en {fileName}", diff --git a/src/locales/fr/commands.json b/src/locales/fr/commands.json index c530334819..e64bf5f6ae 100644 --- a/src/locales/fr/commands.json +++ b/src/locales/fr/commands.json @@ -164,8 +164,20 @@ "Comfy_LoadDefaultWorkflow": { "label": "Charger le flux de travail par défaut" }, - "Comfy_Manager_CustomNodesManager": { - "label": "Gestionnaire de Nœuds Personnalisés" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "Nœuds personnalisés (Beta)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "Nœuds personnalisés (hérités)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "Menu du gestionnaire (héritage)" + }, + "Comfy_Manager_ShowMissingPacks": { + "label": "Installer manquants" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "Vérifier les mises à jour" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Basculer la boîte de dialogue de progression" @@ -179,6 +191,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "Ouvrir l'éditeur de masque pour le nœud sélectionné" }, + "Comfy_Memory_UnloadModels": { + "label": "Décharger les modèles" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "Décharger les modèles et le cache d'exécution" + }, "Comfy_NewBlankWorkflow": { "label": "Nouveau flux de travail vierge" }, diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json index ea6795d1ea..80e87fa7cf 100644 --- a/src/locales/fr/main.json +++ b/src/locales/fr/main.json @@ -272,11 +272,11 @@ "category": "Catégorie", "choose_file_to_upload": "choisissez le fichier à télécharger", "clear": "Effacer", - "clearFilters": "Effacer les filtres", "close": "Fermer", "color": "Couleur", "comingSoon": "Bientôt disponible", "command": "Commande", + "commandProhibited": "La commande {command} est interdite. Contactez un administrateur pour plus d'informations.", "community": "Communauté", "completed": "Terminé", "confirm": "Confirmer", @@ -299,7 +299,6 @@ "disabling": "Désactivation", "dismiss": "Fermer", "download": "Télécharger", - "duplicate": "Dupliquer", "edit": "Modifier", "empty": "Vide", "enableAll": "Activer tout", @@ -570,10 +569,6 @@ "applyingTexture": "Application de la texture...", "backgroundColor": "Couleur de fond", "camera": "Caméra", - "cameraType": { - "orthographic": "Orthographique", - "perspective": "Perspective" - }, "clearRecording": "Effacer l'enregistrement", "edgeThreshold": "Seuil de Bordure", "export": "Exportation", @@ -594,7 +589,6 @@ "wireframe": "Fil de fer" }, "model": "Modèle", - "openIn3DViewer": "Ouvrir dans la visionneuse 3D", "previewOutput": "Aperçu de la sortie", "removeBackgroundImage": "Supprimer l'image de fond", "resizeNodeMatchOutput": "Redimensionner le nœud pour correspondre à la sortie", @@ -605,22 +599,8 @@ "switchCamera": "Changer de caméra", "switchingMaterialMode": "Changement de mode de matériau...", "upDirection": "Direction Haut", - "upDirections": { - "original": "Original" - }, "uploadBackgroundImage": "Télécharger l'image de fond", - "uploadTexture": "Télécharger Texture", - "viewer": { - "apply": "Appliquer", - "cameraSettings": "Paramètres de la caméra", - "cameraType": "Type de caméra", - "cancel": "Annuler", - "exportSettings": "Paramètres d’exportation", - "lightSettings": "Paramètres de l’éclairage", - "modelSettings": "Paramètres du modèle", - "sceneSettings": "Paramètres de la scène", - "title": "Visionneuse 3D (Bêta)" - } + "uploadTexture": "Télécharger Texture" }, "loadWorkflowWarning": { "coreNodesFromVersion": "Nécessite ComfyUI {version} :", @@ -667,6 +647,9 @@ "installationQueue": "File d'attente d'installation", "lastUpdated": "Dernière mise à jour", "latestVersion": "Dernière", + "legacyManagerUI": "Utiliser l'interface utilisateur héritée", + "legacyManagerUIDescription": "Pour utiliser l'interface utilisateur de gestion héritée, démarrez ComfyUI avec --enable-manager-legacy-ui", + "legacyMenuNotAvailable": "Le menu du gestionnaire de l'ancienne version n'est pas disponible dans cette version de ComfyUI. Veuillez utiliser le nouveau menu du gestionnaire à la place.", "license": "Licence", "loadingVersions": "Chargement des versions...", "nightlyVersion": "Nocturne", @@ -749,7 +732,6 @@ "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", @@ -763,11 +745,12 @@ "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 Performance": "Performance du canevas", + "Canvas Toggle Link Visibility": "Basculer la visibilité du lien de la toile", "Canvas Toggle Lock": "Basculer le verrouillage de la toile", + "Canvas Toggle Minimap": "Basculer la mini-carte du canevas", + "Check for Custom Node Updates": "Vérifier les mises à jour des nœuds personnalisés", "Check for Updates": "Vérifier les mises à jour", "Clear Pending Tasks": "Effacer les tâches en attente", "Clear Workflow": "Effacer le flux de travail", @@ -781,28 +764,27 @@ "Contact Support": "Contacter le support", "Convert Selection to Subgraph": "Convertir la sélection en sous-graphe", "Convert selected nodes to group node": "Convertir les nœuds sélectionnés en nœud de groupe", + "Custom Nodes (Legacy)": "Nœuds personnalisés (héritage)", + "Custom Nodes Manager": "Gestionnaire de Nœuds Personnalisés", "Decrease Brush Size in MaskEditor": "Réduire la taille du pinceau dans MaskEditor", "Delete Selected Items": "Supprimer les éléments sélectionnés", "Desktop User Guide": "Guide de l'utilisateur de bureau", "Duplicate Current Workflow": "Dupliquer le flux de travail actuel", "Edit": "Éditer", - "Exit Subgraph": "Quitter le sous-graphe", "Export": "Exporter", "Export (API)": "Exporter (API)", - "File": "Fichier", "Fit Group To Contents": "Ajuster le groupe au contenu", - "Focus Mode": "Mode focus", + "Fit view to selected nodes": "Ajuster la vue aux nœuds sélectionnés", "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", + "Install Missing Custom Nodes": "Installer les nœuds personnalisés manquants", "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", + "Manager Menu (Legacy)": "Menu du gestionnaire (héritage)", "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", @@ -810,10 +792,7 @@ "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", "Open DevTools": "Ouvrir DevTools", "Open Inputs Folder": "Ouvrir le dossier des entrées", @@ -826,7 +805,6 @@ "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", @@ -839,31 +817,26 @@ "Restart": "Redémarrer", "Save": "Enregistrer", "Save As": "Enregistrer sous", - "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 Essential Bottom Panel": "Afficher/Masquer le panneau inférieur essentiel", + "Toggle Bottom Panel": "Basculer le panneau inférieur", + "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 the Custom Nodes Manager": "Basculer le gestionnaire de nœuds personnalisés", + "Toggle Workflows Sidebar": "Afficher/Masquer la barre latérale des workflows", "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é", - "Workflows": "Flux de travail", + "Unload Models": "Décharger les modèles", + "Unload Models and Execution Cache": "Décharger les modèles et le cache d'exécution", + "Workflow": "Flux de travail", "Zoom In": "Zoom avant", - "Zoom Out": "Zoom arrière", - "Zoom to fit": "Ajuster à l’écran" - }, - "minimap": { - "nodeColors": "Couleurs des nœuds", - "renderBypassState": "Afficher l’état de contournement", - "renderErrorState": "Afficher l’état d’erreur", - "showGroups": "Afficher les cadres/groupes", - "showLinks": "Afficher les liens" + "Zoom Out": "Zoom arrière" }, "missingModelsDialog": { "doNotAskAgain": "Ne plus afficher ce message", @@ -1131,7 +1104,6 @@ }, "settingsCategories": { "3D": "3D", - "3DViewer": "Visionneuse 3D", "API Nodes": "Nœuds API", "About": "À Propos", "Appearance": "Apparence", @@ -1183,31 +1155,10 @@ "Window": "Fenêtre", "Workflow": "Flux de Travail" }, - "shortcuts": { - "essentials": "Essentiel", - "keyboardShortcuts": "Raccourcis clavier", - "manageShortcuts": "Gérer les raccourcis", - "noKeybinding": "Aucun raccourci", - "subcategories": { - "node": "Nœud", - "panelControls": "Contrôles du panneau", - "queue": "File d’attente", - "view": "Vue", - "workflow": "Flux de travail" - }, - "viewControls": "Contrôles d’affichage" - }, "sideToolbar": { "browseTemplates": "Parcourir les modèles d'exemple", "downloads": "Téléchargements", "helpCenter": "Centre d'aide", - "labels": { - "models": "Modèles", - "nodes": "Nœuds", - "queue": "File d’attente", - "templates": "Modèles", - "workflows": "Flux de travail" - }, "logout": "Déconnexion", "modelLibrary": "Bibliothèque de modèles", "newBlankWorkflow": "Créer un nouveau flux de travail vierge", @@ -1245,7 +1196,6 @@ }, "showFlatList": "Afficher la liste plate" }, - "templates": "Modèles", "workflowTab": { "confirmDelete": "Êtes-vous sûr de vouloir supprimer ce flux de travail ?", "confirmDeleteTitle": "Supprimer le flux de travail ?", @@ -1292,8 +1242,6 @@ "Video": "Vidéo", "Video API": "API vidéo" }, - "loadingMore": "Chargement de plus de modèles...", - "searchPlaceholder": "Rechercher des modèles...", "template": { "3D": { "3d_hunyuan3d_image_to_model": "Hunyuan3D", @@ -1616,7 +1564,6 @@ "failedToExportModel": "Échec de l'exportation du modèle en {format}", "failedToFetchBalance": "Échec de la récupération du solde : {error}", "failedToFetchLogs": "Échec de la récupération des journaux du serveur", - "failedToInitializeLoad3dViewer": "Échec de l'initialisation du visualiseur 3D", "failedToInitiateCreditPurchase": "Échec de l'initiation de l'achat de crédits : {error}", "failedToPurchaseCredits": "Échec de l'achat de crédits : {error}", "fileLoadError": "Impossible de trouver le flux de travail dans {fileName}", diff --git a/src/locales/ja/commands.json b/src/locales/ja/commands.json index 5464260b55..97cef947da 100644 --- a/src/locales/ja/commands.json +++ b/src/locales/ja/commands.json @@ -164,8 +164,20 @@ "Comfy_LoadDefaultWorkflow": { "label": "デフォルトのワークフローを読み込む" }, - "Comfy_Manager_CustomNodesManager": { - "label": "カスタムノードマネージャ" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "カスタムノード(ベータ版)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "カスタムノード(レガシー)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "マネージャーメニュー(レガシー)" + }, + "Comfy_Manager_ShowMissingPacks": { + "label": "不足しているパックをインストール" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "更新を確認" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "プログレスダイアログの切り替え" @@ -179,6 +191,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "選択したノードのマスクエディタを開く" }, + "Comfy_Memory_UnloadModels": { + "label": "モデルのアンロード" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "モデルと実行キャッシュのアンロード" + }, "Comfy_NewBlankWorkflow": { "label": "新しい空のワークフロー" }, diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json index d6c2bb9dba..1bb0e8d252 100644 --- a/src/locales/ja/main.json +++ b/src/locales/ja/main.json @@ -272,11 +272,11 @@ "category": "カテゴリ", "choose_file_to_upload": "アップロードするファイルを選択", "clear": "クリア", - "clearFilters": "フィルターをクリア", "close": "閉じる", "color": "色", "comingSoon": "近日公開", "command": "コマンド", + "commandProhibited": "コマンド {command} は禁止されています。詳細は管理者にお問い合わせください。", "community": "コミュニティ", "completed": "完了", "confirm": "確認", @@ -299,7 +299,6 @@ "disabling": "無効化", "dismiss": "閉じる", "download": "ダウンロード", - "duplicate": "複製", "edit": "編集", "empty": "空", "enableAll": "すべて有効にする", @@ -570,10 +569,6 @@ "applyingTexture": "テクスチャを適用中...", "backgroundColor": "背景色", "camera": "カメラ", - "cameraType": { - "orthographic": "オルソグラフィック", - "perspective": "パースペクティブ" - }, "clearRecording": "録画をクリア", "edgeThreshold": "エッジ閾値", "export": "エクスポート", @@ -594,7 +589,6 @@ "wireframe": "ワイヤーフレーム" }, "model": "モデル", - "openIn3DViewer": "3Dビューアで開く", "previewOutput": "出力のプレビュー", "removeBackgroundImage": "背景画像を削除", "resizeNodeMatchOutput": "ノードを出力に合わせてリサイズ", @@ -605,22 +599,8 @@ "switchCamera": "カメラを切り替える", "switchingMaterialMode": "マテリアルモードの切り替え中...", "upDirection": "上方向", - "upDirections": { - "original": "オリジナル" - }, "uploadBackgroundImage": "背景画像をアップロード", - "uploadTexture": "テクスチャをアップロード", - "viewer": { - "apply": "適用", - "cameraSettings": "カメラ設定", - "cameraType": "カメラタイプ", - "cancel": "キャンセル", - "exportSettings": "エクスポート設定", - "lightSettings": "ライト設定", - "modelSettings": "モデル設定", - "sceneSettings": "シーン設定", - "title": "3Dビューア(ベータ)" - } + "uploadTexture": "テクスチャをアップロード" }, "loadWorkflowWarning": { "coreNodesFromVersion": "ComfyUI {version} が必要です:", @@ -667,6 +647,9 @@ "installationQueue": "インストールキュー", "lastUpdated": "最終更新日", "latestVersion": "最新", + "legacyManagerUI": "レガシーUIを使用する", + "legacyManagerUIDescription": "レガシーManager UIを使用するには、--enable-manager-legacy-uiを付けてComfyUIを起動してください", + "legacyMenuNotAvailable": "このバージョンのComfyUIでは、レガシーマネージャーメニューは利用できません。新しいマネージャーメニューを使用してください。", "license": "ライセンス", "loadingVersions": "バージョンを読み込んでいます...", "nightlyVersion": "ナイトリー", @@ -749,7 +732,6 @@ "manageExtensions": "拡張機能の管理", "onChange": "変更時", "onChangeTooltip": "変更が行われるとワークフローがキューに追加されます", - "queue": "キューパネル", "refresh": "ノードを更新", "resetView": "ビューをリセット", "run": "実行する", @@ -763,11 +745,12 @@ "menuLabels": { "About ComfyUI": "ComfyUIについて", "Add Edit Model Step": "モデル編集ステップを追加", - "Bottom Panel": "下部パネル", "Browse Templates": "テンプレートを参照", "Bypass/Unbypass Selected Nodes": "選択したノードのバイパス/バイパス解除", - "Canvas Performance": "キャンバスパフォーマンス", + "Canvas Toggle Link Visibility": "キャンバスのリンク表示を切り替え", "Canvas Toggle Lock": "キャンバスのロックを切り替え", + "Canvas Toggle Minimap": "キャンバス ミニマップの切り替え", + "Check for Custom Node Updates": "カスタムノードのアップデートを確認", "Check for Updates": "更新を確認する", "Clear Pending Tasks": "保留中のタスクをクリア", "Clear Workflow": "ワークフローをクリア", @@ -781,28 +764,27 @@ "Contact Support": "サポートに連絡", "Convert Selection to Subgraph": "選択範囲をサブグラフに変換", "Convert selected nodes to group node": "選択したノードをグループノードに変換", + "Custom Nodes (Legacy)": "カスタムノード(レガシー)", + "Custom Nodes Manager": "カスタムノードマネージャ", "Decrease Brush Size in MaskEditor": "マスクエディタでブラシサイズを小さくする", "Delete Selected Items": "選択したアイテムを削除", "Desktop User Guide": "デスクトップユーザーガイド", "Duplicate Current Workflow": "現在のワークフローを複製", "Edit": "編集", - "Exit Subgraph": "サブグラフを終了", "Export": "エクスポート", "Export (API)": "エクスポート (API)", - "File": "ファイル", "Fit Group To Contents": "グループを内容に合わせる", - "Focus Mode": "フォーカスモード", + "Fit view to selected nodes": "選択したノードにビューを合わせる", "Give Feedback": "フィードバックを送る", "Group Selected Nodes": "選択したノードをグループ化", "Help": "ヘルプ", - "Help Center": "ヘルプセンター", "Increase Brush Size in MaskEditor": "マスクエディタでブラシサイズを大きくする", + "Install Missing Custom Nodes": "不足しているカスタムノードをインストール", "Interrupt": "中断", "Load Default Workflow": "デフォルトワークフローを読み込む", "Manage group nodes": "グループノードを管理", "Manager": "マネージャー", - "Minimap": "ミニマップ", - "Model Library": "モデルライブラリ", + "Manager Menu (Legacy)": "マネージャーメニュー(レガシー)", "Move Selected Nodes Down": "選択したノードを下へ移動", "Move Selected Nodes Left": "選択したノードを左へ移動", "Move Selected Nodes Right": "選択したノードを右へ移動", @@ -810,10 +792,7 @@ "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": "カスタムノードフォルダを開く", "Open DevTools": "DevToolsを開く", "Open Inputs Folder": "入力フォルダを開く", @@ -826,7 +805,6 @@ "Pin/Unpin Selected Items": "選択したアイテムのピン留め/ピン留め解除", "Pin/Unpin Selected Nodes": "選択したノードのピン留め/ピン留め解除", "Previous Opened Workflow": "前に開いたワークフロー", - "Queue Panel": "キューパネル", "Queue Prompt": "キューのプロンプト", "Queue Prompt (Front)": "キューのプロンプト (前面)", "Queue Selected Output Nodes": "選択した出力ノードをキューに追加", @@ -839,31 +817,26 @@ "Restart": "再起動", "Save": "保存", "Save As": "名前を付けて保存", - "Show Keybindings Dialog": "キーバインドダイアログを表示", "Show Settings Dialog": "設定ダイアログを表示", "Sign Out": "サインアウト", - "Toggle Essential Bottom Panel": "エッセンシャル下部パネルの切り替え", + "Toggle 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 the Custom Nodes Manager": "カスタムノードマネージャーを切り替え", + "Toggle Workflows Sidebar": "ワークフローサイドバーを切り替え", "Toggle the Custom Nodes Manager Progress Bar": "カスタムノードマネージャーの進行状況バーを切り替え", "Undo": "元に戻す", "Ungroup selected group nodes": "選択したグループノードのグループ解除", - "Unpack the selected Subgraph": "選択したサブグラフを展開", - "Workflows": "ワークフロー", + "Unload Models": "モデルのアンロード", + "Unload Models and Execution Cache": "モデルと実行キャッシュのアンロード", + "Workflow": "ワークフロー", "Zoom In": "ズームイン", - "Zoom Out": "ズームアウト", - "Zoom to fit": "全体表示にズーム" - }, - "minimap": { - "nodeColors": "ノードの色", - "renderBypassState": "バイパス状態を表示", - "renderErrorState": "エラー状態を表示", - "showGroups": "フレーム/グループを表示", - "showLinks": "リンクを表示" + "Zoom Out": "ズームアウト" }, "missingModelsDialog": { "doNotAskAgain": "再度表示しない", @@ -1131,7 +1104,6 @@ }, "settingsCategories": { "3D": "3D", - "3DViewer": "3Dビューア", "API Nodes": "APIノード", "About": "情報", "Appearance": "外観", @@ -1183,31 +1155,10 @@ "Window": "ウィンドウ", "Workflow": "ワークフロー" }, - "shortcuts": { - "essentials": "基本", - "keyboardShortcuts": "キーボードショートカット", - "manageShortcuts": "ショートカットの管理", - "noKeybinding": "キー割り当てなし", - "subcategories": { - "node": "ノード", - "panelControls": "パネルコントロール", - "queue": "キュー", - "view": "ビュー", - "workflow": "ワークフロー" - }, - "viewControls": "表示コントロール" - }, "sideToolbar": { "browseTemplates": "サンプルテンプレートを表示", "downloads": "ダウンロード", "helpCenter": "ヘルプセンター", - "labels": { - "models": "モデル", - "nodes": "ノード", - "queue": "キュー", - "templates": "テンプレート", - "workflows": "ワークフロー" - }, "logout": "ログアウト", "modelLibrary": "モデルライブラリ", "newBlankWorkflow": "新しい空のワークフローを作成", @@ -1245,7 +1196,6 @@ }, "showFlatList": "フラットリストを表示" }, - "templates": "テンプレート", "workflowTab": { "confirmDelete": "このワークフローを削除してもよろしいですか?", "confirmDeleteTitle": "ワークフローを削除しますか?", @@ -1292,8 +1242,6 @@ "Video": "ビデオ", "Video API": "動画API" }, - "loadingMore": "さらにテンプレートを読み込み中...", - "searchPlaceholder": "テンプレートを検索...", "template": { "3D": { "3d_hunyuan3d_image_to_model": "Hunyuan3D", @@ -1616,7 +1564,6 @@ "failedToExportModel": "{format}としてモデルのエクスポートに失敗しました", "failedToFetchBalance": "残高の取得に失敗しました: {error}", "failedToFetchLogs": "サーバーログの取得に失敗しました", - "failedToInitializeLoad3dViewer": "3Dビューアの初期化に失敗しました", "failedToInitiateCreditPurchase": "クレジット購入の開始に失敗しました: {error}", "failedToPurchaseCredits": "クレジットの購入に失敗しました: {error}", "fileLoadError": "{fileName}でワークフローが見つかりません", diff --git a/src/locales/ko/commands.json b/src/locales/ko/commands.json index ab83464986..c757c40a45 100644 --- a/src/locales/ko/commands.json +++ b/src/locales/ko/commands.json @@ -164,8 +164,20 @@ "Comfy_LoadDefaultWorkflow": { "label": "기본 워크플로 로드" }, - "Comfy_Manager_CustomNodesManager": { - "label": "사용자 정의 노드 관리자" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "사용자 정의 노드 (베타)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "커스텀 노드 (레거시)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "매니저 메뉴 (레거시)" + }, + "Comfy_Manager_ShowMissingPacks": { + "label": "누락된 팩 설치" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "업데이트 확인" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "진행 상황 대화 상자 전환" @@ -179,6 +191,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "선택한 노드 마스크 편집기 열기" }, + "Comfy_Memory_UnloadModels": { + "label": "모델 언로드" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "모델 및 실행 캐시 언로드" + }, "Comfy_NewBlankWorkflow": { "label": "새로운 빈 워크플로" }, diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json index 81422e08c8..5c897821ed 100644 --- a/src/locales/ko/main.json +++ b/src/locales/ko/main.json @@ -272,11 +272,11 @@ "category": "카테고리", "choose_file_to_upload": "업로드할 파일 선택", "clear": "지우기", - "clearFilters": "필터 지우기", "close": "닫기", "color": "색상", "comingSoon": "곧 출시 예정", "command": "명령", + "commandProhibited": "명령 {command}은 금지되었습니다. 자세한 정보는 관리자에게 문의하십시오.", "community": "커뮤니티", "completed": "완료됨", "confirm": "확인", @@ -299,7 +299,6 @@ "disabling": "비활성화 중", "dismiss": "닫기", "download": "다운로드", - "duplicate": "복제", "edit": "편집", "empty": "비어 있음", "enableAll": "모두 활성화", @@ -570,10 +569,6 @@ "applyingTexture": "텍스처 적용 중...", "backgroundColor": "배경색", "camera": "카메라", - "cameraType": { - "orthographic": "직교", - "perspective": "원근" - }, "clearRecording": "녹화 지우기", "edgeThreshold": "엣지 임계값", "export": "내보내기", @@ -594,7 +589,6 @@ "wireframe": "와이어프레임" }, "model": "모델", - "openIn3DViewer": "3D 뷰어에서 열기", "previewOutput": "출력 미리보기", "removeBackgroundImage": "배경 이미지 제거", "resizeNodeMatchOutput": "노드 크기를 출력에 맞추기", @@ -605,22 +599,8 @@ "switchCamera": "카메라 전환", "switchingMaterialMode": "재질 모드 전환 중...", "upDirection": "위 방향", - "upDirections": { - "original": "원본" - }, "uploadBackgroundImage": "배경 이미지 업로드", - "uploadTexture": "텍스처 업로드", - "viewer": { - "apply": "적용", - "cameraSettings": "카메라 설정", - "cameraType": "카메라 유형", - "cancel": "취소", - "exportSettings": "내보내기 설정", - "lightSettings": "조명 설정", - "modelSettings": "모델 설정", - "sceneSettings": "씬 설정", - "title": "3D 뷰어 (베타)" - } + "uploadTexture": "텍스처 업로드" }, "loadWorkflowWarning": { "coreNodesFromVersion": "ComfyUI {version} 이상 필요:", @@ -667,6 +647,9 @@ "installationQueue": "설치 대기열", "lastUpdated": "마지막 업데이트", "latestVersion": "최신", + "legacyManagerUI": "레거시 UI 사용", + "legacyManagerUIDescription": "레거시 매니저 UI를 사용하려면, ComfyUI를 --enable-manager-legacy-ui로 시작하세요", + "legacyMenuNotAvailable": "이 버전의 ComfyUI에서는 레거시 매니저 메뉴를 사용할 수 없습니다. 대신 새로운 매니저 메뉴를 사용하십시오.", "license": "라이선스", "loadingVersions": "버전 로딩 중...", "nightlyVersion": "최신 테스트 버전(nightly)", @@ -749,7 +732,6 @@ "manageExtensions": "확장 프로그램 관리", "onChange": "변경 시", "onChangeTooltip": "변경이 있는 경우에만 워크플로를 실행 대기열에 추가합니다.", - "queue": "대기열 패널", "refresh": "노드 정의 새로 고침", "resetView": "캔버스 보기 재설정", "run": "실행", @@ -763,11 +745,12 @@ "menuLabels": { "About ComfyUI": "ComfyUI에 대하여", "Add Edit Model Step": "모델 편집 단계 추가", - "Bottom Panel": "하단 패널", "Browse Templates": "템플릿 탐색", "Bypass/Unbypass Selected Nodes": "선택한 노드 우회/우회 해제", - "Canvas Performance": "캔버스 성능", + "Canvas Toggle Link Visibility": "캔버스 토글 링크 가시성", "Canvas Toggle Lock": "캔버스 토글 잠금", + "Canvas Toggle Minimap": "캔버스 미니맵 전환", + "Check for Custom Node Updates": "커스텀 노드 업데이트 확인", "Check for Updates": "업데이트 확인", "Clear Pending Tasks": "보류 중인 작업 제거하기", "Clear Workflow": "워크플로 지우기", @@ -781,28 +764,27 @@ "Contact Support": "고객 지원 문의", "Convert Selection to Subgraph": "선택 영역을 서브그래프로 변환", "Convert selected nodes to group node": "선택한 노드를 그룹 노드로 변환", + "Custom Nodes (Legacy)": "커스텀 노드(레거시)", + "Custom Nodes Manager": "사용자 정의 노드 관리자", "Decrease Brush Size in MaskEditor": "마스크 편집기에서 브러시 크기 줄이기", "Delete Selected Items": "선택한 항목 삭제", "Desktop User Guide": "데스크톱 사용자 가이드", "Duplicate Current Workflow": "현재 워크플로 복제", "Edit": "편집", - "Exit Subgraph": "서브그래프 종료", "Export": "내보내기", "Export (API)": "내보내기 (API)", - "File": "파일", "Fit Group To Contents": "그룹을 내용에 맞게 조정", - "Focus Mode": "포커스 모드", + "Fit view to selected nodes": "선택한 노드에 맞게 보기 조정", "Give Feedback": "피드백 제공", "Group Selected Nodes": "선택한 노드 그룹화", "Help": "도움말", - "Help Center": "도움말 센터", "Increase Brush Size in MaskEditor": "마스크 편집기에서 브러시 크기 늘리기", + "Install Missing Custom Nodes": "누락된 커스텀 노드 설치", "Interrupt": "중단", "Load Default Workflow": "기본 워크플로 불러오기", "Manage group nodes": "그룹 노드 관리", "Manager": "매니저", - "Minimap": "미니맵", - "Model Library": "모델 라이브러리", + "Manager Menu (Legacy)": "매니저 메뉴(레거시)", "Move Selected Nodes Down": "선택한 노드 아래로 이동", "Move Selected Nodes Left": "선택한 노드 왼쪽으로 이동", "Move Selected Nodes Right": "선택한 노드 오른쪽으로 이동", @@ -810,10 +792,7 @@ "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": "사용자 정의 노드 폴더 열기", "Open DevTools": "개발자 도구 열기", "Open Inputs Folder": "입력 폴더 열기", @@ -826,7 +805,6 @@ "Pin/Unpin Selected Items": "선택한 항목 고정/고정 해제", "Pin/Unpin Selected Nodes": "선택한 노드 고정/고정 해제", "Previous Opened Workflow": "이전 열린 워크플로", - "Queue Panel": "대기열 패널", "Queue Prompt": "실행 대기열에 프롬프트 추가", "Queue Prompt (Front)": "실행 대기열 맨 앞에 프롬프트 추가", "Queue Selected Output Nodes": "선택한 출력 노드 대기열에 추가", @@ -839,31 +817,26 @@ "Restart": "재시작", "Save": "저장", "Save As": "다른 이름으로 저장", - "Show Keybindings Dialog": "키 바인딩 대화상자 표시", "Show Settings Dialog": "설정 대화상자 표시", "Sign Out": "로그아웃", - "Toggle Essential Bottom Panel": "필수 하단 패널 전환", + "Toggle 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 the Custom Nodes Manager": "커스텀 노드 매니저 전환", + "Toggle Workflows Sidebar": "워크플로우 사이드바 전환", "Toggle the Custom Nodes Manager Progress Bar": "커스텀 노드 매니저 진행률 표시줄 전환", "Undo": "실행 취소", "Ungroup selected group nodes": "선택한 그룹 노드 그룹 해제", - "Unpack the selected Subgraph": "선택한 서브그래프 풀기", - "Workflows": "워크플로우", + "Unload Models": "모델 언로드", + "Unload Models and Execution Cache": "모델 및 실행 캐시 언로드", + "Workflow": "워크플로", "Zoom In": "확대", - "Zoom Out": "축소", - "Zoom to fit": "화면에 맞추기" - }, - "minimap": { - "nodeColors": "노드 색상", - "renderBypassState": "바이패스 상태 렌더링", - "renderErrorState": "에러 상태 렌더링", - "showGroups": "프레임/그룹 표시", - "showLinks": "링크 표시" + "Zoom Out": "축소" }, "missingModelsDialog": { "doNotAskAgain": "다시 보지 않기", @@ -1131,7 +1104,6 @@ }, "settingsCategories": { "3D": "3D", - "3DViewer": "3D뷰어", "API Nodes": "API 노드", "About": "정보", "Appearance": "모양", @@ -1183,31 +1155,10 @@ "Window": "창", "Workflow": "워크플로" }, - "shortcuts": { - "essentials": "필수", - "keyboardShortcuts": "키보드 단축키", - "manageShortcuts": "단축키 관리", - "noKeybinding": "단축키 없음", - "subcategories": { - "node": "노드", - "panelControls": "패널 컨트롤", - "queue": "대기열", - "view": "보기", - "workflow": "워크플로우" - }, - "viewControls": "보기 컨트롤" - }, "sideToolbar": { "browseTemplates": "예제 템플릿 탐색", "downloads": "다운로드", "helpCenter": "도움말 센터", - "labels": { - "models": "모델", - "nodes": "노드", - "queue": "대기열", - "templates": "템플릿", - "workflows": "워크플로우" - }, "logout": "로그아웃", "modelLibrary": "모델 라이브러리", "newBlankWorkflow": "새 빈 워크플로 만들기", @@ -1245,7 +1196,6 @@ }, "showFlatList": "평면 목록 표시" }, - "templates": "템플릿", "workflowTab": { "confirmDelete": "정말로 이 워크플로를 삭제하시겠습니까?", "confirmDeleteTitle": "워크플로 삭제", @@ -1292,8 +1242,6 @@ "Video": "비디오", "Video API": "비디오 API" }, - "loadingMore": "템플릿을 더 불러오는 중...", - "searchPlaceholder": "템플릿 검색...", "template": { "3D": { "3d_hunyuan3d_image_to_model": "Hunyuan3D 2.0", @@ -1616,7 +1564,6 @@ "failedToExportModel": "{format} 형식으로 모델 내보내기에 실패했습니다", "failedToFetchBalance": "잔액을 가져오지 못했습니다: {error}", "failedToFetchLogs": "서버 로그를 가져오는 데 실패했습니다", - "failedToInitializeLoad3dViewer": "3D 뷰어 초기화에 실패했습니다", "failedToInitiateCreditPurchase": "크레딧 구매를 시작하지 못했습니다: {error}", "failedToPurchaseCredits": "크레딧 구매에 실패했습니다: {error}", "fileLoadError": "{fileName}에서 워크플로를 찾을 수 없습니다", diff --git a/src/locales/ru/commands.json b/src/locales/ru/commands.json index f43032963a..273934e8f0 100644 --- a/src/locales/ru/commands.json +++ b/src/locales/ru/commands.json @@ -164,8 +164,20 @@ "Comfy_LoadDefaultWorkflow": { "label": "Загрузить стандартный рабочий процесс" }, - "Comfy_Manager_CustomNodesManager": { - "label": "Менеджер Пользовательских Узлов" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "Пользовательские узлы (Бета)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "Пользовательские узлы (устаревшие)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "Меню менеджера (устаревшее)" + }, + "Comfy_Manager_ShowMissingPacks": { + "label": "Установить отсутствующие" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "Проверить наличие обновлений" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Переключить диалоговое окно прогресса" @@ -179,6 +191,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "Открыть редактор масок для выбранной ноды" }, + "Comfy_Memory_UnloadModels": { + "label": "Выгрузить модели" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "Выгрузить модели и кэш выполнения" + }, "Comfy_NewBlankWorkflow": { "label": "Новый пустой рабочий процесс" }, diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json index 2fd0a228dd..d689d00376 100644 --- a/src/locales/ru/main.json +++ b/src/locales/ru/main.json @@ -272,11 +272,11 @@ "category": "Категория", "choose_file_to_upload": "выберите файл для загрузки", "clear": "Очистить", - "clearFilters": "Сбросить фильтры", "close": "Закрыть", "color": "Цвет", "comingSoon": "Скоро будет", "command": "Команда", + "commandProhibited": "Команда {command} запрещена. Свяжитесь с администратором для получения дополнительной информации.", "community": "Сообщество", "completed": "Завершено", "confirm": "Подтвердить", @@ -299,7 +299,6 @@ "disabling": "Отключение", "dismiss": "Закрыть", "download": "Скачать", - "duplicate": "Дублировать", "edit": "Редактировать", "empty": "Пусто", "enableAll": "Включить все", @@ -570,10 +569,6 @@ "applyingTexture": "Применение текстуры...", "backgroundColor": "Цвет фона", "camera": "Камера", - "cameraType": { - "orthographic": "Ортографическая", - "perspective": "Перспективная" - }, "clearRecording": "Очистить запись", "edgeThreshold": "Пороговое значение края", "export": "Экспорт", @@ -594,7 +589,6 @@ "wireframe": "Каркас" }, "model": "Модель", - "openIn3DViewer": "Открыть в 3D просмотрщике", "previewOutput": "Предварительный просмотр", "removeBackgroundImage": "Удалить фоновое изображение", "resizeNodeMatchOutput": "Изменить размер узла под вывод", @@ -605,22 +599,8 @@ "switchCamera": "Переключить камеру", "switchingMaterialMode": "Переключение режима материала...", "upDirection": "Направление Вверх", - "upDirections": { - "original": "Оригинал" - }, "uploadBackgroundImage": "Загрузить фоновое изображение", - "uploadTexture": "Загрузить текстуру", - "viewer": { - "apply": "Применить", - "cameraSettings": "Настройки камеры", - "cameraType": "Тип камеры", - "cancel": "Отмена", - "exportSettings": "Настройки экспорта", - "lightSettings": "Настройки освещения", - "modelSettings": "Настройки модели", - "sceneSettings": "Настройки сцены", - "title": "3D Просмотрщик (Бета)" - } + "uploadTexture": "Загрузить текстуру" }, "loadWorkflowWarning": { "coreNodesFromVersion": "Требуется ComfyUI {version}:", @@ -667,6 +647,9 @@ "installationQueue": "Очередь установки", "lastUpdated": "Последнее обновление", "latestVersion": "Последняя", + "legacyManagerUI": "Использовать устаревший UI", + "legacyManagerUIDescription": "Чтобы использовать устаревший UI менеджера, запустите ComfyUI с --enable-manager-legacy-ui", + "legacyMenuNotAvailable": "Устаревшее меню менеджера недоступно в этой версии ComfyUI. Пожалуйста, используйте новое меню менеджера.", "license": "Лицензия", "loadingVersions": "Загрузка версий...", "nightlyVersion": "Ночная", @@ -749,7 +732,6 @@ "manageExtensions": "Управление расширениями", "onChange": "При изменении", "onChangeTooltip": "Рабочий процесс будет поставлен в очередь после внесения изменений", - "queue": "Панель очереди", "refresh": "Обновить определения нод", "resetView": "Сбросить вид холста", "run": "Запустить", @@ -763,11 +745,12 @@ "menuLabels": { "About ComfyUI": "О ComfyUI", "Add Edit Model Step": "Добавить или изменить шаг модели", - "Bottom Panel": "Нижняя панель", "Browse Templates": "Просмотреть шаблоны", "Bypass/Unbypass Selected Nodes": "Обойти/восстановить выбранные ноды", - "Canvas Performance": "Производительность холста", + "Canvas Toggle Link Visibility": "Переключение видимости ссылки на холст", "Canvas Toggle Lock": "Переключение блокировки холста", + "Canvas Toggle Minimap": "Показать/скрыть миникарту на холсте", + "Check for Custom Node Updates": "Проверить обновления пользовательских узлов", "Check for Updates": "Проверить наличие обновлений", "Clear Pending Tasks": "Очистить ожидающие задачи", "Clear Workflow": "Очистить рабочий процесс", @@ -781,28 +764,27 @@ "Contact Support": "Связаться с поддержкой", "Convert Selection to Subgraph": "Преобразовать выделенное в подграф", "Convert selected nodes to group node": "Преобразовать выбранные ноды в групповую ноду", + "Custom Nodes (Legacy)": "Пользовательские узлы (устаревшие)", + "Custom Nodes Manager": "Менеджер Пользовательских Узлов", "Decrease Brush Size in MaskEditor": "Уменьшить размер кисти в MaskEditor", "Delete Selected Items": "Удалить выбранные элементы", "Desktop User Guide": "Руководство пользователя для настольных ПК", "Duplicate Current Workflow": "Дублировать текущий рабочий процесс", "Edit": "Редактировать", - "Exit Subgraph": "Выйти из подграфа", "Export": "Экспортировать", "Export (API)": "Экспорт (API)", - "File": "Файл", "Fit Group To Contents": "Подогнать группу под содержимое", - "Focus Mode": "Режим фокуса", + "Fit view to selected nodes": "Подогнать вид под выбранные ноды", "Give Feedback": "Оставить отзыв", "Group Selected Nodes": "Сгруппировать выбранные ноды", "Help": "Помощь", - "Help Center": "Центр поддержки", "Increase Brush Size in MaskEditor": "Увеличить размер кисти в MaskEditor", + "Install Missing Custom Nodes": "Установить отсутствующие пользовательские узлы", "Interrupt": "Прервать", "Load Default Workflow": "Загрузить стандартный рабочий процесс", "Manage group nodes": "Управление групповыми нодами", "Manager": "Менеджер", - "Minimap": "Мини-карта", - "Model Library": "Библиотека моделей", + "Manager Menu (Legacy)": "Меню управления (устаревшее)", "Move Selected Nodes Down": "Переместить выбранные узлы вниз", "Move Selected Nodes Left": "Переместить выбранные узлы влево", "Move Selected Nodes Right": "Переместить выбранные узлы вправо", @@ -810,10 +792,7 @@ "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": "Открыть папку пользовательских нод", "Open DevTools": "Открыть инструменты разработчика", "Open Inputs Folder": "Открыть папку входных данных", @@ -826,7 +805,6 @@ "Pin/Unpin Selected Items": "Закрепить/открепить выбранные элементы", "Pin/Unpin Selected Nodes": "Закрепить/открепить выбранные ноды", "Previous Opened Workflow": "Предыдущий открытый рабочий процесс", - "Queue Panel": "Панель очереди", "Queue Prompt": "Запрос в очереди", "Queue Prompt (Front)": "Запрос в очереди (спереди)", "Queue Selected Output Nodes": "Добавить выбранные выходные узлы в очередь", @@ -839,31 +817,26 @@ "Restart": "Перезапустить", "Save": "Сохранить", "Save As": "Сохранить как", - "Show Keybindings Dialog": "Показать диалог клавиш быстрого доступа", "Show Settings Dialog": "Показать диалог настроек", "Sign Out": "Выйти", - "Toggle Essential Bottom Panel": "Показать/скрыть основную нижнюю панель", + "Toggle 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 the Custom Nodes Manager": "Переключить менеджер пользовательских узлов", + "Toggle Workflows Sidebar": "Показать/скрыть боковую панель рабочих процессов", "Toggle the Custom Nodes Manager Progress Bar": "Переключить индикатор выполнения менеджера пользовательских узлов", "Undo": "Отменить", "Ungroup selected group nodes": "Разгруппировать выбранные групповые ноды", - "Unpack the selected Subgraph": "Распаковать выбранный подграф", - "Workflows": "Рабочие процессы", + "Unload Models": "Выгрузить модели", + "Unload Models and Execution Cache": "Выгрузить модели и кэш выполнения", + "Workflow": "Рабочий процесс", "Zoom In": "Увеличить", - "Zoom Out": "Уменьшить", - "Zoom to fit": "Масштабировать по размеру" - }, - "minimap": { - "nodeColors": "Цвета узлов", - "renderBypassState": "Отображать состояние обхода", - "renderErrorState": "Отображать состояние ошибки", - "showGroups": "Показать фреймы/группы", - "showLinks": "Показать связи" + "Zoom Out": "Уменьшить" }, "missingModelsDialog": { "doNotAskAgain": "Больше не показывать это", @@ -1131,7 +1104,6 @@ }, "settingsCategories": { "3D": "3D", - "3DViewer": "3D-просмотрщик", "API Nodes": "API-узлы", "About": "О программе", "Appearance": "Внешний вид", @@ -1183,31 +1155,10 @@ "Window": "Окно", "Workflow": "Рабочий процесс" }, - "shortcuts": { - "essentials": "Основные", - "keyboardShortcuts": "Горячие клавиши", - "manageShortcuts": "Управление горячими клавишами", - "noKeybinding": "Нет сочетания клавиш", - "subcategories": { - "node": "Узел", - "panelControls": "Управление панелью", - "queue": "Очередь", - "view": "Просмотр", - "workflow": "Рабочий процесс" - }, - "viewControls": "Управление просмотром" - }, "sideToolbar": { "browseTemplates": "Просмотреть примеры шаблонов", "downloads": "Загрузки", "helpCenter": "Центр поддержки", - "labels": { - "models": "Модели", - "nodes": "Узлы", - "queue": "Очередь", - "templates": "Шаблоны", - "workflows": "Воркфлоу" - }, "logout": "Выйти", "modelLibrary": "Библиотека моделей", "newBlankWorkflow": "Создайте новый пустой рабочий процесс", @@ -1245,7 +1196,6 @@ }, "showFlatList": "Показать плоский список" }, - "templates": "Шаблоны", "workflowTab": { "confirmDelete": "Вы уверены, что хотите удалить этот рабочий процесс?", "confirmDeleteTitle": "Удалить рабочий процесс?", @@ -1292,8 +1242,6 @@ "Video": "Видео", "Video API": "Video API" }, - "loadingMore": "Загрузка дополнительных шаблонов...", - "searchPlaceholder": "Поиск шаблонов...", "template": { "3D": { "3d_hunyuan3d_image_to_model": "Hunyuan3D", @@ -1616,7 +1564,6 @@ "failedToExportModel": "Не удалось экспортировать модель как {format}", "failedToFetchBalance": "Не удалось получить баланс: {error}", "failedToFetchLogs": "Не удалось получить серверные логи", - "failedToInitializeLoad3dViewer": "Не удалось инициализировать 3D просмотрщик", "failedToInitiateCreditPurchase": "Не удалось начать покупку кредитов: {error}", "failedToPurchaseCredits": "Не удалось купить кредиты: {error}", "fileLoadError": "Не удалось найти рабочий процесс в {fileName}", diff --git a/src/locales/zh-TW/commands.json b/src/locales/zh-TW/commands.json index 01a790f1fc..c07c913309 100644 --- a/src/locales/zh-TW/commands.json +++ b/src/locales/zh-TW/commands.json @@ -164,8 +164,20 @@ "Comfy_LoadDefaultWorkflow": { "label": "載入預設工作流程" }, - "Comfy_Manager_CustomNodesManager": { - "label": "切換自訂節點管理器" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "自訂節點管理器" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "自訂節點(舊版)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "管理選單(舊版)" + }, + "Comfy_Manager_ShowMissingPacks": { + "label": "安裝缺少的自訂節點" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "檢查自訂節點更新" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "切換自訂節點管理器進度條" @@ -179,6 +191,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "為選取的節點開啟 Mask 編輯器" }, + "Comfy_Memory_UnloadModels": { + "label": "卸載模型" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "卸載模型與執行快取" + }, "Comfy_NewBlankWorkflow": { "label": "新增空白工作流程" }, diff --git a/src/locales/zh-TW/main.json b/src/locales/zh-TW/main.json index 31d0f9412d..2f69709eb5 100644 --- a/src/locales/zh-TW/main.json +++ b/src/locales/zh-TW/main.json @@ -272,11 +272,11 @@ "category": "分類", "choose_file_to_upload": "選擇要上傳的檔案", "clear": "清除", - "clearFilters": "清除篩選", "close": "關閉", "color": "顏色", "comingSoon": "即將推出", "command": "指令", + "commandProhibited": "指令 {command} 已被禁止。如需更多資訊,請聯絡管理員。", "community": "社群", "completed": "已完成", "confirm": "確認", @@ -299,7 +299,6 @@ "disabling": "停用中", "dismiss": "關閉", "download": "下載", - "duplicate": "複製", "edit": "編輯", "empty": "空", "enableAll": "全部啟用", @@ -570,10 +569,6 @@ "applyingTexture": "正在套用材質貼圖...", "backgroundColor": "背景顏色", "camera": "相機", - "cameraType": { - "orthographic": "正交", - "perspective": "透視" - }, "clearRecording": "清除錄影", "edgeThreshold": "邊緣閾值", "export": "匯出", @@ -594,7 +589,6 @@ "wireframe": "線框" }, "model": "模型", - "openIn3DViewer": "在 3D 檢視器中開啟", "previewOutput": "預覽輸出", "removeBackgroundImage": "移除背景圖片", "resizeNodeMatchOutput": "調整節點以符合輸出", @@ -605,22 +599,8 @@ "switchCamera": "切換相機", "switchingMaterialMode": "正在切換材質模式...", "upDirection": "上方方向", - "upDirections": { - "original": "原始" - }, "uploadBackgroundImage": "上傳背景圖片", - "uploadTexture": "上傳材質貼圖", - "viewer": { - "apply": "套用", - "cameraSettings": "相機設定", - "cameraType": "相機類型", - "cancel": "取消", - "exportSettings": "匯出設定", - "lightSettings": "燈光設定", - "modelSettings": "模型設定", - "sceneSettings": "場景設定", - "title": "3D 檢視器(測試版)" - } + "uploadTexture": "上傳材質貼圖" }, "loadWorkflowWarning": { "coreNodesFromVersion": "需要 ComfyUI {version}:", @@ -667,6 +647,9 @@ "installationQueue": "安裝佇列", "lastUpdated": "最後更新", "latestVersion": "最新版本", + "legacyManagerUI": "使用舊版介面", + "legacyManagerUIDescription": "若要使用舊版管理介面,請以 --enable-manager-legacy-ui 啟動 ComfyUI", + "legacyMenuNotAvailable": "舊版管理選單不可用,已預設切換至新版管理選單。", "license": "授權條款", "loadingVersions": "正在載入版本...", "nightlyVersion": "每夜建置版", @@ -749,7 +732,6 @@ "manageExtensions": "管理擴充功能", "onChange": "變更時", "onChangeTooltip": "每當有變更時,工作流程會排入佇列", - "queue": "佇列面板", "refresh": "重新整理節點定義", "resetView": "重設畫布視圖", "run": "執行", @@ -763,11 +745,12 @@ "menuLabels": { "About ComfyUI": "關於 ComfyUI", "Add Edit Model Step": "新增編輯模型步驟", - "Bottom Panel": "底部面板", "Browse Templates": "瀏覽範本", "Bypass/Unbypass Selected Nodes": "繞過/取消繞過選取節點", - "Canvas Performance": "畫布效能", + "Canvas Toggle Link Visibility": "切換連結可見性", "Canvas Toggle Lock": "切換畫布鎖定", + "Canvas Toggle Minimap": "畫布切換小地圖", + "Check for Custom Node Updates": "檢查自訂節點更新", "Check for Updates": "檢查更新", "Clear Pending Tasks": "清除待處理任務", "Clear Workflow": "清除工作流程", @@ -781,28 +764,27 @@ "Contact Support": "聯絡支援", "Convert Selection to Subgraph": "將選取內容轉為子圖", "Convert selected nodes to group node": "將選取節點轉為群組節點", + "Custom Nodes (Legacy)": "自訂節點(舊版)", + "Custom Nodes Manager": "自訂節點管理員", "Decrease Brush Size in MaskEditor": "在 MaskEditor 中減小筆刷大小", "Delete Selected Items": "刪除選取項目", "Desktop User Guide": "桌面應用程式使用指南", "Duplicate Current Workflow": "複製目前工作流程", "Edit": "編輯", - "Exit Subgraph": "離開子圖", "Export": "匯出", "Export (API)": "匯出(API)", - "File": "檔案", "Fit Group To Contents": "群組貼合內容", - "Focus Mode": "專注模式", + "Fit view to selected nodes": "視圖貼合選取節點", "Give Feedback": "提供意見回饋", "Group Selected Nodes": "群組選取節點", "Help": "說明", - "Help Center": "說明中心", "Increase Brush Size in MaskEditor": "在 MaskEditor 中增大筆刷大小", + "Install Missing Custom Nodes": "安裝缺少的自訂節點", "Interrupt": "中斷", "Load Default Workflow": "載入預設工作流程", "Manage group nodes": "管理群組節點", "Manager": "管理員", - "Minimap": "縮圖地圖", - "Model Library": "模型庫", + "Manager Menu (Legacy)": "管理員選單(舊版)", "Move Selected Nodes Down": "選取節點下移", "Move Selected Nodes Left": "選取節點左移", "Move Selected Nodes Right": "選取節點右移", @@ -810,10 +792,7 @@ "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": "開啟自訂節點資料夾", "Open DevTools": "開啟開發者工具", "Open Inputs Folder": "開啟輸入資料夾", @@ -826,7 +805,6 @@ "Pin/Unpin Selected Items": "釘選/取消釘選選取項目", "Pin/Unpin Selected Nodes": "釘選/取消釘選選取節點", "Previous Opened Workflow": "上一個已開啟的工作流程", - "Queue Panel": "佇列面板", "Queue Prompt": "加入提示至佇列", "Queue Prompt (Front)": "將提示加入佇列前端", "Queue Selected Output Nodes": "將選取的輸出節點加入佇列", @@ -839,31 +817,26 @@ "Restart": "重新啟動", "Save": "儲存", "Save As": "另存新檔", - "Show Keybindings Dialog": "顯示快捷鍵對話框", "Show Settings Dialog": "顯示設定對話框", "Sign Out": "登出", - "Toggle Essential Bottom Panel": "切換基本下方面板", + "Toggle 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 the Custom Nodes Manager": "切換自訂節點管理器", + "Toggle Workflows Sidebar": "切換工作流程側邊欄", "Toggle the Custom Nodes Manager Progress Bar": "切換自訂節點管理器進度條", "Undo": "復原", "Ungroup selected group nodes": "取消群組選取的群組節點", - "Unpack the selected Subgraph": "解包所選子圖", - "Workflows": "工作流程", + "Unload Models": "卸載模型", + "Unload Models and Execution Cache": "卸載模型與執行快取", + "Workflow": "工作流程", "Zoom In": "放大", - "Zoom Out": "縮小", - "Zoom to fit": "縮放至適合大小" - }, - "minimap": { - "nodeColors": "節點顏色", - "renderBypassState": "顯示繞過狀態", - "renderErrorState": "顯示錯誤狀態", - "showGroups": "顯示框架/群組", - "showLinks": "顯示連結" + "Zoom Out": "縮小" }, "missingModelsDialog": { "doNotAskAgain": "不要再顯示此訊息", @@ -1131,7 +1104,6 @@ }, "settingsCategories": { "3D": "3D", - "3DViewer": "3D 檢視器", "API Nodes": "API 節點", "About": "關於", "Appearance": "外觀", @@ -1183,31 +1155,10 @@ "Window": "視窗", "Workflow": "工作流程" }, - "shortcuts": { - "essentials": "基本", - "keyboardShortcuts": "鍵盤快捷鍵", - "manageShortcuts": "管理快捷鍵", - "noKeybinding": "無快捷鍵", - "subcategories": { - "node": "節點", - "panelControls": "面板控制", - "queue": "佇列", - "view": "檢視", - "workflow": "工作流程" - }, - "viewControls": "檢視控制" - }, "sideToolbar": { "browseTemplates": "瀏覽範例模板", "downloads": "下載", "helpCenter": "說明中心", - "labels": { - "models": "模型", - "nodes": "節點", - "queue": "佇列", - "templates": "範本", - "workflows": "工作流程" - }, "logout": "登出", "modelLibrary": "模型庫", "newBlankWorkflow": "建立新的空白工作流程", @@ -1245,7 +1196,6 @@ }, "showFlatList": "顯示平面清單" }, - "templates": "範本", "workflowTab": { "confirmDelete": "您確定要刪除這個工作流程嗎?", "confirmDeleteTitle": "刪除工作流程?", @@ -1292,8 +1242,6 @@ "Video": "影片", "Video API": "影片 API" }, - "loadingMore": "正在載入更多範本...", - "searchPlaceholder": "搜尋範本...", "template": { "3D": { "3d_hunyuan3d_image_to_model": "Hunyuan3D 2.0", @@ -1616,7 +1564,6 @@ "failedToExportModel": "無法將模型匯出為 {format}", "failedToFetchBalance": "取得餘額失敗:{error}", "failedToFetchLogs": "無法取得伺服器日誌", - "failedToInitializeLoad3dViewer": "初始化 3D 檢視器失敗", "failedToInitiateCreditPurchase": "啟動點數購買失敗:{error}", "failedToPurchaseCredits": "購買點數失敗:{error}", "fileLoadError": "無法在 {fileName} 中找到工作流程", diff --git a/src/locales/zh/commands.json b/src/locales/zh/commands.json index 8a0a1b897c..39d412a295 100644 --- a/src/locales/zh/commands.json +++ b/src/locales/zh/commands.json @@ -164,8 +164,20 @@ "Comfy_LoadDefaultWorkflow": { "label": "加载默认工作流" }, - "Comfy_Manager_CustomNodesManager": { - "label": "自定义节点管理器" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "自定义节点(测试版)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "自訂節點(舊版)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "管理員選單(舊版)" + }, + "Comfy_Manager_ShowMissingPacks": { + "label": "安装缺失的包" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "检查更新" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "切换进度对话框" @@ -179,6 +191,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "打开选中节点的遮罩编辑器" }, + "Comfy_Memory_UnloadModels": { + "label": "卸载模型" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "卸载模型和执行缓存" + }, "Comfy_NewBlankWorkflow": { "label": "新建空白工作流" }, diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json index 3efab33570..2e4eb090fd 100644 --- a/src/locales/zh/main.json +++ b/src/locales/zh/main.json @@ -84,9 +84,9 @@ }, "breadcrumbsMenu": { "clearWorkflow": "清除工作流程", - "deleteWorkflow": "删除工作流程", - "duplicate": "复制", - "enterNewName": "输入新名称" + "deleteWorkflow": "刪除工作流程", + "duplicate": "複製", + "enterNewName": "輸入新名稱" }, "chatHistory": { "cancelEdit": "取消", @@ -272,11 +272,11 @@ "category": "类别", "choose_file_to_upload": "选择要上传的文件", "clear": "清除", - "clearFilters": "清除筛选", "close": "关闭", "color": "颜色", "comingSoon": "即将推出", "command": "指令", + "commandProhibited": "命令 {command} 被禁止。请联系管理员获取更多信息。", "community": "社区", "completed": "已完成", "confirm": "确认", @@ -297,9 +297,8 @@ "devices": "设备", "disableAll": "禁用全部", "disabling": "禁用中", - "dismiss": "关闭", + "dismiss": "關閉", "download": "下载", - "duplicate": "复制", "edit": "编辑", "empty": "空", "enableAll": "启用全部", @@ -314,7 +313,7 @@ "findIssues": "查找问题", "firstTimeUIMessage": "这是您第一次使用新界面。选择 \"菜单 > 使用新菜单 > 禁用\" 来恢复旧界面。", "frontendNewer": "前端版本 {frontendVersion} 可能與後端版本 {backendVersion} 不相容。", - "frontendOutdated": "前端版本 {frontendVersion} 已过时。后端需要 {requiredVersion} 或更高版本。", + "frontendOutdated": "前端版本 {frontendVersion} 已過時。後端需要 {requiredVersion} 或更高版本。", "goToNode": "转到节点", "help": "帮助", "icon": "图标", @@ -401,7 +400,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 +410,7 @@ "resetView": "重置视图", "selectMode": "选择模式", "toggleLinkVisibility": "切换连线可见性", - "toggleMinimap": "切换小地图", + "toggleMinimap": "切換小地圖", "zoomIn": "放大", "zoomOut": "缩小" }, @@ -570,10 +569,6 @@ "applyingTexture": "应用纹理中...", "backgroundColor": "背景颜色", "camera": "摄影机", - "cameraType": { - "orthographic": "正交", - "perspective": "透视" - }, "clearRecording": "清除录制", "edgeThreshold": "边缘阈值", "export": "导出", @@ -594,7 +589,6 @@ "wireframe": "线框" }, "model": "模型", - "openIn3DViewer": "在 3D 查看器中打开", "previewOutput": "预览输出", "removeBackgroundImage": "移除背景图片", "resizeNodeMatchOutput": "调整节点以匹配输出", @@ -605,22 +599,8 @@ "switchCamera": "切换摄影机类型", "switchingMaterialMode": "切换材质模式中...", "upDirection": "上方向", - "upDirections": { - "original": "原始" - }, "uploadBackgroundImage": "上传背景图片", - "uploadTexture": "上传纹理", - "viewer": { - "apply": "应用", - "cameraSettings": "相机设置", - "cameraType": "相机类型", - "cancel": "取消", - "exportSettings": "导出设置", - "lightSettings": "灯光设置", - "modelSettings": "模型设置", - "sceneSettings": "场景设置", - "title": "3D 查看器(测试版)" - } + "uploadTexture": "上传纹理" }, "loadWorkflowWarning": { "coreNodesFromVersion": "需要 ComfyUI {version}:", @@ -667,6 +647,9 @@ "installationQueue": "安装队列", "lastUpdated": "最后更新", "latestVersion": "最新", + "legacyManagerUI": "使用旧版UI", + "legacyManagerUIDescription": "要使用旧版的管理器UI,请启动ComfyUI并使用 --enable-manager-legacy-ui", + "legacyMenuNotAvailable": "在此版本的ComfyUI中,不提供旧版的管理器菜单。请使用新的管理器菜单。", "license": "许可证", "loadingVersions": "正在加载版本...", "nightlyVersion": "每夜", @@ -740,7 +723,7 @@ "disabled": "禁用", "disabledTooltip": "工作流将不会自动执行", "execute": "执行", - "help": "说明", + "help": "說明", "hideMenu": "隐藏菜单", "instant": "实时", "instantTooltip": "工作流将会在生成完成后立即执行", @@ -749,25 +732,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 Performance": "画布性能", + "Canvas Toggle Link Visibility": "切换连线可见性", "Canvas Toggle Lock": "切换视图锁定", + "Canvas Toggle Minimap": "畫布切換小地圖", + "Check for Custom Node Updates": "檢查自訂節點更新", "Check for Updates": "检查更新", "Clear Pending Tasks": "清除待处理任务", "Clear Workflow": "清除工作流", @@ -781,28 +764,27 @@ "Contact Support": "联系支持", "Convert Selection to Subgraph": "将选中内容转换为子图", "Convert selected nodes to group node": "将选中节点转换为组节点", - "Decrease Brush Size in MaskEditor": "在 MaskEditor 中减小笔刷大小", + "Custom Nodes (Legacy)": "自訂節點(舊版)", + "Custom Nodes Manager": "自定义节点管理器", + "Decrease Brush Size in MaskEditor": "在 MaskEditor 中減小筆刷大小", "Delete Selected Items": "删除选定的项目", "Desktop User Guide": "桌面端用户指南", "Duplicate Current Workflow": "复制当前工作流", "Edit": "编辑", - "Exit Subgraph": "退出子图", "Export": "导出", "Export (API)": "导出 (API)", - "File": "文件", "Fit Group To Contents": "适应组内容", - "Focus Mode": "专注模式", + "Fit view to selected nodes": "适应视图到选中节点", "Give Feedback": "提供反馈", "Group Selected Nodes": "将选中节点转换为组节点", "Help": "帮助", - "Help Center": "帮助中心", - "Increase Brush Size in MaskEditor": "在 MaskEditor 中增大笔刷大小", + "Increase Brush Size in MaskEditor": "在 MaskEditor 中增大筆刷大小", + "Install Missing Custom Nodes": "安裝缺少的自訂節點", "Interrupt": "中断", "Load Default Workflow": "加载默认工作流", "Manage group nodes": "管理组节点", "Manager": "管理器", - "Minimap": "小地图", - "Model Library": "模型库", + "Manager Menu (Legacy)": "管理選單(舊版)", "Move Selected Nodes Down": "下移所选节点", "Move Selected Nodes Left": "左移所选节点", "Move Selected Nodes Right": "右移所选节点", @@ -810,10 +792,7 @@ "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": "打开自定义节点文件夹", "Open DevTools": "打开开发者工具", "Open Inputs Folder": "打开输入文件夹", @@ -826,7 +805,6 @@ "Pin/Unpin Selected Items": "固定/取消固定选定项目", "Pin/Unpin Selected Nodes": "固定/取消固定选定节点", "Previous Opened Workflow": "上一个打开的工作流", - "Queue Panel": "队列面板", "Queue Prompt": "执行提示词", "Queue Prompt (Front)": "执行提示词 (优先执行)", "Queue Selected Output Nodes": "将所选输出节点加入队列", @@ -839,31 +817,26 @@ "Restart": "重启", "Save": "保存", "Save As": "另存为", - "Show Keybindings Dialog": "显示快捷键对话框", "Show Settings Dialog": "显示设置对话框", "Sign Out": "退出登录", - "Toggle Essential Bottom Panel": "切换基础底部面板", + "Toggle 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 the Custom Nodes Manager": "切换自定义节点管理器", + "Toggle Workflows Sidebar": "切換工作流程側邊欄", "Toggle the Custom Nodes Manager Progress Bar": "切换自定义节点管理器进度条", "Undo": "撤销", "Ungroup selected group nodes": "解散选中组节点", - "Unpack the selected Subgraph": "解包选中子图", - "Workflows": "工作流", + "Unload Models": "卸载模型", + "Unload Models and Execution Cache": "卸载模型和执行缓存", + "Workflow": "工作流", "Zoom In": "放大画面", - "Zoom Out": "缩小画面", - "Zoom to fit": "缩放以适应" - }, - "minimap": { - "nodeColors": "节点颜色", - "renderBypassState": "渲染绕过状态", - "renderErrorState": "渲染错误状态", - "showGroups": "显示框架/分组", - "showLinks": "显示连接" + "Zoom Out": "缩小画面" }, "missingModelsDialog": { "doNotAskAgain": "不再显示此消息", @@ -1131,7 +1104,6 @@ }, "settingsCategories": { "3D": "3D", - "3DViewer": "3D查看器", "API Nodes": "API 节点", "About": "关于", "Appearance": "外观", @@ -1183,31 +1155,10 @@ "Window": "窗口", "Workflow": "工作流" }, - "shortcuts": { - "essentials": "常用", - "keyboardShortcuts": "键盘快捷键", - "manageShortcuts": "管理快捷键", - "noKeybinding": "无快捷键", - "subcategories": { - "node": "节点", - "panelControls": "面板控制", - "queue": "队列", - "view": "视图", - "workflow": "工作流" - }, - "viewControls": "视图控制" - }, "sideToolbar": { "browseTemplates": "浏览示例模板", "downloads": "下载", "helpCenter": "帮助中心", - "labels": { - "models": "模型", - "nodes": "节点", - "queue": "队列", - "templates": "模板", - "workflows": "工作流" - }, "logout": "登出", "modelLibrary": "模型库", "newBlankWorkflow": "创建空白工作流", @@ -1245,7 +1196,6 @@ }, "showFlatList": "平铺结果" }, - "templates": "模板", "workflowTab": { "confirmDelete": "您确定要删除此工作流吗?", "confirmDeleteTitle": "删除工作流?", @@ -1292,8 +1242,6 @@ "Video": "视频生成", "Video API": "视频 API" }, - "loadingMore": "正在加载更多模板...", - "searchPlaceholder": "搜索模板...", "template": { "3D": { "3d_hunyuan3d_image_to_model": "混元3D 2.0 图生模型", @@ -1616,7 +1564,6 @@ "failedToExportModel": "无法将模型导出为 {format}", "failedToFetchBalance": "获取余额失败:{error}", "failedToFetchLogs": "无法获取服务器日志", - "failedToInitializeLoad3dViewer": "初始化3D查看器失败", "failedToInitiateCreditPurchase": "发起积分购买失败:{error}", "failedToPurchaseCredits": "购买积分失败:{error}", "fileLoadError": "无法在 {fileName} 中找到工作流", @@ -1673,9 +1620,9 @@ "required": "必填" }, "versionMismatchWarning": { - "dismiss": "关闭", + "dismiss": "關閉", "frontendNewer": "前端版本 {frontendVersion} 可能與後端版本 {backendVersion} 不相容。", - "frontendOutdated": "前端版本 {frontendVersion} 已过时。後端需要 {requiredVersion} 版或更高版本。", + "frontendOutdated": "前端版本 {frontendVersion} 已過時。後端需要 {requiredVersion} 版或更高版本。", "title": "版本相容性警告", "updateFrontend": "更新前端" }, diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts index 6e5e2a4d0e..1f30d31ecd 100644 --- a/src/schemas/apiSchema.ts +++ b/src/schemas/apiSchema.ts @@ -495,6 +495,7 @@ const zSettings = z.object({ 'Comfy.Load3D.LightAdjustmentIncrement': z.number(), 'Comfy.Load3D.CameraType': z.enum(['perspective', 'orthographic']), 'Comfy.Load3D.3DViewerEnable': z.boolean(), + 'Comfy.Memory.AllowManualUnload': z.boolean(), 'pysssss.SnapToGrid': z.boolean(), /** VHS setting is used for queue video preview support. */ 'VHS.AdvancedPreviews': z.string(), diff --git a/src/scripts/api.ts b/src/scripts/api.ts index e83eb9f8bf..857626674f 100644 --- a/src/scripts/api.ts +++ b/src/scripts/api.ts @@ -1,4 +1,5 @@ import axios from 'axios' +import { get } from 'es-toolkit/compat' import defaultClientFeatureFlags from '@/config/clientFeatureFlags.json' import type { @@ -35,6 +36,7 @@ import type { NodeId } from '@/schemas/comfyWorkflowSchema' import type { ComfyNodeDef } from '@/schemas/nodeDefSchema' +import { useToastStore } from '@/stores/toastStore' import type { NodeExecutionId } from '@/types/nodeIdentification' import { WorkflowTemplates } from '@/types/workflowTemplateTypes' @@ -1020,6 +1022,56 @@ export class ComfyApi extends EventTarget { return (await axios.get(this.internalURL('/folder_paths'))).data } + /* Frees memory by unloading models and optionally freeing execution cache + * @param {Object} options - The options object + * @param {boolean} options.freeExecutionCache - If true, also frees execution cache + */ + async freeMemory(options: { freeExecutionCache: boolean }) { + try { + let mode = '' + if (options.freeExecutionCache) { + mode = '{"unload_models": true, "free_memory": true}' + } else { + mode = '{"unload_models": true}' + } + + const res = await this.fetchApi(`/free`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: mode + }) + + if (res.status === 200) { + if (options.freeExecutionCache) { + useToastStore().add({ + severity: 'success', + summary: 'Models and Execution Cache have been cleared.', + life: 3000 + }) + } else { + useToastStore().add({ + severity: 'success', + summary: 'Models have been unloaded.', + life: 3000 + }) + } + } else { + useToastStore().add({ + severity: 'error', + summary: + 'Unloading of models failed. Installed ComfyUI may be an outdated version.', + life: 5000 + }) + } + } catch (error) { + useToastStore().add({ + severity: 'error', + summary: 'An error occurred while trying to unload models.', + life: 5000 + }) + } + } + /** * Gets the custom nodes i18n data from the server. * @@ -1031,21 +1083,21 @@ export class ComfyApi extends EventTarget { /** * Checks if the server supports a specific feature. - * @param featureName The name of the feature to check + * @param featureName The name of the feature to check (supports dot notation for nested values) * @returns true if the feature is supported, false otherwise */ serverSupportsFeature(featureName: string): boolean { - return this.serverFeatureFlags[featureName] === true + return get(this.serverFeatureFlags, featureName) === true } /** * Gets a server feature flag value. - * @param featureName The name of the feature to get + * @param featureName The name of the feature to get (supports dot notation for nested values) * @param defaultValue The default value if the feature is not found * @returns The feature value or default */ getServerFeature(featureName: string, defaultValue?: T): T { - return (this.serverFeatureFlags[featureName] ?? defaultValue) as T + return get(this.serverFeatureFlags, featureName, defaultValue) as T } /** diff --git a/src/services/comfyManagerService.ts b/src/services/comfyManagerService.ts index b98be59583..3fcc603ff6 100644 --- a/src/services/comfyManagerService.ts +++ b/src/services/comfyManagerService.ts @@ -1,6 +1,7 @@ import axios, { AxiosError, AxiosResponse } from 'axios' import { ref } from 'vue' +import { ServerFeatureFlag } from '@/composables/useFeatureFlags' import { api } from '@/scripts/api' import { type InstallPackParams, @@ -27,20 +28,26 @@ enum ManagerRoute { UPDATE_ALL = 'manager/queue/update_all', UNINSTALL = 'manager/queue/uninstall', DISABLE = 'manager/queue/disable', + // FIX_NODE is currently unused but kept for potential future implementation FIX_NODE = 'manager/queue/fix', LIST_INSTALLED = 'customnode/installed', - GET_NODES = 'customnode/getmappings', - GET_PACKS = 'customnode/getlist', IMPORT_FAIL_INFO = 'customnode/import_fail_info', - REBOOT = 'manager/reboot' + REBOOT = 'manager/reboot', + IS_LEGACY_MANAGER_UI = 'manager/is_legacy_manager_ui' } -const managerApiClient = axios.create({ - baseURL: api.apiURL(''), - headers: { - 'Content-Type': 'application/json' - } -}) +// Create axios client with conditional v2 prefix based on manager v4 support +const createManagerApiClient = () => { + const supportsV4 = api.getServerFeature(ServerFeatureFlag.MANAGER_SUPPORTS_V4) + const baseURL = supportsV4 ? api.apiURL('/v2/') : api.apiURL('/') + + return axios.create({ + baseURL, + headers: { + 'Content-Type': 'application/json' + } + }) +} /** * Service for interacting with the ComfyUI Manager API @@ -51,6 +58,9 @@ export const useComfyManagerService = () => { const error = ref(null) const didStartQueue = ref(false) + // Initialize the axios client once when the service is created + const managerApiClient = createManagerApiClient() + const handleRequestError = ( err: unknown, context: string, @@ -247,6 +257,15 @@ export const useComfyManagerService = () => { ) } + const isLegacyManagerUI = async (signal?: AbortSignal) => { + const errorContext = 'Checking if user set Manager to use the legacy UI' + + return executeRequest<{ is_legacy_manager_ui: boolean }>( + () => managerApiClient.get(ManagerRoute.IS_LEGACY_MANAGER_UI, { signal }), + { errorContext } + ) + } + return { // State isLoading, @@ -268,6 +287,7 @@ export const useComfyManagerService = () => { updateAllPacks, // System operations - rebootComfyUI + rebootComfyUI, + isLegacyManagerUI } } diff --git a/src/stores/comfyManagerStore.ts b/src/stores/comfyManagerStore.ts index 0568711f77..b9840f4212 100644 --- a/src/stores/comfyManagerStore.ts +++ b/src/stores/comfyManagerStore.ts @@ -29,6 +29,7 @@ export const useComfyManagerStore = defineStore('comfyManager', () => { const enabledPacksIds = ref>(new Set()) const disabledPacksIds = ref>(new Set()) const installedPacksIds = ref>(new Set()) + const installingPacksIds = ref>(new Set()) const isStale = ref(true) const taskLogs = ref([]) @@ -49,6 +50,9 @@ export const useComfyManagerStore = defineStore('comfyManager', () => { isInstalledPackId(packName) && enabledPacksIds.value.has(packName) + const isInstallingPackId = (packName: string | undefined): boolean => + !!packName && installingPacksIds.value.has(packName) + const packsToIdSet = (packs: ManagerPackInstalled[]) => packs.reduce((acc, pack) => { const id = pack.cnr_id || pack.aux_id @@ -117,7 +121,11 @@ export const useComfyManagerStore = defineStore('comfyManager', () => { whenever(isStale, refreshInstalledList, { immediate: true }) whenever(uncompletedCount, () => showManagerProgressDialog()) - const withLogs = (task: () => Promise, taskName: string) => { + const withLogs = ( + task: () => Promise, + taskName: string, + packId?: string + ) => { const { startListening, stopListening, logs } = useServerLogs() const loggedTask = async () => { @@ -128,6 +136,9 @@ export const useComfyManagerStore = defineStore('comfyManager', () => { const onComplete = async () => { await stopListening() + if (packId) { + installingPacksIds.value.delete(packId) + } setStale() } @@ -152,8 +163,11 @@ export const useComfyManagerStore = defineStore('comfyManager', () => { } } + installingPacksIds.value.add(params.id) const task = () => managerService.installPack(params, signal) - enqueueTask(withLogs(task, `${actionDescription} ${params.id}`)) + enqueueTask( + withLogs(task, `${actionDescription} ${params.id}`, params.id) + ) }, { maxSize: 1 } ) @@ -162,14 +176,16 @@ export const useComfyManagerStore = defineStore('comfyManager', () => { installPack.clear() installPack.cancel() const task = () => managerService.uninstallPack(params, signal) - enqueueTask(withLogs(task, t('manager.uninstalling', { id: params.id }))) + enqueueTask( + withLogs(task, t('manager.uninstalling', { id: params.id }), params.id) + ) } const updatePack = useCachedRequest( async (params: ManagerPackInfo, signal?: AbortSignal) => { updateAllPacks.cancel() const task = () => managerService.updatePack(params, signal) - enqueueTask(withLogs(task, t('g.updating', { id: params.id }))) + enqueueTask(withLogs(task, t('g.updating', { id: params.id }), params.id)) }, { maxSize: 1 } ) @@ -184,7 +200,7 @@ export const useComfyManagerStore = defineStore('comfyManager', () => { const disablePack = (params: ManagerPackInfo, signal?: AbortSignal) => { const task = () => managerService.disablePack(params, signal) - enqueueTask(withLogs(task, t('g.disabling', { id: params.id }))) + enqueueTask(withLogs(task, t('g.disabling', { id: params.id }), params.id)) } const getInstalledPackVersion = (packId: string) => { @@ -212,6 +228,7 @@ export const useComfyManagerStore = defineStore('comfyManager', () => { installedPacksIds, isPackInstalled: isInstalledPackId, isPackEnabled: isEnabledPackId, + isPackInstalling: isInstallingPackId, getInstalledPackVersion, refreshInstalledList, diff --git a/src/stores/managerStateStore.ts b/src/stores/managerStateStore.ts new file mode 100644 index 0000000000..deb1de6391 --- /dev/null +++ b/src/stores/managerStateStore.ts @@ -0,0 +1,85 @@ +import { defineStore } from 'pinia' +import { readonly, ref } from 'vue' + +import { useFeatureFlags } from '@/composables/useFeatureFlags' +import { api } from '@/scripts/api' +import { useComfyManagerService } from '@/services/comfyManagerService' +import { useSystemStatsStore } from '@/stores/systemStatsStore' + +export enum ManagerUIState { + DISABLED = 'disabled', + LEGACY_UI = 'legacy', + NEW_UI = 'new' +} + +export const useManagerStateStore = defineStore('managerState', () => { + const managerUIState = ref(null) + const isInitialized = ref(false) + let initializationPromise: Promise | null = null + + const ensureInitialized = async () => { + if (isInitialized.value) return + + // If already initializing, wait for that to complete + if (initializationPromise) { + await initializationPromise + return + } + + // Start initialization + initializationPromise = initializeManagerState() + await initializationPromise + } + + const initializeManagerState = async () => { + const systemStats = useSystemStatsStore().systemStats + const { flags } = useFeatureFlags() + const clientSupportsV4 = + api.getClientFeatureFlags().supports_manager_v4_ui ?? false + + // Check command line args first + if (systemStats?.system?.argv?.includes('--disable-manager')) { + managerUIState.value = ManagerUIState.DISABLED + } else if ( + systemStats?.system?.argv?.includes('--enable-manager-legacy-ui') + ) { + // Check if legacy manager is actually available + try { + await useComfyManagerService().isLegacyManagerUI() + managerUIState.value = ManagerUIState.LEGACY_UI + } catch { + // Legacy manager not installed - disable manager + managerUIState.value = ManagerUIState.DISABLED + } + } else { + // Check if we can use new UI + if (clientSupportsV4 && flags.supportsManagerV4) { + managerUIState.value = ManagerUIState.NEW_UI + } else { + // For old frontend, we need to check if legacy manager exists + try { + await useComfyManagerService().isLegacyManagerUI() + // Route exists but we can't use v4 + managerUIState.value = ManagerUIState.LEGACY_UI + } catch { + // Route doesn't exist = old manager OR no manager + // Old frontend will handle this itself + managerUIState.value = ManagerUIState.LEGACY_UI + } + } + } + + isInitialized.value = true + } + + // Getter that ensures initialization before returning the state + const getManagerUIState = async () => { + await ensureInitialized() + return managerUIState.value + } + + return { + managerUIState: readonly(managerUIState), + getManagerUIState + } +}) diff --git a/src/types/comfyManagerTypes.ts b/src/types/comfyManagerTypes.ts index 6a747701c2..f6f0b3c7c4 100644 --- a/src/types/comfyManagerTypes.ts +++ b/src/types/comfyManagerTypes.ts @@ -19,7 +19,7 @@ export const IsInstallingKey: InjectionKey> = Symbol('isInstalling') export enum ManagerWsQueueStatus { - DONE = 'done', + DONE = 'all-done', IN_PROGRESS = 'in_progress' } diff --git a/tests-ui/tests/components/dialog/footer/ManagerProgressFooter.test.ts b/tests-ui/tests/components/dialog/footer/ManagerProgressFooter.test.ts new file mode 100644 index 0000000000..c92d4c89ad --- /dev/null +++ b/tests-ui/tests/components/dialog/footer/ManagerProgressFooter.test.ts @@ -0,0 +1,440 @@ +import { mount } from '@vue/test-utils' +import PrimeVue from 'primevue/config' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { nextTick } from 'vue' +import { createI18n } from 'vue-i18n' + +import ManagerProgressFooter from '@/components/dialog/footer/ManagerProgressFooter.vue' +import { useComfyManagerService } from '@/services/comfyManagerService' +import { + useComfyManagerStore, + useManagerProgressDialogStore +} from '@/stores/comfyManagerStore' +import { useCommandStore } from '@/stores/commandStore' +import { useDialogStore } from '@/stores/dialogStore' +import { useSettingStore } from '@/stores/settingStore' +import { TaskLog } from '@/types/comfyManagerTypes' + +// Mock modules +vi.mock('@/stores/comfyManagerStore') +vi.mock('@/stores/dialogStore') +vi.mock('@/stores/settingStore') +vi.mock('@/stores/commandStore') +vi.mock('@/services/comfyManagerService') + +// Mock useEventListener to capture the event handler +let reconnectHandler: (() => void) | null = null +vi.mock('@vueuse/core', async () => { + const actual = await vi.importActual('@vueuse/core') + return { + ...actual, + useEventListener: vi.fn( + (_target: any, event: string, handler: any, _options: any) => { + if (event === 'reconnected') { + reconnectHandler = handler + } + } + ) + } +}) +vi.mock('@/services/workflowService', () => ({ + useWorkflowService: vi.fn(() => ({ + reloadCurrentWorkflow: vi.fn().mockResolvedValue(undefined) + })) +})) +vi.mock('@/stores/workspace/colorPaletteStore', () => ({ + useColorPaletteStore: vi.fn(() => ({ + completedActivePalette: { + light_theme: false + } + })) +})) + +// Helper function to mount component with required setup +const mountComponent = (options: { captureError?: boolean } = {}) => { + const i18n = createI18n({ + legacy: false, + locale: 'en', + messages: { + en: {} + } + }) + + const config: any = { + global: { + plugins: [PrimeVue, i18n], + mocks: { + $t: (key: string) => key // Mock i18n translation + } + } + } + + // Add error handler for tests that expect errors + if (options.captureError) { + config.global.config = { + errorHandler: () => { + // Suppress error in test + } + } + } + + return mount(ManagerProgressFooter, config) +} + +describe('ManagerProgressFooter', () => { + const mockTaskLogs: TaskLog[] = [] + + const mockComfyManagerStore = { + uncompletedCount: 0, + taskLogs: mockTaskLogs, + allTasksDone: true, + clearLogs: vi.fn(), + setStale: vi.fn(), + // Add other required properties + isLoading: { value: false }, + error: { value: null }, + statusMessage: { value: 'DONE' }, + installedPacks: {}, + installedPacksIds: new Set(), + isPackInstalled: vi.fn(), + isPackEnabled: vi.fn(), + getInstalledPackVersion: vi.fn(), + refreshInstalledList: vi.fn(), + installPack: vi.fn(), + uninstallPack: vi.fn(), + updatePack: vi.fn(), + updateAllPacks: vi.fn(), + disablePack: vi.fn(), + enablePack: vi.fn() + } + + const mockDialogStore = { + closeDialog: vi.fn(), + // Add other required properties + dialogStack: { value: [] }, + showDialog: vi.fn(), + $id: 'dialog', + $state: {} as any, + $patch: vi.fn(), + $reset: vi.fn(), + $subscribe: vi.fn(), + $dispose: vi.fn(), + $onAction: vi.fn() + } + + const mockSettingStore = { + get: vi.fn().mockReturnValue(false), + set: vi.fn(), + // Add other required properties + settingValues: { value: {} }, + settingsById: { value: {} }, + exists: vi.fn(), + getDefaultValue: vi.fn(), + loadSettingValues: vi.fn(), + updateValue: vi.fn(), + $id: 'setting', + $state: {} as any, + $patch: vi.fn(), + $reset: vi.fn(), + $subscribe: vi.fn(), + $dispose: vi.fn(), + $onAction: vi.fn() + } + + const mockProgressDialogStore = { + isExpanded: false, + toggle: vi.fn(), + collapse: vi.fn(), + expand: vi.fn() + } + + const mockCommandStore = { + execute: vi.fn().mockResolvedValue(undefined) + } + + const mockComfyManagerService = { + rebootComfyUI: vi.fn().mockResolvedValue(null) + } + + beforeEach(() => { + vi.clearAllMocks() + // Reset task logs + mockTaskLogs.length = 0 + mockComfyManagerStore.taskLogs = mockTaskLogs + // Reset event handler + reconnectHandler = null + + vi.mocked(useComfyManagerStore).mockReturnValue( + mockComfyManagerStore as any + ) + vi.mocked(useDialogStore).mockReturnValue(mockDialogStore as any) + vi.mocked(useSettingStore).mockReturnValue(mockSettingStore as any) + vi.mocked(useManagerProgressDialogStore).mockReturnValue( + mockProgressDialogStore as any + ) + vi.mocked(useCommandStore).mockReturnValue(mockCommandStore as any) + vi.mocked(useComfyManagerService).mockReturnValue( + mockComfyManagerService as any + ) + }) + + describe('State 1: Queue Running', () => { + it('should display loading spinner and progress counter when queue is running', async () => { + // Setup queue running state + mockComfyManagerStore.uncompletedCount = 3 + mockTaskLogs.push( + { taskName: 'Installing pack1', logs: [] }, + { taskName: 'Installing pack2', logs: [] }, + { taskName: 'Installing pack3', logs: [] } + ) + + const wrapper = mountComponent() + + // Check loading spinner exists (DotSpinner component) + expect(wrapper.find('.inline-flex').exists()).toBe(true) + + // Check current task name is displayed + expect(wrapper.text()).toContain('Installing pack3') + + // Check progress counter (completed: 2 of 3) + expect(wrapper.text()).toMatch(/2.*3/) + + // Check expand/collapse button exists + const expandButton = wrapper.find('[aria-label="Expand"]') + expect(expandButton.exists()).toBe(true) + + // Check Apply Changes button is NOT shown + expect(wrapper.text()).not.toContain('manager.applyChanges') + }) + + it('should toggle expansion when expand button is clicked', async () => { + mockComfyManagerStore.uncompletedCount = 1 + mockTaskLogs.push({ taskName: 'Installing', logs: [] }) + + const wrapper = mountComponent() + + const expandButton = wrapper.find('[aria-label="Expand"]') + await expandButton.trigger('click') + + expect(mockProgressDialogStore.toggle).toHaveBeenCalled() + }) + }) + + describe('State 2: Tasks Completed (Waiting for Restart)', () => { + it('should display check mark and Apply Changes button when all tasks are done', async () => { + // Setup tasks completed state + mockComfyManagerStore.uncompletedCount = 0 + mockTaskLogs.push( + { taskName: 'Installed pack1', logs: [] }, + { taskName: 'Installed pack2', logs: [] } + ) + mockComfyManagerStore.allTasksDone = true + + const wrapper = mountComponent() + + // Check check mark emoji + expect(wrapper.text()).toContain('✅') + + // Check restart message (split into 3 parts) + expect(wrapper.text()).toContain('manager.clickToFinishSetup') + expect(wrapper.text()).toContain('manager.applyChanges') + expect(wrapper.text()).toContain('manager.toFinishSetup') + + // Check Apply Changes button exists + const applyButton = wrapper + .findAll('button') + .find((btn) => btn.text().includes('manager.applyChanges')) + expect(applyButton).toBeTruthy() + + // Check no progress counter + expect(wrapper.text()).not.toMatch(/\d+.*of.*\d+/) + }) + }) + + describe('State 3: Restarting', () => { + it('should display restarting message and spinner during restart', async () => { + // Setup completed state first + mockComfyManagerStore.uncompletedCount = 0 + mockComfyManagerStore.allTasksDone = true + + const wrapper = mountComponent() + + // Click Apply Changes to trigger restart + const applyButton = wrapper + .findAll('button') + .find((btn) => btn.text().includes('manager.applyChanges')) + await applyButton?.trigger('click') + + // Wait for state update + await nextTick() + + // Check restarting message + expect(wrapper.text()).toContain('manager.restartingBackend') + + // Check loading spinner during restart + expect(wrapper.find('.inline-flex').exists()).toBe(true) + + // Check Apply Changes button is hidden + expect(wrapper.text()).not.toContain('manager.applyChanges') + }) + }) + + describe('State 4: Restart Completed', () => { + it('should display success message and auto-close after 3 seconds', async () => { + vi.useFakeTimers() + + // Setup completed state + mockComfyManagerStore.uncompletedCount = 0 + mockComfyManagerStore.allTasksDone = true + + const wrapper = mountComponent() + + // Trigger restart + const applyButton = wrapper + .findAll('button') + .find((btn) => btn.text().includes('manager.applyChanges')) + await applyButton?.trigger('click') + + // Wait for event listener to be set up + await nextTick() + + // Trigger the reconnect handler directly + if (reconnectHandler) { + await reconnectHandler() + } + + // Wait for restart completed state + await nextTick() + + // Check success message + expect(wrapper.text()).toContain('🎉') + expect(wrapper.text()).toContain( + 'manager.extensionsSuccessfullyInstalled' + ) + + // Check dialog closes after 3 seconds + vi.advanceTimersByTime(3000) + + await nextTick() + + expect(mockDialogStore.closeDialog).toHaveBeenCalledWith({ + key: 'global-manager-progress-dialog' + }) + expect(mockComfyManagerStore.clearLogs).toHaveBeenCalled() + + vi.useRealTimers() + }) + }) + + describe('Common Features', () => { + it('should always display close button', async () => { + const wrapper = mountComponent() + + const closeButton = wrapper.find('[aria-label="Close"]') + expect(closeButton.exists()).toBe(true) + }) + + it('should close dialog when close button is clicked', async () => { + const wrapper = mountComponent() + + const closeButton = wrapper.find('[aria-label="Close"]') + await closeButton.trigger('click') + + expect(mockDialogStore.closeDialog).toHaveBeenCalledWith({ + key: 'global-manager-progress-dialog' + }) + }) + }) + + describe('Toast Management', () => { + it('should suppress reconnection toasts during restart', async () => { + mockComfyManagerStore.uncompletedCount = 0 + mockComfyManagerStore.allTasksDone = true + mockSettingStore.get.mockReturnValue(false) // Original setting + + const wrapper = mountComponent() + + // Click Apply Changes + const applyButton = wrapper + .findAll('button') + .find((btn) => btn.text().includes('manager.applyChanges')) + await applyButton?.trigger('click') + + // Check toast setting was disabled + expect(mockSettingStore.set).toHaveBeenCalledWith( + 'Comfy.Toast.DisableReconnectingToast', + true + ) + }) + + it('should restore toast settings after restart completes', async () => { + mockComfyManagerStore.uncompletedCount = 0 + mockComfyManagerStore.allTasksDone = true + mockSettingStore.get.mockReturnValue(false) // Original setting + + const wrapper = mountComponent() + + // Click Apply Changes + const applyButton = wrapper + .findAll('button') + .find((btn) => btn.text().includes('manager.applyChanges')) + await applyButton?.trigger('click') + + // Wait for event listener to be set up + await nextTick() + + // Trigger the reconnect handler directly + if (reconnectHandler) { + await reconnectHandler() + } + + // Wait for settings restoration + await nextTick() + + expect(mockSettingStore.set).toHaveBeenCalledWith( + 'Comfy.Toast.DisableReconnectingToast', + false // Restored to original + ) + }) + }) + + describe('Error Handling', () => { + it('should restore state and close dialog on restart error', async () => { + mockComfyManagerStore.uncompletedCount = 0 + mockComfyManagerStore.allTasksDone = true + + // Mock restart to throw error + mockComfyManagerService.rebootComfyUI.mockRejectedValue( + new Error('Restart failed') + ) + + const wrapper = mountComponent({ captureError: true }) + + // Click Apply Changes + const applyButton = wrapper + .findAll('button') + .find((btn) => btn.text().includes('manager.applyChanges')) + + expect(applyButton).toBeTruthy() + + // The component throws the error but Vue Test Utils catches it + // We need to check if the error handling logic was executed + await applyButton!.trigger('click').catch(() => { + // Error is expected, ignore it + }) + + // Wait for error handling + await nextTick() + + // Check dialog was closed on error + expect(mockDialogStore.closeDialog).toHaveBeenCalled() + // Check toast settings were restored + expect(mockSettingStore.set).toHaveBeenCalledWith( + 'Comfy.Toast.DisableReconnectingToast', + false + ) + // Check that the error handler was called + expect(mockComfyManagerService.rebootComfyUI).toHaveBeenCalled() + }) + }) +}) diff --git a/tests-ui/tests/composables/useFeatureFlags.test.ts b/tests-ui/tests/composables/useFeatureFlags.test.ts new file mode 100644 index 0000000000..eddb57b650 --- /dev/null +++ b/tests-ui/tests/composables/useFeatureFlags.test.ts @@ -0,0 +1,137 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { isReactive, isReadonly } from 'vue' + +import { + ServerFeatureFlag, + useFeatureFlags +} from '@/composables/useFeatureFlags' +import { api } from '@/scripts/api' + +// Mock the API module +vi.mock('@/scripts/api', () => ({ + api: { + getServerFeature: vi.fn() + } +})) + +describe('useFeatureFlags', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + describe('flags object', () => { + it('should provide reactive readonly flags', () => { + const { flags } = useFeatureFlags() + + expect(isReadonly(flags)).toBe(true) + expect(isReactive(flags)).toBe(true) + }) + + it('should access supportsPreviewMetadata', () => { + vi.mocked(api.getServerFeature).mockImplementation( + (path, defaultValue) => { + if (path === ServerFeatureFlag.SUPPORTS_PREVIEW_METADATA) + return true as any + return defaultValue + } + ) + + const { flags } = useFeatureFlags() + expect(flags.supportsPreviewMetadata).toBe(true) + expect(api.getServerFeature).toHaveBeenCalledWith( + ServerFeatureFlag.SUPPORTS_PREVIEW_METADATA + ) + }) + + it('should access maxUploadSize', () => { + vi.mocked(api.getServerFeature).mockImplementation( + (path, defaultValue) => { + if (path === ServerFeatureFlag.MAX_UPLOAD_SIZE) + return 209715200 as any // 200MB + return defaultValue + } + ) + + const { flags } = useFeatureFlags() + expect(flags.maxUploadSize).toBe(209715200) + expect(api.getServerFeature).toHaveBeenCalledWith( + ServerFeatureFlag.MAX_UPLOAD_SIZE + ) + }) + + it('should access supportsManagerV4', () => { + vi.mocked(api.getServerFeature).mockImplementation( + (path, defaultValue) => { + if (path === ServerFeatureFlag.MANAGER_SUPPORTS_V4) return true as any + return defaultValue + } + ) + + const { flags } = useFeatureFlags() + expect(flags.supportsManagerV4).toBe(true) + expect(api.getServerFeature).toHaveBeenCalledWith( + ServerFeatureFlag.MANAGER_SUPPORTS_V4 + ) + }) + + it('should return undefined when features are not available and no default provided', () => { + vi.mocked(api.getServerFeature).mockImplementation( + (_path, defaultValue) => defaultValue as any + ) + + const { flags } = useFeatureFlags() + expect(flags.supportsPreviewMetadata).toBeUndefined() + expect(flags.maxUploadSize).toBeUndefined() + expect(flags.supportsManagerV4).toBeUndefined() + }) + }) + + describe('featureFlag', () => { + it('should create reactive computed for custom feature flags', () => { + vi.mocked(api.getServerFeature).mockImplementation( + (path, defaultValue) => { + if (path === 'custom.feature') return 'custom-value' as any + return defaultValue + } + ) + + const { featureFlag } = useFeatureFlags() + const customFlag = featureFlag('custom.feature', 'default') + + expect(customFlag.value).toBe('custom-value') + expect(api.getServerFeature).toHaveBeenCalledWith( + 'custom.feature', + 'default' + ) + }) + + it('should handle nested paths', () => { + vi.mocked(api.getServerFeature).mockImplementation( + (path, defaultValue) => { + if (path === 'extension.custom.nested.feature') return true as any + return defaultValue + } + ) + + const { featureFlag } = useFeatureFlags() + const nestedFlag = featureFlag('extension.custom.nested.feature', false) + + expect(nestedFlag.value).toBe(true) + }) + + it('should work with ServerFeatureFlag enum', () => { + vi.mocked(api.getServerFeature).mockImplementation( + (path, defaultValue) => { + if (path === ServerFeatureFlag.MAX_UPLOAD_SIZE) + return 104857600 as any + return defaultValue + } + ) + + const { featureFlag } = useFeatureFlags() + const maxUploadSize = featureFlag(ServerFeatureFlag.MAX_UPLOAD_SIZE) + + expect(maxUploadSize.value).toBe(104857600) + }) + }) +}) diff --git a/tests-ui/tests/composables/widgets/useManagerQueue.test.ts b/tests-ui/tests/composables/widgets/useManagerQueue.test.ts index 30af37f304..7fce2b5e16 100644 --- a/tests-ui/tests/composables/widgets/useManagerQueue.test.ts +++ b/tests-ui/tests/composables/widgets/useManagerQueue.test.ts @@ -28,7 +28,7 @@ describe('useManagerQueue', () => { const getEventListenerCallback = () => vi.mocked(api.addEventListener).mock.calls[0][1] - const simulateServerStatus = async (status: 'done' | 'in_progress') => { + const simulateServerStatus = async (status: 'all-done' | 'in_progress') => { const event = new CustomEvent('cm-queue-status', { detail: { status } }) @@ -49,7 +49,7 @@ describe('useManagerQueue', () => { const queue = useManagerQueue() expect(queue.queueLength.value).toBe(0) - expect(queue.statusMessage.value).toBe('done') + expect(queue.statusMessage.value).toBe('all-done') expect(queue.allTasksDone.value).toBe(true) }) }) @@ -104,7 +104,7 @@ describe('useManagerQueue', () => { await nextTick() // Should maintain the default status - expect(queue.statusMessage.value).toBe('done') + expect(queue.statusMessage.value).toBe('all-done') }) it('should handle missing status property gracefully', async () => { @@ -119,7 +119,7 @@ describe('useManagerQueue', () => { await nextTick() // Should maintain the default status - expect(queue.statusMessage.value).toBe('done') + expect(queue.statusMessage.value).toBe('all-done') }) }) @@ -127,7 +127,7 @@ describe('useManagerQueue', () => { it('should start the next task when server is idle and queue has items', async () => { const { queue, mockTask } = createQueueWithMockTask() - await simulateServerStatus('done') + await simulateServerStatus('all-done') // Task should have been started expect(mockTask.task).toHaveBeenCalled() @@ -138,7 +138,7 @@ describe('useManagerQueue', () => { const { mockTask } = createQueueWithMockTask() // Start the task - await simulateServerStatus('done') + await simulateServerStatus('all-done') expect(mockTask.task).toHaveBeenCalled() // Simulate task completion @@ -148,7 +148,7 @@ describe('useManagerQueue', () => { await simulateServerStatus('in_progress') expect(mockTask.onComplete).not.toHaveBeenCalled() - await simulateServerStatus('done') + await simulateServerStatus('all-done') expect(mockTask.onComplete).toHaveBeenCalled() }) @@ -159,7 +159,7 @@ describe('useManagerQueue', () => { queue.enqueueTask(mockTask) // Start the task - await simulateServerStatus('done') + await simulateServerStatus('all-done') expect(mockTask.task).toHaveBeenCalled() // Simulate task completion @@ -167,7 +167,7 @@ describe('useManagerQueue', () => { // Simulate server cycle await simulateServerStatus('in_progress') - await simulateServerStatus('done') + await simulateServerStatus('all-done') // Should not throw errors even without onComplete expect(queue.allTasksDone.value).toBe(true) @@ -184,14 +184,14 @@ describe('useManagerQueue', () => { expect(queue.queueLength.value).toBe(2) // Process first task - await simulateServerStatus('done') + await simulateServerStatus('all-done') expect(mockTask1.task).toHaveBeenCalled() expect(queue.queueLength.value).toBe(1) // Complete first task await mockTask1.task.mock.results[0].value await simulateServerStatus('in_progress') - await simulateServerStatus('done') + await simulateServerStatus('all-done') expect(mockTask1.onComplete).toHaveBeenCalled() // Process second task @@ -201,7 +201,7 @@ describe('useManagerQueue', () => { // Complete second task await mockTask2.task.mock.results[0].value await simulateServerStatus('in_progress') - await simulateServerStatus('done') + await simulateServerStatus('all-done') expect(mockTask2.onComplete).toHaveBeenCalled() // Queue should be empty and all tasks done @@ -219,7 +219,7 @@ describe('useManagerQueue', () => { queue.enqueueTask(mockTask) // Start the task - await simulateServerStatus('done') + await simulateServerStatus('all-done') expect(mockTask.task).toHaveBeenCalled() // Let the promise rejection happen @@ -231,7 +231,7 @@ describe('useManagerQueue', () => { // Simulate server cycle await simulateServerStatus('in_progress') - await simulateServerStatus('done') + await simulateServerStatus('all-done') // onComplete should still be called for failed tasks expect(mockTask.onComplete).toHaveBeenCalled() @@ -252,7 +252,7 @@ describe('useManagerQueue', () => { ]) // Task 1 - await simulateServerStatus('done') + await simulateServerStatus('all-done') expect(mockTask1.task).toHaveBeenCalled() // Verify state of onComplete callbacks @@ -266,7 +266,7 @@ describe('useManagerQueue', () => { // Task 2 await simulateServerStatus('in_progress') - await simulateServerStatus('done') + await simulateServerStatus('all-done') expect(mockTask2.task).toHaveBeenCalled() // Verify state of onComplete callbacks @@ -279,7 +279,7 @@ describe('useManagerQueue', () => { // Task 3 await simulateServerStatus('in_progress') - await simulateServerStatus('done') + await simulateServerStatus('all-done') // Verify state of onComplete callbacks expect(mockTask3.task).toHaveBeenCalled() @@ -297,7 +297,7 @@ describe('useManagerQueue', () => { // Add first task and start processing queue.enqueueTask(mockTask1) - await simulateServerStatus('done') + await simulateServerStatus('all-done') expect(mockTask1.task).toHaveBeenCalled() // Add second task while first is processing @@ -307,7 +307,7 @@ describe('useManagerQueue', () => { // Complete first task await mockTask1.task.mock.results[0].value await simulateServerStatus('in_progress') - await simulateServerStatus('done') + await simulateServerStatus('all-done') // Second task should now be processed expect(mockTask2.task).toHaveBeenCalled() @@ -318,9 +318,9 @@ describe('useManagerQueue', () => { // Cycle server status without any tasks await simulateServerStatus('in_progress') - await simulateServerStatus('done') + await simulateServerStatus('all-done') await simulateServerStatus('in_progress') - await simulateServerStatus('done') + await simulateServerStatus('all-done') // Should not cause any errors expect(queue.allTasksDone.value).toBe(true) diff --git a/tests-ui/tests/store/comfyManagerStore.test.ts b/tests-ui/tests/store/comfyManagerStore.test.ts index 41ead35327..f99d2b3632 100644 --- a/tests-ui/tests/store/comfyManagerStore.test.ts +++ b/tests-ui/tests/store/comfyManagerStore.test.ts @@ -6,6 +6,8 @@ import { useComfyManagerService } from '@/services/comfyManagerService' import { useComfyManagerStore } from '@/stores/comfyManagerStore' import { InstalledPacksResponse, + ManagerChannel, + ManagerDatabaseSource, ManagerPackInstalled } from '@/types/comfyManagerTypes' @@ -13,6 +15,34 @@ vi.mock('@/services/comfyManagerService', () => ({ useComfyManagerService: vi.fn() })) +vi.mock('@/services/dialogService', () => ({ + useDialogService: () => ({ + showManagerProgressDialog: vi.fn() + }) +})) + +vi.mock('@/composables/useManagerQueue', () => { + const enqueueTaskMock = vi.fn() + + return { + useManagerQueue: () => ({ + statusMessage: ref(''), + allTasksDone: ref(false), + enqueueTask: enqueueTaskMock, + uncompletedCount: ref(0) + }), + enqueueTask: enqueueTaskMock + } +}) + +vi.mock('@/composables/useServerLogs', () => ({ + useServerLogs: () => ({ + startListening: vi.fn(), + stopListening: vi.fn(), + logs: ref([]) + }) +})) + vi.mock('vue-i18n', () => ({ useI18n: () => ({ t: vi.fn((key) => key) @@ -33,11 +63,7 @@ interface EnabledDisabledTestCase { } describe('useComfyManagerStore', () => { - let mockManagerService: { - isLoading: ReturnType> - error: ReturnType> - listInstalledPacks: ReturnType - } + let mockManagerService: ReturnType const triggerPacksChange = async ( installedPacks: InstalledPacksResponse, @@ -55,10 +81,21 @@ describe('useComfyManagerStore', () => { mockManagerService = { isLoading: ref(false), error: ref(null), - listInstalledPacks: vi.fn().mockResolvedValue({}) + startQueue: vi.fn().mockResolvedValue(null), + resetQueue: vi.fn().mockResolvedValue(null), + getQueueStatus: vi.fn().mockResolvedValue(null), + listInstalledPacks: vi.fn().mockResolvedValue({}), + getImportFailInfo: vi.fn().mockResolvedValue(null), + installPack: vi.fn().mockResolvedValue(null), + uninstallPack: vi.fn().mockResolvedValue(null), + enablePack: vi.fn().mockResolvedValue(null), + disablePack: vi.fn().mockResolvedValue(null), + updatePack: vi.fn().mockResolvedValue(null), + updateAllPacks: vi.fn().mockResolvedValue(null), + rebootComfyUI: vi.fn().mockResolvedValue(null), + isLegacyManagerUI: vi.fn().mockResolvedValue(false) } - // @ts-expect-error Mocking the return type of useComfyManagerService vi.mocked(useComfyManagerService).mockReturnValue(mockManagerService) }) @@ -313,4 +350,90 @@ describe('useComfyManagerStore', () => { } ) }) + + describe('isPackInstalling', () => { + it('should return false for packs not being installed', () => { + const store = useComfyManagerStore() + expect(store.isPackInstalling('test-pack')).toBe(false) + expect(store.isPackInstalling(undefined)).toBe(false) + expect(store.isPackInstalling('')).toBe(false) + }) + + it('should track pack as installing when installPack is called', async () => { + const store = useComfyManagerStore() + + // Call installPack + await store.installPack.call({ + id: 'test-pack', + repository: 'https://github.com/test/test-pack', + channel: ManagerChannel.DEV, + mode: ManagerDatabaseSource.CACHE, + selected_version: 'latest', + version: 'latest' + }) + + // Check that the pack is marked as installing + expect(store.isPackInstalling('test-pack')).toBe(true) + }) + + it('should remove pack from installing list when explicitly removed', async () => { + const store = useComfyManagerStore() + + // Call installPack + await store.installPack.call({ + id: 'test-pack', + repository: 'https://github.com/test/test-pack', + channel: ManagerChannel.DEV, + mode: ManagerDatabaseSource.CACHE, + selected_version: 'latest', + version: 'latest' + }) + + // Verify pack is installing + expect(store.isPackInstalling('test-pack')).toBe(true) + + // Call installPack again for another pack to demonstrate multiple installs + await store.installPack.call({ + id: 'another-pack', + repository: 'https://github.com/test/another-pack', + channel: ManagerChannel.DEV, + mode: ManagerDatabaseSource.CACHE, + selected_version: 'latest', + version: 'latest' + }) + + // Both should be installing + expect(store.isPackInstalling('test-pack')).toBe(true) + expect(store.isPackInstalling('another-pack')).toBe(true) + }) + + it('should track multiple packs installing independently', async () => { + const store = useComfyManagerStore() + + // Install pack 1 + await store.installPack.call({ + id: 'pack-1', + repository: 'https://github.com/test/pack-1', + channel: ManagerChannel.DEV, + mode: ManagerDatabaseSource.CACHE, + selected_version: 'latest', + version: 'latest' + }) + + // Install pack 2 + await store.installPack.call({ + id: 'pack-2', + repository: 'https://github.com/test/pack-2', + channel: ManagerChannel.DEV, + mode: ManagerDatabaseSource.CACHE, + selected_version: 'latest', + version: 'latest' + }) + + // Both should be installing + expect(store.isPackInstalling('pack-1')).toBe(true) + expect(store.isPackInstalling('pack-2')).toBe(true) + expect(store.isPackInstalling('pack-3')).toBe(false) + }) + }) }) diff --git a/tests-ui/tests/stores/managerStateStore.test.ts b/tests-ui/tests/stores/managerStateStore.test.ts new file mode 100644 index 0000000000..b39b3c032a --- /dev/null +++ b/tests-ui/tests/stores/managerStateStore.test.ts @@ -0,0 +1,216 @@ +import { createPinia, setActivePinia } from 'pinia' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { useFeatureFlags } from '@/composables/useFeatureFlags' +import { api } from '@/scripts/api' +import { useComfyManagerService } from '@/services/comfyManagerService' +import { + ManagerUIState, + useManagerStateStore +} from '@/stores/managerStateStore' +import { useSystemStatsStore } from '@/stores/systemStatsStore' + +// Mock dependencies +vi.mock('@/scripts/api', () => ({ + api: { + getClientFeatureFlags: vi.fn() + } +})) + +vi.mock('@/composables/useFeatureFlags', () => ({ + useFeatureFlags: vi.fn(() => ({ + flags: { supportsManagerV4: false }, + featureFlag: vi.fn() + })) +})) + +vi.mock('@/services/comfyManagerService', () => ({ + useComfyManagerService: vi.fn() +})) + +vi.mock('@/stores/systemStatsStore', () => ({ + useSystemStatsStore: vi.fn() +})) + +describe('useManagerStateStore', () => { + beforeEach(() => { + setActivePinia(createPinia()) + vi.clearAllMocks() + }) + + describe('getManagerUIState', () => { + it('should set DISABLED state when --disable-manager is present', async () => { + vi.mocked(useSystemStatsStore).mockReturnValue({ + systemStats: { + system: { argv: ['python', 'main.py', '--disable-manager'] } + } + } as any) + vi.mocked(api.getClientFeatureFlags).mockReturnValue({}) + + const store = useManagerStateStore() + const state = await store.getManagerUIState() + + expect(state).toBe(ManagerUIState.DISABLED) + }) + + it('should set LEGACY_UI state when --enable-manager-legacy-ui is present and legacy manager is installed', async () => { + vi.mocked(useSystemStatsStore).mockReturnValue({ + systemStats: { + system: { argv: ['python', 'main.py', '--enable-manager-legacy-ui'] } + } + } as any) + vi.mocked(api.getClientFeatureFlags).mockReturnValue({}) + vi.mocked(useComfyManagerService).mockReturnValue({ + isLegacyManagerUI: vi.fn().mockResolvedValue({}) + } as any) + + const store = useManagerStateStore() + const state = await store.getManagerUIState() + + expect(state).toBe(ManagerUIState.LEGACY_UI) + }) + + it('should set DISABLED state when --enable-manager-legacy-ui is present but legacy manager is NOT installed', async () => { + vi.mocked(useSystemStatsStore).mockReturnValue({ + systemStats: { + system: { argv: ['python', 'main.py', '--enable-manager-legacy-ui'] } + } + } as any) + vi.mocked(api.getClientFeatureFlags).mockReturnValue({}) + vi.mocked(useComfyManagerService).mockReturnValue({ + isLegacyManagerUI: vi.fn().mockRejectedValue(new Error('404')) + } as any) + + const store = useManagerStateStore() + const state = await store.getManagerUIState() + + expect(state).toBe(ManagerUIState.DISABLED) + }) + + it('should set NEW_UI state when client and server both support v4', async () => { + vi.mocked(useSystemStatsStore).mockReturnValue({ + systemStats: { system: { argv: ['python', 'main.py'] } } + } as any) + vi.mocked(api.getClientFeatureFlags).mockReturnValue({ + supports_manager_v4_ui: true + }) + vi.mocked(useFeatureFlags).mockReturnValue({ + flags: { supportsManagerV4: true }, + featureFlag: vi.fn() + } as any) + vi.mocked(useComfyManagerService).mockReturnValue({ + isLegacyManagerUI: vi.fn().mockResolvedValue({}) + } as any) + + const store = useManagerStateStore() + const state = await store.getManagerUIState() + + expect(state).toBe(ManagerUIState.NEW_UI) + }) + + it('should set LEGACY_UI state when client does not support v4', async () => { + vi.mocked(useSystemStatsStore).mockReturnValue({ + systemStats: { system: { argv: ['python', 'main.py'] } } + } as any) + vi.mocked(api.getClientFeatureFlags).mockReturnValue({ + supports_manager_v4_ui: false + }) + vi.mocked(useFeatureFlags).mockReturnValue({ + flags: { supportsManagerV4: true }, + featureFlag: vi.fn() + } as any) + vi.mocked(useComfyManagerService).mockReturnValue({ + isLegacyManagerUI: vi.fn().mockResolvedValue({}) + } as any) + + const store = useManagerStateStore() + const state = await store.getManagerUIState() + + expect(state).toBe(ManagerUIState.LEGACY_UI) + }) + + it('should set LEGACY_UI state when server does not support v4', async () => { + vi.mocked(useSystemStatsStore).mockReturnValue({ + systemStats: { system: { argv: ['python', 'main.py'] } } + } as any) + vi.mocked(api.getClientFeatureFlags).mockReturnValue({ + supports_manager_v4_ui: true + }) + vi.mocked(useFeatureFlags).mockReturnValue({ + flags: { supportsManagerV4: false }, + featureFlag: vi.fn() + } as any) + vi.mocked(useComfyManagerService).mockReturnValue({ + isLegacyManagerUI: vi.fn().mockResolvedValue({}) + } as any) + + const store = useManagerStateStore() + const state = await store.getManagerUIState() + + expect(state).toBe(ManagerUIState.LEGACY_UI) + }) + + it('should set LEGACY_UI state when isLegacyManagerUI route does not exist', async () => { + vi.mocked(useSystemStatsStore).mockReturnValue({ + systemStats: { system: { argv: ['python', 'main.py'] } } + } as any) + vi.mocked(api.getClientFeatureFlags).mockReturnValue({}) + vi.mocked(useFeatureFlags).mockReturnValue({ + flags: { supportsManagerV4: false }, + featureFlag: vi.fn() + } as any) + vi.mocked(useComfyManagerService).mockReturnValue({ + isLegacyManagerUI: vi.fn().mockRejectedValue(new Error('404')) + } as any) + + const store = useManagerStateStore() + const state = await store.getManagerUIState() + + expect(state).toBe(ManagerUIState.LEGACY_UI) + }) + + it('should not re-initialize if already initialized', async () => { + vi.mocked(useSystemStatsStore).mockReturnValue({ + systemStats: { + system: { argv: ['python', 'main.py', '--disable-manager'] } + } + } as any) + + const store = useManagerStateStore() + const state1 = await store.getManagerUIState() + expect(state1).toBe(ManagerUIState.DISABLED) + + // Change the mock to return different value + vi.mocked(useSystemStatsStore).mockReturnValue({ + systemStats: { system: { argv: ['python', 'main.py'] } } + } as any) + + // Try to get state again + const state2 = await store.getManagerUIState() + + // Should still be DISABLED from first initialization + expect(state2).toBe(ManagerUIState.DISABLED) + }) + + it('should handle null systemStats gracefully', async () => { + vi.mocked(useSystemStatsStore).mockReturnValue({ + systemStats: null + } as any) + vi.mocked(api.getClientFeatureFlags).mockReturnValue({ + supports_manager_v4_ui: true + }) + vi.mocked(useFeatureFlags).mockReturnValue({ + flags: { supportsManagerV4: true }, + featureFlag: vi.fn() + } as any) + vi.mocked(useComfyManagerService).mockReturnValue({ + isLegacyManagerUI: vi.fn().mockResolvedValue({}) + } as any) + + const store = useManagerStateStore() + const state = await store.getManagerUIState() + + expect(state).toBe(ManagerUIState.NEW_UI) + }) + }) +})