nightfury commited on
Commit
420dce8
1 Parent(s): 26403d3

Create new file

Browse files
Files changed (1) hide show
  1. index.js +186 -0
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/painterro@1.2.78/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
+ })();