diff --git a/src/components/LiteGraphCanvasSplitterOverlay.vue b/src/components/LiteGraphCanvasSplitterOverlay.vue index f616eebe4..312047775 100644 --- a/src/components/LiteGraphCanvasSplitterOverlay.vue +++ b/src/components/LiteGraphCanvasSplitterOverlay.vue @@ -1,5 +1,8 @@ @@ -23,6 +24,7 @@ import Divider from 'primevue/divider' import WorkflowTabs from '@/components/topbar/WorkflowTabs.vue' import CommandMenubar from '@/components/topbar/CommandMenubar.vue' import Actionbar from '@/components/actionbar/ComfyActionbar.vue' +import BottomPanelToggleButton from '@/components/topbar/BottomPanelToggleButton.vue' import { computed, onMounted, provide, ref } from 'vue' import { useSettingStore } from '@/stores/settingStore' import { app } from '@/scripts/app' diff --git a/src/i18n.ts b/src/i18n.ts index 8669eb3a6..d0d7cff20 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -91,7 +91,8 @@ const messages = { refresh: 'Refresh node definitions', clipspace: 'Open Clipspace', resetView: 'Reset canvas view', - clear: 'Clear workflow' + clear: 'Clear workflow', + toggleBottomPanel: 'Toggle Bottom Panel' }, templateWorkflows: { title: 'Get Started with a Template', @@ -200,7 +201,8 @@ const messages = { refresh: '刷新节点', clipspace: '打开剪贴板', resetView: '重置画布视图', - clear: '清空工作流' + clear: '清空工作流', + toggleBottomPanel: '底部面板' }, templateWorkflows: { title: '从模板开始', diff --git a/src/stores/commandStore.ts b/src/stores/commandStore.ts index 4ca6c4978..db7306dd1 100644 --- a/src/stores/commandStore.ts +++ b/src/stores/commandStore.ts @@ -18,6 +18,7 @@ import { useTitleEditorStore } from './graphStore' import { useErrorHandling } from '@/hooks/errorHooks' import { useWorkflowStore } from './workflowStore' import { type KeybindingImpl, useKeybindingStore } from './keybindingStore' +import { useBottomPanelStore } from './workspace/bottomPanelStore' import { LGraphNode } from '@comfyorg/litegraph' export interface ComfyCommand { @@ -458,6 +459,15 @@ export const useCommandStore = defineStore('command', () => { } } })() + }, + { + id: 'Workspace.ToggleBottomPanel', + icon: 'pi pi-list', + label: 'Toggle Bottom Panel', + versionAdded: '1.3.22', + function: () => { + useBottomPanelStore().toggleBottomPanel() + } } ] diff --git a/src/stores/workspace/bottomPanelStore.ts b/src/stores/workspace/bottomPanelStore.ts new file mode 100644 index 000000000..a308b2523 --- /dev/null +++ b/src/stores/workspace/bottomPanelStore.ts @@ -0,0 +1,59 @@ +import type { BottomPanelExtension } from '@/types/extensionTypes' +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' +import { useCommandStore } from '@/stores/commandStore' + +export const useBottomPanelStore = defineStore('bottomPanel', () => { + const bottomPanelVisible = ref(false) + const toggleBottomPanel = () => { + // If there are no tabs, don't show the bottom panel + if (bottomPanelTabs.value.length === 0) { + return + } + bottomPanelVisible.value = !bottomPanelVisible.value + } + + const bottomPanelTabs = ref([]) + const activeBottomPanelTabId = ref(null) + const activeBottomPanelTab = computed(() => { + return ( + bottomPanelTabs.value.find( + (tab) => tab.id === activeBottomPanelTabId.value + ) ?? null + ) + }) + const setActiveTab = (tabId: string) => { + activeBottomPanelTabId.value = tabId + } + const toggleBottomPanelTab = (tabId: string) => { + if (activeBottomPanelTabId.value === tabId) { + bottomPanelVisible.value = false + } else { + activeBottomPanelTabId.value = tabId + bottomPanelVisible.value = true + } + } + const registerBottomPanelTab = (tab: BottomPanelExtension) => { + bottomPanelTabs.value = [...bottomPanelTabs.value, tab] + if (bottomPanelTabs.value.length === 1) { + activeBottomPanelTabId.value = tab.id + } + useCommandStore().registerCommand({ + id: `Workspace.ToggleBottomPanelTab.${tab.id}`, + icon: 'pi pi-list', + label: tab.title, + function: () => toggleBottomPanelTab(tab.id) + }) + } + + return { + bottomPanelVisible, + toggleBottomPanel, + bottomPanelTabs, + activeBottomPanelTab, + activeBottomPanelTabId, + setActiveTab, + toggleBottomPanelTab, + registerBottomPanelTab + } +}) diff --git a/src/types/extensionTypes.ts b/src/types/extensionTypes.ts index 5cfb393ab..6a0d74171 100644 --- a/src/types/extensionTypes.ts +++ b/src/types/extensionTypes.ts @@ -6,25 +6,41 @@ export interface BaseSidebarTabExtension { title: string icon?: string iconBadge?: string | (() => string | null) - order?: number tooltip?: string } -export interface VueSidebarTabExtension extends BaseSidebarTabExtension { +export interface BaseBottomPanelExtension { + id: string + title: string +} + +export interface VueExtension { + id: string type: 'vue' component: Component } -export interface CustomSidebarTabExtension extends BaseSidebarTabExtension { +export interface CustomExtension { + id: string type: 'custom' render: (container: HTMLElement) => void destroy?: () => void } +export type VueSidebarTabExtension = BaseSidebarTabExtension & VueExtension +export type CustomSidebarTabExtension = BaseSidebarTabExtension & + CustomExtension export type SidebarTabExtension = | VueSidebarTabExtension | CustomSidebarTabExtension +export type VueBottomPanelExtension = BaseBottomPanelExtension & VueExtension +export type CustomBottomPanelExtension = BaseBottomPanelExtension & + CustomExtension +export type BottomPanelExtension = + | VueBottomPanelExtension + | CustomBottomPanelExtension + export type ToastManager = { add(message: ToastMessageOptions): void remove(message: ToastMessageOptions): void