mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-13 17:10:06 +00:00
App mode - Unify menus - 2 (#9023)
## Summary Updates subgraph breadcrumbs menu, workflow tabs context menu & linear mode menu to use a single implementation. Adds new menu items for enter/exit app mode Hides menu when in builder mode ## Changes - **What**: Changes the components to use either a reka-ui context menu or dropdown, with a standard inner list - **Breaking**: Remove existing linear toggle from sidebar as it is now in the menu ## Screenshots (if applicable) It looks basically identical other than the icon changes based on mode: In Graph Mode: <img width="261" height="497" alt="image" src="https://github.com/user-attachments/assets/eb9968a2-b528-4e21-9e14-ab4a67e717ae" /> In App Mode: <img width="254" height="499" alt="image" src="https://github.com/user-attachments/assets/54a89fab-e7b2-4cb0-bcb7-43d6d076ac83" /> Right click tab: <img width="321" height="564" alt="image" src="https://github.com/user-attachments/assets/c12c7d64-2dba-45bb-be76-2615f3e38cc6" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9023-App-mode-Unify-menus-2-30d6d73d36508162bfc0e308d5f705de) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
346
src/composables/useWorkflowActionsMenu.test.ts
Normal file
346
src/composables/useWorkflowActionsMenu.test.ts
Normal file
@@ -0,0 +1,346 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { useWorkflowActionsMenu } from '@/composables/useWorkflowActionsMenu'
|
||||
import type { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import type { WorkflowMenuAction } from '@/types/workflowMenuItem'
|
||||
|
||||
vi.mock('vue-i18n', () => ({
|
||||
useI18n: vi.fn(() => ({
|
||||
t: (key: string) => key
|
||||
}))
|
||||
}))
|
||||
|
||||
const mockBookmarkStore = vi.hoisted(() => ({
|
||||
isBookmarked: vi.fn(() => false),
|
||||
toggleBookmarked: vi.fn()
|
||||
}))
|
||||
|
||||
const mockWorkflowStore = vi.hoisted(() => ({
|
||||
activeWorkflow: { path: 'test.json', isPersisted: true } as ComfyWorkflow
|
||||
}))
|
||||
|
||||
const mockWorkflowService = vi.hoisted(() => ({
|
||||
openWorkflow: vi.fn(),
|
||||
duplicateWorkflow: vi.fn(),
|
||||
saveWorkflowAs: vi.fn(),
|
||||
deleteWorkflow: vi.fn()
|
||||
}))
|
||||
|
||||
const mockCommandStore = vi.hoisted(() => ({
|
||||
execute: vi.fn()
|
||||
}))
|
||||
|
||||
const mockSubgraphStore = vi.hoisted(() => ({
|
||||
isSubgraphBlueprint: vi.fn(() => false)
|
||||
}))
|
||||
|
||||
const mockMenuItemStore = vi.hoisted(() => ({
|
||||
hasSeenLinear: false
|
||||
}))
|
||||
|
||||
const mockCanvasStore = vi.hoisted(() => ({
|
||||
linearMode: false
|
||||
}))
|
||||
|
||||
const mockFeatureFlags = vi.hoisted(() => ({
|
||||
flags: { linearToggleEnabled: false }
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({
|
||||
useWorkflowStore: vi.fn(() => mockWorkflowStore),
|
||||
useWorkflowBookmarkStore: vi.fn(() => mockBookmarkStore)
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/workflow/core/services/workflowService', () => ({
|
||||
useWorkflowService: vi.fn(() => mockWorkflowService)
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/commandStore', () => ({
|
||||
useCommandStore: vi.fn(() => mockCommandStore)
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/subgraphStore', () => ({
|
||||
useSubgraphStore: vi.fn(() => mockSubgraphStore)
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/menuItemStore', () => ({
|
||||
useMenuItemStore: vi.fn(() => mockMenuItemStore)
|
||||
}))
|
||||
|
||||
vi.mock('@/renderer/core/canvas/canvasStore', () => ({
|
||||
useCanvasStore: vi.fn(() => mockCanvasStore)
|
||||
}))
|
||||
|
||||
vi.mock('@/composables/useFeatureFlags', () => ({
|
||||
useFeatureFlags: vi.fn(() => mockFeatureFlags)
|
||||
}))
|
||||
|
||||
type MenuItems = ReturnType<typeof useWorkflowActionsMenu>['menuItems']['value']
|
||||
|
||||
function actionItems(items: MenuItems): WorkflowMenuAction[] {
|
||||
return items.filter((i): i is WorkflowMenuAction => !i.separator)
|
||||
}
|
||||
|
||||
function menuLabels(items: MenuItems) {
|
||||
return actionItems(items).map((i) => i.label)
|
||||
}
|
||||
|
||||
function findItem(items: MenuItems, label: string): WorkflowMenuAction {
|
||||
const item = actionItems(items).find((i) => i.label === label)
|
||||
if (!item) throw new Error(`Menu item "${label}" not found`)
|
||||
return item
|
||||
}
|
||||
|
||||
describe('useWorkflowActionsMenu', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
vi.clearAllMocks()
|
||||
mockBookmarkStore.isBookmarked.mockReturnValue(false)
|
||||
mockSubgraphStore.isSubgraphBlueprint.mockReturnValue(false)
|
||||
mockMenuItemStore.hasSeenLinear = false
|
||||
mockCanvasStore.linearMode = false
|
||||
mockFeatureFlags.flags.linearToggleEnabled = false
|
||||
mockWorkflowStore.activeWorkflow = {
|
||||
path: 'test.json',
|
||||
isPersisted: true
|
||||
} as ComfyWorkflow
|
||||
})
|
||||
|
||||
it('shows root-level items by default', () => {
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
const labels = menuLabels(menuItems.value)
|
||||
|
||||
expect(labels).toContain('g.rename')
|
||||
expect(labels).toContain('breadcrumbsMenu.duplicate')
|
||||
expect(labels).toContain('menuLabels.Save')
|
||||
expect(labels).toContain('menuLabels.Save As')
|
||||
expect(labels).toContain('menuLabels.Export')
|
||||
expect(labels).toContain('menuLabels.Export (API)')
|
||||
expect(labels).toContain('breadcrumbsMenu.clearWorkflow')
|
||||
expect(labels).toContain('breadcrumbsMenu.deleteWorkflow')
|
||||
})
|
||||
|
||||
it('hides root-only items when isRoot is false', () => {
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: false })
|
||||
const labels = menuLabels(menuItems.value)
|
||||
|
||||
expect(labels).toContain('g.rename')
|
||||
expect(labels).toContain('breadcrumbsMenu.clearWorkflow')
|
||||
expect(labels).not.toContain('breadcrumbsMenu.duplicate')
|
||||
expect(labels).not.toContain('menuLabels.Save')
|
||||
expect(labels).not.toContain('menuLabels.Save As')
|
||||
})
|
||||
|
||||
it('hides delete item when includeDelete is false', () => {
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), {
|
||||
isRoot: true,
|
||||
includeDelete: false
|
||||
})
|
||||
const labels = menuLabels(menuItems.value)
|
||||
|
||||
expect(labels).not.toContain('breadcrumbsMenu.deleteWorkflow')
|
||||
})
|
||||
|
||||
it('shows app mode items when linearToggleEnabled flag is set', () => {
|
||||
mockFeatureFlags.flags.linearToggleEnabled = true
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
const labels = menuLabels(menuItems.value)
|
||||
|
||||
expect(labels).toContain('breadcrumbsMenu.enterAppMode')
|
||||
})
|
||||
|
||||
it('shows app mode items when user has seen linear mode', () => {
|
||||
mockMenuItemStore.hasSeenLinear = true
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
const labels = menuLabels(menuItems.value)
|
||||
|
||||
expect(labels).toContain('breadcrumbsMenu.enterAppMode')
|
||||
})
|
||||
|
||||
it('hides app mode items when conditions not met', () => {
|
||||
mockMenuItemStore.hasSeenLinear = false
|
||||
mockFeatureFlags.flags.linearToggleEnabled = false
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
const labels = menuLabels(menuItems.value)
|
||||
|
||||
expect(labels).not.toContain('breadcrumbsMenu.enterAppMode')
|
||||
})
|
||||
|
||||
it('hides app mode items when not root', () => {
|
||||
mockFeatureFlags.flags.linearToggleEnabled = true
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: false })
|
||||
const labels = menuLabels(menuItems.value)
|
||||
|
||||
expect(labels).not.toContain('breadcrumbsMenu.enterAppMode')
|
||||
})
|
||||
|
||||
it('shows "go to workflow mode" when in linear mode', () => {
|
||||
mockFeatureFlags.flags.linearToggleEnabled = true
|
||||
mockCanvasStore.linearMode = true
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
const labels = menuLabels(menuItems.value)
|
||||
|
||||
expect(labels).toContain('breadcrumbsMenu.exitAppMode')
|
||||
expect(labels).not.toContain('breadcrumbsMenu.enterAppMode')
|
||||
})
|
||||
|
||||
it('shows bookmark label based on bookmark state', () => {
|
||||
mockBookmarkStore.isBookmarked.mockReturnValue(true)
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
const labels = menuLabels(menuItems.value)
|
||||
|
||||
expect(labels).toContain('tabMenu.removeFromBookmarks')
|
||||
expect(labels).not.toContain('tabMenu.addToBookmarks')
|
||||
})
|
||||
|
||||
it('adds badge to app mode items', () => {
|
||||
mockFeatureFlags.flags.linearToggleEnabled = true
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
const appModeItem = findItem(
|
||||
menuItems.value,
|
||||
'breadcrumbsMenu.enterAppMode'
|
||||
)
|
||||
|
||||
expect(appModeItem.badge).toBeDefined()
|
||||
})
|
||||
|
||||
it('calls startRename when rename command is invoked', async () => {
|
||||
const startRename = vi.fn()
|
||||
const { menuItems } = useWorkflowActionsMenu(startRename, {
|
||||
isRoot: true
|
||||
})
|
||||
|
||||
await findItem(menuItems.value, 'g.rename').command?.()
|
||||
|
||||
expect(startRename).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('uses provided workflow ref instead of activeWorkflow', () => {
|
||||
const customWorkflow = ref({
|
||||
path: 'custom.json',
|
||||
isPersisted: true,
|
||||
isTemporary: false
|
||||
} as ComfyWorkflow)
|
||||
|
||||
mockBookmarkStore.isBookmarked.mockReturnValue(false)
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), {
|
||||
isRoot: true,
|
||||
workflow: customWorkflow
|
||||
})
|
||||
|
||||
expect(menuItems.value.length).toBeGreaterThan(0)
|
||||
expect(mockBookmarkStore.isBookmarked).toHaveBeenCalledWith('custom.json')
|
||||
})
|
||||
|
||||
it('shows publish item for blueprints', () => {
|
||||
mockSubgraphStore.isSubgraphBlueprint.mockReturnValue(true)
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
const labels = menuLabels(menuItems.value)
|
||||
|
||||
expect(labels).toContain('subgraphStore.publish')
|
||||
expect(labels).toContain('breadcrumbsMenu.deleteBlueprint')
|
||||
expect(labels).not.toContain('breadcrumbsMenu.duplicate')
|
||||
})
|
||||
|
||||
it('duplicate command calls workflowService.duplicateWorkflow', async () => {
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
await findItem(menuItems.value, 'breadcrumbsMenu.duplicate').command?.()
|
||||
|
||||
expect(mockWorkflowService.duplicateWorkflow).toHaveBeenCalledWith(
|
||||
mockWorkflowStore.activeWorkflow
|
||||
)
|
||||
})
|
||||
|
||||
it('save command executes Comfy.SaveWorkflow', async () => {
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
await findItem(menuItems.value, 'menuLabels.Save').command?.()
|
||||
|
||||
expect(mockCommandStore.execute).toHaveBeenCalledWith('Comfy.SaveWorkflow')
|
||||
})
|
||||
|
||||
it('delete command calls workflowService.deleteWorkflow', async () => {
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
await findItem(
|
||||
menuItems.value,
|
||||
'breadcrumbsMenu.deleteWorkflow'
|
||||
).command?.()
|
||||
|
||||
expect(mockWorkflowService.deleteWorkflow).toHaveBeenCalledWith(
|
||||
mockWorkflowStore.activeWorkflow
|
||||
)
|
||||
})
|
||||
|
||||
it('bookmark toggle calls bookmarkStore.toggleBookmarked', async () => {
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
await findItem(menuItems.value, 'tabMenu.addToBookmarks').command?.()
|
||||
|
||||
expect(mockBookmarkStore.toggleBookmarked).toHaveBeenCalledWith('test.json')
|
||||
})
|
||||
|
||||
it('app mode toggle executes Comfy.ToggleLinear', async () => {
|
||||
mockFeatureFlags.flags.linearToggleEnabled = true
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
await findItem(menuItems.value, 'breadcrumbsMenu.enterAppMode').command?.()
|
||||
|
||||
expect(mockCommandStore.execute).toHaveBeenCalledWith(
|
||||
'Comfy.ToggleLinear',
|
||||
{ metadata: { source: 'breadcrumb_menu' } }
|
||||
)
|
||||
})
|
||||
|
||||
it('rename is disabled for unpersisted root workflows', () => {
|
||||
mockWorkflowStore.activeWorkflow = {
|
||||
path: 'test.json',
|
||||
isPersisted: false
|
||||
} as ComfyWorkflow
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
const rename = findItem(menuItems.value, 'g.rename')
|
||||
|
||||
expect(rename.disabled).toBe(true)
|
||||
})
|
||||
|
||||
it('bookmark is disabled for temporary workflows', () => {
|
||||
mockWorkflowStore.activeWorkflow = {
|
||||
path: 'test.json',
|
||||
isPersisted: true,
|
||||
isTemporary: true
|
||||
} as ComfyWorkflow
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(vi.fn(), { isRoot: true })
|
||||
const bookmark = findItem(menuItems.value, 'tabMenu.addToBookmarks')
|
||||
|
||||
expect(bookmark.disabled).toBe(true)
|
||||
})
|
||||
|
||||
it('switches to custom workflow before executing rename', async () => {
|
||||
const customWorkflow = ref({
|
||||
path: 'other.json',
|
||||
isPersisted: true
|
||||
} as ComfyWorkflow)
|
||||
const startRename = vi.fn()
|
||||
|
||||
const { menuItems } = useWorkflowActionsMenu(startRename, {
|
||||
isRoot: true,
|
||||
workflow: customWorkflow
|
||||
})
|
||||
await findItem(menuItems.value, 'g.rename').command?.()
|
||||
|
||||
expect(mockWorkflowService.openWorkflow).toHaveBeenCalledWith(
|
||||
customWorkflow.value
|
||||
)
|
||||
expect(startRename).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1,16 +1,22 @@
|
||||
import type { MenuItem } from 'primevue/menuitem'
|
||||
import type { ComputedRef, Ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
|
||||
import type { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import {
|
||||
useWorkflowBookmarkStore,
|
||||
useWorkflowStore
|
||||
} from '@/platform/workflow/management/stores/workflowStore'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useMenuItemStore } from '@/stores/menuItemStore'
|
||||
import { useSubgraphStore } from '@/stores/subgraphStore'
|
||||
import type {
|
||||
WorkflowMenuAction,
|
||||
WorkflowMenuItem
|
||||
} from '@/types/workflowMenuItem'
|
||||
|
||||
interface WorkflowActionsMenuOptions {
|
||||
/** Whether this is the root workflow level. Defaults to true. */
|
||||
@@ -21,6 +27,16 @@ interface WorkflowActionsMenuOptions {
|
||||
workflow?: Ref<ComfyWorkflow | null> | ComputedRef<ComfyWorkflow | null>
|
||||
}
|
||||
|
||||
interface AddItemOptions {
|
||||
label: string
|
||||
icon: string
|
||||
command: () => void
|
||||
visible?: boolean
|
||||
disabled?: boolean
|
||||
prependSeparator?: boolean
|
||||
isNew?: boolean
|
||||
}
|
||||
|
||||
export function useWorkflowActionsMenu(
|
||||
startRename: () => void,
|
||||
options: WorkflowActionsMenuOptions = {}
|
||||
@@ -32,6 +48,9 @@ export function useWorkflowActionsMenu(
|
||||
const bookmarkStore = useWorkflowBookmarkStore()
|
||||
const commandStore = useCommandStore()
|
||||
const subgraphStore = useSubgraphStore()
|
||||
const menuItemStore = useMenuItemStore()
|
||||
const canvasStore = useCanvasStore()
|
||||
const { flags } = useFeatureFlags()
|
||||
|
||||
const targetWorkflow = computed(
|
||||
() => workflow?.value ?? workflowStore.activeWorkflow
|
||||
@@ -43,145 +62,166 @@ export function useWorkflowActionsMenu(
|
||||
await workflowService.openWorkflow(wf)
|
||||
}
|
||||
|
||||
const menuItems = computed<MenuItem[]>(() => {
|
||||
const menuItems = computed<WorkflowMenuItem[]>(() => {
|
||||
const workflow = targetWorkflow.value
|
||||
const isBlueprint = workflow
|
||||
? subgraphStore.isSubgraphBlueprint(workflow)
|
||||
: false
|
||||
|
||||
const items: MenuItem[] = []
|
||||
const items: WorkflowMenuItem[] = []
|
||||
|
||||
const addItem = (
|
||||
label: string,
|
||||
icon: string,
|
||||
command: () => void,
|
||||
const addItem = ({
|
||||
label,
|
||||
icon,
|
||||
command,
|
||||
visible = true,
|
||||
disabled = false,
|
||||
separator = false
|
||||
) => {
|
||||
prependSeparator = false,
|
||||
isNew = false
|
||||
}: AddItemOptions) => {
|
||||
if (!visible) return
|
||||
if (separator) items.push({ separator: true })
|
||||
items.push({ label, icon, command, disabled })
|
||||
if (prependSeparator) items.push({ separator: true })
|
||||
const item: WorkflowMenuAction = { label, icon, command, disabled }
|
||||
if (isNew) {
|
||||
item.badge = t('contextMenu.new')
|
||||
}
|
||||
items.push(item)
|
||||
}
|
||||
|
||||
addItem(
|
||||
t('g.rename'),
|
||||
'pi pi-pencil',
|
||||
async () => {
|
||||
const isLinearMode = canvasStore.linearMode
|
||||
const showAppModeItems =
|
||||
isRoot && (menuItemStore.hasSeenLinear || flags.linearToggleEnabled)
|
||||
const isBookmarked = bookmarkStore.isBookmarked(workflow?.path ?? '')
|
||||
|
||||
addItem({
|
||||
label: t('g.rename'),
|
||||
icon: 'pi pi-pencil',
|
||||
command: async () => {
|
||||
await ensureWorkflowActive(targetWorkflow.value)
|
||||
startRename()
|
||||
},
|
||||
true,
|
||||
isRoot && !workflow?.isPersisted
|
||||
)
|
||||
disabled: isRoot && !workflow?.isPersisted
|
||||
})
|
||||
|
||||
addItem(
|
||||
t('breadcrumbsMenu.duplicate'),
|
||||
'pi pi-copy',
|
||||
async () => {
|
||||
addItem({
|
||||
label: t('breadcrumbsMenu.duplicate'),
|
||||
icon: 'pi pi-copy',
|
||||
command: async () => {
|
||||
if (workflow) {
|
||||
await workflowService.duplicateWorkflow(workflow)
|
||||
}
|
||||
},
|
||||
isRoot && !isBlueprint
|
||||
)
|
||||
visible: isRoot && !isBlueprint
|
||||
})
|
||||
|
||||
addItem(
|
||||
t('menuLabels.Save'),
|
||||
'pi pi-save',
|
||||
async () => {
|
||||
await ensureWorkflowActive(workflow)
|
||||
await commandStore.execute('Comfy.SaveWorkflow')
|
||||
},
|
||||
isRoot,
|
||||
false,
|
||||
true
|
||||
)
|
||||
|
||||
addItem(
|
||||
t('menuLabels.Save As'),
|
||||
'pi pi-save',
|
||||
async () => {
|
||||
await ensureWorkflowActive(workflow)
|
||||
await commandStore.execute('Comfy.SaveWorkflowAs')
|
||||
},
|
||||
isRoot
|
||||
)
|
||||
|
||||
addItem(
|
||||
bookmarkStore.isBookmarked(workflow?.path ?? '')
|
||||
addItem({
|
||||
label: isBookmarked
|
||||
? t('tabMenu.removeFromBookmarks')
|
||||
: t('tabMenu.addToBookmarks'),
|
||||
'pi pi-bookmark' +
|
||||
(bookmarkStore.isBookmarked(workflow?.path ?? '') ? '-fill' : ''),
|
||||
async () => {
|
||||
icon: 'pi pi-bookmark' + (isBookmarked ? '-fill' : ''),
|
||||
command: async () => {
|
||||
if (workflow?.path) {
|
||||
await bookmarkStore.toggleBookmarked(workflow.path)
|
||||
}
|
||||
},
|
||||
isRoot,
|
||||
workflow?.isTemporary ?? false
|
||||
)
|
||||
visible: isRoot,
|
||||
disabled: workflow?.isTemporary ?? false
|
||||
})
|
||||
|
||||
addItem(
|
||||
t('menuLabels.Export'),
|
||||
'pi pi-download',
|
||||
async () => {
|
||||
addItem({
|
||||
label: t('menuLabels.Save'),
|
||||
icon: 'pi pi-save',
|
||||
command: async () => {
|
||||
await ensureWorkflowActive(workflow)
|
||||
await commandStore.execute('Comfy.SaveWorkflow')
|
||||
},
|
||||
visible: isRoot,
|
||||
prependSeparator: true
|
||||
})
|
||||
|
||||
addItem({
|
||||
label: t('menuLabels.Save As'),
|
||||
icon: 'pi pi-save',
|
||||
command: async () => {
|
||||
await ensureWorkflowActive(workflow)
|
||||
await commandStore.execute('Comfy.SaveWorkflowAs')
|
||||
},
|
||||
visible: isRoot
|
||||
})
|
||||
|
||||
addItem({
|
||||
label: t('menuLabels.Export'),
|
||||
icon: 'pi pi-download',
|
||||
command: async () => {
|
||||
await ensureWorkflowActive(workflow)
|
||||
await commandStore.execute('Comfy.ExportWorkflow')
|
||||
},
|
||||
isRoot
|
||||
)
|
||||
visible: isRoot,
|
||||
prependSeparator: true
|
||||
})
|
||||
|
||||
addItem(
|
||||
t('menuLabels.Export (API)'),
|
||||
'pi pi-download',
|
||||
async () => {
|
||||
addItem({
|
||||
label: t('menuLabels.Export (API)'),
|
||||
icon: 'pi pi-download',
|
||||
command: async () => {
|
||||
await ensureWorkflowActive(workflow)
|
||||
await commandStore.execute('Comfy.ExportWorkflowAPI')
|
||||
},
|
||||
isRoot
|
||||
)
|
||||
visible: isRoot
|
||||
})
|
||||
|
||||
addItem(
|
||||
t('breadcrumbsMenu.clearWorkflow'),
|
||||
'pi pi-trash',
|
||||
async () => {
|
||||
addItem({
|
||||
label: isLinearMode
|
||||
? t('breadcrumbsMenu.exitAppMode')
|
||||
: t('breadcrumbsMenu.enterAppMode'),
|
||||
icon: isLinearMode
|
||||
? 'icon-[comfy--workflow]'
|
||||
: 'icon-[lucide--panels-top-left]',
|
||||
command: async () => {
|
||||
await commandStore.execute('Comfy.ToggleLinear', {
|
||||
metadata: { source: 'breadcrumb_menu' }
|
||||
})
|
||||
},
|
||||
visible: showAppModeItems,
|
||||
prependSeparator: true,
|
||||
isNew: !isLinearMode
|
||||
})
|
||||
|
||||
addItem({
|
||||
label: t('breadcrumbsMenu.clearWorkflow'),
|
||||
icon: 'pi pi-trash',
|
||||
command: async () => {
|
||||
await ensureWorkflowActive(workflow)
|
||||
await commandStore.execute('Comfy.ClearWorkflow')
|
||||
},
|
||||
true,
|
||||
false,
|
||||
true
|
||||
)
|
||||
prependSeparator: true
|
||||
})
|
||||
|
||||
addItem(
|
||||
t('subgraphStore.publish'),
|
||||
'pi pi-upload',
|
||||
async () => {
|
||||
addItem({
|
||||
label: t('subgraphStore.publish'),
|
||||
icon: 'pi pi-upload',
|
||||
command: async () => {
|
||||
if (workflow) {
|
||||
await workflowService.saveWorkflowAs(workflow)
|
||||
}
|
||||
},
|
||||
isRoot && isBlueprint,
|
||||
false,
|
||||
true
|
||||
)
|
||||
visible: isRoot && isBlueprint,
|
||||
prependSeparator: true
|
||||
})
|
||||
|
||||
addItem(
|
||||
isBlueprint
|
||||
addItem({
|
||||
label: isBlueprint
|
||||
? t('breadcrumbsMenu.deleteBlueprint')
|
||||
: t('breadcrumbsMenu.deleteWorkflow'),
|
||||
'pi pi-times',
|
||||
async () => {
|
||||
icon: 'pi pi-times',
|
||||
command: async () => {
|
||||
if (workflow) {
|
||||
await workflowService.deleteWorkflow(workflow)
|
||||
}
|
||||
},
|
||||
isRoot && includeDelete,
|
||||
false,
|
||||
true
|
||||
)
|
||||
visible: isRoot && includeDelete,
|
||||
prependSeparator: true
|
||||
})
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user