feat: vue nodes onboarding toggle in menu

This commit is contained in:
--list
2025-11-12 21:35:03 -08:00
parent f0f554392d
commit f6122397e4
5 changed files with 94 additions and 10 deletions

View File

@@ -1,5 +1,6 @@
<template> <template>
<div <div
ref="menuButtonRef"
v-tooltip="{ v-tooltip="{
value: t('sideToolbar.labels.menu'), value: t('sideToolbar.labels.menu'),
showDelay: 300, showDelay: 300,
@@ -29,6 +30,7 @@
> >
<template #item="{ item, props }"> <template #item="{ item, props }">
<a <a
v-if="item.key !== 'nodes-2.0-toggle'"
class="p-menubar-item-link px-4 py-2" class="p-menubar-item-link px-4 py-2"
v-bind="props.action" v-bind="props.action"
:href="item.url" :href="item.url"
@@ -65,6 +67,34 @@
</span> </span>
<i v-if="item.items" class="pi pi-angle-right ml-auto" /> <i v-if="item.items" class="pi pi-angle-right ml-auto" />
</a> </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> </template>
</TieredMenu> </TieredMenu>
</template> </template>
@@ -73,13 +103,16 @@
import type { MenuItem } from 'primevue/menuitem' import type { MenuItem } from 'primevue/menuitem'
import TieredMenu from 'primevue/tieredmenu' import TieredMenu from 'primevue/tieredmenu'
import type { TieredMenuMethods, TieredMenuState } from 'primevue/tieredmenu' import type { TieredMenuMethods, TieredMenuState } from 'primevue/tieredmenu'
import { computed, nextTick, ref } from 'vue' import ToggleSwitch from 'primevue/toggleswitch'
import { computed, nextTick, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue' import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue'
import ComfyLogo from '@/components/icons/ComfyLogo.vue' import ComfyLogo from '@/components/icons/ComfyLogo.vue'
import { useComfyMenu } from '@/composables/useComfyMenu'
import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemplateSelectorDialog' import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemplateSelectorDialog'
import SettingDialogContent from '@/platform/settings/components/SettingDialogContent.vue' import SettingDialogContent from '@/platform/settings/components/SettingDialogContent.vue'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useTelemetry } from '@/platform/telemetry' import { useTelemetry } from '@/platform/telemetry'
import { useColorPaletteService } from '@/services/colorPaletteService' import { useColorPaletteService } from '@/services/colorPaletteService'
import { useCommandStore } from '@/stores/commandStore' import { useCommandStore } from '@/stores/commandStore'
@@ -98,10 +131,20 @@ const colorPaletteStore = useColorPaletteStore()
const colorPaletteService = useColorPaletteService() const colorPaletteService = useColorPaletteService()
const dialogStore = useDialogStore() const dialogStore = useDialogStore()
const managerState = useManagerState() const managerState = useManagerState()
const settingStore = useSettingStore()
const { registerMenuButton } = useComfyMenu()
const menuRef = ref< const menuRef = ref<
({ dirty: boolean } & TieredMenuMethods & TieredMenuState) | null ({ dirty: boolean } & TieredMenuMethods & TieredMenuState) | null
>(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() const telemetry = useTelemetry()
@@ -112,6 +155,10 @@ function onLogoMenuClick(event: MouseEvent) {
menuRef.value?.toggle(event) menuRef.value?.toggle(event)
} }
onMounted(() => {
registerMenuButton(menuButtonRef.value)
})
const translateMenuItem = (item: MenuItem): MenuItem => { const translateMenuItem = (item: MenuItem): MenuItem => {
const label = typeof item.label === 'function' ? item.label() : item.label const label = typeof item.label === 'function' ? item.label() : item.label
const translatedLabel = label const translatedLabel = label
@@ -164,6 +211,10 @@ const extraMenuItems = computed(() => [
label: t('menu.theme'), label: t('menu.theme'),
items: themeMenuItems.value items: themeMenuItems.value
}, },
{
key: 'nodes-2.0-toggle',
label: 'Nodes 2.0'
},
{ separator: true }, { separator: true },
{ {
key: 'browse-templates', key: 'browse-templates',
@@ -281,6 +332,17 @@ const hasActiveStateSiblings = (item: MenuItem): boolean => {
menuItemStore.menuItemHasActiveStateChildren[item.parentPath]) 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> </script>
<style scoped> <style scoped>

View File

@@ -28,16 +28,16 @@ import Button from 'primevue/button'
import Toast from 'primevue/toast' import Toast from 'primevue/toast'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useComfyMenu } from '@/composables/useComfyMenu'
import { useVueNodesMigrationDismissed } from '@/composables/useVueNodesMigrationDismissed' import { useVueNodesMigrationDismissed } from '@/composables/useVueNodesMigrationDismissed'
import { useDialogService } from '@/services/dialogService'
const { t } = useI18n() const { t } = useI18n()
const toast = useToast() const toast = useToast()
const dialogService = useDialogService() const { openMenu } = useComfyMenu()
const isDismissed = useVueNodesMigrationDismissed() const isDismissed = useVueNodesMigrationDismissed()
const handleOpenSettings = () => { const handleOpenSettings = () => {
dialogService.showSettingsDialog() openMenu()
toast.removeGroup('vue-nodes-migration') toast.removeGroup('vue-nodes-migration')
isDismissed.value = true isDismissed.value = true
} }

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" 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"> <div class="flex items-center">
<i class="icon-[lucide--sparkles]"></i> <i class="icon-[lucide--rocket]"></i>
<span class="pl-2">{{ $t('vueNodesBanner.message') }}</span> <span class="pl-2 text-sm">{{ $t('vueNodesBanner.message') }}</span>
<Button <Button
class="cursor-pointer bg-transparent rounded h-7 px-3 border border-white text-white ml-4 text-xs" class="cursor-pointer bg-transparent rounded h-7 px-3 border border-white text-white ml-4 text-xs"
@click="handleTryItOut" @click="handleTryItOut"

View File

@@ -0,0 +1,22 @@
/**
* Composable for programmatically controlling the ComfyUI main menu
*/
import { ref } from 'vue'
const menuButtonRef = ref<HTMLElement | null>(null)
export function useComfyMenu() {
const registerMenuButton = (el: HTMLElement | null) => {
menuButtonRef.value = el
}
const openMenu = () => {
menuButtonRef.value?.click()
}
return {
registerMenuButton,
openMenu
}
}

View File

@@ -2043,11 +2043,11 @@
} }
}, },
"vueNodesMigration": { "vueNodesMigration": {
"message": "Prefer the classic node design?", "message": "Want to switch back? Toggle 'Nodes 2.0' off in settings",
"button": "Open Settings" "button": "Open main menu"
}, },
"vueNodesBanner": { "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" "tryItOut": "Try it out"
}, },
"cloud": { "cloud": {
@@ -2062,4 +2062,4 @@
"cannotRun": "Workflow contains unsupported nodes (highlighted red). Remove these to run the workflow. " "cannotRun": "Workflow contains unsupported nodes (highlighted red). Remove these to run the workflow. "
} }
} }
} }