diff --git a/.github/workflows/eslint-prettier.yml b/.github/workflows/eslint-prettier.yml new file mode 100644 index 00000000..ef4b743f --- /dev/null +++ b/.github/workflows/eslint-prettier.yml @@ -0,0 +1,16 @@ +name: Lint TS +on: [push, pull_request] +jobs: + lint-ts: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./WebUI + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - run: npm ci + - run: npm run lint:ci + - run: npm run format:ci diff --git a/WebUI/.editorconfig b/WebUI/.editorconfig new file mode 100644 index 00000000..7f5b23fc --- /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 00000000..29a2402e --- /dev/null +++ b/WebUI/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "singleQuote": true, + "printWidth": 100 +} diff --git a/WebUI/build/README.md b/WebUI/build/README.md index da592a61..fb0c0ad3 100644 --- a/WebUI/build/README.md +++ b/WebUI/build/README.md @@ -2,22 +2,23 @@ ## Fetch remote dependencies for build -1. run ```npm install``` +1. run `npm install` 2. aquire windows libuv dlls, e.g. via miniforge: - Install miniforge: https://github.com/conda-forge/miniforge - Create a reference conda environment with libuv installed - ``` - conda create -n cp311_libuv python=3.11 libuv -y - # copy the path to this conda env - conda env list | findstr cp311_libuv - ``` -3. run ```npm run fetch-build-resources -- --conda_env_dir=$PATH_TO_CONDA_ENV``` + ``` + conda create -n cp311_libuv python=3.11 libuv -y + # copy the path to this conda env + conda env list | findstr cp311_libuv + ``` +3. run `npm run fetch-build-resources -- --conda_env_dir=$PATH_TO_CONDA_ENV` ## decide for offline or online installer ### online installer -run +run + ``` npm run prepare-build npm run build @@ -28,6 +29,7 @@ npm run build **FIXME: offline scripts are missing** run + ``` npm run prepare-build:${PLATFORM}-offline npm run build:${PLATFORM}-offline diff --git a/WebUI/build/build-config.json b/WebUI/build/build-config.json index bce0ba33..1ee0cbd9 100644 --- a/WebUI/build/build-config.json +++ b/WebUI/build/build-config.json @@ -24,9 +24,7 @@ { "from": "external/LlamaCPP", "to": "LlamaCPP", - "filter": [ - "!__pycache__/" - ] + "filter": ["!__pycache__/"] }, { "from": "external/intel_extension_for_pytorch-2.3.110+xpu-cp311-cp311-win_amd64.whl", @@ -77,27 +75,19 @@ "directories": { "output": "../release" }, - "files": [ - "dist", - "dist-electron" - ], + "files": ["dist", "dist-electron"], "win": { "target": [ { "target": "nsis", - "arch": [ - "x64" - ] + "arch": ["x64"] } ], "verifyUpdateCodeSignature": false, "requestedExecutionLevel": "asInvoker", "icon": "app.ico", "artifactName": "${productName}-${version}.${ext}", - "electronLanguages": [ - "en-US", - "zh-CN" - ] + "electronLanguages": ["en-US", "zh-CN"] }, "nsis": { "license": "build/license.rtf", diff --git a/WebUI/build/scripts/check-i18n.js b/WebUI/build/scripts/check-i18n.js index 9d353aba..67b7a12d 100644 --- a/WebUI/build/scripts/check-i18n.js +++ b/WebUI/build/scripts/check-i18n.js @@ -1,59 +1,59 @@ // Usage: node check-i18n.js --i18n-dir=$DIR -const fs = require('fs'); -const path = require('path'); -const argv = require('minimist')(process.argv.slice(2)); +const fs = require('fs') +const path = require('path') +const argv = require('minimist')(process.argv.slice(2)) -const i18nDirArg = argv.i18n_dir; +const i18nDirArg = argv.i18n_dir if (!i18nDirArg) { - console.error('Usage: node check-i18n.js --i18n-dir=$DIR\n'); - process.exit(1); + console.error('Usage: node check-i18n.js --i18n-dir=$DIR\n') + process.exit(1) } -const i18nDir = path.resolve(i18nDirArg); -const enData = JSON.parse(fs.readFileSync(path.join(i18nDir, 'en-US.json'))); -const keys = Object.keys(enData); +const i18nDir = path.resolve(i18nDirArg) +const enData = JSON.parse(fs.readFileSync(path.join(i18nDir, 'en-US.json'))) +const keys = Object.keys(enData) -const langs = fs.readdirSync(i18nDir).filter(f => f.endsWith('.json')); -const missingKeys = {}; -const unusedKeys = {}; +const langs = fs.readdirSync(i18nDir).filter((f) => f.endsWith('.json')) +const missingKeys = {} +const unusedKeys = {} -langs.forEach(lang => { - const data = JSON.parse(fs.readFileSync(path.join(i18nDir, lang))); - keys.forEach(key => { - if (!data[key]) { - if (!missingKeys[lang]) { - missingKeys[lang] = []; - } - missingKeys[lang].push(key); - } - }); - - Object.keys(data).forEach(key => { - if (!keys.includes(key)) { - if (!unusedKeys[lang]) { - unusedKeys[lang] = []; - } - unusedKeys[lang].push(key); - } - }); +langs.forEach((lang) => { + const data = JSON.parse(fs.readFileSync(path.join(i18nDir, lang))) + keys.forEach((key) => { + if (!data[key]) { + if (!missingKeys[lang]) { + missingKeys[lang] = [] + } + missingKeys[lang].push(key) + } + }) - if (missingKeys[lang]) { - // print pretty - // 5 entries per row - const entriesPerRow = 5; - const entries = missingKeys[lang]; - const rows = []; - for (let i = 0; i < entries.length; i += entriesPerRow) { - rows.push(entries.slice(i, i + entriesPerRow).join(', ')); - } - console.log(`${missingKeys[lang].length} missing keys for ${lang}:`); - console.log(` ${rows.join('\n ')}`); - console.log(); + Object.keys(data).forEach((key) => { + if (!keys.includes(key)) { + if (!unusedKeys[lang]) { + unusedKeys[lang] = [] + } + unusedKeys[lang].push(key) } - if (unusedKeys[lang]) { - console.log(`${unusedKeys[lang].length} unused keys for ${lang}:`); - console.log(` ${unusedKeys[lang].join(', ')}`); - console.log(); + }) + + if (missingKeys[lang]) { + // print pretty + // 5 entries per row + const entriesPerRow = 5 + const entries = missingKeys[lang] + const rows = [] + for (let i = 0; i < entries.length; i += entriesPerRow) { + rows.push(entries.slice(i, i + entriesPerRow).join(', ')) } -}); + console.log(`${missingKeys[lang].length} missing keys for ${lang}:`) + console.log(` ${rows.join('\n ')}`) + console.log() + } + if (unusedKeys[lang]) { + console.log(`${unusedKeys[lang].length} unused keys for ${lang}:`) + console.log(` ${unusedKeys[lang].join(', ')}`) + console.log() + } +}) diff --git a/WebUI/build/scripts/fetch-python-package-resources.js b/WebUI/build/scripts/fetch-python-package-resources.js index 7c6313d6..d7355e05 100644 --- a/WebUI/build/scripts/fetch-python-package-resources.js +++ b/WebUI/build/scripts/fetch-python-package-resources.js @@ -1,93 +1,99 @@ // Usage: node fetch-python-package-resources.js --target-dir=$DIR --conda_env_dir=$DIR -const https = require('https'); -const fs = require('fs'); -const path = require('path'); -const { HttpsProxyAgent } = require('https-proxy-agent'); +const https = require('https') +const fs = require('fs') +const path = require('path') +const { HttpsProxyAgent } = require('https-proxy-agent') - -const argv = require('minimist')(process.argv.slice(2)); +const argv = require('minimist')(process.argv.slice(2)) const targetDirArg = argv.target_dir const condaEnvDirArg = argv.conda_env_dir if (!targetDirArg || !condaEnvDirArg) { - console.error('Usage: node fetch-python-package-resources.js --target_dir=$DIR --conda_env_dir=$DIR\n'); - process.exit(1); + console.error( + 'Usage: node fetch-python-package-resources.js --target_dir=$DIR --conda_env_dir=$DIR\n', + ) + process.exit(1) } -const targetDir = path.resolve(targetDirArg); +const targetDir = path.resolve(targetDirArg) const condaTargetDir = path.join(targetDir, 'conda-env-lib') -const condaEnvLibraryDir = path.resolve(path.join(condaEnvDirArg, 'Library')); +const condaEnvLibraryDir = path.resolve(path.join(condaEnvDirArg, 'Library')) -const embeddablePythonUrl = 'https://raw.githubusercontent.com/adang1345/PythonWindows/master/3.11.10/python-3.11.10-embed-amd64.zip'; +const embeddablePythonUrl = + 'https://raw.githubusercontent.com/adang1345/PythonWindows/master/3.11.10/python-3.11.10-embed-amd64.zip' const getPipScriptUrl = 'https://bootstrap.pypa.io/get-pip.py' const sevenZrExeUrl = 'https://www.7-zip.org/a/7zr.exe' function fetchFileIfNotPresent(url) { - const expectedFilePath = path.join(targetDir, getBaseFileName(url)) - if (fs.existsSync(expectedFilePath)) { - console.log(`omitting fetching of ${url} as ${expectedFilePath} already exists`) - } else { - fetchFile(url) - } + const expectedFilePath = path.join(targetDir, getBaseFileName(url)) + if (fs.existsSync(expectedFilePath)) { + console.log(`omitting fetching of ${url} as ${expectedFilePath} already exists`) + } else { + fetchFile(url) + } } function fetchFile(url) { - const proxy = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy; - const options = proxy ? { agent: new HttpsProxyAgent(proxy) } : {}; - https.get(url, options, (response) => { - const filePath = path.join(targetDir, getBaseFileName(url)) - const file = fs.createWriteStream(filePath); - response.pipe(file); + const proxy = + process.env.HTTPS_PROXY || + process.env.https_proxy || + process.env.HTTP_PROXY || + process.env.http_proxy + const options = proxy ? { agent: new HttpsProxyAgent(proxy) } : {} + https + .get(url, options, (response) => { + const filePath = path.join(targetDir, getBaseFileName(url)) + const file = fs.createWriteStream(filePath) + response.pipe(file) - file.on('finish', () => { - file.close(); - console.log(`Downloaded ${filePath} successfully!`); - }); - }).on('error', (err) => { - console.error(`Error downloading ${embeddablePythonUrl}: ${err}`); - }); + file.on('finish', () => { + file.close() + console.log(`Downloaded ${filePath} successfully!`) + }) + }) + .on('error', (err) => { + console.error(`Error downloading ${embeddablePythonUrl}: ${err}`) + }) } - function getBaseFileName(url) { - const urlPathSegments = url.split('/'); - const baseFileName = urlPathSegments[urlPathSegments.length - 1] - return baseFileName; + const urlPathSegments = url.split('/') + const baseFileName = urlPathSegments[urlPathSegments.length - 1] + return baseFileName } function prepareTargetPath() { - if (!fs.existsSync(condaTargetDir)) { - fs.mkdirSync(targetDir, { recursive: true }); - } + if (!fs.existsSync(condaTargetDir)) { + fs.mkdirSync(targetDir, { recursive: true }) + } } function copyLibuvDllsIfNotPresent() { - if (fs.existsSync(path.join(condaTargetDir, 'Library', 'bin', 'uv.dll'))) { - console.log(`omitting fetching copying of libuv DLLs, as they already exist`) - } else { - if (!path.join(condaEnvLibraryDir, 'bin', 'uv.dll')) { - console.log(`provided conda env at ${condaEnvLibraryDir} is missing uv.dll. Aborting`) - process.exit(1); - } - fs.cp(condaEnvLibraryDir, path.join(condaTargetDir, 'Library'), { recursive: true }, (err) => { - if (err) { - console.error(err); - console.log('Failed to copy directory'); - process.exit(1) - } else { - console.log('Directory copied successfully'); - } - }); + if (fs.existsSync(path.join(condaTargetDir, 'Library', 'bin', 'uv.dll'))) { + console.log(`omitting fetching copying of libuv DLLs, as they already exist`) + } else { + if (!path.join(condaEnvLibraryDir, 'bin', 'uv.dll')) { + console.log(`provided conda env at ${condaEnvLibraryDir} is missing uv.dll. Aborting`) + process.exit(1) } + fs.cp(condaEnvLibraryDir, path.join(condaTargetDir, 'Library'), { recursive: true }, (err) => { + if (err) { + console.error(err) + console.log('Failed to copy directory') + process.exit(1) + } else { + console.log('Directory copied successfully') + } + }) + } } - function main() { - prepareTargetPath() - fetchFileIfNotPresent(embeddablePythonUrl) - fetchFileIfNotPresent(getPipScriptUrl) - fetchFileIfNotPresent(sevenZrExeUrl) - copyLibuvDllsIfNotPresent() + prepareTargetPath() + fetchFileIfNotPresent(embeddablePythonUrl) + fetchFileIfNotPresent(getPipScriptUrl) + fetchFileIfNotPresent(sevenZrExeUrl) + copyLibuvDllsIfNotPresent() } main() diff --git a/WebUI/build/scripts/install-full-python-env.js b/WebUI/build/scripts/install-full-python-env.js index a4985f90..b865aeb2 100644 --- a/WebUI/build/scripts/install-full-python-env.js +++ b/WebUI/build/scripts/install-full-python-env.js @@ -1,87 +1,98 @@ // Usage: node pack-offline.js // -const fs = require('fs'); -const path = require('path'); -const childProcess = require('child_process'); +const fs = require('fs') +const path = require('path') +const childProcess = require('child_process') -const argv = require('minimist')(process.argv.slice(2)); +const argv = require('minimist')(process.argv.slice(2)) const envDirArg = argv.env_dir const platformArg = argv.platform const comfyUIDIrArg = argv.comfy_ui_dir if (!envDirArg || !platformArg || !comfyUIDIrArg) { - console.error('Usage: node install-full-python-env.js --env_dir=$DIR ---platform=arc|ultra|ultra2\n'); - process.exit(1); + console.error( + 'Usage: node install-full-python-env.js --env_dir=$DIR ---platform=arc|ultra|ultra2\n', + ) + process.exit(1) } -const envDir = existingFileOrExit(path.resolve(envDirArg)); -const comfyUIDIr = existingFileOrExit(path.resolve(comfyUIDIrArg)); -const platform = platformArg; +const envDir = existingFileOrExit(path.resolve(envDirArg)) +const comfyUIDIr = existingFileOrExit(path.resolve(comfyUIDIrArg)) +const platform = platformArg function existingFileOrExit(filePath) { - if (!fs.existsSync(filePath)) { - console.error('Resource not found:', filePath); - process.exit(1); - } - return filePath + if (!fs.existsSync(filePath)) { + console.error('Resource not found:', filePath) + process.exit(1) + } + return filePath } - function installPip(pythonExe, getPipFilePath) { - const runGetPip = childProcess.spawnSync(pythonExe, [getPipFilePath]); - console.log(runGetPip.stdout.toString()); - console.error(runGetPip.stderr.toString()); - if (runGetPip.status!== 0) { - console.error('Failed to install requirements'); - process.exit(1); - } - console.log('Successfully installed pip'); + const runGetPip = childProcess.spawnSync(pythonExe, [getPipFilePath]) + console.log(runGetPip.stdout.toString()) + console.error(runGetPip.stderr.toString()) + if (runGetPip.status !== 0) { + console.error('Failed to install requirements') + process.exit(1) + } + console.log('Successfully installed pip') } function runPipInstall(pythonExe, requirementsFilePath) { - console.log(`installing python dependencies from ${requirementsFilePath}`); - const pipInstall = childProcess.spawnSync(pythonExe, ['-m', 'pip', 'install', '-r', requirementsFilePath]); - console.log(pipInstall.stdout.toString()); - console.error(pipInstall.stderr.toString()); - if (pipInstall.status!== 0) { - console.error('Failed to install requirements'); - process.exit(1); - } - console.log(`Installed dependencies from ${requirementsFilePath} successfully`); + console.log(`installing python dependencies from ${requirementsFilePath}`) + const pipInstall = childProcess.spawnSync(pythonExe, [ + '-m', + 'pip', + 'install', + '-r', + requirementsFilePath, + ]) + console.log(pipInstall.stdout.toString()) + console.error(pipInstall.stderr.toString()) + if (pipInstall.status !== 0) { + console.error('Failed to install requirements') + process.exit(1) + } + console.log(`Installed dependencies from ${requirementsFilePath} successfully`) } function copyToTargetDir(sourceDir, targetDir) { - fs.cpSync(sourceDir, targetDir, { recursive: true }); - console.log(`copied resources to ${targetDir}`) + fs.cpSync(sourceDir, targetDir, { recursive: true }) + console.log(`copied resources to ${targetDir}`) } function prepareTargetDir(targetDir) { - if (fs.existsSync(targetDir)) { - console.log(`clearing previous env dir ${targetDir}`) - fs.rmSync(targetDir, { recursive: true }); - } + if (fs.existsSync(targetDir)) { + console.log(`clearing previous env dir ${targetDir}`) + fs.rmSync(targetDir, { recursive: true }) + } } - function main() { - const targetDir = path.join(envDir, '..', '..', 'offline', platform, 'prototype-python-env'); - prepareTargetDir(targetDir) - copyToTargetDir(envDir, targetDir) - - const pythonExe = existingFileOrExit(path.join(targetDir, 'python.exe')); - const getPipFile = existingFileOrExit(path.join(targetDir, 'get-pip.py')); - - const platformSpecificRequirementsTxt = existingFileOrExit(path.join(__dirname, '..', '..','..', 'service', `requirements-${platform}.txt`)); - const requirementsTxt = existingFileOrExit(path.join(__dirname, '..', '..', '..', 'service', `requirements.txt`)); - const comfyUiRequirementsTxt = existingFileOrExit(path.join(comfyUIDIr, `requirements.txt`)); - const ggufCostomNoderequirementsTxt = existingFileOrExit(path.join(comfyUIDIr, 'custom_nodes', 'ComfyUI-GGUF', `requirements.txt`)); - - - installPip(pythonExe, getPipFile) - runPipInstall(pythonExe, platformSpecificRequirementsTxt) - runPipInstall(pythonExe, requirementsTxt) - runPipInstall(pythonExe, comfyUiRequirementsTxt) - runPipInstall(pythonExe, ggufCostomNoderequirementsTxt) + const targetDir = path.join(envDir, '..', '..', 'offline', platform, 'prototype-python-env') + prepareTargetDir(targetDir) + copyToTargetDir(envDir, targetDir) + + const pythonExe = existingFileOrExit(path.join(targetDir, 'python.exe')) + const getPipFile = existingFileOrExit(path.join(targetDir, 'get-pip.py')) + + const platformSpecificRequirementsTxt = existingFileOrExit( + path.join(__dirname, '..', '..', '..', 'service', `requirements-${platform}.txt`), + ) + const requirementsTxt = existingFileOrExit( + path.join(__dirname, '..', '..', '..', 'service', `requirements.txt`), + ) + const comfyUiRequirementsTxt = existingFileOrExit(path.join(comfyUIDIr, `requirements.txt`)) + const ggufCostomNoderequirementsTxt = existingFileOrExit( + path.join(comfyUIDIr, 'custom_nodes', 'ComfyUI-GGUF', `requirements.txt`), + ) + + installPip(pythonExe, getPipFile) + runPipInstall(pythonExe, platformSpecificRequirementsTxt) + runPipInstall(pythonExe, requirementsTxt) + runPipInstall(pythonExe, comfyUiRequirementsTxt) + runPipInstall(pythonExe, ggufCostomNoderequirementsTxt) } -main(); +main() diff --git a/WebUI/build/scripts/patch-nsis-template.js b/WebUI/build/scripts/patch-nsis-template.js index b7fc155f..19bdb8dd 100644 --- a/WebUI/build/scripts/patch-nsis-template.js +++ b/WebUI/build/scripts/patch-nsis-template.js @@ -1,5 +1,8 @@ -path = require('path'); -const customizedTemplate = path.resolve(__dirname, '../installSection.nsh'); -const targetTemplate = path.resolve(__dirname, '../../node_modules/app-builder-lib/templates/nsis/installSection.nsh'); -console.log(`Copying customized NSIS template ${customizedTemplate} to ${targetTemplate}`); -require('fs').copyFileSync(customizedTemplate, targetTemplate); +path = require('path') +const customizedTemplate = path.resolve(__dirname, '../installSection.nsh') +const targetTemplate = path.resolve( + __dirname, + '../../node_modules/app-builder-lib/templates/nsis/installSection.nsh', +) +console.log(`Copying customized NSIS template ${customizedTemplate} to ${targetTemplate}`) +require('fs').copyFileSync(customizedTemplate, targetTemplate) diff --git a/WebUI/build/scripts/prepare-python-env.js b/WebUI/build/scripts/prepare-python-env.js index 30fb9fbe..e9fd552b 100644 --- a/WebUI/build/scripts/prepare-python-env.js +++ b/WebUI/build/scripts/prepare-python-env.js @@ -1,73 +1,82 @@ // Usage: node prepare-python-env.js --target_dir=$DIR ---env_resources_dir=$DIR -const fs = require('fs'); -const path = require('path'); -const AdmZip = require('adm-zip'); -const childProcess = require('child_process'); +const fs = require('fs') +const path = require('path') +const AdmZip = require('adm-zip') +const childProcess = require('child_process') -const argv = require('minimist')(process.argv.slice(2)); +const argv = require('minimist')(process.argv.slice(2)) const buildResourcesDirArg = argv.build_resources_dir const targetDirArg = argv.target_dir if (!buildResourcesDirArg || !targetDirArg) { - console.error('Usage: node prepare-python-env.js --target_dir=$DIR ---env_resources_dir=$DIR\n'); - process.exit(1); + console.error('Usage: node prepare-python-env.js --target_dir=$DIR ---env_resources_dir=$DIR\n') + process.exit(1) } - -const envResourcesDir = path.resolve(buildResourcesDirArg); -const targetDir = path.resolve(targetDirArg); - -const envResourcesFiles = fs.readdirSync(envResourcesDir); -const pythonEmbedZipFile = path.join(envResourcesDir, envResourcesFiles.find((fileName) => { return fileName.startsWith('python') && fileName.endsWith('.zip') })); -const condaDir = path.join(envResourcesDir, envResourcesFiles.find((fileName) => { return fileName.includes('conda') })); -const condaBinDir = path.join(condaDir, 'Library', 'bin'); -const getPipFile = path.join(envResourcesDir, 'get-pip.py'); +const envResourcesDir = path.resolve(buildResourcesDirArg) +const targetDir = path.resolve(targetDirArg) + +const envResourcesFiles = fs.readdirSync(envResourcesDir) +const pythonEmbedZipFile = path.join( + envResourcesDir, + envResourcesFiles.find((fileName) => { + return fileName.startsWith('python') && fileName.endsWith('.zip') + }), +) +const condaDir = path.join( + envResourcesDir, + envResourcesFiles.find((fileName) => { + return fileName.includes('conda') + }), +) +const condaBinDir = path.join(condaDir, 'Library', 'bin') +const getPipFile = path.join(envResourcesDir, 'get-pip.py') function verifyFilesExist() { - console.log('verifying all required files exist.') - if (!fs.existsSync(pythonEmbedZipFile)) { - console.error('File not found:', pythonEmbedZipFile); - process.exit(1); - } - - if (!fs.existsSync(getPipFile)) { - console.error('File not found:', getPipFile); - process.exit(1); - } - - // check whether libuv has been installed in the conda env - const uvDll = path.join(condaBinDir, 'uv.dll'); - if (!fs.existsSync(uvDll)) { - console.error('uv.dll not found in reference conda env:', uvDll); - process.exit(1); - } - console.log('all required files exist.') - - if (!fs.existsSync(targetDir)) { - console.log(`Creating missing target dir: ${targetDir}`) - fs.mkdirSync(targetDir, { recursive: true }); - } + console.log('verifying all required files exist.') + if (!fs.existsSync(pythonEmbedZipFile)) { + console.error('File not found:', pythonEmbedZipFile) + process.exit(1) + } + + if (!fs.existsSync(getPipFile)) { + console.error('File not found:', getPipFile) + process.exit(1) + } + + // check whether libuv has been installed in the conda env + const uvDll = path.join(condaBinDir, 'uv.dll') + if (!fs.existsSync(uvDll)) { + console.error('uv.dll not found in reference conda env:', uvDll) + process.exit(1) + } + console.log('all required files exist.') + + if (!fs.existsSync(targetDir)) { + console.log(`Creating missing target dir: ${targetDir}`) + fs.mkdirSync(targetDir, { recursive: true }) + } } function preparePythonEnvDir(pyEnvTargetPath) { - if (fs.existsSync(pyEnvTargetPath)) { - console.warn("Removing existing python env directory:", pyEnvTargetPath); - fs.rmSync(pyEnvTargetPath, {recursive: true}); - } + if (fs.existsSync(pyEnvTargetPath)) { + console.warn('Removing existing python env directory:', pyEnvTargetPath) + fs.rmSync(pyEnvTargetPath, { recursive: true }) + } } function createPythonEnvFromEmbedabblePythonZip(targetDir, pythonEmbedZipFile) { - preparePythonEnvDir(targetDir); - console.log('Creating python env.') - const pythonEmbed = new AdmZip(pythonEmbedZipFile); - pythonEmbed.extractAllTo(targetDir, true); - console.log('Extracted embeddable python to:', targetDir); - - // configure path of python env: - console.log('Patching path of python environment'); - const pthFile = path.join(targetDir, 'python311._pth'); - const pthContent = ` + preparePythonEnvDir(targetDir) + console.log('Creating python env.') + const pythonEmbed = new AdmZip(pythonEmbedZipFile) + pythonEmbed.extractAllTo(targetDir, true) + console.log('Extracted embeddable python to:', targetDir) + + // configure path of python env: + console.log('Patching path of python environment') + const pthFile = path.join(targetDir, 'python311._pth') + const pthContent = ` python311.zip . ../service @@ -76,53 +85,52 @@ python311.zip # Uncomment to run site.main() automatically import site -`; - fs.writeFileSync(pthFile, pthContent); - console.log('patched python paths'); - - console.log('Copying get-pip.py'); - const getPipDest = path.join(targetDir, 'get-pip.py'); - fs.copyFileSync(getPipFile, getPipDest); - console.log('Copied get-pip.py to:', getPipDest); - - console.log('Installing pip'); - const pythonExe = path.join(targetDir, 'python.exe'); - const pipInstallCmd = `"${pythonExe}" "${getPipDest}"`; - childProcess.execSync(pipInstallCmd); - console.log('Installed pip'); - - console.log('Installing uv'); - const pipInstallUvCmd = `"${pythonExe}" -m pip install uv`; - childProcess.execSync(pipInstallUvCmd); - console.log('Installed uv'); - - return targetDir; +` + fs.writeFileSync(pthFile, pthContent) + console.log('patched python paths') + + console.log('Copying get-pip.py') + const getPipDest = path.join(targetDir, 'get-pip.py') + fs.copyFileSync(getPipFile, getPipDest) + console.log('Copied get-pip.py to:', getPipDest) + + console.log('Installing pip') + const pythonExe = path.join(targetDir, 'python.exe') + const pipInstallCmd = `"${pythonExe}" "${getPipDest}"` + childProcess.execSync(pipInstallCmd) + console.log('Installed pip') + + console.log('Installing uv') + const pipInstallUvCmd = `"${pythonExe}" -m pip install uv` + childProcess.execSync(pipInstallUvCmd) + console.log('Installed uv') + + return targetDir } function patchCondaDllsIntoPythonEnv(pyEnvDirPath, condaBinDir) { - console.log('Copying conda dlls to python env'); - - for (const condaDll of fs.readdirSync(condaBinDir)) { - const src = path.join(condaBinDir, condaDll); - const dest = path.join(pyEnvDirPath, condaDll); - fs.copyFileSync(src, dest); - } - console.log('Copied conda dlls into:', pyEnvDirPath); + console.log('Copying conda dlls to python env') + + for (const condaDll of fs.readdirSync(condaBinDir)) { + const src = path.join(condaBinDir, condaDll) + const dest = path.join(pyEnvDirPath, condaDll) + fs.copyFileSync(src, dest) + } + console.log('Copied conda dlls into:', pyEnvDirPath) } function prepareTargetDir(targetDir) { - if (fs.existsSync(targetDir)) { - console.log(`clearing previous env dir ${targetDir}`) - fs.rmSync(targetDir, { recursive: true }); - } + if (fs.existsSync(targetDir)) { + console.log(`clearing previous env dir ${targetDir}`) + fs.rmSync(targetDir, { recursive: true }) + } } - function main() { - verifyFilesExist(); - prepareTargetDir(targetDir) - createPythonEnvFromEmbedabblePythonZip(targetDir, pythonEmbedZipFile); - patchCondaDllsIntoPythonEnv(targetDir, condaBinDir); + verifyFilesExist() + prepareTargetDir(targetDir) + createPythonEnvFromEmbedabblePythonZip(targetDir, pythonEmbedZipFile) + patchCondaDllsIntoPythonEnv(targetDir, condaBinDir) } -main(); +main() diff --git a/WebUI/build/scripts/provide-electron-build-resources.js b/WebUI/build/scripts/provide-electron-build-resources.js index b357ec75..c1e254c0 100644 --- a/WebUI/build/scripts/provide-electron-build-resources.js +++ b/WebUI/build/scripts/provide-electron-build-resources.js @@ -1,20 +1,27 @@ // Usage: node provide-electron-build-resources.js --build_resources_dir=$DIR --python_env_dir=$DIR ---backend_dir=$DIR --target_dir=$DIR -const fs = require('fs'); -const path = require('path'); -const childProcess = require('child_process'); +const fs = require('fs') +const path = require('path') +const childProcess = require('child_process') -const argv = require('minimist')(process.argv.slice(2)); +const argv = require('minimist')(process.argv.slice(2)) const buildResourcesDirArg = argv.build_resources_dir const pythonEnvDirArg = argv.python_env_dir const aiBackendDirArg = argv.backend_dir const llamaCppBackendDirArg = argv.llamacpp_dir const targetDirectoryArg = argv.target_dir - -if (!buildResourcesDirArg || !pythonEnvDirArg || !aiBackendDirArg || !targetDirectoryArg || !llamaCppBackendDirArg) { - console.error('Usage: node provide-electron-build-resources.js --build_resources_dir=$DIR --python_env_dir=$DIR --backend_dir=$DIR --llamacpp_dir=$DIR --target_dir=$DIR\n'); - process.exit(1); +if ( + !buildResourcesDirArg || + !pythonEnvDirArg || + !aiBackendDirArg || + !targetDirectoryArg || + !llamaCppBackendDirArg +) { + console.error( + 'Usage: node provide-electron-build-resources.js --build_resources_dir=$DIR --python_env_dir=$DIR --backend_dir=$DIR --llamacpp_dir=$DIR --target_dir=$DIR\n', + ) + process.exit(1) } const buildResourcesDir = path.resolve(buildResourcesDirArg) @@ -23,66 +30,54 @@ const backendDir = path.resolve(aiBackendDirArg) const llamaCppBackendDir = path.resolve(llamaCppBackendDirArg) const targetDir = path.resolve(targetDirectoryArg) - function symlinkDir(dir, target) { - // remove link if already exists - if (fs.existsSync(target)) { - fs.unlinkSync(target); - console.log('Removed symlink:', target); - } - // make link from service to /service - fs.symlinkSync(dir, target, 'junction'); - console.log('Created symlink:', dir); + // remove link if already exists + if (fs.existsSync(target)) { + fs.unlinkSync(target) + console.log('Removed symlink:', target) + } + // make link from service to /service + fs.symlinkSync(dir, target, 'junction') + console.log('Created symlink:', dir) } - function zipPythonEnv(sevenZipExe, pythonEnvDir, targetPath) { - console.log(`zipping ${pythonEnvDir} to ${targetPath}`); + console.log(`zipping ${pythonEnvDir} to ${targetPath}`) - const zip = childProcess.spawnSync(sevenZipExe, ['a', targetPath, pythonEnvDir]); - console.log(zip.stdout.toString()); - console.error(zip.stderr.toString()); - if (zip.status !== 0) { - console.error('Failed to compress offline env directory'); - process.exit(1); - } + const zip = childProcess.spawnSync(sevenZipExe, ['a', targetPath, pythonEnvDir]) + console.log(zip.stdout.toString()) + console.error(zip.stderr.toString()) + if (zip.status !== 0) { + console.error('Failed to compress offline env directory') + process.exit(1) + } - console.log('Offline env has been compressed to:', targetPath); + console.log('Offline env has been compressed to:', targetPath) } function copyFiles(targetDir, ...files) { - for (const file of files) { - fs.copyFileSync(file, path.join(targetDir, path.basename(file))); - console.log('Copied:', file, 'to:', path.join(targetDir, path.basename(file))); - } -} - -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))); - } + for (const file of files) { + fs.copyFileSync(file, path.join(targetDir, path.basename(file))) + console.log('Copied:', file, 'to:', path.join(targetDir, path.basename(file))) + } } function clearPreviousZip(zipFilePath) { - if - (fs.existsSync(zipFilePath)) { - console.log('Removing previous zip file:', zipFilePath)< - fs.rmSync(zipFilePath, { recursive: true }); - } + if (fs.existsSync(zipFilePath)) { + console.log('Removing previous zip file:', zipFilePath) + fs.rmSync(zipFilePath, { recursive: true }) + } } function main() { - const sevenZipExe = path.join(buildResourcesDir, '7zr.exe'); + const sevenZipExe = path.join(buildResourcesDir, '7zr.exe') - clearPreviousZip(path.join(targetDir, `prototype-python-env.7z`)); - zipPythonEnv(sevenZipExe, pythenEnvDir, path.join(targetDir, `prototype-python-env.7z`)); + clearPreviousZip(path.join(targetDir, `prototype-python-env.7z`)) + zipPythonEnv(sevenZipExe, pythenEnvDir, path.join(targetDir, `prototype-python-env.7z`)) - symlinkDir(backendDir, path.join(targetDir, 'service')) - symlinkDir(llamaCppBackendDir, path.join(targetDir, 'LlamaCpp')) - copyFiles(targetDir, - sevenZipExe - ) + symlinkDir(backendDir, path.join(targetDir, 'service')) + symlinkDir(llamaCppBackendDir, path.join(targetDir, 'LlamaCpp')) + copyFiles(targetDir, sevenZipExe) } main() diff --git a/WebUI/build/settings.json b/WebUI/build/settings.json index c602f9e0..6af4a262 100644 --- a/WebUI/build/settings.json +++ b/WebUI/build/settings.json @@ -1,12 +1,12 @@ { - "debug":0, - "comfyUiParameters": [ - "--lowvram", - "--disable-ipex-optimize", - "--bf16-unet", - "--reserve-vram", - "5.0" - ], - "availableThemes": ["dark","lnl","bmg"], - "currentTheme": "bmg" -} \ No newline at end of file + "debug": 0, + "comfyUiParameters": [ + "--lowvram", + "--disable-ipex-optimize", + "--bf16-unet", + "--reserve-vram", + "5.0" + ], + "availableThemes": ["dark", "lnl", "bmg"], + "currentTheme": "bmg" +} diff --git a/WebUI/components.json b/WebUI/components.json index dd363606..2e538269 100644 --- a/WebUI/components.json +++ b/WebUI/components.json @@ -14,4 +14,4 @@ "components": "@/components", "utils": "@/lib/utils" } -} \ No newline at end of file +} diff --git a/WebUI/electron/electron-env.d.ts b/WebUI/electron/electron-env.d.ts index 8d9557c6..75ffc032 100644 --- a/WebUI/electron/electron-env.d.ts +++ b/WebUI/electron/electron-env.d.ts @@ -15,57 +15,58 @@ declare namespace NodeJS { * │ * ``` */ - DIST: string; + DIST: string /** /dist/ or /public/ */ - VITE_PUBLIC: string; + VITE_PUBLIC: string } } // Used in Renderer process, expose in `preload.ts` interface Window { - ipcRenderer: import("electron").IpcRenderer; + ipcRenderer: import('electron').IpcRenderer } type KVObject = { - [key: string]: any; -}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any +} -type Theme = 'dark' | 'lnl' | 'bmg'; +type Theme = 'dark' | 'lnl' | 'bmg' type LocalSettings = { - debug: number; - comfyUiParameters?:string[]; -} & KVObject; + debug: number + comfyUiParameters?: string[] +} & KVObject type ThemeSettings = { - availableThemes: Theme[]; - currentTheme: Theme; + availableThemes: Theme[] + currentTheme: Theme } type ModelPaths = { - llm: string, - embedding: string, - stableDiffusion: string, - inpaint: string, - lora: string, - vae: string, + 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[], + 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, + modelPaths: ModelPaths + modelLists: ModelLists + isAdminExec: boolean + version: string } type UpdateWorkflowsFromIntelResult = { @@ -73,5 +74,14 @@ type UpdateWorkflowsFromIntelResult = { backupDir: string } -type BackendStatus = 'notYetStarted' | 'starting' | 'running' | 'stopped' | 'stopping' | 'failed' | 'notInstalled' | 'installationFailed' | 'installing' | 'uninitializedStatus' - +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 8b3d2113..31d4ea17 100644 --- a/WebUI/electron/logging/logger.ts +++ b/WebUI/electron/logging/logger.ts @@ -1,97 +1,96 @@ -import WebContents = Electron.WebContents; -import fs from "fs"; -import path from "node:path"; -import {app} from "electron"; - +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' - }[] = [] + 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() { - } + constructor() {} - onWebcontentReady(webContents: WebContents) { - this.webContents = webContents - this.startupMessageCache.forEach((logEntry) => { - this.webContents!.send('debugLog', logEntry) - }); - this.startupMessageCache = [] - } + 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}) - } + 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}) - } + 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) - } + error(message: string, source: string, alsoLogToFile: boolean = false) { + if (alsoLogToFile) { + this.logMessageToFile(message, source) + } - console.error(`[${source}]: ${message}`); + 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}) - } + 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') - 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"); - } + 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}` - } + 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 be0ff7e0..91262d62 100644 --- a/WebUI/electron/main.ts +++ b/WebUI/electron/main.ts @@ -1,27 +1,29 @@ 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"; - + 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 @@ -33,621 +35,614 @@ import {updateIntelWorkflows} from "./subprocesses/updateIntelWorkflows.ts"; // │ │ ├── main.js // │ │ └── preload.js // │ -process.env.DIST = path.join(__dirname, "../"); -process.env.VITE_PUBLIC = path.join(__dirname, app.isPackaged ? "../.." : "../../../public"); +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 externalRes = path.resolve( + app.isPackaged ? process.resourcesPath : path.join(__dirname, '../../external/'), +) +const singleInstanceLock = app.requestSingleInstanceLock() const appLogger = appLoggerInstance -let win: BrowserWindow | null; +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 VITE_DEV_SERVER_URL = process.env['VITE_DEV_SERVER_URL'] // const APP_TOOL_HEIGHT = 209; const appSize = { - width: 820, - height: 128, - maxChatContentHeight: 0, -}; + width: 820, + height: 128, + maxChatContentHeight: 0, +} export const settings: LocalSettings = { - isAdminExec: false, - debug: 0, - availableThemes: ["dark", "lnl"], - currentTheme: "lnl", - comfyUiParameters: [], -}; + 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'); + 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; + 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); + 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: '*', + }, }) - - 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.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) } - - 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'); + }) + + win.webContents.session.setPermissionRequestHandler((_, permission, callback) => { + if ( + permission === 'media' || + permission === 'clipboard-sanitized-write' + // permission === "clipboard-sanitized-write" + ) { + callback(true) } else { - await win.loadFile(path.join(process.env.DIST, "index.html")); + callback(false) } - - // 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; + }) + + 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(); - } -}); +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(); +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 + 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, + ) + } + }) - 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('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', } - ); - - 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); + : { + 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("miniWindow", () => { - if (win) { - win.minimize(); - } - }); + ipcMain.on('setFullScreen', (_event: IpcMainEvent, enable: boolean) => { + if (win) { + win.setFullScreen(enable) + } + }) - ipcMain.on("setFullScreen", (event: IpcMainEvent, enable: boolean) => { - if (win) { - win.setFullScreen(enable); - } - }); + ipcMain.on('exitApp', async () => { + if (win) { + win.close() + } + }) - 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'] }], + } - ipcMain.on("saveImage", async (event: IpcMainEvent, url: string) => { - const win = BrowserWindow.fromWebContents(event.sender); - if (!win) { - return; + try { + const result = await dialog.showSaveDialog(win, options) + if (!result.canceled && result.filePath) { + if (fs.existsSync(result.filePath)) { + fs.rmSync(result.filePath) } - 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'); - } - } + 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(`${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; + appLogger.error( + `Download and save error: ${JSON.stringify(error, Object.getOwnPropertyNames, 2)}`, + 'electron-backend', + ) } - 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; - }); + } + } 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) + }) - ipcMain.handle("updateWorkflowsFromIntelRepo", () => { - return updateIntelWorkflows() - }); + const pathsManager = new PathsManager( + path.join(externalRes, app.isPackaged ? 'model_config.json' : 'model_config.dev.json'), + ) - 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'; + 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() + }) - let imagePath: string; - if (backend === 'service') { - imagePath = imageUrl.pathname.replace(/^\/*/, '') - } else { - const s = imageUrl.searchParams; - imagePath = `static/sd_out/${s.get('filename')}` - } + 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 + } - return path.join(externalRes, 'service', imagePath); + 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' - ipcMain.on("openImageWithSystem", (event, url: string) => { - const imagePath = getImagePathFromUrl(url); - if (!imagePath) return; - shell.openPath(imagePath) - }); + let imagePath: string + if (backend === 'service') { + imagePath = imageUrl.pathname.replace(/^\/*/, '') + } else { + const s = imageUrl.searchParams + imagePath = `static/sd_out/${s.get('filename')}` + } - ipcMain.on("selecteImage", (event, url: string) => { - const imagePath = getImagePathFromUrl(url); - if (!imagePath) return; + return path.join(externalRes, 'service', imagePath) + } - // Open the image with the default system image viewer - if (process.platform === 'win32') { - exec(`explorer.exe /select, "${imagePath}"`); - } else { - shell.showItemInFolder(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; +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; + width = display.workAreaSize.width } else if (height > display.workAreaSize.height) { - 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'); - }); -}); + 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); - } - }); + 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(); - } -} - -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'); - } - + 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); + 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/pathsManager.ts b/WebUI/electron/pathsManager.ts index dfa0721c..f0480c62 100644 --- a/WebUI/electron/pathsManager.ts +++ b/WebUI/electron/pathsManager.ts @@ -1,200 +1,189 @@ -import fs from "node:fs"; -import path from "node:path"; +import fs from 'node:fs' +import path from 'node:path' export class PathsManager { - modelPaths: ModelPaths = { - llm: "", - ggufLLM: "", - embedding: "", - stableDiffusion: "", - inpaint: "", - lora: "", - vae: "", - } - configPath: string + modelPaths: ModelPaths = { + llm: '', + ggufLLM: '', + embedding: '', + stableDiffusion: '', + inpaint: '', + lora: '', + vae: '', + } + configPath: string - constructor(configPath: string) { - this.configPath = configPath; - this.loadConfig(); - } - loadConfig() { - this.initModelPaths(JSON.parse(fs.readFileSync(this.configPath).toString()) as ModelPaths); - } - updateModelPahts(modelPaths: ModelPaths) { - this.initModelPaths(modelPaths); - const workDir = process.cwd(); - const savePaths = Object.assign({}, this.modelPaths); - Object.keys(savePaths).forEach((key) => { - let modelPath = path.resolve(modelPaths[key]); - //if the path is in the workDir, save the relative path - if (modelPath.startsWith(workDir)) { - modelPath = path.relative(workDir, modelPath); - } - savePaths[key] = modelPath; - }); - fs.writeFileSync(this.configPath, JSON.stringify(savePaths, null, 4)); + constructor(configPath: string) { + this.configPath = configPath + this.loadConfig() + } + loadConfig() { + this.initModelPaths(JSON.parse(fs.readFileSync(this.configPath).toString()) as ModelPaths) + } + updateModelPahts(modelPaths: ModelPaths) { + this.initModelPaths(modelPaths) + const workDir = process.cwd() + const savePaths = Object.assign({}, this.modelPaths) + Object.keys(savePaths).forEach((key) => { + let modelPath = path.resolve(modelPaths[key]) + //if the path is in the workDir, save the relative path + if (modelPath.startsWith(workDir)) { + modelPath = path.relative(workDir, modelPath) + } + savePaths[key] = modelPath + }) + fs.writeFileSync(this.configPath, JSON.stringify(savePaths, null, 4)) + } + private initModelPaths(modelPaths: ModelPaths) { + Object.keys(this.modelPaths).forEach((key) => { + if (key in modelPaths) { + const modelPath = path.resolve(modelPaths[key]) + this.modelPaths[key] = modelPath + } + }) + } + sacanAll(): ModelLists { + try { + const model_settings: ModelLists = { + stableDiffusion: this.scanSDModleLists(), + inpaint: this.scanInpaint(), + llm: this.scanLLMModles(), + lora: this.scanLora(), + vae: [], + scheduler: [], + embedding: this.scanEmbedding(), + } + return model_settings + } catch (ex) { + fs.appendFileSync(path.join(path.dirname(this.configPath), 'debug.log'), `${ex}\r\n`) + throw ex } - private initModelPaths(modelPaths: ModelPaths) { - Object.keys(this.modelPaths).forEach((key) => { - if (key in modelPaths) { - const modelPath = path.resolve(modelPaths[key]); - this.modelPaths[key] = modelPath; - } - }); - } - sacanAll(): ModelLists { - try { - const model_settings: ModelLists = { - stableDiffusion: this.scanSDModleLists(), - inpaint: this.scanInpaint(), - llm: this.scanLLMModles(), - lora: this.scanLora(), - vae: [], - scheduler: [], - embedding: this.scanEmbedding() - }; - return model_settings; - } catch (ex) { - fs.appendFileSync(path.join(path.dirname(this.configPath), "debug.log"), `${ex}\r\n`); - throw ex; + } + scanSDModleLists(returnDefaults = true) { + const models = returnDefaults ? ['Lykon/dreamshaper-8', 'RunDiffusion/Juggernaut-XL-v9'] : [] + const dir = this.modelPaths.stableDiffusion + if (fs.existsSync(dir)) { + const modelsSet = new Set(models) + fs.readdirSync(dir).forEach((enumPath) => { + const realPath = path.join(dir, enumPath) + const pathStat = fs.statSync(realPath) + if (pathStat.isFile() && (enumPath.endsWith('.bin') || enumPath.endsWith('.safetensors'))) { + const modelName = enumPath + if (!modelsSet.has(modelName)) { + modelsSet.add(modelName) + models.push(modelName) + } + } else if ( + pathStat.isDirectory() && + fs.existsSync(path.join(realPath, 'model_index.json')) + ) { + const modelName = enumPath.replace('---', '/') + if (!modelsSet.has(modelName)) { + modelsSet.add(modelName) + models.push(modelName) + } } + }) + } else { + fs.mkdirSync(dir, { recursive: true }) } - scanSDModleLists(returnDefaults = true) { - const models = returnDefaults ? [ - "Lykon/dreamshaper-8", - "RunDiffusion/Juggernaut-XL-v9", - ] : []; - const dir = this.modelPaths.stableDiffusion; - if (fs.existsSync(dir)) { - - const modelsSet = new Set(models); - fs.readdirSync(dir).forEach(enumPath => { - const realPath = path.join(dir, enumPath); - const pathStat = fs.statSync(realPath); - if (pathStat.isFile() && ( - enumPath.endsWith(".bin") || enumPath.endsWith(".safetensors") - )) { - const modelName = enumPath; - if (!modelsSet.has(modelName)) { - modelsSet.add(modelName) - models.push(modelName) - } - } else if (pathStat.isDirectory() && fs.existsSync( - path.join(realPath, "model_index.json") - )) { - const modelName = enumPath.replace("---", "/") - if (!modelsSet.has(modelName)) { - modelsSet.add(modelName) - models.push(modelName) - } - } - }); - } - else { - fs.mkdirSync(dir, { recursive: true }); - } - return models + return models + } + scanLLMModles() { + const dir = this.modelPaths.llm + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) } - scanLLMModles() { - const dir = this.modelPaths.llm; - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - const modelsSet = fs.readdirSync(dir) - .filter(subDir => { - const fullpath = path.join(dir, subDir); - return fs.statSync(fullpath).isDirectory() && fs.existsSync(path.join(fullpath))}) - .map(subDir => subDir.replace("---", "/")) - .reduce((set, modelName) => set.add(modelName), new Set()); + const modelsSet = fs + .readdirSync(dir) + .filter((subDir) => { + const fullpath = path.join(dir, subDir) + return fs.statSync(fullpath).isDirectory() && fs.existsSync(path.join(fullpath)) + }) + .map((subDir) => subDir.replace('---', '/')) + .reduce((set, modelName) => set.add(modelName), new Set()) - return [...modelsSet] + return [...modelsSet] + } + scanGGUFLLMModels() { + const dir = this.modelPaths.ggufLLM + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) } - scanGGUFLLMModels() { - const dir = this.modelPaths.ggufLLM; - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - console.log('getting models', dir); - const modelsSet = fs.readdirSync(dir, { encoding: 'utf-8', recursive: true}) - .filter(pathName => pathName.endsWith(".gguf")) - .map(path => path.replace("---", "/")) - .map(path => path.replace("\\", "/")) - .reduce((acc, pathname) => acc.add(pathname), new Set()); + console.log('getting models', dir) + const modelsSet = fs + .readdirSync(dir, { encoding: 'utf-8', recursive: true }) + .filter((pathName) => pathName.endsWith('.gguf')) + .map((path) => path.replace('---', '/')) + .map((path) => path.replace('\\', '/')) + .reduce((acc, pathname) => acc.add(pathname), new Set()) - return [...modelsSet] - } - scanLora(returnDefaults = true) { - const models = returnDefaults ? [ - "None", - "latent-consistency/lcm-lora-sdxl", - "latent-consistency/lcm-lora-sdv1-5", - ] : []; - const loraDir = this.modelPaths.lora; - if (fs.existsSync(loraDir)) { - const modelsSet = new Set(models); - fs.readdirSync(loraDir).forEach(pathname => { - const fullPath = path.join(loraDir, pathname); - if (fs.statSync(fullPath).isDirectory()) { - if (fs.existsSync(path.join(fullPath, "pytorch_lora_weights.safetensors")) || fs.existsSync(path.join(fullPath, "pytorch_lora_weights.bin"))) { - const modelName = pathname.replace("---", "/") - if (!modelsSet.has(modelName)) { - modelsSet.add(modelName) - models.push(modelName) - } - } - } - else if (pathname.endsWith(".safetensors") || pathname.endsWith(".bin")) { - const modelName = pathname; - if (!modelsSet.has(modelName)) { - modelsSet.add(modelName) - models.push(modelName) - } - } - }); - } - else { - fs.mkdirSync(loraDir, { recursive: true }); + return [...modelsSet] + } + scanLora(returnDefaults = true) { + const models = returnDefaults + ? ['None', 'latent-consistency/lcm-lora-sdxl', 'latent-consistency/lcm-lora-sdv1-5'] + : [] + const loraDir = this.modelPaths.lora + if (fs.existsSync(loraDir)) { + const modelsSet = new Set(models) + fs.readdirSync(loraDir).forEach((pathname) => { + const fullPath = path.join(loraDir, pathname) + if (fs.statSync(fullPath).isDirectory()) { + if ( + fs.existsSync(path.join(fullPath, 'pytorch_lora_weights.safetensors')) || + fs.existsSync(path.join(fullPath, 'pytorch_lora_weights.bin')) + ) { + const modelName = pathname.replace('---', '/') + if (!modelsSet.has(modelName)) { + modelsSet.add(modelName) + models.push(modelName) + } + } + } else if (pathname.endsWith('.safetensors') || pathname.endsWith('.bin')) { + const modelName = pathname + if (!modelsSet.has(modelName)) { + modelsSet.add(modelName) + models.push(modelName) + } } - return models + }) + } else { + fs.mkdirSync(loraDir, { recursive: true }) } - scanEmbedding(returnDefaults = true) { - return returnDefaults ? [ - "BAAI/bge-large-en-v1.5", - "BAAI/bge-large-zh-v1.5" - ] : []; - } - scanInpaint(returnDefaults = true) { - const models = returnDefaults ? [ - "Lykon/dreamshaper-8-inpainting" - ] : []; - const dir = this.modelPaths.inpaint; - if (fs.existsSync(dir)) { - const modelsSet = new Set(models); - fs.readdirSync(dir).forEach(enumPath => { - const realPath = path.join(dir, enumPath); - const pathStat = fs.statSync(realPath); - if (pathStat.isFile() && ( - enumPath.endsWith(".bin") || enumPath.endsWith(".safetensors") - )) { - const modelName = enumPath; - if (!modelsSet.has(modelName)) { - modelsSet.add(modelName) - models.push(modelName) - } - } else if (pathStat.isDirectory() && fs.existsSync( - path.join(realPath, "model_index.json") - )) { - const modelName = enumPath.replace("---", "/") - if (!modelsSet.has(modelName)) { - modelsSet.add(modelName) - models.push(modelName) - } - } - }); - } - else { - fs.mkdirSync(dir, { recursive: true }); + return models + } + scanEmbedding(returnDefaults = true) { + return returnDefaults ? ['BAAI/bge-large-en-v1.5', 'BAAI/bge-large-zh-v1.5'] : [] + } + scanInpaint(returnDefaults = true) { + const models = returnDefaults ? ['Lykon/dreamshaper-8-inpainting'] : [] + const dir = this.modelPaths.inpaint + if (fs.existsSync(dir)) { + const modelsSet = new Set(models) + fs.readdirSync(dir).forEach((enumPath) => { + const realPath = path.join(dir, enumPath) + const pathStat = fs.statSync(realPath) + if (pathStat.isFile() && (enumPath.endsWith('.bin') || enumPath.endsWith('.safetensors'))) { + const modelName = enumPath + if (!modelsSet.has(modelName)) { + modelsSet.add(modelName) + models.push(modelName) + } + } else if ( + pathStat.isDirectory() && + fs.existsSync(path.join(realPath, 'model_index.json')) + ) { + const modelName = enumPath.replace('---', '/') + if (!modelsSet.has(modelName)) { + modelsSet.add(modelName) + models.push(modelName) + } } - return models + }) + } else { + fs.mkdirSync(dir, { recursive: true }) } -} \ No newline at end of file + return models + } +} diff --git a/WebUI/electron/preload.ts b/WebUI/electron/preload.ts index 815f756a..ebf8e721 100644 --- a/WebUI/electron/preload.ts +++ b/WebUI/electron/preload.ts @@ -1,75 +1,73 @@ -import { contextBridge, ipcRenderer, dialog } from "electron"; -import pkg from "../package.json"; +import { contextBridge, ipcRenderer } from 'electron' +import pkg from '../package.json' -contextBridge.exposeInMainWorld("envVars", { +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), +}) +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), + ipcRenderer.invoke('showSaveDialog', options), showMessageBox: (options: Electron.MessageBoxOptions) => - ipcRenderer.invoke("showMessageBox", options), + 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"), + 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), + 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) + 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) + 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)), + 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)), -}); + 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 9bb036df..31051e66 100644 --- a/WebUI/electron/subprocesses/aiBackendService.ts +++ b/WebUI/electron/subprocesses/aiBackendService.ts @@ -1,90 +1,127 @@ -import * as filesystem from 'fs-extra'; -import { ChildProcess, spawn } from "node:child_process"; -import path from "node:path"; -import { existingFileOrError } from './osProcessHelper.ts'; -import { aiBackendServiceDir, LongLivedPythonApiService, LsLevelZeroService } from './service.ts'; - +import * as filesystem from 'fs-extra' +import { ChildProcess, spawn } from 'node:child_process' +import path from 'node:path' +import { existingFileOrError } from './osProcessHelper.ts' +import { aiBackendServiceDir, LongLivedPythonApiService, LsLevelZeroService } from './service.ts' export class AiBackendService extends LongLivedPythonApiService { - readonly pythonEnvDir = path.resolve(path.join(this.baseDir, `${this.name}-env`)); - readonly lsLevelZero = new LsLevelZeroService(this.pythonEnvDir); - readonly lsLevelZeroDir: string = this.lsLevelZero.dir; - readonly uvPip = this.lsLevelZero.uvPip; - readonly pip = this.uvPip.pip; - readonly python = this.pip.python; + readonly pythonEnvDir = path.resolve(path.join(this.baseDir, `${this.name}-env`)) + readonly lsLevelZero = new LsLevelZeroService(this.pythonEnvDir) + readonly lsLevelZeroDir: string = this.lsLevelZero.dir + readonly uvPip = this.lsLevelZero.uvPip + readonly pip = this.uvPip.pip + readonly python = this.pip.python - readonly isRequired = true - readonly serviceDir = aiBackendServiceDir(); - healthEndpointUrl = `${this.baseUrl}/healthy` - serviceIsSetUp = () => filesystem.existsSync(this.python.getExePath()); - isSetUp = this.serviceIsSetUp(); + readonly isRequired = true + readonly serviceDir = aiBackendServiceDir() + healthEndpointUrl = `${this.baseUrl}/healthy` + serviceIsSetUp = () => filesystem.existsSync(this.python.getExePath()) + isSetUp = this.serviceIsSetUp() - async *set_up(): AsyncIterable { - this.setStatus('installing') - this.appLogger.info("setting up service", this.name) - const self = this + async *set_up(): AsyncIterable { + this.setStatus('installing') + this.appLogger.info('setting up service', this.name) + const self = this - try { - yield {serviceName: self.name, step: "start", status: "executing", debugMessage: "starting to set up environment"}; - // lsLevelZero will ensure uv and pip are installed - await this.lsLevelZero.ensureInstalled(); + try { + yield { + serviceName: self.name, + step: 'start', + status: 'executing', + debugMessage: 'starting to set up environment', + } + // lsLevelZero will ensure uv and pip are installed + await this.lsLevelZero.ensureInstalled() - const deviceArch = await self.lsLevelZero.detectDevice(); - yield {serviceName: self.name, step: `Detecting intel device`, status: "executing", debugMessage: `detected intel hardware ${deviceArch}`}; + const deviceArch = await self.lsLevelZero.detectDevice() + yield { + serviceName: self.name, + step: `Detecting intel device`, + status: 'executing', + debugMessage: `detected intel hardware ${deviceArch}`, + } - yield {serviceName: self.name, step: `install dependencies`, status: "executing", debugMessage: `installing dependencies`}; - const deviceSpecificRequirements = existingFileOrError(path.join(self.serviceDir, `requirements-${deviceArch}.txt`)) - await this.pip.run(["install", "-r", deviceSpecificRequirements]); - if (deviceArch === "bmg") { - const intelSpecificExtension = existingFileOrError(self.customIntelExtensionForPytorch) - await this.pip.run(["install", intelSpecificExtension]); - } + yield { + serviceName: self.name, + step: `install dependencies`, + status: 'executing', + debugMessage: `installing dependencies`, + } + const deviceSpecificRequirements = existingFileOrError( + path.join(self.serviceDir, `requirements-${deviceArch}.txt`), + ) + await this.pip.run(['install', '-r', deviceSpecificRequirements]) + if (deviceArch === 'bmg') { + const intelSpecificExtension = existingFileOrError(self.customIntelExtensionForPytorch) + await this.pip.run(['install', intelSpecificExtension]) + } - const commonRequirements = existingFileOrError(path.join(self.serviceDir, 'requirements.txt')) - await this.uvPip.run(["install", "-r", commonRequirements]); - yield {serviceName: self.name, step: `install dependencies`, status: "executing", debugMessage: `dependencies installed`}; + const commonRequirements = existingFileOrError(path.join(self.serviceDir, 'requirements.txt')) + await this.uvPip.run(['install', '-r', commonRequirements]) + yield { + serviceName: self.name, + step: `install dependencies`, + status: 'executing', + debugMessage: `dependencies installed`, + } - this.setStatus('notYetStarted') - yield {serviceName: self.name, step: "end", status: "success", debugMessage: `service set up completely`}; - } catch (e) { - self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true) - self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true) - this.setStatus('installationFailed') - yield {serviceName: self.name, step: "end", status: "failed", debugMessage: `Failed to setup python environment due to ${e}`}; - } + this.setStatus('notYetStarted') + yield { + serviceName: self.name, + step: 'end', + status: 'success', + debugMessage: `service set up completely`, + } + } catch (e) { + self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true) + self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true) + this.setStatus('installationFailed') + yield { + serviceName: self.name, + step: 'end', + status: 'failed', + debugMessage: `Failed to setup python environment due to ${e}`, + } } + } + async spawnAPIProcess(): Promise<{ + process: ChildProcess + didProcessExitEarlyTracker: Promise + }> { + const additionalEnvVariables = { + SYCL_ENABLE_DEFAULT_CONTEXTS: '1', + SYCL_CACHE_PERSISTENT: '1', + PYTHONIOENCODING: 'utf-8', + ...(await this.lsLevelZero.getDeviceSelectorEnv()), + } - async spawnAPIProcess(): Promise<{ process: ChildProcess; didProcessExitEarlyTracker: Promise; }> { - const additionalEnvVariables = { - "SYCL_ENABLE_DEFAULT_CONTEXTS": "1", - "SYCL_CACHE_PERSISTENT": "1", - "PYTHONIOENCODING": "utf-8", - ...await this.lsLevelZero.getDeviceSelectorEnv(), - }; - - const apiProcess = spawn(this.python.getExePath(), ["web_api.py", "--port", this.port.toString()], { - cwd: this.serviceDir, - windowsHide: true, - env: Object.assign(process.env, additionalEnvVariables) - }); + const apiProcess = spawn( + this.python.getExePath(), + ['web_api.py', '--port', this.port.toString()], + { + cwd: this.serviceDir, + windowsHide: true, + env: Object.assign(process.env, additionalEnvVariables), + }, + ) - //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) => { - apiProcess.on('error', (error) => { - this.appLogger.error(`encountered error of process in ${this.name} : ${error}`, this.name) - resolve(true); - }); - apiProcess.on('exit', () => { - this.appLogger.error(`encountered unexpected exit in ${this.name}.`, this.name) - resolve(true); - }); - }); + //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) => { + apiProcess.on('error', (error) => { + this.appLogger.error(`encountered error of process in ${this.name} : ${error}`, this.name) + resolve(true) + }) + apiProcess.on('exit', () => { + this.appLogger.error(`encountered unexpected exit in ${this.name}.`, this.name) + resolve(true) + }) + }) - return { - process: apiProcess, - didProcessExitEarlyTracker: didProcessExitEarlyTracker, - } + return { + process: apiProcess, + didProcessExitEarlyTracker: didProcessExitEarlyTracker, } + } } diff --git a/WebUI/electron/subprocesses/apiServiceRegistry.ts b/WebUI/electron/subprocesses/apiServiceRegistry.ts index d3802ec6..e2cfb15c 100644 --- a/WebUI/electron/subprocesses/apiServiceRegistry.ts +++ b/WebUI/electron/subprocesses/apiServiceRegistry.ts @@ -1,81 +1,130 @@ -import {ApiService} from "./service.ts"; -import {ComfyUiBackendService} from "./comfyUIBackendService.ts"; -import {AiBackendService} from "./aiBackendService.ts"; -import { BrowserWindow } from "electron"; -import { appLoggerInstance } from "../logging/logger.ts"; -import getPort, {portNumbers} from "get-port"; -import { LlamaCppBackendService} from "./llamaCppBackendService.ts"; +import { ApiService } from './service.ts' +import { ComfyUiBackendService } from './comfyUIBackendService.ts' +import { AiBackendService } from './aiBackendService.ts' +import { BrowserWindow } from 'electron' +import { appLoggerInstance } from '../logging/logger.ts' +import getPort, { portNumbers } from 'get-port' +import { LlamaCppBackendService } from './llamaCppBackendService.ts' export type backend = 'ai-backend' | 'comfyui-backend' export interface ApiServiceRegistry { - register(apiService: ApiService): void - getRegistered(): ApiService[] - getRequired(): ApiService[] + register(apiService: ApiService): void + getRegistered(): ApiService[] + getRequired(): ApiService[] } export class ApiServiceRegistryImpl implements ApiServiceRegistry { - private registeredServices: ApiService[] = [] + private registeredServices: ApiService[] = [] - register(apiService: ApiService): void { - if (this.registeredServices.includes(apiService)) { - return - } - this.registeredServices.push(apiService) + register(apiService: ApiService): void { + if (this.registeredServices.includes(apiService)) { + return } + this.registeredServices.push(apiService) + } - getRegistered(): ApiService[] { - return this.registeredServices - } - getRequired(): ApiService[] { - const requiredServices = this.registeredServices.filter(item => item.name === "ai-backend") - if (requiredServices.length !== 1) { - throw Error("Required Service 'ai-backend' not yet registered") - } - return requiredServices + getRegistered(): ApiService[] { + return this.registeredServices + } + getRequired(): ApiService[] { + const requiredServices = this.registeredServices.filter((item) => item.name === 'ai-backend') + if (requiredServices.length !== 1) { + throw Error("Required Service 'ai-backend' not yet registered") } + return requiredServices + } - getService(serviceName: string): ApiService | undefined { - return this.registeredServices.find(item => item.name === serviceName) - } + getService(serviceName: string): ApiService | undefined { + return this.registeredServices.find((item) => item.name === serviceName) + } - async bootUpAllSetUpServices(): Promise<{ serviceName: string, state: BackendStatus }[]> { - const setUpServices = this.registeredServices.filter(item => item.isSetUp) - return Promise.all(setUpServices.map(service => service.start().then( - state => {return {serviceName: service.name, state}} - ).catch((e) => { - appLoggerInstance.error(`Failed to start service ${service.name} due to ${e}`, 'apiServiceRegistry', true) - return {serviceName: service.name, state: "failed" as BackendStatus} - }))) - } + async bootUpAllSetUpServices(): Promise<{ serviceName: string; state: BackendStatus }[]> { + const setUpServices = this.registeredServices.filter((item) => item.isSetUp) + return Promise.all( + setUpServices.map((service) => + service + .start() + .then((state) => { + return { serviceName: service.name, state } + }) + .catch((e) => { + appLoggerInstance.error( + `Failed to start service ${service.name} due to ${e}`, + 'apiServiceRegistry', + true, + ) + return { serviceName: service.name, state: 'failed' as BackendStatus } + }), + ), + ) + } - async stopAllServices(): Promise<{ serviceName: string, state: BackendStatus }[]> { - appLoggerInstance.info(`stopping all running services`, 'apiServiceRegistry') - const runningServices = this.registeredServices.filter(item => item.currentStatus === "running") - return Promise.all(runningServices.map(service => service.stop().then( - state => { - appLoggerInstance.info(`service ${service.name} now in state ${state}`, 'apiServiceRegistry') - return {serviceName: service.name, state} - } - ).catch((e) => { - appLoggerInstance.error(`Failed to stop service ${service.name} due to ${e}`, 'apiServiceRegistry', true) - return {serviceName: service.name, state: "failed" as BackendStatus} - }))) - } + async stopAllServices(): Promise<{ serviceName: string; state: BackendStatus }[]> { + appLoggerInstance.info(`stopping all running services`, 'apiServiceRegistry') + const runningServices = this.registeredServices.filter( + (item) => item.currentStatus === 'running', + ) + return Promise.all( + runningServices.map((service) => + service + .stop() + .then((state) => { + appLoggerInstance.info( + `service ${service.name} now in state ${state}`, + 'apiServiceRegistry', + ) + return { serviceName: service.name, state } + }) + .catch((e) => { + appLoggerInstance.error( + `Failed to stop service ${service.name} due to ${e}`, + 'apiServiceRegistry', + true, + ) + return { serviceName: service.name, state: 'failed' as BackendStatus } + }), + ), + ) + } - getServiceInformation(): ApiServiceInformation[] { - return this.getRegistered().map(service => service.get_info()) - } + getServiceInformation(): ApiServiceInformation[] { + return this.getRegistered().map((service) => service.get_info()) + } } -let instance: ApiServiceRegistryImpl | null = null +let instance: ApiServiceRegistryImpl | null = null -export async function aiplaygroundApiServiceRegistry(win: BrowserWindow, settings: LocalSettings): Promise { - if (!instance) { - instance = new ApiServiceRegistryImpl() - instance.register(new AiBackendService('ai-backend', await getPort({port: portNumbers(59000, 59999)}), win, settings)) - instance.register(new ComfyUiBackendService('comfyui-backend', await getPort({port: portNumbers(49000, 49999)}), win, settings)) - instance.register(new LlamaCppBackendService('llamacpp-backend', await getPort({port: portNumbers(39000, 39999)}), win, settings)) - } - return instance +export async function aiplaygroundApiServiceRegistry( + win: BrowserWindow, + settings: LocalSettings, +): Promise { + if (!instance) { + instance = new ApiServiceRegistryImpl() + instance.register( + new AiBackendService( + 'ai-backend', + await getPort({ port: portNumbers(59000, 59999) }), + win, + settings, + ), + ) + instance.register( + new ComfyUiBackendService( + 'comfyui-backend', + await getPort({ port: portNumbers(49000, 49999) }), + win, + settings, + ), + ) + instance.register( + new LlamaCppBackendService( + 'llamacpp-backend', + await getPort({ port: portNumbers(39000, 39999) }), + win, + settings, + ), + ) + } + return instance } diff --git a/WebUI/electron/subprocesses/comfyUIBackendService.ts b/WebUI/electron/subprocesses/comfyUIBackendService.ts index 42e02e07..a34af9c0 100644 --- a/WebUI/electron/subprocesses/comfyUIBackendService.ts +++ b/WebUI/electron/subprocesses/comfyUIBackendService.ts @@ -1,168 +1,249 @@ -import {ChildProcess, spawn} from "node:child_process"; -import path from "node:path"; -import fs from "fs"; -import * as filesystem from "fs-extra"; -import {existingFileOrError} from "./osProcessHelper.ts"; -import {updateIntelWorkflows} from "./updateIntelWorkflows.ts"; -import { LsLevelZeroService, LongLivedPythonApiService, aiBackendServiceDir, GitService } from "./service.ts"; - +import { ChildProcess, spawn } from 'node:child_process' +import path from 'node:path' +import fs from 'fs' +import * as filesystem from 'fs-extra' +import { existingFileOrError } from './osProcessHelper.ts' +import { updateIntelWorkflows } from './updateIntelWorkflows.ts' +import { + LsLevelZeroService, + LongLivedPythonApiService, + aiBackendServiceDir, + GitService, +} from './service.ts' export class ComfyUiBackendService extends LongLivedPythonApiService { - readonly isRequired = false - readonly serviceDir = path.resolve(path.join(this.baseDir, "ComfyUI")); - readonly pythonEnvDir = path.resolve(path.join(this.baseDir, `comfyui-backend-env`)); - readonly lsLevelZeroDir = this.pythonEnvDir - readonly lsLevelZero = new LsLevelZeroService(this.lsLevelZeroDir); - readonly uvPip = this.lsLevelZero.uvPip; - readonly git = new GitService(); - healthEndpointUrl = `${this.baseUrl}/queue` - - private readonly remoteUrl = "https://github.com/comfyanonymous/ComfyUI.git" - private readonly revision = "61b5072" - private readonly comfyUIStartupParameters = this.settings.comfyUiParameters ? this.settings.comfyUiParameters : [ - "--lowvram", - "--disable-ipex-optimize", - "--bf16-unet", - "--reserve-vram", - "5.0" - ] + readonly isRequired = false + readonly serviceDir = path.resolve(path.join(this.baseDir, 'ComfyUI')) + readonly pythonEnvDir = path.resolve(path.join(this.baseDir, `comfyui-backend-env`)) + readonly lsLevelZeroDir = this.pythonEnvDir + readonly lsLevelZero = new LsLevelZeroService(this.lsLevelZeroDir) + readonly uvPip = this.lsLevelZero.uvPip + readonly git = new GitService() + healthEndpointUrl = `${this.baseUrl}/queue` + + private readonly remoteUrl = 'https://github.com/comfyanonymous/ComfyUI.git' + private readonly revision = '61b5072' + private readonly comfyUIStartupParameters = this.settings.comfyUiParameters + ? this.settings.comfyUiParameters + : ['--lowvram', '--disable-ipex-optimize', '--bf16-unet', '--reserve-vram', '5.0'] + + serviceIsSetUp(): boolean { + return filesystem.existsSync(this.pythonEnvDir) && filesystem.existsSync(this.serviceDir) + } + + isSetUp = this.serviceIsSetUp() + + async *set_up(): AsyncIterable { + this.appLogger.info('setting up service', this.name) + this.setStatus('installing') + const self = this + + async function checkServiceDir(): Promise { + if (!filesystem.existsSync(self.serviceDir)) { + return false + } + + // Check if it's a valid git repo + try { + await self.git.run(['-C', self.serviceDir, 'status']) + } catch (_e) { + try { + filesystem.removeSync(self.serviceDir) + } finally { + return false + } + } - serviceIsSetUp(): boolean { - return filesystem.existsSync(this.pythonEnvDir) && filesystem.existsSync(this.serviceDir) + return true } - isSetUp = this.serviceIsSetUp(); - - async *set_up(): AsyncIterable { - this.appLogger.info("setting up service", this.name) - this.setStatus('installing') - const self = this - - async function checkServiceDir(): Promise { - if (!filesystem.existsSync(self.serviceDir)) { - return false - } - - // Check if it's a valid git repo - try { - await self.git.run(["-C", self.serviceDir, "status"]) - } catch (e) { - try { - filesystem.removeSync(self.serviceDir) - } finally { - return false - } - } - - return true - } - - async function setupComfyUiBaseService(): Promise { - if (await checkServiceDir()) { - self.appLogger.info("comfyUI already cloned, skipping", self.name) - } else { - await self.git.run(["clone", self.remoteUrl, self.serviceDir]) - await self.git.run(["-C", self.serviceDir, "checkout", self.revision], {}, self.serviceDir) - } - - // Check whether all requirements are installed - const requirementsTextPath = existingFileOrError(path.join(self.serviceDir, 'requirements.txt')) - try { - await self.uvPip.checkRequirementsTxt(requirementsTextPath) - } catch (e) { - await self.uvPip.run(["install", "-r", requirementsTextPath]) - } - } + async function setupComfyUiBaseService(): Promise { + if (await checkServiceDir()) { + self.appLogger.info('comfyUI already cloned, skipping', self.name) + } else { + await self.git.run(['clone', self.remoteUrl, self.serviceDir]) + await self.git.run(['-C', self.serviceDir, 'checkout', self.revision], {}, self.serviceDir) + } + + // Check whether all requirements are installed + const requirementsTextPath = existingFileOrError( + path.join(self.serviceDir, 'requirements.txt'), + ) + try { + await self.uvPip.checkRequirementsTxt(requirementsTextPath) + } catch (_e) { + await self.uvPip.run(['install', '-r', requirementsTextPath]) + } + } - async function configureComfyUI(): Promise { - try { - self.appLogger.info("Configuring extra model paths for comfyUI", self.name) - const extraModelPathsYaml = path.join(self.serviceDir, 'extra_model_paths.yaml') - const extraModelsYaml = `aipg: + async function configureComfyUI(): Promise { + try { + self.appLogger.info('Configuring extra model paths for comfyUI', self.name) + const extraModelPathsYaml = path.join(self.serviceDir, 'extra_model_paths.yaml') + const extraModelsYaml = `aipg: base_path: ${path.resolve(self.baseDir, 'service/models/stable_diffusion')} checkpoints: checkpoints clip: checkpoints vae: checkpoints unet: checkpoints 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) { - self.appLogger.error("Failed to configure extra model paths for comfyUI", self.name) - throw new Error("Failed to configure extra model paths for comfyUI") - } - } + 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) { + self.appLogger.error('Failed to configure extra model paths for comfyUI', self.name) + throw new Error('Failed to configure extra model paths for comfyUI') + } + } - try { - yield {serviceName: self.name, step: "start", status: "executing", debugMessage: "starting to set up comfyUI environment"}; - - await self.lsLevelZero.ensureInstalled(); - await self.git.ensureInstalled(); - - yield {serviceName: self.name, step: `Detecting intel device`, status: "executing", debugMessage: `Trying to identify intel hardware`}; - const deviceArch = await self.lsLevelZero.detectDevice(); - yield {serviceName: self.name, step: `Detecting intel device`, status: "executing", debugMessage: `detected intel hardware ${deviceArch}`}; - - yield {serviceName: self.name, step: `install dependencies`, status: "executing", debugMessage: `installing dependencies`}; - const deviceSpecificRequirements = existingFileOrError(path.join(aiBackendServiceDir(), `requirements-${deviceArch}.txt`)) - await self.uvPip.pip.run(["install", "-r", deviceSpecificRequirements]); - if (deviceArch === "bmg") { - const intelSpecificExtension = existingFileOrError(self.customIntelExtensionForPytorch) - await self.uvPip.pip.run(["install", intelSpecificExtension]); - } - yield {serviceName: self.name, step: `install dependencies`, status: "executing", debugMessage: `dependencies installed`}; - - yield {serviceName: self.name, step: `install comfyUI`, status: "executing", debugMessage: `installing comfyUI base repo`}; - await setupComfyUiBaseService() - yield {serviceName: self.name, step: `install comfyUI`, status: "executing", debugMessage: `installation of comfyUI base repo complete`}; - - yield {serviceName: self.name, step: `configure comfyUI`, status: "executing", debugMessage: `configuring comfyUI base repo`}; - await configureComfyUI() - yield {serviceName: self.name, step: `configure comfyUI`, status: "executing", debugMessage: `configured comfyUI base repo`}; - await updateIntelWorkflows() - - this.setStatus('notYetStarted') - yield {serviceName: self.name, step: "end", status: "success", debugMessage: `service set up completely`}; - } catch (e) { - self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true) - self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true) - this.setStatus('installationFailed') - yield {serviceName: self.name, step: "end", status: "failed", debugMessage: `Failed to setup comfyUI service due to ${e}`}; - } + try { + yield { + serviceName: self.name, + step: 'start', + status: 'executing', + debugMessage: 'starting to set up comfyUI environment', + } + + await self.lsLevelZero.ensureInstalled() + await self.git.ensureInstalled() + + yield { + serviceName: self.name, + step: `Detecting intel device`, + status: 'executing', + debugMessage: `Trying to identify intel hardware`, + } + const deviceArch = await self.lsLevelZero.detectDevice() + yield { + serviceName: self.name, + step: `Detecting intel device`, + status: 'executing', + debugMessage: `detected intel hardware ${deviceArch}`, + } + + yield { + serviceName: self.name, + step: `install dependencies`, + status: 'executing', + debugMessage: `installing dependencies`, + } + const deviceSpecificRequirements = existingFileOrError( + path.join(aiBackendServiceDir(), `requirements-${deviceArch}.txt`), + ) + await self.uvPip.pip.run(['install', '-r', deviceSpecificRequirements]) + if (deviceArch === 'bmg') { + const intelSpecificExtension = existingFileOrError(self.customIntelExtensionForPytorch) + await self.uvPip.pip.run(['install', intelSpecificExtension]) + } + yield { + serviceName: self.name, + step: `install dependencies`, + status: 'executing', + debugMessage: `dependencies installed`, + } + + yield { + serviceName: self.name, + step: `install comfyUI`, + status: 'executing', + debugMessage: `installing comfyUI base repo`, + } + await setupComfyUiBaseService() + yield { + serviceName: self.name, + step: `install comfyUI`, + status: 'executing', + debugMessage: `installation of comfyUI base repo complete`, + } + + yield { + serviceName: self.name, + step: `configure comfyUI`, + status: 'executing', + debugMessage: `configuring comfyUI base repo`, + } + await configureComfyUI() + yield { + serviceName: self.name, + step: `configure comfyUI`, + status: 'executing', + debugMessage: `configured comfyUI base repo`, + } + await updateIntelWorkflows() + + this.setStatus('notYetStarted') + yield { + serviceName: self.name, + step: 'end', + status: 'success', + debugMessage: `service set up completely`, + } + } catch (e) { + self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true) + self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true) + this.setStatus('installationFailed') + yield { + serviceName: self.name, + step: 'end', + status: 'failed', + debugMessage: `Failed to setup comfyUI service due to ${e}`, + } + } + } + + async spawnAPIProcess(): Promise<{ + process: ChildProcess + didProcessExitEarlyTracker: Promise + }> { + const additionalEnvVariables = { + SYCL_ENABLE_DEFAULT_CONTEXTS: '1', + SYCL_CACHE_PERSISTENT: '1', + PYTHONIOENCODING: 'utf-8', + ...(await this.lsLevelZero.getDeviceSelectorEnv()), } - async spawnAPIProcess(): Promise<{ process: ChildProcess; didProcessExitEarlyTracker: Promise; }> { - const additionalEnvVariables = { - "SYCL_ENABLE_DEFAULT_CONTEXTS": "1", - "SYCL_CACHE_PERSISTENT": "1", - "PYTHONIOENCODING": "utf-8", - ...await this.lsLevelZero.getDeviceSelectorEnv(), - }; - - const parameters = ["main.py", "--port", this.port.toString(), "--preview-method", "auto", "--output-directory", "../service/static/sd_out", ...this.comfyUIStartupParameters] - this.appLogger.info(`starting comfyui with ${JSON.stringify({parameters, additionalEnvVariables})}`, this.name, true) - const apiProcess = spawn(this.uvPip.python.getExePath(), parameters, { - cwd: this.serviceDir, - windowsHide: true, - env: Object.assign(process.env, additionalEnvVariables) - }); - - //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) => { - apiProcess.on('exit', () => { - this.appLogger.error(`encountered unexpected exit in ${this.name}.`, this.name) - resolve(true); - }); - apiProcess.on('error', (error) => { - this.appLogger.error(`encountered error of process in ${this.name} : ${error}`, this.name) - resolve(true); - }); - }); - - return { - process: apiProcess, - didProcessExitEarlyTracker: didProcessExitEarlyTracker, - } + const parameters = [ + 'main.py', + '--port', + this.port.toString(), + '--preview-method', + 'auto', + '--output-directory', + '../service/static/sd_out', + ...this.comfyUIStartupParameters, + ] + this.appLogger.info( + `starting comfyui with ${JSON.stringify({ parameters, additionalEnvVariables })}`, + this.name, + true, + ) + const apiProcess = spawn(this.uvPip.python.getExePath(), parameters, { + cwd: this.serviceDir, + windowsHide: true, + env: Object.assign(process.env, additionalEnvVariables), + }) + + //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) => { + apiProcess.on('exit', () => { + this.appLogger.error(`encountered unexpected exit in ${this.name}.`, this.name) + resolve(true) + }) + apiProcess.on('error', (error) => { + this.appLogger.error(`encountered error of process in ${this.name} : ${error}`, this.name) + resolve(true) + }) + }) + + return { + process: apiProcess, + didProcessExitEarlyTracker: didProcessExitEarlyTracker, } + } } diff --git a/WebUI/electron/subprocesses/deviceArch.ts b/WebUI/electron/subprocesses/deviceArch.ts index a5f9b5ba..30c61be2 100644 --- a/WebUI/electron/subprocesses/deviceArch.ts +++ b/WebUI/electron/subprocesses/deviceArch.ts @@ -1,81 +1,86 @@ -// https://github.com/intel/compute-runtime/blob/master/shared/source/dll/devices/devices_base.inl -const ID2ARCH: { [key: number]: Arch } = { - // bmg - 0xE202: "bmg", - 0xE20B: "bmg", - 0xE20C: "bmg", - 0xE20D: "bmg", - 0xE212: "bmg", - - // lnl - 0x6420: "lnl", - 0x64A0: "lnl", - 0x64B0: "lnl", - - // dg2, using alias name "acm" - 0x4F80: "acm", - 0x4F81: "acm", - 0x4F82: "acm", - 0x4F83: "acm", - 0x4F84: "acm", - 0x4F85: "acm", - 0x4F86: "acm", - 0x4F87: "acm", - 0x4F88: "acm", - 0x5690: "acm", - 0x5691: "acm", - 0x5692: "acm", - 0x5693: "acm", - 0x5694: "acm", - 0x5695: "acm", - 0x5696: "acm", - 0x5697: "acm", - 0x56A3: "acm", - 0x56A4: "acm", - 0x56B0: "acm", - 0x56B1: "acm", - 0x56B2: "acm", - 0x56B3: "acm", - 0x56BA: "acm", - 0x56BB: "acm", - 0x56BC: "acm", - 0x56BD: "acm", - 0x56BE: "acm", - 0x56BF: "acm", - 0x56A0: "acm", - 0x56A1: "acm", - 0x56A2: "acm", - 0x56A5: "acm", - 0x56A6: "acm", - 0x56C0: "acm", - 0x56C1: "acm", - 0x56C2: "acm", - - // mtl - 0x7D40: "mtl", - 0x7D55: "mtl", - 0x7DD5: "mtl", - 0x7D45: "mtl", - - // // arl - // 0x7D67: "arl", - // 0x7D51: "arl", - // 0x7DD1: "arl", - // 0x7D41: "arl", -}; - -export function getDeviceArch(deviceId: number): Arch { - return ID2ARCH[deviceId] || "unknown"; -} - -export function getArchPriority(arch: Arch): number { - switch (arch) { - case "bmg": return 4; - case "acm": return 3; - case "lnl": return 2; - case "mtl": return 1; - default: return 0; - } -} - -export type Arch = "bmg" | "acm" | "lnl" | "mtl" | "unknown" \ No newline at end of file +// https://github.com/intel/compute-runtime/blob/master/shared/source/dll/devices/devices_base.inl +const ID2ARCH: { [key: number]: Arch } = { + // bmg + 0xe202: 'bmg', + 0xe20b: 'bmg', + 0xe20c: 'bmg', + 0xe20d: 'bmg', + 0xe212: 'bmg', + + // lnl + 0x6420: 'lnl', + 0x64a0: 'lnl', + 0x64b0: 'lnl', + + // dg2, using alias name "acm" + 0x4f80: 'acm', + 0x4f81: 'acm', + 0x4f82: 'acm', + 0x4f83: 'acm', + 0x4f84: 'acm', + 0x4f85: 'acm', + 0x4f86: 'acm', + 0x4f87: 'acm', + 0x4f88: 'acm', + 0x5690: 'acm', + 0x5691: 'acm', + 0x5692: 'acm', + 0x5693: 'acm', + 0x5694: 'acm', + 0x5695: 'acm', + 0x5696: 'acm', + 0x5697: 'acm', + 0x56a3: 'acm', + 0x56a4: 'acm', + 0x56b0: 'acm', + 0x56b1: 'acm', + 0x56b2: 'acm', + 0x56b3: 'acm', + 0x56ba: 'acm', + 0x56bb: 'acm', + 0x56bc: 'acm', + 0x56bd: 'acm', + 0x56be: 'acm', + 0x56bf: 'acm', + 0x56a0: 'acm', + 0x56a1: 'acm', + 0x56a2: 'acm', + 0x56a5: 'acm', + 0x56a6: 'acm', + 0x56c0: 'acm', + 0x56c1: 'acm', + 0x56c2: 'acm', + + // mtl + 0x7d40: 'mtl', + 0x7d55: 'mtl', + 0x7dd5: 'mtl', + 0x7d45: 'mtl', + + // // arl + // 0x7D67: "arl", + // 0x7D51: "arl", + // 0x7DD1: "arl", + // 0x7D41: "arl", +} + +export function getDeviceArch(deviceId: number): Arch { + return ID2ARCH[deviceId] || 'unknown' +} + +export function getArchPriority(arch: Arch): number { + switch (arch) { + case 'bmg': + return 4 + case 'acm': + return 3 + case 'lnl': + return 2 + case 'mtl': + return 1 + default: + return 0 + } +} + +export type Arch = 'bmg' | 'acm' | 'lnl' | 'mtl' | 'unknown' diff --git a/WebUI/electron/subprocesses/llamaCppBackendService.ts b/WebUI/electron/subprocesses/llamaCppBackendService.ts index 069f9714..b36d7caf 100644 --- a/WebUI/electron/subprocesses/llamaCppBackendService.ts +++ b/WebUI/electron/subprocesses/llamaCppBackendService.ts @@ -1,90 +1,131 @@ -import {app} from "electron"; -import {ChildProcess, spawn} from "node:child_process"; -import path from "node:path"; +import { app } from 'electron' +import { ChildProcess, spawn } from 'node:child_process' +import path from 'node:path' import * as filesystem from 'fs-extra' -import {existingFileOrError} from './osProcessHelper.ts' -import { LsLevelZeroService, UvPipService, LongLivedPythonApiService } from "./service.ts"; +import { existingFileOrError } from './osProcessHelper.ts' +import { LsLevelZeroService, UvPipService, LongLivedPythonApiService } from './service.ts' export class LlamaCppBackendService extends LongLivedPythonApiService { - readonly serviceDir = path.resolve(path.join(this.baseDir, "LlamaCPP")); - readonly pythonEnvDir = path.resolve(path.join(this.baseDir, `llama-cpp-env`)); - // using ls_level_zero from default ai-backend env to avoid oneAPI dep conflicts - readonly lsLevelZeroDir = path.resolve(path.join(this.baseDir, "ai-backend-env")); - readonly isRequired = false; + readonly serviceDir = path.resolve(path.join(this.baseDir, 'LlamaCPP')) + readonly pythonEnvDir = path.resolve(path.join(this.baseDir, `llama-cpp-env`)) + // using ls_level_zero from default ai-backend env to avoid oneAPI dep conflicts + readonly lsLevelZeroDir = path.resolve(path.join(this.baseDir, 'ai-backend-env')) + readonly isRequired = false - healthEndpointUrl = `${this.baseUrl}/health` + healthEndpointUrl = `${this.baseUrl}/health` - readonly lsLevelZero = new LsLevelZeroService(this.lsLevelZeroDir); - readonly uvPip = new UvPipService(this.pythonEnvDir); - readonly python = this.uvPip.python; + readonly lsLevelZero = new LsLevelZeroService(this.lsLevelZeroDir) + readonly uvPip = new UvPipService(this.pythonEnvDir) + readonly python = this.uvPip.python - serviceIsSetUp(): boolean { - return filesystem.existsSync(this.python.getExePath()); - } + serviceIsSetUp(): boolean { + return filesystem.existsSync(this.python.getExePath()) + } - isSetUp = this.serviceIsSetUp(); + isSetUp = this.serviceIsSetUp() - async *set_up(): AsyncIterable { - this.setStatus('installing') - this.appLogger.info("setting up service", this.name) - const self = this + async *set_up(): AsyncIterable { + this.setStatus('installing') + this.appLogger.info('setting up service', this.name) + const self = this - try { - yield {serviceName: self.name, step: "start", status: "executing", debugMessage: "starting to set up python environment"}; - await this.lsLevelZero.ensureInstalled(); - await this.uvPip.ensureInstalled(); + try { + yield { + serviceName: self.name, + step: 'start', + status: 'executing', + debugMessage: 'starting to set up python environment', + } + await this.lsLevelZero.ensureInstalled() + await this.uvPip.ensureInstalled() - const deviceArch = await self.lsLevelZero.detectDevice(); - yield {serviceName: self.name, step: `Detecting intel device`, status: "executing", debugMessage: `detected intel hardware ${deviceArch}`}; + const deviceArch = await self.lsLevelZero.detectDevice() + yield { + serviceName: self.name, + step: `Detecting intel device`, + status: 'executing', + debugMessage: `detected intel hardware ${deviceArch}`, + } - yield {serviceName: self.name, step: `install dependencies`, status: "executing", debugMessage: `installing dependencies`}; - const commonRequirements = existingFileOrError(path.join(self.serviceDir, 'requirements.txt')) - const intelSpecificExtensionDir = app.isPackaged ? this.baseDir : path.join(__dirname, '../../external'); - const intelSpecificExtension = existingFileOrError(path.join(intelSpecificExtensionDir, 'llama_cpp_python-0.3.2-cp311-cp311-win_amd64.whl')) - await this.uvPip.pip.run(["install", intelSpecificExtension]); - await this.uvPip.run(["install", "-r", commonRequirements]); - yield {serviceName: self.name, step: `install dependencies`, status: "executing", debugMessage: `dependencies installed`}; + yield { + serviceName: self.name, + step: `install dependencies`, + status: 'executing', + debugMessage: `installing dependencies`, + } + const commonRequirements = existingFileOrError(path.join(self.serviceDir, 'requirements.txt')) + const intelSpecificExtensionDir = app.isPackaged + ? this.baseDir + : path.join(__dirname, '../../external') + const intelSpecificExtension = existingFileOrError( + path.join(intelSpecificExtensionDir, 'llama_cpp_python-0.3.2-cp311-cp311-win_amd64.whl'), + ) + await this.uvPip.pip.run(['install', intelSpecificExtension]) + await this.uvPip.run(['install', '-r', commonRequirements]) + yield { + serviceName: self.name, + step: `install dependencies`, + status: 'executing', + debugMessage: `dependencies installed`, + } - this.setStatus('notYetStarted') - yield {serviceName: self.name, step: "end", status: "success", debugMessage: `service set up completely`}; - } catch (e) { - self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true) - self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true) - this.setStatus('installationFailed') - yield {serviceName: self.name, step: "end", status: "failed", debugMessage: `Failed to setup python environment due to ${e}`}; - } + this.setStatus('notYetStarted') + yield { + serviceName: self.name, + step: 'end', + status: 'success', + debugMessage: `service set up completely`, + } + } catch (e) { + self.appLogger.warn(`Set up of service failed due to ${e}`, self.name, true) + self.appLogger.warn(`Aborting set up of ${self.name} service environment`, self.name, true) + this.setStatus('installationFailed') + yield { + serviceName: self.name, + step: 'end', + status: 'failed', + debugMessage: `Failed to setup python environment due to ${e}`, + } } + } - async spawnAPIProcess(): Promise<{ process: ChildProcess; didProcessExitEarlyTracker: Promise; }> { - const additionalEnvVariables = { - "SYCL_ENABLE_DEFAULT_CONTEXTS": "1", - "SYCL_CACHE_PERSISTENT": "1", - "PYTHONIOENCODING": "utf-8", - ...await this.lsLevelZero.getDeviceSelectorEnv(), - }; + async spawnAPIProcess(): Promise<{ + process: ChildProcess + didProcessExitEarlyTracker: Promise + }> { + const additionalEnvVariables = { + SYCL_ENABLE_DEFAULT_CONTEXTS: '1', + SYCL_CACHE_PERSISTENT: '1', + PYTHONIOENCODING: 'utf-8', + ...(await this.lsLevelZero.getDeviceSelectorEnv()), + } - const apiProcess = spawn(this.python.getExePath(), ["llama_web_api.py", "--port", this.port.toString()], { - cwd: this.serviceDir, - windowsHide: true, - env: Object.assign(process.env, additionalEnvVariables) - }); + const apiProcess = spawn( + this.python.getExePath(), + ['llama_web_api.py', '--port', this.port.toString()], + { + cwd: this.serviceDir, + windowsHide: true, + env: Object.assign(process.env, additionalEnvVariables), + }, + ) - //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) => { - apiProcess.on('error', (error) => { - this.appLogger.error(`encountered error of process in ${this.name} : ${error}`, this.name) - resolve(true); - }); - apiProcess.on('exit', () => { - this.appLogger.error(`encountered unexpected exit in ${this.name}.`, this.name) - resolve(true); - }); - }); + //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) => { + apiProcess.on('error', (error) => { + this.appLogger.error(`encountered error of process in ${this.name} : ${error}`, this.name) + resolve(true) + }) + apiProcess.on('exit', () => { + this.appLogger.error(`encountered unexpected exit in ${this.name}.`, this.name) + resolve(true) + }) + }) - return { - process: apiProcess, - didProcessExitEarlyTracker: didProcessExitEarlyTracker, - } + return { + process: apiProcess, + didProcessExitEarlyTracker: didProcessExitEarlyTracker, } + } } diff --git a/WebUI/electron/subprocesses/osProcessHelper.ts b/WebUI/electron/subprocesses/osProcessHelper.ts index d9a8a90a..4238c1da 100644 --- a/WebUI/electron/subprocesses/osProcessHelper.ts +++ b/WebUI/electron/subprocesses/osProcessHelper.ts @@ -1,57 +1,65 @@ -import path from "node:path"; +import path from 'node:path' import * as fs from 'fs-extra' -import { spawn } from "node:child_process"; - +import { spawn } from 'node:child_process' export function existingFileOrError(filePath: string) { - const resolvedFilePath = path.resolve(filePath) - if (fs.existsSync(resolvedFilePath)) { - return resolvedFilePath - } - throw Error(`File at ${resolvedFilePath} does not exist`) + const resolvedFilePath = path.resolve(filePath) + if (fs.existsSync(resolvedFilePath)) { + return resolvedFilePath + } + throw Error(`File at ${resolvedFilePath} does not exist`) } -export async function spawnProcessAsync(command: string, args: string[] = [], - logHandler: (data: string) => void = () => { }, extraEnv?: {}, workDir?: string +export async function spawnProcessAsync( + command: string, + args: string[] = [], + logHandler: (data: string) => void = () => {}, + extraEnv?: object, + workDir?: string, ): Promise { - logHandler(`Spawning command ${command} ${args.join(' ')}`); - if (extraEnv) { - logHandler(`Extra env: ${JSON.stringify(extraEnv)}`); - } - const spawnedProcess = spawn(command, args, { - windowsHide: true, - cwd: workDir ?? process.cwd(), - env: { - ...process.env, - ...extraEnv, - } - }); + logHandler(`Spawning command ${command} ${args.join(' ')}`) + if (extraEnv) { + logHandler(`Extra env: ${JSON.stringify(extraEnv)}`) + } + const spawnedProcess = spawn(command, args, { + windowsHide: true, + cwd: workDir ?? process.cwd(), + env: { + ...process.env, + ...extraEnv, + }, + }) - const stdOut: string[] = []; + const stdOut: string[] = [] - spawnedProcess.stdout.on("data", (data: string | Buffer) => { logHandler(data.toString('utf8')); stdOut.push(data.toString('utf8')); }); - spawnedProcess.stderr.on("data", (data) => { logHandler(data.toString('utf8')) }); + spawnedProcess.stdout.on('data', (data: string | Buffer) => { + logHandler(data.toString('utf8')) + stdOut.push(data.toString('utf8')) + }) + spawnedProcess.stderr.on('data', (data) => { + logHandler(data.toString('utf8')) + }) - return new Promise((resolve, reject) => { - spawnedProcess.on("exit", (code) => { - if (code === 0) { - resolve(stdOut.join('\n')); - } else { - reject(new Error(`command ${command} ${args} failed ${code}`)); - } - }); - spawnedProcess.on("error", (err) => { - reject(new Error(`command ${command} ${args} failed with error ${err}`)); - }); - }); + return new Promise((resolve, reject) => { + spawnedProcess.on('exit', (code) => { + if (code === 0) { + resolve(stdOut.join('\n')) + } else { + reject(new Error(`command ${command} ${args} failed ${code}`)) + } + }) + spawnedProcess.on('error', (err) => { + reject(new Error(`command ${command} ${args} failed with error ${err}`)) + }) + }) } export async function copyFileWithDirs(src: string, dest: string) { - const stats = await fs.promises.lstat(src); - if (stats.isSymbolicLink()) { - const linkTarget = await fs.promises.readlink(src); - src = linkTarget; - } - const destDir = path.dirname(dest); - await fs.promises.mkdir(destDir, { recursive: true }); - await fs.promises.cp(src, dest, { recursive: true, force: true }); -} \ No newline at end of file + const stats = await fs.promises.lstat(src) + if (stats.isSymbolicLink()) { + const linkTarget = await fs.promises.readlink(src) + src = linkTarget + } + const destDir = path.dirname(dest) + await fs.promises.mkdir(destDir, { recursive: true }) + await fs.promises.cp(src, dest, { recursive: true, force: true }) +} diff --git a/WebUI/electron/subprocesses/service.ts b/WebUI/electron/subprocesses/service.ts index a533e999..3b7c32cc 100644 --- a/WebUI/electron/subprocesses/service.ts +++ b/WebUI/electron/subprocesses/service.ts @@ -1,657 +1,691 @@ -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"; -import { existingFileOrError, spawnProcessAsync } from "./osProcessHelper"; -import { assert } from 'node:console'; -import { Arch, getArchPriority, getDeviceArch } from './deviceArch'; -import { createHash } from 'crypto'; +import { ChildProcess } from 'node:child_process' +import { app, BrowserWindow, net } from 'electron' +import * as filesystem from 'fs-extra' +import path from 'node:path' +import { appLoggerInstance } from '../logging/logger.ts' +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 - readonly stage: string - - constructor(component: string, stage: string = "all") { - super(`Service ${component} check failed at stage ${stage}`); - this.name = "ServiceCheckError"; - this.component = component - this.stage = stage - } + readonly component: string + readonly stage: string + + constructor(component: string, stage: string = 'all') { + super(`Service ${component} check failed at stage ${stage}`) + this.name = 'ServiceCheckError' + this.component = component + this.stage = stage + } } export interface GenericService { - name: string - - /** - * Check first, then repair/install if check fails - * @throws any error if repair/install throws - */ - ensureInstalled(): Promise - - /** - * @throws ServiceCheckError if check fails - */ - check(): Promise - - /** - * Fresh install - * @throws any error - */ - install(): Promise - - /** - * Repair install - * @param checkError error that caused the repair - * @throws any error - */ - repair(checkError: ServiceCheckError): Promise + name: string + + /** + * Check first, then repair/install if check fails + * @throws any error if repair/install throws + */ + ensureInstalled(): Promise + + /** + * @throws ServiceCheckError if check fails + */ + check(): Promise + + /** + * Fresh install + * @throws any error + */ + install(): Promise + + /** + * Repair install + * @param checkError error that caused the repair + * @throws any error + */ + repair(checkError: ServiceCheckError): Promise } export abstract class GenericServiceImpl implements GenericService { - name: string - - readonly appLogger = appLoggerInstance - readonly baseDir = app.isPackaged ? process.resourcesPath : path.join(__dirname, "../../../"); - - constructor(name: string) { - this.name = name - } - - async ensureInstalled(): Promise { - try { - await this.check() - } catch (e) { - if (e instanceof ServiceCheckError) { - await this.repair(e) - } else { - await this.install() - } - } + name: string + + readonly appLogger = appLoggerInstance + readonly baseDir = app.isPackaged ? process.resourcesPath : path.join(__dirname, '../../../') + + constructor(name: string) { + this.name = name + } + + async ensureInstalled(): Promise { + try { + await this.check() + } catch (e) { + if (e instanceof ServiceCheckError) { + await this.repair(e) + } else { + await this.install() + } } + } - abstract check(): Promise - abstract install(): Promise - abstract repair(checkError: ServiceCheckError): Promise + abstract check(): Promise + abstract install(): Promise + abstract repair(checkError: ServiceCheckError): Promise - log(msg: string) { - this.appLogger.info(msg, this.name) - } + log(msg: string) { + this.appLogger.info(msg, this.name) + } - logError(msg: string) { - this.appLogger.error(msg, this.name, true) - } + logError(msg: string) { + this.appLogger.error(msg, this.name, true) + } } abstract class ExecutableService extends GenericServiceImpl { - dir: string + dir: string - constructor(name: string, dir: string) { - super(name) - this.dir = dir - } + constructor(name: string, dir: string) { + super(name) + this.dir = dir + } - abstract getExePath(): string + abstract getExePath(): string - async run(args: string[] = [], extraEnv?: {}, workDir?: string): Promise { - const exePath = existingFileOrError(this.getExePath()) - return spawnProcessAsync(exePath, args, (data) => this.log(data), extraEnv, workDir) - } + async run(args: string[] = [], extraEnv?: object, workDir?: string): Promise { + const exePath = existingFileOrError(this.getExePath()) + return spawnProcessAsync(exePath, args, (data) => this.log(data), extraEnv, workDir) + } } export class PythonService extends ExecutableService { - constructor(readonly dir: string) { - super("python", dir) - } - - getExePath(): string { - return path.resolve(path.join(this.dir, "python.exe")) - } - - async check(): Promise { - this.log("checking") - try { - await this.run(["--version"]) - } catch (e) { - this.log(`warning: ${e}`) - throw new ServiceCheckError(this.name) - } - } - - async install(): Promise { - this.log("start installing") - await this.clonePythonEnv() - } - - async repair(checkError: ServiceCheckError): Promise { - assert(checkError.component === this.name) - await this.install() - } - - readonly prototypicalEnvDir = app.isPackaged ? path.join(this.baseDir, "prototype-python-env") : path.join(this.baseDir, "build-envs/online/prototype-python-env"); - private async clonePythonEnv(): Promise { - existingFileOrError(this.prototypicalEnvDir) - if (filesystem.existsSync(this.dir)) { - this.log(`removing existing python env at ${this.dir}`) - filesystem.removeSync(this.dir) - } - this.log(`copying prototypical python env to ${this.dir}`) - await filesystem.copy(this.prototypicalEnvDir, this.dir) - } + constructor(readonly dir: string) { + super('python', dir) + } + + getExePath(): string { + return path.resolve(path.join(this.dir, 'python.exe')) + } + + async check(): Promise { + this.log('checking') + try { + await this.run(['--version']) + } catch (e) { + this.log(`warning: ${e}`) + throw new ServiceCheckError(this.name) + } + } + + async install(): Promise { + this.log('start installing') + await this.clonePythonEnv() + } + + async repair(checkError: ServiceCheckError): Promise { + assert(checkError.component === this.name) + await this.install() + } + + readonly prototypicalEnvDir = app.isPackaged + ? path.join(this.baseDir, 'prototype-python-env') + : path.join(this.baseDir, 'build-envs/online/prototype-python-env') + private async clonePythonEnv(): Promise { + existingFileOrError(this.prototypicalEnvDir) + if (filesystem.existsSync(this.dir)) { + this.log(`removing existing python env at ${this.dir}`) + filesystem.removeSync(this.dir) + } + this.log(`copying prototypical python env to ${this.dir}`) + await filesystem.copy(this.prototypicalEnvDir, this.dir) + } } export class PipService extends ExecutableService { - readonly python: PythonService = new PythonService(this.dir) - - constructor(readonly pythonEnvDir: string) { - super("pip", pythonEnvDir) - } - - getExePath(): string { - return this.python.getExePath() - } - - async run(args: string[] = [], extraEnv?: {}, workDir?: string): Promise { - return this.python.run(["-m", "pip", ...args], extraEnv, workDir) - } - - async check(): Promise { - this.log("checking") - try { - await this.python.check() - await this.run(["--version"]) - await this.run(["show", "setuptools"]) - return - } catch (e) { - this.log(`warning: ${e}`) - if (e instanceof ServiceCheckError) - throw e - if (e instanceof Error && e.message.includes("setuptools")) - throw new ServiceCheckError(this.name, "setuptools") - throw new ServiceCheckError(this.name) - } - } - - async install(): Promise { - this.log("start installing") - await this.python.ensureInstalled() + readonly python: PythonService = new PythonService(this.dir) + + constructor(readonly pythonEnvDir: string) { + super('pip', pythonEnvDir) + } + + getExePath(): string { + return this.python.getExePath() + } + + async run(args: string[] = [], extraEnv?: object, workDir?: string): Promise { + return this.python.run(['-m', 'pip', ...args], extraEnv, workDir) + } + + async check(): Promise { + this.log('checking') + try { + await this.python.check() + await this.run(['--version']) + await this.run(['show', 'setuptools']) + return + } catch (e) { + this.log(`warning: ${e}`) + if (e instanceof ServiceCheckError) throw e + if (e instanceof Error && e.message.includes('setuptools')) + throw new ServiceCheckError(this.name, 'setuptools') + throw new ServiceCheckError(this.name) + } + } + + async install(): Promise { + this.log('start installing') + await this.python.ensureInstalled() + await this.getPip() + await this.run(['install', 'setuptools']) + } + + async repair(checkError: ServiceCheckError): Promise { + this.log('repairing') + if (checkError.component !== this.name) { + await this.python.repair(checkError) + } + + switch (checkError.stage) { + default: await this.getPip() - await this.run(["install", "setuptools"]) - } - - async repair(checkError: ServiceCheckError): Promise { - this.log("repairing") - if (checkError.component !== this.name) { - await this.python.repair(checkError) - } - - switch (checkError.stage) { - default: - await this.getPip() - // fallthrough - case "setuptools": - await this.run(["install", "setuptools"]) - } - } - - private async getPip(): Promise { - const getPipScript = existingFileOrError(path.join(this.dir, "get-pip.py")) - await this.python.run([getPipScript]) - } - - async installRequirementsTxt(requirementsTxtPath: string): Promise { - await this.run(["install", "-r", requirementsTxtPath]) - } - - 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`) - }) - } + // fallthrough + case 'setuptools': + await this.run(['install', 'setuptools']) + } + } + + private async getPip(): Promise { + const getPipScript = existingFileOrError(path.join(this.dir, 'get-pip.py')) + await this.python.run([getPipScript]) + } + + async installRequirementsTxt(requirementsTxtPath: string): Promise { + await this.run(['install', '-r', requirementsTxtPath]) + } + + 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: unknown) => { + throw new Error(`requirements check failed: ${e}`) + }) + } } export class UvPipService extends PipService { - readonly pip: PipService = new PipService(this.dir) - readonly python: PythonService = this.pip.python - - constructor(readonly pythonEnvDir: string) { - super(pythonEnvDir) - this.name = "uvpip" - } - - async run(args: string[] = [], extraEnv?: {}, workDir?: string): Promise { - return this.python.run(["-m", "uv", "pip", ...args], extraEnv, workDir) - } - - async check(): Promise { - this.log("checking") - try { - await this.pip.check() - await this.run(["--version"]) - } catch (e) { - this.log(`warning: ${e}`) - if (e instanceof ServiceCheckError) - throw e - throw new ServiceCheckError(this.name) - } - } - - async install(): Promise { - this.log("start installing") - await this.pip.ensureInstalled() - await this.pip.run(["install", "uv"]) - } - - async repair(checkError: ServiceCheckError): Promise { - this.log("repairing") - if (checkError.component !== this.name) { - await this.pip.repair(checkError) - } - await this.pip.run(["install", "uv"]) - } + readonly pip: PipService = new PipService(this.dir) + readonly python: PythonService = this.pip.python + + constructor(readonly pythonEnvDir: string) { + super(pythonEnvDir) + this.name = 'uvpip' + } + + async run(args: string[] = [], extraEnv?: object, workDir?: string): Promise { + return this.python.run(['-m', 'uv', 'pip', ...args], extraEnv, workDir) + } + + async check(): Promise { + this.log('checking') + try { + await this.pip.check() + await this.run(['--version']) + } catch (e) { + this.log(`warning: ${e}`) + if (e instanceof ServiceCheckError) throw e + throw new ServiceCheckError(this.name) + } + } + + async install(): Promise { + this.log('start installing') + await this.pip.ensureInstalled() + await this.pip.run(['install', 'uv']) + } + + async repair(checkError: ServiceCheckError): Promise { + this.log('repairing') + if (checkError.component !== this.name) { + await this.pip.repair(checkError) + } + await this.pip.run(['install', 'uv']) + } } export class LsLevelZeroService extends ExecutableService { - readonly uvPip: UvPipService = new UvPipService(this.dir) - readonly requirementsTxtPath = path.resolve(path.join(this.baseDir, "service/requirements-ls_level_zero.txt")); - readonly srcExePath = path.resolve(path.join(this.baseDir, "service/tools/ls_level_zero.exe")); - - private allLevelZeroDevices: {name: string, device_id: number, arch: Arch}[] = []; - private selectedDeviceIdx: number = -1; - - constructor(readonly pythonEnvDir: string) { - super("lslevelzero", pythonEnvDir) - } - - getExePath(): string { - return path.resolve(path.join(this.dir, "Library/bin/ls_level_zero.exe")) - } - - async run(args: string[] = [], extraEnv?: {}, workDir?: string): Promise { - // reset ONEAPI_DEVICE_SELECTOR to ensure full device discovery - const env = { - ...extraEnv, - ONEAPI_DEVICE_SELECTOR: "level_zero:*" - } - return super.run(args, env, workDir) - } - - async check(): Promise { - this.log("checking") - try { - await this.uvPip.check() - await this.uvPip.checkRequirementsTxt(this.requirementsTxtPath) - await this.run() - } catch (e) { - this.log(`warning: ${e}`) - if (e instanceof ServiceCheckError) - throw e - if (e instanceof Error && e.message.includes("requirements check failed")) - throw new ServiceCheckError(this.name, "requirements") - throw new ServiceCheckError(this.name, "main") - } - } - - async install(): Promise { - this.log("start installing") - await this.uvPip.ensureInstalled() + readonly uvPip: UvPipService = new UvPipService(this.dir) + readonly requirementsTxtPath = path.resolve( + path.join(this.baseDir, 'service/requirements-ls_level_zero.txt'), + ) + readonly srcExePath = path.resolve(path.join(this.baseDir, 'service/tools/ls_level_zero.exe')) + + private allLevelZeroDevices: { name: string; device_id: number; arch: Arch }[] = [] + private selectedDeviceIdx: number = -1 + + constructor(readonly pythonEnvDir: string) { + super('lslevelzero', pythonEnvDir) + } + + getExePath(): string { + return path.resolve(path.join(this.dir, 'Library/bin/ls_level_zero.exe')) + } + + async run(args: string[] = [], extraEnv?: object, workDir?: string): Promise { + // reset ONEAPI_DEVICE_SELECTOR to ensure full device discovery + const env = { + ...extraEnv, + ONEAPI_DEVICE_SELECTOR: 'level_zero:*', + } + return super.run(args, env, workDir) + } + + async check(): Promise { + this.log('checking') + try { + await this.uvPip.check() + await this.uvPip.checkRequirementsTxt(this.requirementsTxtPath) + await this.run() + } catch (e) { + this.log(`warning: ${e}`) + if (e instanceof ServiceCheckError) throw e + if (e instanceof Error && e.message.includes('requirements check failed')) + throw new ServiceCheckError(this.name, 'requirements') + throw new ServiceCheckError(this.name, 'main') + } + } + + async install(): Promise { + this.log('start installing') + await this.uvPip.ensureInstalled() + await this.installRequirements() + await this.cloneLsLevelZero() + } + + async repair(checkError: ServiceCheckError): Promise { + this.log('repairing') + if (checkError.component !== this.name) { + await this.uvPip.repair(checkError) + } + + switch (checkError.stage) { + default: + case 'requirements': await this.installRequirements() + // fallthrough + case 'main': await this.cloneLsLevelZero() } - - async repair(checkError: ServiceCheckError): Promise { - this.log("repairing") - if (checkError.component !== this.name) { - await this.uvPip.repair(checkError) - } - - switch (checkError.stage) { - default: - case "requirements": - await this.installRequirements() - // fallthrough - case "main": - await this.cloneLsLevelZero() - } - } - - async installRequirements(): Promise { - await this.uvPip.installRequirementsTxt(this.requirementsTxtPath) - } - private async cloneLsLevelZero(): Promise { - existingFileOrError(this.srcExePath) - if (filesystem.existsSync(this.getExePath())) { - filesystem.removeSync(this.getExePath()) - } - this.log(`copying ls-level-zero to ${this.getExePath()}`) - await filesystem.copy(this.srcExePath, this.getExePath()) - } - - - async detectDevice(): Promise { - if (this.selectedDeviceIdx >= 0 && this.selectedDeviceIdx < this.allLevelZeroDevices.length) { - return this.allLevelZeroDevices[this.selectedDeviceIdx]?.arch ?? 'unknown'; - } - - this.log("Detecting device"); - try { - const devices = JSON.parse(await this.run()); - this.allLevelZeroDevices = devices.map((d: {id: number, name: string, device_id: number}) => { - return {name: d.name, device_id: d.device_id, arch: getDeviceArch(d.device_id)}; - }); - this.selectBestDevice(); - return this.allLevelZeroDevices[this.selectedDeviceIdx]?.arch ?? 'unknown'; - } catch (e) { - this.logError(`Failed to detect device due to ${e}`); - return 'unknown'; - } - } - - private selectBestDevice(): number { - let priority = -1; - for (let i = 0; i < this.allLevelZeroDevices.length; i++) { - const device = this.allLevelZeroDevices[i]; - const deviceArchPriority = getArchPriority(device?.arch ?? 'unknown'); - if (deviceArchPriority > priority) { - this.selectedDeviceIdx = i; - priority = deviceArchPriority; - } - } - return this.selectedDeviceIdx; - } - - async getDeviceSelectorEnv(): Promise<{ONEAPI_DEVICE_SELECTOR: string}> { - if (this.selectedDeviceIdx < 0 || this.selectedDeviceIdx >= this.allLevelZeroDevices.length) { - await this.detectDevice(); - } - - if (this.selectedDeviceIdx < 0) { - this.logError("No supported device"); - return {ONEAPI_DEVICE_SELECTOR: "level_zero:*"}; - } - - return {ONEAPI_DEVICE_SELECTOR: `level_zero:${this.selectedDeviceIdx}`}; - } + } + + async installRequirements(): Promise { + await this.uvPip.installRequirementsTxt(this.requirementsTxtPath) + } + private async cloneLsLevelZero(): Promise { + existingFileOrError(this.srcExePath) + if (filesystem.existsSync(this.getExePath())) { + filesystem.removeSync(this.getExePath()) + } + this.log(`copying ls-level-zero to ${this.getExePath()}`) + await filesystem.copy(this.srcExePath, this.getExePath()) + } + + async detectDevice(): Promise { + if (this.selectedDeviceIdx >= 0 && this.selectedDeviceIdx < this.allLevelZeroDevices.length) { + return this.allLevelZeroDevices[this.selectedDeviceIdx]?.arch ?? 'unknown' + } + + this.log('Detecting device') + try { + const devices = JSON.parse(await this.run()) + this.allLevelZeroDevices = devices.map( + (d: { id: number; name: string; device_id: number }) => { + return { name: d.name, device_id: d.device_id, arch: getDeviceArch(d.device_id) } + }, + ) + this.selectBestDevice() + return this.allLevelZeroDevices[this.selectedDeviceIdx]?.arch ?? 'unknown' + } catch (e) { + this.logError(`Failed to detect device due to ${e}`) + return 'unknown' + } + } + + private selectBestDevice(): number { + let priority = -1 + for (let i = 0; i < this.allLevelZeroDevices.length; i++) { + const device = this.allLevelZeroDevices[i] + const deviceArchPriority = getArchPriority(device?.arch ?? 'unknown') + if (deviceArchPriority > priority) { + this.selectedDeviceIdx = i + priority = deviceArchPriority + } + } + return this.selectedDeviceIdx + } + + async getDeviceSelectorEnv(): Promise<{ ONEAPI_DEVICE_SELECTOR: string }> { + if (this.selectedDeviceIdx < 0 || this.selectedDeviceIdx >= this.allLevelZeroDevices.length) { + await this.detectDevice() + } + + if (this.selectedDeviceIdx < 0) { + this.logError('No supported device') + return { ONEAPI_DEVICE_SELECTOR: 'level_zero:*' } + } + + return { ONEAPI_DEVICE_SELECTOR: `level_zero:${this.selectedDeviceIdx}` } + } } export class GitService extends ExecutableService { - constructor() { - super("git", "") - this.dir = path.resolve(path.join(this.baseDir, "portable-git")) - } - - getExePath(): string { - return path.resolve(path.join(this.dir, "cmd/git.exe")) - } - - async run(args: string[] = [], extraEnv?: {}, workDir?: string): Promise { - // Explicitly specify the cert file bundled with portable git, - // to avoid being affected by the system git configuration. - const env = { - ...extraEnv, - GIT_SSL_CAINFO: path.resolve(path.join(this.dir, "mingw64/etc/ssl/certs/ca-bundle.crt")) - } - return super.run(args, env, workDir) - } - - async check(): Promise { - this.log("checking") - try { - await this.run(["--version"]) - } catch (e) { - this.log(`warning: ${e}`) - throw new ServiceCheckError(this.name) - } - } - - async install(): Promise { - this.log("start installing") - await this.downloadGitZip() - await this.unzipGit() - - // cleanup - if (filesystem.existsSync(this.zipPath)) { - filesystem.removeSync(this.zipPath) - } - } - - async repair(checkError: ServiceCheckError): Promise { - assert(checkError.component === this.name) - await this.install() - } - - // https://github.com/git-for-windows/git/releases/tag/v2.47.1.windows.1 - readonly remoteUrl = "https://github.com/git-for-windows/git/releases/download/v2.47.1.windows.1/MinGit-2.47.1-64-bit.zip" - readonly sha256 = "50b04b55425b5c465d076cdb184f63a0cd0f86f6ec8bb4d5860114a713d2c29a" - readonly zipPath = path.resolve(path.join(this.baseDir, "portable-git.zip")) - - private async checkGitZip(): Promise { - if (!filesystem.existsSync(this.zipPath)) { - return false; - } - const sha256sum = await filesystem.readFile(this.zipPath).then((data) => createHash("sha256").update(data).digest("hex")); - return sha256sum === this.sha256; - } - - private async downloadGitZip(): Promise { - this.log("downloading git archive"); - if (await this.checkGitZip()) { - this.log("Using existing git archive"); - return; - } - - // Reuse existing zip if checksum matches - if (filesystem.existsSync(this.zipPath)) { - this.logError("Removing broken git archive"); - filesystem.removeSync(this.zipPath); - } - - // Using electron net for better proxy support - const response = await net.fetch(this.remoteUrl); - if (!response.ok || response.status !== 200 || !response.body) { - throw new Error(`Failed to download git: ${response.statusText}`); - } - const buffer = await response.arrayBuffer(); - await filesystem.writeFile(this.zipPath, Buffer.from(buffer)); - if (!await this.checkGitZip()) { - throw new Error(`Checksum mismatch: ${this.zipPath}`); - } - } - - private async unzipGit(): Promise { - const extract = require("extract-zip"); - await extract(this.zipPath, {dir: this.dir}); - } + constructor() { + super('git', '') + this.dir = path.resolve(path.join(this.baseDir, 'portable-git')) + } + + getExePath(): string { + return path.resolve(path.join(this.dir, 'cmd/git.exe')) + } + + 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 = { + ...extraEnv, + GIT_SSL_CAINFO: path.resolve(path.join(this.dir, 'mingw64/etc/ssl/certs/ca-bundle.crt')), + } + return super.run(args, env, workDir) + } + + async check(): Promise { + this.log('checking') + try { + await this.run(['--version']) + } catch (e) { + this.log(`warning: ${e}`) + throw new ServiceCheckError(this.name) + } + } + + async install(): Promise { + this.log('start installing') + await this.downloadGitZip() + await this.unzipGit() + + // cleanup + if (filesystem.existsSync(this.zipPath)) { + filesystem.removeSync(this.zipPath) + } + } + + async repair(checkError: ServiceCheckError): Promise { + assert(checkError.component === this.name) + await this.install() + } + + // https://github.com/git-for-windows/git/releases/tag/v2.47.1.windows.1 + readonly remoteUrl = + 'https://github.com/git-for-windows/git/releases/download/v2.47.1.windows.1/MinGit-2.47.1-64-bit.zip' + readonly sha256 = '50b04b55425b5c465d076cdb184f63a0cd0f86f6ec8bb4d5860114a713d2c29a' + readonly zipPath = path.resolve(path.join(this.baseDir, 'portable-git.zip')) + + private async checkGitZip(): Promise { + if (!filesystem.existsSync(this.zipPath)) { + return false + } + const sha256sum = await filesystem + .readFile(this.zipPath) + .then((data) => createHash('sha256').update(data).digest('hex')) + return sha256sum === this.sha256 + } + + private async downloadGitZip(): Promise { + this.log('downloading git archive') + if (await this.checkGitZip()) { + this.log('Using existing git archive') + return + } + + // Reuse existing zip if checksum matches + if (filesystem.existsSync(this.zipPath)) { + this.logError('Removing broken git archive') + filesystem.removeSync(this.zipPath) + } + + // Using electron net for better proxy support + const response = await net.fetch(this.remoteUrl) + if (!response.ok || response.status !== 200 || !response.body) { + throw new Error(`Failed to download git: ${response.statusText}`) + } + const buffer = await response.arrayBuffer() + await filesystem.writeFile(this.zipPath, Buffer.from(buffer)) + if (!(await this.checkGitZip())) { + throw new Error(`Checksum mismatch: ${this.zipPath}`) + } + } + + private async unzipGit(): Promise { + await extract(this.zipPath, { dir: this.dir }) + } } +export const aiBackendServiceDir = () => + path.resolve( + app.isPackaged + ? path.join(process.resourcesPath, 'service') + : path.join(__dirname, '../../../service'), + ) -export const aiBackendServiceDir = () => path.resolve(app.isPackaged ? path.join(process.resourcesPath, "service") : path.join(__dirname, "../../../service")); - -const ipexWheel = "intel_extension_for_pytorch-2.3.110+xpu-cp311-cp311-win_amd64.whl" +const ipexWheel = 'intel_extension_for_pytorch-2.3.110+xpu-cp311-cp311-win_amd64.whl' export interface ApiService { - readonly name: string - readonly baseUrl: string - readonly port: number - readonly isRequired: boolean - currentStatus: BackendStatus; - isSetUp: boolean; - - set_up(): AsyncIterable; - start(): Promise; - stop(): Promise; - get_info(): ApiServiceInformation; + readonly name: string + readonly baseUrl: string + readonly port: number + readonly isRequired: boolean + currentStatus: BackendStatus + isSetUp: boolean + + set_up(): AsyncIterable + start(): Promise + stop(): Promise + get_info(): ApiServiceInformation } export abstract class LongLivedPythonApiService implements ApiService { - readonly name: BackendServiceName - readonly baseUrl: string - readonly port: number - readonly win: BrowserWindow - readonly settings: LocalSettings - abstract readonly isRequired: boolean - abstract healthEndpointUrl: string - - encapsulatedProcess: ChildProcess | null = null - - readonly baseDir = app.isPackaged ? process.resourcesPath : path.join(__dirname, "../../../"); - readonly prototypicalPythonEnv = app.isPackaged ? path.join(this.baseDir, "prototype-python-env") : path.join(this.baseDir, "build-envs/online/prototype-python-env") - readonly customIntelExtensionForPytorch = path.join(app.isPackaged ? this.baseDir : path.join(__dirname, "../../external/"), ipexWheel) - abstract readonly pythonEnvDir: string - abstract readonly lsLevelZeroDir: string - abstract readonly serviceDir: string - abstract isSetUp: boolean; - - desiredStatus: BackendStatus = "uninitializedStatus" - currentStatus: BackendStatus = "uninitializedStatus" - - readonly appLogger = appLoggerInstance - - constructor(name: BackendServiceName, port: number, win: BrowserWindow, settings: LocalSettings) { - this.win = win - this.name = name - this.port = port - this.baseUrl = `http://127.0.0.1:${port}` - this.settings = settings - } - - abstract serviceIsSetUp(): boolean - - setStatus(status: BackendStatus) { - this.currentStatus = status - this.updateStatus() - } - - updateStatus() { - this.win.webContents.send("serviceInfoUpdate", this.get_info()); - } - - get_info(): ApiServiceInformation { - if(this.currentStatus === "uninitializedStatus") { - this.currentStatus = this.isSetUp ? "notYetStarted" : "notInstalled" - } - return { - serviceName: this.name, - status: this.currentStatus, - baseUrl: this.baseUrl, - port: this.port, - isSetUp: this.isSetUp, - isRequired: this.isRequired - } - } - - abstract set_up(): AsyncIterable - - async start(): Promise { - if (this.desiredStatus === "stopped" && this.currentStatus !== "stopped") { - throw new Error('Server currently stopping. Cannot start it.') - } - if (this.currentStatus === "running") { - return "running" - } - if (this.desiredStatus === "running") { - throw new Error('Server startup already requested') - } - - this.desiredStatus = "running" - this.setStatus('starting') - try { - this.appLogger.info(` trying to start ${this.name} python API`, this.name) - const trackedProcess = await this.spawnAPIProcess() - this.encapsulatedProcess = trackedProcess.process - this.pipeProcessLogs(trackedProcess.process) - if (await this.listenServerReady(trackedProcess.didProcessExitEarlyTracker)) { - this.currentStatus = "running" - this.appLogger.info(`started server ${this.name} on ${this.baseUrl}`, this.name) - this.isSetUp = true - } else { - this.currentStatus = "failed" - this.desiredStatus = "failed" - this.isSetUp = false - this.appLogger.error(`server ${this.name} failed to boot`, this.name) - this.encapsulatedProcess?.kill() - } - } catch (error) { - this.appLogger.error(` failed to start server due to ${error}`, this.name) - this.currentStatus = "failed" - this.desiredStatus = "failed" - this.isSetUp = false - this.encapsulatedProcess?.kill() - this.encapsulatedProcess = null - } finally { - this.win.webContents.send("serviceInfoUpdate", this.get_info()); - } - return this.currentStatus; - } - - - async stop(): Promise { - this.appLogger.info(`Stopping backend ${this.name}. It was in state ${this.currentStatus}`, this.name) - this.desiredStatus = "stopped" - this.setStatus('stopping') + readonly name: BackendServiceName + readonly baseUrl: string + readonly port: number + readonly win: BrowserWindow + readonly settings: LocalSettings + abstract readonly isRequired: boolean + abstract healthEndpointUrl: string + + encapsulatedProcess: ChildProcess | null = null + + readonly baseDir = app.isPackaged ? process.resourcesPath : path.join(__dirname, '../../../') + readonly prototypicalPythonEnv = app.isPackaged + ? path.join(this.baseDir, 'prototype-python-env') + : path.join(this.baseDir, 'build-envs/online/prototype-python-env') + readonly customIntelExtensionForPytorch = path.join( + app.isPackaged ? this.baseDir : path.join(__dirname, '../../external/'), + ipexWheel, + ) + abstract readonly pythonEnvDir: string + abstract readonly lsLevelZeroDir: string + abstract readonly serviceDir: string + abstract isSetUp: boolean + + desiredStatus: BackendStatus = 'uninitializedStatus' + currentStatus: BackendStatus = 'uninitializedStatus' + + readonly appLogger = appLoggerInstance + + constructor(name: BackendServiceName, port: number, win: BrowserWindow, settings: LocalSettings) { + this.win = win + this.name = name + this.port = port + this.baseUrl = `http://127.0.0.1:${port}` + this.settings = settings + } + + abstract serviceIsSetUp(): boolean + + setStatus(status: BackendStatus) { + this.currentStatus = status + this.updateStatus() + } + + updateStatus() { + this.win.webContents.send('serviceInfoUpdate', this.get_info()) + } + + get_info(): ApiServiceInformation { + if (this.currentStatus === 'uninitializedStatus') { + this.currentStatus = this.isSetUp ? 'notYetStarted' : 'notInstalled' + } + return { + serviceName: this.name, + status: this.currentStatus, + baseUrl: this.baseUrl, + port: this.port, + isSetUp: this.isSetUp, + isRequired: this.isRequired, + } + } + + abstract set_up(): AsyncIterable + + async start(): Promise { + if (this.desiredStatus === 'stopped' && this.currentStatus !== 'stopped') { + throw new Error('Server currently stopping. Cannot start it.') + } + if (this.currentStatus === 'running') { + return 'running' + } + if (this.desiredStatus === 'running') { + throw new Error('Server startup already requested') + } + + this.desiredStatus = 'running' + this.setStatus('starting') + try { + this.appLogger.info(` trying to start ${this.name} python API`, this.name) + const trackedProcess = await this.spawnAPIProcess() + this.encapsulatedProcess = trackedProcess.process + this.pipeProcessLogs(trackedProcess.process) + if (await this.listenServerReady(trackedProcess.didProcessExitEarlyTracker)) { + this.currentStatus = 'running' + this.appLogger.info(`started server ${this.name} on ${this.baseUrl}`, this.name) + this.isSetUp = true + } else { + this.currentStatus = 'failed' + this.desiredStatus = 'failed' + this.isSetUp = false + this.appLogger.error(`server ${this.name} failed to boot`, this.name) this.encapsulatedProcess?.kill() - await new Promise(resolve => { - setTimeout(() => { - resolve("killedprocess (hopefully)") - }, 1000) - }) - - this.encapsulatedProcess = null - this.currentStatus = "stopped" - return "stopped" - } - - abstract spawnAPIProcess(): Promise<{ process: ChildProcess; didProcessExitEarlyTracker: Promise; }> - - pipeProcessLogs(process: ChildProcess) { - process.stdout!.on('data', (message) => { - if (message.toString().startsWith('INFO')) { - this.appLogger.info(`${message}`, this.name) - } else if (message.toString().startsWith('WARN')) { - this.appLogger.warn(`${message}`, this.name) - } else { - this.appLogger.error(`${message}`, this.name) - } - }) - - process.stderr!.on('data', (message) => { - this.appLogger.error(`${message}`, this.name) - }) - process.on('error', (message) => { - this.appLogger.error(`backend process ${this.name} exited abruptly due to : ${message}`, this.name) - }) - } - - - async listenServerReady(didProcessExitEarlyTracker: Promise): Promise { - const startTime = performance.now() - const processStartupCompletePromise = new Promise(async (resolve) => { - const queryIntervalMs = 250 - const startupPeriodMaxMs = 120000 - while (performance.now() < startTime + startupPeriodMaxMs) { - try { - const serviceHealthResponse = await fetch(this.healthEndpointUrl); - this.appLogger.info(`received response: ${serviceHealthResponse.status}`, this.name) - if (serviceHealthResponse.status === 200) { - const endTime = performance.now() - this.appLogger.info(`${this.name} server startup complete after ${(endTime - startTime) / 1000} seconds`, this.name) - resolve(true) - break - } - } catch (e) { - //fetch will simply fail while server not up - } - await new Promise(resolve => setTimeout(resolve, queryIntervalMs)); - } - if (performance.now() >= startTime + startupPeriodMaxMs) { - this.appLogger.warn(`Server ${this.name} did not return healthy response within ${startupPeriodMaxMs / 1000} seconds`, this.name) - resolve(false) - } - }) - - const processStartupFailedDueToEarlyExit = didProcessExitEarlyTracker.then(earlyExit => !earlyExit) - - return await Promise.race([processStartupFailedDueToEarlyExit, processStartupCompletePromise]) - } + } + } catch (error) { + this.appLogger.error(` failed to start server due to ${error}`, this.name) + this.currentStatus = 'failed' + this.desiredStatus = 'failed' + this.isSetUp = false + this.encapsulatedProcess?.kill() + this.encapsulatedProcess = null + } finally { + this.win.webContents.send('serviceInfoUpdate', this.get_info()) + } + return this.currentStatus + } + + async stop(): Promise { + this.appLogger.info( + `Stopping backend ${this.name}. It was in state ${this.currentStatus}`, + this.name, + ) + this.desiredStatus = 'stopped' + this.setStatus('stopping') + this.encapsulatedProcess?.kill() + await new Promise((resolve) => { + setTimeout(() => { + resolve('killedprocess (hopefully)') + }, 1000) + }) + + this.encapsulatedProcess = null + this.currentStatus = 'stopped' + return 'stopped' + } + + abstract spawnAPIProcess(): Promise<{ + process: ChildProcess + didProcessExitEarlyTracker: Promise + }> + + pipeProcessLogs(process: ChildProcess) { + process.stdout!.on('data', (message) => { + if (message.toString().startsWith('INFO')) { + this.appLogger.info(`${message}`, this.name) + } else if (message.toString().startsWith('WARN')) { + this.appLogger.warn(`${message}`, this.name) + } else { + this.appLogger.error(`${message}`, this.name) + } + }) + + process.stderr!.on('data', (message) => { + this.appLogger.error(`${message}`, this.name) + }) + process.on('error', (message) => { + this.appLogger.error( + `backend process ${this.name} exited abruptly due to : ${message}`, + this.name, + ) + }) + } + + async listenServerReady(didProcessExitEarlyTracker: Promise): Promise { + const startTime = performance.now() + const processStartupCompletePromise = new Promise(async (resolve) => { + const queryIntervalMs = 250 + const startupPeriodMaxMs = 120000 + while (performance.now() < startTime + startupPeriodMaxMs) { + try { + const serviceHealthResponse = await fetch(this.healthEndpointUrl) + this.appLogger.info(`received response: ${serviceHealthResponse.status}`, this.name) + if (serviceHealthResponse.status === 200) { + const endTime = performance.now() + this.appLogger.info( + `${this.name} server startup complete after ${(endTime - startTime) / 1000} seconds`, + this.name, + ) + resolve(true) + break + } + // 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)) + } + if (performance.now() >= startTime + startupPeriodMaxMs) { + this.appLogger.warn( + `Server ${this.name} did not return healthy response within ${startupPeriodMaxMs / 1000} seconds`, + this.name, + ) + resolve(false) + } + }) + + const processStartupFailedDueToEarlyExit = didProcessExitEarlyTracker.then( + (earlyExit) => !earlyExit, + ) + + return await Promise.race([processStartupFailedDueToEarlyExit, processStartupCompletePromise]) + } } diff --git a/WebUI/electron/subprocesses/updateIntelWorkflows.ts b/WebUI/electron/subprocesses/updateIntelWorkflows.ts index a9cf37e0..b33da99b 100644 --- a/WebUI/electron/subprocesses/updateIntelWorkflows.ts +++ b/WebUI/electron/subprocesses/updateIntelWorkflows.ts @@ -1,92 +1,130 @@ -import {appLoggerInstance} from "../logging/logger.ts"; -import path, * as Path from "node:path"; -import * as fs from "fs-extra"; -import * as filesystem from "fs-extra"; -import {copyFileWithDirs, existingFileOrError, spawnProcessAsync} from "./osProcessHelper.ts"; -import {app} from "electron"; +import { appLoggerInstance } from '../logging/logger.ts' +import path, * as Path from 'node:path' +import * as fs from 'fs-extra' +import * as filesystem from 'fs-extra' +import { copyFileWithDirs, existingFileOrError, spawnProcessAsync } from './osProcessHelper.ts' +import { app } from 'electron' const logger = appLoggerInstance -const processLogHandler = (data: string) => {logger.info(data, logSourceName, true)} -const logSourceName = "updateIntelWorkflows" +const processLogHandler = (data: string) => { + logger.info(data, logSourceName, true) +} +const logSourceName = 'updateIntelWorkflows' -const resourcesBaseDir = app.isPackaged ? process.resourcesPath : path.join(__dirname, "../../../"); -const externalRes = path.resolve(app.isPackaged ? process.resourcesPath : path.join(__dirname, "../../external/")); +const resourcesBaseDir = app.isPackaged ? process.resourcesPath : path.join(__dirname, '../../../') +const externalRes = path.resolve( + app.isPackaged ? process.resourcesPath : path.join(__dirname, '../../external/'), +) -const gitExePath = Path.join(resourcesBaseDir, "portable-git", "cmd" , "git.exe") -const workflowDirTargetPath = Path.join(externalRes, "workflows") -const workflowDirSpareGitRepoPath = Path.join(externalRes, "workflows_intel") -const intelWorkflowDirPath = Path.join(workflowDirSpareGitRepoPath, "WebUI", "external", "workflows") -const workflowDirBakTargetPath = Path.join(externalRes, "workflows_bak") +const gitExePath = Path.join(resourcesBaseDir, 'portable-git', 'cmd', 'git.exe') +const workflowDirTargetPath = Path.join(externalRes, 'workflows') +const workflowDirSpareGitRepoPath = Path.join(externalRes, 'workflows_intel') +const intelWorkflowDirPath = Path.join( + workflowDirSpareGitRepoPath, + 'WebUI', + 'external', + 'workflows', +) +const workflowDirBakTargetPath = Path.join(externalRes, 'workflows_bak') -const intelRepoUrl = "https://github.com/intel/AI-Playground" -const gitRef = "dev" +const intelRepoUrl = 'https://github.com/intel/AI-Playground' +const gitRef = 'dev' export async function updateIntelWorkflows(): Promise { - try { - await fetchNewIntelWorkflows() - await backUpCurrentWorkflows() - await replaceCurrentWorkflowsWithIntelWorkflows() - return { - success: true, - backupDir: workflowDirBakTargetPath - } - } catch (e) { - logger.error(`updating intel workflows failed due to ${e}`, logSourceName, true) - if (!filesystem.existsSync(workflowDirTargetPath)) { - logger.info(`restoring previous workflows from ${workflowDirBakTargetPath}`, logSourceName, true) - await copyFileWithDirs(intelWorkflowDirPath, workflowDirTargetPath) - } - return { - success: false, - backupDir: workflowDirBakTargetPath - } + try { + await fetchNewIntelWorkflows() + await backUpCurrentWorkflows() + await replaceCurrentWorkflowsWithIntelWorkflows() + return { + success: true, + backupDir: workflowDirBakTargetPath, + } + } catch (e) { + logger.error(`updating intel workflows failed due to ${e}`, logSourceName, true) + if (!filesystem.existsSync(workflowDirTargetPath)) { + logger.info( + `restoring previous workflows from ${workflowDirBakTargetPath}`, + logSourceName, + true, + ) + await copyFileWithDirs(intelWorkflowDirPath, workflowDirTargetPath) + } + return { + success: false, + backupDir: workflowDirBakTargetPath, } + } } async function fetchNewIntelWorkflows() { - const gitExe = existingFileOrError(gitExePath) - const gitWorkDir = workflowDirSpareGitRepoPath - await prepareSparseGitRepoDir(gitWorkDir) - await prepareSparseGitCheckout(gitWorkDir, gitExe) - await spawnProcessAsync(gitExe, ["checkout", gitRef], processLogHandler, {}, gitWorkDir) - await spawnProcessAsync(gitExe, ["pull"], processLogHandler, {}, gitWorkDir) - logger.info(`cloned current intel workflows from ${gitRef} into ${workflowDirBakTargetPath}`, logSourceName, true) - + const gitExe = existingFileOrError(gitExePath) + const gitWorkDir = workflowDirSpareGitRepoPath + await prepareSparseGitRepoDir(gitWorkDir) + await prepareSparseGitCheckout(gitWorkDir, gitExe) + await spawnProcessAsync(gitExe, ['checkout', gitRef], processLogHandler, {}, gitWorkDir) + await spawnProcessAsync(gitExe, ['pull'], processLogHandler, {}, gitWorkDir) + logger.info( + `cloned current intel workflows from ${gitRef} into ${workflowDirBakTargetPath}`, + logSourceName, + true, + ) } async function backUpCurrentWorkflows() { - await copyFileWithDirs(workflowDirTargetPath, workflowDirBakTargetPath) - logger.info(`backed up current user workflows at ${workflowDirBakTargetPath}`, logSourceName, true) - return + await copyFileWithDirs(workflowDirTargetPath, workflowDirBakTargetPath) + logger.info( + `backed up current user workflows at ${workflowDirBakTargetPath}`, + logSourceName, + true, + ) + return } async function replaceCurrentWorkflowsWithIntelWorkflows() { - if (filesystem.existsSync(workflowDirTargetPath)) { - logger.warn(`removing previous workflow dir at ${workflowDirTargetPath}`, logSourceName, true) - await fs.promises.rm(workflowDirTargetPath, {recursive: true, force: true}) - } - await fs.promises.mkdir(workflowDirTargetPath, { recursive: true }); - await copyFileWithDirs(intelWorkflowDirPath, workflowDirTargetPath) - logger.info(`repopulated workflow dir with intel workflows at ${workflowDirTargetPath}`, logSourceName, true) + if (filesystem.existsSync(workflowDirTargetPath)) { + logger.warn(`removing previous workflow dir at ${workflowDirTargetPath}`, logSourceName, true) + await fs.promises.rm(workflowDirTargetPath, { recursive: true, force: true }) + } + await fs.promises.mkdir(workflowDirTargetPath, { recursive: true }) + await copyFileWithDirs(intelWorkflowDirPath, workflowDirTargetPath) + logger.info( + `repopulated workflow dir with intel workflows at ${workflowDirTargetPath}`, + logSourceName, + true, + ) } async function prepareSparseGitRepoDir(dirPath: string) { - if (!filesystem.existsSync(dirPath)) { - await fs.promises.mkdir(dirPath, { recursive: true }); - logger.info(`Created containment directory at ${dirPath}`, logSourceName, true) - return - } - logger.info(`reusing existing dir ${dirPath} for fetching remote workflows`, logSourceName, true) + if (!filesystem.existsSync(dirPath)) { + await fs.promises.mkdir(dirPath, { recursive: true }) + logger.info(`Created containment directory at ${dirPath}`, logSourceName, true) + return + } + logger.info(`reusing existing dir ${dirPath} for fetching remote workflows`, logSourceName, true) } - async function prepareSparseGitCheckout(workDir: string, gitExe: string) { - const sparseCheckoutConfigFile = path.join(workDir, ".git" ,"info", "sparse-checkout") - if (!filesystem.existsSync(sparseCheckoutConfigFile)) { - await spawnProcessAsync(gitExe, ["init"] , processLogHandler, {}, workDir) - await spawnProcessAsync(gitExe, ["config", "core.sparseCheckout", "true"] , processLogHandler, {}, workDir) - await spawnProcessAsync(gitExe, ["remote", "add", "-f", "origin", intelRepoUrl] , processLogHandler, {}, workDir) - await fs.promises.writeFile(sparseCheckoutConfigFile, "WebUI/external/workflows/*", {encoding: 'utf-8', flag: 'w'}); - } - logger.info(`using existing sparse checkout config`, logSourceName, true) + const sparseCheckoutConfigFile = path.join(workDir, '.git', 'info', 'sparse-checkout') + if (!filesystem.existsSync(sparseCheckoutConfigFile)) { + await spawnProcessAsync(gitExe, ['init'], processLogHandler, {}, workDir) + await spawnProcessAsync( + gitExe, + ['config', 'core.sparseCheckout', 'true'], + processLogHandler, + {}, + workDir, + ) + await spawnProcessAsync( + gitExe, + ['remote', 'add', '-f', 'origin', intelRepoUrl], + processLogHandler, + {}, + workDir, + ) + await fs.promises.writeFile(sparseCheckoutConfigFile, 'WebUI/external/workflows/*', { + encoding: 'utf-8', + flag: 'w', + }) + } + logger.info(`using existing sparse checkout config`, logSourceName, true) } diff --git a/WebUI/eslint.config.ts b/WebUI/eslint.config.ts new file mode 100644 index 00000000..b251adba --- /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/external/model_config.dev.json b/WebUI/external/model_config.dev.json index 5f718bc8..fc725096 100644 --- a/WebUI/external/model_config.dev.json +++ b/WebUI/external/model_config.dev.json @@ -1,9 +1,9 @@ { - "llm": "../service/models/llm/checkpoints", - "ggufLLM": "../service/models/llm/ggufLLM", - "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" -} \ No newline at end of file + "llm": "../service/models/llm/checkpoints", + "ggufLLM": "../service/models/llm/ggufLLM", + "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" +} diff --git a/WebUI/external/settings-dev.json b/WebUI/external/settings-dev.json index 11ce3784..044ae844 100644 --- a/WebUI/external/settings-dev.json +++ b/WebUI/external/settings-dev.json @@ -1,12 +1,12 @@ { - "debug":1, - "comfyUiParameters": [ - "--lowvram", - "--disable-ipex-optimize", - "--bf16-unet", - "--reserve-vram", - "5.0" - ], - "availableThemes": ["dark","lnl","bmg"], - "currentTheme": "bmg" -} \ No newline at end of file + "debug": 1, + "comfyUiParameters": [ + "--lowvram", + "--disable-ipex-optimize", + "--bf16-unet", + "--reserve-vram", + "5.0" + ], + "availableThemes": ["dark", "lnl", "bmg"], + "currentTheme": "bmg" +} diff --git a/WebUI/external/workflows/CopyFace.json b/WebUI/external/workflows/CopyFace.json index 590b04cd..c2a929b7 100644 --- a/WebUI/external/workflows/CopyFace.json +++ b/WebUI/external/workflows/CopyFace.json @@ -1,163 +1,140 @@ -{ - "name": "CopyFace", - "backend": "comfyui", - "comfyUIRequirements": { - "pythonPackages": [ - "https://github.com/Gourieff/Assets/raw/main/Insightface/insightface-0.7.3-cp311-cp311-win_amd64.whl", - "onnxruntime" - ], - "customNodes": [ - "Gourieff/comfyui-reactor@ee13ff754f35d2af284f0cf9d81ccce4defe1e5e" - ], - "requiredModels": [ - { - "type": "defaultCheckpoint", - "model": "RunDiffusion/Juggernaut-XL-v9/unet/diffusion_pytorch_model.fp16.safetensors" - }, - { - "type": "defaultCheckpoint", - "model": "RunDiffusion/Juggernaut-XL-v9/text_encoder/model.fp16.safetensors" - }, - { - "type": "defaultCheckpoint", - "model": "RunDiffusion/Juggernaut-XL-v9/text_encoder_2/model.fp16.safetensors" - }, - { - "type": "defaultCheckpoint", - "model": "RunDiffusion/Juggernaut-XL-v9/vae/diffusion_pytorch_model.fp16.safetensors" - }, - { - "type": "defaultLora", - "model": "latent-consistency/lcm-lora-sdxl/pytorch_lora_weights.safetensors" - }, - { - "type": "faceswap", - "model": "Aitrepreneur/insightface/inswapper_128.onnx", - "additionalLicenceLink": "https://huggingface.co/datasets/Gourieff/ReActor" - }, - { - "type": "facerestore", - "model": "gmk123/GFPGAN/GFPGANv1.4.pth" - } - ] - }, - "tags": [ - "SDXL", - "Fast-LCM", - "non-com" - ], - "requirements": [ - "high-vram" - ], - "inputs": [ - { - "nodeTitle": "ReActor 🌌 Fast Face Swap", - "nodeInput": "enabled", - "type": "boolean", - "label": "Activate FaceSwap", - "defaultValue": true - }, - { - "nodeTitle": "Load Image2", - "nodeInput": "image", - "type": "image", - "label": "Original Image", - "defaultValue": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAZMElEQVR4nO3daXMTZ9aA4aPFFl6wDdgQ2xAChAEya/7/T0hmMkvlTZFJUiSYxYDBOCAv2t4PlKmEAWy1JUutc11fZoqopce2pL776a3y9ddf9wIASKU66gEAAKdPAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACdVHPQCgHA4ODmJ7ezt2dnbi4OAgWq1WtNvtaLVa0el0YmpqKur1ekxNTcXU1FScOXMmFhcXY3FxMapV2xowbgRAid27dy82Njb6Xm59fT2uXbtW6DW//vrr2Nvb62uZa9euxfr6+pGP++qrr2J/f7/QuD6kVqu9XSkd/u/8/HwsLi7G/Pz8QF+r6Pi//PLLmJubG+hYBqXZbMajR49ie3s7dnd3P/rYg4ODODg4+N2/bWxsRKVSiYWFhTh37lysrq5GrVYb5pA/6OnTp3H37t2BPueNGzdidXV1oM85jM9BRESlUol6vf67z0Oj0XgbaVNTUwN/TcabAGCidTqd6HQ6v/tCffr0aURE1Ov1WFhYiIsXL8by8vKohjiWdnd345dffnn7uzqJXq8XL1++jJcvX8bGxkasr6/H2traqYfAUQFTRLPZHPhzDkuv14tWqxWtVut3v4tHjx5FRMTs7OzbSDtz5syohskpEgCk1W634/nz5/H8+fOYnZ2NK1euxMrKyqiHNVLdbjd++umnePz48VCev91ux88//xwPHjyI69evx8WLF4fyOu8zjJX1MKJiVJrNZjSbzXj48GFcvHgxrly5IgQmnB1zEG++/O7evRv/+Mc/JupLvR+tViv+85//DG3l/1vtdju+//77uHfv3tBf69AwAqBMMwDH1ev1YnNzM/7+97/Hzz//POrhMEQCAH6j2WzGv/71r9je3h71UE7V69ev45///Gf8+uuvp/q6Gxsb8d1330W32x3q6/R6vb6PXTmOg4ODaLfbA3/ecdDr9eL+/fun8vdhNAQAvKPdbse3334bm5ubox7KqWi1WvHtt98O5cCz49ja2or//ve/Q32N/f39oa3EJn3GaGtrK/79739Hq9Ua9VAYMAEA79Hr9eKHH3449S3iUfj+++//5+j90/b06dOhBtcwp+oncTfAu169ejXwMygYPQEAH9Dr9eLu3bsTO8Ub8WYK/sWLF6MeRkRE/Pjjj0NbmQ5zJT3pMwCHtre348GDB6MeBgMkAOAj9vb24scffxz1MIZif39/rA7y6na7Q/tdD3MlnWEG4NC9e/fi1atXox4GAyIA4AhPnz6N169fj3oYA/fo0aPo9XqjHsbvvHz5cigrGDMAg9Hr9cYqGjkZAQDH8PDhw1EPYaA6nc6pnO5XxDCmmYe5kt7b20t1lPyLFy9SRc8kEwBwDE+fPp2oo6A3NzcHcmxDtVqNRqMRs7OzMT09PYCRRTx79mygZyQM+1S9YZ1iOM4Orx5IubkSIKU3MzPzwZvNHN605qS63W5sbm7G5cuXT/xc4+DZs2cnWv78+fOxtrYWCwsLv/vdt1qteP78edy/f7/wSrHX68XW1lasra2daIyHTmNrtdlsxuzs7NBf52M+9jmIeDPrM6hQ2dzcjKtXr47svg4MhgCg9G7fvv3Rm+l0Op3Y3d2NJ0+enGi/9/b29kQEQLfbPdF+9o/dAGdqaiouXboUKysrcffu3dja2ir0Gjs7OwMLgNM4SG8cDgQ86nMQ8eZvv7u7Gy9evIgHDx4UjuNOpxOvXr2KxcXFQsszHuwCYOLVarWYn5+P69evx5/+9KfCt6adlKOfX716VXif9fr6+rHufletVuPWrVsxMzNT6HV2dnYKLfc+p7FyLss+8Wq1GnNzc3H58uX429/+dqJZiwzXyJh0AoBUFhcX4+rVq4WWbbfbY7Gld1JFV661Wi0+/fTTYz++Wq3GZ599Vui1Dg4OBjZdfVq7AMqm0WjEnTt3CgexACg/AUA6q6urUa8X2/s1CV96RX+GlZWVvvf5nj9/vvB95gc149LvyrnIHfDKMgPwrpmZmbh06VKhZQc5S8NoCADSqVarce7cuULLlvWL/reK7vddWFjoe5lKpVJouYgYyOWJO51O389z/vz5vl+n2+2W9kyAordkbrVaE32VzAwEACkddbDUh0zCF17Rn6Ho72yUv+t+t/6r1WosLS0Veq2yxuH8/HxUKpVCy07C5yEzAUBKRaZ5IybjC6/oz1B0t0nR5Qbxu+53pXzmzJnCBy6W8TiAiDezNEV/5kn4PGQmAEhplCulUSv6MxQ953uUv+t+V8ozMzNx5syZQlvEZZ0BiMj9echMAJBS0SOfy/6F1+12C58CWHSaeJTTy/2ulGdmZqJSqRSaISrrDEBE8bgr++chOwEAfeh0OqMewomM281/PmYQYy0yA/Db/x3ma42TopGW6R4Ik0gAABOpyDX6TxIA7XZ7ou4XweQTAMBE2t3d7XsW4SQBEFHuWQDyEQDAROp3ZVyv199etKhoAJT5QEDyEQDARCpyAOD7/n8/zABQJgIAmEhFDwCMiJieni50ZLwAoEwEADCRTjIDEJHrngDkJACAiXTSACiyG2B/f7/0p4qShwAAJk6RFfG7W/wOBGTSCQBg4hTZFz+IGYCirw2jIACAidPvVvj7DvozA8CkEwDAxDnJGQAf+7dhvDaMigCAPhS9aQqnaxABUK/XC90lzwwAZSEASKnoTUyK3jaV03XSMwCO+vejXrtMN10iLwFASkVvYyoAxl+Rm/IMMgCK3IQIRkEAkNL+/n6h5QTA+BvEGQBH/fswxgCnTQCQ0qtXrwotJwDGX78r30ql8sGr/gkAJpkAIJ1utxsvXrwotGzRFQKnp9/9/41GIyqVynv/m1MBmWQCgHQeP35c+BiAs2fPDng0DNogzgA4zn8b5BhgFAQAqezs7MS9e/cKLVur1WJ2dnawA2LgBnUGQEREtVqNRqMx9DHAKNihycTrdruxt7cXT548iYcPHxY+BdDW//g7/Fv346it/JmZmb4PGu10OrG/v18oHuC0CABK7+7du1Gtvn8yq9VqFT7i/11LS0sDeR6GZ5BnABwqclvgiDezAAKAcSYAKL3T2N9arVbj0qVLQ38dTqbI1PtxZgCKaDabopGx5hgAOIbl5eWYmpoa9TA4Qr8xeJx9/A4EZFIJADiGtbW1UQ+BYxjkAYD9PGYQY4HTJgDgCMvLyzE/Pz/qYXAMgzwF8NCZM2c+eJ2AQY4FTpsAgI9oNBrx+eefj3oYHEOv1xvKDMDHrhT4Ma1Wq/D1JuA0CAD4gEqlErdu3XL535LY29vr+y58x53edxwAk0gAwHtUKpW4ceNGLCwsjHooHNMwzgDo93HvchwA48ymDbyjXq/H7du3ncJVMkW2to87tW8GgEkkAOA3Zmdn486dO276U0L9rmzr9fqxT+0sejEgAcA4EwAQb7bwrly5EisrK4WO+Gb0hnEAYJHH/pZdAIwzAUBatVotFhcXY2VlJVZWVkY9HE5omAHQaDSiWq32fR+Jvb296Ha7H7xUNYySAGCi1Wq1t1O99Xo96vV6zM/Px9LSUszNzdnanxAHBwd9n3LX71b9zMxMvH79uq9lIt6EydzcXN/LwbAJAErvyy+/9AWb3DBuAvS+xxcJgGaz6f3JWDIvBZTeME8BLPr4Q44DYFwJAKD0hnkK4CGnAjJpBABQev1uZU9PT0etVutrGTMATBoBAJTeMG4CNIhlIt4EQL+XKIbT4CBAoNQ6nU4cHBz0tczU1FShLfNarRadTqevZbrdbuzv7xe+mBAMiwAASq3IPvZnz57Fs2fPhjCa92s2mwKAsWMXAFBqZTjIrgxjJB8BAJRaGQ6yK8MYyUcAAKVWhq3rMoyRfAQAUGpl2LouwxjJRwAApdXr9WJvb2/UwzhSu93u+0wFGDYBUGJuZEO/yvSeOc5Yy3SO/TjvBij6O3SXw3Lz1yuxfq9kNir1urNNx0W1Wi0cAUVXEkWXO877ZpxXqu8a590A/V7b4JDPdrkJgBIr+uHr97apJ13Wl8R4mZqaKrRc0ZVE0ffbcd4347xSfdc4x8ow/0aMLwFQYqcdAL1er9BKwJfEeDnt980wVy7jvFJ917jGykmOo/DZLjcBUGJFP3xF7mke8ebLtsh0ri+J8XLa75uiy01aAIzrWF+/fh3dbrfQsj7b5SYASqzoVO7e3l7s7+/3vdzOzk6h1ys6Toaj6N+jyN+/1+vFr7/+Wuj1jjPOcd2qfp+Dg4MT7X4bls3NzULLTU1NCYCS89crsdnZ2ahWq4XqfXNzMz799NO+lnn8+HHfrzM9PR3T09N9L8fwnD17Nra2tvpe7tmzZ3H9+vW+jvx+8eJF4dPfzp49+9H/vre31/d7/8KFC3Hnzp1C4/mtr776qlBE7+7uHvlznaa9vb148uRJoWXH6eegGDMAJVapVGJ+fr7Qsg8ePOhrv9/jx48LTeX6khg/CwsLhZZrt9vxyy+/HPvx3W437t27V+i1pqenj7x5TpGt/6K39B3U84zTjMXBwUF89913hQ/uLPo+YnwIgJIr+iHsdDrxf//3f8eKgK2trfjpp58KvY4AGD/z8/OFTwXc2Ng41kxQt9uN77//vvB+7+O8r4s896gDYNTHAfR6vdjd3Y2HDx/GN998U/j4jAif7UlgF0DJneRD2Gw245tvvonV1dVYWlqKmZmZaDQa0e12Y29vL5rNZjx9+rTQdPEhWwlHu3v37tAuqHL16tU4d+7c7/6tWq3G/Px84X3zP/zwQ7x48SLW19fj7Nmzv4uJdrsdz58/j/v3759oa/c475tRzgDMzs4WWm6YAXDU+6jT6cTe3t5ALpxUq9UKzz4yPgRAyS0tLUWtVis8jdfpdGJjYyM2NjYiIgofU/A+09PTthKOYZgrhQ8ddLayslI4ACLezAptbW1FtVqN6enpqNVq0W63C+0Xf1elUokLFy4c+bgiv7eiK+53jeMugNOcXbh48WJpLkTGh9kFUHK1Wi0uXbo0sOcb1Mo/ImJ1dbVUl57N5NKlSwP5Aj+cLXr9+vVAVv4Rbw7UazQaRz6u3xVevV4f2FHrRQNgUFvgo7a2tjbqITAAAmACrK6ujnoI/6NarcYnn3wy6mHwAbVabWz/Puvr60c+ptVq9X1K3aC2/iMiGo1God02h/vgy+xwdyHlJwAmwMzMzP/s5x215eVl5/+PubW1tbGboVlYWDjWbqNR7v8/6fON+kDAk6hUKnH16tVRD4MBEQAT4vr162OzT65er8dnn3026mFwhEaj0fe1IIapWq3GjRs3jvXYUZ4BcNLnK/MMwNWrVx3XM0EEwISYmZmJ69evj3oYERHxhz/8wcV/SuLKlSuxtLQ06mFExJuInZubO9ZjyxwAZZ0BWFpaisuXL496GAyQAJggly5diuXl5ZGOYXV1Nc6fPz/SMdCfW7dujTzYlpeX+zomocy7AMo4AzA3Nxe3bt0a9TAYMAEwYW7evDmy4wGWl5fj2rVrI3ltipuamoo//vGPxzryfhjOnz8fN2/e7GuZfreiK5XK2ARA2WYALly4EH/5y18c0zOBBMCEqdVq8cUXX5z6Ed6XL1+O27dvD+2CNgzX3Nxc/PWvfz31/bvr6+vxxRdf9HX8Srfb7fuUw0ajMfADHosGwOGpk+OuUqnE5cuX486dO2NzfBGD5UJAE6hSqcTnn38es7Oz8fPPPxe+SNBx1Ov1uHbt2kCvRcBoTE9Px5///Of48ccfC98h7rhO8r4Z5QWAfqter8fU1FS0Wq2+l93d3T3yXgejUqlU4uLFi3HlypWxHSODIQAm2NraWqysrMTGxkY8evRooBf5qdVqsba2Fuvr624JOkGq1WrcvHkz1tfX45dffolnz54N9Pnr9Xqsra3F2tpa4ffNOOz//+3zFgmAZrM5dqfuHp5OvLa2ZsWfhG/uCTc1NRXXrl2L9fX1ePjwYWxvb8erV68KPdfh3QfPnTsXq6ur9glOsNnZ2bh9+3Y0m8149OhRbG9vFz54rVKpxNmzZ+P8+fPxySefnDgYx+EMgEOzs7Oxs7PT93KjOBCwUqm8vRri4exFo9GIhYWFWFxcHPmBoJy+ytdff13+61LSl3a7Hdvb2/Hy5cvY39+PdrsdrVYrWq1WdDqd331BHH5JLC4uxuLioq39xPb39+Ply5exs7MTBwcHb98z7Xb77fvm8D1Tr9fjzJkzsbS0FAsLC/YhwxgSAACQkEO2ASAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJ/T8r8gNH5WpNaAAAAABJRU5ErkJggg==" - }, - { - "nodeTitle": "Load Image", - "nodeInput": "image", - "type": "image", - "label": "New Face Image", - "defaultValue": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAZMElEQVR4nO3daXMTZ9aA4aPFFl6wDdgQ2xAChAEya/7/T0hmMkvlTZFJUiSYxYDBOCAv2t4PlKmEAWy1JUutc11fZoqopce2pL776a3y9ddf9wIASKU66gEAAKdPAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACdVHPQCgHA4ODmJ7ezt2dnbi4OAgWq1WtNvtaLVa0el0YmpqKur1ekxNTcXU1FScOXMmFhcXY3FxMapV2xowbgRAid27dy82Njb6Xm59fT2uXbtW6DW//vrr2Nvb62uZa9euxfr6+pGP++qrr2J/f7/QuD6kVqu9XSkd/u/8/HwsLi7G/Pz8QF+r6Pi//PLLmJubG+hYBqXZbMajR49ie3s7dnd3P/rYg4ODODg4+N2/bWxsRKVSiYWFhTh37lysrq5GrVYb5pA/6OnTp3H37t2BPueNGzdidXV1oM85jM9BRESlUol6vf67z0Oj0XgbaVNTUwN/TcabAGCidTqd6HQ6v/tCffr0aURE1Ov1WFhYiIsXL8by8vKohjiWdnd345dffnn7uzqJXq8XL1++jJcvX8bGxkasr6/H2traqYfAUQFTRLPZHPhzDkuv14tWqxWtVut3v4tHjx5FRMTs7OzbSDtz5syohskpEgCk1W634/nz5/H8+fOYnZ2NK1euxMrKyqiHNVLdbjd++umnePz48VCev91ux88//xwPHjyI69evx8WLF4fyOu8zjJX1MKJiVJrNZjSbzXj48GFcvHgxrly5IgQmnB1zEG++/O7evRv/+Mc/JupLvR+tViv+85//DG3l/1vtdju+//77uHfv3tBf69AwAqBMMwDH1ev1YnNzM/7+97/Hzz//POrhMEQCAH6j2WzGv/71r9je3h71UE7V69ev45///Gf8+uuvp/q6Gxsb8d1330W32x3q6/R6vb6PXTmOg4ODaLfbA3/ecdDr9eL+/fun8vdhNAQAvKPdbse3334bm5ubox7KqWi1WvHtt98O5cCz49ja2or//ve/Q32N/f39oa3EJn3GaGtrK/79739Hq9Ua9VAYMAEA79Hr9eKHH3449S3iUfj+++//5+j90/b06dOhBtcwp+oncTfAu169ejXwMygYPQEAH9Dr9eLu3bsTO8Ub8WYK/sWLF6MeRkRE/Pjjj0NbmQ5zJT3pMwCHtre348GDB6MeBgMkAOAj9vb24scffxz1MIZif39/rA7y6na7Q/tdD3MlnWEG4NC9e/fi1atXox4GAyIA4AhPnz6N169fj3oYA/fo0aPo9XqjHsbvvHz5cigrGDMAg9Hr9cYqGjkZAQDH8PDhw1EPYaA6nc6pnO5XxDCmmYe5kt7b20t1lPyLFy9SRc8kEwBwDE+fPp2oo6A3NzcHcmxDtVqNRqMRs7OzMT09PYCRRTx79mygZyQM+1S9YZ1iOM4Orx5IubkSIKU3MzPzwZvNHN605qS63W5sbm7G5cuXT/xc4+DZs2cnWv78+fOxtrYWCwsLv/vdt1qteP78edy/f7/wSrHX68XW1lasra2daIyHTmNrtdlsxuzs7NBf52M+9jmIeDPrM6hQ2dzcjKtXr47svg4MhgCg9G7fvv3Rm+l0Op3Y3d2NJ0+enGi/9/b29kQEQLfbPdF+9o/dAGdqaiouXboUKysrcffu3dja2ir0Gjs7OwMLgNM4SG8cDgQ86nMQ8eZvv7u7Gy9evIgHDx4UjuNOpxOvXr2KxcXFQsszHuwCYOLVarWYn5+P69evx5/+9KfCt6adlKOfX716VXif9fr6+rHufletVuPWrVsxMzNT6HV2dnYKLfc+p7FyLss+8Wq1GnNzc3H58uX429/+dqJZiwzXyJh0AoBUFhcX4+rVq4WWbbfbY7Gld1JFV661Wi0+/fTTYz++Wq3GZ599Vui1Dg4OBjZdfVq7AMqm0WjEnTt3CgexACg/AUA6q6urUa8X2/s1CV96RX+GlZWVvvf5nj9/vvB95gc149LvyrnIHfDKMgPwrpmZmbh06VKhZQc5S8NoCADSqVarce7cuULLlvWL/reK7vddWFjoe5lKpVJouYgYyOWJO51O389z/vz5vl+n2+2W9kyAordkbrVaE32VzAwEACkddbDUh0zCF17Rn6Ho72yUv+t+t/6r1WosLS0Veq2yxuH8/HxUKpVCy07C5yEzAUBKRaZ5IybjC6/oz1B0t0nR5Qbxu+53pXzmzJnCBy6W8TiAiDezNEV/5kn4PGQmAEhplCulUSv6MxQ953uUv+t+V8ozMzNx5syZQlvEZZ0BiMj9echMAJBS0SOfy/6F1+12C58CWHSaeJTTy/2ulGdmZqJSqRSaISrrDEBE8bgr++chOwEAfeh0OqMewomM281/PmYQYy0yA/Db/x3ma42TopGW6R4Ik0gAABOpyDX6TxIA7XZ7ou4XweQTAMBE2t3d7XsW4SQBEFHuWQDyEQDAROp3ZVyv199etKhoAJT5QEDyEQDARCpyAOD7/n8/zABQJgIAmEhFDwCMiJieni50ZLwAoEwEADCRTjIDEJHrngDkJACAiXTSACiyG2B/f7/0p4qShwAAJk6RFfG7W/wOBGTSCQBg4hTZFz+IGYCirw2jIACAidPvVvj7DvozA8CkEwDAxDnJGQAf+7dhvDaMigCAPhS9aQqnaxABUK/XC90lzwwAZSEASKnoTUyK3jaV03XSMwCO+vejXrtMN10iLwFASkVvYyoAxl+Rm/IMMgCK3IQIRkEAkNL+/n6h5QTA+BvEGQBH/fswxgCnTQCQ0qtXrwotJwDGX78r30ql8sGr/gkAJpkAIJ1utxsvXrwotGzRFQKnp9/9/41GIyqVynv/m1MBmWQCgHQeP35c+BiAs2fPDng0DNogzgA4zn8b5BhgFAQAqezs7MS9e/cKLVur1WJ2dnawA2LgBnUGQEREtVqNRqMx9DHAKNihycTrdruxt7cXT548iYcPHxY+BdDW//g7/Fv346it/JmZmb4PGu10OrG/v18oHuC0CABK7+7du1Gtvn8yq9VqFT7i/11LS0sDeR6GZ5BnABwqclvgiDezAAKAcSYAKL3T2N9arVbj0qVLQ38dTqbI1PtxZgCKaDabopGx5hgAOIbl5eWYmpoa9TA4Qr8xeJx9/A4EZFIJADiGtbW1UQ+BYxjkAYD9PGYQY4HTJgDgCMvLyzE/Pz/qYXAMgzwF8NCZM2c+eJ2AQY4FTpsAgI9oNBrx+eefj3oYHEOv1xvKDMDHrhT4Ma1Wq/D1JuA0CAD4gEqlErdu3XL535LY29vr+y58x53edxwAk0gAwHtUKpW4ceNGLCwsjHooHNMwzgDo93HvchwA48ymDbyjXq/H7du3ncJVMkW2to87tW8GgEkkAOA3Zmdn486dO276U0L9rmzr9fqxT+0sejEgAcA4EwAQb7bwrly5EisrK4WO+Gb0hnEAYJHH/pZdAIwzAUBatVotFhcXY2VlJVZWVkY9HE5omAHQaDSiWq32fR+Jvb296Ha7H7xUNYySAGCi1Wq1t1O99Xo96vV6zM/Px9LSUszNzdnanxAHBwd9n3LX71b9zMxMvH79uq9lIt6EydzcXN/LwbAJAErvyy+/9AWb3DBuAvS+xxcJgGaz6f3JWDIvBZTeME8BLPr4Q44DYFwJAKD0hnkK4CGnAjJpBABQev1uZU9PT0etVutrGTMATBoBAJTeMG4CNIhlIt4EQL+XKIbT4CBAoNQ6nU4cHBz0tczU1FShLfNarRadTqevZbrdbuzv7xe+mBAMiwAASq3IPvZnz57Fs2fPhjCa92s2mwKAsWMXAFBqZTjIrgxjJB8BAJRaGQ6yK8MYyUcAAKVWhq3rMoyRfAQAUGpl2LouwxjJRwAApdXr9WJvb2/UwzhSu93u+0wFGDYBUGJuZEO/yvSeOc5Yy3SO/TjvBij6O3SXw3Lz1yuxfq9kNir1urNNx0W1Wi0cAUVXEkWXO877ZpxXqu8a590A/V7b4JDPdrkJgBIr+uHr97apJ13Wl8R4mZqaKrRc0ZVE0ffbcd4347xSfdc4x8ow/0aMLwFQYqcdAL1er9BKwJfEeDnt980wVy7jvFJ917jGykmOo/DZLjcBUGJFP3xF7mke8ebLtsh0ri+J8XLa75uiy01aAIzrWF+/fh3dbrfQsj7b5SYASqzoVO7e3l7s7+/3vdzOzk6h1ys6Toaj6N+jyN+/1+vFr7/+Wuj1jjPOcd2qfp+Dg4MT7X4bls3NzULLTU1NCYCS89crsdnZ2ahWq4XqfXNzMz799NO+lnn8+HHfrzM9PR3T09N9L8fwnD17Nra2tvpe7tmzZ3H9+vW+jvx+8eJF4dPfzp49+9H/vre31/d7/8KFC3Hnzp1C4/mtr776qlBE7+7uHvlznaa9vb148uRJoWXH6eegGDMAJVapVGJ+fr7Qsg8ePOhrv9/jx48LTeX6khg/CwsLhZZrt9vxyy+/HPvx3W437t27V+i1pqenj7x5TpGt/6K39B3U84zTjMXBwUF89913hQ/uLPo+YnwIgJIr+iHsdDrxf//3f8eKgK2trfjpp58KvY4AGD/z8/OFTwXc2Ng41kxQt9uN77//vvB+7+O8r4s896gDYNTHAfR6vdjd3Y2HDx/GN998U/j4jAif7UlgF0DJneRD2Gw245tvvonV1dVYWlqKmZmZaDQa0e12Y29vL5rNZjx9+rTQdPEhWwlHu3v37tAuqHL16tU4d+7c7/6tWq3G/Px84X3zP/zwQ7x48SLW19fj7Nmzv4uJdrsdz58/j/v3759oa/c475tRzgDMzs4WWm6YAXDU+6jT6cTe3t5ALpxUq9UKzz4yPgRAyS0tLUWtVis8jdfpdGJjYyM2NjYiIgofU/A+09PTthKOYZgrhQ8ddLayslI4ACLezAptbW1FtVqN6enpqNVq0W63C+0Xf1elUokLFy4c+bgiv7eiK+53jeMugNOcXbh48WJpLkTGh9kFUHK1Wi0uXbo0sOcb1Mo/ImJ1dbVUl57N5NKlSwP5Aj+cLXr9+vVAVv4Rbw7UazQaRz6u3xVevV4f2FHrRQNgUFvgo7a2tjbqITAAAmACrK6ujnoI/6NarcYnn3wy6mHwAbVabWz/Puvr60c+ptVq9X1K3aC2/iMiGo1God02h/vgy+xwdyHlJwAmwMzMzP/s5x215eVl5/+PubW1tbGboVlYWDjWbqNR7v8/6fON+kDAk6hUKnH16tVRD4MBEQAT4vr162OzT65er8dnn3026mFwhEaj0fe1IIapWq3GjRs3jvXYUZ4BcNLnK/MMwNWrVx3XM0EEwISYmZmJ69evj3oYERHxhz/8wcV/SuLKlSuxtLQ06mFExJuInZubO9ZjyxwAZZ0BWFpaisuXL496GAyQAJggly5diuXl5ZGOYXV1Nc6fPz/SMdCfW7dujTzYlpeX+zomocy7AMo4AzA3Nxe3bt0a9TAYMAEwYW7evDmy4wGWl5fj2rVrI3ltipuamoo//vGPxzryfhjOnz8fN2/e7GuZfreiK5XK2ARA2WYALly4EH/5y18c0zOBBMCEqdVq8cUXX5z6Ed6XL1+O27dvD+2CNgzX3Nxc/PWvfz31/bvr6+vxxRdf9HX8Srfb7fuUw0ajMfADHosGwOGpk+OuUqnE5cuX486dO2NzfBGD5UJAE6hSqcTnn38es7Oz8fPPPxe+SNBx1Ov1uHbt2kCvRcBoTE9Px5///Of48ccfC98h7rhO8r4Z5QWAfqter8fU1FS0Wq2+l93d3T3yXgejUqlU4uLFi3HlypWxHSODIQAm2NraWqysrMTGxkY8evRooBf5qdVqsba2Fuvr624JOkGq1WrcvHkz1tfX45dffolnz54N9Pnr9Xqsra3F2tpa4ffNOOz//+3zFgmAZrM5dqfuHp5OvLa2ZsWfhG/uCTc1NRXXrl2L9fX1ePjwYWxvb8erV68KPdfh3QfPnTsXq6ur9glOsNnZ2bh9+3Y0m8149OhRbG9vFz54rVKpxNmzZ+P8+fPxySefnDgYx+EMgEOzs7Oxs7PT93KjOBCwUqm8vRri4exFo9GIhYWFWFxcHPmBoJy+ytdff13+61LSl3a7Hdvb2/Hy5cvY39+PdrsdrVYrWq1WdDqd331BHH5JLC4uxuLioq39xPb39+Ply5exs7MTBwcHb98z7Xb77fvm8D1Tr9fjzJkzsbS0FAsLC/YhwxgSAACQkEO2ASAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJ/T8r8gNH5WpNaAAAAABJRU5ErkJggg==" - }, - - { - "nodeTitle": "Save Image", - "nodeInput": "filename_prefix", - "type": "string", - "label": "Filename Prefix", - "defaultValue": "ComfyUI" - } - ], - "outputs": [ - { - "name": "output_image", - "type": "image" - } - ], - "defaultSettings": { - "resolution": "896x896", - "inferenceSteps": 4 - }, - "displayedSettings": [], - "modifiableSettings": [ - "inferenceSteps", - "seed", - "batchSize", - "imagePreview", - "resolution" - ], - "comfyUiApiWorkflow": { - "9": { - "inputs": { - "filename_prefix": "ComfyUI", - "images": [ - "25", - 0 - ] - }, - "class_type": "SaveImage", - "_meta": { - "title": "Save Image" - } - }, - "15": { - "inputs": { - "image": "07b33c98c19fcb8bbf124d3eefa72c18ad8f5c3cfbe3b3661fbf85cecffb7668.png", - "upload": "image" - }, - "class_type": "LoadImage", - "_meta": { - "title": "Load Image" - } - }, - "25": { - "inputs": { - "enabled": true, - "swap_model": "Aitrepreneur---insightface---inswapper_128.onnx", - "facedetection": "retinaface_resnet50", - "face_restore_model": "gmk123---GFPGAN---GFPGANv1.4.pth", - "face_restore_visibility": 1, - "codeformer_weight": 0.5, - "detect_gender_input": "no", - "detect_gender_source": "no", - "input_faces_index": "0", - "source_faces_index": "0", - "console_log_level": 1, - "input_image": [ - "26", - 0 - ], - "source_image": [ - "15", - 0 - ] - }, - "class_type": "ReActorFaceSwap", - "_meta": { - "title": "ReActor 🌌 Fast Face Swap" - } - }, - "26": { - "inputs": { - "image": "ComfyUI_00007_.png", - "upload": "image" - }, - "class_type": "LoadImage", - "_meta": { - "title": "Load Image2" - } - } -} -} +{ + "name": "CopyFace", + "backend": "comfyui", + "comfyUIRequirements": { + "pythonPackages": [ + "https://github.com/Gourieff/Assets/raw/main/Insightface/insightface-0.7.3-cp311-cp311-win_amd64.whl", + "onnxruntime" + ], + "customNodes": ["Gourieff/comfyui-reactor@ee13ff754f35d2af284f0cf9d81ccce4defe1e5e"], + "requiredModels": [ + { + "type": "defaultCheckpoint", + "model": "RunDiffusion/Juggernaut-XL-v9/unet/diffusion_pytorch_model.fp16.safetensors" + }, + { + "type": "defaultCheckpoint", + "model": "RunDiffusion/Juggernaut-XL-v9/text_encoder/model.fp16.safetensors" + }, + { + "type": "defaultCheckpoint", + "model": "RunDiffusion/Juggernaut-XL-v9/text_encoder_2/model.fp16.safetensors" + }, + { + "type": "defaultCheckpoint", + "model": "RunDiffusion/Juggernaut-XL-v9/vae/diffusion_pytorch_model.fp16.safetensors" + }, + { + "type": "defaultLora", + "model": "latent-consistency/lcm-lora-sdxl/pytorch_lora_weights.safetensors" + }, + { + "type": "faceswap", + "model": "Aitrepreneur/insightface/inswapper_128.onnx", + "additionalLicenceLink": "https://huggingface.co/datasets/Gourieff/ReActor" + }, + { + "type": "facerestore", + "model": "gmk123/GFPGAN/GFPGANv1.4.pth" + } + ] + }, + "tags": ["SDXL", "Fast-LCM", "non-com"], + "requirements": ["high-vram"], + "inputs": [ + { + "nodeTitle": "ReActor 🌌 Fast Face Swap", + "nodeInput": "enabled", + "type": "boolean", + "label": "Activate FaceSwap", + "defaultValue": true + }, + { + "nodeTitle": "Load Image2", + "nodeInput": "image", + "type": "image", + "label": "Original Image", + "defaultValue": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAZMElEQVR4nO3daXMTZ9aA4aPFFl6wDdgQ2xAChAEya/7/T0hmMkvlTZFJUiSYxYDBOCAv2t4PlKmEAWy1JUutc11fZoqopce2pL776a3y9ddf9wIASKU66gEAAKdPAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACdVHPQCgHA4ODmJ7ezt2dnbi4OAgWq1WtNvtaLVa0el0YmpqKur1ekxNTcXU1FScOXMmFhcXY3FxMapV2xowbgRAid27dy82Njb6Xm59fT2uXbtW6DW//vrr2Nvb62uZa9euxfr6+pGP++qrr2J/f7/QuD6kVqu9XSkd/u/8/HwsLi7G/Pz8QF+r6Pi//PLLmJubG+hYBqXZbMajR49ie3s7dnd3P/rYg4ODODg4+N2/bWxsRKVSiYWFhTh37lysrq5GrVYb5pA/6OnTp3H37t2BPueNGzdidXV1oM85jM9BRESlUol6vf67z0Oj0XgbaVNTUwN/TcabAGCidTqd6HQ6v/tCffr0aURE1Ov1WFhYiIsXL8by8vKohjiWdnd345dffnn7uzqJXq8XL1++jJcvX8bGxkasr6/H2traqYfAUQFTRLPZHPhzDkuv14tWqxWtVut3v4tHjx5FRMTs7OzbSDtz5syohskpEgCk1W634/nz5/H8+fOYnZ2NK1euxMrKyqiHNVLdbjd++umnePz48VCev91ux88//xwPHjyI69evx8WLF4fyOu8zjJX1MKJiVJrNZjSbzXj48GFcvHgxrly5IgQmnB1zEG++/O7evRv/+Mc/JupLvR+tViv+85//DG3l/1vtdju+//77uHfv3tBf69AwAqBMMwDH1ev1YnNzM/7+97/Hzz//POrhMEQCAH6j2WzGv/71r9je3h71UE7V69ev45///Gf8+uuvp/q6Gxsb8d1330W32x3q6/R6vb6PXTmOg4ODaLfbA3/ecdDr9eL+/fun8vdhNAQAvKPdbse3334bm5ubox7KqWi1WvHtt98O5cCz49ja2or//ve/Q32N/f39oa3EJn3GaGtrK/79739Hq9Ua9VAYMAEA79Hr9eKHH3449S3iUfj+++//5+j90/b06dOhBtcwp+oncTfAu169ejXwMygYPQEAH9Dr9eLu3bsTO8Ub8WYK/sWLF6MeRkRE/Pjjj0NbmQ5zJT3pMwCHtre348GDB6MeBgMkAOAj9vb24scffxz1MIZif39/rA7y6na7Q/tdD3MlnWEG4NC9e/fi1atXox4GAyIA4AhPnz6N169fj3oYA/fo0aPo9XqjHsbvvHz5cigrGDMAg9Hr9cYqGjkZAQDH8PDhw1EPYaA6nc6pnO5XxDCmmYe5kt7b20t1lPyLFy9SRc8kEwBwDE+fPp2oo6A3NzcHcmxDtVqNRqMRs7OzMT09PYCRRTx79mygZyQM+1S9YZ1iOM4Orx5IubkSIKU3MzPzwZvNHN605qS63W5sbm7G5cuXT/xc4+DZs2cnWv78+fOxtrYWCwsLv/vdt1qteP78edy/f7/wSrHX68XW1lasra2daIyHTmNrtdlsxuzs7NBf52M+9jmIeDPrM6hQ2dzcjKtXr47svg4MhgCg9G7fvv3Rm+l0Op3Y3d2NJ0+enGi/9/b29kQEQLfbPdF+9o/dAGdqaiouXboUKysrcffu3dja2ir0Gjs7OwMLgNM4SG8cDgQ86nMQ8eZvv7u7Gy9evIgHDx4UjuNOpxOvXr2KxcXFQsszHuwCYOLVarWYn5+P69evx5/+9KfCt6adlKOfX716VXif9fr6+rHufletVuPWrVsxMzNT6HV2dnYKLfc+p7FyLss+8Wq1GnNzc3H58uX429/+dqJZiwzXyJh0AoBUFhcX4+rVq4WWbbfbY7Gld1JFV661Wi0+/fTTYz++Wq3GZ599Vui1Dg4OBjZdfVq7AMqm0WjEnTt3CgexACg/AUA6q6urUa8X2/s1CV96RX+GlZWVvvf5nj9/vvB95gc149LvyrnIHfDKMgPwrpmZmbh06VKhZQc5S8NoCADSqVarce7cuULLlvWL/reK7vddWFjoe5lKpVJouYgYyOWJO51O389z/vz5vl+n2+2W9kyAordkbrVaE32VzAwEACkddbDUh0zCF17Rn6Ho72yUv+t+t/6r1WosLS0Veq2yxuH8/HxUKpVCy07C5yEzAUBKRaZ5IybjC6/oz1B0t0nR5Qbxu+53pXzmzJnCBy6W8TiAiDezNEV/5kn4PGQmAEhplCulUSv6MxQ953uUv+t+V8ozMzNx5syZQlvEZZ0BiMj9echMAJBS0SOfy/6F1+12C58CWHSaeJTTy/2ulGdmZqJSqRSaISrrDEBE8bgr++chOwEAfeh0OqMewomM281/PmYQYy0yA/Db/x3ma42TopGW6R4Ik0gAABOpyDX6TxIA7XZ7ou4XweQTAMBE2t3d7XsW4SQBEFHuWQDyEQDAROp3ZVyv199etKhoAJT5QEDyEQDARCpyAOD7/n8/zABQJgIAmEhFDwCMiJieni50ZLwAoEwEADCRTjIDEJHrngDkJACAiXTSACiyG2B/f7/0p4qShwAAJk6RFfG7W/wOBGTSCQBg4hTZFz+IGYCirw2jIACAidPvVvj7DvozA8CkEwDAxDnJGQAf+7dhvDaMigCAPhS9aQqnaxABUK/XC90lzwwAZSEASKnoTUyK3jaV03XSMwCO+vejXrtMN10iLwFASkVvYyoAxl+Rm/IMMgCK3IQIRkEAkNL+/n6h5QTA+BvEGQBH/fswxgCnTQCQ0qtXrwotJwDGX78r30ql8sGr/gkAJpkAIJ1utxsvXrwotGzRFQKnp9/9/41GIyqVynv/m1MBmWQCgHQeP35c+BiAs2fPDng0DNogzgA4zn8b5BhgFAQAqezs7MS9e/cKLVur1WJ2dnawA2LgBnUGQEREtVqNRqMx9DHAKNihycTrdruxt7cXT548iYcPHxY+BdDW//g7/Fv346it/JmZmb4PGu10OrG/v18oHuC0CABK7+7du1Gtvn8yq9VqFT7i/11LS0sDeR6GZ5BnABwqclvgiDezAAKAcSYAKL3T2N9arVbj0qVLQ38dTqbI1PtxZgCKaDabopGx5hgAOIbl5eWYmpoa9TA4Qr8xeJx9/A4EZFIJADiGtbW1UQ+BYxjkAYD9PGYQY4HTJgDgCMvLyzE/Pz/qYXAMgzwF8NCZM2c+eJ2AQY4FTpsAgI9oNBrx+eefj3oYHEOv1xvKDMDHrhT4Ma1Wq/D1JuA0CAD4gEqlErdu3XL535LY29vr+y58x53edxwAk0gAwHtUKpW4ceNGLCwsjHooHNMwzgDo93HvchwA48ymDbyjXq/H7du3ncJVMkW2to87tW8GgEkkAOA3Zmdn486dO276U0L9rmzr9fqxT+0sejEgAcA4EwAQb7bwrly5EisrK4WO+Gb0hnEAYJHH/pZdAIwzAUBatVotFhcXY2VlJVZWVkY9HE5omAHQaDSiWq32fR+Jvb296Ha7H7xUNYySAGCi1Wq1t1O99Xo96vV6zM/Px9LSUszNzdnanxAHBwd9n3LX71b9zMxMvH79uq9lIt6EydzcXN/LwbAJAErvyy+/9AWb3DBuAvS+xxcJgGaz6f3JWDIvBZTeME8BLPr4Q44DYFwJAKD0hnkK4CGnAjJpBABQev1uZU9PT0etVutrGTMATBoBAJTeMG4CNIhlIt4EQL+XKIbT4CBAoNQ6nU4cHBz0tczU1FShLfNarRadTqevZbrdbuzv7xe+mBAMiwAASq3IPvZnz57Fs2fPhjCa92s2mwKAsWMXAFBqZTjIrgxjJB8BAJRaGQ6yK8MYyUcAAKVWhq3rMoyRfAQAUGpl2LouwxjJRwAApdXr9WJvb2/UwzhSu93u+0wFGDYBUGJuZEO/yvSeOc5Yy3SO/TjvBij6O3SXw3Lz1yuxfq9kNir1urNNx0W1Wi0cAUVXEkWXO877ZpxXqu8a590A/V7b4JDPdrkJgBIr+uHr97apJ13Wl8R4mZqaKrRc0ZVE0ffbcd4347xSfdc4x8ow/0aMLwFQYqcdAL1er9BKwJfEeDnt980wVy7jvFJ917jGykmOo/DZLjcBUGJFP3xF7mke8ebLtsh0ri+J8XLa75uiy01aAIzrWF+/fh3dbrfQsj7b5SYASqzoVO7e3l7s7+/3vdzOzk6h1ys6Toaj6N+jyN+/1+vFr7/+Wuj1jjPOcd2qfp+Dg4MT7X4bls3NzULLTU1NCYCS89crsdnZ2ahWq4XqfXNzMz799NO+lnn8+HHfrzM9PR3T09N9L8fwnD17Nra2tvpe7tmzZ3H9+vW+jvx+8eJF4dPfzp49+9H/vre31/d7/8KFC3Hnzp1C4/mtr776qlBE7+7uHvlznaa9vb148uRJoWXH6eegGDMAJVapVGJ+fr7Qsg8ePOhrv9/jx48LTeX6khg/CwsLhZZrt9vxyy+/HPvx3W437t27V+i1pqenj7x5TpGt/6K39B3U84zTjMXBwUF89913hQ/uLPo+YnwIgJIr+iHsdDrxf//3f8eKgK2trfjpp58KvY4AGD/z8/OFTwXc2Ng41kxQt9uN77//vvB+7+O8r4s896gDYNTHAfR6vdjd3Y2HDx/GN998U/j4jAif7UlgF0DJneRD2Gw245tvvonV1dVYWlqKmZmZaDQa0e12Y29vL5rNZjx9+rTQdPEhWwlHu3v37tAuqHL16tU4d+7c7/6tWq3G/Px84X3zP/zwQ7x48SLW19fj7Nmzv4uJdrsdz58/j/v3759oa/c475tRzgDMzs4WWm6YAXDU+6jT6cTe3t5ALpxUq9UKzz4yPgRAyS0tLUWtVis8jdfpdGJjYyM2NjYiIgofU/A+09PTthKOYZgrhQ8ddLayslI4ACLezAptbW1FtVqN6enpqNVq0W63C+0Xf1elUokLFy4c+bgiv7eiK+53jeMugNOcXbh48WJpLkTGh9kFUHK1Wi0uXbo0sOcb1Mo/ImJ1dbVUl57N5NKlSwP5Aj+cLXr9+vVAVv4Rbw7UazQaRz6u3xVevV4f2FHrRQNgUFvgo7a2tjbqITAAAmACrK6ujnoI/6NarcYnn3wy6mHwAbVabWz/Puvr60c+ptVq9X1K3aC2/iMiGo1God02h/vgy+xwdyHlJwAmwMzMzP/s5x215eVl5/+PubW1tbGboVlYWDjWbqNR7v8/6fON+kDAk6hUKnH16tVRD4MBEQAT4vr162OzT65er8dnn3026mFwhEaj0fe1IIapWq3GjRs3jvXYUZ4BcNLnK/MMwNWrVx3XM0EEwISYmZmJ69evj3oYERHxhz/8wcV/SuLKlSuxtLQ06mFExJuInZubO9ZjyxwAZZ0BWFpaisuXL496GAyQAJggly5diuXl5ZGOYXV1Nc6fPz/SMdCfW7dujTzYlpeX+zomocy7AMo4AzA3Nxe3bt0a9TAYMAEwYW7evDmy4wGWl5fj2rVrI3ltipuamoo//vGPxzryfhjOnz8fN2/e7GuZfreiK5XK2ARA2WYALly4EH/5y18c0zOBBMCEqdVq8cUXX5z6Ed6XL1+O27dvD+2CNgzX3Nxc/PWvfz31/bvr6+vxxRdf9HX8Srfb7fuUw0ajMfADHosGwOGpk+OuUqnE5cuX486dO2NzfBGD5UJAE6hSqcTnn38es7Oz8fPPPxe+SNBx1Ov1uHbt2kCvRcBoTE9Px5///Of48ccfC98h7rhO8r4Z5QWAfqter8fU1FS0Wq2+l93d3T3yXgejUqlU4uLFi3HlypWxHSODIQAm2NraWqysrMTGxkY8evRooBf5qdVqsba2Fuvr624JOkGq1WrcvHkz1tfX45dffolnz54N9Pnr9Xqsra3F2tpa4ffNOOz//+3zFgmAZrM5dqfuHp5OvLa2ZsWfhG/uCTc1NRXXrl2L9fX1ePjwYWxvb8erV68KPdfh3QfPnTsXq6ur9glOsNnZ2bh9+3Y0m8149OhRbG9vFz54rVKpxNmzZ+P8+fPxySefnDgYx+EMgEOzs7Oxs7PT93KjOBCwUqm8vRri4exFo9GIhYWFWFxcHPmBoJy+ytdff13+61LSl3a7Hdvb2/Hy5cvY39+PdrsdrVYrWq1WdDqd331BHH5JLC4uxuLioq39xPb39+Ply5exs7MTBwcHb98z7Xb77fvm8D1Tr9fjzJkzsbS0FAsLC/YhwxgSAACQkEO2ASAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJ/T8r8gNH5WpNaAAAAABJRU5ErkJggg==" + }, + { + "nodeTitle": "Load Image", + "nodeInput": "image", + "type": "image", + "label": "New Face Image", + "defaultValue": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAZMElEQVR4nO3daXMTZ9aA4aPFFl6wDdgQ2xAChAEya/7/T0hmMkvlTZFJUiSYxYDBOCAv2t4PlKmEAWy1JUutc11fZoqopce2pL776a3y9ddf9wIASKU66gEAAKdPAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACdVHPQCgHA4ODmJ7ezt2dnbi4OAgWq1WtNvtaLVa0el0YmpqKur1ekxNTcXU1FScOXMmFhcXY3FxMapV2xowbgRAid27dy82Njb6Xm59fT2uXbtW6DW//vrr2Nvb62uZa9euxfr6+pGP++qrr2J/f7/QuD6kVqu9XSkd/u/8/HwsLi7G/Pz8QF+r6Pi//PLLmJubG+hYBqXZbMajR49ie3s7dnd3P/rYg4ODODg4+N2/bWxsRKVSiYWFhTh37lysrq5GrVYb5pA/6OnTp3H37t2BPueNGzdidXV1oM85jM9BRESlUol6vf67z0Oj0XgbaVNTUwN/TcabAGCidTqd6HQ6v/tCffr0aURE1Ov1WFhYiIsXL8by8vKohjiWdnd345dffnn7uzqJXq8XL1++jJcvX8bGxkasr6/H2traqYfAUQFTRLPZHPhzDkuv14tWqxWtVut3v4tHjx5FRMTs7OzbSDtz5syohskpEgCk1W634/nz5/H8+fOYnZ2NK1euxMrKyqiHNVLdbjd++umnePz48VCev91ux88//xwPHjyI69evx8WLF4fyOu8zjJX1MKJiVJrNZjSbzXj48GFcvHgxrly5IgQmnB1zEG++/O7evRv/+Mc/JupLvR+tViv+85//DG3l/1vtdju+//77uHfv3tBf69AwAqBMMwDH1ev1YnNzM/7+97/Hzz//POrhMEQCAH6j2WzGv/71r9je3h71UE7V69ev45///Gf8+uuvp/q6Gxsb8d1330W32x3q6/R6vb6PXTmOg4ODaLfbA3/ecdDr9eL+/fun8vdhNAQAvKPdbse3334bm5ubox7KqWi1WvHtt98O5cCz49ja2or//ve/Q32N/f39oa3EJn3GaGtrK/79739Hq9Ua9VAYMAEA79Hr9eKHH3449S3iUfj+++//5+j90/b06dOhBtcwp+oncTfAu169ejXwMygYPQEAH9Dr9eLu3bsTO8Ub8WYK/sWLF6MeRkRE/Pjjj0NbmQ5zJT3pMwCHtre348GDB6MeBgMkAOAj9vb24scffxz1MIZif39/rA7y6na7Q/tdD3MlnWEG4NC9e/fi1atXox4GAyIA4AhPnz6N169fj3oYA/fo0aPo9XqjHsbvvHz5cigrGDMAg9Hr9cYqGjkZAQDH8PDhw1EPYaA6nc6pnO5XxDCmmYe5kt7b20t1lPyLFy9SRc8kEwBwDE+fPp2oo6A3NzcHcmxDtVqNRqMRs7OzMT09PYCRRTx79mygZyQM+1S9YZ1iOM4Orx5IubkSIKU3MzPzwZvNHN605qS63W5sbm7G5cuXT/xc4+DZs2cnWv78+fOxtrYWCwsLv/vdt1qteP78edy/f7/wSrHX68XW1lasra2daIyHTmNrtdlsxuzs7NBf52M+9jmIeDPrM6hQ2dzcjKtXr47svg4MhgCg9G7fvv3Rm+l0Op3Y3d2NJ0+enGi/9/b29kQEQLfbPdF+9o/dAGdqaiouXboUKysrcffu3dja2ir0Gjs7OwMLgNM4SG8cDgQ86nMQ8eZvv7u7Gy9evIgHDx4UjuNOpxOvXr2KxcXFQsszHuwCYOLVarWYn5+P69evx5/+9KfCt6adlKOfX716VXif9fr6+rHufletVuPWrVsxMzNT6HV2dnYKLfc+p7FyLss+8Wq1GnNzc3H58uX429/+dqJZiwzXyJh0AoBUFhcX4+rVq4WWbbfbY7Gld1JFV661Wi0+/fTTYz++Wq3GZ599Vui1Dg4OBjZdfVq7AMqm0WjEnTt3CgexACg/AUA6q6urUa8X2/s1CV96RX+GlZWVvvf5nj9/vvB95gc149LvyrnIHfDKMgPwrpmZmbh06VKhZQc5S8NoCADSqVarce7cuULLlvWL/reK7vddWFjoe5lKpVJouYgYyOWJO51O389z/vz5vl+n2+2W9kyAordkbrVaE32VzAwEACkddbDUh0zCF17Rn6Ho72yUv+t+t/6r1WosLS0Veq2yxuH8/HxUKpVCy07C5yEzAUBKRaZ5IybjC6/oz1B0t0nR5Qbxu+53pXzmzJnCBy6W8TiAiDezNEV/5kn4PGQmAEhplCulUSv6MxQ953uUv+t+V8ozMzNx5syZQlvEZZ0BiMj9echMAJBS0SOfy/6F1+12C58CWHSaeJTTy/2ulGdmZqJSqRSaISrrDEBE8bgr++chOwEAfeh0OqMewomM281/PmYQYy0yA/Db/x3ma42TopGW6R4Ik0gAABOpyDX6TxIA7XZ7ou4XweQTAMBE2t3d7XsW4SQBEFHuWQDyEQDAROp3ZVyv199etKhoAJT5QEDyEQDARCpyAOD7/n8/zABQJgIAmEhFDwCMiJieni50ZLwAoEwEADCRTjIDEJHrngDkJACAiXTSACiyG2B/f7/0p4qShwAAJk6RFfG7W/wOBGTSCQBg4hTZFz+IGYCirw2jIACAidPvVvj7DvozA8CkEwDAxDnJGQAf+7dhvDaMigCAPhS9aQqnaxABUK/XC90lzwwAZSEASKnoTUyK3jaV03XSMwCO+vejXrtMN10iLwFASkVvYyoAxl+Rm/IMMgCK3IQIRkEAkNL+/n6h5QTA+BvEGQBH/fswxgCnTQCQ0qtXrwotJwDGX78r30ql8sGr/gkAJpkAIJ1utxsvXrwotGzRFQKnp9/9/41GIyqVynv/m1MBmWQCgHQeP35c+BiAs2fPDng0DNogzgA4zn8b5BhgFAQAqezs7MS9e/cKLVur1WJ2dnawA2LgBnUGQEREtVqNRqMx9DHAKNihycTrdruxt7cXT548iYcPHxY+BdDW//g7/Fv346it/JmZmb4PGu10OrG/v18oHuC0CABK7+7du1Gtvn8yq9VqFT7i/11LS0sDeR6GZ5BnABwqclvgiDezAAKAcSYAKL3T2N9arVbj0qVLQ38dTqbI1PtxZgCKaDabopGx5hgAOIbl5eWYmpoa9TA4Qr8xeJx9/A4EZFIJADiGtbW1UQ+BYxjkAYD9PGYQY4HTJgDgCMvLyzE/Pz/qYXAMgzwF8NCZM2c+eJ2AQY4FTpsAgI9oNBrx+eefj3oYHEOv1xvKDMDHrhT4Ma1Wq/D1JuA0CAD4gEqlErdu3XL535LY29vr+y58x53edxwAk0gAwHtUKpW4ceNGLCwsjHooHNMwzgDo93HvchwA48ymDbyjXq/H7du3ncJVMkW2to87tW8GgEkkAOA3Zmdn486dO276U0L9rmzr9fqxT+0sejEgAcA4EwAQb7bwrly5EisrK4WO+Gb0hnEAYJHH/pZdAIwzAUBatVotFhcXY2VlJVZWVkY9HE5omAHQaDSiWq32fR+Jvb296Ha7H7xUNYySAGCi1Wq1t1O99Xo96vV6zM/Px9LSUszNzdnanxAHBwd9n3LX71b9zMxMvH79uq9lIt6EydzcXN/LwbAJAErvyy+/9AWb3DBuAvS+xxcJgGaz6f3JWDIvBZTeME8BLPr4Q44DYFwJAKD0hnkK4CGnAjJpBABQev1uZU9PT0etVutrGTMATBoBAJTeMG4CNIhlIt4EQL+XKIbT4CBAoNQ6nU4cHBz0tczU1FShLfNarRadTqevZbrdbuzv7xe+mBAMiwAASq3IPvZnz57Fs2fPhjCa92s2mwKAsWMXAFBqZTjIrgxjJB8BAJRaGQ6yK8MYyUcAAKVWhq3rMoyRfAQAUGpl2LouwxjJRwAApdXr9WJvb2/UwzhSu93u+0wFGDYBUGJuZEO/yvSeOc5Yy3SO/TjvBij6O3SXw3Lz1yuxfq9kNir1urNNx0W1Wi0cAUVXEkWXO877ZpxXqu8a590A/V7b4JDPdrkJgBIr+uHr97apJ13Wl8R4mZqaKrRc0ZVE0ffbcd4347xSfdc4x8ow/0aMLwFQYqcdAL1er9BKwJfEeDnt980wVy7jvFJ917jGykmOo/DZLjcBUGJFP3xF7mke8ebLtsh0ri+J8XLa75uiy01aAIzrWF+/fh3dbrfQsj7b5SYASqzoVO7e3l7s7+/3vdzOzk6h1ys6Toaj6N+jyN+/1+vFr7/+Wuj1jjPOcd2qfp+Dg4MT7X4bls3NzULLTU1NCYCS89crsdnZ2ahWq4XqfXNzMz799NO+lnn8+HHfrzM9PR3T09N9L8fwnD17Nra2tvpe7tmzZ3H9+vW+jvx+8eJF4dPfzp49+9H/vre31/d7/8KFC3Hnzp1C4/mtr776qlBE7+7uHvlznaa9vb148uRJoWXH6eegGDMAJVapVGJ+fr7Qsg8ePOhrv9/jx48LTeX6khg/CwsLhZZrt9vxyy+/HPvx3W437t27V+i1pqenj7x5TpGt/6K39B3U84zTjMXBwUF89913hQ/uLPo+YnwIgJIr+iHsdDrxf//3f8eKgK2trfjpp58KvY4AGD/z8/OFTwXc2Ng41kxQt9uN77//vvB+7+O8r4s896gDYNTHAfR6vdjd3Y2HDx/GN998U/j4jAif7UlgF0DJneRD2Gw245tvvonV1dVYWlqKmZmZaDQa0e12Y29vL5rNZjx9+rTQdPEhWwlHu3v37tAuqHL16tU4d+7c7/6tWq3G/Px84X3zP/zwQ7x48SLW19fj7Nmzv4uJdrsdz58/j/v3759oa/c475tRzgDMzs4WWm6YAXDU+6jT6cTe3t5ALpxUq9UKzz4yPgRAyS0tLUWtVis8jdfpdGJjYyM2NjYiIgofU/A+09PTthKOYZgrhQ8ddLayslI4ACLezAptbW1FtVqN6enpqNVq0W63C+0Xf1elUokLFy4c+bgiv7eiK+53jeMugNOcXbh48WJpLkTGh9kFUHK1Wi0uXbo0sOcb1Mo/ImJ1dbVUl57N5NKlSwP5Aj+cLXr9+vVAVv4Rbw7UazQaRz6u3xVevV4f2FHrRQNgUFvgo7a2tjbqITAAAmACrK6ujnoI/6NarcYnn3wy6mHwAbVabWz/Puvr60c+ptVq9X1K3aC2/iMiGo1God02h/vgy+xwdyHlJwAmwMzMzP/s5x215eVl5/+PubW1tbGboVlYWDjWbqNR7v8/6fON+kDAk6hUKnH16tVRD4MBEQAT4vr162OzT65er8dnn3026mFwhEaj0fe1IIapWq3GjRs3jvXYUZ4BcNLnK/MMwNWrVx3XM0EEwISYmZmJ69evj3oYERHxhz/8wcV/SuLKlSuxtLQ06mFExJuInZubO9ZjyxwAZZ0BWFpaisuXL496GAyQAJggly5diuXl5ZGOYXV1Nc6fPz/SMdCfW7dujTzYlpeX+zomocy7AMo4AzA3Nxe3bt0a9TAYMAEwYW7evDmy4wGWl5fj2rVrI3ltipuamoo//vGPxzryfhjOnz8fN2/e7GuZfreiK5XK2ARA2WYALly4EH/5y18c0zOBBMCEqdVq8cUXX5z6Ed6XL1+O27dvD+2CNgzX3Nxc/PWvfz31/bvr6+vxxRdf9HX8Srfb7fuUw0ajMfADHosGwOGpk+OuUqnE5cuX486dO2NzfBGD5UJAE6hSqcTnn38es7Oz8fPPPxe+SNBx1Ov1uHbt2kCvRcBoTE9Px5///Of48ccfC98h7rhO8r4Z5QWAfqter8fU1FS0Wq2+l93d3T3yXgejUqlU4uLFi3HlypWxHSODIQAm2NraWqysrMTGxkY8evRooBf5qdVqsba2Fuvr624JOkGq1WrcvHkz1tfX45dffolnz54N9Pnr9Xqsra3F2tpa4ffNOOz//+3zFgmAZrM5dqfuHp5OvLa2ZsWfhG/uCTc1NRXXrl2L9fX1ePjwYWxvb8erV68KPdfh3QfPnTsXq6ur9glOsNnZ2bh9+3Y0m8149OhRbG9vFz54rVKpxNmzZ+P8+fPxySefnDgYx+EMgEOzs7Oxs7PT93KjOBCwUqm8vRri4exFo9GIhYWFWFxcHPmBoJy+ytdff13+61LSl3a7Hdvb2/Hy5cvY39+PdrsdrVYrWq1WdDqd331BHH5JLC4uxuLioq39xPb39+Ply5exs7MTBwcHb98z7Xb77fvm8D1Tr9fjzJkzsbS0FAsLC/YhwxgSAACQkEO2ASAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJCQAASEgAAEBCAgAAEhIAAJCQAACAhAQAACQkAAAgIQEAAAkJAABISAAAQEICAAASEgAAkJAAAICEBAAAJCQAACAhAQAACQkAAEhIAABAQgIAABISAACQkAAAgIQEAAAkJAAAICEBAAAJ/T8r8gNH5WpNaAAAAABJRU5ErkJggg==" + }, + + { + "nodeTitle": "Save Image", + "nodeInput": "filename_prefix", + "type": "string", + "label": "Filename Prefix", + "defaultValue": "ComfyUI" + } + ], + "outputs": [ + { + "name": "output_image", + "type": "image" + } + ], + "defaultSettings": { + "resolution": "896x896", + "inferenceSteps": 4 + }, + "displayedSettings": [], + "modifiableSettings": ["inferenceSteps", "seed", "batchSize", "imagePreview", "resolution"], + "comfyUiApiWorkflow": { + "9": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": ["25", 0] + }, + "class_type": "SaveImage", + "_meta": { + "title": "Save Image" + } + }, + "15": { + "inputs": { + "image": "07b33c98c19fcb8bbf124d3eefa72c18ad8f5c3cfbe3b3661fbf85cecffb7668.png", + "upload": "image" + }, + "class_type": "LoadImage", + "_meta": { + "title": "Load Image" + } + }, + "25": { + "inputs": { + "enabled": true, + "swap_model": "Aitrepreneur---insightface---inswapper_128.onnx", + "facedetection": "retinaface_resnet50", + "face_restore_model": "gmk123---GFPGAN---GFPGANv1.4.pth", + "face_restore_visibility": 1, + "codeformer_weight": 0.5, + "detect_gender_input": "no", + "detect_gender_source": "no", + "input_faces_index": "0", + "source_faces_index": "0", + "console_log_level": 1, + "input_image": ["26", 0], + "source_image": ["15", 0] + }, + "class_type": "ReActorFaceSwap", + "_meta": { + "title": "ReActor 🌌 Fast Face Swap" + } + }, + "26": { + "inputs": { + "image": "ComfyUI_00007_.png", + "upload": "image" + }, + "class_type": "LoadImage", + "_meta": { + "title": "Load Image2" + } + } + } +} diff --git a/WebUI/external/workflows/FaceSwapHD.json b/WebUI/external/workflows/FaceSwapHD.json index f06f99b4..e8323d7f 100644 --- a/WebUI/external/workflows/FaceSwapHD.json +++ b/WebUI/external/workflows/FaceSwapHD.json @@ -6,9 +6,7 @@ "https://github.com/Gourieff/Assets/raw/main/Insightface/insightface-0.7.3-cp311-cp311-win_amd64.whl", "onnxruntime" ], - "customNodes": [ - "Gourieff/comfyui-reactor@ee13ff754f35d2af284f0cf9d81ccce4defe1e5e" - ], + "customNodes": ["Gourieff/comfyui-reactor@ee13ff754f35d2af284f0cf9d81ccce4defe1e5e"], "requiredModels": [ { "type": "defaultCheckpoint", @@ -41,14 +39,8 @@ } ] }, - "tags": [ - "SDXL", - "Fast-LCM", - "non-com" - ], - "requirements": [ - "high-vram" - ], + "tags": ["SDXL", "Fast-LCM", "non-com"], + "requirements": ["high-vram"], "inputs": [ { "nodeTitle": "ReActor 🌌 Fast Face Swap", @@ -83,13 +75,7 @@ "inferenceSteps": 4 }, "displayedSettings": [], - "modifiableSettings": [ - "inferenceSteps", - "seed", - "batchSize", - "imagePreview", - "resolution" - ], + "modifiableSettings": ["inferenceSteps", "seed", "batchSize", "imagePreview", "resolution"], "comfyUiApiWorkflow": { "3": { "inputs": { @@ -99,22 +85,10 @@ "sampler_name": "lcm", "scheduler": "simple", "denoise": 1, - "model": [ - "19", - 0 - ], - "positive": [ - "6", - 0 - ], - "negative": [ - "7", - 0 - ], - "latent_image": [ - "5", - 0 - ] + "model": ["19", 0], + "positive": ["6", 0], + "negative": ["7", 0], + "latent_image": ["5", 0] }, "class_type": "KSampler", "_meta": { @@ -135,10 +109,7 @@ "6": { "inputs": { "text": "a photograph of a dudde", - "clip": [ - "19", - 1 - ] + "clip": ["19", 1] }, "class_type": "CLIPTextEncode", "_meta": { @@ -148,10 +119,7 @@ "7": { "inputs": { "text": "nsfw", - "clip": [ - "19", - 1 - ] + "clip": ["19", 1] }, "class_type": "CLIPTextEncode", "_meta": { @@ -160,14 +128,8 @@ }, "8": { "inputs": { - "samples": [ - "3", - 0 - ], - "vae": [ - "22", - 0 - ] + "samples": ["3", 0], + "vae": ["22", 0] }, "class_type": "VAEDecode", "_meta": { @@ -177,10 +139,7 @@ "9": { "inputs": { "filename_prefix": "ComfyUI", - "images": [ - "25", - 0 - ] + "images": ["25", 0] }, "class_type": "SaveImage", "_meta": { @@ -202,14 +161,8 @@ "lora_name": "latent-consistency---lcm-lora-sdxl\\pytorch_lora_weights.safetensors", "strength_model": 1, "strength_clip": 1, - "model": [ - "23", - 0 - ], - "clip": [ - "24", - 0 - ] + "model": ["23", 0], + "clip": ["24", 0] }, "class_type": "LoraLoader", "_meta": { @@ -259,14 +212,8 @@ "input_faces_index": "0", "source_faces_index": "0", "console_log_level": 1, - "input_image": [ - "8", - 0 - ], - "source_image": [ - "15", - 0 - ] + "input_image": ["8", 0], + "source_image": ["15", 0] }, "class_type": "ReActorFaceSwap", "_meta": { diff --git a/WebUI/external/workflows/Line2ImageHD-Fast.json b/WebUI/external/workflows/Line2ImageHD-Fast.json index b0b12cb9..fb66cfa2 100644 --- a/WebUI/external/workflows/Line2ImageHD-Fast.json +++ b/WebUI/external/workflows/Line2ImageHD-Fast.json @@ -3,8 +3,7 @@ "displayPriority": 200, "backend": "comfyui", "comfyUIRequirements": { - "customNodes": [ - ], + "customNodes": [], "requiredModels": [ { "type": "defaultCheckpoint", @@ -32,13 +31,8 @@ } ] }, - "tags": [ - "SDXL", - "Fast-LCM" - ], - "requirements": [ - "high-vram" - ], + "tags": ["SDXL", "Fast-LCM"], + "requirements": ["high-vram"], "inputs": [ { "nodeTitle": "Canny", @@ -51,15 +45,15 @@ "max": 1 }, { - "nodeTitle": "Canny", - "nodeInput": "high_threshold", - "type": "number", - "label": "Canny - High Threshold", - "defaultValue": 0.6, - "step": 0.1, - "min": 0, - "max": 1 - }, + "nodeTitle": "Canny", + "nodeInput": "high_threshold", + "type": "number", + "label": "Canny - High Threshold", + "defaultValue": 0.6, + "step": 0.1, + "min": 0, + "max": 1 + }, { "nodeTitle": "Apply ControlNet (OLD)", "nodeInput": "strength", @@ -89,13 +83,7 @@ "inferenceSteps": 4 }, "displayedSettings": [], - "modifiableSettings": [ - "inferenceSteps", - "seed", - "batchSize", - "imagePreview", - "resolution" - ], + "modifiableSettings": ["inferenceSteps", "seed", "batchSize", "imagePreview", "resolution"], "comfyUiApiWorkflow": { "3": { "inputs": { @@ -105,22 +93,10 @@ "sampler_name": "lcm", "scheduler": "simple", "denoise": 1, - "model": [ - "19", - 0 - ], - "positive": [ - "18", - 0 - ], - "negative": [ - "7", - 0 - ], - "latent_image": [ - "5", - 0 - ] + "model": ["19", 0], + "positive": ["18", 0], + "negative": ["7", 0], + "latent_image": ["5", 0] }, "class_type": "KSampler", "_meta": { @@ -141,10 +117,7 @@ "6": { "inputs": { "text": "modern apartment building, beach town, sunset, lawn, tropical", - "clip": [ - "19", - 1 - ] + "clip": ["19", 1] }, "class_type": "CLIPTextEncode", "_meta": { @@ -154,10 +127,7 @@ "7": { "inputs": { "text": "cartoon, painting, illustration, nsfw, text, watermark", - "clip": [ - "19", - 1 - ] + "clip": ["19", 1] }, "class_type": "CLIPTextEncode", "_meta": { @@ -166,14 +136,8 @@ }, "8": { "inputs": { - "samples": [ - "3", - 0 - ], - "vae": [ - "22", - 0 - ] + "samples": ["3", 0], + "vae": ["22", 0] }, "class_type": "VAEDecode", "_meta": { @@ -183,10 +147,7 @@ "9": { "inputs": { "filename_prefix": "ComfyUI", - "images": [ - "8", - 0 - ] + "images": ["8", 0] }, "class_type": "SaveImage", "_meta": { @@ -205,10 +166,7 @@ }, "16": { "inputs": { - "images": [ - "20", - 0 - ] + "images": ["20", 0] }, "class_type": "PreviewImage", "_meta": { @@ -227,18 +185,9 @@ "18": { "inputs": { "strength": 0.5, - "conditioning": [ - "6", - 0 - ], - "control_net": [ - "17", - 0 - ], - "image": [ - "20", - 0 - ] + "conditioning": ["6", 0], + "control_net": ["17", 0], + "image": ["20", 0] }, "class_type": "ControlNetApply", "_meta": { @@ -250,14 +199,8 @@ "lora_name": "latent-consistency---lcm-lora-sdxl\\pytorch_lora_weights.safetensors", "strength_model": 1, "strength_clip": 1, - "model": [ - "23", - 0 - ], - "clip": [ - "24", - 0 - ] + "model": ["23", 0], + "clip": ["24", 0] }, "class_type": "LoraLoader", "_meta": { @@ -268,10 +211,7 @@ "inputs": { "low_threshold": 0.2, "high_threshold": 0.8, - "image": [ - "15", - 0 - ] + "image": ["15", 0] }, "class_type": "Canny", "_meta": { diff --git a/WebUI/external/workflows/Line2ImageHD-Quality.json b/WebUI/external/workflows/Line2ImageHD-Quality.json index cda98303..14a97822 100644 --- a/WebUI/external/workflows/Line2ImageHD-Quality.json +++ b/WebUI/external/workflows/Line2ImageHD-Quality.json @@ -3,8 +3,7 @@ "displayPriority": 150, "backend": "comfyui", "comfyUIRequirements": { - "customNodes": [ - ], + "customNodes": [], "requiredModels": [ { "type": "defaultCheckpoint", @@ -28,13 +27,8 @@ } ] }, - "tags": [ - "SDXL", - "20-Steps" - ], - "requirements": [ - "high-vram" - ], + "tags": ["SDXL", "20-Steps"], + "requirements": ["high-vram"], "inputs": [ { "nodeTitle": "Canny", @@ -47,15 +41,15 @@ "max": 1 }, { - "nodeTitle": "Canny", - "nodeInput": "high_threshold", - "type": "number", - "label": "Canny - High Threshold", - "defaultValue": 0.6, - "step": 0.1, - "min": 0, - "max": 1 - }, + "nodeTitle": "Canny", + "nodeInput": "high_threshold", + "type": "number", + "label": "Canny - High Threshold", + "defaultValue": 0.6, + "step": 0.1, + "min": 0, + "max": 1 + }, { "nodeTitle": "Apply ControlNet (OLD)", "nodeInput": "strength", @@ -85,13 +79,7 @@ "inferenceSteps": 20 }, "displayedSettings": [], - "modifiableSettings": [ - "inferenceSteps", - "seed", - "batchSize", - "imagePreview", - "resolution" - ], + "modifiableSettings": ["inferenceSteps", "seed", "batchSize", "imagePreview", "resolution"], "comfyUiApiWorkflow": { "3": { "inputs": { @@ -101,22 +89,10 @@ "sampler_name": "heun", "scheduler": "sgm_uniform", "denoise": 1, - "model": [ - "23", - 0 - ], - "positive": [ - "18", - 0 - ], - "negative": [ - "7", - 0 - ], - "latent_image": [ - "5", - 0 - ] + "model": ["23", 0], + "positive": ["18", 0], + "negative": ["7", 0], + "latent_image": ["5", 0] }, "class_type": "KSampler" }, @@ -131,10 +107,7 @@ "6": { "inputs": { "text": "modern apartment high rise downtown New York City, highly detailed, ultra realistic, film grain, depth of field", - "clip": [ - "24", - 0 - ] + "clip": ["24", 0] }, "class_type": "CLIPTextEncode", "_meta": { @@ -144,10 +117,7 @@ "7": { "inputs": { "text": "nsfw", - "clip": [ - "24", - 0 - ] + "clip": ["24", 0] }, "class_type": "CLIPTextEncode", "_meta": { @@ -156,14 +126,8 @@ }, "8": { "inputs": { - "samples": [ - "3", - 0 - ], - "vae": [ - "22", - 0 - ] + "samples": ["3", 0], + "vae": ["22", 0] }, "class_type": "VAEDecode", "_meta": { @@ -173,10 +137,7 @@ "9": { "inputs": { "filename_prefix": "ComfyUI", - "images": [ - "8", - 0 - ] + "images": ["8", 0] }, "class_type": "SaveImage", "_meta": { @@ -195,10 +156,7 @@ }, "16": { "inputs": { - "images": [ - "20", - 0 - ] + "images": ["20", 0] }, "class_type": "PreviewImage", "_meta": { @@ -217,18 +175,9 @@ "18": { "inputs": { "strength": 0.9, - "conditioning": [ - "6", - 0 - ], - "control_net": [ - "17", - 0 - ], - "image": [ - "20", - 0 - ] + "conditioning": ["6", 0], + "control_net": ["17", 0], + "image": ["20", 0] }, "class_type": "ControlNetApply", "_meta": { @@ -239,10 +188,7 @@ "inputs": { "low_threshold": 0.2, "high_threshold": 0.8, - "image": [ - "15", - 0 - ] + "image": ["15", 0] }, "class_type": "Canny", "_meta": { diff --git a/WebUI/external/workflows/fluxQ4.json b/WebUI/external/workflows/fluxQ4.json index 7a2eb4cb..20e98520 100644 --- a/WebUI/external/workflows/fluxQ4.json +++ b/WebUI/external/workflows/fluxQ4.json @@ -1,230 +1,177 @@ { - "name": "Flux.1-Schnell Med Quality", - "displayPriority": 500, - "tags": [ - "Q4", - "Fast" - ], - "backend": "comfyui", - "comfyUIRequirements": { - "customNodes": [ - "city96/ComfyUI-GGUF@65a7c895bb0ac9547ba2f89d55fbdb609aa2bfe7" - ], - "requiredModels": [ - { - "type": "unet", - "model": "city96/FLUX.1-schnell-gguf/flux1-schnell-Q4_K_S.gguf" - }, - { - "type": "clip", - "model": "city96/t5-v1_1-xxl-encoder-gguf/t5-v1_1-xxl-encoder-Q3_K_M.gguf" - }, - { - "type": "clip", - "model": "comfyanonymous/flux_text_encoders/clip_l.safetensors" - }, - { - "type": "vae", - "model": "black-forest-labs/FLUX.1-schnell/ae.safetensors" - } - ] - }, + "name": "Flux.1-Schnell Med Quality", + "displayPriority": 500, + "tags": ["Q4", "Fast"], + "backend": "comfyui", + "comfyUIRequirements": { + "customNodes": ["city96/ComfyUI-GGUF@65a7c895bb0ac9547ba2f89d55fbdb609aa2bfe7"], + "requiredModels": [ + { + "type": "unet", + "model": "city96/FLUX.1-schnell-gguf/flux1-schnell-Q4_K_S.gguf" + }, + { + "type": "clip", + "model": "city96/t5-v1_1-xxl-encoder-gguf/t5-v1_1-xxl-encoder-Q3_K_M.gguf" + }, + { + "type": "clip", + "model": "comfyanonymous/flux_text_encoders/clip_l.safetensors" + }, + { + "type": "vae", + "model": "black-forest-labs/FLUX.1-schnell/ae.safetensors" + } + ] + }, - "requirements": [ - "high-vram" - ], - "inputs": [], - "outputs": [ - { - "name": "output_image", - "type": "image" - } - ], - "defaultSettings": { - "resolution": "896x896", - "inferenceSteps": 4 + "requirements": ["high-vram"], + "inputs": [], + "outputs": [ + { + "name": "output_image", + "type": "image" + } + ], + "defaultSettings": { + "resolution": "896x896", + "inferenceSteps": 4 + }, + "displayedSettings": [], + "modifiableSettings": ["inferenceSteps", "seed", "batchSize", "imagePreview", "resolution"], + "comfyUiApiWorkflow": { + "167": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": ["179", 0] + }, + "class_type": "SaveImage", + "_meta": { + "title": "Save Image" + } + }, + "169": { + "inputs": { + "noise": ["184", 0], + "guider": ["178", 0], + "sampler": ["185", 0], + "sigmas": ["181", 0], + "latent_image": ["180", 0] + }, + "class_type": "SamplerCustomAdvanced", + "_meta": { + "title": "SamplerCustomAdvanced" + } + }, + "170": { + "inputs": { + "unet_name": "city96---FLUX.1-schnell-gguf\\flux1-schnell-Q4_K_S.gguf" + }, + "class_type": "UnetLoaderGGUF", + "_meta": { + "title": "Unet Loader (GGUF)" + } + }, + "171": { + "inputs": { + "vae_name": "black-forest-labs---FLUX.1-schnell\\ae.safetensors" + }, + "class_type": "VAELoader", + "_meta": { + "title": "Load VAE" + } + }, + "174": { + "inputs": { + "guidance": 1, + "conditioning": ["177", 0] + }, + "class_type": "FluxGuidance", + "_meta": { + "title": "FluxGuidance" + } + }, + "177": { + "inputs": { + "text": "A cool llama wearing a pair of sunglasses, holding a blue and purple neon sign that says \"Lunar Lake\" in front, vibrant colors, blurry cyberpunk gaming background.", + "clip": ["188", 0] + }, + "class_type": "CLIPTextEncode", + "_meta": { + "title": "prompt" + } + }, + "178": { + "inputs": { + "model": ["170", 0], + "conditioning": ["174", 0] + }, + "class_type": "BasicGuider", + "_meta": { + "title": "BasicGuider" + } + }, + "179": { + "inputs": { + "samples": ["169", 1], + "vae": ["171", 0] + }, + "class_type": "VAEDecode", + "_meta": { + "title": "VAE Decode" + } + }, + "180": { + "inputs": { + "width": 768, + "height": 768, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage", + "_meta": { + "title": "Empty Latent Image" + } + }, + "181": { + "inputs": { + "scheduler": "normal", + "steps": 4, + "denoise": 1, + "model": ["170", 0] + }, + "class_type": "BasicScheduler", + "_meta": { + "title": "BasicScheduler" + } + }, + "184": { + "inputs": { + "noise_seed": 508274201813129 + }, + "class_type": "RandomNoise", + "_meta": { + "title": "RandomNoise" + } + }, + "185": { + "inputs": { + "sampler_name": "euler" + }, + "class_type": "KSamplerSelect", + "_meta": { + "title": "KSamplerSelect" + } }, - "displayedSettings": [ - ], - "modifiableSettings": [ - "inferenceSteps", - "seed", - "batchSize", - "imagePreview", - "resolution" - ], - "comfyUiApiWorkflow": { - "167": { - "inputs": { - "filename_prefix": "ComfyUI", - "images": [ - "179", - 0 - ] - }, - "class_type": "SaveImage", - "_meta": { - "title": "Save Image" - } - }, - "169": { - "inputs": { - "noise": [ - "184", - 0 - ], - "guider": [ - "178", - 0 - ], - "sampler": [ - "185", - 0 - ], - "sigmas": [ - "181", - 0 - ], - "latent_image": [ - "180", - 0 - ] - }, - "class_type": "SamplerCustomAdvanced", - "_meta": { - "title": "SamplerCustomAdvanced" - } - }, - "170": { - "inputs": { - "unet_name": "city96---FLUX.1-schnell-gguf\\flux1-schnell-Q4_K_S.gguf" - }, - "class_type": "UnetLoaderGGUF", - "_meta": { - "title": "Unet Loader (GGUF)" - } - }, - "171": { - "inputs": { - "vae_name": "black-forest-labs---FLUX.1-schnell\\ae.safetensors" - }, - "class_type": "VAELoader", - "_meta": { - "title": "Load VAE" - } - }, - "174": { - "inputs": { - "guidance": 1, - "conditioning": [ - "177", - 0 - ] - }, - "class_type": "FluxGuidance", - "_meta": { - "title": "FluxGuidance" - } - }, - "177": { - "inputs": { - "text": "A cool llama wearing a pair of sunglasses, holding a blue and purple neon sign that says \"Lunar Lake\" in front, vibrant colors, blurry cyberpunk gaming background.", - "clip": [ - "188", - 0 - ] - }, - "class_type": "CLIPTextEncode", - "_meta": { - "title": "prompt" - } - }, - "178": { - "inputs": { - "model": [ - "170", - 0 - ], - "conditioning": [ - "174", - 0 - ] - }, - "class_type": "BasicGuider", - "_meta": { - "title": "BasicGuider" - } - }, - "179": { - "inputs": { - "samples": [ - "169", - 1 - ], - "vae": [ - "171", - 0 - ] - }, - "class_type": "VAEDecode", - "_meta": { - "title": "VAE Decode" - } - }, - "180": { - "inputs": { - "width": 768, - "height": 768, - "batch_size": 1 - }, - "class_type": "EmptyLatentImage", - "_meta": { - "title": "Empty Latent Image" - } - }, - "181": { - "inputs": { - "scheduler": "normal", - "steps": 4, - "denoise": 1, - "model": [ - "170", - 0 - ] - }, - "class_type": "BasicScheduler", - "_meta": { - "title": "BasicScheduler" - } - }, - "184": { - "inputs": { - "noise_seed": 508274201813129 - }, - "class_type": "RandomNoise", - "_meta": { - "title": "RandomNoise" - } - }, - "185": { - "inputs": { - "sampler_name": "euler" - }, - "class_type": "KSamplerSelect", - "_meta": { - "title": "KSamplerSelect" - } - }, - "188": { - "inputs": { - "clip_name1": "city96---t5-v1_1-xxl-encoder-gguf\\t5-v1_1-xxl-encoder-Q3_K_M.gguf", - "clip_name2": "comfyanonymous---flux_text_encoders\\clip_l.safetensors", - "type": "flux" - }, - "class_type": "DualCLIPLoaderGGUF", - "_meta": { - "title": "DualCLIPLoader (GGUF)" - } - } + "188": { + "inputs": { + "clip_name1": "city96---t5-v1_1-xxl-encoder-gguf\\t5-v1_1-xxl-encoder-Q3_K_M.gguf", + "clip_name2": "comfyanonymous---flux_text_encoders\\clip_l.safetensors", + "type": "flux" + }, + "class_type": "DualCLIPLoaderGGUF", + "_meta": { + "title": "DualCLIPLoader (GGUF)" + } } -} \ No newline at end of file + } +} diff --git a/WebUI/external/workflows/fluxQ8.json b/WebUI/external/workflows/fluxQ8.json index 69e19d8b..3cde2584 100644 --- a/WebUI/external/workflows/fluxQ8.json +++ b/WebUI/external/workflows/fluxQ8.json @@ -1,229 +1,176 @@ { - "name": "Flux.1-Schnell High Quality", - "displayPriority": 450, - "backend": "comfyui", - "comfyUIRequirements": { - "customNodes": [ - "city96/ComfyUI-GGUF@65a7c895bb0ac9547ba2f89d55fbdb609aa2bfe7" - ], - "requiredModels": [ - { - "type": "unet", - "model": "city96/FLUX.1-schnell-gguf/flux1-schnell-Q8_0.gguf" - }, - { - "type": "clip", - "model": "city96/t5-v1_1-xxl-encoder-gguf/t5-v1_1-xxl-encoder-Q3_K_M.gguf" - }, - { - "type": "clip", - "model": "comfyanonymous/flux_text_encoders/clip_l.safetensors" - }, - { - "type": "vae", - "model": "black-forest-labs/FLUX.1-schnell/ae.safetensors" - } - ] + "name": "Flux.1-Schnell High Quality", + "displayPriority": 450, + "backend": "comfyui", + "comfyUIRequirements": { + "customNodes": ["city96/ComfyUI-GGUF@65a7c895bb0ac9547ba2f89d55fbdb609aa2bfe7"], + "requiredModels": [ + { + "type": "unet", + "model": "city96/FLUX.1-schnell-gguf/flux1-schnell-Q8_0.gguf" + }, + { + "type": "clip", + "model": "city96/t5-v1_1-xxl-encoder-gguf/t5-v1_1-xxl-encoder-Q3_K_M.gguf" + }, + { + "type": "clip", + "model": "comfyanonymous/flux_text_encoders/clip_l.safetensors" + }, + { + "type": "vae", + "model": "black-forest-labs/FLUX.1-schnell/ae.safetensors" + } + ] + }, + "tags": ["Q8", "Fast"], + "requirements": ["high-vram"], + "inputs": [], + "outputs": [ + { + "name": "output_image", + "type": "image" + } + ], + "defaultSettings": { + "resolution": "896x896", + "inferenceSteps": 4 + }, + "displayedSettings": [], + "modifiableSettings": ["inferenceSteps", "seed", "batchSize", "imagePreview", "resolution"], + "comfyUiApiWorkflow": { + "167": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": ["179", 0] + }, + "class_type": "SaveImage", + "_meta": { + "title": "Save Image" + } + }, + "169": { + "inputs": { + "noise": ["184", 0], + "guider": ["178", 0], + "sampler": ["185", 0], + "sigmas": ["181", 0], + "latent_image": ["180", 0] + }, + "class_type": "SamplerCustomAdvanced", + "_meta": { + "title": "SamplerCustomAdvanced" + } + }, + "170": { + "inputs": { + "unet_name": "city96---FLUX.1-schnell-gguf\\flux1-schnell-Q8_0.gguf" + }, + "class_type": "UnetLoaderGGUF", + "_meta": { + "title": "Unet Loader (GGUF)" + } + }, + "171": { + "inputs": { + "vae_name": "black-forest-labs---FLUX.1-schnell\\ae.safetensors" + }, + "class_type": "VAELoader", + "_meta": { + "title": "Load VAE" + } + }, + "174": { + "inputs": { + "guidance": 1, + "conditioning": ["177", 0] + }, + "class_type": "FluxGuidance", + "_meta": { + "title": "FluxGuidance" + } + }, + "177": { + "inputs": { + "text": "A cool llama wearing a pair of sunglasses, holding a blue and purple neon sign that says \"Lunar Lake\" in front, vibrant colors, blurry cyberpunk gaming background.", + "clip": ["188", 0] + }, + "class_type": "CLIPTextEncode", + "_meta": { + "title": "prompt" + } + }, + "178": { + "inputs": { + "model": ["170", 0], + "conditioning": ["174", 0] + }, + "class_type": "BasicGuider", + "_meta": { + "title": "BasicGuider" + } + }, + "179": { + "inputs": { + "samples": ["169", 1], + "vae": ["171", 0] + }, + "class_type": "VAEDecode", + "_meta": { + "title": "VAE Decode" + } + }, + "180": { + "inputs": { + "width": 768, + "height": 768, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage", + "_meta": { + "title": "Empty Latent Image" + } + }, + "181": { + "inputs": { + "scheduler": "normal", + "steps": 4, + "denoise": 1, + "model": ["170", 0] + }, + "class_type": "BasicScheduler", + "_meta": { + "title": "BasicScheduler" + } + }, + "184": { + "inputs": { + "noise_seed": 508274201813129 + }, + "class_type": "RandomNoise", + "_meta": { + "title": "RandomNoise" + } }, - "tags": [ - "Q8", - "Fast" - ], - "requirements": [ - "high-vram" - ], - "inputs": [], - "outputs": [ - { - "name": "output_image", - "type": "image" - } - ], - "defaultSettings": { - "resolution": "896x896", - "inferenceSteps": 4 + "185": { + "inputs": { + "sampler_name": "euler" + }, + "class_type": "KSamplerSelect", + "_meta": { + "title": "KSamplerSelect" + } }, - "displayedSettings": [ - ], - "modifiableSettings": [ - "inferenceSteps", - "seed", - "batchSize", - "imagePreview", - "resolution" - ], - "comfyUiApiWorkflow": { - "167": { - "inputs": { - "filename_prefix": "ComfyUI", - "images": [ - "179", - 0 - ] - }, - "class_type": "SaveImage", - "_meta": { - "title": "Save Image" - } - }, - "169": { - "inputs": { - "noise": [ - "184", - 0 - ], - "guider": [ - "178", - 0 - ], - "sampler": [ - "185", - 0 - ], - "sigmas": [ - "181", - 0 - ], - "latent_image": [ - "180", - 0 - ] - }, - "class_type": "SamplerCustomAdvanced", - "_meta": { - "title": "SamplerCustomAdvanced" - } - }, - "170": { - "inputs": { - "unet_name": "city96---FLUX.1-schnell-gguf\\flux1-schnell-Q8_0.gguf" - }, - "class_type": "UnetLoaderGGUF", - "_meta": { - "title": "Unet Loader (GGUF)" - } - }, - "171": { - "inputs": { - "vae_name": "black-forest-labs---FLUX.1-schnell\\ae.safetensors" - }, - "class_type": "VAELoader", - "_meta": { - "title": "Load VAE" - } - }, - "174": { - "inputs": { - "guidance": 1, - "conditioning": [ - "177", - 0 - ] - }, - "class_type": "FluxGuidance", - "_meta": { - "title": "FluxGuidance" - } - }, - "177": { - "inputs": { - "text": "A cool llama wearing a pair of sunglasses, holding a blue and purple neon sign that says \"Lunar Lake\" in front, vibrant colors, blurry cyberpunk gaming background.", - "clip": [ - "188", - 0 - ] - }, - "class_type": "CLIPTextEncode", - "_meta": { - "title": "prompt" - } - }, - "178": { - "inputs": { - "model": [ - "170", - 0 - ], - "conditioning": [ - "174", - 0 - ] - }, - "class_type": "BasicGuider", - "_meta": { - "title": "BasicGuider" - } - }, - "179": { - "inputs": { - "samples": [ - "169", - 1 - ], - "vae": [ - "171", - 0 - ] - }, - "class_type": "VAEDecode", - "_meta": { - "title": "VAE Decode" - } - }, - "180": { - "inputs": { - "width": 768, - "height": 768, - "batch_size": 1 - }, - "class_type": "EmptyLatentImage", - "_meta": { - "title": "Empty Latent Image" - } - }, - "181": { - "inputs": { - "scheduler": "normal", - "steps": 4, - "denoise": 1, - "model": [ - "170", - 0 - ] - }, - "class_type": "BasicScheduler", - "_meta": { - "title": "BasicScheduler" - } - }, - "184": { - "inputs": { - "noise_seed": 508274201813129 - }, - "class_type": "RandomNoise", - "_meta": { - "title": "RandomNoise" - } - }, - "185": { - "inputs": { - "sampler_name": "euler" - }, - "class_type": "KSamplerSelect", - "_meta": { - "title": "KSamplerSelect" - } - }, - "188": { - "inputs": { - "clip_name1": "city96---t5-v1_1-xxl-encoder-gguf\\t5-v1_1-xxl-encoder-Q3_K_M.gguf", - "clip_name2": "comfyanonymous---flux_text_encoders\\clip_l.safetensors", - "type": "flux" - }, - "class_type": "DualCLIPLoaderGGUF", - "_meta": { - "title": "DualCLIPLoader (GGUF)" - } - } + "188": { + "inputs": { + "clip_name1": "city96---t5-v1_1-xxl-encoder-gguf\\t5-v1_1-xxl-encoder-Q3_K_M.gguf", + "clip_name2": "comfyanonymous---flux_text_encoders\\clip_l.safetensors", + "type": "flux" + }, + "class_type": "DualCLIPLoaderGGUF", + "_meta": { + "title": "DualCLIPLoader (GGUF)" + } } -} \ No newline at end of file + } +} diff --git a/WebUI/index.html b/WebUI/index.html index 35fcc628..43fea109 100644 --- a/WebUI/index.html +++ b/WebUI/index.html @@ -3,7 +3,10 @@ - + AI Playground diff --git a/WebUI/package-lock.json b/WebUI/package-lock.json index 1ec407f6..c220c2a0 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 bf549c9d..df0a77d0 100644 --- a/WebUI/package.json +++ b/WebUI/package.json @@ -10,7 +10,12 @@ "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 .", + "lint:ci": "eslint .", + "format:ci": "prettier --check ." }, "dependencies": { "@radix-icons/vue": "^1.0.0", @@ -39,15 +44,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", diff --git a/WebUI/postcss.config.cjs b/WebUI/postcss.config.cjs index c321bd90..825cb186 100644 --- a/WebUI/postcss.config.cjs +++ b/WebUI/postcss.config.cjs @@ -1,8 +1,8 @@ module.exports = { plugins: { - "postcss-import":{}, - "tailwindcss/nesting":{}, + 'postcss-import': {}, + 'tailwindcss/nesting': {}, tailwindcss: {}, - autoprefixer: {} + autoprefixer: {}, }, } diff --git a/WebUI/src/App.vue b/WebUI/src/App.vue index cce9ff5d..e19d50f1 100644 --- a/WebUI/src/App.vue +++ b/WebUI/src/App.vue @@ -1,55 +1,108 @@ diff --git a/WebUI/src/assets/css/compontents.css b/WebUI/src/assets/css/compontents.css index fda89acf..d8f629b2 100644 --- a/WebUI/src/assets/css/compontents.css +++ b/WebUI/src/assets/css/compontents.css @@ -59,7 +59,7 @@ .v-slide-bar { position: absolute; border-radius: 50%; - background: url("@/assets/svg/slide-bar.svg"); + background: url('@/assets/svg/slide-bar.svg'); width: 13px; height: 13px; transition: left ease 0.1s; @@ -116,7 +116,7 @@ &.v-radio-checked { border: none; - background: url("@/assets/svg/radio-checked.svg"); + background: url('@/assets/svg/radio-checked.svg'); } } @@ -235,7 +235,7 @@ mask-size: 100% 100%; -webkit-mask-image: var(--svg); mask-image: var(--svg); - --svg: url("@/assets/svg/toggle.svg"); + --svg: url('@/assets/svg/toggle.svg'); &.revese { transform: rotate(180deg); @@ -393,7 +393,7 @@ .v-slide-bar { position: absolute; border-radius: 50%; - background: url("@/assets/svg/slide-bar.svg"); + background: url('@/assets/svg/slide-bar.svg'); width: 13px; height: 13px; transition: bottom ease 0.1s; @@ -437,14 +437,12 @@ justify-content: center; align-items: center; - &::after { - content: " "; + content: ' '; width: 14px; height: 14px; - background: url("@/assets/svg/right.svg") 0px 0px no-repeat; + background: url('@/assets/svg/right.svg') 0px 0px no-repeat; background-size: contain; - } } } @@ -461,14 +459,12 @@ align-items: center; margin: 0 auto; - &::after { - content: " "; + content: ' '; width: 14px; height: 14px; - background: url("@/assets/svg/right.svg") 0px 0px no-repeat; + background: url('@/assets/svg/right.svg') 0px 0px no-repeat; background-size: contain; - } } } diff --git a/WebUI/src/assets/css/index.css b/WebUI/src/assets/css/index.css index c70f0c64..7e17112a 100644 --- a/WebUI/src/assets/css/index.css +++ b/WebUI/src/assets/css/index.css @@ -1,78 +1,78 @@ -@import "tailwindcss/base"; -@import "tailwindcss/components"; -@import "tailwindcss/utilities"; -@import "./theme.css"; -@import "./main.css"; -@import "./pc.css"; -@import "./compontents.css"; -@import "./svg.css"; - +@import 'tailwindcss/base'; +@import 'tailwindcss/components'; +@import 'tailwindcss/utilities'; +@import './theme.css'; +@import './main.css'; +@import './pc.css'; +@import './compontents.css'; +@import './svg.css'; + @layer base { :root { --background: 0 0% 100%; --foreground: 0 0% 3.9%; - + --muted: 0 0% 96.1%; --muted-foreground: 0 0% 45.1%; - + --popover: 0 0% 100%; --popover-foreground: 0 0% 3.9%; - + --card: 0 0% 100%; --card-foreground: 0 0% 3.9%; - + --border: 0 0% 89.8%; --input: 0 0% 89.8%; - + --primary: 0 0% 9%; --primary-foreground: 0 0% 98%; - + --secondary: 0 0% 96.1%; --secondary-foreground: 0 0% 9%; - + --accent: 0 0% 96.1%; --accent-foreground: 0 0% 9%; - + --destructive: 0 84.2% 60.2%; --destructive-foreground: 0 0% 98%; - + --ring: 0 0% 3.9%; - + --radius: 0.5rem; } - + .dark { --background: 0 0% 3.9%; --foreground: 0 0% 98%; - + --muted: 0 0% 14.9%; --muted-foreground: 0 0% 63.9%; - + --popover: 0 0% 3.9%; --popover-foreground: 0 0% 98%; - + --card: 0 0% 3.9%; --card-foreground: 0 0% 98%; - + --border: 0 0% 14.9%; --input: 0 0% 14.9%; - + --primary: 0 0% 98%; --primary-foreground: 0 0% 9%; - + --secondary: 0 0% 14.9%; --secondary-foreground: 0 0% 98%; - + --accent: 0 0% 14.9%; --accent-foreground: 0 0% 98%; - + --destructive: 0 62.8% 30.6%; --destructive-foreground: 0 0% 98%; - + --ring: 0 0% 83.1%; } } - + @layer base { * { @apply border-border; @@ -83,4 +83,4 @@ code { @apply break-words whitespace-pre-wrap; } -} \ No newline at end of file +} diff --git a/WebUI/src/assets/css/main.css b/WebUI/src/assets/css/main.css index 641b72d6..df661f8b 100644 --- a/WebUI/src/assets/css/main.css +++ b/WebUI/src/assets/css/main.css @@ -1,5 +1,5 @@ * { - font-family: IntelOne, "Microsoft YaHei", Arial, Helvetica, sans-serif; + font-family: IntelOne, 'Microsoft YaHei', Arial, Helvetica, sans-serif; } button, @@ -62,10 +62,10 @@ textarea { &.active { color: #fff; - background: url("@/assets/svg/tab-active.svg") 0px 0px no-repeat; + background: url('@/assets/svg/tab-active.svg') 0px 0px no-repeat; z-index: 10; &:first-of-type { - background: url("@/assets/svg/tab-active-first.svg") 0px 0px no-repeat; + background: url('@/assets/svg/tab-active-first.svg') 0px 0px no-repeat; > * { margin-left: -15px; @@ -115,7 +115,7 @@ textarea { right: -25px; top: 0px; height: 100%; - content: ""; + content: ''; border-right: 1px solid var(--color-spilter); } } @@ -204,7 +204,7 @@ textarea { .outpaint-control-bg { width: 142px; height: 92px; - background: url("@/assets/svg/outpaint-bg.svg") 0px 0px no-repeat; + background: url('@/assets/svg/outpaint-bg.svg') 0px 0px no-repeat; position: relative; } @@ -221,7 +221,7 @@ textarea { &::after { position: absolute; - content: ""; + content: ''; width: 18px; height: 18px; left: calc(50% - 9px); @@ -231,8 +231,8 @@ textarea { mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; mask-size: 100% 100%; - -webkit-mask-image: url("@/assets/svg/outpaint-dir.svg"); - mask-image: url("@/assets/svg/outpaint-dir.svg"); + -webkit-mask-image: url('@/assets/svg/outpaint-dir.svg'); + mask-image: url('@/assets/svg/outpaint-dir.svg'); } &.top { left: calc(50% - 14px); @@ -260,7 +260,7 @@ textarea { } .cursor-block::after { - content: "_"; + content: '_'; animation: cursorBlink 0.5s ease-in-out infinite alternate; } @@ -310,11 +310,15 @@ textarea { opacity: 0.6; &.pen { - cursor: url("@/assets/svg/pen.svg") 4 16, auto; + cursor: + url('@/assets/svg/pen.svg') 4 16, + auto; } &.easer { - cursor: url("@/assets/svg/easer.svg") 4 16, auto; + cursor: + url('@/assets/svg/easer.svg') 4 16, + auto; } } @@ -342,7 +346,7 @@ textarea { position: relative; &::after { - content: " "; + content: ' '; position: absolute; width: 100%; left: 0px; @@ -409,8 +413,8 @@ footer { color: #666; } -#chatPanel .chat-content{ +#chatPanel .chat-content { word-break: break-word; word-spacing: 2px; overflow-wrap: anywhere; -} \ No newline at end of file +} diff --git a/WebUI/src/assets/css/svg.css b/WebUI/src/assets/css/svg.css index cbf560ec..43263172 100644 --- a/WebUI/src/assets/css/svg.css +++ b/WebUI/src/assets/css/svg.css @@ -10,165 +10,163 @@ } .i-setup { - --svg: url("@/assets/svg/setup.svg"); + --svg: url('@/assets/svg/setup.svg'); } .i-mini { - --svg: url("@/assets/svg/mini.svg"); + --svg: url('@/assets/svg/mini.svg'); } .i-close { - --svg: url("@/assets/svg/close.svg"); + --svg: url('@/assets/svg/close.svg'); } .i-tab-active { - --svg: url("@/assets/svg/tab-active.svg"); + --svg: url('@/assets/svg/tab-active.svg'); } .i-tab-active-first { - --svg: url("@/assets/svg/tab-active-first.svg"); + --svg: url('@/assets/svg/tab-active-first.svg'); } .i-generate-add { - --svg: url("@/assets/svg/generate-add.svg"); + --svg: url('@/assets/svg/generate-add.svg'); } .i-history { - --svg: url("@/assets/svg/history.svg"); + --svg: url('@/assets/svg/history.svg'); } .i-hide-history { - --svg: url("@/assets/svg/hide-history.svg"); + --svg: url('@/assets/svg/hide-history.svg'); } .i-copy { - --svg: url("@/assets/svg/copy.svg"); + --svg: url('@/assets/svg/copy.svg'); } .i-download { - --svg: url("@/assets/svg/download.svg"); + --svg: url('@/assets/svg/download.svg'); } .i-refresh { - --svg: url("@/assets/svg/refresh.svg"); + --svg: url('@/assets/svg/refresh.svg'); } .i-download-cloud { - --svg: url("@/assets/svg/download-cloud.svg"); + --svg: url('@/assets/svg/download-cloud.svg'); } .i-delete { - --svg: url("@/assets/svg/delete.svg"); + --svg: url('@/assets/svg/delete.svg'); } .i-outpaint-dir { - --svg: url("@/assets/svg/outpaint-dir.svg"); + --svg: url('@/assets/svg/outpaint-dir.svg'); } .i-pdf { - --svg: url("@/assets/svg/pdf.svg"); + --svg: url('@/assets/svg/pdf.svg'); } .i-txt { - --svg: url("@/assets/svg/txt.svg"); + --svg: url('@/assets/svg/txt.svg'); } .i-ppt { - --svg: url("@/assets/svg/ppt.svg"); + --svg: url('@/assets/svg/ppt.svg'); } .i-word { - --svg: url("@/assets/svg/word.svg"); + --svg: url('@/assets/svg/word.svg'); } .i-md { - --svg: url("@/assets/svg/md.svg"); + --svg: url('@/assets/svg/md.svg'); } - .i-add { - --svg: url("@/assets/svg/add.svg"); + --svg: url('@/assets/svg/add.svg'); } .i-queue { - --svg: url("@/assets/svg/queue.svg"); + --svg: url('@/assets/svg/queue.svg'); } .i-right { - --svg: url("@/assets/svg/right.svg"); + --svg: url('@/assets/svg/right.svg'); } .i-arrow-right { - --svg: url("@/assets/svg/arrow-right.svg"); + --svg: url('@/assets/svg/arrow-right.svg'); } .i-loading { - --svg: url("@/assets/svg/loading.svg"); + --svg: url('@/assets/svg/loading.svg'); } .i-add-box { - --svg: url("@/assets/svg/add-box.svg"); + --svg: url('@/assets/svg/add-box.svg'); } .i-pen { - --svg: url("@/assets/svg/pen.svg"); + --svg: url('@/assets/svg/pen.svg'); } .i-easer { - --svg: url("@/assets/svg/easer.svg"); + --svg: url('@/assets/svg/easer.svg'); } .i-broom { - --svg: url("@/assets/svg/broom.svg"); + --svg: url('@/assets/svg/broom.svg'); } .i-right-arrow { - --svg: url("@/assets/svg/right-arrow.svg"); + --svg: url('@/assets/svg/right-arrow.svg'); } .i-dice { - --svg: url("@/assets/svg/dice.svg"); + --svg: url('@/assets/svg/dice.svg'); } .i-reset { - --svg: url("@/assets/svg/reset.svg"); + --svg: url('@/assets/svg/reset.svg'); } .i-folder { - --svg: url("@/assets/svg/folder.svg"); + --svg: url('@/assets/svg/folder.svg'); } .i-transfer { - --svg: url("@/assets/svg/transfer.svg"); + --svg: url('@/assets/svg/transfer.svg'); } .i-zoom-in { - --svg: url("@/assets/svg/zoom-in.svg"); + --svg: url('@/assets/svg/zoom-in.svg'); } .i-zoom-out { - --svg: url("@/assets/svg/zoom-out.svg"); + --svg: url('@/assets/svg/zoom-out.svg'); } .i-clear { - --svg: url("@/assets/svg/clear.svg"); + --svg: url('@/assets/svg/clear.svg'); } .i-upload { - --svg: url("@/assets/svg/upload.svg"); + --svg: url('@/assets/svg/upload.svg'); } .i-stop { - --svg: url("@/assets/svg/stop.svg"); + --svg: url('@/assets/svg/stop.svg'); } .i-info { - --svg: url("@/assets/svg/info.svg"); + --svg: url('@/assets/svg/info.svg'); } .i-fullscreen { - --svg: url("@/assets/svg/fullscreen.svg"); + --svg: url('@/assets/svg/fullscreen.svg'); } - .i-fullscreen-exit { - --svg: url("@/assets/svg/fullscreen-exit.svg"); -} \ No newline at end of file + --svg: url('@/assets/svg/fullscreen-exit.svg'); +} diff --git a/WebUI/src/assets/css/theme.css b/WebUI/src/assets/css/theme.css index 9cdd561d..f53f4088 100644 --- a/WebUI/src/assets/css/theme.css +++ b/WebUI/src/assets/css/theme.css @@ -8,38 +8,35 @@ --color-gray-244: #e0e0e0; --color-image-tool-button: #585561; --color-active: #aa05ff; - --color-control-bg: #38383D; + --color-control-bg: #38383d; --main-gradient: linear-gradient(75deg, #2da9ff, #aa05ff); --textbox-bg: #38383d; --compontents-main: #fff; --color-image-bg: #110f1779; - --textbox-gradient-border: linear-gradient( - var(--textbox-bg), - var(--textbox-bg) - ), + --textbox-gradient-border: linear-gradient(var(--textbox-bg), var(--textbox-bg)), var(--main-gradient); --textbox-clip: padding-box, border-box; } @font-face { font-family: IntelOne; - src: url("@/assets/fonts/intelone-display-regular.woff2") format("woff2"); + src: url('@/assets/fonts/intelone-display-regular.woff2') format('woff2'); font-weight: 400; } @font-face { font-family: IntelOne; - src: url("@/assets/fonts/intelone-display-medium.woff2") format("woff2"); + src: url('@/assets/fonts/intelone-display-medium.woff2') format('woff2'); font-weight: 500; } @font-face { font-family: IntelOne; - src: url("@/assets/fonts/intelone-display-bold.woff2") format("woff2"); + src: url('@/assets/fonts/intelone-display-bold.woff2') format('woff2'); font-weight: 700; } @font-face { font-family: IntelOne; - src: url("@/assets/fonts/intelone-display-light.woff2") format("woff2"); + src: url('@/assets/fonts/intelone-display-light.woff2') format('woff2'); font-weight: 300; } @@ -53,7 +50,7 @@ } ::-webkit-scrollbar-thumb { - background: rgba(113, 109, 134, 1);; + background: rgba(113, 109, 134, 1); border-radius: 8px; cursor: default; } @@ -69,7 +66,7 @@ button:hover { opacity: 0.9; } -input[type="text"], +input[type='text'], textarea { background-color: var(--textbox-bg); border: 1px solid var(--color-gray-666); @@ -122,7 +119,7 @@ textarea { @keyframes scaleIn { from { - transform: scale(0.5) + transform: scale(0.5); } to { transform: scale(1); @@ -136,32 +133,39 @@ textarea { } .lnl-bottom-grid { - transform: translateX(calc(-50% + calc(5% * calc(var(--position-index) - 1)))) translateY(-25%) rotateX(78deg) skewX(-5deg); + transform: translateX(calc(-50% + calc(5% * calc(var(--position-index) - 1)))) translateY(-25%) + rotateX(78deg) skewX(-5deg); } .lnl-top-grid { - transform: translateX(calc(-50% + calc(5% * calc(var(--position-index) - 1)))) translateY(-75%) rotateX(102deg) skewX(-5deg); + transform: translateX(calc(-50% + calc(5% * calc(var(--position-index) - 1)))) translateY(-75%) + rotateX(102deg) skewX(-5deg); } .pos-0 { ---position-index: 0 + --position-index: 0; } .pos-1 { ---position-index: 1 + --position-index: 1; } .pos-2 { ---position-index: 2 + --position-index: 2; } .pos-3 { ---position-index: 3 + --position-index: 3; } .lnl-gradient { position: absolute; - background: linear-gradient( rgba(0, 25, 138, 0.2) 0%, rgb(0, 25, 138) 35%, rgb(0, 25, 138) 65%, rgba(0, 25, 138, 0.2) 100%); + background: linear-gradient( + rgba(0, 25, 138, 0.2) 0%, + rgb(0, 25, 138) 35%, + rgb(0, 25, 138) 65%, + rgba(0, 25, 138, 0.2) 100% + ); width: 100%; height: 100%; z-index: -5; @@ -181,11 +185,11 @@ textarea { } .main-tab-glider { -transform: translateX(calc(calc(168px + 0.25rem) * var(--position-index))); -transition: all 0.3s ease-in-out; -background: url("@/assets/svg/tab-active.svg") 0px 0px no-repeat; -z-index: -5; -position: absolute; + transform: translateX(calc(calc(168px + 0.25rem) * var(--position-index))); + transition: all 0.3s ease-in-out; + background: url('@/assets/svg/tab-active.svg') 0px 0px no-repeat; + z-index: -5; + position: absolute; } .lnl-grid::before { @@ -198,4 +202,4 @@ position: absolute; background-image: url('data:image/svg+xml;utf8,'); background-size: 40px 40px; opacity: 0.3; -} \ No newline at end of file +} diff --git a/WebUI/src/assets/css/vertical.css b/WebUI/src/assets/css/vertical.css index 2dace472..5ac8245b 100644 --- a/WebUI/src/assets/css/vertical.css +++ b/WebUI/src/assets/css/vertical.css @@ -1,3 +1,2 @@ -.vertical_tab{ - -} \ No newline at end of file +.vertical_tab { +} diff --git a/WebUI/src/assets/i18n/de.json b/WebUI/src/assets/i18n/de.json index 9b6b4868..10becf3b 100644 --- a/WebUI/src/assets/i18n/de.json +++ b/WebUI/src/assets/i18n/de.json @@ -1,230 +1,230 @@ { -"MAIN_TITLE": "AI PLAYGROUND", -"COM_SETTINGS": "Einstellungen", -"COM_MINI": "Minimieren", -"COM_CLOSE": "Schließen", -"COM_ADD": "Hinzufügen", -"COM_RESTORE": "Standardwerte wiederherstellen", -"COM_SD_PROMPT": "Eingabeprompt für Bildgenerierung", -"COM_LLM_PROMPT": "Eingabeprompt für Antwortgenerierung", -"COM_LLM_HF_PROMPT": "/", -"COM_LLM_HF_PROMPT_GGUF": "//", -"COM_CLICK_UPLOAD": "- Klicken Sie zum Hochladen eines Bildes -", -"COM_GENERATE": "Generieren", -"COM_GENERATING": "Generierung", -"COM_HISTORY": "Verlauf", -"COM_POST_TO_ENHANCE_PROCESS": "Veröffentlichen, um den Prozess zu verbessern", -"COM_ZOOM_IN": "Originalgröße anzeigen", -"COM_COPY": "Kopieren", -"COM_COPY_SUCCESS_TIP": "Kopieren erfolgreich", -"COM_APPLY": "Anwenden", -"COM_DOWNLOAD": "Herunterladen", -"COM_REGENERATE": "Regenerieren", -"COM_RESTART": "Neustart", -"COM_START": "Start", -"COM_DELETE": "Löschen", -"COM_DOWNLOAD_MODEL": "Modell herunterladen", -"COM_DOWNLOAD_SPEED": "Herunterladegeschwindigkeit", -"COM_LOADING_MODEL": "KI-Modell laden", -"COM_LOADING_MODEL_COMPONENTS": "KI-Modellkomponenten laden", -"COM_NO_SELECTED": "Keine Auswahl", -"COM_CONFIRM": "Bestätigen", -"COM_CANCEL": "Abbrechen", -"COM_VISIT": "BESUCHEN", -"COM_REQUESTING": "Anfrage", -"COM_DEFAULT": "Standard", -"COM_STOP": "Stop", -"COM_DEBUG": "Entwickler-Log öffnen", -"COM_OPEN_LOCATION": "Öffnen", -"COM_OPEN_PARAMS": "Parameter-Info", -"COM_LOW": "niedrig", -"COM_HIGH": "hoch", -"COM_FULLSCREEN": "Vollbild", -"COM_FULLSCREEN_EXIT": "Vollbild beenden", -"COM_DO_NOT_SHOW_AGAIN": "Nicht wieder anzeigen", -"COM_INSTALL": "Installieren", -"COM_INSTALL_ALL": "Installieren/Reparieren aller ausgewählten", -"COM_REPAIR": "Reparieren", -"COM_CONTINUE": "Fortsetzen", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "Der HD-Modus kann auf Systemen mit weniger als 12 GB VRAM oder 24 GB Systemarbeitsspeicher für Intel Core Ultra-PCs zu einer langsameren Leistung führen", -"SETTINGS_MODEL_WORKFLOW_COMFYUI_CONFIRM": "Die Verwendung des Workflow-Modus erfordert die Installation von ComfyUI. Dies ist ein optionaler Komponent, der im AI-Playground-Setup installiert werden kann.", -"SETTINGS_MODEL_WORKFLOW_COMFYUI_DOWNLOADING": "Installation im Gange...", -"SETTINGS_MODEL_WORKFLOW_COMFYUI_COMPLETED": "Installation erfolgreich!", -"SETTINGS_MODEL_WORKFLOW_COMFYUI_ERROR": "Installation fehlgeschlagen, wegen", -"SETTINGS_TAB_IMAGE": "Bild", -"SETTINGS_TAB_BASIC": "Allgemein", -"SETTINGS_TAB_MODEL": "Modelle", -"SETTINGS_INFERENCE_DEVICE": "Inferenzgerät", -"SETTINGS_LLM_BACKEND": "LLM-Backend", -"SETTINGS_MODEL_IMAGE_SIZE": "Bildgröße", -"SETTINGS_MODEL_IMAGE_RESOLUTION": "Bildauflösung", -"SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Standard", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", -"SETTINGS_MODEL_QUALITY": "Generierungsqualität", -"SETTINGS_MODEL_QUALITY_STANDARD": "Standard", -"SETTINGS_MODEL_QUALITY_HIGH": "Hohe Qualität", -"SETTINGS_MODEL_QUALITY_FAST": "Schnell", -"SETTINGS_MODEL_QUALITY_MANUAL": "Manuell", -"SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Einstellbare Optionen", -"SETTINGS_MODEL_MANUAL_OPTIONS": "Manuelle Optionen", -"SETTINGS_MODEL_NEGATIVE_PROMPT": "Negativer Prompt", -"SETTINGS_MODEL_SEED": "Seed", -"SETTINGS_MODEL_GENERATE_NUMBER": "Bildanzahl generieren", -"SETTINGS_MODEL_LLM_MODEL": "Großes Sprachmodell", -"SETTINGS_MODEL_IMAGE_PREVIEW": "Bildvorschau", -"SETTINGS_MODEL_SAFE_CHECK": "Sicherheitsprüfung (nur für SD 1.5)", -"SETTINGS_MODEL_IMAGE_MODEL": "Bildmodell", -"SETTINGS_MODEL_INPAINT_MODEL": "Inpaint/Outpaint-Modell", -"SETTINGS_MODEL_IMAGE_WIDTH": "Breite", -"SETTINGS_MODEL_IMAGE_HEIGHT": "Höhe", -"SETTINGS_MODEL_IMAGE_STEPS": "Schritte", -"SETTINGS_MODEL_IMAGE_CFG": "CFG", -"SETTINGS_MODEL_LORA": "Lora", -"SETTINGS_MODEL_SCHEDULER": "Zeitplaner", -"SETTINGS_MODEL_LIST_CHANGE_TIP": "Die Änderungen in der Modellliste haben zu Änderungen in einigen Ihrer Einstellungen geführt. Bitte beachten Sie die grundlegenden Einstellungen für weitere Informationen.", -"SETTINGS_BASIC_LANGUAGE": "Sprache", -"SETTINGS_BASIC_PATHS": "Pfade", -"SETTINGS_BASIC_LLM_CHECKPOINTS": "LLM-Modellpfad", -"SETTINGS_BASIC_GENERAL": "Allgemeine Einstellungen", -"SETTINGS_BASIC_DEVICES": "Geräte", -"SETTINGS_BASIC_BACKEND": "Backends", -"SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", -"SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API-Token", -"SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "Bitte geben Sie einen gültigen Token (hf_***) ein.", -"SETTINGS_MODEL_SD_CHECKPOINTS": "Bildmodellpfad", -"SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Inpaint/Outpaint-Modellpfad", -"SETTINGS_MODEL_SD_VAE": "Vae-Pfad", -"SETTINGS_MODEL_SD_LORA": "Lora-Pfad", -"SETTINGS_MODEL_SD_SCHEDULER": "Zeitplaner-Pfad", -"SETTINGS_MODEL_SD_PRESET_MODEL": "Standard-Bildmodell", -"SETTINGS_MODEL_SD_STANDARD_MODEL": "Standard-Modell", -"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Standard-Inpaint/Outpaint-Modell", -"SETTINGS_MODEL_SD_HD_MODEL": "HD-Standardmodell", -"SETTINGS_MODEL_RAG_MODEL": "Rag-Abfrage-Modell", -"SETTINGS_BACKEND_STATUS": "Backend-Status", -"SETTINGS_MODEL_MANAGE_BACKEND": "Backend-Komponenten verwalten", -"SETTINGS_MODEL_EXIST": "Das Modell existiert bereits. Eine erneute Download ist nicht notwendig.", -"SETTINGS_MODEL_DOWNLOAD": "Modell herunterladen", -"SETTINGS_MODEL_DOWNLOAD_DESC": "Unten finden Sie eine Liste von Modellen, die verschiedene KI-Aufgaben im AI-Playground ausführen können. Überprüfen Sie die Bedingungen jedes Modells, bevor Sie es herunterladen und verwenden. Alternativ können Sie andere Modellquellen auswählen, indem Sie die Modellpfade und Standardmodelle oben ändern.", -"SETTINGS_IMAGE_MODE": "Modus", -"SETTINGS_IMAGE_WORKFLOW": "Workflow", -"TAB_CREATE": "Erstellen", -"TAB_ENHANCE": "Verbessern", -"TAB_ANSWER": "Antwort", -"TAB_LEARN_MORE": "Mehr erfahren", -"ENHANCE_INPUT_IMAGE_REQUIRED": "Bitte setzen Sie das Eingabebild, bevor Sie generieren", -"ENHANCE_UPSCALE": "Auflösung erhöhen", -"ENHANCE_IMAGE_PROMPT": "Bild-Prompt", -"ENHANCE_INPAINT": "Inpaint", -"ENHANCE_OUTPAINT": "Outpaint", -"ENHANCE_UPSCALE_SCALE": "Auflösung", -"ENHANCE_UPSCALE_SCALE_X1_5": "Auflösung (1,5X)", -"ENHANCE_UPSCALE_SCALE_X2_0": "Auflösung (2,0X)", -"ENHANCE_UPSCALE_VARIATION": "Variation", -"ENHANCE_UPSCALE_VARIATION_NONE": "Keine", -"ENHANCE_UPSCALE_VARIATION_DISABLE": "Subtil", -"ENHANCE_UPSCALE_VARIATION_STRONG": "Stark", -"ENHANCE_INPAINT_TYPE": "Typ", -"ENHANCE_INPAINT_FILL": "Mit etwas Neuem füllen", -"ENHANCE_INPAINT_FIX": "Maskierte Fläche reparieren", -"ENHANCE_INPAINT_MASK_REQUIRED": "Bitte zeichnen Sie die Inpaint-Maske, bevor Sie generieren", -"ENHANCE_INPAINT_USE_IMAGE_MODEL": "Aktuelles Bildmodell verwenden", -"ENHANCE_COM_DENOISE": "Einfluss", -"ENHANCE_OUTPAINT_DIRECTION": "Richtung", -"ENHANCE_PREVIEW_BEFORE_PROCESS": "Vor dem Prozess", -"ENHANCE_PREVIEW_AFTER_PROCESS": "Nach dem Prozess", -"ENHANCE_IMAGE_PROMPT_TIP": "Beschreiben Sie, wie Sie das Bild stylen möchten. Fügen Sie Beschreibungen des Originalbildes hinzu, das Sie beibehalten möchten, und Beschreibungen dessen, was Sie anders haben möchten. Passen Sie den Einfluss-Regler an, um den Einfluss Ihrer Prompt auf das Ergebnis zu erhöhen oder zu verringern", -"ENHANCE_INPAINT_TIP": "Verwenden Sie das Pen-Tool, um eine Fläche zu maskieren. Beschreiben Sie im Prompt, was Sie reparieren oder füllen möchten. Wählen Sie die Option 'Reparieren' oder 'Füllen' und passen Sie den Einfluss-Regler an", -"ENHANCE_OUTPAINT_TIP": "Wählen Sie die Richtung, in die Sie erweitern möchten, passen Sie den Denoise-Wert an, wenn notwendig, und geben Sie ein Prompt-Wort im Textfeld ein. Das erweiterte Bild wird generiert", -"ENHANCE_UPSCALE_TIP": "Laden Sie das Bild hoch, das manipuliert werden soll, wählen Sie den Auflösungsverstärkungsfaktor, passen Sie die Amplitude des Denoise an (wenn weniger als 0,1, ändert sich der Bildinhalt nicht, aber die Größe wird immer noch aufgeskaliert), geben Sie ein Prompt-Wort im Textfeld ein, um das vergrößerte Bild zu generieren", -"ERROR_UNSUPPORTED_IMAGE_TYPE": "Nur PNG-, JPG-, GIF- und BMP-Bildformate werden unterstützt", -"ANSWER_USER_NAME": "Sie", -"ANSWER_AI_NAME": "Playground", -"ANSWER_ERROR_NOT_PROMPT": "Bitte geben Sie einen Prompt ein", -"ANSWER_ERROR_CLEAR_SESSION": "Chat-Verlauf löschen", -"INCREASE_FONT_SIZE": "Text vergrößern", -"DECREASE_FONT_SIZE": "Text verkleinern", -"ANSWER_RAG_ENABLE": "Datei-Abfrage aktivieren", -"ANSWER_RAG_OPEN_DIALOG": "Datei-Uploader öffnen", -"REQUEST_LLM_MODEL_NAME": "Fügen Sie ein Modell Ihrer Wahl von huggingface.co hinzu", -"REQUEST_LLM_MODEL_DESCRIPTION": "Sie können ein Modell-Repository mit der Syntax herunterladen", -"REQUEST_LLM_MODEL_EXAMPLE": "/, z.B. 'facebook/opt-1.3b'", -"REQUEST_LLM_SINGLE_EXAMPLE": "//, z.B. 'microsoft/Phi-3-mini-4k-instruct-gguf/Phi-3-mini-4k-instruct-q4.gguf'", -"REQUEST_LLM_MODEL_DISCLAIMER_1": "HINWEIS: Nicht jedes Modell auf huggingface.co ist für die Aufgabe geeignet, die Sie ausführen möchten.", -"REQUEST_LLM_MODEL_DISCLAIMER_2": "Lesen Sie die Modellbeschreibung und die Anforderungen sorgfältig durch, bevor Sie es herunterladen.", -"DOWNLOADER_CONFRIM_TIP": "Sie fehlen einem oder mehreren Modellen, die zum Ausführen benötigt werden. Möchten Sie die unten aufgeführten Modelle herunterladen?", -"DOWNLOADER_MODEL": "Modell", -"DOWNLOADER_INFO": "Info", -"DOWNLOADER_FILE_SIZE": "Größe", -"DOWNLOADER_GATED": "Gesperrt", -"DOWNLOADER_GATED_TOKEN": "Überprüfen Sie, ob Sie einen gültigen huggingface.co-API-Token in Ihren Einstellungen hinzugefügt haben.", -"DOWNLOADER_ACCESS_INFO_SINGLE": "Sie haben keinen Zugriff auf das Modell, das Sie herunterladen möchten", -"DOWNLOADER_GATED_ACCEPT_SINGLE": "Das Modell ist gesperrt. Bitte besuchen Sie die Modell-Info-Seite und beantragen Sie den Zugriff.", -"DOWNLOADER_ACCESS_ACCEPT_SINGLE": "Ein unzugängliches Modell kann nicht heruntergeladen werden.", -"DOWNLOADER_ACCESS_INFO": "Sie haben keinen Zugriff auf einige der Modelle, die Sie herunterladen möchten", -"DOWNLOADER_GATED_ACCEPT": "Einige der Modelle sind gesperrt. Bitte besuchen Sie die Modell-Info-Seite und beantragen Sie den Zugriff.", -"DOWNLOADER_ACCESS_ACCEPT": "Unzugängliche Modelle werden nicht heruntergeladen.", -"DOWNLOADER_REASON": "Typ", -"DOWNLOADER_TERMS": "Besuchen", -"DOWNLOADER_LICENSE": "Bedingungen", -"DOWNLOADER_CONFLICT": "Ein anderer Download-Vorgang ist derzeit im Gange und ein neuer Vorgang kann nicht gestartet werden. Sie können den aktuellen Download-Vorgang abbrechen und einen neuen Download-Vorgang starten", -"DOWNLOADER_TERMS_TIP": "Ich habe die Modellkarten und Lizenzen überprüft. Ich stimme allen Bedingungen und Konditionen zu und möchte die Drittanbieter-Modelle herunterladen.", -"DOWNLOADER_FOR_ANSWER_GENERATE": "Antwort-Modell", -"DOWNLOADER_FOR_RAG_QUERY": "RAG-Abfrage-Modell", -"DOWNLOADER_FOR_IMAGE_GENERATE": "Bild-Modell", -"DOWNLOADER_FOR_INAPINT_GENERATE": "Inpaint/OutPaint-Modell", -"DOWNLOADER_FOR_IMAGE_PREVIEW": "Bild-Vorschau-Modell", -"DOWNLOADER_FOR_IMAGE_UPSCALE": "Bild-Auflösungsverstärkungs-Modell", -"DOWNLOADER_FOR_IMAGE_LORA": "Schnelles Bild-Modell", -"DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Modell-Downloads abgeschlossen", -"RAG_FILE_TOTAL_FORMAT": "Datei-Gesamtzahl: {total}", -"RAG_DRAG_UPLOAD": "Dateien per Drag-and-Drop hochladen", -"RAG_DRAG_UPLOAD_UNSUPPORTED": "Das aktuelle Programm wird mit Administrator-Rechten gestartet. Aufgrund der Windows-UAC-Einschränkungen kann der Drag-and-Drop-Upload nicht verwendet werden. Bitte verwenden Sie die Schaltfläche 'Hinzufügen' oben, um Dateien hochzuladen.", -"RAG_UPLOAD_MIME_TYPE": "Unterstützte Dateitypen:\n\tTextdokument: .txt .md\n\tOffice-Dokument: .doc(x)\n\tPDF-Dokument: .pdf", -"RAG_ENABLE_TIP": "Antworten auf hochgeladene Dateien", -"RAG_UPLOAD_TYPE_ERROR": "Es gibt einen nicht unterstützten Dateityp in der hochgeladenen Datei, der automatisch ausgeschlossen wurde", -"RAG_UPLOAD_FILE_EXISTS": "Die hochgeladene Datei {filename} existiert bereits", -"RAG_ANALYZE_FILE_FAILED": "Die Datei {filename} konnte nicht analysiert werden", -"RAG_USE_REJECT": "Die interaktive Dokumentenliste ist leer, bitte laden Sie das Dokument zuerst hoch", -"RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Bitte warten Sie, bis alle Dokumente analysiert wurden", -"RAG_SOURCE": "Von Quelle:", -"ERR_NOT_ENOUGH_DISK_SPACE": "Nicht genügend Festplattenspeicher. Es werden {requires_space} benötigt, aber nur {free_space} Festplattenspeicher sind verfügbar", -"ERR_DOWNLOAD_FAILED": "Modell-Datei-Download fehlgeschlagen", -"ERROR_RUNTIME_ERROR": "Wenn ein kritischer Fehler auftritt, bitte das Programm neu starten und erneut versuchen", -"ERROR_GENERATE_UNKONW_EXCEPTION": "Ein unbekannter Fehler ist aufgetreten. Bildgenerierung aus dem Modell fehlgeschlagen", -"ERROR_FOLDER_NOT_EXISTS": "Das angegebene Verzeichnis existiert nicht", -"ERROR_REPO_NOT_EXISTS": "Die angegebene Repository-ID konnte nicht gefunden werden", -"ERROR_ALREADY_IN_MODELS": "Das angegebene Modell ist bereits in der Liste", -"ERROR_ENHANCE_IMAGE_NOT_SET": "Bitte konfigurieren Sie das Eingabebild, das generiert werden soll", -"ERROR_UNFOUND_GRAPHICS": "Wenn die notwendige Hardware für die Programmausführung auf dem Computergerät nicht erkannt wird, wird das Programm beendet, wenn Sie auf OK klicken.", -"ERROR_PYTHON_BACKEND_INIT": "Backend-Initialisierung fehlgeschlagen", -"ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "Die KI-Inferenz-Backend-Initialisierung ist fehlgeschlagen. Bitte versuchen Sie, die Anwendung neu zu starten. Wenn das Problem weiterhin besteht, können Sie die Details für weitere Informationen über den Fehler überprüfen.", -"ERROR_PYTHON_BACKEND_INIT_DETAILS": "Details", -"ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "Log öffnen", -"WARNING_MODEL_TYPE_WRONG": "Der Modelltyp scheint nicht den Anforderungen zu entsprechen. Sind Sie sicher, dass Sie fortfahren möchten?", -"BACKEND_MANAGE": "AI Playground-Setup", -"BACKEND_REQUIRED_COMPONENTS": "Erforderliche Komponenten", -"BACKEND_OPTIONAL_COMPONENTS": "Optionale Komponenten", -"BACKEND_SINGLE_COMPONENT": "Komponente ", -"BACKEND_TYPE": "Typ", -"BACKEND_STATUS": "Status ", -"BACKEND_INFORMATION": "Information", -"BACKEND_ENABLE": "Aktivieren *", -"BACKEND_ACTION": "Aktion", -"BACKEND_REQUIRED": "Notwendig", -"BACKEND_OPTIONAL": "Optional", -"BACKEND_REQUIRED_COMPONENTS_MESSAGE": "Bevor Sie den Intel AI Playground verwenden können, müssen wir einige zusätzliche Komponenten herunterladen. Bitte stellen Sie sicher, dass Sie eine stabile und ungemessene Internetverbindung haben.", -"BACKEND_OPTIONAL_COMPONENTS_MESSAGE": "Optionale Komponenten sind nicht erforderlich, damit der AI Playground funktioniert, aber sie bieten alternative Funktionen. Wenn Sie sie verwenden möchten, klicken Sie bitte auf die Info-Schaltflächen, um sich mit den Bedingungen und Konditionen vertraut zu machen, bevor Sie sie aktivieren.", -"BACKEND_TERMS_AND_CONDITIONS": "* Ich habe die optionalen Komponenten überprüft. Ich stimme allen Bedingungen und Konditionen zu und möchte die Drittanbieter-Software herunterladen und aktivieren, wenn zutreffend.", -"BACKEND_STATUS_RUNNING": "Läuft", -"BACKEND_STATUS_STOPPING": "Beendet", -"BACKEND_STATUS_STARTING": "Startet", -"BACKEND_STATUS_INSTALLED": "Installiert", -"BACKEND_STATUS_NOT_INSTALLED": "Nicht installiert", -"BACKEND_STATUS_INSTALLING": "Wird installiert", -"BACKEND_STATUS_FAILED": "Fehlgeschlagen", -"WORKFLOW_RELOAD_INFO": "Workflows von der Festplatte neu laden.", -"WORKFLOW_DOWNLOAD_INFO": "Die neuesten Workflows herunterladen und vorhandene Workflows sichern.", -"WORKFLOW_HIGH_VRAM_INFO": "Dieses Modell benötigt viel VRAM. Reduzieren Sie die Auflösung und/oder starten Sie das ComfyUI-Backend-Server neu, wenn Sie eine schlechte Leistung erleben.", -"WORKFLOW_HIGH_VRAM_WARNING": "Viel VRAM erforderlich" -} \ No newline at end of file + "MAIN_TITLE": "AI PLAYGROUND", + "COM_SETTINGS": "Einstellungen", + "COM_MINI": "Minimieren", + "COM_CLOSE": "Schließen", + "COM_ADD": "Hinzufügen", + "COM_RESTORE": "Standardwerte wiederherstellen", + "COM_SD_PROMPT": "Eingabeprompt für Bildgenerierung", + "COM_LLM_PROMPT": "Eingabeprompt für Antwortgenerierung", + "COM_LLM_HF_PROMPT": "/", + "COM_LLM_HF_PROMPT_GGUF": "//", + "COM_CLICK_UPLOAD": "- Klicken Sie zum Hochladen eines Bildes -", + "COM_GENERATE": "Generieren", + "COM_GENERATING": "Generierung", + "COM_HISTORY": "Verlauf", + "COM_POST_TO_ENHANCE_PROCESS": "Veröffentlichen, um den Prozess zu verbessern", + "COM_ZOOM_IN": "Originalgröße anzeigen", + "COM_COPY": "Kopieren", + "COM_COPY_SUCCESS_TIP": "Kopieren erfolgreich", + "COM_APPLY": "Anwenden", + "COM_DOWNLOAD": "Herunterladen", + "COM_REGENERATE": "Regenerieren", + "COM_RESTART": "Neustart", + "COM_START": "Start", + "COM_DELETE": "Löschen", + "COM_DOWNLOAD_MODEL": "Modell herunterladen", + "COM_DOWNLOAD_SPEED": "Herunterladegeschwindigkeit", + "COM_LOADING_MODEL": "KI-Modell laden", + "COM_LOADING_MODEL_COMPONENTS": "KI-Modellkomponenten laden", + "COM_NO_SELECTED": "Keine Auswahl", + "COM_CONFIRM": "Bestätigen", + "COM_CANCEL": "Abbrechen", + "COM_VISIT": "BESUCHEN", + "COM_REQUESTING": "Anfrage", + "COM_DEFAULT": "Standard", + "COM_STOP": "Stop", + "COM_DEBUG": "Entwickler-Log öffnen", + "COM_OPEN_LOCATION": "Öffnen", + "COM_OPEN_PARAMS": "Parameter-Info", + "COM_LOW": "niedrig", + "COM_HIGH": "hoch", + "COM_FULLSCREEN": "Vollbild", + "COM_FULLSCREEN_EXIT": "Vollbild beenden", + "COM_DO_NOT_SHOW_AGAIN": "Nicht wieder anzeigen", + "COM_INSTALL": "Installieren", + "COM_INSTALL_ALL": "Installieren/Reparieren aller ausgewählten", + "COM_REPAIR": "Reparieren", + "COM_CONTINUE": "Fortsetzen", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "Der HD-Modus kann auf Systemen mit weniger als 12 GB VRAM oder 24 GB Systemarbeitsspeicher für Intel Core Ultra-PCs zu einer langsameren Leistung führen", + "SETTINGS_MODEL_WORKFLOW_COMFYUI_CONFIRM": "Die Verwendung des Workflow-Modus erfordert die Installation von ComfyUI. Dies ist ein optionaler Komponent, der im AI-Playground-Setup installiert werden kann.", + "SETTINGS_MODEL_WORKFLOW_COMFYUI_DOWNLOADING": "Installation im Gange...", + "SETTINGS_MODEL_WORKFLOW_COMFYUI_COMPLETED": "Installation erfolgreich!", + "SETTINGS_MODEL_WORKFLOW_COMFYUI_ERROR": "Installation fehlgeschlagen, wegen", + "SETTINGS_TAB_IMAGE": "Bild", + "SETTINGS_TAB_BASIC": "Allgemein", + "SETTINGS_TAB_MODEL": "Modelle", + "SETTINGS_INFERENCE_DEVICE": "Inferenzgerät", + "SETTINGS_LLM_BACKEND": "LLM-Backend", + "SETTINGS_MODEL_IMAGE_SIZE": "Bildgröße", + "SETTINGS_MODEL_IMAGE_RESOLUTION": "Bildauflösung", + "SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Standard", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", + "SETTINGS_MODEL_QUALITY": "Generierungsqualität", + "SETTINGS_MODEL_QUALITY_STANDARD": "Standard", + "SETTINGS_MODEL_QUALITY_HIGH": "Hohe Qualität", + "SETTINGS_MODEL_QUALITY_FAST": "Schnell", + "SETTINGS_MODEL_QUALITY_MANUAL": "Manuell", + "SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Einstellbare Optionen", + "SETTINGS_MODEL_MANUAL_OPTIONS": "Manuelle Optionen", + "SETTINGS_MODEL_NEGATIVE_PROMPT": "Negativer Prompt", + "SETTINGS_MODEL_SEED": "Seed", + "SETTINGS_MODEL_GENERATE_NUMBER": "Bildanzahl generieren", + "SETTINGS_MODEL_LLM_MODEL": "Großes Sprachmodell", + "SETTINGS_MODEL_IMAGE_PREVIEW": "Bildvorschau", + "SETTINGS_MODEL_SAFE_CHECK": "Sicherheitsprüfung (nur für SD 1.5)", + "SETTINGS_MODEL_IMAGE_MODEL": "Bildmodell", + "SETTINGS_MODEL_INPAINT_MODEL": "Inpaint/Outpaint-Modell", + "SETTINGS_MODEL_IMAGE_WIDTH": "Breite", + "SETTINGS_MODEL_IMAGE_HEIGHT": "Höhe", + "SETTINGS_MODEL_IMAGE_STEPS": "Schritte", + "SETTINGS_MODEL_IMAGE_CFG": "CFG", + "SETTINGS_MODEL_LORA": "Lora", + "SETTINGS_MODEL_SCHEDULER": "Zeitplaner", + "SETTINGS_MODEL_LIST_CHANGE_TIP": "Die Änderungen in der Modellliste haben zu Änderungen in einigen Ihrer Einstellungen geführt. Bitte beachten Sie die grundlegenden Einstellungen für weitere Informationen.", + "SETTINGS_BASIC_LANGUAGE": "Sprache", + "SETTINGS_BASIC_PATHS": "Pfade", + "SETTINGS_BASIC_LLM_CHECKPOINTS": "LLM-Modellpfad", + "SETTINGS_BASIC_GENERAL": "Allgemeine Einstellungen", + "SETTINGS_BASIC_DEVICES": "Geräte", + "SETTINGS_BASIC_BACKEND": "Backends", + "SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", + "SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API-Token", + "SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "Bitte geben Sie einen gültigen Token (hf_***) ein.", + "SETTINGS_MODEL_SD_CHECKPOINTS": "Bildmodellpfad", + "SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Inpaint/Outpaint-Modellpfad", + "SETTINGS_MODEL_SD_VAE": "Vae-Pfad", + "SETTINGS_MODEL_SD_LORA": "Lora-Pfad", + "SETTINGS_MODEL_SD_SCHEDULER": "Zeitplaner-Pfad", + "SETTINGS_MODEL_SD_PRESET_MODEL": "Standard-Bildmodell", + "SETTINGS_MODEL_SD_STANDARD_MODEL": "Standard-Modell", + "SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Standard-Inpaint/Outpaint-Modell", + "SETTINGS_MODEL_SD_HD_MODEL": "HD-Standardmodell", + "SETTINGS_MODEL_RAG_MODEL": "Rag-Abfrage-Modell", + "SETTINGS_BACKEND_STATUS": "Backend-Status", + "SETTINGS_MODEL_MANAGE_BACKEND": "Backend-Komponenten verwalten", + "SETTINGS_MODEL_EXIST": "Das Modell existiert bereits. Eine erneute Download ist nicht notwendig.", + "SETTINGS_MODEL_DOWNLOAD": "Modell herunterladen", + "SETTINGS_MODEL_DOWNLOAD_DESC": "Unten finden Sie eine Liste von Modellen, die verschiedene KI-Aufgaben im AI-Playground ausführen können. Überprüfen Sie die Bedingungen jedes Modells, bevor Sie es herunterladen und verwenden. Alternativ können Sie andere Modellquellen auswählen, indem Sie die Modellpfade und Standardmodelle oben ändern.", + "SETTINGS_IMAGE_MODE": "Modus", + "SETTINGS_IMAGE_WORKFLOW": "Workflow", + "TAB_CREATE": "Erstellen", + "TAB_ENHANCE": "Verbessern", + "TAB_ANSWER": "Antwort", + "TAB_LEARN_MORE": "Mehr erfahren", + "ENHANCE_INPUT_IMAGE_REQUIRED": "Bitte setzen Sie das Eingabebild, bevor Sie generieren", + "ENHANCE_UPSCALE": "Auflösung erhöhen", + "ENHANCE_IMAGE_PROMPT": "Bild-Prompt", + "ENHANCE_INPAINT": "Inpaint", + "ENHANCE_OUTPAINT": "Outpaint", + "ENHANCE_UPSCALE_SCALE": "Auflösung", + "ENHANCE_UPSCALE_SCALE_X1_5": "Auflösung (1,5X)", + "ENHANCE_UPSCALE_SCALE_X2_0": "Auflösung (2,0X)", + "ENHANCE_UPSCALE_VARIATION": "Variation", + "ENHANCE_UPSCALE_VARIATION_NONE": "Keine", + "ENHANCE_UPSCALE_VARIATION_DISABLE": "Subtil", + "ENHANCE_UPSCALE_VARIATION_STRONG": "Stark", + "ENHANCE_INPAINT_TYPE": "Typ", + "ENHANCE_INPAINT_FILL": "Mit etwas Neuem füllen", + "ENHANCE_INPAINT_FIX": "Maskierte Fläche reparieren", + "ENHANCE_INPAINT_MASK_REQUIRED": "Bitte zeichnen Sie die Inpaint-Maske, bevor Sie generieren", + "ENHANCE_INPAINT_USE_IMAGE_MODEL": "Aktuelles Bildmodell verwenden", + "ENHANCE_COM_DENOISE": "Einfluss", + "ENHANCE_OUTPAINT_DIRECTION": "Richtung", + "ENHANCE_PREVIEW_BEFORE_PROCESS": "Vor dem Prozess", + "ENHANCE_PREVIEW_AFTER_PROCESS": "Nach dem Prozess", + "ENHANCE_IMAGE_PROMPT_TIP": "Beschreiben Sie, wie Sie das Bild stylen möchten. Fügen Sie Beschreibungen des Originalbildes hinzu, das Sie beibehalten möchten, und Beschreibungen dessen, was Sie anders haben möchten. Passen Sie den Einfluss-Regler an, um den Einfluss Ihrer Prompt auf das Ergebnis zu erhöhen oder zu verringern", + "ENHANCE_INPAINT_TIP": "Verwenden Sie das Pen-Tool, um eine Fläche zu maskieren. Beschreiben Sie im Prompt, was Sie reparieren oder füllen möchten. Wählen Sie die Option 'Reparieren' oder 'Füllen' und passen Sie den Einfluss-Regler an", + "ENHANCE_OUTPAINT_TIP": "Wählen Sie die Richtung, in die Sie erweitern möchten, passen Sie den Denoise-Wert an, wenn notwendig, und geben Sie ein Prompt-Wort im Textfeld ein. Das erweiterte Bild wird generiert", + "ENHANCE_UPSCALE_TIP": "Laden Sie das Bild hoch, das manipuliert werden soll, wählen Sie den Auflösungsverstärkungsfaktor, passen Sie die Amplitude des Denoise an (wenn weniger als 0,1, ändert sich der Bildinhalt nicht, aber die Größe wird immer noch aufgeskaliert), geben Sie ein Prompt-Wort im Textfeld ein, um das vergrößerte Bild zu generieren", + "ERROR_UNSUPPORTED_IMAGE_TYPE": "Nur PNG-, JPG-, GIF- und BMP-Bildformate werden unterstützt", + "ANSWER_USER_NAME": "Sie", + "ANSWER_AI_NAME": "Playground", + "ANSWER_ERROR_NOT_PROMPT": "Bitte geben Sie einen Prompt ein", + "ANSWER_ERROR_CLEAR_SESSION": "Chat-Verlauf löschen", + "INCREASE_FONT_SIZE": "Text vergrößern", + "DECREASE_FONT_SIZE": "Text verkleinern", + "ANSWER_RAG_ENABLE": "Datei-Abfrage aktivieren", + "ANSWER_RAG_OPEN_DIALOG": "Datei-Uploader öffnen", + "REQUEST_LLM_MODEL_NAME": "Fügen Sie ein Modell Ihrer Wahl von huggingface.co hinzu", + "REQUEST_LLM_MODEL_DESCRIPTION": "Sie können ein Modell-Repository mit der Syntax herunterladen", + "REQUEST_LLM_MODEL_EXAMPLE": "/, z.B. 'facebook/opt-1.3b'", + "REQUEST_LLM_SINGLE_EXAMPLE": "//, z.B. 'microsoft/Phi-3-mini-4k-instruct-gguf/Phi-3-mini-4k-instruct-q4.gguf'", + "REQUEST_LLM_MODEL_DISCLAIMER_1": "HINWEIS: Nicht jedes Modell auf huggingface.co ist für die Aufgabe geeignet, die Sie ausführen möchten.", + "REQUEST_LLM_MODEL_DISCLAIMER_2": "Lesen Sie die Modellbeschreibung und die Anforderungen sorgfältig durch, bevor Sie es herunterladen.", + "DOWNLOADER_CONFRIM_TIP": "Sie fehlen einem oder mehreren Modellen, die zum Ausführen benötigt werden. Möchten Sie die unten aufgeführten Modelle herunterladen?", + "DOWNLOADER_MODEL": "Modell", + "DOWNLOADER_INFO": "Info", + "DOWNLOADER_FILE_SIZE": "Größe", + "DOWNLOADER_GATED": "Gesperrt", + "DOWNLOADER_GATED_TOKEN": "Überprüfen Sie, ob Sie einen gültigen huggingface.co-API-Token in Ihren Einstellungen hinzugefügt haben.", + "DOWNLOADER_ACCESS_INFO_SINGLE": "Sie haben keinen Zugriff auf das Modell, das Sie herunterladen möchten", + "DOWNLOADER_GATED_ACCEPT_SINGLE": "Das Modell ist gesperrt. Bitte besuchen Sie die Modell-Info-Seite und beantragen Sie den Zugriff.", + "DOWNLOADER_ACCESS_ACCEPT_SINGLE": "Ein unzugängliches Modell kann nicht heruntergeladen werden.", + "DOWNLOADER_ACCESS_INFO": "Sie haben keinen Zugriff auf einige der Modelle, die Sie herunterladen möchten", + "DOWNLOADER_GATED_ACCEPT": "Einige der Modelle sind gesperrt. Bitte besuchen Sie die Modell-Info-Seite und beantragen Sie den Zugriff.", + "DOWNLOADER_ACCESS_ACCEPT": "Unzugängliche Modelle werden nicht heruntergeladen.", + "DOWNLOADER_REASON": "Typ", + "DOWNLOADER_TERMS": "Besuchen", + "DOWNLOADER_LICENSE": "Bedingungen", + "DOWNLOADER_CONFLICT": "Ein anderer Download-Vorgang ist derzeit im Gange und ein neuer Vorgang kann nicht gestartet werden. Sie können den aktuellen Download-Vorgang abbrechen und einen neuen Download-Vorgang starten", + "DOWNLOADER_TERMS_TIP": "Ich habe die Modellkarten und Lizenzen überprüft. Ich stimme allen Bedingungen und Konditionen zu und möchte die Drittanbieter-Modelle herunterladen.", + "DOWNLOADER_FOR_ANSWER_GENERATE": "Antwort-Modell", + "DOWNLOADER_FOR_RAG_QUERY": "RAG-Abfrage-Modell", + "DOWNLOADER_FOR_IMAGE_GENERATE": "Bild-Modell", + "DOWNLOADER_FOR_INAPINT_GENERATE": "Inpaint/OutPaint-Modell", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "Bild-Vorschau-Modell", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "Bild-Auflösungsverstärkungs-Modell", + "DOWNLOADER_FOR_IMAGE_LORA": "Schnelles Bild-Modell", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Modell-Downloads abgeschlossen", + "RAG_FILE_TOTAL_FORMAT": "Datei-Gesamtzahl: {total}", + "RAG_DRAG_UPLOAD": "Dateien per Drag-and-Drop hochladen", + "RAG_DRAG_UPLOAD_UNSUPPORTED": "Das aktuelle Programm wird mit Administrator-Rechten gestartet. Aufgrund der Windows-UAC-Einschränkungen kann der Drag-and-Drop-Upload nicht verwendet werden. Bitte verwenden Sie die Schaltfläche 'Hinzufügen' oben, um Dateien hochzuladen.", + "RAG_UPLOAD_MIME_TYPE": "Unterstützte Dateitypen:\n\tTextdokument: .txt .md\n\tOffice-Dokument: .doc(x)\n\tPDF-Dokument: .pdf", + "RAG_ENABLE_TIP": "Antworten auf hochgeladene Dateien", + "RAG_UPLOAD_TYPE_ERROR": "Es gibt einen nicht unterstützten Dateityp in der hochgeladenen Datei, der automatisch ausgeschlossen wurde", + "RAG_UPLOAD_FILE_EXISTS": "Die hochgeladene Datei {filename} existiert bereits", + "RAG_ANALYZE_FILE_FAILED": "Die Datei {filename} konnte nicht analysiert werden", + "RAG_USE_REJECT": "Die interaktive Dokumentenliste ist leer, bitte laden Sie das Dokument zuerst hoch", + "RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Bitte warten Sie, bis alle Dokumente analysiert wurden", + "RAG_SOURCE": "Von Quelle:", + "ERR_NOT_ENOUGH_DISK_SPACE": "Nicht genügend Festplattenspeicher. Es werden {requires_space} benötigt, aber nur {free_space} Festplattenspeicher sind verfügbar", + "ERR_DOWNLOAD_FAILED": "Modell-Datei-Download fehlgeschlagen", + "ERROR_RUNTIME_ERROR": "Wenn ein kritischer Fehler auftritt, bitte das Programm neu starten und erneut versuchen", + "ERROR_GENERATE_UNKONW_EXCEPTION": "Ein unbekannter Fehler ist aufgetreten. Bildgenerierung aus dem Modell fehlgeschlagen", + "ERROR_FOLDER_NOT_EXISTS": "Das angegebene Verzeichnis existiert nicht", + "ERROR_REPO_NOT_EXISTS": "Die angegebene Repository-ID konnte nicht gefunden werden", + "ERROR_ALREADY_IN_MODELS": "Das angegebene Modell ist bereits in der Liste", + "ERROR_ENHANCE_IMAGE_NOT_SET": "Bitte konfigurieren Sie das Eingabebild, das generiert werden soll", + "ERROR_UNFOUND_GRAPHICS": "Wenn die notwendige Hardware für die Programmausführung auf dem Computergerät nicht erkannt wird, wird das Programm beendet, wenn Sie auf OK klicken.", + "ERROR_PYTHON_BACKEND_INIT": "Backend-Initialisierung fehlgeschlagen", + "ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "Die KI-Inferenz-Backend-Initialisierung ist fehlgeschlagen. Bitte versuchen Sie, die Anwendung neu zu starten. Wenn das Problem weiterhin besteht, können Sie die Details für weitere Informationen über den Fehler überprüfen.", + "ERROR_PYTHON_BACKEND_INIT_DETAILS": "Details", + "ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "Log öffnen", + "WARNING_MODEL_TYPE_WRONG": "Der Modelltyp scheint nicht den Anforderungen zu entsprechen. Sind Sie sicher, dass Sie fortfahren möchten?", + "BACKEND_MANAGE": "AI Playground-Setup", + "BACKEND_REQUIRED_COMPONENTS": "Erforderliche Komponenten", + "BACKEND_OPTIONAL_COMPONENTS": "Optionale Komponenten", + "BACKEND_SINGLE_COMPONENT": "Komponente ", + "BACKEND_TYPE": "Typ", + "BACKEND_STATUS": "Status ", + "BACKEND_INFORMATION": "Information", + "BACKEND_ENABLE": "Aktivieren *", + "BACKEND_ACTION": "Aktion", + "BACKEND_REQUIRED": "Notwendig", + "BACKEND_OPTIONAL": "Optional", + "BACKEND_REQUIRED_COMPONENTS_MESSAGE": "Bevor Sie den Intel AI Playground verwenden können, müssen wir einige zusätzliche Komponenten herunterladen. Bitte stellen Sie sicher, dass Sie eine stabile und ungemessene Internetverbindung haben.", + "BACKEND_OPTIONAL_COMPONENTS_MESSAGE": "Optionale Komponenten sind nicht erforderlich, damit der AI Playground funktioniert, aber sie bieten alternative Funktionen. Wenn Sie sie verwenden möchten, klicken Sie bitte auf die Info-Schaltflächen, um sich mit den Bedingungen und Konditionen vertraut zu machen, bevor Sie sie aktivieren.", + "BACKEND_TERMS_AND_CONDITIONS": "* Ich habe die optionalen Komponenten überprüft. Ich stimme allen Bedingungen und Konditionen zu und möchte die Drittanbieter-Software herunterladen und aktivieren, wenn zutreffend.", + "BACKEND_STATUS_RUNNING": "Läuft", + "BACKEND_STATUS_STOPPING": "Beendet", + "BACKEND_STATUS_STARTING": "Startet", + "BACKEND_STATUS_INSTALLED": "Installiert", + "BACKEND_STATUS_NOT_INSTALLED": "Nicht installiert", + "BACKEND_STATUS_INSTALLING": "Wird installiert", + "BACKEND_STATUS_FAILED": "Fehlgeschlagen", + "WORKFLOW_RELOAD_INFO": "Workflows von der Festplatte neu laden.", + "WORKFLOW_DOWNLOAD_INFO": "Die neuesten Workflows herunterladen und vorhandene Workflows sichern.", + "WORKFLOW_HIGH_VRAM_INFO": "Dieses Modell benötigt viel VRAM. Reduzieren Sie die Auflösung und/oder starten Sie das ComfyUI-Backend-Server neu, wenn Sie eine schlechte Leistung erleben.", + "WORKFLOW_HIGH_VRAM_WARNING": "Viel VRAM erforderlich" +} diff --git a/WebUI/src/assets/i18n/en-US.json b/WebUI/src/assets/i18n/en-US.json index 613c74ba..6feb7cd5 100644 --- a/WebUI/src/assets/i18n/en-US.json +++ b/WebUI/src/assets/i18n/en-US.json @@ -31,11 +31,11 @@ "COM_CONFIRM": "Confirm", "COM_CANCEL": "Cancel", "COM_VISIT": "VISIT", - "COM_REQUESTING":"Requesting", - "COM_DEFAULT":"Default", - "COM_STOP":"Stop", + "COM_REQUESTING": "Requesting", + "COM_DEFAULT": "Default", + "COM_STOP": "Stop", "COM_DEBUG": "Open Developer Logs", - "COM_OPEN_LOCATION":"Open Location", + "COM_OPEN_LOCATION": "Open Location", "COM_OPEN_PARAMS": "Parameters Info", "COM_LOW": "low", "COM_HIGH": "high", @@ -105,7 +105,7 @@ "SETTINGS_MODEL_MANAGE_BACKEND": "Manage Backend Components", "SETTINGS_MODEL_EXIST": "The model already exist. Repeating the download is unnecessary.", "SETTINGS_MODEL_DOWNLOAD": "Model Download", - "SETTINGS_MODEL_DOWNLOAD_DESC":"Below is a list of models which can perform various AI tasks in AI Playground. Review terms of each model before download and use. Alternatively choose to add in and select other model sources by changing the model paths and model defaults above.", + "SETTINGS_MODEL_DOWNLOAD_DESC": "Below is a list of models which can perform various AI tasks in AI Playground. Review terms of each model before download and use. Alternatively choose to add in and select other model sources by changing the model paths and model defaults above.", "TAB_CREATE": "Create", "TAB_ENHANCE": "Enhance", "TAB_ANSWER": "Answer", @@ -131,29 +131,29 @@ "ENHANCE_OUTPAINT_DIRECTION": "Direction", "ENHANCE_PREVIEW_BEFORE_PROCESS": "Before Process", "ENHANCE_PREVIEW_AFTER_PROCESS": "After Process", - "ENHANCE_IMAGE_PROMPT_TIP":"Describe how you want the image to be stylized. Include descriptions of the original image you want to keep and descriptions of what you want to be different. Adjust the influence slider to increase or decrease the how much influence your prompt has in the outcome", - "ENHANCE_INPAINT_TIP":"Use the pen tool to mask an area. Describe in the prompt what you want to Fix or Fill. Select the Fix or Fill option, then adjust the level of influence the prompt has", - "ENHANCE_OUTPAINT_TIP":"Select the direction you want to expand, adjust the value of denoise if necessary, and enter a prompt word in the text box. The expanded image will be generated", - "ENHANCE_UPSCALE_TIP":"Upload the image that needs to be manipulated, select the amplification factor, adjust the amplitude of denoise (if less than 0.1, the image content will not change, but the size will still upsacle), enter the prompt word in the text box to generate the enlarged image", + "ENHANCE_IMAGE_PROMPT_TIP": "Describe how you want the image to be stylized. Include descriptions of the original image you want to keep and descriptions of what you want to be different. Adjust the influence slider to increase or decrease the how much influence your prompt has in the outcome", + "ENHANCE_INPAINT_TIP": "Use the pen tool to mask an area. Describe in the prompt what you want to Fix or Fill. Select the Fix or Fill option, then adjust the level of influence the prompt has", + "ENHANCE_OUTPAINT_TIP": "Select the direction you want to expand, adjust the value of denoise if necessary, and enter a prompt word in the text box. The expanded image will be generated", + "ENHANCE_UPSCALE_TIP": "Upload the image that needs to be manipulated, select the amplification factor, adjust the amplitude of denoise (if less than 0.1, the image content will not change, but the size will still upsacle), enter the prompt word in the text box to generate the enlarged image", "ERROR_UNSUPPORTED_IMAGE_TYPE": "Only supports PNG, JPG, GIF, and BMP image formats", "ANSWER_USER_NAME": "You", "ANSWER_AI_NAME": "Playground", "ANSWER_ERROR_NOT_PROMPT": "Please input prompt", - "ANSWER_ERROR_CLEAR_SESSION":"Clear Chat History", + "ANSWER_ERROR_CLEAR_SESSION": "Clear Chat History", "INCREASE_FONT_SIZE": "Enlarge Text", "DECREASE_FONT_SIZE": "Shrink Text", - "ANSWER_RAG_ENABLE":"Enable File Query", - "ANSWER_RAG_OPEN_DIALOG":"Open File Uploader", - "REQUEST_LLM_MODEL_NAME":"Add a model of your choice from huggingface.co", + "ANSWER_RAG_ENABLE": "Enable File Query", + "ANSWER_RAG_OPEN_DIALOG": "Open File Uploader", + "REQUEST_LLM_MODEL_NAME": "Add a model of your choice from huggingface.co", "REQUEST_LLM_MODEL_DESCRIPTION": "You can download a model repository with the syntax", "REQUEST_LLM_MODEL_EXAMPLE": "/, e.g. 'facebook/opt-1.3b'", "REQUEST_LLM_SINGLE_EXAMPLE": "//, e.g. 'microsoft/Phi-3-mini-4k-instruct-gguf/Phi-3-mini-4k-instruct-q4.gguf'", "REQUEST_LLM_MODEL_DISCLAIMER_1": "NOTE: Not every model on huggingface.co is suited for the task you want it to perform.", "REQUEST_LLM_MODEL_DISCLAIMER_2": "Carefully read the model introduction and requirements before downloading.", - "DOWNLOADER_CONFRIM_TIP":"You are missing one or more models needed to run. Would you like to download the model(s) listed below?", - "DOWNLOADER_MODEL":"Model", - "DOWNLOADER_INFO":"Info", - "DOWNLOADER_FILE_SIZE":"Size", + "DOWNLOADER_CONFRIM_TIP": "You are missing one or more models needed to run. Would you like to download the model(s) listed below?", + "DOWNLOADER_MODEL": "Model", + "DOWNLOADER_INFO": "Info", + "DOWNLOADER_FILE_SIZE": "Size", "DOWNLOADER_GATED": "Gated", "DOWNLOADER_GATED_TOKEN": "Check, if you have a valid huggingface.co API token added to your settings. ", "DOWNLOADER_ACCESS_INFO_SINGLE": "You don't have access to the model you want to download", @@ -162,19 +162,19 @@ "DOWNLOADER_ACCESS_INFO": "You don't have access to some models you want to download", "DOWNLOADER_GATED_ACCEPT": "Some of the models are gated. Please make sure to visit the model info page and request access. ", "DOWNLOADER_ACCESS_ACCEPT": "Inaccessible models will not be downloaded.", - "DOWNLOADER_REASON":"Type", - "DOWNLOADER_TERMS":"Visit", - "DOWNLOADER_LICENSE":"Terms", - "DOWNLOADER_CONFLICT":"Another download task is currently in progress, and a new task cannot be started. You can cancel the current download task and start a new download task", - "DOWNLOADER_TERMS_TIP":"I have reviewed the model card(s) and license(s). I agree to all terms and conditions would like to download the third-party model(s).", - "DOWNLOADER_FOR_ANSWER_GENERATE":"Answer Model", - "DOWNLOADER_FOR_RAG_QUERY":"RAG Embed Model", - "DOWNLOADER_FOR_IMAGE_GENERATE":"Image Model", - "DOWNLOADER_FOR_INAPINT_GENERATE":"Inpaint/OutPaint Model", - "DOWNLOADER_FOR_IMAGE_PREVIEW":"Image Preview Model", - "DOWNLOADER_FOR_IMAGE_UPSCALE":"Image Upscale Model", + "DOWNLOADER_REASON": "Type", + "DOWNLOADER_TERMS": "Visit", + "DOWNLOADER_LICENSE": "Terms", + "DOWNLOADER_CONFLICT": "Another download task is currently in progress, and a new task cannot be started. You can cancel the current download task and start a new download task", + "DOWNLOADER_TERMS_TIP": "I have reviewed the model card(s) and license(s). I agree to all terms and conditions would like to download the third-party model(s).", + "DOWNLOADER_FOR_ANSWER_GENERATE": "Answer Model", + "DOWNLOADER_FOR_RAG_QUERY": "RAG Embed Model", + "DOWNLOADER_FOR_IMAGE_GENERATE": "Image Model", + "DOWNLOADER_FOR_INAPINT_GENERATE": "Inpaint/OutPaint Model", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "Image Preview Model", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "Image Upscale Model", "DOWNLOADER_FOR_IMAGE_LORA": "Fast Image Model", - "DOWNLOADER_DONWLOAD_TASK_PROGRESS":"Model Downloads Complete", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Model Downloads Complete", "RAG_FILE_TOTAL_FORMAT": "File total: {total}", "RAG_DRAG_UPLOAD": "Drag files to upload", "RAG_DRAG_UPLOAD_UNSUPPORTED": "The current program is started with administrator privileges. Due to Windows UAC limitations, drag and drop upload cannot be used. Please use the add button at the top to upload files.", diff --git a/WebUI/src/assets/i18n/es.json b/WebUI/src/assets/i18n/es.json index 28fa7fd5..7d92ff3e 100644 --- a/WebUI/src/assets/i18n/es.json +++ b/WebUI/src/assets/i18n/es.json @@ -1,152 +1,152 @@ { -"MAIN_TITLE": "AI PLAYGROUND", -"COM_SETTINGS": "Configuración", -"COM_MINI": "Miniminizar", -"COM_CLOSE": "Cerrar", -"COM_RESTORE": "Restaurar valores predeterminados", -"COM_SD_PROMPT": "Solicitud de entrada para generar la imagen", -"COM_LLM_PROMPT": "Solicitud de entrada para generar una respuesta", -"COM_CLICK_UPLOAD": "Haga clic para cargar la imagen", -"COM_GENERATE": "Generar", -"COM_GENERATING": "Generando", -"COM_HISTORY": "Historial", -"COM_POST_TO_ENHANCE_PROCESS": "Publicación para mejorar el proceso", -"COM_ZOOM_IN": "Ver en tamaño original", -"COM_COPY": "Copiar", -"COM_COPY_SUCCESS_TIP": "Copia realizada", -"COM_APPLY": "Aplicar", -"COM_DOWNLOAD": "Descargar", -"COM_REGENERATE": "Regenerar", -"COM_DELETE": "Borrar", -"COM_DOWNLOAD_MODEL": "Modelos para descargar", -"COM_DOWNLOAD_SPEED": "Velocidad de descarga", -"COM_LOADING_MODEL": "Cargando el modelo AI", -"COM_LOADING_MODEL_COMPONENTS": "Cargando componentes del modelo AI", -"COM_NO_SELECTED": "No seleccionado", -"COM_CONFIRM": "Confirmar", -"COM_CANCEL": "Borrar", -"COM_VISIT": "VISITAR", -"COM_REQUESTING": "Solicitar", -"COM_DEFAULT": "Predeterminado", -"COM_STOP": "Detener", -"COM_OPEN_LOCATION": "Posición abierta", -"COM_OPEN_PARAMS": "Información de parámetros", -"COM_LOW": "Bajo", -"COM_HIGH": "Alto", -"COM_FULLSCREEN": "Pantalla completa", -"COM_FULLSCREEN_EXIT": "Salir de pantalla completa", -"SETTINGS_TAB_BASIC": "Configuración básica", -"SETTINGS_TAB_MODEL": "Modelos", -"SETTINGS_INFERENCE_DEVICE": "Dispositivo de inferencia", -"SETTINGS_MODEL_IMAGE_RESOLUTION": "Resolución de imagen", -"SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Estándar", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", -"SETTINGS_MODEL_QUALITY": "Generar calidad", -"SETTINGS_MODEL_QUALITY_STANDARD": "Standard", -"SETTINGS_MODEL_QUALITY_HIGH": "Alta calidad", -"SETTINGS_MODEL_QUALITY_FAST": "Rápido", -"SETTINGS_MODEL_QUALITY_MANUAL": "Manual", -"SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Opciones ajustables", -"SETTINGS_MODEL_MANUAL_OPTIONS": "Opciones manuales", -"SETTINGS_MODEL_NEGATIVE_PROMPT": "Solicitud incorrecta", -"SETTINGS_MODEL_SEED": "Seed", -"SETTINGS_MODEL_GENERATE_NUMBER": "Generar número de imagen", -"SETTINGS_MODEL_LLM_MODEL": "Modelo de lenguaje grande", -"SETTINGS_MODEL_IMAGE_PREVIEW": "Vista previa de la imagen", -"SETTINGS_MODEL_SAFE_CHECK": "Control de seguridad (efectivo solo en SD 1.5)", -"SETTINGS_MODEL_IMAGE_MODEL": "Modelo de imagen", -"SETTINGS_MODEL_INPAINT_MODEL": "Modelo Inpaint/Outpaint", -"SETTINGS_MODEL_IMAGE_WIDTH": "Ancho", -"SETTINGS_MODEL_IMAGE_HEIGHT": "Altura", -"SETTINGS_MODEL_IMAGE_STEPS": "Pasos", -"SETTINGS_MODEL_IMAGE_CFG": "CFG", -"SETTINGS_MODEL_LORA": "Lora", -"SETTINGS_MODEL_SCHEDULER": "Planificación", -"SETTINGS_MODEL_LIST_CHANGE_TIP": "Los cambios en la lista de modelos han implicado modificaciones en algunas de tus configuraciones. Para más información, consulta las configuraciones básicas.", -"SETTINGS_BASIC_LANGUAGE": "Idioma", -"SETTINGS_BASIC_PATHS": "Rutas", -"SETTINGS_BASIC_LLM_CHECKPOINTS": "Ruta del modelo LLM", -"SETTINGS_MODEL_SD_CHECKPOINTS": "Ruta del modelo de imagen", -"SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Ruta del modelo de imagen Inpaint", -"SETTINGS_MODEL_SD_VAE": "Ruta Vae", -"SETTINGS_MODEL_SD_LORA": "Ruta Lora", -"SETTINGS_MODEL_SD_SCHEDULER": "Ruta de planificación", -"SETTINGS_MODEL_SD_PRESET_MODEL": "Modelo predeterminado de la imagen", -"SETTINGS_MODEL_SD_STANDARD_MODEL": "Modelo predeterminado estándar", -"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Modelo predeterminado estándar Inpaint/OutPaint ", -"SETTINGS_MODEL_SD_HD_MODEL": "Modelo predeterminado HD", -"SETTINGS_MODEL_RAG_MODEL": "Modelo de consulta RAG", -"SETTINGS_MODEL_EXIST": "El modelo ya existe. No es necesario repetir la descarga.", -"SETTINGS_MODEL_DOWNLOAD": "Descarga del modelo", -"SETTINGS_MODEL_DOWNLOAD_DESC": "A continuación se muestra una lista de modelos que pueden realizar varias actividades en AI Playground. Revisa los términos de cada modelo antes de descargar y usar. Alternativamente, elige añadir y seleccionar otras fuentes de modelo modificando las rutas de los modelos y configuraciones predeterminadas de arriba.", -"TAB_CREATE": "Crear", -"TAB_ENHANCE": "Mejorar", -"TAB_ANSWER": "Responder", -"TAB_LEARN_MORE": "Saber más", -"ENHANCE_INPUT_IMAGE_REQUIRED": "Establecer la imagen de entrada antes de generarla", -"ENHANCE_UPSCALE": "Aumentar resolución", -"ENHANCE_IMAGE_PROMPT": "Solicitud de imagen", -"ENHANCE_INPAINT": "Inpaint", -"ENHANCE_OUTPAINT": "Outpaint", -"ENHANCE_UPSCALE_SCALE": "Aumentar resolución", -"ENHANCE_UPSCALE_SCALE_X1_5": "Aumentar resolución (1.5X)", -"ENHANCE_UPSCALE_SCALE_X2_0": "Aumentar resolución (2.0X)", -"ENHANCE_UPSCALE_VARIATION": "Variación", -"ENHANCE_UPSCALE_VARIATION_NONE": "Ninguna", -"ENHANCE_UPSCALE_VARIATION_DISABLE": "Sutil", -"ENHANCE_UPSCALE_VARIATION_STRONG": "Denso", -"ENHANCE_INPAINT_TYPE": "Tipo", -"ENHANCE_INPAINT_FILL": "Rellenar con algo nuevo", -"ENHANCE_INPAINT_FIX": "Ajustar y reparar el área seleccionada", -"ENHANCE_INPAINT_MASK_REQUIRED": "Por favor, dibuje la zona para inpaint antes de generarla", -"ENHANCE_INPAINT_USE_IMAGE_MODEL": "Utilizar el modelo de imagen actual", -"ENHANCE_COM_DENOISE": "Influencia", -"ENHANCE_OUTPAINT_DIRECTION": "Dirección", -"ENHANCE_PREVIEW_BEFORE_PROCESS": "Antes del proceso", -"ENHANCE_PREVIEW_AFTER_PROCESS": "Después del proceso", -"ENHANCE_IMAGE_PROMPT_TIP": "Describe cómo quieres que se estilice la imagen. Incluye las descripciones de la imagen original que deseas conservar y las descripciones de lo que quieres que sea diferente. Ajusta el control deslizante de influencia para aumentar o disminuir la influencia que tu comando tiene sobre el resultado final.", -"ENHANCE_INPAINT_TIP": "Utiliza la herramienta lápiz para seleccionar un área. Describe en el comando lo que deseas corregir o rellenar. Selecciona la opción Corregir o Rellenar, luego ajusta el nivel de influencia del comando.", -"ENHANCE_OUTPAINT_TIP": "Selecciona la dirección en la que deseas expandir, ajusta el valor de reducción de ruido si es necesario e ingresa una palabra en el cuadro de texto. Se generará la imagen expandida.", -"ENHANCE_UPSCALE_TIP": "Carga la imagen que debe ser manipulada, selecciona el factor de amplificación, ajusta el ancho de ruido (si es inferior a 0,1, el contenido de la imagen no cambiará, pero el tamaño se aumentará de todos modos), ingresa la palabra solicitada en el cuadro de texto para generar la imagen ampliada.", -"ERROR_UNSUPPORTED_IMAGE_TYPE": "Solo se admiten los formatos de imagen PNG, JPG, GIF y BMP.", -"ANSWER_USER_NAME": "Tú", -"ANSWER_AI_NAME": "Playground (Zona de pruebas)", -"ANSWER_ERROR_NOT_PROMPT": "Por favor, ingresa el comando", -"ANSWER_ERROR_CLEAR_SESSION": "Borrar el historial del chat", -"ANSWER_RAG_ENABLE": "Activar consulta de archivos", -"ANSWER_RAG_OPEN_DIALOG": "Abrir cargador de archivos", -"DOWNLOADER_CONFRIM_TIP": "Te faltan uno o más modelos necesarios para la ejecución. ¿Deseas descargar los modelos listados a continuación?", -"DOWNLOADER_MODEL": "Modelo", -"DOWNLOADER_INFO": "Información", -"DOWNLOADER_FILE_SIZE": "Tamaño", -"DOWNLOADER_REASON": "Motivo", -"DOWNLOADER_TERMS": "Visitar", -"DOWNLOADER_CONFLICT": "Otra actividad de descarga está en curso y no se puede iniciar una nueva. Puedes cancelar la actividad de descarga actual e iniciar una nueva.", -"DOWNLOADER_TERMS_TIP": "He revisado las tarjetas del modelo y las licencias. Acepto todos los términos y condiciones. Me gustaría descargar los modelos de terceros.", -"DOWNLOADER_FOR_ANSWER_GENERATE": "Modelo de respuesta", -"DOWNLOADER_FOR_RAG_QUERY": "Modelo Embed RAG", -"DOWNLOADER_FOR_IMAGE_GENERATE": "Modelo de imagen", -"DOWNLOADER_FOR_INAPINT_GENERATE": "Modelo Inpaint/OutPaint", -"DOWNLOADER_FOR_IMAGE_PREVIEW": "Modelo de vista previa de imagen", -"DOWNLOADER_FOR_IMAGE_UPSCALE": "Modelo de imagen aumentada", -"DOWNLOADER_FOR_IMAGE_LORA": "Modelo de imagen rápida", -"DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Descargas del modelo completadas", -"RAG_FILE_TOTAL_FORMAT": "Archivos totales: {total}", -"RAG_DRAG_UPLOAD": "Arrastra los archivos para cargar", -"RAG_DRAG_UPLOAD_UNSUPPORTED": "El programa actual se ejecuta con privilegios de administrador. Debido a las limitaciones de UAC de Windows, no es posible utilizar la carga mediante arrastre. Usa el botón de arriba \"Agregar\" para cargar los archivos.", -"RAG_UPLOAD_MIME_TYPE": "Tipos de archivo admitidos: \n\tText Doc: .txt .md\n\tOffice Doc: .doc(x)\n\tPDF Doc: .pdf", -"RAG_ENABLE_TIP": "Responder solo a los archivos cargados", -"RAG_UPLOAD_TYPE_ERROR": "El archivo cargado contiene un tipo de archivo no admitido, que ha sido excluido automáticamente.", -"RAG_UPLOAD_FILE_EXISTS": "El archivo cargado {filename} ya existe.", -"RAG_ANALYZE_FILE_FAILED": "El análisis del archivo {filename} falló.", -"RAG_USE_REJECT": "La lista de documentos interactivos está vacía, carga el documento primero.", -"RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Por favor, espera a que se complete el análisis de todos los documentos.", -"RAG_SOURCE": "Desde la fuente:", -"ERR_NOT_ENOUGH_DISK_SPACE": "Espacio en disco insuficiente. Requiere {requires_space}, pero solo hay {free_space} de espacio libre.", -"ERR_DOWNLOAD_FAILED": "Fallo en la descarga del archivo del modelo.", -"ERROR_RUNTIME_ERROR": "Si ocurre un error crítico, reinicia el programa y vuelve a intentarlo.", -"ERROR_GENERATE_UNKONW_EXCEPTION": "Ha ocurrido un error desconocido. Imposible generar desde el modelo.", -"ERROR_FOLDER_NOT_EXISTS": "El directorio especificado no existe.", -"ERROR_ENHANCE_IMAGE_NOT_SET": "Configura la imagen de entrada a generar.", -"ERROR_UNFOUND_GRAPHICS": "Si no se detecta el hardware necesario en el dispositivo, el programa se cerrará al hacer clic en OK." + "MAIN_TITLE": "AI PLAYGROUND", + "COM_SETTINGS": "Configuración", + "COM_MINI": "Miniminizar", + "COM_CLOSE": "Cerrar", + "COM_RESTORE": "Restaurar valores predeterminados", + "COM_SD_PROMPT": "Solicitud de entrada para generar la imagen", + "COM_LLM_PROMPT": "Solicitud de entrada para generar una respuesta", + "COM_CLICK_UPLOAD": "Haga clic para cargar la imagen", + "COM_GENERATE": "Generar", + "COM_GENERATING": "Generando", + "COM_HISTORY": "Historial", + "COM_POST_TO_ENHANCE_PROCESS": "Publicación para mejorar el proceso", + "COM_ZOOM_IN": "Ver en tamaño original", + "COM_COPY": "Copiar", + "COM_COPY_SUCCESS_TIP": "Copia realizada", + "COM_APPLY": "Aplicar", + "COM_DOWNLOAD": "Descargar", + "COM_REGENERATE": "Regenerar", + "COM_DELETE": "Borrar", + "COM_DOWNLOAD_MODEL": "Modelos para descargar", + "COM_DOWNLOAD_SPEED": "Velocidad de descarga", + "COM_LOADING_MODEL": "Cargando el modelo AI", + "COM_LOADING_MODEL_COMPONENTS": "Cargando componentes del modelo AI", + "COM_NO_SELECTED": "No seleccionado", + "COM_CONFIRM": "Confirmar", + "COM_CANCEL": "Borrar", + "COM_VISIT": "VISITAR", + "COM_REQUESTING": "Solicitar", + "COM_DEFAULT": "Predeterminado", + "COM_STOP": "Detener", + "COM_OPEN_LOCATION": "Posición abierta", + "COM_OPEN_PARAMS": "Información de parámetros", + "COM_LOW": "Bajo", + "COM_HIGH": "Alto", + "COM_FULLSCREEN": "Pantalla completa", + "COM_FULLSCREEN_EXIT": "Salir de pantalla completa", + "SETTINGS_TAB_BASIC": "Configuración básica", + "SETTINGS_TAB_MODEL": "Modelos", + "SETTINGS_INFERENCE_DEVICE": "Dispositivo de inferencia", + "SETTINGS_MODEL_IMAGE_RESOLUTION": "Resolución de imagen", + "SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Estándar", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", + "SETTINGS_MODEL_QUALITY": "Generar calidad", + "SETTINGS_MODEL_QUALITY_STANDARD": "Standard", + "SETTINGS_MODEL_QUALITY_HIGH": "Alta calidad", + "SETTINGS_MODEL_QUALITY_FAST": "Rápido", + "SETTINGS_MODEL_QUALITY_MANUAL": "Manual", + "SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Opciones ajustables", + "SETTINGS_MODEL_MANUAL_OPTIONS": "Opciones manuales", + "SETTINGS_MODEL_NEGATIVE_PROMPT": "Solicitud incorrecta", + "SETTINGS_MODEL_SEED": "Seed", + "SETTINGS_MODEL_GENERATE_NUMBER": "Generar número de imagen", + "SETTINGS_MODEL_LLM_MODEL": "Modelo de lenguaje grande", + "SETTINGS_MODEL_IMAGE_PREVIEW": "Vista previa de la imagen", + "SETTINGS_MODEL_SAFE_CHECK": "Control de seguridad (efectivo solo en SD 1.5)", + "SETTINGS_MODEL_IMAGE_MODEL": "Modelo de imagen", + "SETTINGS_MODEL_INPAINT_MODEL": "Modelo Inpaint/Outpaint", + "SETTINGS_MODEL_IMAGE_WIDTH": "Ancho", + "SETTINGS_MODEL_IMAGE_HEIGHT": "Altura", + "SETTINGS_MODEL_IMAGE_STEPS": "Pasos", + "SETTINGS_MODEL_IMAGE_CFG": "CFG", + "SETTINGS_MODEL_LORA": "Lora", + "SETTINGS_MODEL_SCHEDULER": "Planificación", + "SETTINGS_MODEL_LIST_CHANGE_TIP": "Los cambios en la lista de modelos han implicado modificaciones en algunas de tus configuraciones. Para más información, consulta las configuraciones básicas.", + "SETTINGS_BASIC_LANGUAGE": "Idioma", + "SETTINGS_BASIC_PATHS": "Rutas", + "SETTINGS_BASIC_LLM_CHECKPOINTS": "Ruta del modelo LLM", + "SETTINGS_MODEL_SD_CHECKPOINTS": "Ruta del modelo de imagen", + "SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Ruta del modelo de imagen Inpaint", + "SETTINGS_MODEL_SD_VAE": "Ruta Vae", + "SETTINGS_MODEL_SD_LORA": "Ruta Lora", + "SETTINGS_MODEL_SD_SCHEDULER": "Ruta de planificación", + "SETTINGS_MODEL_SD_PRESET_MODEL": "Modelo predeterminado de la imagen", + "SETTINGS_MODEL_SD_STANDARD_MODEL": "Modelo predeterminado estándar", + "SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Modelo predeterminado estándar Inpaint/OutPaint ", + "SETTINGS_MODEL_SD_HD_MODEL": "Modelo predeterminado HD", + "SETTINGS_MODEL_RAG_MODEL": "Modelo de consulta RAG", + "SETTINGS_MODEL_EXIST": "El modelo ya existe. No es necesario repetir la descarga.", + "SETTINGS_MODEL_DOWNLOAD": "Descarga del modelo", + "SETTINGS_MODEL_DOWNLOAD_DESC": "A continuación se muestra una lista de modelos que pueden realizar varias actividades en AI Playground. Revisa los términos de cada modelo antes de descargar y usar. Alternativamente, elige añadir y seleccionar otras fuentes de modelo modificando las rutas de los modelos y configuraciones predeterminadas de arriba.", + "TAB_CREATE": "Crear", + "TAB_ENHANCE": "Mejorar", + "TAB_ANSWER": "Responder", + "TAB_LEARN_MORE": "Saber más", + "ENHANCE_INPUT_IMAGE_REQUIRED": "Establecer la imagen de entrada antes de generarla", + "ENHANCE_UPSCALE": "Aumentar resolución", + "ENHANCE_IMAGE_PROMPT": "Solicitud de imagen", + "ENHANCE_INPAINT": "Inpaint", + "ENHANCE_OUTPAINT": "Outpaint", + "ENHANCE_UPSCALE_SCALE": "Aumentar resolución", + "ENHANCE_UPSCALE_SCALE_X1_5": "Aumentar resolución (1.5X)", + "ENHANCE_UPSCALE_SCALE_X2_0": "Aumentar resolución (2.0X)", + "ENHANCE_UPSCALE_VARIATION": "Variación", + "ENHANCE_UPSCALE_VARIATION_NONE": "Ninguna", + "ENHANCE_UPSCALE_VARIATION_DISABLE": "Sutil", + "ENHANCE_UPSCALE_VARIATION_STRONG": "Denso", + "ENHANCE_INPAINT_TYPE": "Tipo", + "ENHANCE_INPAINT_FILL": "Rellenar con algo nuevo", + "ENHANCE_INPAINT_FIX": "Ajustar y reparar el área seleccionada", + "ENHANCE_INPAINT_MASK_REQUIRED": "Por favor, dibuje la zona para inpaint antes de generarla", + "ENHANCE_INPAINT_USE_IMAGE_MODEL": "Utilizar el modelo de imagen actual", + "ENHANCE_COM_DENOISE": "Influencia", + "ENHANCE_OUTPAINT_DIRECTION": "Dirección", + "ENHANCE_PREVIEW_BEFORE_PROCESS": "Antes del proceso", + "ENHANCE_PREVIEW_AFTER_PROCESS": "Después del proceso", + "ENHANCE_IMAGE_PROMPT_TIP": "Describe cómo quieres que se estilice la imagen. Incluye las descripciones de la imagen original que deseas conservar y las descripciones de lo que quieres que sea diferente. Ajusta el control deslizante de influencia para aumentar o disminuir la influencia que tu comando tiene sobre el resultado final.", + "ENHANCE_INPAINT_TIP": "Utiliza la herramienta lápiz para seleccionar un área. Describe en el comando lo que deseas corregir o rellenar. Selecciona la opción Corregir o Rellenar, luego ajusta el nivel de influencia del comando.", + "ENHANCE_OUTPAINT_TIP": "Selecciona la dirección en la que deseas expandir, ajusta el valor de reducción de ruido si es necesario e ingresa una palabra en el cuadro de texto. Se generará la imagen expandida.", + "ENHANCE_UPSCALE_TIP": "Carga la imagen que debe ser manipulada, selecciona el factor de amplificación, ajusta el ancho de ruido (si es inferior a 0,1, el contenido de la imagen no cambiará, pero el tamaño se aumentará de todos modos), ingresa la palabra solicitada en el cuadro de texto para generar la imagen ampliada.", + "ERROR_UNSUPPORTED_IMAGE_TYPE": "Solo se admiten los formatos de imagen PNG, JPG, GIF y BMP.", + "ANSWER_USER_NAME": "Tú", + "ANSWER_AI_NAME": "Playground (Zona de pruebas)", + "ANSWER_ERROR_NOT_PROMPT": "Por favor, ingresa el comando", + "ANSWER_ERROR_CLEAR_SESSION": "Borrar el historial del chat", + "ANSWER_RAG_ENABLE": "Activar consulta de archivos", + "ANSWER_RAG_OPEN_DIALOG": "Abrir cargador de archivos", + "DOWNLOADER_CONFRIM_TIP": "Te faltan uno o más modelos necesarios para la ejecución. ¿Deseas descargar los modelos listados a continuación?", + "DOWNLOADER_MODEL": "Modelo", + "DOWNLOADER_INFO": "Información", + "DOWNLOADER_FILE_SIZE": "Tamaño", + "DOWNLOADER_REASON": "Motivo", + "DOWNLOADER_TERMS": "Visitar", + "DOWNLOADER_CONFLICT": "Otra actividad de descarga está en curso y no se puede iniciar una nueva. Puedes cancelar la actividad de descarga actual e iniciar una nueva.", + "DOWNLOADER_TERMS_TIP": "He revisado las tarjetas del modelo y las licencias. Acepto todos los términos y condiciones. Me gustaría descargar los modelos de terceros.", + "DOWNLOADER_FOR_ANSWER_GENERATE": "Modelo de respuesta", + "DOWNLOADER_FOR_RAG_QUERY": "Modelo Embed RAG", + "DOWNLOADER_FOR_IMAGE_GENERATE": "Modelo de imagen", + "DOWNLOADER_FOR_INAPINT_GENERATE": "Modelo Inpaint/OutPaint", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "Modelo de vista previa de imagen", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "Modelo de imagen aumentada", + "DOWNLOADER_FOR_IMAGE_LORA": "Modelo de imagen rápida", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Descargas del modelo completadas", + "RAG_FILE_TOTAL_FORMAT": "Archivos totales: {total}", + "RAG_DRAG_UPLOAD": "Arrastra los archivos para cargar", + "RAG_DRAG_UPLOAD_UNSUPPORTED": "El programa actual se ejecuta con privilegios de administrador. Debido a las limitaciones de UAC de Windows, no es posible utilizar la carga mediante arrastre. Usa el botón de arriba \"Agregar\" para cargar los archivos.", + "RAG_UPLOAD_MIME_TYPE": "Tipos de archivo admitidos: \n\tText Doc: .txt .md\n\tOffice Doc: .doc(x)\n\tPDF Doc: .pdf", + "RAG_ENABLE_TIP": "Responder solo a los archivos cargados", + "RAG_UPLOAD_TYPE_ERROR": "El archivo cargado contiene un tipo de archivo no admitido, que ha sido excluido automáticamente.", + "RAG_UPLOAD_FILE_EXISTS": "El archivo cargado {filename} ya existe.", + "RAG_ANALYZE_FILE_FAILED": "El análisis del archivo {filename} falló.", + "RAG_USE_REJECT": "La lista de documentos interactivos está vacía, carga el documento primero.", + "RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Por favor, espera a que se complete el análisis de todos los documentos.", + "RAG_SOURCE": "Desde la fuente:", + "ERR_NOT_ENOUGH_DISK_SPACE": "Espacio en disco insuficiente. Requiere {requires_space}, pero solo hay {free_space} de espacio libre.", + "ERR_DOWNLOAD_FAILED": "Fallo en la descarga del archivo del modelo.", + "ERROR_RUNTIME_ERROR": "Si ocurre un error crítico, reinicia el programa y vuelve a intentarlo.", + "ERROR_GENERATE_UNKONW_EXCEPTION": "Ha ocurrido un error desconocido. Imposible generar desde el modelo.", + "ERROR_FOLDER_NOT_EXISTS": "El directorio especificado no existe.", + "ERROR_ENHANCE_IMAGE_NOT_SET": "Configura la imagen de entrada a generar.", + "ERROR_UNFOUND_GRAPHICS": "Si no se detecta el hardware necesario en el dispositivo, el programa se cerrará al hacer clic en OK." } diff --git a/WebUI/src/assets/i18n/id.json b/WebUI/src/assets/i18n/id.json index 2b259429..3b1f142d 100644 --- a/WebUI/src/assets/i18n/id.json +++ b/WebUI/src/assets/i18n/id.json @@ -1,168 +1,168 @@ { -"MAIN_TITLE": "AI PLAYGROUND", -"COM_SETTINGS": "Pengaturan", -"COM_MINI": "Minimalkan", -"COM_CLOSE": "Tutup", -"COM_RESTORE": "Kembalikan Default", -"COM_SD_PROMPT": "Masukkan perintah untuk membuat gambar", -"COM_LLM_PROMPT": "Masukkan perintah untuk membuat jawaban", -"COM_CLICK_UPLOAD": "- Klik Untuk Mengunggah Gambar -", -"COM_GENERATE": "Buat", -"COM_GENERATING": "Membuat", -"COM_HISTORY": "Riwayat", -"COM_POST_TO_ENHANCE_PROCESS": "Proses Posting Untuk Disempurnakan", -"COM_ZOOM_IN": "Lihat Ukuran Asli", -"COM_COPY": "Salin", -"COM_COPY_SUCCESS_TIP": "Salin Berhasil", -"COM_APPLY": "Terapkan", -"COM_DOWNLOAD": "Unduh", -"COM_REGENERATE": "Buat ulang", -"COM_DELETE": "Hapus", -"COM_DOWNLOAD_MODEL": "Model pengunduhan", -"COM_DOWNLOAD_SPEED": "Kecepatan pengunduhan", -"COM_LOADING_MODEL": "Memuat Model AI", -"COM_LOADING_MODEL_COMPONENTS": "Memuat Komponen Model AI", -"COM_NO_SELECTED": "Tidak Dipilih", -"COM_CONFIRM": "Konfirmasi", -"COM_CANCEL": "Batal", -"COM_VISIT": "KUNJUNGI", -"COM_REQUESTING": "Meminta", -"COM_DEFAULT": "Default", -"COM_STOP": "Berhenti", -"COM_OPEN_LOCATION": "Buka Lokasi", -"COM_OPEN_PARAMS": "Info Parameter", -"COM_LOW": "rendah", -"COM_HIGH": "tinggi", -"COM_FULLSCREEN": "Layar Penuh", -"COM_FULLSCREEN_EXIT": "Keluar dari Layar Penuh", -"SETTINGS_TAB_BASIC": "Pengaturan Dasar", -"SETTINGS_TAB_MODEL": "Model", -"SETTINGS_INFERENCE_DEVICE": "Perangkat Inferensi", -"SETTINGS_MODEL_IMAGE_RESOLUTION": "Resolusi Gambar", -"SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Standar", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", -"SETTINGS_MODEL_QUALITY": "Hasilkan Kualitas", -"SETTINGS_MODEL_QUALITY_STANDARD": "Standar", -"SETTINGS_MODEL_QUALITY_HIGH": "Kualitas Tinggi", -"SETTINGS_MODEL_QUALITY_FAST": "Cepat", -"SETTINGS_MODEL_QUALITY_MANUAL": "Manual", -"SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Opsi yang Dapat Disesuaikan", -"SETTINGS_MODEL_MANUAL_OPTIONS": "Opsi Manual", -"SETTINGS_MODEL_NEGATIVE_PROMPT": "Perintah Negatif", -"SETTINGS_MODEL_SEED": "Benih", -"SETTINGS_MODEL_GENERATE_NUMBER": "Buat Nomor Gambar", -"SETTINGS_MODEL_LLM_MODEL": "Model Bahasa Besar", -"SETTINGS_MODEL_IMAGE_PREVIEW": "Pratinjau Gambar", -"SETTINGS_MODEL_SAFE_CHECK": "Pemeriksaan Aman (Berlaku hanya pada SD 1.5)", -"SETTINGS_MODEL_IMAGE_MODEL": "Model Gambar", -"SETTINGS_MODEL_INPAINT_MODEL": "Model Inpaint/Outpaint", -"SETTINGS_MODEL_IMAGE_WIDTH": "Lebar", -"SETTINGS_MODEL_IMAGE_HEIGHT": "Tinggi", -"SETTINGS_MODEL_IMAGE_STEPS": "Langkah", -"SETTINGS_MODEL_IMAGE_CFG": "CFG", -"SETTINGS_MODEL_LORA": "Lora", -"SETTINGS_MODEL_SCHEDULER": "Penjadwal", -"SETTINGS_MODEL_LIST_CHANGE_TIP": "Perubahan dalam daftar model telah mengakibatkan modifikasi pada beberapa pengaturan Anda. Silakan lihat pengaturan dasar untuk informasi lebih lanjut.", -"SETTINGS_BASIC_LANGUAGE": "Bahasa", -"SETTINGS_BASIC_PATHS": "Jalur", -"SETTINGS_BASIC_LLM_CHECKPOINTS": "Jalur Model LLM", -"SETTINGS_MODEL_SD_CHECKPOINTS": "Jalur Model Gambar", -"SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Jalur Model Inpaint Gambar", -"SETTINGS_MODEL_SD_VAE": "Jalur Vae", -"SETTINGS_MODEL_SD_LORA": "Jalur Lora", -"SETTINGS_MODEL_SD_SCHEDULER": "Jalur Penjadwal", -"SETTINGS_MODEL_SD_PRESET_MODEL": "Model Default Gambar", -"SETTINGS_MODEL_SD_STANDARD_MODEL": "Model Default Standar", -"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Model Inpaint/OutPaint Default Standar", -"SETTINGS_MODEL_SD_HD_MODEL": "Model Default HD", -"SETTINGS_MODEL_RAG_MODEL": "Model Kueri Rag", -"SETTINGS_MODEL_EXIST": "Model sudah ada. Pengulangan pengunduhan tidak diperlukan.", -"SETTINGS_MODEL_DOWNLOAD": "Pengunduhan Model", -"SETTINGS_MODEL_DOWNLOAD_DESC": "Di bawah ini adalah daftar model yang dapat melakukan berbagai tugas Al di Al Playground. Tinjau ketentuan setiap model sebelum mengunduh dan menggunakan. Atau pilih untuk menambahkan dan memilih sumber model lain dengan mengubah jalur model dan default model di atas.", -"TAB_CREATE": "Buat", -"TAB_ENHANCE": "Tingkatkan", -"TAB_ANSWER": "Jawab", -"TAB_LEARN_MORE": "Pelajari Lebih Lanjut", -"ENHANCE_INPUT_IMAGE_REQUIRED": "Silakan tetapkan gambar input sebelum membuat", -"ENHANCE_UPSCALE": "Naikkan", -"ENHANCE_IMAGE_PROMPT": "Perintah Gambar", -"ENHANCE_INPAINT": "Inpaint", -"ENHANCE_OUTPAINT": "Outpaint", -"ENHANCE_UPSCALE_SCALE": "Naikkan", -"ENHANCE_UPSCALE_SCALE_X1_5": "Naikkan (1,5X)", -"ENHANCE_UPSCALE_SCALE_X2_0": "Naikkan (2.0X)", -"ENHANCE_UPSCALE_VARIATION": "Variasi", -"ENHANCE_UPSCALE_VARIATION_NONE": "Tidak ada", -"ENHANCE_UPSCALE_VARIATION_DISABLE": "Halus", -"ENHANCE_UPSCALE_VARIATION_STRONG": "Tajam", -"ENHANCE_INPAINT_TYPE": "Jenis", -"ENHANCE_INPAINT_FILL": "Isi dengan Sesuatu yang Baru", -"ENHANCE_INPAINT_FIX": "Perbaiki dan Perbaiki Area yang Ditandai", -"ENHANCE_INPAINT_MASK_REQUIRED": "Silakan gambar inpaint mask sebelum membuat", -"ENHANCE_INPAINT_USE_IMAGE_MODEL": "Gunakan Model Gambar Saat Ini", -"ENHANCE_COM_DENOISE": "Pengaruh", -"ENHANCE_OUTPAINT_DIRECTION": "Arah", -"ENHANCE_PREVIEW_BEFORE_PROCESS": "Sebelum Proses", -"ENHANCE_PREVIEW_AFTER_PROCESS": "Setelah Proses", -"ENHANCE_IMAGE_PROMPT_TIP": "Jelaskan bagaimana Anda ingin gambar tersebut diberi gaya. Sertakan deskripsi gambar asli yang ingin Anda pertahankan dan deskripsi tentang apa yang ingin Anda bedakan. Sesuaikan slider pengaruh untuk menambah atau mengurangi seberapa besar pengaruh perintah Anda terhadap hasil", -"ENHANCE_INPAINT_TIP": "Gunakan alat pena untuk menutupi suatu area. Jelaskan dalam perintah apa yang ingin Anda Perbaiki atau Isi. Pilih opsi Perbaiki atau Isi, lalu sesuaikan tingkat pengaruh yang dimiliki perintah tersebut", -"ENHANCE_OUTPAINT_TIP": "Pilih arah yang ingin Anda perluas, sesuaikan nilai denoise jika perlu, dan masukkan kata perintah di kotak teks. Gambar yang diperluas akan dibuat", -"ENHANCE_UPSCALE_TIP": "Unggah gambar yang perlu dimanipulasi, pilih faktor amplifikasi, sesuaikan amplitudo denoise (jika kurang dari 0,1, konten gambar tidak akan berubah, tetapi ukurannya akan tetap meningkat), masukkan kata perintah di kotak teks untuk membuat gambar yang diperbesar", -"ERROR_UNSUPPORTED_IMAGE_TYPE": "Hanya mendukung format gambar PNG, JPG, GIF, dan BMP", -"ANSWER_USER_NAME": "Anda", -"ANSWER_AI_NAME": "Taman bermain", -"ANSWER_ERROR_NOT_PROMPT": "Silakan masukkan perintah", -"ANSWER_ERROR_CLEAR_SESSION": "Hapus Riwayat Obrolan", -"ANSWER_RAG_ENABLE": "Aktifkan Kueri File", -"ANSWER_RAG_OPEN_DIALOG": "Buka Pengunggah File", -"DOWNLOADER_CONFRIM_TIP": "Anda kehilangan satu atau beberapa model yang diperlukan untuk menjalankannya. Apakah Anda ingin mengunduh model yang tercantum di bawah ini?", -"DOWNLOADER_MODEL": "Model", -"DOWNLOADER_INFO": "Info", -"DOWNLOADER_FILE_SIZE": "Ukuran", -"DOWNLOADER_REASON": "Alasan", -"DOWNLOADER_TERMS": "Kunjungi", -"DOWNLOADER_CONFLICT": "Tugas unduhan lain sedang berlangsung, dan tugas baru tidak dapat dimulai. Anda dapat membatalkan tugas unduhan saat ini dan memulai tugas unduhan baru", -"DOWNLOADER_TERMS_TIP": "Saya telah meninjau kartu model dan lisensi. Saya setuju dengan semua syarat dan ketentuan ingin mengunduh model pihak ketiga.", -"DOWNLOADER_FOR_ANSWER_GENERATE": "Model Jawaban", -"DOWNLOADER_FOR_RAG_QUERY": "Model Embed RAG", -"DOWNLOADER_FOR_IMAGE_GENERATE": "Model Gambar", -"DOWNLOADER_FOR_INAPINT_GENERATE": "Model Inpaint/OutPaint", -"DOWNLOADER_FOR_IMAGE_PREVIEW": "Model Pratinjau Gambar", -"DOWNLOADER_FOR_IMAGE_UPSCALE": "Model Peningkatan Gambar", -"DOWNLOADER_FOR_IMAGE_LORA": "Model Gambar Cepat", -"DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Model Unduhan Selesai", -"RAG_FILE_TOTAL_FORMAT": "Total file: {total}", -"RAG_DRAG_UPLOAD": "Seret file untuk diunggah", -"RAG_DRAG_UPLOAD_UNSUPPORTED": "Program saat ini dimulai dengan hak istimewa administrator. Karena keterbatasan UAC Windows, unggahan dengan cara drag and drop tidak dapat digunakan. Silakan gunakan tombol tambah di bagian atas untuk mengunggah berkas.", -"RAG_UPLOAD_MIME_TYPE": "Jenis berkas yang didukung:\n\tDokumen Teks: .txt .md\n\tDokumen Office: .doc(x)\n\tDokumen PDF: .pdf", -"RAG_ENABLE_TIP": "berikan jawaban hanya pada berkas yang diunggah", -"RAG_UPLOAD_TYPE_ERROR": "Ada jenis berkas yang tidak didukung dalam berkas yang diunggah, yang telah dikecualikan secara otomatis", -"RAG_UPLOAD_FILE_EXISTS": "Berkas yang diunggah {filename} sudah ada", -"RAG_ANALYZE_FILE_FAILED": "Berkas {filename} gagal dianalisis", -"RAG_USE_REJECT": "Daftar dokumen interaktif kosong, silakan unggah dokumen terlebih dahulu", -"RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Silakan tunggu hingga semua dokumen dianalisis", -"RAG_SOURCE": "Dari Sumber:", -"ERR_NOT_ENOUGH_DISK_SPACE": "Ruang disk tidak cukup. Diperlukan {requires_space}, tetapi hanya {free_space} ruang kosong yang tersedia", -"ERR_DOWNLOAD_FAILED": "Gagal mengunduh file model", -"ERROR_RUNTIME_ERROR": "Jika terjadi kegagalan kritis, silakan mulai ulang program dan coba lagi", -"ERROR_GENERATE_UNKONW_EXCEPTION": "Terjadi kesalahan yang tidak diketahui. gagal dibuat dari model", -"ERROR_FOLDER_NOT_EXISTS": "Direktori yang ditentukan tidak ada", -"ERROR_ENHANCE_IMAGE_NOT_SET": "Silakan konfigurasikan gambar masukan yang akan dibuat", -"ERROR_UNFOUND_GRAPHICS": "Jika perangkat keras yang diperlukan untuk eksekusi program tidak terdeteksi pada perangkat komputer, program akan berakhir setelah mengklik OK.", -"COM_DO_NOT_SHOW_AGAIN": "Jangan tampilkan lagi", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "Mode HD dapat mengakibatkan kinerja yang lebih lambat dari biasanya pada sistem dengan VRAM kurang dari 12 GB atau memori sistem 24 GB untuk PC Intel Core Ultra", -"SETTINGS_MODEL_IMAGE_SIZE": "Ukuran Gambar", -"SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", -"SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API Token", -"SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "Mohon masukkan token yang valid (hf_***).", -"INCREASE_FONT_SIZE": "Memperbesar Teks", -"DECREASE_FONT_SIZE": "Mengecilkan Teks", -"DOWNLOADER_GATED": "Terbatas", -"DOWNLOADER_GATED_INFO": "Beberapa model yang Anda coba unduh memiliki akses terbatas.", -"DOWNLOADER_GATED_TOKEN": "Silakan tambahkan token API huggingface.co Anda di pengaturan.", -"DOWNLOADER_GATED_ACCEPT": "Pastikan untuk mengunjungi halaman info model dan meminta akses. Jika Anda belum diberi akses ke model yang terbatas, pengunduhan akan gagal.", -"ERROR_PYTHON_BACKEND_INIT": "Inisialisasi backend gagal", -"ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "Backend inferensi AI gagal diinisialisasi. Coba mulai ulang aplikasi. Jika masalah berlanjut, Anda dapat memeriksa Detail untuk informasi tambahan tentang kesalahan tersebut.", -"ERROR_PYTHON_BACKEND_INIT_DETAILS": "Detail", -"ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "Buka Log" + "MAIN_TITLE": "AI PLAYGROUND", + "COM_SETTINGS": "Pengaturan", + "COM_MINI": "Minimalkan", + "COM_CLOSE": "Tutup", + "COM_RESTORE": "Kembalikan Default", + "COM_SD_PROMPT": "Masukkan perintah untuk membuat gambar", + "COM_LLM_PROMPT": "Masukkan perintah untuk membuat jawaban", + "COM_CLICK_UPLOAD": "- Klik Untuk Mengunggah Gambar -", + "COM_GENERATE": "Buat", + "COM_GENERATING": "Membuat", + "COM_HISTORY": "Riwayat", + "COM_POST_TO_ENHANCE_PROCESS": "Proses Posting Untuk Disempurnakan", + "COM_ZOOM_IN": "Lihat Ukuran Asli", + "COM_COPY": "Salin", + "COM_COPY_SUCCESS_TIP": "Salin Berhasil", + "COM_APPLY": "Terapkan", + "COM_DOWNLOAD": "Unduh", + "COM_REGENERATE": "Buat ulang", + "COM_DELETE": "Hapus", + "COM_DOWNLOAD_MODEL": "Model pengunduhan", + "COM_DOWNLOAD_SPEED": "Kecepatan pengunduhan", + "COM_LOADING_MODEL": "Memuat Model AI", + "COM_LOADING_MODEL_COMPONENTS": "Memuat Komponen Model AI", + "COM_NO_SELECTED": "Tidak Dipilih", + "COM_CONFIRM": "Konfirmasi", + "COM_CANCEL": "Batal", + "COM_VISIT": "KUNJUNGI", + "COM_REQUESTING": "Meminta", + "COM_DEFAULT": "Default", + "COM_STOP": "Berhenti", + "COM_OPEN_LOCATION": "Buka Lokasi", + "COM_OPEN_PARAMS": "Info Parameter", + "COM_LOW": "rendah", + "COM_HIGH": "tinggi", + "COM_FULLSCREEN": "Layar Penuh", + "COM_FULLSCREEN_EXIT": "Keluar dari Layar Penuh", + "SETTINGS_TAB_BASIC": "Pengaturan Dasar", + "SETTINGS_TAB_MODEL": "Model", + "SETTINGS_INFERENCE_DEVICE": "Perangkat Inferensi", + "SETTINGS_MODEL_IMAGE_RESOLUTION": "Resolusi Gambar", + "SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Standar", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", + "SETTINGS_MODEL_QUALITY": "Hasilkan Kualitas", + "SETTINGS_MODEL_QUALITY_STANDARD": "Standar", + "SETTINGS_MODEL_QUALITY_HIGH": "Kualitas Tinggi", + "SETTINGS_MODEL_QUALITY_FAST": "Cepat", + "SETTINGS_MODEL_QUALITY_MANUAL": "Manual", + "SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Opsi yang Dapat Disesuaikan", + "SETTINGS_MODEL_MANUAL_OPTIONS": "Opsi Manual", + "SETTINGS_MODEL_NEGATIVE_PROMPT": "Perintah Negatif", + "SETTINGS_MODEL_SEED": "Benih", + "SETTINGS_MODEL_GENERATE_NUMBER": "Buat Nomor Gambar", + "SETTINGS_MODEL_LLM_MODEL": "Model Bahasa Besar", + "SETTINGS_MODEL_IMAGE_PREVIEW": "Pratinjau Gambar", + "SETTINGS_MODEL_SAFE_CHECK": "Pemeriksaan Aman (Berlaku hanya pada SD 1.5)", + "SETTINGS_MODEL_IMAGE_MODEL": "Model Gambar", + "SETTINGS_MODEL_INPAINT_MODEL": "Model Inpaint/Outpaint", + "SETTINGS_MODEL_IMAGE_WIDTH": "Lebar", + "SETTINGS_MODEL_IMAGE_HEIGHT": "Tinggi", + "SETTINGS_MODEL_IMAGE_STEPS": "Langkah", + "SETTINGS_MODEL_IMAGE_CFG": "CFG", + "SETTINGS_MODEL_LORA": "Lora", + "SETTINGS_MODEL_SCHEDULER": "Penjadwal", + "SETTINGS_MODEL_LIST_CHANGE_TIP": "Perubahan dalam daftar model telah mengakibatkan modifikasi pada beberapa pengaturan Anda. Silakan lihat pengaturan dasar untuk informasi lebih lanjut.", + "SETTINGS_BASIC_LANGUAGE": "Bahasa", + "SETTINGS_BASIC_PATHS": "Jalur", + "SETTINGS_BASIC_LLM_CHECKPOINTS": "Jalur Model LLM", + "SETTINGS_MODEL_SD_CHECKPOINTS": "Jalur Model Gambar", + "SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Jalur Model Inpaint Gambar", + "SETTINGS_MODEL_SD_VAE": "Jalur Vae", + "SETTINGS_MODEL_SD_LORA": "Jalur Lora", + "SETTINGS_MODEL_SD_SCHEDULER": "Jalur Penjadwal", + "SETTINGS_MODEL_SD_PRESET_MODEL": "Model Default Gambar", + "SETTINGS_MODEL_SD_STANDARD_MODEL": "Model Default Standar", + "SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Model Inpaint/OutPaint Default Standar", + "SETTINGS_MODEL_SD_HD_MODEL": "Model Default HD", + "SETTINGS_MODEL_RAG_MODEL": "Model Kueri Rag", + "SETTINGS_MODEL_EXIST": "Model sudah ada. Pengulangan pengunduhan tidak diperlukan.", + "SETTINGS_MODEL_DOWNLOAD": "Pengunduhan Model", + "SETTINGS_MODEL_DOWNLOAD_DESC": "Di bawah ini adalah daftar model yang dapat melakukan berbagai tugas Al di Al Playground. Tinjau ketentuan setiap model sebelum mengunduh dan menggunakan. Atau pilih untuk menambahkan dan memilih sumber model lain dengan mengubah jalur model dan default model di atas.", + "TAB_CREATE": "Buat", + "TAB_ENHANCE": "Tingkatkan", + "TAB_ANSWER": "Jawab", + "TAB_LEARN_MORE": "Pelajari Lebih Lanjut", + "ENHANCE_INPUT_IMAGE_REQUIRED": "Silakan tetapkan gambar input sebelum membuat", + "ENHANCE_UPSCALE": "Naikkan", + "ENHANCE_IMAGE_PROMPT": "Perintah Gambar", + "ENHANCE_INPAINT": "Inpaint", + "ENHANCE_OUTPAINT": "Outpaint", + "ENHANCE_UPSCALE_SCALE": "Naikkan", + "ENHANCE_UPSCALE_SCALE_X1_5": "Naikkan (1,5X)", + "ENHANCE_UPSCALE_SCALE_X2_0": "Naikkan (2.0X)", + "ENHANCE_UPSCALE_VARIATION": "Variasi", + "ENHANCE_UPSCALE_VARIATION_NONE": "Tidak ada", + "ENHANCE_UPSCALE_VARIATION_DISABLE": "Halus", + "ENHANCE_UPSCALE_VARIATION_STRONG": "Tajam", + "ENHANCE_INPAINT_TYPE": "Jenis", + "ENHANCE_INPAINT_FILL": "Isi dengan Sesuatu yang Baru", + "ENHANCE_INPAINT_FIX": "Perbaiki dan Perbaiki Area yang Ditandai", + "ENHANCE_INPAINT_MASK_REQUIRED": "Silakan gambar inpaint mask sebelum membuat", + "ENHANCE_INPAINT_USE_IMAGE_MODEL": "Gunakan Model Gambar Saat Ini", + "ENHANCE_COM_DENOISE": "Pengaruh", + "ENHANCE_OUTPAINT_DIRECTION": "Arah", + "ENHANCE_PREVIEW_BEFORE_PROCESS": "Sebelum Proses", + "ENHANCE_PREVIEW_AFTER_PROCESS": "Setelah Proses", + "ENHANCE_IMAGE_PROMPT_TIP": "Jelaskan bagaimana Anda ingin gambar tersebut diberi gaya. Sertakan deskripsi gambar asli yang ingin Anda pertahankan dan deskripsi tentang apa yang ingin Anda bedakan. Sesuaikan slider pengaruh untuk menambah atau mengurangi seberapa besar pengaruh perintah Anda terhadap hasil", + "ENHANCE_INPAINT_TIP": "Gunakan alat pena untuk menutupi suatu area. Jelaskan dalam perintah apa yang ingin Anda Perbaiki atau Isi. Pilih opsi Perbaiki atau Isi, lalu sesuaikan tingkat pengaruh yang dimiliki perintah tersebut", + "ENHANCE_OUTPAINT_TIP": "Pilih arah yang ingin Anda perluas, sesuaikan nilai denoise jika perlu, dan masukkan kata perintah di kotak teks. Gambar yang diperluas akan dibuat", + "ENHANCE_UPSCALE_TIP": "Unggah gambar yang perlu dimanipulasi, pilih faktor amplifikasi, sesuaikan amplitudo denoise (jika kurang dari 0,1, konten gambar tidak akan berubah, tetapi ukurannya akan tetap meningkat), masukkan kata perintah di kotak teks untuk membuat gambar yang diperbesar", + "ERROR_UNSUPPORTED_IMAGE_TYPE": "Hanya mendukung format gambar PNG, JPG, GIF, dan BMP", + "ANSWER_USER_NAME": "Anda", + "ANSWER_AI_NAME": "Taman bermain", + "ANSWER_ERROR_NOT_PROMPT": "Silakan masukkan perintah", + "ANSWER_ERROR_CLEAR_SESSION": "Hapus Riwayat Obrolan", + "ANSWER_RAG_ENABLE": "Aktifkan Kueri File", + "ANSWER_RAG_OPEN_DIALOG": "Buka Pengunggah File", + "DOWNLOADER_CONFRIM_TIP": "Anda kehilangan satu atau beberapa model yang diperlukan untuk menjalankannya. Apakah Anda ingin mengunduh model yang tercantum di bawah ini?", + "DOWNLOADER_MODEL": "Model", + "DOWNLOADER_INFO": "Info", + "DOWNLOADER_FILE_SIZE": "Ukuran", + "DOWNLOADER_REASON": "Alasan", + "DOWNLOADER_TERMS": "Kunjungi", + "DOWNLOADER_CONFLICT": "Tugas unduhan lain sedang berlangsung, dan tugas baru tidak dapat dimulai. Anda dapat membatalkan tugas unduhan saat ini dan memulai tugas unduhan baru", + "DOWNLOADER_TERMS_TIP": "Saya telah meninjau kartu model dan lisensi. Saya setuju dengan semua syarat dan ketentuan ingin mengunduh model pihak ketiga.", + "DOWNLOADER_FOR_ANSWER_GENERATE": "Model Jawaban", + "DOWNLOADER_FOR_RAG_QUERY": "Model Embed RAG", + "DOWNLOADER_FOR_IMAGE_GENERATE": "Model Gambar", + "DOWNLOADER_FOR_INAPINT_GENERATE": "Model Inpaint/OutPaint", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "Model Pratinjau Gambar", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "Model Peningkatan Gambar", + "DOWNLOADER_FOR_IMAGE_LORA": "Model Gambar Cepat", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Model Unduhan Selesai", + "RAG_FILE_TOTAL_FORMAT": "Total file: {total}", + "RAG_DRAG_UPLOAD": "Seret file untuk diunggah", + "RAG_DRAG_UPLOAD_UNSUPPORTED": "Program saat ini dimulai dengan hak istimewa administrator. Karena keterbatasan UAC Windows, unggahan dengan cara drag and drop tidak dapat digunakan. Silakan gunakan tombol tambah di bagian atas untuk mengunggah berkas.", + "RAG_UPLOAD_MIME_TYPE": "Jenis berkas yang didukung:\n\tDokumen Teks: .txt .md\n\tDokumen Office: .doc(x)\n\tDokumen PDF: .pdf", + "RAG_ENABLE_TIP": "berikan jawaban hanya pada berkas yang diunggah", + "RAG_UPLOAD_TYPE_ERROR": "Ada jenis berkas yang tidak didukung dalam berkas yang diunggah, yang telah dikecualikan secara otomatis", + "RAG_UPLOAD_FILE_EXISTS": "Berkas yang diunggah {filename} sudah ada", + "RAG_ANALYZE_FILE_FAILED": "Berkas {filename} gagal dianalisis", + "RAG_USE_REJECT": "Daftar dokumen interaktif kosong, silakan unggah dokumen terlebih dahulu", + "RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Silakan tunggu hingga semua dokumen dianalisis", + "RAG_SOURCE": "Dari Sumber:", + "ERR_NOT_ENOUGH_DISK_SPACE": "Ruang disk tidak cukup. Diperlukan {requires_space}, tetapi hanya {free_space} ruang kosong yang tersedia", + "ERR_DOWNLOAD_FAILED": "Gagal mengunduh file model", + "ERROR_RUNTIME_ERROR": "Jika terjadi kegagalan kritis, silakan mulai ulang program dan coba lagi", + "ERROR_GENERATE_UNKONW_EXCEPTION": "Terjadi kesalahan yang tidak diketahui. gagal dibuat dari model", + "ERROR_FOLDER_NOT_EXISTS": "Direktori yang ditentukan tidak ada", + "ERROR_ENHANCE_IMAGE_NOT_SET": "Silakan konfigurasikan gambar masukan yang akan dibuat", + "ERROR_UNFOUND_GRAPHICS": "Jika perangkat keras yang diperlukan untuk eksekusi program tidak terdeteksi pada perangkat komputer, program akan berakhir setelah mengklik OK.", + "COM_DO_NOT_SHOW_AGAIN": "Jangan tampilkan lagi", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "Mode HD dapat mengakibatkan kinerja yang lebih lambat dari biasanya pada sistem dengan VRAM kurang dari 12 GB atau memori sistem 24 GB untuk PC Intel Core Ultra", + "SETTINGS_MODEL_IMAGE_SIZE": "Ukuran Gambar", + "SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", + "SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API Token", + "SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "Mohon masukkan token yang valid (hf_***).", + "INCREASE_FONT_SIZE": "Memperbesar Teks", + "DECREASE_FONT_SIZE": "Mengecilkan Teks", + "DOWNLOADER_GATED": "Terbatas", + "DOWNLOADER_GATED_INFO": "Beberapa model yang Anda coba unduh memiliki akses terbatas.", + "DOWNLOADER_GATED_TOKEN": "Silakan tambahkan token API huggingface.co Anda di pengaturan.", + "DOWNLOADER_GATED_ACCEPT": "Pastikan untuk mengunjungi halaman info model dan meminta akses. Jika Anda belum diberi akses ke model yang terbatas, pengunduhan akan gagal.", + "ERROR_PYTHON_BACKEND_INIT": "Inisialisasi backend gagal", + "ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "Backend inferensi AI gagal diinisialisasi. Coba mulai ulang aplikasi. Jika masalah berlanjut, Anda dapat memeriksa Detail untuk informasi tambahan tentang kesalahan tersebut.", + "ERROR_PYTHON_BACKEND_INIT_DETAILS": "Detail", + "ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "Buka Log" } diff --git a/WebUI/src/assets/i18n/it.json b/WebUI/src/assets/i18n/it.json index 6764f683..199fbfbc 100644 --- a/WebUI/src/assets/i18n/it.json +++ b/WebUI/src/assets/i18n/it.json @@ -1,152 +1,152 @@ { -"MAIN_TITLE": "AI PLAYGROUND", -"COM_SETTINGS": "Impostazioni", -"COM_MINI": "Minimizzare", -"COM_CLOSE": "Chiudere", -"COM_RESTORE": "Ripristinare le impostazioni predefinite", -"COM_SD_PROMPT": "Richiesta di input per generare l'immagine", -"COM_LLM_PROMPT": "Richiesta di input per generare una risposta", -"COM_CLICK_UPLOAD": "Cliccare per caricare l'immagine", -"COM_GENERATE": "Generare", -"COM_GENERATING": "Sto Generando", -"COM_HISTORY": "Cronologia", -"COM_POST_TO_ENHANCE_PROCESS": "Post per migliorare il processo", -"COM_ZOOM_IN": "Vedi in dimensione originale", -"COM_COPY": "Copia", -"COM_COPY_SUCCESS_TIP": "Copia Eseguita", -"COM_APPLY": "Applica", -"COM_DOWNLOAD": "Download", -"COM_REGENERATE": "Rigenera", -"COM_DELETE": "Cancella", -"COM_DOWNLOAD_MODEL": "Modelli da scaricare", -"COM_DOWNLOAD_SPEED": "Velocita' di download", -"COM_LOADING_MODEL": "Sto caricando il modello AI", -"COM_LOADING_MODEL_COMPONENTS": "Sto caricando i componenti del modello AI", -"COM_NO_SELECTED": "Non selezionato", -"COM_CONFIRM": "Conferma", -"COM_CANCEL": "Cancella", -"COM_VISIT": "VISITA", -"COM_REQUESTING": "Richiedi", -"COM_DEFAULT": "Predefinito", -"COM_STOP": "Stop", -"COM_OPEN_LOCATION": "Posizione Aperta", -"COM_OPEN_PARAMS": "Info Parametri", -"COM_LOW": "Basso", -"COM_HIGH": "Alto", -"COM_FULLSCREEN": "Schermo Intero", -"COM_FULLSCREEN_EXIT": "Esci da Schermo Intero", -"SETTINGS_TAB_BASIC": "Impostazioni di base", -"SETTINGS_TAB_MODEL": "Modelli", -"SETTINGS_INFERENCE_DEVICE": "Dispositivo di Inferenza", -"SETTINGS_MODEL_IMAGE_RESOLUTION": "Risoluzione Immagine", -"SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Standard", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", -"SETTINGS_MODEL_QUALITY": "Generare Qualità", -"SETTINGS_MODEL_QUALITY_STANDARD": "Standard", -"SETTINGS_MODEL_QUALITY_HIGH": "Alta Qualità", -"SETTINGS_MODEL_QUALITY_FAST": "Veloce", -"SETTINGS_MODEL_QUALITY_MANUAL": "Manuale", -"SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Opzioni Regolabili", -"SETTINGS_MODEL_MANUAL_OPTIONS": "Opzioni Manuali", -"SETTINGS_MODEL_NEGATIVE_PROMPT": "Richiesta Errata", -"SETTINGS_MODEL_SEED": "Seed", -"SETTINGS_MODEL_GENERATE_NUMBER": "Generare Numero Immagine", -"SETTINGS_MODEL_LLM_MODEL": "Large Language Model", -"SETTINGS_MODEL_IMAGE_PREVIEW": "Anteprima Immagine", -"SETTINGS_MODEL_SAFE_CHECK": "Controllo Sicurezza (efettivo solo su SD 1.5)", -"SETTINGS_MODEL_IMAGE_MODEL": "Modello Immagine", -"SETTINGS_MODEL_INPAINT_MODEL": "Modello Inpaint/Outpaint", -"SETTINGS_MODEL_IMAGE_WIDTH": "Larghezza", -"SETTINGS_MODEL_IMAGE_HEIGHT": "Altezza", -"SETTINGS_MODEL_IMAGE_STEPS": "Passi", -"SETTINGS_MODEL_IMAGE_CFG": "CFG", -"SETTINGS_MODEL_LORA": "Lora", -"SETTINGS_MODEL_SCHEDULER": "Pianificazione", -"SETTINGS_MODEL_LIST_CHANGE_TIP": "I cambiamenti nell'elenco dei modelli hanno comportato modifiche ad alcune delle tue impostazioni. Per ulteriori informazioni fare riferimento alle impostazioni di base", -"SETTINGS_BASIC_LANGUAGE": "Lingua", -"SETTINGS_BASIC_PATHS": "Percorsi", -"SETTINGS_BASIC_LLM_CHECKPOINTS": "Percorso del Modello LLM", -"SETTINGS_MODEL_SD_CHECKPOINTS": "Percorso del modello di immagine", -"SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Percorso del modello immagine Inpaint", -"SETTINGS_MODEL_SD_VAE": "Percorso Vae", -"SETTINGS_MODEL_SD_LORA": "Percorso Lora", -"SETTINGS_MODEL_SD_SCHEDULER": "Percorso Pianificazione", -"SETTINGS_MODEL_SD_PRESET_MODEL": "Modello predefinito dell'immagine", -"SETTINGS_MODEL_SD_STANDARD_MODEL": "Modello predefinito Standard", -"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Modello predefinito Standard di pittura/ripittura", -"SETTINGS_MODEL_SD_HD_MODEL": "Modello predefinito HD", -"SETTINGS_MODEL_RAG_MODEL": "modello di interrogazione RAG", -"SETTINGS_MODEL_EXIST": "Il modello esiste già. Non è necessario ripetere il download.", -"SETTINGS_MODEL_DOWNLOAD": "download del modello", -"SETTINGS_MODEL_DOWNLOAD_DESC": "Di seguito è riportato un elenco di modelli che possono eseguire varie attività Al in Al Playground. Rivedi i termini di ciascun modello prima del download e dell'utilizzo. In alternativa, scegli di aggiungere e selezionare altre sorgenti del modello modificando i percorsi del modello e le impostazioni predefinite del modello sopra.", -"TAB_CREATE": "Creare", -"TAB_ENHANCE": "Migliorare", -"TAB_ANSWER": "Rispondere", -"TAB_LEARN_MORE": "Saperne di piu'", -"ENHANCE_INPUT_IMAGE_REQUIRED": "Imposta l'immagine di input prima di generarla", -"ENHANCE_UPSCALE": "Aumenta Risoluzione", -"ENHANCE_IMAGE_PROMPT": "richiesta di immagine", -"ENHANCE_INPAINT": "Inpaint", -"ENHANCE_OUTPAINT": "Outpaint", -"ENHANCE_UPSCALE_SCALE": "Aumenta Risoluzione", -"ENHANCE_UPSCALE_SCALE_X1_5": "Aumenta Risoluzione (1.5X)", -"ENHANCE_UPSCALE_SCALE_X2_0": "Aumenta Risoluzione (2.0X)", -"ENHANCE_UPSCALE_VARIATION": "Variazione", -"ENHANCE_UPSCALE_VARIATION_NONE": "Nessuno", -"ENHANCE_UPSCALE_VARIATION_DISABLE": "Sottile", -"ENHANCE_UPSCALE_VARIATION_STRONG": "Spesso", -"ENHANCE_INPAINT_TYPE": "Tipo", -"ENHANCE_INPAINT_FILL": "Riempi con qualcosa di nuovo", -"ENHANCE_INPAINT_FIX": "Aggiusta e Ripara l'area selezionata", -"ENHANCE_INPAINT_MASK_REQUIRED": "Si prega di disegnare la zona per inpaint prima di generarla", -"ENHANCE_INPAINT_USE_IMAGE_MODEL": "Utilizza il modello di immagine corrente", -"ENHANCE_COM_DENOISE": "Influenza", -"ENHANCE_OUTPAINT_DIRECTION": "Direzione", -"ENHANCE_PREVIEW_BEFORE_PROCESS": "Prima del processo", -"ENHANCE_PREVIEW_AFTER_PROCESS": "Dopo il processo", -"ENHANCE_IMAGE_PROMPT_TIP": "Descrivi come vuoi che l'immagine sia stilizzata. Includi le descrizioni dell'immagine originale che desideri conservare e le descrizioni di ciò che desideri che sia differente. Regola il cursore dell'influenza per aumentare o diminuire l'influenza che il tuo comando ha sul risultato finale.", -"ENHANCE_INPAINT_TIP": "Utilizzare lo strumento penna per selezionare un'area. Descrivi nel comando cosa vuoi correggere o riempire. Selezionare l'opzione Correggi o Riempi, quindi regolare il livello di influenza del comando.", -"ENHANCE_OUTPAINT_TIP": "Seleziona la direzione in cui desideri espandere, regola il valore di riduzione rumore se necessario e inserisci una parola nella casella di testo. Verrà generata l'immagine espansa", -"ENHANCE_UPSCALE_TIP": "Carica l'immagine che deve essere manipolata, seleziona il fattore di amplificazione, regola l'ampiezza del rumore (se inferiore a 0,1, il contenuto dell'immagine non cambierà, ma la dimensione verrà comunque aumentata), inserisci la parola richiesta nella casella di testo per generare l'immagine ingrandita", -"ERROR_UNSUPPORTED_IMAGE_TYPE": "Supporta solo i formati immagine PNG, JPG, GIF e BMP", -"ANSWER_USER_NAME": "Tu", -"ANSWER_AI_NAME": "Playground", -"ANSWER_ERROR_NOT_PROMPT": "Si prega di inserire il comando", -"ANSWER_ERROR_CLEAR_SESSION": "Cancella la cronologia della chat", -"ANSWER_RAG_ENABLE": "Abilitare Interrogazione file", -"ANSWER_RAG_OPEN_DIALOG": "Apri Caricatore file", -"DOWNLOADER_CONFRIM_TIP": "Ti mancano uno o più modelli necessari per l'esecuzione. Desideri scaricare i modelli elencati di seguito?", -"DOWNLOADER_MODEL": "Modello", -"DOWNLOADER_INFO": "Informazioni", -"DOWNLOADER_FILE_SIZE": "Dimensione", -"DOWNLOADER_REASON": "Motivo", -"DOWNLOADER_TERMS": "Visita", -"DOWNLOADER_CONFLICT": "Un'altra attività di download è attualmente in corso e non è possibile avviarne una nuova. È possibile annullare l'attività di download corrente e avviarne una nuova", -"DOWNLOADER_TERMS_TIP": "Ho esaminato le schede modello e le licenze. Accetto tutti i termini e le condizioni. Vorrei scaricare i modelli di terze parti", -"DOWNLOADER_FOR_ANSWER_GENERATE": "Modello di Risposta", -"DOWNLOADER_FOR_RAG_QUERY": "Modello Embed RAG", -"DOWNLOADER_FOR_IMAGE_GENERATE": "Modello Immagine", -"DOWNLOADER_FOR_INAPINT_GENERATE": "Modello Inpaint/OutPaint", -"DOWNLOADER_FOR_IMAGE_PREVIEW": "Modello Anteprima Immagine", -"DOWNLOADER_FOR_IMAGE_UPSCALE": "Modello Immagine Aumentata", -"DOWNLOADER_FOR_IMAGE_LORA": "Modello Immagine Veloce", -"DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Download del modello completato", -"RAG_FILE_TOTAL_FORMAT": "File totali: {total}", -"RAG_DRAG_UPLOAD": "Trascina i file da caricare", -"RAG_DRAG_UPLOAD_UNSUPPORTED": "Il programma attuale viene avviato con privilegi da amministratore. A causa delle limitazioni dell'UAC di Windows, non è possibile utilizzare il caricamento tramite trascinamento. Utilizza in alto il pulsante Aggiungi per caricare i file", -"RAG_UPLOAD_MIME_TYPE": "Tipi di file supportati: \n\tText Doc: .txt .md\n\tOffice Doc: .doc(x)\n\tPDF Doc: .pdf", -"RAG_ENABLE_TIP": "fornire risposte solo su quelle caricate", -"RAG_UPLOAD_TYPE_ERROR": "Nel file caricato è presente un tipo di file non supportato, che è stato automaticamente escluso", -"RAG_UPLOAD_FILE_EXISTS": "Il file caricato {filename} esiste già", -"RAG_ANALYZE_FILE_FAILED": "L'analisi del file {filename} non è riuscita", -"RAG_USE_REJECT": "L'elenco dei documenti interattivi è vuoto, carica prima il documento", -"RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Si prega di attendere il completamento dell'analisi di tutti i documenti", -"RAG_SOURCE": "Dalla fonte:", -"ERR_NOT_ENOUGH_DISK_SPACE": "Spazio su disco insufficiente. Richiede {requires_space}, ma sono disponibili solo {free_space} di spazio libero", -"ERR_DOWNLOAD_FAILED": "Download del file del modello non riuscito", -"ERROR_RUNTIME_ERROR": "Se si verifica un errore critico, riavviare il programma e riprovare", -"ERROR_GENERATE_UNKONW_EXCEPTION": "Si è verificato un errore sconosciuto. Impossibile generare dal modello", -"ERROR_FOLDER_NOT_EXISTS": "La directory specificata non esiste", -"ERROR_ENHANCE_IMAGE_NOT_SET": "Configura l'immagine di input da generare", -"ERROR_UNFOUND_GRAPHICS": "Se sul dispositivo del computer non viene rilevato l'hardware necessario per l'esecuzione del programma, il programma verrà terminato facendo clic su OK" + "MAIN_TITLE": "AI PLAYGROUND", + "COM_SETTINGS": "Impostazioni", + "COM_MINI": "Minimizzare", + "COM_CLOSE": "Chiudere", + "COM_RESTORE": "Ripristinare le impostazioni predefinite", + "COM_SD_PROMPT": "Richiesta di input per generare l'immagine", + "COM_LLM_PROMPT": "Richiesta di input per generare una risposta", + "COM_CLICK_UPLOAD": "Cliccare per caricare l'immagine", + "COM_GENERATE": "Generare", + "COM_GENERATING": "Sto Generando", + "COM_HISTORY": "Cronologia", + "COM_POST_TO_ENHANCE_PROCESS": "Post per migliorare il processo", + "COM_ZOOM_IN": "Vedi in dimensione originale", + "COM_COPY": "Copia", + "COM_COPY_SUCCESS_TIP": "Copia Eseguita", + "COM_APPLY": "Applica", + "COM_DOWNLOAD": "Download", + "COM_REGENERATE": "Rigenera", + "COM_DELETE": "Cancella", + "COM_DOWNLOAD_MODEL": "Modelli da scaricare", + "COM_DOWNLOAD_SPEED": "Velocita' di download", + "COM_LOADING_MODEL": "Sto caricando il modello AI", + "COM_LOADING_MODEL_COMPONENTS": "Sto caricando i componenti del modello AI", + "COM_NO_SELECTED": "Non selezionato", + "COM_CONFIRM": "Conferma", + "COM_CANCEL": "Cancella", + "COM_VISIT": "VISITA", + "COM_REQUESTING": "Richiedi", + "COM_DEFAULT": "Predefinito", + "COM_STOP": "Stop", + "COM_OPEN_LOCATION": "Posizione Aperta", + "COM_OPEN_PARAMS": "Info Parametri", + "COM_LOW": "Basso", + "COM_HIGH": "Alto", + "COM_FULLSCREEN": "Schermo Intero", + "COM_FULLSCREEN_EXIT": "Esci da Schermo Intero", + "SETTINGS_TAB_BASIC": "Impostazioni di base", + "SETTINGS_TAB_MODEL": "Modelli", + "SETTINGS_INFERENCE_DEVICE": "Dispositivo di Inferenza", + "SETTINGS_MODEL_IMAGE_RESOLUTION": "Risoluzione Immagine", + "SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Standard", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", + "SETTINGS_MODEL_QUALITY": "Generare Qualità", + "SETTINGS_MODEL_QUALITY_STANDARD": "Standard", + "SETTINGS_MODEL_QUALITY_HIGH": "Alta Qualità", + "SETTINGS_MODEL_QUALITY_FAST": "Veloce", + "SETTINGS_MODEL_QUALITY_MANUAL": "Manuale", + "SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Opzioni Regolabili", + "SETTINGS_MODEL_MANUAL_OPTIONS": "Opzioni Manuali", + "SETTINGS_MODEL_NEGATIVE_PROMPT": "Richiesta Errata", + "SETTINGS_MODEL_SEED": "Seed", + "SETTINGS_MODEL_GENERATE_NUMBER": "Generare Numero Immagine", + "SETTINGS_MODEL_LLM_MODEL": "Large Language Model", + "SETTINGS_MODEL_IMAGE_PREVIEW": "Anteprima Immagine", + "SETTINGS_MODEL_SAFE_CHECK": "Controllo Sicurezza (efettivo solo su SD 1.5)", + "SETTINGS_MODEL_IMAGE_MODEL": "Modello Immagine", + "SETTINGS_MODEL_INPAINT_MODEL": "Modello Inpaint/Outpaint", + "SETTINGS_MODEL_IMAGE_WIDTH": "Larghezza", + "SETTINGS_MODEL_IMAGE_HEIGHT": "Altezza", + "SETTINGS_MODEL_IMAGE_STEPS": "Passi", + "SETTINGS_MODEL_IMAGE_CFG": "CFG", + "SETTINGS_MODEL_LORA": "Lora", + "SETTINGS_MODEL_SCHEDULER": "Pianificazione", + "SETTINGS_MODEL_LIST_CHANGE_TIP": "I cambiamenti nell'elenco dei modelli hanno comportato modifiche ad alcune delle tue impostazioni. Per ulteriori informazioni fare riferimento alle impostazioni di base", + "SETTINGS_BASIC_LANGUAGE": "Lingua", + "SETTINGS_BASIC_PATHS": "Percorsi", + "SETTINGS_BASIC_LLM_CHECKPOINTS": "Percorso del Modello LLM", + "SETTINGS_MODEL_SD_CHECKPOINTS": "Percorso del modello di immagine", + "SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Percorso del modello immagine Inpaint", + "SETTINGS_MODEL_SD_VAE": "Percorso Vae", + "SETTINGS_MODEL_SD_LORA": "Percorso Lora", + "SETTINGS_MODEL_SD_SCHEDULER": "Percorso Pianificazione", + "SETTINGS_MODEL_SD_PRESET_MODEL": "Modello predefinito dell'immagine", + "SETTINGS_MODEL_SD_STANDARD_MODEL": "Modello predefinito Standard", + "SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Modello predefinito Standard di pittura/ripittura", + "SETTINGS_MODEL_SD_HD_MODEL": "Modello predefinito HD", + "SETTINGS_MODEL_RAG_MODEL": "modello di interrogazione RAG", + "SETTINGS_MODEL_EXIST": "Il modello esiste già. Non è necessario ripetere il download.", + "SETTINGS_MODEL_DOWNLOAD": "download del modello", + "SETTINGS_MODEL_DOWNLOAD_DESC": "Di seguito è riportato un elenco di modelli che possono eseguire varie attività Al in Al Playground. Rivedi i termini di ciascun modello prima del download e dell'utilizzo. In alternativa, scegli di aggiungere e selezionare altre sorgenti del modello modificando i percorsi del modello e le impostazioni predefinite del modello sopra.", + "TAB_CREATE": "Creare", + "TAB_ENHANCE": "Migliorare", + "TAB_ANSWER": "Rispondere", + "TAB_LEARN_MORE": "Saperne di piu'", + "ENHANCE_INPUT_IMAGE_REQUIRED": "Imposta l'immagine di input prima di generarla", + "ENHANCE_UPSCALE": "Aumenta Risoluzione", + "ENHANCE_IMAGE_PROMPT": "richiesta di immagine", + "ENHANCE_INPAINT": "Inpaint", + "ENHANCE_OUTPAINT": "Outpaint", + "ENHANCE_UPSCALE_SCALE": "Aumenta Risoluzione", + "ENHANCE_UPSCALE_SCALE_X1_5": "Aumenta Risoluzione (1.5X)", + "ENHANCE_UPSCALE_SCALE_X2_0": "Aumenta Risoluzione (2.0X)", + "ENHANCE_UPSCALE_VARIATION": "Variazione", + "ENHANCE_UPSCALE_VARIATION_NONE": "Nessuno", + "ENHANCE_UPSCALE_VARIATION_DISABLE": "Sottile", + "ENHANCE_UPSCALE_VARIATION_STRONG": "Spesso", + "ENHANCE_INPAINT_TYPE": "Tipo", + "ENHANCE_INPAINT_FILL": "Riempi con qualcosa di nuovo", + "ENHANCE_INPAINT_FIX": "Aggiusta e Ripara l'area selezionata", + "ENHANCE_INPAINT_MASK_REQUIRED": "Si prega di disegnare la zona per inpaint prima di generarla", + "ENHANCE_INPAINT_USE_IMAGE_MODEL": "Utilizza il modello di immagine corrente", + "ENHANCE_COM_DENOISE": "Influenza", + "ENHANCE_OUTPAINT_DIRECTION": "Direzione", + "ENHANCE_PREVIEW_BEFORE_PROCESS": "Prima del processo", + "ENHANCE_PREVIEW_AFTER_PROCESS": "Dopo il processo", + "ENHANCE_IMAGE_PROMPT_TIP": "Descrivi come vuoi che l'immagine sia stilizzata. Includi le descrizioni dell'immagine originale che desideri conservare e le descrizioni di ciò che desideri che sia differente. Regola il cursore dell'influenza per aumentare o diminuire l'influenza che il tuo comando ha sul risultato finale.", + "ENHANCE_INPAINT_TIP": "Utilizzare lo strumento penna per selezionare un'area. Descrivi nel comando cosa vuoi correggere o riempire. Selezionare l'opzione Correggi o Riempi, quindi regolare il livello di influenza del comando.", + "ENHANCE_OUTPAINT_TIP": "Seleziona la direzione in cui desideri espandere, regola il valore di riduzione rumore se necessario e inserisci una parola nella casella di testo. Verrà generata l'immagine espansa", + "ENHANCE_UPSCALE_TIP": "Carica l'immagine che deve essere manipolata, seleziona il fattore di amplificazione, regola l'ampiezza del rumore (se inferiore a 0,1, il contenuto dell'immagine non cambierà, ma la dimensione verrà comunque aumentata), inserisci la parola richiesta nella casella di testo per generare l'immagine ingrandita", + "ERROR_UNSUPPORTED_IMAGE_TYPE": "Supporta solo i formati immagine PNG, JPG, GIF e BMP", + "ANSWER_USER_NAME": "Tu", + "ANSWER_AI_NAME": "Playground", + "ANSWER_ERROR_NOT_PROMPT": "Si prega di inserire il comando", + "ANSWER_ERROR_CLEAR_SESSION": "Cancella la cronologia della chat", + "ANSWER_RAG_ENABLE": "Abilitare Interrogazione file", + "ANSWER_RAG_OPEN_DIALOG": "Apri Caricatore file", + "DOWNLOADER_CONFRIM_TIP": "Ti mancano uno o più modelli necessari per l'esecuzione. Desideri scaricare i modelli elencati di seguito?", + "DOWNLOADER_MODEL": "Modello", + "DOWNLOADER_INFO": "Informazioni", + "DOWNLOADER_FILE_SIZE": "Dimensione", + "DOWNLOADER_REASON": "Motivo", + "DOWNLOADER_TERMS": "Visita", + "DOWNLOADER_CONFLICT": "Un'altra attività di download è attualmente in corso e non è possibile avviarne una nuova. È possibile annullare l'attività di download corrente e avviarne una nuova", + "DOWNLOADER_TERMS_TIP": "Ho esaminato le schede modello e le licenze. Accetto tutti i termini e le condizioni. Vorrei scaricare i modelli di terze parti", + "DOWNLOADER_FOR_ANSWER_GENERATE": "Modello di Risposta", + "DOWNLOADER_FOR_RAG_QUERY": "Modello Embed RAG", + "DOWNLOADER_FOR_IMAGE_GENERATE": "Modello Immagine", + "DOWNLOADER_FOR_INAPINT_GENERATE": "Modello Inpaint/OutPaint", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "Modello Anteprima Immagine", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "Modello Immagine Aumentata", + "DOWNLOADER_FOR_IMAGE_LORA": "Modello Immagine Veloce", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Download del modello completato", + "RAG_FILE_TOTAL_FORMAT": "File totali: {total}", + "RAG_DRAG_UPLOAD": "Trascina i file da caricare", + "RAG_DRAG_UPLOAD_UNSUPPORTED": "Il programma attuale viene avviato con privilegi da amministratore. A causa delle limitazioni dell'UAC di Windows, non è possibile utilizzare il caricamento tramite trascinamento. Utilizza in alto il pulsante Aggiungi per caricare i file", + "RAG_UPLOAD_MIME_TYPE": "Tipi di file supportati: \n\tText Doc: .txt .md\n\tOffice Doc: .doc(x)\n\tPDF Doc: .pdf", + "RAG_ENABLE_TIP": "fornire risposte solo su quelle caricate", + "RAG_UPLOAD_TYPE_ERROR": "Nel file caricato è presente un tipo di file non supportato, che è stato automaticamente escluso", + "RAG_UPLOAD_FILE_EXISTS": "Il file caricato {filename} esiste già", + "RAG_ANALYZE_FILE_FAILED": "L'analisi del file {filename} non è riuscita", + "RAG_USE_REJECT": "L'elenco dei documenti interattivi è vuoto, carica prima il documento", + "RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Si prega di attendere il completamento dell'analisi di tutti i documenti", + "RAG_SOURCE": "Dalla fonte:", + "ERR_NOT_ENOUGH_DISK_SPACE": "Spazio su disco insufficiente. Richiede {requires_space}, ma sono disponibili solo {free_space} di spazio libero", + "ERR_DOWNLOAD_FAILED": "Download del file del modello non riuscito", + "ERROR_RUNTIME_ERROR": "Se si verifica un errore critico, riavviare il programma e riprovare", + "ERROR_GENERATE_UNKONW_EXCEPTION": "Si è verificato un errore sconosciuto. Impossibile generare dal modello", + "ERROR_FOLDER_NOT_EXISTS": "La directory specificata non esiste", + "ERROR_ENHANCE_IMAGE_NOT_SET": "Configura l'immagine di input da generare", + "ERROR_UNFOUND_GRAPHICS": "Se sul dispositivo del computer non viene rilevato l'hardware necessario per l'esecuzione del programma, il programma verrà terminato facendo clic su OK" } diff --git a/WebUI/src/assets/i18n/ja.json b/WebUI/src/assets/i18n/ja.json index d69446f7..46358bf5 100644 --- a/WebUI/src/assets/i18n/ja.json +++ b/WebUI/src/assets/i18n/ja.json @@ -1,168 +1,168 @@ { -"MAIN_TITLE": "AI PLAYGROUND", -"COM_SETTINGS": "設定", -"COM_MINI": "最小化", -"COM_CLOSE": "閉じる", -"COM_RESTORE": "デフォルトに戻す", -"COM_SD_PROMPT": "画像を生成するための入力プロンプト", -"COM_LLM_PROMPT": "回答を生成するための入力プロンプト", -"COM_CLICK_UPLOAD": "- クリックして画像をアップロード -", -"COM_GENERATE": "生成", -"COM_GENERATING": "生成中", -"COM_HISTORY": "履歴", -"COM_POST_TO_ENHANCE_PROCESS": "ポストエンハンスプロセス", -"COM_ZOOM_IN": "元のサイズで表示", -"COM_COPY": "コピー", -"COM_COPY_SUCCESS_TIP": "コピー成功", -"COM_APPLY": "適用", -"COM_DOWNLOAD": "ダウンロード", -"COM_REGENERATE": "再生成", -"COM_DELETE": "削除", -"COM_DOWNLOAD_MODEL": "モデルをダウンロードしています", -"COM_DOWNLOAD_SPEED": "ダウンロード速度", -"COM_LOADING_MODEL": "AI モデルをロードしています", -"COM_LOADING_MODEL_COMPONENTS": "AI モデル コンポーネントをロードしています", -"COM_NO_SELECTED": "選択されていません", -"COM_CONFIRM": "確認", -"COM_CANCEL": "キャンセル", -"COM_VISIT": "VISIT", -"COM_REQUESTING": "リクエストしています", -"COM_DEFAULT": "デフォルト", -"COM_STOP": "停止", -"COM_OPEN_LOCATION": "場所を開く", -"COM_OPEN_PARAMS": "パラメータ情報", -"COM_LOW": "低", -"COM_HIGH": "高", -"COM_FULLSCREEN": "全画面", -"COM_FULLSCREEN_EXIT": "全画面終了", -"SETTINGS_TAB_BASIC": "基本設定", -"SETTINGS_TAB_MODEL": "モデル", -"SETTINGS_INFERENCE_DEVICE": "推論デバイス", -"SETTINGS_MODEL_IMAGE_RESOLUTION": "画像解像度", -"SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "標準", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", -"SETTINGS_MODEL_QUALITY": "生成品質", -"SETTINGS_MODEL_QUALITY_STANDARD": "標準", -"SETTINGS_MODEL_QUALITY_HIGH": "高品質", -"SETTINGS_MODEL_QUALITY_FAST": "高速", -"SETTINGS_MODEL_QUALITY_MANUAL": "手動", -"SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "調整可能なオプション", -"SETTINGS_MODEL_MANUAL_OPTIONS": "手動オプション", -"SETTINGS_MODEL_NEGATIVE_PROMPT": "否定プロンプト", -"SETTINGS_MODEL_SEED": "シード", -"SETTINGS_MODEL_GENERATE_NUMBER": "画像番号を生成", -"SETTINGS_MODEL_LLM_MODEL": "大規模言語モデル", -"SETTINGS_MODEL_IMAGE_PREVIEW": "画像プレビュー", -"SETTINGS_MODEL_SAFE_CHECK": "セーフチェック(SD 1.5 のみ有効)", -"SETTINGS_MODEL_IMAGE_MODEL": "画像モデル", -"SETTINGS_MODEL_INPAINT_MODEL": "インペイント/アウトペイントモデル", -"SETTINGS_MODEL_IMAGE_WIDTH": "幅」", -"SETTINGS_MODEL_IMAGE_HEIGHT": "高さ", -"SETTINGS_MODEL_IMAGE_STEPS": "ステップ", -"SETTINGS_MODEL_IMAGE_CFG": "CFG", -"SETTINGS_MODEL_LORA": "Lora", -"SETTINGS_MODEL_SCHEDULER": "スケジューラ", -"SETTINGS_MODEL_LIST_CHANGE_TIP": "モデル リストの変更により、一部の設定が変更されました。詳細については、基本設定を参照してください。", -"SETTINGS_BASIC_LANGUAGE": "言語", -"SETTINGS_BASIC_PATHS": "パス", -"SETTINGS_BASIC_LLM_CHECKPOINTS": "LLM モデル パス", -"SETTINGS_MODEL_SD_CHECKPOINTS": "画像モデル パス", -"SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "画像インペイント モデル パス", -"SETTINGS_MODEL_SD_VAE": "Vae パス", -"SETTINGS_MODEL_SD_LORA": "Lora パス", -"SETTINGS_MODEL_SD_SCHEDULER": "スケジューラ パス", -"SETTINGS_MODEL_SD_PRESET_MODEL": "画像のデフォルトモデル", -"SETTINGS_MODEL_SD_STANDARD_MODEL": "標準のデフォルトモデル", -"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "標準のデフォルトInpaint/OutPaintモデル", -"SETTINGS_MODEL_SD_HD_MODEL": "HDのデフォルトモデル", -"SETTINGS_MODEL_RAG_MODEL": "Rag Queryモデル", -"SETTINGS_MODEL_EXIST": "モデルはすでに存在します。ダウンロードを繰り返す必要はありません。", -"SETTINGS_MODEL_DOWNLOAD": "モデルのダウンロード", -"SETTINGS_MODEL_DOWNLOAD_DESC": "以下は、Al PlaygroundでさまざまなAlタスクを実行できるモデルのリストです。ダウンロードして使用する前に、各モデルの条件を確認してください。または、上記のモデル パスとモデルのデフォルトを変更して、他のモデル ソースを追加して選択することもできます。", -"TAB_CREATE": "作成", -"TAB_ENHANCE": "強化", -"TAB_ANSWER": "回答", -"TAB_LEARN_MORE": "詳細", -"ENHANCE_INPUT_IMAGE_REQUIRED": "生成前に入力イメージを設定してください", -"ENHANCE_UPSCALE": "アップスケール", -"ENHANCE_IMAGE_PROMPT": "イメージ プロンプト", -"ENHANCE_INPAINT": "インペイント", -"ENHANCE_OUTPAINT": "アウトペイント", -"ENHANCE_UPSCALE_SCALE": "アップスケール", -"ENHANCE_UPSCALE_SCALE_X1_5": "アップスケール (1.5 倍)", -"ENHANCE_UPSCALE_SCALE_X2_0": "アップスケール (2.X)", -"ENHANCE_UPSCALE_VARIATION": "バリエーション", -"ENHANCE_UPSCALE_VARIATION_NONE": "なし", -"ENHANCE_UPSCALE_VARIATION_DISABLE": "微妙", -"ENHANCE_UPSCALE_VARIATION_STRONG": "強い", -"ENHANCE_INPAINT_TYPE": "タイプ", -"ENHANCE_INPAINT_FILL": "新しいもので塗りつぶす", -"ENHANCE_INPAINT_FIX": "マスクされた領域を修正して修復する", -"ENHANCE_INPAINT_MASK_REQUIRED": "生成する前にインペイントマスクを描画してください", -"ENHANCE_INPAINT_USE_IMAGE_MODEL": "現在の画像モデルを使用する", -"ENHANCE_COM_DENOISE": "影響", -"ENHANCE_OUTPAINT_DIRECTION": "方向", -"ENHANCE_PREVIEW_BEFORE_PROCESS": "処理前", -"ENHANCE_PREVIEW_AFTER_PROCESS": "処理後", -"ENHANCE_IMAGE_PROMPT_TIP": "画像をどのようにスタイル設定するかを説明します。保持したい元の画像の説明と、変更したい点の説明を含めます。影響スライダーを調整して、プロンプトが結果に与える影響を増減します", -"ENHANCE_INPAINT_TIP": "ペン ツールを使用して領域をマスクします。プロンプトで、修正または塗りつぶしたいものを説明します。[修正] または [塗りつぶし] オプションを選択してから、プロンプトの影響レベルを調整します", -"ENHANCE_OUTPAINT_TIP": "拡大する方向を選択し、必要に応じてノイズ除去の値を調整し、テキスト ボックスにプロンプトの単語を入力します。拡大画像が生成されます", -"ENHANCE_UPSCALE_TIP": "操作する必要がある画像をアップロードし、増幅係数を選択し、ノイズ除去の振幅を調整します (0.1 未満の場合、画像の内容は変更されませんが、サイズは拡大されます)。テキスト ボックスにプロンプト ワードを入力して拡大画像を生成します", -"ERROR_UNSUPPORTED_IMAGE_TYPE": "PNG、JPG、GIF、および BMP の画像形式のみをサポートします", -"ANSWER_USER_NAME": "あなた", -"ANSWER_AI_NAME": "プレイグラウンド", -"ANSWER_ERROR_NOT_PROMPT": "プロンプトを入力してください", -"ANSWER_ERROR_CLEAR_SESSION": "チャット履歴を消去します", -"ANSWER_RAG_ENABLE": "ファイル クエリを有効にします", -"ANSWER_RAG_OPEN_DIALOG": "ファイル アップローダーを開きます", -"DOWNLOADER_CONFRIM_TIP": "実行に必要なモデルが 1 つ以上ありません。以下にリストされているモデルをダウンロードしますか?", -"DOWNLOADER_MODEL": "モデル", -"DOWNLOADER_INFO": "情報", -"DOWNLOADER_FILE_SIZE": "サイズ", -"DOWNLOADER_REASON": "理由", -"DOWNLOADER_TERMS": "訪問", -"DOWNLOADER_CONFLICT": "現在別のダウンロード タスクが進行中のため、新しいタスクを開始できません。現在のダウンロード タスクをキャンセルして、新しいダウンロード タスクを開始できます", -"DOWNLOADER_TERMS_TIP": "モデル カードとライセンスを確認しました。すべての利用規約に同意し、サードパーティのモデルをダウンロードします。", -"DOWNLOADER_FOR_ANSWER_GENERATE": "回答モデル", -"DOWNLOADER_FOR_RAG_QUERY": "RAG 埋め込みモデル", -"DOWNLOADER_FOR_IMAGE_GENERATE": "画像モデル", -"DOWNLOADER_FOR_INAPINT_GENERATE": "インペイント/アウトペイントモデル", -"DOWNLOADER_FOR_IMAGE_PREVIEW": "画像プレビューモデル", -"DOWNLOADER_FOR_IMAGE_UPSCALE": "画像アップスケールモデル", -"DOWNLOADER_FOR_IMAGE_LORA": "高速画像モデル", -"DOWNLOADER_DONWLOAD_TASK_PROGRESS": "モデルのダウンロードが完了しました", -"RAG_FILE_TOTAL_FORMAT": "ファイルの合計: {total}", -"RAG_DRAG_UPLOAD": "アップロードするファイルをドラッグしてください", -"RAG_DRAG_UPLOAD_UNSUPPORTED": "現在のプログラムは管理者権限で起動されています。Windows UAC の制限により、ドラッグ アンド ドロップによるアップロードは使用できません。ファイルをアップロードするには、上部にある追加ボタンを使用してください。", -"RAG_UPLOAD_MIME_TYPE": "サポートされているファイルの種類:\n\tテキスト ドキュメント: .txt .md\n\tOffice ドキュメント: .doc(x)\n\tPDF ドキュメント: .pdf", -"RAG_ENABLE_TIP": "アップロードされたものにのみ回答を提供します", -"RAG_UPLOAD_TYPE_ERROR": "アップロードされたファイルにはサポートされていないファイルの種類があるため、自動的に除外されました", -"RAG_UPLOAD_FILE_EXISTS": "アップロードされたファイル {filename} は既に存在します", -"RAG_ANALYZE_FILE_FAILED": "ファイル {filename} の分析に失敗しました", -"RAG_USE_REJECT": "インタラクティブ ドキュメント リストが空です。まずドキュメントをアップロードしてください", -"RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "すべてのドキュメントが分析されるまでお待ちください", -"RAG_SOURCE": "ソース:", -"ERR_NOT_ENOUGH_DISK_SPACE": "ディスク容量が足りません。{requires_space} が必要ですが、使用可能な空き容量は {free_space} のみです", -"ERR_DOWNLOAD_FAILED": "モデル ファイルのダウンロードに失敗しました", -"ERROR_RUNTIME_ERROR": "重大な障害が発生した場合は、プログラムを再起動して再試行してください", -"ERROR_GENERATE_UNKONW_EXCEPTION": "不明なエラーが発生しました。モデルから生成できませんでした", -"ERROR_FOLDER_NOT_EXISTS": "指定されたディレクトリが存在しません", -"ERROR_ENHANCE_IMAGE_NOT_SET": "生成する入力イメージを設定してください", -"ERROR_UNFOUND_GRAPHICS": "プログラムの実行に必要なハードウェアがコンピューター デバイス上で検出されない場合、[OK] をクリックするとプログラムは終了します。 ", -"COM_DO_NOT_SHOW_AGAIN": "再び表示しない", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "12GB以下のVRAMまたは24GB以下のシステムメモリー搭載のCore Utra PCにて、HDモード選択時には通常よりも遅い場合があります", -"SETTINGS_MODEL_IMAGE_SIZE": "イメージサイズ", -"SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", -"SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API トークン", -"SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "有効なトークンを入力してください (hf_***)", -"INCREASE_FONT_SIZE": "テキスト拡大", -"DECREASE_FONT_SIZE": "テキスト縮小", -"DOWNLOADER_GATED": "ゲーテッド(Gated)", -"DOWNLOADER_GATED_INFO": "ダウンロードを試みているモデルの中にはゲーテッドされているものがあります。", -"DOWNLOADER_GATED_TOKEN": "設定にて huggingface.co API トークンを入力してください", -"DOWNLOADER_GATED_ACCEPT": "モデルの情報ページに行きアクセス権を申請してください。承認されていない場合には該当のモデルがゲーテッドされ、ダウンロードできません。", -"ERROR_PYTHON_BACKEND_INIT": "バックエンド初期化に失敗", -"ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "AI推論のバックエンド初期化に失敗しました。アプリケーションを再起動してください。問題が再発する様でしたら、詳細にある追加情報を参照ください。", -"ERROR_PYTHON_BACKEND_INIT_DETAILS": "詳細", -"ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "ログを開く" + "MAIN_TITLE": "AI PLAYGROUND", + "COM_SETTINGS": "設定", + "COM_MINI": "最小化", + "COM_CLOSE": "閉じる", + "COM_RESTORE": "デフォルトに戻す", + "COM_SD_PROMPT": "画像を生成するための入力プロンプト", + "COM_LLM_PROMPT": "回答を生成するための入力プロンプト", + "COM_CLICK_UPLOAD": "- クリックして画像をアップロード -", + "COM_GENERATE": "生成", + "COM_GENERATING": "生成中", + "COM_HISTORY": "履歴", + "COM_POST_TO_ENHANCE_PROCESS": "ポストエンハンスプロセス", + "COM_ZOOM_IN": "元のサイズで表示", + "COM_COPY": "コピー", + "COM_COPY_SUCCESS_TIP": "コピー成功", + "COM_APPLY": "適用", + "COM_DOWNLOAD": "ダウンロード", + "COM_REGENERATE": "再生成", + "COM_DELETE": "削除", + "COM_DOWNLOAD_MODEL": "モデルをダウンロードしています", + "COM_DOWNLOAD_SPEED": "ダウンロード速度", + "COM_LOADING_MODEL": "AI モデルをロードしています", + "COM_LOADING_MODEL_COMPONENTS": "AI モデル コンポーネントをロードしています", + "COM_NO_SELECTED": "選択されていません", + "COM_CONFIRM": "確認", + "COM_CANCEL": "キャンセル", + "COM_VISIT": "VISIT", + "COM_REQUESTING": "リクエストしています", + "COM_DEFAULT": "デフォルト", + "COM_STOP": "停止", + "COM_OPEN_LOCATION": "場所を開く", + "COM_OPEN_PARAMS": "パラメータ情報", + "COM_LOW": "低", + "COM_HIGH": "高", + "COM_FULLSCREEN": "全画面", + "COM_FULLSCREEN_EXIT": "全画面終了", + "SETTINGS_TAB_BASIC": "基本設定", + "SETTINGS_TAB_MODEL": "モデル", + "SETTINGS_INFERENCE_DEVICE": "推論デバイス", + "SETTINGS_MODEL_IMAGE_RESOLUTION": "画像解像度", + "SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "標準", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", + "SETTINGS_MODEL_QUALITY": "生成品質", + "SETTINGS_MODEL_QUALITY_STANDARD": "標準", + "SETTINGS_MODEL_QUALITY_HIGH": "高品質", + "SETTINGS_MODEL_QUALITY_FAST": "高速", + "SETTINGS_MODEL_QUALITY_MANUAL": "手動", + "SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "調整可能なオプション", + "SETTINGS_MODEL_MANUAL_OPTIONS": "手動オプション", + "SETTINGS_MODEL_NEGATIVE_PROMPT": "否定プロンプト", + "SETTINGS_MODEL_SEED": "シード", + "SETTINGS_MODEL_GENERATE_NUMBER": "画像番号を生成", + "SETTINGS_MODEL_LLM_MODEL": "大規模言語モデル", + "SETTINGS_MODEL_IMAGE_PREVIEW": "画像プレビュー", + "SETTINGS_MODEL_SAFE_CHECK": "セーフチェック(SD 1.5 のみ有効)", + "SETTINGS_MODEL_IMAGE_MODEL": "画像モデル", + "SETTINGS_MODEL_INPAINT_MODEL": "インペイント/アウトペイントモデル", + "SETTINGS_MODEL_IMAGE_WIDTH": "幅」", + "SETTINGS_MODEL_IMAGE_HEIGHT": "高さ", + "SETTINGS_MODEL_IMAGE_STEPS": "ステップ", + "SETTINGS_MODEL_IMAGE_CFG": "CFG", + "SETTINGS_MODEL_LORA": "Lora", + "SETTINGS_MODEL_SCHEDULER": "スケジューラ", + "SETTINGS_MODEL_LIST_CHANGE_TIP": "モデル リストの変更により、一部の設定が変更されました。詳細については、基本設定を参照してください。", + "SETTINGS_BASIC_LANGUAGE": "言語", + "SETTINGS_BASIC_PATHS": "パス", + "SETTINGS_BASIC_LLM_CHECKPOINTS": "LLM モデル パス", + "SETTINGS_MODEL_SD_CHECKPOINTS": "画像モデル パス", + "SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "画像インペイント モデル パス", + "SETTINGS_MODEL_SD_VAE": "Vae パス", + "SETTINGS_MODEL_SD_LORA": "Lora パス", + "SETTINGS_MODEL_SD_SCHEDULER": "スケジューラ パス", + "SETTINGS_MODEL_SD_PRESET_MODEL": "画像のデフォルトモデル", + "SETTINGS_MODEL_SD_STANDARD_MODEL": "標準のデフォルトモデル", + "SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "標準のデフォルトInpaint/OutPaintモデル", + "SETTINGS_MODEL_SD_HD_MODEL": "HDのデフォルトモデル", + "SETTINGS_MODEL_RAG_MODEL": "Rag Queryモデル", + "SETTINGS_MODEL_EXIST": "モデルはすでに存在します。ダウンロードを繰り返す必要はありません。", + "SETTINGS_MODEL_DOWNLOAD": "モデルのダウンロード", + "SETTINGS_MODEL_DOWNLOAD_DESC": "以下は、Al PlaygroundでさまざまなAlタスクを実行できるモデルのリストです。ダウンロードして使用する前に、各モデルの条件を確認してください。または、上記のモデル パスとモデルのデフォルトを変更して、他のモデル ソースを追加して選択することもできます。", + "TAB_CREATE": "作成", + "TAB_ENHANCE": "強化", + "TAB_ANSWER": "回答", + "TAB_LEARN_MORE": "詳細", + "ENHANCE_INPUT_IMAGE_REQUIRED": "生成前に入力イメージを設定してください", + "ENHANCE_UPSCALE": "アップスケール", + "ENHANCE_IMAGE_PROMPT": "イメージ プロンプト", + "ENHANCE_INPAINT": "インペイント", + "ENHANCE_OUTPAINT": "アウトペイント", + "ENHANCE_UPSCALE_SCALE": "アップスケール", + "ENHANCE_UPSCALE_SCALE_X1_5": "アップスケール (1.5 倍)", + "ENHANCE_UPSCALE_SCALE_X2_0": "アップスケール (2.X)", + "ENHANCE_UPSCALE_VARIATION": "バリエーション", + "ENHANCE_UPSCALE_VARIATION_NONE": "なし", + "ENHANCE_UPSCALE_VARIATION_DISABLE": "微妙", + "ENHANCE_UPSCALE_VARIATION_STRONG": "強い", + "ENHANCE_INPAINT_TYPE": "タイプ", + "ENHANCE_INPAINT_FILL": "新しいもので塗りつぶす", + "ENHANCE_INPAINT_FIX": "マスクされた領域を修正して修復する", + "ENHANCE_INPAINT_MASK_REQUIRED": "生成する前にインペイントマスクを描画してください", + "ENHANCE_INPAINT_USE_IMAGE_MODEL": "現在の画像モデルを使用する", + "ENHANCE_COM_DENOISE": "影響", + "ENHANCE_OUTPAINT_DIRECTION": "方向", + "ENHANCE_PREVIEW_BEFORE_PROCESS": "処理前", + "ENHANCE_PREVIEW_AFTER_PROCESS": "処理後", + "ENHANCE_IMAGE_PROMPT_TIP": "画像をどのようにスタイル設定するかを説明します。保持したい元の画像の説明と、変更したい点の説明を含めます。影響スライダーを調整して、プロンプトが結果に与える影響を増減します", + "ENHANCE_INPAINT_TIP": "ペン ツールを使用して領域をマスクします。プロンプトで、修正または塗りつぶしたいものを説明します。[修正] または [塗りつぶし] オプションを選択してから、プロンプトの影響レベルを調整します", + "ENHANCE_OUTPAINT_TIP": "拡大する方向を選択し、必要に応じてノイズ除去の値を調整し、テキスト ボックスにプロンプトの単語を入力します。拡大画像が生成されます", + "ENHANCE_UPSCALE_TIP": "操作する必要がある画像をアップロードし、増幅係数を選択し、ノイズ除去の振幅を調整します (0.1 未満の場合、画像の内容は変更されませんが、サイズは拡大されます)。テキスト ボックスにプロンプト ワードを入力して拡大画像を生成します", + "ERROR_UNSUPPORTED_IMAGE_TYPE": "PNG、JPG、GIF、および BMP の画像形式のみをサポートします", + "ANSWER_USER_NAME": "あなた", + "ANSWER_AI_NAME": "プレイグラウンド", + "ANSWER_ERROR_NOT_PROMPT": "プロンプトを入力してください", + "ANSWER_ERROR_CLEAR_SESSION": "チャット履歴を消去します", + "ANSWER_RAG_ENABLE": "ファイル クエリを有効にします", + "ANSWER_RAG_OPEN_DIALOG": "ファイル アップローダーを開きます", + "DOWNLOADER_CONFRIM_TIP": "実行に必要なモデルが 1 つ以上ありません。以下にリストされているモデルをダウンロードしますか?", + "DOWNLOADER_MODEL": "モデル", + "DOWNLOADER_INFO": "情報", + "DOWNLOADER_FILE_SIZE": "サイズ", + "DOWNLOADER_REASON": "理由", + "DOWNLOADER_TERMS": "訪問", + "DOWNLOADER_CONFLICT": "現在別のダウンロード タスクが進行中のため、新しいタスクを開始できません。現在のダウンロード タスクをキャンセルして、新しいダウンロード タスクを開始できます", + "DOWNLOADER_TERMS_TIP": "モデル カードとライセンスを確認しました。すべての利用規約に同意し、サードパーティのモデルをダウンロードします。", + "DOWNLOADER_FOR_ANSWER_GENERATE": "回答モデル", + "DOWNLOADER_FOR_RAG_QUERY": "RAG 埋め込みモデル", + "DOWNLOADER_FOR_IMAGE_GENERATE": "画像モデル", + "DOWNLOADER_FOR_INAPINT_GENERATE": "インペイント/アウトペイントモデル", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "画像プレビューモデル", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "画像アップスケールモデル", + "DOWNLOADER_FOR_IMAGE_LORA": "高速画像モデル", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "モデルのダウンロードが完了しました", + "RAG_FILE_TOTAL_FORMAT": "ファイルの合計: {total}", + "RAG_DRAG_UPLOAD": "アップロードするファイルをドラッグしてください", + "RAG_DRAG_UPLOAD_UNSUPPORTED": "現在のプログラムは管理者権限で起動されています。Windows UAC の制限により、ドラッグ アンド ドロップによるアップロードは使用できません。ファイルをアップロードするには、上部にある追加ボタンを使用してください。", + "RAG_UPLOAD_MIME_TYPE": "サポートされているファイルの種類:\n\tテキスト ドキュメント: .txt .md\n\tOffice ドキュメント: .doc(x)\n\tPDF ドキュメント: .pdf", + "RAG_ENABLE_TIP": "アップロードされたものにのみ回答を提供します", + "RAG_UPLOAD_TYPE_ERROR": "アップロードされたファイルにはサポートされていないファイルの種類があるため、自動的に除外されました", + "RAG_UPLOAD_FILE_EXISTS": "アップロードされたファイル {filename} は既に存在します", + "RAG_ANALYZE_FILE_FAILED": "ファイル {filename} の分析に失敗しました", + "RAG_USE_REJECT": "インタラクティブ ドキュメント リストが空です。まずドキュメントをアップロードしてください", + "RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "すべてのドキュメントが分析されるまでお待ちください", + "RAG_SOURCE": "ソース:", + "ERR_NOT_ENOUGH_DISK_SPACE": "ディスク容量が足りません。{requires_space} が必要ですが、使用可能な空き容量は {free_space} のみです", + "ERR_DOWNLOAD_FAILED": "モデル ファイルのダウンロードに失敗しました", + "ERROR_RUNTIME_ERROR": "重大な障害が発生した場合は、プログラムを再起動して再試行してください", + "ERROR_GENERATE_UNKONW_EXCEPTION": "不明なエラーが発生しました。モデルから生成できませんでした", + "ERROR_FOLDER_NOT_EXISTS": "指定されたディレクトリが存在しません", + "ERROR_ENHANCE_IMAGE_NOT_SET": "生成する入力イメージを設定してください", + "ERROR_UNFOUND_GRAPHICS": "プログラムの実行に必要なハードウェアがコンピューター デバイス上で検出されない場合、[OK] をクリックするとプログラムは終了します。 ", + "COM_DO_NOT_SHOW_AGAIN": "再び表示しない", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "12GB以下のVRAMまたは24GB以下のシステムメモリー搭載のCore Utra PCにて、HDモード選択時には通常よりも遅い場合があります", + "SETTINGS_MODEL_IMAGE_SIZE": "イメージサイズ", + "SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", + "SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API トークン", + "SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "有効なトークンを入力してください (hf_***)", + "INCREASE_FONT_SIZE": "テキスト拡大", + "DECREASE_FONT_SIZE": "テキスト縮小", + "DOWNLOADER_GATED": "ゲーテッド(Gated)", + "DOWNLOADER_GATED_INFO": "ダウンロードを試みているモデルの中にはゲーテッドされているものがあります。", + "DOWNLOADER_GATED_TOKEN": "設定にて huggingface.co API トークンを入力してください", + "DOWNLOADER_GATED_ACCEPT": "モデルの情報ページに行きアクセス権を申請してください。承認されていない場合には該当のモデルがゲーテッドされ、ダウンロードできません。", + "ERROR_PYTHON_BACKEND_INIT": "バックエンド初期化に失敗", + "ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "AI推論のバックエンド初期化に失敗しました。アプリケーションを再起動してください。問題が再発する様でしたら、詳細にある追加情報を参照ください。", + "ERROR_PYTHON_BACKEND_INIT_DETAILS": "詳細", + "ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "ログを開く" } diff --git a/WebUI/src/assets/i18n/ko.json b/WebUI/src/assets/i18n/ko.json index ad716bc5..1c8216e3 100644 --- a/WebUI/src/assets/i18n/ko.json +++ b/WebUI/src/assets/i18n/ko.json @@ -26,10 +26,10 @@ "COM_CONFIRM": "확인", "COM_CANCEL": "취소", "COM_VISIT": "방문", - "COM_REQUESTING":"요청 중", - "COM_DEFAULT":"기본값", - "COM_STOP":"정지", - "COM_OPEN_LOCATION":"파일 위치 열기", + "COM_REQUESTING": "요청 중", + "COM_DEFAULT": "기본값", + "COM_STOP": "정지", + "COM_OPEN_LOCATION": "파일 위치 열기", "COM_OPEN_PARAMS": "매개변수 정보", "COM_LOW": "낮음", "COM_HIGH": "높음", @@ -131,7 +131,7 @@ "DOWNLOADER_FOR_IMAGE_PREVIEW": "이미지 미리보기 모델", "DOWNLOADER_FOR_IMAGE_UPSCALE": "이미지 업스케일 모델", "DOWNLOADER_FOR_IMAGE_LORA": "빠른 이미지 모델", - "DOWNLOADER_DONWLOAD_TASK_PROGRESS":"모델 다운로드 완료", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "모델 다운로드 완료", "RAG_FILE_TOTAL_FORMAT": "파일 총 개수: {total}", "RAG_DRAG_UPLOAD": "파일을 끌어다 놓아 업로드", "RAG_DRAG_UPLOAD_UNSUPPORTED": "현재 프로그램은 관리자 권한으로 실행 중입니다. Windows UAC 제한으로 인해 드래그 앤 드롭 업로드를 사용할 수 없습니다. 파일을 업로드하려면 상단의 추가 버튼을 사용하세요.", diff --git a/WebUI/src/assets/i18n/pl.json b/WebUI/src/assets/i18n/pl.json index 11748858..5eee9572 100644 --- a/WebUI/src/assets/i18n/pl.json +++ b/WebUI/src/assets/i18n/pl.json @@ -1,168 +1,168 @@ { -"MAIN_TITLE": "AI PLAYGROUND", -"COM_SETTINGS": "Ustawienia", -"COM_MINI": "Minimalizuj", -"COM_CLOSE": "Zamknij", -"COM_RESTORE": "Przywróć ustawienia domyślne", -"COM_SD_PROMPT": "Wprowadź podpowiedź o wygenerowanie obrazu", -"COM_LLM_PROMPT": "Wprowadź podpowiedź, aby wygenerować odpowiedź", -"COM_CLICK_UPLOAD": "- Kliknij, aby przesłać obraz -", -"COM_GENERATE": "Generuj", -"COM_GENERATING": "Generowanie", -"COM_HISTORY": "Historia", -"COM_POST_TO_ENHANCE_PROCESS": "Opublikuj, aby ulepszyć proces", -"COM_ZOOM_IN": "Wyświetl oryginalny rozmiar", -"COM_COPY": "Skopiuj", -"COM_COPY_SUCCESS_TIP": "Powodzenie kopiowania", -"COM_APPLY": "Zastosuj", -"COM_DOWNLOAD": "Pobieranie", -"COM_REGENERATE": "Regeneruj", -"COM_DELETE": "Usuń", -"COM_DOWNLOAD_MODEL": "Model pobierania", -"COM_DOWNLOAD_SPEED": "Prędkość pobierania", -"COM_LOADING_MODEL": "Ładowanie modelu AI", -"COM_LOADING_MODEL_COMPONENTS": "Ładowanie komponentów modelu AI", -"COM_NO_SELECTED": "Nie wybrano", -"COM_CONFIRM": "Potwierdź", -"COM_CANCEL": "Anuluj", -"COM_VISIT": "WIZYTA", -"COM_REQUESTING": "Żądanie", -"COM_DEFAULT": "Domyślny", -"COM_STOP": "Stop", -"COM_OPEN_LOCATION": "Otwórz lokalizację", -"COM_OPEN_PARAMS": "Informacja o parametrach", -"COM_LOW": "niski", -"COM_HIGH": "wysoki", -"COM_FULLSCREEN": "Pełny ekran", -"COM_FULLSCREEN_EXIT": "Wyjście z trybu pełnoekranowego", -"SETTINGS_TAB_BASIC": "Ustawienia podstawowe", -"SETTINGS_TAB_MODEL": "Modele", -"SETTINGS_INFERENCE_DEVICE": "Urządzenie do wnioskowania", -"SETTINGS_MODEL_IMAGE_RESOLUTION": "Rozdzielczość obrazu", -"SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Standardowa", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", -"SETTINGS_MODEL_QUALITY": "Jakość generowania", -"SETTINGS_MODEL_QUALITY_STANDARD": "Standardowa", -"SETTINGS_MODEL_QUALITY_HIGH": "Wysoka jakość", -"SETTINGS_MODEL_QUALITY_FAST": "Szybko", -"SETTINGS_MODEL_QUALITY_MANUAL": "Ręcznie", -"SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Opcje regulowane", -"SETTINGS_MODEL_MANUAL_OPTIONS": "Opcje ręczne", -"SETTINGS_MODEL_NEGATIVE_PROMPT": "Negatywny monit", -"SETTINGS_MODEL_SEED": "Seed", -"SETTINGS_MODEL_GENERATE_NUMBER": "Generuj numer obrazu", -"SETTINGS_MODEL_LLM_MODEL": "Duży model językowy", -"SETTINGS_MODEL_IMAGE_PREVIEW": "Podgląd obrazu", -"SETTINGS_MODEL_SAFE_CHECK": "Bezpieczne sprawdzanie (efekt tylko w SD 1.5)", -"SETTINGS_MODEL_IMAGE_MODEL": "Model obrazu", -"SETTINGS_MODEL_INPAINT_MODEL": "Inpaint/Outpaint Model", -"SETTINGS_MODEL_IMAGE_WIDTH": "Szerokość", -"SETTINGS_MODEL_IMAGE_HEIGHT": "Wysokość", -"SETTINGS_MODEL_IMAGE_STEPS": "Kroki", -"SETTINGS_MODEL_IMAGE_CFG": "CFG", -"SETTINGS_MODEL_LORA": "Lora", -"SETTINGS_MODEL_SCHEDULER": "Harmonogram", -"SETTINGS_MODEL_LIST_CHANGE_TIP": "Zmiany na liście modeli spowodowały modyfikację niektórych ustawień. Więcej informacji można znaleźć w ustawieniach podstawowych.", -"SETTINGS_BASIC_LANGUAGE": "Język", -"SETTINGS_BASIC_PATHS": "Ścieżki", -"SETTINGS_BASIC_LLM_CHECKPOINTS": "Ścieżka modelu LLM", -"SETTINGS_MODEL_SD_CHECKPOINTS": "Ścieżka modelu obrazu", -"SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Ścieżka modelu malowania obrazu", -"SETTINGS_MODEL_SD_VAE": "Ścieżka Vae", -"SETTINGS_MODEL_SD_LORA": "Ścieżka Lora", -"SETTINGS_MODEL_SD_SCHEDULER": "Ścieżka harmonogramu", -"SETTINGS_MODEL_SD_PRESET_MODEL": "Domyślny model obrazu", -"SETTINGS_MODEL_SD_STANDARD_MODEL": "Standardowy model domyślny", -"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Standardowy model domyślny Inpaint/OutPaint", -"SETTINGS_MODEL_SD_HD_MODEL": "Domyślny model HD", -"SETTINGS_MODEL_RAG_MODEL": "Model zapytania Rag", -"SETTINGS_MODEL_EXIST": "Model już istnieje. Powtarzanie pobierania nie jest konieczne.", -"SETTINGS_MODEL_DOWNLOAD": "Pobieranie modelu", -"SETTINGS_MODEL_DOWNLOAD_DESC": "Poniżej znajduje się lista modeli, które mogą wykonywać różne zadania Al w Al Playground. Zapoznaj się z warunkami każdego modelu przed jego pobraniem i użyciem. Alternatywnie można dodać i wybrać inne źródła modeli, zmieniając ścieżki modeli i domyślne ustawienia modeli powyżej.", -"TAB_CREATE": "Utwórz", -"TAB_ENHANCE": "Ulepsz", -"TAB_ANSWER": "OdpowiedZ", -"TAB_LEARN_MORE": "Dowiedz się więcej", -"ENHANCE_INPUT_IMAGE_REQUIRED": "Ustaw obraz wejściowy przed wygenerowaniem", -"ENHANCE_UPSCALE": "Zwiększ skalę", -"ENHANCE_IMAGE_PROMPT": "Podpowiedź obrazu", -"ENHANCE_INPAINT": "Domaluj", -"ENHANCE_OUTPAINT": "Przemaluj", -"ENHANCE_UPSCALE_SCALE": "Zwiększ skalę", -"ENHANCE_UPSCALE_SCALE_X1_5": "Zwiększ skalę (1.5X)", -"ENHANCE_UPSCALE_SCALE_X2_0": "Zwiększ skalę (2.0X)", -"ENHANCE_UPSCALE_VARIATION": "Wariacja", -"ENHANCE_UPSCALE_VARIATION_NONE": "Brak", -"ENHANCE_UPSCALE_VARIATION_DISABLE": "Subtelny", -"ENHANCE_UPSCALE_VARIATION_STRONG": "Silny", -"ENHANCE_INPAINT_TYPE": "Typ", -"ENHANCE_INPAINT_FILL": "Wypełnienie czymś nowym", -"ENHANCE_INPAINT_FIX": "Naprawianie zamaskowanego obszaru", -"ENHANCE_INPAINT_MASK_REQUIRED": "Narysuj maskę inpaint przed wygenerowaniem", -"ENHANCE_INPAINT_USE_IMAGE_MODEL": "Użyj bieżącego modelu obrazu", -"ENHANCE_COM_DENOISE": "Wpływ", -"ENHANCE_OUTPAINT_DIRECTION": "Kierunek", -"ENHANCE_PREVIEW_BEFORE_PROCESS": "Przed procesem", -"ENHANCE_PREVIEW_AFTER_PROCESS": "Po przetworzeniu", -"ENHANCE_IMAGE_PROMPT_TIP": "Opisz sposób stylizacji obrazu. Dołącz opisy oryginalnego obrazu, który chcesz zachować, oraz opisy tego, co chcesz zmienić. Dostosuj suwak wpływu, aby zwiększyć lub zmniejszyć wpływ podpowiedzi na wynik.", -"ENHANCE_INPAINT_TIP": "Użyj narzędzia pióra, aby zamaskować obszar. Opisz w podpowiedzi, co chcesz naprawić lub wypełnić. Wybierz opcję Napraw lub Wypełnij, a następnie dostosuj poziom wpływu podpowiedzi.", -"ENHANCE_OUTPAINT_TIP": "Wybierz kierunek, który chcesz rozszerzyć, w razie potrzeby dostosuj wartość odszumiania i wprowadź słowo zachęty w polu tekstowym. Rozszerzony obraz zostanie wygenerowany", -"ENHANCE_UPSCALE_TIP": "Prześlij obraz, który ma zostać zmodyfikowany, wybierz współczynnik wzmocnienia, dostosuj amplitudę odszumiania (jeśli jest mniejsza niż 0,1, zawartość obrazu nie zmieni się, ale rozmiar nadal będzie się zwiększał), wprowadź słowo zachęty w polu tekstowym, aby wygenerować powiększony obraz.", -"ERROR_UNSUPPORTED_IMAGE_TYPE": "Obsługuje tylko formaty obrazów PNG, JPG, GIF i BMP", -"ANSWER_USER_NAME": "Ty", -"ANSWER_AI_NAME": "Plac zabaw", -"ANSWER_ERROR_NOT_PROMPT": "Wprowadź podpowiedź", -"ANSWER_ERROR_CLEAR_SESSION": "Wyczyść historię czatu", -"ANSWER_RAG_ENABLE": "Włącz zapytanie o plik", -"ANSWER_RAG_OPEN_DIALOG": "Otwórz narzędzie do przesyłania plików", -"DOWNLOADER_CONFRIM_TIP": "Brakuje jednego lub więcej modeli potrzebnych do uruchomienia. Czy chcesz pobrać modele wymienione poniżej?", -"DOWNLOADER_MODEL": "Model", -"DOWNLOADER_INFO": "Informacje", -"DOWNLOADER_FILE_SIZE": "Rozmiar", -"DOWNLOADER_REASON": "Powód", -"DOWNLOADER_TERMS": "Wizyta", -"DOWNLOADER_CONFLICT": "Obecnie trwa inne zadanie pobierania i nie można rozpocząć nowego zadania. Można anulować bieżące zadanie pobierania i rozpocząć nowe zadanie pobierania", -"DOWNLOADER_TERMS_TIP": "Zapoznałem(-am) się z kartą(-ami) modelu(-ów) i licencją(-ami). Zgadzam się na wszystkie warunki i chciałbym pobrać model(e) innej firmy.", -"DOWNLOADER_FOR_ANSWER_GENERATE": "Answer Model", -"DOWNLOADER_FOR_RAG_QUERY": "RAG Embed Model", -"DOWNLOADER_FOR_IMAGE_GENERATE": "Model obrazu", -"DOWNLOADER_FOR_INAPINT_GENERATE": "Model Inpaint/OutPaint", -"DOWNLOADER_FOR_IMAGE_PREVIEW": "Model podglądu obrazu", -"DOWNLOADER_FOR_IMAGE_UPSCALE": "Model skalowania obrazu", -"DOWNLOADER_FOR_IMAGE_LORA": "Szybki model obrazu", -"DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Pobierz kompletny model", -"RAG_FILE_TOTAL_FORMAT": "Łączna liczba plików: {łącznie}", -"RAG_DRAG_UPLOAD": "Przeciągnij pliki do przesłania", -"RAG_DRAG_UPLOAD_UNSUPPORTED": "Bieżący program jest uruchamiany z uprawnieniami administratora. Ze względu na ograniczenia UAC systemu Windows nie można korzystać z przesyłania metodą „przeciągnij i upuść”. Aby przesłać pliki, użyj przycisku dodawania u góry.", -"RAG_UPLOAD_MIME_TYPE": "Obsługiwane typy plików:\n\tDokument tekstowy: .txt .md\n\tDokument biurowy: .doc(x)\n\tDokument PDF: .pdf", -"RAG_ENABLE_TIP": "udzielaj odpowiedzi tylko na przesłanych plikach", -"RAG_UPLOAD_TYPE_ERROR": "W przesłanym pliku znajduje się nieobsługiwany typ pliku, który został automatycznie wykluczony.", -"RAG_UPLOAD_FILE_EXISTS": "Przesłany plik {nazwa_pliku} już istnieje", -"RAG_ANALYZE_FILE_FAILED": "Analiza pliku {nazwa_pliku} nie powiodła się", -"RAG_USE_REJECT": "Interaktywna lista dokumentów jest pusta, prześlij dokument jako pierwszy.", -"RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Poczekaj, aż wszystkie dokumenty zostaną przeanalizowane", -"RAG_SOURCE": "Ze źródła:", -"ERR_NOT_ENOUGH_DISK_SPACE": "Za mało miejsca na dysku. Wymaga {requires_space}, ale dostępne jest tylko {free_space} wolnego miejsca.", -"ERR_DOWNLOAD_FAILED": "Pobieranie pliku modelu nie powiodło się", -"ERROR_RUNTIME_ERROR": "Jeśli wystąpi błąd krytyczny, uruchom ponownie program i spróbuj jeszcze raz", -"ERROR_GENERATE_UNKONW_EXCEPTION": "Wystąpił nieznany błąd. Nie udało się wygenerować z modelu", -"ERROR_FOLDER_NOT_EXISTS": "Określony katalog nie istnieje", -"ERROR_ENHANCE_IMAGE_NOT_SET": "Skonfiguruj obraz wejściowy do wygenerowania", -"ERROR_UNFOUND_GRAPHICS": "Jeśli na urządzeniu komputerowym nie zostanie wykryty sprzęt niezbędny do wykonania programu, program zakończy działanie po kliknięciu przycisku OK.", -"COM_DO_NOT_SHOW_AGAIN": "Nie pokazuj ponownie", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "Tryb HD może powodować wolniejsze działanie na systemach z pojemnością mniejszą niż 12 GB VRAM lub 24 GB pamięci systemowej na komputerach z procesorem Intel Core Ultra.", -"SETTINGS_MODEL_IMAGE_SIZE": "Rozmiar obrazu", -"SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", -"SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API Token", -"SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "Wprowadź prawidłowy token (hf_***).", -"INCREASE_FONT_SIZE": "Powiększ tekst", -"DECREASE_FONT_SIZE": "Pomniejsz tekst", -"DOWNLOADER_GATED": "bramka (do pobierania)", -"DOWNLOADER_GATED_INFO": "Niektóre z modeli, które próbujesz pobrać, są zablokowane.", -"DOWNLOADER_GATED_TOKEN": "Dodaj swój token API huggingface.co w ustawieniach.", -"DOWNLOADER_GATED_ACCEPT": "Upewnij się, że odwiedziłeś stronę z informacjami o modelu i poprosiłeś o dostęp. Jeśli nie otrzymałeś dostępu do modelu z bramką, pobieranie nie powiedzie się.", -"ERROR_PYTHON_BACKEND_INIT": "Inicjalizacja backendu nie powiodła się", -"ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "Nie udało się zainicjować AI inference backend. Spróbuj ponownie uruchomić aplikację. Jeśli problem nadal występuje, możesz sprawdzić Szczegóły, aby uzyskać dodatkowe informacje o błędzie.", -"ERROR_PYTHON_BACKEND_INIT_DETAILS": "Szczegóły", -"ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "Otwórz dziennik" + "MAIN_TITLE": "AI PLAYGROUND", + "COM_SETTINGS": "Ustawienia", + "COM_MINI": "Minimalizuj", + "COM_CLOSE": "Zamknij", + "COM_RESTORE": "Przywróć ustawienia domyślne", + "COM_SD_PROMPT": "Wprowadź podpowiedź o wygenerowanie obrazu", + "COM_LLM_PROMPT": "Wprowadź podpowiedź, aby wygenerować odpowiedź", + "COM_CLICK_UPLOAD": "- Kliknij, aby przesłać obraz -", + "COM_GENERATE": "Generuj", + "COM_GENERATING": "Generowanie", + "COM_HISTORY": "Historia", + "COM_POST_TO_ENHANCE_PROCESS": "Opublikuj, aby ulepszyć proces", + "COM_ZOOM_IN": "Wyświetl oryginalny rozmiar", + "COM_COPY": "Skopiuj", + "COM_COPY_SUCCESS_TIP": "Powodzenie kopiowania", + "COM_APPLY": "Zastosuj", + "COM_DOWNLOAD": "Pobieranie", + "COM_REGENERATE": "Regeneruj", + "COM_DELETE": "Usuń", + "COM_DOWNLOAD_MODEL": "Model pobierania", + "COM_DOWNLOAD_SPEED": "Prędkość pobierania", + "COM_LOADING_MODEL": "Ładowanie modelu AI", + "COM_LOADING_MODEL_COMPONENTS": "Ładowanie komponentów modelu AI", + "COM_NO_SELECTED": "Nie wybrano", + "COM_CONFIRM": "Potwierdź", + "COM_CANCEL": "Anuluj", + "COM_VISIT": "WIZYTA", + "COM_REQUESTING": "Żądanie", + "COM_DEFAULT": "Domyślny", + "COM_STOP": "Stop", + "COM_OPEN_LOCATION": "Otwórz lokalizację", + "COM_OPEN_PARAMS": "Informacja o parametrach", + "COM_LOW": "niski", + "COM_HIGH": "wysoki", + "COM_FULLSCREEN": "Pełny ekran", + "COM_FULLSCREEN_EXIT": "Wyjście z trybu pełnoekranowego", + "SETTINGS_TAB_BASIC": "Ustawienia podstawowe", + "SETTINGS_TAB_MODEL": "Modele", + "SETTINGS_INFERENCE_DEVICE": "Urządzenie do wnioskowania", + "SETTINGS_MODEL_IMAGE_RESOLUTION": "Rozdzielczość obrazu", + "SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Standardowa", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", + "SETTINGS_MODEL_QUALITY": "Jakość generowania", + "SETTINGS_MODEL_QUALITY_STANDARD": "Standardowa", + "SETTINGS_MODEL_QUALITY_HIGH": "Wysoka jakość", + "SETTINGS_MODEL_QUALITY_FAST": "Szybko", + "SETTINGS_MODEL_QUALITY_MANUAL": "Ręcznie", + "SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Opcje regulowane", + "SETTINGS_MODEL_MANUAL_OPTIONS": "Opcje ręczne", + "SETTINGS_MODEL_NEGATIVE_PROMPT": "Negatywny monit", + "SETTINGS_MODEL_SEED": "Seed", + "SETTINGS_MODEL_GENERATE_NUMBER": "Generuj numer obrazu", + "SETTINGS_MODEL_LLM_MODEL": "Duży model językowy", + "SETTINGS_MODEL_IMAGE_PREVIEW": "Podgląd obrazu", + "SETTINGS_MODEL_SAFE_CHECK": "Bezpieczne sprawdzanie (efekt tylko w SD 1.5)", + "SETTINGS_MODEL_IMAGE_MODEL": "Model obrazu", + "SETTINGS_MODEL_INPAINT_MODEL": "Inpaint/Outpaint Model", + "SETTINGS_MODEL_IMAGE_WIDTH": "Szerokość", + "SETTINGS_MODEL_IMAGE_HEIGHT": "Wysokość", + "SETTINGS_MODEL_IMAGE_STEPS": "Kroki", + "SETTINGS_MODEL_IMAGE_CFG": "CFG", + "SETTINGS_MODEL_LORA": "Lora", + "SETTINGS_MODEL_SCHEDULER": "Harmonogram", + "SETTINGS_MODEL_LIST_CHANGE_TIP": "Zmiany na liście modeli spowodowały modyfikację niektórych ustawień. Więcej informacji można znaleźć w ustawieniach podstawowych.", + "SETTINGS_BASIC_LANGUAGE": "Język", + "SETTINGS_BASIC_PATHS": "Ścieżki", + "SETTINGS_BASIC_LLM_CHECKPOINTS": "Ścieżka modelu LLM", + "SETTINGS_MODEL_SD_CHECKPOINTS": "Ścieżka modelu obrazu", + "SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Ścieżka modelu malowania obrazu", + "SETTINGS_MODEL_SD_VAE": "Ścieżka Vae", + "SETTINGS_MODEL_SD_LORA": "Ścieżka Lora", + "SETTINGS_MODEL_SD_SCHEDULER": "Ścieżka harmonogramu", + "SETTINGS_MODEL_SD_PRESET_MODEL": "Domyślny model obrazu", + "SETTINGS_MODEL_SD_STANDARD_MODEL": "Standardowy model domyślny", + "SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Standardowy model domyślny Inpaint/OutPaint", + "SETTINGS_MODEL_SD_HD_MODEL": "Domyślny model HD", + "SETTINGS_MODEL_RAG_MODEL": "Model zapytania Rag", + "SETTINGS_MODEL_EXIST": "Model już istnieje. Powtarzanie pobierania nie jest konieczne.", + "SETTINGS_MODEL_DOWNLOAD": "Pobieranie modelu", + "SETTINGS_MODEL_DOWNLOAD_DESC": "Poniżej znajduje się lista modeli, które mogą wykonywać różne zadania Al w Al Playground. Zapoznaj się z warunkami każdego modelu przed jego pobraniem i użyciem. Alternatywnie można dodać i wybrać inne źródła modeli, zmieniając ścieżki modeli i domyślne ustawienia modeli powyżej.", + "TAB_CREATE": "Utwórz", + "TAB_ENHANCE": "Ulepsz", + "TAB_ANSWER": "OdpowiedZ", + "TAB_LEARN_MORE": "Dowiedz się więcej", + "ENHANCE_INPUT_IMAGE_REQUIRED": "Ustaw obraz wejściowy przed wygenerowaniem", + "ENHANCE_UPSCALE": "Zwiększ skalę", + "ENHANCE_IMAGE_PROMPT": "Podpowiedź obrazu", + "ENHANCE_INPAINT": "Domaluj", + "ENHANCE_OUTPAINT": "Przemaluj", + "ENHANCE_UPSCALE_SCALE": "Zwiększ skalę", + "ENHANCE_UPSCALE_SCALE_X1_5": "Zwiększ skalę (1.5X)", + "ENHANCE_UPSCALE_SCALE_X2_0": "Zwiększ skalę (2.0X)", + "ENHANCE_UPSCALE_VARIATION": "Wariacja", + "ENHANCE_UPSCALE_VARIATION_NONE": "Brak", + "ENHANCE_UPSCALE_VARIATION_DISABLE": "Subtelny", + "ENHANCE_UPSCALE_VARIATION_STRONG": "Silny", + "ENHANCE_INPAINT_TYPE": "Typ", + "ENHANCE_INPAINT_FILL": "Wypełnienie czymś nowym", + "ENHANCE_INPAINT_FIX": "Naprawianie zamaskowanego obszaru", + "ENHANCE_INPAINT_MASK_REQUIRED": "Narysuj maskę inpaint przed wygenerowaniem", + "ENHANCE_INPAINT_USE_IMAGE_MODEL": "Użyj bieżącego modelu obrazu", + "ENHANCE_COM_DENOISE": "Wpływ", + "ENHANCE_OUTPAINT_DIRECTION": "Kierunek", + "ENHANCE_PREVIEW_BEFORE_PROCESS": "Przed procesem", + "ENHANCE_PREVIEW_AFTER_PROCESS": "Po przetworzeniu", + "ENHANCE_IMAGE_PROMPT_TIP": "Opisz sposób stylizacji obrazu. Dołącz opisy oryginalnego obrazu, który chcesz zachować, oraz opisy tego, co chcesz zmienić. Dostosuj suwak wpływu, aby zwiększyć lub zmniejszyć wpływ podpowiedzi na wynik.", + "ENHANCE_INPAINT_TIP": "Użyj narzędzia pióra, aby zamaskować obszar. Opisz w podpowiedzi, co chcesz naprawić lub wypełnić. Wybierz opcję Napraw lub Wypełnij, a następnie dostosuj poziom wpływu podpowiedzi.", + "ENHANCE_OUTPAINT_TIP": "Wybierz kierunek, który chcesz rozszerzyć, w razie potrzeby dostosuj wartość odszumiania i wprowadź słowo zachęty w polu tekstowym. Rozszerzony obraz zostanie wygenerowany", + "ENHANCE_UPSCALE_TIP": "Prześlij obraz, który ma zostać zmodyfikowany, wybierz współczynnik wzmocnienia, dostosuj amplitudę odszumiania (jeśli jest mniejsza niż 0,1, zawartość obrazu nie zmieni się, ale rozmiar nadal będzie się zwiększał), wprowadź słowo zachęty w polu tekstowym, aby wygenerować powiększony obraz.", + "ERROR_UNSUPPORTED_IMAGE_TYPE": "Obsługuje tylko formaty obrazów PNG, JPG, GIF i BMP", + "ANSWER_USER_NAME": "Ty", + "ANSWER_AI_NAME": "Plac zabaw", + "ANSWER_ERROR_NOT_PROMPT": "Wprowadź podpowiedź", + "ANSWER_ERROR_CLEAR_SESSION": "Wyczyść historię czatu", + "ANSWER_RAG_ENABLE": "Włącz zapytanie o plik", + "ANSWER_RAG_OPEN_DIALOG": "Otwórz narzędzie do przesyłania plików", + "DOWNLOADER_CONFRIM_TIP": "Brakuje jednego lub więcej modeli potrzebnych do uruchomienia. Czy chcesz pobrać modele wymienione poniżej?", + "DOWNLOADER_MODEL": "Model", + "DOWNLOADER_INFO": "Informacje", + "DOWNLOADER_FILE_SIZE": "Rozmiar", + "DOWNLOADER_REASON": "Powód", + "DOWNLOADER_TERMS": "Wizyta", + "DOWNLOADER_CONFLICT": "Obecnie trwa inne zadanie pobierania i nie można rozpocząć nowego zadania. Można anulować bieżące zadanie pobierania i rozpocząć nowe zadanie pobierania", + "DOWNLOADER_TERMS_TIP": "Zapoznałem(-am) się z kartą(-ami) modelu(-ów) i licencją(-ami). Zgadzam się na wszystkie warunki i chciałbym pobrać model(e) innej firmy.", + "DOWNLOADER_FOR_ANSWER_GENERATE": "Answer Model", + "DOWNLOADER_FOR_RAG_QUERY": "RAG Embed Model", + "DOWNLOADER_FOR_IMAGE_GENERATE": "Model obrazu", + "DOWNLOADER_FOR_INAPINT_GENERATE": "Model Inpaint/OutPaint", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "Model podglądu obrazu", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "Model skalowania obrazu", + "DOWNLOADER_FOR_IMAGE_LORA": "Szybki model obrazu", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Pobierz kompletny model", + "RAG_FILE_TOTAL_FORMAT": "Łączna liczba plików: {łącznie}", + "RAG_DRAG_UPLOAD": "Przeciągnij pliki do przesłania", + "RAG_DRAG_UPLOAD_UNSUPPORTED": "Bieżący program jest uruchamiany z uprawnieniami administratora. Ze względu na ograniczenia UAC systemu Windows nie można korzystać z przesyłania metodą „przeciągnij i upuść”. Aby przesłać pliki, użyj przycisku dodawania u góry.", + "RAG_UPLOAD_MIME_TYPE": "Obsługiwane typy plików:\n\tDokument tekstowy: .txt .md\n\tDokument biurowy: .doc(x)\n\tDokument PDF: .pdf", + "RAG_ENABLE_TIP": "udzielaj odpowiedzi tylko na przesłanych plikach", + "RAG_UPLOAD_TYPE_ERROR": "W przesłanym pliku znajduje się nieobsługiwany typ pliku, który został automatycznie wykluczony.", + "RAG_UPLOAD_FILE_EXISTS": "Przesłany plik {nazwa_pliku} już istnieje", + "RAG_ANALYZE_FILE_FAILED": "Analiza pliku {nazwa_pliku} nie powiodła się", + "RAG_USE_REJECT": "Interaktywna lista dokumentów jest pusta, prześlij dokument jako pierwszy.", + "RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Poczekaj, aż wszystkie dokumenty zostaną przeanalizowane", + "RAG_SOURCE": "Ze źródła:", + "ERR_NOT_ENOUGH_DISK_SPACE": "Za mało miejsca na dysku. Wymaga {requires_space}, ale dostępne jest tylko {free_space} wolnego miejsca.", + "ERR_DOWNLOAD_FAILED": "Pobieranie pliku modelu nie powiodło się", + "ERROR_RUNTIME_ERROR": "Jeśli wystąpi błąd krytyczny, uruchom ponownie program i spróbuj jeszcze raz", + "ERROR_GENERATE_UNKONW_EXCEPTION": "Wystąpił nieznany błąd. Nie udało się wygenerować z modelu", + "ERROR_FOLDER_NOT_EXISTS": "Określony katalog nie istnieje", + "ERROR_ENHANCE_IMAGE_NOT_SET": "Skonfiguruj obraz wejściowy do wygenerowania", + "ERROR_UNFOUND_GRAPHICS": "Jeśli na urządzeniu komputerowym nie zostanie wykryty sprzęt niezbędny do wykonania programu, program zakończy działanie po kliknięciu przycisku OK.", + "COM_DO_NOT_SHOW_AGAIN": "Nie pokazuj ponownie", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "Tryb HD może powodować wolniejsze działanie na systemach z pojemnością mniejszą niż 12 GB VRAM lub 24 GB pamięci systemowej na komputerach z procesorem Intel Core Ultra.", + "SETTINGS_MODEL_IMAGE_SIZE": "Rozmiar obrazu", + "SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", + "SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API Token", + "SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "Wprowadź prawidłowy token (hf_***).", + "INCREASE_FONT_SIZE": "Powiększ tekst", + "DECREASE_FONT_SIZE": "Pomniejsz tekst", + "DOWNLOADER_GATED": "bramka (do pobierania)", + "DOWNLOADER_GATED_INFO": "Niektóre z modeli, które próbujesz pobrać, są zablokowane.", + "DOWNLOADER_GATED_TOKEN": "Dodaj swój token API huggingface.co w ustawieniach.", + "DOWNLOADER_GATED_ACCEPT": "Upewnij się, że odwiedziłeś stronę z informacjami o modelu i poprosiłeś o dostęp. Jeśli nie otrzymałeś dostępu do modelu z bramką, pobieranie nie powiedzie się.", + "ERROR_PYTHON_BACKEND_INIT": "Inicjalizacja backendu nie powiodła się", + "ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "Nie udało się zainicjować AI inference backend. Spróbuj ponownie uruchomić aplikację. Jeśli problem nadal występuje, możesz sprawdzić Szczegóły, aby uzyskać dodatkowe informacje o błędzie.", + "ERROR_PYTHON_BACKEND_INIT_DETAILS": "Szczegóły", + "ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "Otwórz dziennik" } diff --git a/WebUI/src/assets/i18n/th.json b/WebUI/src/assets/i18n/th.json index ac0fd940..5364b79b 100644 --- a/WebUI/src/assets/i18n/th.json +++ b/WebUI/src/assets/i18n/th.json @@ -1,168 +1,168 @@ { -"MAIN_TITLE": "AI PLAYGROUND", -"COM_SETTINGS": "การตั้งค่า", -"COM_MINI": "ย่อหน้า", -"COM_CLOSE": "ปิด", -"COM_RESTORE": "คืนค่าเริ่มต้น", -"COM_SD_PROMPT": "ป้อนคำสั่งเพื่อสร้างภาพ", -"COM_LLM_PROMPT": "ป้อนคำสั่งเพื่อสร้างคำตอบ", -"COM_CLICK_UPLOAD": "- คลิกเพื่ออัพโหลดรูปภาพ -", -"COM_GENERATE": "เริ่มสร้าง", -"COM_GENERATING": "กำลังสร้าง", -"COM_HISTORY": "History", -"COM_POST_TO_ENHANCE_PROCESS": "โพสต์เพื่อปรับปรุงกระบวนการ", -"COM_ZOOM_IN": "ดูขนาดต้นฉบับ", -"COM_COPY": "Copy", -"COM_COPY_SUCCESS_TIP": "Copy Success", -"COM_APPLY": "Apply", -"COM_DOWNLOAD": "Download", -"COM_REGENERATE": "เริ่มสร้าง", -"COM_DELETE": "ลบ", -"COM_DOWNLOAD_MODEL": "กำลังดาวน์โหลดโมเดล", -"COM_DOWNLOAD_SPEED": "ความเร็วในการดาวน์โหลด", -"COM_LOADING_MODEL": "กำลังโหลดโมเดล AI", -"COM_LOADING_MODEL_COMPONENTS": "กำลังโหลดส่วนประกอบโมเดล AI", -"COM_NO_SELECTED": "ไม่มีการเลือก", -"COM_CONFIRM": "ยืนยัน", -"COM_CANCEL": "ยกเลิก", -"COM_VISIT": "VISIT", -"COM_REQUESTING": "Requesting", -"COM_DEFAULT": "ค่าเริ่มต้น", -"COM_STOP": "หยุด", -"COM_OPEN_LOCATION": "Open Location", -"COM_OPEN_PARAMS": "ข้อมูลพารามิเตอร์", -"COM_LOW": "low", -"COM_HIGH": "high", -"COM_FULLSCREEN": "เต็มจอ", -"COM_FULLSCREEN_EXIT": "FullScreen Exit", -"SETTINGS_TAB_BASIC": "การตั้งค่าพื้นฐาน", -"SETTINGS_TAB_MODEL": "โมเดล", -"SETTINGS_INFERENCE_DEVICE": "Inference Device", -"SETTINGS_MODEL_IMAGE_RESOLUTION": "ความละเอียดของภาพ", -"SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "มาตรฐาน", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", -"SETTINGS_MODEL_QUALITY": "คุณภาพที่ต้องการ", -"SETTINGS_MODEL_QUALITY_STANDARD": "มาตรฐาน", -"SETTINGS_MODEL_QUALITY_HIGH": "คุณภาพสูง", -"SETTINGS_MODEL_QUALITY_FAST": "อย่างรวดเร็ว", -"SETTINGS_MODEL_QUALITY_MANUAL": "ปรับแต่งเอง", -"SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "ตัวเลือกที่ปรับได้", -"SETTINGS_MODEL_MANUAL_OPTIONS": "เลือกด้วยตนเอง", -"SETTINGS_MODEL_NEGATIVE_PROMPT": "Negative Prompt", -"SETTINGS_MODEL_SEED": "Seed", -"SETTINGS_MODEL_GENERATE_NUMBER": "Generate Image Number", -"SETTINGS_MODEL_LLM_MODEL": "Large Language Model", -"SETTINGS_MODEL_IMAGE_PREVIEW": "Image Preview", -"SETTINGS_MODEL_SAFE_CHECK": "Safe Check(Effect only on SD 1.5)", -"SETTINGS_MODEL_IMAGE_MODEL": "Image Model", -"SETTINGS_MODEL_INPAINT_MODEL": "Inpaint/Outpaint Model", -"SETTINGS_MODEL_IMAGE_WIDTH": "ความกว้าง", -"SETTINGS_MODEL_IMAGE_HEIGHT": "Height", -"SETTINGS_MODEL_IMAGE_STEPS": "Steps", -"SETTINGS_MODEL_IMAGE_CFG": "CFG", -"SETTINGS_MODEL_LORA": "Lora", -"SETTINGS_MODEL_SCHEDULER": "ตัวกำหนดตารางเวลา", -"SETTINGS_MODEL_LIST_CHANGE_TIP": "การเปลี่ยนแปลงในรายการโมเดลส่งผลให้มีการปรับเปลี่ยนการตั้งค่าบางอย่าง โปรดดูการตั้งค่าพื้นฐานสำหรับข้อมูลเพิ่มเติม", -"SETTINGS_BASIC_LANGUAGE": "ภาษา", -"SETTINGS_BASIC_PATHS": "เส้นทาง", -"SETTINGS_BASIC_LLM_CHECKPOINTS": "LLM Model Path", -"SETTINGS_MODEL_SD_CHECKPOINTS": "Image Model Path", -"SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "เส้นทางโมเดล Inpaint ของภาพ", -"SETTINGS_MODEL_SD_VAE": "Vae Path", -"SETTINGS_MODEL_SD_LORA": "Lora Path", -"SETTINGS_MODEL_SD_SCHEDULER": "เส้นทางตัวกำหนดตารางเวลา", -"SETTINGS_MODEL_SD_PRESET_MODEL": "โมเดลเริ่มต้นของภาพ", -"SETTINGS_MODEL_SD_STANDARD_MODEL": "โมเดลเริ่มต้นมาตรฐาน", -"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "โมเดล Inpaint/OutPaint เริ่มต้นมาตรฐาน", -"SETTINGS_MODEL_SD_HD_MODEL": "โมเดลเริ่มต้น HD", -"SETTINGS_MODEL_RAG_MODEL": "โมเดลการค้นหา Rag", -"SETTINGS_MODEL_EXIST": "โมเดลมีอยู่แล้ว ไม่จำเป็นต้องดาวน์โหลดซ้ำ", -"SETTINGS_MODEL_DOWNLOAD": "ดาวน์โหลดโมเดล", -"SETTINGS_MODEL_DOWNLOAD_DESC": "ด้านล่างนี้เป็นรายการโมเดลที่สามารถทำงานต่างๆ ใน ​​Al Playground ได้ ตรวจสอบเงื่อนไขของแต่ละโมเดลก่อนดาวน์โหลดและใช้งาน หรือเลือกที่จะเพิ่มเข้าไปและเลือกแหล่งโมเดลอื่นโดยเปลี่ยนเส้นทางโมเดลและค่าเริ่มต้นของโมเดลด้านบน", -"TAB_CREATE": "สร้าง", -"TAB_ENHANCE": "ปรับปรุง", -"TAB_ANSWER": "คำตอบ", -"TAB_LEARN_MORE": "เรียนรู้เพิ่มเติม", -"ENHANCE_INPUT_IMAGE_REQUIRED": "กรุณาตั้งค่ารูปภาพอินพุตก่อนสร้าง", -"ENHANCE_UPSCALE": "อัปสเกล", -"ENHANCE_IMAGE_PROMPT": "พรอมต์รูปภาพ", -"ENHANCE_INPAINT": "Inpaint", -"ENHANCE_OUTPAINT": "Outpaint", -"ENHANCE_UPSCALE_SCALE": "อัปสเกล", -"ENHANCE_UPSCALE_SCALE_X1_5": "อัปสเกล (1.5 เท่า)", -"ENHANCE_UPSCALE_SCALE_X2_0": "อัปสเกล (2.0 เท่า)", -"ENHANCE_UPSCALE_VARIATION": "การเปลี่ยนแปลง", -"ENHANCE_UPSCALE_VARIATION_NONE": "ไม่มี", -"ENHANCE_UPSCALE_VARIATION_DISABLE": "ละเอียดอ่อน", -"ENHANCE_UPSCALE_VARIATION_STRONG": "Strong", -"ENHANCE_INPAINT_TYPE": "ประเภท", -"ENHANCE_INPAINT_FILL": "Fill With Something New", -"ENHANCE_INPAINT_FIX": "Fix and Repair Masked Area", -"ENHANCE_INPAINT_MASK_REQUIRED": "Please draw inpaint mask before generate", -"ENHANCE_INPAINT_USE_IMAGE_MODEL": "ใช้แบบจำลองภาพปัจจุบัน", -"ENHANCE_COM_DENOISE": "Influenceล", -"ENHANCE_OUTPAINT_DIRECTION": "ทิศทาง", -"ENHANCE_PREVIEW_BEFORE_PROCESS": "ก่อนดำเนินการ", -"ENHANCE_PREVIEW_AFTER_PROCESS": "หลังดำเนินการ", -"ENHANCE_IMAGE_PROMPT_TIP": "Describe how you want the image to be stylized. Include descriptions of the original image you want to keep and descriptions of what you want to be different. Adjust the influence slider to increase or decrease the how much influence your prompt has in the outcome", -"ENHANCE_INPAINT_TIP": "Use the pen tool to mask an area. Describe in the prompt what you want to Fix or Fill. Select the Fix or Fill option, then adjust the level of influence the prompt has", -"ENHANCE_OUTPAINT_TIP": "Select the direction you want to expand, adjust the value of denoise if necessary, and enter a prompt word in the text box. The expanded image will be generated", -"ENHANCE_UPSCALE_TIP": "Upload the image that needs to be manipulated, select the amplification factor, adjust the amplitude of denoise (if less than 0.1, the image content will not change, but the size will still upsacle), enter the prompt word in the text box to generate the enlarged image", -"ERROR_UNSUPPORTED_IMAGE_TYPE": "Only supports PNG, JPG, GIF, and BMP image formats", -"ANSWER_USER_NAME": "You", -"ANSWER_AI_NAME": "Playground", -"ANSWER_ERROR_NOT_PROMPT": "Please input prompt", -"ANSWER_ERROR_CLEAR_SESSION": "Clear Chat History", -"ANSWER_RAG_ENABLE": "Enable File Query", -"ANSWER_RAG_OPEN_DIALOG": "Open File Uploader", -"DOWNLOADER_CONFRIM_TIP": "You are missing one or more models needed to run. Would you like to download the model(s) listed below?", -"DOWNLOADER_MODEL": "Model", -"DOWNLOADER_INFO": "Info", -"DOWNLOADER_FILE_SIZE": "Size", -"DOWNLOADER_REASON": "Reason", -"DOWNLOADER_TERMS": "Visit", -"DOWNLOADER_CONFLICT": "Another download task is currently in progress, and a new task cannot be started. You can cancel the current download task and start a new download task", -"DOWNLOADER_TERMS_TIP": "I have reviewed the model card(s) and license(s). I agree to all terms and conditions would like to download the third-party model(s).", -"DOWNLOADER_FOR_ANSWER_GENERATE": "Answer Model", -"DOWNLOADER_FOR_RAG_QUERY": "RAG Embed Model", -"DOWNLOADER_FOR_IMAGE_GENERATE": "แบบจำลองภาพ", -"DOWNLOADER_FOR_INAPINT_GENERATE": "โมเดล InPaint/OutPaint", -"DOWNLOADER_FOR_IMAGE_PREVIEW": "Image Preview Model", -"DOWNLOADER_FOR_IMAGE_UPSCALE": "ภาพโมเดลอัพสเกล", -"DOWNLOADER_FOR_IMAGE_LORA": "แบบจำลองภาพรวดเร็ว", -"DOWNLOADER_DONWLOAD_TASK_PROGRESS": "ดาวน์โหลดโมเดลเสร็จสมบูรณ์", -"RAG_FILE_TOTAL_FORMAT": "File total: {total}", -"RAG_DRAG_UPLOAD": "ลากไฟล์เพื่ออัพโหลด", -"RAG_DRAG_UPLOAD_UNSUPPORTED": "โปรแกรมปัจจุบันเริ่มทำงานด้วยสิทธิ์ของผู้ดูแลระบบ เนื่องจากข้อจำกัดของ UAC ของ Windows จึงไม่สามารถใช้การลากและวางเพื่ออัปโหลดไฟล์ได้ โปรดใช้ปุ่มเพิ่มที่ด้านบนเพื่ออัปโหลดไฟล์.", -"RAG_UPLOAD_MIME_TYPE": "ประเภทไฟล์ที่รองรับ:\n\tText Doc: .txt .md\n\tOffice Doc: .doc(x)\n\tPDF Doc: .pdf", -"RAG_ENABLE_TIP": "ให้คำตอบเฉพาะที่อัพโหลดเท่านั้น", -"RAG_UPLOAD_TYPE_ERROR": "มีประเภทไฟล์ที่ไม่รองรับในไฟล์ที่อัพโหลดซึ่งจะถูกแยกออกโดยอัตโนมัติ", -"RAG_UPLOAD_FILE_EXISTS": "ไฟล์ที่อัพโหลด {filename} มีอยู่แล้ว", -"RAG_ANALYZE_FILE_FAILED": "การวิเคราะห์ไฟล์ {filename} ล้มเหลว", -"RAG_USE_REJECT": "ไม่พบรายการเอกสาร กรุณาอัปโหลดเอกสารก่อน", -"RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "กรุณารอสักครู่จนกว่าเอกสารทั้งหมดจะได้รับการวิเคราะห์", -"RAG_SOURCE": "จากแหล่งที่มา:", -"ERR_NOT_ENOUGH_DISK_SPACE": "พื้นที่ดิสก์ไม่เพียงพอ ต้องใช้พื้นที่ว่าง {requires_space} แต่มีพื้นที่ว่างเพียง {free_space} เท่านั้น", -"ERR_DOWNLOAD_FAILED": "การดาวน์โหลดไฟล์โมเดลล้มเหลว", -"ERROR_RUNTIME_ERROR": "หากเกิดข้อผิดพลาด โปรดรีสตาร์ทโปรแกรมและลองอีกครั้ง", -"ERROR_GENERATE_UNKONW_EXCEPTION": "เกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ ไม่สามารถสร้างจากแบบจำลองได้", -"ERROR_FOLDER_NOT_EXISTS": "ไม่พบไดเร็กทอรีที่ระบุ", -"ERROR_ENHANCE_IMAGE_NOT_SET": "กรุณาตั้งค่าภาพอินพุตที่จะสร้างขึ้น", -"ERROR_UNFOUND_GRAPHICS": "หากไม่พบฮาร์ดแวร์ที่จำเป็นสำหรับการทำงานของโปรแกรมบนอุปกรณ์คอมพิวเตอร์ โปรแกรมจะหยุดทำงานเมื่อคลิกตกลง.", -"COM_DO_NOT_SHOW_AGAIN": "อย่าแสดงข้อความนี้อีก", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "โหมด HD อาจส่งผลให้ประสิทธิภาพการทำงานช้าลงกว่าปกติบนระบบที่มี VRAM น้อยกว่า 12 GB หรือหน่วยความจำระบบ 24GB สำหรับพีซี Intel Core Ultra", -"SETTINGS_MODEL_IMAGE_SIZE": "ขนาดภาพ", -"SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", -"SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API Token", -"SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "โปรดป้อนโทเค็นที่ถูกต้อง (hf_***)", -"INCREASE_FONT_SIZE": "ขยายข้อความ", -"DECREASE_FONT_SIZE": "ย่อข้อความ", -"DOWNLOADER_GATED": "Gated", -"DOWNLOADER_GATED_INFO": "Some of the models you are trying to download are gated.", -"DOWNLOADER_GATED_TOKEN": "โปรดเพิ่มโทเค็น API ของ Huggingface.co ในการตั้งค่า", -"DOWNLOADER_GATED_ACCEPT": "โปรดอย่าลืมไปที่หน้าข้อมูลโมเดลและขอสิทธิ์เข้าถึง หากคุณไม่ได้รับอนุญาตให้เข้าถึงโมเดลที่มีGated การดาวน์โหลดจะล้มเหลว", -"ERROR_PYTHON_BACKEND_INIT": "การเริ่มต้นแบ็กเอนด์ล้มเหลว", -"ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "แบ็กเอนด์การอนุมาน AI ไม่สามารถเริ่มต้นได้ โปรดลองรีสตาร์ทแอปพลิเคชัน หากปัญหายังคงมีอยู่ คุณสามารถตรวจสอบรายละเอียดเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับข้อผิดพลาดได้", -"ERROR_PYTHON_BACKEND_INIT_DETAILS": "รายละเอียด", -"ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "Open Log" + "MAIN_TITLE": "AI PLAYGROUND", + "COM_SETTINGS": "การตั้งค่า", + "COM_MINI": "ย่อหน้า", + "COM_CLOSE": "ปิด", + "COM_RESTORE": "คืนค่าเริ่มต้น", + "COM_SD_PROMPT": "ป้อนคำสั่งเพื่อสร้างภาพ", + "COM_LLM_PROMPT": "ป้อนคำสั่งเพื่อสร้างคำตอบ", + "COM_CLICK_UPLOAD": "- คลิกเพื่ออัพโหลดรูปภาพ -", + "COM_GENERATE": "เริ่มสร้าง", + "COM_GENERATING": "กำลังสร้าง", + "COM_HISTORY": "History", + "COM_POST_TO_ENHANCE_PROCESS": "โพสต์เพื่อปรับปรุงกระบวนการ", + "COM_ZOOM_IN": "ดูขนาดต้นฉบับ", + "COM_COPY": "Copy", + "COM_COPY_SUCCESS_TIP": "Copy Success", + "COM_APPLY": "Apply", + "COM_DOWNLOAD": "Download", + "COM_REGENERATE": "เริ่มสร้าง", + "COM_DELETE": "ลบ", + "COM_DOWNLOAD_MODEL": "กำลังดาวน์โหลดโมเดล", + "COM_DOWNLOAD_SPEED": "ความเร็วในการดาวน์โหลด", + "COM_LOADING_MODEL": "กำลังโหลดโมเดล AI", + "COM_LOADING_MODEL_COMPONENTS": "กำลังโหลดส่วนประกอบโมเดล AI", + "COM_NO_SELECTED": "ไม่มีการเลือก", + "COM_CONFIRM": "ยืนยัน", + "COM_CANCEL": "ยกเลิก", + "COM_VISIT": "VISIT", + "COM_REQUESTING": "Requesting", + "COM_DEFAULT": "ค่าเริ่มต้น", + "COM_STOP": "หยุด", + "COM_OPEN_LOCATION": "Open Location", + "COM_OPEN_PARAMS": "ข้อมูลพารามิเตอร์", + "COM_LOW": "low", + "COM_HIGH": "high", + "COM_FULLSCREEN": "เต็มจอ", + "COM_FULLSCREEN_EXIT": "FullScreen Exit", + "SETTINGS_TAB_BASIC": "การตั้งค่าพื้นฐาน", + "SETTINGS_TAB_MODEL": "โมเดล", + "SETTINGS_INFERENCE_DEVICE": "Inference Device", + "SETTINGS_MODEL_IMAGE_RESOLUTION": "ความละเอียดของภาพ", + "SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "มาตรฐาน", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "HD", + "SETTINGS_MODEL_QUALITY": "คุณภาพที่ต้องการ", + "SETTINGS_MODEL_QUALITY_STANDARD": "มาตรฐาน", + "SETTINGS_MODEL_QUALITY_HIGH": "คุณภาพสูง", + "SETTINGS_MODEL_QUALITY_FAST": "อย่างรวดเร็ว", + "SETTINGS_MODEL_QUALITY_MANUAL": "ปรับแต่งเอง", + "SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "ตัวเลือกที่ปรับได้", + "SETTINGS_MODEL_MANUAL_OPTIONS": "เลือกด้วยตนเอง", + "SETTINGS_MODEL_NEGATIVE_PROMPT": "Negative Prompt", + "SETTINGS_MODEL_SEED": "Seed", + "SETTINGS_MODEL_GENERATE_NUMBER": "Generate Image Number", + "SETTINGS_MODEL_LLM_MODEL": "Large Language Model", + "SETTINGS_MODEL_IMAGE_PREVIEW": "Image Preview", + "SETTINGS_MODEL_SAFE_CHECK": "Safe Check(Effect only on SD 1.5)", + "SETTINGS_MODEL_IMAGE_MODEL": "Image Model", + "SETTINGS_MODEL_INPAINT_MODEL": "Inpaint/Outpaint Model", + "SETTINGS_MODEL_IMAGE_WIDTH": "ความกว้าง", + "SETTINGS_MODEL_IMAGE_HEIGHT": "Height", + "SETTINGS_MODEL_IMAGE_STEPS": "Steps", + "SETTINGS_MODEL_IMAGE_CFG": "CFG", + "SETTINGS_MODEL_LORA": "Lora", + "SETTINGS_MODEL_SCHEDULER": "ตัวกำหนดตารางเวลา", + "SETTINGS_MODEL_LIST_CHANGE_TIP": "การเปลี่ยนแปลงในรายการโมเดลส่งผลให้มีการปรับเปลี่ยนการตั้งค่าบางอย่าง โปรดดูการตั้งค่าพื้นฐานสำหรับข้อมูลเพิ่มเติม", + "SETTINGS_BASIC_LANGUAGE": "ภาษา", + "SETTINGS_BASIC_PATHS": "เส้นทาง", + "SETTINGS_BASIC_LLM_CHECKPOINTS": "LLM Model Path", + "SETTINGS_MODEL_SD_CHECKPOINTS": "Image Model Path", + "SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "เส้นทางโมเดล Inpaint ของภาพ", + "SETTINGS_MODEL_SD_VAE": "Vae Path", + "SETTINGS_MODEL_SD_LORA": "Lora Path", + "SETTINGS_MODEL_SD_SCHEDULER": "เส้นทางตัวกำหนดตารางเวลา", + "SETTINGS_MODEL_SD_PRESET_MODEL": "โมเดลเริ่มต้นของภาพ", + "SETTINGS_MODEL_SD_STANDARD_MODEL": "โมเดลเริ่มต้นมาตรฐาน", + "SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "โมเดล Inpaint/OutPaint เริ่มต้นมาตรฐาน", + "SETTINGS_MODEL_SD_HD_MODEL": "โมเดลเริ่มต้น HD", + "SETTINGS_MODEL_RAG_MODEL": "โมเดลการค้นหา Rag", + "SETTINGS_MODEL_EXIST": "โมเดลมีอยู่แล้ว ไม่จำเป็นต้องดาวน์โหลดซ้ำ", + "SETTINGS_MODEL_DOWNLOAD": "ดาวน์โหลดโมเดล", + "SETTINGS_MODEL_DOWNLOAD_DESC": "ด้านล่างนี้เป็นรายการโมเดลที่สามารถทำงานต่างๆ ใน ​​Al Playground ได้ ตรวจสอบเงื่อนไขของแต่ละโมเดลก่อนดาวน์โหลดและใช้งาน หรือเลือกที่จะเพิ่มเข้าไปและเลือกแหล่งโมเดลอื่นโดยเปลี่ยนเส้นทางโมเดลและค่าเริ่มต้นของโมเดลด้านบน", + "TAB_CREATE": "สร้าง", + "TAB_ENHANCE": "ปรับปรุง", + "TAB_ANSWER": "คำตอบ", + "TAB_LEARN_MORE": "เรียนรู้เพิ่มเติม", + "ENHANCE_INPUT_IMAGE_REQUIRED": "กรุณาตั้งค่ารูปภาพอินพุตก่อนสร้าง", + "ENHANCE_UPSCALE": "อัปสเกล", + "ENHANCE_IMAGE_PROMPT": "พรอมต์รูปภาพ", + "ENHANCE_INPAINT": "Inpaint", + "ENHANCE_OUTPAINT": "Outpaint", + "ENHANCE_UPSCALE_SCALE": "อัปสเกล", + "ENHANCE_UPSCALE_SCALE_X1_5": "อัปสเกล (1.5 เท่า)", + "ENHANCE_UPSCALE_SCALE_X2_0": "อัปสเกล (2.0 เท่า)", + "ENHANCE_UPSCALE_VARIATION": "การเปลี่ยนแปลง", + "ENHANCE_UPSCALE_VARIATION_NONE": "ไม่มี", + "ENHANCE_UPSCALE_VARIATION_DISABLE": "ละเอียดอ่อน", + "ENHANCE_UPSCALE_VARIATION_STRONG": "Strong", + "ENHANCE_INPAINT_TYPE": "ประเภท", + "ENHANCE_INPAINT_FILL": "Fill With Something New", + "ENHANCE_INPAINT_FIX": "Fix and Repair Masked Area", + "ENHANCE_INPAINT_MASK_REQUIRED": "Please draw inpaint mask before generate", + "ENHANCE_INPAINT_USE_IMAGE_MODEL": "ใช้แบบจำลองภาพปัจจุบัน", + "ENHANCE_COM_DENOISE": "Influenceล", + "ENHANCE_OUTPAINT_DIRECTION": "ทิศทาง", + "ENHANCE_PREVIEW_BEFORE_PROCESS": "ก่อนดำเนินการ", + "ENHANCE_PREVIEW_AFTER_PROCESS": "หลังดำเนินการ", + "ENHANCE_IMAGE_PROMPT_TIP": "Describe how you want the image to be stylized. Include descriptions of the original image you want to keep and descriptions of what you want to be different. Adjust the influence slider to increase or decrease the how much influence your prompt has in the outcome", + "ENHANCE_INPAINT_TIP": "Use the pen tool to mask an area. Describe in the prompt what you want to Fix or Fill. Select the Fix or Fill option, then adjust the level of influence the prompt has", + "ENHANCE_OUTPAINT_TIP": "Select the direction you want to expand, adjust the value of denoise if necessary, and enter a prompt word in the text box. The expanded image will be generated", + "ENHANCE_UPSCALE_TIP": "Upload the image that needs to be manipulated, select the amplification factor, adjust the amplitude of denoise (if less than 0.1, the image content will not change, but the size will still upsacle), enter the prompt word in the text box to generate the enlarged image", + "ERROR_UNSUPPORTED_IMAGE_TYPE": "Only supports PNG, JPG, GIF, and BMP image formats", + "ANSWER_USER_NAME": "You", + "ANSWER_AI_NAME": "Playground", + "ANSWER_ERROR_NOT_PROMPT": "Please input prompt", + "ANSWER_ERROR_CLEAR_SESSION": "Clear Chat History", + "ANSWER_RAG_ENABLE": "Enable File Query", + "ANSWER_RAG_OPEN_DIALOG": "Open File Uploader", + "DOWNLOADER_CONFRIM_TIP": "You are missing one or more models needed to run. Would you like to download the model(s) listed below?", + "DOWNLOADER_MODEL": "Model", + "DOWNLOADER_INFO": "Info", + "DOWNLOADER_FILE_SIZE": "Size", + "DOWNLOADER_REASON": "Reason", + "DOWNLOADER_TERMS": "Visit", + "DOWNLOADER_CONFLICT": "Another download task is currently in progress, and a new task cannot be started. You can cancel the current download task and start a new download task", + "DOWNLOADER_TERMS_TIP": "I have reviewed the model card(s) and license(s). I agree to all terms and conditions would like to download the third-party model(s).", + "DOWNLOADER_FOR_ANSWER_GENERATE": "Answer Model", + "DOWNLOADER_FOR_RAG_QUERY": "RAG Embed Model", + "DOWNLOADER_FOR_IMAGE_GENERATE": "แบบจำลองภาพ", + "DOWNLOADER_FOR_INAPINT_GENERATE": "โมเดล InPaint/OutPaint", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "Image Preview Model", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "ภาพโมเดลอัพสเกล", + "DOWNLOADER_FOR_IMAGE_LORA": "แบบจำลองภาพรวดเร็ว", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "ดาวน์โหลดโมเดลเสร็จสมบูรณ์", + "RAG_FILE_TOTAL_FORMAT": "File total: {total}", + "RAG_DRAG_UPLOAD": "ลากไฟล์เพื่ออัพโหลด", + "RAG_DRAG_UPLOAD_UNSUPPORTED": "โปรแกรมปัจจุบันเริ่มทำงานด้วยสิทธิ์ของผู้ดูแลระบบ เนื่องจากข้อจำกัดของ UAC ของ Windows จึงไม่สามารถใช้การลากและวางเพื่ออัปโหลดไฟล์ได้ โปรดใช้ปุ่มเพิ่มที่ด้านบนเพื่ออัปโหลดไฟล์.", + "RAG_UPLOAD_MIME_TYPE": "ประเภทไฟล์ที่รองรับ:\n\tText Doc: .txt .md\n\tOffice Doc: .doc(x)\n\tPDF Doc: .pdf", + "RAG_ENABLE_TIP": "ให้คำตอบเฉพาะที่อัพโหลดเท่านั้น", + "RAG_UPLOAD_TYPE_ERROR": "มีประเภทไฟล์ที่ไม่รองรับในไฟล์ที่อัพโหลดซึ่งจะถูกแยกออกโดยอัตโนมัติ", + "RAG_UPLOAD_FILE_EXISTS": "ไฟล์ที่อัพโหลด {filename} มีอยู่แล้ว", + "RAG_ANALYZE_FILE_FAILED": "การวิเคราะห์ไฟล์ {filename} ล้มเหลว", + "RAG_USE_REJECT": "ไม่พบรายการเอกสาร กรุณาอัปโหลดเอกสารก่อน", + "RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "กรุณารอสักครู่จนกว่าเอกสารทั้งหมดจะได้รับการวิเคราะห์", + "RAG_SOURCE": "จากแหล่งที่มา:", + "ERR_NOT_ENOUGH_DISK_SPACE": "พื้นที่ดิสก์ไม่เพียงพอ ต้องใช้พื้นที่ว่าง {requires_space} แต่มีพื้นที่ว่างเพียง {free_space} เท่านั้น", + "ERR_DOWNLOAD_FAILED": "การดาวน์โหลดไฟล์โมเดลล้มเหลว", + "ERROR_RUNTIME_ERROR": "หากเกิดข้อผิดพลาด โปรดรีสตาร์ทโปรแกรมและลองอีกครั้ง", + "ERROR_GENERATE_UNKONW_EXCEPTION": "เกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ ไม่สามารถสร้างจากแบบจำลองได้", + "ERROR_FOLDER_NOT_EXISTS": "ไม่พบไดเร็กทอรีที่ระบุ", + "ERROR_ENHANCE_IMAGE_NOT_SET": "กรุณาตั้งค่าภาพอินพุตที่จะสร้างขึ้น", + "ERROR_UNFOUND_GRAPHICS": "หากไม่พบฮาร์ดแวร์ที่จำเป็นสำหรับการทำงานของโปรแกรมบนอุปกรณ์คอมพิวเตอร์ โปรแกรมจะหยุดทำงานเมื่อคลิกตกลง.", + "COM_DO_NOT_SHOW_AGAIN": "อย่าแสดงข้อความนี้อีก", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "โหมด HD อาจส่งผลให้ประสิทธิภาพการทำงานช้าลงกว่าปกติบนระบบที่มี VRAM น้อยกว่า 12 GB หรือหน่วยความจำระบบ 24GB สำหรับพีซี Intel Core Ultra", + "SETTINGS_MODEL_IMAGE_SIZE": "ขนาดภาพ", + "SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", + "SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API Token", + "SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "โปรดป้อนโทเค็นที่ถูกต้อง (hf_***)", + "INCREASE_FONT_SIZE": "ขยายข้อความ", + "DECREASE_FONT_SIZE": "ย่อข้อความ", + "DOWNLOADER_GATED": "Gated", + "DOWNLOADER_GATED_INFO": "Some of the models you are trying to download are gated.", + "DOWNLOADER_GATED_TOKEN": "โปรดเพิ่มโทเค็น API ของ Huggingface.co ในการตั้งค่า", + "DOWNLOADER_GATED_ACCEPT": "โปรดอย่าลืมไปที่หน้าข้อมูลโมเดลและขอสิทธิ์เข้าถึง หากคุณไม่ได้รับอนุญาตให้เข้าถึงโมเดลที่มีGated การดาวน์โหลดจะล้มเหลว", + "ERROR_PYTHON_BACKEND_INIT": "การเริ่มต้นแบ็กเอนด์ล้มเหลว", + "ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "แบ็กเอนด์การอนุมาน AI ไม่สามารถเริ่มต้นได้ โปรดลองรีสตาร์ทแอปพลิเคชัน หากปัญหายังคงมีอยู่ คุณสามารถตรวจสอบรายละเอียดเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับข้อผิดพลาดได้", + "ERROR_PYTHON_BACKEND_INIT_DETAILS": "รายละเอียด", + "ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "Open Log" } diff --git a/WebUI/src/assets/i18n/vi.json b/WebUI/src/assets/i18n/vi.json index c25789d3..a0bf4202 100644 --- a/WebUI/src/assets/i18n/vi.json +++ b/WebUI/src/assets/i18n/vi.json @@ -1,168 +1,168 @@ { -"MAIN_TITLE": "AI PLAYGROUND", -"COM_SETTINGS": "Cài đặt", -"COM_MINI": "Thu nhỏ", -"COM_CLOSE": "Đóng", -"COM_RESTORE": "Phục hồi mặc định", -"COM_SD_PROMPT": "Nhập lệnh để sáng tạo ảnh", -"COM_LLM_PROMPT": "Nhập lệnh để bắt đầu", -"COM_CLICK_UPLOAD": "- Tải hình ảnh -", -"COM_GENERATE": "Tạo", -"COM_GENERATING": "Đang tạo", -"COM_HISTORY": "Lịch sử", -"COM_POST_TO_ENHANCE_PROCESS": "Cải thiện xử lý", -"COM_ZOOM_IN": "Coi kích thước gốc", -"COM_COPY": "Sao chép", -"COM_COPY_SUCCESS_TIP": "Sao chép thành công", -"COM_APPLY": "Cập nhật", -"COM_DOWNLOAD": "Tải về", -"COM_REGENERATE": "Tạo lại", -"COM_DELETE": "Xóa", -"COM_DOWNLOAD_MODEL": "Tải về mô hình", -"COM_DOWNLOAD_SPEED": "Tốc độ tải về", -"COM_LOADING_MODEL": "Chọn mô hình AI", -"COM_LOADING_MODEL_COMPONENTS": "Chọn thành phần của mẫu AI", -"COM_NO_SELECTED": "Không chọn", -"COM_CONFIRM": "Xác nhận", -"COM_CANCEL": "Hủy bỏ", -"COM_VISIT": "Ghé thăm", -"COM_REQUESTING": "Đang yêu cầu", -"COM_DEFAULT": "Mặc định", -"COM_STOP": "Dừng", -"COM_OPEN_LOCATION": "Mở thư mục", -"COM_OPEN_PARAMS": "Thông tin tham số", -"COM_LOW": "thấp", -"COM_HIGH": "cao", -"COM_FULLSCREEN": "Toàn màn hình", -"COM_FULLSCREEN_EXIT": "Thoát toàn màn hình", -"SETTINGS_TAB_BASIC": "Cài đặt cơ bản", -"SETTINGS_TAB_MODEL": "Mẫu", -"SETTINGS_INFERENCE_DEVICE": "Thiết bị suy luận", -"SETTINGS_MODEL_IMAGE_RESOLUTION": "Độ phân giải ảnh", -"SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Tiêu chuẩn", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "Cao", -"SETTINGS_MODEL_QUALITY": "Chất lượng đầu ra", -"SETTINGS_MODEL_QUALITY_STANDARD": "Tiêu chuẩn", -"SETTINGS_MODEL_QUALITY_HIGH": "Chất lượng cao", -"SETTINGS_MODEL_QUALITY_FAST": "Nhanh", -"SETTINGS_MODEL_QUALITY_MANUAL": "Tùy chọn", -"SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Các tùy chọn điều chỉnh", -"SETTINGS_MODEL_MANUAL_OPTIONS": "Các tùy chọn thủ công", -"SETTINGS_MODEL_NEGATIVE_PROMPT": "Từ ngữ tiêu cực", -"SETTINGS_MODEL_SEED": "Nguyên bản", -"SETTINGS_MODEL_GENERATE_NUMBER": "Số lượng ảnh được tạo", -"SETTINGS_MODEL_LLM_MODEL": "Mô hình ngôn ngữ lớn", -"SETTINGS_MODEL_IMAGE_PREVIEW": "Ảnh xem trước", -"SETTINGS_MODEL_SAFE_CHECK": "Kiểm tra an toàn(Chỉ có ở SD 1.5)", -"SETTINGS_MODEL_IMAGE_MODEL": "Mô hình tạo ảnh", -"SETTINGS_MODEL_INPAINT_MODEL": "Mô hình Inpaint/Outpaint", -"SETTINGS_MODEL_IMAGE_WIDTH": "Ngang", -"SETTINGS_MODEL_IMAGE_HEIGHT": "Cao", -"SETTINGS_MODEL_IMAGE_STEPS": "Số bước", -"SETTINGS_MODEL_IMAGE_CFG": "CFG", -"SETTINGS_MODEL_LORA": "Lora", -"SETTINGS_MODEL_SCHEDULER": "Quản lý hàng chờ", -"SETTINGS_MODEL_LIST_CHANGE_TIP": "Những thay đổi trong danh sách mô hình đã dẫn đến việc sửa đổi một số cài đặt của bạn. Vui lòng tham khảo cài đặt cơ bản để biết thêm thông tin.", -"SETTINGS_BASIC_LANGUAGE": "Ngôn ngữ", -"SETTINGS_BASIC_PATHS": "Đường dẫn", -"SETTINGS_BASIC_LLM_CHECKPOINTS": "Đường dẫn mô hình ngôn ngữ lớn", -"SETTINGS_MODEL_SD_CHECKPOINTS": "Đường dẫn mô hình tạo ảnh", -"SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Đường dẫn mô hình Inpaint", -"SETTINGS_MODEL_SD_VAE": "Vae Path", -"SETTINGS_MODEL_SD_LORA": "Lora Path", -"SETTINGS_MODEL_SD_SCHEDULER": "Đường dẫn quản lý hàng chờ", -"SETTINGS_MODEL_SD_PRESET_MODEL": "Mô hình tạo ảnh mặc định", -"SETTINGS_MODEL_SD_STANDARD_MODEL": "Mô hình tiêu chuẩn mặc định", -"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Mô hình tiêu chuẩn mặc định Inpaint/OutPaint", -"SETTINGS_MODEL_SD_HD_MODEL": "Mô hình chất lượng cao mặc định", -"SETTINGS_MODEL_RAG_MODEL": "Mô hình Rag Query", -"SETTINGS_MODEL_EXIST": "Mô hình đã có sẵn. Việc tải về là không cần thiết.", -"SETTINGS_MODEL_DOWNLOAD": "Tải về mô hình", -"SETTINGS_MODEL_DOWNLOAD_DESC": "Dưới đây là danh sách các mô hình AI để hoạt động với AI Playground. Hãy coi các điều khoản của từng mô hình trước khi tải về và sử dụng. Cũng có thể thêm vào các mô hình ở các nguồn khác bằng cách thay đổi đường dẫn đến thư mục chứa mô hình mặc định ở trên.", -"TAB_CREATE": "Tạo ảnh", -"TAB_ENHANCE": "Cải tiến ảnh", -"TAB_ANSWER": "Chatbot", -"TAB_LEARN_MORE": "Tìm hiểu thêm", -"ENHANCE_INPUT_IMAGE_REQUIRED": "Vui lòng tải ảnh lên trước", -"ENHANCE_UPSCALE": "Tăng độ phân giải", -"ENHANCE_IMAGE_PROMPT": "Câu lệnh hình ảnh", -"ENHANCE_INPAINT": "Inpaint", -"ENHANCE_OUTPAINT": "Outpaint", -"ENHANCE_UPSCALE_SCALE": "Tăng độ phân giải", -"ENHANCE_UPSCALE_SCALE_X1_5": "Tăng độ phân giải (1.5X)", -"ENHANCE_UPSCALE_SCALE_X2_0": "Tăng độ phân giải (2.0X)", -"ENHANCE_UPSCALE_VARIATION": "Biến thể", -"ENHANCE_UPSCALE_VARIATION_NONE": "Không", -"ENHANCE_UPSCALE_VARIATION_DISABLE": "Tinh tế", -"ENHANCE_UPSCALE_VARIATION_STRONG": "Mạnh", -"ENHANCE_INPAINT_TYPE": "Kiểu", -"ENHANCE_INPAINT_FILL": "Thay đổi một kiểu mới", -"ENHANCE_INPAINT_FIX": "Chỉnh sửa vùng được chọn", -"ENHANCE_INPAINT_MASK_REQUIRED": "Vui lòng chọn một vùng trước", -"ENHANCE_INPAINT_USE_IMAGE_MODEL": "Sử dụng mô hình Ảnh hiện tại", -"ENHANCE_COM_DENOISE": "Ảnh hưởng", -"ENHANCE_OUTPAINT_DIRECTION": "Phương hướng", -"ENHANCE_PREVIEW_BEFORE_PROCESS": "Trước khi xử lý", -"ENHANCE_PREVIEW_AFTER_PROCESS": "Sau khi xử lý", -"ENHANCE_IMAGE_PROMPT_TIP": "Mô tả cách bạn muốn hình ảnh được cách điệu. Bao gồm mô tả về hình ảnh gốc mà bạn muốn giữ lại và mô tả về những gì bạn muốn khác biệt. Điều chỉnh thanh trượt ảnh hưởng để tăng hoặc giảm mức độ ảnh hưởng của lời nhắc của bạn đến kết quả", -"ENHANCE_INPAINT_TIP": "Sử dụng công cụ bút để che một vùng. Mô tả trong lời nhắc những gì bạn muốn Sửa hoặc Tô. Chọn tùy chọn Sửa hoặc Tô, sau đó điều chỉnh mức độ ảnh hưởng mà lời nhắc có", -"ENHANCE_OUTPAINT_TIP": "Chọn hướng bạn muốn mở rộng, điều chỉnh giá trị khử nhiễu nếu cần và nhập từ nhắc vào hộp văn bản. Hình ảnh mở rộng sẽ được tạo", -"ENHANCE_UPSCALE_TIP": "Tải ảnh cần xử lý lên, chọn hệ số khuếch đại, điều chỉnh biên độ khử nhiễu (nếu nhỏ hơn 0,1 thì nội dung ảnh không thay đổi nhưng kích thước vẫn tăng), nhập từ khóa vào hộp văn bản để tạo ảnh phóng to.", -"ERROR_UNSUPPORTED_IMAGE_TYPE": "Chỉ hỗ trợ các định dạng hình ảnh PNG, JPG, GIF và BMP", -"ANSWER_USER_NAME": "Tên bạn", -"ANSWER_AI_NAME": "Playground", -"ANSWER_ERROR_NOT_PROMPT": "Vui lòng nhập lệnh", -"ANSWER_ERROR_CLEAR_SESSION": "Xóa lịch sử Chat", -"ANSWER_RAG_ENABLE": "Bật truy vấn tài liệu", -"ANSWER_RAG_OPEN_DIALOG": "Mở trình tải lên tài liệu", -"DOWNLOADER_CONFRIM_TIP": "Bạn đang thiếu một hoặc nhiều mô hình cần thiết để chạy. Bạn có muốn tải xuống các mô hình được liệt kê bên dưới không?", -"DOWNLOADER_MODEL": "Mô hình", -"DOWNLOADER_INFO": "Thông tin", -"DOWNLOADER_FILE_SIZE": "Kích thước", -"DOWNLOADER_REASON": "Nguyên nhân", -"DOWNLOADER_TERMS": "Ghé thăm", -"DOWNLOADER_CONFLICT": "Một tác vụ tải xuống khác hiện đang được tiến hành và không thể bắt đầu tác vụ mới. Bạn có thể hủy tác vụ tải xuống hiện tại và bắt đầu tác vụ tải xuống mới", -"DOWNLOADER_TERMS_TIP": "Tôi đã xem xét thẻ mẫu và giấy phép. Tôi đồng ý với tất cả các điều khoản và điều kiện muốn tải xuống mô hình của bên thứ ba.", -"DOWNLOADER_FOR_ANSWER_GENERATE": "Mô hình trả lời", -"DOWNLOADER_FOR_RAG_QUERY": "Mô hình RAG Embed", -"DOWNLOADER_FOR_IMAGE_GENERATE": "Mô hình tạo ảnh", -"DOWNLOADER_FOR_INAPINT_GENERATE": "Mô hình cách điệu ảnh", -"DOWNLOADER_FOR_IMAGE_PREVIEW": "Mô hình xem trước ảnh", -"DOWNLOADER_FOR_IMAGE_UPSCALE": "Mô hình tăng độ phân giải ảnh", -"DOWNLOADER_FOR_IMAGE_LORA": "Mô hình tạo ảnh nhanh", -"DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Tải về mô hình hoàn tất", -"RAG_FILE_TOTAL_FORMAT": "Tổng cộng tài liệu: {tổng cộng}", -"RAG_DRAG_UPLOAD": "Kéo file vào đây để tải lên", -"RAG_DRAG_UPLOAD_UNSUPPORTED": "Chương trình hiện tại được khởi động với quyền quản trị viên. Do giới hạn UAC của Windows, không thể sử dụng chức năng kéo và thả để tải lên. Vui lòng sử dụng nút thêm ở trên cùng để tải lên tệp.", -"RAG_UPLOAD_MIME_TYPE": "Hỗ trợ các loại tài liệu:\n\tText Doc: .txt .md\n\tOffice Doc: .doc(x)\n\tPDF Doc: .pdf", -"RAG_ENABLE_TIP": "chỉ cung cấp câu trả lời cho tài liệu đã tải lên", -"RAG_UPLOAD_TYPE_ERROR": "Có một loại tệp không được hỗ trợ trong tệp đã tải lên, loại tệp này đã bị loại trừ tự động", -"RAG_UPLOAD_FILE_EXISTS": "Tài liệu {filename} đã tồn tại", -"RAG_ANALYZE_FILE_FAILED": "Phân tích tài liệu {filename} không thành công", -"RAG_USE_REJECT": "Danh sách tài liệu tương tác đang trống, vui lòng tải tài liệu lên trước", -"RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Vui lòng đợi cho đến khi tất cả các tài liệu được phân tích xong", -"RAG_SOURCE": "Từ nguồn:", -"ERR_NOT_ENOUGH_DISK_SPACE": "Không đủ dung lượng lưu trữ. Hệ thống yêu cầu {requires_space}, nhưng hiện tại chỉ trống {free_space}", -"ERR_DOWNLOAD_FAILED": "Tải về mô hình tài liệu không thành công", -"ERROR_RUNTIME_ERROR": "Nếu xảy ra lỗi nghiêm trọng, vui lòng khởi động lại chương trình và thử lại", -"ERROR_GENERATE_UNKONW_EXCEPTION": "Đã xảy ra lỗi không xác định. Không thể tạo từ mô hình", -"ERROR_FOLDER_NOT_EXISTS": "Thư mục được chỉ định không tồn tại", -"ERROR_ENHANCE_IMAGE_NOT_SET": "Vui lòng cấu hình hình ảnh đầu vào để tạo ra", -"ERROR_UNFOUND_GRAPHICS": "Nếu phần cứng cần thiết để thực hiện chương trình không được phát hiện trên thiết bị máy tính, chương trình sẽ kết thúc khi nhấp vào OK.", -"COM_DO_NOT_SHOW_AGAIN": "Không hiển thị lại", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "Chế độ HD có thể khiến hiệu suất chậm hơn bình thường trên các hệ thống có VRAM dưới 12 GB hoặc bộ nhớ hệ thống 24 GB cho PC Intel Core Ultra", -"SETTINGS_MODEL_IMAGE_SIZE": "Kích thước ảnh", -"SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", -"SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API Token", -"SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "Vui lòng nhập vào Token đúng (hf_***)", -"INCREASE_FONT_SIZE": "Tăng cỡ chữ", -"DECREASE_FONT_SIZE": "Giảm cỡ chữ", -"DOWNLOADER_GATED": "Cổng tải về", -"DOWNLOADER_GATED_INFO": "Thông tin cổng tải về", -"DOWNLOADER_GATED_TOKEN": "Vui lòng bổ sung API Token của huggingface.com trong cài đặt", -"DOWNLOADER_GATED_ACCEPT": "Hãy đảm bảo truy cập trang thông tin mô hình và yêu cầu quyền truy cập. Nếu bạn chưa được cấp quyền truy cập vào mô hình có cổng, quá trình tải xuống sẽ không thành công.", -"ERROR_PYTHON_BACKEND_INIT": "Khởi tạo hệ thống nền không thành công", -"ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "Không thể khởi tạo phần phụ trợ suy luận AI. Vui lòng thử khởi động lại ứng dụng. Nếu sự cố vẫn tiếp diễn, bạn có thể kiểm tra Chi tiết để biết thêm thông tin về lỗi.", -"ERROR_PYTHON_BACKEND_INIT_DETAILS": "Chi tiết", -"ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "Mở lịch sử hoạt động" + "MAIN_TITLE": "AI PLAYGROUND", + "COM_SETTINGS": "Cài đặt", + "COM_MINI": "Thu nhỏ", + "COM_CLOSE": "Đóng", + "COM_RESTORE": "Phục hồi mặc định", + "COM_SD_PROMPT": "Nhập lệnh để sáng tạo ảnh", + "COM_LLM_PROMPT": "Nhập lệnh để bắt đầu", + "COM_CLICK_UPLOAD": "- Tải hình ảnh -", + "COM_GENERATE": "Tạo", + "COM_GENERATING": "Đang tạo", + "COM_HISTORY": "Lịch sử", + "COM_POST_TO_ENHANCE_PROCESS": "Cải thiện xử lý", + "COM_ZOOM_IN": "Coi kích thước gốc", + "COM_COPY": "Sao chép", + "COM_COPY_SUCCESS_TIP": "Sao chép thành công", + "COM_APPLY": "Cập nhật", + "COM_DOWNLOAD": "Tải về", + "COM_REGENERATE": "Tạo lại", + "COM_DELETE": "Xóa", + "COM_DOWNLOAD_MODEL": "Tải về mô hình", + "COM_DOWNLOAD_SPEED": "Tốc độ tải về", + "COM_LOADING_MODEL": "Chọn mô hình AI", + "COM_LOADING_MODEL_COMPONENTS": "Chọn thành phần của mẫu AI", + "COM_NO_SELECTED": "Không chọn", + "COM_CONFIRM": "Xác nhận", + "COM_CANCEL": "Hủy bỏ", + "COM_VISIT": "Ghé thăm", + "COM_REQUESTING": "Đang yêu cầu", + "COM_DEFAULT": "Mặc định", + "COM_STOP": "Dừng", + "COM_OPEN_LOCATION": "Mở thư mục", + "COM_OPEN_PARAMS": "Thông tin tham số", + "COM_LOW": "thấp", + "COM_HIGH": "cao", + "COM_FULLSCREEN": "Toàn màn hình", + "COM_FULLSCREEN_EXIT": "Thoát toàn màn hình", + "SETTINGS_TAB_BASIC": "Cài đặt cơ bản", + "SETTINGS_TAB_MODEL": "Mẫu", + "SETTINGS_INFERENCE_DEVICE": "Thiết bị suy luận", + "SETTINGS_MODEL_IMAGE_RESOLUTION": "Độ phân giải ảnh", + "SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "Tiêu chuẩn", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "Cao", + "SETTINGS_MODEL_QUALITY": "Chất lượng đầu ra", + "SETTINGS_MODEL_QUALITY_STANDARD": "Tiêu chuẩn", + "SETTINGS_MODEL_QUALITY_HIGH": "Chất lượng cao", + "SETTINGS_MODEL_QUALITY_FAST": "Nhanh", + "SETTINGS_MODEL_QUALITY_MANUAL": "Tùy chọn", + "SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "Các tùy chọn điều chỉnh", + "SETTINGS_MODEL_MANUAL_OPTIONS": "Các tùy chọn thủ công", + "SETTINGS_MODEL_NEGATIVE_PROMPT": "Từ ngữ tiêu cực", + "SETTINGS_MODEL_SEED": "Nguyên bản", + "SETTINGS_MODEL_GENERATE_NUMBER": "Số lượng ảnh được tạo", + "SETTINGS_MODEL_LLM_MODEL": "Mô hình ngôn ngữ lớn", + "SETTINGS_MODEL_IMAGE_PREVIEW": "Ảnh xem trước", + "SETTINGS_MODEL_SAFE_CHECK": "Kiểm tra an toàn(Chỉ có ở SD 1.5)", + "SETTINGS_MODEL_IMAGE_MODEL": "Mô hình tạo ảnh", + "SETTINGS_MODEL_INPAINT_MODEL": "Mô hình Inpaint/Outpaint", + "SETTINGS_MODEL_IMAGE_WIDTH": "Ngang", + "SETTINGS_MODEL_IMAGE_HEIGHT": "Cao", + "SETTINGS_MODEL_IMAGE_STEPS": "Số bước", + "SETTINGS_MODEL_IMAGE_CFG": "CFG", + "SETTINGS_MODEL_LORA": "Lora", + "SETTINGS_MODEL_SCHEDULER": "Quản lý hàng chờ", + "SETTINGS_MODEL_LIST_CHANGE_TIP": "Những thay đổi trong danh sách mô hình đã dẫn đến việc sửa đổi một số cài đặt của bạn. Vui lòng tham khảo cài đặt cơ bản để biết thêm thông tin.", + "SETTINGS_BASIC_LANGUAGE": "Ngôn ngữ", + "SETTINGS_BASIC_PATHS": "Đường dẫn", + "SETTINGS_BASIC_LLM_CHECKPOINTS": "Đường dẫn mô hình ngôn ngữ lớn", + "SETTINGS_MODEL_SD_CHECKPOINTS": "Đường dẫn mô hình tạo ảnh", + "SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "Đường dẫn mô hình Inpaint", + "SETTINGS_MODEL_SD_VAE": "Vae Path", + "SETTINGS_MODEL_SD_LORA": "Lora Path", + "SETTINGS_MODEL_SD_SCHEDULER": "Đường dẫn quản lý hàng chờ", + "SETTINGS_MODEL_SD_PRESET_MODEL": "Mô hình tạo ảnh mặc định", + "SETTINGS_MODEL_SD_STANDARD_MODEL": "Mô hình tiêu chuẩn mặc định", + "SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "Mô hình tiêu chuẩn mặc định Inpaint/OutPaint", + "SETTINGS_MODEL_SD_HD_MODEL": "Mô hình chất lượng cao mặc định", + "SETTINGS_MODEL_RAG_MODEL": "Mô hình Rag Query", + "SETTINGS_MODEL_EXIST": "Mô hình đã có sẵn. Việc tải về là không cần thiết.", + "SETTINGS_MODEL_DOWNLOAD": "Tải về mô hình", + "SETTINGS_MODEL_DOWNLOAD_DESC": "Dưới đây là danh sách các mô hình AI để hoạt động với AI Playground. Hãy coi các điều khoản của từng mô hình trước khi tải về và sử dụng. Cũng có thể thêm vào các mô hình ở các nguồn khác bằng cách thay đổi đường dẫn đến thư mục chứa mô hình mặc định ở trên.", + "TAB_CREATE": "Tạo ảnh", + "TAB_ENHANCE": "Cải tiến ảnh", + "TAB_ANSWER": "Chatbot", + "TAB_LEARN_MORE": "Tìm hiểu thêm", + "ENHANCE_INPUT_IMAGE_REQUIRED": "Vui lòng tải ảnh lên trước", + "ENHANCE_UPSCALE": "Tăng độ phân giải", + "ENHANCE_IMAGE_PROMPT": "Câu lệnh hình ảnh", + "ENHANCE_INPAINT": "Inpaint", + "ENHANCE_OUTPAINT": "Outpaint", + "ENHANCE_UPSCALE_SCALE": "Tăng độ phân giải", + "ENHANCE_UPSCALE_SCALE_X1_5": "Tăng độ phân giải (1.5X)", + "ENHANCE_UPSCALE_SCALE_X2_0": "Tăng độ phân giải (2.0X)", + "ENHANCE_UPSCALE_VARIATION": "Biến thể", + "ENHANCE_UPSCALE_VARIATION_NONE": "Không", + "ENHANCE_UPSCALE_VARIATION_DISABLE": "Tinh tế", + "ENHANCE_UPSCALE_VARIATION_STRONG": "Mạnh", + "ENHANCE_INPAINT_TYPE": "Kiểu", + "ENHANCE_INPAINT_FILL": "Thay đổi một kiểu mới", + "ENHANCE_INPAINT_FIX": "Chỉnh sửa vùng được chọn", + "ENHANCE_INPAINT_MASK_REQUIRED": "Vui lòng chọn một vùng trước", + "ENHANCE_INPAINT_USE_IMAGE_MODEL": "Sử dụng mô hình Ảnh hiện tại", + "ENHANCE_COM_DENOISE": "Ảnh hưởng", + "ENHANCE_OUTPAINT_DIRECTION": "Phương hướng", + "ENHANCE_PREVIEW_BEFORE_PROCESS": "Trước khi xử lý", + "ENHANCE_PREVIEW_AFTER_PROCESS": "Sau khi xử lý", + "ENHANCE_IMAGE_PROMPT_TIP": "Mô tả cách bạn muốn hình ảnh được cách điệu. Bao gồm mô tả về hình ảnh gốc mà bạn muốn giữ lại và mô tả về những gì bạn muốn khác biệt. Điều chỉnh thanh trượt ảnh hưởng để tăng hoặc giảm mức độ ảnh hưởng của lời nhắc của bạn đến kết quả", + "ENHANCE_INPAINT_TIP": "Sử dụng công cụ bút để che một vùng. Mô tả trong lời nhắc những gì bạn muốn Sửa hoặc Tô. Chọn tùy chọn Sửa hoặc Tô, sau đó điều chỉnh mức độ ảnh hưởng mà lời nhắc có", + "ENHANCE_OUTPAINT_TIP": "Chọn hướng bạn muốn mở rộng, điều chỉnh giá trị khử nhiễu nếu cần và nhập từ nhắc vào hộp văn bản. Hình ảnh mở rộng sẽ được tạo", + "ENHANCE_UPSCALE_TIP": "Tải ảnh cần xử lý lên, chọn hệ số khuếch đại, điều chỉnh biên độ khử nhiễu (nếu nhỏ hơn 0,1 thì nội dung ảnh không thay đổi nhưng kích thước vẫn tăng), nhập từ khóa vào hộp văn bản để tạo ảnh phóng to.", + "ERROR_UNSUPPORTED_IMAGE_TYPE": "Chỉ hỗ trợ các định dạng hình ảnh PNG, JPG, GIF và BMP", + "ANSWER_USER_NAME": "Tên bạn", + "ANSWER_AI_NAME": "Playground", + "ANSWER_ERROR_NOT_PROMPT": "Vui lòng nhập lệnh", + "ANSWER_ERROR_CLEAR_SESSION": "Xóa lịch sử Chat", + "ANSWER_RAG_ENABLE": "Bật truy vấn tài liệu", + "ANSWER_RAG_OPEN_DIALOG": "Mở trình tải lên tài liệu", + "DOWNLOADER_CONFRIM_TIP": "Bạn đang thiếu một hoặc nhiều mô hình cần thiết để chạy. Bạn có muốn tải xuống các mô hình được liệt kê bên dưới không?", + "DOWNLOADER_MODEL": "Mô hình", + "DOWNLOADER_INFO": "Thông tin", + "DOWNLOADER_FILE_SIZE": "Kích thước", + "DOWNLOADER_REASON": "Nguyên nhân", + "DOWNLOADER_TERMS": "Ghé thăm", + "DOWNLOADER_CONFLICT": "Một tác vụ tải xuống khác hiện đang được tiến hành và không thể bắt đầu tác vụ mới. Bạn có thể hủy tác vụ tải xuống hiện tại và bắt đầu tác vụ tải xuống mới", + "DOWNLOADER_TERMS_TIP": "Tôi đã xem xét thẻ mẫu và giấy phép. Tôi đồng ý với tất cả các điều khoản và điều kiện muốn tải xuống mô hình của bên thứ ba.", + "DOWNLOADER_FOR_ANSWER_GENERATE": "Mô hình trả lời", + "DOWNLOADER_FOR_RAG_QUERY": "Mô hình RAG Embed", + "DOWNLOADER_FOR_IMAGE_GENERATE": "Mô hình tạo ảnh", + "DOWNLOADER_FOR_INAPINT_GENERATE": "Mô hình cách điệu ảnh", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "Mô hình xem trước ảnh", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "Mô hình tăng độ phân giải ảnh", + "DOWNLOADER_FOR_IMAGE_LORA": "Mô hình tạo ảnh nhanh", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "Tải về mô hình hoàn tất", + "RAG_FILE_TOTAL_FORMAT": "Tổng cộng tài liệu: {tổng cộng}", + "RAG_DRAG_UPLOAD": "Kéo file vào đây để tải lên", + "RAG_DRAG_UPLOAD_UNSUPPORTED": "Chương trình hiện tại được khởi động với quyền quản trị viên. Do giới hạn UAC của Windows, không thể sử dụng chức năng kéo và thả để tải lên. Vui lòng sử dụng nút thêm ở trên cùng để tải lên tệp.", + "RAG_UPLOAD_MIME_TYPE": "Hỗ trợ các loại tài liệu:\n\tText Doc: .txt .md\n\tOffice Doc: .doc(x)\n\tPDF Doc: .pdf", + "RAG_ENABLE_TIP": "chỉ cung cấp câu trả lời cho tài liệu đã tải lên", + "RAG_UPLOAD_TYPE_ERROR": "Có một loại tệp không được hỗ trợ trong tệp đã tải lên, loại tệp này đã bị loại trừ tự động", + "RAG_UPLOAD_FILE_EXISTS": "Tài liệu {filename} đã tồn tại", + "RAG_ANALYZE_FILE_FAILED": "Phân tích tài liệu {filename} không thành công", + "RAG_USE_REJECT": "Danh sách tài liệu tương tác đang trống, vui lòng tải tài liệu lên trước", + "RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "Vui lòng đợi cho đến khi tất cả các tài liệu được phân tích xong", + "RAG_SOURCE": "Từ nguồn:", + "ERR_NOT_ENOUGH_DISK_SPACE": "Không đủ dung lượng lưu trữ. Hệ thống yêu cầu {requires_space}, nhưng hiện tại chỉ trống {free_space}", + "ERR_DOWNLOAD_FAILED": "Tải về mô hình tài liệu không thành công", + "ERROR_RUNTIME_ERROR": "Nếu xảy ra lỗi nghiêm trọng, vui lòng khởi động lại chương trình và thử lại", + "ERROR_GENERATE_UNKONW_EXCEPTION": "Đã xảy ra lỗi không xác định. Không thể tạo từ mô hình", + "ERROR_FOLDER_NOT_EXISTS": "Thư mục được chỉ định không tồn tại", + "ERROR_ENHANCE_IMAGE_NOT_SET": "Vui lòng cấu hình hình ảnh đầu vào để tạo ra", + "ERROR_UNFOUND_GRAPHICS": "Nếu phần cứng cần thiết để thực hiện chương trình không được phát hiện trên thiết bị máy tính, chương trình sẽ kết thúc khi nhấp vào OK.", + "COM_DO_NOT_SHOW_AGAIN": "Không hiển thị lại", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "Chế độ HD có thể khiến hiệu suất chậm hơn bình thường trên các hệ thống có VRAM dưới 12 GB hoặc bộ nhớ hệ thống 24 GB cho PC Intel Core Ultra", + "SETTINGS_MODEL_IMAGE_SIZE": "Kích thước ảnh", + "SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", + "SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API Token", + "SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "Vui lòng nhập vào Token đúng (hf_***)", + "INCREASE_FONT_SIZE": "Tăng cỡ chữ", + "DECREASE_FONT_SIZE": "Giảm cỡ chữ", + "DOWNLOADER_GATED": "Cổng tải về", + "DOWNLOADER_GATED_INFO": "Thông tin cổng tải về", + "DOWNLOADER_GATED_TOKEN": "Vui lòng bổ sung API Token của huggingface.com trong cài đặt", + "DOWNLOADER_GATED_ACCEPT": "Hãy đảm bảo truy cập trang thông tin mô hình và yêu cầu quyền truy cập. Nếu bạn chưa được cấp quyền truy cập vào mô hình có cổng, quá trình tải xuống sẽ không thành công.", + "ERROR_PYTHON_BACKEND_INIT": "Khởi tạo hệ thống nền không thành công", + "ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "Không thể khởi tạo phần phụ trợ suy luận AI. Vui lòng thử khởi động lại ứng dụng. Nếu sự cố vẫn tiếp diễn, bạn có thể kiểm tra Chi tiết để biết thêm thông tin về lỗi.", + "ERROR_PYTHON_BACKEND_INIT_DETAILS": "Chi tiết", + "ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "Mở lịch sử hoạt động" } diff --git a/WebUI/src/assets/i18n/zh-CN.json b/WebUI/src/assets/i18n/zh-CN.json index 7a2ba424..046d15a6 100644 --- a/WebUI/src/assets/i18n/zh-CN.json +++ b/WebUI/src/assets/i18n/zh-CN.json @@ -31,10 +31,10 @@ "COM_CONFIRM": "确认", "COM_CANCEL": "取消", "COM_VISIT": "跳转", - "COM_REQUESTING":"请求中", - "COM_DEFAULT":"默认", - "COM_STOP":"停止", - "COM_OPEN_LOCATION":"打开文件所在位置", + "COM_REQUESTING": "请求中", + "COM_DEFAULT": "默认", + "COM_STOP": "停止", + "COM_OPEN_LOCATION": "打开文件所在位置", "COM_OPEN_PARAMS": "参数信息", "COM_LOW": "低", "COM_HIGH": "高", @@ -102,7 +102,7 @@ "SETTINGS_MODEL_MANAGE_BACKEND": "管理后端组件", "SETTINGS_MODEL_EXIST": "模型已存在,无需重复下载。", "SETTINGS_MODEL_DOWNLOAD": "下载模型", - "SETTINGS_MODEL_DOWNLOAD_DESC":"以下是可以在 AI Playground 中执行各种 AI 任务的模型列表。在下载和使用之前,请查看每个模型的许可条款。也可以通过更改上面的模型路径和默认值,选择添加其他模型来源。", + "SETTINGS_MODEL_DOWNLOAD_DESC": "以下是可以在 AI Playground 中执行各种 AI 任务的模型列表。在下载和使用之前,请查看每个模型的许可条款。也可以通过更改上面的模型路径和默认值,选择添加其他模型来源。", "TAB_CREATE": "创作", "TAB_ENHANCE": "增强", "TAB_ANSWER": "问答", @@ -128,49 +128,49 @@ "ENHANCE_OUTPAINT_DIRECTION": "扩图方向", "ENHANCE_PREVIEW_BEFORE_PROCESS": "处理前", "ENHANCE_PREVIEW_AFTER_PROCESS": "处理后", - "ENHANCE_IMAGE_PROMPT_TIP":"描述您期望图像的风格化方式。包括您希望保留的原始图像描述以及您希望有所不同的描述。调整影响强度滑块以增加或减少提示对结果的影响程度。", - "ENHANCE_INPAINT_TIP":"使用画笔工具绘制一个遮罩区域。在提示中描述您想要修复或填充的内容。选择修复或填充选项,然后调整提示对结果的影响程度。", - "ENHANCE_OUTPAINT_TIP":"选择您要扩图的方向,按需调整强度,并在文本框中输入提示词。将生成扩展后的图片。", - "ENHANCE_UPSCALE_TIP":"上传需要处理的图像,选择放大倍数,调整放大过程中的重绘强度(如果小于0.1,图像内容不会改变,但尺寸仍会放大),在文本框中输入提示词用来引导生成放大的图像。", + "ENHANCE_IMAGE_PROMPT_TIP": "描述您期望图像的风格化方式。包括您希望保留的原始图像描述以及您希望有所不同的描述。调整影响强度滑块以增加或减少提示对结果的影响程度。", + "ENHANCE_INPAINT_TIP": "使用画笔工具绘制一个遮罩区域。在提示中描述您想要修复或填充的内容。选择修复或填充选项,然后调整提示对结果的影响程度。", + "ENHANCE_OUTPAINT_TIP": "选择您要扩图的方向,按需调整强度,并在文本框中输入提示词。将生成扩展后的图片。", + "ENHANCE_UPSCALE_TIP": "上传需要处理的图像,选择放大倍数,调整放大过程中的重绘强度(如果小于0.1,图像内容不会改变,但尺寸仍会放大),在文本框中输入提示词用来引导生成放大的图像。", "ERROR_UNSUPPORTED_IMAGE_TYPE": "仅支持 PNG, JPG, GIF, BMP 图片格式", "ANSWER_USER_NAME": "您", "ANSWER_AI_NAME": "Playground", "ANSWER_ERROR_NOT_PROMPT": "请输入您的问题", - "ANSWER_ERROR_CLEAR_SESSION":"清空聊天记录", + "ANSWER_ERROR_CLEAR_SESSION": "清空聊天记录", "INCREASE_FONT_SIZE": "增大字体", "DECREASE_FONT_SIZE": "减小字体", - "ANSWER_RAG_ENABLE":"启用文件检索增强(RAG)", - "ANSWER_RAG_OPEN_DIALOG":"上传文件", - "REQUEST_LLM_MODEL_NAME":"添加一个来自 huggingface.co 的模型", + "ANSWER_RAG_ENABLE": "启用文件检索增强(RAG)", + "ANSWER_RAG_OPEN_DIALOG": "上传文件", + "REQUEST_LLM_MODEL_NAME": "添加一个来自 huggingface.co 的模型", "REQUEST_LLM_MODEL_DESCRIPTION": "您可以安装以下格式下载模型仓库:", "REQUEST_LLM_MODEL_EXAMPLE": "<作者名>/<仓库名>, 例如 'facebook/opt-1.3b'", "REQUEST_LLM_SINGLE_EXAMPLE": "<作者名>/<仓库名>/<文件名>, 例如 'microsoft/Phi-3-mini-4k-instruct-gguf/Phi-3-mini-4k-instruct-q4.gguf'", "REQUEST_LLM_MODEL_DISCLAIMER_1": "注意: 并非所有 huggingface.co 上的模型都完全适合您想执行的任务。", "REQUEST_LLM_MODEL_DISCLAIMER_2": "请在下载前仔细阅读模型介绍及其要求。", - "DOWNLOADER_CONFRIM_TIP":"您缺少运行所需的一个或多个模型。您想下载以下模型吗?", - "DOWNLOADER_MODEL":"模型", - "DOWNLOADER_INFO":"信息", - "DOWNLOADER_FILE_SIZE":"文件大小", - "DOWNLOADER_GATED":"受限", + "DOWNLOADER_CONFRIM_TIP": "您缺少运行所需的一个或多个模型。您想下载以下模型吗?", + "DOWNLOADER_MODEL": "模型", + "DOWNLOADER_INFO": "信息", + "DOWNLOADER_FILE_SIZE": "文件大小", + "DOWNLOADER_GATED": "受限", "DOWNLOADER_ACCESS_INFO_SINGLE": "您没有下载当前模型的访问权限。", "DOWNLOADER_GATED_ACCEPT_SINGLE": "该模型为受限模型,请参阅模型简介页面并获取权限。", "DOWNLOADER_ACCESS_ACCEPT_SINGLE": "无法下载受限模型。", "DOWNLOADER_ACCESS_INFO": "您尝试下载的一些模型是受限的。", "DOWNLOADER_GATED_TOKEN": "请在设置中添加您的huggingface.co API令牌。", - "DOWNLOADER_GATED_ACCEPT":"请确保访问模型信息页面并请求访问权限。如果您未被授予受限模型的访问权限,下载将会失败。", + "DOWNLOADER_GATED_ACCEPT": "请确保访问模型信息页面并请求访问权限。如果您未被授予受限模型的访问权限,下载将会失败。", "DOWNLOADER_ACCESS_ACCEPT": "无法下载受限模型。", - "DOWNLOADER_REASON":"用途", - "DOWNLOADER_TERMS":"跳转", - "DOWNLOADER_CONFLICT":"当前有另一个下载任务正在进行,无法启动新任务。您可以取消当前下载任务并启动新下载任务。", - "DOWNLOADER_TERMS_TIP":"我已阅读模型详细信息和许可证。我已同意所有条款和条件,并希望下载第三方模型。", - "DOWNLOADER_FOR_ANSWER_GENERATE":"回答模型", - "DOWNLOADER_FOR_RAG_QUERY":"RAG 嵌入模型", - "DOWNLOADER_FOR_IMAGE_GENERATE":"生图模型", - "DOWNLOADER_FOR_INAPINT_GENERATE":"重绘/扩图模型", - "DOWNLOADER_FOR_IMAGE_PREVIEW":"图片预览模型", - "DOWNLOADER_FOR_IMAGE_UPSCALE":"图片放大模型", + "DOWNLOADER_REASON": "用途", + "DOWNLOADER_TERMS": "跳转", + "DOWNLOADER_CONFLICT": "当前有另一个下载任务正在进行,无法启动新任务。您可以取消当前下载任务并启动新下载任务。", + "DOWNLOADER_TERMS_TIP": "我已阅读模型详细信息和许可证。我已同意所有条款和条件,并希望下载第三方模型。", + "DOWNLOADER_FOR_ANSWER_GENERATE": "回答模型", + "DOWNLOADER_FOR_RAG_QUERY": "RAG 嵌入模型", + "DOWNLOADER_FOR_IMAGE_GENERATE": "生图模型", + "DOWNLOADER_FOR_INAPINT_GENERATE": "重绘/扩图模型", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "图片预览模型", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "图片放大模型", "DOWNLOADER_FOR_IMAGE_LORA": "极速生图模型", - "DOWNLOADER_DONWLOAD_TASK_PROGRESS":"模型下载完成", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "模型下载完成", "RAG_FILE_TOTAL_FORMAT": "文件数量:{total}", "RAG_DRAG_UPLOAD": "将需要上传的文件拖动至此", "RAG_DRAG_UPLOAD_UNSUPPORTED": "当前程序以管理员权限启动。由于Windows UAC限制,无法使用拖拽上传功能。请使用顶部的添加按钮上传文件。", diff --git a/WebUI/src/assets/i18n/zh-TW.json b/WebUI/src/assets/i18n/zh-TW.json index b1ce2f8c..9c3d01cb 100644 --- a/WebUI/src/assets/i18n/zh-TW.json +++ b/WebUI/src/assets/i18n/zh-TW.json @@ -1,168 +1,168 @@ { -"MAIN_TITLE": "AI PLAYGROUND", -"COM_SETTINGS": "設定", -"COM_MINI": "最小化", -"COM_CLOSE": "關閉", -"COM_RESTORE": "回復預設", -"COM_SD_PROMPT": "輸入提示詞以產生圖片", -"COM_LLM_PROMPT": "輸入提示詞以產生回答", -"COM_CLICK_UPLOAD": "- 上傳圖片 -", -"COM_GENERATE": "生成", -"COM_GENERATING": "生成中", -"COM_HISTORY": "歷史紀錄", -"COM_POST_TO_ENHANCE_PROCESS": "進行增強處理", -"COM_ZOOM_IN": "檢視原始尺寸", -"COM_COPY": "複製", -"COM_COPY_SUCCESS_TIP": "複製完成", -"COM_APPLY": "套用", -"COM_DOWNLOAD": "下載", -"COM_REGENERATE": "重新生成", -"COM_DELETE": "刪除", -"COM_DOWNLOAD_MODEL": "模型下載中", -"COM_DOWNLOAD_SPEED": "下載速度", -"COM_LOADING_MODEL": "模型載入中", -"COM_LOADING_MODEL_COMPONENTS": "模型套件載入中", -"COM_NO_SELECTED": "無選取物件", -"COM_CONFIRM": "確認", -"COM_CANCEL": "取消", -"COM_VISIT": "瀏覽", -"COM_REQUESTING": "請求中", -"COM_DEFAULT": "預設", -"COM_STOP": "停止", -"COM_OPEN_LOCATION": "開啟文件所在位置", -"COM_OPEN_PARAMS": "參數資訊", -"COM_LOW": "低", -"COM_HIGH": "高", -"COM_FULLSCREEN": "全螢幕", -"COM_FULLSCREEN_EXIT": "解除全螢幕", -"SETTINGS_TAB_BASIC": "基本設定", -"SETTINGS_TAB_MODEL": "模型", -"SETTINGS_INFERENCE_DEVICE": "推論裝置", -"SETTINGS_MODEL_IMAGE_RESOLUTION": "圖像生成解析度", -"SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "標準", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "高解析", -"SETTINGS_MODEL_QUALITY": "圖像生成畫質", -"SETTINGS_MODEL_QUALITY_STANDARD": "標準畫質", -"SETTINGS_MODEL_QUALITY_HIGH": "高畫質", -"SETTINGS_MODEL_QUALITY_FAST": "極速", -"SETTINGS_MODEL_QUALITY_MANUAL": "自訂", -"SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "可自訂選項", -"SETTINGS_MODEL_MANUAL_OPTIONS": "自訂選項", -"SETTINGS_MODEL_NEGATIVE_PROMPT": "負向提示詞", -"SETTINGS_MODEL_SEED": "亂數種子", -"SETTINGS_MODEL_GENERATE_NUMBER": "圖像生成數目", -"SETTINGS_MODEL_LLM_MODEL": "大語言模型", -"SETTINGS_MODEL_IMAGE_PREVIEW": "生成圖像預覽", -"SETTINGS_MODEL_SAFE_CHECK": "敏感內容檢查(只對SD 1.5生效)", -"SETTINGS_MODEL_IMAGE_MODEL": "圖像生成模型", -"SETTINGS_MODEL_INPAINT_MODEL": "局部重繪/邊緣擴繪模型", -"SETTINGS_MODEL_IMAGE_WIDTH": "寬度", -"SETTINGS_MODEL_IMAGE_HEIGHT": "高度", -"SETTINGS_MODEL_IMAGE_STEPS": "步數", -"SETTINGS_MODEL_IMAGE_CFG": "提示詞引導強度(CFG)", -"SETTINGS_MODEL_LORA": "Lora", -"SETTINGS_MODEL_SCHEDULER": "排程器", -"SETTINGS_MODEL_LIST_CHANGE_TIP": "您的某些設定已被調整來符合模型清單中的變化,如需了解進一步訊息,請參閱基本設定說明。", -"SETTINGS_BASIC_LANGUAGE": "語言", -"SETTINGS_BASIC_PATHS": "路徑", -"SETTINGS_BASIC_LLM_CHECKPOINTS": "大語言模型路徑", -"SETTINGS_MODEL_SD_CHECKPOINTS": "圖像生成模型路徑", -"SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "局部重繪模型路徑", -"SETTINGS_MODEL_SD_VAE": "Vae 路徑", -"SETTINGS_MODEL_SD_LORA": "Lora 路徑", -"SETTINGS_MODEL_SD_SCHEDULER": "Scheduler 路徑", -"SETTINGS_MODEL_SD_PRESET_MODEL": "圖片生成預設模型", -"SETTINGS_MODEL_SD_STANDARD_MODEL": "標準畫質預設模型", -"SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "標準畫質 局部重繪/邊緣擴繪預設模型", -"SETTINGS_MODEL_SD_HD_MODEL": "高畫質圖像生成預設模型", -"SETTINGS_MODEL_RAG_MODEL": "增強檢索模型", -"SETTINGS_MODEL_EXIST": "模型已存在", -"SETTINGS_MODEL_DOWNLOAD": "下載模型", -"SETTINGS_MODEL_DOWNLOAD_DESC": "以下是可以在AI Playground中執行各種AI任務的模型清單。請在下載和使用模型前詳閱其使用條款。也可以透過更改上方模型路徑和預設值,選擇添加其他模型來源。", -"TAB_CREATE": "創作", -"TAB_ENHANCE": "增強", -"TAB_ANSWER": "對話", -"TAB_LEARN_MORE": "了解更多", -"ENHANCE_INPUT_IMAGE_REQUIRED": "請在生成前選取輸入圖片", -"ENHANCE_UPSCALE": "無損放大", -"ENHANCE_IMAGE_PROMPT": "以圖生圖", -"ENHANCE_INPAINT": "局部重繪", -"ENHANCE_OUTPAINT": "邊緣擴繪", -"ENHANCE_UPSCALE_SCALE": "放大倍率", -"ENHANCE_UPSCALE_SCALE_X1_5": "放大(1.5X)", -"ENHANCE_UPSCALE_SCALE_X2_0": "放大(2.0X)", -"ENHANCE_UPSCALE_VARIATION": "變化度", -"ENHANCE_UPSCALE_VARIATION_NONE": "None", -"ENHANCE_UPSCALE_VARIATION_DISABLE": "輕微", -"ENHANCE_UPSCALE_VARIATION_STRONG": "強烈", -"ENHANCE_INPAINT_TYPE": "類型", -"ENHANCE_INPAINT_FILL": "生成新內容填充", -"ENHANCE_INPAINT_FIX": "修復遮罩區域", -"ENHANCE_INPAINT_MASK_REQUIRED": "請在生成之前繪製遮罩區域", -"ENHANCE_INPAINT_USE_IMAGE_MODEL": "使用圖像生成模型", -"ENHANCE_COM_DENOISE": "影響程度", -"ENHANCE_OUTPAINT_DIRECTION": "擴繪方向", -"ENHANCE_PREVIEW_BEFORE_PROCESS": "處理前", -"ENHANCE_PREVIEW_AFTER_PROCESS": "處理後", -"ENHANCE_IMAGE_PROMPT_TIP": "描述您想要圖像風格化的方式。包含描述您想保留的原圖部分以及想更改的部分。調整影響程度滑桿以增加或減少提示詞對結果的影響。", -"ENHANCE_INPAINT_TIP": "使用畫筆工具遮罩區域。在提示詞中描述您想修復或填充的內容,選擇修復或填充選項,然後調整提示詞對結果的影響程度。", -"ENHANCE_OUTPAINT_TIP": "選擇擴展的方向,根據需要調整去噪值,並在文本框中輸入提示詞。擴展後的圖像將會生成。", -"ENHANCE_UPSCALE_TIP": "上傳需要操作的圖片,選擇放大倍率,調整去噪幅度(如果小於0.1,圖像內容不會改變,但尺寸仍然會放大),在文本框中輸入提示詞來生成放大的圖像。", -"ERROR_UNSUPPORTED_IMAGE_TYPE": "僅支援PNG、JPG、GIF和BMP圖像格式", -"ANSWER_USER_NAME": "您", -"ANSWER_AI_NAME": "Playground", -"ANSWER_ERROR_NOT_PROMPT": "請輸入提示詞", -"ANSWER_ERROR_CLEAR_SESSION": "清除對話紀錄", -"ANSWER_RAG_ENABLE": "開啟文件增強檢索(RAG)", -"ANSWER_RAG_OPEN_DIALOG": "上傳文件", -"DOWNLOADER_CONFRIM_TIP": "您缺少運行所需的一個或多個模型。 您想下載以下模型嗎?", -"DOWNLOADER_MODEL": "模型", -"DOWNLOADER_INFO": "訊息", -"DOWNLOADER_FILE_SIZE": "文件大小", -"DOWNLOADER_REASON": "用途", -"DOWNLOADER_TERMS": "瀏覽", -"DOWNLOADER_CONFLICT": "因目前已有下載任務執行中,無法開始新任務。您可以取消目前的任務後再開始新的下載任務", -"DOWNLOADER_TERMS_TIP": "我已閱讀模型詳細訊息和授權。我已同意所有條款與條件,並希望下載外部模型", -"DOWNLOADER_FOR_ANSWER_GENERATE": "對話模型", -"DOWNLOADER_FOR_RAG_QUERY": "RAG嵌入模型", -"DOWNLOADER_FOR_IMAGE_GENERATE": "圖像生成模型", -"DOWNLOADER_FOR_INAPINT_GENERATE": "局部重繪/邊緣擴繪模型", -"DOWNLOADER_FOR_IMAGE_PREVIEW": "生成圖像預覽模型", -"DOWNLOADER_FOR_IMAGE_UPSCALE": "無損放大模型", -"DOWNLOADER_FOR_IMAGE_LORA": "極速圖像生成模型", -"DOWNLOADER_DONWLOAD_TASK_PROGRESS": "模型下載完成", -"RAG_FILE_TOTAL_FORMAT": "文件總數: {total}", -"RAG_DRAG_UPLOAD": "將需要上傳的文件拖曳至此", -"RAG_DRAG_UPLOAD_UNSUPPORTED": "目前程式以管理員權限啟動。因Windows使用者帳戶控制下,無法使用直接拖曳檔案進行上傳。請點選上方按鈕來進行上傳", -"RAG_UPLOAD_MIME_TYPE": "支援的文件類型:\n\t文字檔案: .txt .md\n\tOffice檔案: .doc(x)\n\tPDF檔案: .pdf", -"RAG_ENABLE_TIP": "會已上傳的文件作為主要參考來進行回答", -"RAG_UPLOAD_TYPE_ERROR": "已自動排除上傳檔案中某些不支援格式檔案", -"RAG_UPLOAD_FILE_EXISTS": "上傳的文件 {filename} 已存在", -"RAG_ANALYZE_FILE_FAILED": "文件 {filename} 解析失敗", -"RAG_USE_REJECT": "您的互動式檔案清單不存在,請先上傳文件。", -"RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "請等待所有文件解析完畢", -"RAG_SOURCE": "文件來源:", -"ERR_NOT_ENOUGH_DISK_SPACE": "磁碟空間不足,尚需要 {requires_space}, 僅剩 {free_space} 可用空間", -"ERR_DOWNLOAD_FAILED": "模型下載失敗", -"ERROR_RUNTIME_ERROR": "如有任何錯誤,請重啟程式並再試一次", -"ERROR_GENERATE_UNKONW_EXCEPTION": "因不明原因錯誤,無法透過模型生成內容", -"ERROR_FOLDER_NOT_EXISTS": "指定的檔案路徑不存在", -"ERROR_ENHANCE_IMAGE_NOT_SET": "請指定輸入的圖片", -"ERROR_UNFOUND_GRAPHICS": "如果在系統中未偵測到所需的硬體,程式將會在選擇OK後確認", -"COM_DO_NOT_SHOW_AGAIN": "不再顯示", -"SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "高解析度模式可能導致性能下降,特別是在視訊記憶體小於12GB或系統記憶體小於24GB的Intel Core Ultra PC上。", -"SETTINGS_MODEL_IMAGE_SIZE": "圖片尺寸", -"SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", -"SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API Token", -"SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "請輸入有效的Token (ht_***)", -"INCREASE_FONT_SIZE": "放大字體", -"DECREASE_FONT_SIZE": "縮小字體", -"DOWNLOADER_GATED": "受限制", -"DOWNLOADER_GATED_INFO": "您嘗試下載受限制的模型。", -"DOWNLOADER_GATED_TOKEN": "請在設定頁中新增您的 huggingface.co API token。", -"DOWNLOADER_GATED_ACCEPT": "請確保在瀏覽模型資訊頁面後並請求存取權限。如果您未被賦予受限制模型的存取權限,下載將會失敗。", -"ERROR_PYTHON_BACKEND_INIT": "後端初始化失敗", -"ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "AI後端初始化失敗,請嘗試重新啟動應用程式。如果問題依然存在,您可以查看詳細資訊以獲取有關錯誤的更多訊息。", -"ERROR_PYTHON_BACKEND_INIT_DETAILS": "詳細資訊", -"ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "開啟日誌" + "MAIN_TITLE": "AI PLAYGROUND", + "COM_SETTINGS": "設定", + "COM_MINI": "最小化", + "COM_CLOSE": "關閉", + "COM_RESTORE": "回復預設", + "COM_SD_PROMPT": "輸入提示詞以產生圖片", + "COM_LLM_PROMPT": "輸入提示詞以產生回答", + "COM_CLICK_UPLOAD": "- 上傳圖片 -", + "COM_GENERATE": "生成", + "COM_GENERATING": "生成中", + "COM_HISTORY": "歷史紀錄", + "COM_POST_TO_ENHANCE_PROCESS": "進行增強處理", + "COM_ZOOM_IN": "檢視原始尺寸", + "COM_COPY": "複製", + "COM_COPY_SUCCESS_TIP": "複製完成", + "COM_APPLY": "套用", + "COM_DOWNLOAD": "下載", + "COM_REGENERATE": "重新生成", + "COM_DELETE": "刪除", + "COM_DOWNLOAD_MODEL": "模型下載中", + "COM_DOWNLOAD_SPEED": "下載速度", + "COM_LOADING_MODEL": "模型載入中", + "COM_LOADING_MODEL_COMPONENTS": "模型套件載入中", + "COM_NO_SELECTED": "無選取物件", + "COM_CONFIRM": "確認", + "COM_CANCEL": "取消", + "COM_VISIT": "瀏覽", + "COM_REQUESTING": "請求中", + "COM_DEFAULT": "預設", + "COM_STOP": "停止", + "COM_OPEN_LOCATION": "開啟文件所在位置", + "COM_OPEN_PARAMS": "參數資訊", + "COM_LOW": "低", + "COM_HIGH": "高", + "COM_FULLSCREEN": "全螢幕", + "COM_FULLSCREEN_EXIT": "解除全螢幕", + "SETTINGS_TAB_BASIC": "基本設定", + "SETTINGS_TAB_MODEL": "模型", + "SETTINGS_INFERENCE_DEVICE": "推論裝置", + "SETTINGS_MODEL_IMAGE_RESOLUTION": "圖像生成解析度", + "SETTINGS_MODEL_IMAGE_RESOLUTION_STRANDARD": "標準", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD": "高解析", + "SETTINGS_MODEL_QUALITY": "圖像生成畫質", + "SETTINGS_MODEL_QUALITY_STANDARD": "標準畫質", + "SETTINGS_MODEL_QUALITY_HIGH": "高畫質", + "SETTINGS_MODEL_QUALITY_FAST": "極速", + "SETTINGS_MODEL_QUALITY_MANUAL": "自訂", + "SETTINGS_MODEL_ADJUSTABLE_OPTIONS": "可自訂選項", + "SETTINGS_MODEL_MANUAL_OPTIONS": "自訂選項", + "SETTINGS_MODEL_NEGATIVE_PROMPT": "負向提示詞", + "SETTINGS_MODEL_SEED": "亂數種子", + "SETTINGS_MODEL_GENERATE_NUMBER": "圖像生成數目", + "SETTINGS_MODEL_LLM_MODEL": "大語言模型", + "SETTINGS_MODEL_IMAGE_PREVIEW": "生成圖像預覽", + "SETTINGS_MODEL_SAFE_CHECK": "敏感內容檢查(只對SD 1.5生效)", + "SETTINGS_MODEL_IMAGE_MODEL": "圖像生成模型", + "SETTINGS_MODEL_INPAINT_MODEL": "局部重繪/邊緣擴繪模型", + "SETTINGS_MODEL_IMAGE_WIDTH": "寬度", + "SETTINGS_MODEL_IMAGE_HEIGHT": "高度", + "SETTINGS_MODEL_IMAGE_STEPS": "步數", + "SETTINGS_MODEL_IMAGE_CFG": "提示詞引導強度(CFG)", + "SETTINGS_MODEL_LORA": "Lora", + "SETTINGS_MODEL_SCHEDULER": "排程器", + "SETTINGS_MODEL_LIST_CHANGE_TIP": "您的某些設定已被調整來符合模型清單中的變化,如需了解進一步訊息,請參閱基本設定說明。", + "SETTINGS_BASIC_LANGUAGE": "語言", + "SETTINGS_BASIC_PATHS": "路徑", + "SETTINGS_BASIC_LLM_CHECKPOINTS": "大語言模型路徑", + "SETTINGS_MODEL_SD_CHECKPOINTS": "圖像生成模型路徑", + "SETTINGS_MODEL_SD_INPAINT_CHECKPOINTS": "局部重繪模型路徑", + "SETTINGS_MODEL_SD_VAE": "Vae 路徑", + "SETTINGS_MODEL_SD_LORA": "Lora 路徑", + "SETTINGS_MODEL_SD_SCHEDULER": "Scheduler 路徑", + "SETTINGS_MODEL_SD_PRESET_MODEL": "圖片生成預設模型", + "SETTINGS_MODEL_SD_STANDARD_MODEL": "標準畫質預設模型", + "SETTINGS_MODEL_SD_STANDARD_INPAINT_MODEL": "標準畫質 局部重繪/邊緣擴繪預設模型", + "SETTINGS_MODEL_SD_HD_MODEL": "高畫質圖像生成預設模型", + "SETTINGS_MODEL_RAG_MODEL": "增強檢索模型", + "SETTINGS_MODEL_EXIST": "模型已存在", + "SETTINGS_MODEL_DOWNLOAD": "下載模型", + "SETTINGS_MODEL_DOWNLOAD_DESC": "以下是可以在AI Playground中執行各種AI任務的模型清單。請在下載和使用模型前詳閱其使用條款。也可以透過更改上方模型路徑和預設值,選擇添加其他模型來源。", + "TAB_CREATE": "創作", + "TAB_ENHANCE": "增強", + "TAB_ANSWER": "對話", + "TAB_LEARN_MORE": "了解更多", + "ENHANCE_INPUT_IMAGE_REQUIRED": "請在生成前選取輸入圖片", + "ENHANCE_UPSCALE": "無損放大", + "ENHANCE_IMAGE_PROMPT": "以圖生圖", + "ENHANCE_INPAINT": "局部重繪", + "ENHANCE_OUTPAINT": "邊緣擴繪", + "ENHANCE_UPSCALE_SCALE": "放大倍率", + "ENHANCE_UPSCALE_SCALE_X1_5": "放大(1.5X)", + "ENHANCE_UPSCALE_SCALE_X2_0": "放大(2.0X)", + "ENHANCE_UPSCALE_VARIATION": "變化度", + "ENHANCE_UPSCALE_VARIATION_NONE": "None", + "ENHANCE_UPSCALE_VARIATION_DISABLE": "輕微", + "ENHANCE_UPSCALE_VARIATION_STRONG": "強烈", + "ENHANCE_INPAINT_TYPE": "類型", + "ENHANCE_INPAINT_FILL": "生成新內容填充", + "ENHANCE_INPAINT_FIX": "修復遮罩區域", + "ENHANCE_INPAINT_MASK_REQUIRED": "請在生成之前繪製遮罩區域", + "ENHANCE_INPAINT_USE_IMAGE_MODEL": "使用圖像生成模型", + "ENHANCE_COM_DENOISE": "影響程度", + "ENHANCE_OUTPAINT_DIRECTION": "擴繪方向", + "ENHANCE_PREVIEW_BEFORE_PROCESS": "處理前", + "ENHANCE_PREVIEW_AFTER_PROCESS": "處理後", + "ENHANCE_IMAGE_PROMPT_TIP": "描述您想要圖像風格化的方式。包含描述您想保留的原圖部分以及想更改的部分。調整影響程度滑桿以增加或減少提示詞對結果的影響。", + "ENHANCE_INPAINT_TIP": "使用畫筆工具遮罩區域。在提示詞中描述您想修復或填充的內容,選擇修復或填充選項,然後調整提示詞對結果的影響程度。", + "ENHANCE_OUTPAINT_TIP": "選擇擴展的方向,根據需要調整去噪值,並在文本框中輸入提示詞。擴展後的圖像將會生成。", + "ENHANCE_UPSCALE_TIP": "上傳需要操作的圖片,選擇放大倍率,調整去噪幅度(如果小於0.1,圖像內容不會改變,但尺寸仍然會放大),在文本框中輸入提示詞來生成放大的圖像。", + "ERROR_UNSUPPORTED_IMAGE_TYPE": "僅支援PNG、JPG、GIF和BMP圖像格式", + "ANSWER_USER_NAME": "您", + "ANSWER_AI_NAME": "Playground", + "ANSWER_ERROR_NOT_PROMPT": "請輸入提示詞", + "ANSWER_ERROR_CLEAR_SESSION": "清除對話紀錄", + "ANSWER_RAG_ENABLE": "開啟文件增強檢索(RAG)", + "ANSWER_RAG_OPEN_DIALOG": "上傳文件", + "DOWNLOADER_CONFRIM_TIP": "您缺少運行所需的一個或多個模型。 您想下載以下模型嗎?", + "DOWNLOADER_MODEL": "模型", + "DOWNLOADER_INFO": "訊息", + "DOWNLOADER_FILE_SIZE": "文件大小", + "DOWNLOADER_REASON": "用途", + "DOWNLOADER_TERMS": "瀏覽", + "DOWNLOADER_CONFLICT": "因目前已有下載任務執行中,無法開始新任務。您可以取消目前的任務後再開始新的下載任務", + "DOWNLOADER_TERMS_TIP": "我已閱讀模型詳細訊息和授權。我已同意所有條款與條件,並希望下載外部模型", + "DOWNLOADER_FOR_ANSWER_GENERATE": "對話模型", + "DOWNLOADER_FOR_RAG_QUERY": "RAG嵌入模型", + "DOWNLOADER_FOR_IMAGE_GENERATE": "圖像生成模型", + "DOWNLOADER_FOR_INAPINT_GENERATE": "局部重繪/邊緣擴繪模型", + "DOWNLOADER_FOR_IMAGE_PREVIEW": "生成圖像預覽模型", + "DOWNLOADER_FOR_IMAGE_UPSCALE": "無損放大模型", + "DOWNLOADER_FOR_IMAGE_LORA": "極速圖像生成模型", + "DOWNLOADER_DONWLOAD_TASK_PROGRESS": "模型下載完成", + "RAG_FILE_TOTAL_FORMAT": "文件總數: {total}", + "RAG_DRAG_UPLOAD": "將需要上傳的文件拖曳至此", + "RAG_DRAG_UPLOAD_UNSUPPORTED": "目前程式以管理員權限啟動。因Windows使用者帳戶控制下,無法使用直接拖曳檔案進行上傳。請點選上方按鈕來進行上傳", + "RAG_UPLOAD_MIME_TYPE": "支援的文件類型:\n\t文字檔案: .txt .md\n\tOffice檔案: .doc(x)\n\tPDF檔案: .pdf", + "RAG_ENABLE_TIP": "會已上傳的文件作為主要參考來進行回答", + "RAG_UPLOAD_TYPE_ERROR": "已自動排除上傳檔案中某些不支援格式檔案", + "RAG_UPLOAD_FILE_EXISTS": "上傳的文件 {filename} 已存在", + "RAG_ANALYZE_FILE_FAILED": "文件 {filename} 解析失敗", + "RAG_USE_REJECT": "您的互動式檔案清單不存在,請先上傳文件。", + "RAG_WHEN_CLOSE_PANEL_AT_UPLODING": "請等待所有文件解析完畢", + "RAG_SOURCE": "文件來源:", + "ERR_NOT_ENOUGH_DISK_SPACE": "磁碟空間不足,尚需要 {requires_space}, 僅剩 {free_space} 可用空間", + "ERR_DOWNLOAD_FAILED": "模型下載失敗", + "ERROR_RUNTIME_ERROR": "如有任何錯誤,請重啟程式並再試一次", + "ERROR_GENERATE_UNKONW_EXCEPTION": "因不明原因錯誤,無法透過模型生成內容", + "ERROR_FOLDER_NOT_EXISTS": "指定的檔案路徑不存在", + "ERROR_ENHANCE_IMAGE_NOT_SET": "請指定輸入的圖片", + "ERROR_UNFOUND_GRAPHICS": "如果在系統中未偵測到所需的硬體,程式將會在選擇OK後確認", + "COM_DO_NOT_SHOW_AGAIN": "不再顯示", + "SETTINGS_MODEL_IMAGE_RESOLUTION_HD_CONFIRM": "高解析度模式可能導致性能下降,特別是在視訊記憶體小於12GB或系統記憶體小於24GB的Intel Core Ultra PC上。", + "SETTINGS_MODEL_IMAGE_SIZE": "圖片尺寸", + "SETTINGS_MODEL_HUGGINGFACE": "Hugging Face", + "SETTINGS_MODEL_HUGGINGFACE_API_TOKEN": "API Token", + "SETTINGS_MODEL_HUGGINGFACE_INVALID_TOKEN_TEXT": "請輸入有效的Token (ht_***)", + "INCREASE_FONT_SIZE": "放大字體", + "DECREASE_FONT_SIZE": "縮小字體", + "DOWNLOADER_GATED": "受限制", + "DOWNLOADER_GATED_INFO": "您嘗試下載受限制的模型。", + "DOWNLOADER_GATED_TOKEN": "請在設定頁中新增您的 huggingface.co API token。", + "DOWNLOADER_GATED_ACCEPT": "請確保在瀏覽模型資訊頁面後並請求存取權限。如果您未被賦予受限制模型的存取權限,下載將會失敗。", + "ERROR_PYTHON_BACKEND_INIT": "後端初始化失敗", + "ERROR_PYTHON_BACKEND_INIT_DETAILS_TEXT": "AI後端初始化失敗,請嘗試重新啟動應用程式。如果問題依然存在,您可以查看詳細資訊以獲取有關錯誤的更多訊息。", + "ERROR_PYTHON_BACKEND_INIT_DETAILS": "詳細資訊", + "ERROR_PYTHON_BACKEND_INIT_OPEN_LOG": "開啟日誌" } diff --git a/WebUI/src/assets/js/clientAPI.ts b/WebUI/src/assets/js/clientAPI.ts index bef28ff2..f872c0b6 100644 --- a/WebUI/src/assets/js/clientAPI.ts +++ b/WebUI/src/assets/js/clientAPI.ts @@ -1,35 +1,28 @@ -import { toast } from "./toast"; +export const isClient = + window.chrome.webview && + window.chrome.webview.hostObjects && + window.chrome.webview.hostObjects.clientAPI -export module clientAPI { - - export const isClient = window.chrome.webview && - window.chrome.webview.hostObjects && - window.chrome.webview.hostObjects.clientAPI; - - - export function minimizedWin() { - window.electronAPI.miniWindow(); - } - - export function exitApp() { - window.electronAPI.exitApp(); - } - - export function setWinSize(width: number, height: number) { - return window.electronAPI.setWinSize(width,height) - } +export function minimizedWin() { + window.electronAPI.miniWindow() +} +export function exitApp() { + window.electronAPI.exitApp() +} - export function showOpenDialog(options: Electron.OpenDialogOptions) { - return window.electronAPI.showOpenDialog(options); - } +export function setWinSize(width: number, height: number) { + return window.electronAPI.setWinSize(width, height) +} - export function showSaveDialog(options: Electron.SaveDialogOptions) { - return window.electronAPI.showSaveDialog(options); - } +export function showOpenDialog(options: Electron.OpenDialogOptions) { + return window.electronAPI.showOpenDialog(options) +} +export function showSaveDialog(options: Electron.SaveDialogOptions) { + return window.electronAPI.showSaveDialog(options) +} - export function saveImage(url: string) { - return window.electronAPI.saveImage(url) - } +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 2d7b5e90..d9061383 100644 --- a/WebUI/src/assets/js/const.ts +++ b/WebUI/src/assets/js/const.ts @@ -1,19 +1,17 @@ -export module Const { - export const MODEL_TYPE_LLM = 0; - export const MODEL_TYPE_STABLE_DIFFUSION = 1; - export const MODEL_TYPE_LORA = 2; - export const MODEL_TYPE_VAE = 3; - export const MODEL_TYPE_ESRGAN = 4; - export const MODEL_TYPE_EMBEDDING = 5; - export const MODEL_TYPE_INPAINT = 6; - export const MODEL_TYPE_PREVIEW = 7; - export const MODEL_TYPE_LLAMA_CPP = 8; - export const MODEL_TYPE_COMFY_UNET = 100; - export const MODEL_TYPE_COMFY_CLIP = 101; - export const MODEL_TYPE_COMFY_VAE = 102; - export const MODEL_TYPE_COMFY_DEFAULT_CHECKPOINT = 103; - export const MODEL_TYPE_COMFY_DEFAULT_LORA = 104; - 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 +export const MODEL_TYPE_LLM = 0 +export const MODEL_TYPE_STABLE_DIFFUSION = 1 +export const MODEL_TYPE_LORA = 2 +export const MODEL_TYPE_VAE = 3 +export const MODEL_TYPE_ESRGAN = 4 +export const MODEL_TYPE_EMBEDDING = 5 +export const MODEL_TYPE_INPAINT = 6 +export const MODEL_TYPE_PREVIEW = 7 +export const MODEL_TYPE_LLAMA_CPP = 8 +export const MODEL_TYPE_COMFY_UNET = 100 +export const MODEL_TYPE_COMFY_CLIP = 101 +export const MODEL_TYPE_COMFY_VAE = 102 +export const MODEL_TYPE_COMFY_DEFAULT_CHECKPOINT = 103 +export const MODEL_TYPE_COMFY_DEFAULT_LORA = 104 +export const MODEL_TYPE_COMFY_CONTROL_NET = 105 +export const MODEL_TYPE_FACESWAP = 106 +export const MODEL_TYPE_FACERESTORE = 107 diff --git a/WebUI/src/assets/js/markdownParser.ts b/WebUI/src/assets/js/markdownParser.ts index 3818aa9f..3477f745 100644 --- a/WebUI/src/assets/js/markdownParser.ts +++ b/WebUI/src/assets/js/markdownParser.ts @@ -1,60 +1,60 @@ -import { Marked, Renderer } from "marked"; -import { markedHighlight } from "marked-highlight"; -import hljs from "highlight.js"; -import { util } from "./util"; +import { Marked, Renderer } from 'marked' +import { markedHighlight } from 'marked-highlight' +import hljs from 'highlight.js' +import * as util from './util' export class MarkdownParser { - pattern = /```([^\n]*)(.*?)(?:(?:```)|$)/gs; - copyText = ""; - marked: Marked; - constructor(copyText: string) { - this.copyText = copyText; - this.marked = new Marked( - markedHighlight({ - langPrefix: 'hljs language-', - highlight(code, lang, info) { - const language = hljs.getLanguage(lang) ? lang : 'plaintext'; - const html = hljs.highlight(code, { language }).value; - return html; - } - }) - ); - this.marked.setOptions({ - renderer: new Renderer(), - pedantic: false, - gfm: true, - breaks: true, - }); - } + pattern = /```([^\n]*)(.*?)(?:(?:```)|$)/gs + copyText = '' + marked: Marked + constructor(copyText: string) { + this.copyText = copyText + this.marked = new Marked( + markedHighlight({ + langPrefix: 'hljs language-', + highlight(code, lang, _info) { + const language = hljs.getLanguage(lang) ? lang : 'plaintext' + const html = hljs.highlight(code, { language }).value + return html + }, + }), + ) + this.marked.setOptions({ + renderer: new Renderer(), + pedantic: false, + gfm: true, + breaks: true, + }) + } - parseMarkdown(content: string) { - let lastIndex = 0; - let html = ""; - let matches; - //删除所有Html标签 - content = content.replace(/<[^>]+>/g, ""); - while ((matches = this.pattern.exec(content))) { - const index = matches.index; - if (index > lastIndex) { - html += util.processHTMLTag(content.substring(lastIndex, index)); - } - html += `
+ parseMarkdown(content: string) { + let lastIndex = 0 + let html = '' + let matches + //删除所有Html标签 + content = content.replace(/<[^>]+>/g, '') + while ((matches = this.pattern.exec(content))) { + const index = matches.index + if (index > lastIndex) { + html += util.processHTMLTag(content.substring(lastIndex, index)) + } + html += `
- ${matches[1] || ""} + ${matches[1] || ''}
${this.marked.parse(matches[0])}
-
`; - lastIndex = index + matches[0].length; - } - if (lastIndex == 0) { - html = util.processHTMLTag(content); - } else if (lastIndex != content.length - 1) { - html += util.processHTMLTag(content.substring(lastIndex)); - } - return html; +
` + lastIndex = index + matches[0].length + } + if (lastIndex == 0) { + html = util.processHTMLTag(content) + } else if (lastIndex != content.length - 1) { + html += util.processHTMLTag(content.substring(lastIndex)) } -} \ No newline at end of file + return html + } +} diff --git a/WebUI/src/assets/js/sseProcessor.ts b/WebUI/src/assets/js/sseProcessor.ts index fc15edd7..d6bcf2a4 100644 --- a/WebUI/src/assets/js/sseProcessor.ts +++ b/WebUI/src/assets/js/sseProcessor.ts @@ -1,53 +1,58 @@ export class SSEProcessor { - readonly reader: ReadableStreamDefaultReader; - readonly onData?: (data: string) => void; - readonly onFinish?: () => void; - readonly decoder = new TextDecoder(); - processing = false; - message = ""; + readonly reader: ReadableStreamDefaultReader + readonly onData?: (data: string) => void + readonly onFinish?: () => void + readonly decoder = new TextDecoder() + processing = false + message = '' - constructor(reader: ReadableStreamDefaultReader, onData?: (data: string) => void, onFinish?: () => void) { - this.reader = reader; - this.onData = onData; - this.onFinish = onFinish; - } + constructor( + reader: ReadableStreamDefaultReader, + onData?: (data: string) => void, + onFinish?: () => void, + ) { + this.reader = reader + this.onData = onData + this.onFinish = onFinish + } - async start() { - if (this.processing) { return; } - this.processing = true; - const result = await this.reader.read(); - return this.processText(result) + async start() { + if (this.processing) { + return } + this.processing = true + const result = await this.reader.read() + return this.processText(result) + } - async processText({ done, value }: ReadableStreamReadResult) { - if (done) { - this.processing = false; - this.onFinish?.call(this); - return; - } + async processText({ done, value }: ReadableStreamReadResult) { + if (done) { + this.processing = false + this.onFinish?.call(this) + return + } - const text = this.decoder.decode(value); - let start = 0; - let pos: number; - while (start < text.length) { - pos = text.indexOf('\0', start) - if (pos > -1) { - const line = text.substring(start, pos); - if (line.startsWith('data:')) { - this.onData?.call(this, line); - } else { - this.message += line; - this.onData?.call(this, this.message); - } - this.message = ""; - start = pos + 1; - } else { - this.message += text.substring(start); - break; - } + const text = this.decoder.decode(value) + let start = 0 + let pos: number + while (start < text.length) { + pos = text.indexOf('\0', start) + if (pos > -1) { + const line = text.substring(start, pos) + if (line.startsWith('data:')) { + this.onData?.call(this, line) + } else { + this.message += line + this.onData?.call(this, this.message) } - const result = await this.reader.read(); - await this.processText(result); + this.message = '' + start = pos + 1 + } else { + this.message += text.substring(start) + break + } } - -} \ No newline at end of file + const result = await this.reader.read() + await this.processText(result) + } +} diff --git a/WebUI/src/assets/js/store/backendServices.ts b/WebUI/src/assets/js/store/backendServices.ts index 122844d7..b19ffc39 100644 --- a/WebUI/src/assets/js/store/backendServices.ts +++ b/WebUI/src/assets/js/store/backendServices.ts @@ -1,140 +1,164 @@ -import {defineStore} from "pinia"; +import { defineStore } from 'pinia' - -export const useBackendServices = defineStore("backendServices", () => { - const currentServiceInfo = ref([]); +export const useBackendServices = defineStore( + 'backendServices', + () => { + const currentServiceInfo = ref([]) const serviceListeners: Map = new Map([ - ["ai-backend", new BackendServiceSetupProgressListener("ai-backend")], - ["comfyui-backend", new BackendServiceSetupProgressListener("comfyui-backend")], - ["llamacpp-backend", new BackendServiceSetupProgressListener("llamacpp-backend")], - ]); - - window.electronAPI.getServices().catch(async (reason: any) => { - console.warn("initial service call failed - retrying") - await new Promise(resolve => { - setTimeout(async () => {resolve()}, 1000) + ['ai-backend', new BackendServiceSetupProgressListener('ai-backend')], + ['comfyui-backend', new BackendServiceSetupProgressListener('comfyui-backend')], + ['llamacpp-backend', new BackendServiceSetupProgressListener('llamacpp-backend')], + ]) + + window.electronAPI + .getServices() + .catch(async (_reason: unknown) => { + console.warn('initial service call failed - retrying') + await new Promise((resolve) => { + setTimeout(async () => { + resolve() + }, 1000) }) return window.electronAPI.getServices() - }).then(services => { - currentServiceInfo.value = services; - }); + }) + .then((services) => { + currentServiceInfo.value = services + }) setTimeout(() => { - window.electronAPI.getServices().then(services => { - console.log('getServices', services) - currentServiceInfo.value = services; - }); + window.electronAPI.getServices().then((services) => { + console.log('getServices', services) + currentServiceInfo.value = services + }) }, 5000) - window.electronAPI.onServiceInfoUpdate(updatedInfo => { - currentServiceInfo.value = currentServiceInfo.value.map(oldInfo => oldInfo.serviceName === updatedInfo.serviceName ? updatedInfo : oldInfo); - }); + window.electronAPI.onServiceInfoUpdate((updatedInfo) => { + currentServiceInfo.value = currentServiceInfo.value.map((oldInfo) => + oldInfo.serviceName === updatedInfo.serviceName ? updatedInfo : oldInfo, + ) + }) window.electronAPI.onServiceSetUpProgress(async (data) => { - const associatedListener = serviceListeners.get(data.serviceName) - if (!associatedListener) { - console.warn(`received unexpected setup update for service ${data.serviceName}`) - return - } - associatedListener.addData(data) + const associatedListener = serviceListeners.get(data.serviceName) + if (!associatedListener) { + console.warn(`received unexpected setup update for service ${data.serviceName}`) + return + } + associatedListener.addData(data) }) - const serviceInfoUpdatePresent = computed(() => currentServiceInfo.value.length > 0) - const initalStartupRequestComplete = ref(false) - const allRequiredSetUp = computed(() => currentServiceInfo.value.length > 0 && currentServiceInfo.value.filter(s => s.isRequired).every(s => s.isSetUp)); - const allRequiredRunning = computed(() => currentServiceInfo.value.length > 0 && currentServiceInfo.value.filter(s => s.isRequired).every(s => s.status === "running")); - - - async function startAllSetUpServices(): Promise<{allServicesStarted: boolean}> { - const serverStartups = await Promise.all(currentServiceInfo.value.filter(s => s.isSetUp).map(s => window.electronAPI.sendStartSignal(s.serviceName))); - const serverStartupsCompleted = { allServicesStarted: serverStartups.every(serverStatus => serverStatus === "running")} - if (!serverStartupsCompleted.allServicesStarted) { - console.warn("Not all services started") - } - return serverStartupsCompleted + const serviceInfoUpdatePresent = computed(() => currentServiceInfo.value.length > 0) + const initalStartupRequestComplete = ref(false) + const allRequiredSetUp = computed( + () => + currentServiceInfo.value.length > 0 && + currentServiceInfo.value.filter((s) => s.isRequired).every((s) => s.isSetUp), + ) + const allRequiredRunning = computed( + () => + currentServiceInfo.value.length > 0 && + currentServiceInfo.value.filter((s) => s.isRequired).every((s) => s.status === 'running'), + ) + + async function startAllSetUpServices(): Promise<{ allServicesStarted: boolean }> { + const serverStartups = await Promise.all( + currentServiceInfo.value + .filter((s) => s.isSetUp) + .map((s) => window.electronAPI.sendStartSignal(s.serviceName)), + ) + const serverStartupsCompleted = { + allServicesStarted: serverStartups.every((serverStatus) => serverStatus === 'running'), + } + if (!serverStartupsCompleted.allServicesStarted) { + console.warn('Not all services started') + } + return serverStartupsCompleted } - - async function setUpService(serviceName: BackendServiceName): Promise<{success: boolean, logs: SetupProgress[]}> { - console.log("starting setup") - const listener = serviceListeners.get(serviceName) - if (!listener) { - new Error(`service name ${serviceName} not found.`) - } - listener!.isActive = true - window.electronAPI.sendSetUpSignal(serviceName) - return listener!.awaitFinalizationAndResetData() + async function setUpService( + serviceName: BackendServiceName, + ): Promise<{ success: boolean; logs: SetupProgress[] }> { + console.log('starting setup') + const listener = serviceListeners.get(serviceName) + if (!listener) { + new Error(`service name ${serviceName} not found.`) + } + listener!.isActive = true + window.electronAPI.sendSetUpSignal(serviceName) + return listener!.awaitFinalizationAndResetData() } async function startService(serviceName: BackendServiceName): Promise { - return window.electronAPI.sendStartSignal(serviceName) + return window.electronAPI.sendStartSignal(serviceName) } async function stopService(serviceName: BackendServiceName): Promise { - return window.electronAPI.sendStopSignal(serviceName) + return window.electronAPI.sendStopSignal(serviceName) } return { - info: currentServiceInfo, - serviceInfoUpdateReceived: serviceInfoUpdatePresent, - allRequiredSetUp, - allRequiredRunning, - initalStartupRequestComplete, - startAllSetUpServices, - setUpService, - startService, - stopService, - + info: currentServiceInfo, + serviceInfoUpdateReceived: serviceInfoUpdatePresent, + allRequiredSetUp, + allRequiredRunning, + initalStartupRequestComplete, + startAllSetUpServices, + setUpService, + startService, + stopService, } -}, { + }, + { persist: { - pick: [], - } -}); - + pick: [], + }, + }, +) class BackendServiceSetupProgressListener { - isActive: Boolean = false - readonly associatedServiceName : string - private collectedSetupProgress: SetupProgress[] = [] - private terminalUpdateReceived = false - private installationSuccess: boolean = false - - constructor(associatedServiceName: string) { - this.associatedServiceName = associatedServiceName + isActive: boolean = false + readonly associatedServiceName: string + private collectedSetupProgress: SetupProgress[] = [] + private terminalUpdateReceived = false + private installationSuccess: boolean = false + + constructor(associatedServiceName: string) { + this.associatedServiceName = associatedServiceName + this.isActive = false + } + + addData(data: SetupProgress) { + console.log( + `${data.serviceName} ${data.status} in stage ${data.step}. Debugmessage: ${data.debugMessage}`, + ) + + if (this.isActive && data.serviceName == this.associatedServiceName) { + this.collectedSetupProgress.push(data) + if (data.status === 'success' || data.status == 'failed') { + this.terminalUpdateReceived = true this.isActive = false + this.installationSuccess = data.status === 'success' + } } - - addData(data: SetupProgress) { - console.log(`${data.serviceName} ${data.status} in stage ${data.step}. Debugmessage: ${data.debugMessage}`) - - if (this.isActive && data.serviceName == this.associatedServiceName) { - this.collectedSetupProgress.push(data) - if (data.status === "success" || data.status == "failed") { - this.terminalUpdateReceived = true - this.isActive = false - this.installationSuccess = data.status === "success" - } - } - } - - private async awaitFinalization(): Promise { - if(this.terminalUpdateReceived) { - return this.collectedSetupProgress - } else { - return await new Promise(resolve => { - setTimeout(() => { - resolve(this.awaitFinalization()) - }, 1000) - }) - } + } + + private async awaitFinalization(): Promise { + if (this.terminalUpdateReceived) { + return this.collectedSetupProgress + } else { + return await new Promise((resolve) => { + setTimeout(() => { + resolve(this.awaitFinalization()) + }, 1000) + }) } - - async awaitFinalizationAndResetData(): Promise<{success: boolean, logs: SetupProgress[]}> { - return this.awaitFinalization().then( collectedSetupProgress => { - console.log(`server startup complete for ${this.associatedServiceName}`) - const clonedSetupProgress = collectedSetupProgress.slice() - this.collectedSetupProgress = [] - this.terminalUpdateReceived = false - return { success: this.installationSuccess, logs: clonedSetupProgress} - }) - } -} \ No newline at end of file + } + + async awaitFinalizationAndResetData(): Promise<{ success: boolean; logs: SetupProgress[] }> { + return this.awaitFinalization().then((collectedSetupProgress) => { + console.log(`server startup complete for ${this.associatedServiceName}`) + const clonedSetupProgress = collectedSetupProgress.slice() + this.collectedSetupProgress = [] + this.terminalUpdateReceived = false + return { success: this.installationSuccess, logs: clonedSetupProgress } + }) + } +} diff --git a/WebUI/src/assets/js/store/comfyUi.ts b/WebUI/src/assets/js/store/comfyUi.ts index 6ca2c7ec..dc496eca 100644 --- a/WebUI/src/assets/js/store/comfyUi.ts +++ b/WebUI/src/assets/js/store/comfyUi.ts @@ -1,384 +1,455 @@ -import { defineStore, acceptHMRUpdate } from "pinia"; -import { WebSocket } from "partysocket"; -import { ComfyUIApiWorkflow, Setting, useImageGeneration } from "./imageGeneration"; -import { useI18N } from "./i18n"; -import { toast } from "../toast"; -import {useGlobalSetup} from "@/assets/js/store/globalSetup.ts"; -import {useBackendServices} from "@/assets/js/store/backendServices.ts"; +import { defineStore, acceptHMRUpdate } from 'pinia' +import { WebSocket } from 'partysocket' +import { ComfyUIApiWorkflow, Setting, useImageGeneration } from './imageGeneration' +import { useI18N } from './i18n' +import * as toast from '../toast' +import { useGlobalSetup } from '@/assets/js/store/globalSetup.ts' +import { useBackendServices } from '@/assets/js/store/backendServices.ts' -const WEBSOCKET_OPEN = 1; +const WEBSOCKET_OPEN = 1 -export const useComfyUi = defineStore("comfyUi", () => { - - const imageGeneration = useImageGeneration(); - const globalSetup = useGlobalSetup(); - const i18nState = useI18N().state; +export const useComfyUi = defineStore( + 'comfyUi', + () => { + const imageGeneration = useImageGeneration() + const globalSetup = useGlobalSetup() + const i18nState = useI18N().state const comfyPort = computed(() => comfyUiState.value.port) const comfyBaseUrl = computed(() => comfyUiState.value.baseUrl) - const websocket = ref(null); - const clientId = '12345'; - const loaderNodes = ref([]); + const websocket = ref(null) + const clientId = '12345' + const loaderNodes = ref([]) - const backendServices = useBackendServices(); + const backendServices = useBackendServices() const comfyUiState = computed(() => { - const comfyUiState = backendServices.info.find(item => item.serviceName === "comfyui-backend")?? { serviceName: "comfyui-backend", status: "uninitializedStatus" , baseUrl: "???", port: -1, isSetUp: false, isRequired: false } - return comfyUiState - }); + const comfyUiState = backendServices.info.find( + (item) => item.serviceName === 'comfyui-backend', + ) ?? { + serviceName: 'comfyui-backend', + status: 'uninitializedStatus', + baseUrl: '???', + port: -1, + isSetUp: false, + isRequired: false, + } + return comfyUiState + }) async function installCustomNodesForActiveWorkflowFully() { - await triggerInstallPythonPackagesForActiveWorkflow() - const requiresServerReboot = await installCustomNodesForActiveWorkflow() - if (requiresServerReboot) { - console.info("restarting comfyUI to finalize installation of required custom nodes") - await backendServices.stopService('comfyui-backend') - const startingResult = await backendServices.startService('comfyui-backend') - if (startingResult !== "running") { - throw new Error("Failed to restart comfyUI. Required Nodes are not active.") - } - console.info("restart complete") + await triggerInstallPythonPackagesForActiveWorkflow() + const requiresServerReboot = await installCustomNodesForActiveWorkflow() + if (requiresServerReboot) { + console.info('restarting comfyUI to finalize installation of required custom nodes') + await backendServices.stopService('comfyui-backend') + const startingResult = await backendServices.startService('comfyui-backend') + if (startingResult !== 'running') { + throw new Error('Failed to restart comfyUI. Required Nodes are not active.') } + console.info('restart complete') + } } - function extractCustomNodeInfo(workflowNodeInfoString: string): ComfyUICustomNodesRequestParameters { - const repoInfoWithPotentialGitRefSplitted = workflowNodeInfoString.replace(" ", "").split("@") - if (repoInfoWithPotentialGitRefSplitted.length > 2 || repoInfoWithPotentialGitRefSplitted.length < 1) { - console.error(`Could not extract comfyUI node description from ${workflowNodeInfoString}`) - throw new Error("Could not extract comfyUI node description from ${workflowNodeInfoString}") - } - const [repoInfoString, gitRef] = repoInfoWithPotentialGitRefSplitted - if (!gitRef) { - console.warn(`No gitRef provided in ${workflowNodeInfoString}.`) - } - const repoInfoSplitted = repoInfoString.replace(" ", "").split("/") - if (repoInfoSplitted.length !== 2) { - console.error(`Could not extract comfyUI node description from ${workflowNodeInfoString}`) - throw new Error("Could not extract comfyUI node description from ${workflowNodeInfoString}") - } - const [username, repoName] = repoInfoSplitted - console.info(JSON.stringify({username: username, repoName: repoName, gitRef: gitRef})) - return {username: username, repoName: repoName, gitRef: gitRef} + function extractCustomNodeInfo( + workflowNodeInfoString: string, + ): ComfyUICustomNodesRequestParameters { + const repoInfoWithPotentialGitRefSplitted = workflowNodeInfoString.replace(' ', '').split('@') + if ( + repoInfoWithPotentialGitRefSplitted.length > 2 || + repoInfoWithPotentialGitRefSplitted.length < 1 + ) { + console.error(`Could not extract comfyUI node description from ${workflowNodeInfoString}`) + throw new Error('Could not extract comfyUI node description from ${workflowNodeInfoString}') + } + const [repoInfoString, gitRef] = repoInfoWithPotentialGitRefSplitted + if (!gitRef) { + console.warn(`No gitRef provided in ${workflowNodeInfoString}.`) + } + const repoInfoSplitted = repoInfoString.replace(' ', '').split('/') + if (repoInfoSplitted.length !== 2) { + console.error(`Could not extract comfyUI node description from ${workflowNodeInfoString}`) + throw new Error('Could not extract comfyUI node description from ${workflowNodeInfoString}') + } + const [username, repoName] = repoInfoSplitted + console.info(JSON.stringify({ username: username, repoName: repoName, gitRef: gitRef })) + return { username: username, repoName: repoName, gitRef: gitRef } } async function installCustomNodesForActiveWorkflow(): Promise { - const uniqueCustomNodes = new Set(imageGeneration.workflows.filter(w => w.name === imageGeneration.activeWorkflowName).filter(w => w.backend === 'comfyui').flatMap((item) => item.comfyUIRequirements.customNodes)) - const requiredCustomNodes: ComfyUICustomNodesRequestParameters[] = - [...uniqueCustomNodes].map((nodeName) => extractCustomNodeInfo(nodeName)) - const response = await fetch(`${globalSetup.apiHost}/api/comfyUi/loadCustomNodes`, { - method: 'POST', - body: JSON.stringify({data: requiredCustomNodes}), - headers: { - "Content-Type": "application/json" - } - }) - if (response.status !== 200) { - throw new Error("Request Failure to install required comfyUINode"); - } - const data = await response.json() as { node: string, success: boolean }[]; - const notInstalledNodes = data.filter(item => !item.success) - if (notInstalledNodes.length > 0) { - throw new Error(`Failed to install required comfyUI custom nodes: ${notInstalledNodes}`) - } - const areNewNodesInstalled = data.length > 0 - return areNewNodesInstalled; + const uniqueCustomNodes = new Set( + imageGeneration.workflows + .filter((w) => w.name === imageGeneration.activeWorkflowName) + .filter((w) => w.backend === 'comfyui') + .flatMap((item) => item.comfyUIRequirements.customNodes), + ) + const requiredCustomNodes: ComfyUICustomNodesRequestParameters[] = [...uniqueCustomNodes].map( + (nodeName) => extractCustomNodeInfo(nodeName), + ) + const response = await fetch(`${globalSetup.apiHost}/api/comfyUi/loadCustomNodes`, { + method: 'POST', + body: JSON.stringify({ data: requiredCustomNodes }), + headers: { + 'Content-Type': 'application/json', + }, + }) + if (response.status !== 200) { + throw new Error('Request Failure to install required comfyUINode') + } + const data = (await response.json()) as { node: string; success: boolean }[] + const notInstalledNodes = data.filter((item) => !item.success) + if (notInstalledNodes.length > 0) { + throw new Error(`Failed to install required comfyUI custom nodes: ${notInstalledNodes}`) + } + const areNewNodesInstalled = data.length > 0 + return areNewNodesInstalled } - async function triggerInstallPythonPackagesForActiveWorkflow() { - const uniquePackages = new Set(imageGeneration.workflows.filter(w => w.name === imageGeneration.activeWorkflowName).filter(w => w.backend === 'comfyui').flatMap((item) => item.comfyUIRequirements.pythonPackages?? [])) - const toBeInstalledPackages = [...uniquePackages] - console.info("Installing python packages", { toBeInstalledPackages }) - const response = await fetch(`${globalSetup.apiHost}/api/comfyUi/installPythonPackage`, { - method: 'POST', - body: JSON.stringify({data: toBeInstalledPackages}), - headers: { - "Content-Type": "application/json" - } - }) - if (response.status === 200) { - console.info("python package installation completed") - return; - } - const data = await response.json(); - throw new Error(data.error_message); + const uniquePackages = new Set( + imageGeneration.workflows + .filter((w) => w.name === imageGeneration.activeWorkflowName) + .filter((w) => w.backend === 'comfyui') + .flatMap((item) => item.comfyUIRequirements.pythonPackages ?? []), + ) + const toBeInstalledPackages = [...uniquePackages] + console.info('Installing python packages', { toBeInstalledPackages }) + const response = await fetch(`${globalSetup.apiHost}/api/comfyUi/installPythonPackage`, { + method: 'POST', + body: JSON.stringify({ data: toBeInstalledPackages }), + headers: { + 'Content-Type': 'application/json', + }, + }) + if (response.status === 200) { + console.info('python package installation completed') + return + } + const data = await response.json() + throw new Error(data.error_message) } function connectToComfyUi() { - if (comfyUiState.value.status !== "running") { - console.warn('ComfyUI backend not running, cannot start websocket'); - return; - } - const comfyWsUrl = `ws://localhost:${comfyPort.value}/ws?clientId=${clientId}` - console.info('Connecting to ComfyUI', { comfyWsUrl }); - websocket.value = new WebSocket(comfyWsUrl); - websocket.value.binaryType = 'arraybuffer' - websocket.value.addEventListener('message', (event) => { - try { - if (event.data instanceof ArrayBuffer) { - const view = new DataView(event.data) - const eventType = view.getUint32(0) - const buffer = event.data.slice(4) - switch (eventType) { - case 1: - const view2 = new DataView(event.data) - const imageType = view2.getUint32(0) - let imageMime - switch (imageType) { - case 1: - default: - imageMime = 'image/jpeg' - break - case 2: - imageMime = 'image/png' - } - const imageBlob = new Blob([buffer.slice(4)], { - type: imageMime - }) - console.log('got image blob') - const imageUrl = URL.createObjectURL(imageBlob) - console.log('image url', imageUrl) - if (imageBlob) { - imageGeneration.previewIdx = imageGeneration.generateIdx; - imageGeneration.updateDestImage(imageGeneration.generateIdx, imageUrl); - } - break - default: - throw new Error( - `Unknown binary websocket message of type ${eventType}` - ) - } + if (comfyUiState.value.status !== 'running') { + console.warn('ComfyUI backend not running, cannot start websocket') + return + } + const comfyWsUrl = `ws://localhost:${comfyPort.value}/ws?clientId=${clientId}` + console.info('Connecting to ComfyUI', { comfyWsUrl }) + websocket.value = new WebSocket(comfyWsUrl) + websocket.value.binaryType = 'arraybuffer' + websocket.value.addEventListener('message', (event) => { + try { + if (event.data instanceof ArrayBuffer) { + const view = new DataView(event.data) + const eventType = view.getUint32(0) + const buffer = event.data.slice(4) + switch (eventType) { + case 1: + const view2 = new DataView(event.data) + const imageType = view2.getUint32(0) + let imageMime + switch (imageType) { + case 1: + default: + imageMime = 'image/jpeg' + break + case 2: + imageMime = 'image/png' + } + const imageBlob = new Blob([buffer.slice(4)], { + type: imageMime, + }) + console.log('got image blob') + const imageUrl = URL.createObjectURL(imageBlob) + console.log('image url', imageUrl) + if (imageBlob) { + imageGeneration.previewIdx = imageGeneration.generateIdx + imageGeneration.updateDestImage(imageGeneration.generateIdx, imageUrl) + } + break + default: + throw new Error(`Unknown binary websocket message of type ${eventType}`) + } + } else { + const msg = JSON.parse(event.data) + switch (msg.type) { + case 'status': + break + case 'progress': + imageGeneration.currentState = 'generating' + imageGeneration.stepText = `${i18nState.COM_GENERATING} ${msg.data.value}/${msg.data.max}` + console.log('progress', { data: msg.data }) + break + case 'executing': + console.log('executing', { + detail: msg.data.display_node || msg.data.node, + }) + if (loaderNodes.value.includes(msg?.data?.node)) { + imageGeneration.currentState = 'load_model' } else { - const msg = JSON.parse(event.data) - switch (msg.type) { - case 'status': - break - case 'progress': - imageGeneration.currentState = "generating"; - imageGeneration.stepText = `${i18nState.COM_GENERATING} ${msg.data.value}/${msg.data.max}`; - console.log('progress', { data: msg.data }) - break - case 'executing': - console.log('executing', { - detail: msg.data.display_node || msg.data.node - }) - if (loaderNodes.value.includes(msg?.data?.node)) { - imageGeneration.currentState = 'load_model' - } else { - imageGeneration.currentState = 'generating' - } - break - case 'executed': - const images: { filename: string, type: string, subfolder: string }[] = msg.data?.output?.images?.filter((i: { type: string }) => i.type === 'output'); - images.forEach((image) => { - imageGeneration.updateDestImage(imageGeneration.generateIdx, `${comfyBaseUrl.value}/view?filename=${image.filename}&type=${image.type}&subfolder=${image.subfolder ?? ''}`); - imageGeneration.generateIdx++; - }); - console.log('executed', { detail: msg.data }) - break - case 'execution_start': - imageGeneration.processing = true; - console.log('execution_start', { detail: msg.data }) - break - case 'execution_success': - imageGeneration.processing = false; - console.log('execution_success', { detail: msg.data }) - break - case 'execution_error': - imageGeneration.processing = false; - break - case 'execution_interrupted': - imageGeneration.processing = false; - break - case 'execution_cached': - break - } + imageGeneration.currentState = 'generating' } - } catch (error) { - console.warn('Unhandled message:', event.data, error) + break + case 'executed': + const images: { filename: string; type: string; subfolder: string }[] = + msg.data?.output?.images?.filter((i: { type: string }) => i.type === 'output') + images.forEach((image) => { + imageGeneration.updateDestImage( + imageGeneration.generateIdx, + `${comfyBaseUrl.value}/view?filename=${image.filename}&type=${image.type}&subfolder=${image.subfolder ?? ''}`, + ) + imageGeneration.generateIdx++ + }) + console.log('executed', { detail: msg.data }) + break + case 'execution_start': + imageGeneration.processing = true + console.log('execution_start', { detail: msg.data }) + break + case 'execution_success': + imageGeneration.processing = false + console.log('execution_success', { detail: msg.data }) + break + case 'execution_error': + imageGeneration.processing = false + break + case 'execution_interrupted': + imageGeneration.processing = false + break + case 'execution_cached': + break } - }) + } + } catch (error) { + console.warn('Unhandled message:', event.data, error) + } + }) } watchEffect(() => { - if (comfyPort && comfyUiState.value.status === "running") { - connectToComfyUi(); - } - }); + if (comfyPort && comfyUiState.value.status === 'running') { + connectToComfyUi() + } + }) function dataURItoBlob(dataURI: string) { - const bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ? atob(dataURI.split(',')[1]) : unescape(dataURI.split(',')[1]); - const mimeType = dataURI.split(',')[0].split(':')[1].split(';')[0]; + const bytes = + dataURI.split(',')[0].indexOf('base64') >= 0 + ? atob(dataURI.split(',')[1]) + : unescape(dataURI.split(',')[1]) + const mimeType = dataURI.split(',')[0].split(':')[1].split(';')[0] - const intArray = new Uint8Array(bytes.length); - for (let i = 0; i < bytes.length; i++) { - intArray[i] = bytes.charCodeAt(i); - } + const intArray = new Uint8Array(bytes.length) + for (let i = 0; i < bytes.length; i++) { + intArray[i] = bytes.charCodeAt(i) + } - return new Blob([intArray], {type:mimeType}); + return new Blob([intArray], { type: mimeType }) } - async function modifyDynamicSettingsInWorkflow(mutableWorkflow: ComfyUIApiWorkflow) { - for (const input of imageGeneration.comfyInputs) { - const keys = findKeysByTitle(mutableWorkflow, input.nodeTitle); - if (input.type === 'number' || input.type === 'string' || input.type === 'boolean') { - 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); - (mutableWorkflow[keys[0]].inputs as any)[input.nodeInput] = input.current.value; - } - } - if (input.type === 'image') { - if (typeof input.current.value !== 'string') continue; - const uploadImageHash = Array.from(new Uint8Array(await window.crypto.subtle.digest("SHA-256", new TextEncoder().encode(input.current.value)))).map((b) => b.toString(16).padStart(2, "0")).join(""); - const uploadImageExtension = input.current.value.match(/data:image\/(png|jpeg|webp);base64,/)?.[1]; - const uploadImageName = `${uploadImageHash}.${uploadImageExtension}`; - console.log('uploadImageName', uploadImageName); - if (mutableWorkflow[keys[0]].inputs !== undefined) { - (mutableWorkflow[keys[0]].inputs as any)[input.nodeInput] = uploadImageName; - } - const data = new FormData(); - data.append('image', dataURItoBlob(input.current.value), uploadImageName); - await fetch(`${comfyBaseUrl.value}/upload/image`, { - method: 'POST', - body: data - }); - } + for (const input of imageGeneration.comfyInputs) { + const keys = findKeysByTitle(mutableWorkflow, input.nodeTitle) + if (input.type === 'number' || input.type === 'string' || input.type === 'boolean') { + 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 + } + } + if (input.type === 'image') { + if (typeof input.current.value !== 'string') continue + const uploadImageHash = Array.from( + new Uint8Array( + await window.crypto.subtle.digest( + 'SHA-256', + new TextEncoder().encode(input.current.value), + ), + ), + ) + .map((b) => b.toString(16).padStart(2, '0')) + .join('') + const uploadImageExtension = input.current.value.match( + /data:image\/(png|jpeg|webp);base64,/, + )?.[1] + 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() + data.append('image', dataURItoBlob(input.current.value), uploadImageName) + await fetch(`${comfyBaseUrl.value}/upload/image`, { + method: 'POST', + body: data, + }) } + } } async function generate() { - console.log('generateWithComfy') - if (imageGeneration.activeWorkflow.backend !== 'comfyui') { - console.warn('The selected workflow is not a comfyui workflow'); - return; - } - if (imageGeneration.processing) { - console.warn('Already processing'); - return; - } - if (websocket.value?.readyState !== WEBSOCKET_OPEN) { - console.warn('Websocket not open'); - return; - } + console.log('generateWithComfy') + if (imageGeneration.activeWorkflow.backend !== 'comfyui') { + console.warn('The selected workflow is not a comfyui workflow') + return + } + if (imageGeneration.processing) { + console.warn('Already processing') + return + } + if (websocket.value?.readyState !== WEBSOCKET_OPEN) { + console.warn('Websocket not open') + return + } - try { - await installCustomNodesForActiveWorkflowFully() + try { + await installCustomNodesForActiveWorkflowFully() - const mutableWorkflow: ComfyUIApiWorkflow = JSON.parse(JSON.stringify(imageGeneration.activeWorkflow.comfyUiApiWorkflow)) - const seed = imageGeneration.seed === -1 ? (Math.random() * 1000000) : imageGeneration.seed; + const mutableWorkflow: ComfyUIApiWorkflow = JSON.parse( + JSON.stringify(imageGeneration.activeWorkflow.comfyUiApiWorkflow), + ) + const seed = imageGeneration.seed === -1 ? Math.random() * 1000000 : imageGeneration.seed - modifySettingInWorkflow(mutableWorkflow, 'inferenceSteps', imageGeneration.inferenceSteps); - modifySettingInWorkflow(mutableWorkflow, 'height', imageGeneration.height); - modifySettingInWorkflow(mutableWorkflow, 'width', imageGeneration.width); - modifySettingInWorkflow(mutableWorkflow, 'prompt', imageGeneration.prompt); - modifySettingInWorkflow(mutableWorkflow, 'negativePrompt', imageGeneration.negativePrompt); + modifySettingInWorkflow(mutableWorkflow, 'inferenceSteps', imageGeneration.inferenceSteps) + modifySettingInWorkflow(mutableWorkflow, 'height', imageGeneration.height) + modifySettingInWorkflow(mutableWorkflow, 'width', imageGeneration.width) + modifySettingInWorkflow(mutableWorkflow, 'prompt', imageGeneration.prompt) + modifySettingInWorkflow(mutableWorkflow, 'negativePrompt', imageGeneration.negativePrompt) - await modifyDynamicSettingsInWorkflow(mutableWorkflow); + await modifyDynamicSettingsInWorkflow(mutableWorkflow) - loaderNodes.value = [ - ...findKeysByClassType(mutableWorkflow, 'CheckpointLoaderSimple'), - ...findKeysByClassType(mutableWorkflow, 'Unet Loader (GGUF)'), - ...findKeysByClassType(mutableWorkflow, 'DualCLIPLoader (GGUF)'), - ]; + loaderNodes.value = [ + ...findKeysByClassType(mutableWorkflow, 'CheckpointLoaderSimple'), + ...findKeysByClassType(mutableWorkflow, 'Unet Loader (GGUF)'), + ...findKeysByClassType(mutableWorkflow, 'DualCLIPLoader (GGUF)'), + ] - for (let i = 0; i < imageGeneration.batchSize; i++) { - modifySettingInWorkflow(mutableWorkflow, 'seed', `${(seed + i).toFixed(0)}`); + for (let i = 0; i < imageGeneration.batchSize; i++) { + modifySettingInWorkflow(mutableWorkflow, 'seed', `${(seed + i).toFixed(0)}`) - const result = await fetch(`${comfyBaseUrl.value}/prompt`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - prompt: mutableWorkflow, - client_id: clientId - }) - }) - if (result.status > 299) { - throw new Error(`ComfyUI Backend responded with ${result.status}: ${await result.text()}`) - } - } - } catch (ex) { - console.error('Error generating image', ex); - toast.error('Backend could not generate image.'); - imageGeneration.processing = false; - imageGeneration.currentState = "no_start" - } finally { + const result = await fetch(`${comfyBaseUrl.value}/prompt`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + prompt: mutableWorkflow, + client_id: clientId, + }), + }) + if (result.status > 299) { + throw new Error( + `ComfyUI Backend responded with ${result.status}: ${await result.text()}`, + ) + } } + } catch (ex) { + console.error('Error generating image', ex) + toast.error('Backend could not generate image.') + imageGeneration.processing = false + imageGeneration.currentState = 'no_start' + } finally { + } } async function stop() { - await fetch(`${comfyBaseUrl.value}/queue`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ clear: true }) - }) - await fetch(`${comfyBaseUrl.value}/interrupt`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } - }) + await fetch(`${comfyBaseUrl.value}/queue`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ clear: true }), + }) + await fetch(`${comfyBaseUrl.value}/interrupt`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }) } return { - generate, - stop, + generate, + stop, } -}, { + }, + { persist: { - pick: ['backend'] - } -}); + pick: ['backend'], + }, + }, +) const settingToComfyInputsName = { - 'seed': ['seed', 'noise_seed'], - 'inferenceSteps': ['steps'], - 'height': ['height'], - 'width': ['width'], - 'prompt': ['text'], - 'negativePrompt': ['text'], - 'guidanceScale': ['cfg'], - 'scheduler': ['scheduler'], - 'batchSize': ['batch_size'], -} satisfies Partial>; -type ComfySetting = keyof typeof settingToComfyInputsName; + seed: ['seed', 'noise_seed'], + inferenceSteps: ['steps'], + height: ['height'], + width: ['width'], + prompt: ['text'], + negativePrompt: ['text'], + guidanceScale: ['cfg'], + scheduler: ['scheduler'], + batchSize: ['batch_size'], +} satisfies Partial> +type ComfySetting = keyof typeof settingToComfyInputsName const findKeysByTitle = (workflow: ComfyUIApiWorkflow, title: ComfySetting | 'loader' | string) => - Object.entries(workflow).filter(([_key, value]) => (value as any)?.['_meta']?.title === title).map(([key, _value]) => key); + Object.entries(workflow) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .filter(([_key, value]) => (value as any)?.['_meta']?.title === title) + .map(([key, _value]) => key) const findKeysByClassType = (workflow: ComfyUIApiWorkflow, classType: string) => - Object.entries(workflow).filter(([_key, value]) => (value as any)?.['class_type'] === classType).map(([key, _value]) => key); + Object.entries(workflow) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .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; - const keys = Object.entries(workflow).filter(([_key, value]) => (value as any)?.['inputs']?.[inputName ?? ''] !== undefined).map(([key, _value]) => key) - if (keys.length > 0) return keys; - } - return []; -}; -const getInputNameBySettingAndKey = (workflow: ComfyUIApiWorkflow, key: string, setting: ComfySetting) => { - for (const inputName of settingToComfyInputsName[setting]) { - if (workflow[key]?.inputs?.[inputName ?? '']) return inputName; - } - return ''; + for (const inputName of settingToComfyInputsName[setting]) { + if (inputName === 'text') continue + const keys = Object.entries(workflow) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .filter(([_key, value]) => (value as any)?.['inputs']?.[inputName ?? ''] !== undefined) + .map(([key, _value]) => key) + if (keys.length > 0) return keys + } + return [] } -function modifySettingInWorkflow(workflow: ComfyUIApiWorkflow, setting: ComfySetting, value: any) { - 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`); - return; - } - if (keys.length > 1) { - console.warn(`Multiple keys found for setting ${setting}. Using first one`); - } - const key = keys[0]; - if (workflow[key]?.inputs?.[getInputNameBySettingAndKey(workflow, key, setting)] !== undefined) { - workflow[key].inputs[getInputNameBySettingAndKey(workflow, key, setting)] = value; - } +const getInputNameBySettingAndKey = ( + workflow: ComfyUIApiWorkflow, + key: string, + setting: ComfySetting, +) => { + for (const inputName of settingToComfyInputsName[setting]) { + if (workflow[key]?.inputs?.[inputName ?? '']) return inputName + } + return '' +} + +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`) + return + } + if (keys.length > 1) { + console.warn(`Multiple keys found for setting ${setting}. Using first one`) + } + const key = keys[0] + if (workflow[key]?.inputs?.[getInputNameBySettingAndKey(workflow, key, setting)] !== undefined) { + workflow[key].inputs[getInputNameBySettingAndKey(workflow, key, setting)] = value + } } if (import.meta.hot) { - import.meta.hot.accept(acceptHMRUpdate(useComfyUi, import.meta.hot)) -} \ No newline at end of file + import.meta.hot.accept(acceptHMRUpdate(useComfyUi, import.meta.hot)) +} diff --git a/WebUI/src/assets/js/store/conversations.ts b/WebUI/src/assets/js/store/conversations.ts index 2cd9973a..2663c0ca 100644 --- a/WebUI/src/assets/js/store/conversations.ts +++ b/WebUI/src/assets/js/store/conversations.ts @@ -1,70 +1,75 @@ -import { defineStore } from "pinia"; +import { defineStore } from 'pinia' -export const useConversations = defineStore("conversations", () => { - const conversationList = ref>({}); - const activeKey = ref(''); - const activeConversation = computed(() => conversationList.value[activeKey.value]); +export const useConversations = defineStore( + 'conversations', + () => { + const conversationList = ref>({}) + const activeKey = ref('') + const activeConversation = computed(() => conversationList.value[activeKey.value]) const addToActiveConversation = (key: string, item: ChatItem) => { - const list = conversationList.value[key]; - list.push(item); - addNewConversationIfLatestIsNotEmpty(conversationList.value, activeKey.value); + const list = conversationList.value[key] + list.push(item) + addNewConversationIfLatestIsNotEmpty(conversationList.value, activeKey.value) } function deleteConversation(conversationKey: string) { - delete conversationList.value[conversationKey]; + delete conversationList.value[conversationKey] } function clearConversation(conversationKey: string) { - conversationList.value[conversationKey] = []; + conversationList.value[conversationKey] = [] } function deleteItemFromConversation(conversationKey: string, index: number) { - conversationList.value[conversationKey].splice(index, 1); + conversationList.value[conversationKey].splice(index, 1) } - const isNewConversation = (key: string) => conversationList.value[key].length === 0; + const isNewConversation = (key: string) => conversationList.value[key].length === 0 watchEffect(() => { - if (Object.keys(conversationList.value).includes(activeKey.value)) return; - const latestConversationKey = Object.keys(conversationList.value).at(-1); - if (!latestConversationKey) return; - activeKey.value = latestConversationKey; + if (Object.keys(conversationList.value).includes(activeKey.value)) return + const latestConversationKey = Object.keys(conversationList.value).at(-1) + if (!latestConversationKey) return + activeKey.value = latestConversationKey }) return { - conversationList, - activeKey, - activeConversation, - addToActiveConversation, - deleteConversation, - clearConversation, - deleteItemFromConversation, - isNewConversation - }; -}, { - persist: { - pick: ['conversationList'], - afterHydrate: (ctx) => addNewConversationIfLatestIsNotEmpty(ctx.store.$state.conversationList) + conversationList, + activeKey, + activeConversation, + addToActiveConversation, + deleteConversation, + clearConversation, + deleteItemFromConversation, + isNewConversation, } -}); + }, + { + persist: { + pick: ['conversationList'], + afterHydrate: (ctx) => + addNewConversationIfLatestIsNotEmpty(ctx.store.$state.conversationList), + }, + }, +) function addNewConversationIfLatestIsNotEmpty( - list: Record, - conversationKey?: string - ) { - if (conversationKey && list[conversationKey].length !== 0) { - // If the last conversation is already empty, do nothing - const lastKey = Object.keys(list).at(-1); - if (lastKey && list[lastKey].length === 0) return; - - // Otherwise, create a fresh conversation - list[new Date().getTime().toString()] = []; - return; - } - - // Fallback old logic - if (Object.values(list).at(-1)?.length !== 0) { - list[new Date().getTime().toString()] = []; - } - } \ No newline at end of file + list: Record, + conversationKey?: string, +) { + if (conversationKey && list[conversationKey].length !== 0) { + // If the last conversation is already empty, do nothing + const lastKey = Object.keys(list).at(-1) + if (lastKey && list[lastKey].length === 0) return + + // Otherwise, create a fresh conversation + list[new Date().getTime().toString()] = [] + return + } + + // Fallback old logic + if (Object.values(list).at(-1)?.length !== 0) { + list[new Date().getTime().toString()] = [] + } +} diff --git a/WebUI/src/assets/js/store/globalSetup.ts b/WebUI/src/assets/js/store/globalSetup.ts index 33946931..8780d2c4 100644 --- a/WebUI/src/assets/js/store/globalSetup.ts +++ b/WebUI/src/assets/js/store/globalSetup.ts @@ -1,348 +1,338 @@ -import {defineStore} from "pinia"; -import {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" - -export const useGlobalSetup = defineStore("globalSetup", () => { - const state = reactive({ - isAdminExec: false, - device: "", - version: "0.0.0.1" - }); - - const defaultBackendBaseUrl = ref("http://127.0.0.1:9999"); - const lastUsedBackend = ref("None") - - const models = ref({ - llm: new Array(), - stableDiffusion: new Array(), - inpaint: new Array(), - lora: new Array(), - vae: new Array(), - scheduler: new Array(), - embedding: new Array() - }); - - const modelSettings = reactive({ - graphics: 0, - resolution: 0, - quality: 0, - enableRag: false, - llm_model: "microsoft/Phi-3-mini-4k-instruct", - ggufLLM_model: "meta-llama-3.1-8b-instruct.Q5_K_M.gguf", - sd_model: "Lykon/dreamshaper-8", - inpaint_model: "Lykon/dreamshaper-8-inpainting", - negativePrompt: "bad hands, nsfw", - generateNumber: 1, - width: 512, - height: 512, - guidanceScale: 7.5, - inferenceSteps: 20, - seed: -1, - lora: "None", - scheduler: "None", - embedding: "BAAI/bge-large-en-v1.5", - imagePreview: 1, - safeCheck: 1 - }); - - const paths = ref({ - llm: "", - ggufLLM: "", - embedding: "", - stableDiffusion: "", - inpaint: "", - lora: "", - vae: "", - ESRGAN: "", - }); - - const presetModel = reactive({ - SDStandard: "Lykon/dreamshaper-8", - SDStandardInpaint: "Lykon/dreamshaper-8-inpainting", - SDHD: "RunDiffusion/Juggernaut-XL-v9", - SDHDInpaint: useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL, - }); - - const graphicsList = ref(new Array()); - - const loadingState = ref("verifyBackend"); - const errorMessage = ref(""); - const hdPersistentConfirmation = ref(localStorage.getItem("HdPersistentConfirmation") === "true"); - - const backendServices = useBackendServices() - - watchEffect(() => { - localStorage.setItem("HdPersistentConfirmation", hdPersistentConfirmation.value.toString()); - }); - - async function initSetup() { - const setupData = await window.electronAPI.getInitSetting(); - const apiServiceInformation = await window.electronAPI.getServices() - paths.value = setupData.modelPaths; - models.value = setupData.modelLists; - models.value.inpaint.push(useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL); - state.isAdminExec = setupData.isAdminExec; - state.version = setupData.version; - const aiBackendInfo = apiServiceInformation.find(item => item.serviceName === "ai-backend") - if (!aiBackendInfo) { - throw new Error("ai-backend service not found") - } - defaultBackendBaseUrl.value = aiBackendInfo.baseUrl; - loadPresetModelSettings(); - const postJson = JSON.stringify(toRaw(paths.value)); - const delay = 2000; - - while (true) { - try { - models.value.scheduler.push(...await initWebSettings(postJson)); - models.value.scheduler.unshift("None"); - break; - } catch (error) { - await util.delay(delay); - } - } - await reloadGraphics(); - if (graphicsList.value.length == 0) { - await window.electronAPI.showMessageBoxSync({ message: useI18N().state.ERROR_UNFOUND_GRAPHICS, title: "error", icon: "error" }); - window.electronAPI.exitApp(); - } - loadUserSettings(); - } - - async function initWebSettings(postJson: string) { - const response = await fetch(`${defaultBackendBaseUrl.value}/api/init`, { - headers: { - "Content-Type": "application/json", - }, - method: "post", - body: postJson, - }); - if (response.status !== 200) { - throw new Error(`Received error response from AI inference backend:\n\n ${await response.status}:${await response.text()}`) - } - return await response.json() as string[]; +import { defineStore } from 'pinia' +import * as util from '../util' +import { useI18N } from './i18n' +import { useBackendServices } from './backendServices' + +type GlobalSetupState = 'running' | 'verifyBackend' | 'manageInstallations' | 'loading' | 'failed' +type LastUsedBackend = BackendServiceName | 'None' + +export const useGlobalSetup = defineStore('globalSetup', () => { + const state = reactive({ + isAdminExec: false, + device: '', + version: '0.0.0.1', + }) + + const defaultBackendBaseUrl = ref('http://127.0.0.1:9999') + const lastUsedBackend = ref('None') + + const models = ref({ + llm: new Array(), + stableDiffusion: new Array(), + inpaint: new Array(), + lora: new Array(), + vae: new Array(), + scheduler: new Array(), + embedding: new Array(), + }) + + const modelSettings = reactive({ + graphics: 0, + resolution: 0, + quality: 0, + enableRag: false, + llm_model: 'microsoft/Phi-3-mini-4k-instruct', + ggufLLM_model: 'meta-llama-3.1-8b-instruct.Q5_K_M.gguf', + sd_model: 'Lykon/dreamshaper-8', + inpaint_model: 'Lykon/dreamshaper-8-inpainting', + negativePrompt: 'bad hands, nsfw', + generateNumber: 1, + width: 512, + height: 512, + guidanceScale: 7.5, + inferenceSteps: 20, + seed: -1, + lora: 'None', + scheduler: 'None', + embedding: 'BAAI/bge-large-en-v1.5', + imagePreview: 1, + safeCheck: 1, + }) + + const paths = ref({ + llm: '', + ggufLLM: '', + embedding: '', + stableDiffusion: '', + inpaint: '', + lora: '', + vae: '', + ESRGAN: '', + }) + + const presetModel = reactive({ + SDStandard: 'Lykon/dreamshaper-8', + SDStandardInpaint: 'Lykon/dreamshaper-8-inpainting', + SDHD: 'RunDiffusion/Juggernaut-XL-v9', + SDHDInpaint: useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL, + }) + + const graphicsList = ref(new Array()) + + const loadingState = ref('verifyBackend') + const errorMessage = ref('') + const hdPersistentConfirmation = ref(localStorage.getItem('HdPersistentConfirmation') === 'true') + + const backendServices = useBackendServices() + + watchEffect(() => { + localStorage.setItem('HdPersistentConfirmation', hdPersistentConfirmation.value.toString()) + }) + + async function initSetup() { + const setupData = await window.electronAPI.getInitSetting() + const apiServiceInformation = await window.electronAPI.getServices() + paths.value = setupData.modelPaths + models.value = setupData.modelLists + models.value.inpaint.push(useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL) + state.isAdminExec = setupData.isAdminExec + state.version = setupData.version + const aiBackendInfo = apiServiceInformation.find((item) => item.serviceName === 'ai-backend') + if (!aiBackendInfo) { + throw new Error('ai-backend service not found') } - - async function reloadGraphics() { - const response = await fetch(`${defaultBackendBaseUrl.value}/api/getGraphics`, { - method: "POST" - }); - const graphics = (await response.json()) as GraphicsItem[]; - graphicsList.value.splice(0, graphicsList.value.length, ...graphics); + defaultBackendBaseUrl.value = aiBackendInfo.baseUrl + loadPresetModelSettings() + const postJson = JSON.stringify(toRaw(paths.value)) + const delay = 2000 + + while (true) { + try { + models.value.scheduler.push(...(await initWebSettings(postJson))) + models.value.scheduler.unshift('None') + break + } catch (_error: unknown) { + await util.delay(delay) + } } - - async function refreshLLMModles() { - models.value.stableDiffusion = await window.electronAPI.refreshLLMModles(); + await reloadGraphics() + if (graphicsList.value.length == 0) { + await window.electronAPI.showMessageBoxSync({ + message: useI18N().state.ERROR_UNFOUND_GRAPHICS, + title: 'error', + icon: 'error', + }) + window.electronAPI.exitApp() } - - async function refreshSDModles() { - models.value.stableDiffusion = await window.electronAPI.refreshSDModles(); + loadUserSettings() + } + + async function initWebSettings(postJson: string) { + const response = await fetch(`${defaultBackendBaseUrl.value}/api/init`, { + headers: { + 'Content-Type': 'application/json', + }, + method: 'post', + body: postJson, + }) + if (response.status !== 200) { + throw new Error( + `Received error response from AI inference backend:\n\n ${await response.status}:${await response.text()}`, + ) } - - async function refreshInpaintModles() { - models.value.inpaint = await window.electronAPI.refreshInpaintModles(); + return (await response.json()) as string[] + } + + async function reloadGraphics() { + const response = await fetch(`${defaultBackendBaseUrl.value}/api/getGraphics`, { + method: 'POST', + }) + const graphics = (await response.json()) as GraphicsItem[] + graphicsList.value.splice(0, graphicsList.value.length, ...graphics) + } + + async function refreshLLMModles() { + models.value.stableDiffusion = await window.electronAPI.refreshLLMModles() + } + + async function refreshSDModles() { + models.value.stableDiffusion = await window.electronAPI.refreshSDModles() + } + + async function refreshInpaintModles() { + models.value.inpaint = await window.electronAPI.refreshInpaintModles() + } + + async function refreshLora() { + models.value.lora = await window.electronAPI.refreshLora() + } + + async function applyPathsSettings(newPaths: ModelPaths) { + models.value = await window.electronAPI.updateModelPaths(newPaths) + const postJson = JSON.stringify(newPaths) + await initWebSettings(postJson) + paths.value = newPaths + if (models.value.inpaint) { + models.value.inpaint = [] } - - async function refreshLora() { - models.value.lora = await window.electronAPI.refreshLora(); + models.value.inpaint.push(useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL) + return assertSelectExist() + } + + async function restorePathsSettings() { + await window.electronAPI.restorePathsSettings() + const setupData = await window.electronAPI.getInitSetting() + paths.value = setupData.modelPaths + models.value = setupData.modelLists + models.value.inpaint.push(useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL) + loadPresetModelSettings() + const postJson = JSON.stringify(toRaw(paths.value)) + while (true) { + try { + models.value.scheduler.push(...(await initWebSettings(postJson))) + models.value.scheduler.unshift('None') + break + } catch { + await util.delay(500) + } } - - async function applyPathsSettings(newPaths: ModelPaths) { - models.value = await window.electronAPI.updateModelPaths(newPaths); - const postJson = JSON.stringify(newPaths); - await initWebSettings(postJson); - paths.value = newPaths; - if (models.value.inpaint) { - models.value.inpaint = []; + return assertSelectExist() + } + + function loadPresetModelSettings() { + const dataStr = localStorage.getItem('PresetModelSettings') + if (dataStr) { + const data = JSON.parse(dataStr) as StringKV + Object.keys(presetModel).forEach((key) => { + if (key in data) { + presetModel[key] = data[key] } - models.value.inpaint.push(useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL); - return assertSelectExist(); + }) } - - async function restorePathsSettings() { - await window.electronAPI.restorePathsSettings(); - const setupData = await window.electronAPI.getInitSetting(); - paths.value = setupData.modelPaths; - models.value = setupData.modelLists; - models.value.inpaint.push(useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL); - loadPresetModelSettings(); - const postJson = JSON.stringify(toRaw(paths.value)); - while (true) { - try { - models.value.scheduler.push(...await initWebSettings(postJson)); - models.value.scheduler.unshift("None"); - break; - } catch { - await util.delay(500); - } - } - return assertSelectExist(); + } + + function applyPresetModelSettings(presetModelSettings: StringKV) { + Object.keys(presetModel).forEach((key) => { + if (key in presetModelSettings) { + presetModel[key] = presetModelSettings[key] + } + }) + localStorage.setItem('PresetModelSettings', JSON.stringify(toRaw(presetModel))) + } + + function loadUserSettings() { + const dataStr = localStorage.getItem('ModelSettings') + if (dataStr) { + const data = JSON.parse(dataStr) as KVObject + Object.keys(data).forEach((key) => { + modelSettings[key] = data[key] + }) } + assertSelectExist() + } - function loadPresetModelSettings() { - const dataStr = localStorage.getItem("PresetModelSettings"); - if (dataStr) { - const data = JSON.parse(dataStr) as StringKV; - Object.keys(presetModel).forEach((key) => { - if (key in data) { - presetModel[key] = data[key]; - } - }); - } - } + function updateLastUsedBackend(currentInferenceBackend: BackendServiceName) { + lastUsedBackend.value = currentInferenceBackend + } - function applyPresetModelSettings(presetModelSettings: StringKV) { - Object.keys(presetModel).forEach((key) => { - if (key in presetModelSettings) { - presetModel[key] = presetModelSettings[key]; - } - }); - localStorage.setItem("PresetModelSettings", JSON.stringify(toRaw(presetModel))); + async function resetLastUsedInferenceBackend(currentInferenceBackend: BackendServiceName) { + const lastUsedBackendSnapshot = lastUsedBackend.value + if (lastUsedBackendSnapshot === 'None' || lastUsedBackendSnapshot === currentInferenceBackend) { + return } - - - function loadUserSettings() { - const dataStr = localStorage.getItem("ModelSettings"); - if (dataStr) { - const data = JSON.parse(dataStr) as KVObject; - Object.keys(data).forEach((key) => { - modelSettings[key] = data[key]; - }); - } - assertSelectExist(); + try { + const stopStatus = await backendServices.stopService(lastUsedBackendSnapshot) + console.info(`unused service ${lastUsedBackendSnapshot} now in state ${stopStatus}`) + const startStatus = await backendServices.startService(lastUsedBackendSnapshot) + console.info(`service ${lastUsedBackendSnapshot} now in state ${startStatus}`) + } catch (e) { + console.warn( + `Could not reset last used inference backend ${lastUsedBackendSnapshot} due to ${e}`, + ) } + } - function updateLastUsedBackend(currentInferenceBackend: BackendServiceName) { - lastUsedBackend.value = currentInferenceBackend + function assertSelectExist() { + let changeUserSetup = false + if (models.value.llm.length > 0 && !models.value.llm.includes(modelSettings.llm_model)) { + modelSettings.llm = models.value.llm[0] + changeUserSetup = true } - - async function resetLastUsedInferenceBackend(currentInferenceBackend: BackendServiceName) { - const lastUsedBackendSnapshot = lastUsedBackend.value - if (lastUsedBackendSnapshot === "None" || lastUsedBackendSnapshot === currentInferenceBackend) { - return - } - try { - const stopStatus = await backendServices.stopService(lastUsedBackendSnapshot) - console.info(`unused service ${lastUsedBackendSnapshot} now in state ${stopStatus}`) - const startStatus = await backendServices.startService(lastUsedBackendSnapshot) - console.info(`service ${lastUsedBackendSnapshot} now in state ${startStatus}`) - } catch (e) { - console.warn(`Could not reset last used inference backend ${lastUsedBackendSnapshot} due to ${e}`) - } + if ( + models.value.stableDiffusion.length > 0 && + !models.value.stableDiffusion.includes(modelSettings.sd_model) + ) { + modelSettings.sd_model = models.value.stableDiffusion[0] + changeUserSetup = true } - - 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" - } - } + if (models.value.lora.length > 0 && !models.value.lora.includes(modelSettings.lora)) { + modelSettings.lora = models.value.lora[0] + changeUserSetup = true } - - function assertSelectExist() { - let changeUserSetup = false; - if (models.value.llm.length > 0 && !models.value.llm.includes(modelSettings.llm_model)) { - modelSettings.llm = models.value.llm[0]; - changeUserSetup = true; - } - if (models.value.stableDiffusion.length > 0 && !models.value.stableDiffusion.includes(modelSettings.sd_model)) { - modelSettings.sd_model = models.value.stableDiffusion[0]; - changeUserSetup = true; - } - if (models.value.lora.length > 0 && !models.value.lora.includes(modelSettings.lora)) { - modelSettings.lora = models.value.lora[0]; - changeUserSetup = true; - } - if (!graphicsList.value.find(item => item.index == modelSettings.graphics)) { - modelSettings.graphics = graphicsList.value[0].index; - } - if (changeUserSetup) { - localStorage.setItem("ModelSettings", JSON.stringify(toRaw(modelSettings))); - } - return changeUserSetup; + if (!graphicsList.value.find((item) => item.index == modelSettings.graphics)) { + modelSettings.graphics = graphicsList.value[0].index } - - function applyModelSettings(newSettings: KVObject) { - Object.keys(newSettings).forEach((key) => { - if (key in modelSettings) { - modelSettings[key] = newSettings[key]; - } - }); - const rawModelSettings = toRaw(modelSettings) - localStorage.setItem("ModelSettings", JSON.stringify(rawModelSettings)); - if (modelSettings["resolution"] == 3) { - const manualModelSettings: StringKV = {}; - Object.keys(rawModelSettings).forEach((key) => { - if (key != "resolution" && key != "quality") { - manualModelSettings[key] = rawModelSettings[key]; - } - }); - localStorage.setItem("ManualModelSettings", JSON.stringify(manualModelSettings)); - } + if (changeUserSetup) { + localStorage.setItem('ModelSettings', JSON.stringify(toRaw(modelSettings))) } - - async function checkModelAlreadyLoaded(params: CheckModelAlreadyLoadedParameters[]) { - const response = await fetch(`${defaultBackendBaseUrl.value}/api/checkModelAlreadyLoaded`, { - method: "POST", - body: JSON.stringify({ 'data': params}), - headers: { - "Content-Type": "application/json" - } - }); - const parsedResponse = (await response.json()) as ApiResponse & { data: CheckModelAlreadyLoadedResult[] }; - return parsedResponse.data; + return changeUserSetup + } + + function applyModelSettings(newSettings: KVObject) { + Object.keys(newSettings).forEach((key) => { + if (key in modelSettings) { + modelSettings[key] = newSettings[key] + } + }) + const rawModelSettings = toRaw(modelSettings) + localStorage.setItem('ModelSettings', JSON.stringify(rawModelSettings)) + if (modelSettings['resolution'] == 3) { + const manualModelSettings: StringKV = {} + Object.keys(rawModelSettings).forEach((key) => { + if (key != 'resolution' && key != 'quality') { + manualModelSettings[key] = rawModelSettings[key] + } + }) + localStorage.setItem('ManualModelSettings', JSON.stringify(manualModelSettings)) } - - async function checkIfHuggingFaceUrlExists(repo_id: string) { - const response = await fetch(`${defaultBackendBaseUrl.value}/api/checkHFRepoExists?repo_id=${repo_id}`) - const data = await response.json() - return data.exists; + } + + async function checkModelAlreadyLoaded(params: CheckModelAlreadyLoadedParameters[]) { + const response = await fetch(`${defaultBackendBaseUrl.value}/api/checkModelAlreadyLoaded`, { + method: 'POST', + body: JSON.stringify({ data: params }), + headers: { + 'Content-Type': 'application/json', + }, + }) + const parsedResponse = (await response.json()) as ApiResponse & { + data: CheckModelAlreadyLoadedResult[] } - - return { - state, - modelSettings, - presetModel, - models, - paths, - apiHost: defaultBackendBaseUrl, - graphicsList, - loadingState, - lastUsedBackend, - errorMessage, - hdPersistentConfirmation, - updateLastUsedBackend, - resetLastUsedInferenceBackend, - initSetup, - applyPathsSettings, - applyModelSettings, - refreshLLMModles, - refreshSDModles, - refreshInpaintModles, - refreshLora, - checkModelAlreadyLoaded: checkModelAlreadyLoaded, - checkIfHuggingFaceUrlExists, - applyPresetModelSettings, - restorePathsSettings, - }; -}); \ No newline at end of file + return parsedResponse.data + } + + async function checkIfHuggingFaceUrlExists(repo_id: string) { + const response = await fetch( + `${defaultBackendBaseUrl.value}/api/checkHFRepoExists?repo_id=${repo_id}`, + ) + const data = await response.json() + return data.exists + } + + return { + state, + modelSettings, + presetModel, + models, + paths, + apiHost: defaultBackendBaseUrl, + graphicsList, + loadingState, + lastUsedBackend, + errorMessage, + hdPersistentConfirmation, + updateLastUsedBackend, + resetLastUsedInferenceBackend, + initSetup, + applyPathsSettings, + applyModelSettings, + refreshLLMModles, + refreshSDModles, + refreshInpaintModles, + refreshLora, + checkModelAlreadyLoaded: checkModelAlreadyLoaded, + checkIfHuggingFaceUrlExists, + applyPresetModelSettings, + restorePathsSettings, + } +}) diff --git a/WebUI/src/assets/js/store/i18n.ts b/WebUI/src/assets/js/store/i18n.ts index b5412f2c..2918eda5 100644 --- a/WebUI/src/assets/js/store/i18n.ts +++ b/WebUI/src/assets/js/store/i18n.ts @@ -1,76 +1,80 @@ -import { defineStore } from "pinia"; +import { defineStore } from 'pinia' -export const useI18N = defineStore("i18n", () => { - const langName = ref("en-US"); - const currentLanguageName = ref("English"); - const state = reactive({ - }); +export const useI18N = defineStore('i18n', () => { + const langName = ref('en-US') + const currentLanguageName = ref('English') + const state = reactive({}) // locale naming reference: // https://source.chromium.org/chromium/chromium/src/+/main:ui/base/l10n/l10n_util.cc const languageOptions = ref([ - { value: 'de', name: "Deutsch" }, - { value: 'en-US', name: "English" }, - { value: 'es', name: "Español" }, - { value: 'id', name: "Bahasa Indonesia" }, - { value: 'it', name: "Italiano" }, - { value: 'ja', name: "日本語" }, - { value: 'ko', name: "영어" }, - { value: 'pl', name: "język polski" }, - { value: 'th', name: "ภาษาไทย" }, - { value: 'tr', name: "Türkçe" }, - { value: 'vi', name: "Tiếng Việt" }, - { value: 'zh-CN', name: "简体中文" }, - { value: 'zh-TW', name: "繁體中文" }, - ]); + { value: 'de', name: 'Deutsch' }, + { value: 'en-US', name: 'English' }, + { value: 'es', name: 'Español' }, + { value: 'id', name: 'Bahasa Indonesia' }, + { value: 'it', name: 'Italiano' }, + { value: 'ja', name: '日本語' }, + { value: 'ko', name: '영어' }, + { value: 'pl', name: 'język polski' }, + { value: 'th', name: 'ภาษาไทย' }, + { value: 'tr', name: 'Türkçe' }, + { value: 'vi', name: 'Tiếng Việt' }, + { value: 'zh-CN', name: '简体中文' }, + { value: 'zh-TW', name: '繁體中文' }, + ]) window.electronAPI.getLocalSettings().then((settings) => { - const locale = settings.locale; - console.debug("system locale:", locale); + const locale = settings.locale + console.debug('system locale:', locale) if (locale) { - if (languageOptions.value.find((item) => item.value === locale)) { // use the locale directly if it's supported - langName.value = locale; - } else if (locale.includes('-')) { // fallback if the locale contains '-' - const lang = locale.split('-')[0]; - langName.value = languageOptions.value.find((item) => item.value.includes(lang))?.value || 'en-US'; - } else { // fallback to en-US - langName.value = 'en-US'; + if (languageOptions.value.find((item) => item.value === locale)) { + // use the locale directly if it's supported + langName.value = locale + } else if (locale.includes('-')) { + // fallback if the locale contains '-' + const lang = locale.split('-')[0] + langName.value = + languageOptions.value.find((item) => item.value.includes(lang))?.value || 'en-US' + } else { + // fallback to en-US + langName.value = 'en-US' } } - }); + }) async function init() { - const locale = localStorage.getItem("locale"); + const locale = localStorage.getItem('locale') if (locale) { - langName.value = locale; + langName.value = locale } - console.debug("init i18n:", langName.value); - await switchLanguage(langName.value); + console.debug('init i18n:', langName.value) + await switchLanguage(langName.value) } - function changeLanguage(value: any, index: number) { + function changeLanguage(value: { value: string }, _index: number) { switchLanguage(value.value) - } + } function updateLanguageRecord(record: Record) { Object.keys(record).forEach((key) => { - state[key] = record[key]; - }); - document.title = state.MAIN_TITLE; + state[key] = record[key] + }) + document.title = state.MAIN_TITLE } async function switchLanguage(lang: string) { const [languageRecords, fallbackRecords] = await Promise.all([ import(`../../i18n/${lang}.json`).catch(() => ({})), - import(`../../i18n/en-US.json`) - ]); + import(`../../i18n/en-US.json`), + ]) - const mergedRecords = { ...fallbackRecords, ...languageRecords }; - updateLanguageRecord(mergedRecords); - langName.value = lang; - currentLanguageName.value = languageOptions.value.find((item) => item.value === lang)?.name || "Unknown"; + const mergedRecords = { ...fallbackRecords, ...languageRecords } + updateLanguageRecord(mergedRecords) + langName.value = lang + currentLanguageName.value = + languageOptions.value.find((item) => item.value === lang)?.name || 'Unknown' - localStorage.setItem("locale", lang); + localStorage.setItem('locale', lang) } return { @@ -81,6 +85,5 @@ export const useI18N = defineStore("i18n", () => { init, switchLanguage, changeLanguage, - }; - -}); + } +}) diff --git a/WebUI/src/assets/js/store/imageGeneration.ts b/WebUI/src/assets/js/store/imageGeneration.ts index 84f460ef..b9f61297 100644 --- a/WebUI/src/assets/js/store/imageGeneration.ts +++ b/WebUI/src/assets/js/store/imageGeneration.ts @@ -1,754 +1,821 @@ -import { acceptHMRUpdate, defineStore } from "pinia"; -import z from "zod"; -import { useComfyUi } from "./comfyUi"; -import { useStableDiffusion } from "./stableDiffusion"; -import { useI18N } from "./i18n"; -import { Const } from "../const"; -import { useGlobalSetup } from "./globalSetup"; -import {toast} from "@/assets/js/toast.ts"; +import { acceptHMRUpdate, defineStore } from 'pinia' +import z from 'zod' +import { useComfyUi } from './comfyUi' +import { useStableDiffusion } from './stableDiffusion' +import { useI18N } from './i18n' +import * as Const from '../const' +import { useGlobalSetup } from './globalSetup' +import * as toast from '@/assets/js/toast.ts' export type StableDiffusionSettings = { - resolution: 'standard' | 'hd' | 'manual', // ~ modelSettings.resolution 0, 1, 3 - quality: 'standard' | 'high' | 'fast', // ~ modelSettings.quality 0, 1, 2 - imageModel: string, - inpaintModel: string, - negativePrompt: string, - batchSize: number, // ~ modelSettings.generateNumber - pickerResolution?: string, - width: number, - height: number, - guidanceScale: number, - inferenceSteps: number, - seed: number, - lora: string | null, - scheduler: string | null, - imagePreview: boolean, - safetyCheck: boolean + resolution: 'standard' | 'hd' | 'manual' // ~ modelSettings.resolution 0, 1, 3 + quality: 'standard' | 'high' | 'fast' // ~ modelSettings.quality 0, 1, 2 + imageModel: string + inpaintModel: string + negativePrompt: string + batchSize: number // ~ modelSettings.generateNumber + pickerResolution?: string + width: number + height: number + guidanceScale: number + inferenceSteps: number + seed: number + lora: string | null + scheduler: string | null + imagePreview: boolean + safetyCheck: boolean } const SettingsSchema = z.object({ - imageModel: z.string(), - inpaintModel: z.string(), - negativePrompt: z.string(), - batchSize: z.number(), - width: z.number(), - height: z.number(), - prompt: z.string(), - resolution: z.string(), - guidanceScale: z.number(), - inferenceSteps: z.number(), - seed: z.number(), - lora: z.string().nullable(), - scheduler: z.string().nullable(), - imagePreview: z.boolean(), - safetyCheck: z.boolean() + imageModel: z.string(), + inpaintModel: z.string(), + negativePrompt: z.string(), + batchSize: z.number(), + width: z.number(), + height: z.number(), + prompt: z.string(), + resolution: z.string(), + guidanceScale: z.number(), + inferenceSteps: z.number(), + seed: z.number(), + lora: z.string().nullable(), + scheduler: z.string().nullable(), + imagePreview: z.boolean(), + safetyCheck: z.boolean(), }) -const SettingSchema = SettingsSchema.keyof(); +const SettingSchema = SettingsSchema.keyof() export type Setting = z.infer const WorkflowRequirementSchema = z.enum(['high-vram']) const RequiredModelSchema = z.object({ - type: z.string(), - model: z.string(), - additionalLicenceLink: z.string().optional(), + type: z.string(), + model: z.string(), + additionalLicenceLink: z.string().optional(), }) -export type RequiredModel = z.infer; - -const ComfyUIApiWorkflowSchema = z.record(z.string(), z.object({ - inputs: z.object({ - text: z.string().optional(), - }).passthrough().optional(), -}).passthrough()); -export type ComfyUIApiWorkflow = z.infer; +export type RequiredModel = z.infer +const ComfyUIApiWorkflowSchema = z.record( + z.string(), + z + .object({ + inputs: z + .object({ + text: z.string().optional(), + }) + .passthrough() + .optional(), + }) + .passthrough(), +) +export type ComfyUIApiWorkflow = z.infer const DefaultWorkflowSchema = z.object({ - name: z.string(), - displayPriority: z.number().optional(), - backend: z.literal('default'), - tags: z.array(z.string()), - requiredModels: z.array(RequiredModelSchema).optional(), - requirements: z.array(WorkflowRequirementSchema), - inputs: z.array(z.object({ - name: z.string(), - type: z.enum(['image', 'mask', 'text']) - })), - outputs: z.array(z.object({ - name: z.string(), - type: z.literal('image') - })), - defaultSettings: SettingsSchema.partial().optional(), - displayedSettings: z.array(SettingsSchema.keyof()), - modifiableSettings: z.array(SettingsSchema.keyof()), - dependencies: z.array(z.unknown()).optional() -}); -export type DefaultWorkflow = z.infer; + name: z.string(), + displayPriority: z.number().optional(), + backend: z.literal('default'), + tags: z.array(z.string()), + requiredModels: z.array(RequiredModelSchema).optional(), + requirements: z.array(WorkflowRequirementSchema), + inputs: z.array( + z.object({ + name: z.string(), + type: z.enum(['image', 'mask', 'text']), + }), + ), + outputs: z.array( + z.object({ + name: z.string(), + type: z.literal('image'), + }), + ), + defaultSettings: SettingsSchema.partial().optional(), + displayedSettings: z.array(SettingsSchema.keyof()), + modifiableSettings: z.array(SettingsSchema.keyof()), + dependencies: z.array(z.unknown()).optional(), +}) +export type DefaultWorkflow = z.infer const ComfyNumberInputSchema = z.object({ - nodeTitle: z.string(), - nodeInput: z.string(), - type: z.literal('number'), - label: z.string(), - defaultValue: z.number(), - min: z.number(), - max: z.number(), - step: z.number(), -}); -export type ComfyNumberInput = z.infer; + nodeTitle: z.string(), + nodeInput: z.string(), + type: z.literal('number'), + label: z.string(), + defaultValue: z.number(), + min: z.number(), + max: z.number(), + step: z.number(), +}) +export type ComfyNumberInput = z.infer const ComfyImageInputSchema = z.object({ - nodeTitle: z.string(), - nodeInput: z.string(), - type: z.literal('image'), - defaultValue: z.string(), - label: z.string() -}); -export type ComfyImageInput = z.infer; + nodeTitle: z.string(), + nodeInput: z.string(), + type: z.literal('image'), + defaultValue: z.string(), + label: z.string(), +}) +export type ComfyImageInput = z.infer const ComfyStringInputSchema = z.object({ - nodeTitle: z.string(), - nodeInput: z.string(), - type: z.literal('string'), - defaultValue: z.string(), - label: z.string() -}); -export type ComfyStringInput = z.infer; + nodeTitle: z.string(), + nodeInput: z.string(), + type: z.literal('string'), + defaultValue: z.string(), + label: z.string(), +}) +export type ComfyStringInput = z.infer const ComfyBooleanInputSchema = z.object({ - nodeTitle: z.string(), - nodeInput: z.string(), - type: z.literal('boolean'), - defaultValue: z.boolean(), - label: z.string() -}); -export type ComfyBooleanInput = z.infer; + nodeTitle: z.string(), + nodeInput: z.string(), + type: z.literal('boolean'), + defaultValue: z.boolean(), + label: z.string(), +}) +export type ComfyBooleanInput = z.infer const ComfyDynamicInputSchema = z.discriminatedUnion('type', [ - ComfyNumberInputSchema, - ComfyImageInputSchema, - ComfyStringInputSchema, - ComfyBooleanInputSchema, -]); -type ComfyDynamicInput = z.infer; + ComfyNumberInputSchema, + ComfyImageInputSchema, + ComfyStringInputSchema, + ComfyBooleanInputSchema, +]) +type ComfyDynamicInput = z.infer const ComfyUiWorkflowSchema = z.object({ - name: z.string(), - displayPriority: z.number().default(0), - backend: z.literal('comfyui'), - comfyUIRequirements: z.object({ - pythonPackages: z.array(z.string()).optional(), - customNodes: z.array(z.string()), - requiredModels: z.array(RequiredModelSchema) + name: z.string(), + displayPriority: z.number().default(0), + backend: z.literal('comfyui'), + comfyUIRequirements: z.object({ + pythonPackages: z.array(z.string()).optional(), + customNodes: z.array(z.string()), + requiredModels: z.array(RequiredModelSchema), + }), + tags: z.array(z.string()), + requiredModels: z.array(z.string()).optional(), + requirements: z.array(WorkflowRequirementSchema), + inputs: z.array(ComfyDynamicInputSchema), + outputs: z.array( + z.object({ + name: z.string(), + type: z.literal('image'), }), - tags: z.array(z.string()), - requiredModels: z.array(z.string()).optional(), - requirements: z.array(WorkflowRequirementSchema), - inputs: z.array(ComfyDynamicInputSchema), - outputs: z.array(z.object({ - name: z.string(), - type: z.literal('image') - })), - defaultSettings: SettingsSchema.partial().optional(), - displayedSettings: z.array(SettingsSchema.keyof()), - modifiableSettings: z.array(SettingsSchema.keyof()), - dependencies: z.array(z.unknown()).optional(), - comfyUiApiWorkflow: ComfyUIApiWorkflowSchema -}); -export type ComfyUiWorkflow = z.infer; -const WorkflowSchema = - z.discriminatedUnion('backend', [ - DefaultWorkflowSchema, - ComfyUiWorkflowSchema - ] - ) -export type Workflow = z.infer; - + ), + defaultSettings: SettingsSchema.partial().optional(), + displayedSettings: z.array(SettingsSchema.keyof()), + modifiableSettings: z.array(SettingsSchema.keyof()), + dependencies: z.array(z.unknown()).optional(), + comfyUiApiWorkflow: ComfyUIApiWorkflowSchema, +}) +export type ComfyUiWorkflow = z.infer +const WorkflowSchema = z.discriminatedUnion('backend', [ + DefaultWorkflowSchema, + ComfyUiWorkflowSchema, +]) +export type Workflow = z.infer const globalDefaultSettings = { - seed: -1, - width: 512, - height: 512, - inferenceSteps: 6, - resolution: '704x384', - batchSize: 4, - negativePrompt: 'nsfw', - imageModel: 'Lykon/dreamshaper-8', - inpaintModel: 'Lykon/dreamshaper-8-inpainting', - guidanceScale: 7, - lora: "None", - scheduler: 'DPM++ SDE Karras', + seed: -1, + width: 512, + height: 512, + inferenceSteps: 6, + resolution: '704x384', + batchSize: 4, + negativePrompt: 'nsfw', + imageModel: 'Lykon/dreamshaper-8', + inpaintModel: 'Lykon/dreamshaper-8-inpainting', + guidanceScale: 7, + lora: 'None', + scheduler: 'DPM++ SDE Karras', } const generalDefaultSettings = { - prompt: '', - seed: -1, - imagePreview: true, - safeCheck: true, + prompt: '', + seed: -1, + imagePreview: true, + safeCheck: true, } -export const useImageGeneration = defineStore("imageGeneration", () => { - +export const useImageGeneration = defineStore( + 'imageGeneration', + () => { const predefinedWorkflows: Workflow[] = [ - { - name: 'Standard', - backend: 'default', - tags: ['sd1.5'], - requirements: [], - inputs: [], - outputs: [{ name: 'output_image', type: 'image' }], - defaultSettings: { - imageModel: 'Lykon/dreamshaper-8', - inpaintModel: 'Lykon/dreamshaper-8-inpainting', - resolution: '512x512', - guidanceScale: 7, - inferenceSteps: 20, - scheduler: "DPM++ SDE Karras" - }, - displayedSettings: [ - 'imageModel', - 'inpaintModel', - 'guidanceScale', - 'inferenceSteps', - 'scheduler', - ], - modifiableSettings: [ - 'resolution', - 'seed', - 'inferenceSteps', - 'negativePrompt', - 'batchSize', - 'imagePreview', - 'safetyCheck', - ] - }, - { - name: 'Standard - High Quality', - backend: 'default', - tags: ['sd1.5', 'hq'], - requirements: [], - inputs: [], - outputs: [{ name: 'output_image', type: 'image' }], - defaultSettings: { - imageModel: 'Lykon/dreamshaper-8', - inpaintModel: 'Lykon/dreamshaper-8-inpainting', - resolution: '512x512', - guidanceScale: 7, - inferenceSteps: 50, - scheduler: "DPM++ SDE Karras" - }, - displayedSettings: [ - 'imageModel', - 'inpaintModel', - 'guidanceScale', - 'scheduler', - ], - modifiableSettings: [ - 'resolution', - 'seed', - 'inferenceSteps', - 'negativePrompt', - 'batchSize', - 'imagePreview', - 'safetyCheck', - ] + { + name: 'Standard', + backend: 'default', + tags: ['sd1.5'], + requirements: [], + inputs: [], + outputs: [{ name: 'output_image', type: 'image' }], + defaultSettings: { + imageModel: 'Lykon/dreamshaper-8', + inpaintModel: 'Lykon/dreamshaper-8-inpainting', + resolution: '512x512', + guidanceScale: 7, + inferenceSteps: 20, + scheduler: 'DPM++ SDE Karras', }, - { - name: 'Standard - Fast', - backend: 'default', - tags: ['sd1.5', 'fast'], - requirements: [], - inputs: [], - outputs: [{ name: 'output_image', type: 'image' }], - defaultSettings: { - imageModel: 'Lykon/dreamshaper-8', - inpaintModel: 'Lykon/dreamshaper-8-inpainting', - resolution: '704x384', - guidanceScale: 1, - inferenceSteps: 6, - batchSize: 4, - scheduler: "LCM", - lora: "latent-consistency/lcm-lora-sdv1-5" - }, - displayedSettings: [ - 'imageModel', - 'inpaintModel', - 'guidanceScale', - 'scheduler', - 'lora' - ], - modifiableSettings: [ - 'resolution', - 'seed', - 'inferenceSteps', - 'negativePrompt', - 'batchSize', - 'imagePreview', - 'safetyCheck', - ] + displayedSettings: [ + 'imageModel', + 'inpaintModel', + 'guidanceScale', + 'inferenceSteps', + 'scheduler', + ], + modifiableSettings: [ + 'resolution', + 'seed', + 'inferenceSteps', + 'negativePrompt', + 'batchSize', + 'imagePreview', + 'safetyCheck', + ], + }, + { + name: 'Standard - High Quality', + backend: 'default', + tags: ['sd1.5', 'hq'], + requirements: [], + inputs: [], + outputs: [{ name: 'output_image', type: 'image' }], + defaultSettings: { + imageModel: 'Lykon/dreamshaper-8', + inpaintModel: 'Lykon/dreamshaper-8-inpainting', + resolution: '512x512', + guidanceScale: 7, + inferenceSteps: 50, + scheduler: 'DPM++ SDE Karras', }, - { - name: 'HD', - backend: 'default', - tags: ['sdxl', 'high-vram'], - requirements: [], - inputs: [], - outputs: [{ name: 'output_image', type: 'image' }], - defaultSettings: { - imageModel: 'RunDiffusion/Juggernaut-XL-v9', - inpaintModel: useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL, - resolution: '1024x1024', - guidanceScale: 7, - inferenceSteps: 20, - scheduler: "DPM++ SDE", - lora: "None" - }, - displayedSettings: [ - 'imageModel', - 'inpaintModel', - 'guidanceScale', - 'scheduler', - ], - modifiableSettings: [ - 'resolution', - 'seed', - 'inferenceSteps', - 'negativePrompt', - 'batchSize', - 'imagePreview', - 'safetyCheck', - ] + displayedSettings: ['imageModel', 'inpaintModel', 'guidanceScale', 'scheduler'], + modifiableSettings: [ + 'resolution', + 'seed', + 'inferenceSteps', + 'negativePrompt', + 'batchSize', + 'imagePreview', + 'safetyCheck', + ], + }, + { + name: 'Standard - Fast', + backend: 'default', + tags: ['sd1.5', 'fast'], + requirements: [], + inputs: [], + outputs: [{ name: 'output_image', type: 'image' }], + defaultSettings: { + imageModel: 'Lykon/dreamshaper-8', + inpaintModel: 'Lykon/dreamshaper-8-inpainting', + resolution: '704x384', + guidanceScale: 1, + inferenceSteps: 6, + batchSize: 4, + scheduler: 'LCM', + lora: 'latent-consistency/lcm-lora-sdv1-5', }, - { - name: 'HD - High Quality', - backend: 'default', - tags: ['sdxl', 'high-vram', 'hq'], - requirements: [], - inputs: [], - outputs: [{ name: 'output_image', type: 'image' }], - defaultSettings: { - imageModel: 'RunDiffusion/Juggernaut-XL-v9', - inpaintModel: useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL, - resolution: '1024x1024', - guidanceScale: 7, - inferenceSteps: 50, - scheduler: "DPM++ SDE", - lora: "None" - }, - displayedSettings: [ - 'imageModel', - 'inpaintModel', - 'guidanceScale', - 'scheduler', - ], - modifiableSettings: [ - 'resolution', - 'seed', - 'inferenceSteps', - 'negativePrompt', - 'batchSize', - 'imagePreview', - 'safetyCheck', - ] + displayedSettings: ['imageModel', 'inpaintModel', 'guidanceScale', 'scheduler', 'lora'], + modifiableSettings: [ + 'resolution', + 'seed', + 'inferenceSteps', + 'negativePrompt', + 'batchSize', + 'imagePreview', + 'safetyCheck', + ], + }, + { + name: 'HD', + backend: 'default', + tags: ['sdxl', 'high-vram'], + requirements: [], + inputs: [], + outputs: [{ name: 'output_image', type: 'image' }], + defaultSettings: { + imageModel: 'RunDiffusion/Juggernaut-XL-v9', + inpaintModel: useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL, + resolution: '1024x1024', + guidanceScale: 7, + inferenceSteps: 20, + scheduler: 'DPM++ SDE', + lora: 'None', }, - { - name: 'HD - Fast', - backend: 'default', - tags: ['sdxl', 'high-vram', 'fast'], - requirements: [], - inputs: [], - outputs: [{ name: 'output_image', type: 'image' }], - defaultSettings: { - imageModel: 'RunDiffusion/Juggernaut-XL-v9', - inpaintModel: useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL, - resolution: '1024x1024', - guidanceScale: 1, - inferenceSteps: 6, - scheduler: "LCM", - lora: "latent-consistency/lcm-lora-sdxl" - }, - displayedSettings: [ - 'imageModel', - 'inpaintModel', - 'guidanceScale', - 'scheduler', - ], - modifiableSettings: [ - 'resolution', - 'seed', - 'inferenceSteps', - 'negativePrompt', - 'batchSize', - 'imagePreview', - 'safetyCheck', - ] + displayedSettings: ['imageModel', 'inpaintModel', 'guidanceScale', 'scheduler'], + modifiableSettings: [ + 'resolution', + 'seed', + 'inferenceSteps', + 'negativePrompt', + 'batchSize', + 'imagePreview', + 'safetyCheck', + ], + }, + { + name: 'HD - High Quality', + backend: 'default', + tags: ['sdxl', 'high-vram', 'hq'], + requirements: [], + inputs: [], + outputs: [{ name: 'output_image', type: 'image' }], + defaultSettings: { + imageModel: 'RunDiffusion/Juggernaut-XL-v9', + inpaintModel: useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL, + resolution: '1024x1024', + guidanceScale: 7, + inferenceSteps: 50, + scheduler: 'DPM++ SDE', + lora: 'None', }, - { - name: 'Manual', - backend: 'default', - tags: ['sd1.5', 'sdxl'], - requirements: [], - inputs: [], - outputs: [{ name: 'output_image', type: 'image' }], - displayedSettings: [ - ], - modifiableSettings: [ - 'seed', - 'negativePrompt', - 'batchSize', - 'imagePreview', - 'safetyCheck', - 'width', - 'height', - 'imageModel', - 'inpaintModel', - 'inferenceSteps', - 'guidanceScale', - 'scheduler', - 'lora', - ] + displayedSettings: ['imageModel', 'inpaintModel', 'guidanceScale', 'scheduler'], + modifiableSettings: [ + 'resolution', + 'seed', + 'inferenceSteps', + 'negativePrompt', + 'batchSize', + 'imagePreview', + 'safetyCheck', + ], + }, + { + name: 'HD - Fast', + backend: 'default', + tags: ['sdxl', 'high-vram', 'fast'], + requirements: [], + inputs: [], + outputs: [{ name: 'output_image', type: 'image' }], + defaultSettings: { + imageModel: 'RunDiffusion/Juggernaut-XL-v9', + inpaintModel: useI18N().state.ENHANCE_INPAINT_USE_IMAGE_MODEL, + resolution: '1024x1024', + guidanceScale: 1, + inferenceSteps: 6, + scheduler: 'LCM', + lora: 'latent-consistency/lcm-lora-sdxl', }, + displayedSettings: ['imageModel', 'inpaintModel', 'guidanceScale', 'scheduler'], + modifiableSettings: [ + 'resolution', + 'seed', + 'inferenceSteps', + 'negativePrompt', + 'batchSize', + 'imagePreview', + 'safetyCheck', + ], + }, + { + name: 'Manual', + backend: 'default', + tags: ['sd1.5', 'sdxl'], + requirements: [], + inputs: [], + outputs: [{ name: 'output_image', type: 'image' }], + displayedSettings: [], + modifiableSettings: [ + 'seed', + 'negativePrompt', + 'batchSize', + 'imagePreview', + 'safetyCheck', + 'width', + 'height', + 'imageModel', + 'inpaintModel', + 'inferenceSteps', + 'guidanceScale', + 'scheduler', + 'lora', + ], + }, ] - const comfyUi = useComfyUi(); - const stableDiffusion = useStableDiffusion(); - const globalSetup = useGlobalSetup(); - const i18nState = useI18N().state; + const comfyUi = useComfyUi() + const stableDiffusion = useStableDiffusion() + const globalSetup = useGlobalSetup() + const i18nState = useI18N().state - const hdWarningDismissed = ref(false); + const hdWarningDismissed = ref(false) - const workflows = ref(predefinedWorkflows); - const activeWorkflowName = ref('Standard - Fast'); + const workflows = ref(predefinedWorkflows) + const activeWorkflowName = ref('Standard - Fast') const activeWorkflow = computed(() => { - console.log('### activeWorkflowName', activeWorkflowName.value) - return workflows.value.find(w => w.name === activeWorkflowName.value) ?? predefinedWorkflows[0] - }); - const processing = ref(false); - const stopping = ref(false); - + console.log('### activeWorkflowName', activeWorkflowName.value) + return ( + workflows.value.find((w) => w.name === activeWorkflowName.value) ?? predefinedWorkflows[0] + ) + }) + const processing = ref(false) + const stopping = ref(false) // general settings - const prompt = ref(generalDefaultSettings.prompt); - const seed = ref(generalDefaultSettings.seed); - const imagePreview = ref(generalDefaultSettings.imagePreview); - const safeCheck = ref(generalDefaultSettings.safeCheck); - const batchSize = ref(globalDefaultSettings.batchSize); // TODO this should be imageCount instead, as we only support batchSize 1 due to memory constraints + const prompt = ref(generalDefaultSettings.prompt) + const seed = ref(generalDefaultSettings.seed) + const imagePreview = ref(generalDefaultSettings.imagePreview) + const safeCheck = ref(generalDefaultSettings.safeCheck) + const batchSize = ref(globalDefaultSettings.batchSize) // TODO this should be imageCount instead, as we only support batchSize 1 due to memory constraints const resetActiveWorkflowSettings = () => { - prompt.value = generalDefaultSettings.prompt; - seed.value = generalDefaultSettings.seed; - imagePreview.value = generalDefaultSettings.imagePreview; - safeCheck.value = generalDefaultSettings.safeCheck; - settingsPerWorkflow.value[activeWorkflowName.value ?? ''] = undefined; - comfyInputsPerWorkflow.value[activeWorkflowName.value ?? ''] = undefined; - loadSettingsForActiveWorkflow(); + prompt.value = generalDefaultSettings.prompt + seed.value = generalDefaultSettings.seed + imagePreview.value = generalDefaultSettings.imagePreview + safeCheck.value = generalDefaultSettings.safeCheck + settingsPerWorkflow.value[activeWorkflowName.value ?? ''] = undefined + comfyInputsPerWorkflow.value[activeWorkflowName.value ?? ''] = undefined + loadSettingsForActiveWorkflow() } // model specific settings - const negativePrompt = ref(globalDefaultSettings.negativePrompt); - const width = ref(globalDefaultSettings.width); - const height = ref(globalDefaultSettings.height); - const scheduler = ref(globalDefaultSettings.scheduler); - const imageModel = ref(globalDefaultSettings.imageModel); - const inpaintModel = ref(activeWorkflow.value.defaultSettings?.inpaintModel ?? globalDefaultSettings.inpaintModel); - const lora = ref(globalDefaultSettings.lora); - const guidanceScale = ref(globalDefaultSettings.guidanceScale); - const inferenceSteps = ref(globalDefaultSettings.inferenceSteps); + const negativePrompt = ref(globalDefaultSettings.negativePrompt) + const width = ref(globalDefaultSettings.width) + const height = ref(globalDefaultSettings.height) + const scheduler = ref(globalDefaultSettings.scheduler) + const imageModel = ref(globalDefaultSettings.imageModel) + const inpaintModel = ref( + activeWorkflow.value.defaultSettings?.inpaintModel ?? globalDefaultSettings.inpaintModel, + ) + const lora = ref(globalDefaultSettings.lora) + const guidanceScale = ref(globalDefaultSettings.guidanceScale) + const inferenceSteps = ref(globalDefaultSettings.inferenceSteps) const resolution = computed({ - get() { - return `${width.value}x${height.value}` - }, - set(newValue) { - [width.value, height.value] = newValue.split('x').map(Number); - } + get() { + return `${width.value}x${height.value}` + }, + set(newValue) { + ;[width.value, height.value] = newValue.split('x').map(Number) + }, }) - const settings = { seed, inferenceSteps, width, height, resolution, batchSize, negativePrompt, lora, scheduler, guidanceScale, imageModel, inpaintModel }; - type ModifiableSettings = keyof typeof settings; + const settings = { + seed, + inferenceSteps, + width, + height, + resolution, + batchSize, + negativePrompt, + lora, + scheduler, + guidanceScale, + imageModel, + inpaintModel, + } + type ModifiableSettings = keyof typeof settings const backend = computed({ - get() { - return activeWorkflow.value.backend; - }, - set(newValue) { - const sortedWorkflowsForBackend = workflows.value - .filter(w => w.backend === newValue) - .sort((a, b) => (b.displayPriority ?? 0) - (a.displayPriority ?? 0)) - activeWorkflowName.value = sortedWorkflowsForBackend.find(w => w.name === lastWorkflowPerBackend.value[newValue])?.name ?? sortedWorkflowsForBackend[0]?.name ?? activeWorkflowName.value; - } - }); + get() { + return activeWorkflow.value.backend + }, + set(newValue) { + const sortedWorkflowsForBackend = workflows.value + .filter((w) => w.backend === newValue) + .sort((a, b) => (b.displayPriority ?? 0) - (a.displayPriority ?? 0)) + activeWorkflowName.value = + sortedWorkflowsForBackend.find((w) => w.name === lastWorkflowPerBackend.value[newValue]) + ?.name ?? + sortedWorkflowsForBackend[0]?.name ?? + activeWorkflowName.value + }, + }) const lastWorkflowPerBackend = ref>({ - comfyui: null, - default: null - }); + comfyui: null, + default: null, + }) const comfyInputs = computed(() => { - if (activeWorkflow.value.backend !== 'comfyui') return [] - const inputRef = (input: ComfyDynamicInput): NodeInputReference => `${input.nodeTitle}.${input.nodeInput}`; - const savePerWorkflow = (input: ComfyDynamicInput, newValue: ComfyDynamicInput['defaultValue']) => { - if (!activeWorkflowName.value) return; - comfyInputsPerWorkflow.value[activeWorkflowName.value] = { - ...comfyInputsPerWorkflow.value[activeWorkflowName.value], - [inputRef(input)]: newValue - } - console.log('saving', { nodeTitle: input.nodeTitle, nodeInput: input.nodeInput, newValue }); + if (activeWorkflow.value.backend !== 'comfyui') return [] + const inputRef = (input: ComfyDynamicInput): NodeInputReference => + `${input.nodeTitle}.${input.nodeInput}` + const savePerWorkflow = ( + input: ComfyDynamicInput, + newValue: ComfyDynamicInput['defaultValue'], + ) => { + if (!activeWorkflowName.value) return + comfyInputsPerWorkflow.value[activeWorkflowName.value] = { + ...comfyInputsPerWorkflow.value[activeWorkflowName.value], + [inputRef(input)]: newValue, } - const getSavedOrDefault = (input: ComfyDynamicInput) => { - if (!activeWorkflowName.value) return input.defaultValue; - const saved = comfyInputsPerWorkflow.value[activeWorkflowName.value]?.[inputRef(input)]; - if (saved) console.log('got saved dynamic input', { nodeTitle: input.nodeTitle, nodeInput: input.nodeInput, saved }); - return saved ?? input.defaultValue; - }; - - return activeWorkflow.value.inputs.map(input => { - const _current = ref(getSavedOrDefault(input)) - - const current = computed({ - get() { - return _current.value; - }, - set(newValue) { - _current.value = newValue; - savePerWorkflow(input, newValue) - } - }) - - return { ...input, current } + console.log('saving', { nodeTitle: input.nodeTitle, nodeInput: input.nodeInput, newValue }) + } + const getSavedOrDefault = (input: ComfyDynamicInput) => { + if (!activeWorkflowName.value) return input.defaultValue + const saved = comfyInputsPerWorkflow.value[activeWorkflowName.value]?.[inputRef(input)] + if (saved) + console.log('got saved dynamic input', { + nodeTitle: input.nodeTitle, + nodeInput: input.nodeInput, + saved, + }) + return saved ?? input.defaultValue + } + + return activeWorkflow.value.inputs.map((input) => { + const _current = ref(getSavedOrDefault(input)) + + const current = computed({ + get() { + return _current.value + }, + set(newValue) { + _current.value = newValue + savePerWorkflow(input, newValue) + }, }) - }); - - type WorkflowName = string; - type NodeInputReference = string; // nodeTitle.nodeInput - const comfyInputsPerWorkflow = ref | undefined>>({}); - const settingsPerWorkflow = ref>({}); - const isModifiable = (settingName: ModifiableSettings) => activeWorkflow.value.modifiableSettings.includes(settingName); + return { ...input, current } + }) + }) - watch([activeWorkflowName, workflows], () => { - setTimeout(() => lastWorkflowPerBackend.value[activeWorkflow.value.backend] = activeWorkflowName.value) - loadSettingsForActiveWorkflow(); - }, {}); + type WorkflowName = string + type NodeInputReference = string // nodeTitle.nodeInput + const comfyInputsPerWorkflow = ref< + Record< + WorkflowName, + Record | undefined + > + >({}) + const settingsPerWorkflow = ref>({}) + + const isModifiable = (settingName: ModifiableSettings) => + activeWorkflow.value.modifiableSettings.includes(settingName) + + watch( + [activeWorkflowName, workflows], + () => { + setTimeout( + () => + (lastWorkflowPerBackend.value[activeWorkflow.value.backend] = activeWorkflowName.value), + ) + loadSettingsForActiveWorkflow() + }, + {}, + ) watch(resolution, () => { - const [width, height] = resolution.value.split('x').map(Number); - settings.width.value = width; - settings.height.value = height; - }); + const [width, height] = resolution.value.split('x').map(Number) + settings.width.value = width + settings.height.value = height + }) watch([inferenceSteps, width, height], () => { - console.log('saving to settingsPerWorkflow'); - const saveToSettingsPerWorkflow = (settingName: ModifiableSettings) => { - if (!activeWorkflowName.value) return; - if (isModifiable(settingName)) { - settingsPerWorkflow.value[activeWorkflowName.value] = { - ...settingsPerWorkflow.value[activeWorkflowName.value], - [settingName]: settings[settingName].value - } - console.log('saving', { settingName, value: settings[settingName].value }); - } + console.log('saving to settingsPerWorkflow') + const saveToSettingsPerWorkflow = (settingName: ModifiableSettings) => { + if (!activeWorkflowName.value) return + if (isModifiable(settingName)) { + settingsPerWorkflow.value[activeWorkflowName.value] = { + ...settingsPerWorkflow.value[activeWorkflowName.value], + [settingName]: settings[settingName].value, + } + console.log('saving', { settingName, value: settings[settingName].value }) } - saveToSettingsPerWorkflow('seed'); - saveToSettingsPerWorkflow('inferenceSteps'); - saveToSettingsPerWorkflow('width'); - saveToSettingsPerWorkflow('height'); - saveToSettingsPerWorkflow('resolution'); - saveToSettingsPerWorkflow('batchSize'); - saveToSettingsPerWorkflow('negativePrompt'); - saveToSettingsPerWorkflow('lora'); - saveToSettingsPerWorkflow('scheduler'); - saveToSettingsPerWorkflow('guidanceScale'); - saveToSettingsPerWorkflow('imageModel'); - saveToSettingsPerWorkflow('inpaintModel'); - }); - - - const imageUrls = ref([]); - const currentState = ref("no_start"); - const stepText = ref(""); - const previewIdx = ref(0); - const generateIdx = ref(-999); + } + saveToSettingsPerWorkflow('seed') + saveToSettingsPerWorkflow('inferenceSteps') + saveToSettingsPerWorkflow('width') + saveToSettingsPerWorkflow('height') + saveToSettingsPerWorkflow('resolution') + saveToSettingsPerWorkflow('batchSize') + saveToSettingsPerWorkflow('negativePrompt') + saveToSettingsPerWorkflow('lora') + saveToSettingsPerWorkflow('scheduler') + saveToSettingsPerWorkflow('guidanceScale') + saveToSettingsPerWorkflow('imageModel') + saveToSettingsPerWorkflow('inpaintModel') + }) + + const imageUrls = ref([]) + const currentState = ref('no_start') + const stepText = ref('') + const previewIdx = ref(0) + const generateIdx = ref(-999) function loadSettingsForActiveWorkflow() { - console.log('loading settings for', activeWorkflowName.value); - const getSavedOrDefault = (settingName: ModifiableSettings) => { - if (!activeWorkflowName.value) return; - let saved = undefined; - if (isModifiable(settingName)) { - saved = settingsPerWorkflow.value[activeWorkflowName.value]?.[settingName]; - console.log('got saved', { settingName, saved }); - } - settings[settingName].value = saved ?? activeWorkflow.value?.defaultSettings?.[settingName] ?? globalDefaultSettings[settingName]; - }; - - getSavedOrDefault('seed'); - getSavedOrDefault('inferenceSteps'); - getSavedOrDefault('width'); - getSavedOrDefault('height'); - getSavedOrDefault('resolution'); - getSavedOrDefault('batchSize'); - getSavedOrDefault('negativePrompt'); - getSavedOrDefault('lora'); - getSavedOrDefault('scheduler'); - getSavedOrDefault('guidanceScale'); - getSavedOrDefault('imageModel'); - getSavedOrDefault('inpaintModel'); + console.log('loading settings for', activeWorkflowName.value) + const getSavedOrDefault = (settingName: ModifiableSettings) => { + if (!activeWorkflowName.value) return + let saved = undefined + if (isModifiable(settingName)) { + saved = settingsPerWorkflow.value[activeWorkflowName.value]?.[settingName] + console.log('got saved', { settingName, saved }) + } + settings[settingName].value = + saved ?? + activeWorkflow.value?.defaultSettings?.[settingName] ?? + globalDefaultSettings[settingName] + } + + getSavedOrDefault('seed') + getSavedOrDefault('inferenceSteps') + getSavedOrDefault('width') + getSavedOrDefault('height') + getSavedOrDefault('resolution') + getSavedOrDefault('batchSize') + getSavedOrDefault('negativePrompt') + getSavedOrDefault('lora') + getSavedOrDefault('scheduler') + getSavedOrDefault('guidanceScale') + getSavedOrDefault('imageModel') + getSavedOrDefault('inpaintModel') } async function updateDestImage(index: number, image: string) { - if (index + 1 > imageUrls.value.length) { - imageUrls.value.push(image); - } else { - imageUrls.value.splice(index, 1, image); - } + if (index + 1 > imageUrls.value.length) { + imageUrls.value.push(image) + } else { + imageUrls.value.splice(index, 1, image) + } } - async function loadWorkflowsFromIntel() { - const syncResponse = await window.electronAPI.updateWorkflowsFromIntelRepo(); - await loadWorkflowsFromJson() - return syncResponse + const syncResponse = await window.electronAPI.updateWorkflowsFromIntelRepo() + await loadWorkflowsFromJson() + return syncResponse } async function loadWorkflowsFromJson() { - const workflowsFromFiles = await window.electronAPI.reloadImageWorkflows(); - const parsedWorkflows = workflowsFromFiles.map((workflow) => { - try { - return WorkflowSchema.parse(JSON.parse(workflow)); - } catch (error) { - console.error('Failed to parse workflow', { error, workflow }); - return undefined; - } - }).filter((wf) => wf !== undefined); - workflows.value = [...predefinedWorkflows, ...parsedWorkflows]; + const workflowsFromFiles = await window.electronAPI.reloadImageWorkflows() + const parsedWorkflows = workflowsFromFiles + .map((workflow) => { + try { + return WorkflowSchema.parse(JSON.parse(workflow)) + } catch (error) { + console.error('Failed to parse workflow', { error, workflow }) + return undefined + } + }) + .filter((wf) => wf !== undefined) + workflows.value = [...predefinedWorkflows, ...parsedWorkflows] } async function getMissingModels(): Promise { - if (activeWorkflow.value.backend === "default") { - return getMissingDefaultBackendModels() - } else { - return getMissingComfyuiBackendModels(activeWorkflow.value) - } + if (activeWorkflow.value.backend === 'default') { + return getMissingDefaultBackendModels() + } else { + return getMissingComfyuiBackendModels(activeWorkflow.value) + } } - async function getMissingComfyuiBackendModels(workflow: ComfyUiWorkflow): Promise { - function extractDownloadModelParamsFromString(requiredModel: RequiredModel): CheckModelAlreadyLoadedParameters { - function modelTypeToId(type: string) { - switch (type) { - case "unet" : return Const.MODEL_TYPE_COMFY_UNET - case "clip" : return Const.MODEL_TYPE_COMFY_CLIP - case "vae" : return Const.MODEL_TYPE_COMFY_VAE - case "faceswap" : return Const.MODEL_TYPE_FACESWAP - case "facerestore" : return Const.MODEL_TYPE_FACERESTORE - case "defaultCheckpoint" : return Const.MODEL_TYPE_COMFY_DEFAULT_CHECKPOINT - case "defaultLora" : return Const.MODEL_TYPE_COMFY_DEFAULT_LORA - case "controlNet" : return Const.MODEL_TYPE_COMFY_CONTROL_NET - default: - console.warn("received unknown comfyUI type: ", type) - return -1 - } - } - return {type: modelTypeToId(requiredModel.type), repo_id: requiredModel.model, backend: "comfyui" , additionalLicenseLink : requiredModel.additionalLicenceLink} + async function getMissingComfyuiBackendModels( + workflow: ComfyUiWorkflow, + ): Promise { + function extractDownloadModelParamsFromString( + requiredModel: RequiredModel, + ): CheckModelAlreadyLoadedParameters { + function modelTypeToId(type: string) { + switch (type) { + case 'unet': + return Const.MODEL_TYPE_COMFY_UNET + case 'clip': + return Const.MODEL_TYPE_COMFY_CLIP + case 'vae': + return Const.MODEL_TYPE_COMFY_VAE + case 'faceswap': + return Const.MODEL_TYPE_FACESWAP + case 'facerestore': + return Const.MODEL_TYPE_FACERESTORE + case 'defaultCheckpoint': + return Const.MODEL_TYPE_COMFY_DEFAULT_CHECKPOINT + case 'defaultLora': + return Const.MODEL_TYPE_COMFY_DEFAULT_LORA + case 'controlNet': + return Const.MODEL_TYPE_COMFY_CONTROL_NET + default: + console.warn('received unknown comfyUI type: ', type) + return -1 + } + } + return { + type: modelTypeToId(requiredModel.type), + repo_id: requiredModel.model, + backend: 'comfyui', + additionalLicenseLink: requiredModel.additionalLicenceLink, } - const checkList: CheckModelAlreadyLoadedParameters[] = workflow.comfyUIRequirements.requiredModels.map( extractDownloadModelParamsFromString ) - const checkedModels: CheckModelAlreadyLoadedResult[] = await globalSetup.checkModelAlreadyLoaded(checkList); - const modelsToBeLoaded = checkedModels.filter(checkModelExistsResult => !checkModelExistsResult.already_loaded) - for (const item of modelsToBeLoaded) { - if(!await globalSetup.checkIfHuggingFaceUrlExists(item.repo_id)) { - toast.error(`declared model ${item.repo_id} does not exist. Aborting Generation.`) - return [] - } + } + const checkList: CheckModelAlreadyLoadedParameters[] = + workflow.comfyUIRequirements.requiredModels.map(extractDownloadModelParamsFromString) + const checkedModels: CheckModelAlreadyLoadedResult[] = + await globalSetup.checkModelAlreadyLoaded(checkList) + const modelsToBeLoaded = checkedModels.filter( + (checkModelExistsResult) => !checkModelExistsResult.already_loaded, + ) + for (const item of modelsToBeLoaded) { + if (!(await globalSetup.checkIfHuggingFaceUrlExists(item.repo_id))) { + toast.error(`declared model ${item.repo_id} does not exist. Aborting Generation.`) + return [] } - return modelsToBeLoaded + } + return modelsToBeLoaded } async function getMissingDefaultBackendModels(): Promise { - const checkList: CheckModelAlreadyLoadedParameters[] = [{ repo_id: imageModel.value, type: Const.MODEL_TYPE_STABLE_DIFFUSION, backend: "default" }]; - if (lora.value !== "None") { - checkList.push({ repo_id: lora.value, type: Const.MODEL_TYPE_LORA, backend: "default" }) - } - if (imagePreview.value) { - checkList.push({ repo_id: "madebyollin/taesd", type: Const.MODEL_TYPE_PREVIEW , backend: "default"}) - checkList.push({ repo_id: "madebyollin/taesdxl", type: Const.MODEL_TYPE_PREVIEW , backend: "default"}) - } + const checkList: CheckModelAlreadyLoadedParameters[] = [ + { repo_id: imageModel.value, type: Const.MODEL_TYPE_STABLE_DIFFUSION, backend: 'default' }, + ] + if (lora.value !== 'None') { + checkList.push({ repo_id: lora.value, type: Const.MODEL_TYPE_LORA, backend: 'default' }) + } + if (imagePreview.value) { + checkList.push({ + repo_id: 'madebyollin/taesd', + type: Const.MODEL_TYPE_PREVIEW, + backend: 'default', + }) + checkList.push({ + repo_id: 'madebyollin/taesdxl', + type: Const.MODEL_TYPE_PREVIEW, + backend: 'default', + }) + } - const result = await globalSetup.checkModelAlreadyLoaded(checkList); - return result - .filter(checkModelExistsResult => !checkModelExistsResult.already_loaded) + const result = await globalSetup.checkModelAlreadyLoaded(checkList) + return result.filter((checkModelExistsResult) => !checkModelExistsResult.already_loaded) } async function generate() { - generateIdx.value = 0; - previewIdx.value = 0; - stepText.value = i18nState.COM_GENERATING; - if (activeWorkflow.value.backend === 'default') { - stableDiffusion.generate(); - } else { - comfyUi.generate(); - } + generateIdx.value = 0 + previewIdx.value = 0 + stepText.value = i18nState.COM_GENERATING + if (activeWorkflow.value.backend === 'default') { + stableDiffusion.generate() + } else { + comfyUi.generate() + } } function stopGeneration() { - stableDiffusion.stop(); - comfyUi.stop(); + stableDiffusion.stop() + comfyUi.stop() } function reset() { - currentState.value = "no_start"; - stableDiffusion.generateParams.length = 0; - imageUrls.value.length = 0; - generateIdx.value = -999; - previewIdx.value = -1; + currentState.value = 'no_start' + stableDiffusion.generateParams.length = 0 + imageUrls.value.length = 0 + generateIdx.value = -999 + previewIdx.value = -1 } - loadWorkflowsFromJson(); - + loadWorkflowsFromJson() return { - hdWarningDismissed, - backend, - workflows, - activeWorkflowName, - activeWorkflow, - processing, - prompt, - imageUrls, - currentState, - stepText, - stopping, - previewIdx, - generateIdx, - imageModel, - inpaintModel, - lora, - scheduler, - guidanceScale, - imagePreview, - safeCheck, - inferenceSteps, - seed, - width, - height, - batchSize, - negativePrompt, - settingsPerWorkflow, - comfyInputsPerWorkflow, - comfyInputs, - lastWorkflowPerBackend, - resetActiveWorkflowSettings, - loadWorkflowsFromJson, - loadWorkflowsFromIntel, - getMissingModels, - updateDestImage, - generate, - stopGeneration, - reset + hdWarningDismissed, + backend, + workflows, + activeWorkflowName, + activeWorkflow, + processing, + prompt, + imageUrls, + currentState, + stepText, + stopping, + previewIdx, + generateIdx, + imageModel, + inpaintModel, + lora, + scheduler, + guidanceScale, + imagePreview, + safeCheck, + inferenceSteps, + seed, + width, + height, + batchSize, + negativePrompt, + settingsPerWorkflow, + comfyInputsPerWorkflow, + comfyInputs, + lastWorkflowPerBackend, + resetActiveWorkflowSettings, + loadWorkflowsFromJson, + loadWorkflowsFromIntel, + getMissingModels, + updateDestImage, + generate, + stopGeneration, + reset, } -}, { + }, + { persist: { - debug: true, - pick: ['backend', 'activeWorkflowName', 'settingsPerWorkflow', 'comfyInputsPerWorkflow', 'hdWarningDismissed', 'lastWorkflowPerBackend'] - } -}); + debug: true, + pick: [ + 'backend', + 'activeWorkflowName', + 'settingsPerWorkflow', + 'comfyInputsPerWorkflow', + 'hdWarningDismissed', + 'lastWorkflowPerBackend', + ], + }, + }, +) if (import.meta.hot) { - import.meta.hot.accept(acceptHMRUpdate(useImageGeneration, import.meta.hot)) -} \ No newline at end of file + import.meta.hot.accept(acceptHMRUpdate(useImageGeneration, import.meta.hot)) +} diff --git a/WebUI/src/assets/js/store/models.ts b/WebUI/src/assets/js/store/models.ts index b5a206b9..fd15d91f 100644 --- a/WebUI/src/assets/js/store/models.ts +++ b/WebUI/src/assets/js/store/models.ts @@ -1,80 +1,110 @@ -import { acceptHMRUpdate, defineStore } from "pinia"; +import { acceptHMRUpdate, defineStore } from 'pinia' -export type ModelType = "llm" | "embedding" | "stableDiffusion" | "inpaint" | "lora" | "vae" | "undefined" | "ggufLLM"; +export type ModelType = + | 'llm' + | 'embedding' + | 'stableDiffusion' + | 'inpaint' + | 'lora' + | 'vae' + | 'undefined' + | 'ggufLLM' export type Model = { - name: string; - downloaded: boolean; - type: ModelType; + name: string + downloaded: boolean + type: ModelType } const predefinedModels: Model[] = [ - { name: 'Qwen/Qwen2-1.5B-Instruct', type: 'llm', downloaded: false }, - { name: 'microsoft/Phi-3-mini-4k-instruct', type: 'llm', downloaded: false }, - // { name: 'meta-llama/Meta-Llama-3.1-8B-Instruct', type: 'llm', downloaded: false }, - { name: 'mistralai/Mistral-7B-Instruct-v0.3', type: 'llm', downloaded: false }, - // { name: 'google/gemma-7b', type: 'llm', downloaded: false }, - // { name: 'THUDM/chatglm3-6b', type: 'llm', downloaded: false }, - { name: 'bartowski/Llama-3.2-3B-Instruct-GGUF/Llama-3.2-3B-Instruct-Q4_K_S.gguf', type: 'ggufLLM', downloaded: false }, - { name: 'bartowski/Llama-3.2-3B-Instruct-GGUF/Llama-3.2-3B-Instruct-Q8_0.gguf', type: 'ggufLLM', downloaded: false }, - { name: 'bartowski/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q5_K_S.gguf', type: 'ggufLLM', downloaded: false }, - { name: 'HuggingFaceTB/SmolLM2-1.7B-Instruct-GGUF/smollm2-1.7b-instruct-q4_k_m.gguf', type: 'ggufLLM', downloaded: false }, + { name: 'Qwen/Qwen2-1.5B-Instruct', type: 'llm', downloaded: false }, + { name: 'microsoft/Phi-3-mini-4k-instruct', type: 'llm', downloaded: false }, + // { name: 'meta-llama/Meta-Llama-3.1-8B-Instruct', type: 'llm', downloaded: false }, + { name: 'mistralai/Mistral-7B-Instruct-v0.3', type: 'llm', downloaded: false }, + // { name: 'google/gemma-7b', type: 'llm', downloaded: false }, + // { name: 'THUDM/chatglm3-6b', type: 'llm', downloaded: false }, + { + name: 'bartowski/Llama-3.2-3B-Instruct-GGUF/Llama-3.2-3B-Instruct-Q4_K_S.gguf', + type: 'ggufLLM', + downloaded: false, + }, + { + name: 'bartowski/Llama-3.2-3B-Instruct-GGUF/Llama-3.2-3B-Instruct-Q8_0.gguf', + type: 'ggufLLM', + downloaded: false, + }, + { + name: 'bartowski/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q5_K_S.gguf', + type: 'ggufLLM', + downloaded: false, + }, + { + name: 'HuggingFaceTB/SmolLM2-1.7B-Instruct-GGUF/smollm2-1.7b-instruct-q4_k_m.gguf', + type: 'ggufLLM', + downloaded: false, + }, ] -export const userModels: Model[] = [ -] +export const userModels: Model[] = [] -export const useModels = defineStore("models", () => { - const hfToken = ref(undefined); - const models = ref(predefinedModels); - const llms = computed(() => models.value.filter(m => m.type === 'llm')); +export const useModels = defineStore( + 'models', + () => { + const hfToken = ref(undefined) + const models = ref(predefinedModels) + const llms = computed(() => models.value.filter((m) => m.type === 'llm')) - const downloadList = ref([]); - const ggufLLMs = computed(() => models.value.filter(m => m.type === 'ggufLLM')); + const downloadList = ref([]) + const ggufLLMs = computed(() => models.value.filter((m) => m.type === 'ggufLLM')) async function refreshModels() { - const sdModels = await window.electronAPI.getDownloadedDiffusionModels(); - const llmModels = await window.electronAPI.getDownloadedLLMs(); - const ggufModels = await window.electronAPI.getDownloadedGGUFLLMs(); - const loraModels = await window.electronAPI.getDownloadedLoras(); - const inpaintModels = await window.electronAPI.getDownloadedInpaintModels(); - const embeddingModels = await window.electronAPI.getDownloadedEmbeddingModels(); - - const downloadedModels = [ - ...sdModels.map(name => ({ name, type: 'stableDiffusion', downloaded: true })), - ...llmModels.map(name => ({ name, type: 'llm', downloaded: true })), - ...ggufModels.map(name => ({ name, type: 'ggufLLM', downloaded: true })), - ...loraModels.map(name => ({ name, type: 'lora', downloaded: true })), - ...inpaintModels.map(name => ({ name, type: 'inpaint', downloaded: true })), - ...embeddingModels.map(name => ({ name, type: 'embedding', downloaded: true })), - ]; + const sdModels = await window.electronAPI.getDownloadedDiffusionModels() + const llmModels = await window.electronAPI.getDownloadedLLMs() + const ggufModels = await window.electronAPI.getDownloadedGGUFLLMs() + const loraModels = await window.electronAPI.getDownloadedLoras() + const inpaintModels = await window.electronAPI.getDownloadedInpaintModels() + const embeddingModels = await window.electronAPI.getDownloadedEmbeddingModels() - const notYetDownloaded = (model: Model) => !downloadedModels.map(m => m.name).includes(model.name); + const downloadedModels = [ + ...sdModels.map((name) => ({ name, type: 'stableDiffusion', downloaded: true })), + ...llmModels.map((name) => ({ name, type: 'llm', downloaded: true })), + ...ggufModels.map((name) => ({ name, type: 'ggufLLM', downloaded: true })), + ...loraModels.map((name) => ({ name, type: 'lora', downloaded: true })), + ...inpaintModels.map((name) => ({ name, type: 'inpaint', downloaded: true })), + ...embeddingModels.map((name) => ({ name, type: 'embedding', downloaded: true })), + ] - models.value = [...downloadedModels, ...userModels.filter(notYetDownloaded), ...predefinedModels.filter(notYetDownloaded)]; + const notYetDownloaded = (model: Model) => + !downloadedModels.map((m) => m.name).includes(model.name) + models.value = [ + ...downloadedModels, + ...userModels.filter(notYetDownloaded), + ...predefinedModels.filter(notYetDownloaded), + ] } - async function download(models: DownloadModelParam[]) { - }; + async function download(_models: DownloadModelParam[]) {} refreshModels() return { - models, - llms, - ggufLLMs, - hfToken, - hfTokenIsValid: computed(() => hfToken.value?.startsWith('hf_')), - downloadList, - refreshModels, - download, + models, + llms, + ggufLLMs, + hfToken, + hfTokenIsValid: computed(() => hfToken.value?.startsWith('hf_')), + downloadList, + refreshModels, + download, } -}, { + }, + { persist: { - pick: ['hfToken'], - } -}); + pick: ['hfToken'], + }, + }, +) if (import.meta.hot) { - import.meta.hot.accept(acceptHMRUpdate(useModels, import.meta.hot)) + import.meta.hot.accept(acceptHMRUpdate(useModels, import.meta.hot)) } diff --git a/WebUI/src/assets/js/store/stableDiffusion.ts b/WebUI/src/assets/js/store/stableDiffusion.ts index ea22ee56..29e96937 100644 --- a/WebUI/src/assets/js/store/stableDiffusion.ts +++ b/WebUI/src/assets/js/store/stableDiffusion.ts @@ -1,188 +1,215 @@ -import { defineStore } from "pinia"; -import { useImageGeneration } from "./imageGeneration"; -import { useGlobalSetup } from "./globalSetup"; -import { Const } from "../const"; -import { useModels } from "./models"; -import { util } from "../util"; -import { SSEProcessor } from "../sseProcessor"; -import { useI18N } from "./i18n"; -import { toast } from "../toast"; +import { defineStore } from 'pinia' +import { useImageGeneration } from './imageGeneration' +import { useGlobalSetup } from './globalSetup' +import * as Const from '../const' +import { useModels } from './models' +import * as util from '../util' +import { SSEProcessor } from '../sseProcessor' +import { useI18N } from './i18n' +import * as toast from '../toast' type BackendParams = { - mode: number, - device: string, - prompt: string, - model_repo_id: string, - negative_prompt: string, - generate_number: number, - inference_steps: number, - guidance_scale: number, - seed: number, - height: number, - width: number, - lora: string, - scheduler: string, - image_preview: boolean, - safe_check: boolean + mode: number + device: string + prompt: string + model_repo_id: string + negative_prompt: string + generate_number: number + inference_steps: number + guidance_scale: number + seed: number + height: number + width: number + lora: string + scheduler: string + image_preview: boolean + safe_check: boolean } -export const useStableDiffusion = defineStore("stableDiffusion", () => { +export const useStableDiffusion = defineStore( + 'stableDiffusion', + () => { + const imageGeneration = useImageGeneration() + const globalSetup = useGlobalSetup() + const i18nState = useI18N().state + const models = useModels() - const imageGeneration = useImageGeneration(); - const globalSetup = useGlobalSetup(); - const i18nState = useI18N().state; - const models = useModels(); - - let abortContooler: AbortController | null; - const generateParams = ref(new Array()); + let abortContooler: AbortController | null + const generateParams = ref(new Array()) async function generate() { - if (imageGeneration.processing) { return; } - try { - imageGeneration.processing = true; - await checkModel(); - const defaultBackendParams = { - mode: 0, - device: globalSetup.modelSettings.graphics, - prompt: imageGeneration.prompt, - model_repo_id: `stableDiffusion:${imageGeneration.imageModel}`, - negative_prompt: imageGeneration.negativePrompt, - generate_number: imageGeneration.batchSize, - inference_steps: imageGeneration.inferenceSteps, - guidance_scale: imageGeneration.guidanceScale, - seed: imageGeneration.seed, - height: imageGeneration.height, - width: imageGeneration.width, - lora: imageGeneration.lora, - scheduler: imageGeneration.scheduler, - image_preview: imageGeneration.imagePreview, - safe_check: imageGeneration.safeCheck - }; - - await sendGenerate(defaultBackendParams); - } catch (ex) { - } finally { - imageGeneration.processing = false; + if (imageGeneration.processing) { + return + } + try { + imageGeneration.processing = true + await checkModel() + const defaultBackendParams = { + mode: 0, + device: globalSetup.modelSettings.graphics, + prompt: imageGeneration.prompt, + model_repo_id: `stableDiffusion:${imageGeneration.imageModel}`, + negative_prompt: imageGeneration.negativePrompt, + generate_number: imageGeneration.batchSize, + inference_steps: imageGeneration.inferenceSteps, + guidance_scale: imageGeneration.guidanceScale, + seed: imageGeneration.seed, + height: imageGeneration.height, + width: imageGeneration.width, + lora: imageGeneration.lora, + scheduler: imageGeneration.scheduler, + image_preview: imageGeneration.imagePreview, + safe_check: imageGeneration.safeCheck, } + + await sendGenerate(defaultBackendParams) + } catch (_error: unknown) { + } finally { + imageGeneration.processing = false + } } async function checkModel() { - 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"}) - } - if (globalSetup.modelSettings.imagePreview) { - checkList.push({ repo_id: "madebyollin/taesd", type: Const.MODEL_TYPE_PREVIEW , backend: "default"}) - checkList.push({ repo_id: "madebyollin/taesdxl", type: Const.MODEL_TYPE_PREVIEW , backend: "default"}) - } - const result = await globalSetup.checkModelAlreadyLoaded(checkList); - const downloadList: CheckModelAlreadyLoadedParameters[] = []; - for (const item of result) { - if (!item.already_loaded) { - downloadList.push({ repo_id: item.repo_id, type: item.type, backend: "default" }) - } - } - await models.download(downloadList); - resolve(); - }); + 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', + }) + } + if (globalSetup.modelSettings.imagePreview) { + checkList.push({ + repo_id: 'madebyollin/taesd', + type: Const.MODEL_TYPE_PREVIEW, + backend: 'default', + }) + checkList.push({ + repo_id: 'madebyollin/taesdxl', + type: Const.MODEL_TYPE_PREVIEW, + backend: 'default', + }) + } + const result = await globalSetup.checkModelAlreadyLoaded(checkList) + const downloadList: CheckModelAlreadyLoadedParameters[] = [] + for (const item of result) { + if (!item.already_loaded) { + downloadList.push({ repo_id: item.repo_id, type: item.type, backend: 'default' }) + } + } + await models.download(downloadList) + resolve() + }) } - function finishGenerate() { - imageGeneration.processing = false; + imageGeneration.processing = false } async function dataProcess(line: string) { - util.log(`SD data: ${line}`); - const dataJson = line.slice(5); - const data = JSON.parse(dataJson) as SDOutCallback; - switch (data.type) { - case "image_out": - imageGeneration.currentState = "image_out"; - if (!data.safe_check_pass) { - data.image = '/src/assets/image/nsfw_result_detected.png' - } - await imageGeneration.updateDestImage(data.index, data.image); - generateParams.value.push(data.params); - imageGeneration.generateIdx++; - break; - case "step_end": - imageGeneration.currentState = "generating"; - imageGeneration.stepText = `${i18nState.COM_GENERATING} ${data.step}/${data.total_step}`; - if (data.image) { - await imageGeneration.updateDestImage(data.index, data.image); - } - if (data.step == 0) { - imageGeneration.previewIdx = data.index; - } - break; - case "load_model": - imageGeneration.currentState = "load_model"; - break; - case "load_model_components": - imageGeneration.currentState = data.event == "finish" ? "generating" : "load_model_components"; - break; - case "error": - imageGeneration.processing = false; - imageGeneration.currentState = "error"; - switch (data.err_type) { - case "not_enough_disk_space": - toast.error(i18nState.ERR_NOT_ENOUGH_DISK_SPACE.replace("{requires_space}", data.requires_space).replace("{free_space}", data.free_space)); - break; - case "download_exception": - toast.error(i18nState.ERR_DOWNLOAD_FAILED); - break; - case "runtime_error": - toast.error(i18nState.ERROR_RUNTIME_ERROR); - break; - case "unknow_exception": - toast.error(i18nState.ERROR_GENERATE_UNKONW_EXCEPTION); - break; - } - break; - } + util.log(`SD data: ${line}`) + const dataJson = line.slice(5) + const data = JSON.parse(dataJson) as SDOutCallback + switch (data.type) { + case 'image_out': + imageGeneration.currentState = 'image_out' + if (!data.safe_check_pass) { + data.image = '/src/assets/image/nsfw_result_detected.png' + } + await imageGeneration.updateDestImage(data.index, data.image) + generateParams.value.push(data.params) + imageGeneration.generateIdx++ + break + case 'step_end': + imageGeneration.currentState = 'generating' + imageGeneration.stepText = `${i18nState.COM_GENERATING} ${data.step}/${data.total_step}` + if (data.image) { + await imageGeneration.updateDestImage(data.index, data.image) + } + if (data.step == 0) { + imageGeneration.previewIdx = data.index + } + break + case 'load_model': + imageGeneration.currentState = 'load_model' + break + case 'load_model_components': + imageGeneration.currentState = + data.event == 'finish' ? 'generating' : 'load_model_components' + break + case 'error': + imageGeneration.processing = false + imageGeneration.currentState = 'error' + switch (data.err_type) { + case 'not_enough_disk_space': + toast.error( + i18nState.ERR_NOT_ENOUGH_DISK_SPACE.replace( + '{requires_space}', + data.requires_space, + ).replace('{free_space}', data.free_space), + ) + break + case 'download_exception': + toast.error(i18nState.ERR_DOWNLOAD_FAILED) + break + case 'runtime_error': + toast.error(i18nState.ERROR_RUNTIME_ERROR) + break + case 'unknow_exception': + toast.error(i18nState.ERROR_GENERATE_UNKONW_EXCEPTION) + break + } + break + } } async function sendGenerate(defaultBackendParams: BackendParams) { - try { - imageGeneration.processing = true; - if (!abortContooler) { - abortContooler = new AbortController() - } - const response = await fetch(`${useGlobalSetup().apiHost}/api/sd/generate`, { - method: "POST", - body: util.convertToFormData(defaultBackendParams), - signal: abortContooler.signal - }) - const reader = response.body!.getReader(); - await new SSEProcessor(reader, dataProcess, finishGenerate).start(); - } finally { - imageGeneration.processing = false; + try { + imageGeneration.processing = true + if (!abortContooler) { + abortContooler = new AbortController() } + const response = await fetch(`${useGlobalSetup().apiHost}/api/sd/generate`, { + method: 'POST', + body: util.convertToFormData(defaultBackendParams), + signal: abortContooler.signal, + }) + const reader = response.body!.getReader() + await new SSEProcessor(reader, dataProcess, finishGenerate).start() + } finally { + imageGeneration.processing = false + } } async function stop() { - if (imageGeneration.processing && !imageGeneration.stopping) { - imageGeneration.stopping = true; - await fetch(`${globalSetup.apiHost}/api/sd/stopGenerate`); - if (abortContooler) { - abortContooler.abort(); - abortContooler = null; - } - imageGeneration.processing = false; - imageGeneration.stopping = false; + if (imageGeneration.processing && !imageGeneration.stopping) { + imageGeneration.stopping = true + await fetch(`${globalSetup.apiHost}/api/sd/stopGenerate`) + if (abortContooler) { + abortContooler.abort() + abortContooler = null } + imageGeneration.processing = false + imageGeneration.stopping = false + } } return { - generateParams, - generate, - stop, + generateParams, + generate, + stop, } - -}, { + }, + { persist: { - pick: ['settings', 'hdWarningDismissed'] - } -}); + pick: ['settings', 'hdWarningDismissed'], + }, + }, +) diff --git a/WebUI/src/assets/js/store/textInference.ts b/WebUI/src/assets/js/store/textInference.ts index 9afc2aba..d635869b 100644 --- a/WebUI/src/assets/js/store/textInference.ts +++ b/WebUI/src/assets/js/store/textInference.ts @@ -1,49 +1,53 @@ -import { acceptHMRUpdate, defineStore } from "pinia"; -import { useGlobalSetup } from "./globalSetup"; -import { z } from "zod"; -import { useBackendServices } from "./backendServices"; +import { acceptHMRUpdate, defineStore } from 'pinia' +import { useGlobalSetup } from './globalSetup' +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; +export const backendTypes = ['IPEX-LLM', 'LLAMA.CPP'] as const +const BackendSchema = z.enum(backendTypes) +export type Backend = z.infer const backendModelKey = { - 'IPEX-LLM': 'llm_model', - 'LLAMA.CPP': 'ggufLLM_model', + 'IPEX-LLM': 'llm_model', + 'LLAMA.CPP': 'ggufLLM_model', } -export const useTextInference = defineStore("textInference", () => { - - const globalSetup = useGlobalSetup(); - const backendServices = useBackendServices(); - const backend = ref('IPEX-LLM'); - const activeModel = ref(null); +export const useTextInference = defineStore( + 'textInference', + () => { + const globalSetup = useGlobalSetup() + const backendServices = useBackendServices() + const backend = ref('IPEX-LLM') + const activeModel = ref(null) const llamaBackendUrl = computed(() => { - const url = backendServices.info.find(item => item.serviceName === "llamacpp-backend")?.baseUrl; - console.log('url', url); - return url; - }); + const url = backendServices.info.find( + (item) => item.serviceName === 'llamacpp-backend', + )?.baseUrl + console.log('url', url) + return url + }) watch([llamaBackendUrl], () => { - console.log('llamaBackendUrl changed', llamaBackendUrl.value); - } - ); - + console.log('llamaBackendUrl changed', llamaBackendUrl.value) + }) + watch([activeModel], () => { - console.log('activeModel changed', activeModel.value); - globalSetup.applyModelSettings({ [backendModelKey[backend.value]]: activeModel.value }); - }); + console.log('activeModel changed', activeModel.value) + globalSetup.applyModelSettings({ [backendModelKey[backend.value]]: activeModel.value }) + }) return { - backend, - activeModel, - llamaBackendUrl, + backend, + activeModel, + llamaBackendUrl, } -}, { + }, + { persist: { - pick: ['backend', 'activeModel'], - } -}); + pick: ['backend', 'activeModel'], + }, + }, +) if (import.meta.hot) { - import.meta.hot.accept(acceptHMRUpdate(useTextInference, import.meta.hot)) -} \ No newline at end of file + import.meta.hot.accept(acceptHMRUpdate(useTextInference, import.meta.hot)) +} diff --git a/WebUI/src/assets/js/store/theme.ts b/WebUI/src/assets/js/store/theme.ts index 3dc201fd..39b0e551 100644 --- a/WebUI/src/assets/js/store/theme.ts +++ b/WebUI/src/assets/js/store/theme.ts @@ -1,24 +1,34 @@ -import { defineStore } from "pinia"; +import { defineStore } from 'pinia' -const knownThemes: Theme[] = ['dark', 'lnl', 'bmg']; -export const useTheme = defineStore("theme", () => { - - const selected = ref(null); - const availableThemes = ref([...knownThemes]); +const knownThemes: Theme[] = ['dark', 'lnl', 'bmg'] +export const useTheme = defineStore( + 'theme', + () => { + const selected = ref(null) + const availableThemes = ref([...knownThemes]) window.electronAPI.getThemeSettings().then((themeSettings) => { - const themesFromSettings = themeSettings.availableThemes.filter(t => knownThemes.includes(t)); - if (themesFromSettings.length > 0) availableThemes.value = themesFromSettings; - if (knownThemes.includes(themeSettings.currentTheme)) selected.value = themeSettings.currentTheme; - }); + const themesFromSettings = themeSettings.availableThemes.filter((t) => + knownThemes.includes(t), + ) + if (themesFromSettings.length > 0) availableThemes.value = themesFromSettings + if (knownThemes.includes(themeSettings.currentTheme)) + selected.value = themeSettings.currentTheme + }) return { - selected, - availableThemes, - active: computed(() => selected.value && availableThemes.value.includes(selected.value) ? selected.value : availableThemes.value[0]), + selected, + availableThemes, + active: computed(() => + selected.value && availableThemes.value.includes(selected.value) + ? selected.value + : availableThemes.value[0], + ), } -}, { + }, + { persist: { - pick: ['selected'], - } -}); + pick: ['selected'], + }, + }, +) diff --git a/WebUI/src/assets/js/toast.ts b/WebUI/src/assets/js/toast.ts index 9da0a83a..60f05835 100644 --- a/WebUI/src/assets/js/toast.ts +++ b/WebUI/src/assets/js/toast.ts @@ -1,241 +1,221 @@ function mergeOptions(initialObj: KVObject, customObj: KVObject) { - const newObj = initialObj ? Object.assign({}, initialObj) : {}; - for (const key in customObj) { - const customProp = customObj[key]; - if (!customProp) { - continue; - } - if (typeof customProp === "object") { - newObj[key] = mergeOptions(newObj[key], customProp); - } else { - newObj[key] = customProp; - } + const newObj = initialObj ? Object.assign({}, initialObj) : {} + for (const key in customObj) { + const customProp = customObj[key] + if (!customProp) { + continue } - return newObj; - } - - /** - * Stylize the Toast. - */ - function stylize(element: HTMLElement, styles: KVObject) { - Object.keys(styles).forEach((key) => { - element.style.setProperty(key, styles[key]); - }); - } - - export module toast { - const TOAST_ANIMATION_SPEED = 400; - - const DEFAULT_TRANSITIONS: ToastTransitions = { - show: { - transition: `opacity 0.5s`, - opacity: "1", - }, - - hide: { - opacity: "0", - "-webkit-transform": "translateY(150%) translateZ(0)", - transform: "translateY(150%) translateZ(0)", - transition: `all ${TOAST_ANIMATION_SPEED}ms ease-in`, - }, - }; - - /** - * The default Toast settings - * @type {Object} - */ - const DEFAULT_SETTINGS: ToastOptions = { - mounted: "body", - style: { - main: { - position: "fixed", - left: "0px", - top: "0px", - width: "100%", - height: "100%", - "text-align": "center", - "pointer-events": "none", - opacity: "0", - "z-index": "99999", - display: "flex", - "align-items": "center", - "justify-content": "center", - }, - content: { - background: "#1677ff", - "box-shadow": "0 0 10px rgba(0, 0, 0, .8)", - "border-radius": "3px", - "z-index": "99999", - color: "rgba(255, 255, 255, .9)", - "font-size": "14px", - padding: "10px 15px", - "max-width": "60%", - "word-break": "keep-all", - margin: "0px auto", - "text-align": "center", - display: "flex", - "pointer-events": "none", - }, - }, - - settings: { - duration: 4000, - }, - }; - - /** - * The toastStage. This is the HTML element in which the toast resides - * Getter and setter methods are available privately - */ - let _toastStage: HTMLElement; - - /** - * The Timeout object for animations. - * This should be shared among the Toasts, because timeouts may be cancelled e.g. on explicit call of hide() - */ - let _timeout: number; - - /** - * The main Toast object - * @param text The text to put inside the Toast - * @param options Optional; the Toast options. See Toast.prototype.DEFAULT_SETTINGS for more information - * @param transitions Optional; the Transitions object. This should not be used unless you know what you're doing - */ - export function show( - text: string, - options?: ToastOptions, - transitions?: ToastTransitions - ) { - if (_toastStage != null) { - // If there is already a Toast being shown, put this Toast in the queue to show later - // toastQueue.push({ text, options, transitions: _transitions }); - if (_timeout) { - window.clearTimeout(_timeout); - } - destroyToast(); - } - const _options = mergeOptions( - DEFAULT_SETTINGS, - options || {} - ) as ToastOptions; - - showToast(text, _options, transitions || DEFAULT_TRANSITIONS); - - return { - hide: () => hideToast(transitions || DEFAULT_TRANSITIONS), - }; - } - - export function success(text: string, settings?: ToastSettings) { - show(text, { - style: { - content: { background: "#22c55e", color: "#ffffff" }, - }, - settings: settings, - }); - } - - export function error(text: string, settings?: ToastSettings) { - show(text, { - style: { - content: { background: "#ef4444", color: "#ffffff" }, - }, - settings: settings, - }); - } - - export function warning( - text: string, - settings?: ToastSettings, - mounted?: HTMLElement | string - ) { - show(text, { - mounted, - style: { - content: { background: "#f97316", color: "#ffffff" }, - }, - settings: settings, - }); - } - - /** - * Show the Toast - * @param text The text to show inside the Toast - * @param options The object containing the options for the Toast - */ - function showToast( - text: string, - options: ToastOptions, - transitions: ToastTransitions - ) { - _toastStage = generateToast(text, options.style); - - const parentEL = - options.mounted instanceof HTMLElement - ? options.mounted - : typeof options.mounted == "string" - ? document.querySelector(options.mounted) - : null; - if (!parentEL) { - throw new Error("options.mounted is undefine"); - } - 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. - _toastStage.offsetHeight; - - stylize(_toastStage, transitions.show); - - // Hide the Toast after the specified time - // clearTimeout(timeout); - if (options.settings && options.settings.duration !== 0) { - _timeout = window.setTimeout( - () => hideToast(transitions), - options.settings.duration - ); - } + if (typeof customProp === 'object') { + newObj[key] = mergeOptions(newObj[key], customProp) + } else { + newObj[key] = customProp } - - /** - * Hide the Toast that's currently shown. - */ - function hideToast(transitions: ToastTransitions) { - stylize(_toastStage, transitions.hide); - - // Destroy the Toast element after animations end. - clearTimeout(_timeout); - _timeout = 0; - _toastStage.addEventListener("transitionend", destroyToast, { once: true }); + } + return newObj +} + +/** + * Stylize the Toast. + */ +function stylize(element: HTMLElement, styles: KVObject) { + Object.keys(styles).forEach((key) => { + element.style.setProperty(key, styles[key]) + }) +} + +const TOAST_ANIMATION_SPEED = 400 + +const DEFAULT_TRANSITIONS: ToastTransitions = { + show: { + transition: `opacity 0.5s`, + opacity: '1', + }, + + hide: { + opacity: '0', + '-webkit-transform': 'translateY(150%) translateZ(0)', + transform: 'translateY(150%) translateZ(0)', + transition: `all ${TOAST_ANIMATION_SPEED}ms ease-in`, + }, +} + +/** + * The default Toast settings + * @type {Object} + */ +const DEFAULT_SETTINGS: ToastOptions = { + mounted: 'body', + style: { + main: { + position: 'fixed', + left: '0px', + top: '0px', + width: '100%', + height: '100%', + 'text-align': 'center', + 'pointer-events': 'none', + opacity: '0', + 'z-index': '99999', + display: 'flex', + 'align-items': 'center', + 'justify-content': 'center', + }, + content: { + background: '#1677ff', + 'box-shadow': '0 0 10px rgba(0, 0, 0, .8)', + 'border-radius': '3px', + 'z-index': '99999', + color: 'rgba(255, 255, 255, .9)', + 'font-size': '14px', + padding: '10px 15px', + 'max-width': '60%', + 'word-break': 'keep-all', + margin: '0px auto', + 'text-align': 'center', + display: 'flex', + 'pointer-events': 'none', + }, + }, + + settings: { + duration: 4000, + }, +} + +/** + * The toastStage. This is the HTML element in which the toast resides + * Getter and setter methods are available privately + */ +let _toastStage: HTMLElement + +/** + * The Timeout object for animations. + * This should be shared among the Toasts, because timeouts may be cancelled e.g. on explicit call of hide() + */ +let _timeout: number + +/** + * The main Toast object + * @param text The text to put inside the Toast + * @param options Optional; the Toast options. See Toast.prototype.DEFAULT_SETTINGS for more information + * @param transitions Optional; the Transitions object. This should not be used unless you know what you're doing + */ +export function show(text: string, options?: ToastOptions, transitions?: ToastTransitions) { + if (_toastStage != null) { + // If there is already a Toast being shown, put this Toast in the queue to show later + // toastQueue.push({ text, options, transitions: _transitions }); + if (_timeout) { + window.clearTimeout(_timeout) } - - /** - * Generate the Toast with the specified text. - * @param text The text to show inside the Toast, can be an HTML element or plain text - * @param style The style to set for the Toast - */ - function generateToast(text: string, style?: ToastStyle) { - const content = document.createElement("div"); - const toastStage = document.createElement("div"); - const textNode = document.createTextNode(text); - content.appendChild(textNode); - toastStage.appendChild(content); - toastStage.style.pointerEvents; - if (style) { - if (style.content) { - stylize(content, style.content); - } - if (style.main) { - stylize(toastStage, style.main); - } - } - return toastStage; + destroyToast() + } + const _options = mergeOptions(DEFAULT_SETTINGS, options || {}) as ToastOptions + + showToast(text, _options, transitions || DEFAULT_TRANSITIONS) + + return { + hide: () => hideToast(transitions || DEFAULT_TRANSITIONS), + } +} + +export function success(text: string, settings?: ToastSettings) { + show(text, { + style: { + content: { background: '#22c55e', color: '#ffffff' }, + }, + settings: settings, + }) +} + +export function error(text: string, settings?: ToastSettings) { + show(text, { + style: { + content: { background: '#ef4444', color: '#ffffff' }, + }, + settings: settings, + }) +} + +export function warning(text: string, settings?: ToastSettings, mounted?: HTMLElement | string) { + show(text, { + mounted, + style: { + content: { background: '#f97316', color: '#ffffff' }, + }, + settings: settings, + }) +} + +/** + * Show the Toast + * @param text The text to show inside the Toast + * @param options The object containing the options for the Toast + */ +function showToast(text: string, options: ToastOptions, transitions: ToastTransitions) { + _toastStage = generateToast(text, options.style) + + const parentEL = + options.mounted instanceof HTMLElement + ? options.mounted + : typeof options.mounted == 'string' + ? document.querySelector(options.mounted) + : null + if (!parentEL) { + throw new Error('options.mounted is undefine') + } + 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) + + // Hide the Toast after the specified time + // clearTimeout(timeout); + if (options.settings && options.settings.duration !== 0) { + _timeout = window.setTimeout(() => hideToast(transitions), options.settings.duration) + } +} + +/** + * Hide the Toast that's currently shown. + */ +function hideToast(transitions: ToastTransitions) { + stylize(_toastStage, transitions.hide) + + // Destroy the Toast element after animations end. + clearTimeout(_timeout) + _timeout = 0 + _toastStage.addEventListener('transitionend', destroyToast, { once: true }) +} + +/** + * Generate the Toast with the specified text. + * @param text The text to show inside the Toast, can be an HTML element or plain text + * @param style The style to set for the Toast + */ +function generateToast(text: string, style?: ToastStyle) { + const content = document.createElement('div') + const toastStage = document.createElement('div') + const textNode = document.createTextNode(text) + content.appendChild(textNode) + toastStage.appendChild(content) + if (style) { + if (style.content) { + stylize(content, style.content) } - - /** - * Clean up after the Toast slides away. Namely, removing the Toast from the DOM. - * After the Toast is cleaned up, display the next Toast in the queue if any exists. - */ - function destroyToast() { - _toastStage.remove(); + if (style.main) { + stylize(toastStage, style.main) } - } \ No newline at end of file + } + return toastStage +} + +/** + * Clean up after the Toast slides away. Namely, removing the Toast from the DOM. + * After the Toast is cleaned up, display the next Toast in the queue if any exists. + */ +function destroyToast() { + _toastStage.remove() +} diff --git a/WebUI/src/assets/js/util.ts b/WebUI/src/assets/js/util.ts index bf67c0b1..ce0fb2d6 100644 --- a/WebUI/src/assets/js/util.ts +++ b/WebUI/src/assets/js/util.ts @@ -1,162 +1,153 @@ +export async function copyImage(url: string) { + const response = await fetch(url) + const blob = await response.blob() + navigator.clipboard.write([ + new ClipboardItem({ + 'image/png': blob, + }), + ]) +} +/** + * Copy text to clipboard + * @param text + */ +export async function copyText(text: string) { + const textbox = document.createElement('textarea') + textbox.value = text + textbox.style.opacity = '0' + textbox.style.position = 'absolute' + textbox.style.left = '-9999px' + textbox.style.top = '-9999px' + document.body.append(textbox) + textbox.select() + try { + document.execCommand('copy') + } finally { + textbox.remove() + } +} -export module util { - export async function copyImage(url: string) { - const response = await fetch(url); - const blob = await response.blob(); - navigator.clipboard.write([ - new ClipboardItem({ - "image/png": blob, - }), - ]); - } - - /** - * Copy text to clipboard - * @param text - */ - export async function copyText(text: string) { - const textbox = document.createElement("textarea"); - textbox.value = text; - textbox.style.opacity = "0"; - textbox.style.position = "absolute"; - textbox.style.left = "-9999px"; - textbox.style.top = "-9999px"; - document.body.append(textbox); - textbox.select(); - try { - document.execCommand("copy"); - } finally { - textbox.remove(); - } - - } - - /** - * Execute after X ms delay - * @param ms millisecond - * @returns - */ - export function delay(ms: number) { - return new Promise((resolve) => { - const t = setTimeout(() => { - clearTimeout(t); - resolve && resolve(); - }, ms); - }); - } - /** - * 日期格式化 - * @param date 日期 - * @param format 格式化字符串 y-年 M-月 d-日 h-时 m-分 s-秒 f-毫秒 - */ - export function dateFormat(date: Date, format: string) { - const dateVals: Record = { - 'M': date.getMonth() + 1, - 'd': date.getDate(), - 'h': date.getHours(), - 'm': date.getMinutes(), - 's': date.getSeconds(), - 'f': date.getMilliseconds() - }; - const result = format.replace( - /(M{1,2}|d{1,2}|h{1,2}|m{1,2}|s{1,2})|f{1,3}/g, - function (v) { - const val = dateVals[v.substring(0, 1)]; - return val.toString().padStart(v.length, "0"); - } - ); - return result.replace(/(y+)/g, function (v) { - const year = date.getFullYear().toString(); - return year.substring(year.length - v.length); - }); - } - /** - * HTML escape of line breaks and Spaces in text - * @param content - */ - function escapeWhiteSpace(content: string) { - return content.replace(/\n/g, "
"); - } +/** + * Execute after X ms delay + * @param ms millisecond + * @returns + */ +export function delay(ms: number) { + return new Promise((resolve) => { + const t = setTimeout(() => { + clearTimeout(t) + resolve() + }, ms) + }) +} +/** + * 日期格式化 + * @param date 日期 + * @param format 格式化字符串 y-年 M-月 d-日 h-时 m-分 s-秒 f-毫秒 + */ +export function dateFormat(date: Date, format: string) { + const dateVals: Record = { + M: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + m: date.getMinutes(), + s: date.getSeconds(), + f: date.getMilliseconds(), + } + const result = format.replace(/(M{1,2}|d{1,2}|h{1,2}|m{1,2}|s{1,2})|f{1,3}/g, function (v) { + const val = dateVals[v.substring(0, 1)] + return val.toString().padStart(v.length, '0') + }) + return result.replace(/(y+)/g, function (v) { + const year = date.getFullYear().toString() + return year.substring(year.length - v.length) + }) +} +/** + * HTML escape of line breaks and Spaces in text + * @param content + */ +function escapeWhiteSpace(content: string) { + return content.replace(/\n/g, '
') +} - /** - * Handling HTML tags - * @param content - */ - export function processHTMLTag(content: string) { - return escapeWhiteSpace(content.replace(/<[^>]+>/g, "")); - } +/** + * Handling HTML tags + * @param content + */ +export function processHTMLTag(content: string) { + return escapeWhiteSpace(content.replace(/<[^>]+>/g, '')) +} - const htmlEscape = new Map( - Object.entries({ - "<": "<", - ">": ">", - "&": "&", - '"': """, - "©": "©", - "®": "®", - " ": " ", - "\n": "
", - }) - ); +const htmlEscape = new Map( + Object.entries({ + '<': '<', + '>': '>', + '&': '&', + '"': '"', + '©': '©', + '®': '®', + ' ': ' ', + '\n': '
', + }), +) - const arrEntities = new Map( - Object.entries({ - "<": "<", - ">": ">", - " ": " ", - "&": "&", - """: '"', - }) - ); +const arrEntities = new Map( + Object.entries({ + '<': '<', + '>': '>', + ' ': ' ', + '&': '&', + '"': '"', + }), +) - export function html2Escape(sHtml: string) { - return sHtml.replace(/[<>&" ©®]/g, function (c) { - return htmlEscape.get(c) || ""; - }); - } +export function html2Escape(sHtml: string) { + return sHtml.replace(/[<>&" ©®]/g, function (c) { + return htmlEscape.get(c) || '' + }) +} - export function escape2Html(str: string) { - return str.replace(/&(lt|gt|nbsp|amp|quot|copy|reg);/gi, function (c) { - return arrEntities.get(c) || ""; - }); - } +export function escape2Html(str: string) { + return str.replace(/&(lt|gt|nbsp|amp|quot|copy|reg);/gi, function (c) { + return arrEntities.get(c) || '' + }) +} - export function log(message: string) { - console.log(`[${util.dateFormat(new Date(), "hh:mm:ss:fff")}] ${message}`); - } +export function log(message: string) { + console.log(`[${dateFormat(new Date(), 'hh:mm:ss:fff')}] ${message}`) +} - export function convertToFormData(data: any) { - const formData = new FormData(); - for (const key in data) { - const val = data[key]; - if (val == null) { - continue; - } - if (typeof val == "boolean") { - formData.append(key, val ? "1" : "0"); - } else if (val as Blob) { - formData.append(key, val); - } else { - formData.append(key, val.toString()); - } - } - return formData; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function convertToFormData(data: any) { + const formData = new FormData() + for (const key in data) { + const val = data[key] + if (val == null) { + continue } - - export function getDomOffset(target: HTMLElement) { - const offset = { left: 0, top: 0 }; - let enumElement: HTMLElement | null = target; - while (enumElement && enumElement != document.body) { - offset.left += enumElement.offsetLeft; - offset.top == enumElement.offsetTop; - enumElement = enumElement.parentElement; - } - return offset; + if (typeof val == 'boolean') { + formData.append(key, val ? '1' : '0') + } else if (val as Blob) { + formData.append(key, val) + } else { + formData.append(key, val.toString()) } + } + return formData +} - export function toFixed(num: number, fractionDigits: number) { - return parseFloat(num.toFixed(fractionDigits)); - } +export function getDomOffset(target: HTMLElement) { + const offset = { left: 0, top: 0 } + let enumElement: HTMLElement | null = target + while (enumElement && enumElement != document.body) { + offset.left += enumElement.offsetLeft + enumElement = enumElement.parentElement + } + return offset +} +export function toFixed(num: number, fractionDigits: number) { + return parseFloat(num.toFixed(fractionDigits)) } diff --git a/WebUI/src/auto-import.d.ts b/WebUI/src/auto-import.d.ts index acb72642..bb541615 100644 --- a/WebUI/src/auto-import.d.ts +++ b/WebUI/src/auto-import.d.ts @@ -6,66 +6,81 @@ // biome-ignore lint: disable export {} declare global { - const EffectScope: typeof import('vue')['EffectScope'] - const computed: typeof import('vue')['computed'] - const createApp: typeof import('vue')['createApp'] - const customRef: typeof import('vue')['customRef'] - const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] - const defineComponent: typeof import('vue')['defineComponent'] - const effectScope: typeof import('vue')['effectScope'] - const getCurrentInstance: typeof import('vue')['getCurrentInstance'] - const getCurrentScope: typeof import('vue')['getCurrentScope'] - const h: typeof import('vue')['h'] - const inject: typeof import('vue')['inject'] - const isProxy: typeof import('vue')['isProxy'] - const isReactive: typeof import('vue')['isReactive'] - const isReadonly: typeof import('vue')['isReadonly'] - const isRef: typeof import('vue')['isRef'] - const markRaw: typeof import('vue')['markRaw'] - const nextTick: typeof import('vue')['nextTick'] - const onActivated: typeof import('vue')['onActivated'] - const onBeforeMount: typeof import('vue')['onBeforeMount'] - const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] - const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] - const onDeactivated: typeof import('vue')['onDeactivated'] - const onErrorCaptured: typeof import('vue')['onErrorCaptured'] - const onMounted: typeof import('vue')['onMounted'] - const onRenderTracked: typeof import('vue')['onRenderTracked'] - const onRenderTriggered: typeof import('vue')['onRenderTriggered'] - const onScopeDispose: typeof import('vue')['onScopeDispose'] - const onServerPrefetch: typeof import('vue')['onServerPrefetch'] - const onUnmounted: typeof import('vue')['onUnmounted'] - const onUpdated: typeof import('vue')['onUpdated'] - const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] - const provide: typeof import('vue')['provide'] - const reactive: typeof import('vue')['reactive'] - const readonly: typeof import('vue')['readonly'] - const ref: typeof import('vue')['ref'] - const resolveComponent: typeof import('vue')['resolveComponent'] - const shallowReactive: typeof import('vue')['shallowReactive'] - const shallowReadonly: typeof import('vue')['shallowReadonly'] - const shallowRef: typeof import('vue')['shallowRef'] - const toRaw: typeof import('vue')['toRaw'] - const toRef: typeof import('vue')['toRef'] - const toRefs: typeof import('vue')['toRefs'] - const toValue: typeof import('vue')['toValue'] - const triggerRef: typeof import('vue')['triggerRef'] - const unref: typeof import('vue')['unref'] - const useAttrs: typeof import('vue')['useAttrs'] - const useCssModule: typeof import('vue')['useCssModule'] - const useCssVars: typeof import('vue')['useCssVars'] - const useId: typeof import('vue')['useId'] - const useModel: typeof import('vue')['useModel'] - const useSlots: typeof import('vue')['useSlots'] - const useTemplateRef: typeof import('vue')['useTemplateRef'] - const watch: typeof import('vue')['watch'] - const watchEffect: typeof import('vue')['watchEffect'] - const watchPostEffect: typeof import('vue')['watchPostEffect'] - const watchSyncEffect: typeof import('vue')['watchSyncEffect'] + const EffectScope: (typeof import('vue'))['EffectScope'] + const computed: (typeof import('vue'))['computed'] + const createApp: (typeof import('vue'))['createApp'] + const customRef: (typeof import('vue'))['customRef'] + const defineAsyncComponent: (typeof import('vue'))['defineAsyncComponent'] + const defineComponent: (typeof import('vue'))['defineComponent'] + const effectScope: (typeof import('vue'))['effectScope'] + const getCurrentInstance: (typeof import('vue'))['getCurrentInstance'] + const getCurrentScope: (typeof import('vue'))['getCurrentScope'] + const h: (typeof import('vue'))['h'] + const inject: (typeof import('vue'))['inject'] + const isProxy: (typeof import('vue'))['isProxy'] + const isReactive: (typeof import('vue'))['isReactive'] + const isReadonly: (typeof import('vue'))['isReadonly'] + const isRef: (typeof import('vue'))['isRef'] + const markRaw: (typeof import('vue'))['markRaw'] + const nextTick: (typeof import('vue'))['nextTick'] + const onActivated: (typeof import('vue'))['onActivated'] + const onBeforeMount: (typeof import('vue'))['onBeforeMount'] + const onBeforeUnmount: (typeof import('vue'))['onBeforeUnmount'] + const onBeforeUpdate: (typeof import('vue'))['onBeforeUpdate'] + const onDeactivated: (typeof import('vue'))['onDeactivated'] + const onErrorCaptured: (typeof import('vue'))['onErrorCaptured'] + const onMounted: (typeof import('vue'))['onMounted'] + const onRenderTracked: (typeof import('vue'))['onRenderTracked'] + const onRenderTriggered: (typeof import('vue'))['onRenderTriggered'] + const onScopeDispose: (typeof import('vue'))['onScopeDispose'] + const onServerPrefetch: (typeof import('vue'))['onServerPrefetch'] + const onUnmounted: (typeof import('vue'))['onUnmounted'] + const onUpdated: (typeof import('vue'))['onUpdated'] + const onWatcherCleanup: (typeof import('vue'))['onWatcherCleanup'] + const provide: (typeof import('vue'))['provide'] + const reactive: (typeof import('vue'))['reactive'] + const readonly: (typeof import('vue'))['readonly'] + const ref: (typeof import('vue'))['ref'] + const resolveComponent: (typeof import('vue'))['resolveComponent'] + const shallowReactive: (typeof import('vue'))['shallowReactive'] + const shallowReadonly: (typeof import('vue'))['shallowReadonly'] + const shallowRef: (typeof import('vue'))['shallowRef'] + const toRaw: (typeof import('vue'))['toRaw'] + const toRef: (typeof import('vue'))['toRef'] + const toRefs: (typeof import('vue'))['toRefs'] + const toValue: (typeof import('vue'))['toValue'] + const triggerRef: (typeof import('vue'))['triggerRef'] + const unref: (typeof import('vue'))['unref'] + const useAttrs: (typeof import('vue'))['useAttrs'] + const useCssModule: (typeof import('vue'))['useCssModule'] + const useCssVars: (typeof import('vue'))['useCssVars'] + const useId: (typeof import('vue'))['useId'] + const useModel: (typeof import('vue'))['useModel'] + const useSlots: (typeof import('vue'))['useSlots'] + const useTemplateRef: (typeof import('vue'))['useTemplateRef'] + const watch: (typeof import('vue'))['watch'] + const watchEffect: (typeof import('vue'))['watchEffect'] + const watchPostEffect: (typeof import('vue'))['watchPostEffect'] + const watchSyncEffect: (typeof import('vue'))['watchSyncEffect'] } // for type re-export declare global { // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' + export type { + Component, + ComponentPublicInstance, + ComputedRef, + DirectiveBinding, + ExtractDefaultPropTypes, + ExtractPropTypes, + ExtractPublicPropTypes, + InjectionKey, + PropType, + Ref, + MaybeRef, + MaybeRefOrGetter, + VNode, + WritableComputedRef, + } from 'vue' import('vue') } diff --git a/WebUI/src/components/AddLLMDialog.vue b/WebUI/src/components/AddLLMDialog.vue index 0bbe74d9..3ca24e86 100644 --- a/WebUI/src/components/AddLLMDialog.vue +++ b/WebUI/src/components/AddLLMDialog.vue @@ -1,28 +1,46 @@ \ No newline at end of file + diff --git a/WebUI/src/components/LanguageSelector.vue b/WebUI/src/components/LanguageSelector.vue index d414249d..a4377fe0 100644 --- a/WebUI/src/components/LanguageSelector.vue +++ b/WebUI/src/components/LanguageSelector.vue @@ -16,9 +16,7 @@ diff --git a/WebUI/src/components/LoadingBar.vue b/WebUI/src/components/LoadingBar.vue index fe4fc599..3719812f 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 0a6fcde1..7e21e4f5 100644 --- a/WebUI/src/components/ModelDropDownItem.vue +++ b/WebUI/src/components/ModelDropDownItem.vue @@ -1,25 +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 9f029573..b91409f9 100644 --- a/WebUI/src/components/OutpaintOptions.vue +++ b/WebUI/src/components/OutpaintOptions.vue @@ -1,58 +1,72 @@ \ No newline at end of file + diff --git a/WebUI/src/components/PaintInfo.vue b/WebUI/src/components/PaintInfo.vue index 38f4c9ec..227f693b 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 d0cf77fc..75ba5d57 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 de30d32c..5ee885f7 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 ee51d876..6779c9f3 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 8a9bcb2c..c9a44c72 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 29981d81..2ff58671 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 b39e7123..63d23e58 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,73 +115,76 @@
\ No newline at end of file + diff --git a/WebUI/src/components/SettingsImageComfyDynamic.vue b/WebUI/src/components/SettingsImageComfyDynamic.vue index b6ef7533..3636f6b0 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 d3857840..c3ad8a1e 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 be36ce03..88415955 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 f723587e..f1640e05 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,120 +322,122 @@ \ No newline at end of file + diff --git a/WebUI/src/components/SlideBar.vue b/WebUI/src/components/SlideBar.vue index ac1c1086..1bb7d5da 100644 --- a/WebUI/src/components/SlideBar.vue +++ b/WebUI/src/components/SlideBar.vue @@ -1,153 +1,167 @@ \ No newline at end of file + + + diff --git a/WebUI/src/components/TextToImage.vue b/WebUI/src/components/TextToImage.vue index 923538b4..cb2dd1f5 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 144937d6..806ac3a0 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 b3c8b257..567be771 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 7cfe91a9..60446cc9 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 9f30898b..92acb773 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 a16a1ef9..31ea6274 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 02b72c29..65d43504 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 f2d0b64a..a3e2e956 100644 --- a/WebUI/src/components/ui/slider/ResolutionPicker.vue +++ b/WebUI/src/components/ui/slider/ResolutionPicker.vue @@ -1,5 +1,5 @@