mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-24 16:29:45 +00:00
Rename AppMenu to Actionbar (#1099)
* Rename AppMenu to Actionbar * nit * nit
This commit is contained in:
75
src/components/actionbar/BatchCountEdit.vue
Normal file
75
src/components/actionbar/BatchCountEdit.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div
|
||||
class="batch-count"
|
||||
:class="props.class"
|
||||
v-tooltip.bottom="$t('menu.batchCount')"
|
||||
>
|
||||
<InputNumber
|
||||
class="w-14"
|
||||
v-model="batchCount"
|
||||
:min="minQueueCount"
|
||||
:max="maxQueueCount"
|
||||
fluid
|
||||
showButtons
|
||||
:pt="{
|
||||
incrementButton: {
|
||||
class: 'w-6',
|
||||
onmousedown: () => {
|
||||
handleClick(true)
|
||||
}
|
||||
},
|
||||
decrementButton: {
|
||||
class: 'w-6',
|
||||
onmousedown: () => {
|
||||
handleClick(false)
|
||||
}
|
||||
}
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useQueueSettingsStore } from '@/stores/queueStore'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import InputNumber from 'primevue/inputnumber'
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Props {
|
||||
class?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
class: ''
|
||||
})
|
||||
|
||||
const queueSettingsStore = useQueueSettingsStore()
|
||||
const { batchCount } = storeToRefs(queueSettingsStore)
|
||||
const minQueueCount = 1
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const maxQueueCount = computed(() =>
|
||||
settingStore.get('Comfy.QueueButton.BatchCountLimit')
|
||||
)
|
||||
|
||||
const handleClick = (increment: boolean) => {
|
||||
let newCount: number
|
||||
if (increment) {
|
||||
const originalCount = batchCount.value - 1
|
||||
newCount = originalCount * 2
|
||||
} else {
|
||||
const originalCount = batchCount.value + 1
|
||||
newCount = Math.floor(originalCount / 2)
|
||||
}
|
||||
|
||||
batchCount.value = newCount
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.p-inputtext) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
</style>
|
||||
241
src/components/actionbar/ComfyActionbar.vue
Normal file
241
src/components/actionbar/ComfyActionbar.vue
Normal file
@@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<Panel
|
||||
v-show="visible"
|
||||
class="actionbar w-fit"
|
||||
:style="style"
|
||||
:class="{ 'is-dragging': isDragging }"
|
||||
>
|
||||
<div class="actionbar-content flex items-center" ref="panelRef">
|
||||
<span class="drag-handle cursor-move mr-2 p-0!" ref="dragHandleRef">
|
||||
</span>
|
||||
<div class="queue-button-group flex">
|
||||
<SplitButton
|
||||
class="comfyui-queue-button"
|
||||
:label="activeQueueModeMenuItem.label"
|
||||
:icon="activeQueueModeMenuItem.icon"
|
||||
severity="primary"
|
||||
@click="queuePrompt"
|
||||
:model="queueModeMenuItems"
|
||||
data-testid="queue-button"
|
||||
v-tooltip.bottom="$t('menu.queueWorkflow')"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
<Button
|
||||
:label="item.label"
|
||||
:icon="item.icon"
|
||||
:severity="item.key === queueMode ? 'primary' : 'secondary'"
|
||||
text
|
||||
v-tooltip="item.tooltip"
|
||||
/>
|
||||
</template>
|
||||
</SplitButton>
|
||||
<BatchCountEdit />
|
||||
<ButtonGroup class="execution-actions flex flex-nowrap">
|
||||
<Button
|
||||
v-tooltip.bottom="$t('menu.interrupt')"
|
||||
icon="pi pi-times"
|
||||
:severity="executingPrompt ? 'danger' : 'secondary'"
|
||||
:disabled="!executingPrompt"
|
||||
text
|
||||
@click="() => commandStore.getCommandFunction('Comfy.Interrupt')()"
|
||||
>
|
||||
</Button>
|
||||
<Button
|
||||
v-tooltip.bottom="$t('sideToolbar.queueTab.clearPendingTasks')"
|
||||
icon="pi pi-stop"
|
||||
:severity="hasPendingTasks ? 'danger' : 'secondary'"
|
||||
:disabled="!hasPendingTasks"
|
||||
text
|
||||
@click="
|
||||
() => commandStore.getCommandFunction('Comfy.ClearPendingTasks')()
|
||||
"
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<Divider layout="vertical" class="mx-1" />
|
||||
<ButtonGroup class="flex flex-nowrap">
|
||||
<Button
|
||||
v-tooltip.bottom="$t('menu.refresh')"
|
||||
icon="pi pi-refresh"
|
||||
severity="secondary"
|
||||
text
|
||||
@click="
|
||||
() =>
|
||||
commandStore.getCommandFunction('Comfy.RefreshNodeDefinitions')()
|
||||
"
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||
import Panel from 'primevue/panel'
|
||||
import Divider from 'primevue/divider'
|
||||
import SplitButton from 'primevue/splitbutton'
|
||||
import Button from 'primevue/button'
|
||||
import ButtonGroup from 'primevue/buttongroup'
|
||||
import BatchCountEdit from './BatchCountEdit.vue'
|
||||
import {
|
||||
AutoQueueMode,
|
||||
useQueuePendingTaskCountStore,
|
||||
useQueueSettingsStore
|
||||
} from '@/stores/queueStore'
|
||||
import { app } from '@/scripts/app'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { MenuItem } from 'primevue/menuitem'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDraggable, useEventListener, useLocalStorage } from '@vueuse/core'
|
||||
import { debounce, clamp } from 'lodash'
|
||||
|
||||
const settingsStore = useSettingStore()
|
||||
const commandStore = useCommandStore()
|
||||
const queueCountStore = storeToRefs(useQueuePendingTaskCountStore())
|
||||
const { mode: queueMode } = storeToRefs(useQueueSettingsStore())
|
||||
|
||||
const visible = computed(
|
||||
() => settingsStore.get('Comfy.UseNewMenu') === 'Floating'
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
const queueModeMenuItemLookup: Record<AutoQueueMode, MenuItem> = {
|
||||
disabled: {
|
||||
key: 'disabled',
|
||||
label: 'Queue',
|
||||
icon: 'pi pi-play',
|
||||
tooltip: t('menu.disabledTooltip'),
|
||||
command: () => {
|
||||
queueMode.value = 'disabled'
|
||||
}
|
||||
},
|
||||
instant: {
|
||||
key: 'instant',
|
||||
label: 'Queue (Instant)',
|
||||
icon: 'pi pi-forward',
|
||||
tooltip: t('menu.instantTooltip'),
|
||||
command: () => {
|
||||
queueMode.value = 'instant'
|
||||
}
|
||||
},
|
||||
change: {
|
||||
key: 'change',
|
||||
label: 'Queue (Change)',
|
||||
icon: 'pi pi-step-forward-alt',
|
||||
tooltip: t('menu.changeTooltip'),
|
||||
command: () => {
|
||||
queueMode.value = 'change'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const activeQueueModeMenuItem = computed(
|
||||
() => queueModeMenuItemLookup[queueMode.value]
|
||||
)
|
||||
const queueModeMenuItems = computed(() =>
|
||||
Object.values(queueModeMenuItemLookup)
|
||||
)
|
||||
|
||||
const executingPrompt = computed(() => !!queueCountStore.count.value)
|
||||
const hasPendingTasks = computed(() => queueCountStore.count.value > 1)
|
||||
|
||||
const queuePrompt = (e: MouseEvent) => {
|
||||
const commandId = e.shiftKey ? 'Comfy.QueuePromptFront' : 'Comfy.QueuePrompt'
|
||||
commandStore.getCommandFunction(commandId)()
|
||||
}
|
||||
|
||||
const panelRef = ref<HTMLElement | null>(null)
|
||||
const dragHandleRef = ref<HTMLElement | null>(null)
|
||||
const storedPosition = useLocalStorage('Comfy.MenuPosition.Floating', {
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
const { x, y, style, isDragging } = useDraggable(panelRef, {
|
||||
initialValue: { x: 0, y: 0 },
|
||||
handle: dragHandleRef,
|
||||
containerElement: document.body
|
||||
})
|
||||
|
||||
// Update storedPosition when x or y changes
|
||||
watch(
|
||||
[x, y],
|
||||
debounce(([newX, newY]) => {
|
||||
storedPosition.value = { x: newX, y: newY }
|
||||
}, 300)
|
||||
)
|
||||
|
||||
// Set initial position to bottom center
|
||||
const setInitialPosition = () => {
|
||||
if (x.value !== 0 || y.value !== 0) {
|
||||
return
|
||||
}
|
||||
if (storedPosition.value.x !== 0 || storedPosition.value.y !== 0) {
|
||||
x.value = storedPosition.value.x
|
||||
y.value = storedPosition.value.y
|
||||
return
|
||||
}
|
||||
if (panelRef.value) {
|
||||
const screenWidth = window.innerWidth
|
||||
const screenHeight = window.innerHeight
|
||||
const menuWidth = panelRef.value.offsetWidth
|
||||
const menuHeight = panelRef.value.offsetHeight
|
||||
|
||||
if (menuWidth === 0 || menuHeight === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
x.value = (screenWidth - menuWidth) / 2
|
||||
y.value = screenHeight - menuHeight - 10 // 10px margin from bottom
|
||||
}
|
||||
}
|
||||
onMounted(setInitialPosition)
|
||||
watch(visible, (newVisible) => {
|
||||
if (newVisible) {
|
||||
nextTick(setInitialPosition)
|
||||
}
|
||||
})
|
||||
|
||||
const adjustMenuPosition = () => {
|
||||
if (panelRef.value) {
|
||||
const screenWidth = window.innerWidth
|
||||
const screenHeight = window.innerHeight
|
||||
const menuWidth = panelRef.value.offsetWidth
|
||||
const menuHeight = panelRef.value.offsetHeight
|
||||
|
||||
// Adjust x position if menu is off-screen horizontally
|
||||
x.value = clamp(x.value, 0, screenWidth - menuWidth)
|
||||
|
||||
// Adjust y position if menu is off-screen vertically
|
||||
y.value = clamp(y.value, 0, screenHeight - menuHeight)
|
||||
}
|
||||
}
|
||||
|
||||
useEventListener(window, 'resize', adjustMenuPosition)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.actionbar {
|
||||
pointer-events: all;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.actionbar.is-dragging {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
:deep(.p-panel-content) {
|
||||
@apply p-1;
|
||||
}
|
||||
|
||||
:deep(.p-panel-header) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.comfyui-queue-button :deep(.p-splitbutton-dropdown) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user