Compare commits

...

6 Commits

Author SHA1 Message Date
huang47
1b0d458f36 fix: update desktop maintenance test mocks 2026-07-08 16:39:54 -07:00
huang47
96ec732104 fix: update desktop terminal test mocks 2026-07-08 16:39:54 -07:00
huang47
134e27cca1 fix: update desktop ambient typings 2026-07-08 16:39:53 -07:00
huang47
f1761a41fe fix: type shared PII event fixtures 2026-07-08 16:39:53 -07:00
huang47
68c2c9f0f5 fix: bridge TypeGPU device typing 2026-07-08 16:39:53 -07:00
huang47
fc3a945003 chore: add TypeScript 7 compiler alias 2026-07-08 16:39:53 -07:00
18 changed files with 664 additions and 362 deletions

View File

@@ -77,7 +77,6 @@ const preview: Preview = {
{ value: 'light', icon: 'sun', title: 'Light' },
{ value: 'dark', icon: 'moon', title: 'Dark' }
],
showName: true,
dynamicTitle: true
}
}

View File

@@ -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
})
return { mockTerminal, MockTerminal, mockFitAddon, 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
})
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 }))

View File

@@ -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 }
})
@@ -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'
@@ -33,7 +32,7 @@ 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')
})
@@ -41,7 +40,7 @@ describe('useTerminalBuffer', () => {
mockSerialize.mockReturnValue('')
const { copyTo } = withSetup(() => useTerminalBuffer())
const mockWrite = vi.fn()
copyTo({ write: mockWrite } as Pick<Terminal, 'write'>)
copyTo({ write: mockWrite })
expect(mockWrite).toHaveBeenCalledWith('')
})
})

View File

@@ -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())
}

View File

@@ -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
)
})

View File

@@ -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)
}
}
}))
@@ -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'

View File

@@ -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>>()
}
}
}))

View File

@@ -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() {

View File

@@ -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({
@@ -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')
@@ -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: () =>
@@ -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 {
@@ -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 {
@@ -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,
@@ -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,

View File

@@ -6,7 +6,14 @@
"paths": {
"@/*": ["./src/*"],
"@frontend-locales/*": ["../../src/locales/*"]
}
},
"types": [
"vite/client",
"node",
"vitest/globals",
"@webgpu/types",
"@testing-library/jest-dom/vitest"
]
},
"include": [
".storybook/**/*",

View File

@@ -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:",
@@ -19,6 +19,7 @@
},
"devDependencies": {
"tailwindcss": "catalog:",
"typescript": "catalog:"
"typescript": "catalog:",
"typescript-7": "catalog:"
}
}

View File

@@ -11,7 +11,7 @@
},
"scripts": {
"test": "vitest run --config ./vitest.config.ts",
"typecheck": "tsc --noEmit"
"typecheck": "node ./node_modules/typescript-7/bin/tsc --noEmit"
},
"dependencies": {
"zod": "catalog:",
@@ -19,6 +19,7 @@
},
"devDependencies": {
"typescript": "catalog:",
"typescript-7": "catalog:",
"vitest": "catalog:"
}
}

View File

@@ -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:"
}
}

View File

@@ -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',
@@ -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)

View File

@@ -9,13 +9,14 @@
".": "./src/index.ts"
},
"scripts": {
"typecheck": "tsc --noEmit"
"typecheck": "node ./node_modules/typescript-7/bin/tsc --noEmit"
},
"dependencies": {
"clsx": "^2.1.1",
"tailwind-merge": "^2.2.0"
},
"devDependencies": {
"typescript": "catalog:"
"typescript": "catalog:",
"typescript-7": "catalog:"
}
}

818
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -125,7 +125,8 @@ catalog:
tsx: ^4.15.6
tw-animate-css: ^1.3.8
typegpu: ^0.8.2
typescript: ^5.9.3
typescript: ^6.0.3
typescript-7: npm:typescript@^7.0.2
typescript-eslint: ^8.60.0
unplugin-icons: ^22.5.0
unplugin-typegpu: 0.8.0
@@ -174,3 +175,26 @@ overrides:
minimatch@^9.0.0: ^9.0.7
minimatch@^10.0.0: ^10.2.3
ajv@^8.0.0: ^8.18.0
minimumReleaseAgeExclude:
- '@typescript/typescript-aix-ppc64@7.0.2'
- '@typescript/typescript-darwin-arm64@7.0.2'
- '@typescript/typescript-darwin-x64@7.0.2'
- '@typescript/typescript-freebsd-arm64@7.0.2'
- '@typescript/typescript-freebsd-x64@7.0.2'
- '@typescript/typescript-linux-arm64@7.0.2'
- '@typescript/typescript-linux-arm@7.0.2'
- '@typescript/typescript-linux-loong64@7.0.2'
- '@typescript/typescript-linux-mips64el@7.0.2'
- '@typescript/typescript-linux-ppc64@7.0.2'
- '@typescript/typescript-linux-riscv64@7.0.2'
- '@typescript/typescript-linux-s390x@7.0.2'
- '@typescript/typescript-linux-x64@7.0.2'
- '@typescript/typescript-netbsd-arm64@7.0.2'
- '@typescript/typescript-netbsd-x64@7.0.2'
- '@typescript/typescript-openbsd-arm64@7.0.2'
- '@typescript/typescript-openbsd-x64@7.0.2'
- '@typescript/typescript-sunos-x64@7.0.2'
- '@typescript/typescript-win32-arm64@7.0.2'
- '@typescript/typescript-win32-x64@7.0.2'
- typescript@7.0.2

View File

@@ -207,7 +207,8 @@ export function useGPUResources() {
async function initTypeGPU(): Promise<void> {
if (store.tgpuRoot) {
/* c8 ignore start */
device = store.tgpuRoot.device
// typegpu's GPUDevice type can lag @webgpu/types; the runtime object is still a browser GPUDevice.
device = store.tgpuRoot.device as GPUDevice
return
/* c8 ignore stop */
}
@@ -215,7 +216,7 @@ export function useGPUResources() {
/* c8 ignore start — requires functional WebGPU hardware */
const root = await tgpu.init()
store.tgpuRoot = root
device = root.device
device = root.device as GPUDevice
console.warn('✅ TypeGPU initialized! Root:', root)
console.warn('Device info:', root.device.limits)
/* c8 ignore stop */