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:
Chenlei Hu
2024-09-26 10:44:15 +09:00
committed by GitHub
parent a53f0ba4db
commit 3585cb69f5
11 changed files with 417 additions and 193 deletions

View File

@@ -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
}
})