Compare commits

...

11 Commits

Author SHA1 Message Date
Benjamin Lu
7aa758b3d2 Update src/composables/useTemplateFiltering.ts
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-06-17 09:48:56 -07:00
Benjamin Lu
fce9112ce9 Update .github/workflows/ci-dist-telemetry-scan.yaml
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-06-17 09:48:35 -07:00
Benjamin Lu
4439319655 Update .github/workflows/ci-dist-telemetry-scan.yaml
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-06-17 09:48:09 -07:00
Benjamin Lu
e5fe11788a fix: treat desktop2 templates as local 2026-06-16 17:08:50 -07:00
Benjamin Lu
8b0b985e77 refactor: share distribution type 2026-06-16 16:54:38 -07:00
Benjamin Lu
ed9f7ea428 feat: add desktop2 frontend distribution 2026-06-15 22:12:50 -07:00
Benjamin Lu
a3c4233887 fix: declare bridge types package dependency 2026-06-15 14:24:16 -07:00
Benjamin Lu
73546f02ef Update bridge types package 2026-06-15 14:08:45 -07:00
Benjamin Lu
cd3b322ba5 Use bridge remote mode for Desktop downloads 2026-06-15 13:52:00 -07:00
Benjamin Lu
7d09c17646 Fix workspace YAML comment 2026-06-15 13:08:34 -07:00
Benjamin Lu
8e28087cab Use Comfy Desktop bridge types package 2026-06-15 10:44:39 -07:00
20 changed files with 129 additions and 61 deletions

View File

@@ -109,3 +109,21 @@ jobs:
exit 1
fi
echo '✅ No PostHog references found'
- name: Scan dist for Desktop telemetry references
run: |
set -euo pipefail
echo '🔍 Scanning for Desktop telemetry references...'
if rg --no-ignore -n \
-g '*.html' \
-g '*.js' \
-e 'DesktopTelemetry' \
-e 'initDesktopTelemetry' \
-e '__comfyDesktop2\??\.Telemetry' \
-e 'Telemetry\??\.capture' \
dist; then
echo '❌ ERROR: Desktop telemetry references found in OSS dist assets!'
echo 'Desktop telemetry must only ship in the desktop2 distribution.'
exit 1
fi
echo '✅ No Desktop telemetry references found'

View File

@@ -231,6 +231,7 @@ jobs:
run: |
pnpm install --frozen-lockfile
pnpm build
pnpm build:desktop2
- name: Set up Python
uses: actions/setup-python@v6
@@ -244,7 +245,9 @@ jobs:
run: |
set -euo pipefail
mkdir -p comfyui_frontend_package/comfyui_frontend_package/static/
mkdir -p comfyui_frontend_package/comfyui_frontend_package/static-desktop2/
cp -r dist/* comfyui_frontend_package/comfyui_frontend_package/static/
cp -r dist-desktop2/* comfyui_frontend_package/comfyui_frontend_package/static-desktop2/
- name: Build pypi package
run: python -m build

View File

@@ -38,6 +38,7 @@ jobs:
run: |
pnpm install --frozen-lockfile
pnpm build
pnpm build:desktop2
pnpm zipdist
- name: Upload dist artifact
uses: actions/upload-artifact@v6
@@ -45,6 +46,7 @@ jobs:
name: dist-files
path: |
dist/
dist-desktop2/
dist.zip
publish_pypi:
@@ -66,7 +68,9 @@ jobs:
- name: Setup pypi package
run: |
mkdir -p comfyui_frontend_package/comfyui_frontend_package/static/
mkdir -p comfyui_frontend_package/comfyui_frontend_package/static-desktop2/
cp -r dist/* comfyui_frontend_package/comfyui_frontend_package/static/
cp -r dist-desktop2/* comfyui_frontend_package/comfyui_frontend_package/static-desktop2/
- name: Build pypi package
run: python -m build
working-directory: comfyui_frontend_package

3
.gitignore vendored
View File

@@ -21,6 +21,7 @@ node_modules
.pnpm-store
.nx
dist
dist-desktop2
dist-ssr
*.local
# Claude configuration
@@ -96,4 +97,4 @@ vitest.config.*.timestamp*
# Weekly docs check output
/output.txt
.amp
.amp

View File

@@ -1 +1,2 @@
recursive-include comfyui_frontend_package/static *
recursive-include comfyui_frontend_package/static-desktop2 *

View File

@@ -2,7 +2,7 @@
This is the pypi package structure for the comfyui frontend.
During build process, the compiled assets are copied into the `${PROJECT_ROOT}/comfyui_frontend_package/comfyui_frontend_package/static` directory.
During build process, the compiled OSS assets are copied into the `${PROJECT_ROOT}/comfyui_frontend_package/comfyui_frontend_package/static` directory. Desktop2 assets are copied into `${PROJECT_ROOT}/comfyui_frontend_package/comfyui_frontend_package/static-desktop2`.
The package can be installed with the following command:

View File

@@ -10,11 +10,12 @@
"scripts": {
"build:cloud": "cross-env DISTRIBUTION=cloud vite build --config vite.config.mts",
"build:desktop": "pnpm --filter @comfyorg/desktop-ui run build",
"build:desktop2": "cross-env DISTRIBUTION=desktop2 vite build --config vite.config.mts --outDir dist-desktop2",
"build-storybook": "storybook build",
"build:types": "vite build --config vite.types.config.mts && node scripts/prepare-types.js",
"build:analyze": "cross-env ANALYZE_BUNDLE=true pnpm build",
"build": "pnpm typecheck && vite build --config vite.config.mts",
"clean": "pnpm dlx rimraf dist dist-ssr coverage playwright-report blob-report test-results node_modules/.vite apps/desktop-ui/dist apps/website/dist",
"clean": "pnpm dlx rimraf dist dist-desktop2 dist-ssr coverage playwright-report blob-report test-results node_modules/.vite apps/desktop-ui/dist apps/website/dist",
"clean:all": "pnpm clean && pnpm dlx rimraf node_modules",
"size:collect": "node scripts/size-collect.js",
"size:report": "node scripts/size-report.js",
@@ -59,6 +60,7 @@
"dependencies": {
"@alloc/quick-lru": "catalog:",
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
"@comfyorg/comfyui-desktop-bridge-types": "catalog:",
"@comfyorg/comfyui-electron-types": "catalog:",
"@comfyorg/design-system": "workspace:*",
"@comfyorg/fbx-exporter-three": "^1.0.1",

19
pnpm-lock.yaml generated
View File

@@ -18,6 +18,9 @@ catalogs:
'@astrojs/vue':
specifier: ^6.0.1
version: 6.0.1
'@comfyorg/comfyui-desktop-bridge-types':
specifier: 0.1.2
version: 0.1.2
'@comfyorg/comfyui-electron-types':
specifier: 0.6.2
version: 0.6.2
@@ -426,6 +429,9 @@ importers:
'@atlaskit/pragmatic-drag-and-drop':
specifier: ^1.3.1
version: 1.3.1
'@comfyorg/comfyui-desktop-bridge-types':
specifier: 'catalog:'
version: 0.1.2
'@comfyorg/comfyui-electron-types':
specifier: 'catalog:'
version: 0.6.2
@@ -1383,6 +1389,9 @@ packages:
resolution: {integrity: sha512-wKh+wTjmrUoUdkZg8KpJO5X+p9PWV+KE9mePseq9UYWkukgTKsGS47RRL2HstwVcvDQH+PenrPJWII8+MfiiyA==}
engines: {node: '>= 20.12.0'}
'@comfyorg/comfyui-desktop-bridge-types@0.1.2':
resolution: {integrity: sha512-yU2xerunYGDM+IF8MCrEAQGE2C/9mX8Dec7xAwsMmHUriY1GBhZJO9hjjLmHnSh4O4qTpzfO5AJgo2d0jzdDNA==}
'@comfyorg/comfyui-electron-types@0.6.2':
resolution: {integrity: sha512-r3By5Wbizq8jagUrhtcym79HYUTinsvoBnYkFFWbUmrURBWIaC0HduFVkRkI1PNdI76piW+JSOJJnw00YCVXeg==}
@@ -8640,8 +8649,8 @@ packages:
vue-component-type-helpers@3.3.2:
resolution: {integrity: sha512-l4Z2Y34m7nFMlx8vrslJaVtXxUpzgDMSESC7TakG/c5kwjYT/do+E0NcT2/vWDzaoIhsShg/2OKwX7Q4nbzC0g==}
vue-component-type-helpers@3.3.4:
resolution: {integrity: sha512-joip1uZTaQR0nD23N400gIdJ7xY+WiiiMA/BCKz842gvGBknqDQAzklUvDEhqFvvrhQY8S2ZANBMu4X70VMFGw==}
vue-component-type-helpers@3.3.5:
resolution: {integrity: sha512-Fe1jyPJoUGpJOYKOri44jduR7My4yYINOMJISuMAbmrs+L5LbIDUc8NTWZYY3EJLK0yPLuCmcd5zoCsE4k2/KA==}
vue-demi@0.14.10:
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
@@ -9450,6 +9459,8 @@ snapshots:
fast-wrap-ansi: 0.2.2
sisteransi: 1.0.5
'@comfyorg/comfyui-desktop-bridge-types@0.1.2': {}
'@comfyorg/comfyui-electron-types@0.6.2': {}
'@comfyorg/fbx-exporter-three@1.0.1(@types/three@0.184.1)(three@0.184.0)':
@@ -11323,7 +11334,7 @@ snapshots:
storybook: 10.2.10(@testing-library/dom@10.4.1)(prettier@3.7.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
type-fest: 2.19.0
vue: 3.5.34(typescript@5.9.3)
vue-component-type-helpers: 3.3.4
vue-component-type-helpers: 3.3.5
'@swc/helpers@0.5.21':
dependencies:
@@ -17469,7 +17480,7 @@ snapshots:
vue-component-type-helpers@3.3.2: {}
vue-component-type-helpers@3.3.4: {}
vue-component-type-helpers@3.3.5: {}
vue-demi@0.14.10(vue@3.5.34(typescript@5.9.3)):
dependencies:

View File

@@ -9,11 +9,15 @@ catalogMode: prefer
publicHoistPattern:
- '@parcel/watcher'
minimumReleaseAgeExclude:
- '@comfyorg/comfyui-desktop-bridge-types'
catalog:
'@alloc/quick-lru': ^5.2.0
'@astrojs/check': ^0.9.9
'@astrojs/sitemap': ^3.7.3
'@astrojs/vue': ^6.0.1
'@comfyorg/comfyui-desktop-bridge-types': 0.1.2
'@comfyorg/comfyui-electron-types': 0.6.2
'@eslint/js': ^10.0.1
'@formkit/auto-animate': ^0.9.0
@@ -164,7 +168,7 @@ overrides:
vite: 'catalog:'
'@tiptap/pm': 2.27.2
'@types/eslint': '-'
#Security overrides
# Security overrides
lodash: ^4.18.0
yaml: ^2.8.3
minimatch@^9.0.0: ^9.0.7

View File

@@ -16,7 +16,10 @@ const typesPackage = {
homepage: mainPackage.homepage,
description: `TypeScript definitions for ${mainPackage.name}`,
license: mainPackage.license,
dependencies: {},
dependencies: {
'@comfyorg/comfyui-desktop-bridge-types':
mainPackage.dependencies['@comfyorg/comfyui-desktop-bridge-types']
},
peerDependencies: {
vue: mainPackage.dependencies.vue,
zod: mainPackage.dependencies.zod
@@ -34,5 +37,3 @@ fs.writeFileSync(
path.join(distDir, 'package.json'),
JSON.stringify(typesPackage, null, 2)
)
console.log('Types package.json have been prepared in the dist directory')

View File

@@ -3,6 +3,8 @@
* This file should be imported before any code that uses Vite define variables
*/
import type { Distribution } from '../src/platform/distribution/types'
// Define global constants that Vite would normally replace at build time
declare global {
const __COMFYUI_FRONTEND_VERSION__: string
@@ -11,7 +13,7 @@ declare global {
const __ALGOLIA_APP_ID__: string
const __ALGOLIA_API_KEY__: string
const __USE_PROD_CONFIG__: boolean
const __DISTRIBUTION__: 'desktop' | 'localhost' | 'cloud'
const __DISTRIBUTION__: Distribution
const __IS_NIGHTLY__: boolean
}
@@ -22,7 +24,7 @@ type GlobalWithDefines = typeof globalThis & {
__ALGOLIA_APP_ID__: string
__ALGOLIA_API_KEY__: string
__USE_PROD_CONFIG__: boolean
__DISTRIBUTION__: 'desktop' | 'localhost' | 'cloud'
__DISTRIBUTION__: Distribution
__IS_NIGHTLY__: boolean
window?: Record<string, unknown>
}

View File

@@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { nextTick, ref } from 'vue'
import type { IFuseOptions } from 'fuse.js'
import type { Distribution } from '@/platform/distribution/types'
import type { TemplateInfo } from '@/platform/workflow/templates/types/template'
import { TemplateIncludeOnDistributionEnum } from '@/platform/workflow/templates/types/template'
import { useTemplateFiltering } from '@/composables/useTemplateFiltering'
@@ -436,7 +437,7 @@ describe('useTemplateFiltering', () => {
})
describe('Distribution filtering', () => {
const setDistribution = (distribution: 'desktop' | 'localhost' | 'cloud') =>
const setDistribution = (distribution: Distribution) =>
vi.stubGlobal('__DISTRIBUTION__', distribution)
const cloudTemplate: TemplateInfo = {
@@ -457,6 +458,14 @@ describe('useTemplateFiltering', () => {
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Desktop]
}
const localTemplate: TemplateInfo = {
name: 'local-only',
description: 'Local template',
mediaType: 'image',
mediaSubtype: 'png',
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Local]
}
const universalTemplate: TemplateInfo = {
name: 'universal',
description: 'Universal template',
@@ -540,14 +549,6 @@ describe('useTemplateFiltering', () => {
it('shows local templates on localhost distribution', () => {
setDistribution('localhost')
const localTemplate: TemplateInfo = {
name: 'local-only',
description: 'Local template',
mediaType: 'image',
mediaSubtype: 'png',
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Local]
}
const templates = ref([localTemplate, cloudTemplate, desktopTemplate])
const { filteredTemplates, filteredCount, totalCount } =
@@ -558,6 +559,18 @@ describe('useTemplateFiltering', () => {
expect(filteredTemplates.value[0].name).toBe('local-only')
})
it('shows local templates on desktop2 distribution', () => {
setDistribution('desktop2')
const templates = ref([localTemplate, desktopTemplate, cloudTemplate])
const { filteredTemplates, filteredCount, totalCount } =
useTemplateFiltering(templates)
expect(filteredCount.value).toBe(1)
expect(totalCount.value).toBe(1)
expect(filteredTemplates.value[0].name).toBe('local-only')
})
it('includes templates with multiple distributions when any match', () => {
setDistribution('cloud')
const templates = ref([multiDistTemplate])

View File

@@ -79,9 +79,9 @@ export function useTemplateFiltering(
case 'cloud':
return [TemplateIncludeOnDistributionEnum.Cloud]
case 'localhost':
case 'desktop2':
return [TemplateIncludeOnDistributionEnum.Local]
case 'desktop':
default:
if (systemStatsStore.systemStats?.system.os === 'darwin') {
return [
TemplateIncludeOnDistributionEnum.Desktop,

View File

@@ -1,6 +1,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { effectScope } from 'vue'
import type { Distribution } from '@/platform/distribution/types'
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
import { PENDING_SUBSCRIPTION_CHECKOUT_STORAGE_KEY } from '@/platform/cloud/subscription/utils/subscriptionCheckoutTracker'
@@ -58,7 +59,6 @@ const {
}))
let scope: ReturnType<typeof effectScope> | undefined
type Distribution = 'desktop' | 'localhost' | 'cloud'
const setDistribution = (distribution: Distribution) => {
;(

View File

@@ -1,6 +1,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { computed, reactive } from 'vue'
import type { Distribution } from '@/platform/distribution/types'
import { PENDING_SUBSCRIPTION_CHECKOUT_STORAGE_KEY } from '@/platform/cloud/subscription/utils/subscriptionCheckoutTracker'
import { performSubscriptionCheckout } from './subscriptionCheckoutUtil'
@@ -89,8 +90,6 @@ vi.mock('@/platform/telemetry/utils/checkoutAttribution', () => ({
global.fetch = vi.fn()
type Distribution = 'desktop' | 'localhost' | 'cloud'
const setDistribution = (distribution: Distribution) => {
;(
globalThis as typeof globalThis & { __DISTRIBUTION__: Distribution }

View File

@@ -1,9 +1,9 @@
/**
* Distribution types and compile-time constants for managing
* multi-distribution builds (Desktop, Localhost, Cloud)
* multi-distribution builds (Desktop, Desktop2, Localhost, Cloud)
*/
type Distribution = 'desktop' | 'localhost' | 'cloud'
export type Distribution = 'desktop' | 'desktop2' | 'localhost' | 'cloud'
declare global {
const __DISTRIBUTION__: Distribution

View File

@@ -39,7 +39,6 @@ beforeEach(() => {
vi.restoreAllMocks()
vi.resetAllMocks()
delete window.__comfyDesktop2
delete window.__comfyDesktop2Remote
})
describe('fetchModelMetadata', () => {
@@ -258,7 +257,10 @@ describe('downloadModel', () => {
(url: string, filename: string, directory: string) => Promise<boolean>
>()
.mockResolvedValue(true)
window.__comfyDesktop2 = { downloadModel: desktopDownloadModel }
window.__comfyDesktop2 = {
isRemote: () => false,
downloadModel: desktopDownloadModel
}
downloadModel(
{
@@ -289,7 +291,10 @@ describe('downloadModel', () => {
(url: string, filename: string, directory: string) => Promise<boolean>
>()
.mockRejectedValue(bridgeError)
window.__comfyDesktop2 = { downloadModel: desktopDownloadModel }
window.__comfyDesktop2 = {
isRemote: () => false,
downloadModel: desktopDownloadModel
}
downloadModel(
{
@@ -323,7 +328,10 @@ describe('downloadModel', () => {
.mockImplementation(() => {
throw bridgeError
})
window.__comfyDesktop2 = { downloadModel: desktopDownloadModel }
window.__comfyDesktop2 = {
isRemote: () => false,
downloadModel: desktopDownloadModel
}
downloadModel(
{
@@ -353,8 +361,10 @@ describe('downloadModel', () => {
(url: string, filename: string, directory: string) => Promise<boolean>
>()
.mockResolvedValue(true)
window.__comfyDesktop2 = { downloadModel: desktopDownloadModel }
window.__comfyDesktop2Remote = true
window.__comfyDesktop2 = {
isRemote: () => true,
downloadModel: desktopDownloadModel
}
downloadModel(
{

View File

@@ -2,21 +2,7 @@ import { downloadUrlToHfRepoUrl, isCivitaiModelUrl } from '@/utils/formatUtil'
import { isDesktop } from '@/platform/distribution/types'
import { useElectronDownloadStore } from '@/stores/electronDownloadStore'
import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore'
interface ComfyDesktop2Bridge {
downloadModel: (
url: string,
filename: string,
directory: string
) => Promise<boolean>
}
declare global {
interface Window {
__comfyDesktop2?: ComfyDesktop2Bridge
__comfyDesktop2Remote?: boolean
}
}
import type { ComfyDesktop2Bridge } from '@/types'
const ALLOWED_SOURCES = [
'https://civitai.com/',
@@ -51,11 +37,11 @@ export interface ModelWithUrl {
}
async function startDesktop2ModelDownload(
bridge: ComfyDesktop2Bridge,
downloadModel: NonNullable<ComfyDesktop2Bridge['downloadModel']>,
model: ModelWithUrl
): Promise<void> {
try {
await bridge.downloadModel(model.url, model.name, model.directory)
await downloadModel(model.url, model.name, model.directory)
} catch (error: unknown) {
console.error('Failed to start Desktop2 model download:', error)
}
@@ -90,8 +76,9 @@ export function downloadModel(
paths: Record<string, string[]>
): void {
const desktop2Bridge = window.__comfyDesktop2
if (desktop2Bridge?.downloadModel && !window.__comfyDesktop2Remote) {
void startDesktop2ModelDownload(desktop2Bridge, model)
const desktop2DownloadModel = desktop2Bridge?.downloadModel
if (desktop2Bridge && desktop2DownloadModel && !desktop2Bridge.isRemote()) {
void startDesktop2ModelDownload(desktop2DownloadModel, model)
return
}

View File

@@ -1,3 +1,4 @@
import type { ComfyDesktop2Bridge } from '@comfyorg/comfyui-desktop-bridge-types'
import type {
DeviceStats,
EmbeddingsResponse,
@@ -25,6 +26,7 @@ import type {
} from './extensionTypes'
export type { ComfyExtension } from './comfy'
export type { ComfyDesktop2Bridge } from '@comfyorg/comfyui-desktop-bridge-types'
export type { ComfyApi } from '@/scripts/api'
export type { ComfyApp } from '@/scripts/app'
export type { ComfyNodeDef } from '@/schemas/nodeDefSchema'
@@ -88,5 +90,7 @@ declare global {
/** For use in tests to track app initialization state */
__appReadiness?: AppReadiness
__comfyDesktop2?: ComfyDesktop2Bridge
}
}

View File

@@ -18,6 +18,7 @@ import { createHtmlPlugin } from 'vite-plugin-html'
import vueDevTools from 'vite-plugin-vue-devtools'
import { comfyAPIPlugin } from './build/plugins'
import type { Distribution } from './src/platform/distribution/types'
dotenvConfig()
@@ -43,14 +44,21 @@ const VITE_OG_KEYWORDS = 'ComfyUI, Comfy Cloud, ComfyUI online'
const DEV_SERVER_COMFYUI_ENV_URL = process.env.DEV_SERVER_COMFYUI_URL
const IS_CLOUD_URL = DEV_SERVER_COMFYUI_ENV_URL?.includes('.comfy.org')
const DISTRIBUTION: 'desktop' | 'localhost' | 'cloud' =
process.env.DISTRIBUTION === 'desktop' ||
process.env.DISTRIBUTION === 'localhost' ||
process.env.DISTRIBUTION === 'cloud'
? process.env.DISTRIBUTION
: IS_CLOUD_URL
? 'cloud'
: 'localhost'
function parseDistribution(
value: string | undefined
): Distribution | undefined {
switch (value) {
case 'desktop':
case 'desktop2':
case 'localhost':
case 'cloud':
return value
}
}
const DISTRIBUTION: Distribution =
parseDistribution(process.env.DISTRIBUTION) ??
(IS_CLOUD_URL ? 'cloud' : 'localhost')
// Nightly builds are from main branch; RC/stable builds are from core/* branches
// Can be overridden via IS_NIGHTLY env var for testing