Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/desktop-ui/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ const preview: Preview = {
{ value: 'light', icon: 'sun', title: 'Light' },
{ value: 'dark', icon: 'moon', title: 'Dark' }
],
showName: true,
dynamicTitle: true
}
}
Expand Down
46 changes: 22 additions & 24 deletions apps/desktop-ui/src/composables/bottomPanelTabs/useTerminal.test.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { ref } from 'vue'

const { mockTerminal, MockTerminal, mockFitAddon, MockFitAddon } = vi.hoisted(
() => {
const mockTerminal = {
loadAddon: vi.fn(),
attachCustomKeyEventHandler: vi.fn(),
open: vi.fn(),
dispose: vi.fn(),
hasSelection: vi.fn<[], boolean>(),
resize: vi.fn(),
cols: 80,
rows: 24
}
const MockTerminal = vi.fn(function () {
return mockTerminal
})

const mockFitAddon = {
proposeDimensions: vi.fn().mockReturnValue({ cols: 80, rows: 24 })
}
const MockFitAddon = vi.fn(function () {
return mockFitAddon
})
const { mockTerminal, MockTerminal, MockFitAddon } = vi.hoisted(() => {
const mockTerminal = {
loadAddon: vi.fn(),
attachCustomKeyEventHandler: vi.fn(),
open: vi.fn(),
dispose: vi.fn(),
hasSelection: vi.fn<() => boolean>(),
resize: vi.fn(),
cols: 80,
rows: 24
}
const MockTerminal = vi.fn(function () {
return mockTerminal
})

return { mockTerminal, MockTerminal, mockFitAddon, MockFitAddon }
const mockFitAddon = {
proposeDimensions: vi.fn().mockReturnValue({ cols: 80, rows: 24 })
}
)
const MockFitAddon = vi.fn(function () {
return mockFitAddon
})

return { mockTerminal, MockTerminal, MockFitAddon }
})

vi.mock('@xterm/xterm', () => ({ Terminal: MockTerminal }))
vi.mock('@xterm/addon-fit', () => ({ FitAddon: MockFitAddon }))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'

const { mockSerialize, MockSerializeAddon } = vi.hoisted(() => {
const mockSerialize = vi.fn<[], string>()
const mockSerialize = vi.fn<() => string>()
const MockSerializeAddon = vi.fn(function () {
return { serialize: mockSerialize }
})
Expand All @@ -18,7 +18,6 @@ vi.mock('@xterm/addon-serialize', () => ({
SerializeAddon: MockSerializeAddon
}))

import type { Terminal } from '@xterm/xterm'
import { withSetup } from '@/test/withSetup'
import { useTerminalBuffer } from '@/composables/bottomPanelTabs/useTerminalBuffer'

Expand All @@ -33,15 +32,15 @@ describe('useTerminalBuffer', () => {
mockSerialize.mockReturnValue('hello world')
const { copyTo } = withSetup(() => useTerminalBuffer())
const mockWrite = vi.fn()
copyTo({ write: mockWrite } as Pick<Terminal, 'write'>)
copyTo({ write: mockWrite })
expect(mockWrite).toHaveBeenCalledWith('hello world')
})

it('writes empty string when buffer is empty', () => {
mockSerialize.mockReturnValue('')
const { copyTo } = withSetup(() => useTerminalBuffer())
const mockWrite = vi.fn()
copyTo({ write: mockWrite } as Pick<Terminal, 'write'>)
copyTo({ write: mockWrite })
expect(mockWrite).toHaveBeenCalledWith('')
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function useTerminalBuffer() {
const serializeAddon = new SerializeAddon()
const terminal = markRaw(new Terminal({ convertEol: true }))

const copyTo = (destinationTerminal: Terminal) => {
const copyTo = (destinationTerminal: Pick<Terminal, 'write'>) => {
destinationTerminal.write(serializeAddon.serialize())
}

Expand Down
12 changes: 8 additions & 4 deletions apps/desktop-ui/src/constants/desktopDialogs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ describe('getDialog', () => {

it('returns a deep clone — mutations do not affect the original', () => {
const result = getDialog('reinstallVenv')
const originalFirstLabel = DESKTOP_DIALOGS.reinstallVenv.buttons[0].label
result.buttons[0].label = 'Mutated'
expect(DESKTOP_DIALOGS.reinstallVenv.buttons[0].label).toBe(
originalFirstLabel
const originalButtonCount = DESKTOP_DIALOGS.reinstallVenv.buttons.length
result.buttons.push({
label: 'Mutated',
action: 'cancel',
returnValue: 'mutated'
})
expect(DESKTOP_DIALOGS.reinstallVenv.buttons).toHaveLength(
originalButtonCount
)
})

Expand Down
20 changes: 10 additions & 10 deletions apps/desktop-ui/src/constants/desktopMaintenanceTasks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
const { mockElectron } = vi.hoisted(() => ({
mockElectron: {
setBasePath: vi.fn(),
reinstall: vi.fn<[], Promise<void>>().mockResolvedValue(undefined),
reinstall: vi.fn<() => Promise<void>>().mockResolvedValue(undefined),
uv: {
installRequirements: vi.fn<[], Promise<void>>(),
clearCache: vi.fn<[], Promise<void>>().mockResolvedValue(undefined),
resetVenv: vi.fn<[], Promise<void>>().mockResolvedValue(undefined)
installRequirements: vi.fn<() => Promise<void>>(),
clearCache: vi.fn<() => Promise<void>>().mockResolvedValue(undefined),
resetVenv: vi.fn<() => Promise<void>>().mockResolvedValue(undefined)
}
}
}))
Expand Down Expand Up @@ -48,24 +48,24 @@ describe('desktopMaintenanceTasks', () => {
})

describe('URL-opening tasks', () => {
it('git execute opens the git download page', () => {
findTask('git').execute()
it('git execute opens the git download page', async () => {
expect(await findTask('git').execute()).toBe(true)
expect(window.open).toHaveBeenCalledWith(
'https://git-scm.com/downloads/',
'_blank'
)
})

it('uv execute opens the uv installation page', () => {
findTask('uv').execute()
it('uv execute opens the uv installation page', async () => {
expect(await findTask('uv').execute()).toBe(true)
expect(window.open).toHaveBeenCalledWith(
'https://docs.astral.sh/uv/getting-started/installation/',
'_blank'
)
})

it('vcRedist execute opens the VC++ redistributable download', () => {
findTask('vcRedist').execute()
it('vcRedist execute opens the VC++ redistributable download', async () => {
expect(await findTask('vcRedist').execute()).toBe(true)
expect(window.open).toHaveBeenCalledWith(
'https://aka.ms/vs/17/release/vc_redist.x64.exe',
'_blank'
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop-ui/src/utils/electronMirrorCheck.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
const { mockElectron } = vi.hoisted(() => ({
mockElectron: {
NetWork: {
canAccessUrl: vi.fn<[url: string], Promise<boolean>>()
canAccessUrl: vi.fn<(url: string) => Promise<boolean>>()
}
}
}))
Expand Down
6 changes: 5 additions & 1 deletion apps/desktop-ui/src/utils/envUtil.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type { ElectronAPI } from '@comfyorg/comfyui-electron-types'

type ElectronWindow = typeof window & {
electronAPI: ElectronAPI
}

export function isElectron() {
return 'electronAPI' in window && window.electronAPI !== undefined
}

export function electronAPI() {
return (window as any).electronAPI as ElectronAPI
return (window as ElectronWindow).electronAPI
}

export function isNativeWindow() {
Expand Down
45 changes: 37 additions & 8 deletions apps/desktop-ui/src/views/InstallView.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,38 @@
import type { Meta, StoryObj } from '@storybook/vue3'
import type { ElectronAPI } from '@comfyorg/comfyui-electron-types'
import { nextTick, provide } from 'vue'
import type { ElectronWindow } from '@/utils/envUtil'
import { createMemoryHistory, createRouter } from 'vue-router'

import InstallView from './InstallView.vue'

type InstallViewElectronAPI = {
getPlatform: ElectronAPI['getPlatform']
Config: {
getDetectedGpu: ElectronAPI['Config']['getDetectedGpu']
}
Events: {
trackEvent: ElectronAPI['Events']['trackEvent']
}
installComfyUI: ElectronAPI['installComfyUI']
changeTheme: ElectronAPI['changeTheme']
getSystemPaths: ElectronAPI['getSystemPaths']
validateInstallPath: ElectronAPI['validateInstallPath']
validateComfyUISource: ElectronAPI['validateComfyUISource']
showDirectoryPicker: ElectronAPI['showDirectoryPicker']
}

type InstallViewElectronWindow = Window & {
electronAPI?: InstallViewElectronAPI
}

const installViewElectronAPI = () => {
const api = (window as InstallViewElectronWindow).electronAPI
if (!api) {
throw new Error('InstallView story electronAPI was not initialized')
}
return api
}

// Create a mock router for stories
const createMockRouter = () =>
createRouter({
Expand Down Expand Up @@ -44,7 +71,7 @@ const meta: Meta<typeof InstallView> = {
const router = createMockRouter()

// Mock electron API
;(window as ElectronWindow).electronAPI = {
;(window as InstallViewElectronWindow).electronAPI = {
getPlatform: () => 'darwin',
Config: {
getDetectedGpu: () => Promise.resolve('mps')
Expand All @@ -61,6 +88,8 @@ const meta: Meta<typeof InstallView> = {
changeTheme: (_theme: Parameters<ElectronAPI['changeTheme']>[0]) => {},
getSystemPaths: () =>
Promise.resolve({
appData: '/Users/username/Library/Application Support/ComfyUI',
appPath: '/Applications/ComfyUI.app',
defaultInstallPath: '/Users/username/ComfyUI'
}),
validateInstallPath: () =>
Expand Down Expand Up @@ -247,8 +276,8 @@ export const DesktopSettings: Story = {
export const WindowsPlatform: Story = {
render: () => {
// Override the platform to Windows
;(window as ElectronWindow).electronAPI.getPlatform = () => 'win32'
;(window as ElectronWindow).electronAPI.Config.getDetectedGpu = () =>
installViewElectronAPI().getPlatform = () => 'win32'
installViewElectronAPI().Config.getDetectedGpu = () =>
Promise.resolve('nvidia')

return {
Expand All @@ -266,8 +295,8 @@ export const MacOSPlatform: Story = {
name: 'macOS Platform',
render: () => {
// Override the platform to macOS
;(window as ElectronWindow).electronAPI.getPlatform = () => 'darwin'
;(window as ElectronWindow).electronAPI.Config.getDetectedGpu = () =>
installViewElectronAPI().getPlatform = () => 'darwin'
installViewElectronAPI().Config.getDetectedGpu = () =>
Promise.resolve('mps')

return {
Expand Down Expand Up @@ -334,7 +363,7 @@ export const ManualInstall: Story = {
export const ErrorState: Story = {
render: () => {
// Override validation to return an error
;(window as ElectronWindow).electronAPI.validateInstallPath = () =>
installViewElectronAPI().validateInstallPath = () =>
Promise.resolve({
isValid: false,
exists: false,
Expand Down Expand Up @@ -382,7 +411,7 @@ export const ErrorState: Story = {
export const WarningState: Story = {
render: () => {
// Override validation to return a warning about non-default drive
;(window as ElectronWindow).electronAPI.validateInstallPath = () =>
installViewElectronAPI().validateInstallPath = () =>
Promise.resolve({
isValid: true,
exists: false,
Expand Down
9 changes: 8 additions & 1 deletion apps/desktop-ui/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
"paths": {
"@/*": ["./src/*"],
"@frontend-locales/*": ["../../src/locales/*"]
}
},
"types": [
"vite/client",
"node",
"vitest/globals",
"@webgpu/types",
"@testing-library/jest-dom/vitest"
]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
},
"include": [
".storybook/**/*",
Expand Down
5 changes: 3 additions & 2 deletions packages/design-system/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"./css/*": "./src/css/*"
},
"scripts": {
"typecheck": "tsc --noEmit"
"typecheck": "node ./node_modules/typescript-7/bin/tsc --noEmit"
},
"dependencies": {
"@iconify-json/lucide": "catalog:",
Expand All @@ -19,6 +19,7 @@
},
"devDependencies": {
"tailwindcss": "catalog:",
"typescript": "catalog:"
"typescript": "catalog:",
"typescript-7": "catalog:"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
3 changes: 2 additions & 1 deletion packages/object-info-parser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
},
"scripts": {
"test": "vitest run --config ./vitest.config.ts",
"typecheck": "tsc --noEmit"
"typecheck": "node ./node_modules/typescript-7/bin/tsc --noEmit"
},
"dependencies": {
"zod": "catalog:",
"zod-validation-error": "catalog:"
},
"devDependencies": {
"typescript": "catalog:",
"typescript-7": "catalog:",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check what binaries each typescript package exposes in the lockfile
echo "== typescript and typescript-7 in lockfile =="
rg -n -A10 'typescript-7:' pnpm-lock.yaml | head -30
echo "---"
rg -n -A10 "^\s+typescript:" pnpm-lock.yaml | head -30

# Check if TS 6.x provides a differently-named bin (e.g. tsc6) or standard tsc
echo
echo "== Check bin field in installed typescript packages =="
for pkg in typescript typescript-7; do
  if [ -f "node_modules/$pkg/package.json" ]; then
    echo "--- $pkg bin ---"
    jq '.bin' "node_modules/$pkg/package.json" 2>/dev/null || echo "no package.json"
  fi
done

# Check .bin directory for tsc symlinks
echo
echo "== .bin/tsc resolution =="
ls -la node_modules/.bin/tsc* 2>/dev/null || echo "no .bin/tsc found"
readlink -f node_modules/.bin/tsc 2>/dev/null || true

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant package manifests =="
for f in \
  packages/object-info-parser/package.json \
  packages/design-system/package.json \
  packages/shared-frontend-utils/package.json \
  packages/tailwind-utils/package.json \
  package.json
do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    cat -n "$f" | sed -n '1,220p'
    echo
  fi
done

echo "== workspace references to tsc/typecheck =="
rg -n '"typecheck"|\\btsc --noEmit\\b|typescript-7|typescript": "catalog:|typescript-7": "catalog:' packages package.json pnpm-workspace.yaml pnpm-lock.yaml -g '!**/dist/**' -g '!**/build/**'

echo
echo "== installed package bin metadata, if present =="
for pkg in typescript typescript-7; do
  if [ -f "node_modules/$pkg/package.json" ]; then
    echo "--- node_modules/$pkg/package.json ---"
    jq '{name, version, bin}' "node_modules/$pkg/package.json"
  fi
done

echo
echo "== .bin entries related to tsc, if present =="
ls -la node_modules/.bin/tsc* 2>/dev/null || true
readlink -f node_modules/.bin/tsc 2>/dev/null || true

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 15684


🌐 Web query:

pnpm alias package binary conflict two dependencies expose same bin name .bin resolution order tsc typescript alias

💡 Result:

When two dependencies expose a binary with the same name, pnpm may encounter a conflict in the node_modules/.bin directory [1][2]. Because pnpm does not currently enforce strict conflict prevention for non-global installs, one of the binaries will generally overwrite or take precedence over the other, often depending on the order of operations or package name [1][2]. For scenarios involving TypeScript (tsc) or other tools where bin conflicts arise (e.g., when using multiple versions of a package via aliases), you can manage these conflicts using the following strategies: 1. Use pnpm exec: Instead of relying on the binaries linked in node_modules/.bin, use pnpm exec . This command automatically resolves the binary to the correct package based on your project's dependency graph, effectively bypassing path-based resolution conflicts [3]. 2. Avoid reliance on.bin resolution: If you are encountering issues where tools like tsc call the wrong binary, ensure your scripts are explicitly running via pnpm run or pnpm exec, which set up the environment to correctly prioritize the locally installed versions of your direct dependencies. 3. Monitor Issue Tracker: pnpm development is ongoing regarding bin conflict resolution, with efforts to improve consistency, particularly by applying bin ownership overrides [4]. If you are using package aliases (e.g., installing a package under a different name to avoid version conflicts), note that pnpm treats these as separate packages [5]. If those separate packages still expose the same bin name, the conflict will persist because the binary filename remains the same [1]. There is currently no native feature to automatically rename binaries via package aliases in the dependencies section of package.json [1]. You may need to use tools or scripts to manually manage or symlink the desired binary if pnpm's default conflict resolution does not select the one you require [2]. Top results: [1][4][2][3][5]

Citations:


Invoke TypeScript 7 explicitly in these typecheck scripts. tsc --noEmit is ambiguous when both typescript and typescript-7 expose tsc, so these packages can still run against 6.x instead of 7.x: packages/object-info-parser/package.json, packages/design-system/package.json, packages/shared-frontend-utils/package.json, and packages/tailwind-utils/package.json.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/object-info-parser/package.json` at line 22, The typecheck scripts
are still invoking the generic tsc binary, which can resolve to TypeScript 6.x
instead of the intended 7.x when both toolchains are present. Update the
typecheck commands in the affected package manifests to explicitly use the
TypeScript 7 entrypoint, and keep the existing typescript-7 dependency wiring in
place so the scripts in packages/object-info-parser, packages/design-system,
packages/shared-frontend-utils, and packages/tailwind-utils consistently run
against 7.x.

"vitest": "catalog:"
}
}
5 changes: 3 additions & 2 deletions packages/shared-frontend-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
"./piiUtil": "./src/piiUtil.ts"
},
"scripts": {
"typecheck": "tsc --noEmit"
"typecheck": "node ./node_modules/typescript-7/bin/tsc --noEmit"
},
"dependencies": {
"axios": "catalog:",
"dompurify": "catalog:"
},
"devDependencies": {
"typescript": "catalog:"
"typescript": "catalog:",
"typescript-7": "catalog:"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
7 changes: 5 additions & 2 deletions packages/shared-frontend-utils/src/piiUtil.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { createPostHogBeforeSend } from './piiUtil'

describe('createPostHogBeforeSend', () => {
const beforeSend = createPostHogBeforeSend()
type BeforeSendEvent = NonNullable<Parameters<typeof beforeSend>[0]>

it('returns null for null input', () => {
expect(beforeSend(null)).toBeNull()
})

it('strips all PII keys from properties, $set, and $set_once', () => {
const event = {
const event: BeforeSendEvent = {
properties: {
email: 'a@example.com',
prompt: 'hello',
Expand Down Expand Up @@ -48,7 +49,9 @@ describe('createPostHogBeforeSend', () => {
})

it('handles missing property bags gracefully', () => {
const event = { properties: { email: 'a@example.com', safe: true } }
const event: BeforeSendEvent = {
properties: { email: 'a@example.com', safe: true }
}
const result = beforeSend(event)!
expect(result.properties).not.toHaveProperty('email')
expect(result.properties).toHaveProperty('safe', true)
Expand Down
Loading
Loading