Upload index.js
Browse files- .js/index.js +186 -0
.js/index.js
ADDED
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
window.SD = (() => {
|
2 |
+
/*
|
3 |
+
* Painterro is made a field of the SD global object
|
4 |
+
* To provide convinience when using w() method in css_and_js.py
|
5 |
+
*/
|
6 |
+
class PainterroClass {
|
7 |
+
static isOpen = false;
|
8 |
+
static async init ({ x, toId }) {
|
9 |
+
console.log(x)
|
10 |
+
|
11 |
+
const originalImage = x[2] === 'Mask' ? x[1]?.image : x[0];
|
12 |
+
|
13 |
+
if (window.Painterro === undefined) {
|
14 |
+
try {
|
15 |
+
await this.load();
|
16 |
+
} catch (e) {
|
17 |
+
SDClass.error(e);
|
18 |
+
|
19 |
+
return this.fallback(originalImage);
|
20 |
+
}
|
21 |
+
}
|
22 |
+
|
23 |
+
if (this.isOpen) {
|
24 |
+
return this.fallback(originalImage);
|
25 |
+
}
|
26 |
+
this.isOpen = true;
|
27 |
+
|
28 |
+
let resolveResult;
|
29 |
+
const paintClient = Painterro({
|
30 |
+
hiddenTools: ['arrow'],
|
31 |
+
onHide: () => {
|
32 |
+
resolveResult?.(null);
|
33 |
+
},
|
34 |
+
saveHandler: (image, done) => {
|
35 |
+
const data = image.asDataURL();
|
36 |
+
|
37 |
+
// ensures stable performance even
|
38 |
+
// when the editor is in interactive mode
|
39 |
+
SD.clearImageInput(SD.el.get(`#${toId}`));
|
40 |
+
|
41 |
+
resolveResult(data);
|
42 |
+
|
43 |
+
done(true);
|
44 |
+
paintClient.hide();
|
45 |
+
},
|
46 |
+
});
|
47 |
+
|
48 |
+
const result = await new Promise((resolve) => {
|
49 |
+
resolveResult = resolve;
|
50 |
+
paintClient.show(originalImage);
|
51 |
+
});
|
52 |
+
this.isOpen = false;
|
53 |
+
|
54 |
+
return result ? this.success(result) : this.fallback(originalImage);
|
55 |
+
}
|
56 |
+
static success (result) { return [result, { image: result, mask: result }] };
|
57 |
+
static fallback (image) { return [image, { image: image, mask: image }] };
|
58 |
+
static load () {
|
59 |
+
return new Promise((resolve, reject) => {
|
60 |
+
const scriptId = '__painterro-script';
|
61 |
+
if (document.getElementById(scriptId)) {
|
62 |
+
reject(new Error('Tried to load painterro script, but script tag already exists.'));
|
63 |
+
return;
|
64 |
+
}
|
65 |
+
|
66 |
+
const styleId = '__painterro-css-override';
|
67 |
+
if (!document.getElementById(styleId)) {
|
68 |
+
/* Ensure Painterro window is always on top */
|
69 |
+
const style = document.createElement('style');
|
70 |
+
style.id = styleId;
|
71 |
+
style.setAttribute('type', 'text/css');
|
72 |
+
style.appendChild(document.createTextNode(`
|
73 |
+
.ptro-holder-wrapper {
|
74 |
+
z-index: 100;
|
75 |
+
}
|
76 |
+
`));
|
77 |
+
document.head.appendChild(style);
|
78 |
+
}
|
79 |
+
|
80 |
+
const script = document.createElement('script');
|
81 |
+
script.id = scriptId;
|
82 |
+
script.src = 'https://unpkg.com/[email protected]/build/painterro.min.js';
|
83 |
+
script.onload = () => resolve(true);
|
84 |
+
script.onerror = (e) => {
|
85 |
+
// remove self on error to enable reattempting load
|
86 |
+
document.head.removeChild(script);
|
87 |
+
reject(e);
|
88 |
+
};
|
89 |
+
document.head.appendChild(script);
|
90 |
+
});
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
/*
|
95 |
+
* Turns out caching elements doesn't actually work in gradio
|
96 |
+
* As elements in tabs might get recreated
|
97 |
+
*/
|
98 |
+
class ElementCache {
|
99 |
+
#el;
|
100 |
+
constructor () {
|
101 |
+
this.root = document.querySelector('gradio-app').shadowRoot;
|
102 |
+
}
|
103 |
+
get (selector) {
|
104 |
+
return this.root.querySelector(selector);
|
105 |
+
}
|
106 |
+
}
|
107 |
+
|
108 |
+
/*
|
109 |
+
* The main helper class to incapsulate functions
|
110 |
+
* that change gradio ui functionality
|
111 |
+
*/
|
112 |
+
class SDClass {
|
113 |
+
el = new ElementCache();
|
114 |
+
Painterro = PainterroClass;
|
115 |
+
moveImageFromGallery ({ x, fromId, toId }) {
|
116 |
+
x = x[0];
|
117 |
+
if (!Array.isArray(x) || x.length === 0) return;
|
118 |
+
|
119 |
+
this.clearImageInput(this.el.get(`#${toId}`));
|
120 |
+
|
121 |
+
const i = this.#getGallerySelectedIndex(this.el.get(`#${fromId}`));
|
122 |
+
|
123 |
+
return [x[i].replace('data:;','data:image/png;')];
|
124 |
+
}
|
125 |
+
async copyImageFromGalleryToClipboard ({ x, fromId }) {
|
126 |
+
x = x[0];
|
127 |
+
if (!Array.isArray(x) || x.length === 0) return;
|
128 |
+
|
129 |
+
const i = this.#getGallerySelectedIndex(this.el.get(`#${fromId}`));
|
130 |
+
|
131 |
+
const data = x[i];
|
132 |
+
const blob = await (await fetch(data.replace('data:;','data:image/png;'))).blob();
|
133 |
+
const item = new ClipboardItem({'image/png': blob});
|
134 |
+
|
135 |
+
await this.copyToClipboard([item]);
|
136 |
+
}
|
137 |
+
clickFirstVisibleButton({ rowId }) {
|
138 |
+
const generateButtons = this.el.get(`#${rowId}`).querySelectorAll('.gr-button-primary');
|
139 |
+
|
140 |
+
if (!generateButtons) return;
|
141 |
+
|
142 |
+
for (let i = 0, arr = [...generateButtons]; i < arr.length; i++) {
|
143 |
+
const cs = window.getComputedStyle(arr[i]);
|
144 |
+
|
145 |
+
if (cs.display !== 'none' && cs.visibility !== 'hidden') {
|
146 |
+
console.log(arr[i]);
|
147 |
+
|
148 |
+
arr[i].click();
|
149 |
+
break;
|
150 |
+
}
|
151 |
+
}
|
152 |
+
}
|
153 |
+
async gradioInputToClipboard ({ x }) { return this.copyToClipboard(x[0]); }
|
154 |
+
async copyToClipboard (value) {
|
155 |
+
if (!value || typeof value === 'boolean') return;
|
156 |
+
try {
|
157 |
+
if (Array.isArray(value) &&
|
158 |
+
value.length &&
|
159 |
+
value[0] instanceof ClipboardItem) {
|
160 |
+
await navigator.clipboard.write(value);
|
161 |
+
} else {
|
162 |
+
await navigator.clipboard.writeText(value);
|
163 |
+
}
|
164 |
+
} catch (e) {
|
165 |
+
SDClass.error(e);
|
166 |
+
}
|
167 |
+
}
|
168 |
+
static error (e) {
|
169 |
+
console.error(e);
|
170 |
+
if (typeof e === 'string') {
|
171 |
+
alert(e);
|
172 |
+
} else if(typeof e === 'object' && Object.hasOwn(e, 'message')) {
|
173 |
+
alert(e.message);
|
174 |
+
}
|
175 |
+
}
|
176 |
+
clearImageInput (imageEditor) {
|
177 |
+
imageEditor?.querySelector('.modify-upload button:last-child')?.click();
|
178 |
+
}
|
179 |
+
#getGallerySelectedIndex (gallery) {
|
180 |
+
const selected = gallery.querySelector(`.\\!ring-2`);
|
181 |
+
return selected ? [...selected.parentNode.children].indexOf(selected) : 0;
|
182 |
+
}
|
183 |
+
}
|
184 |
+
|
185 |
+
return new SDClass();
|
186 |
+
})();
|