mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-02 14:27:40 +00:00
Backward compatibility with extension injections on legacy menu bar (#970)
* Compatible to legacy top menu extensions * Rework css * nit
This commit is contained in:
@@ -1,140 +1,37 @@
|
||||
import type { ComfyApp } from '@/scripts/app'
|
||||
import { api } from '../../api'
|
||||
import { $el } from '../../ui'
|
||||
import { downloadBlob } from '../../utils'
|
||||
import { ComfyButton } from '../components/button'
|
||||
import { ComfyButtonGroup } from '../components/buttonGroup'
|
||||
import { ComfySplitButton } from '../components/splitButton'
|
||||
import { ComfyQueueButton } from './queueButton'
|
||||
import { getInterruptButton } from './interruptButton'
|
||||
import './menu.css'
|
||||
|
||||
const collapseOnMobile = (t) => {
|
||||
;(t.element ?? t).classList.add('comfyui-menu-mobile-collapse')
|
||||
return t
|
||||
}
|
||||
const showOnMobile = (t) => {
|
||||
;(t.element ?? t).classList.add('lt-lg-show')
|
||||
return t
|
||||
}
|
||||
// Import ComfyButton to make sure it's shimmed and exported by vite
|
||||
import { ComfyButton } from '../components/button'
|
||||
import { ComfySplitButton } from '../components/splitButton'
|
||||
import { ComfyPopup } from '../components/popup'
|
||||
console.debug(
|
||||
`Keep following definitions ${ComfyButton} ${ComfySplitButton} ${ComfyPopup}`
|
||||
)
|
||||
|
||||
export class ComfyAppMenu {
|
||||
app: ComfyApp
|
||||
logo: HTMLElement
|
||||
saveButton: ComfySplitButton
|
||||
actionsGroup: ComfyButtonGroup
|
||||
settingsGroup: ComfyButtonGroup
|
||||
viewGroup: ComfyButtonGroup
|
||||
mobileMenuButton: ComfyButton
|
||||
queueButton: ComfyQueueButton
|
||||
element: HTMLElement
|
||||
|
||||
constructor(app: ComfyApp) {
|
||||
this.app = app
|
||||
const getSaveButton = (t?: string) =>
|
||||
new ComfyButton({
|
||||
icon: 'content-save',
|
||||
tooltip: 'Save the current workflow',
|
||||
action: () => app.workflowManager.activeWorkflow.save(),
|
||||
content: t
|
||||
})
|
||||
|
||||
this.logo = $el('h1.comfyui-logo.nlg-hide', { title: 'ComfyUI' }, 'ComfyUI')
|
||||
this.saveButton = new ComfySplitButton(
|
||||
{
|
||||
primary: getSaveButton(),
|
||||
mode: 'hover',
|
||||
position: 'absolute'
|
||||
},
|
||||
getSaveButton('Save'),
|
||||
new ComfyButton({
|
||||
icon: 'content-save-edit',
|
||||
content: 'Save As',
|
||||
tooltip: 'Save the current graph as a new workflow',
|
||||
action: () => app.workflowManager.activeWorkflow.save(true)
|
||||
}),
|
||||
new ComfyButton({
|
||||
icon: 'download',
|
||||
content: 'Export',
|
||||
tooltip: 'Export the current workflow as JSON',
|
||||
action: () => this.exportWorkflow('workflow', 'workflow')
|
||||
}),
|
||||
new ComfyButton({
|
||||
icon: 'api',
|
||||
content: 'Export (API Format)',
|
||||
tooltip:
|
||||
'Export the current workflow as JSON for use with the ComfyUI API',
|
||||
action: () => this.exportWorkflow('workflow_api', 'output'),
|
||||
visibilitySetting: { id: 'Comfy.DevMode', showValue: true },
|
||||
app
|
||||
})
|
||||
)
|
||||
|
||||
const actionButtons = [
|
||||
new ComfyButton({
|
||||
icon: 'refresh',
|
||||
content: 'Refresh',
|
||||
tooltip: 'Refresh widgets in nodes to find new models or files',
|
||||
action: () => app.refreshComboInNodes()
|
||||
}),
|
||||
new ComfyButton({
|
||||
icon: 'clipboard-edit-outline',
|
||||
content: 'Clipspace',
|
||||
tooltip: 'Open Clipspace window',
|
||||
action: () => app['openClipspace']()
|
||||
}),
|
||||
new ComfyButton({
|
||||
icon: 'fit-to-page-outline',
|
||||
content: 'Reset View',
|
||||
tooltip: 'Reset the canvas view',
|
||||
action: () => app.resetView()
|
||||
}),
|
||||
new ComfyButton({
|
||||
icon: 'cancel',
|
||||
content: 'Clear',
|
||||
tooltip: 'Clears current workflow',
|
||||
action: () => {
|
||||
if (
|
||||
!app.ui.settings.getSettingValue('Comfy.ConfirmClear', true) ||
|
||||
confirm('Clear workflow?')
|
||||
) {
|
||||
app.clean()
|
||||
app.graph.clear()
|
||||
api.dispatchEvent(new CustomEvent('graphCleared'))
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
this.actionsGroup = new ComfyButtonGroup(...actionButtons)
|
||||
|
||||
// Keep the settings group as there are custom scripts attaching extra
|
||||
// Keep the group as there are custom scripts attaching extra
|
||||
// elements to it.
|
||||
this.actionsGroup = new ComfyButtonGroup()
|
||||
this.settingsGroup = new ComfyButtonGroup()
|
||||
const interruptButton = getInterruptButton('nlg-hide').element
|
||||
this.viewGroup = new ComfyButtonGroup(interruptButton)
|
||||
this.mobileMenuButton = new ComfyButton({
|
||||
icon: 'menu',
|
||||
action: (_, btn) => {
|
||||
btn.icon = this.element.classList.toggle('expanded')
|
||||
? 'menu-open'
|
||||
: 'menu'
|
||||
window.dispatchEvent(new Event('resize'))
|
||||
},
|
||||
classList: 'comfyui-button comfyui-menu-button'
|
||||
})
|
||||
this.queueButton = new ComfyQueueButton(app)
|
||||
this.viewGroup = new ComfyButtonGroup()
|
||||
|
||||
this.element = $el('nav.comfyui-menu.lg', { style: { display: 'none' } }, [
|
||||
this.logo,
|
||||
this.saveButton.element,
|
||||
collapseOnMobile(this.actionsGroup).element,
|
||||
$el('section.comfyui-menu-push'),
|
||||
collapseOnMobile(this.settingsGroup).element,
|
||||
collapseOnMobile(this.viewGroup).element,
|
||||
|
||||
getInterruptButton('lt-lg-show').element,
|
||||
this.queueButton.element,
|
||||
showOnMobile(this.mobileMenuButton).element
|
||||
this.element = $el('div.flex.gap-2.mx-2', [
|
||||
this.actionsGroup.element,
|
||||
this.settingsGroup.element,
|
||||
this.viewGroup.element
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { StatusWsMessageStatus } from '@/types/apiTypes'
|
||||
import { api } from '../../api'
|
||||
import { ComfyButton } from '../components/button'
|
||||
import { useToastStore } from '@/stores/toastStore'
|
||||
|
||||
export function getInterruptButton(visibility: string) {
|
||||
const btn = new ComfyButton({
|
||||
icon: 'close',
|
||||
tooltip: 'Cancel current generation',
|
||||
enabled: false,
|
||||
action: async () => {
|
||||
await api.interrupt()
|
||||
useToastStore().add({
|
||||
severity: 'info',
|
||||
summary: 'Interrupted',
|
||||
detail: 'Execution has been interrupted',
|
||||
life: 1000
|
||||
})
|
||||
},
|
||||
classList: ['comfyui-button', 'comfyui-interrupt-button', visibility]
|
||||
})
|
||||
|
||||
api.addEventListener(
|
||||
'status',
|
||||
({ detail }: CustomEvent<StatusWsMessageStatus>) => {
|
||||
const sz = detail?.exec_info?.queue_remaining
|
||||
btn.enabled = sz > 0
|
||||
}
|
||||
)
|
||||
|
||||
return btn
|
||||
}
|
||||
@@ -122,9 +122,6 @@
|
||||
}
|
||||
|
||||
/* Menu */
|
||||
.comfyui-menu>* {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.comfyui-menu .mdi::before {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
import { ComfyButton } from '../components/button'
|
||||
import { $el } from '../../ui'
|
||||
import { api } from '../../api'
|
||||
import { ComfySplitButton } from '../components/splitButton'
|
||||
import { ComfyQueueOptions } from './queueOptions'
|
||||
import { prop } from '../../utils'
|
||||
import type { ComfyApp } from '@/scripts/app'
|
||||
import { StatusWsMessageStatus } from '@/types/apiTypes'
|
||||
|
||||
export class ComfyQueueButton {
|
||||
element = $el('div.comfyui-queue-button')
|
||||
#internalQueueSize = 0
|
||||
|
||||
queuePrompt = async (e?: MouseEvent) => {
|
||||
this.#internalQueueSize += this.queueOptions.batchCount
|
||||
// Hold shift to queue front, event is undefined when auto-queue is enabled
|
||||
await this.app.queuePrompt(
|
||||
e?.shiftKey ? -1 : 0,
|
||||
this.queueOptions.batchCount
|
||||
)
|
||||
}
|
||||
queueOptions: ComfyQueueOptions
|
||||
app: ComfyApp
|
||||
autoQueueMode: string
|
||||
graphHasChanged: boolean
|
||||
|
||||
constructor(app: ComfyApp) {
|
||||
this.app = app
|
||||
|
||||
const queue = new ComfyButton({
|
||||
content: $el('div', [
|
||||
$el('span', {
|
||||
textContent: 'Queue'
|
||||
})
|
||||
]),
|
||||
icon: 'play',
|
||||
classList: 'comfyui-button',
|
||||
action: this.queuePrompt
|
||||
})
|
||||
|
||||
this.queueOptions = new ComfyQueueOptions(app)
|
||||
|
||||
const btn = new ComfySplitButton(
|
||||
{
|
||||
primary: queue,
|
||||
mode: 'click',
|
||||
position: 'absolute',
|
||||
horizontal: 'right'
|
||||
},
|
||||
this.queueOptions.element
|
||||
)
|
||||
btn.element.classList.add('primary')
|
||||
this.element.append(btn.element)
|
||||
|
||||
this.autoQueueMode = prop(this, 'autoQueueMode', '', () => {
|
||||
switch (this.autoQueueMode) {
|
||||
case 'instant':
|
||||
queue.icon = 'infinity'
|
||||
break
|
||||
case 'change':
|
||||
queue.icon = 'auto-mode'
|
||||
break
|
||||
default:
|
||||
queue.icon = 'play'
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
this.queueOptions.addEventListener(
|
||||
'autoQueueMode',
|
||||
(e) => (this.autoQueueMode = e['detail'])
|
||||
)
|
||||
|
||||
api.addEventListener('graphChanged', () => {
|
||||
if (this.autoQueueMode === 'change') {
|
||||
if (this.#internalQueueSize) {
|
||||
this.graphHasChanged = true
|
||||
} else {
|
||||
this.graphHasChanged = false
|
||||
this.queuePrompt()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
api.addEventListener(
|
||||
'status',
|
||||
({ detail }: CustomEvent<StatusWsMessageStatus>) => {
|
||||
this.#internalQueueSize = detail?.exec_info?.queue_remaining
|
||||
if (this.#internalQueueSize != null) {
|
||||
if (!this.#internalQueueSize && !app.lastExecutionError) {
|
||||
if (
|
||||
this.autoQueueMode === 'instant' ||
|
||||
(this.autoQueueMode === 'change' && this.graphHasChanged)
|
||||
) {
|
||||
this.graphHasChanged = false
|
||||
this.queuePrompt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import type { ComfyApp } from '@/scripts/app'
|
||||
import { $el } from '../../ui'
|
||||
import { prop } from '../../utils'
|
||||
|
||||
export class ComfyQueueOptions extends EventTarget {
|
||||
element = $el('div.comfyui-queue-options')
|
||||
app: ComfyApp
|
||||
batchCountInput: HTMLInputElement
|
||||
batchCount: number
|
||||
batchCountRange: HTMLInputElement
|
||||
autoQueueMode: string
|
||||
autoQueueEl: HTMLElement
|
||||
|
||||
constructor(app: ComfyApp) {
|
||||
super()
|
||||
this.app = app
|
||||
|
||||
this.batchCountInput = $el('input', {
|
||||
className: 'comfyui-queue-batch-value',
|
||||
type: 'number',
|
||||
min: '1',
|
||||
value: '1',
|
||||
oninput: () => (this.batchCount = +this.batchCountInput.value)
|
||||
})
|
||||
|
||||
this.batchCountRange = $el('input', {
|
||||
type: 'range',
|
||||
min: '1',
|
||||
max: '100',
|
||||
value: '1',
|
||||
oninput: () => (this.batchCount = +this.batchCountRange.value)
|
||||
})
|
||||
|
||||
this.element.append(
|
||||
$el('div.comfyui-queue-batch', [
|
||||
$el(
|
||||
'label',
|
||||
{
|
||||
textContent: 'Batch count: '
|
||||
},
|
||||
this.batchCountInput
|
||||
),
|
||||
this.batchCountRange
|
||||
])
|
||||
)
|
||||
|
||||
const createOption = (text, value, checked = false) =>
|
||||
$el(
|
||||
'label',
|
||||
{ textContent: text },
|
||||
$el('input', {
|
||||
type: 'radio',
|
||||
name: 'AutoQueueMode',
|
||||
checked,
|
||||
value,
|
||||
oninput: (e) => (this.autoQueueMode = e.target['value'])
|
||||
})
|
||||
)
|
||||
|
||||
this.autoQueueEl = $el('div.comfyui-queue-mode', [
|
||||
$el('span', 'Auto Queue:'),
|
||||
createOption('Disabled', '', true),
|
||||
createOption('Instant', 'instant'),
|
||||
createOption('On Change', 'change')
|
||||
])
|
||||
|
||||
this.element.append(this.autoQueueEl)
|
||||
|
||||
this.batchCount = prop(this, 'batchCount', 1, () => {
|
||||
this.batchCountInput.value = this.batchCount + ''
|
||||
this.batchCountRange.value = this.batchCount + ''
|
||||
})
|
||||
|
||||
this.autoQueueMode = prop(this, 'autoQueueMode', 'Disabled', () => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('autoQueueMode', {
|
||||
detail: this.autoQueueMode
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user