diff --git a/.github/workflows/lint-and-format.yaml b/.github/workflows/lint-and-format.yaml index 7ade4a667..5d9a50d83 100644 --- a/.github/workflows/lint-and-format.yaml +++ b/.github/workflows/lint-and-format.yaml @@ -56,6 +56,7 @@ jobs: run: | npm run lint npm run format:check + npm run knip - name: Comment on PR about auto-fix if: steps.verify-changed-files.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository diff --git a/knip.config.ts b/knip.config.ts new file mode 100644 index 000000000..4d102262a --- /dev/null +++ b/knip.config.ts @@ -0,0 +1,75 @@ +import type { KnipConfig } from 'knip' + +const config: KnipConfig = { + entry: [ + 'src/main.ts', + 'vite.config.mts', + 'vite.electron.config.mts', + 'vite.types.config.mts', + 'eslint.config.js', + 'tailwind.config.js', + 'postcss.config.js', + 'playwright.config.ts', + 'playwright.i18n.config.ts', + 'vitest.config.ts', + 'scripts/**/*.{js,ts}' + ], + project: [ + 'src/**/*.{js,ts,vue}', + 'tests-ui/**/*.{js,ts,vue}', + 'browser_tests/**/*.{js,ts}', + 'scripts/**/*.{js,ts}' + ], + ignore: [ + // Generated files + 'dist/**', + 'types/**', + 'node_modules/**', + // Config files that might not show direct usage + '.husky/**', + // Temporary or cache files + '.vite/**', + 'coverage/**', + // i18n config + '.i18nrc.cjs', + // Test setup files + 'browser_tests/globalSetup.ts', + 'browser_tests/globalTeardown.ts', + 'browser_tests/utils/**', + // Scripts + 'scripts/**', + // Vite config files + 'vite.electron.config.mts', + 'vite.types.config.mts', + // Auto generated manager types + 'src/types/generatedManagerTypes.ts' + ], + ignoreExportsUsedInFile: true, + // Vue-specific configuration + vue: true, + // Only check for unused files, disable all other rules + // TODO: Gradually enable other rules - see https://github.com/Comfy-Org/ComfyUI_frontend/issues/4888 + rules: { + binaries: 'off', + classMembers: 'off', + dependencies: 'off', + devDependencies: 'off', + duplicates: 'off', + enumMembers: 'off', + exports: 'off', + nsExports: 'off', + nsTypes: 'off', + types: 'off', + unlisted: 'off' + }, + // Include dependencies analysis + includeEntryExports: true, + // Workspace configuration for monorepo-like structure + workspaces: { + '.': { + entry: ['src/main.ts'] + } + } +} + +export default config diff --git a/package-lock.json b/package-lock.json index 3241b8800..135a1d41d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,6 +80,7 @@ "happy-dom": "^15.11.0", "husky": "^9.0.11", "identity-obj-proxy": "^3.0.0", + "knip": "^5.62.0", "lint-staged": "^15.2.7", "postcss": "^8.4.39", "prettier": "^3.3.2", @@ -1001,6 +1002,40 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@emnapi/core": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", + "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", + "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -3069,6 +3104,19 @@ "node": ">=18" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.3.tgz", + "integrity": "sha512-rZxtMsLwjdXkMUGC3WwsPwLNVqVqnTJT6MNIB6e+5fhMcSCPP0AOsNWuMQ5mdCq6HNjs/ZeWAEchpqeprqBD2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.5", + "@emnapi/runtime": "^1.4.5", + "@tybys/wasm-util": "^0.10.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3120,6 +3168,275 @@ "node": ">=8.0.0" } }, + "node_modules/@oxc-resolver/binding-android-arm-eabi": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.6.1.tgz", + "integrity": "sha512-Ma/kg29QJX1Jzelv0Q/j2iFuUad1WnjgPjpThvjqPjpOyLjCUaiFCCnshhmWjyS51Ki1Iol3fjf1qAzObf8GIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-android-arm64": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.6.1.tgz", + "integrity": "sha512-xjL/FKKc5p8JkFWiH7pJWSzsewif3fRf1rw2qiRxRvq1uIa6l7Zoa14Zq2TNWEsqDjdeOrlJtfWiPNRnevK0oQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-arm64": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.6.1.tgz", + "integrity": "sha512-u0yrJ3NHE0zyCjiYpIyz4Vmov21MA0yFKbhHgixDU/G6R6nvC8ZpuSFql3+7C8ttAK9p8WpqOGweepfcilH5Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-x64": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.6.1.tgz", + "integrity": "sha512-2lox165h1EhzxcC8edUy0znXC/hnAbUPaMpYKVlzLpB2AoYmgU4/pmofFApj+axm2FXpNamjcppld8EoHo06rw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-freebsd-x64": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.6.1.tgz", + "integrity": "sha512-F45MhEQ7QbHfsvZtVNuA/9obu3il7QhpXYmCMfxn7Zt9nfAOw4pQ8hlS5DroHVp3rW35u9F7x0sixk/QEAi3qQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.6.1.tgz", + "integrity": "sha512-r+3+MTTl0tD4NoWbfTIItAxJvuyIU7V0fwPDXrv7Uj64vZ3OYaiyV+lVaeU89Bk/FUUQxeUpWBwdKNKHjyRNQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.6.1.tgz", + "integrity": "sha512-TBTZ63otsWZ72Z8ZNK2JVS0HW1w9zgOixJTFDNrYPUUW1pXGa28KAjQ1yGawj242WLAdu3lwdNIWtkxeO2BLxQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.6.1.tgz", + "integrity": "sha512-SjwhNynjSG2yMdyA0f7wz7Yvo3ppejO+ET7n2oiI7ApCXrwxMzeRWjBzQt+oVWr2HzVOfaEcDS9rMtnR83ulig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-musl": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.6.1.tgz", + "integrity": "sha512-f4EMidK6rosInBzPMnJ0Ri4RttFCvvLNUNDFUBtELW/MFkBwPTDlvbsmW0u0Mk/ruBQ2WmRfOZ6tT62kWMcX2Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.6.1.tgz", + "integrity": "sha512-1umENVKeUsrWnf5IlF/6SM7DCv8G6CoKI2LnYR6qhZuLYDPS4PBZ0Jow3UDV9Rtbv5KRPcA3/uXjI88ntWIcOQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.6.1.tgz", + "integrity": "sha512-Hjyp1FRdJhsEpIxsZq5VcDuFc8abC0Bgy8DWEa31trCKoTz7JqA7x3E2dkFbrAKsEFmZZ0NvuG5Ip3oIRARhow==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.6.1.tgz", + "integrity": "sha512-ODJOJng6f3QxpAXhLel3kyWs8rPsJeo9XIZHzA7p//e+5kLMDU7bTVk4eZnUHuxsqsB8MEvPCicJkKCEuur5Ag==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.6.1.tgz", + "integrity": "sha512-hCzRiLhqe1ZOpHTsTGKp7gnMJRORlbCthawBueer2u22RVAka74pV/+4pP1tqM07mSlQn7VATuWaDw9gCl+cVg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-gnu": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.6.1.tgz", + "integrity": "sha512-JansPD8ftOzMYIC3NfXJ68tt63LEcIAx44Blx6BAd7eY880KX7A0KN3hluCrelCz5aQkPaD95g8HBiJmKaEi2w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-musl": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.6.1.tgz", + "integrity": "sha512-R78ES1rd4z2x5NrFPtSWb/ViR1B8wdl+QN2X8DdtoYcqZE/4tvWtn9ZTCXMEzUp23tchJ2wUB+p6hXoonkyLpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.6.1.tgz", + "integrity": "sha512-qAR3tYIf3afkij/XYunZtlz3OH2Y4ni10etmCFIJB5VRGsqJyI6Hl+2dXHHGJNwbwjXjSEH/KWJBpVroF3TxBw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.6.1.tgz", + "integrity": "sha512-QqygWygIuemGkaBA48POOTeinbVvlamqh6ucm8arGDGz/mB5O00gXWxed12/uVrYEjeqbMkla/CuL3fjL3EKvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-ia32-msvc": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.6.1.tgz", + "integrity": "sha512-N2+kkWwt/bk0JTCxhPuK8t8JMp3nd0n2OhwOkU8KO4a7roAJEa4K1SZVjMv5CqUIr5sx2CxtXRBoFDiORX5oBg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-x64-msvc": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.6.1.tgz", + "integrity": "sha512-DfMg3cU9bJUbN62Prbp4fGCtLgexuwyEaQGtZAp8xmi1Ii26uflOGx0FJkFTF6lVMSFoIRFvIL8gsw5/ZdHrMw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@pinia/testing": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/@pinia/testing/-/testing-0.1.5.tgz", @@ -4486,6 +4803,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", + "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/argparse": { "version": "1.0.38", "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", @@ -8469,16 +8797,17 @@ "license": "Apache-2.0" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -8551,6 +8880,16 @@ "node": ">=0.8.0" } }, + "node_modules/fd-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", + "integrity": "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "walk-up-path": "^4.0.0" + } + }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -8846,6 +9185,22 @@ "node": ">=0.4.x" } }, + "node_modules/formatly": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.2.4.tgz", + "integrity": "sha512-lIN7GpcvX/l/i24r/L9bnJ0I8Qn01qijWpQpDDvTLL29nKqSaJJu4h20+7VJ6m2CAhQ2/En/GbxDiHCzq/0MyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fd-package-json": "^2.0.0" + }, + "bin": { + "formatly": "bin/index.mjs" + }, + "engines": { + "node": ">=18.3.0" + } + }, "node_modules/formdata-node": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", @@ -10517,6 +10872,109 @@ "node": ">=0.10.0" } }, + "node_modules/knip": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.62.0.tgz", + "integrity": "sha512-hfTUVzmrMNMT1khlZfAYmBABeehwWUUrizLQoLamoRhSFkygsGIXWx31kaWKBgEaIVL77T3Uz7IxGvSw+CvQ6A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/knip" + }, + { + "type": "polar", + "url": "https://polar.sh/webpro-nl" + } + ], + "license": "ISC", + "dependencies": { + "@nodelib/fs.walk": "^1.2.3", + "fast-glob": "^3.3.3", + "formatly": "^0.2.4", + "jiti": "^2.4.2", + "js-yaml": "^4.1.0", + "minimist": "^1.2.8", + "oxc-resolver": "^11.1.0", + "picocolors": "^1.1.1", + "picomatch": "^4.0.1", + "smol-toml": "^1.3.4", + "strip-json-comments": "5.0.2", + "zod": "^3.22.4", + "zod-validation-error": "^3.0.3" + }, + "bin": { + "knip": "bin/knip.js", + "knip-bun": "bin/knip-bun.js" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "@types/node": ">=18", + "typescript": ">=5.0.4" + } + }, + "node_modules/knip/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/knip/node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/knip/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/knip/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/knip/node_modules/strip-json-comments": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.2.tgz", + "integrity": "sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/kolorist": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", @@ -12388,6 +12846,22 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-postinstall": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", + "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -12741,6 +13215,41 @@ "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", "license": "MIT" }, + "node_modules/oxc-resolver": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.6.1.tgz", + "integrity": "sha512-WQgmxevT4cM5MZ9ioQnEwJiHpPzbvntV5nInGAKo9NQZzegcOonHvcVcnkYqld7bTG35UFHEKeF7VwwsmA3cZg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-resolver/binding-android-arm-eabi": "11.6.1", + "@oxc-resolver/binding-android-arm64": "11.6.1", + "@oxc-resolver/binding-darwin-arm64": "11.6.1", + "@oxc-resolver/binding-darwin-x64": "11.6.1", + "@oxc-resolver/binding-freebsd-x64": "11.6.1", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.6.1", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.6.1", + "@oxc-resolver/binding-linux-arm64-gnu": "11.6.1", + "@oxc-resolver/binding-linux-arm64-musl": "11.6.1", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.6.1", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.6.1", + "@oxc-resolver/binding-linux-riscv64-musl": "11.6.1", + "@oxc-resolver/binding-linux-s390x-gnu": "11.6.1", + "@oxc-resolver/binding-linux-x64-gnu": "11.6.1", + "@oxc-resolver/binding-linux-x64-musl": "11.6.1", + "@oxc-resolver/binding-wasm32-wasi": "11.6.1", + "@oxc-resolver/binding-win32-arm64-msvc": "11.6.1", + "@oxc-resolver/binding-win32-ia32-msvc": "11.6.1", + "@oxc-resolver/binding-win32-x64-msvc": "11.6.1" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -14804,6 +15313,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/smol-toml": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.2.tgz", + "integrity": "sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -17400,6 +17922,16 @@ "node": ">=14" } }, + "node_modules/walk-up-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/web-streams-polyfill": { "version": "4.0.0-beta.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", diff --git a/package.json b/package.json index 2a920c311..27cc90d77 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "preview": "vite preview", "lint": "eslint src", "lint:fix": "eslint src --fix", + "knip": "knip", "locale": "lobe-i18n locale", "collect-i18n": "playwright test --config=playwright.i18n.config.ts", "json-schema": "tsx scripts/generate-json-schema.ts" @@ -56,6 +57,7 @@ "happy-dom": "^15.11.0", "husky": "^9.0.11", "identity-obj-proxy": "^3.0.0", + "knip": "^5.62.0", "lint-staged": "^15.2.7", "postcss": "^8.4.39", "prettier": "^3.3.2", diff --git a/src/components/common/ApiNodesCostBreakdown.vue b/src/components/common/ApiNodesCostBreakdown.vue deleted file mode 100644 index 318421555..000000000 --- a/src/components/common/ApiNodesCostBreakdown.vue +++ /dev/null @@ -1,75 +0,0 @@ - - - diff --git a/src/components/common/ApiNodesList.vue b/src/components/common/ApiNodesList.vue deleted file mode 100644 index 38d89607b..000000000 --- a/src/components/common/ApiNodesList.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - diff --git a/src/components/dialog/content/MissingModelsWarning.vue b/src/components/dialog/content/MissingModelsWarning.vue index df1c7d470..911454d2e 100644 --- a/src/components/dialog/content/MissingModelsWarning.vue +++ b/src/components/dialog/content/MissingModelsWarning.vue @@ -36,6 +36,7 @@ import ListBox from 'primevue/listbox' import { computed, onBeforeUnmount, ref } from 'vue' import { useI18n } from 'vue-i18n' +import ElectronFileDownload from '@/components/common/ElectronFileDownload.vue' import FileDownload from '@/components/common/FileDownload.vue' import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue' import { useSettingStore } from '@/stores/settingStore' diff --git a/src/components/dialog/content/manager/packCard/PackCardHeader.vue b/src/components/dialog/content/manager/packCard/PackCardHeader.vue deleted file mode 100644 index 77049e571..000000000 --- a/src/components/dialog/content/manager/packCard/PackCardHeader.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - diff --git a/src/constants/coreTemplates.ts b/src/constants/coreTemplates.ts deleted file mode 100644 index 42ce36569..000000000 --- a/src/constants/coreTemplates.ts +++ /dev/null @@ -1,438 +0,0 @@ -export const CORE_TEMPLATES = [ - { - moduleName: 'default', - title: 'Basics', - type: 'image', - templates: [ - { - name: 'default', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Generate images from text descriptions.' - }, - { - name: 'image2image', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Transform existing images using text prompts.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/img2img/' - }, - { - name: 'lora', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Apply LoRA models for specialized styles or subjects.', - tutorialUrl: 'https://comfyanonymous.github.io/ComfyUI_examples/lora/' - }, - { - name: 'inpaint_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Edit specific parts of images seamlessly.', - thumbnailVariant: 'compareSlider', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/inpaint/' - }, - { - name: 'inpain_model_outpainting', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Extend images beyond their original boundaries.', - thumbnailVariant: 'compareSlider', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/inpaint/#outpainting' - }, - { - name: 'embedding_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Use textual inversion for consistent styles', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/textual_inversion_embeddings/' - }, - { - name: 'gligen_textbox_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Specify the location and size of objects.', - tutorialUrl: 'https://comfyanonymous.github.io/ComfyUI_examples/gligen/' - }, - { - name: 'lora_multiple', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Combine multiple LoRA models for unique results.', - tutorialUrl: 'https://comfyanonymous.github.io/ComfyUI_examples/lora/' - } - ] - }, - { - moduleName: 'default', - title: 'Flux', - type: 'image', - templates: [ - { - name: 'flux_dev_checkpoint_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Create images using Flux development models.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/flux/#flux-dev-1' - }, - { - name: 'flux_schnell', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Generate images quickly with Flux Schnell.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/flux/#flux-schnell-1' - }, - { - name: 'flux_fill_inpaint_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Fill in missing parts of images.', - thumbnailVariant: 'compareSlider', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/flux/#fill-inpainting-model' - }, - { - name: 'flux_fill_outpaint_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Extend images using Flux outpainting.', - thumbnailVariant: 'compareSlider', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/flux/#fill-inpainting-model' - }, - { - name: 'flux_canny_model_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Generate images from edge detection.', - thumbnailVariant: 'hoverDissolve', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/flux/#canny-and-depth' - }, - { - name: 'flux_depth_lora_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Create images with depth-aware LoRA.', - thumbnailVariant: 'hoverDissolve', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/flux/#canny-and-depth' - }, - { - name: 'flux_redux_model_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: - 'Transfer style from a reference image to guide image generation with Flux.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/flux/#redux' - } - ] - }, - { - moduleName: 'default', - title: 'ControlNet', - type: 'image', - templates: [ - { - name: 'controlnet_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Control image generation with reference images.', - thumbnailVariant: 'hoverDissolve', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/controlnet/' - }, - { - name: '2_pass_pose_worship', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Generate images from pose references.', - thumbnailVariant: 'hoverDissolve', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/controlnet/#pose-controlnet' - }, - { - name: 'depth_controlnet', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Create images with depth-aware generation.', - thumbnailVariant: 'hoverDissolve', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/controlnet/#t2i-adapter-vs-controlnets' - }, - { - name: 'depth_t2i_adapter', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Quickly generate depth-aware images with a T2I adapter.', - thumbnailVariant: 'hoverDissolve', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/controlnet/#t2i-adapter-vs-controlnets' - }, - { - name: 'mixing_controlnets', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Combine multiple ControlNet models together.', - thumbnailVariant: 'hoverDissolve', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/controlnet/#mixing-controlnets' - } - ] - }, - { - moduleName: 'default', - title: 'Upscaling', - type: 'image', - templates: [ - { - name: 'hiresfix_latent_workflow', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Enhance image quality in latent space.', - thumbnailVariant: 'zoomHover', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/2_pass_txt2img/' - }, - { - name: 'esrgan_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Use upscale models to enhance image quality.', - thumbnailVariant: 'zoomHover', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/upscale_models/' - }, - { - name: 'hiresfix_esrgan_workflow', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Use upscale models during intermediate steps.', - thumbnailVariant: 'zoomHover', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/2_pass_txt2img/#non-latent-upscaling' - }, - { - name: 'latent_upscale_different_prompt_model', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Upscale and change prompt across passes', - thumbnailVariant: 'zoomHover', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/2_pass_txt2img/#more-examples' - } - ] - }, - { - moduleName: 'default', - title: 'Video', - type: 'video', - templates: [ - { - name: 'ltxv_text_to_video', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Generate videos from text descriptions.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/ltxv/#text-to-video' - }, - { - name: 'ltxv_image_to_video', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Convert still images into videos.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/ltxv/#image-to-video' - }, - { - name: 'mochi_text_to_video_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Create videos with Mochi model.', - tutorialUrl: 'https://comfyanonymous.github.io/ComfyUI_examples/mochi/' - }, - { - name: 'hunyuan_video_text_to_video', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Generate videos using Hunyuan model.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/hunyuan_video/' - }, - { - name: 'image_to_video', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Transform images into animated videos.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/video/#image-to-video' - }, - { - name: 'txt_to_image_to_video', - mediaType: 'image', - mediaSubtype: 'webp', - description: - 'Generate images from text and then convert them into videos.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/video/#image-to-video' - } - ] - }, - { - moduleName: 'default', - title: 'SD3.5', - type: 'image', - templates: [ - { - name: 'sd3.5_simple_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Generate images with SD 3.5.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/sd3/#sd35' - }, - { - name: 'sd3.5_large_canny_controlnet_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: - 'Use edge detection to guide image generation with SD 3.5.', - thumbnailVariant: 'hoverDissolve', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/sd3/#sd35-controlnets' - }, - { - name: 'sd3.5_large_depth', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Create depth-aware images with SD 3.5.', - thumbnailVariant: 'hoverDissolve', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/sd3/#sd35-controlnets' - }, - { - name: 'sd3.5_large_blur', - mediaType: 'image', - mediaSubtype: 'webp', - description: - 'Generate images from blurred reference images with SD 3.5.', - thumbnailVariant: 'hoverDissolve', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/sd3/#sd35-controlnets' - } - ] - }, - { - moduleName: 'default', - title: 'SDXL', - type: 'image', - templates: [ - { - name: 'sdxl_simple_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Create high-quality images with SDXL.', - tutorialUrl: 'https://comfyanonymous.github.io/ComfyUI_examples/sdxl/' - }, - { - name: 'sdxl_refiner_prompt_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Enhance SDXL outputs with refiners.', - tutorialUrl: 'https://comfyanonymous.github.io/ComfyUI_examples/sdxl/' - }, - { - name: 'sdxl_revision_text_prompts', - mediaType: 'image', - mediaSubtype: 'webp', - description: - 'Transfer concepts from reference images to guide image generation with SDXL.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/sdxl/#revision' - }, - { - name: 'sdxl_revision_zero_positive', - mediaType: 'image', - mediaSubtype: 'webp', - description: - 'Add text prompts alongside reference images to guide image generation with SDXL.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/sdxl/#revision' - }, - { - name: 'sdxlturbo_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Generate images in a single step with SDXL Turbo.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/sdturbo/' - } - ] - }, - { - moduleName: 'default', - title: 'Area Composition', - type: 'image', - templates: [ - { - name: 'area_composition', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Control image composition with areas.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/area_composition/' - }, - { - name: 'area_composition_reversed', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Reverse area composition workflow.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/area_composition/' - }, - { - name: 'area_composition_square_area_for_subject', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Create consistent subject placement.', - tutorialUrl: - 'https://comfyanonymous.github.io/ComfyUI_examples/area_composition/#increasing-consistency-of-images-with-area-composition' - } - ] - }, - { - moduleName: 'default', - title: '3D', - type: 'video', - templates: [ - { - name: 'stable_zero123_example', - mediaType: 'image', - mediaSubtype: 'webp', - description: 'Generate 3D views from single images.', - tutorialUrl: 'https://comfyanonymous.github.io/ComfyUI_examples/3d/' - } - ] - }, - { - moduleName: 'default', - title: 'Audio', - type: 'audio', - templates: [ - { - name: 'stable_audio_example', - mediaType: 'audio', - mediaSubtype: 'mp3', - description: 'Generate audio from text descriptions.', - tutorialUrl: 'https://comfyanonymous.github.io/ComfyUI_examples/audio/' - } - ] - } -] diff --git a/src/extensions/core/load3d/conditional-lines/OutsideEdgesGeometry.js b/src/extensions/core/load3d/conditional-lines/OutsideEdgesGeometry.js deleted file mode 100644 index 19099c829..000000000 --- a/src/extensions/core/load3d/conditional-lines/OutsideEdgesGeometry.js +++ /dev/null @@ -1,48 +0,0 @@ -/* -Taken from: https://github.com/gkjohnson/threejs-sandbox/tree/master/conditional-lines -under MIT license - */ -import { BufferAttribute, BufferGeometry, Vector3 } from 'three' - -const vec = new Vector3() -export class OutsideEdgesGeometry extends BufferGeometry { - constructor(geometry) { - super() - - const edgeInfo = {} - const index = geometry.index - const position = geometry.attributes.position - for (let i = 0, l = index.count; i < l; i += 3) { - const indices = [index.getX(i + 0), index.getX(i + 1), index.getX(i + 2)] - - for (let j = 0; j < 3; j++) { - const index0 = indices[j] - const index1 = indices[(j + 1) % 3] - - const hash = `${index0}_${index1}` - const reverseHash = `${index1}_${index0}` - if (reverseHash in edgeInfo) { - delete edgeInfo[reverseHash] - } else { - edgeInfo[hash] = [index0, index1] - } - } - } - - const edgePositions = [] - for (const key in edgeInfo) { - const [i0, i1] = edgeInfo[key] - - vec.fromBufferAttribute(position, i0) - edgePositions.push(vec.x, vec.y, vec.z) - - vec.fromBufferAttribute(position, i1) - edgePositions.push(vec.x, vec.y, vec.z) - } - - this.setAttribute( - 'position', - new BufferAttribute(new Float32Array(edgePositions), 3, false) - ) - } -} diff --git a/src/lib/litegraph/test/subgraph/fixtures/advancedEventHelpers.ts b/src/lib/litegraph/test/subgraph/fixtures/advancedEventHelpers.ts deleted file mode 100644 index a544c392d..000000000 --- a/src/lib/litegraph/test/subgraph/fixtures/advancedEventHelpers.ts +++ /dev/null @@ -1,289 +0,0 @@ -import { expect } from 'vitest' - -import type { CapturedEvent } from './subgraphHelpers' - -/** - * Extended captured event with additional metadata not in the base infrastructure - */ -export interface ExtendedCapturedEvent extends CapturedEvent { - defaultPrevented: boolean - bubbles: boolean - cancelable: boolean -} - -/** - * Creates an enhanced event capture that includes additional event properties - * This extends the basic createEventCapture with more metadata - */ -export function createExtendedEventCapture( - eventTarget: EventTarget, - eventTypes: string[] -) { - const capturedEvents: ExtendedCapturedEvent[] = [] - const listeners: Array<() => void> = [] - - for (const eventType of eventTypes) { - const listener = (event: Event) => { - capturedEvents.push({ - type: eventType, - detail: (event as CustomEvent).detail, - timestamp: Date.now(), - defaultPrevented: event.defaultPrevented, - bubbles: event.bubbles, - cancelable: event.cancelable - }) - } - - eventTarget.addEventListener(eventType, listener) - listeners.push(() => eventTarget.removeEventListener(eventType, listener)) - } - - return { - events: capturedEvents, - clear: () => { - capturedEvents.length = 0 - }, - cleanup: () => { - for (const cleanup of listeners) cleanup() - }, - getEventsByType: (type: string) => - capturedEvents.filter((e) => e.type === type), - getLatestEvent: () => capturedEvents.at(-1), - getFirstEvent: () => capturedEvents[0], - - /** - * Wait for a specific event type to be captured - */ - async waitForEvent( - type: string, - timeoutMs: number = 1000 - ): Promise> { - const existingEvent = capturedEvents.find((e) => e.type === type) - if (existingEvent) return existingEvent - - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - eventTarget.removeEventListener(type, eventListener) - reject(new Error(`Event ${type} not received within ${timeoutMs}ms`)) - }, timeoutMs) - - const eventListener = (_event: Event) => { - const capturedEvent = capturedEvents.find((e) => e.type === type) - if (capturedEvent) { - clearTimeout(timeout) - eventTarget.removeEventListener(type, eventListener) - resolve(capturedEvent) - } - } - - eventTarget.addEventListener(type, eventListener) - }) - }, - - /** - * Wait for a sequence of events to occur in order - */ - async waitForSequence( - expectedSequence: string[], - timeoutMs: number = 1000 - ): Promise[]> { - // Check if sequence is already complete - if (capturedEvents.length >= expectedSequence.length) { - const actualSequence = capturedEvents - .slice(0, expectedSequence.length) - .map((e) => e.type) - if ( - JSON.stringify(actualSequence) === JSON.stringify(expectedSequence) - ) { - return capturedEvents.slice(0, expectedSequence.length) - } - } - - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - cleanup() - const actual = capturedEvents.map((e) => e.type).join(', ') - const expected = expectedSequence.join(', ') - reject( - new Error( - `Event sequence not completed within ${timeoutMs}ms. Expected: ${expected}, Got: ${actual}` - ) - ) - }, timeoutMs) - - const checkSequence = () => { - if (capturedEvents.length >= expectedSequence.length) { - const actualSequence = capturedEvents - .slice(0, expectedSequence.length) - .map((e) => e.type) - if ( - JSON.stringify(actualSequence) === - JSON.stringify(expectedSequence) - ) { - cleanup() - resolve(capturedEvents.slice(0, expectedSequence.length)) - } - } - } - - const eventListener = () => checkSequence() - - const cleanup = () => { - clearTimeout(timeout) - for (const type of expectedSequence) { - eventTarget.removeEventListener(type, eventListener) - } - } - - // Listen for all expected event types - for (const type of expectedSequence) { - eventTarget.addEventListener(type, eventListener) - } - - // Initial check in case events already exist - checkSequence() - }) - } - } -} - -/** - * Options for memory leak testing - */ -export interface MemoryLeakTestOptions { - cycles?: number - instancesPerCycle?: number - gcAfterEach?: boolean - maxMemoryGrowth?: number -} - -/** - * Creates a memory leak test factory - * Useful for testing that event listeners and references are properly cleaned up - */ -export function createMemoryLeakTest( - // @ts-expect-error TODO: Fix after merge - T does not satisfy constraint 'object' - setupFn: () => { ref: WeakRef; cleanup: () => void }, - options: MemoryLeakTestOptions = {} -) { - const { - cycles = 1, - instancesPerCycle = 1, - gcAfterEach = true, - maxMemoryGrowth = 0 - } = options - - return async () => { - // @ts-expect-error Type 'T' does not satisfy the constraint 'object' - const refs: WeakRef[] = [] - const initialMemory = process.memoryUsage?.()?.heapUsed || 0 - - for (let cycle = 0; cycle < cycles; cycle++) { - // @ts-expect-error Type 'T' does not satisfy the constraint 'object' - const cycleRefs: WeakRef[] = [] - - for (let instance = 0; instance < instancesPerCycle; instance++) { - const { ref, cleanup } = setupFn() - cycleRefs.push(ref) - cleanup() - } - - refs.push(...cycleRefs) - - if (gcAfterEach && global.gc) { - global.gc() - await new Promise((resolve) => setTimeout(resolve, 10)) - } - } - - // Final garbage collection - if (global.gc) { - global.gc() - await new Promise((resolve) => setTimeout(resolve, 50)) - - // Check if objects were collected - const uncollectedRefs = refs.filter((ref) => ref.deref() !== undefined) - if (uncollectedRefs.length > 0) { - console.warn( - `${uncollectedRefs.length} objects were not garbage collected` - ) - } - } - - // Memory growth check - if (maxMemoryGrowth > 0 && process.memoryUsage) { - const finalMemory = process.memoryUsage().heapUsed - const memoryGrowth = finalMemory - initialMemory - - if (memoryGrowth > maxMemoryGrowth) { - throw new Error( - `Memory growth ${memoryGrowth} bytes exceeds limit ${maxMemoryGrowth} bytes` - ) - } - } - - return refs - } -} - -/** - * Creates a performance monitor for event operations - */ -export function createEventPerformanceMonitor() { - const measurements: Array<{ - operation: string - duration: number - timestamp: number - }> = [] - - return { - measure: (operation: string, fn: () => T): T => { - const start = performance.now() - const result = fn() - const end = performance.now() - - measurements.push({ - operation, - duration: end - start, - timestamp: start - }) - - return result - }, - - getMeasurements: () => [...measurements], - - getAverageDuration: (operation: string) => { - const operationMeasurements = measurements.filter( - (m) => m.operation === operation - ) - if (operationMeasurements.length === 0) return 0 - - const totalDuration = operationMeasurements.reduce( - (sum, m) => sum + m.duration, - 0 - ) - return totalDuration / operationMeasurements.length - }, - - clear: () => { - measurements.length = 0 - }, - - assertPerformance: (operation: string, maxDuration: number) => { - // @ts-expect-error 'this' implicitly has type 'any' - const measurements = this.getMeasurements() - const relevantMeasurements = measurements.filter( - // @ts-expect-error Parameter 'm' implicitly has an 'any' type - (m) => m.operation === operation - ) - if (relevantMeasurements.length === 0) return - - const avgDuration = - // @ts-expect-error Parameter 'sum' and 'm' implicitly have 'any' type - relevantMeasurements.reduce((sum, m) => sum + m.duration, 0) / - relevantMeasurements.length - expect(avgDuration).toBeLessThan(maxDuration) - } - } -} diff --git a/src/scripts/ui/spinner.css b/src/scripts/ui/spinner.css deleted file mode 100644 index 5d20f8e23..000000000 --- a/src/scripts/ui/spinner.css +++ /dev/null @@ -1,34 +0,0 @@ -.lds-ring { - display: inline-block; - position: relative; - width: 1em; - height: 1em; -} -.lds-ring div { - box-sizing: border-box; - display: block; - position: absolute; - width: 100%; - height: 100%; - border: 0.15em solid #fff; - border-radius: 50%; - animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; - border-color: #fff transparent transparent transparent; -} -.lds-ring div:nth-child(1) { - animation-delay: -0.45s; -} -.lds-ring div:nth-child(2) { - animation-delay: -0.3s; -} -.lds-ring div:nth-child(3) { - animation-delay: -0.15s; -} -@keyframes lds-ring { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} diff --git a/src/scripts/ui/spinner.ts b/src/scripts/ui/spinner.ts deleted file mode 100644 index d55a745f6..000000000 --- a/src/scripts/ui/spinner.ts +++ /dev/null @@ -1,7 +0,0 @@ -import './spinner.css' - -export function createSpinner() { - const div = document.createElement('div') - div.innerHTML = `
` - return div.firstElementChild -} diff --git a/src/types/apiNodeTypes.ts b/src/types/apiNodeTypes.ts deleted file mode 100644 index e60db27b8..000000000 --- a/src/types/apiNodeTypes.ts +++ /dev/null @@ -1,24 +0,0 @@ -export interface ApiNodeCost { - name: string - cost: number -} - -/** - * Information about an API node's cost and pricing details - */ -export interface ApiNodeCostData { - /** The vendor/company providing the API service (e.g., 'OpenAI', 'Stability') */ - vendor: string - /** The human-readable name of the node as displayed in the UI */ - nodeName: string - /** Parameters that affect pricing (e.g., 'size | quality', 'duration', '-' if none) */ - pricingParams: string - /** The price range per run (e.g., '$0.05', '$0.04 x n', 'dynamic') */ - pricePerRunRange: string - /** Formatted price string for display in the UI */ - displayPrice: string - /** URL to the vendor's pricing documentation page */ - rateDocumentationUrl?: string -} - -export type ApiNodeCostRecord = Record