complete refactor javascript to typescript and reorg frontend files and folders
Signed-off-by: Vladimir Mandic <mandic00@live.com>
@@ -11,7 +11,7 @@ General app structure is:
|
||||
This file contains general guidelines for contributing to the SD.Next codebase, including conventions, tools, and project structure. For more specific guidance on working with particular areas of the codebase, please refer to the instructions files linked below:
|
||||
- [Core Runtime Guidelines](instructions/core.instructions.md): Use when editing Python core runtime code, startup flow, model loading, API internals, backend/device logic, or shared state in modules and pipelines.
|
||||
- [UI And Frontend Guidelines](instructions/ui.instructions.md): Use when editing frontend UI code, JavaScript, HTML, CSS, localization files, or built-in UI extensions including modernui and kanvas.
|
||||
- [Hint Typography Guidelines](instructions/hints.instructions.md): Use when editing hint text or other UI strings in localization JSON files (`html/locale_*.json`, `html/override_*.json`).
|
||||
- [Hint Typography Guidelines](instructions/hints.instructions.md): Use when editing hint text or other UI strings in localization JSON files (`ui/locale/locale_*.json`, `ui/locale/override_*.json`).
|
||||
|
||||
## Agent Guidelines
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
description: "Use when editing hint text or other UI strings in localization JSON files; follow the ordered rules below for consistent formatting."
|
||||
name: "Hint Typography Guidelines"
|
||||
applyTo: "html/locale_*.json, html/override_*.json"
|
||||
applyTo: "ui/locale/*.json"
|
||||
---
|
||||
# Hint Typography Guidelines
|
||||
|
||||
@@ -40,11 +40,11 @@ Hint strings render as HTML. Use this small set of inline tags to keep hints sca
|
||||
|
||||
## Translation propagation
|
||||
|
||||
1. `html/locale_en.json` is the source of truth. Other `html/locale_*.json` files are auto-generated by `cli/localize.js`; edit only the English file.
|
||||
2. Per-locale corrections live in `html/override_{locale}.json`.
|
||||
1. `ui/locale/locale_en.json` is the source of truth. Other `ui/locale/locale_*.json` files are auto-generated by `cli/localize.js`; edit only the English file.
|
||||
2. Per-locale corrections live in `ui/locale/override_{locale}.json`.
|
||||
|
||||
## Validation
|
||||
|
||||
1. Validate JSON syntax with `jq empty html/locale_en.json`.
|
||||
2. Lint with `pnpm eslint -- html/locale_en.json` (silent success).
|
||||
1. Validate JSON syntax with `jq empty ui/locale/locale_en.json`.
|
||||
2. Lint with `pnpm eslint -- ui/locale/locale_en.json` (silent success).
|
||||
3. See `wiki/Hints.md` for the wiki-facing version of these rules.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
description: "Use when editing frontend UI code, JavaScript, HTML, CSS, localization files, or built-in UI extensions including modernui and kanvas."
|
||||
description: "Use when editing frontend UI code, TypesScript, JavaScript, HTML, CSS, localization files, or built-in UI extensions including modernui and kanvas."
|
||||
name: "UI And Frontend Guidelines"
|
||||
applyTo: "javascript/**/*.js, html/**/*.html, html/**/*.css, html/**/*.js, extensions-builtin/sdnext-modernui/**/*, extensions-builtin/sdnext-kanvas/**/*"
|
||||
applyTo: "ui/**/*, extensions-builtin/sdnext-modernui/**/*, extensions-builtin/sdnext-kanvas/**/*"
|
||||
---
|
||||
# UI And Frontend Guidelines
|
||||
|
||||
@@ -9,8 +9,8 @@ Apply these rules in priority order:
|
||||
|
||||
1. Preserve the current event-handling logic and data flow between Gradio/Python endpoints and frontend handlers; do not change payload shapes without backend alignment.
|
||||
2. Follow existing project lint and style patterns; prefer consistency with nearby files over introducing new frameworks or architecture.
|
||||
3. Keep localization-friendly UI text changes synchronized with locale resources in `html/locale_*.json` when user-facing strings are added or changed.
|
||||
3. Keep localization-friendly UI text changes synchronized with locale resources in `ui/locale/locale_*.json` when user-facing strings are added or changed.
|
||||
4. Avoid bundling unrelated visual refactors with functional fixes; keep UI PRs scoped and reviewable.
|
||||
5. For extension UI work, respect each extension's boundaries and avoid cross-extension coupling.
|
||||
6. Validate JavaScript changes with `pnpm eslint`; for modern UI extension changes also run `pnpm eslint-ui`.
|
||||
6. Validate TypeScript and JavaScript changes with `pnpm eslint` and `pnpm tsc`.
|
||||
7. Maintain mobile compatibility when touching layout or interaction behavior.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
{
|
||||
"files.eol": "\n",
|
||||
"python.analysis.extraPaths": [".", "./modules", "./scripts", "./pipelines"],
|
||||
"python.analysis.typeCheckingMode": "off",
|
||||
"editor.formatOnSave": false,
|
||||
"python.REPL.enableREPLSmartSend": false,
|
||||
"eslint.enable": true,
|
||||
@@ -13,6 +11,13 @@
|
||||
"json",
|
||||
"markdown"
|
||||
],
|
||||
"search.exclude": {
|
||||
"**/__pycache__": true,
|
||||
"**/node_modules": true,
|
||||
"**/venv": true,
|
||||
"**/*.mjs": true,
|
||||
"**/*.map": true
|
||||
},
|
||||
"githubPullRequests.ignoredPullRequestBranches": [
|
||||
"master"
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Change Log for SD.Next
|
||||
|
||||
## Update for 2026-05-19
|
||||
## Update for 2026-05-20
|
||||
|
||||
- **Models**
|
||||
- [CircleStone Anima 1.0](https://huggingface.co/circlestone-labs/Anima) in *Base* and *Turbo* (distilled) variants
|
||||
@@ -32,7 +32,10 @@
|
||||
- Automated fixes using `/check-` skills
|
||||
- Automated syntax, spelling and readability improvements to `/wiki` pages
|
||||
- **Internal**
|
||||
- complete refactor of `modernui` javascript codebase to typescript!
|
||||
- complete refactor of `core` JavaScript codebase to TypeScript!
|
||||
- complete refactor of `modernui` JavaScript codebase to TypeScript!
|
||||
- remove of `/html` and `/javascript` folders
|
||||
- add `/ui` folder for all ui-related code/css/assets
|
||||
- improve `kanvas` typing
|
||||
- additional strong typing in core, thanks @awsr
|
||||
- **Fixes**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div align="center">
|
||||
<img src="https://github.com/vladmandic/sdnext/raw/master/html/logo-transparent.png" width=200 alt="SD.Next: AI art generator logo">
|
||||
<img src="https://github.com/vladmandic/sdnext/raw/dev/ui/assets/logo-transparent.png" width=200 alt="SD.Next: AI art generator logo">
|
||||
|
||||
# SD.Next: All-in-one WebUI
|
||||
|
||||
@@ -30,7 +30,7 @@ SD.Next is a powerful, open-source WebUI app for AI image and video generation,
|
||||
### Screenshot: Desktop interface
|
||||
|
||||
<div align="center">
|
||||
<img src="https://github.com/vladmandic/sdnext/raw/dev/html/screenshot-robot.jpg" alt="SD.Next: AI art generator desktop interface screenshot" width="90%">
|
||||
<img src="https://github.com/vladmandic/sdnext/raw/dev/ui/assets/screenshot-robot.jpg" alt="SD.Next: AI art generator desktop interface screenshot" width="90%">
|
||||
</div>
|
||||
|
||||
### Screenshot: Mobile interface
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
## Issues
|
||||
|
||||
- Kanvas changing blur/dilate makes mask unusable
|
||||
- LTX audio leaks from previous runs
|
||||
- Dev-document UI:Core/ModernUI/Kanvas
|
||||
- Dev-document Hints
|
||||
- Dev-document LoRA
|
||||
- Torch lazy-load
|
||||
|
||||
## Features
|
||||
|
||||
@@ -160,20 +163,16 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
|
||||
> npm run todo
|
||||
|
||||
```code
|
||||
installer.py:TODO rocm: switch to pytorch source when it becomes available
|
||||
modules/control/run.py:TODO modernui: monkey-patch for missing tabs.select event
|
||||
modules/history.py:TODO: apply metadata, preview, load/save
|
||||
modules/image/resize.py:TODO resize image: enable full VAE mode for resize-latent
|
||||
modules/lora/lora_load.py:TODO lora: add t5 key support for sd35/f1
|
||||
modules/masking.py:TODO: additional masking algorithms
|
||||
modules/modular_guiders.py:TODO: guiders
|
||||
modules/processing_class.py:TODO processing: remove duplicate mask params
|
||||
modules/sd_hijack_hypertile.py:TODO hypertile: vae breaks when using non-standard sizes
|
||||
modules/sd_models.py:TODO model load: implement model in-memory caching
|
||||
modules/sd_samplers_diffusers.py:TODO enso-required
|
||||
modules/sd_unet.py:TODO model load: force-reloading entire model as loading transformers only leads to massive memory usage
|
||||
modules/transformer_cache.py:TODO fc: autodetect distilled based on model
|
||||
modules/transformer_cache.py:TODO fc: autodetect tensor format based on model
|
||||
modules/ui_models_load.py:TODO loader: load receipe
|
||||
modules/ui_models_load.py:TODO loader: save receipe
|
||||
installer.py:652:15: W0511: TODO rocm: switch to pytorch source when it becomes available (fixme)
|
||||
modules/sd_models_compile.py:90:5: W0511: TODO pruna: enable when it supports transformers==5.5 (fixme)
|
||||
modules/transformer_cache.py:29:61: W0511: TODO fc: autodetect tensor format based on model (fixme)
|
||||
modules/transformer_cache.py:30:50: W0511: TODO fc: autodetect distilled based on model (fixme)
|
||||
modules/processing_class.py:406:32: W0511: TODO processing: remove duplicate mask params (fixme)
|
||||
modules/sd_samplers_diffusers.py:370:31: W0511: TODO enso-required (fixme)
|
||||
modules/sd_models.py:1424:5: W0511: TODO model load: implement model in-memory caching (fixme)
|
||||
modules/ui_models_load.py:257:5: W0511: TODO loader: load receipe (fixme)
|
||||
modules/ui_models_load.py:264:5: W0511: TODO loader: save receipe (fixme)
|
||||
modules/sd_hijack_hypertile.py:123:17: W0511: TODO hypertile: vae breaks when using non-standard sizes (fixme)
|
||||
modules/sd_unet.py:77:39: W0511: TODO model load: force-reloading entire model as loading transformers only leads to massive memory usage (fixme)
|
||||
modules/modular_guiders.py:66:51: W0511: TODO: guiders (fixme)
|
||||
```
|
||||
|
||||
@@ -29,88 +29,6 @@ const jsConfig = defineConfig([
|
||||
...globals.builtin,
|
||||
...globals.browser,
|
||||
...globals.jquery,
|
||||
panzoom: 'readonly',
|
||||
authFetch: 'readonly',
|
||||
initServerInfo: 'readonly',
|
||||
log: 'readonly',
|
||||
debug: 'readonly',
|
||||
error: 'readonly',
|
||||
timer: 'readonly',
|
||||
xhrGet: 'readonly',
|
||||
xhrPost: 'readonly',
|
||||
gradioApp: 'readonly',
|
||||
executeCallbacks: 'readonly',
|
||||
onAfterUiUpdate: 'readonly',
|
||||
onOptionsChanged: 'readonly',
|
||||
optionsChangedCallbacks: 'readonly',
|
||||
onUiLoaded: 'readonly',
|
||||
onUiUpdate: 'readonly',
|
||||
onUiTabChange: 'readonly',
|
||||
onUiReady: 'readonly',
|
||||
uiCurrentTab: 'writable',
|
||||
uiElementIsVisible: 'readonly',
|
||||
uiElementInSight: 'readonly',
|
||||
getUICurrentTabContent: 'readonly',
|
||||
waitForFlag: 'readonly',
|
||||
logFn: 'readonly',
|
||||
logTimers: 'readonly',
|
||||
generateForever: 'readonly',
|
||||
showContributors: 'readonly',
|
||||
opts: 'writable',
|
||||
monitorOption: 'readonly',
|
||||
sortUIElements: 'readonly',
|
||||
all_gallery_buttons: 'readonly',
|
||||
selected_gallery_button: 'readonly',
|
||||
selected_gallery_index: 'readonly',
|
||||
switch_to_txt2img: 'readonly',
|
||||
switch_to_img2img_tab: 'readonly',
|
||||
switch_to_img2img: 'readonly',
|
||||
switch_to_sketch: 'readonly',
|
||||
switch_to_inpaint: 'readonly',
|
||||
witch_to_inpaint_sketch: 'readonly',
|
||||
switch_to_extras: 'readonly',
|
||||
get_tab_index: 'readonly',
|
||||
create_submit_args: 'readonly',
|
||||
restartReload: 'readonly',
|
||||
markSelectedCards: 'readonly',
|
||||
updateInput: 'readonly',
|
||||
toggleCompact: 'readonly',
|
||||
setFontSize: 'readonly',
|
||||
setTheme: 'readonly',
|
||||
registerDragDrop: 'readonly',
|
||||
getToken: 'readonly',
|
||||
getENActiveTab: 'readonly',
|
||||
quickApplyStyle: 'readonly',
|
||||
quickSaveStyle: 'readonly',
|
||||
setupExtraNetworks: 'readonly',
|
||||
showNetworks: 'readonly',
|
||||
localization: 'readonly',
|
||||
randomId: 'readonly',
|
||||
requestProgress: 'readonly',
|
||||
setRefreshInterval: 'readonly',
|
||||
modalPrevImage: 'readonly',
|
||||
modalNextImage: 'readonly',
|
||||
galleryClickEventHandler: 'readonly',
|
||||
getExif: 'readonly',
|
||||
jobStatusEl: 'readonly',
|
||||
removeSplash: 'readonly',
|
||||
initGPU: 'readonly',
|
||||
startGPU: 'readonly',
|
||||
disableNVML: 'readonly',
|
||||
hash: 'readonly',
|
||||
idbGet: 'readonly',
|
||||
idbPut: 'readonly',
|
||||
idbDel: 'readonly',
|
||||
idbAdd: 'readonly',
|
||||
initTableSorter: 'readonly',
|
||||
idbCount: 'readonly',
|
||||
idbFolderCleanup: 'readonly',
|
||||
idbClearAll: 'readonly',
|
||||
idbIsReady: 'readonly',
|
||||
initChangelog: 'readonly',
|
||||
sendNotification: 'readonly',
|
||||
monitorConnection: 'readonly',
|
||||
ConnectionMonitorState: 'readonly',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -153,6 +71,7 @@ const jsConfig = defineConfig([
|
||||
'prefer-template': 'warn',
|
||||
'promise/no-nesting': 'off',
|
||||
'@typescript-eslint/no-for-in-array': 'off',
|
||||
'import-x/no-extraneous-dependencies': 'off',
|
||||
radix: 'off',
|
||||
'@stylistic/brace-style': [
|
||||
'error',
|
||||
@@ -205,22 +124,28 @@ const jsConfig = defineConfig([
|
||||
},
|
||||
]);
|
||||
|
||||
// const typescriptConfig = defineConfig([
|
||||
// // TypeScript ESLint plugin
|
||||
// plugins.typescriptEslint,
|
||||
// // Airbnb base TypeScript config
|
||||
// ...configs.base.typescript,
|
||||
// {
|
||||
// name: 'sdnext/typescript',
|
||||
// files: helpers.extensions.tsFiles,
|
||||
// rules: {
|
||||
// '@typescript-eslint/ban-ts-comment': 'off',
|
||||
// '@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
// '@typescript-eslint/no-shadow': 'error',
|
||||
// '@typescript-eslint/no-var-requires': 'off',
|
||||
// },
|
||||
// },
|
||||
// ]);
|
||||
const typescriptConfig = defineConfig([
|
||||
// TypeScript ESLint plugin
|
||||
plugins.typescriptEslint,
|
||||
// Airbnb base TypeScript config
|
||||
...configs.base.typescript,
|
||||
{
|
||||
name: 'sdnext/ts',
|
||||
files: ['ui/**/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-shadow': 'error',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-for-in-array': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/prefer-destructuring': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
'import-x/prefer-default-export': 'off',
|
||||
'import-x/no-extraneous-dependencies': 'off',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const nodeConfig = defineConfig([
|
||||
// Node plugin
|
||||
@@ -332,21 +257,16 @@ export default defineConfig([
|
||||
// Ignore files and folders listed in .gitignore
|
||||
includeIgnoreFile(gitignorePath),
|
||||
globalIgnores([
|
||||
'**/venv',
|
||||
'**/node_modules',
|
||||
'**/extensions',
|
||||
'**/extensions-builtin',
|
||||
'**/repositories',
|
||||
'**/venv',
|
||||
'**/panZoom.js',
|
||||
'**/split.js',
|
||||
'**/exifr.js',
|
||||
'**/jquery.js',
|
||||
'**/sparkline.js',
|
||||
'**/sha256.js',
|
||||
'**/iframeResizer.min.js',
|
||||
'javascript/*',
|
||||
'ui/dist/*',
|
||||
'ui/js/*',
|
||||
]),
|
||||
...jsConfig,
|
||||
// ...typescriptConfig,
|
||||
...typescriptConfig,
|
||||
...nodeConfig,
|
||||
...jsonConfig,
|
||||
...markdownConfig,
|
||||
|
||||
@@ -1,710 +0,0 @@
|
||||
<style>
|
||||
#licenses h2 {font-size: 1.2em; font-weight: bold; margin-bottom: 0.2em;}
|
||||
#licenses small {font-size: 0.95em; opacity: 0.85;}
|
||||
#licenses pre { margin: 1em 0 2em 0;}
|
||||
</style>
|
||||
|
||||
|
||||
<h2><a href="https://github.com/victorca25/iNNfer/blob/main/LICENSE">ESRGAN</a></h2>
|
||||
<small>Code for architecture and reading models copied.</small>
|
||||
<pre>
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 victorca25
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/xinntao/Real-ESRGAN/blob/master/LICENSE">Real-ESRGAN</a></h2>
|
||||
<small>Some code is copied to support ESRGAN models.</small>
|
||||
<pre>
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, Xintao Wang
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/invoke-ai/InvokeAI/blob/main/LICENSE">InvokeAI</a></h2>
|
||||
<small>Some code for compatibility with OSX is taken from lstein's repository.</small>
|
||||
<pre>
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 InvokeAI Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/Hafiidz/latent-diffusion/blob/main/LICENSE">LDSR</a></h2>
|
||||
<small>Code added by contirubtors, most likely copied from this repository.</small>
|
||||
<pre>
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Machine Vision and Learning Group, LMU Munich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/pharmapsychotic/clip-interrogator/blob/main/LICENSE">CLIP Interrogator</a></h2>
|
||||
<small>Some small amounts of code borrowed and reworked.</small>
|
||||
<pre>
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 pharmapsychotic
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/JingyunLiang/SwinIR/blob/main/LICENSE">SwinIR</a></h2>
|
||||
<small>Code added by contributors, most likely copied from this repository.</small>
|
||||
|
||||
<pre>
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [2021] [SwinIR Authors]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/AminRezaei0x443/memory-efficient-attention/blob/main/LICENSE">Memory Efficient Attention</a></h2>
|
||||
<small>The sub-quadratic cross attention optimization uses modified code from the Memory Efficient Attention package that Alex Birch optimized for 3D tensors. This license is updated to reflect that.</small>
|
||||
<pre>
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Alex Birch
|
||||
Copyright (c) 2023 Amin Rezaei
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/huggingface/diffusers/blob/c7da8fd23359a22d0df2741688b5b4f33c26df21/LICENSE">Scaled Dot Product Attention</a></h2>
|
||||
<small>Some small amounts of code borrowed and reworked.</small>
|
||||
<pre>
|
||||
Copyright 2023 The HuggingFace Team. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/Dao-AILab/flash-attention/blob/main/LICENSE">Flash Attention</a></h2>
|
||||
<small>Fast and memory-efficient exact attention</small>
|
||||
<pre>
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2022, the respective contributors, as shown by the AUTHORS file.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/explosion/curated-transformers/blob/main/LICENSE">Curated transformers</a></h2>
|
||||
<small>The MPS workaround for nn.Linear on macOS 13.2.X is based on the MPS workaround for nn.Linear created by danieldk for Curated transformers</small>
|
||||
<pre>
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2021 ExplosionAI GmbH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/madebyollin/taesd/blob/main/LICENSE">TAESD</a></h2>
|
||||
<small>Tiny AutoEncoder for Stable Diffusion option for live previews</small>
|
||||
<pre>
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Ollin Boer Bohan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<h2><a href="https://github.com/microsoft/Olive/blob/main/LICENSE">Olive</a></h2>
|
||||
<small>An easy-to-use hardware-aware model optimization tool that composes industry-leading techniques across model compression, optimization, and compilation.</small>
|
||||
<pre>
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
</pre>
|
||||
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 473 KiB |
|
Before Width: | Height: | Size: 156 KiB |
@@ -322,7 +322,7 @@ def git(arg: str, folder: str | None= None, ignore: bool = False, optional: bool
|
||||
elif "no submodule mapping found" in txt:
|
||||
log.warning(f'Git: folder="{folder}" submodules changed')
|
||||
elif 'or stash them' in txt:
|
||||
log.error(f'Git: folder="{folder}" local changes detected')
|
||||
log.warning(f'Git: folder="{folder}" local changes detected')
|
||||
else:
|
||||
log.error(f'Git: folder="{folder}" arg="{arg}" output={txt}')
|
||||
errors.append(f'git: {folder}')
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
let user;
|
||||
let token;
|
||||
|
||||
async function getToken() {
|
||||
if (token === undefined || user === undefined) {
|
||||
const res = await fetch(`${window.subpath}/token`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
user = data.user;
|
||||
token = data.token;
|
||||
log('getToken', user);
|
||||
}
|
||||
}
|
||||
return { user, token };
|
||||
}
|
||||
|
||||
async function authFetch(url, options = {}) {
|
||||
await getToken();
|
||||
if (user && token) {
|
||||
if (!options.headers) options.headers = {};
|
||||
const encoded = btoa(`${user}:${token}`);
|
||||
options.headers.Authorization = `Basic ${encoded}`;
|
||||
}
|
||||
let res;
|
||||
try {
|
||||
res = await fetch(url, options);
|
||||
if (!res.ok) error('fetch', { status: res?.status || 503, url, user, token });
|
||||
} catch (err) {
|
||||
if (ConnectionMonitorState.online) {
|
||||
error('fetch', { status: res?.status || 503, url, user, token, error: err });
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
let lastGitHubSearch = '';
|
||||
let lastDocsSearch = '';
|
||||
|
||||
async function clickGitHubWikiPage(page) {
|
||||
log(`clickGitHubWikiPage: page="${page}"`);
|
||||
lastGitHubSearch = page;
|
||||
const el = gradioApp().getElementById('github_md_btn');
|
||||
if (el) el.click();
|
||||
}
|
||||
|
||||
function getGitHubWikiPage() {
|
||||
return lastGitHubSearch;
|
||||
}
|
||||
|
||||
async function clickDocsPage(page) {
|
||||
log(`clickDocsPage: page="${page}"`);
|
||||
lastDocsSearch = page;
|
||||
const el = gradioApp().getElementById('docs_md_btn');
|
||||
if (el) el.click();
|
||||
}
|
||||
|
||||
function getDocsPage() {
|
||||
return lastDocsSearch;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
function extensions_apply(extensions_disabled_list, extensions_update_list, disable_all) {
|
||||
const disable = [];
|
||||
const update = [];
|
||||
gradioApp().querySelectorAll('#extensions input[type="checkbox"]').forEach((x) => {
|
||||
if (x.name.startsWith('enable_') && !x.checked) disable.push(x.name.substring(7));
|
||||
if (x.name.startsWith('update_') && x.checked) update.push(x.name.substring(7));
|
||||
});
|
||||
restartReload();
|
||||
log('Extensions apply:', { disable, update });
|
||||
return [JSON.stringify(disable), JSON.stringify(update), disable_all];
|
||||
}
|
||||
|
||||
function extensions_check(info, extensions_disabled_list, search_text, sort_column) {
|
||||
const disable = [];
|
||||
gradioApp().querySelectorAll('#extensions input[type="checkbox"]').forEach((x) => {
|
||||
if (x.name.startsWith('enable_') && !x.checked) disable.push(x.name.substring(7));
|
||||
});
|
||||
const id = randomId();
|
||||
log('Extensions check:', { disable });
|
||||
return [id, JSON.stringify(disable), search_text, sort_column];
|
||||
}
|
||||
|
||||
function install_extension(button, url) {
|
||||
button.disabled = 'disabled';
|
||||
button.value = 'Installing...';
|
||||
button.innerHTML = 'installing';
|
||||
const textarea = gradioApp().querySelector('#extension_to_install textarea');
|
||||
textarea.value = url;
|
||||
updateInput(textarea);
|
||||
log('Extension install:', { url });
|
||||
gradioApp().querySelector('#install_extension_button').click();
|
||||
}
|
||||
|
||||
function uninstall_extension(button, url) {
|
||||
button.disabled = 'disabled';
|
||||
button.value = 'Uninstalling...';
|
||||
button.innerHTML = 'uninstalling';
|
||||
const textarea = gradioApp().querySelector('#extension_to_install textarea');
|
||||
textarea.value = url;
|
||||
updateInput(textarea);
|
||||
log('Extension uninstall:', { url });
|
||||
gradioApp().querySelector('#uninstall_extension_button').click();
|
||||
}
|
||||
|
||||
function update_extension(button, url) {
|
||||
button.value = 'Updating...';
|
||||
button.innerHTML = 'updating';
|
||||
const textarea = gradioApp().querySelector('#extension_to_install textarea');
|
||||
textarea.value = url;
|
||||
updateInput(textarea);
|
||||
log('Extension update:', { url });
|
||||
gradioApp().querySelector('#update_extension_button').click();
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// attaches listeners to the txt2img and img2img galleries to update displayed generation param text when the image changes
|
||||
|
||||
function attachGalleryListeners(tabName) {
|
||||
const gallery = gradioApp().querySelector(`#${tabName}_gallery`);
|
||||
if (!gallery) return null;
|
||||
gallery.addEventListener('click', () => {
|
||||
// log('galleryItemSelected:', tabName);
|
||||
const btn = gradioApp().getElementById(`${tabName}_generation_info_button`);
|
||||
if (btn) btn.click();
|
||||
});
|
||||
gallery?.addEventListener('keydown', (e) => {
|
||||
if (e.keyCode === 37 || e.keyCode === 39) gradioApp().getElementById(`${tabName}_generation_info_button`).click(); // left or right arrow
|
||||
});
|
||||
return gallery;
|
||||
}
|
||||
|
||||
let txt2img_gallery;
|
||||
let img2img_gallery;
|
||||
let control_gallery;
|
||||
let modal;
|
||||
|
||||
async function initiGenerationParams() {
|
||||
const t0 = performance.now();
|
||||
if (!modal) modal = gradioApp().getElementById('lightboxModal');
|
||||
if (!modal) return;
|
||||
|
||||
const modalObserver = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutationRecord) => {
|
||||
const tabName = getENActiveTab();
|
||||
if (mutationRecord.target.style.display === 'none') {
|
||||
const btn = gradioApp().getElementById(`${tabName}_generation_info_button`);
|
||||
if (btn) btn.click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!txt2img_gallery) txt2img_gallery = attachGalleryListeners('txt2img');
|
||||
if (!img2img_gallery) img2img_gallery = attachGalleryListeners('img2img');
|
||||
if (!control_gallery) control_gallery = attachGalleryListeners('control');
|
||||
modalObserver.observe(modal, { attributes: true, attributeFilter: ['style'] });
|
||||
const t1 = performance.now();
|
||||
log('initGenerationParams', Math.round(t1 - t0));
|
||||
timer('initGenerationParams', t1 - t0);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
function onCalcResolutionHires(width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler) {
|
||||
const setInactive = (elem, inactive) => elem.classList.toggle('inactive', !!inactive);
|
||||
const hrUpscaleBy = gradioApp().getElementById('txt2img_hr_scale');
|
||||
const hrResizeX = gradioApp().getElementById('txt2img_hr_resize_x');
|
||||
const hrResizeY = gradioApp().getElementById('txt2img_hr_resize_y');
|
||||
setInactive(hrUpscaleBy, hr_resize_x > 0 || hr_resize_y > 0);
|
||||
setInactive(hrResizeX, hr_resize_x === 0);
|
||||
setInactive(hrResizeY, hr_resize_y === 0);
|
||||
return [width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler];
|
||||
}
|
||||
@@ -1,408 +0,0 @@
|
||||
// SHA-256 (+ HMAC and PBKDF2) for JavaScript.
|
||||
//
|
||||
// Written in 2014-2016 by Dmitry Chestnykh.
|
||||
// Public domain, no warranty.
|
||||
//
|
||||
// Functions (accept and return Uint8Arrays):
|
||||
//
|
||||
// sha256(message) -> hash
|
||||
// sha256.hmac(key, message) -> mac
|
||||
// sha256.pbkdf2(password, salt, rounds, dkLen) -> dk
|
||||
//
|
||||
// Classes:
|
||||
//
|
||||
// new sha256.Hash()
|
||||
// new sha256.HMAC(key)
|
||||
//
|
||||
const digestLength = 32;
|
||||
const blockSize = 64;
|
||||
|
||||
// SHA-256 constants
|
||||
|
||||
var K = new Uint32Array([
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
|
||||
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
|
||||
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
|
||||
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
|
||||
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
||||
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
|
||||
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
|
||||
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
|
||||
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
|
||||
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
]);
|
||||
function hashBlocks(w, v, p, pos, len) {
|
||||
var a, b, c, d, e, f, g, h, u, i, j, t1, t2;
|
||||
while (len >= 64) {
|
||||
a = v[0];
|
||||
b = v[1];
|
||||
c = v[2];
|
||||
d = v[3];
|
||||
e = v[4];
|
||||
f = v[5];
|
||||
g = v[6];
|
||||
h = v[7];
|
||||
for (i = 0; i < 16; i++) {
|
||||
j = pos + i * 4;
|
||||
w[i] = (((p[j] & 0xff) << 24) | ((p[j + 1] & 0xff) << 16) |
|
||||
((p[j + 2] & 0xff) << 8) | (p[j + 3] & 0xff));
|
||||
}
|
||||
for (i = 16; i < 64; i++) {
|
||||
u = w[i - 2];
|
||||
t1 = (u >>> 17 | u << (32 - 17)) ^ (u >>> 19 | u << (32 - 19)) ^ (u >>> 10);
|
||||
u = w[i - 15];
|
||||
t2 = (u >>> 7 | u << (32 - 7)) ^ (u >>> 18 | u << (32 - 18)) ^ (u >>> 3);
|
||||
w[i] = (t1 + w[i - 7] | 0) + (t2 + w[i - 16] | 0);
|
||||
}
|
||||
for (i = 0; i < 64; i++) {
|
||||
t1 = (((((e >>> 6 | e << (32 - 6)) ^ (e >>> 11 | e << (32 - 11)) ^
|
||||
(e >>> 25 | e << (32 - 25))) + ((e & f) ^ (~e & g))) | 0) +
|
||||
((h + ((K[i] + w[i]) | 0)) | 0)) | 0;
|
||||
t2 = (((a >>> 2 | a << (32 - 2)) ^ (a >>> 13 | a << (32 - 13)) ^
|
||||
(a >>> 22 | a << (32 - 22))) + ((a & b) ^ (a & c) ^ (b & c))) | 0;
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = (d + t1) | 0;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = (t1 + t2) | 0;
|
||||
}
|
||||
v[0] += a;
|
||||
v[1] += b;
|
||||
v[2] += c;
|
||||
v[3] += d;
|
||||
v[4] += e;
|
||||
v[5] += f;
|
||||
v[6] += g;
|
||||
v[7] += h;
|
||||
pos += 64;
|
||||
len -= 64;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
// Hash implements SHA256 hash algorithm.
|
||||
var Hash = /** @class */ (function () {
|
||||
function Hash() {
|
||||
this.digestLength = digestLength;
|
||||
this.blockSize = blockSize;
|
||||
// Note: Int32Array is used instead of Uint32Array for performance reasons.
|
||||
this.state = new Int32Array(8); // hash state
|
||||
this.temp = new Int32Array(64); // temporary state
|
||||
this.buffer = new Uint8Array(128); // buffer for data to hash
|
||||
this.bufferLength = 0; // number of bytes in buffer
|
||||
this.bytesHashed = 0; // number of total bytes hashed
|
||||
this.finished = false; // indicates whether the hash was finalized
|
||||
this.reset();
|
||||
}
|
||||
// Resets hash state making it possible
|
||||
// to re-use this instance to hash other data.
|
||||
Hash.prototype.reset = function () {
|
||||
this.state[0] = 0x6a09e667;
|
||||
this.state[1] = 0xbb67ae85;
|
||||
this.state[2] = 0x3c6ef372;
|
||||
this.state[3] = 0xa54ff53a;
|
||||
this.state[4] = 0x510e527f;
|
||||
this.state[5] = 0x9b05688c;
|
||||
this.state[6] = 0x1f83d9ab;
|
||||
this.state[7] = 0x5be0cd19;
|
||||
this.bufferLength = 0;
|
||||
this.bytesHashed = 0;
|
||||
this.finished = false;
|
||||
return this;
|
||||
};
|
||||
// Cleans internal buffers and re-initializes hash state.
|
||||
Hash.prototype.clean = function () {
|
||||
for (var i = 0; i < this.buffer.length; i++) {
|
||||
this.buffer[i] = 0;
|
||||
}
|
||||
for (var i = 0; i < this.temp.length; i++) {
|
||||
this.temp[i] = 0;
|
||||
}
|
||||
this.reset();
|
||||
};
|
||||
// Updates hash state with the given data.
|
||||
//
|
||||
// Optionally, length of the data can be specified to hash
|
||||
// fewer bytes than data.length.
|
||||
//
|
||||
// Throws error when trying to update already finalized hash:
|
||||
// instance must be reset to use it again.
|
||||
Hash.prototype.update = function (data, dataLength) {
|
||||
if (dataLength === void 0) { dataLength = data.length; }
|
||||
if (this.finished) {
|
||||
throw new Error("SHA256: can't update because hash was finished.");
|
||||
}
|
||||
var dataPos = 0;
|
||||
this.bytesHashed += dataLength;
|
||||
if (this.bufferLength > 0) {
|
||||
while (this.bufferLength < 64 && dataLength > 0) {
|
||||
this.buffer[this.bufferLength++] = data[dataPos++];
|
||||
dataLength--;
|
||||
}
|
||||
if (this.bufferLength === 64) {
|
||||
hashBlocks(this.temp, this.state, this.buffer, 0, 64);
|
||||
this.bufferLength = 0;
|
||||
}
|
||||
}
|
||||
if (dataLength >= 64) {
|
||||
dataPos = hashBlocks(this.temp, this.state, data, dataPos, dataLength);
|
||||
dataLength %= 64;
|
||||
}
|
||||
while (dataLength > 0) {
|
||||
this.buffer[this.bufferLength++] = data[dataPos++];
|
||||
dataLength--;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
// Finalizes hash state and puts hash into out.
|
||||
//
|
||||
// If hash was already finalized, puts the same value.
|
||||
Hash.prototype.finish = function (out) {
|
||||
if (!this.finished) {
|
||||
var bytesHashed = this.bytesHashed;
|
||||
var left = this.bufferLength;
|
||||
var bitLenHi = (bytesHashed / 0x20000000) | 0;
|
||||
var bitLenLo = bytesHashed << 3;
|
||||
var padLength = (bytesHashed % 64 < 56) ? 64 : 128;
|
||||
this.buffer[left] = 0x80;
|
||||
for (var i = left + 1; i < padLength - 8; i++) {
|
||||
this.buffer[i] = 0;
|
||||
}
|
||||
this.buffer[padLength - 8] = (bitLenHi >>> 24) & 0xff;
|
||||
this.buffer[padLength - 7] = (bitLenHi >>> 16) & 0xff;
|
||||
this.buffer[padLength - 6] = (bitLenHi >>> 8) & 0xff;
|
||||
this.buffer[padLength - 5] = (bitLenHi >>> 0) & 0xff;
|
||||
this.buffer[padLength - 4] = (bitLenLo >>> 24) & 0xff;
|
||||
this.buffer[padLength - 3] = (bitLenLo >>> 16) & 0xff;
|
||||
this.buffer[padLength - 2] = (bitLenLo >>> 8) & 0xff;
|
||||
this.buffer[padLength - 1] = (bitLenLo >>> 0) & 0xff;
|
||||
hashBlocks(this.temp, this.state, this.buffer, 0, padLength);
|
||||
this.finished = true;
|
||||
}
|
||||
for (var i = 0; i < 8; i++) {
|
||||
out[i * 4 + 0] = (this.state[i] >>> 24) & 0xff;
|
||||
out[i * 4 + 1] = (this.state[i] >>> 16) & 0xff;
|
||||
out[i * 4 + 2] = (this.state[i] >>> 8) & 0xff;
|
||||
out[i * 4 + 3] = (this.state[i] >>> 0) & 0xff;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
// Returns the final hash digest.
|
||||
Hash.prototype.digest = function () {
|
||||
var out = new Uint8Array(this.digestLength);
|
||||
this.finish(out);
|
||||
return out;
|
||||
};
|
||||
// Internal function for use in HMAC for optimization.
|
||||
Hash.prototype._saveState = function (out) {
|
||||
for (var i = 0; i < this.state.length; i++) {
|
||||
out[i] = this.state[i];
|
||||
}
|
||||
};
|
||||
// Internal function for use in HMAC for optimization.
|
||||
Hash.prototype._restoreState = function (from, bytesHashed) {
|
||||
for (var i = 0; i < this.state.length; i++) {
|
||||
this.state[i] = from[i];
|
||||
}
|
||||
this.bytesHashed = bytesHashed;
|
||||
this.finished = false;
|
||||
this.bufferLength = 0;
|
||||
};
|
||||
return Hash;
|
||||
}());
|
||||
window.Hash = Hash;
|
||||
// HMAC implements HMAC-SHA256 message authentication algorithm.
|
||||
var HMAC = /** @class */ (function () {
|
||||
function HMAC(key) {
|
||||
this.inner = new Hash();
|
||||
this.outer = new Hash();
|
||||
this.blockSize = this.inner.blockSize;
|
||||
this.digestLength = this.inner.digestLength;
|
||||
var pad = new Uint8Array(this.blockSize);
|
||||
if (key.length > this.blockSize) {
|
||||
(new Hash()).update(key).finish(pad).clean();
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < key.length; i++) {
|
||||
pad[i] = key[i];
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < pad.length; i++) {
|
||||
pad[i] ^= 0x36;
|
||||
}
|
||||
this.inner.update(pad);
|
||||
for (var i = 0; i < pad.length; i++) {
|
||||
pad[i] ^= 0x36 ^ 0x5c;
|
||||
}
|
||||
this.outer.update(pad);
|
||||
this.istate = new Uint32Array(8);
|
||||
this.ostate = new Uint32Array(8);
|
||||
this.inner._saveState(this.istate);
|
||||
this.outer._saveState(this.ostate);
|
||||
for (var i = 0; i < pad.length; i++) {
|
||||
pad[i] = 0;
|
||||
}
|
||||
}
|
||||
// Returns HMAC state to the state initialized with key
|
||||
// to make it possible to run HMAC over the other data with the same
|
||||
// key without creating a new instance.
|
||||
HMAC.prototype.reset = function () {
|
||||
this.inner._restoreState(this.istate, this.inner.blockSize);
|
||||
this.outer._restoreState(this.ostate, this.outer.blockSize);
|
||||
return this;
|
||||
};
|
||||
// Cleans HMAC state.
|
||||
HMAC.prototype.clean = function () {
|
||||
for (var i = 0; i < this.istate.length; i++) {
|
||||
this.ostate[i] = this.istate[i] = 0;
|
||||
}
|
||||
this.inner.clean();
|
||||
this.outer.clean();
|
||||
};
|
||||
// Updates state with provided data.
|
||||
HMAC.prototype.update = function (data) {
|
||||
this.inner.update(data);
|
||||
return this;
|
||||
};
|
||||
// Finalizes HMAC and puts the result in out.
|
||||
HMAC.prototype.finish = function (out) {
|
||||
if (this.outer.finished) {
|
||||
this.outer.finish(out);
|
||||
}
|
||||
else {
|
||||
this.inner.finish(out);
|
||||
this.outer.update(out, this.digestLength).finish(out);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
// Returns message authentication code.
|
||||
HMAC.prototype.digest = function () {
|
||||
var out = new Uint8Array(this.digestLength);
|
||||
this.finish(out);
|
||||
return out;
|
||||
};
|
||||
return HMAC;
|
||||
}());
|
||||
window.HMAC = HMAC;
|
||||
// Returns SHA256 hash of data.
|
||||
function hash(data) {
|
||||
var h = (new Hash()).update(data);
|
||||
var digest = h.digest();
|
||||
h.clean();
|
||||
return digest;
|
||||
}
|
||||
window.hash = hash;
|
||||
// Function hash is both available as module.hash and as default export.
|
||||
// Returns HMAC-SHA256 of data under the key.
|
||||
function hmac(key, data) {
|
||||
var h = (new HMAC(key)).update(data);
|
||||
var digest = h.digest();
|
||||
h.clean();
|
||||
return digest;
|
||||
}
|
||||
window.hmac = hmac;
|
||||
// Fills hkdf buffer like this:
|
||||
// T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
|
||||
function fillBuffer(buffer, hmac, info, counter) {
|
||||
// Counter is a byte value: check if it overflowed.
|
||||
var num = counter[0];
|
||||
if (num === 0) {
|
||||
throw new Error("hkdf: cannot expand more");
|
||||
}
|
||||
// Prepare HMAC instance for new data with old key.
|
||||
hmac.reset();
|
||||
// Hash in previous output if it was generated
|
||||
// (i.e. counter is greater than 1).
|
||||
if (num > 1) {
|
||||
hmac.update(buffer);
|
||||
}
|
||||
// Hash in info if it exists.
|
||||
if (info) {
|
||||
hmac.update(info);
|
||||
}
|
||||
// Hash in the counter.
|
||||
hmac.update(counter);
|
||||
// Output result to buffer and clean HMAC instance.
|
||||
hmac.finish(buffer);
|
||||
// Increment counter inside typed array, this works properly.
|
||||
counter[0]++;
|
||||
}
|
||||
var hkdfSalt = new Uint8Array(digestLength); // Filled with zeroes.
|
||||
function hkdf(key, salt, info, length) {
|
||||
if (salt === void 0) { salt = hkdfSalt; }
|
||||
if (length === void 0) { length = 32; }
|
||||
var counter = new Uint8Array([1]);
|
||||
// HKDF-Extract uses salt as HMAC key, and key as data.
|
||||
var okm = hmac(salt, key);
|
||||
// Initialize HMAC for expanding with extracted key.
|
||||
// Ensure no collisions with `hmac` function.
|
||||
var hmac_ = new HMAC(okm);
|
||||
// Allocate buffer.
|
||||
var buffer = new Uint8Array(hmac_.digestLength);
|
||||
var bufpos = buffer.length;
|
||||
var out = new Uint8Array(length);
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (bufpos === buffer.length) {
|
||||
fillBuffer(buffer, hmac_, info, counter);
|
||||
bufpos = 0;
|
||||
}
|
||||
out[i] = buffer[bufpos++];
|
||||
}
|
||||
hmac_.clean();
|
||||
buffer.fill(0);
|
||||
counter.fill(0);
|
||||
return out;
|
||||
}
|
||||
window.hkdf = hkdf;
|
||||
// Derives a key from password and salt using PBKDF2-HMAC-SHA256
|
||||
// with the given number of iterations.
|
||||
//
|
||||
// The number of bytes returned is equal to dkLen.
|
||||
//
|
||||
// (For better security, avoid dkLen greater than hash length - 32 bytes).
|
||||
function pbkdf2(password, salt, iterations, dkLen) {
|
||||
var prf = new HMAC(password);
|
||||
var len = prf.digestLength;
|
||||
var ctr = new Uint8Array(4);
|
||||
var t = new Uint8Array(len);
|
||||
var u = new Uint8Array(len);
|
||||
var dk = new Uint8Array(dkLen);
|
||||
for (var i = 0; i * len < dkLen; i++) {
|
||||
var c = i + 1;
|
||||
ctr[0] = (c >>> 24) & 0xff;
|
||||
ctr[1] = (c >>> 16) & 0xff;
|
||||
ctr[2] = (c >>> 8) & 0xff;
|
||||
ctr[3] = (c >>> 0) & 0xff;
|
||||
prf.reset();
|
||||
prf.update(salt);
|
||||
prf.update(ctr);
|
||||
prf.finish(u);
|
||||
for (var j = 0; j < len; j++) {
|
||||
t[j] = u[j];
|
||||
}
|
||||
for (var j = 2; j <= iterations; j++) {
|
||||
prf.reset();
|
||||
prf.update(u).finish(u);
|
||||
for (var k = 0; k < len; k++) {
|
||||
t[k] ^= u[k];
|
||||
}
|
||||
}
|
||||
for (var j = 0; j < len && i * len + j < dkLen; j++) {
|
||||
dk[i * len + j] = t[j];
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < len; i++) {
|
||||
t[i] = u[i] = 0;
|
||||
}
|
||||
for (var i = 0; i < 4; i++) {
|
||||
ctr[i] = 0;
|
||||
}
|
||||
prf.clean();
|
||||
return dk;
|
||||
}
|
||||
window.pbkdf2 = pbkdf2;
|
||||
@@ -1,105 +0,0 @@
|
||||
/* eslint-disable no-undef */
|
||||
window.api = '/sdapi/v1';
|
||||
window.subpath = '';
|
||||
|
||||
const startupPromises = [];
|
||||
|
||||
async function waitForOpts() {
|
||||
// make sure all of the ui is ready and options are loaded
|
||||
const t0 = performance.now();
|
||||
let t1 = performance.now();
|
||||
while (true) {
|
||||
if (t1 - t0 > 120000) {
|
||||
log('waitForOpts timeout');
|
||||
break;
|
||||
}
|
||||
if (window.opts && Object.keys(window.opts).length > 0) {
|
||||
ok = window.opts.theme_type === 'Modern' ? 'uiux_separator_appearance' in window.opts : true;
|
||||
if (ok) {
|
||||
log('waitForOpts', Math.round(t1 - t0));
|
||||
timer('waitForOpts', t1 - t0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
await sleep(100);
|
||||
t1 = performance.now();
|
||||
}
|
||||
}
|
||||
|
||||
async function postStartup() {
|
||||
log('postStartup');
|
||||
// if (window.gradioObserver) window.gradioObserver.disconnect();
|
||||
if (window.hintsObserver) window.hintsObserver.disconnect();
|
||||
logTimers();
|
||||
}
|
||||
|
||||
async function initStartup() {
|
||||
const t0 = performance.now();
|
||||
log('initGradio', Math.round(t0 - appStartTime));
|
||||
timer('initGradio', t0 - appStartTime);
|
||||
log('initUi');
|
||||
if (window.setupLogger) await setupLogger();
|
||||
|
||||
// all items here are non-blocking async calls
|
||||
|
||||
startupPromises.push(initModels());
|
||||
startupPromises.push(getUIDefaults());
|
||||
startupPromises.push(initPromptChecker());
|
||||
startupPromises.push(initContextMenu());
|
||||
startupPromises.push(initDragDrop());
|
||||
startupPromises.push(initAccordions());
|
||||
startupPromises.push(initSettings());
|
||||
startupPromises.push(initImageViewer());
|
||||
startupPromises.push(initGallery());
|
||||
startupPromises.push(initiGenerationParams());
|
||||
startupPromises.push(initChangelog());
|
||||
startupPromises.push(setupControlUI());
|
||||
|
||||
// reconnect server session
|
||||
await reconnectUI();
|
||||
await waitForOpts();
|
||||
|
||||
log('mountURL', window.opts.subpath);
|
||||
if (window.opts.subpath?.length > 0) {
|
||||
window.subpath = window.opts.subpath;
|
||||
window.api = `${window.subpath}/sdapi/v1`;
|
||||
}
|
||||
|
||||
startupPromises.push(initLogMonitor());
|
||||
|
||||
executeCallbacks(uiReadyCallbacks);
|
||||
|
||||
// optinally wait for modern ui
|
||||
if (window.waitForUiReady) await waitForUiReady();
|
||||
|
||||
// post startup tasks that may take longer but are not critical
|
||||
startupPromises.push(initGallery());
|
||||
startupPromises.push(setRefreshInterval());
|
||||
startupPromises.push(setupExtraNetworks());
|
||||
startupPromises.push(initAutocomplete());
|
||||
startupPromises.push(monitorConnection());
|
||||
startupPromises.push(showNetworks());
|
||||
startupPromises.push(setHints());
|
||||
startupPromises.push(applyStyles());
|
||||
startupPromises.push(initIndexDB());
|
||||
startupPromises.push(initTableSorter());
|
||||
|
||||
t1 = performance.now();
|
||||
log('initStartup', Math.round(1000 * (t1 - t0) / 1000000));
|
||||
|
||||
removeSplash();
|
||||
|
||||
await Promise.all(startupPromises);
|
||||
t2 = performance.now();
|
||||
log('initComplete', Math.round(1000 * (t2 - t0) / 1000000));
|
||||
postStartup();
|
||||
}
|
||||
|
||||
onUiLoaded(initStartup);
|
||||
onUiReady(() => log('uiReady'));
|
||||
|
||||
// onAfterUiUpdate(() => log('evt onAfterUiUpdate'));
|
||||
// onUiLoaded(() => log('evt onUiLoaded'));
|
||||
// onOptionsChanged(() => log('evt onOptionsChanged'));
|
||||
// onUiTabChange(() => log('evt onUiTabChange'));
|
||||
// onUiUpdate(() => log('evt onUiUpdate'));
|
||||
@@ -1,9 +0,0 @@
|
||||
function startTrainMonitor() {
|
||||
gradioApp().querySelector('#train_error').innerHTML = '';
|
||||
const id = randomId();
|
||||
const onProgress = (progress) => { gradioApp().getElementById('train_progress').innerHTML = progress.textinfo; };
|
||||
requestProgress(id, gradioApp().getElementById('train_gallery'), null, onProgress, false);
|
||||
const res = Array.from(arguments);
|
||||
res[0] = id;
|
||||
return res;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
function uiOpenSubmenus() {
|
||||
const accordions = Array.from(gradioApp().querySelectorAll('.gradio-accordion'));
|
||||
const states = {};
|
||||
accordions.forEach((el) => {
|
||||
const name = el.querySelector('.label-wrap > span:not(.icon)').innerText.trim();
|
||||
const children = Array.from(el.childNodes);
|
||||
const open = children.filter((c) => c.style?.display === 'block');
|
||||
if (states[name] === undefined) states[name] = open.length > 0;
|
||||
});
|
||||
return states;
|
||||
}
|
||||
|
||||
async function getUIDefaults() {
|
||||
const btn = gradioApp().getElementById('ui_defaults_view');
|
||||
if (!btn) return;
|
||||
const intersectionObserver = new IntersectionObserver((entries) => {
|
||||
if (entries[0].intersectionRatio <= 0) { /* Pass */ }
|
||||
if (entries[0].intersectionRatio > 0) btn.click();
|
||||
});
|
||||
intersectionObserver.observe(btn); // monitor visibility of tab
|
||||
}
|
||||
@@ -71,12 +71,10 @@ def create_docs(app: FastAPI):
|
||||
res = get_swagger_ui_html(
|
||||
title=f'{app.title}: Swagger UI',
|
||||
openapi_url=app.openapi_url,
|
||||
swagger_favicon_url='/file=html/favicon.svg',
|
||||
swagger_css_url='/file=html/swagger.css',
|
||||
swagger_favicon_url='/file=ui/assets/favicon.svg',
|
||||
swagger_css_url='/file=ui/css/swagger.css',
|
||||
swagger_ui_parameters=swagger_ui_parameters,
|
||||
# swagger_extra_css_url='file=html/swagger.css',
|
||||
)
|
||||
# res = inject_css(html.content, 'html/swagger.css')
|
||||
return res
|
||||
|
||||
|
||||
@@ -86,6 +84,6 @@ def create_redocs(app: FastAPI):
|
||||
res = get_redoc_html(
|
||||
title=f'{app.title}: ReDoc',
|
||||
openapi_url=app.openapi_url,
|
||||
redoc_favicon_url='/file=html/favicon.svg',
|
||||
redoc_favicon_url='/file=ui/assets/favicon.svg',
|
||||
)
|
||||
return res
|
||||
|
||||
@@ -73,7 +73,7 @@ def setup_middleware(app: FastAPI, cmd_opts):
|
||||
}
|
||||
if err['code'] == 401 and 'file=' in req.url.path: # dont spam with unauth
|
||||
return JSONResponse(status_code=err['code'], content=jsonable_encoder(err))
|
||||
if err['code'] == 404 and 'file=html/' in req.url.path: # dont spam with locales
|
||||
if err['code'] == 404 and 'file=ui/' in req.url.path: # dont spam with locales
|
||||
return JSONResponse(status_code=err['code'], content=jsonable_encoder(err))
|
||||
if err["code"] == 429: # dont spam with rate limit errors
|
||||
return JSONResponse(status_code=err["code"], content=jsonable_encoder(err))
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# VQA Detection Utilities
|
||||
# Parsing, formatting, and drawing functions for detection results (points, bboxes, gaze)
|
||||
|
||||
import os
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from modules import shared
|
||||
from modules.paths import script_path
|
||||
|
||||
|
||||
def parse_points(result) -> list:
|
||||
@@ -311,7 +313,7 @@ def draw_bounding_boxes(image: Image.Image, detections: list, points: list | Non
|
||||
# Try to load a font, fall back to default if unavailable
|
||||
try:
|
||||
font_size = max(12, int(min(width, height) * 0.02))
|
||||
font_path = shared.opts.font or "javascript/notosans-nerdfont-regular.ttf"
|
||||
font_path = shared.opts.font or os.path.join(script_path, "ui", "fonts", "notosans-nerdfont-regular.ttf")
|
||||
font = ImageFont.truetype(font_path, size=font_size)
|
||||
except Exception:
|
||||
font = ImageFont.load_default()
|
||||
|
||||
@@ -96,7 +96,7 @@ def create_model_cards(all_models: list[CivitModel]) -> str:
|
||||
if image.url and not image.url.lower().endswith('.mp4'):
|
||||
previews.append(image.url)
|
||||
if not previews:
|
||||
previews = ['/sdapi/v1/network/thumb?filename=html/missing.png']
|
||||
previews = ['/sdapi/v1/network/thumb?filename=ui/assets/missing.png']
|
||||
all_cards += card.format(id=model.id, name=model.name, type=model.type, preview=previews[0])
|
||||
html = details + cards.format(cards=all_cards)
|
||||
return html
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
import math
|
||||
from typing import NamedTuple
|
||||
|
||||
@@ -6,6 +7,7 @@ from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from modules import script_callbacks, shared
|
||||
from modules.logger import log
|
||||
from modules.paths import script_path
|
||||
|
||||
|
||||
class Grid(NamedTuple):
|
||||
@@ -143,9 +145,9 @@ class GridAnnotation:
|
||||
|
||||
def get_font(fontsize: float):
|
||||
try:
|
||||
return ImageFont.truetype(shared.opts.font or "javascript/notosans-nerdfont-regular.ttf", fontsize)
|
||||
return ImageFont.truetype(shared.opts.font or os.path.join(script_path, "ui", "fonts", "notosans-nerdfont-regular.ttf"), fontsize)
|
||||
except Exception:
|
||||
return ImageFont.truetype("javascript/notosans-nerdfont-regular.ttf", fontsize)
|
||||
return ImageFont.truetype(os.path.join(script_path, "ui", "fonts", "notosans-nerdfont-regular.ttf"), fontsize)
|
||||
|
||||
|
||||
def draw_grid_annotations(im: Image.Image, width: int, height: int, x_texts: list[list[GridAnnotation]], y_texts: list[list[GridAnnotation]], margin=0, title: list[GridAnnotation] | None = None):
|
||||
|
||||
@@ -1,9 +1,2 @@
|
||||
def hijack_transformers():
|
||||
# transformers>=4.56 flattened CLIPTextModel internals; diffusers single-file loader still expects `text_model`.
|
||||
return
|
||||
try:
|
||||
import transformers
|
||||
if hasattr(transformers, 'CLIPTextModel') and not hasattr(transformers.CLIPTextModel, 'text_model'):
|
||||
transformers.CLIPTextModel.text_model = property(lambda self: self)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -4,7 +4,7 @@ import logging
|
||||
import torch
|
||||
from modules import shared, errors, devices, sd_models, sd_models_utils
|
||||
from modules.logger import log
|
||||
from installer import setup_logging, install
|
||||
from installer import setup_logging
|
||||
|
||||
debug = os.environ.get('SD_COMPILE_DEBUG', None) is not None
|
||||
debug_log = log.trace if debug else lambda *args, **kwargs: None
|
||||
@@ -87,8 +87,8 @@ def optimize_openvino(sd_model, clear_cache=True):
|
||||
|
||||
|
||||
def compile_pruna(sd_model):
|
||||
# TODO
|
||||
# install('pruna') # TODO pruna: enable when it supports transformers==5.5
|
||||
# TODO pruna: enable when it supports transformers==5.5
|
||||
# install('pruna')
|
||||
"""
|
||||
from pruna import smash, SmashConfig
|
||||
smash_config = SmashConfig(["deepcache", "stable_fast"])
|
||||
|
||||
@@ -348,7 +348,7 @@ class StyleDatabase:
|
||||
for fn in style_files:
|
||||
future_items[executor.submit(self.load_style, fn, None)] = fn
|
||||
if self.built_in:
|
||||
fn = os.path.join('html', 'art-styles.json')
|
||||
fn = os.path.join('data', 'art-styles.json')
|
||||
future_items[executor.submit(self.load_style, fn, 'Reference')] = fn
|
||||
for future in concurrent.futures.as_completed(future_items):
|
||||
future.result()
|
||||
|
||||
@@ -19,7 +19,10 @@ gradio_theme = gr.themes.Base()
|
||||
|
||||
|
||||
def list_builtin_themes():
|
||||
files = [os.path.splitext(f)[0] for f in os.listdir('javascript') if f.endswith('.css') and f not in ['base.css', 'sdnext.css', 'style.css']]
|
||||
from modules.paths import script_path
|
||||
folder = os.path.join(script_path, "ui", "css")
|
||||
exclude = ['base.css', 'sdnext.css', 'style.css', 'timesheet.css', 'swagger.css']
|
||||
files = [os.path.splitext(f)[0] for f in os.listdir(folder) if f.endswith('.css') and f not in exclude]
|
||||
return files
|
||||
|
||||
|
||||
|
||||
@@ -564,7 +564,7 @@ def create_settings(cmd_opts):
|
||||
"live_preview_downscale": OptionInfo(True, "Downscale high resolution live previews"),
|
||||
|
||||
"notification_audio_enable": OptionInfo(False, "Play a notification upon completion"),
|
||||
"notification_audio_path": OptionInfo("html/notification.mp3","Path to notification sound", component_args=hide_dirs, folder=True),
|
||||
"notification_audio_path": OptionInfo("ui/assets/notification.mp3","Path to notification sound", component_args=hide_dirs, folder=True),
|
||||
}))
|
||||
|
||||
# --- Postprocessing ---
|
||||
|
||||
@@ -61,8 +61,8 @@ def init_api():
|
||||
if filename is None or len(filename) == 0:
|
||||
return JSONResponse({ "error": "no filename" }, status_code=400)
|
||||
if not os.path.exists(filename) or not os.path.isfile(filename) or os.path.getsize(filename) == 0:
|
||||
return FileResponse('html/missing.png', headers={"Accept-Ranges": "bytes"})
|
||||
if filename.startswith('html/') or filename.startswith('models/'):
|
||||
return FileResponse('ui/assets/missing.png', headers={"Accept-Ranges": "bytes"})
|
||||
if filename.startswith('html/') or filename.startswith('models/') or filename.startswith('data/') or filename.startswith('ui/'):
|
||||
return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
|
||||
if not any(Path(folder).absolute() in Path(filename).absolute().parents for folder in allowed_dirs):
|
||||
return JSONResponse({ "error": f"file {filename}: must be in one of allowed directories" }, status_code=403)
|
||||
@@ -414,7 +414,7 @@ class ExtraNetworksPage:
|
||||
"filename": html.escape(item.get('filename', ''), quote=True),
|
||||
"short": os.path.splitext(os.path.basename(item.get('filename', '')))[0],
|
||||
"tags": '|'.join([item.get('tags')] if isinstance(item.get('tags', {}), str) else list(item.get('tags', {}).keys())),
|
||||
"preview": html.escape(item.get('preview', None) or self.link_preview('html/missing.png')),
|
||||
"preview": html.escape(item.get('preview', None) or self.link_preview('ui/assets/missing.png')),
|
||||
"width": 'var(--card-size)',
|
||||
"height": 'var(--card-size)' if shared.opts.extra_networks_card_square else 'auto',
|
||||
"fit": shared.opts.extra_networks_card_fit,
|
||||
@@ -440,7 +440,7 @@ class ExtraNetworksPage:
|
||||
|
||||
def find_preview_file(self, path: str | None):
|
||||
if path is None:
|
||||
return 'html/missing.png'
|
||||
return 'ui/assets/missing.png'
|
||||
if os.path.join('models', 'Reference') in path:
|
||||
return path
|
||||
exts = ["jpg", "jpeg", "png", "webp", "tiff", "jp2", "jxl"]
|
||||
@@ -457,7 +457,7 @@ class ExtraNetworksPage:
|
||||
if '.thumb.' not in file:
|
||||
self.missing_thumbs.append(file)
|
||||
return file
|
||||
return 'html/missing.png'
|
||||
return 'ui/assets/missing.png'
|
||||
|
||||
def find_preview(self, filename: str):
|
||||
t0 = time.time()
|
||||
@@ -508,7 +508,7 @@ class ExtraNetworksPage:
|
||||
item['preview'] = self.link_preview(found)
|
||||
debug(f'EN mapped-preview: {item["name"]}={found}')
|
||||
if item.get('preview', None) is None:
|
||||
item['preview'] = self.link_preview('html/missing.png')
|
||||
item['preview'] = self.link_preview('ui/assets/missing.png')
|
||||
debug(f'EN missing-preview: {item["name"]}')
|
||||
self.preview_time += time.time() - t0
|
||||
|
||||
@@ -787,19 +787,19 @@ def create_ui(container, button_parent: gr.Button, tabname: str, skip_indexing =
|
||||
|
||||
def fn_save_img(image):
|
||||
if ui.last_item is None or ui.last_item.local_preview is None:
|
||||
return 'html/missing.png'
|
||||
return 'ui/assets/missing.png'
|
||||
images = []
|
||||
if ui.gallery is not None:
|
||||
images = list(ui.gallery.temp_files) # gallery cannot be used as input component so looking at most recently registered temp files
|
||||
if len(images) < 1:
|
||||
log.warning(f'Network no image: item="{ui.last_item.name}"')
|
||||
return 'html/missing.png'
|
||||
return 'ui/assets/missing.png'
|
||||
try:
|
||||
images.sort(key=lambda f: os.path.getmtime(f), reverse=True)
|
||||
image = Image.open(images[0])
|
||||
except Exception as e:
|
||||
log.error(f'Network error opening image: item="{ui.last_item.name}" {e}')
|
||||
return 'html/missing.png'
|
||||
return 'ui/assets/missing.png'
|
||||
fn_delete_img(image)
|
||||
if image.width > 512 or image.height > 512:
|
||||
image = image.convert('RGB')
|
||||
@@ -818,7 +818,7 @@ def create_ui(container, button_parent: gr.Button, tabname: str, skip_indexing =
|
||||
if os.path.exists(file):
|
||||
os.remove(file)
|
||||
log.debug(f'Network delete image: item="{ui.last_item.name}" filename="{file}"')
|
||||
return 'html/missing.png'
|
||||
return 'ui/assets/missing.png'
|
||||
|
||||
def fn_save_desc(desc):
|
||||
if hasattr(ui.last_item, 'type') and ui.last_item.type == 'Style':
|
||||
|
||||
@@ -114,7 +114,7 @@ class ExtraNetworkStyles(extra_networks.ExtraNetwork):
|
||||
super().__init__('style')
|
||||
self.indexes = {}
|
||||
|
||||
def activate(self, p, params_list):
|
||||
def activate(self, p, params_list, *args, **kwargs):
|
||||
for param in params_list:
|
||||
if len(param.items) > 0:
|
||||
style = None
|
||||
|
||||
@@ -19,16 +19,17 @@ def webpath(fn):
|
||||
|
||||
def html_head():
|
||||
head = ''
|
||||
main = ['script.js']
|
||||
main = ['sdnext.mjs']
|
||||
skip = ['login.js']
|
||||
for js in main:
|
||||
script_js = os.path.join(script_path, "javascript", js)
|
||||
# script_js = os.path.join(script_path, 'javascript', js)
|
||||
script_js = os.path.join(script_path, "ui", "dist", js)
|
||||
if '.esm' in js or '.mjs' in js:
|
||||
head += f'<script type="module" src="{webpath(script_js)}"></script>\n'
|
||||
else:
|
||||
head += f'<script type="text/javascript" src="{webpath(script_js)}"></script>\n'
|
||||
added = []
|
||||
for script in scripts_manager.list_scripts("javascript", ".js"):
|
||||
for script in scripts_manager.list_scripts('javascript', ".js"):
|
||||
if script.filename in main or script.filename in skip:
|
||||
continue
|
||||
if '.esm' in script.filename or '.mjs' in script.filename:
|
||||
@@ -36,7 +37,7 @@ def html_head():
|
||||
else:
|
||||
head += f'<script type="text/javascript" src="{webpath(script.path)}"></script>\n'
|
||||
added.append(script.path)
|
||||
for script in scripts_manager.list_scripts("javascript", ".mjs"):
|
||||
for script in scripts_manager.list_scripts('javascript', ".mjs"):
|
||||
head += f'<script type="module" src="{webpath(script.path)}"></script>\n'
|
||||
added.append(script.path)
|
||||
added = [a.replace(script_path, '').replace('\\', '/') for a in added]
|
||||
@@ -54,7 +55,8 @@ def html_body():
|
||||
|
||||
|
||||
def html_login():
|
||||
fn = os.path.join(script_path, "javascript", "login.js")
|
||||
# fn = os.path.join(script_path, 'javascript', 'login.js')
|
||||
fn = os.path.join(script_path, "ui", "js", "login.js")
|
||||
with open(fn, encoding='utf8') as f:
|
||||
inline = f.read()
|
||||
js = f'<script type="text/javascript">{inline}</script>\n'
|
||||
@@ -67,7 +69,8 @@ def html_css(css: list[str]):
|
||||
|
||||
head = ''
|
||||
for cssfile in css:
|
||||
f = os.path.join(script_path, 'javascript', cssfile)
|
||||
# f = os.path.join(script_path, 'javascript', cssfile)
|
||||
f = os.path.join(script_path, 'ui', 'css', cssfile)
|
||||
if os.path.isfile(f):
|
||||
head += stylesheet(f)
|
||||
for cssfile in scripts_manager.list_files_with_name("style.css"):
|
||||
@@ -77,7 +80,8 @@ def html_css(css: list[str]):
|
||||
|
||||
usercss = os.path.join(data_path, "user.css") if os.path.exists(os.path.join(data_path, "user.css")) else None
|
||||
if shared.opts.theme_type == 'Standard':
|
||||
themecss = os.path.join(script_path, "javascript", f"{shared.opts.gradio_theme}.css")
|
||||
# themecss = os.path.join(script_path, 'javascript', f"{shared.opts.gradio_theme}.css")
|
||||
themecss = os.path.join(script_path, 'ui', 'css', f"{shared.opts.gradio_theme}.css")
|
||||
if os.path.exists(themecss):
|
||||
head += stylesheet(themecss)
|
||||
log.debug(f'UI theme: css="{themecss}" base="{css}" user="{usercss}"')
|
||||
@@ -98,7 +102,7 @@ def html_css(css: list[str]):
|
||||
|
||||
def reload_javascript():
|
||||
title = '<title>SD.Next</title>'
|
||||
manifest = f'<link rel="manifest" href="{webpath(os.path.join(script_path, "html", "manifest.json"))}">'
|
||||
manifest = f'<link rel="manifest" href="{webpath(os.path.join(script_path, "ui", "manifest", "manifest.json"))}">'
|
||||
login = html_login()
|
||||
js = html_head()
|
||||
|
||||
@@ -121,8 +125,8 @@ def reload_javascript():
|
||||
for line in lines:
|
||||
if 'meta name="twitter:' in line:
|
||||
res.body = res.body.replace(line.encode("utf8"), b'')
|
||||
if 'iframeResizer.contentWindow.min.js' in line:
|
||||
res.body = res.body.replace(line.encode("utf8"), b'src="file=javascript/iframeResizer.min.js"')
|
||||
if 'iframeResizer.contentWindow' in line:
|
||||
res.body = res.body.replace(line.encode("utf8"), b'src="file=ui/js/iframeResizer.js"')
|
||||
res.init_headers()
|
||||
return res
|
||||
|
||||
|
||||
@@ -19,45 +19,57 @@
|
||||
"start": ". venv/bin/activate; python launch.py --debug",
|
||||
"localize": "node cli/localize.js",
|
||||
"packages": ". venv/bin/activate && pip install --upgrade accelerate huggingface_hub hf_xet safetensors tokenizers peft pytorch_lightning pylint ruff",
|
||||
"format": ". venv/bin/activate && pre-commit run --all-files",
|
||||
"format-win": "venv\\scripts\\activate && pre-commit run --all-files",
|
||||
"eslint": "eslint javascript/",
|
||||
"eslint-ui": "cd extensions-builtin/sdnext-modernui && eslint . javascript/",
|
||||
"eslint-kanvas": "cd extensions-builtin/sdnext-kanvas && eslint . src/",
|
||||
"ruff": ". venv/bin/activate && ruff check",
|
||||
"ruff-win": "venv\\scripts\\activate && ruff check",
|
||||
"pylint": ". venv/bin/activate && pylint *.py modules/ pipelines/ scripts/ extensions-builtin/ | grep -v '^*'",
|
||||
"pylint-win": "venv\\scripts\\activate && pylint *.py modules/ pipelines/ scripts/ extensions-builtin/",
|
||||
"pyright": ". venv/bin/activate && pyright --threads 4",
|
||||
"pyright-win": "venv\\scripts\\activate && pyright --threads 4",
|
||||
"ty": ". venv/bin/activate && ty check --force-exclude",
|
||||
"ty-win": "venv\\scripts\\activate && ty check --force-exclude",
|
||||
"lint": "npm run format && npm run eslint && npm run eslint-ui && npm run eslint-kanvas && npm run ruff && npm run pylint",
|
||||
"lint-win": "npm run format-win && npm run eslint && npm run eslint-ui && npm run eslint-kanvas && npm run ruff-win && npm run pylint-win",
|
||||
"precommit": ". venv/bin/activate && pre-commit run --all-files",
|
||||
"lint": "npm run precommit && npm run eslint && npm run tsc && npm run ruff && npm run pylint",
|
||||
"test": ". venv/bin/activate; python launch.py --debug --test",
|
||||
"todo": "grep -oIPR 'TODO.*' *.py modules/ pipelines/ | sort -u",
|
||||
"debug": "grep -ohIPR 'SD_.*?_DEBUG' *.py modules/ pipelines/ | sort -u"
|
||||
"debug": "grep -ohIPR 'SD_.*?_DEBUG' *.py modules/ pipelines/ | sort -u",
|
||||
"eslint:modernui": "cd extensions-builtin/sdnext-modernui && eslint",
|
||||
"eslint:kanvas": "cd extensions-builtin/sdnext-kanvas && eslint",
|
||||
"eslint:core": "eslint",
|
||||
"eslint": "npm run eslint:core && npm run eslint:modernui && npm run eslint:kanvas",
|
||||
"tsc:modernui": "cd extensions-builtin/sdnext-modernui && tsc --noEmit",
|
||||
"tsc:kanvas": "cd extensions-builtin/sdnext-kanvas && tsc --noEmit",
|
||||
"tsc:core": "cd ui && tsc --noEmit",
|
||||
"tsc": "npm run tsc:modernui && npm run tsc:kanvas && npm run tsc:core",
|
||||
"build:modernui": "cd extensions-builtin/sdnext-modernui && build --profile production",
|
||||
"build:kanvas": "cd extensions-builtin/sdnext-kanvas && build --profile production",
|
||||
"build:core": "build --profile production --config ui/.build.json",
|
||||
"build": "npm run build:modernui && npm run build:kanvas && npm run build:core",
|
||||
"ui": "npm run eslint && npm run tsc && npm run build",
|
||||
"dev:modernui": "cd extensions-builtin/sdnext-modernui && build --profile development",
|
||||
"dev:kanvas": "cd extensions-builtin/sdnext-kanvas && build --profile development",
|
||||
"dev:core": "build --profile development --config ui/.build.json",
|
||||
"ruff": ". venv/bin/activate && ruff check",
|
||||
"pylint": ". venv/bin/activate && pylint *.py modules/ pipelines/ scripts/ extensions-builtin/ | grep -v '^*'",
|
||||
"pyright": ". venv/bin/activate && pyright --threads 4",
|
||||
"ty": ". venv/bin/activate && ty check --force-exclude"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/css": "^0.14.1",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@eslint/json": "^0.14.0",
|
||||
"@eslint/markdown": "^7.5.1",
|
||||
"@google/genai": "^1.41.0",
|
||||
"@html-eslint/eslint-plugin": "^0.52.1",
|
||||
"@eslint/compat": "^2.1.0",
|
||||
"@eslint/css": "^1.2.0",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@eslint/json": "^1.2.0",
|
||||
"@eslint/markdown": "^8.0.1",
|
||||
"@google/genai": "^2.4.0",
|
||||
"@html-eslint/eslint-plugin": "^0.60.0",
|
||||
"@stylistic/eslint-plugin": "^5.10.0",
|
||||
"@types/jquery": "^4.0.0",
|
||||
"@types/node": "^25.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.59.4",
|
||||
"@typescript-eslint/parser": "^8.59.4",
|
||||
"@vladmandic/build": "^0.10.3",
|
||||
"argparse": "^2.0.1",
|
||||
"debug": "^4.4.3",
|
||||
"esbuild": "^0.27.2",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-airbnb-extended": "^3.0.0",
|
||||
"eslint-plugin-promise": "^7.2.1",
|
||||
"globals": "^17.0.0"
|
||||
},
|
||||
"//": {
|
||||
"disabled": {
|
||||
"typescript": "^5.9.3",
|
||||
"@types/node": "^25.0.3"
|
||||
}
|
||||
"esbuild": "^0.28.0",
|
||||
"eslint": "^10.4.0",
|
||||
"eslint-config-airbnb-extended": "^3.1.0",
|
||||
"eslint-plugin-promise": "^7.3.0",
|
||||
"exifr": "^7.1.3",
|
||||
"globals": "^17.6.0",
|
||||
"jquery": "^4.0.0",
|
||||
"jquery-sparkline": "^2.4.0",
|
||||
"panzoom": "^9.4.4",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@ node cli/api-pulid.js
|
||||
|
||||
source venv/bin/activate
|
||||
echo image-exif
|
||||
python cli/api-info.py --input html/logo-bg-0.jpg
|
||||
python cli/api-info.py --input ui/assets/logo-bg-0.jpg
|
||||
echo txt2img
|
||||
python cli/api-txt2img.py --detailer --prompt "girl on a mountain" --seed 42 --sampler DEIS --width 1280 --height 800 --steps 10
|
||||
echo img2img
|
||||
python cli/api-img2img.py --init html/logo-bg-0.jpg --steps 10
|
||||
python cli/api-img2img.py --init ui/assets/logo-bg-0.jpg --steps 10
|
||||
echo inpaint
|
||||
python cli/api-img2img.py --init html/logo-bg-0.jpg --mask html/logo-dark.png --steps 10
|
||||
python cli/api-img2img.py --init ui/assets/logo-bg-0.jpg --mask ui/assets/logo-dark.png --steps 10
|
||||
echo upscale
|
||||
python cli/api-upscale.py --input html/logo-bg-0.jpg --upscaler "ESRGAN 4x Valar" --scale 4
|
||||
python cli/api-upscale.py --input ui/assets/logo-bg-0.jpg --upscaler "ESRGAN 4x Valar" --scale 4
|
||||
echo vqa
|
||||
python cli/api-vqa.py --input html/logo-bg-0.jpg
|
||||
python cli/api-vqa.py --input ui/assets/logo-bg-0.jpg
|
||||
echo detailer
|
||||
python cli/api-detect.py --image html/invoked.jpg
|
||||
echo faceid
|
||||
@@ -23,10 +23,10 @@ python cli/api-faceid.py --face html/simple-dark.jpg
|
||||
echo control-txt2img
|
||||
python cli/api-control.py --prompt "cute robot"
|
||||
echo control-img2img
|
||||
python cli/api-control.py --prompt "cute robot" --input html/logo-bg-0.jpg
|
||||
python cli/api-control.py --prompt "cute robot" --input ui/assets/logo-bg-0.jpg
|
||||
echo control-ipsadapter
|
||||
python cli/api-control.py --prompt "cute robot" --ipadapter "Base SDXL:html/logo-bg-0.jpg:0.8"
|
||||
python cli/api-control.py --prompt "cute robot" --ipadapter "Base SDXL:ui/assets/logo-bg-0.jpg:0.8"
|
||||
echo control-preprocess
|
||||
python cli/api-preprocess.py --input html/logo-bg-0.jpg --model "Zoe Depth"
|
||||
python cli/api-preprocess.py --input ui/assets/logo-bg-0.jpg --model "Zoe Depth"
|
||||
echo control-controlnet
|
||||
python cli/api-control.py --prompt "cute robot" --input html/logo-bg-0.jpg --type controlnet --control "Zoe Depth:Xinsir Union XL:0.5"
|
||||
python cli/api-control.py --prompt "cute robot" --input ui/assets/logo-bg-0.jpg --type controlnet --control "Zoe Depth:Xinsir Union XL:0.5"
|
||||
|
||||
@@ -15,7 +15,7 @@ if __name__ == "__main__":
|
||||
labels = []
|
||||
override = None
|
||||
try:
|
||||
with open('html/locale_en.json', 'r', encoding="utf-8") as f:
|
||||
with open('ui/locale/locale_en.json', 'r', encoding="utf-8") as f:
|
||||
locale = json.load(f)
|
||||
for v in locale.values():
|
||||
for item in v:
|
||||
|
||||
@@ -92,7 +92,7 @@ async function localize() {
|
||||
};
|
||||
console.log('params:', params);
|
||||
|
||||
const raw = fs.readFileSync('html/locale_en.json');
|
||||
const raw = fs.readFileSync('ui/locale/locale_en.json');
|
||||
console.log('raw:', { bytes: raw.length });
|
||||
const json = JSON.parse(raw);
|
||||
console.log('targets:', { lang: Object.keys(languages), count: Object.keys(languages).length });
|
||||
@@ -102,7 +102,7 @@ async function localize() {
|
||||
const lang = languages[locale];
|
||||
const langPrompt = prompt.replace('{language}', lang).trim();
|
||||
const output = {};
|
||||
const fn = `html/locale_${locale}.json`;
|
||||
const fn = `ui/locale/locale_${locale}.json`;
|
||||
if (fs.existsSync(fn)) {
|
||||
console.log('skip:', { index, locale, lang, fn });
|
||||
continue;
|
||||
|
||||
@@ -35,7 +35,7 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
# Default test images (in order of preference)
|
||||
DEFAULT_TEST_IMAGES = [
|
||||
'html/sdnext-robot-2k.jpg',
|
||||
'html/favicon.png',
|
||||
'ui/assets/favicon.png',
|
||||
'extensions-builtin/sdnext-modernui/html/logo.png',
|
||||
]
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ FACE_TEST_IMAGES = [
|
||||
# Fallback images (no guaranteed faces)
|
||||
FALLBACK_IMAGES = [
|
||||
'html/sdnext-robot-2k.jpg',
|
||||
'html/favicon.png',
|
||||
'ui/assets/favicon.png',
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from rich import print # pylint: disable=redefined-builtin
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.argv.pop(0)
|
||||
fn = sys.argv[0] if len(sys.argv) > 0 else 'html/locale_en.json'
|
||||
fn = sys.argv[0] if len(sys.argv) > 0 else 'ui/locale/locale_en.json'
|
||||
if not os.path.isfile(fn):
|
||||
print(f'File not found: {fn}')
|
||||
sys.exit(1)
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"log": {
|
||||
"enabled": true,
|
||||
"debug": false,
|
||||
"console": true,
|
||||
"output": "build.log"
|
||||
},
|
||||
"profiles": {
|
||||
"production": ["compile"],
|
||||
"development": ["serve", "watch", "compile"]
|
||||
},
|
||||
"watch": {
|
||||
"locations": ["ui"]
|
||||
},
|
||||
"serve": {
|
||||
"httpPort": 8000,
|
||||
"documentRoot": ".",
|
||||
"defaultFolder": ""
|
||||
},
|
||||
"build": {
|
||||
"global": {
|
||||
"target": "es2024",
|
||||
"sourcemap": true,
|
||||
"banner": { "js": "/*\n SD.Next core UI bundle — generated by @vladmandic/build\n*/\n" }
|
||||
},
|
||||
"production": {
|
||||
"minify": false
|
||||
},
|
||||
"development": {
|
||||
"minify": false
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"name": "build module",
|
||||
"input": "ui/index.ts",
|
||||
"output": "ui/dist/sdnext.mjs",
|
||||
"platform": "node",
|
||||
"external": ["typedoc", "typescript", "eslint", "esbuild", "fsevents"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"typescript": {
|
||||
"module": "es2024",
|
||||
"target": "es2024",
|
||||
"typeRoots": ["node_modules/@types"],
|
||||
"lib": ["lib.esnext.d.ts", "lib.dom.d.ts"],
|
||||
"baseUrl": "./",
|
||||
"paths": { "tslib": ["node_modules/tslib/tslib.d.ts"] },
|
||||
"sourceMap": true,
|
||||
"noEmitOnError": false,
|
||||
"emitDeclarationOnly": false,
|
||||
"declaration": false,
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"importHelpers": true,
|
||||
"pretty": true,
|
||||
"removeComments": false,
|
||||
"skipLibCheck": true,
|
||||
"listEmittedFiles": true,
|
||||
"allowUnreachableCode": false,
|
||||
"allowUnusedLabels": false,
|
||||
"alwaysStrict": true,
|
||||
"experimentalDecorators": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": false,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": true,
|
||||
"preserveConstEnums": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"no-restricted-syntax": "off"
|
||||
},
|
||||
"lint": {
|
||||
"locations": ["ui/**/*.ts"],
|
||||
"env": { "browser": true, "commonjs": true, "node": true, "es2024": true },
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": { "ecmaVersion": 2024 },
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended"],
|
||||
"ignorePatterns": ["vendor/**", "dist/**"],
|
||||
"rules": {
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-shadow": "error",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"dot-notation": "off",
|
||||
"func-names": "off",
|
||||
"guard-for-in": "off",
|
||||
"import/extensions": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"lines-between-class-members": "off",
|
||||
"newline-per-chained-call": "off",
|
||||
"no-async-promise-executor": "off",
|
||||
"no-await-in-loop": "off",
|
||||
"no-bitwise": "off",
|
||||
"no-case-declarations": "off",
|
||||
"no-continue": "off",
|
||||
"no-plusplus": "off",
|
||||
"object-curly-newline": "off",
|
||||
"prefer-destructuring": "off",
|
||||
"prefer-template": "off",
|
||||
"promise/always-return": "off",
|
||||
"promise/catch-or-return": "off",
|
||||
"radix": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-restricted-syntax": "off",
|
||||
"no-return-assign": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,28 @@
|
||||
let currentWidth = null;
|
||||
let currentHeight = null;
|
||||
let arFrameTimeout = null;
|
||||
import { gradioApp, onAfterUiUpdate } from './script';
|
||||
import { get_tab_index } from './ui';
|
||||
|
||||
function dimensionChange(e, is_width, is_height) {
|
||||
if (is_width) currentWidth = e.target.value * 1.0;
|
||||
if (is_height) currentHeight = e.target.value * 1.0;
|
||||
const inImg2img = gradioApp().querySelector('#tab_img2img').style.display === 'block';
|
||||
let currentWidth: number | null = null;
|
||||
let currentHeight: number | null = null;
|
||||
let arFrameTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function dimensionChange(e: Event, isWidth: boolean, isHeight: boolean): void {
|
||||
const { target } = e;
|
||||
if (!(target instanceof HTMLInputElement)) return;
|
||||
if (isWidth) currentWidth = Number(target.value);
|
||||
if (isHeight) currentHeight = Number(target.value);
|
||||
const tabImg2img = gradioApp().querySelector('#tab_img2img');
|
||||
if (!(tabImg2img instanceof HTMLElement)) return;
|
||||
const inImg2img = tabImg2img.style.display === 'block';
|
||||
if (!inImg2img) return;
|
||||
let targetElement = null;
|
||||
let targetElement: HTMLImageElement | null = null;
|
||||
const tabIndex = get_tab_index('mode_img2img');
|
||||
if (tabIndex === 0) targetElement = gradioApp().querySelector('#img2img_image div[data-testid=image] img'); // img2img
|
||||
else if (tabIndex === 1) targetElement = gradioApp().querySelector('#img2img_sketch div[data-testid=image] img'); // Sketch
|
||||
else if (tabIndex === 2) targetElement = gradioApp().querySelector('#img2maskimg div[data-testid=image] img'); // Inpaint
|
||||
else if (tabIndex === 3) targetElement = gradioApp().querySelector('#composite div[data-testid=image] img'); // Inpaint sketch
|
||||
|
||||
if (targetElement) {
|
||||
let arPreviewRect = gradioApp().querySelector('#imageARPreview');
|
||||
if (targetElement && currentWidth && currentHeight) {
|
||||
let arPreviewRect = gradioApp().querySelector('#imageARPreview') as HTMLElement | null;
|
||||
if (!arPreviewRect) {
|
||||
arPreviewRect = document.createElement('div');
|
||||
arPreviewRect.id = 'imageARPreview';
|
||||
@@ -48,23 +55,24 @@ function dimensionChange(e, is_width, is_height) {
|
||||
}
|
||||
}
|
||||
|
||||
function aspectRatioCallback() {
|
||||
export function aspectRatioCallback(): void {
|
||||
const arPreviewRect = gradioApp().querySelector('#imageARPreview');
|
||||
if (arPreviewRect) arPreviewRect.style.display = 'none';
|
||||
if (arPreviewRect instanceof HTMLElement) arPreviewRect.style.display = 'none';
|
||||
const tabImg2img = gradioApp().querySelector('#tab_img2img');
|
||||
if (tabImg2img) {
|
||||
if (tabImg2img instanceof HTMLElement) {
|
||||
const inImg2img = tabImg2img.style.display === 'block';
|
||||
if (inImg2img) {
|
||||
const inputs = gradioApp().querySelectorAll('input');
|
||||
inputs.forEach((e) => {
|
||||
const is_width = e.parentElement.id === 'img2img_width';
|
||||
const is_height = e.parentElement.id === 'img2img_height';
|
||||
if ((is_width || is_height) && !e.classList.contains('scrollwatch')) {
|
||||
e.addEventListener('input', (evt) => { dimensionChange(evt, is_width, is_height); });
|
||||
if (!(e instanceof HTMLInputElement) || !(e.parentElement instanceof HTMLElement)) return;
|
||||
const isWidth = e.parentElement.id === 'img2img_width';
|
||||
const isHeight = e.parentElement.id === 'img2img_height';
|
||||
if ((isWidth || isHeight) && !e.classList.contains('scrollwatch')) {
|
||||
e.addEventListener('input', (evt) => { dimensionChange(evt, isWidth, isHeight); });
|
||||
e.classList.add('scrollwatch');
|
||||
}
|
||||
if (is_width) currentWidth = e.value * 1.0;
|
||||
if (is_height) currentHeight = e.value * 1.0;
|
||||
if (isWidth) currentWidth = Number(e.value);
|
||||
if (isHeight) currentHeight = Number(e.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 337 KiB After Width: | Height: | Size: 337 KiB |
@@ -0,0 +1,43 @@
|
||||
import { log, error } from './logger';
|
||||
|
||||
interface TokenResponse {
|
||||
user?: string;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
let user: string | undefined;
|
||||
let token: string | undefined;
|
||||
|
||||
export async function getToken(): Promise<{ user: string | undefined; token: string | undefined }> {
|
||||
if (token === undefined || user === undefined) {
|
||||
const res = await fetch(`${window.subpath}/token`);
|
||||
if (res.ok) {
|
||||
const data = (await res.json()) as TokenResponse;
|
||||
user = data.user;
|
||||
token = data.token;
|
||||
log('getToken', user);
|
||||
}
|
||||
}
|
||||
return { user, token };
|
||||
}
|
||||
|
||||
export async function authFetch(url: RequestInfo | URL, options: RequestInit = {}): Promise<Response | undefined> {
|
||||
await getToken();
|
||||
if (user && token) {
|
||||
const encoded = btoa(`${user}:${token}`);
|
||||
const headers = new Headers(options.headers);
|
||||
headers.set('Authorization', `Basic ${encoded}`);
|
||||
options.headers = headers;
|
||||
}
|
||||
let res: Response | undefined;
|
||||
try {
|
||||
res = await fetch(url, options);
|
||||
if (!res.ok) error('fetch', { status: res?.status || 503, url, user, token });
|
||||
} catch (err) {
|
||||
if (navigator.onLine) {
|
||||
error('fetch', { status: res?.status || 503, url, user, token, error: err });
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
window.authFetch = authFetch;
|
||||
@@ -1,3 +1,9 @@
|
||||
import { xnEngine, lowerBound } from './autocomplete_xn';
|
||||
import { log } from './logger';
|
||||
import { gradioApp, onAfterUiUpdate, onOptionsChanged, executeCallbacks, optionsChangedCallbacks } from './script';
|
||||
import { timer } from './timers';
|
||||
import { updateInput } from './ui';
|
||||
|
||||
/*
|
||||
* Tag autocomplete for SD.Next prompt textareas.
|
||||
*
|
||||
@@ -52,22 +58,21 @@ const KIND_GLYPHS = {
|
||||
|
||||
let active = false;
|
||||
|
||||
// -- Utilities (ported from Enso) --
|
||||
|
||||
/** Binary search for the first tag where tag.name >= prefix. */
|
||||
function lowerBound(tags, prefix) {
|
||||
let lo = 0;
|
||||
let hi = tags.length;
|
||||
while (lo < hi) {
|
||||
const mid = (lo + hi) >>> 1;
|
||||
if (tags[mid].name < prefix) lo = mid + 1;
|
||||
else hi = mid;
|
||||
}
|
||||
return lo;
|
||||
interface TagResult {
|
||||
name: string;
|
||||
display: string;
|
||||
category: number | string;
|
||||
count: number;
|
||||
aliases?: string[];
|
||||
matchedVia?: 'alias' | 'translation';
|
||||
matchedAlias?: string;
|
||||
matchedTerm?: string;
|
||||
}
|
||||
|
||||
// -- Utilities (ported from Enso) --
|
||||
|
||||
/** Format post count as abbreviated string. */
|
||||
function formatCount(count) {
|
||||
function formatCount(count: number): string {
|
||||
if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M`;
|
||||
if (count >= 1_000) return `${Math.round(count / 1_000)}k`;
|
||||
return String(count);
|
||||
@@ -78,13 +83,13 @@ function formatCount(count) {
|
||||
* offscreen mirror div. Styles and width are re-read from the textarea on
|
||||
* every call so resized textareas are handled correctly.
|
||||
*/
|
||||
let caretMirror = null;
|
||||
let caretMarker = null;
|
||||
let caretMirror: HTMLDivElement | null = null;
|
||||
let caretMarker: HTMLSpanElement | null = null;
|
||||
const MIRROR_PROPS = ['fontFamily', 'fontSize', 'fontWeight', 'fontStyle',
|
||||
'lineHeight', 'letterSpacing', 'wordSpacing', 'textTransform',
|
||||
'padding', 'border', 'boxSizing'];
|
||||
|
||||
function caretViewportY(textarea) {
|
||||
function caretViewportY(textarea: HTMLTextAreaElement): number {
|
||||
if (!caretMirror) {
|
||||
caretMirror = document.createElement('div');
|
||||
caretMirror.className = 'autocomplete-mirror';
|
||||
@@ -109,7 +114,14 @@ function caretViewportY(textarea) {
|
||||
// -- TagIndex --
|
||||
|
||||
class TagIndex {
|
||||
constructor(data) {
|
||||
categories: Record<string, { name?: string; color?: string }>;
|
||||
tags: TagResult[];
|
||||
aliasEntries: { name: string; display: string; tag: TagResult }[];
|
||||
translations: Map<string, { canonical: string; foreign: string }>;
|
||||
tagByName: Map<string, TagResult>;
|
||||
translationEntries: { name: string; foreign: string; canonical: string }[];
|
||||
|
||||
constructor(data: any) {
|
||||
this.categories = data.categories || {};
|
||||
// Tuples are [name, catId, count] or [name, catId, count, aliases]. Default `aliases = []`
|
||||
// keeps legacy 3-tuple dictionaries working unchanged.
|
||||
@@ -147,7 +159,7 @@ class TagIndex {
|
||||
}
|
||||
|
||||
/** Prefix search with binary search across canonical names and aliases. Returns matches sorted by count descending. */
|
||||
search(prefix, limit = 20) {
|
||||
search(prefix: string, limit = 20): TagResult[] {
|
||||
const query = prefix.toLowerCase().replace(/ /g, '_');
|
||||
if (!query) return [];
|
||||
// Canonical prefix matches
|
||||
@@ -233,8 +245,9 @@ const engine = {
|
||||
// Extract category colors from first loaded file
|
||||
if (data.categories) {
|
||||
Object.entries(data.categories).forEach(([id, cat]) => {
|
||||
if (cat.color) this.categoryColors[id] = cat.color;
|
||||
if (cat.name) this.categoryNames[id] = cat.name;
|
||||
const category: any = cat;
|
||||
if (category.color) this.categoryColors[id] = category.color;
|
||||
if (category.name) this.categoryNames[id] = category.name;
|
||||
});
|
||||
}
|
||||
const t1 = performance.now();
|
||||
@@ -398,6 +411,7 @@ const dropdown = {
|
||||
textarea: null,
|
||||
query: '',
|
||||
visible: false,
|
||||
resizeObserver: null as ResizeObserver | null,
|
||||
|
||||
init() {
|
||||
this.el = document.createElement('div');
|
||||
@@ -426,7 +440,7 @@ const dropdown = {
|
||||
if (results.length === 0) { this.hide(); return; }
|
||||
// Switching textareas: clear prior state so a stale render can't leak across.
|
||||
if (this.textarea && this.textarea !== textarea) this.hide();
|
||||
if (this.textarea !== textarea) this.resizeObserver.observe(textarea);
|
||||
if (this.textarea !== textarea) this.resizeObserver?.observe(textarea);
|
||||
this.results = results;
|
||||
this.textarea = textarea;
|
||||
this.query = query || '';
|
||||
@@ -438,7 +452,7 @@ const dropdown = {
|
||||
},
|
||||
|
||||
hide() {
|
||||
if (this.textarea) this.resizeObserver.unobserve(this.textarea);
|
||||
if (this.textarea) this.resizeObserver?.unobserve(this.textarea);
|
||||
this.textarea = null;
|
||||
this.el.style.display = 'none';
|
||||
this.visible = false;
|
||||
@@ -597,15 +611,15 @@ function onInput(textarea) {
|
||||
debounceTimer = setTimeout(() => {
|
||||
let results;
|
||||
if (info.mode === 'lora') {
|
||||
results = window.autocompleteXn ? window.autocompleteXn.searchLoras(info.word) : [];
|
||||
results = xnEngine.searchLoras(info.word);
|
||||
} else if (info.mode === 'wildcard') {
|
||||
results = window.autocompleteXn ? window.autocompleteXn.searchWildcards(info.word) : [];
|
||||
results = xnEngine.searchWildcards(info.word);
|
||||
} else if (info.mode === 'artist') {
|
||||
// `@` trigger: tag-search filtered to the artist category. The category-1 color carries the visual cue.
|
||||
results = engine.searchAll(info.word).filter((t) => t.category === ARTIST_CATEGORY_ID);
|
||||
} else {
|
||||
const tagResults = engine.searchAll(info.word);
|
||||
const embedResults = window.autocompleteXn ? window.autocompleteXn.searchEmbeddings(info.word) : [];
|
||||
const embedResults = xnEngine.searchEmbeddings(info.word);
|
||||
// Embeddings fold into tag-mode results (a1111 tagcomplete parity).
|
||||
results = [...embedResults, ...tagResults];
|
||||
}
|
||||
@@ -740,7 +754,7 @@ function patchConfigBridge() {
|
||||
|
||||
// -- Initialization --
|
||||
|
||||
async function initAutocomplete() {
|
||||
export async function initAutocomplete() {
|
||||
const t0 = performance.now();
|
||||
const enabled = window.opts?.autocomplete_enabled || [];
|
||||
active = window.opts?.autocomplete_active || false;
|
||||
@@ -768,7 +782,7 @@ async function initAutocomplete() {
|
||||
document.head.appendChild(style);
|
||||
dropdown.init();
|
||||
await engine.loadEnabled();
|
||||
if (window.autocompleteXn) window.autocompleteXn.loadAll();
|
||||
xnEngine.loadAll();
|
||||
// Attach to all prompt textareas; even if no dictionaries loaded yet, they may be enabled later via script UI
|
||||
let attached = 0;
|
||||
PROMPT_IDS.forEach((id) => {
|
||||
@@ -791,7 +805,7 @@ async function initAutocomplete() {
|
||||
active = newActive;
|
||||
patchActiveButton();
|
||||
}
|
||||
if (window.autocompleteXn) window.autocompleteXn.loadAll();
|
||||
xnEngine.loadAll();
|
||||
}
|
||||
onOptionsChanged(optionsChangedCallback);
|
||||
// Watch for config updates from the script UI bridge
|
||||
@@ -1,19 +1,54 @@
|
||||
import { log } from './logger';
|
||||
|
||||
/*
|
||||
* Extra-networks completion for SD.Next prompt textareas.
|
||||
*
|
||||
* Companion to autocomplete.js: exposes sorted indices for LoRAs, embeddings, and wildcards,
|
||||
* each backed by an existing enumeration endpoint. Dispatch and insertion are driven from
|
||||
* autocomplete.js via the mode returned by getCurrentWord().
|
||||
*
|
||||
* This file relies on globals declared in autocomplete.js (lowerBound, log, engine).
|
||||
*/
|
||||
|
||||
/* global lowerBound */
|
||||
interface XnItem {
|
||||
name: string;
|
||||
display?: string;
|
||||
}
|
||||
|
||||
interface SearchItem {
|
||||
name: string;
|
||||
display: string;
|
||||
}
|
||||
|
||||
type SearchResult = SearchItem & { kind: 'lora' | 'embed' | 'wildcard' };
|
||||
|
||||
/** Binary search for the first item where item.name >= query. */
|
||||
export function lowerBound(items: { name: string }[], query: string): number {
|
||||
let lo = 0;
|
||||
let hi = items.length;
|
||||
while (lo < hi) {
|
||||
const mid = (lo + hi) >>> 1;
|
||||
if (items[mid].name < query) lo = mid + 1;
|
||||
else hi = mid;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
interface XnEngine {
|
||||
lora: XnIndex;
|
||||
embed: XnIndex;
|
||||
wildcard: XnIndex;
|
||||
fetchJson(path: string): Promise<unknown>;
|
||||
loadAll(): Promise<void>;
|
||||
searchLoras(prefix: string, limit?: number): SearchResult[];
|
||||
searchEmbeddings(prefix: string, limit?: number): SearchResult[];
|
||||
searchWildcards(prefix: string, limit?: number): SearchResult[];
|
||||
}
|
||||
|
||||
// -- Indices --
|
||||
|
||||
class XnIndex {
|
||||
constructor(items) {
|
||||
items: SearchItem[];
|
||||
|
||||
constructor(items: XnItem[]) {
|
||||
// items: [{ name, display }]. Sorted in-place by lowercase name.
|
||||
this.items = items.map(({ name, display }) => ({
|
||||
name: String(name).toLowerCase(),
|
||||
@@ -22,7 +57,7 @@ class XnIndex {
|
||||
this.items.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
search(prefix, limit = 20) {
|
||||
search(prefix: string, limit = 20): SearchItem[] {
|
||||
const query = String(prefix).toLowerCase();
|
||||
// Empty query returns the first `limit` items so `<lora:` or `__` alone shows a browsable list.
|
||||
if (!query) return this.items.slice(0, limit);
|
||||
@@ -44,7 +79,7 @@ class XnIndex {
|
||||
|
||||
// -- Engine --
|
||||
|
||||
const xnEngine = {
|
||||
export const xnEngine: XnEngine = {
|
||||
lora: new XnIndex([]),
|
||||
embed: new XnIndex([]),
|
||||
wildcard: new XnIndex([]),
|
||||
@@ -64,23 +99,27 @@ const xnEngine = {
|
||||
// LoRAs: [{name, alias, path, metadata}, ...]
|
||||
const loraData = await this.fetchJson('/loras');
|
||||
if (Array.isArray(loraData)) {
|
||||
const items = [];
|
||||
const items: XnItem[] = [];
|
||||
for (const lo of loraData) {
|
||||
if (lo?.name) items.push({ name: lo.name });
|
||||
if (lo?.alias && lo.alias !== lo.name) items.push({ name: lo.alias });
|
||||
if (typeof lo === 'object' && lo && 'name' in lo && typeof lo.name === 'string') items.push({ name: lo.name });
|
||||
if (typeof lo === 'object' && lo && 'alias' in lo && typeof lo.alias === 'string' && lo.alias !== (lo as { name?: string }).name) items.push({ name: lo.alias });
|
||||
}
|
||||
this.lora = new XnIndex(items);
|
||||
}
|
||||
// Embeddings: {loaded: [...], skipped: [...]}
|
||||
const embData = await this.fetchJson('/embeddings');
|
||||
const embData = await this.fetchJson('/embeddings') as Record<string, unknown> | null;
|
||||
if (embData && typeof embData === 'object') {
|
||||
const loaded = Array.isArray(embData.loaded) ? embData.loaded : [];
|
||||
this.embed = new XnIndex(loaded.map((name) => ({ name })));
|
||||
this.embed = new XnIndex(loaded.map((name) => ({ name: String(name) })));
|
||||
}
|
||||
// Wildcards: [{name}, ...]
|
||||
const wcData = await this.fetchJson('/wildcards');
|
||||
if (Array.isArray(wcData)) {
|
||||
this.wildcard = new XnIndex(wcData.filter((w) => w?.name).map((w) => ({ name: w.name })));
|
||||
this.wildcard = new XnIndex(
|
||||
wcData
|
||||
.filter((w) => typeof w === 'object' && w && 'name' in w && typeof w.name === 'string')
|
||||
.map((w) => ({ name: w.name })),
|
||||
);
|
||||
}
|
||||
log('autoComplete', {
|
||||
xnLoaded: true,
|
||||
@@ -91,17 +130,14 @@ const xnEngine = {
|
||||
},
|
||||
|
||||
searchLoras(prefix, limit = 20) {
|
||||
return this.lora.search(prefix, limit).map((item) => ({ ...item, kind: 'lora' }));
|
||||
return this.lora.search(prefix, limit).map((item) => ({ ...item, kind: 'lora' as const }));
|
||||
},
|
||||
|
||||
searchEmbeddings(prefix, limit = 20) {
|
||||
return this.embed.search(prefix, limit).map((item) => ({ ...item, kind: 'embed' }));
|
||||
return this.embed.search(prefix, limit).map((item) => ({ ...item, kind: 'embed' as const }));
|
||||
},
|
||||
|
||||
searchWildcards(prefix, limit = 20) {
|
||||
return this.wildcard.search(prefix, limit).map((item) => ({ ...item, kind: 'wildcard' }));
|
||||
return this.wildcard.search(prefix, limit).map((item) => ({ ...item, kind: 'wildcard' as const }));
|
||||
},
|
||||
};
|
||||
|
||||
// Expose globally so autocomplete.js can dispatch to it.
|
||||
window.autocompleteXn = xnEngine;
|
||||
@@ -1,7 +1,9 @@
|
||||
let changelogElements = [];
|
||||
import { gradioApp } from './script';
|
||||
|
||||
const getAllChildren = (el) => {
|
||||
const elements = [];
|
||||
let changelogElements: Element[] = [];
|
||||
|
||||
const getAllChildren = (el: Element): Element[] => {
|
||||
const elements: Element[] = [];
|
||||
for (let i = 0; i < el.children.length; i++) {
|
||||
elements.push(el.children[i]);
|
||||
if (el.children[i].children.length) elements.push(...getAllChildren(el.children[i]));
|
||||
@@ -9,18 +11,19 @@ const getAllChildren = (el) => {
|
||||
return elements;
|
||||
};
|
||||
|
||||
function getText(el) {
|
||||
function getText(el: Element): string {
|
||||
let text = '';
|
||||
el.childNodes.forEach((node) => {
|
||||
if (node.nodeType === Node.TEXT_NODE) text += node.nodeValue;
|
||||
if (node.nodeType === Node.TEXT_NODE) text += node.nodeValue ?? '';
|
||||
});
|
||||
return text.trim();
|
||||
}
|
||||
|
||||
let currentElement = -1;
|
||||
|
||||
function changelogNavigate(found) {
|
||||
function changelogNavigate(found: Element[]): void {
|
||||
const result = gradioApp().getElementById('changelog_result');
|
||||
if (!result) return;
|
||||
result.innerHTML = '';
|
||||
const text = document.createElement('p');
|
||||
|
||||
@@ -57,14 +60,14 @@ function changelogNavigate(found) {
|
||||
result.appendChild(text);
|
||||
}
|
||||
|
||||
async function initChangelog() {
|
||||
export async function initChangelog() {
|
||||
const search = gradioApp().querySelector('#changelog_search > label> textarea');
|
||||
const md = gradioApp().getElementById('changelog_markdown');
|
||||
if (!search || !md) {
|
||||
if (!(search instanceof HTMLTextAreaElement) || !md) {
|
||||
// error('initChangelog', 'Missing search or markdown elements');
|
||||
return;
|
||||
}
|
||||
const searchChangelog = async (e) => {
|
||||
const searchChangelog = async () => {
|
||||
if (changelogElements.length < 100) changelogElements = getAllChildren(md);
|
||||
const found = [];
|
||||
for (const el of changelogElements) {
|
||||
@@ -1,25 +1,68 @@
|
||||
String.prototype.format = function (args) { // eslint-disable-line no-extend-native, func-names
|
||||
import { gradioApp, onUiLoaded } from './script';
|
||||
import { log, error } from './logger';
|
||||
import { authFetch } from './authWrap';
|
||||
|
||||
interface CivitFile {
|
||||
url?: string;
|
||||
name?: string;
|
||||
type?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
interface CivitImage {
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface CivitVersion {
|
||||
id: number;
|
||||
name?: string;
|
||||
base?: string;
|
||||
mtime: string;
|
||||
availability?: string;
|
||||
desc?: string;
|
||||
files: CivitFile[];
|
||||
images: CivitImage[];
|
||||
}
|
||||
|
||||
interface CivitModel {
|
||||
id: number;
|
||||
url: string;
|
||||
name?: string;
|
||||
type?: string;
|
||||
tags?: string[];
|
||||
nsfw?: boolean;
|
||||
level?: number;
|
||||
availability?: string;
|
||||
downloads?: number;
|
||||
creator?: string;
|
||||
desc?: string;
|
||||
versions: CivitVersion[];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-extend-native
|
||||
String.prototype.format = function format(this: string, args: Record<string, string | number>): string {
|
||||
let thisString = '';
|
||||
for (let charPos = 0; charPos < this.length; charPos++) thisString += this[charPos];
|
||||
for (const key in args) {
|
||||
const stringKey = `{${key}}`;
|
||||
thisString = thisString.replace(new RegExp(stringKey, 'g'), args[key]);
|
||||
thisString = thisString.replace(new RegExp(stringKey, 'g'), String(args[key]));
|
||||
}
|
||||
return thisString;
|
||||
};
|
||||
|
||||
let selectedURL = '';
|
||||
let selectedName = '';
|
||||
let selectedType = '';
|
||||
let selectedBase = '';
|
||||
let selectedModelId = '';
|
||||
let selectedVersionId = '';
|
||||
let selectedURL: string[] = [];
|
||||
let selectedName: string[] = [];
|
||||
let selectedType: string[] = [];
|
||||
let selectedBase: string[] = [];
|
||||
let selectedModelId: number[] = [];
|
||||
let selectedVersionId: number[] = [];
|
||||
|
||||
function clearModelDetails() {
|
||||
export function clearModelDetails() {
|
||||
const el = gradioApp().getElementById('model-details') || gradioApp().getElementById('civitai_models_output') || gradioApp().getElementById('models_outcome');
|
||||
if (!el) return;
|
||||
el.innerHTML = '';
|
||||
}
|
||||
window.clearModelDetails = clearModelDetails;
|
||||
|
||||
const modelDetailsHTML = `
|
||||
<div>
|
||||
@@ -72,7 +115,7 @@ const modelVersionsHTML = `
|
||||
</tr>
|
||||
`;
|
||||
|
||||
async function modelCardClick(id) {
|
||||
export async function modelCardClick(id) {
|
||||
log('modelCardClick id', id);
|
||||
const el = gradioApp().getElementById('model-details') || gradioApp().getElementById('civitai_models_output') || gradioApp().getElementById('models_outcome');
|
||||
if (!el) return;
|
||||
@@ -81,12 +124,12 @@ async function modelCardClick(id) {
|
||||
error(`modelCardClick: id=${id} status=${res ? res.status : 'unknown'}`);
|
||||
return;
|
||||
}
|
||||
let data = await res.json();
|
||||
log('modelCardClick data', data);
|
||||
if (!data || data.length === 0) return;
|
||||
data = data[0]; // assuming the first item is the one we want
|
||||
const dataArray = await res.json();
|
||||
log('modelCardClick data', dataArray);
|
||||
if (!dataArray || dataArray.length === 0) return;
|
||||
const data: any = dataArray[0]; // assuming the first item is the one we want
|
||||
|
||||
const versionsHTML = data.versions.map((v) => modelVersionsHTML.format({
|
||||
const versionsHTML = data.versions.map((v: CivitVersion) => modelVersionsHTML.format({
|
||||
url: `<div class="link" onclick="startCivitDownload('${v.files[0]?.url}', '${v.files[0]?.name}', '${data.type}', '${v.base || ''}', ${data.id}, ${v.id})"> \udb80\uddda </div>`,
|
||||
name: v.name || 'unknown',
|
||||
type: v.files[0]?.type || 'unknown',
|
||||
@@ -99,7 +142,7 @@ async function modelCardClick(id) {
|
||||
})).join('');
|
||||
const url = `<a href="${data.url}" target="_blank" rel="noopener noreferrer">${data.name || 'unknown'}</a>`;
|
||||
const creator = `<a href="https://civitai.com/user/${data.creator}" target="_blank" rel="noopener noreferrer">${data.creator || 'unknown'}</a>`;
|
||||
const images = data.versions.map((v) => v.images).flat().map((i) => i.url); // TODO image gallery
|
||||
const images = data.versions.map((v: CivitVersion) => v.images).flat().map((i: CivitImage) => i.url); // TODO image gallery
|
||||
const modelHTML = modelDetailsHTML.format({
|
||||
name: url,
|
||||
type: data.type || 'unknown',
|
||||
@@ -110,13 +153,14 @@ async function modelCardClick(id) {
|
||||
downloads: data.downloads?.toString() || '',
|
||||
creator,
|
||||
desc: data.desc || 'no description available',
|
||||
image: images.length > 0 ? images[0] : '/sdapi/v1/network/thumb?filename=html/missing.png',
|
||||
image: images.length > 0 ? images[0] : '/sdapi/v1/network/thumb?filename=ui/assets/missing.png',
|
||||
versions: versionsHTML || '',
|
||||
});
|
||||
el.innerHTML = modelHTML;
|
||||
}
|
||||
window.modelCardClick = modelCardClick;
|
||||
|
||||
function startCivitDownload(url, name, type, base, modelId, versionId) {
|
||||
export function startCivitDownload(url, name, type, base, modelId, versionId) {
|
||||
log('startCivitDownload', { url, name, type, base, modelId, versionId });
|
||||
selectedURL = [url];
|
||||
selectedName = [name];
|
||||
@@ -127,10 +171,13 @@ function startCivitDownload(url, name, type, base, modelId, versionId) {
|
||||
const civitDownloadBtn = gradioApp().getElementById('civitai_download_btn');
|
||||
if (civitDownloadBtn) civitDownloadBtn.click();
|
||||
}
|
||||
window.startCivitDownload = startCivitDownload;
|
||||
|
||||
function startCivitAllDownload(evt) {
|
||||
export function startCivitAllDownload(evt) {
|
||||
log('startCivitAllDownload', evt);
|
||||
const versions = gradioApp().getElementById('model-versions-table').querySelectorAll('tr');
|
||||
const table = gradioApp().getElementById('model-versions-table');
|
||||
if (!table) return;
|
||||
const versions = table.querySelectorAll('tr');
|
||||
selectedURL = [];
|
||||
selectedName = [];
|
||||
selectedType = [];
|
||||
@@ -150,17 +197,19 @@ function startCivitAllDownload(evt) {
|
||||
const civitDownloadBtn = gradioApp().getElementById('civitai_download_btn');
|
||||
if (civitDownloadBtn) civitDownloadBtn.click();
|
||||
}
|
||||
window.startCivitAllDownload = startCivitAllDownload;
|
||||
|
||||
function downloadCivitModel(modelUrl, modelName, modelType, modelBase, mId, vId, modelPath, civitToken, innerHTML) {
|
||||
export function downloadCivitModel(modelUrl, modelName, modelType, modelBase, mId, vId, modelPath, civitToken, innerHTML) {
|
||||
log('downloadCivitModel', { modelUrl, modelName, modelType, modelBase, mId, vId, modelPath, civitToken });
|
||||
const el = gradioApp().getElementById('civitai_models_output') || gradioApp().getElementById('models_outcome');
|
||||
const currentHTML = el?.innerHTML || '';
|
||||
return [selectedURL, selectedName, selectedType, selectedBase, selectedModelId, selectedVersionId, modelPath, civitToken, currentHTML];
|
||||
}
|
||||
window.downloadCivitModel = downloadCivitModel;
|
||||
|
||||
let civitMutualExcludeBound = false;
|
||||
|
||||
function civitaiMutualExclude() {
|
||||
export function civitaiMutualExclude() {
|
||||
if (civitMutualExcludeBound) return;
|
||||
const searchEl = gradioApp().querySelector('#civit_search_text textarea');
|
||||
const tagEl = gradioApp().querySelector('#civit_search_tag textarea');
|
||||
@@ -1,10 +1,22 @@
|
||||
import { gradioApp, getUICurrentTabContent } from './script';
|
||||
import { log } from './logger';
|
||||
import { authFetch } from './authWrap';
|
||||
import { quickApplyStyle, quickSaveStyle } from './extraNetworks';
|
||||
|
||||
interface ContextMenuItem {
|
||||
id: string;
|
||||
name: string;
|
||||
func: () => void;
|
||||
primary: boolean;
|
||||
}
|
||||
|
||||
const contextMenuInit = () => {
|
||||
let eventListenerApplied = false;
|
||||
const menuSpecs = new Map();
|
||||
const menuSpecs = new Map<string, ContextMenuItem[]>();
|
||||
|
||||
const uid = () => Date.now().toString(36) + Math.random().toString(36).substring(2);
|
||||
|
||||
function showContextMenu(event, element, menuEntries) {
|
||||
function showContextMenu(event: MouseEvent, _element: Element, menuEntries: ContextMenuItem[]): void {
|
||||
const posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
|
||||
const posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
|
||||
const oldMenu = gradioApp().querySelector('#context-menu');
|
||||
@@ -19,7 +31,7 @@ const contextMenuInit = () => {
|
||||
menuEntries.forEach((entry) => {
|
||||
const contextMenuEntry = document.createElement('a');
|
||||
contextMenuEntry.innerHTML = entry.name;
|
||||
contextMenuEntry.addEventListener('click', (e) => entry.func());
|
||||
contextMenuEntry.addEventListener('click', () => entry.func());
|
||||
contextMenuList.append(contextMenuEntry);
|
||||
});
|
||||
gradioApp().appendChild(contextMenu);
|
||||
@@ -31,7 +43,7 @@ const contextMenuInit = () => {
|
||||
if ((windowHeight - posy) < menuHeight) contextMenu.style.top = `${windowHeight - menuHeight}px`;
|
||||
}
|
||||
|
||||
function appendContextMenuOption(targetElementSelector, entryName, entryFunction, primary = false) {
|
||||
function appendContextMenuOption(targetElementSelector: string, entryName: string, entryFunction: () => void, primary = false): string {
|
||||
let currentItems = menuSpecs.get(targetElementSelector);
|
||||
if (!currentItems) {
|
||||
currentItems = [];
|
||||
@@ -48,7 +60,7 @@ const contextMenuInit = () => {
|
||||
return newItem.id;
|
||||
}
|
||||
|
||||
function removeContextMenuOption(id) {
|
||||
function removeContextMenuOption(id: string): void {
|
||||
menuSpecs.forEach((v, k) => {
|
||||
let index = -1;
|
||||
v.forEach((e, ei) => {
|
||||
@@ -58,7 +70,7 @@ const contextMenuInit = () => {
|
||||
});
|
||||
}
|
||||
|
||||
async function addContextMenuEventListener() {
|
||||
async function addContextMenuEventListener(): Promise<void> {
|
||||
if (eventListenerApplied) return;
|
||||
log('initContextMenu');
|
||||
gradioApp().addEventListener('click', (e) => {
|
||||
@@ -67,7 +79,9 @@ const contextMenuInit = () => {
|
||||
if (oldMenu) oldMenu.remove();
|
||||
menuSpecs.forEach((v, k) => {
|
||||
const items = v.filter((item) => item.primary);
|
||||
const matched = e.target.closest(k);
|
||||
const target = e.target as Element | null;
|
||||
if (!target) return;
|
||||
const matched = target.closest(k);
|
||||
if (items.length > 0 && matched) {
|
||||
showContextMenu(e, matched, items);
|
||||
e.preventDefault();
|
||||
@@ -79,7 +93,9 @@ const contextMenuInit = () => {
|
||||
if (oldMenu) oldMenu.remove();
|
||||
menuSpecs.forEach((v, k) => {
|
||||
const items = v.filter((item) => !item.primary);
|
||||
const matched = e.target.closest(k);
|
||||
const target = e.target as Element | null;
|
||||
if (!target) return;
|
||||
const matched = target.closest(k);
|
||||
if (items.length > 0 && matched) {
|
||||
showContextMenu(e, matched, items);
|
||||
e.preventDefault();
|
||||
@@ -94,15 +110,18 @@ const contextMenuInit = () => {
|
||||
const initContextResponse = contextMenuInit();
|
||||
const appendContextMenuOption = initContextResponse[0];
|
||||
const removeContextMenuOption = initContextResponse[1];
|
||||
const addContextMenuEventListener = initContextResponse[2];
|
||||
const addContextMenuEventListener = initContextResponse[2] as () => void;
|
||||
|
||||
const generateForever = (genbuttonid) => {
|
||||
if (window.generateOnRepeatInterval) {
|
||||
let generateOnRepeatInterval: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
const generateForever = (genbuttonid: string): void => {
|
||||
if (generateOnRepeatInterval) {
|
||||
log('generateForever: cancel');
|
||||
clearInterval(window.generateOnRepeatInterval);
|
||||
window.generateOnRepeatInterval = null;
|
||||
clearInterval(generateOnRepeatInterval);
|
||||
generateOnRepeatInterval = null;
|
||||
} else {
|
||||
const genbutton = gradioApp().querySelector(genbuttonid);
|
||||
if (!(genbutton instanceof HTMLElement)) return;
|
||||
const isBusy = () => {
|
||||
let busy = document.getElementById('progressbar')?.style.display === 'block';
|
||||
if (!busy) {
|
||||
@@ -114,13 +133,13 @@ const generateForever = (genbuttonid) => {
|
||||
};
|
||||
log('generateForever: start');
|
||||
if (!isBusy()) genbutton.click();
|
||||
window.generateOnRepeatInterval = setInterval(() => {
|
||||
generateOnRepeatInterval = setInterval(() => {
|
||||
if (!isBusy()) genbutton.click();
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
const reprocessClick = (tabId, state) => {
|
||||
const reprocessClick = (tabId: string, state: string): void => {
|
||||
const btn = document.getElementById(`${tabId}_${state}`);
|
||||
window.submit_state = state;
|
||||
if (btn) btn.click();
|
||||
@@ -139,38 +158,34 @@ const getStatus = async () => {
|
||||
if (res?.ok) {
|
||||
data = await res.json();
|
||||
log('progressInternal:', data);
|
||||
if (el) el.innerText += '\nProgress internal:\n' + JSON.stringify(data, null, 2); // eslint-disable-line prefer-template
|
||||
if (el) el.innerText += `\nProgress internal:\n${JSON.stringify(data, null, 2)}`;
|
||||
}
|
||||
res = await authFetch('./sdapi/v1/progress?skip_current_image=true', { method: 'GET', headers });
|
||||
if (res?.ok) {
|
||||
data = await res.json();
|
||||
log('progressAPI:', data);
|
||||
if (el) el.innerText += '\nProgress API:\n' + JSON.stringify(data, null, 2); // eslint-disable-line prefer-template
|
||||
if (el) el.innerText += `\nProgress API:\n${JSON.stringify(data, null, 2)}`;
|
||||
}
|
||||
};
|
||||
|
||||
async function initContextMenu() {
|
||||
let id = '';
|
||||
export async function initContextMenu() {
|
||||
for (const tab of ['txt2img', 'img2img', 'control', 'video']) {
|
||||
id = `#${tab}_generate`;
|
||||
appendContextMenuOption(id, 'Get server status', getStatus);
|
||||
appendContextMenuOption(id, 'Copy prompt to clipboard', () => navigator.clipboard.writeText(document.querySelector(`#${tab}_prompt > label > textarea`).value));
|
||||
appendContextMenuOption(id, 'Generate forever', () => generateForever(`#${tab}_generate`));
|
||||
appendContextMenuOption(id, 'Apply selected style', quickApplyStyle);
|
||||
appendContextMenuOption(id, 'Quick save style', quickSaveStyle);
|
||||
id = `#${tab}_reprocess`;
|
||||
appendContextMenuOption(id, 'Decode full quality', () => reprocessClick(`${tab}`, 'reprocess_decode'), true);
|
||||
appendContextMenuOption(id, 'Refine & HiRes pass', () => reprocessClick(`${tab}`, 'reprocess_refine'), true);
|
||||
appendContextMenuOption(id, 'Detailer pass', () => reprocessClick(`${tab}`, 'reprocess_detail'), true);
|
||||
appendContextMenuOption(`#${tab}_generate`, 'Get server status', getStatus);
|
||||
appendContextMenuOption(`#${tab}_generate`, 'Copy prompt to clipboard', () => navigator.clipboard.writeText(document.querySelector(`#${tab}_prompt > label > textarea`).value));
|
||||
appendContextMenuOption(`#${tab}_generate`, 'Generate forever', () => generateForever(`#${tab}_generate`));
|
||||
appendContextMenuOption(`#${tab}_generate`, 'Apply selected style', quickApplyStyle);
|
||||
appendContextMenuOption(`#${tab}_generate`, 'Quick save style', quickSaveStyle);
|
||||
appendContextMenuOption(`#${tab}_reprocess`, 'Decode full quality', () => reprocessClick(tab, 'reprocess_decode'), true);
|
||||
appendContextMenuOption(`#${tab}_reprocess`, 'Refine & HiRes pass', () => reprocessClick(tab, 'reprocess_refine'), true);
|
||||
appendContextMenuOption(`#${tab}_reprocess`, 'Detailer pass', () => reprocessClick(tab, 'reprocess_detail'), true);
|
||||
}
|
||||
// Right-click send-to-control button for prompt/params-only transfer.
|
||||
for (const tab of ['gallery', 'txt2img', 'img2img', 'extras']) {
|
||||
id = `#${tab}_tabitem #control_tab`;
|
||||
appendContextMenuOption(id, 'Transfer only prompt to Images tab', () => {
|
||||
appendContextMenuOption(`#${tab}_tabitem #control_tab`, 'Transfer only prompt to Images tab', () => {
|
||||
document.querySelector(`#image_buttons_${tab} #control_tab_prompt`)?.click();
|
||||
document.getElementById('control_nav')?.click();
|
||||
});
|
||||
appendContextMenuOption(id, 'Transfer all parameters to Images tab', () => {
|
||||
appendContextMenuOption(`#${tab}_tabitem #control_tab`, 'Transfer all parameters to Images tab', () => {
|
||||
document.querySelector(`#image_buttons_${tab} #control_tab_params`)?.click();
|
||||
document.getElementById('control_nav')?.click();
|
||||
});
|
||||
@@ -1,16 +1,20 @@
|
||||
function controlInputMode(inputMode, ...args) {
|
||||
import { gradioApp } from './script';
|
||||
import { log } from './logger';
|
||||
import { timer } from './timers';
|
||||
|
||||
export function controlInputMode(inputMode: string, ...args: unknown[]): unknown[] {
|
||||
const updateEl = gradioApp().getElementById('control_update');
|
||||
if (updateEl) updateEl.click();
|
||||
const tab = gradioApp().querySelector('#control-tab-input button.selected');
|
||||
if (!tab) return ['Image', ...args];
|
||||
const tabs = Array.from(gradioApp().querySelectorAll('#control-tab-input button'));
|
||||
const tabs = Array.from<any>(gradioApp().querySelectorAll('#control-tab-input button'));
|
||||
const tabIdx = tabs.findIndex((btn) => btn.classList.contains('selected'));
|
||||
const tabNames = ['Image', 'Video', 'Batch', 'Folder'];
|
||||
let inputTab = tabNames[tabIdx] || 'Image';
|
||||
log('controlInputMode', { mode: inputMode, tab: inputTab, kanvas: typeof Kanvas });
|
||||
log('controlInputMode', { mode: inputMode, tab: inputTab, kanvas: typeof window.Kanvas });
|
||||
|
||||
// if kanvas is available overwrite image inputs with kanvas images
|
||||
if ((inputTab === 'Image') && (typeof 'Kanvas' !== 'undefined')) {
|
||||
if ((inputTab === 'Image') && (typeof window.Kanvas !== 'undefined') && window.kanvas) {
|
||||
inputTab = 'Kanvas';
|
||||
for (let i = 0; i < window.kanvas.stages.maxStages; i++) {
|
||||
args[4 + i] = window.kanvas.getImage(1 + i, false, false);
|
||||
@@ -20,7 +24,9 @@ function controlInputMode(inputMode, ...args) {
|
||||
return [inputTab, ...args];
|
||||
}
|
||||
|
||||
async function setupControlUI() {
|
||||
window.controlInputMode = controlInputMode;
|
||||
|
||||
export async function setupControlUI() {
|
||||
const t0 = performance.now();
|
||||
const tabs = ['input', 'output', 'preview'];
|
||||
for (const tab of tabs) {
|
||||
@@ -29,8 +35,10 @@ async function setupControlUI() {
|
||||
btn.style.cursor = 'pointer';
|
||||
btn.onclick = () => {
|
||||
const t = gradioApp().getElementById(`control-tab-${tab}`);
|
||||
if (!t) return;
|
||||
t.style.display = t.style.display === 'none' ? 'block' : 'none';
|
||||
const c = gradioApp().getElementById(`control-${tab}-column`);
|
||||
if (!c) return;
|
||||
c.style.flexGrow = c.style.flexGrow === '0' ? '9' : '0';
|
||||
};
|
||||
}
|
||||
@@ -39,8 +47,9 @@ async function setupControlUI() {
|
||||
if (!el) return;
|
||||
const intersectionObserver = new IntersectionObserver((entries) => {
|
||||
if (entries[0].intersectionRatio > 0) {
|
||||
const allTabs = Array.from(gradioApp().querySelectorAll('#control-tabs > .tab-nav > .selected'));
|
||||
const allTabs = Array.from<any>(gradioApp().querySelectorAll('#control-tabs > .tab-nav > .selected'));
|
||||
for (const tab of allTabs) {
|
||||
if (!(tab instanceof HTMLElement)) continue;
|
||||
const name = tab.innerText.toLowerCase();
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
const btn = gradioApp().getElementById(`refresh_${name}_models_${i}`);
|
||||
@@ -1,4 +1,4 @@
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('notosans-nerdfont-regular.ttf') }
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('fonts/notosans-nerdfont-regular.ttf') }
|
||||
|
||||
/* toolbutton */
|
||||
.gradio-button.tool { max-width: min-content; min-width: min-content !important; align-self: end; font-size: 1.4em; color: var(--body-text-color) !important; }
|
||||
@@ -1,5 +1,5 @@
|
||||
/* generic html tags */
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('notosans-nerdfont-regular.ttf') }
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('fonts/notosans-nerdfont-regular.ttf') }
|
||||
:root, .light, .dark {
|
||||
--font: 'NotoSans';
|
||||
--font-mono: 'ui-monospace', 'Consolas', monospace;
|
||||
@@ -1,5 +1,5 @@
|
||||
/* generic html tags */
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('notosans-nerdfont-regular.ttf') }
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('fonts/notosans-nerdfont-regular.ttf') }
|
||||
:root, .light, .dark {
|
||||
--font: 'NotoSans';
|
||||
--font-mono: 'ui-monospace', 'Consolas', monospace;
|
||||
@@ -4,7 +4,7 @@
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('NotoSansNerd'), url('notosans-nerdfont-regular.ttf');
|
||||
src: local('NotoSansNerd'), url('fonts/notosans-nerdfont-regular.ttf');
|
||||
}
|
||||
|
||||
html {
|
||||
@@ -1,5 +1,5 @@
|
||||
/* generic html tags */
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('notosans-nerdfont-regular.ttf') }
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('fonts/notosans-nerdfont-regular.ttf') }
|
||||
:root, .light, .dark {
|
||||
--font: 'NotoSans';
|
||||
--font-mono: 'ui-monospace', 'Consolas', monospace;
|
||||
@@ -1,5 +1,5 @@
|
||||
/* generic html tags */
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('notosans-nerdfont-regular.ttf') }
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('fonts/notosans-nerdfont-regular.ttf') }
|
||||
:root, .light, .dark {
|
||||
--font: 'NotoSans';
|
||||
--font-mono: 'ui-monospace', 'Consolas', monospace;
|
||||
@@ -3,7 +3,7 @@
|
||||
font-family: 'NotoSans';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('NotoSansNerd'), url('notosans-nerdfont-regular.ttf');
|
||||
src: local('NotoSansNerd'), url('fonts/notosans-nerdfont-regular.ttf');
|
||||
}
|
||||
|
||||
:root {
|
||||
@@ -1,5 +1,5 @@
|
||||
/* generic html tags */
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('notosans-nerdfont-regular.ttf') }
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('fonts/notosans-nerdfont-regular.ttf') }
|
||||
:root, .light, .dark {
|
||||
--font: 'NotoSans';
|
||||
--font-mono: 'ui-monospace', 'Consolas', monospace;
|
||||
@@ -1,5 +1,5 @@
|
||||
/* generic html tags */
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('notosans-nerdfont-regular.ttf') }
|
||||
@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSansNerd'), url('fonts/notosans-nerdfont-regular.ttf') }
|
||||
:root, .light, .dark {
|
||||
--font: 'NotoSans';
|
||||
--font-mono: 'ui-monospace', 'Consolas', monospace;
|
||||