diff --git a/frontend/css_and_js.py b/frontend/css_and_js.py index 94e04a5..03f6a63 100644 --- a/frontend/css_and_js.py +++ b/frontend/css_and_js.py @@ -1,4 +1,6 @@ from os import path +import json + def readTextFile(*args): dir = path.dirname(__file__) @@ -7,31 +9,25 @@ def readTextFile(*args): data = f.read() return data + def css(opt): styling = readTextFile("css", "styles.css") if not opt.no_progressbar_hiding: styling += readTextFile("css", "no_progress_bar.css") return styling + def js(opt): data = readTextFile("js", "index.js") data = "(z) => {" + data + "; return z ?? [] }" return data + # Wrap the typical SD method call into async closure for ease of use -# If you call frontend method without wrapping -# DONT FORGET to bind input argument if you need it: SD.with(x) -def w(sd_method_call): - return f"async (x) => {{ return await SD.with(x).{sd_method_call} ?? x ?? []; }}" - -def js_move_image(from_id, to_id): - return w(f"moveImageFromGallery('{from_id}', '{to_id}')") - -def js_copy_to_clipboard(from_id): - return w(f"copyImageFromGalleryToClipboard('{from_id}')") - -def js_painterro_launch(to_id): - return w(f"Painterro.init('{to_id}')") - -def js_img2img_submit(prompt_row_id): - return w(f"clickFirstVisibleButton('{prompt_row_id}')") +# Supplies the js function with a params object +# That includes all the passed arguments and input from Gradio: x +# Example call in Gradio component's event handler (pass the result to _js arg): +# _js=call_JS("myJsMethod", arg1="string", arg2=100, arg3=[]) +def call_JS(sd_method, **kwargs): + param_str = json.dumps(kwargs) + return f"async (x) => {{ return await SD.{sd_method}({{ x, ...{param_str} }}) ?? []; }}" diff --git a/frontend/frontend.py b/frontend/frontend.py index 69c0b04..4f1c46f 100644 --- a/frontend/frontend.py +++ b/frontend/frontend.py @@ -1,6 +1,5 @@ import gradio as gr -from frontend.css_and_js import * -from frontend.css_and_js import css +from frontend.css_and_js import css, js, call_JS import frontend.ui_functions as uifn def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaults={}, RealESRGAN=True, GFPGAN=True, @@ -47,10 +46,13 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul gr.Markdown( 'Select an image from the gallery, then click one of the buttons below to perform an action.') with gr.Row(): - output_txt2img_copy_clipboard = gr.Button("Copy to clipboard").click(fn=None, - inputs=output_txt2img_gallery, - outputs=[], - _js=js_copy_to_clipboard('txt2img_gallery_output')) + output_txt2img_copy_clipboard = gr.Button("Copy to clipboard")\ + .click(fn=None, + inputs=output_txt2img_gallery, + outputs=[], + _js=call_JS("copyImageFromGalleryToClipboard", + fromId="txt2img_gallery_output") + ) output_txt2img_copy_to_input_btn = gr.Button("Push to img2img") if RealESRGAN is not None: output_txt2img_to_upscale_esrgan = gr.Button("Upscale w/ ESRGAN") @@ -60,11 +62,13 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul with gr.Row(): output_txt2img_copy_params = gr.Button("Copy full parameters").click( inputs=output_txt2img_params, outputs=[], - _js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False) + fn=None, show_progress=False, + _js=call_JS("gradioInputToClipboard") + ) output_txt2img_seed = gr.Number(label='Seed', interactive=False, visible=False) output_txt2img_copy_seed = gr.Button("Copy only seed").click( inputs=output_txt2img_seed, outputs=[], - _js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False) + _js=call_JS("gradioInputToClipboard"), fn=None, show_progress=False) output_txt2img_stats = gr.HTML(label='Stats') with gr.Column(): @@ -153,7 +157,7 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul value=3, visible=False) img2img_resize = gr.Radio(label="Resize mode", - choices=["Just resize", "Crop and resize", "Resize and fill"], + choices=["Just resize"], type="index", value=img2img_resize_modes[img2img_defaults['resize_mode']]) @@ -172,17 +176,18 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul output_img2img_copy_to_clipboard_btn = gr.Button("Copy to clipboard") output_img2img_copy_to_input_btn = gr.Button("Push to img2img input") output_img2img_copy_to_mask_btn = gr.Button("Push to img2img input mask") + gr.Markdown("Warning: This will clear your current image and mask settings!") with gr.TabItem("Output info", id="img2img_output_info_tab"): output_img2img_params = gr.Textbox(label="Generation parameters") with gr.Row(): output_img2img_copy_params = gr.Button("Copy full parameters").click( inputs=output_img2img_params, outputs=[], - _js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False) + _js=call_JS("gradioInputToClipboard"), fn=None, show_progress=False) output_img2img_seed = gr.Number(label='Seed', interactive=False, visible=False) output_img2img_copy_seed = gr.Button("Copy only seed").click( inputs=output_img2img_seed, outputs=[], - _js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False) + _js=call_JS("gradioInputToClipboard"), fn=None, show_progress=False) output_img2img_stats = gr.HTML(label='Stats') gr.Markdown('# img2img settings') @@ -247,24 +252,32 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul uifn.copy_img_to_input, [output_txt2img_gallery], [img2img_image_editor, img2img_image_mask, tabs], - _js=js_move_image('txt2img_gallery_output', 'img2img_editor') + _js=call_JS("moveImageFromGallery", + fromId="txt2img_gallery_output", + toId="img2img_editor") ) output_img2img_copy_to_input_btn.click( uifn.copy_img_to_edit, [output_img2img_gallery], [img2img_image_editor, tabs, img2img_image_editor_mode], - _js=js_move_image('img2img_gallery_output', 'img2img_editor') + _js=call_JS("moveImageFromGallery", + fromId="img2img_gallery_output", + toId="img2img_editor") ) output_img2img_copy_to_mask_btn.click( uifn.copy_img_to_mask, [output_img2img_gallery], [img2img_image_mask, tabs, img2img_image_editor_mode], - _js=js_move_image('img2img_gallery_output', 'img2img_editor') + _js=call_JS("moveImageFromGallery", + fromId="img2img_gallery_output", + toId="img2img_editor") ) output_img2img_copy_to_clipboard_btn.click(fn=None, inputs=output_img2img_gallery, outputs=[], - _js=js_copy_to_clipboard('img2img_gallery_output')) + _js=call_JS("copyImageFromGalleryToClipboard", + fromId="img2img_gallery_output") + ) img2img_btn_mask.click( img2img, @@ -287,9 +300,14 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul # GENERATE ON ENTER img2img_prompt.submit(None, None, None, - _js=js_img2img_submit("prompt_row")) + _js=call_JS("clickFirstVisibleButton", + rowId="prompt_row")) - img2img_painterro_btn.click(None, [img2img_image_editor], [img2img_image_editor, img2img_image_mask], _js=js_painterro_launch('img2img_editor')) + img2img_painterro_btn.click(None, + [img2img_image_editor], + [img2img_image_editor, img2img_image_mask], + _js=call_JS("Painterro.init", toId="img2img_editor") + ) img2img_width.change(fn=uifn.update_dimensions_info, inputs=[img2img_width, img2img_height], outputs=img2img_dimensions_info_text_box) img2img_height.change(fn=uifn.update_dimensions_info, inputs=[img2img_width, img2img_height], outputs=img2img_dimensions_info_text_box) @@ -338,7 +356,10 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul uifn.copy_img_to_upscale_esrgan, output_txt2img_gallery, [realesrgan_source, tabs], - _js=js_move_image('txt2img_gallery_output', 'img2img_editor')) + _js=call_JS("moveImageFromGallery", + fromId="txt2img_gallery_output", + toId="img2img_editor") + ) gr.HTML("""
diff --git a/frontend/js/index.js b/frontend/js/index.js index 1dae126..64b9f18 100644 --- a/frontend/js/index.js +++ b/frontend/js/index.js @@ -5,8 +5,8 @@ window.SD = (() => { */ class PainterroClass { static isOpen = false; - static async init (toId) { - const img = SD.x; + static async init ({ x, toId }) { + const img = x; const originalImage = Array.isArray(img) ? img[0] : img; if (window.Painterro === undefined) { @@ -110,42 +110,28 @@ window.SD = (() => { */ class SDClass { el = new ElementCache(); - x; Painterro = PainterroClass; - with (x) { - this.x = x; - return this; - } - moveImageFromGallery (fromId, toId) { - if (!Array.isArray(this.x) || this.x.length === 0) return; + moveImageFromGallery ({ x, fromId, toId }) { + if (!Array.isArray(x) || x.length === 0) return; this.clearImageInput(this.el.get(`#${toId}`)); const i = this.#getGallerySelectedIndex(this.el.get(`#${fromId}`)); - return [this.x[i].replace('data:;','data:image/png;')]; + return [x[i].replace('data:;','data:image/png;')]; } - async copyImageFromGalleryToClipboard (fromId) { - if (!Array.isArray(this.x) || this.x.length === 0) return; + async copyImageFromGalleryToClipboard ({ x, fromId }) { + if (!Array.isArray(x) || x.length === 0) return; const i = this.#getGallerySelectedIndex(this.el.get(`#${fromId}`)); - const data = this.x[i]; + const data = x[i]; const blob = await (await fetch(data.replace('data:;','data:image/png;'))).blob(); const item = new ClipboardItem({'image/png': blob}); - try { - navigator.clipboard.write([item]); - } catch (e) { - SDClass.error(e); - } - - return this.x; + await this.copyToClipboard([item]); } - clearImageInput (imageEditor) { - imageEditor?.querySelector('.modify-upload button:last-child')?.click(); - } - clickFirstVisibleButton(rowId) { + clickFirstVisibleButton({ rowId }) { const generateButtons = this.el.get(`#${rowId}`).querySelectorAll('.gr-button-primary'); if (!generateButtons) return; @@ -161,6 +147,21 @@ window.SD = (() => { } } } + async gradioInputToClipboard ({ x }) { return this.copyToClipboard(x); } + async copyToClipboard (value) { + if (!value || typeof value === 'boolean') return; + try { + if (Array.isArray(value) && + value.length && + value[0] instanceof ClipboardItem) { + await navigator.clipboard.write(value); + } else { + await navigator.clipboard.writeText(value); + } + } catch (e) { + SDClass.error(e); + } + } static error (e) { console.error(e); if (typeof e === 'string') { @@ -169,6 +170,9 @@ window.SD = (() => { alert(e.message); } } + clearImageInput (imageEditor) { + imageEditor?.querySelector('.modify-upload button:last-child')?.click(); + } #getGallerySelectedIndex (gallery) { const selected = gallery.querySelector(`.\\!ring-2`); return selected ? [...selected.parentNode.children].indexOf(selected) : 0;