mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-24 08:44:06 +00:00
Allow extension register custom topbar menu command (#982)
* Refactor command store * Rename coreMenuStore to menuStore * Extension API to register command * Update README * Add playwright test
This commit is contained in:
@@ -8,90 +8,198 @@ import { useToastStore } from '@/stores/toastStore'
|
||||
import { showTemplateWorkflowsDialog } from '@/services/dialogService'
|
||||
import { useQueueStore } from './queueStore'
|
||||
|
||||
type Command = () => void | Promise<void>
|
||||
export interface ComfyCommand {
|
||||
id: string
|
||||
function: () => void | Promise<void>
|
||||
|
||||
label?: string | (() => string)
|
||||
icon?: string | (() => string)
|
||||
tooltip?: string | (() => string)
|
||||
shortcut?: string
|
||||
}
|
||||
|
||||
const getTracker = () =>
|
||||
app.workflowManager.activeWorkflow?.changeTracker ?? globalTracker
|
||||
|
||||
export const useCommandStore = defineStore('command', () => {
|
||||
const settingStore = useSettingStore()
|
||||
const commands = ref<Record<string, Command>>({
|
||||
'Comfy.NewBlankWorkflow': () => {
|
||||
app.workflowManager.setWorkflow(null)
|
||||
app.clean()
|
||||
app.graph.clear()
|
||||
app.workflowManager.activeWorkflow.track()
|
||||
},
|
||||
'Comfy.OpenWorkflow': () => {
|
||||
app.ui.loadFile()
|
||||
},
|
||||
'Comfy.LoadDefaultWorkflow': async () => {
|
||||
await app.loadGraphData()
|
||||
},
|
||||
'Comfy.SaveWorkflow': () => {
|
||||
app.workflowManager.activeWorkflow.save()
|
||||
},
|
||||
'Comfy.SaveWorkflowAs': () => {
|
||||
app.workflowManager.activeWorkflow.save(true)
|
||||
},
|
||||
'Comfy.ExportWorkflow': () => {
|
||||
app.menu.exportWorkflow('workflow', 'workflow')
|
||||
},
|
||||
'Comfy.ExportWorkflowAPI': () => {
|
||||
app.menu.exportWorkflow('workflow_api', 'output')
|
||||
},
|
||||
'Comfy.Undo': async () => {
|
||||
await getTracker().undo()
|
||||
},
|
||||
'Comfy.Redo': async () => {
|
||||
await getTracker().redo()
|
||||
},
|
||||
'Comfy.ClearWorkflow': () => {
|
||||
if (
|
||||
!settingStore.get('Comfy.ComfirmClear') ||
|
||||
confirm('Clear workflow?')
|
||||
) {
|
||||
|
||||
const commands = ref<Record<string, ComfyCommand>>({})
|
||||
const registerCommand = (command: ComfyCommand) => {
|
||||
if (commands.value[command.id]) {
|
||||
console.warn(`Command ${command.id} already registered`)
|
||||
}
|
||||
commands.value[command.id] = command
|
||||
}
|
||||
|
||||
const commandDefinitions: ComfyCommand[] = [
|
||||
{
|
||||
id: 'Comfy.NewBlankWorkflow',
|
||||
icon: 'pi pi-plus',
|
||||
label: 'New Blank Workflow',
|
||||
function: () => {
|
||||
app.workflowManager.setWorkflow(null)
|
||||
app.clean()
|
||||
app.graph.clear()
|
||||
api.dispatchEvent(new CustomEvent('graphCleared'))
|
||||
app.workflowManager.activeWorkflow.track()
|
||||
}
|
||||
},
|
||||
'Comfy.ResetView': () => {
|
||||
app.resetView()
|
||||
{
|
||||
id: 'Comfy.OpenWorkflow',
|
||||
icon: 'pi pi-folder-open',
|
||||
label: 'Open Workflow',
|
||||
function: () => {
|
||||
app.ui.loadFile()
|
||||
}
|
||||
},
|
||||
'Comfy.OpenClipspace': () => {
|
||||
app['openClipspace']?.()
|
||||
{
|
||||
id: 'Comfy.LoadDefaultWorkflow',
|
||||
icon: 'pi pi-code',
|
||||
label: 'Load Default Workflow',
|
||||
function: async () => {
|
||||
await app.loadGraphData()
|
||||
}
|
||||
},
|
||||
'Comfy.RefreshNodeDefinitions': async () => {
|
||||
await app.refreshComboInNodes()
|
||||
{
|
||||
id: 'Comfy.SaveWorkflow',
|
||||
icon: 'pi pi-save',
|
||||
label: 'Save Workflow',
|
||||
function: () => {
|
||||
app.workflowManager.activeWorkflow.save()
|
||||
}
|
||||
},
|
||||
'Comfy.Interrupt': async () => {
|
||||
await api.interrupt()
|
||||
useToastStore().add({
|
||||
severity: 'info',
|
||||
summary: 'Interrupted',
|
||||
detail: 'Execution has been interrupted',
|
||||
life: 1000
|
||||
})
|
||||
{
|
||||
id: 'Comfy.SaveWorkflowAs',
|
||||
icon: 'pi pi-save',
|
||||
label: 'Save Workflow As',
|
||||
function: () => {
|
||||
app.workflowManager.activeWorkflow.save(true)
|
||||
}
|
||||
},
|
||||
'Comfy.ClearPendingTasks': async () => {
|
||||
await useQueueStore().clear(['queue'])
|
||||
useToastStore().add({
|
||||
severity: 'info',
|
||||
summary: 'Confirmed',
|
||||
detail: 'Pending tasks deleted',
|
||||
life: 3000
|
||||
})
|
||||
{
|
||||
id: 'Comfy.ExportWorkflow',
|
||||
icon: 'pi pi-download',
|
||||
label: 'Export Workflow',
|
||||
function: () => {
|
||||
app.menu.exportWorkflow('workflow', 'workflow')
|
||||
}
|
||||
},
|
||||
'Comfy.BrowseTemplates': showTemplateWorkflowsDialog
|
||||
})
|
||||
{
|
||||
id: 'Comfy.ExportWorkflowAPI',
|
||||
icon: 'pi pi-download',
|
||||
label: 'Export Workflow (API Format)',
|
||||
function: () => {
|
||||
app.menu.exportWorkflow('workflow_api', 'output')
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Undo',
|
||||
icon: 'pi pi-undo',
|
||||
label: 'Undo',
|
||||
function: async () => {
|
||||
await getTracker().undo()
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Redo',
|
||||
icon: 'pi pi-refresh',
|
||||
label: 'Redo',
|
||||
function: async () => {
|
||||
await getTracker().redo()
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.ClearWorkflow',
|
||||
icon: 'pi pi-trash',
|
||||
label: 'Clear Workflow',
|
||||
function: () => {
|
||||
if (
|
||||
!settingStore.get('Comfy.ComfirmClear') ||
|
||||
confirm('Clear workflow?')
|
||||
) {
|
||||
app.clean()
|
||||
app.graph.clear()
|
||||
api.dispatchEvent(new CustomEvent('graphCleared'))
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.ResetView',
|
||||
icon: 'pi pi-expand',
|
||||
label: 'Reset View',
|
||||
function: () => {
|
||||
app.resetView()
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.OpenClipspace',
|
||||
icon: 'pi pi-clipboard',
|
||||
label: 'Clipspace',
|
||||
function: () => {
|
||||
app['openClipspace']?.()
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.RefreshNodeDefinitions',
|
||||
icon: 'pi pi-refresh',
|
||||
label: 'Refresh Node Definitions',
|
||||
function: async () => {
|
||||
await app.refreshComboInNodes()
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Interrupt',
|
||||
icon: 'pi pi-stop',
|
||||
label: 'Interrupt',
|
||||
function: async () => {
|
||||
await api.interrupt()
|
||||
useToastStore().add({
|
||||
severity: 'info',
|
||||
summary: 'Interrupted',
|
||||
detail: 'Execution has been interrupted',
|
||||
life: 1000
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.ClearPendingTasks',
|
||||
icon: 'pi pi-stop',
|
||||
label: 'Clear Pending Tasks',
|
||||
function: async () => {
|
||||
await useQueueStore().clear(['queue'])
|
||||
useToastStore().add({
|
||||
severity: 'info',
|
||||
summary: 'Confirmed',
|
||||
detail: 'Pending tasks deleted',
|
||||
life: 3000
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Comfy.BrowseTemplates',
|
||||
icon: 'pi pi-folder-open',
|
||||
label: 'Browse Templates',
|
||||
function: showTemplateWorkflowsDialog
|
||||
}
|
||||
]
|
||||
|
||||
commandDefinitions.forEach(registerCommand)
|
||||
const getCommandFunction = (command: string) => {
|
||||
return commands.value[command]?.function ?? (() => {})
|
||||
}
|
||||
|
||||
const getCommand = (command: string) => {
|
||||
return commands.value[command] ?? (() => {})
|
||||
return commands.value[command]
|
||||
}
|
||||
|
||||
const isRegistered = (command: string) => {
|
||||
return !!commands.value[command]
|
||||
}
|
||||
|
||||
return {
|
||||
commands,
|
||||
getCommand
|
||||
getCommand,
|
||||
getCommandFunction,
|
||||
registerCommand,
|
||||
isRegistered
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user