mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 19:21:54 +00:00
feat: vue nodes onboarding toggle in menu
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
22
src/composables/useComfyMenu.ts
Normal file
22
src/composables/useComfyMenu.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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. "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user