From 1097cda2cbd709336e8c7251848f8bb5ad772874 Mon Sep 17 00:00:00 2001 From: Markus Schuettler Date: Fri, 24 Jan 2025 16:55:01 +0100 Subject: [PATCH 1/4] add eslint and prettier packages and config Signed-off-by: Markus Schuettler --- WebUI/.editorconfig | 9 ++++++++ WebUI/.prettierrc.json | 7 ++++++ WebUI/eslint.config.ts | 51 ++++++++++++++++++++++++++++++++++++++++++ WebUI/package.json | 11 ++++++++- 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 WebUI/.editorconfig create mode 100644 WebUI/.prettierrc.json create mode 100644 WebUI/eslint.config.ts diff --git a/WebUI/.editorconfig b/WebUI/.editorconfig new file mode 100644 index 0000000..7f5b23f --- /dev/null +++ b/WebUI/.editorconfig @@ -0,0 +1,9 @@ +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +end_of_line = lf +max_line_length = 100 diff --git a/WebUI/.prettierrc.json b/WebUI/.prettierrc.json new file mode 100644 index 0000000..17a23d0 --- /dev/null +++ b/WebUI/.prettierrc.json @@ -0,0 +1,7 @@ + +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "singleQuote": true, + "printWidth": 100 +} diff --git a/WebUI/eslint.config.ts b/WebUI/eslint.config.ts new file mode 100644 index 0000000..a140816 --- /dev/null +++ b/WebUI/eslint.config.ts @@ -0,0 +1,51 @@ +import pluginVue from 'eslint-plugin-vue' +import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' +import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' + +// To allow more languages other than `ts` in `.vue` files, uncomment the following lines: +// import { configureVueProject } from '@vue/eslint-config-typescript' +// configureVueProject({ scriptLangs: ['ts', 'tsx'] }) +// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup + +export default defineConfigWithVueTs( + { + name: 'app/files-to-lint', + files: ['**/*.{ts,mts,tsx,vue}'], + }, + + { + name: 'app/files-to-ignore', + ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**', '**/*.cjs'], + }, + + pluginVue.configs['flat/essential'], + vueTsConfigs.recommended, + skipFormatting, + { + rules: { + 'vue/multi-word-component-names': 'off', + 'vue/require-v-for-key': 'warn', + 'vue/no-use-v-if-with-v-for': 'warn', + '@typescript-eslint/no-this-alias': 'warn', + "@typescript-eslint/no-unused-vars": [ + "error", + { + "args": "all", + "argsIgnorePattern": "^_", + "caughtErrors": "all", + "caughtErrorsIgnorePattern": "^_", + "destructuredArrayIgnorePattern": "^_", + "varsIgnorePattern": "(^_|Schema$)", + "ignoreRestSiblings": true + } + ] + }, + }, + { + name: 'allow-cjs-build-scripts', + files: ['**/build/scripts/*.js'], + rules: { + '@typescript-eslint/no-require-imports': 'off', + }, + }, +) diff --git a/WebUI/package.json b/WebUI/package.json index bf549c9..e049530 100644 --- a/WebUI/package.json +++ b/WebUI/package.json @@ -10,7 +10,10 @@ "install-full-python-env": "cross-env node ./build/scripts/install-full-python-env.js --env_dir=../build-envs/online/prototype-python-env --comfy_ui_dir=../build_resources/ComfyUI", "provide-electron-build-resources": "cross-env node build/scripts/provide-electron-build-resources.js --build_resources_dir=../build_resources --backend_dir=../service --llamacpp_dir=../LlamaCPP --target_dir=./external", "prepare-build": "cross-env npm run prepare-python-env && npm run provide-electron-build-resources -- --python_env_dir=../build-envs/online/prototype-python-env", - "build": "node ./build/scripts/patch-nsis-template.js && cross-env-shell VITE_PLATFORM_TITLE=\"for Intel® Arc™\" \"vue-tsc && vite build && electron-builder --config build/build-config.json --win --x64\"" + "build": "node ./build/scripts/patch-nsis-template.js && cross-env-shell VITE_PLATFORM_TITLE=\"for Intel® Arc™\" \"vue-tsc && vite build && electron-builder --config build/build-config.json --win --x64\"", + "lint:eslint": "eslint . --fix", + "lint": "run-s lint:*", + "format": "prettier --write src/" }, "dependencies": { "@radix-icons/vue": "^1.0.0", @@ -42,12 +45,18 @@ "@types/node": "^20.17.10", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools": "^7.6.8", + "@vue/eslint-config-prettier": "^10.1.0", + "@vue/eslint-config-typescript": "^14.3.0", "adm-zip": "^0.5.16", "electron": "^33.2.1", "electron-builder": "^25.1.8", "electron-builder-squirrel-windows": "^25.1.8", + "eslint": "^9.18.0", + "eslint-plugin-oxlint": "^0.15.6", + "eslint-plugin-vue": "^9.32.0", "postcss": "^8.4.49", "postcss-import": "^16.1.0", + "prettier": "^3.4.2", "tailwindcss": "^3.4.16", "typescript": "^5.7.2", "vite": "^5.4.11", From 4f7572d7608655d3735d02b01ef19608f568d847 Mon Sep 17 00:00:00 2001 From: Markus Schuettler Date: Fri, 24 Jan 2025 16:55:36 +0100 Subject: [PATCH 2/4] =?UTF-8?q?fix=20most=20eslint=20error=20(and=20ignore?= =?UTF-8?q?=20some=20others=20=F0=9F=99=88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Markus Schuettler --- .../provide-electron-build-resources.js | 9 +- WebUI/electron/electron-env.d.ts | 155 +- WebUI/electron/logging/logger.ts | 194 +-- WebUI/electron/main.ts | 1292 ++++++++------- WebUI/electron/preload.ts | 150 +- .../electron/subprocesses/aiBackendService.ts | 2 +- .../subprocesses/comfyUIBackendService.ts | 8 +- .../subprocesses/llamaCppBackendService.ts | 2 +- .../electron/subprocesses/osProcessHelper.ts | 2 +- WebUI/electron/subprocesses/service.ts | 21 +- WebUI/package-lock.json | 1417 ++++++++++++++++- WebUI/package.json | 1 + WebUI/src/App.vue | 2 +- WebUI/src/assets/js/clientAPI.ts | 5 - WebUI/src/assets/js/const.ts | 2 - WebUI/src/assets/js/markdownParser.ts | 4 +- WebUI/src/assets/js/store/backendServices.ts | 4 +- WebUI/src/assets/js/store/comfyUi.ts | 10 +- WebUI/src/assets/js/store/globalSetup.ts | 28 +- WebUI/src/assets/js/store/i18n.ts | 2 +- WebUI/src/assets/js/store/imageGeneration.ts | 4 +- WebUI/src/assets/js/store/models.ts | 2 +- WebUI/src/assets/js/store/stableDiffusion.ts | 10 +- WebUI/src/assets/js/store/textInference.ts | 4 +- WebUI/src/assets/js/toast.ts | 4 +- WebUI/src/assets/js/util.ts | 13 +- WebUI/src/components/DownloadDialog.vue | 22 +- WebUI/src/components/DropSelector.vue | 12 +- WebUI/src/components/FolderSelector.vue | 2 +- .../src/components/InstallationManagement.vue | 4 +- WebUI/src/components/LoadingBar.vue | 2 +- WebUI/src/components/ModelDropDownItem.vue | 1 + WebUI/src/components/OutpaintOptions.vue | 2 - WebUI/src/components/Rag.vue | 4 +- WebUI/src/components/SettingsBasic.vue | 8 +- .../components/SettingsImageComfyDynamic.vue | 2 +- .../SettingsImageWorkflowSelector.vue | 6 +- WebUI/src/components/SettingsModel.vue | 9 +- WebUI/src/components/SlideBar.vue | 4 +- WebUI/src/components/TextToImage.vue | 2 +- .../src/components/ui/loadImage/LoadImage.vue | 2 +- .../components/ui/slider/ResolutionPicker.vue | 6 +- WebUI/src/env.d.ts | 4 + WebUI/src/toast.d.ts | 4 +- WebUI/src/views/Answer.vue | 23 +- WebUI/src/views/AppSettings.vue | 2 +- WebUI/src/views/Create.vue | 6 +- WebUI/src/views/Enhance.vue | 14 +- WebUI/src/vite-env.d.ts | 3 +- WebUI/vite.config.ts | 215 ++- 50 files changed, 2500 insertions(+), 1206 deletions(-) diff --git a/WebUI/build/scripts/provide-electron-build-resources.js b/WebUI/build/scripts/provide-electron-build-resources.js index b357ec7..c8b42bd 100644 --- a/WebUI/build/scripts/provide-electron-build-resources.js +++ b/WebUI/build/scripts/provide-electron-build-resources.js @@ -57,17 +57,10 @@ function copyFiles(targetDir, ...files) { } } -function copyDirectories(targetDir, ...dirs) { - for (const dir of dirs) { - fs.cpSync(dir, path.join(targetDir, path.basename(dir)), { recursive: true }); - console.log('Copied:', dir, 'to:', path.join(targetDir, path.basename(dir))); - } -} - function clearPreviousZip(zipFilePath) { if (fs.existsSync(zipFilePath)) { - console.log('Removing previous zip file:', zipFilePath)< + console.log('Removing previous zip file:', zipFilePath) fs.rmSync(zipFilePath, { recursive: true }); } } diff --git a/WebUI/electron/electron-env.d.ts b/WebUI/electron/electron-env.d.ts index 8d9557c..760a79e 100644 --- a/WebUI/electron/electron-env.d.ts +++ b/WebUI/electron/electron-env.d.ts @@ -1,77 +1,78 @@ -/// - -declare namespace NodeJS { - interface ProcessEnv { - /** - * The built directory structure - * - * ```tree - * ├─┬─┬ dist - * │ │ └── index.html - * │ │ - * │ ├─┬ dist-electron - * │ │ ├── main.js - * │ │ └── preload.js - * │ - * ``` - */ - DIST: string; - /** /dist/ or /public/ */ - VITE_PUBLIC: string; - } -} - -// Used in Renderer process, expose in `preload.ts` -interface Window { - ipcRenderer: import("electron").IpcRenderer; -} - -type KVObject = { - [key: string]: any; -}; - -type Theme = 'dark' | 'lnl' | 'bmg'; - -type LocalSettings = { - debug: number; - comfyUiParameters?:string[]; -} & KVObject; - -type ThemeSettings = { - availableThemes: Theme[]; - currentTheme: Theme; -} - -type ModelPaths = { - llm: string, - embedding: string, - stableDiffusion: string, - inpaint: string, - lora: string, - vae: string, -} & StringKV - -type ModelLists = { - llm: string[], - stableDiffusion: string[], - lora: string[], - vae: string[], - scheduler: string[], - embedding: string[], - inpaint: string[] -} & { [key: string]: Array } - -type SetupData = { - modelPaths: ModelPaths, - modelLists: ModelLists, - isAdminExec:boolean, - version:string, -} - -type UpdateWorkflowsFromIntelResult = { - success: boolean - backupDir: string -} - -type BackendStatus = 'notYetStarted' | 'starting' | 'running' | 'stopped' | 'stopping' | 'failed' | 'notInstalled' | 'installationFailed' | 'installing' | 'uninitializedStatus' - +/// + +declare namespace NodeJS { + interface ProcessEnv { + /** + * The built directory structure + * + * ```tree + * ├─┬─┬ dist + * │ │ └── index.html + * │ │ + * │ ├─┬ dist-electron + * │ │ ├── main.js + * │ │ └── preload.js + * │ + * ``` + */ + DIST: string; + /** /dist/ or /public/ */ + VITE_PUBLIC: string; + } +} + +// Used in Renderer process, expose in `preload.ts` +interface Window { + ipcRenderer: import("electron").IpcRenderer; +} + +type KVObject = { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; +}; + +type Theme = 'dark' | 'lnl' | 'bmg'; + +type LocalSettings = { + debug: number; + comfyUiParameters?:string[]; +} & KVObject; + +type ThemeSettings = { + availableThemes: Theme[]; + currentTheme: Theme; +} + +type ModelPaths = { + llm: string, + embedding: string, + stableDiffusion: string, + inpaint: string, + lora: string, + vae: string, +} & StringKV + +type ModelLists = { + llm: string[], + stableDiffusion: string[], + lora: string[], + vae: string[], + scheduler: string[], + embedding: string[], + inpaint: string[] +} & { [key: string]: Array } + +type SetupData = { + modelPaths: ModelPaths, + modelLists: ModelLists, + isAdminExec:boolean, + version:string, +} + +type UpdateWorkflowsFromIntelResult = { + success: boolean + backupDir: string +} + +type BackendStatus = 'notYetStarted' | 'starting' | 'running' | 'stopped' | 'stopping' | 'failed' | 'notInstalled' | 'installationFailed' | 'installing' | 'uninitializedStatus' + diff --git a/WebUI/electron/logging/logger.ts b/WebUI/electron/logging/logger.ts index 8b3d211..a83c24e 100644 --- a/WebUI/electron/logging/logger.ts +++ b/WebUI/electron/logging/logger.ts @@ -1,97 +1,97 @@ -import WebContents = Electron.WebContents; -import fs from "fs"; -import path from "node:path"; -import {app} from "electron"; - - -class Logger { - webContents: WebContents | null = null - private pathToLogFiles: string = path.resolve(app.isPackaged ? process.resourcesPath : path.join(__dirname, "../../external/")); - private startupMessageCache: { - message: string, - source: string, - level: 'error' | 'warn' | 'info' - }[] = [] - - constructor() { - } - - onWebcontentReady(webContents: WebContents) { - this.webContents = webContents - this.startupMessageCache.forEach((logEntry) => { - this.webContents!.send('debugLog', logEntry) - }); - this.startupMessageCache = [] - } - - info(message: string, source: string, alsoLogToFile: boolean = false) { - if (alsoLogToFile) { - this.logMessageToFile(message, source) - } - console.info(`[${source}]: ${message}`); - if (this.webContents) { - try { - this.webContents.send('debugLog', {level: 'info', source, message}) - } catch (error) { - console.error('Could not send debug log to renderer process'); - } - } else { - this.startupMessageCache.push({level: 'info', source, message}) - } - } - - warn(message: string, source: string, alsoLogToFile: boolean = false) { - if (alsoLogToFile) { - this.logMessageToFile(message, source) - } - console.warn(`[${source}]: ${message}`); - if (this.webContents) { - try { - this.webContents.send('debugLog', {level: 'warn', source, message}) - } catch (error) { - console.error('Could not send debug log to renderer process'); - } - } else { - this.startupMessageCache.push({level: 'error', source, message}) - } - } - - error(message: string, source: string, alsoLogToFile: boolean = false) { - if (alsoLogToFile) { - this.logMessageToFile(message, source) - } - - console.error(`[${source}]: ${message}`); - - if (this.webContents) { - try { - this.webContents.send('debugLog', {level: 'error', source, message}) - } catch (error) { - console.error('Could not send debug log to renderer process'); - } - } else { - this.startupMessageCache.push({level: 'error', source, message}) - } - } - - - logMessageToFile(message: string, source: string) { - const fileName = `${this.getDebugFileName()}.log` - const currentDate = new Date(); - const hours = currentDate.getHours().toString().padStart(2, '0'); - const minutes = currentDate.getMinutes().toString().padStart(2, '0'); - const seconds = currentDate.getSeconds().toString().padStart(2, '0'); - - const formattedTime = `${hours}:${minutes}:${seconds}`; - const logMessage = `${formattedTime}|${source}|${message}` - fs.appendFileSync(path.join(this.pathToLogFiles, fileName), logMessage + "\r\n"); - } - - getDebugFileName(): string { - const currentDate = new Date(); - const formattedDate = currentDate.toISOString().split('T')[0]; - return `aip-${formattedDate}` - } -} - -export const appLoggerInstance = new Logger() +import WebContents = Electron.WebContents; +import fs from "fs"; +import path from "node:path"; +import {app} from "electron"; + + +class Logger { + webContents: WebContents | null = null + private pathToLogFiles: string = path.resolve(app.isPackaged ? process.resourcesPath : path.join(__dirname, "../../external/")); + private startupMessageCache: { + message: string, + source: string, + level: 'error' | 'warn' | 'info' + }[] = [] + + constructor() { + } + + onWebcontentReady(webContents: WebContents) { + this.webContents = webContents + this.startupMessageCache.forEach((logEntry) => { + this.webContents!.send('debugLog', logEntry) + }); + this.startupMessageCache = [] + } + + info(message: string, source: string, alsoLogToFile: boolean = false) { + if (alsoLogToFile) { + this.logMessageToFile(message, source) + } + console.info(`[${source}]: ${message}`); + if (this.webContents) { + try { + this.webContents.send('debugLog', {level: 'info', source, message}) + } catch (_error) { + console.error('Could not send debug log to renderer process'); + } + } else { + this.startupMessageCache.push({level: 'info', source, message}) + } + } + + warn(message: string, source: string, alsoLogToFile: boolean = false) { + if (alsoLogToFile) { + this.logMessageToFile(message, source) + } + console.warn(`[${source}]: ${message}`); + if (this.webContents) { + try { + this.webContents.send('debugLog', {level: 'warn', source, message}) + } catch (_error) { + console.error('Could not send debug log to renderer process'); + } + } else { + this.startupMessageCache.push({level: 'error', source, message}) + } + } + + error(message: string, source: string, alsoLogToFile: boolean = false) { + if (alsoLogToFile) { + this.logMessageToFile(message, source) + } + + console.error(`[${source}]: ${message}`); + + if (this.webContents) { + try { + this.webContents.send('debugLog', {level: 'error', source, message}) + } catch (_error) { + console.error('Could not send debug log to renderer process'); + } + } else { + this.startupMessageCache.push({level: 'error', source, message}) + } + } + + + logMessageToFile(message: string, source: string) { + const fileName = `${this.getDebugFileName()}.log` + const currentDate = new Date(); + const hours = currentDate.getHours().toString().padStart(2, '0'); + const minutes = currentDate.getMinutes().toString().padStart(2, '0'); + const seconds = currentDate.getSeconds().toString().padStart(2, '0'); + + const formattedTime = `${hours}:${minutes}:${seconds}`; + const logMessage = `${formattedTime}|${source}|${message}` + fs.appendFileSync(path.join(this.pathToLogFiles, fileName), logMessage + "\r\n"); + } + + getDebugFileName(): string { + const currentDate = new Date(); + const formattedDate = currentDate.toISOString().split('T')[0]; + return `aip-${formattedDate}` + } +} + +export const appLoggerInstance = new Logger() diff --git a/WebUI/electron/main.ts b/WebUI/electron/main.ts index be0ff7e..db2ae2c 100644 --- a/WebUI/electron/main.ts +++ b/WebUI/electron/main.ts @@ -1,653 +1,639 @@ -import { - app, - BrowserWindow, - dialog, - ipcMain, - IpcMainEvent, - IpcMainInvokeEvent, - MessageBoxOptions, - MessageBoxSyncOptions, - OpenDialogSyncOptions, - screen, - shell, -} from "electron"; -import path from "node:path"; -import fs from "fs"; -import {exec} from "node:child_process"; -import {randomUUID} from "node:crypto"; -import koffi from 'koffi'; -import sudo from "sudo-prompt"; -import {PathsManager} from "./pathsManager"; -import {appLoggerInstance} from "./logging/logger.ts"; -import {aiplaygroundApiServiceRegistry, ApiServiceRegistryImpl} from "./subprocesses/apiServiceRegistry"; -import {updateIntelWorkflows} from "./subprocesses/updateIntelWorkflows.ts"; - - -// } -// The built directory structure -// -// ├─┬─┬ dist -// │ │ └── index.html -// │ │ -// │ ├─┬ dist-electron -// │ │ ├── main.js -// │ │ └── preload.js -// │ -process.env.DIST = path.join(__dirname, "../"); -process.env.VITE_PUBLIC = path.join(__dirname, app.isPackaged ? "../.." : "../../../public"); - - -const resourcesBaseDir = app.isPackaged ? process.resourcesPath : path.join(__dirname, "../../../"); -const externalRes = path.resolve(app.isPackaged ? process.resourcesPath : path.join(__dirname, "../../external/")); -const singleInstanceLock = app.requestSingleInstanceLock(); - -const appLogger = appLoggerInstance - -let win: BrowserWindow | null; -let serviceRegistry: ApiServiceRegistryImpl | null = null - -// 🚧 Use ['ENV_NAME'] avoid vite:define plugin - Vite@2.x -const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]; -// const APP_TOOL_HEIGHT = 209; -const appSize = { - width: 820, - height: 128, - maxChatContentHeight: 0, -}; - -export const settings: LocalSettings = { - isAdminExec: false, - debug: 0, - availableThemes: ["dark", "lnl"], - currentTheme: "lnl", - comfyUiParameters: [], -}; - -async function loadSettings() { - const settingPath = app.isPackaged - ? path.join(process.resourcesPath, "settings.json") - : path.join(__dirname, "../../external/settings-dev.json"); - - appLogger.info(`loading settings from ${settingPath}`, 'electron-backend'); - if (fs.existsSync(settingPath)) { - const loadSettings = JSON.parse( - fs.readFileSync(settingPath, {encoding: "utf8"}) - ); - Object.keys(loadSettings).forEach((key) => { - if (key in settings) { - settings[key] = loadSettings[key]; - } - }); - } - appLogger.info(`settings loaded: ${JSON.stringify({settings})}`, 'electron-backend'); - - return settings; -} - -async function createWindow() { - win = new BrowserWindow({ - title: "AI PLAYGROUND", - icon: path.join(process.env.VITE_PUBLIC, "app-ico.svg"), - transparent: false, - resizable: true, - frame: false, - // fullscreen: true, - width: 1440, - height: 951, - webPreferences: { - preload: path.join(__dirname, "../preload/preload.js"), - contextIsolation: true - }, - }); - win.webContents.on('did-finish-load', () => { - setTimeout(() => { - appLogger.onWebcontentReady(win!.webContents) - }, 100); - }) - - const session = win.webContents.session; - - if (!app.isPackaged || settings.debug) { - //Open devTool if the app is not packaged - win.webContents.openDevTools({mode: "detach", activate: true}); - } - - session.webRequest.onBeforeSendHeaders((details, callback) => { - callback({ - requestHeaders: { - ...details.requestHeaders, - Origin: "*", - }, - }); - }); - session.webRequest.onHeadersReceived((details, callback) => { - if (details.url.match(/^http:\/\/(localhost|127.0.0.1)/)) { - // if (details.method === "OPTIONS") { - // details.statusLine = "HTTP/1.1 200 OK"; - // details.statusCode = 200; - // return callback(details); - // } - - details.responseHeaders = { - ...details.responseHeaders, - "Access-Control-Allow-Origin": ["*"], - "Access-Control-Allow-Methods": ["GET,POST"], - "Access-Control-Allow-Headers": ["x-requested-with,Content-Type,Authorization"], - } - callback(details); - } else { - return callback(details); - } - }); - - win.webContents.session.setPermissionRequestHandler( - (_, permission, callback) => { - if ( - permission === "media" || - permission === "clipboard-sanitized-write" - // permission === "clipboard-sanitized-write" - ) { - callback(true); - } else { - callback(false); - } - } - ); - - if (VITE_DEV_SERVER_URL) { - await win.loadURL(VITE_DEV_SERVER_URL); - appLogger.info("load url:" + VITE_DEV_SERVER_URL, 'electron-backend'); - } else { - await win.loadFile(path.join(process.env.DIST, "index.html")); - } - - // Make all links open with the browser, not with the application - win.webContents.setWindowOpenHandler(({url}) => { - if (url.startsWith("https:")) shell.openExternal(url); - return {action: "deny"}; - }); - return win; -} - - -app.on("quit", async () => { - if (singleInstanceLock) { - app.releaseSingleInstanceLock(); - } -}); -// Quit when all windows are closed, except on macOS. There, it's common -// for applications and their menu bar to stay active until the user quits -// explicitly with Cmd + Q. -app.on("window-all-closed", async () => { - try { - await serviceRegistry?.stopAllServices() - } catch { - - } - if (process.platform !== "darwin") { - app.quit(); - win = null; - } -}); - -app.on("activate", () => { - // On OS X it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) { - createWindow(); - } -}); - -app.on('second-instance', (event, commandLine, workingDirectory) => { - if (win && !win.isDestroyed()) { - if (win.isMinimized()) { - win.restore(); - } - win.focus(); - } -}); - -async function initServiceRegistry(win: BrowserWindow, settings: LocalSettings) { - serviceRegistry = await aiplaygroundApiServiceRegistry(win, settings) - return serviceRegistry -} - -function initEventHandle() { - - screen.on("display-metrics-changed", (event, display, changedMetrics) => { - if (win) { - win.setBounds({ - x: 0, - y: 0, - width: display.workAreaSize.width, - height: display.workAreaSize.height, - }); - win.webContents.send( - "display-metrics-changed", - display.workAreaSize.width, - display.workAreaSize.height - ); - } - }); - - ipcMain.handle("getThemeSettings", async () => { - return { - availableThemes: settings.availableThemes, - currentTheme: settings.currentTheme - - }; - }); - - ipcMain.handle("getLocalSettings", async () => { - return { - showIndex: settings.showIndex, - showBenchmark: settings.showBenchmark, - isAdminExec: isAdmin(), - locale: app.getLocale(), - }; - }); - - ipcMain.handle("getWinSize", () => { - return appSize; - }); - - ipcMain.on("openUrl", (event, url: string) => { - return shell.openExternal(url); - }); - - ipcMain.handle( - "setWinSize", - (event: IpcMainInvokeEvent, width: number, height: number) => { - const win = BrowserWindow.fromWebContents(event.sender)!; - const winRect = win.getBounds(); - if (winRect.width != width || winRect.height != height) { - const y = winRect.y + (winRect.height - height); - win.setBounds({x: winRect.x, y, width, height}); - } - } - ); - - ipcMain.handle( - "restorePathsSettings", - (event: IpcMainInvokeEvent) => { - const paths = app.isPackaged ? { - "llm": "./resources/service/models/llm/checkpoints", - "embedding": "./resources/service/models/llm/embedding", - "stableDiffusion": "./resources/service/models/stable_diffusion/checkpoints", - "inpaint": "./resources/service/models/stable_diffusion/inpaint", - "lora": "./resources/service/models/stable_diffusion/lora", - "vae": "./resources/service/models/stable_diffusion/vae" - } : { - "llm": "../service/models/llm/checkpoints", - "embedding": "../service/models/llm/embedding", - "stableDiffusion": "../service/models/stable_diffusion/checkpoints", - "inpaint": "../service/models/stable_diffusion/inpaint", - "lora": "../service/models/stable_diffusion/lora", - "vae": "../service/models/stable_diffusion/vae" - } - pathsManager.updateModelPahts(paths); - } - ); - - - ipcMain.on("miniWindow", () => { - if (win) { - win.minimize(); - } - }); - - ipcMain.on("setFullScreen", (event: IpcMainEvent, enable: boolean) => { - if (win) { - win.setFullScreen(enable); - } - }); - - ipcMain.on("exitApp", async () => { - if (win) { - win.close(); - } - }); - - ipcMain.on("saveImage", async (event: IpcMainEvent, url: string) => { - const win = BrowserWindow.fromWebContents(event.sender); - if (!win) { - return; - } - const options = { - title: "Save Image", - defaultPath: path.join(app.getPath("documents"), "example.png"), - filters: [{name: "AIGC-Gennerate.png", extensions: ["png"]}], - }; - - try { - const result = await dialog - .showSaveDialog(win, options); - if (!result.canceled && result.filePath) { - if (fs.existsSync(result.filePath)) { - fs.rmSync(result.filePath); - } - try { - const response = await fetch(url); - const arrayBuffer = await response.arrayBuffer(); - const buffer = Buffer.from(arrayBuffer); - fs.writeFileSync(result.filePath, buffer); - appLogger.info(`File downloaded and saved: ${result.filePath}`, 'electron-backend'); - } catch (error) { - appLogger.error(`Download and save error: ${JSON.stringify(error, Object.getOwnPropertyNames, 2)}`, 'electron-backend'); - } - } - } catch (error) { - appLogger.error(`${JSON.stringify(error, Object.getOwnPropertyNames, 2)}`, 'electron-backend'); - } - }); - - ipcMain.handle("showOpenDialog", async (event, options: OpenDialogSyncOptions) => { - const win = BrowserWindow.fromWebContents(event.sender)!; - return await dialog - .showOpenDialog(win, options); - }); - - ipcMain.handle("showMessageBox", async (event, options: MessageBoxOptions) => { - const win = BrowserWindow.fromWebContents(event.sender)!; - return dialog.showMessageBox(win, options); - }); - - - ipcMain.handle("showMessageBoxSync", async (event, options: MessageBoxSyncOptions) => { - const win = BrowserWindow.fromWebContents(event.sender)!; - return dialog.showMessageBoxSync(win, options); - }); - - - ipcMain.handle("existsPath", async (event, path: string) => { - const win = BrowserWindow.fromWebContents(event.sender); - if (!win) { - return; - } - return fs.existsSync(path); - }); - - let pathsManager = new PathsManager(path.join(externalRes, app.isPackaged ? "model_config.json" : "model_config.dev.json")); - - ipcMain.handle("getInitSetting", (event) => { - const win = BrowserWindow.fromWebContents(event.sender); - if (!win) { - return; - } - return { - modelLists: pathsManager.sacanAll(), - modelPaths: pathsManager.modelPaths, - isAdminExec: settings.isAdminExec, - version: app.getVersion() - }; - - }); - - ipcMain.handle("updateModelPaths", (event, modelPaths: ModelPaths) => { - pathsManager.updateModelPahts(modelPaths); - return pathsManager.sacanAll(); - }); - - ipcMain.handle("refreshSDModles", (event) => { - return pathsManager.scanSDModleLists(); - }); - - ipcMain.handle("refreshInpaintModles", (event) => { - return pathsManager.scanInpaint(); - }); - - ipcMain.handle("refreshLora", (event) => { - return pathsManager.scanLora(); - }); - - ipcMain.handle("refreshLLMModles", (event) => { - return pathsManager.scanLLMModles(); - }); - - ipcMain.handle("refreshEmbeddingModels", (event) => { - return pathsManager.scanEmbedding(); - }); - - ipcMain.handle("getDownloadedDiffusionModels", (event) => { - return pathsManager.scanSDModleLists(false); - }); - - ipcMain.handle("getDownloadedInpaintModels", (event) => { - return pathsManager.scanInpaint(false); - }); - - ipcMain.handle("getDownloadedLoras", (event) => { - return pathsManager.scanLora(false); - }); - - ipcMain.handle("getDownloadedLLMs", (event) => { - return pathsManager.scanLLMModles(); - }); - - ipcMain.handle("getDownloadedGGUFLLMs", (event) => { - return pathsManager.scanGGUFLLMModels(); - }); - - ipcMain.handle("getDownloadedEmbeddingModels", (event) => { - return pathsManager.scanEmbedding(false); - }); - - ipcMain.on("openDevTools", () => { - win?.webContents.openDevTools({mode: "detach", activate: true}); - }); - - ipcMain.handle("getServices", () => { - if(!serviceRegistry) { - appLogger.warn('frontend tried to getServices too early during aipg startup', 'electron-backend'); - return [];} - return serviceRegistry.getServiceInformation() - }); - - ipcMain.handle("sendStartSignal", (event: IpcMainInvokeEvent, serviceName: string) => { - if(!serviceRegistry) { - appLogger.warn('received start signal too early during aipg startup', 'electron-backend'); - return;} - const service = serviceRegistry.getService(serviceName); - if(!service) { - appLogger.warn(`Tried to start service ${serviceName} which is not known`, 'electron-backend') - return; - } - return service.start() - }); - ipcMain.handle("sendStopSignal", (event: IpcMainInvokeEvent, serviceName: string) => { - if(!serviceRegistry) { - appLogger.warn('received stop signal too early during aipg startup', 'electron-backend'); - return;} - const service = serviceRegistry.getService(serviceName); - if(!service) { - appLogger.warn(`Tried to stop service ${serviceName} which is not known`, 'electron-backend') - return; - } - return service.stop() - }); - ipcMain.handle("sendSetUpSignal", async (event: IpcMainInvokeEvent, serviceName: string) => { - if(!serviceRegistry || !win) { - appLogger.warn('received setup signal too early during aipg startup', 'electron-backend'); - return;} - const service = serviceRegistry.getService(serviceName); - if(!service) { - appLogger.warn(`Tried to set up service ${serviceName} which is not known`, 'electron-backend') - return; - } - - for await (const progressUpdate of service.set_up()) { - win.webContents.send('serviceSetUpProgress', progressUpdate) - if (progressUpdate.status === "failed" || progressUpdate.status === "success") { - appLogger.info(`Received terminal progress update for set up request for ${serviceName}`, 'electron-backend') - break - } - } - }); - - - ipcMain.handle("reloadImageWorkflows", () => { - const files = fs.readdirSync(path.join(externalRes, "workflows")); - const workflows = files.map((file) => fs.readFileSync(path.join(externalRes, "workflows", file), {encoding: "utf-8"})); - return workflows; - }); - - ipcMain.handle("updateWorkflowsFromIntelRepo", () => { - return updateIntelWorkflows() - }); - - const getImagePathFromUrl = (url: string) => { - const imageUrl = URL.parse(url) - if (!imageUrl) { - console.error('Could not find image for URL', {url}) - return; - } - const aiBackendUrl = serviceRegistry?.getService('ai-backend')?.baseUrl - const backend = (aiBackendUrl && url.includes(aiBackendUrl)) ? 'service' : 'ComfyUI'; - - let imagePath: string; - if (backend === 'service') { - imagePath = imageUrl.pathname.replace(/^\/*/, '') - } else { - const s = imageUrl.searchParams; - imagePath = `static/sd_out/${s.get('filename')}` - } - - return path.join(externalRes, 'service', imagePath); - } - - ipcMain.on("openImageWithSystem", (event, url: string) => { - const imagePath = getImagePathFromUrl(url); - if (!imagePath) return; - shell.openPath(imagePath) - }); - - ipcMain.on("selecteImage", (event, url: string) => { - const imagePath = getImagePathFromUrl(url); - if (!imagePath) return; - - // Open the image with the default system image viewer - if (process.platform === 'win32') { - exec(`explorer.exe /select, "${imagePath}"`); - } else { - shell.showItemInFolder(imagePath) - } - - }) - -} - - -ipcMain.on("openImageWin", (_: IpcMainEvent, url: string, title: string, width: number, height: number) => { - const display = screen.getPrimaryDisplay(); - width += 32; - height += 48; - if (width > display.workAreaSize.width) { - width = display.workAreaSize.width; - } else if (height > display.workAreaSize.height) { - height = display.workAreaSize.height; - } - const imgWin = new BrowserWindow({ - icon: path.join(process.env.VITE_PUBLIC, "app-ico.svg"), - resizable: true, - center: true, - frame: true, - width: width, - height: height, - autoHideMenuBar: true, - show: false, - parent: win || undefined, - webPreferences: { - devTools: false - } - }); - imgWin.setMenu(null); - imgWin.loadURL(url); - imgWin.once("ready-to-show", function () { - imgWin.show(); - imgWin.setTitle(title); - }); -}); - -ipcMain.handle('showSaveDialog', async (event, options: Electron.SaveDialogOptions) => { - dialog.showSaveDialog(options).then(result => { - return result; - }).catch(error => { - appLogger.error(`${JSON.stringify(error, Object.getOwnPropertyNames, 2)}`, 'electron-backend'); - }); -}); - -function needAdminPermission() { - return new Promise((resolve) => { - const filename = path.join(externalRes, `${randomUUID()}.txt`); - fs.writeFile(filename, '', (err) => { - if (err) { - if (err && err.code == 'EPERM') { - if (path.parse(externalRes).root == path.parse(process.env.windir!).root) { - resolve && resolve(!isAdmin()); - } - } else { - resolve && resolve(false); - } - } else { - fs.rmSync(filename); - resolve && resolve(false); - } - }); - }) -} - -function isAdmin(): boolean { - const lib = koffi.load("Shell32.dll"); - try { - const IsUserAnAdmin = lib.func("IsUserAnAdmin", "bool", []); - return IsUserAnAdmin(); - } finally { - lib.unload(); - } -} - -async function setupPyenv() { - const iterable: AsyncIterable = serviceRegistry!.getService("ai-backend")!.set_up() - try { - for await (const value of iterable) { - appLogger.info(`reported progress: ${value.step}|${value.status}|${value.debugMessage}`, 'electron-backend'); - } - } catch (e) { - appLogger.warn(`caught error: ${e}`, 'electron-backend'); - } - -} - -app.whenReady().then(async () => { - /* - The current user does not have write permission for files in the program directory and is not an administrator. - Close the current program and let the user start the program with administrator privileges - */ - if (await needAdminPermission()) { - if (singleInstanceLock) { - app.releaseSingleInstanceLock(); - } - //It is possible that the program is installed in a directory that requires administrator privileges - const message = `start "" "${process.argv.join(' ').trim()}`; - sudo.exec(message, (err, stdout, stderr) => { - app.exit(0); - }); - return; - } - - - /**Single instance processing */ - if (!singleInstanceLock) { - dialog.showMessageBoxSync({ - message: app.getLocale() == "zh-CN" ? "本程序仅允许单实例运行,确认后本次运行将自动结束" : "This program only allows a single instance to run, and the run will automatically end after confirmation", - title: "error", - type: "error" - }); - app.exit(); - } else { - const settings = await loadSettings(); - initEventHandle(); - const window = await createWindow(); - await initServiceRegistry(window, settings); - } -}); +import { + app, + BrowserWindow, + dialog, + ipcMain, + IpcMainEvent, + IpcMainInvokeEvent, + MessageBoxOptions, + MessageBoxSyncOptions, + OpenDialogSyncOptions, + screen, + shell, +} from "electron"; +import path from "node:path"; +import fs from "fs"; +import {exec} from "node:child_process"; +import {randomUUID} from "node:crypto"; +import koffi from 'koffi'; +import sudo from "sudo-prompt"; +import {PathsManager} from "./pathsManager"; +import {appLoggerInstance} from "./logging/logger.ts"; +import {aiplaygroundApiServiceRegistry, ApiServiceRegistryImpl} from "./subprocesses/apiServiceRegistry"; +import {updateIntelWorkflows} from "./subprocesses/updateIntelWorkflows.ts"; + + +// } +// The built directory structure +// +// ├─┬─┬ dist +// │ │ └── index.html +// │ │ +// │ ├─┬ dist-electron +// │ │ ├── main.js +// │ │ └── preload.js +// │ +process.env.DIST = path.join(__dirname, "../"); +process.env.VITE_PUBLIC = path.join(__dirname, app.isPackaged ? "../.." : "../../../public"); + +const externalRes = path.resolve(app.isPackaged ? process.resourcesPath : path.join(__dirname, "../../external/")); +const singleInstanceLock = app.requestSingleInstanceLock(); + +const appLogger = appLoggerInstance + +let win: BrowserWindow | null; +let serviceRegistry: ApiServiceRegistryImpl | null = null + +// 🚧 Use ['ENV_NAME'] avoid vite:define plugin - Vite@2.x +const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]; +// const APP_TOOL_HEIGHT = 209; +const appSize = { + width: 820, + height: 128, + maxChatContentHeight: 0, +}; + +export const settings: LocalSettings = { + isAdminExec: false, + debug: 0, + availableThemes: ["dark", "lnl"], + currentTheme: "lnl", + comfyUiParameters: [], +}; + +async function loadSettings() { + const settingPath = app.isPackaged + ? path.join(process.resourcesPath, "settings.json") + : path.join(__dirname, "../../external/settings-dev.json"); + + appLogger.info(`loading settings from ${settingPath}`, 'electron-backend'); + if (fs.existsSync(settingPath)) { + const loadSettings = JSON.parse( + fs.readFileSync(settingPath, {encoding: "utf8"}) + ); + Object.keys(loadSettings).forEach((key) => { + if (key in settings) { + settings[key] = loadSettings[key]; + } + }); + } + appLogger.info(`settings loaded: ${JSON.stringify({settings})}`, 'electron-backend'); + + return settings; +} + +async function createWindow() { + win = new BrowserWindow({ + title: "AI PLAYGROUND", + icon: path.join(process.env.VITE_PUBLIC, "app-ico.svg"), + transparent: false, + resizable: true, + frame: false, + // fullscreen: true, + width: 1440, + height: 951, + webPreferences: { + preload: path.join(__dirname, "../preload/preload.js"), + contextIsolation: true + }, + }); + win.webContents.on('did-finish-load', () => { + setTimeout(() => { + appLogger.onWebcontentReady(win!.webContents) + }, 100); + }) + + const session = win.webContents.session; + + if (!app.isPackaged || settings.debug) { + //Open devTool if the app is not packaged + win.webContents.openDevTools({mode: "detach", activate: true}); + } + + session.webRequest.onBeforeSendHeaders((details, callback) => { + callback({ + requestHeaders: { + ...details.requestHeaders, + Origin: "*", + }, + }); + }); + session.webRequest.onHeadersReceived((details, callback) => { + if (details.url.match(/^http:\/\/(localhost|127.0.0.1)/)) { + // if (details.method === "OPTIONS") { + // details.statusLine = "HTTP/1.1 200 OK"; + // details.statusCode = 200; + // return callback(details); + // } + + details.responseHeaders = { + ...details.responseHeaders, + "Access-Control-Allow-Origin": ["*"], + "Access-Control-Allow-Methods": ["GET,POST"], + "Access-Control-Allow-Headers": ["x-requested-with,Content-Type,Authorization"], + } + callback(details); + } else { + return callback(details); + } + }); + + win.webContents.session.setPermissionRequestHandler( + (_, permission, callback) => { + if ( + permission === "media" || + permission === "clipboard-sanitized-write" + // permission === "clipboard-sanitized-write" + ) { + callback(true); + } else { + callback(false); + } + } + ); + + if (VITE_DEV_SERVER_URL) { + await win.loadURL(VITE_DEV_SERVER_URL); + appLogger.info("load url:" + VITE_DEV_SERVER_URL, 'electron-backend'); + } else { + await win.loadFile(path.join(process.env.DIST, "index.html")); + } + + // Make all links open with the browser, not with the application + win.webContents.setWindowOpenHandler(({url}) => { + if (url.startsWith("https:")) shell.openExternal(url); + return {action: "deny"}; + }); + return win; +} + + +app.on("quit", async () => { + if (singleInstanceLock) { + app.releaseSingleInstanceLock(); + } +}); +// Quit when all windows are closed, except on macOS. There, it's common +// for applications and their menu bar to stay active until the user quits +// explicitly with Cmd + Q. +app.on("window-all-closed", async () => { + try { + await serviceRegistry?.stopAllServices() + } catch { + + } + if (process.platform !== "darwin") { + app.quit(); + win = null; + } +}); + +app.on("activate", () => { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } +}); + +app.on('second-instance', (_event, _commandLine, _workingDirectory) => { + if (win && !win.isDestroyed()) { + if (win.isMinimized()) { + win.restore(); + } + win.focus(); + } +}); + +async function initServiceRegistry(win: BrowserWindow, settings: LocalSettings) { + serviceRegistry = await aiplaygroundApiServiceRegistry(win, settings) + return serviceRegistry +} + +function initEventHandle() { + + screen.on("display-metrics-changed", (_event, display, _changedMetrics) => { + if (win) { + win.setBounds({ + x: 0, + y: 0, + width: display.workAreaSize.width, + height: display.workAreaSize.height, + }); + win.webContents.send( + "display-metrics-changed", + display.workAreaSize.width, + display.workAreaSize.height + ); + } + }); + + ipcMain.handle("getThemeSettings", async () => { + return { + availableThemes: settings.availableThemes, + currentTheme: settings.currentTheme + + }; + }); + + ipcMain.handle("getLocalSettings", async () => { + return { + showIndex: settings.showIndex, + showBenchmark: settings.showBenchmark, + isAdminExec: isAdmin(), + locale: app.getLocale(), + }; + }); + + ipcMain.handle("getWinSize", () => { + return appSize; + }); + + ipcMain.on("openUrl", (_event, url: string) => { + return shell.openExternal(url); + }); + + ipcMain.handle( + "setWinSize", + (event: IpcMainInvokeEvent, width: number, height: number) => { + const win = BrowserWindow.fromWebContents(event.sender)!; + const winRect = win.getBounds(); + if (winRect.width != width || winRect.height != height) { + const y = winRect.y + (winRect.height - height); + win.setBounds({x: winRect.x, y, width, height}); + } + } + ); + + ipcMain.handle( + "restorePathsSettings", + (_event: IpcMainInvokeEvent) => { + const paths = app.isPackaged ? { + "llm": "./resources/service/models/llm/checkpoints", + "embedding": "./resources/service/models/llm/embedding", + "stableDiffusion": "./resources/service/models/stable_diffusion/checkpoints", + "inpaint": "./resources/service/models/stable_diffusion/inpaint", + "lora": "./resources/service/models/stable_diffusion/lora", + "vae": "./resources/service/models/stable_diffusion/vae" + } : { + "llm": "../service/models/llm/checkpoints", + "embedding": "../service/models/llm/embedding", + "stableDiffusion": "../service/models/stable_diffusion/checkpoints", + "inpaint": "../service/models/stable_diffusion/inpaint", + "lora": "../service/models/stable_diffusion/lora", + "vae": "../service/models/stable_diffusion/vae" + } + pathsManager.updateModelPahts(paths); + } + ); + + + ipcMain.on("miniWindow", () => { + if (win) { + win.minimize(); + } + }); + + ipcMain.on("setFullScreen", (_event: IpcMainEvent, enable: boolean) => { + if (win) { + win.setFullScreen(enable); + } + }); + + ipcMain.on("exitApp", async () => { + if (win) { + win.close(); + } + }); + + ipcMain.on("saveImage", async (event: IpcMainEvent, url: string) => { + const win = BrowserWindow.fromWebContents(event.sender); + if (!win) { + return; + } + const options = { + title: "Save Image", + defaultPath: path.join(app.getPath("documents"), "example.png"), + filters: [{name: "AIGC-Gennerate.png", extensions: ["png"]}], + }; + + try { + const result = await dialog + .showSaveDialog(win, options); + if (!result.canceled && result.filePath) { + if (fs.existsSync(result.filePath)) { + fs.rmSync(result.filePath); + } + try { + const response = await fetch(url); + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + fs.writeFileSync(result.filePath, buffer); + appLogger.info(`File downloaded and saved: ${result.filePath}`, 'electron-backend'); + } catch (error) { + appLogger.error(`Download and save error: ${JSON.stringify(error, Object.getOwnPropertyNames, 2)}`, 'electron-backend'); + } + } + } catch (error) { + appLogger.error(`${JSON.stringify(error, Object.getOwnPropertyNames, 2)}`, 'electron-backend'); + } + }); + + ipcMain.handle("showOpenDialog", async (event, options: OpenDialogSyncOptions) => { + const win = BrowserWindow.fromWebContents(event.sender)!; + return await dialog + .showOpenDialog(win, options); + }); + + ipcMain.handle("showMessageBox", async (event, options: MessageBoxOptions) => { + const win = BrowserWindow.fromWebContents(event.sender)!; + return dialog.showMessageBox(win, options); + }); + + + ipcMain.handle("showMessageBoxSync", async (event, options: MessageBoxSyncOptions) => { + const win = BrowserWindow.fromWebContents(event.sender)!; + return dialog.showMessageBoxSync(win, options); + }); + + + ipcMain.handle("existsPath", async (event, path: string) => { + const win = BrowserWindow.fromWebContents(event.sender); + if (!win) { + return; + } + return fs.existsSync(path); + }); + + const pathsManager = new PathsManager(path.join(externalRes, app.isPackaged ? "model_config.json" : "model_config.dev.json")); + + ipcMain.handle("getInitSetting", (event) => { + const win = BrowserWindow.fromWebContents(event.sender); + if (!win) { + return; + } + return { + modelLists: pathsManager.sacanAll(), + modelPaths: pathsManager.modelPaths, + isAdminExec: settings.isAdminExec, + version: app.getVersion() + }; + + }); + + ipcMain.handle("updateModelPaths", (_event, modelPaths: ModelPaths) => { + pathsManager.updateModelPahts(modelPaths); + return pathsManager.sacanAll(); + }); + + ipcMain.handle("refreshSDModles", (_event) => { + return pathsManager.scanSDModleLists(); + }); + + ipcMain.handle("refreshInpaintModles", (_event) => { + return pathsManager.scanInpaint(); + }); + + ipcMain.handle("refreshLora", (_event) => { + return pathsManager.scanLora(); + }); + + ipcMain.handle("refreshLLMModles", (_event) => { + return pathsManager.scanLLMModles(); + }); + + ipcMain.handle("refreshEmbeddingModels", (_event) => { + return pathsManager.scanEmbedding(); + }); + + ipcMain.handle("getDownloadedDiffusionModels", (_event) => { + return pathsManager.scanSDModleLists(false); + }); + + ipcMain.handle("getDownloadedInpaintModels", (_event) => { + return pathsManager.scanInpaint(false); + }); + + ipcMain.handle("getDownloadedLoras", (_event) => { + return pathsManager.scanLora(false); + }); + + ipcMain.handle("getDownloadedLLMs", (_event) => { + return pathsManager.scanLLMModles(); + }); + + ipcMain.handle("getDownloadedGGUFLLMs", (_event) => { + return pathsManager.scanGGUFLLMModels(); + }); + + ipcMain.handle("getDownloadedEmbeddingModels", (_event) => { + return pathsManager.scanEmbedding(false); + }); + + ipcMain.on("openDevTools", () => { + win?.webContents.openDevTools({mode: "detach", activate: true}); + }); + + ipcMain.handle("getServices", () => { + if(!serviceRegistry) { + appLogger.warn('frontend tried to getServices too early during aipg startup', 'electron-backend'); + return [];} + return serviceRegistry.getServiceInformation() + }); + + ipcMain.handle("sendStartSignal", (_event: IpcMainInvokeEvent, serviceName: string) => { + if(!serviceRegistry) { + appLogger.warn('received start signal too early during aipg startup', 'electron-backend'); + return;} + const service = serviceRegistry.getService(serviceName); + if(!service) { + appLogger.warn(`Tried to start service ${serviceName} which is not known`, 'electron-backend') + return; + } + return service.start() + }); + ipcMain.handle("sendStopSignal", (_event: IpcMainInvokeEvent, serviceName: string) => { + if(!serviceRegistry) { + appLogger.warn('received stop signal too early during aipg startup', 'electron-backend'); + return;} + const service = serviceRegistry.getService(serviceName); + if(!service) { + appLogger.warn(`Tried to stop service ${serviceName} which is not known`, 'electron-backend') + return; + } + return service.stop() + }); + ipcMain.handle("sendSetUpSignal", async (_event: IpcMainInvokeEvent, serviceName: string) => { + if(!serviceRegistry || !win) { + appLogger.warn('received setup signal too early during aipg startup', 'electron-backend'); + return;} + const service = serviceRegistry.getService(serviceName); + if(!service) { + appLogger.warn(`Tried to set up service ${serviceName} which is not known`, 'electron-backend') + return; + } + + for await (const progressUpdate of service.set_up()) { + win.webContents.send('serviceSetUpProgress', progressUpdate) + if (progressUpdate.status === "failed" || progressUpdate.status === "success") { + appLogger.info(`Received terminal progress update for set up request for ${serviceName}`, 'electron-backend') + break + } + } + }); + + + ipcMain.handle("reloadImageWorkflows", () => { + const files = fs.readdirSync(path.join(externalRes, "workflows")); + const workflows = files.map((file) => fs.readFileSync(path.join(externalRes, "workflows", file), {encoding: "utf-8"})); + return workflows; + }); + + ipcMain.handle("updateWorkflowsFromIntelRepo", () => { + return updateIntelWorkflows() + }); + + const getImagePathFromUrl = (url: string) => { + const imageUrl = URL.parse(url) + if (!imageUrl) { + console.error('Could not find image for URL', {url}) + return; + } + const aiBackendUrl = serviceRegistry?.getService('ai-backend')?.baseUrl + const backend = (aiBackendUrl && url.includes(aiBackendUrl)) ? 'service' : 'ComfyUI'; + + let imagePath: string; + if (backend === 'service') { + imagePath = imageUrl.pathname.replace(/^\/*/, '') + } else { + const s = imageUrl.searchParams; + imagePath = `static/sd_out/${s.get('filename')}` + } + + return path.join(externalRes, 'service', imagePath); + } + + ipcMain.on("openImageWithSystem", (_event, url: string) => { + const imagePath = getImagePathFromUrl(url); + if (!imagePath) return; + shell.openPath(imagePath) + }); + + ipcMain.on("selecteImage", (_event, url: string) => { + const imagePath = getImagePathFromUrl(url); + if (!imagePath) return; + + // Open the image with the default system image viewer + if (process.platform === 'win32') { + exec(`explorer.exe /select, "${imagePath}"`); + } else { + shell.showItemInFolder(imagePath) + } + + }) + +} + + +ipcMain.on("openImageWin", (_: IpcMainEvent, url: string, title: string, width: number, height: number) => { + const display = screen.getPrimaryDisplay(); + width += 32; + height += 48; + if (width > display.workAreaSize.width) { + width = display.workAreaSize.width; + } else if (height > display.workAreaSize.height) { + height = display.workAreaSize.height; + } + const imgWin = new BrowserWindow({ + icon: path.join(process.env.VITE_PUBLIC, "app-ico.svg"), + resizable: true, + center: true, + frame: true, + width: width, + height: height, + autoHideMenuBar: true, + show: false, + parent: win || undefined, + webPreferences: { + devTools: false + } + }); + imgWin.setMenu(null); + imgWin.loadURL(url); + imgWin.once("ready-to-show", function () { + imgWin.show(); + imgWin.setTitle(title); + }); +}); + +ipcMain.handle('showSaveDialog', async (_event, options: Electron.SaveDialogOptions) => { + dialog.showSaveDialog(options).then(result => { + return result; + }).catch(error => { + appLogger.error(`${JSON.stringify(error, Object.getOwnPropertyNames, 2)}`, 'electron-backend'); + }); +}); + +function needAdminPermission() { + return new Promise((resolve) => { + const filename = path.join(externalRes, `${randomUUID()}.txt`); + fs.writeFile(filename, '', (err) => { + if (err) { + if (err && err.code == 'EPERM') { + if (path.parse(externalRes).root == path.parse(process.env.windir!).root) { + resolve(!isAdmin()); + } + } else { + resolve(false); + } + } else { + fs.rmSync(filename); + resolve(false); + } + }); + }) +} + +function isAdmin(): boolean { + const lib = koffi.load("Shell32.dll"); + try { + const IsUserAnAdmin = lib.func("IsUserAnAdmin", "bool", []); + return IsUserAnAdmin(); + } finally { + lib.unload(); + } +} + +app.whenReady().then(async () => { + /* + The current user does not have write permission for files in the program directory and is not an administrator. + Close the current program and let the user start the program with administrator privileges + */ + if (await needAdminPermission()) { + if (singleInstanceLock) { + app.releaseSingleInstanceLock(); + } + //It is possible that the program is installed in a directory that requires administrator privileges + const message = `start "" "${process.argv.join(' ').trim()}`; + sudo.exec(message, (_err, _stdout, _stderr) => { + app.exit(0); + }); + return; + } + + + /**Single instance processing */ + if (!singleInstanceLock) { + dialog.showMessageBoxSync({ + message: app.getLocale() == "zh-CN" ? "本程序仅允许单实例运行,确认后本次运行将自动结束" : "This program only allows a single instance to run, and the run will automatically end after confirmation", + title: "error", + type: "error" + }); + app.exit(); + } else { + const settings = await loadSettings(); + initEventHandle(); + const window = await createWindow(); + await initServiceRegistry(window, settings); + } +}); diff --git a/WebUI/electron/preload.ts b/WebUI/electron/preload.ts index 815f756..d6bf6ed 100644 --- a/WebUI/electron/preload.ts +++ b/WebUI/electron/preload.ts @@ -1,75 +1,75 @@ -import { contextBridge, ipcRenderer, dialog } from "electron"; -import pkg from "../package.json"; - -contextBridge.exposeInMainWorld("envVars", { - platformTitle: import.meta.env.VITE_PLATFORM_TITLE, - productVersion: pkg.version, -}); -contextBridge.exposeInMainWorld("electronAPI", { - getServices: () => ipcRenderer.invoke("getServices"), - sendStartSignal: (serviceName: string) => ipcRenderer.invoke("sendStartSignal", serviceName), - sendStopSignal: (serviceName: string) => ipcRenderer.invoke("sendStopSignal", serviceName), - sendSetUpSignal: (serviceName: string) => ipcRenderer.invoke("sendSetUpSignal", serviceName), - updateWorkflowsFromIntelRepo: () => ipcRenderer.invoke("updateWorkflowsFromIntelRepo"), - reloadImageWorkflows: () => ipcRenderer.invoke("reloadImageWorkflows"), - openDevTools: () => ipcRenderer.send("openDevTools"), - openUrl: (url: string) => ipcRenderer.send("openUrl", url), - getLocalSettings: () => ipcRenderer.invoke("getLocalSettings"), - getThemeSettings: () => ipcRenderer.invoke("getThemeSettings"), - getWinSize: () => ipcRenderer.invoke("getWinSize"), - setWinSize: (width: number, height: number) => - ipcRenderer.invoke("setWinSize", width, height), - showSaveDialog: (options: Electron.SaveDialogOptions) => - ipcRenderer.invoke("showSaveDialog", options), - showMessageBox: (options: Electron.MessageBoxOptions) => - ipcRenderer.invoke("showMessageBox", options), - showMessageBoxSync: (options: Electron.MessageBoxSyncOptions) => - ipcRenderer.invoke("showMessageBox", options), - dragWinToMoveStart: (x: number, y: number) => - ipcRenderer.send("dragWinToMoveStart", x, y), - dragWinToMove: (x: number, y: number) => - ipcRenderer.send("dragWinToMove", x, y), - dragWinToMoveStop: () => ipcRenderer.send("dragWinToMoveStop"), - setIgnoreMouseEvents: (igrnore: boolean) => - ipcRenderer.send("setIgnoreMouseEvents", igrnore), - miniWindow: () => ipcRenderer.send("miniWindow"), - exitApp: () => ipcRenderer.send("exitApp"), - showOpenDialog: (options: Electron.OpenDialogOptions) => ipcRenderer.invoke("showOpenDialog", options), - reportClientEvent: (eventId: number) => ipcRenderer.send("reportClientEvent", eventId), - saveImage: (url: string) => ipcRenderer.send("saveImage", url), - wakeupApiService: () => ipcRenderer.send("wakeupApiService"), - openImageWin: (url: string, title: string, width: number, height: number) => - ipcRenderer.send("openImageWin", url, title, width, height), - screenChange: (callback: (width: number, height: number) => void) => - ipcRenderer.on( - "display-metrics-changed", - (_event, width: number, height: number) => callback(width, height) - ), - webServiceExit: (callback: (seriveName: string, normalExit: boolean) => void) => - ipcRenderer.on( - "webServiceExit", - (_event, seriveName: string, normalExit: boolean) => callback(seriveName, normalExit) - ), - existsPath: (path: string) => ipcRenderer.invoke("existsPath", path), - getInitSetting: () => ipcRenderer.invoke("getInitSetting"), - updateModelPaths: (modelPaths: ModelPaths) => ipcRenderer.invoke("updateModelPaths", modelPaths), - restorePathsSettings :()=>ipcRenderer.invoke("restorePathsSettings"), - refreshSDModles: () => ipcRenderer.invoke("refreshSDModles"), - refreshInpaintModles: () => ipcRenderer.invoke("refreshInpaintModles"), - refreshLLMModles: () => ipcRenderer.invoke("refreshLLMModles"), - refreshLora: () => ipcRenderer.invoke("refreshLora"), - refreshEmbeddingModels: () => ipcRenderer.invoke("refreshEmbeddingModels"), - getDownloadedDiffusionModels: () => ipcRenderer.invoke("getDownloadedDiffusionModels"), - getDownloadedInpaintModels: () => ipcRenderer.invoke("getDownloadedInpaintModels"), - getDownloadedLoras: () => ipcRenderer.invoke("getDownloadedLoras"), - getDownloadedLLMs: () => ipcRenderer.invoke("getDownloadedLLMs"), - getDownloadedGGUFLLMs: () => ipcRenderer.invoke("getDownloadedGGUFLLMs"), - getDownloadedEmbeddingModels: () => ipcRenderer.invoke("getDownloadedEmbeddingModels"), - openImageWithSystem: (url: string) => ipcRenderer.send("openImageWithSystem", url), - selecteImage: (url: string) => ipcRenderer.send("selecteImage", url), - setFullScreen: (enable: boolean) => ipcRenderer.send("setFullScreen", enable), - onDebugLog: (callback: (data: { level: string, source: string, message: string}) => void) => ipcRenderer.on('debugLog', (_event, value) => callback(value)), - wakeupComfyUIService: () => ipcRenderer.send('wakeupComfyUIService'), - onServiceSetUpProgress: (callback: (data: SetupProgress) => void) => ipcRenderer.on('serviceSetUpProgress', (_event, value) => callback(value)), - onServiceInfoUpdate: (callback: (service: ApiServiceInformation) => void) => ipcRenderer.on('serviceInfoUpdate', (_event, value) => callback(value)), -}); +import { contextBridge, ipcRenderer } from "electron"; +import pkg from "../package.json"; + +contextBridge.exposeInMainWorld("envVars", { + platformTitle: import.meta.env.VITE_PLATFORM_TITLE, + productVersion: pkg.version, +}); +contextBridge.exposeInMainWorld("electronAPI", { + getServices: () => ipcRenderer.invoke("getServices"), + sendStartSignal: (serviceName: string) => ipcRenderer.invoke("sendStartSignal", serviceName), + sendStopSignal: (serviceName: string) => ipcRenderer.invoke("sendStopSignal", serviceName), + sendSetUpSignal: (serviceName: string) => ipcRenderer.invoke("sendSetUpSignal", serviceName), + updateWorkflowsFromIntelRepo: () => ipcRenderer.invoke("updateWorkflowsFromIntelRepo"), + reloadImageWorkflows: () => ipcRenderer.invoke("reloadImageWorkflows"), + openDevTools: () => ipcRenderer.send("openDevTools"), + openUrl: (url: string) => ipcRenderer.send("openUrl", url), + getLocalSettings: () => ipcRenderer.invoke("getLocalSettings"), + getThemeSettings: () => ipcRenderer.invoke("getThemeSettings"), + getWinSize: () => ipcRenderer.invoke("getWinSize"), + setWinSize: (width: number, height: number) => + ipcRenderer.invoke("setWinSize", width, height), + showSaveDialog: (options: Electron.SaveDialogOptions) => + ipcRenderer.invoke("showSaveDialog", options), + showMessageBox: (options: Electron.MessageBoxOptions) => + ipcRenderer.invoke("showMessageBox", options), + showMessageBoxSync: (options: Electron.MessageBoxSyncOptions) => + ipcRenderer.invoke("showMessageBox", options), + dragWinToMoveStart: (x: number, y: number) => + ipcRenderer.send("dragWinToMoveStart", x, y), + dragWinToMove: (x: number, y: number) => + ipcRenderer.send("dragWinToMove", x, y), + dragWinToMoveStop: () => ipcRenderer.send("dragWinToMoveStop"), + setIgnoreMouseEvents: (igrnore: boolean) => + ipcRenderer.send("setIgnoreMouseEvents", igrnore), + miniWindow: () => ipcRenderer.send("miniWindow"), + exitApp: () => ipcRenderer.send("exitApp"), + showOpenDialog: (options: Electron.OpenDialogOptions) => ipcRenderer.invoke("showOpenDialog", options), + reportClientEvent: (eventId: number) => ipcRenderer.send("reportClientEvent", eventId), + saveImage: (url: string) => ipcRenderer.send("saveImage", url), + wakeupApiService: () => ipcRenderer.send("wakeupApiService"), + openImageWin: (url: string, title: string, width: number, height: number) => + ipcRenderer.send("openImageWin", url, title, width, height), + screenChange: (callback: (width: number, height: number) => void) => + ipcRenderer.on( + "display-metrics-changed", + (_event, width: number, height: number) => callback(width, height) + ), + webServiceExit: (callback: (seriveName: string, normalExit: boolean) => void) => + ipcRenderer.on( + "webServiceExit", + (_event, seriveName: string, normalExit: boolean) => callback(seriveName, normalExit) + ), + existsPath: (path: string) => ipcRenderer.invoke("existsPath", path), + getInitSetting: () => ipcRenderer.invoke("getInitSetting"), + updateModelPaths: (modelPaths: ModelPaths) => ipcRenderer.invoke("updateModelPaths", modelPaths), + restorePathsSettings :()=>ipcRenderer.invoke("restorePathsSettings"), + refreshSDModles: () => ipcRenderer.invoke("refreshSDModles"), + refreshInpaintModles: () => ipcRenderer.invoke("refreshInpaintModles"), + refreshLLMModles: () => ipcRenderer.invoke("refreshLLMModles"), + refreshLora: () => ipcRenderer.invoke("refreshLora"), + refreshEmbeddingModels: () => ipcRenderer.invoke("refreshEmbeddingModels"), + getDownloadedDiffusionModels: () => ipcRenderer.invoke("getDownloadedDiffusionModels"), + getDownloadedInpaintModels: () => ipcRenderer.invoke("getDownloadedInpaintModels"), + getDownloadedLoras: () => ipcRenderer.invoke("getDownloadedLoras"), + getDownloadedLLMs: () => ipcRenderer.invoke("getDownloadedLLMs"), + getDownloadedGGUFLLMs: () => ipcRenderer.invoke("getDownloadedGGUFLLMs"), + getDownloadedEmbeddingModels: () => ipcRenderer.invoke("getDownloadedEmbeddingModels"), + openImageWithSystem: (url: string) => ipcRenderer.send("openImageWithSystem", url), + selecteImage: (url: string) => ipcRenderer.send("selecteImage", url), + setFullScreen: (enable: boolean) => ipcRenderer.send("setFullScreen", enable), + onDebugLog: (callback: (data: { level: string, source: string, message: string}) => void) => ipcRenderer.on('debugLog', (_event, value) => callback(value)), + wakeupComfyUIService: () => ipcRenderer.send('wakeupComfyUIService'), + onServiceSetUpProgress: (callback: (data: SetupProgress) => void) => ipcRenderer.on('serviceSetUpProgress', (_event, value) => callback(value)), + onServiceInfoUpdate: (callback: (service: ApiServiceInformation) => void) => ipcRenderer.on('serviceInfoUpdate', (_event, value) => callback(value)), +}); diff --git a/WebUI/electron/subprocesses/aiBackendService.ts b/WebUI/electron/subprocesses/aiBackendService.ts index 9bb036d..79d9874 100644 --- a/WebUI/electron/subprocesses/aiBackendService.ts +++ b/WebUI/electron/subprocesses/aiBackendService.ts @@ -71,7 +71,7 @@ export class AiBackendService extends LongLivedPythonApiService { //must be at the same tick as the spawn function call //otherwise we cannot really track errors given the nature of spawn() with a longlived process - const didProcessExitEarlyTracker = new Promise((resolve, reject) => { + const didProcessExitEarlyTracker = new Promise((resolve, _reject) => { apiProcess.on('error', (error) => { this.appLogger.error(`encountered error of process in ${this.name} : ${error}`, this.name) resolve(true); diff --git a/WebUI/electron/subprocesses/comfyUIBackendService.ts b/WebUI/electron/subprocesses/comfyUIBackendService.ts index 42e02e0..3462753 100644 --- a/WebUI/electron/subprocesses/comfyUIBackendService.ts +++ b/WebUI/electron/subprocesses/comfyUIBackendService.ts @@ -46,7 +46,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService { // Check if it's a valid git repo try { await self.git.run(["-C", self.serviceDir, "status"]) - } catch (e) { + } catch (_e) { try { filesystem.removeSync(self.serviceDir) } finally { @@ -69,7 +69,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService { const requirementsTextPath = existingFileOrError(path.join(self.serviceDir, 'requirements.txt')) try { await self.uvPip.checkRequirementsTxt(requirementsTextPath) - } catch (e) { + } catch (_e) { await self.uvPip.run(["install", "-r", requirementsTextPath]) } } @@ -87,7 +87,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService { loras: lora` fs.promises.writeFile(extraModelPathsYaml, extraModelsYaml, {encoding: 'utf-8', flag: 'w'}); self.appLogger.info(`Configured extra model paths for comfyUI at ${extraModelPathsYaml} as ${extraModelsYaml} `, self.name) - } catch (e) { + } catch (_e) { self.appLogger.error("Failed to configure extra model paths for comfyUI", self.name) throw new Error("Failed to configure extra model paths for comfyUI") } @@ -149,7 +149,7 @@ export class ComfyUiBackendService extends LongLivedPythonApiService { //must be at the same tick as the spawn function call //otherwise we cannot really track errors given the nature of spawn() with a longlived process - const didProcessExitEarlyTracker = new Promise((resolve, reject) => { + const didProcessExitEarlyTracker = new Promise((resolve, _reject) => { apiProcess.on('exit', () => { this.appLogger.error(`encountered unexpected exit in ${this.name}.`, this.name) resolve(true); diff --git a/WebUI/electron/subprocesses/llamaCppBackendService.ts b/WebUI/electron/subprocesses/llamaCppBackendService.ts index 069f971..baf1d45 100644 --- a/WebUI/electron/subprocesses/llamaCppBackendService.ts +++ b/WebUI/electron/subprocesses/llamaCppBackendService.ts @@ -71,7 +71,7 @@ export class LlamaCppBackendService extends LongLivedPythonApiService { //must be at the same tick as the spawn function call //otherwise we cannot really track errors given the nature of spawn() with a longlived process - const didProcessExitEarlyTracker = new Promise((resolve, reject) => { + const didProcessExitEarlyTracker = new Promise((resolve, _reject) => { apiProcess.on('error', (error) => { this.appLogger.error(`encountered error of process in ${this.name} : ${error}`, this.name) resolve(true); diff --git a/WebUI/electron/subprocesses/osProcessHelper.ts b/WebUI/electron/subprocesses/osProcessHelper.ts index d9a8a90..0376d0b 100644 --- a/WebUI/electron/subprocesses/osProcessHelper.ts +++ b/WebUI/electron/subprocesses/osProcessHelper.ts @@ -11,7 +11,7 @@ export function existingFileOrError(filePath: string) { throw Error(`File at ${resolvedFilePath} does not exist`) } export async function spawnProcessAsync(command: string, args: string[] = [], - logHandler: (data: string) => void = () => { }, extraEnv?: {}, workDir?: string + logHandler: (data: string) => void = () => { }, extraEnv?: object, workDir?: string ): Promise { logHandler(`Spawning command ${command} ${args.join(' ')}`); if (extraEnv) { diff --git a/WebUI/electron/subprocesses/service.ts b/WebUI/electron/subprocesses/service.ts index a533e99..f0a15ee 100644 --- a/WebUI/electron/subprocesses/service.ts +++ b/WebUI/electron/subprocesses/service.ts @@ -1,6 +1,5 @@ import {ChildProcess} from "node:child_process"; import {app, BrowserWindow, net} from "electron"; -import fs from "fs"; import * as filesystem from 'fs-extra'; import path from "node:path"; import { appLoggerInstance } from "../logging/logger.ts"; @@ -8,6 +7,7 @@ import { existingFileOrError, spawnProcessAsync } from "./osProcessHelper"; import { assert } from 'node:console'; import { Arch, getArchPriority, getDeviceArch } from './deviceArch'; import { createHash } from 'crypto'; +import extract from 'extract-zip'; class ServiceCheckError extends Error { readonly component: string @@ -94,7 +94,7 @@ abstract class ExecutableService extends GenericServiceImpl { abstract getExePath(): string - async run(args: string[] = [], extraEnv?: {}, workDir?: string): Promise { + async run(args: string[] = [], extraEnv?: object, workDir?: string): Promise { const exePath = existingFileOrError(this.getExePath()) return spawnProcessAsync(exePath, args, (data) => this.log(data), extraEnv, workDir) } @@ -152,7 +152,7 @@ export class PipService extends ExecutableService { return this.python.getExePath() } - async run(args: string[] = [], extraEnv?: {}, workDir?: string): Promise { + async run(args: string[] = [], extraEnv?: object, workDir?: string): Promise { return this.python.run(["-m", "pip", ...args], extraEnv, workDir) } @@ -205,8 +205,8 @@ export class PipService extends ExecutableService { } async checkRequirementsTxt(requirementsTxtPath: string): Promise { - await this.python.run(["-c", `import pkg_resources; pkg_resources.require([s for s in open(r'${requirementsTxtPath}') if s and s[0].isalpha()])`]).catch((e) => { - throw new Error(`requirements check failed`) + await this.python.run(["-c", `import pkg_resources; pkg_resources.require([s for s in open(r'${requirementsTxtPath}') if s and s[0].isalpha()])`]).catch((e: unknown) => { + throw new Error(`requirements check failed: ${e}`,) }) } } @@ -220,7 +220,7 @@ export class UvPipService extends PipService { this.name = "uvpip" } - async run(args: string[] = [], extraEnv?: {}, workDir?: string): Promise { + async run(args: string[] = [], extraEnv?: object, workDir?: string): Promise { return this.python.run(["-m", "uv", "pip", ...args], extraEnv, workDir) } @@ -268,7 +268,7 @@ export class LsLevelZeroService extends ExecutableService { return path.resolve(path.join(this.dir, "Library/bin/ls_level_zero.exe")) } - async run(args: string[] = [], extraEnv?: {}, workDir?: string): Promise { + async run(args: string[] = [], extraEnv?: object, workDir?: string): Promise { // reset ONEAPI_DEVICE_SELECTOR to ensure full device discovery const env = { ...extraEnv, @@ -385,7 +385,7 @@ export class GitService extends ExecutableService { return path.resolve(path.join(this.dir, "cmd/git.exe")) } - async run(args: string[] = [], extraEnv?: {}, workDir?: string): Promise { + async run(args: string[] = [], extraEnv?: object, workDir?: string): Promise { // Explicitly specify the cert file bundled with portable git, // to avoid being affected by the system git configuration. const env = { @@ -460,7 +460,6 @@ export class GitService extends ExecutableService { } private async unzipGit(): Promise { - const extract = require("extract-zip"); await extract(this.zipPath, {dir: this.dir}); } } @@ -639,7 +638,9 @@ export abstract class LongLivedPythonApiService implements ApiService { resolve(true) break } - } catch (e) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e: unknown) { + //fetch will simply fail while server not up } await new Promise(resolve => setTimeout(resolve, queryIntervalMs)); diff --git a/WebUI/package-lock.json b/WebUI/package-lock.json index 1ec407f..c220c2a 100644 --- a/WebUI/package-lock.json +++ b/WebUI/package-lock.json @@ -34,15 +34,22 @@ }, "devDependencies": { "@types/exif": "^0.6.5", + "@types/minimist": "^1.2.5", "@types/node": "^20.17.10", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools": "^7.6.8", + "@vue/eslint-config-prettier": "^10.1.0", + "@vue/eslint-config-typescript": "^14.3.0", "adm-zip": "^0.5.16", "electron": "^33.2.1", "electron-builder": "^25.1.8", "electron-builder-squirrel-windows": "^25.1.8", + "eslint": "^9.18.0", + "eslint-plugin-oxlint": "^0.15.6", + "eslint-plugin-vue": "^9.32.0", "postcss": "^8.4.49", "postcss-import": "^16.1.0", + "prettier": "^3.4.2", "tailwindcss": "^3.4.16", "typescript": "^5.7.2", "vite": "^5.4.11", @@ -1083,6 +1090,192 @@ "node": ">=12" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.5", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/core": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", + "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.10.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@floating-ui/core": { "version": "1.6.8", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", @@ -1126,6 +1319,72 @@ "dev": true, "license": "MIT" }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@internationalized/date": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.6.0.tgz", @@ -1520,6 +1779,19 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@radix-icons/vue": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-icons/vue/-/vue-1.0.0.tgz", @@ -2006,6 +2278,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -2016,6 +2295,13 @@ "@types/node": "*" } }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", @@ -2027,7 +2313,7 @@ "version": "20.17.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.10.tgz", "integrity": "sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -2073,13 +2359,244 @@ "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", + "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/type-utils": "8.21.0", + "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", + "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/typescript-estree": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", + "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", + "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.21.0", + "@typescript-eslint/utils": "8.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", + "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", + "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/visitor-keys": "8.21.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", + "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.21.0", + "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/typescript-estree": "8.21.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", + "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.21.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@vitejs/plugin-vue": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", @@ -2298,6 +2815,47 @@ "rfdc": "^1.4.1" } }, + "node_modules/@vue/eslint-config-prettier": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz", + "integrity": "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2" + }, + "peerDependencies": { + "eslint": ">= 8.21.0", + "prettier": ">= 3.0.0" + } + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.3.0.tgz", + "integrity": "sha512-bOreIxlSC/xsUdhDdKIHb1grwJah+IokNeJ50LqA1StdOHeSPUxSIPNxyKgRx4YdjhyzC6TKtrCf6yYK99x3Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.20.0", + "fast-glob": "^3.3.3", + "typescript-eslint": "^8.20.0", + "vue-eslint-parser": "^9.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0", + "eslint-plugin-vue": "^9.28.0", + "typescript": ">=4.8.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@vue/language-core": { "version": "2.1.10", "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.10.tgz", @@ -2485,6 +3043,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/adm-zip": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", @@ -3017,6 +3585,13 @@ "bluebird": "^3.5.5" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, "node_modules/boolean": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", @@ -3108,7 +3683,6 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, "license": "MIT", "engines": { "node": "*" @@ -3345,6 +3919,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -3950,6 +4534,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/deep-pick-omit": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/deep-pick-omit/-/deep-pick-omit-1.2.1.tgz", @@ -4459,7 +5050,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -4660,7 +5250,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=10" }, @@ -4668,6 +5257,342 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", + "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.10.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.18.0", + "@eslint/plugin-kit": "^0.2.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz", + "integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "build/bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-oxlint": { + "version": "0.15.8", + "resolved": "https://registry.npmjs.org/eslint-plugin-oxlint/-/eslint-plugin-oxlint-0.15.8.tgz", + "integrity": "sha512-Nmg9bLvcOvoTtV+NIXPu8FzCxs1ydgXVQx5bFnri2EvW+Vdu+XVfIQ/61kk3M1E7WFdreuWfjLWuGBF7tWLNrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.3.1" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", + "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.32.0.tgz", + "integrity": "sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "globals": "^13.24.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "vue-eslint-parser": "^9.4.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-vue/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-vue/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-vue/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -4677,6 +5602,16 @@ "@types/estree": "^1.0.0" } }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/event-target-shim": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-6.0.2.tgz", @@ -4727,7 +5662,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "debug": "^4.1.1", @@ -4748,7 +5682,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, "license": "MIT", "dependencies": { "pump": "^3.0.0" @@ -4777,17 +5710,24 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "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==", "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" @@ -4800,6 +5740,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -4813,7 +5760,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, "license": "MIT", "dependencies": { "pend": "~1.2.0" @@ -4858,6 +5804,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -4873,26 +5832,64 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, - "license": "ISC", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">=8" + "node": ">=16" } }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "license": "ISC" + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -5326,6 +6323,13 @@ "dev": true, "license": "ISC" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/h3": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/h3/-/h3-1.13.0.tgz", @@ -5562,6 +6566,23 @@ "node": ">= 4" } }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -5933,6 +6954,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -5953,6 +6981,13 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -6048,6 +7083,20 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -6082,6 +7131,22 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -6117,6 +7182,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.union": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", @@ -6588,6 +7660,13 @@ "node": "^18 || >=20" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/negotiator": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", @@ -6837,6 +7916,19 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/nypm": { "version": "0.3.12", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.3.12.tgz", @@ -7018,7 +8110,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -7040,6 +8131,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -7103,6 +8212,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -7125,6 +8250,19 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-ms": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", @@ -7154,6 +8292,16 @@ "dev": true, "license": "MIT" }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -7247,7 +8395,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, "license": "MIT" }, "node_modules/perfect-debounce": { @@ -7526,6 +8673,45 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-ms": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", @@ -7584,7 +8770,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "dev": true, "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -7834,6 +9019,16 @@ "dev": true, "license": "MIT" }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/responselike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", @@ -8511,6 +9706,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-literal": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", @@ -8655,6 +9863,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/tailwind-merge": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.5.tgz", @@ -8952,6 +10177,19 @@ "utf8-byte-length": "^1.0.1" } }, + "node_modules/ts-api-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -8964,6 +10202,19 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-fest": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", @@ -8992,6 +10243,29 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.21.0.tgz", + "integrity": "sha512-txEKYY4XMKwPXxNkN8+AxAdX6iIJAPiJbHE/FpQccs/sxw8Lf26kqwC3cn0xkHlW8kEbLhkhCsjWuMveaY9Rxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.21.0", + "@typescript-eslint/parser": "8.21.0", + "@typescript-eslint/utils": "8.21.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, "node_modules/ufo": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", @@ -9020,7 +10294,7 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/unenv": { @@ -9479,6 +10753,79 @@ } } }, + "node_modules/vue-eslint-parser": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/vue-tsc": { "version": "2.1.10", "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.10.tgz", @@ -9560,6 +10907,16 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -9600,7 +10957,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/ws": { @@ -9625,6 +10981,16 @@ } } }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", @@ -9705,7 +11071,6 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", diff --git a/WebUI/package.json b/WebUI/package.json index e049530..babdeeb 100644 --- a/WebUI/package.json +++ b/WebUI/package.json @@ -42,6 +42,7 @@ }, "devDependencies": { "@types/exif": "^0.6.5", + "@types/minimist": "^1.2.5", "@types/node": "^20.17.10", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools": "^7.6.8", diff --git a/WebUI/src/App.vue b/WebUI/src/App.vue index cce9ff5..0f9969b 100644 --- a/WebUI/src/App.vue +++ b/WebUI/src/App.vue @@ -256,7 +256,7 @@ function switchTab(index: number) { } } -watch(textInference, (newSetting, oldSetting) => { +watch(textInference, (newSetting, _oldSetting) => { if (newSetting.backend === 'LLAMA.CPP') { answer.value!.disableRag(); } else { diff --git a/WebUI/src/assets/js/clientAPI.ts b/WebUI/src/assets/js/clientAPI.ts index bef28ff..49fce71 100644 --- a/WebUI/src/assets/js/clientAPI.ts +++ b/WebUI/src/assets/js/clientAPI.ts @@ -1,7 +1,3 @@ -import { toast } from "./toast"; - -export module clientAPI { - export const isClient = window.chrome.webview && window.chrome.webview.hostObjects && window.chrome.webview.hostObjects.clientAPI; @@ -32,4 +28,3 @@ export module clientAPI { export function saveImage(url: string) { return window.electronAPI.saveImage(url) } -} diff --git a/WebUI/src/assets/js/const.ts b/WebUI/src/assets/js/const.ts index 2d7b5e9..425dafb 100644 --- a/WebUI/src/assets/js/const.ts +++ b/WebUI/src/assets/js/const.ts @@ -1,4 +1,3 @@ -export module Const { export const MODEL_TYPE_LLM = 0; export const MODEL_TYPE_STABLE_DIFFUSION = 1; export const MODEL_TYPE_LORA = 2; @@ -16,4 +15,3 @@ export module Const { export const MODEL_TYPE_COMFY_CONTROL_NET = 105; export const MODEL_TYPE_FACESWAP = 106; export const MODEL_TYPE_FACERESTORE = 107; -} \ No newline at end of file diff --git a/WebUI/src/assets/js/markdownParser.ts b/WebUI/src/assets/js/markdownParser.ts index 3818aa9..251ff09 100644 --- a/WebUI/src/assets/js/markdownParser.ts +++ b/WebUI/src/assets/js/markdownParser.ts @@ -1,7 +1,7 @@ import { Marked, Renderer } from "marked"; import { markedHighlight } from "marked-highlight"; import hljs from "highlight.js"; -import { util } from "./util"; +import * as util from "./util"; export class MarkdownParser { pattern = /```([^\n]*)(.*?)(?:(?:```)|$)/gs; @@ -12,7 +12,7 @@ export class MarkdownParser { this.marked = new Marked( markedHighlight({ langPrefix: 'hljs language-', - highlight(code, lang, info) { + highlight(code, lang, _info) { const language = hljs.getLanguage(lang) ? lang : 'plaintext'; const html = hljs.highlight(code, { language }).value; return html; diff --git a/WebUI/src/assets/js/store/backendServices.ts b/WebUI/src/assets/js/store/backendServices.ts index 122844d..aa3622f 100644 --- a/WebUI/src/assets/js/store/backendServices.ts +++ b/WebUI/src/assets/js/store/backendServices.ts @@ -9,7 +9,7 @@ export const useBackendServices = defineStore("backendServices", () => { ["llamacpp-backend", new BackendServiceSetupProgressListener("llamacpp-backend")], ]); - window.electronAPI.getServices().catch(async (reason: any) => { + window.electronAPI.getServices().catch(async (_reason: unknown) => { console.warn("initial service call failed - retrying") await new Promise(resolve => { setTimeout(async () => {resolve()}, 1000) @@ -92,7 +92,7 @@ export const useBackendServices = defineStore("backendServices", () => { class BackendServiceSetupProgressListener { - isActive: Boolean = false + isActive: boolean = false readonly associatedServiceName : string private collectedSetupProgress: SetupProgress[] = [] private terminalUpdateReceived = false diff --git a/WebUI/src/assets/js/store/comfyUi.ts b/WebUI/src/assets/js/store/comfyUi.ts index 6ca2c7e..bac24ae 100644 --- a/WebUI/src/assets/js/store/comfyUi.ts +++ b/WebUI/src/assets/js/store/comfyUi.ts @@ -2,7 +2,7 @@ import { defineStore, acceptHMRUpdate } from "pinia"; import { WebSocket } from "partysocket"; import { ComfyUIApiWorkflow, Setting, useImageGeneration } from "./imageGeneration"; import { useI18N } from "./i18n"; -import { toast } from "../toast"; +import * as toast from "../toast"; import {useGlobalSetup} from "@/assets/js/store/globalSetup.ts"; import {useBackendServices} from "@/assets/js/store/backendServices.ts"; @@ -225,6 +225,7 @@ export const useComfyUi = defineStore("comfyUi", () => { if (input.type === 'string' ) console.log('probably modifying string', input.label, input.current.value); if (mutableWorkflow[keys[0]].inputs !== undefined) { if (input.type === 'string') console.log('actually modifying string', input.label, input.current.value); + // eslint-disable-next-line @typescript-eslint/no-explicit-any (mutableWorkflow[keys[0]].inputs as any)[input.nodeInput] = input.current.value; } } @@ -235,6 +236,7 @@ export const useComfyUi = defineStore("comfyUi", () => { const uploadImageName = `${uploadImageHash}.${uploadImageExtension}`; console.log('uploadImageName', uploadImageName); if (mutableWorkflow[keys[0]].inputs !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any (mutableWorkflow[keys[0]].inputs as any)[input.nodeInput] = uploadImageName; } const data = new FormData(); @@ -347,12 +349,15 @@ const settingToComfyInputsName = { } satisfies Partial>; type ComfySetting = keyof typeof settingToComfyInputsName; const findKeysByTitle = (workflow: ComfyUIApiWorkflow, title: ComfySetting | 'loader' | string) => + // eslint-disable-next-line @typescript-eslint/no-explicit-any Object.entries(workflow).filter(([_key, value]) => (value as any)?.['_meta']?.title === title).map(([key, _value]) => key); const findKeysByClassType = (workflow: ComfyUIApiWorkflow, classType: string) => + // eslint-disable-next-line @typescript-eslint/no-explicit-any Object.entries(workflow).filter(([_key, value]) => (value as any)?.['class_type'] === classType).map(([key, _value]) => key); const findKeysByInputsName = (workflow: ComfyUIApiWorkflow, setting: ComfySetting) => { for (const inputName of settingToComfyInputsName[setting]) { if (inputName === 'text') continue; + // eslint-disable-next-line @typescript-eslint/no-explicit-any const keys = Object.entries(workflow).filter(([_key, value]) => (value as any)?.['inputs']?.[inputName ?? ''] !== undefined).map(([key, _value]) => key) if (keys.length > 0) return keys; } @@ -364,7 +369,8 @@ const getInputNameBySettingAndKey = (workflow: ComfyUIApiWorkflow, key: string, } return ''; } -function modifySettingInWorkflow(workflow: ComfyUIApiWorkflow, setting: ComfySetting, value: any) { + +function modifySettingInWorkflow(workflow: ComfyUIApiWorkflow, setting: ComfySetting, value: unknown) { const keys = findKeysByTitle(workflow, setting).length > 0 ? findKeysByTitle(workflow, setting) : findKeysByInputsName(workflow, setting); if (keys.length === 0) { console.error(`No key found for setting ${setting}. Stopping generation`); diff --git a/WebUI/src/assets/js/store/globalSetup.ts b/WebUI/src/assets/js/store/globalSetup.ts index 3394693..e20761a 100644 --- a/WebUI/src/assets/js/store/globalSetup.ts +++ b/WebUI/src/assets/js/store/globalSetup.ts @@ -1,8 +1,7 @@ import {defineStore} from "pinia"; -import {util} from "../util"; +import * as util from "../util"; import {useI18N} from "./i18n"; import {useBackendServices} from "./backendServices"; -import {toast} from "@/assets/js/toast.ts"; type GlobalSetupState = "running" | "verifyBackend" | "manageInstallations" | "loading" | "failed" type LastUsedBackend = BackendServiceName | "None" @@ -102,7 +101,7 @@ export const useGlobalSetup = defineStore("globalSetup", () => { models.value.scheduler.push(...await initWebSettings(postJson)); models.value.scheduler.unshift("None"); break; - } catch (error) { + } catch (_error: unknown) { await util.delay(delay); } } @@ -236,29 +235,6 @@ export const useGlobalSetup = defineStore("globalSetup", () => { } } - function getManualModelSettings() { - const dataStr = localStorage.getItem("ManualModelSettings"); - if (dataStr) { - return JSON.parse(dataStr) as KVObject; - } else { - return { - llm_model: "microsoft/Phi-3-mini-4k-instruct", - enableRag: false, - sd_model: "Lykon/dreamshaper-8", - negativePrompt: "bad hands, nsfw", - generateNumber: 1, - width: 512, - height: 512, - guidanceScale: 7.5, - inferenceSteps: 40, - seed: -1, - lora: "None", - scheduler: "None", - embedding: "BAAI/bge-large-en-v1.5" - } - } - } - function assertSelectExist() { let changeUserSetup = false; if (models.value.llm.length > 0 && !models.value.llm.includes(modelSettings.llm_model)) { diff --git a/WebUI/src/assets/js/store/i18n.ts b/WebUI/src/assets/js/store/i18n.ts index b5412f2..c5cfc63 100644 --- a/WebUI/src/assets/js/store/i18n.ts +++ b/WebUI/src/assets/js/store/i18n.ts @@ -48,7 +48,7 @@ export const useI18N = defineStore("i18n", () => { await switchLanguage(langName.value); } - function changeLanguage(value: any, index: number) { + function changeLanguage(value: { value: string }, _index: number) { switchLanguage(value.value) } diff --git a/WebUI/src/assets/js/store/imageGeneration.ts b/WebUI/src/assets/js/store/imageGeneration.ts index 84f460e..407a89a 100644 --- a/WebUI/src/assets/js/store/imageGeneration.ts +++ b/WebUI/src/assets/js/store/imageGeneration.ts @@ -3,9 +3,9 @@ import z from "zod"; import { useComfyUi } from "./comfyUi"; import { useStableDiffusion } from "./stableDiffusion"; import { useI18N } from "./i18n"; -import { Const } from "../const"; +import * as Const from "../const"; import { useGlobalSetup } from "./globalSetup"; -import {toast} from "@/assets/js/toast.ts"; +import * as toast from "@/assets/js/toast.ts"; export type StableDiffusionSettings = { resolution: 'standard' | 'hd' | 'manual', // ~ modelSettings.resolution 0, 1, 3 diff --git a/WebUI/src/assets/js/store/models.ts b/WebUI/src/assets/js/store/models.ts index b5a206b..823f406 100644 --- a/WebUI/src/assets/js/store/models.ts +++ b/WebUI/src/assets/js/store/models.ts @@ -55,7 +55,7 @@ export const useModels = defineStore("models", () => { } - async function download(models: DownloadModelParam[]) { + async function download(_models: DownloadModelParam[]) { }; refreshModels() diff --git a/WebUI/src/assets/js/store/stableDiffusion.ts b/WebUI/src/assets/js/store/stableDiffusion.ts index ea22ee5..ad39e39 100644 --- a/WebUI/src/assets/js/store/stableDiffusion.ts +++ b/WebUI/src/assets/js/store/stableDiffusion.ts @@ -1,12 +1,12 @@ import { defineStore } from "pinia"; import { useImageGeneration } from "./imageGeneration"; import { useGlobalSetup } from "./globalSetup"; -import { Const } from "../const"; +import * as Const from "../const"; import { useModels } from "./models"; -import { util } from "../util"; +import * as util from "../util"; import { SSEProcessor } from "../sseProcessor"; import { useI18N } from "./i18n"; -import { toast } from "../toast"; +import * as toast from "../toast"; type BackendParams = { mode: number, @@ -60,14 +60,14 @@ export const useStableDiffusion = defineStore("stableDiffusion", () => { }; await sendGenerate(defaultBackendParams); - } catch (ex) { + } catch (_error: unknown) { } finally { imageGeneration.processing = false; } } async function checkModel() { - return new Promise(async (resolve, reject) => { + return new Promise(async (resolve, _reject) => { const checkList: CheckModelAlreadyLoadedParameters[] = [{ repo_id: globalSetup.modelSettings.sd_model, type: Const.MODEL_TYPE_STABLE_DIFFUSION, backend: "default" }]; if (globalSetup.modelSettings.lora != "None") { checkList.push({ repo_id: globalSetup.modelSettings.lora, type: Const.MODEL_TYPE_LORA , backend: "default"}) diff --git a/WebUI/src/assets/js/store/textInference.ts b/WebUI/src/assets/js/store/textInference.ts index 9afc2ab..1278eee 100644 --- a/WebUI/src/assets/js/store/textInference.ts +++ b/WebUI/src/assets/js/store/textInference.ts @@ -4,8 +4,8 @@ import { z } from "zod"; import { useBackendServices } from "./backendServices"; export const backendTypes = ['IPEX-LLM', 'LLAMA.CPP'] as const; -const backend = z.enum(backendTypes); -export type Backend = z.infer; +const BackendSchema = z.enum(backendTypes); +export type Backend = z.infer; const backendModelKey = { 'IPEX-LLM': 'llm_model', diff --git a/WebUI/src/assets/js/toast.ts b/WebUI/src/assets/js/toast.ts index 9da0a83..9281508 100644 --- a/WebUI/src/assets/js/toast.ts +++ b/WebUI/src/assets/js/toast.ts @@ -23,7 +23,6 @@ function mergeOptions(initialObj: KVObject, customObj: KVObject) { }); } - export module toast { const TOAST_ANIMATION_SPEED = 400; const DEFAULT_TRANSITIONS: ToastTransitions = { @@ -182,6 +181,7 @@ function mergeOptions(initialObj: KVObject, customObj: KVObject) { parentEL.appendChild(_toastStage); // This is a hack to get animations started. Apparently without explicitly redrawing, it'll just attach the class and no animations would be done. + // eslint-disable-next-line @typescript-eslint/no-unused-expressions _toastStage.offsetHeight; stylize(_toastStage, transitions.show); @@ -219,7 +219,6 @@ function mergeOptions(initialObj: KVObject, customObj: KVObject) { const textNode = document.createTextNode(text); content.appendChild(textNode); toastStage.appendChild(content); - toastStage.style.pointerEvents; if (style) { if (style.content) { stylize(content, style.content); @@ -238,4 +237,3 @@ function mergeOptions(initialObj: KVObject, customObj: KVObject) { function destroyToast() { _toastStage.remove(); } - } \ No newline at end of file diff --git a/WebUI/src/assets/js/util.ts b/WebUI/src/assets/js/util.ts index bf67c0b..7ca29f3 100644 --- a/WebUI/src/assets/js/util.ts +++ b/WebUI/src/assets/js/util.ts @@ -1,7 +1,4 @@ - - -export module util { - export async function copyImage(url: string) { + export async function copyImage(url: string) { const response = await fetch(url); const blob = await response.blob(); navigator.clipboard.write([ @@ -41,7 +38,7 @@ export module util { return new Promise((resolve) => { const t = setTimeout(() => { clearTimeout(t); - resolve && resolve(); + resolve(); }, ms); }); } @@ -123,9 +120,10 @@ export module util { } export function log(message: string) { - console.log(`[${util.dateFormat(new Date(), "hh:mm:ss:fff")}] ${message}`); + console.log(`[${dateFormat(new Date(), "hh:mm:ss:fff")}] ${message}`); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any export function convertToFormData(data: any) { const formData = new FormData(); for (const key in data) { @@ -149,7 +147,6 @@ export module util { let enumElement: HTMLElement | null = target; while (enumElement && enumElement != document.body) { offset.left += enumElement.offsetLeft; - offset.top == enumElement.offsetTop; enumElement = enumElement.parentElement; } return offset; @@ -158,5 +155,3 @@ export module util { export function toFixed(num: number, fractionDigits: number) { return parseFloat(num.toFixed(fractionDigits)); } - -} diff --git a/WebUI/src/components/DownloadDialog.vue b/WebUI/src/components/DownloadDialog.vue index f2bd0f8..1d71b4c 100644 --- a/WebUI/src/components/DownloadDialog.vue +++ b/WebUI/src/components/DownloadDialog.vue @@ -17,7 +17,7 @@ - + {{ item.repo_id }}
@@ -113,9 +113,9 @@ import { useGlobalSetup } from '@/assets/js/store/globalSetup'; import ProgressBar from './ProgressBar.vue'; import { useI18N } from '@/assets/js/store/i18n'; import { SSEProcessor } from '@/assets/js/sseProcessor'; -import { util } from '@/assets/js/util'; -import { Const } from '@/assets/js/const'; -import { toast } from '@/assets/js/toast'; +import * as util from '@/assets/js/util'; +import * as Const from '@/assets/js/const'; +import * as toast from '@/assets/js/toast'; import { useModels } from '@/assets/js/store/models'; const i18nState = useI18N().state; @@ -155,7 +155,7 @@ function dataProcess(line: string) { if (completeCount.value == allTaskCount) { downloding = false; emits("close"); - downloadResolve && downloadResolve(); + downloadResolve?.(); } else { taskPercent.value = util.toFixed(completeCount.value / allTaskCount * 100, 1); percent.value = 100; @@ -171,7 +171,7 @@ function dataProcess(line: string) { hashError.value = true; abortController?.abort(); fetch(`${globalSetup.apiHost}/api/stopDownloadModel`) - downloadReject && downloadReject({ type: "error", error: errorText.value }); + downloadReject?.({ type: "error", error: errorText.value }); switch (data.err_type) { case "not_enough_disk_space": @@ -197,7 +197,7 @@ let downloadReject: ((args: DownloadFailedParams) => void) | undefined async function showConfirmDialog(downList: DownloadModelParam[], success?: () => void, fail?: (args: DownloadFailedParams) => void) { if (downloding) { toast.error(i18nState.DOWNLOADER_CONFLICT); - fail && fail({ type: "conflict" }) + fail?.({ type: "conflict" }) return; } sizeRequesting.value = true; @@ -245,7 +245,7 @@ async function showConfirmDialog(downList: DownloadModelParam[], success?: () => } sizeRequesting.value = false; } catch (ex) { - fail && fail({ type: "error", error: ex }); + fail?.({ type: "error", error: ex }); sizeRequesting.value = false; } } @@ -317,13 +317,13 @@ function download() { const reader = response.body!.getReader(); return new SSEProcessor(reader, dataProcess, undefined).start(); }).catch(ex => { - downloadReject && downloadReject({ type: "error", error: ex }); + downloadReject?.({ type: "error", error: ex }); downloding = false; }) } function cancelConfirm() { - downloadReject && downloadReject({ type: "cancelConfrim" }); + downloadReject?.({ type: "cancelConfrim" }); emits("close"); } @@ -336,7 +336,7 @@ function confirmDownload() { function cancelDownload() { abortController?.abort(); fetch(`${globalSetup.apiHost}/api/stopDownloadModel`) - downloadReject && downloadReject({ type: "cancelDownload" }); + downloadReject?.({ type: "cancelDownload" }); emits("close"); } diff --git a/WebUI/src/components/DropSelector.vue b/WebUI/src/components/DropSelector.vue index e252032..21a88d0 100644 --- a/WebUI/src/components/DropSelector.vue +++ b/WebUI/src/components/DropSelector.vue @@ -1,5 +1,6 @@ + \ No newline at end of file diff --git a/WebUI/src/components/ui/loadImage/LoadImage.vue b/WebUI/src/components/ui/loadImage/LoadImage.vue index 02b72c2..f8f4e63 100644 --- a/WebUI/src/components/ui/loadImage/LoadImage.vue +++ b/WebUI/src/components/ui/loadImage/LoadImage.vue @@ -57,7 +57,7 @@ function processFiles(files: File[] | null, inputCurrent: Ref) { diff --git a/WebUI/src/components/LoadingBar.vue b/WebUI/src/components/LoadingBar.vue index 62a20b7..3719812 100644 --- a/WebUI/src/components/LoadingBar.vue +++ b/WebUI/src/components/LoadingBar.vue @@ -1,16 +1,15 @@ \ No newline at end of file + text: string +}>() + diff --git a/WebUI/src/components/ModelDropDownItem.vue b/WebUI/src/components/ModelDropDownItem.vue index 5b8b697..7e21e4f 100644 --- a/WebUI/src/components/ModelDropDownItem.vue +++ b/WebUI/src/components/ModelDropDownItem.vue @@ -1,26 +1,31 @@ \ No newline at end of file + name: 'model not found', + type: 'llm', + downloaded: false, + }), + }, +) + diff --git a/WebUI/src/components/OutpaintOptions.vue b/WebUI/src/components/OutpaintOptions.vue index 000926a..b91409f 100644 --- a/WebUI/src/components/OutpaintOptions.vue +++ b/WebUI/src/components/OutpaintOptions.vue @@ -1,56 +1,72 @@ \ No newline at end of file + diff --git a/WebUI/src/components/PaintInfo.vue b/WebUI/src/components/PaintInfo.vue index 38f4c9e..227f693 100644 --- a/WebUI/src/components/PaintInfo.vue +++ b/WebUI/src/components/PaintInfo.vue @@ -1,49 +1,52 @@ \ No newline at end of file + diff --git a/WebUI/src/components/ProgressBar.vue b/WebUI/src/components/ProgressBar.vue index d0cf77f..75ba5d5 100644 --- a/WebUI/src/components/ProgressBar.vue +++ b/WebUI/src/components/ProgressBar.vue @@ -1,16 +1,16 @@ \ No newline at end of file +const html = computed(() => (props.text == null ? '' : props.text.replace('\r\n', '
'))) + diff --git a/WebUI/src/components/Radio.vue b/WebUI/src/components/Radio.vue index de30d32..5ee885f 100644 --- a/WebUI/src/components/Radio.vue +++ b/WebUI/src/components/Radio.vue @@ -1,24 +1,26 @@ \ No newline at end of file + (e: 'click'): void +}>() + diff --git a/WebUI/src/components/RadioBlock.vue b/WebUI/src/components/RadioBlock.vue index ee51d87..6779c9f 100644 --- a/WebUI/src/components/RadioBlock.vue +++ b/WebUI/src/components/RadioBlock.vue @@ -1,20 +1,29 @@ \ No newline at end of file + (e: 'click'): void +}>() + diff --git a/WebUI/src/components/Rag.vue b/WebUI/src/components/Rag.vue index 724a318..c9a44c7 100644 --- a/WebUI/src/components/Rag.vue +++ b/WebUI/src/components/Rag.vue @@ -1,222 +1,259 @@ \ No newline at end of file + diff --git a/WebUI/src/components/RandomNumber.vue b/WebUI/src/components/RandomNumber.vue index 29981d8..2ff5867 100644 --- a/WebUI/src/components/RandomNumber.vue +++ b/WebUI/src/components/RandomNumber.vue @@ -1,68 +1,72 @@ \ No newline at end of file + diff --git a/WebUI/src/components/SettingsBasic.vue b/WebUI/src/components/SettingsBasic.vue index f905904..63d23e5 100644 --- a/WebUI/src/components/SettingsBasic.vue +++ b/WebUI/src/components/SettingsBasic.vue @@ -7,10 +7,14 @@

Theme

-
- +
+
@@ -39,26 +43,49 @@

{{ languages.SETTINGS_LLM_BACKEND }}

- + @@ -68,15 +95,17 @@

{{ languages.SETTINGS_BACKEND_STATUS }}

- - - - + + + +
{{ mapServiceNameToDisplayName(item.serviceName) }}{{ mapToDisplayStatus(item.status) }}
{{ mapServiceNameToDisplayName(item.serviceName) }} + {{ mapToDisplayStatus(item.status) }} +
-
@@ -86,50 +115,54 @@
\ No newline at end of file + diff --git a/WebUI/src/components/SettingsImageComfyDynamic.vue b/WebUI/src/components/SettingsImageComfyDynamic.vue index 5da09d8..3636f6b 100644 --- a/WebUI/src/components/SettingsImageComfyDynamic.vue +++ b/WebUI/src/components/SettingsImageComfyDynamic.vue @@ -1,31 +1,49 @@ \ No newline at end of file +const imageGeneration = useImageGeneration() + diff --git a/WebUI/src/components/SettingsImageGeneration.vue b/WebUI/src/components/SettingsImageGeneration.vue index d385784..c3ad8a1 100644 --- a/WebUI/src/components/SettingsImageGeneration.vue +++ b/WebUI/src/components/SettingsImageGeneration.vue @@ -1,150 +1,237 @@ - \ No newline at end of file +const anyModifiableOrDisplayed = (settings: Setting[]) => + settings.some((setting) => modifiableOrDisplayed(setting)) +const modifiableOrDisplayed = (setting: Setting) => + imageGeneration.activeWorkflow.modifiableSettings.includes(setting) || + imageGeneration.activeWorkflow.displayedSettings.includes(setting) +const modifiable = (setting: Setting) => + imageGeneration.activeWorkflow.modifiableSettings.includes(setting) + diff --git a/WebUI/src/components/SettingsImageWorkflowSelector.vue b/WebUI/src/components/SettingsImageWorkflowSelector.vue index 714c568..8841595 100644 --- a/WebUI/src/components/SettingsImageWorkflowSelector.vue +++ b/WebUI/src/components/SettingsImageWorkflowSelector.vue @@ -1,6 +1,8 @@ \ No newline at end of file + diff --git a/WebUI/src/components/SettingsModel.vue b/WebUI/src/components/SettingsModel.vue index 672f143..f1640e0 100644 --- a/WebUI/src/components/SettingsModel.vue +++ b/WebUI/src/components/SettingsModel.vue @@ -4,10 +4,15 @@

{{ languages.SETTINGS_MODEL_HUGGINGFACE_API_TOKEN }}

- -
+ +
{{ languages.SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT }}
@@ -18,8 +23,14 @@

{{ languages.SETTINGS_MODEL_SD_STANDARD_MODEL }}

- + - +

{{ languages.SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL }}

- + - +

{{ languages.SETTINGS_MODEL_SD_HD_MODEL }}

- + - +

{{ languages.SETTINGS_MODEL_SD_HD_INPAINT_MODEL }}

- + - +
{{ - languages.COM_RESTORE - }} - -
@@ -121,18 +160,24 @@

{{ languages.SETTINGS_BASIC_PATHS }}

{{ languages.SETTINGS_BASIC_LLM_CHECKPOINTS }}

- +

{{ languages.SETTINGS_MODEL_SD_CHECKPOINTS }}

- +

{{ languages.SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS }}

- +

{{ languages.SETTINGS_MODEL_SD_LORA }}

- +
{{ - languages.COM_RESTORE - }} - -

{{ languages.SETTINGS_MODEL_DOWNLOAD }}

-

{{ languages.SETTINGS_MODEL_DOWNLOAD_DESC }}

+

{{ languages.SETTINGS_MODEL_DOWNLOAD_DESC }}

-

{{ languages.DOWNLOADER_FOR_IMAGE_GENERATE }} -

+

{{ languages.DOWNLOADER_FOR_IMAGE_GENERATE }}

- {{ - languages.SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD - }}: - dreamshaper-8 + {{ languages.SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD }}: dreamshaper-8 6.46 GB -
- {{ languages.SETTINGS_MODEL_IMAGE_RESOLUTION_HD }}: - Juggernaut-XL-v9 + {{ languages.SETTINGS_MODEL_IMAGE_RESOLUTION_HD }}: Juggernaut-XL-v9 7.65 GB -

{{ languages.DOWNLOADER_FOR_INAPINT_GENERATE }}

- {{ - languages.SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD - }}: - dreamshaper-8-inpainting + {{ languages.SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD }}: dreamshaper-8-inpainting 4.45 GB -

{{ languages.DOWNLOADER_FOR_IMAGE_LORA }}

- {{ - languages.SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD - }}: - lcm-lora-sdv1-5 + {{ languages.SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD }}: lcm-lora-sdv1-5 128 MB -
- {{ languages.SETTINGS_MODEL_IMAGE_RESOLUTION_HD }}: - lcm-lora-sdxl + {{ languages.SETTINGS_MODEL_IMAGE_RESOLUTION_HD }}: lcm-lora-sdxl 375.61 MB -
@@ -235,35 +283,38 @@
microsoft/Phi-3-mini-4k-instruct 7.11 GB -

{{ languages.DOWNLOADER_FOR_RAG_QUERY }}

- {{ - languages.SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD - }}: - bge-large-en-v1.5 + {{ languages.SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD }}: bge-large-en-v1.5 1.25 GB -
- {{ languages.SETTINGS_MODEL_IMAGE_RESOLUTION_HD }}: - bge-large-zh-v1.5 + {{ languages.SETTINGS_MODEL_IMAGE_RESOLUTION_HD }}: bge-large-zh-v1.5 1.21 GB -
@@ -271,119 +322,122 @@ \ No newline at end of file + diff --git a/WebUI/src/components/SlideBar.vue b/WebUI/src/components/SlideBar.vue index 6686d57..1bb7d5d 100644 --- a/WebUI/src/components/SlideBar.vue +++ b/WebUI/src/components/SlideBar.vue @@ -1,151 +1,167 @@ \ No newline at end of file + + + diff --git a/WebUI/src/components/TextToImage.vue b/WebUI/src/components/TextToImage.vue index f155044..cb2dd1f 100644 --- a/WebUI/src/components/TextToImage.vue +++ b/WebUI/src/components/TextToImage.vue @@ -1,18 +1,20 @@ \ No newline at end of file +useI18N() + diff --git a/WebUI/src/components/UpscaleOptions.vue b/WebUI/src/components/UpscaleOptions.vue index 144937d..806ac3a 100644 --- a/WebUI/src/components/UpscaleOptions.vue +++ b/WebUI/src/components/UpscaleOptions.vue @@ -1,45 +1,52 @@ \ No newline at end of file + diff --git a/WebUI/src/components/VerticalSlideBar.vue b/WebUI/src/components/VerticalSlideBar.vue index b3c8b25..567be77 100644 --- a/WebUI/src/components/VerticalSlideBar.vue +++ b/WebUI/src/components/VerticalSlideBar.vue @@ -1,148 +1,161 @@ \ No newline at end of file + + + diff --git a/WebUI/src/components/WarningDialog.vue b/WebUI/src/components/WarningDialog.vue index 7cfe91a..60446cc 100644 --- a/WebUI/src/components/WarningDialog.vue +++ b/WebUI/src/components/WarningDialog.vue @@ -1,41 +1,47 @@ \ No newline at end of file +defineExpose({ warningMessage, confirmFunction, onShow }) + diff --git a/WebUI/src/components/ui/collapsible/CollapsibleContent.vue b/WebUI/src/components/ui/collapsible/CollapsibleContent.vue index 9f30898..92acb77 100644 --- a/WebUI/src/components/ui/collapsible/CollapsibleContent.vue +++ b/WebUI/src/components/ui/collapsible/CollapsibleContent.vue @@ -5,7 +5,10 @@ const props = defineProps() diff --git a/WebUI/src/components/ui/input/Input.vue b/WebUI/src/components/ui/input/Input.vue index a16a1ef..31ea627 100644 --- a/WebUI/src/components/ui/input/Input.vue +++ b/WebUI/src/components/ui/input/Input.vue @@ -20,5 +20,13 @@ const modelValue = useVModel(props, 'modelValue', emits, { diff --git a/WebUI/src/components/ui/loadImage/LoadImage.vue b/WebUI/src/components/ui/loadImage/LoadImage.vue index f8f4e63..65d4350 100644 --- a/WebUI/src/components/ui/loadImage/LoadImage.vue +++ b/WebUI/src/components/ui/loadImage/LoadImage.vue @@ -1,7 +1,7 @@ diff --git a/WebUI/src/components/ui/slider/ResolutionPicker.vue b/WebUI/src/components/ui/slider/ResolutionPicker.vue index 842a42b..a3e2e95 100644 --- a/WebUI/src/components/ui/slider/ResolutionPicker.vue +++ b/WebUI/src/components/ui/slider/ResolutionPicker.vue @@ -23,16 +23,18 @@ const aspectRatios = [ ] const megaPixelsOptions = computed(() => - imageGeneration.activeWorkflow.tags.includes('sd1.5') ? [ - {label: '0.25', totalPixels: 512 * 512}, - {label: '0.5', totalPixels: 704 * 704} - ] : - [ - {label: '0.25', totalPixels: 512 * 512}, - {label: '0.5', totalPixels: 704 * 704}, - {label: '0.8', totalPixels: 896 * 896}, - {label: '1.0', totalPixels: 1024 * 1024}, - ]) + imageGeneration.activeWorkflow.tags.includes('sd1.5') + ? [ + { label: '0.25', totalPixels: 512 * 512 }, + { label: '0.5', totalPixels: 704 * 704 }, + ] + : [ + { label: '0.25', totalPixels: 512 * 512 }, + { label: '0.5', totalPixels: 704 * 704 }, + { label: '0.8', totalPixels: 896 * 896 }, + { label: '1.0', totalPixels: 1024 * 1024 }, + ], +) function findBestResolution(totalPixels: number, aspectRatio: number) { const MIN_SIZE = 256 @@ -61,102 +63,153 @@ function findBestResolution(totalPixels: number, aspectRatio: number) { } const resolutionsPerMegaPixelsOption = computed(() => - megaPixelsOptions.value.map(megaPixels => - aspectRatios.map(aspectRatio => ({ + megaPixelsOptions.value.map((megaPixels) => + aspectRatios.map((aspectRatio) => ({ aspectRatio: aspectRatio.label, - ...findBestResolution(megaPixels.totalPixels, aspectRatio.value) - }))) - ); + ...findBestResolution(megaPixels.totalPixels, aspectRatio.value), + })), + ), +) const megaPixelsIndex = computed({ get: () => { const currentTotalPixels = imageGeneration.width * imageGeneration.height - const bestMatch = megaPixelsOptions.value.map( - option => ({ ...option, distance: Math.abs(option.totalPixels - currentTotalPixels) }) - ).sort((a, b) => a.distance - b.distance)[0] - const index = megaPixelsOptions.value.findIndex(option => option.label === bestMatch.label) + const bestMatch = megaPixelsOptions.value + .map((option) => ({ ...option, distance: Math.abs(option.totalPixels - currentTotalPixels) })) + .sort((a, b) => a.distance - b.distance)[0] + const index = megaPixelsOptions.value.findIndex((option) => option.label === bestMatch.label) return index === -1 ? 0 : index }, set: (index) => { - const currentAspectRatio = resolutionsPerMegaPixelsOption.value[index][resolutionIndex.value[0]].aspectRatio; - const res = resolutionsPerMegaPixelsOption.value[index].find( - res => res.aspectRatio === currentAspectRatio - ) ?? resolutionsPerMegaPixelsOption.value[index][0] + const currentAspectRatio = + resolutionsPerMegaPixelsOption.value[index][resolutionIndex.value[0]].aspectRatio + const res = + resolutionsPerMegaPixelsOption.value[index].find( + (res) => res.aspectRatio === currentAspectRatio, + ) ?? resolutionsPerMegaPixelsOption.value[index][0] imageGeneration.width = res.width imageGeneration.height = res.height - } + }, }) const resolutionIndex = computed({ get: () => { const index = resolutionsPerMegaPixelsOption.value[megaPixelsIndex.value].findIndex( - res => res.width === imageGeneration.width && res.height === imageGeneration.height + (res) => res.width === imageGeneration.width && res.height === imageGeneration.height, ) - if (index === -1) return [resolutionsPerMegaPixelsOption.value[megaPixelsIndex.value].findIndex( - res => res.aspectRatio === '1/1')] + if (index === -1) + return [ + resolutionsPerMegaPixelsOption.value[megaPixelsIndex.value].findIndex( + (res) => res.aspectRatio === '1/1', + ), + ] return [index] }, set: (resIndex) => { const res = resolutionsPerMegaPixelsOption.value[megaPixelsIndex.value][resIndex[0]] imageGeneration.width = res.width imageGeneration.height = res.height - } + }, }) const sliderModel = computed({ get: () => resolutionIndex.value, set: (value) => { resolutionIndex.value = value - } + }, }) -