mirror of
https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
synced 2025-04-24 22:09:00 +08:00
Display features for PNG Info tab
Text on PNG Info tab is parsed and colorized in a way that makes it easier to read. Parameter values can be copied by clicking on them. - High performance regex used for parsing - Normal values are displayed in blue, but string content is displayed in orange to improve readability (i.e. adetailer prompts) - Clicking to copy uses a pointer cursor and a green color animation to show something is happening - Color choices configured differently for dark mode in order to optimize readability - Copying strings with \n automatically converts to newlines during copy operation - Settings that don't follow normal conventions are not parsed, but displayed in the old style (i.e. dynamic prompt templates) - If there are parsing issues (or exceptions), defaults to the old display mode
This commit is contained in:
parent
82a973c043
commit
efa87a6abe
@ -2,25 +2,60 @@ import os
|
|||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import json
|
import json
|
||||||
|
import html
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import tqdm
|
import tqdm
|
||||||
|
|
||||||
from modules import shared, images, sd_models, sd_vae, sd_models_config, errors
|
from modules import shared, images, sd_models, sd_vae, sd_models_config, errors, png_parser
|
||||||
from modules.ui_common import plaintext_to_html
|
from modules.ui_common import plaintext_to_html
|
||||||
import gradio as gr
|
import gradio as gr
|
||||||
import safetensors.torch
|
import safetensors.torch
|
||||||
|
|
||||||
|
|
||||||
|
def pnginfo_format_setting(name, value):
|
||||||
|
cls_name = 'geninfo-setting-string' if value.startswith('"') else 'geninfo-setting-value'
|
||||||
|
return f"<span class='geninfo-setting-name'>{html.escape(name)}:</span> <span class='{cls_name}' onclick='uiCopyElementText(this)'>{html.escape(value)}</span>"
|
||||||
|
|
||||||
def run_pnginfo(image):
|
def run_pnginfo(image):
|
||||||
if image is None:
|
if image is None:
|
||||||
return '', '', ''
|
return '', '', ''
|
||||||
|
|
||||||
geninfo, items = images.read_info_from_image(image)
|
geninfo, items = images.read_info_from_image(image)
|
||||||
items = {**{'parameters': geninfo}, **items}
|
|
||||||
|
|
||||||
info = ''
|
info = ''
|
||||||
|
parser = png_parser.PngParser(geninfo)
|
||||||
|
if parser.valid:
|
||||||
|
info += f"""
|
||||||
|
<div class='pnginfo-page'>
|
||||||
|
<p><b>parameters</b></p>
|
||||||
|
{plaintext_to_html(str(parser.positive))}
|
||||||
|
<p>
|
||||||
|
<span class='geninfo-setting-name'>Negative prompt:</span><br>{html.escape(str(parser.negative))}
|
||||||
|
</p>
|
||||||
|
"""
|
||||||
|
if parser.settings is None:
|
||||||
|
info += f"{plaintext_to_html(str(parser.parameters))}"
|
||||||
|
else:
|
||||||
|
info += f"<p>"
|
||||||
|
first = True
|
||||||
|
for setting in parser.settings:
|
||||||
|
if first:
|
||||||
|
first = False
|
||||||
|
else:
|
||||||
|
info += ", "
|
||||||
|
info += pnginfo_format_setting(str(setting[0]), str(setting[1])+str(setting[2]))
|
||||||
|
info += f"</p>"
|
||||||
|
|
||||||
|
if parser.extra is not None:
|
||||||
|
info += f"{plaintext_to_html(str(parser.extra))}"
|
||||||
|
|
||||||
|
info += f"""
|
||||||
|
</div>\n
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
items = {**{'parameters': geninfo}, **items}
|
||||||
|
|
||||||
for key, text in items.items():
|
for key, text in items.items():
|
||||||
info += f"""
|
info += f"""
|
||||||
<div>
|
<div>
|
||||||
|
48
modules/png_parser.py
Normal file
48
modules/png_parser.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
class PngParser:
|
||||||
|
re_top_level = None
|
||||||
|
re_extra_newline = None
|
||||||
|
re_parameters = None
|
||||||
|
|
||||||
|
def __init__(self, pnginfo_string):
|
||||||
|
PngParser.init_re()
|
||||||
|
|
||||||
|
self.valid = self.parse_pnginfo(pnginfo_string)
|
||||||
|
|
||||||
|
def parse_pnginfo(self, pnginfo_string):
|
||||||
|
try:
|
||||||
|
# separate positive, negative, and parameters
|
||||||
|
tlen = len(pnginfo_string)
|
||||||
|
m = PngParser.re_top_level.search(pnginfo_string)
|
||||||
|
if m is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.positive = m.group(1)
|
||||||
|
self.negative = m.group(2)
|
||||||
|
self.parameters = m.group(3)
|
||||||
|
self.extra = None
|
||||||
|
self.settings = None
|
||||||
|
|
||||||
|
# parse extra parameters (if they exist) by a newline outside of quotes
|
||||||
|
m = PngParser.re_extra_newline.search(self.parameters)
|
||||||
|
if m is not None:
|
||||||
|
s = m.span()
|
||||||
|
self.extra = self.parameters[s[1]:]
|
||||||
|
self.parameters = self.parameters[:s[0]]
|
||||||
|
|
||||||
|
# parse standard parameters
|
||||||
|
self.settings = PngParser.re_parameters.findall(self.parameters)
|
||||||
|
if self.settings is None:
|
||||||
|
return False
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init_re(cls):
|
||||||
|
if cls.re_top_level is None:
|
||||||
|
cls.re_top_level = re.compile(r'^(?P<positive>(?:.|\n)*)\nNegative prompt: (?P<negative>(?:.|\n)*)\n(?=Steps: )(?P<parameters>(?:.|\n)*)$')
|
||||||
|
cls.re_extra_newline = re.compile(r'\n(?=(?:[^"]*"[^"]*")*[^"]*$)')
|
||||||
|
cls.re_parameters = re.compile(r'\s*(?P<param>[^:,]+):\s*(?P<quote>")?(?P<value>(?(2)(?:.)*?(?:(?<!\\)")|.*?))(?:\s*,\s*|$)')
|
14
script.js
14
script.js
@ -212,3 +212,17 @@ function uiElementInSight(el) {
|
|||||||
|
|
||||||
return isOnScreen;
|
return isOnScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function uiCopyElementText(el) {
|
||||||
|
text = el.innerText
|
||||||
|
if (text.startsWith('"')) {
|
||||||
|
text = text.substring(1, text.length-1).replace('\\n', '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(text)
|
||||||
|
|
||||||
|
el.classList.remove('animate');
|
||||||
|
setTimeout(() => {
|
||||||
|
el.classList.add('animate');
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
76
style.css
76
style.css
@ -1665,3 +1665,79 @@ body.resizing .resize-handle {
|
|||||||
visibility: visible;
|
visibility: visible;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PngInfo colors */
|
||||||
|
:root {
|
||||||
|
--pnginfo-value-color:var(--secondary-600);
|
||||||
|
--pnginfo-string-color:var(--primary-600);
|
||||||
|
--pnginfo-value-hover:var(--secondary-100);
|
||||||
|
--pnginfo-string-hover:var(--primary-100);
|
||||||
|
--pnginfo-copy-color:#22c922;
|
||||||
|
--pnginfo-copy-background:#a9cfa9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--pnginfo-value-color:var(--secondary-400);
|
||||||
|
--pnginfo-string-color:var(--primary-400);
|
||||||
|
--pnginfo-value-hover:var(--secondary-700);
|
||||||
|
--pnginfo-string-hover:var(--primary-700);
|
||||||
|
--pnginfo-copy-color:#a9cfa9;
|
||||||
|
--pnginfo-copy-background:#22c922;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PngInfo styles */
|
||||||
|
.pnginfo-page p span.geninfo-setting-name {
|
||||||
|
font-weight: var(--weight-semibold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pnginfo-page p span.geninfo-setting-value {
|
||||||
|
color: var(--pnginfo-value-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pnginfo-page p span.geninfo-setting-value:hover {
|
||||||
|
background-color: var(--pnginfo-value-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pnginfo-page p span.geninfo-setting-string {
|
||||||
|
color: var(--pnginfo-string-color);
|
||||||
|
font-style: italic;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pnginfo-page p span.geninfo-setting-string:hover {
|
||||||
|
background-color: var(--pnginfo-string-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PngInfo animations */
|
||||||
|
@keyframes copyAnimationSettingValue {
|
||||||
|
0% {
|
||||||
|
color: var(--pnginfo-copy-color);
|
||||||
|
background-color: var(--pnginfo-copy-background);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
color: var(--pnginfo-value-color);
|
||||||
|
background-color: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span.geninfo-setting-value.animate {
|
||||||
|
-webkit-animation: copyAnimationSettingValue 1s 1;
|
||||||
|
animation: copyAnimationSettingValue 1s 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes copyAnimationSettingString {
|
||||||
|
0% {
|
||||||
|
color: var(--pnginfo-copy-color);
|
||||||
|
background-color: var(--pnginfo-copy-background);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
color: var(--pnginfo-string-color);
|
||||||
|
background-color: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span.geninfo-setting-string.animate {
|
||||||
|
-webkit-animation: copyAnimationSettingString 1s 1;
|
||||||
|
animation: copyAnimationSettingString 1s 1;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user