Skip to content

Commit

Permalink
Add topbar dropdown menu (#937)
Browse files Browse the repository at this point in the history
* Add basic menu

* Add workflows/edit to menu bar

* Add command store

* Fix z-index

* Fix beta menu setting switch

* nit

* Drop to center

* Fix command invocation
  • Loading branch information
huchenlei committed Sep 25, 2024
1 parent 6a158d4 commit 0d28c10
Show file tree
Hide file tree
Showing 13 changed files with 298 additions and 106 deletions.
1 change: 1 addition & 0 deletions browser_tests/colorPalette.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ test.describe('Color Palette', () => {
test.afterEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.CustomColorPalettes', {})
await comfyPage.setSetting('Comfy.ColorPalette', 'dark')
await comfyPage.setSetting('Comfy.Node.Opacity', 1.0)
})

test('Can show custom color palette', async ({ comfyPage }) => {
Expand Down
16 changes: 15 additions & 1 deletion browser_tests/menu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,21 @@ test.describe('Menu', () => {
const count = await comfyPage.getGraphNodesCount()
// Drag the node onto the canvas
const canvasSelector = '#graph-canvas'
await comfyPage.page.dragAndDrop(nodeSelector, canvasSelector)

// Get the bounding box of the canvas element
const canvasBoundingBox = (await comfyPage.page
.locator(canvasSelector)
.boundingBox())!

// Calculate the center position of the canvas
const targetPosition = {
x: canvasBoundingBox.x + canvasBoundingBox.width / 2,
y: canvasBoundingBox.y + canvasBoundingBox.height / 2
}

await comfyPage.page.dragAndDrop(nodeSelector, canvasSelector, {
targetPosition
})

// Verify the node is added to the canvas
expect(await comfyPage.getGraphNodesCount()).toBe(count + 1)
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="user.css" />
<link rel="stylesheet" type="text/css" href="materialdesignicons.min.css" />
</head>
<body class="litegraph">
<body class="litegraph grid">
<div id="vue-app"></div>
<div id="comfy-user-selection" class="comfy-user-selection" style="display: none;">
<main class="comfy-user-selection-inner">
Expand Down
3 changes: 2 additions & 1 deletion src/assets/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ body {
grid-column: 1/-1;
/* Position at the first row */
grid-row: 1;
z-index: 10;
/* Top menu bar dropdown needs to be above of graph canvas splitter overlay which is z-index: 999 */
z-index: 1000;
display: flex;
flex-direction: column;
}
Expand Down
43 changes: 6 additions & 37 deletions src/components/appMenu/AppMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,33 +65,27 @@
icon="pi pi-times"
severity="secondary"
:disabled="!executingPrompt"
@click="actions.interrupt"
@click="commandStore.getCommand('Comfy.Interrupt')"
></Button>

<ButtonGroup>
<Button
v-tooltip.bottom="$t('menu.refresh')"
icon="pi pi-refresh"
severity="secondary"
@click="actions.refresh"
@click="commandStore.getCommand('Comfy.RefreshNodeDefinitions')"
/>
<Button
v-tooltip.bottom="$t('menu.clipspace')"
icon="pi pi-clipboard"
severity="secondary"
@click="actions.openClipspace"
@click="commandStore.getCommand('Comfy.OpenClipspace')"
/>
<Button
v-tooltip.bottom="$t('menu.resetView')"
icon="pi pi-expand"
severity="secondary"
@click="actions.resetView"
/>
<Button
v-tooltip.bottom="$t('menu.clear')"
icon="pi pi-ban"
severity="secondary"
@click="actions.clearWorkflow"
@click="commandStore.getCommand('Comfy.ResetView')"
/>
</ButtonGroup>
</div>
Expand All @@ -115,12 +109,12 @@ import {
useQueueSettingsStore
} from '@/stores/queueStore'
import { app } from '@/scripts/app'
import { api } from '@/scripts/api'
import { storeToRefs } from 'pinia'
import { useSettingStore } from '@/stores/settingStore'
import { useToastStore } from '@/stores/toastStore'
import { useCommandStore } from '@/stores/commandStore'
const settingsStore = useSettingStore()
const commandStore = useCommandStore()
const queueCountStore = storeToRefs(useQueuePendingTaskCountStore())
const { batchCount, mode: queueMode } = storeToRefs(useQueueSettingsStore())
Expand All @@ -147,31 +141,6 @@ const executingPrompt = computed(() => !!queueCountStore.count.value)
const queuePrompt = (e: MouseEvent) => {
app.queuePrompt(e.shiftKey ? -1 : 0, batchCount.value)
}
const actions = {
interrupt: async () => {
await api.interrupt()
useToastStore().add({
severity: 'info',
summary: 'Interrupted',
detail: 'Execution has been interrupted',
life: 1000
})
},
clearWorkflow: () => {
if (
!(settingsStore.get('Comfy.ComfirmClear') ?? true) ||
confirm('Clear workflow?')
) {
app.clean()
app.graph.clear()
api.dispatchEvent(new CustomEvent('graphCleared'))
}
},
resetView: () => app.resetView(),
openClipspace: () => app['openClipspace'](),
refresh: () => app.refreshComboInNodes()
}
</script>

<style scoped>
Expand Down
30 changes: 6 additions & 24 deletions src/components/sidebar/tabs/WorkflowsSidebarTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,27 @@
icon="pi pi-th-large"
v-tooltip="$t('sideToolbar.browseTemplates')"
text
@click="browseTemplates"
@click="() => commandStore.getCommand('Comfy.BrowseTemplates')()"
/>
<Button
class="browse-workflows-button"
icon="pi pi-folder-open"
v-tooltip="'Browse for an image or exported workflow'"
text
@click="browse"
@click="() => commandStore.getCommand('Comfy.OpenWorkflow')()"
/>
<Button
class="new-default-workflow-button"
icon="pi pi-code"
v-tooltip="'Load default workflow'"
text
@click="loadDefault"
@click="() => commandStore.getCommand('Comfy.LoadDefaultWorkflow')()"
/>
<Button
class="new-blank-workflow-button"
icon="pi pi-plus"
v-tooltip="'Create a new blank workflow'"
@click="createBlank"
@click="() => commandStore.getCommand('Comfy.NewBlankWorkflow')()"
text
/>
</template>
Expand Down Expand Up @@ -114,12 +114,12 @@ import TextDivider from '@/components/common/TextDivider.vue'
import { app } from '@/scripts/app'
import { computed, nextTick, ref } from 'vue'
import { useWorkflowStore } from '@/stores/workflowStore'
import { useCommandStore } from '@/stores/commandStore'
import type { TreeNode } from 'primevue/treenode'
import { TreeExplorerNode } from '@/types/treeExplorerTypes'
import { ComfyWorkflow } from '@/scripts/workflows'
import { useI18n } from 'vue-i18n'
import { useTreeExpansion } from '@/hooks/treeHooks'
import { showTemplateWorkflowsDialog } from '@/services/dialogService'
const searchQuery = ref('')
const isSearching = computed(() => searchQuery.value.length > 0)
Expand All @@ -144,25 +144,7 @@ const handleSearch = (query: string) => {
})
}
const loadDefault = () => {
app.loadGraphData()
app.resetView()
}
const browse = () => {
app.ui.loadFile()
}
const browseTemplates = () => {
showTemplateWorkflowsDialog()
}
const createBlank = () => {
app.workflowManager.setWorkflow(null)
app.clean()
app.graph.clear()
app.workflowManager.activeWorkflow.track()
}
const commandStore = useCommandStore()
const workflowStore = useWorkflowStore()
const { t } = useI18n()
Expand Down
58 changes: 58 additions & 0 deletions src/components/topbar/TopMenubar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<teleport to=".comfyui-body-top">
<div class="top-menubar comfyui-menu" v-if="betaMenuEnabled">
<h1 class="comfyui-logo mx-2">ComfyUI</h1>
<Menubar
:model="items"
class="border-none p-0 bg-transparent"
:pt="{
rootList: 'gap-0'
}"
/>
</div>
</teleport>
</template>

<script setup lang="ts">
import Menubar from 'primevue/menubar'
import { useCoreMenuItemStore } from '@/stores/coreMenuItemStore'
import { computed } from 'vue'
import { useSettingStore } from '@/stores/settingStore'
const settingStore = useSettingStore()
const betaMenuEnabled = computed(
() => settingStore.get('Comfy.UseNewMenu') !== 'Disabled'
)
const coreMenuItemsStore = useCoreMenuItemStore()
const items = coreMenuItemsStore.menuItems
</script>

<style scoped>
.comfyui-menu {
width: 100vw;
background: var(--comfy-menu-bg);
color: var(--fg-color);
font-family: Arial, Helvetica, sans-serif;
font-size: 0.8em;
display: flex;
align-items: center;
box-sizing: border-box;
z-index: 1000;
order: 0;
grid-column: 1/-1;
overflow: auto;
max-height: 90vh;
}
.comfyui-logo {
font-size: 1.2em;
user-select: none;
cursor: default;
}
</style>

<style>
.top-menubar .p-menubar-item-link svg {
display: none;
}
</style>
16 changes: 16 additions & 0 deletions src/components/topbar/WorkflowTabs.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<div class="workflow-tabs">
<SelectButton
v-model="workflowStore.activeWorkflow"
:options="workflowStore.openWorkflows"
aria-labelledby="basic"
/>
</div>
</template>

<script setup lang="ts">
import { useWorkflowStore } from '@/stores/workflowStore'
import SelectButton from 'primevue/selectbutton'
const workflowStore = useWorkflowStore()
</script>
26 changes: 17 additions & 9 deletions src/scripts/changeTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { ComfyWorkflow } from './workflows'
export class ChangeTracker {
static MAX_HISTORY = 50
#app: ComfyApp
undo = []
redo = []
undoQueue = []
redoQueue = []
activeState = null
isOurLoad = false
workflow: ComfyWorkflow | null
Expand Down Expand Up @@ -54,12 +54,12 @@ export class ChangeTracker {
return
}
if (!ChangeTracker.graphEqual(this.activeState, currentState)) {
this.undo.push(this.activeState)
if (this.undo.length > ChangeTracker.MAX_HISTORY) {
this.undo.shift()
this.undoQueue.push(this.activeState)
if (this.undoQueue.length > ChangeTracker.MAX_HISTORY) {
this.undoQueue.shift()
}
this.activeState = clone(currentState)
this.redo.length = 0
this.redoQueue.length = 0
this.workflow.unsaved = true
api.dispatchEvent(
new CustomEvent('graphChanged', { detail: this.activeState })
Expand All @@ -80,13 +80,21 @@ export class ChangeTracker {
}
}

async undo() {
await this.updateState(this.undoQueue, this.redoQueue)
}

async redo() {
await this.updateState(this.redoQueue, this.undoQueue)
}

async undoRedo(e) {
if (e.ctrlKey || e.metaKey) {
if (e.key === 'y') {
this.updateState(this.redo, this.undo)
await this.redo()
return true
} else if (e.key === 'z') {
this.updateState(this.undo, this.redo)
await this.undo()
return true
}
}
Expand Down Expand Up @@ -276,4 +284,4 @@ export class ChangeTracker {
}
}

const globalTracker = new ChangeTracker({} as ComfyWorkflow)
export const globalTracker = new ChangeTracker({} as ComfyWorkflow)
25 changes: 0 additions & 25 deletions src/scripts/ui/menu/menu.css
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,6 @@
}

/* Menu */
.comfyui-menu {
width: 100vw;
background: var(--comfy-menu-bg);
color: var(--fg-color);
font-family: Arial, Helvetica, sans-serif;
font-size: 0.8em;
display: flex;
padding: 4px 8px;
align-items: center;
gap: 8px;
box-sizing: border-box;
z-index: 1000;
order: 0;
grid-column: 1/-1;
overflow: auto;
max-height: 90vh;
}

.comfyui-menu>* {
flex-shrink: 0;
}
Expand Down Expand Up @@ -183,13 +165,6 @@
flex: auto;
}

.comfyui-logo {
font-size: 1.2em;
margin: 0;
user-select: none;
cursor: default;
}

/** Send to workflow widget selection dialog */
.comfy-widget-selection-dialog {
border: none;
Expand Down
Loading

0 comments on commit 0d28c10

Please sign in to comment.