diff --git a/apps/desktop-ui/src/views/InstallView.stories.ts b/apps/desktop-ui/src/views/InstallView.stories.ts index d66003831..d7247161b 100644 --- a/apps/desktop-ui/src/views/InstallView.stories.ts +++ b/apps/desktop-ui/src/views/InstallView.stories.ts @@ -1,6 +1,8 @@ // eslint-disable-next-line storybook/no-renderer-packages 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' @@ -42,16 +44,21 @@ const meta: Meta = { const router = createMockRouter() // Mock electron API - ;(window as any).electronAPI = { + ;(window as ElectronWindow).electronAPI = { getPlatform: () => 'darwin', Config: { getDetectedGpu: () => Promise.resolve('mps') }, Events: { - trackEvent: (_eventName: string, _data?: any) => {} + trackEvent: ( + _eventName: string, + _data?: Record + ) => {} }, - installComfyUI: (_options: any) => {}, - changeTheme: (_theme: any) => {}, + installComfyUI: ( + _options: Parameters[0] + ) => {}, + changeTheme: (_theme: Parameters[0]) => {}, getSystemPaths: () => Promise.resolve({ defaultInstallPath: '/Users/username/ComfyUI' @@ -240,8 +247,8 @@ export const DesktopSettings: Story = { export const WindowsPlatform: Story = { render: () => { // Override the platform to Windows - ;(window as any).electronAPI.getPlatform = () => 'win32' - ;(window as any).electronAPI.Config.getDetectedGpu = () => + ;(window as ElectronWindow).electronAPI.getPlatform = () => 'win32' + ;(window as ElectronWindow).electronAPI.Config.getDetectedGpu = () => Promise.resolve('nvidia') return { @@ -259,8 +266,8 @@ export const MacOSPlatform: Story = { name: 'macOS Platform', render: () => { // Override the platform to macOS - ;(window as any).electronAPI.getPlatform = () => 'darwin' - ;(window as any).electronAPI.Config.getDetectedGpu = () => + ;(window as ElectronWindow).electronAPI.getPlatform = () => 'darwin' + ;(window as ElectronWindow).electronAPI.Config.getDetectedGpu = () => Promise.resolve('mps') return { @@ -327,7 +334,7 @@ export const ManualInstall: Story = { export const ErrorState: Story = { render: () => { // Override validation to return an error - ;(window as any).electronAPI.validateInstallPath = () => + ;(window as ElectronWindow).electronAPI.validateInstallPath = () => Promise.resolve({ isValid: false, exists: false, @@ -375,7 +382,7 @@ export const ErrorState: Story = { export const WarningState: Story = { render: () => { // Override validation to return a warning about non-default drive - ;(window as any).electronAPI.validateInstallPath = () => + ;(window as ElectronWindow).electronAPI.validateInstallPath = () => Promise.resolve({ isValid: true, exists: false, diff --git a/browser_tests/fixtures/ws.ts b/browser_tests/fixtures/ws.ts index 02e68bdb7..4fa701918 100644 --- a/browser_tests/fixtures/ws.ts +++ b/browser_tests/fixtures/ws.ts @@ -1,5 +1,9 @@ import { test as base } from '@playwright/test' +interface TestWindow extends Window { + __ws__?: Record +} + export const webSocketFixture = base.extend<{ ws: { trigger(data: unknown, url?: string): Promise } }>({ diff --git a/scripts/check-unused-i18n-keys.ts b/scripts/check-unused-i18n-keys.ts index f459b8c23..5998369d7 100755 --- a/scripts/check-unused-i18n-keys.ts +++ b/scripts/check-unused-i18n-keys.ts @@ -2,10 +2,7 @@ import { execSync } from 'child_process' import * as fs from 'fs' import { globSync } from 'glob' - -interface LocaleData { - [key: string]: any -} +import type { LocaleData } from './i18n-types' // Configuration const SOURCE_PATTERNS = ['src/**/*.{js,ts,vue}', '!src/locales/**/*'] @@ -45,7 +42,7 @@ function getStagedLocaleFiles(): string[] { } // Extract all keys from a nested object -function extractKeys(obj: any, prefix = ''): string[] { +function extractKeys(obj: LocaleData, prefix = ''): string[] { const keys: string[] = [] for (const [key, value] of Object.entries(obj)) { @@ -166,17 +163,17 @@ async function checkNewUnusedKeys() { // Report results if (unusedNewKeys.length > 0) { - console.log('\n⚠️ Warning: Found unused NEW i18n keys:\n') + console.warn('\n⚠️ Warning: Found unused NEW i18n keys:\n') for (const key of unusedNewKeys.sort()) { - console.log(` - ${key}`) + console.warn(` - ${key}`) } - console.log(`\n✨ Total unused new keys: ${unusedNewKeys.length}`) - console.log( + console.warn(`\n✨ Total unused new keys: ${unusedNewKeys.length}`) + console.warn( '\nThese keys were added but are not used anywhere in the codebase.' ) - console.log('Consider using them or removing them in a future update.') + console.warn('Consider using them or removing them in a future update.') // Changed from process.exit(1) to process.exit(0) for warning only process.exit(0) diff --git a/scripts/diff-i18n.ts b/scripts/diff-i18n.ts index 7b4ff8da1..cf71177aa 100644 --- a/scripts/diff-i18n.ts +++ b/scripts/diff-i18n.ts @@ -7,6 +7,7 @@ import { writeFileSync } from 'fs' import { dirname, join } from 'path' +import type { LocaleData } from './i18n-types' // Ensure directories exist function ensureDir(dir: string) { @@ -41,8 +42,8 @@ function getAllJsonFiles(dir: string): string[] { } // Find additions in new object compared to base -function findAdditions(base: any, updated: any): Record { - const additions: Record = {} +function findAdditions(base: LocaleData, updated: LocaleData): LocaleData { + const additions: LocaleData = {} for (const key in updated) { if (!(key in base)) { @@ -74,7 +75,7 @@ function capture(srcLocaleDir: string, tempBaseDir: string) { ensureDir(dirname(targetPath)) writeFileSync(targetPath, readFileSync(file, 'utf8')) } - console.log('Captured current locale files to temp/base/') + console.warn('Captured current locale files to temp/base/') } // Diff command @@ -94,7 +95,7 @@ function diff(srcLocaleDir: string, tempBaseDir: string, tempDiffDir: string) { if (Object.keys(additions).length > 0) { ensureDir(dirname(diffPath)) writeFileSync(diffPath, JSON.stringify(additions, null, 2)) - console.log(`Wrote diff to ${diffPath}`) + console.warn(`Wrote diff to ${diffPath}`) } } } @@ -116,9 +117,9 @@ switch (command) { // Remove temp directory recursively if (existsSync('temp')) { rmSync('temp', { recursive: true, force: true }) - console.log('Removed temp directory') + console.warn('Removed temp directory') } break default: - console.log('Please specify either "capture" or "diff" command') + console.error('Please specify either "capture" or "diff" command') } diff --git a/scripts/i18n-types.ts b/scripts/i18n-types.ts new file mode 100644 index 000000000..5a64c1dbe --- /dev/null +++ b/scripts/i18n-types.ts @@ -0,0 +1,5 @@ +/** + * Shared types for i18n-related scripts + */ + +export type LocaleData = { [key: string]: string | LocaleData } diff --git a/src/components/common/LazyImage.vue b/src/components/common/LazyImage.vue index 938fc9387..b29aebc15 100644 --- a/src/components/common/LazyImage.vue +++ b/src/components/common/LazyImage.vue @@ -38,6 +38,7 @@ diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts b/src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts index 765572752..336799968 100644 --- a/src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts +++ b/src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts @@ -32,7 +32,7 @@ async function getAuthHeaders() { return {} } -const dataCache = new Map>() +const dataCache = new Map>() const createCacheKey = (config: RemoteWidgetConfig): string => { const { route, query_params = {}, refresh = 0 } = config @@ -49,7 +49,9 @@ const getBackoff = (retryCount: number) => Math.min(1000 * Math.pow(2, retryCount), 512) const isInitialized = (entry: CacheEntry | undefined) => - entry?.data && entry?.timestamp && entry.timestamp > 0 + entry?.data !== undefined && + entry?.timestamp !== undefined && + entry.timestamp > 0 const isStale = (entry: CacheEntry | undefined, ttl: number) => entry?.timestamp && Date.now() - entry.timestamp >= ttl @@ -128,9 +130,11 @@ export function useRemoteWidget< return !isLoaded && isInitialized(dataCache.get(cacheKey)) } - const onFirstLoad = (data: T[]) => { + const onFirstLoad = (data: T | T[]) => { isLoaded = true - widget.value = data[0] + const nextValue = + Array.isArray(data) && data.length > 0 ? data[0] : undefined + widget.value = nextValue ?? (Array.isArray(data) ? defaultValue : data) widget.callback?.(widget.value) node.graph?.setDirtyCanvas(true) } @@ -138,13 +142,16 @@ export function useRemoteWidget< const fetchValue = async () => { const entry = dataCache.get(cacheKey) - if (isFailed(entry)) return entry!.data + if (isFailed(entry)) return entry!.data as T const isValid = isInitialized(entry) && (isPermanent || !isStale(entry, refresh)) - if (isValid || isBackingOff(entry) || isFetching(entry)) return entry!.data + if (isValid || isBackingOff(entry) || isFetching(entry)) + return entry!.data as T - const currentEntry: CacheEntry = entry || { data: defaultValue } + const currentEntry: CacheEntry = (entry as + | CacheEntry + | undefined) || { data: defaultValue } dataCache.set(cacheKey, currentEntry) try { diff --git a/src/services/colorPaletteService.ts b/src/services/colorPaletteService.ts index 612d4fab2..533488471 100644 --- a/src/services/colorPaletteService.ts +++ b/src/services/colorPaletteService.ts @@ -188,7 +188,7 @@ export const useColorPaletteService = () => { * @param schema - The Zod schema object to analyze. * @returns Array of optional key names. */ - const getOptionalKeys = (schema: z.ZodObject) => { + const getOptionalKeys = (schema: z.ZodObject) => { const optionalKeys: string[] = [] const shape = schema.shape diff --git a/src/services/litegraphService.ts b/src/services/litegraphService.ts index ac2d47705..6e544133b 100644 --- a/src/services/litegraphService.ts +++ b/src/services/litegraphService.ts @@ -849,7 +849,7 @@ export const useLitegraphService = () => { function addNodeOnGraph( nodeDef: ComfyNodeDefV1 | ComfyNodeDefV2, - options: Record = {} + options: Record & { pos?: Point } = {} ): LGraphNode { options.pos ??= getCanvasCenter() diff --git a/src/services/load3dService.ts b/src/services/load3dService.ts index 632331752..93ce8cc26 100644 --- a/src/services/load3dService.ts +++ b/src/services/load3dService.ts @@ -32,7 +32,6 @@ type UseLoad3dViewerFn = (node?: LGraphNode) => { handleModelDrop: (file: File) => Promise handleSeek: (progress: number) => void needApplyChanges: { value: boolean } - [key: string]: unknown } // Type for SkeletonUtils module @@ -81,7 +80,7 @@ interface Load3DNode extends LGraphNode { syncLoad3dConfig?: () => void } -const viewerInstances = new Map() +const viewerInstances = new Map>() export class Load3dService { private static instance: Load3dService @@ -165,12 +164,15 @@ export class Load3dService { * Only works after useLoad3dViewer has been loaded. * Returns null if module not yet loaded - use async version instead. */ - getOrCreateViewerSync(node: LGraphNode, useLoad3dViewer: UseLoad3dViewerFn) { + getOrCreateViewerSync( + node: LGraphNode, + useLoad3dViewer: T + ): ReturnType { if (!viewerInstances.has(node.id)) { viewerInstances.set(node.id, useLoad3dViewer(node)) } - return viewerInstances.get(node.id) + return viewerInstances.get(node.id) as ReturnType } removeViewer(node: LGraphNode) { @@ -288,6 +290,7 @@ export class Load3dService { async handleViewerClose(node: LGraphNode) { const viewer = await useLoad3dService().getOrCreateViewer(node) + if (!viewer) return if (viewer.needApplyChanges.value) { await viewer.applyChanges() diff --git a/src/stores/maskEditorStore.ts b/src/stores/maskEditorStore.ts index 515a3dee3..741446ffb 100644 --- a/src/stores/maskEditorStore.ts +++ b/src/stores/maskEditorStore.ts @@ -1,6 +1,7 @@ import { defineStore } from 'pinia' import { computed, ref, watch } from 'vue' import _ from 'es-toolkit/compat' +import type { TgpuRoot } from 'typegpu' import { BrushShape, @@ -71,7 +72,7 @@ export const useMaskEditorStore = defineStore('maskEditor', () => { const canvasHistory = useCanvasHistory(20) - const tgpuRoot = ref(null) + const tgpuRoot = ref(null) const colorInput = ref(null) diff --git a/src/stores/nodeDefStore.ts b/src/stores/nodeDefStore.ts index b80eb671f..3333697cc 100644 --- a/src/stores/nodeDefStore.ts +++ b/src/stores/nodeDefStore.ts @@ -86,7 +86,7 @@ export class ComfyNodeDefImpl // V2 fields readonly inputs: Record readonly outputs: OutputSpecV2[] - readonly hidden?: Record + readonly hidden?: Record // ComfyNodeDefImpl fields readonly nodeSource: NodeSource diff --git a/src/types/algoliaTypes.ts b/src/types/algoliaTypes.ts index de42fb78a..92b39b631 100644 --- a/src/types/algoliaTypes.ts +++ b/src/types/algoliaTypes.ts @@ -72,7 +72,7 @@ export interface NodesIndexSuggestion { exact_nb_hits: number facets: { exact_matches: Record - analytics: Record + analytics: Record } } objectID: RegistryNodePack['id'] diff --git a/src/types/simplifiedWidget.ts b/src/types/simplifiedWidget.ts index b4654406b..4e0f48666 100644 --- a/src/types/simplifiedWidget.ts +++ b/src/types/simplifiedWidget.ts @@ -3,6 +3,7 @@ * Removes all DOM manipulation and positioning concerns */ import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2' +import type { IWidgetOptions } from '@/lib/litegraph/src/types/widgets' /** Valid types for widget values */ export type WidgetValue = @@ -39,7 +40,7 @@ export type SafeControlWidget = { export interface SimplifiedWidget< T extends WidgetValue = WidgetValue, - O = Record + O extends IWidgetOptions = IWidgetOptions > { /** Display name of the widget */ name: string @@ -68,7 +69,7 @@ export interface SimplifiedWidget< nodeType?: string /** Optional serialization method for custom value handling */ - serializeValue?: () => any + serializeValue?: () => unknown /** Optional input specification backing this widget */ spec?: InputSpecV2 @@ -78,7 +79,7 @@ export interface SimplifiedWidget< export interface SimplifiedControlWidget< T extends WidgetValue = WidgetValue, - O = Record + O extends IWidgetOptions = IWidgetOptions > extends SimplifiedWidget { controlWidget: SafeControlWidget } diff --git a/src/utils/envUtil.ts b/src/utils/envUtil.ts index 8e411bf03..b95456aaf 100644 --- a/src/utils/envUtil.ts +++ b/src/utils/envUtil.ts @@ -1,7 +1,11 @@ import type { ElectronAPI } from '@comfyorg/comfyui-electron-types' -// Extend Window interface to include electronAPI -type ElectronWindow = typeof window & { +/** + * Extend Window interface to include electronAPI + * Used by desktop-ui app storybook stories + * @public + */ +export type ElectronWindow = typeof window & { electronAPI?: ElectronAPI } diff --git a/src/utils/queueDisplay.ts b/src/utils/queueDisplay.ts index e0e0bce18..75d962be1 100644 --- a/src/utils/queueDisplay.ts +++ b/src/utils/queueDisplay.ts @@ -4,7 +4,7 @@ import { formatDuration } from '@/utils/formatUtil' import { clampPercentInt, formatPercent0 } from '@/utils/numberUtil' export type BuildJobDisplayCtx = { - t: (k: string, v?: Record) => string + t: (k: string, v?: Record) => string locale: string formatClockTimeFn: (ts: number, locale: string) => string isActive: boolean diff --git a/src/utils/widgetPropFilter.ts b/src/utils/widgetPropFilter.ts index af2bac794..d5925fbe6 100644 --- a/src/utils/widgetPropFilter.ts +++ b/src/utils/widgetPropFilter.ts @@ -55,13 +55,13 @@ export const BADGE_EXCLUDED_PROPS = [ * @param excludeList - List of property names to exclude * @returns Filtered props object */ -export function filterWidgetProps>( +export function filterWidgetProps( props: T | undefined, excludeList: readonly string[] ): Partial { if (!props) return {} - const filtered: Record = {} + const filtered: Record = {} for (const [key, value] of Object.entries(props)) { if (!excludeList.includes(key)) { filtered[key] = value diff --git a/src/workbench/extensions/manager/components/manager/skeleton/PackCardGridSkeleton.test.ts b/src/workbench/extensions/manager/components/manager/skeleton/PackCardGridSkeleton.test.ts index 3a48d7eab..dfcbc2a9b 100644 --- a/src/workbench/extensions/manager/components/manager/skeleton/PackCardGridSkeleton.test.ts +++ b/src/workbench/extensions/manager/components/manager/skeleton/PackCardGridSkeleton.test.ts @@ -5,6 +5,7 @@ import PrimeVue from 'primevue/config' import { describe, expect, it } from 'vitest' import { nextTick } from 'vue' import { createI18n } from 'vue-i18n' +import type { ComponentProps } from 'vue-component-type-helpers' import enMessages from '@/locales/en/main.json' with { type: 'json' } @@ -12,9 +13,11 @@ import GridSkeleton from './GridSkeleton.vue' import PackCardSkeleton from './PackCardSkeleton.vue' describe('GridSkeleton', () => { - const mountComponent = ({ + function mountComponent({ props = {} - }: Record = {}): VueWrapper => { + }: { + props?: Partial> + } = {}): VueWrapper { const i18n = createI18n({ legacy: false, locale: 'en', diff --git a/src/workbench/extensions/manager/composables/useConflictDetection.ts b/src/workbench/extensions/manager/composables/useConflictDetection.ts index aad300abe..ee9754671 100644 --- a/src/workbench/extensions/manager/composables/useConflictDetection.ts +++ b/src/workbench/extensions/manager/composables/useConflictDetection.ts @@ -19,6 +19,7 @@ import type { ConflictDetail, ConflictDetectionResponse, ConflictDetectionResult, + ImportFailureMap, Node, NodeRequirements, SystemEnvironment @@ -336,7 +337,7 @@ export function useConflictDetection() { * Gets installed packages and checks each one for import failures using bulk API. * @returns Promise that resolves to import failure data */ - async function fetchImportFailInfo(): Promise> { + async function fetchImportFailInfo(): Promise { try { const comfyManagerService = useComfyManagerService() @@ -362,7 +363,7 @@ export function useConflictDetection() { if (bulkResult) { // Filter out null values (packages without import failures) - const importFailures: Record = {} + const importFailures: ImportFailureMap = {} Object.entries(bulkResult).forEach(([packageId, failInfo]) => { if (failInfo !== null) { @@ -389,10 +390,7 @@ export function useConflictDetection() { * @returns Array of conflict detection results for failed imports */ function detectImportFailConflicts( - importFailInfo: Record< - string, - { error?: string; traceback?: string } | null - > + importFailInfo: ImportFailureMap ): ConflictDetectionResult[] { const results: ConflictDetectionResult[] = [] if (!importFailInfo || typeof importFailInfo !== 'object') { diff --git a/src/workbench/extensions/manager/composables/useManagerQueue.ts b/src/workbench/extensions/manager/composables/useManagerQueue.ts index 7ac796414..124311e3f 100644 --- a/src/workbench/extensions/manager/composables/useManagerQueue.ts +++ b/src/workbench/extensions/manager/composables/useManagerQueue.ts @@ -24,7 +24,7 @@ const MANAGER_WS_TASK_STARTED_NAME = 'cm-task-started' export const useManagerQueue = ( taskHistory: Ref, taskQueue: Ref, - installedPacks: Ref> + installedPacks: Ref> ) => { // Task queue state (read-only from server) const maxHistoryItems = ref(64) diff --git a/src/workbench/extensions/manager/services/comfyManagerService.ts b/src/workbench/extensions/manager/services/comfyManagerService.ts index 4b0f25ae5..f58878ab2 100644 --- a/src/workbench/extensions/manager/services/comfyManagerService.ts +++ b/src/workbench/extensions/manager/services/comfyManagerService.ts @@ -156,7 +156,7 @@ export const useComfyManagerService = () => { const getImportFailInfo = async (signal?: AbortSignal) => { const errorContext = 'Fetching import failure information' - return executeRequest( + return executeRequest>( () => managerApiClient.get(ManagerRoute.IMPORT_FAIL_INFO, { signal }), { errorContext } ) diff --git a/src/workbench/extensions/manager/types/conflictDetectionTypes.ts b/src/workbench/extensions/manager/types/conflictDetectionTypes.ts index ab7efe08a..58c626e52 100644 --- a/src/workbench/extensions/manager/types/conflictDetectionTypes.ts +++ b/src/workbench/extensions/manager/types/conflictDetectionTypes.ts @@ -78,3 +78,16 @@ export interface ConflictDetectionResponse { results: ConflictDetectionResult[] detected_system_environment?: Partial } + +/** + * Detailed information about a Python import failure + */ +interface ImportFailureDetail { + error?: string + traceback?: string +} + +/** + * Map of package IDs to their import failure information + */ +export type ImportFailureMap = Record diff --git a/src/workbench/extensions/manager/utils/conflictMessageUtil.ts b/src/workbench/extensions/manager/utils/conflictMessageUtil.ts index c175760b5..3f7ace6c3 100644 --- a/src/workbench/extensions/manager/utils/conflictMessageUtil.ts +++ b/src/workbench/extensions/manager/utils/conflictMessageUtil.ts @@ -10,7 +10,7 @@ import type { ConflictDetail } from '@/workbench/extensions/manager/types/confli */ export function getConflictMessage( conflict: ConflictDetail, - t: (key: string, params?: Record) => string + t: (key: string, params?: Record) => string ): string { const messageKey = `manager.conflicts.conflictMessages.${conflict.type}` @@ -53,7 +53,7 @@ export function getConflictMessage( */ export function getJoinedConflictMessages( conflicts: ConflictDetail[], - t: (key: string, params?: Record) => string, + t: (key: string, params?: Record) => string, separator = '; ' ): string { return conflicts