diff --git a/.eslintrc.json b/.eslintrc.json index 4e5d16a65..0fa3cf216 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -75,6 +75,8 @@ //extraNetworks.js "requestGet": "readonly", "getENActiveTab": "readonly", + "quickApplyStyle": "readonly", + "quickSaveStyle": "readonly", // from python "localization": "readonly", // progressbar.js diff --git a/javascript/contextMenus.js b/javascript/contextMenus.js index fab220da4..40e93f4e4 100644 --- a/javascript/contextMenus.js +++ b/javascript/contextMenus.js @@ -86,25 +86,33 @@ const removeContextMenuOption = initResponse[1]; const addContextMenuEventListener = initResponse[2]; function initContextMenu() { - // Start example Context Menu Items - const generateOnRepeat = (genbuttonid, interruptbuttonid) => { - const genbutton = gradioApp().querySelector(genbuttonid); - const busy = document.getElementById('progressbar')?.style.display === 'block'; - if (!busy) genbutton.click(); - clearInterval(window.generateOnRepeatInterval); - window.generateOnRepeatInterval = setInterval(() => { - const pbBusy = document.getElementById('progressbar')?.style.display === 'block'; - if (!pbBusy) genbutton.click(); - }, 500); + const generateForever = (genbuttonid, interruptbuttonid) => { + if (window.generateOnRepeatInterval) { + log('generateForever: cancel'); + clearInterval(window.generateOnRepeatInterval); + window.generateOnRepeatInterval = null; + } else { + log('generateForever: start'); + const genbutton = gradioApp().querySelector(genbuttonid); + const busy = document.getElementById('progressbar')?.style.display === 'block'; + if (!busy) genbutton.click(); + window.generateOnRepeatInterval = setInterval(() => { + const pbBusy = document.getElementById('progressbar')?.style.display === 'block'; + if (!pbBusy) genbutton.click(); + }, 500); + } }; - const cancelGenerateForever = () => clearInterval(window.generateOnRepeatInterval); - appendContextMenuOption('#txt2img_generate', 'Generate forever', () => generateOnRepeat('#txt2img_generate', '#txt2img_interrupt')); - appendContextMenuOption('#img2img_generate', 'Generate forever', () => generateOnRepeat('#img2img_generate', '#img2img_interrupt')); - appendContextMenuOption('#txt2img_generate', 'Cancel generate forever', cancelGenerateForever); - appendContextMenuOption('#img2img_generate', 'Cancel generate forever', cancelGenerateForever); - appendContextMenuOption('#txt2img_generate', 'Show NVML overlay', initNVML); - appendContextMenuOption('#txt2img_generate', 'Hide NVML overlay', disableNVML); + for (const tab of ['txt2img', 'img2img']) { + for (const el of ['prompt > label > textarea', 'generate']) { + const id = `#${tab}_${el}`; + appendContextMenuOption(id, 'Copy 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); + appendContextMenuOption(id, 'nVidia overlay', initNVML); + } + } } onUiLoaded(initContextMenu); diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index b2704ba42..432da7980 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -216,6 +216,18 @@ function applyStyles(styles) { return newStyles.join('|'); } +function quickApplyStyle() { + const tabname = getENActiveTab(); + const btnApply = gradioApp().getElementById(`${tabname}_extra_apply`); + if (btnApply) btnApply.click(); +} + +function quickSaveStyle() { + const tabname = getENActiveTab(); + const btnSave = gradioApp().getElementById(`${tabname}_extra_quicksave`); + if (btnSave) btnSave.click(); +} + // init function setupExtraNetworksForTab(tabname) { @@ -243,6 +255,14 @@ function setupExtraNetworksForTab(tabname) { btnModel.onclick = () => btnModel.classList.toggle('toolbutton-selected'); tabs.appendChild(buttons); + // details + const detailsImg = gradioApp().getElementById(`${tabname}_extra_details_img`); + const detailsClose = gradioApp().getElementById(`${tabname}_extra_details_close`); + if (detailsImg && detailsClose) { + detailsImg.title = 'Close details'; + detailsImg.onclick = () => detailsClose.click(); + } + // search and description const div = document.createElement('div'); div.classList.add('second-line'); diff --git a/javascript/nvml.js b/javascript/nvml.js index a557c43d5..cf0187367 100644 --- a/javascript/nvml.js +++ b/javascript/nvml.js @@ -1,4 +1,4 @@ -let nvmlInterval = true; // eslint-disable-line prefer-const +let nvmlInterval = null; // eslint-disable-line prefer-const let nvmlEl = null; let nvmlTable = null; @@ -60,7 +60,13 @@ async function initNVML() { gradioApp().appendChild(nvmlEl); log('initNVML'); } - nvmlInterval = setInterval(updateNVML, 1000); + if (nvmlInterval) { + clearInterval(nvmlInterval); + nvmlInterval = null; + nvmlEl.style.display = 'none'; + } else { + nvmlInterval = setInterval(updateNVML, 1000); + } } async function disableNVML() { diff --git a/javascript/sdnext.css b/javascript/sdnext.css index c0201dd78..4fef1203f 100644 --- a/javascript/sdnext.css +++ b/javascript/sdnext.css @@ -157,7 +157,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt /* context menu (ie for the generate button) */ #context-menu { z-index: 9999; position: absolute; display: block; padding: var(--spacing-md); border: 2px solid var(--highlight-color); background: var(--background-fill-primary); color: var(--body-text-color); } -.context-menu-items { list-style: none; margin: 0; padding: 0; } +.context-menu-items { list-style: none; margin: 0; padding: 0; font-size: 0.9em; } .context-menu-items a { display: block; padding: var(--spacing-md); cursor: pointer; font-weight: normal; } .context-menu-items a:hover { background: var(--highlight-color) } @@ -204,6 +204,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt .extra-network-cards .card:hover .actions { display: block; } .extra-network-cards .card:hover .overlay .tags { display: block; } .extra-network-cards .card .actions { font-size: 3em; display: none; text-align-last: right; cursor: pointer; font-variant: unicase; position: absolute; z-index: 100; right: 0; height: 0.7em; width: 100%; background: rgba(0, 0, 0, 0.40); } +.extra-details-close { position: fixed; top: 0.2em; right: 0.2em; z-index: 99; background: var(--button-secondary-background-fill) !important; } #txt2img_description, #img2img_description { max-height: 63px; overflow-y: auto !important; } #txt2img_description > label > textarea, #img2img_description > label > textarea { font-size: 0.9em } diff --git a/modules/sd_models.py b/modules/sd_models.py index 3dfd4ca01..f68d82f1e 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -879,7 +879,7 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No shared.log.error(f'Failed loading {op}: {checkpoint_info.path} auto={err1} diffusion={err2}') return elif os.path.isfile(checkpoint_info.path) and checkpoint_info.path.lower().endswith('.safetensors'): - diffusers_load_config["local_files_only"] = True + # diffusers_load_config["local_files_only"] = True diffusers_load_config["extract_ema"] = shared.opts.diffusers_extract_ema pipeline, model_type = detect_pipeline(checkpoint_info.path, op) if pipeline is None: diff --git a/modules/ui.py b/modules/ui.py index 633aa6f83..3ded98772 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -126,7 +126,7 @@ def process_interrogate(interrogation_function, mode, ii_input_files, ii_input_d images = [f.name for f in ii_input_files] else: if not os.path.isdir(ii_input_dir): - log.error(f"Input directory not found: {ii_input_dir}") + log.error(f"Interrogate: Input directory not found: {ii_input_dir}") return [gr.update(), None] images = modules.shared.listfiles(ii_input_dir) if ii_output_dir != "": @@ -142,6 +142,9 @@ def process_interrogate(interrogation_function, mode, ii_input_files, ii_input_d def interrogate(image): + if image is None: + log.error("Interrogate: no image selected") + return gr.update() prompt = modules.shared.interrogator.interrogate(image.convert("RGB")) return gr.update() if prompt is None else prompt diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index b849b1293..ea702d542 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -393,6 +393,7 @@ class ExtraNetworksUi: self.button_details: gr.Button = None self.button_refresh: gr.Button = None self.button_scan: gr.Button = None + self.button_quicksave: gr.Button = None self.button_save: gr.Button = None self.button_sort: gr.Button = None self.button_apply: gr.Button = None @@ -449,14 +450,14 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): return is_visible, gr.update(visible=is_visible), gr.update(variant=("secondary-down" if is_visible else "secondary")) with ui.details: - details_close = ToolButton(symbols.close, elem_id=tabname+"_extra_details_close") + details_close = ToolButton(symbols.close, elem_id=tabname+"_extra_details_close", elem_classes=['extra-details-close']) details_close.click(fn=lambda: gr.update(visible=False), inputs=[], outputs=[ui.details]) with gr.Row(): with gr.Column(scale=1): text = gr.HTML('
title
') ui.details_components.append(text) with gr.Column(scale=1): - img = gr.Image(value=None, show_label=False, interactive=False, container=True) + img = gr.Image(value=None, show_label=False, interactive=False, container=False, show_download_button=False, show_info=False, elem_id=tabname+"_extra_details_img", elem_classes=['extra-details-img']) ui.details_components.append(img) with gr.Row(): btn_save_img = gr.Button('Replace', elem_classes=['small-button']) @@ -468,12 +469,16 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): with gr.Row(): btn_save_desc = gr.Button('Save', elem_classes=['small-button']) btn_delete_desc = gr.Button('Delete', elem_classes=['small-button']) + btn_close_info = gr.Button('Close', elem_classes=['small-button']) + btn_close_info.click(fn=lambda: gr.update(visible=False), inputs=[], outputs=[ui.details]) with gr.Tab('Model metadata'): info = gr.JSON({}, show_label=False) ui.details_components.append(info) with gr.Row(): btn_save_info = gr.Button('Save', elem_classes=['small-button']) btn_delete_info = gr.Button('Delete', elem_classes=['small-button']) + btn_close_info = gr.Button('Close', elem_classes=['small-button']) + btn_close_info.click(fn=lambda: gr.update(visible=False), inputs=[], outputs=[ui.details]) with gr.Tab('Embedded metadata'): meta = gr.JSON({}, show_label=False) ui.details_components.append(meta) @@ -487,6 +492,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): ui.button_refresh = ToolButton(symbols.refresh, elem_id=tabname+"_extra_refresh") ui.button_scan = ToolButton(symbols.scan, elem_id=tabname+"_extra_scan", visible=True) + ui.button_quicksave = ToolButton(symbols.book, elem_id=tabname+"_extra_quicksave", visible=False) ui.button_save = ToolButton(symbols.book, elem_id=tabname+"_extra_save", visible=False) ui.button_sort = ToolButton(symbols.sort, elem_id=tabname+"_extra_sort", visible=True) ui.button_close = ToolButton(symbols.close, elem_id=tabname+"_extra_close", visible=True) @@ -531,9 +537,9 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): image.thumbnail((512, 512), Image.HAMMING) try: image.save(ui.last_item.local_preview, quality=50) - shared.log.debug(f'Extra network save image: item={ui.last_item.name} filename={ui.last_item.local_preview}') + shared.log.debug(f'Extra network save image: item={ui.last_item.name} filename="{ui.last_item.local_preview}"') except Exception as e: - shared.log.error(f'Extra network save image: item={ui.last_item.name} filename={ui.last_item.local_preview} {e}') + shared.log.error(f'Extra network save image: item={ui.last_item.name} filename="{ui.last_item.local_preview}" {e}') return image def fn_delete_img(): @@ -542,7 +548,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): for file in [f'{fn}{mid}{ext}' for ext in preview_extensions for mid in ['.thumb.', '.preview.', '.']]: if os.path.exists(file): os.remove(file) - shared.log.debug(f'Extra network delete image: item={ui.last_item.name} filename={file}') + shared.log.debug(f'Extra network delete image: item={ui.last_item.name} filename="{file}"') return 'html/card-no-preview.png' def fn_save_desc(desc): @@ -554,7 +560,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): fn = os.path.splitext(ui.last_item.filename)[0] + '.txt' with open(fn, 'w', encoding='utf-8') as f: f.write(desc) - shared.log.debug(f'Extra network save desc: item={ui.last_item.name} filename={fn}') + shared.log.debug(f'Extra network save desc: item={ui.last_item.name} filename="{fn}"') return desc def fn_delete_desc(desc): @@ -565,7 +571,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): else: fn = os.path.splitext(ui.last_item.filename)[0] + '.txt' if os.path.exists(fn): - shared.log.debug(f'Extra network delete desc: item={ui.last_item.name} filename={fn}') + shared.log.debug(f'Extra network delete desc: item={ui.last_item.name} filename="{fn}"') os.remove(fn) return '' return desc @@ -573,7 +579,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): def fn_save_info(info): fn = os.path.splitext(ui.last_item.filename)[0] + '.json' shared.writefile(info, fn, silent=True) - shared.log.debug(f'Extra network save info: item={ui.last_item.name} filename={fn}') + shared.log.debug(f'Extra network save info: item={ui.last_item.name} filename="{fn}"') return info def fn_delete_info(info): @@ -581,7 +587,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): return info fn = os.path.splitext(ui.last_item.filename)[0] + '.json' if os.path.exists(fn): - shared.log.debug(f'Extra network delete info: item={ui.last_item.name} filename={fn}') + shared.log.debug(f'Extra network delete info: item={ui.last_item.name} filename="{fn}"') os.remove(fn) return '' return info @@ -656,7 +662,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): ''' desc = f'Name: {os.path.basename(item.name)}\nDescription: {item.description}\nPrompt: {item.prompt}\nNegative: {item.negative}\nExtra: {item.extra}\n' text = f''' -

{item.name}

+

{item.name}

@@ -704,6 +710,32 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): res = show_details(text=None, img=None, desc=None, info=None, meta=None, params=params) return res + def ui_quicksave_click(name): + from modules import paths, generation_parameters_copypaste + fn = os.path.join(paths.data_path, "params.txt") + if os.path.exists(fn): + with open(fn, "r", encoding="utf8") as file: + prompt = file.read() + else: + prompt = '' + params = generation_parameters_copypaste.parse_generation_parameters(prompt) + fn = os.path.join(shared.opts.styles_dir, os.path.splitext(name)[0] + '.json') + item = { + "type": 'Style', + "name": name, + "title": name, + "filename": fn, + "search_term": None, + "preview": None, + "description": '', + "prompt": params.get('Prompt', ''), + "negative": params.get('Negative prompt', ''), + "extra": '', + "local_preview": None, + } + shared.writefile(item, fn, silent=True) + shared.log.debug(f"Extra network quick save style: item={item['name']} filename='{fn}'") + def ui_sort_cards(msg): shared.log.debug(f'Extra networks: {msg}') return msg @@ -715,6 +747,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): ui.button_refresh.click(fn=ui_refresh_click, _js='getENActivePage', inputs=[ui.search], outputs=ui.pages) ui.button_scan.click(fn=ui_scan_click, _js='getENActivePage', inputs=[ui.search], outputs=ui.pages) ui.button_save.click(fn=ui_save_click, inputs=[], outputs=ui.details_components + [ui.details]) + ui.button_quicksave.click(fn=ui_quicksave_click, _js="() => prompt('Prompt name', '')", inputs=[ui.search], outputs=[]) ui.button_details.click(show_details, _js="getCardDetails", inputs=ui.details_components + [dummy_state], outputs=ui.details_components + [ui.details]) ui.state.change(state_change, inputs=[ui.state], outputs=[]) return ui
Type{page.title}
Alias{getattr(item, 'alias', 'N/A')}