mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 03:01:54 +00:00
Topbar: add Custom Nodes Manager button (#7400)
Adds a desktop-only "Custom Nodes Manager" topbar button (Lucide puzzle) in its own bordered island left of the actionbar. Button opens the manager via useManagerState().openManager(). - New i18n key: menu.customNodesManager <img width="318" height="147" alt="image" src="https://github.com/user-attachments/assets/e36c5c7f-80d1-454c-87de-e0daa822fad1" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7400-Topbar-add-Custom-Nodes-Manager-button-2c76d73d3650810b9122da61a3c4be39) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -10,48 +10,66 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mx-1 flex flex-col items-end gap-1">
|
<div class="mx-1 flex flex-col items-end gap-1">
|
||||||
<div
|
<div class="flex items-center gap-2">
|
||||||
class="actionbar-container pointer-events-auto flex h-12 items-center rounded-lg border border-interface-stroke px-2 shadow-interface"
|
|
||||||
>
|
|
||||||
<ActionBarButtons />
|
|
||||||
<!-- Support for legacy topbar elements attached by custom scripts, hidden if no elements present -->
|
|
||||||
<div
|
<div
|
||||||
ref="legacyCommandsContainerRef"
|
v-if="managerState.shouldShowManagerButtons.value && isDesktop"
|
||||||
class="[&:not(:has(*>*:not(:empty)))]:hidden"
|
class="pointer-events-auto flex h-12 shrink-0 items-center rounded-lg border border-interface-stroke bg-comfy-menu-bg px-2 shadow-interface"
|
||||||
></div>
|
|
||||||
<ComfyActionbar />
|
|
||||||
<IconButton
|
|
||||||
v-tooltip.bottom="queueHistoryTooltipConfig"
|
|
||||||
type="transparent"
|
|
||||||
size="sm"
|
|
||||||
class="relative mr-2 text-base-foreground transition-colors duration-200 ease-in-out bg-secondary-background hover:bg-secondary-background-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-background"
|
|
||||||
:aria-pressed="isQueueOverlayExpanded"
|
|
||||||
:aria-label="
|
|
||||||
t('sideToolbar.queueProgressOverlay.expandCollapsedQueue')
|
|
||||||
"
|
|
||||||
@click="toggleQueueOverlay"
|
|
||||||
>
|
>
|
||||||
<i class="icon-[lucide--history] size-4" />
|
<IconButton
|
||||||
<span
|
v-tooltip.bottom="customNodesManagerTooltipConfig"
|
||||||
v-if="queuedCount > 0"
|
type="transparent"
|
||||||
class="absolute -top-1 -right-1 min-w-[16px] rounded-full bg-primary-background py-0.25 text-[10px] font-medium leading-[14px] text-white"
|
size="sm"
|
||||||
|
class="text-base-foreground transition-colors duration-200 ease-in-out bg-secondary-background hover:bg-secondary-background-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-background"
|
||||||
|
:aria-label="t('menu.customNodesManager')"
|
||||||
|
@click="openCustomNodeManager"
|
||||||
>
|
>
|
||||||
{{ queuedCount }}
|
<i class="icon-[lucide--puzzle] size-4" />
|
||||||
</span>
|
</IconButton>
|
||||||
</IconButton>
|
</div>
|
||||||
<CurrentUserButton v-if="isLoggedIn" class="shrink-0" />
|
|
||||||
<LoginButton v-else-if="isDesktop" />
|
<div
|
||||||
<IconButton
|
class="actionbar-container pointer-events-auto flex h-12 items-center rounded-lg border border-interface-stroke bg-comfy-menu-bg px-2 shadow-interface"
|
||||||
v-if="!isRightSidePanelOpen"
|
|
||||||
v-tooltip.bottom="rightSidePanelTooltipConfig"
|
|
||||||
type="transparent"
|
|
||||||
size="sm"
|
|
||||||
class="mr-2 text-base-foreground transition-colors duration-200 ease-in-out bg-secondary-background hover:bg-secondary-background-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-background"
|
|
||||||
:aria-label="t('rightSidePanel.togglePanel')"
|
|
||||||
@click="rightSidePanelStore.togglePanel"
|
|
||||||
>
|
>
|
||||||
<i class="icon-[lucide--panel-right] size-4" />
|
<ActionBarButtons />
|
||||||
</IconButton>
|
<!-- Support for legacy topbar elements attached by custom scripts, hidden if no elements present -->
|
||||||
|
<div
|
||||||
|
ref="legacyCommandsContainerRef"
|
||||||
|
class="[&:not(:has(*>*:not(:empty)))]:hidden"
|
||||||
|
></div>
|
||||||
|
<ComfyActionbar />
|
||||||
|
<IconButton
|
||||||
|
v-tooltip.bottom="queueHistoryTooltipConfig"
|
||||||
|
type="transparent"
|
||||||
|
size="sm"
|
||||||
|
class="relative mr-2 text-base-foreground transition-colors duration-200 ease-in-out bg-secondary-background hover:bg-secondary-background-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-background"
|
||||||
|
:aria-pressed="isQueueOverlayExpanded"
|
||||||
|
:aria-label="
|
||||||
|
t('sideToolbar.queueProgressOverlay.expandCollapsedQueue')
|
||||||
|
"
|
||||||
|
@click="toggleQueueOverlay"
|
||||||
|
>
|
||||||
|
<i class="icon-[lucide--history] size-4" />
|
||||||
|
<span
|
||||||
|
v-if="queuedCount > 0"
|
||||||
|
class="absolute -top-1 -right-1 min-w-[16px] rounded-full bg-primary-background py-0.25 text-[10px] font-medium leading-[14px] text-white"
|
||||||
|
>
|
||||||
|
{{ queuedCount }}
|
||||||
|
</span>
|
||||||
|
</IconButton>
|
||||||
|
<CurrentUserButton v-if="isLoggedIn" class="shrink-0" />
|
||||||
|
<LoginButton v-else-if="isDesktop" />
|
||||||
|
<IconButton
|
||||||
|
v-if="!isRightSidePanelOpen"
|
||||||
|
v-tooltip.bottom="rightSidePanelTooltipConfig"
|
||||||
|
type="transparent"
|
||||||
|
size="sm"
|
||||||
|
class="mr-2 text-base-foreground transition-colors duration-200 ease-in-out bg-secondary-background hover:bg-secondary-background-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-background"
|
||||||
|
:aria-label="t('rightSidePanel.togglePanel')"
|
||||||
|
@click="rightSidePanelStore.togglePanel"
|
||||||
|
>
|
||||||
|
<i class="icon-[lucide--panel-right] size-4" />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<QueueProgressOverlay
|
<QueueProgressOverlay
|
||||||
v-model:expanded="isQueueOverlayExpanded"
|
v-model:expanded="isQueueOverlayExpanded"
|
||||||
@@ -74,18 +92,23 @@ import ActionBarButtons from '@/components/topbar/ActionBarButtons.vue'
|
|||||||
import CurrentUserButton from '@/components/topbar/CurrentUserButton.vue'
|
import CurrentUserButton from '@/components/topbar/CurrentUserButton.vue'
|
||||||
import LoginButton from '@/components/topbar/LoginButton.vue'
|
import LoginButton from '@/components/topbar/LoginButton.vue'
|
||||||
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||||
|
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||||
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
||||||
import { app } from '@/scripts/app'
|
import { app } from '@/scripts/app'
|
||||||
import { useQueueStore } from '@/stores/queueStore'
|
import { useQueueStore } from '@/stores/queueStore'
|
||||||
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
|
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
|
||||||
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
||||||
import { isElectron } from '@/utils/envUtil'
|
import { isElectron } from '@/utils/envUtil'
|
||||||
|
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
|
||||||
|
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||||
|
|
||||||
const workspaceStore = useWorkspaceStore()
|
const workspaceStore = useWorkspaceStore()
|
||||||
const rightSidePanelStore = useRightSidePanelStore()
|
const rightSidePanelStore = useRightSidePanelStore()
|
||||||
|
const managerState = useManagerState()
|
||||||
const { isLoggedIn } = useCurrentUser()
|
const { isLoggedIn } = useCurrentUser()
|
||||||
const isDesktop = isElectron()
|
const isDesktop = isElectron()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
const { toastErrorHandler } = useErrorHandling()
|
||||||
const isQueueOverlayExpanded = ref(false)
|
const isQueueOverlayExpanded = ref(false)
|
||||||
const queueStore = useQueueStore()
|
const queueStore = useQueueStore()
|
||||||
const isTopMenuHovered = ref(false)
|
const isTopMenuHovered = ref(false)
|
||||||
@@ -93,6 +116,9 @@ const queuedCount = computed(() => queueStore.pendingTasks.length)
|
|||||||
const queueHistoryTooltipConfig = computed(() =>
|
const queueHistoryTooltipConfig = computed(() =>
|
||||||
buildTooltipConfig(t('sideToolbar.queueProgressOverlay.viewJobHistory'))
|
buildTooltipConfig(t('sideToolbar.queueProgressOverlay.viewJobHistory'))
|
||||||
)
|
)
|
||||||
|
const customNodesManagerTooltipConfig = computed(() =>
|
||||||
|
buildTooltipConfig(t('menu.customNodesManager'))
|
||||||
|
)
|
||||||
|
|
||||||
// Right side panel toggle
|
// Right side panel toggle
|
||||||
const { isOpen: isRightSidePanelOpen } = storeToRefs(rightSidePanelStore)
|
const { isOpen: isRightSidePanelOpen } = storeToRefs(rightSidePanelStore)
|
||||||
@@ -112,10 +138,20 @@ onMounted(() => {
|
|||||||
const toggleQueueOverlay = () => {
|
const toggleQueueOverlay = () => {
|
||||||
isQueueOverlayExpanded.value = !isQueueOverlayExpanded.value
|
isQueueOverlayExpanded.value = !isQueueOverlayExpanded.value
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
const openCustomNodeManager = async () => {
|
||||||
.actionbar-container {
|
try {
|
||||||
background-color: var(--comfy-menu-bg);
|
await managerState.openManager({
|
||||||
|
initialTab: ManagerTab.All,
|
||||||
|
showToastOnLegacyError: false
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
try {
|
||||||
|
toastErrorHandler(error)
|
||||||
|
} catch (toastError) {
|
||||||
|
console.error(error)
|
||||||
|
console.error(toastError)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</script>
|
||||||
|
|||||||
@@ -807,6 +807,7 @@
|
|||||||
"dark": "Dark",
|
"dark": "Dark",
|
||||||
"light": "Light",
|
"light": "Light",
|
||||||
"manageExtensions": "Manage Extensions",
|
"manageExtensions": "Manage Extensions",
|
||||||
|
"customNodesManager": "Custom Nodes Manager",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
"queue": "Queue Panel"
|
"queue": "Queue Panel"
|
||||||
|
|||||||
Reference in New Issue
Block a user