mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-10 10:00:08 +00:00
refactor: replace runtime isElectron() with build-time isDesktop constant (#8710)
## 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)
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import { isElectron } from '@/utils/envUtil'
|
||||
|
||||
/**
|
||||
* Distribution types and compile-time constants for managing
|
||||
* multi-distribution builds (Desktop, Localhost, Cloud)
|
||||
@@ -15,10 +13,8 @@ declare global {
|
||||
/** Current distribution - replaced at compile time */
|
||||
const DISTRIBUTION: Distribution = __DISTRIBUTION__
|
||||
|
||||
/** Distribution type checks */
|
||||
export const isDesktop = DISTRIBUTION === 'desktop' || isElectron() // TODO: replace with build var
|
||||
export const isDesktop = DISTRIBUTION === 'desktop'
|
||||
export const isCloud = DISTRIBUTION === 'cloud'
|
||||
// export const isLocalhost = DISTRIBUTION === 'localhost' || (!isDesktop && !isCloud)
|
||||
|
||||
/**
|
||||
* Whether this is a nightly build (from main branch).
|
||||
|
||||
@@ -6,11 +6,11 @@ import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||
import { useBillingContext } from '@/composables/billing/useBillingContext'
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import { useVueFeatureFlags } from '@/composables/useVueFeatureFlags'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { isCloud, isDesktop } from '@/platform/distribution/types'
|
||||
import type { SettingTreeNode } from '@/platform/settings/settingStore'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import type { SettingParams } from '@/platform/settings/types'
|
||||
import { isElectron } from '@/utils/envUtil'
|
||||
|
||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
||||
import { buildTree } from '@/utils/treeUtil'
|
||||
|
||||
@@ -226,7 +226,7 @@ export function useSettingUI(
|
||||
...(shouldShowWorkspacePanel.value ? [workspacePanel] : []),
|
||||
keybindingPanel,
|
||||
extensionPanel,
|
||||
...(isElectron() ? [serverConfigPanel] : []),
|
||||
...(isDesktop ? [serverConfigPanel] : []),
|
||||
...(shouldShowPlanCreditsPanel.value && subscriptionPanel
|
||||
? [subscriptionPanel]
|
||||
: []),
|
||||
@@ -283,7 +283,7 @@ export function useSettingUI(
|
||||
translateCategory(keybindingPanel.node),
|
||||
translateCategory(extensionPanel.node),
|
||||
translateCategory(aboutPanel.node),
|
||||
...(isElectron() ? [translateCategory(serverConfigPanel.node)] : [])
|
||||
...(isDesktop ? [translateCategory(serverConfigPanel.node)] : [])
|
||||
]
|
||||
}),
|
||||
// Custom node settings (only shown if custom nodes have registered settings)
|
||||
@@ -332,7 +332,7 @@ export function useSettingUI(
|
||||
keybindingPanel.node,
|
||||
extensionPanel.node,
|
||||
aboutPanel.node,
|
||||
...(isElectron() ? [serverConfigPanel.node] : [])
|
||||
...(isDesktop ? [serverConfigPanel.node] : [])
|
||||
].map(translateCategory)
|
||||
}
|
||||
])
|
||||
|
||||
@@ -9,7 +9,6 @@ import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useReleaseStore } from '@/platform/updates/common/releaseStore'
|
||||
import { useReleaseService } from '@/platform/updates/common/releaseService'
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import { isElectron } from '@/utils/envUtil'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import type { SystemStats } from '@/types'
|
||||
|
||||
@@ -19,11 +18,14 @@ vi.mock('semver', () => ({
|
||||
valid: vi.fn(() => '1.0.0')
|
||||
}))
|
||||
|
||||
vi.mock('@/utils/envUtil', () => ({
|
||||
isElectron: vi.fn(() => true)
|
||||
}))
|
||||
const mockData = vi.hoisted(() => ({ isDesktop: true }))
|
||||
|
||||
vi.mock('@/platform/distribution/types', () => ({ isCloud: false }))
|
||||
vi.mock('@/platform/distribution/types', () => ({
|
||||
get isDesktop() {
|
||||
return mockData.isDesktop
|
||||
},
|
||||
isCloud: false
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/updates/common/releaseService', () => {
|
||||
const getReleases = vi.fn()
|
||||
@@ -675,10 +677,10 @@ describe('useReleaseStore', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('isElectron environment checks', () => {
|
||||
describe('when running in Electron (desktop)', () => {
|
||||
describe('isDesktop environment checks', () => {
|
||||
describe('when running on desktop', () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(isElectron).mockReturnValue(true)
|
||||
mockData.isDesktop = true
|
||||
})
|
||||
|
||||
it('should show toast when conditions are met', () => {
|
||||
@@ -709,9 +711,9 @@ describe('useReleaseStore', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('when NOT running in Electron (web)', () => {
|
||||
describe('when NOT running on desktop (web)', () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(isElectron).mockReturnValue(false)
|
||||
mockData.isDesktop = false
|
||||
})
|
||||
|
||||
it('should NOT show toast even when all other conditions are met', () => {
|
||||
|
||||
@@ -3,10 +3,9 @@ import { defineStore } from 'pinia'
|
||||
import { compare, valid } from 'semver'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { isCloud, isDesktop } from '@/platform/distribution/types'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import { isElectron } from '@/utils/envUtil'
|
||||
import { stringToLocale } from '@/utils/formatUtil'
|
||||
|
||||
import { useReleaseService } from './releaseService'
|
||||
@@ -95,7 +94,7 @@ export const useReleaseStore = defineStore('release', () => {
|
||||
// Show toast if needed
|
||||
const shouldShowToast = computed(() => {
|
||||
// Only show on desktop version
|
||||
if (!isElectron() || isCloud) {
|
||||
if (!isDesktop || isCloud) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -127,7 +126,7 @@ export const useReleaseStore = defineStore('release', () => {
|
||||
// Show red-dot indicator
|
||||
const shouldShowRedDot = computed(() => {
|
||||
// Only show on desktop version
|
||||
if (!isElectron() || isCloud) {
|
||||
if (!isDesktop || isCloud) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -172,7 +171,7 @@ export const useReleaseStore = defineStore('release', () => {
|
||||
})
|
||||
|
||||
const shouldShowPopup = computed(() => {
|
||||
if (!isElectron() && !isCloud) {
|
||||
if (!isDesktop && !isCloud) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { ReleaseNote } from '../common/releaseService'
|
||||
import ReleaseNotificationToast from './ReleaseNotificationToast.vue'
|
||||
|
||||
interface TestWindow extends Window {
|
||||
electronAPI?: Record<string, unknown>
|
||||
}
|
||||
const mockData = vi.hoisted(() => ({ isDesktop: false }))
|
||||
|
||||
const { commandExecuteMock } = vi.hoisted(() => ({
|
||||
commandExecuteMock: vi.fn()
|
||||
@@ -17,6 +15,14 @@ const { toastErrorHandlerMock } = vi.hoisted(() => ({
|
||||
toastErrorHandlerMock: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/distribution/types', () => ({
|
||||
isCloud: false,
|
||||
isNightly: false,
|
||||
get isDesktop() {
|
||||
return mockData.isDesktop
|
||||
}
|
||||
}))
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('vue-i18n', () => ({
|
||||
useI18n: vi.fn(() => ({
|
||||
@@ -105,6 +111,7 @@ describe('ReleaseNotificationToast', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockData.isDesktop = false
|
||||
// Reset store state
|
||||
mockReleaseStore.recentRelease = null
|
||||
mockReleaseStore.shouldShowToast = true // Force show for testing
|
||||
@@ -183,7 +190,8 @@ describe('ReleaseNotificationToast', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('executes desktop updater flow when running in Electron', async () => {
|
||||
it('executes desktop updater flow when running on desktop', async () => {
|
||||
mockData.isDesktop = true
|
||||
mockReleaseStore.recentRelease = {
|
||||
version: '1.2.3',
|
||||
content: '# Test Release'
|
||||
@@ -196,7 +204,6 @@ describe('ReleaseNotificationToast', () => {
|
||||
value: mockWindowOpen,
|
||||
writable: true
|
||||
})
|
||||
;(window as TestWindow).electronAPI = {}
|
||||
|
||||
wrapper = mountComponent()
|
||||
await wrapper.vm.handleUpdate()
|
||||
@@ -206,11 +213,10 @@ describe('ReleaseNotificationToast', () => {
|
||||
)
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled()
|
||||
expect(toastErrorHandlerMock).not.toHaveBeenCalled()
|
||||
|
||||
delete (window as TestWindow).electronAPI
|
||||
})
|
||||
|
||||
it('shows an error toast if the desktop updater flow fails in Electron', async () => {
|
||||
it('shows an error toast if the desktop updater flow fails on desktop', async () => {
|
||||
mockData.isDesktop = true
|
||||
mockReleaseStore.recentRelease = {
|
||||
version: '1.2.3',
|
||||
content: '# Test Release'
|
||||
@@ -224,15 +230,12 @@ describe('ReleaseNotificationToast', () => {
|
||||
value: mockWindowOpen,
|
||||
writable: true
|
||||
})
|
||||
;(window as TestWindow).electronAPI = {}
|
||||
|
||||
wrapper = mountComponent()
|
||||
await wrapper.vm.handleUpdate()
|
||||
|
||||
expect(toastErrorHandlerMock).toHaveBeenCalledWith(error)
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled()
|
||||
|
||||
delete (window as TestWindow).electronAPI
|
||||
})
|
||||
|
||||
it('calls handleShowChangelog when learn more link is clicked', async () => {
|
||||
|
||||
@@ -72,7 +72,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import { useExternalLink } from '@/composables/useExternalLink'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { isElectron } from '@/utils/envUtil'
|
||||
import { isDesktop } from '@/platform/distribution/types'
|
||||
import { formatVersionAnchor } from '@/utils/formatUtil'
|
||||
import { renderMarkdownToHtml } from '@/utils/markdownRendererUtil'
|
||||
|
||||
@@ -177,7 +177,7 @@ const handleLearnMore = () => {
|
||||
}
|
||||
|
||||
const handleUpdate = async () => {
|
||||
if (isElectron()) {
|
||||
if (isDesktop) {
|
||||
try {
|
||||
await useCommandStore().execute('Comfy-Desktop.CheckForUpdates')
|
||||
dismissToast()
|
||||
|
||||
Reference in New Issue
Block a user