Repository: AUTOMATIC1111/stable-diffusion-webui

Issue:
[Feature Request]: Allow extensions to send paste params to other tabs
### Is there an existing issue for this?

- [X] I have searched the existing issues and checked the recent builds/commits

### What would your feature do ?

Currently it is not possible for infotext fields added by extensions to hook into the `Send to <X>` feature. They can be parsed from the infotext, but the list of parameters that can be sent (`paste_field_names`) is hardcoded.

Keeping in mind the various "Allow sending X to img2img" options that have been added thus far, it would be nice if there were some kind of uniformity for choosing what to send. There could later be an option that allows disabling one of these extension-added send to parameters.

### Proposed workflow

1. Generate an image with an always-on script extension parameter
2. Click "Send to img2img"
3. The extension parameters are copied to the other UI

### Additional information

_No response_


Discussion:
Yo I'm curious if this is for an extension you are doing.  
I faced something similar before.  

Since my script is (was), being ran twice, once for txt2img and once for img2img, there are two instances in memory, but technically one class.  

Set the data you want passed back and forth as class members, when your components change, have them push and pull from the class variables. This way, when they change tab, it's already populated.  

You may have to store a reference of self in a list as a class variable. This way one instance can grab the other instance from the list and update those components as the local ones are changed.  

Another way is to create some controllers is js, add event listeners that will set the other components.  
You can test it using some of these controllers. https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/7601/files#diff-f273af4b9b957263caf8afbdf69c1c5bfe7c53a62c1de4a5adc267d77cc8ebc5  
Just copy and paste what you need. I'm trying to get my basic library included, while also implementing a feature, my feature uses about half of them, so it might be allowed.  

Anyways, for the js controllers, since they are classes with a constructor, you call them using the 'new' keyword.  
These classes are built to take in an html element.  
The html element that you would use are the same elements that the element id gets created on when creating a gradio component.  
i.e.- `gr.Text(elem_id="my_id")`  

Then in javascript you'll call it like this:
```javascript
let myComponentController = new TextComponentController(gradioApp().getElementById("my_id"));
```  

Just think of it like a python class, with a getter and setter that triggers the appropriate event handler that updates the front and backend. Like this.  
```javascript
myComponentController.setVal("My cool Prompt")
```  

So then on your `myComponentController`, you can add an event listener that will set the val of the other text area.  
```javascript
myComponentController.child.addEventListener("change", (e) => {e.preventDefault(); myOtherComponent.setVal(myComponentController.getVal())})
```

This way, when one changes, the other changes automatically, now they're in sync, front and backend.  

Oh, but there might be a circular call, so you might want to create a class and hold some value that can reference which one started the calls, so when it comes back around you can stop it. You can call this class, your AppController, or whatever makes sense.  

You can also say screw (I'm a carpenter so I can say screw it) making my own prompt box, I'm just going to hook into the regular one.  
```javascript
let txt2imgPrompt = new TextComponentController(gradioApp().getElementById("txt2img_prompt"));
txt2imgPrompt.setVal("FTW");
```

Resolving patch:
diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py
--- a/modules/generation_parameters_copypaste.py
+++ b/modules/generation_parameters_copypaste.py
@@ -23,13 +23,14 @@
 
 
 class ParamBinding:
-    def __init__(self, paste_button, tabname, source_text_component=None, source_image_component=None, source_tabname=None, override_settings_component=None):
+    def __init__(self, paste_button, tabname, source_text_component=None, source_image_component=None, source_tabname=None, override_settings_component=None, paste_field_names=[]):
         self.paste_button = paste_button
         self.tabname = tabname
         self.source_text_component = source_text_component
         self.source_image_component = source_image_component
         self.source_tabname = source_tabname
         self.override_settings_component = override_settings_component
+        self.paste_field_names = paste_field_names
 
 
 def reset():
@@ -133,7 +134,7 @@ def connect_paste_params_buttons():
             connect_paste(binding.paste_button, fields, binding.source_text_component, binding.override_settings_component, binding.tabname)
 
         if binding.source_tabname is not None and fields is not None:
-            paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration'] + (["Seed"] if shared.opts.send_seed else [])
+            paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration'] + (["Seed"] if shared.opts.send_seed else []) + binding.paste_field_names
             binding.paste_button.click(
                 fn=lambda *x: x,
                 inputs=[field for field, name in paste_fields[binding.source_tabname]["fields"] if name in paste_field_names],
diff --git a/modules/scripts.py b/modules/scripts.py
--- a/modules/scripts.py
+++ b/modules/scripts.py
@@ -33,6 +33,11 @@ class Script:
     parsing infotext to set the value for the component; see ui.py's txt2img_paste_fields for an example
     """
 
+    paste_field_names = None
+    """if set in ui(), this is a list of names of infotext fields; the fields will be sent through the
+    various "Send to <X>" buttons when clicked
+    """
+
     def title(self):
         """this function should return the title of the script. This is what will be displayed in the dropdown menu."""
 
@@ -256,6 +261,7 @@ def __init__(self):
         self.alwayson_scripts = []
         self.titles = []
         self.infotext_fields = []
+        self.paste_field_names = []
 
     def initialize_scripts(self, is_img2img):
         from modules import scripts_auto_postprocessing
@@ -304,6 +310,9 @@ def create_script_ui(script, inputs, inputs_alwayson):
             if script.infotext_fields is not None:
                 self.infotext_fields += script.infotext_fields
 
+            if script.paste_field_names is not None:
+                self.paste_field_names += script.paste_field_names
+
             inputs += controls
             inputs_alwayson += [script.alwayson for _ in controls]
             script.args_to = len(inputs)
diff --git a/modules/ui_common.py b/modules/ui_common.py
--- a/modules/ui_common.py
+++ b/modules/ui_common.py
@@ -198,9 +198,16 @@ def open_folder(f):
                 html_info = gr.HTML(elem_id=f'html_info_{tabname}')
                 html_log = gr.HTML(elem_id=f'html_log_{tabname}')
 
+            paste_field_names = []
+            if tabname == "txt2img":
+                paste_field_names = modules.scripts.scripts_txt2img.paste_field_names
+            elif tabname == "img2img":
+                paste_field_names = modules.scripts.scripts_img2img.paste_field_names
+
             for paste_tabname, paste_button in buttons.items():
                 parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding(
-                    paste_button=paste_button, tabname=paste_tabname, source_tabname="txt2img" if tabname == "txt2img" else None, source_image_component=result_gallery
+                    paste_button=paste_button, tabname=paste_tabname, source_tabname="txt2img" if tabname == "txt2img" else None, source_image_component=result_gallery,
+                    paste_field_names=paste_field_names
                 ))
 
             return result_gallery, generation_info if tabname != "extras" else html_info_x, html_info, html_log


Test patch: