mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-14 09:27:41 +00:00
## Summary Replace all runtime `isElectron()` function calls with the build-time `isDesktop` constant from `@/platform/distribution/types`, enabling dead-code elimination in non-desktop builds. ## Changes - **What**: Migrate 30 files from runtime `isElectron()` detection (checking `window.electronAPI`) to the compile-time `isDesktop` constant (driven by `__DISTRIBUTION__` Vite define). Remove `isElectron` from `envUtil.ts`. Update `isNativeWindow()` to use `isDesktop`. Guard `electronAPI()` calls behind `isDesktop` checks in stores. Update 7 test files to use `vi.hoisted` + getter mock pattern for per-test `isDesktop` toggling. Add `DISTRIBUTION=desktop` to `dev:electron` script. ## Review Focus - The `electronDownloadStore.ts` now guards the top-level `electronAPI()` call behind `isDesktop` to prevent crashes on non-desktop builds. - Test mocking pattern uses `vi.hoisted` with a getter to allow per-test toggling of the `isDesktop` value. - Pre-existing issues not addressed: `as ElectronAPI` cast in `envUtil.ts`, `:class="[]"` in `BaseViewTemplate.vue`, `@ts-expect-error` in `ModelLibrarySidebarTab.vue`. - This subsumes PR #8627 and renders PR #6122 and PR #7374 obsolete. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8710-refactor-replace-runtime-isElectron-with-build-time-isDesktop-constant-3006d73d365081c08037f0e61c2f6c77) by [Unito](https://www.unito.io)
174 lines
5.2 KiB
TypeScript
174 lines
5.2 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import { computed, ref } from 'vue'
|
|
|
|
import { useShortcutsTab } from '@/composables/bottomPanelTabs/useShortcutsTab'
|
|
|
|
import { isDesktop } from '@/platform/distribution/types'
|
|
import { useCommandStore } from '@/stores/commandStore'
|
|
import type { ComfyExtension } from '@/types/comfy'
|
|
import type { BottomPanelExtension } from '@/types/extensionTypes'
|
|
|
|
type PanelType = 'terminal' | 'shortcuts'
|
|
|
|
interface PanelState {
|
|
tabs: BottomPanelExtension[]
|
|
activeTabId: string
|
|
visible: boolean
|
|
}
|
|
|
|
export const useBottomPanelStore = defineStore('bottomPanel', () => {
|
|
// Multi-panel state
|
|
const panels = ref<Record<PanelType, PanelState>>({
|
|
terminal: { tabs: [], activeTabId: '', visible: false },
|
|
shortcuts: { tabs: [], activeTabId: '', visible: false }
|
|
})
|
|
|
|
const activePanel = ref<PanelType | null>(null)
|
|
|
|
// Computed properties for active panel
|
|
const activePanelState = computed(() =>
|
|
activePanel.value ? panels.value[activePanel.value] : null
|
|
)
|
|
|
|
const activeBottomPanelTab = computed<BottomPanelExtension | null>(() => {
|
|
const state = activePanelState.value
|
|
if (!state) return null
|
|
return state.tabs.find((tab) => tab.id === state.activeTabId) ?? null
|
|
})
|
|
|
|
const bottomPanelVisible = computed({
|
|
get: () => !!activePanel.value,
|
|
set: (visible: boolean) => {
|
|
if (!visible) {
|
|
activePanel.value = null
|
|
}
|
|
}
|
|
})
|
|
const bottomPanelTabs = computed(() => activePanelState.value?.tabs ?? [])
|
|
const activeBottomPanelTabId = computed({
|
|
get: () => activePanelState.value?.activeTabId ?? '',
|
|
set: (tabId: string) => {
|
|
const state = activePanelState.value
|
|
if (state) {
|
|
state.activeTabId = tabId
|
|
}
|
|
}
|
|
})
|
|
|
|
const togglePanel = (panelType: PanelType) => {
|
|
const panel = panels.value[panelType]
|
|
if (panel.tabs.length === 0) return
|
|
|
|
if (activePanel.value === panelType) {
|
|
// Hide current panel
|
|
activePanel.value = null
|
|
} else {
|
|
// Show target panel
|
|
activePanel.value = panelType
|
|
if (!panel.activeTabId && panel.tabs.length > 0) {
|
|
panel.activeTabId = panel.tabs[0].id
|
|
}
|
|
}
|
|
}
|
|
|
|
const toggleBottomPanel = () => {
|
|
// Toggles the terminal panel if available, otherwise falls back to shortcuts
|
|
// Terminal tabs are loaded asynchronously, so may not be available immediately
|
|
const terminalPanel = panels.value.terminal
|
|
if (terminalPanel.tabs.length > 0) {
|
|
togglePanel('terminal')
|
|
} else {
|
|
// Terminal tabs not loaded yet - fall back to shortcuts panel
|
|
// If no panel is open, open shortcuts
|
|
// If shortcuts is already open, close it
|
|
// If another panel is open (shouldn't happen), switch to shortcuts
|
|
togglePanel('shortcuts')
|
|
}
|
|
}
|
|
|
|
const setActiveTab = (tabId: string) => {
|
|
const state = activePanelState.value
|
|
if (state) {
|
|
state.activeTabId = tabId
|
|
}
|
|
}
|
|
|
|
const toggleBottomPanelTab = (tabId: string) => {
|
|
// Find which panel contains this tab
|
|
for (const [panelType, panel] of Object.entries(panels.value)) {
|
|
const tab = panel.tabs.find((t) => t.id === tabId)
|
|
if (tab) {
|
|
if (activePanel.value === panelType && panel.activeTabId === tabId) {
|
|
activePanel.value = null
|
|
} else {
|
|
activePanel.value = panelType as PanelType
|
|
panel.activeTabId = tabId
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
const registerBottomPanelTab = (tab: BottomPanelExtension) => {
|
|
const targetPanel = tab.targetPanel ?? 'terminal'
|
|
const panel = panels.value[targetPanel]
|
|
|
|
panel.tabs = [...panel.tabs, tab]
|
|
if (panel.tabs.length === 1) {
|
|
panel.activeTabId = tab.id
|
|
}
|
|
|
|
const tabName = tab.title || tab.titleKey || tab.id
|
|
useCommandStore().registerCommand({
|
|
id: `Workspace.ToggleBottomPanelTab.${tab.id}`,
|
|
icon: 'pi pi-list',
|
|
label: `Toggle ${tabName} Bottom Panel`,
|
|
category: 'view-controls' as const,
|
|
function: () => toggleBottomPanelTab(tab.id),
|
|
source: 'System'
|
|
})
|
|
}
|
|
|
|
const registerCoreBottomPanelTabs = async () => {
|
|
// Register shortcuts tabs first (synchronous, always available)
|
|
useShortcutsTab().forEach(registerBottomPanelTab)
|
|
|
|
// Use __DISTRIBUTION__ directly for proper dead code elimination
|
|
if (__DISTRIBUTION__ !== 'cloud') {
|
|
try {
|
|
const { useLogsTerminalTab, useCommandTerminalTab } =
|
|
await import('@/composables/bottomPanelTabs/useTerminalTabs')
|
|
registerBottomPanelTab(useLogsTerminalTab())
|
|
if (isDesktop) {
|
|
registerBottomPanelTab(useCommandTerminalTab())
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load terminal tabs:', error)
|
|
}
|
|
}
|
|
}
|
|
|
|
const registerExtensionBottomPanelTabs = (extension: ComfyExtension) => {
|
|
if (extension.bottomPanelTabs) {
|
|
extension.bottomPanelTabs.forEach(registerBottomPanelTab)
|
|
}
|
|
}
|
|
|
|
return {
|
|
// Multi-panel API
|
|
panels,
|
|
activePanel,
|
|
togglePanel,
|
|
|
|
bottomPanelVisible,
|
|
toggleBottomPanel,
|
|
bottomPanelTabs,
|
|
activeBottomPanelTab,
|
|
activeBottomPanelTabId,
|
|
setActiveTab,
|
|
toggleBottomPanelTab,
|
|
registerBottomPanelTab,
|
|
registerCoreBottomPanelTabs,
|
|
registerExtensionBottomPanelTabs
|
|
}
|
|
})
|