Skip to content

Commit 07a74e3

Browse files
authored
Decouple Desktop UI into monorepo app (#5912)
## Summary Extracts desktop UI into apps/desktop-ui package with minimal changes. ## Changes - **What**: - Separates desktop-specific code into standalone package with independent Vite config, router, and i18n - Drastically simplifies the main app router by removing all desktop routes - Adds a some code duplication, most due to the existing design - Some duplication can be refactored to be *simpler* on either side - no need to split things by `isElectron()` - Rudimentary storybook support has been added - **Breaking**: Stacked PR for publishing must be merged before this PR makes it to stable core (but publishing _could_ be done manually) - #5915 - **Dependencies**: Takes full advantage of pnpm catalog. No additional dependencies added. ## Review Focus - Should be no changes to normal frontend operation - Scripts added to root package.json are acceptable - The duplication in this PR is copied as is, wherever possible. Any corrections or fix-ups beyond the scope of simply migrating the functionality as-is, can be addressed in later PRs. That said, if any changes are made, it instantly becomes more difficult to separate the duplicated code out into a shared utility. - Tracking issue to address concerns: #5925 ### i18n Fixing i18n is out of scope for this PR. It is a larger task that we should consider carefully and implement properly. Attempting to isolate the desktop i18n and duplicate the _current_ localisation scripts would be wasted energy.
1 parent ac9ebe1 commit 07a74e3

File tree

72 files changed

+1213
-101
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1213
-101
lines changed

.storybook/main.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,6 @@ const config: StorybookConfig = {
7676
},
7777
build: {
7878
rollupOptions: {
79-
external: () => {
80-
// Don't externalize any modules in Storybook build
81-
// This ensures PrimeVue and other dependencies are bundled
82-
return false
83-
},
8479
onwarn: (warning, warn) => {
8580
// Suppress specific warnings
8681
if (

CODEOWNERS

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
# Desktop/Electron
2-
/src/types/desktop/ @webfiltered
3-
/src/constants/desktopDialogs.ts @webfiltered
4-
/src/constants/desktopMaintenanceTasks.ts @webfiltered
2+
/apps/desktop-ui/ @webfiltered
53
/src/stores/electronDownloadStore.ts @webfiltered
64
/src/extensions/core/electronAdapter.ts @webfiltered
7-
/src/views/DesktopDialogView.vue @webfiltered
8-
/src/components/install/ @webfiltered
9-
/src/components/maintenance/ @webfiltered
105
/vite.electron.config.mts @webfiltered
116

127
# Common UI Components

apps/desktop-ui/.storybook/main.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import type { StorybookConfig } from '@storybook/vue3-vite'
2+
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
3+
import IconsResolver from 'unplugin-icons/resolver'
4+
import Icons from 'unplugin-icons/vite'
5+
import Components from 'unplugin-vue-components/vite'
6+
import type { InlineConfig } from 'vite'
7+
8+
const config: StorybookConfig = {
9+
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
10+
addons: ['@storybook/addon-docs'],
11+
framework: {
12+
name: '@storybook/vue3-vite',
13+
options: {}
14+
},
15+
staticDirs: [{ from: '../public', to: '/' }],
16+
async viteFinal(config) {
17+
// Use dynamic import to avoid CJS deprecation warning
18+
const { mergeConfig } = await import('vite')
19+
const { default: tailwindcss } = await import('@tailwindcss/vite')
20+
21+
// Filter out any plugins that might generate import maps
22+
if (config.plugins) {
23+
config.plugins = config.plugins
24+
// Type guard: ensure we have valid plugin objects with names
25+
.filter(
26+
(plugin): plugin is NonNullable<typeof plugin> & { name: string } => {
27+
return (
28+
plugin !== null &&
29+
plugin !== undefined &&
30+
typeof plugin === 'object' &&
31+
'name' in plugin &&
32+
typeof plugin.name === 'string'
33+
)
34+
}
35+
)
36+
// Business logic: filter out import-map plugins
37+
.filter((plugin) => !plugin.name.includes('import-map'))
38+
}
39+
40+
return mergeConfig(config, {
41+
// Replace plugins entirely to avoid inheritance issues
42+
plugins: [
43+
// Only include plugins we explicitly need for Storybook
44+
tailwindcss(),
45+
Icons({
46+
compiler: 'vue3',
47+
customCollections: {
48+
comfy: FileSystemIconLoader(
49+
process.cwd() + '/../../packages/design-system/src/icons'
50+
)
51+
}
52+
}),
53+
Components({
54+
dts: false, // Disable dts generation in Storybook
55+
resolvers: [
56+
IconsResolver({
57+
customCollections: ['comfy']
58+
})
59+
],
60+
dirs: [
61+
process.cwd() + '/src/components',
62+
process.cwd() + '/src/views'
63+
],
64+
deep: true,
65+
extensions: ['vue'],
66+
directoryAsNamespace: true
67+
})
68+
],
69+
server: {
70+
allowedHosts: true
71+
},
72+
resolve: {
73+
alias: {
74+
'@': process.cwd() + '/src',
75+
'@frontend-locales': process.cwd() + '/../../src/locales'
76+
}
77+
},
78+
build: {
79+
rollupOptions: {
80+
onwarn: (warning, warn) => {
81+
// Suppress specific warnings
82+
if (
83+
warning.code === 'UNUSED_EXTERNAL_IMPORT' &&
84+
warning.message?.includes('resolveComponent')
85+
) {
86+
return
87+
}
88+
// Suppress Storybook font asset warnings
89+
if (
90+
warning.code === 'UNRESOLVED_IMPORT' &&
91+
warning.message?.includes('nunito-sans')
92+
) {
93+
return
94+
}
95+
warn(warning)
96+
}
97+
},
98+
chunkSizeWarningLimit: 1000
99+
}
100+
} satisfies InlineConfig)
101+
}
102+
}
103+
export default config
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { definePreset } from '@primevue/themes'
2+
import Aura from '@primevue/themes/aura'
3+
import { setup } from '@storybook/vue3'
4+
import type { Preview, StoryContext, StoryFn } from '@storybook/vue3-vite'
5+
import { createPinia } from 'pinia'
6+
import 'primeicons/primeicons.css'
7+
import PrimeVue from 'primevue/config'
8+
import ConfirmationService from 'primevue/confirmationservice'
9+
import ToastService from 'primevue/toastservice'
10+
import Tooltip from 'primevue/tooltip'
11+
12+
import '@/assets/css/style.css'
13+
import { i18n } from '@/i18n'
14+
15+
const ComfyUIPreset = definePreset(Aura, {
16+
semantic: {
17+
// @ts-expect-error prime type quirk
18+
primary: Aura['primitive'].blue
19+
}
20+
})
21+
22+
setup((app) => {
23+
app.directive('tooltip', Tooltip)
24+
25+
const pinia = createPinia()
26+
27+
app.use(pinia)
28+
app.use(i18n)
29+
app.use(PrimeVue, {
30+
theme: {
31+
preset: ComfyUIPreset,
32+
options: {
33+
prefix: 'p',
34+
cssLayer: { name: 'primevue', order: 'primevue, tailwind-utilities' },
35+
darkModeSelector: '.dark-theme, :root:has(.dark-theme)'
36+
}
37+
}
38+
})
39+
app.use(ConfirmationService)
40+
app.use(ToastService)
41+
})
42+
43+
export const withTheme = (Story: StoryFn, context: StoryContext) => {
44+
const theme = context.globals.theme || 'light'
45+
if (theme === 'dark') {
46+
document.documentElement.classList.add('dark-theme')
47+
document.body.classList.add('dark-theme')
48+
} else {
49+
document.documentElement.classList.remove('dark-theme')
50+
document.body.classList.remove('dark-theme')
51+
}
52+
53+
return Story(context.args, context)
54+
}
55+
56+
const preview: Preview = {
57+
parameters: {
58+
controls: {
59+
matchers: { color: /(background|color)$/i, date: /Date$/i }
60+
},
61+
backgrounds: {
62+
default: 'light',
63+
values: [
64+
{ name: 'light', value: '#ffffff' },
65+
{ name: 'dark', value: '#0a0a0a' }
66+
]
67+
}
68+
},
69+
globalTypes: {
70+
theme: {
71+
name: 'Theme',
72+
description: 'Global theme for components',
73+
defaultValue: 'light',
74+
toolbar: {
75+
icon: 'circlehollow',
76+
items: [
77+
{ value: 'light', icon: 'sun', title: 'Light' },
78+
{ value: 'dark', icon: 'moon', title: 'Dark' }
79+
],
80+
showName: true,
81+
dynamicTitle: true
82+
}
83+
}
84+
},
85+
decorators: [withTheme]
86+
}
87+
88+
export default preview

apps/desktop-ui/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>ComfyUI Desktop</title>
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
7+
</head>
8+
<body>
9+
<div id="desktop-app"></div>
10+
<script type="module" src="src/main.ts"></script>
11+
</body>
12+
</html>

apps/desktop-ui/package.json

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
{
2+
"name": "@comfyorg/desktop-ui",
3+
"version": "0.0.1",
4+
"type": "module",
5+
"nx": {
6+
"tags": [
7+
"scope:desktop",
8+
"type:app"
9+
],
10+
"targets": {
11+
"dev": {
12+
"executor": "nx:run-commands",
13+
"continuous": true,
14+
"options": {
15+
"cwd": "apps/desktop-ui",
16+
"command": "vite --config vite.config.mts"
17+
}
18+
},
19+
"serve": {
20+
"executor": "nx:run-commands",
21+
"continuous": true,
22+
"options": {
23+
"cwd": "apps/desktop-ui",
24+
"command": "vite --config vite.config.mts"
25+
}
26+
},
27+
"build": {
28+
"executor": "nx:run-commands",
29+
"cache": true,
30+
"dependsOn": [
31+
"^build"
32+
],
33+
"options": {
34+
"cwd": "apps/desktop-ui",
35+
"command": "vite build --config vite.config.mts"
36+
},
37+
"outputs": [
38+
"{projectRoot}/dist"
39+
]
40+
},
41+
"preview": {
42+
"executor": "nx:run-commands",
43+
"continuous": true,
44+
"dependsOn": [
45+
"build"
46+
],
47+
"options": {
48+
"cwd": "apps/desktop-ui",
49+
"command": "vite preview --config vite.config.mts"
50+
}
51+
},
52+
"storybook": {
53+
"executor": "nx:run-commands",
54+
"continuous": true,
55+
"options": {
56+
"cwd": "apps/desktop-ui",
57+
"command": "storybook dev -p 6007"
58+
}
59+
},
60+
"build-storybook": {
61+
"executor": "nx:run-commands",
62+
"cache": true,
63+
"options": {
64+
"cwd": "apps/desktop-ui",
65+
"command": "storybook build -o dist/storybook"
66+
},
67+
"outputs": [
68+
"{projectRoot}/dist/storybook"
69+
]
70+
},
71+
"lint": {
72+
"executor": "nx:run-commands",
73+
"cache": true,
74+
"options": {
75+
"cwd": "apps/desktop-ui",
76+
"command": "eslint src --cache"
77+
}
78+
},
79+
"typecheck": {
80+
"executor": "nx:run-commands",
81+
"cache": true,
82+
"options": {
83+
"cwd": "apps/desktop-ui",
84+
"command": "vue-tsc --noEmit -p tsconfig.json"
85+
}
86+
}
87+
}
88+
},
89+
"scripts": {
90+
"storybook": "storybook dev -p 6007",
91+
"build-storybook": "storybook build -o dist/storybook"
92+
},
93+
"dependencies": {
94+
"@comfyorg/comfyui-electron-types": "0.4.73-0",
95+
"@comfyorg/shared-frontend-utils": "workspace:*",
96+
"@primevue/core": "catalog:",
97+
"@primevue/themes": "catalog:",
98+
"@vueuse/core": "catalog:",
99+
"pinia": "catalog:",
100+
"primeicons": "catalog:",
101+
"primevue": "catalog:",
102+
"vue": "catalog:",
103+
"vue-i18n": "catalog:",
104+
"vue-router": "catalog:"
105+
},
106+
"devDependencies": {
107+
"@tailwindcss/vite": "catalog:",
108+
"@vitejs/plugin-vue": "catalog:",
109+
"dotenv": "catalog:",
110+
"unplugin-icons": "catalog:",
111+
"unplugin-vue-components": "catalog:",
112+
"vite": "catalog:",
113+
"vite-plugin-html": "catalog:",
114+
"vite-plugin-vue-devtools": "catalog:",
115+
"vue-tsc": "catalog:"
116+
}
117+
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)