feat: vue nodes onboarding toggle in menu (#6671)

## Summary

Added Nodes 2.0 menu items with a toggle. 
Updated copy in banner and toast to be more descriptive.

## Screenshots (if applicable)


https://github.com/user-attachments/assets/85bf3ae4-0e0b-4e04-82c7-a26a73cbdd5b

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6671-feat-vue-nodes-onboarding-toggle-in-menu-2aa6d73d3650817d8e5bef0ad0f8bebb)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Simula_r
2025-11-13 20:19:18 -08:00
committed by GitHub
parent 693d408c4a
commit ecd87ae0f4
15 changed files with 111 additions and 14 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 115 KiB

View File

@@ -1,5 +1,6 @@
<template>
<div
ref="menuButtonRef"
v-tooltip="{
value: t('sideToolbar.labels.menu'),
showDelay: 300,
@@ -29,6 +30,7 @@
>
<template #item="{ item, props }">
<a
v-if="item.key !== 'nodes-2.0-toggle'"
class="p-menubar-item-link px-4 py-2"
v-bind="props.action"
:href="item.url"
@@ -65,6 +67,34 @@
</span>
<i v-if="item.items" class="pi pi-angle-right ml-auto" />
</a>
<div
v-else
class="flex items-center justify-between px-4 py-2"
@click.stop="handleNodes2ToggleClick"
>
<span class="p-menubar-item-label text-nowrap">{{ item.label }}</span>
<ToggleSwitch
v-model="nodes2Enabled"
class="ml-4"
:aria-label="item.label"
:pt="{
root: {
style: {
width: '38px',
height: '20px'
}
},
handle: {
style: {
width: '16px',
height: '16px'
}
}
}"
@click.stop
@update:model-value="onNodes2ToggleChange"
/>
</div>
</template>
</TieredMenu>
</template>
@@ -73,6 +103,7 @@
import type { MenuItem } from 'primevue/menuitem'
import TieredMenu from 'primevue/tieredmenu'
import type { TieredMenuMethods, TieredMenuState } from 'primevue/tieredmenu'
import ToggleSwitch from 'primevue/toggleswitch'
import { computed, nextTick, ref } from 'vue'
import { useI18n } from 'vue-i18n'
@@ -80,6 +111,7 @@ import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.
import ComfyLogo from '@/components/icons/ComfyLogo.vue'
import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemplateSelectorDialog'
import SettingDialogContent from '@/platform/settings/components/SettingDialogContent.vue'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useTelemetry } from '@/platform/telemetry'
import { useColorPaletteService } from '@/services/colorPaletteService'
import { useCommandStore } from '@/stores/commandStore'
@@ -98,10 +130,19 @@ const colorPaletteStore = useColorPaletteStore()
const colorPaletteService = useColorPaletteService()
const dialogStore = useDialogStore()
const managerState = useManagerState()
const settingStore = useSettingStore()
const menuRef = ref<
({ dirty: boolean } & TieredMenuMethods & TieredMenuState) | null
>(null)
const menuButtonRef = ref<HTMLElement | null>(null)
const nodes2Enabled = computed({
get: () => settingStore.get('Comfy.VueNodes.Enabled') ?? false,
set: async (value: boolean) => {
await settingStore.set('Comfy.VueNodes.Enabled', value)
}
})
const telemetry = useTelemetry()
@@ -164,6 +205,10 @@ const extraMenuItems = computed(() => [
label: t('menu.theme'),
items: themeMenuItems.value
},
{
key: 'nodes-2.0-toggle',
label: 'Nodes 2.0'
},
{ separator: true },
{
key: 'browse-templates',
@@ -281,6 +326,17 @@ const hasActiveStateSiblings = (item: MenuItem): boolean => {
menuItemStore.menuItemHasActiveStateChildren[item.parentPath])
)
}
const handleNodes2ToggleClick = () => {
return false
}
const onNodes2ToggleChange = async (value: boolean) => {
await settingStore.set('Comfy.VueNodes.Enabled', value)
telemetry?.trackUiButtonClicked({
button_id: `menu_nodes_2.0_toggle_${value ? 'enabled' : 'disabled'}`
})
}
</script>
<style scoped>

View File

@@ -15,11 +15,24 @@
size="small"
:label="t('vueNodesMigration.button')"
text
@click="handleOpenSettings"
@click="switchBack"
/>
</div>
</template>
</Toast>
<Toast
group="vue-nodes-check-main-menu"
position="bottom-center"
class="w-auto"
>
<template #message>
<div class="flex flex-auto items-center justify-between gap-4">
<span class="whitespace-nowrap">{{
t('vueNodesMigrationMainMenu.message')
}}</span>
</div>
</template>
</Toast>
</template>
<script setup lang="ts">
@@ -29,20 +42,38 @@ import Toast from 'primevue/toast'
import { useI18n } from 'vue-i18n'
import { useVueNodesMigrationDismissed } from '@/composables/useVueNodesMigrationDismissed'
import { useDialogService } from '@/services/dialogService'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useTelemetry } from '@/platform/telemetry'
import { useToastStore } from '@/platform/updates/common/toastStore'
const { t } = useI18n()
const toast = useToast()
const dialogService = useDialogService()
const isDismissed = useVueNodesMigrationDismissed()
const handleOpenSettings = () => {
dialogService.showSettingsDialog()
const switchBack = async () => {
await disableVueNodes()
toast.removeGroup('vue-nodes-migration')
isDismissed.value = true
showMainMenuToast()
}
const handleClose = () => {
isDismissed.value = true
showMainMenuToast()
}
const disableVueNodes = async () => {
await useSettingStore().set('Comfy.VueNodes.Enabled', false)
useTelemetry()?.trackUiButtonClicked({
button_id: `vue_nodes_migration_toast_switch_back_clicked`
})
}
const showMainMenuToast = () => {
useToastStore().add({
group: 'vue-nodes-check-main-menu',
severity: 'info',
life: 5000
})
}
</script>

View File

@@ -4,8 +4,8 @@
class="pointer-events-auto relative w-full h-10 bg-gradient-to-r from-blue-600 to-blue-700 flex items-center justify-center px-4"
>
<div class="flex items-center">
<i class="icon-[lucide--sparkles]"></i>
<span class="pl-2">{{ $t('vueNodesBanner.message') }}</span>
<i class="icon-[lucide--rocket]"></i>
<span class="pl-2 text-sm">{{ $t('vueNodesBanner.message') }}</span>
<Button
class="cursor-pointer bg-transparent rounded h-7 px-3 border border-white text-white ml-4 text-xs"
@click="handleTryItOut"

View File

@@ -25,6 +25,8 @@ function useVueNodeLifecycleIndividual() {
const isVueNodeToastDismissed = useVueNodesMigrationDismissed()
let hasShownMigrationToast = false
const initializeNodeManager = () => {
// Use canvas graph if available (handles subgraph contexts), fallback to app graph
const activeGraph = comfyApp.canvas?.graph
@@ -85,7 +87,12 @@ function useVueNodeLifecycleIndividual() {
ensureCorrectLayoutScale(
comfyApp.canvas?.graph?.extra.workflowRendererVersion
)
if (!wasEnabled && !isVueNodeToastDismissed.value) {
if (
wasEnabled === false &&
!isVueNodeToastDismissed.value &&
!hasShownMigrationToast
) {
hasShownMigrationToast = true
useToastStore().add({
group: 'vue-nodes-migration',
severity: 'info',

View File

@@ -2043,14 +2043,17 @@
}
}
},
"vueNodesMigration": {
"message": "Prefer the classic node design?",
"button": "Open Settings"
},
"vueNodesBanner": {
"message": "Nodes just got a new look and feel",
"message": "Introducing Nodes 2.0 More flexible workflows, powerful new widgets, built for extensibility",
"tryItOut": "Try it out"
},
"vueNodesMigration": {
"message": "Prefer the legacy design?",
"button": "Switch back"
},
"vueNodesMigrationMainMenu": {
"message": "Switch back to Nodes 2.0 anytime from the main menu."
},
"cloud": {
"missingNodes": {
"title": "These nodes aren't available on Comfy Cloud yet",
@@ -2063,4 +2066,4 @@
"cannotRun": "Workflow contains unsupported nodes (highlighted red). Remove these to run the workflow. "
}
}
}
}