diff --git a/.gitignore b/.gitignore
index 57e31e9..baeabd5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,7 +30,8 @@ build_chb.py
/interrogate
/user.css
/.idea
-notification.mp3
+/notification.ogg
+/notification.mp3
/SwinIR
/textual_inversion
.vscode
diff --git a/css/style.css b/css/style.css
new file mode 100644
index 0000000..d94ec87
--- /dev/null
+++ b/css/style.css
@@ -0,0 +1,28 @@
+/* based on https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/v1.6.0/style.css */
+
+#context-menu{
+ z-index:9999;
+ position:absolute;
+ display:block;
+ padding:0px 0;
+ border:2px solid #a55000;
+ border-radius:8px;
+ box-shadow:1px 1px 2px #CE6400;
+ width: 200px;
+}
+
+.context-menu-items{
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.context-menu-items a{
+ display:block;
+ padding:5px;
+ cursor:pointer;
+}
+
+.context-menu-items a:hover{
+ background: #a55000;
+}
diff --git a/javascript/contextMenus.js b/javascript/contextMenus.js
new file mode 100644
index 0000000..cf76993
--- /dev/null
+++ b/javascript/contextMenus.js
@@ -0,0 +1,171 @@
+// based on https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/v1.6.0/javascript/contextMenus.js
+
+var contextMenuInit = function() {
+ let eventListenerApplied = false;
+ let menuSpecs = new Map();
+
+ const uid = function() {
+ return Date.now().toString(36) + Math.random().toString(36).substring(2);
+ };
+
+ function showContextMenu(event, element, menuEntries) {
+ let posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
+ let posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
+
+ let oldMenu = gradioApp().querySelector('#context-menu');
+ if (oldMenu) {
+ oldMenu.remove();
+ }
+
+ let baseStyle = window.getComputedStyle(gradioApp().querySelector('button.selected'));
+
+ const contextMenu = document.createElement('nav');
+ contextMenu.id = "context-menu";
+ contextMenu.style.background = baseStyle.background;
+ contextMenu.style.color = baseStyle.color;
+ contextMenu.style.fontFamily = baseStyle.fontFamily;
+ contextMenu.style.top = posy + 'px';
+ contextMenu.style.left = posx + 'px';
+
+ const contextMenuList = document.createElement('ul');
+ contextMenuList.className = 'context-menu-items';
+ contextMenu.append(contextMenuList);
+
+ menuEntries.forEach(function(entry) {
+ let contextMenuEntry = document.createElement('a');
+ contextMenuEntry.innerHTML = entry['name'];
+ contextMenuEntry.addEventListener("click", function() {
+ entry['func']();
+ });
+ contextMenuList.append(contextMenuEntry);
+
+ });
+
+ gradioApp().appendChild(contextMenu);
+
+ let menuWidth = contextMenu.offsetWidth + 4;
+ let menuHeight = contextMenu.offsetHeight + 4;
+
+ let windowWidth = window.innerWidth;
+ let windowHeight = window.innerHeight;
+
+ if ((windowWidth - posx) < menuWidth) {
+ contextMenu.style.left = windowWidth - menuWidth + "px";
+ }
+
+ if ((windowHeight - posy) < menuHeight) {
+ contextMenu.style.top = windowHeight - menuHeight + "px";
+ }
+
+ }
+
+ function appendContextMenuOption(targetElementSelector, entryName, entryFunction) {
+
+ var currentItems = menuSpecs.get(targetElementSelector);
+
+ if (!currentItems) {
+ currentItems = [];
+ menuSpecs.set(targetElementSelector, currentItems);
+ }
+ let newItem = {
+ id: targetElementSelector + '_' + uid(),
+ name: entryName,
+ func: entryFunction,
+ isNew: true
+ };
+
+ currentItems.push(newItem);
+ return newItem['id'];
+ }
+
+ function removeContextMenuOption(uid) {
+ menuSpecs.forEach(function(v) {
+ let index = -1;
+ v.forEach(function(e, ei) {
+ if (e['id'] == uid) {
+ index = ei;
+ }
+ });
+ if (index >= 0) {
+ v.splice(index, 1);
+ }
+ });
+ }
+
+ function addContextMenuEventListener() {
+ if (eventListenerApplied) {
+ return;
+ }
+ gradioApp().addEventListener("click", function(e) {
+ if (!e.isTrusted) {
+ return;
+ }
+
+ let oldMenu = gradioApp().querySelector('#context-menu');
+ if (oldMenu) {
+ oldMenu.remove();
+ }
+ });
+ gradioApp().addEventListener("contextmenu", function(e) {
+ let oldMenu = gradioApp().querySelector('#context-menu');
+ if (oldMenu) {
+ oldMenu.remove();
+ }
+ menuSpecs.forEach(function(v, k) {
+ if (e.composedPath()[0].matches(k)) {
+ showContextMenu(e, e.composedPath()[0], v);
+ e.preventDefault();
+ }
+ });
+ });
+ eventListenerApplied = true;
+
+ }
+
+ return [appendContextMenuOption, removeContextMenuOption, addContextMenuEventListener];
+};
+
+var initResponse = contextMenuInit();
+var appendContextMenuOption = initResponse[0];
+var removeContextMenuOption = initResponse[1];
+var addContextMenuEventListener = initResponse[2];
+
+(function() {
+ //Start example Context Menu Items
+ let generateOnRepeat = function(genbuttonid, interruptbuttonid) {
+ let genbutton = gradioApp().querySelector(genbuttonid);
+ let interruptbutton = gradioApp().querySelector(interruptbuttonid);
+ if (!interruptbutton.offsetParent) {
+ genbutton.click();
+ }
+ clearInterval(window.generateOnRepeatInterval);
+ window.generateOnRepeatInterval = setInterval(function() {
+ if (!interruptbutton.offsetParent) {
+ genbutton.click();
+ }
+ },
+ 500);
+ };
+
+ let generateOnRepeatForButtons = function() {
+ generateOnRepeat('#generate_button', '#stop_button');
+ };
+
+ appendContextMenuOption('#generate_button', 'Generate forever', generateOnRepeatForButtons);
+ appendContextMenuOption('#stop_button', 'Generate forever', generateOnRepeatForButtons);
+
+ let cancelGenerateForever = function() {
+ clearInterval(window.generateOnRepeatInterval);
+ };
+
+ appendContextMenuOption('#stop_button', 'Cancel generate forever', cancelGenerateForever);
+ appendContextMenuOption('#generate_button', 'Cancel generate forever', cancelGenerateForever);
+
+})();
+//End example Context Menu Items
+
+document.onreadystatechange = function () {
+ if (document.readyState == "complete") {
+ addContextMenuEventListener();
+ }
+};
diff --git a/javascript/script.js b/javascript/script.js
new file mode 100644
index 0000000..1098aff
--- /dev/null
+++ b/javascript/script.js
@@ -0,0 +1,33 @@
+// based on https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/v1.6.0/script.js
+
+function gradioApp() {
+ const elems = document.getElementsByTagName('gradio-app');
+ const elem = elems.length == 0 ? document : elems[0];
+
+ if (elem !== document) {
+ elem.getElementById = function(id) {
+ return document.getElementById(id);
+ };
+ }
+ return elem.shadowRoot ? elem.shadowRoot : elem;
+}
+
+function playNotification() {
+ gradioApp().querySelector('#audio_notification audio')?.play();
+}
+
+document.addEventListener('keydown', function(e) {
+ var handled = false;
+ if (e.key !== undefined) {
+ if ((e.key == "Enter" && (e.metaKey || e.ctrlKey || e.altKey))) handled = true;
+ } else if (e.keyCode !== undefined) {
+ if ((e.keyCode == 13 && (e.metaKey || e.ctrlKey || e.altKey))) handled = true;
+ }
+ if (handled) {
+ var button = gradioApp().querySelector('button[id=generate_button]');
+ if (button) {
+ button.click();
+ }
+ e.preventDefault();
+ }
+});
diff --git a/modules/ui_gradio_extensions.py b/modules/ui_gradio_extensions.py
new file mode 100644
index 0000000..9c90857
--- /dev/null
+++ b/modules/ui_gradio_extensions.py
@@ -0,0 +1,46 @@
+# based on https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/v1.6.0/modules/ui_gradio_extensions.py
+
+import os
+import gradio as gr
+
+GradioTemplateResponseOriginal = gr.routes.templates.TemplateResponse
+
+modules_path = os.path.dirname(os.path.realpath(__file__))
+script_path = os.path.dirname(modules_path)
+
+
+def webpath(fn):
+ if fn.startswith(script_path):
+ web_path = os.path.relpath(fn, script_path).replace('\\', '/')
+ else:
+ web_path = os.path.abspath(fn)
+
+ return f'file={web_path}?{os.path.getmtime(fn)}'
+
+
+def javascript_html():
+ script_js_path = webpath('javascript/script.js')
+ context_menus_js_path = webpath('javascript/contextMenus.js')
+ head = f'\n'
+ head += f'\n'
+ return head
+
+
+def css_html():
+ style_css_path = webpath('css/style.css')
+ head = f''
+ return head
+
+
+def reload_javascript():
+ js = javascript_html()
+ css = css_html()
+
+ def template_response(*args, **kwargs):
+ res = GradioTemplateResponseOriginal(*args, **kwargs)
+ res.body = res.body.replace(b'', f'{js}'.encode("utf8"))
+ res.body = res.body.replace(b'