feat: show vue node settings toggle and show banner to switch back (#6382)

## Summary

- When switching from litegraph to vue nodes, show a toast that opens
settings to switch back
- Show a toggle in settings under Vue nodes for now called Modern Node
Design (Vue Nodes)
- Plan to update all the nomenclature as we get closer to GA

## Screenshots (if applicable)

<img width="1480" height="911" alt="image"
src="https://github.com/user-attachments/assets/fa3a34b9-2631-4175-917a-aa319f9fe415"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6382-feat-show-vue-node-settings-toggle-and-show-banner-to-switch-back-29b6d73d365081d29252c83349f06c0a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Simula_r
2025-10-29 22:39:52 -07:00
committed by GitHub
parent b27c741d7d
commit 9e309308ed
16 changed files with 81 additions and 6 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

@@ -0,0 +1,48 @@
<template>
<Toast
group="vue-nodes-migration"
position="bottom-center"
class="w-auto"
@close="handleClose"
>
<template #message>
<div class="flex flex-auto items-center justify-between gap-4">
<span class="whitespace-nowrap">{{
t('vueNodesMigration.message')
}}</span>
<Button
class="whitespace-nowrap"
size="small"
:label="t('vueNodesMigration.button')"
text
@click="handleOpenSettings"
/>
</div>
</template>
</Toast>
</template>
<script setup lang="ts">
import { useToast } from 'primevue'
import Button from 'primevue/button'
import Toast from 'primevue/toast'
import { useI18n } from 'vue-i18n'
import { useVueNodesMigrationDismissed } from '@/composables/useVueNodesMigrationDismissed'
import { useDialogService } from '@/services/dialogService'
const { t } = useI18n()
const toast = useToast()
const dialogService = useDialogService()
const isDismissed = useVueNodesMigrationDismissed()
const handleOpenSettings = () => {
dialogService.showSettingsDialog()
toast.removeGroup('vue-nodes-migration')
isDismissed.value = true
}
const handleClose = () => {
isDismissed.value = true
}
</script>

View File

@@ -4,6 +4,7 @@ import { shallowRef, watch } from 'vue'
import { useGraphNodeManager } from '@/composables/graph/useGraphNodeManager'
import type { GraphNodeManager } from '@/composables/graph/useGraphNodeManager'
import { useVueFeatureFlags } from '@/composables/useVueFeatureFlags'
import { useVueNodesMigrationDismissed } from '@/composables/useVueNodesMigrationDismissed'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
@@ -11,6 +12,7 @@ import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
import { useLayoutSync } from '@/renderer/core/layout/sync/useLayoutSync'
import { ensureCorrectLayoutScale } from '@/renderer/extensions/vueNodes/layout/ensureCorrectLayoutScale'
import { app as comfyApp } from '@/scripts/app'
import { useToastStore } from '@/platform/updates/common/toastStore'
function useVueNodeLifecycleIndividual() {
const canvasStore = useCanvasStore()
@@ -21,6 +23,8 @@ function useVueNodeLifecycleIndividual() {
const { startSync } = useLayoutSync()
const isVueNodeToastDismissed = useVueNodesMigrationDismissed()
const initializeNodeManager = () => {
// Use canvas graph if available (handles subgraph contexts), fallback to app graph
const activeGraph = comfyApp.canvas?.graph
@@ -75,11 +79,20 @@ function useVueNodeLifecycleIndividual() {
// Watch for Vue nodes enabled state changes
watch(
() => shouldRenderVueNodes.value && Boolean(comfyApp.canvas?.graph),
(enabled) => {
(enabled, wasEnabled) => {
if (enabled) {
initializeNodeManager()
ensureCorrectLayoutScale()
if (!wasEnabled && !isVueNodeToastDismissed.value) {
useToastStore().add({
group: 'vue-nodes-migration',
severity: 'info',
life: 0
})
}
} else {
comfyApp.canvas?.setDirty(true, true)
disposeNodeManagerAndSyncs()
}
},

View File

@@ -0,0 +1,8 @@
import { createSharedComposable, useLocalStorage } from '@vueuse/core'
// Browser storage events don't fire in the same tab, so separate
// useLocalStorage() calls create isolated reactive refs. Use shared
// composable to ensure all components use the same ref instance.
export const useVueNodesMigrationDismissed = createSharedComposable(() =>
useLocalStorage('comfy.vueNodesMigration.dismissed', false)
)

View File

@@ -1820,8 +1820,12 @@
}
}
},
"vueNodesMigration": {
"message": "Prefer the classic node design?",
"button": "Open Settings"
},
"vueNodesBanner": {
"message": "Nodes just got a new look and feel",
"tryItOut": "Try it out"
}
}
}

View File

@@ -1057,17 +1057,17 @@ export const CORE_SETTINGS: SettingParams[] = [
*/
{
id: 'Comfy.VueNodes.Enabled',
name: 'Enable Vue node rendering (hidden)',
type: 'hidden',
name: 'Modern Node Design (Vue Nodes)',
type: 'boolean',
tooltip:
'Render nodes as Vue components instead of canvas. Hidden; toggle via Experimental keybinding.',
'Modern: DOM-based rendering with enhanced interactivity, native browser features, and updated visual design. Classic: Traditional canvas rendering.',
defaultValue: false,
experimental: true,
versionAdded: '1.27.1'
},
{
id: 'Comfy.VueNodes.AutoScaleLayout',
name: 'Auto-scale layout for Vue nodes',
name: 'Auto-scale layout (Vue nodes)',
tooltip:
'Automatically scale node positions when switching to Vue rendering to prevent overlap',
type: 'boolean',

View File

@@ -15,6 +15,7 @@
<GlobalToast />
<RerouteMigrationToast />
<VueNodesMigrationToast />
<UnloadWindowConfirmDialog v-if="!isElectron()" />
<MenuHamburger />
</template>
@@ -40,6 +41,7 @@ import UnloadWindowConfirmDialog from '@/components/dialog/UnloadWindowConfirmDi
import GraphCanvas from '@/components/graph/GraphCanvas.vue'
import GlobalToast from '@/components/toast/GlobalToast.vue'
import RerouteMigrationToast from '@/components/toast/RerouteMigrationToast.vue'
import VueNodesMigrationToast from '@/components/toast/VueNodesMigrationToast.vue'
import { useBrowserTabTitle } from '@/composables/useBrowserTabTitle'
import { useCoreCommands } from '@/composables/useCoreCommands'
import { useErrorHandling } from '@/composables/useErrorHandling'