diff --git a/invokeai/app/api_app.py b/invokeai/app/api_app.py index e07b037dd1..f45541e63b 100644 --- a/invokeai/app/api_app.py +++ b/invokeai/app/api_app.py @@ -23,6 +23,7 @@ if True: # hack to make flake8 happy with imports coming after setting up the c from fastapi_events.handlers.local import local_handler from fastapi_events.middleware import EventHandlerASGIMiddleware from pydantic.json_schema import models_json_schema + from fastapi.responses import FileResponse # noinspection PyUnresolvedReferences import invokeai.backend.util.hotfixes # noqa: F401 (monkeypatching on import) @@ -173,16 +174,13 @@ def custom_openapi(): app.openapi = custom_openapi # type: ignore [method-assign] # this is a valid assignment -# Override API doc favicons -app.mount("/static", StaticFiles(directory=Path(web_dir.__path__[0], "static/dream_web")), name="static") - @app.get("/docs", include_in_schema=False) def overridden_swagger(): return get_swagger_ui_html( openapi_url=app.openapi_url, title=app.title, - swagger_favicon_url="/static/favicon.ico", + swagger_favicon_url="/static/docs/favicon.ico", ) @@ -191,12 +189,24 @@ def overridden_redoc(): return get_redoc_html( openapi_url=app.openapi_url, title=app.title, - redoc_favicon_url="/static/favicon.ico", + redoc_favicon_url="/static/docs/favicon.ico", ) -# Must mount *after* the other routes else it borks em -app.mount("/", StaticFiles(directory=Path(web_dir.__path__[0], "dist"), html=True), name="ui") +web_root_path = Path(list(web_dir.__path__)[0]) + + +# Cannot add headers to StaticFiles, so we must serve index.html with a custom route +# Add cache-control: no-store header to prevent caching of index.html, which leads to broken UIs at release +@app.get("/", include_in_schema=False, name="ui_root") +def get_index() -> FileResponse: + return FileResponse(Path(web_root_path, "dist/index.html"), headers={"Cache-Control": "no-store"}) + + +# # Must mount *after* the other routes else it borks em +app.mount("/static", StaticFiles(directory=Path(web_root_path, "static/")), name="static") # docs favicon is in here +app.mount("/assets", StaticFiles(directory=Path(web_root_path, "dist/assets/")), name="assets") +app.mount("/locales", StaticFiles(directory=Path(web_root_path, "dist/locales/")), name="locales") def invoke_api(): diff --git a/invokeai/frontend/web/static/dream_web/favicon.ico b/invokeai/frontend/web/static/docs/favicon.ico similarity index 100% rename from invokeai/frontend/web/static/dream_web/favicon.ico rename to invokeai/frontend/web/static/docs/favicon.ico diff --git a/invokeai/frontend/web/static/dream_web/index.css b/invokeai/frontend/web/static/dream_web/index.css deleted file mode 100644 index 25a0994a3d..0000000000 --- a/invokeai/frontend/web/static/dream_web/index.css +++ /dev/null @@ -1,179 +0,0 @@ -:root { - --fields-dark:#DCDCDC; - --fields-light:#F5F5F5; -} - -* { - font-family: 'Arial'; - font-size: 100%; -} -body { - font-size: 1em; -} -textarea { - font-size: 0.95em; -} -header, form, #progress-section { - margin-left: auto; - margin-right: auto; - max-width: 1024px; - text-align: center; -} -fieldset { - border: none; - line-height: 2.2em; -} -fieldset > legend { - width: auto; - margin-left: 0; - margin-right: auto; - font-weight:bold; -} -select, input { - margin-right: 10px; - padding: 2px; -} -input:disabled { - cursor:auto; -} -input[type=submit] { - cursor: pointer; - background-color: #666; - color: white; -} -input[type=checkbox] { - cursor: pointer; - margin-right: 0px; - width: 20px; - height: 20px; - vertical-align: middle; -} -input#seed { - margin-right: 0px; -} -div { - padding: 10px 10px 10px 10px; -} -header { - margin-bottom: 16px; -} -header h1 { - margin-bottom: 0; - font-size: 2em; -} -#search-box { - display: flex; -} -#scaling-inprocess-message { - font-weight: bold; - font-style: italic; - display: none; -} -#prompt { - flex-grow: 1; - padding: 5px 10px 5px 10px; - border: 1px solid #999; - outline: none; -} -#submit { - padding: 5px 10px 5px 10px; - border: 1px solid #999; -} -#reset-all, #remove-image { - margin-top: 12px; - font-size: 0.8em; - background-color: pink; - border: 1px solid #999; - border-radius: 4px; -} -#results { - text-align: center; - margin: auto; - padding-top: 10px; -} -#results figure { - display: inline-block; - margin: 10px; -} -#results figcaption { - font-size: 0.8em; - padding: 3px; - color: #888; - cursor: pointer; -} -#results img { - border-radius: 5px; - object-fit: contain; - background-color: var(--fields-dark); -} -#fieldset-config { - line-height:2em; -} -input[type="number"] { - width: 60px; -} -#seed { - width: 150px; -} -button#reset-seed { - font-size: 1.7em; - background: #efefef; - border: 1px solid #999; - border-radius: 4px; - line-height: 0.8; - margin: 0 10px 0 0; - padding: 0 5px 3px; - vertical-align: middle; -} -label { - white-space: nowrap; -} -#progress-section { - display: none; -} -#progress-image { - width: 30vh; - height: 30vh; - object-fit: contain; - background-color: var(--fields-dark); -} -#cancel-button { - cursor: pointer; - color: red; -} -#txt2img { - background-color: var(--fields-dark); -} -#variations { - background-color: var(--fields-light); -} -#initimg { - background-color: var(--fields-dark); -} -#img2img { - background-color: var(--fields-light); -} -#initimg > :not(legend) { - background-color: var(--fields-light); - margin: .5em; -} - -#postprocess, #initimg { - display:flex; - flex-wrap:wrap; - padding: 0; - margin-top: 1em; - background-color: var(--fields-dark); -} -#postprocess > fieldset, #initimg > * { - flex-grow: 1; -} -#postprocess > fieldset { - background-color: var(--fields-dark); -} -#progress-section { - background-color: var(--fields-light); -} -#no-results-message:not(:only-child) { - display: none; -} diff --git a/invokeai/frontend/web/static/dream_web/index.html b/invokeai/frontend/web/static/dream_web/index.html deleted file mode 100644 index feb542adb2..0000000000 --- a/invokeai/frontend/web/static/dream_web/index.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - Stable Diffusion Dream Server - - - - - - - - - - - -
-

Stable Diffusion Dream Server

-
- For news and support for this web service, visit our GitHub - site -
-
- -
- -
-
- - - - - - - - - - - - - - - -
- - - - - - - - - -
- - - - - -
-
- - - - -
-
-
- - - - -
- - - -
-
- - - - - - - - -
-
-
-
- - - - - - -
-
- - - - - - - - -
-
- -
-
-
-
- - -
- -
- Postprocessing...1/3 -
-
-
- -
-
-
- - - diff --git a/invokeai/frontend/web/static/dream_web/index.js b/invokeai/frontend/web/static/dream_web/index.js deleted file mode 100644 index 438232f0c7..0000000000 --- a/invokeai/frontend/web/static/dream_web/index.js +++ /dev/null @@ -1,409 +0,0 @@ -const socket = io(); - -var priorResultsLoadState = { - page: 0, - pages: 1, - per_page: 10, - total: 20, - offset: 0, // number of items generated since last load - loading: false, - initialized: false, -}; - -function loadPriorResults() { - // Fix next page by offset - let offsetPages = - priorResultsLoadState.offset / priorResultsLoadState.per_page; - priorResultsLoadState.page += offsetPages; - priorResultsLoadState.pages += offsetPages; - priorResultsLoadState.total += priorResultsLoadState.offset; - priorResultsLoadState.offset = 0; - - if (priorResultsLoadState.loading) { - return; - } - - if (priorResultsLoadState.page >= priorResultsLoadState.pages) { - return; // Nothing more to load - } - - // Load - priorResultsLoadState.loading = true; - let url = new URL('/api/images', document.baseURI); - url.searchParams.append( - 'page', - priorResultsLoadState.initialized - ? priorResultsLoadState.page + 1 - : priorResultsLoadState.page - ); - url.searchParams.append('per_page', priorResultsLoadState.per_page); - fetch(url.href, { - method: 'GET', - headers: new Headers({ 'content-type': 'application/json' }), - }) - .then((response) => response.json()) - .then((data) => { - priorResultsLoadState.page = data.page; - priorResultsLoadState.pages = data.pages; - priorResultsLoadState.per_page = data.per_page; - priorResultsLoadState.total = data.total; - - data.items.forEach(function (dreamId, index) { - let src = 'api/images/' + dreamId; - fetch('/api/images/' + dreamId + '/metadata', { - method: 'GET', - headers: new Headers({ 'content-type': 'application/json' }), - }) - .then((response) => response.json()) - .then((metadata) => { - let seed = metadata.seed || 0; // TODO: Parse old metadata - appendOutput(src, seed, metadata, true); - }); - }); - - // Load until page is full - if (!priorResultsLoadState.initialized) { - if (document.body.scrollHeight <= window.innerHeight) { - loadPriorResults(); - } - } - }) - .finally(() => { - priorResultsLoadState.loading = false; - priorResultsLoadState.initialized = true; - }); -} - -function resetForm() { - var form = document.getElementById('generate-form'); - form.querySelector('fieldset').removeAttribute('disabled'); -} - -function initProgress(totalSteps, showProgressImages) { - // TODO: Progress could theoretically come from multiple jobs at the same time (in the future) - let progressSectionEle = document.querySelector('#progress-section'); - progressSectionEle.style.display = 'initial'; - let progressEle = document.querySelector('#progress-bar'); - progressEle.setAttribute('max', totalSteps); - - let progressImageEle = document.querySelector('#progress-image'); - progressImageEle.src = BLANK_IMAGE_URL; - progressImageEle.style.display = showProgressImages ? 'initial' : 'none'; -} - -function setProgress(step, totalSteps, src) { - let progressEle = document.querySelector('#progress-bar'); - progressEle.setAttribute('value', step); - - if (src) { - let progressImageEle = document.querySelector('#progress-image'); - progressImageEle.src = src; - } -} - -function resetProgress(hide = true) { - if (hide) { - let progressSectionEle = document.querySelector('#progress-section'); - progressSectionEle.style.display = 'none'; - } - let progressEle = document.querySelector('#progress-bar'); - progressEle.setAttribute('value', 0); -} - -function toBase64(file) { - return new Promise((resolve, reject) => { - const r = new FileReader(); - r.readAsDataURL(file); - r.onload = () => resolve(r.result); - r.onerror = (error) => reject(error); - }); -} - -function ondragdream(event) { - let dream = event.target.dataset.dream; - event.dataTransfer.setData('dream', dream); -} - -function seedClick(event) { - // Get element - var image = event.target.closest('figure').querySelector('img'); - var dream = JSON.parse(decodeURIComponent(image.dataset.dream)); - - let form = document.querySelector('#generate-form'); - for (const [k, v] of new FormData(form)) { - if (k == 'initimg') { - continue; - } - let formElem = form.querySelector(`*[name=${k}]`); - formElem.value = dream[k] !== undefined ? dream[k] : formElem.defaultValue; - } - - document.querySelector('#seed').value = dream.seed; - document.querySelector('#iterations').value = 1; // Reset to 1 iteration since we clicked a single image (not a full job) - - // NOTE: leaving this manual for the user for now - it was very confusing with this behavior - // document.querySelector("#with_variations").value = variations || ''; - // if (document.querySelector("#variation_amount").value <= 0) { - // document.querySelector("#variation_amount").value = 0.2; - // } - - saveFields(document.querySelector('#generate-form')); -} - -function appendOutput(src, seed, config, toEnd = false) { - let outputNode = document.createElement('figure'); - let altText = seed.toString() + ' | ' + config.prompt; - - // img needs width and height for lazy loading to work - // TODO: store the full config in a data attribute on the image? - const figureContents = ` - - ${altText} - -
${seed}
- `; - - outputNode.innerHTML = figureContents; - - if (toEnd) { - document.querySelector('#results').append(outputNode); - } else { - document.querySelector('#results').prepend(outputNode); - } - document.querySelector('#no-results-message')?.remove(); -} - -function saveFields(form) { - for (const [k, v] of new FormData(form)) { - if (typeof v !== 'object') { - // Don't save 'file' type - localStorage.setItem(k, v); - } - } -} - -function loadFields(form) { - for (const [k, v] of new FormData(form)) { - const item = localStorage.getItem(k); - if (item != null) { - form.querySelector(`*[name=${k}]`).value = item; - } - } -} - -function clearFields(form) { - localStorage.clear(); - let prompt = form.prompt.value; - form.reset(); - form.prompt.value = prompt; -} - -const BLANK_IMAGE_URL = - 'data:image/svg+xml,'; -async function generateSubmit(form) { - // Convert file data to base64 - // TODO: Should probably uplaod files with formdata or something, and store them in the backend? - let formData = Object.fromEntries(new FormData(form)); - if (!formData.enable_generate && !formData.enable_init_image) { - gen_label = document.querySelector('label[for=enable_generate]').innerHTML; - initimg_label = document.querySelector( - 'label[for=enable_init_image]' - ).innerHTML; - alert(`Error: one of "${gen_label}" or "${initimg_label}" must be set`); - } - - formData.initimg_name = formData.initimg.name; - formData.initimg = - formData.initimg.name !== '' ? await toBase64(formData.initimg) : null; - - // Evaluate all checkboxes - let checkboxes = form.querySelectorAll('input[type=checkbox]'); - checkboxes.forEach(function (checkbox) { - if (checkbox.checked) { - formData[checkbox.name] = 'true'; - } - }); - - let strength = formData.strength; - let totalSteps = formData.initimg - ? Math.floor(strength * formData.steps) - : formData.steps; - let showProgressImages = formData.progress_images; - - // Set enabling flags - - // Initialize the progress bar - initProgress(totalSteps, showProgressImages); - - // POST, use response to listen for events - fetch(form.action, { - method: form.method, - headers: new Headers({ 'content-type': 'application/json' }), - body: JSON.stringify(formData), - }) - .then((response) => response.json()) - .then((data) => { - var jobId = data.jobId; - socket.emit('join_room', { room: jobId }); - }); - - form.querySelector('fieldset').setAttribute('disabled', ''); -} - -function fieldSetEnableChecked(event) { - cb = event.target; - fields = cb.closest('fieldset'); - fields.disabled = !cb.checked; -} - -// Socket listeners -socket.on('job_started', (data) => {}); - -socket.on('dream_result', (data) => { - var jobId = data.jobId; - var dreamId = data.dreamId; - var dreamRequest = data.dreamRequest; - var src = 'api/images/' + dreamId; - - priorResultsLoadState.offset += 1; - appendOutput(src, dreamRequest.seed, dreamRequest); - - resetProgress(false); -}); - -socket.on('dream_progress', (data) => { - // TODO: it'd be nice if we could get a seed reported here, but the generator would need to be updated - var step = data.step; - var totalSteps = data.totalSteps; - var jobId = data.jobId; - var dreamId = data.dreamId; - - var progressType = data.progressType; - if (progressType === 'GENERATION') { - var src = data.hasProgressImage - ? 'api/intermediates/' + dreamId + '/' + step - : null; - setProgress(step, totalSteps, src); - } else if (progressType === 'UPSCALING_STARTED') { - // step and totalSteps are used for upscale count on this message - document.getElementById('processing_cnt').textContent = step; - document.getElementById('processing_total').textContent = totalSteps; - document.getElementById('scaling-inprocess-message').style.display = - 'block'; - } else if (progressType == 'UPSCALING_DONE') { - document.getElementById('scaling-inprocess-message').style.display = 'none'; - } -}); - -socket.on('job_canceled', (data) => { - resetForm(); - resetProgress(); -}); - -socket.on('job_done', (data) => { - jobId = data.jobId; - socket.emit('leave_room', { room: jobId }); - - resetForm(); - resetProgress(); -}); - -window.onload = async () => { - document.querySelector('#prompt').addEventListener('keydown', (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - const form = e.target.form; - generateSubmit(form); - } - }); - document.querySelector('#generate-form').addEventListener('submit', (e) => { - e.preventDefault(); - const form = e.target; - - generateSubmit(form); - }); - document.querySelector('#generate-form').addEventListener('change', (e) => { - saveFields(e.target.form); - }); - document.querySelector('#reset-seed').addEventListener('click', (e) => { - document.querySelector('#seed').value = 0; - saveFields(e.target.form); - }); - document.querySelector('#reset-all').addEventListener('click', (e) => { - clearFields(e.target.form); - }); - document.querySelector('#remove-image').addEventListener('click', (e) => { - initimg.value = null; - }); - loadFields(document.querySelector('#generate-form')); - - document.querySelector('#cancel-button').addEventListener('click', () => { - fetch('/api/cancel').catch((e) => { - console.error(e); - }); - }); - document.documentElement.addEventListener('keydown', (e) => { - if (e.key === 'Escape') - fetch('/api/cancel').catch((err) => { - console.error(err); - }); - }); - - if (!config.gfpgan_model_exists) { - document.querySelector('#gfpgan').style.display = 'none'; - } - - window.addEventListener('scroll', () => { - if (window.innerHeight + window.pageYOffset >= document.body.offsetHeight) { - loadPriorResults(); - } - }); - - // Enable/disable forms by checkboxes - document - .querySelectorAll('legend > input[type=checkbox]') - .forEach(function (cb) { - cb.addEventListener('change', fieldSetEnableChecked); - fieldSetEnableChecked({ target: cb }); - }); - - // Load some of the previous results - loadPriorResults(); - - // Image drop/upload WIP - /* - let drop = document.getElementById('dropper'); - function ondrop(event) { - let dreamData = event.dataTransfer.getData('dream'); - if (dreamData) { - var dream = JSON.parse(decodeURIComponent(dreamData)); - alert(dream.dreamId); - } - }; - - function ondragenter(event) { - event.preventDefault(); - }; - - function ondragover(event) { - event.preventDefault(); - }; - - function ondragleave(event) { - - } - - drop.addEventListener('drop', ondrop); - drop.addEventListener('dragenter', ondragenter); - drop.addEventListener('dragover', ondragover); - drop.addEventListener('dragleave', ondragleave); - */ -}; diff --git a/invokeai/frontend/web/static/dream_web/test.html b/invokeai/frontend/web/static/dream_web/test.html deleted file mode 100644 index cbb746a5a1..0000000000 --- a/invokeai/frontend/web/static/dream_web/test.html +++ /dev/null @@ -1,246 +0,0 @@ - - - InvokeAI Test - - - - - - - - - - - - - - - -
- -
- - - - diff --git a/invokeai/frontend/web/static/legacy_web/favicon.ico b/invokeai/frontend/web/static/legacy_web/favicon.ico deleted file mode 100644 index 51eb844a6a..0000000000 Binary files a/invokeai/frontend/web/static/legacy_web/favicon.ico and /dev/null differ diff --git a/invokeai/frontend/web/static/legacy_web/index.css b/invokeai/frontend/web/static/legacy_web/index.css deleted file mode 100644 index 51f0f267c3..0000000000 --- a/invokeai/frontend/web/static/legacy_web/index.css +++ /dev/null @@ -1,152 +0,0 @@ -* { - font-family: 'Arial'; - font-size: 100%; -} -body { - font-size: 1em; -} -textarea { - font-size: 0.95em; -} -header, form, #progress-section { - margin-left: auto; - margin-right: auto; - max-width: 1024px; - text-align: center; -} -fieldset { - border: none; - line-height: 2.2em; -} -select, input { - margin-right: 10px; - padding: 2px; -} -input[type=submit] { - background-color: #666; - color: white; -} -input[type=checkbox] { - margin-right: 0px; - width: 20px; - height: 20px; - vertical-align: middle; -} -input#seed { - margin-right: 0px; -} -div { - padding: 10px 10px 10px 10px; -} -header { - margin-bottom: 16px; -} -header h1 { - margin-bottom: 0; - font-size: 2em; -} -#search-box { - display: flex; -} -#scaling-inprocess-message { - font-weight: bold; - font-style: italic; - display: none; -} -#prompt { - flex-grow: 1; - padding: 5px 10px 5px 10px; - border: 1px solid #999; - outline: none; -} -#submit { - padding: 5px 10px 5px 10px; - border: 1px solid #999; -} -#reset-all, #remove-image { - margin-top: 12px; - font-size: 0.8em; - background-color: pink; - border: 1px solid #999; - border-radius: 4px; -} -#results { - text-align: center; - margin: auto; - padding-top: 10px; -} -#results figure { - display: inline-block; - margin: 10px; -} -#results figcaption { - font-size: 0.8em; - padding: 3px; - color: #888; - cursor: pointer; -} -#results img { - border-radius: 5px; - object-fit: cover; -} -#fieldset-config { - line-height:2em; - background-color: #F0F0F0; -} -input[type="number"] { - width: 60px; -} -#seed { - width: 150px; -} -button#reset-seed { - font-size: 1.7em; - background: #efefef; - border: 1px solid #999; - border-radius: 4px; - line-height: 0.8; - margin: 0 10px 0 0; - padding: 0 5px 3px; - vertical-align: middle; -} -label { - white-space: nowrap; -} -#progress-section { - display: none; -} -#progress-image { - width: 30vh; - height: 30vh; -} -#cancel-button { - cursor: pointer; - color: red; -} -#basic-parameters { - background-color: #EEEEEE; -} -#txt2img { - background-color: #DCDCDC; -} -#variations { - background-color: #EEEEEE; -} -#img2img { - background-color: #DCDCDC; -} -#gfpgan { - background-color: #EEEEEE; -} -#progress-section { - background-color: #F5F5F5; -} -.section-header { - text-align: left; - font-weight: bold; - padding: 0 0 0 0; -} -#no-results-message:not(:only-child) { - display: none; -} - diff --git a/invokeai/frontend/web/static/legacy_web/index.html b/invokeai/frontend/web/static/legacy_web/index.html deleted file mode 100644 index c96eed54c3..0000000000 --- a/invokeai/frontend/web/static/legacy_web/index.html +++ /dev/null @@ -1,137 +0,0 @@ - - - Stable Diffusion Dream Server - - - - - - - - -
-

Stable Diffusion Dream Server

-
- For news and support for this web service, visit our GitHub site -
-
- -
-
-
- -
-
-
Basic options
- - - - - - - - - - -
- - - - - - - - - -
- - - - - -
- - - - - - -
-
-
Image-to-image options
- - - -
- - - - -
-
-
Post-processing options
- - - - - - -
-
-
-
-
- - -
- -
- Postprocessing...1/3 -
- -
- -
-
-

No results...

-
-
-
- - diff --git a/invokeai/frontend/web/static/legacy_web/index.js b/invokeai/frontend/web/static/legacy_web/index.js deleted file mode 100644 index a150f3f2e9..0000000000 --- a/invokeai/frontend/web/static/legacy_web/index.js +++ /dev/null @@ -1,234 +0,0 @@ -function toBase64(file) { - return new Promise((resolve, reject) => { - const r = new FileReader(); - r.readAsDataURL(file); - r.onload = () => resolve(r.result); - r.onerror = (error) => reject(error); - }); -} - -function appendOutput(src, seed, config) { - let outputNode = document.createElement('figure'); - - let variations = config.with_variations; - if (config.variation_amount > 0) { - variations = - (variations ? variations + ',' : '') + - seed + - ':' + - config.variation_amount; - } - let baseseed = - config.with_variations || config.variation_amount > 0 ? config.seed : seed; - let altText = - baseseed + ' | ' + (variations ? variations + ' | ' : '') + config.prompt; - - // img needs width and height for lazy loading to work - const figureContents = ` - - ${altText} - -
${seed}
- `; - - outputNode.innerHTML = figureContents; - let figcaption = outputNode.querySelector('figcaption'); - - // Reload image config - figcaption.addEventListener('click', () => { - let form = document.querySelector('#generate-form'); - for (const [k, v] of new FormData(form)) { - if (k == 'initimg') { - continue; - } - form.querySelector(`*[name=${k}]`).value = config[k]; - } - - document.querySelector('#seed').value = baseseed; - document.querySelector('#with_variations').value = variations || ''; - if (document.querySelector('#variation_amount').value <= 0) { - document.querySelector('#variation_amount').value = 0.2; - } - - saveFields(document.querySelector('#generate-form')); - }); - - document.querySelector('#results').prepend(outputNode); -} - -function saveFields(form) { - for (const [k, v] of new FormData(form)) { - if (typeof v !== 'object') { - // Don't save 'file' type - localStorage.setItem(k, v); - } - } -} - -function loadFields(form) { - for (const [k, v] of new FormData(form)) { - const item = localStorage.getItem(k); - if (item != null) { - form.querySelector(`*[name=${k}]`).value = item; - } - } -} - -function clearFields(form) { - localStorage.clear(); - let prompt = form.prompt.value; - form.reset(); - form.prompt.value = prompt; -} - -const BLANK_IMAGE_URL = - 'data:image/svg+xml,'; -async function generateSubmit(form) { - const prompt = document.querySelector('#prompt').value; - - // Convert file data to base64 - let formData = Object.fromEntries(new FormData(form)); - formData.initimg_name = formData.initimg.name; - formData.initimg = - formData.initimg.name !== '' ? await toBase64(formData.initimg) : null; - - let strength = formData.strength; - let totalSteps = formData.initimg - ? Math.floor(strength * formData.steps) - : formData.steps; - - let progressSectionEle = document.querySelector('#progress-section'); - progressSectionEle.style.display = 'initial'; - let progressEle = document.querySelector('#progress-bar'); - progressEle.setAttribute('max', totalSteps); - let progressImageEle = document.querySelector('#progress-image'); - progressImageEle.src = BLANK_IMAGE_URL; - - progressImageEle.style.display = {}.hasOwnProperty.call( - formData, - 'progress_images' - ) - ? 'initial' - : 'none'; - - // Post as JSON, using Fetch streaming to get results - fetch(form.action, { - method: form.method, - body: JSON.stringify(formData), - }).then(async (response) => { - const reader = response.body.getReader(); - - let noOutputs = true; - while (true) { - let { value, done } = await reader.read(); - value = new TextDecoder().decode(value); - if (done) { - progressSectionEle.style.display = 'none'; - break; - } - - for (let event of value.split('\n').filter((e) => e !== '')) { - const data = JSON.parse(event); - - if (data.event === 'result') { - noOutputs = false; - appendOutput(data.url, data.seed, data.config); - progressEle.setAttribute('value', 0); - progressEle.setAttribute('max', totalSteps); - } else if (data.event === 'upscaling-started') { - document.getElementById('processing_cnt').textContent = - data.processed_file_cnt; - document.getElementById('scaling-inprocess-message').style.display = - 'block'; - } else if (data.event === 'upscaling-done') { - document.getElementById('scaling-inprocess-message').style.display = - 'none'; - } else if (data.event === 'step') { - progressEle.setAttribute('value', data.step); - if (data.url) { - progressImageEle.src = data.url; - } - } else if (data.event === 'canceled') { - // avoid alerting as if this were an error case - noOutputs = false; - } - } - } - - // Re-enable form, remove no-results-message - form.querySelector('fieldset').removeAttribute('disabled'); - document.querySelector('#prompt').value = prompt; - document.querySelector('progress').setAttribute('value', '0'); - - if (noOutputs) { - alert('Error occurred while generating.'); - } - }); - - // Disable form while generating - form.querySelector('fieldset').setAttribute('disabled', ''); - document.querySelector('#prompt').value = `Generating: "${prompt}"`; -} - -async function fetchRunLog() { - try { - let response = await fetch('/run_log.json'); - const data = await response.json(); - for (let item of data.run_log) { - appendOutput(item.url, item.seed, item); - } - } catch (e) { - console.error(e); - } -} - -window.onload = async () => { - document.querySelector('#prompt').addEventListener('keydown', (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - const form = e.target.form; - generateSubmit(form); - } - }); - document.querySelector('#generate-form').addEventListener('submit', (e) => { - e.preventDefault(); - const form = e.target; - - generateSubmit(form); - }); - document.querySelector('#generate-form').addEventListener('change', (e) => { - saveFields(e.target.form); - }); - document.querySelector('#reset-seed').addEventListener('click', (e) => { - document.querySelector('#seed').value = -1; - saveFields(e.target.form); - }); - document.querySelector('#reset-all').addEventListener('click', (e) => { - clearFields(e.target.form); - }); - document.querySelector('#remove-image').addEventListener('click', (e) => { - initimg.value = null; - }); - loadFields(document.querySelector('#generate-form')); - - document.querySelector('#cancel-button').addEventListener('click', () => { - fetch('/cancel').catch((e) => { - console.error(e); - }); - }); - document.documentElement.addEventListener('keydown', (e) => { - if (e.key === 'Escape') - fetch('/cancel').catch((err) => { - console.error(err); - }); - }); - - if (!config.gfpgan_model_exists) { - document.querySelector('#gfpgan').style.display = 'none'; - } - await fetchRunLog(); -};