Upload easy_prompt_selector.js
Browse files- easy_prompt_selector.js +342 -0
easy_prompt_selector.js
ADDED
@@ -0,0 +1,342 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
class EPSElementBuilder {
|
2 |
+
// Templates
|
3 |
+
static baseButton(text, { size = 'sm', color = 'primary' }) {
|
4 |
+
const button = gradioApp().getElementById('txt2img_generate').cloneNode()
|
5 |
+
button.id = ''
|
6 |
+
button.classList.remove('gr-button-lg', 'gr-button-primary', 'lg', 'primary')
|
7 |
+
button.classList.add(
|
8 |
+
// gradio 3.16
|
9 |
+
`gr-button-${size}`,
|
10 |
+
`gr-button-${color}`,
|
11 |
+
// gradio 3.22
|
12 |
+
size,
|
13 |
+
color
|
14 |
+
)
|
15 |
+
button.textContent = text
|
16 |
+
|
17 |
+
return button
|
18 |
+
}
|
19 |
+
|
20 |
+
static tagFields() {
|
21 |
+
const fields = document.createElement('div')
|
22 |
+
fields.style.display = 'flex'
|
23 |
+
fields.style.flexDirection = 'row'
|
24 |
+
fields.style.flexWrap = 'wrap'
|
25 |
+
fields.style.minWidth = 'min(320px, 100%)'
|
26 |
+
fields.style.maxWidth = '100%'
|
27 |
+
fields.style.flex = '1 calc(50% - 20px)'
|
28 |
+
fields.style.borderWidth = '1px'
|
29 |
+
fields.style.borderColor = 'var(--block-border-color,#374151)'
|
30 |
+
fields.style.borderRadius = 'var(--block-radius,8px)'
|
31 |
+
fields.style.padding = '8px'
|
32 |
+
fields.style.height = 'fit-content'
|
33 |
+
|
34 |
+
return fields
|
35 |
+
}
|
36 |
+
|
37 |
+
// Elements
|
38 |
+
static openButton({ onClick }) {
|
39 |
+
const button = EPSElementBuilder.baseButton('🔯提示词', { size: 'sm', color: 'secondary' })
|
40 |
+
button.classList.add('easy_prompt_selector_button')
|
41 |
+
button.addEventListener('click', onClick)
|
42 |
+
|
43 |
+
return button
|
44 |
+
}
|
45 |
+
|
46 |
+
static areaContainer(id = undefined) {
|
47 |
+
const container = gradioApp().getElementById('txt2img_results').cloneNode()
|
48 |
+
container.id = id
|
49 |
+
container.style.gap = 0
|
50 |
+
container.style.display = 'none'
|
51 |
+
|
52 |
+
return container
|
53 |
+
}
|
54 |
+
|
55 |
+
static tagButton({ title, onClick, onRightClick, color = 'primary' }) {
|
56 |
+
const button = EPSElementBuilder.baseButton(title, { color })
|
57 |
+
button.style.height = '2rem'
|
58 |
+
button.style.flexGrow = '0'
|
59 |
+
button.style.margin = '2px'
|
60 |
+
|
61 |
+
button.addEventListener('click', onClick)
|
62 |
+
button.addEventListener('contextmenu', onRightClick)
|
63 |
+
|
64 |
+
return button
|
65 |
+
}
|
66 |
+
|
67 |
+
static dropDown(id, options, { onChange }) {
|
68 |
+
const select = document.createElement('select')
|
69 |
+
select.id = id
|
70 |
+
|
71 |
+
// gradio 3.16
|
72 |
+
select.classList.add('gr-box', 'gr-input')
|
73 |
+
|
74 |
+
// gradio 3.22
|
75 |
+
select.style.color = 'var(--body-text-color)'
|
76 |
+
select.style.backgroundColor = 'var(--body-background-fill)'
|
77 |
+
select.style.borderColor = 'var(--block-border-color)'
|
78 |
+
select.style.borderRadius = 'var(--block-radius)'
|
79 |
+
select.style.margin = '2px'
|
80 |
+
select.addEventListener('change', (event) => { onChange(event.target.value) })
|
81 |
+
|
82 |
+
const none = ['空']
|
83 |
+
none.concat(options).forEach((key) => {
|
84 |
+
const option = document.createElement('option')
|
85 |
+
option.value = key
|
86 |
+
option.textContent = key
|
87 |
+
select.appendChild(option)
|
88 |
+
})
|
89 |
+
|
90 |
+
return select
|
91 |
+
}
|
92 |
+
|
93 |
+
static checkbox(text, { onChange }) {
|
94 |
+
const label = document.createElement('label')
|
95 |
+
label.style.display = 'flex'
|
96 |
+
label.style.alignItems = 'center'
|
97 |
+
|
98 |
+
const checkbox = gradioApp().querySelector('input[type=checkbox]').cloneNode()
|
99 |
+
checkbox.checked = false
|
100 |
+
checkbox.addEventListener('change', (event) => {
|
101 |
+
onChange(event.target.checked)
|
102 |
+
})
|
103 |
+
|
104 |
+
const span = document.createElement('span')
|
105 |
+
span.style.marginLeft = 'var(--size-2, 8px)'
|
106 |
+
span.textContent = text
|
107 |
+
|
108 |
+
label.appendChild(checkbox)
|
109 |
+
label.appendChild(span)
|
110 |
+
|
111 |
+
return label
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
class EasyPromptSelector {
|
116 |
+
PATH_FILE = 'tmp/easyPromptSelector.txt'
|
117 |
+
AREA_ID = 'easy-prompt-selector'
|
118 |
+
SELECT_ID = 'easy-prompt-selector-select'
|
119 |
+
CONTENT_ID = 'easy-prompt-selector-content'
|
120 |
+
TO_NEGATIVE_PROMPT_ID = 'easy-prompt-selector-to-negative-prompt'
|
121 |
+
|
122 |
+
constructor(yaml, gradioApp) {
|
123 |
+
this.yaml = yaml
|
124 |
+
this.gradioApp = gradioApp
|
125 |
+
this.visible = false
|
126 |
+
this.toNegative = false
|
127 |
+
this.tags = undefined
|
128 |
+
}
|
129 |
+
|
130 |
+
async init() {
|
131 |
+
this.tags = await this.parseFiles()
|
132 |
+
|
133 |
+
const tagArea = gradioApp().querySelector(`#${this.AREA_ID}`)
|
134 |
+
if (tagArea != null) {
|
135 |
+
this.visible = false
|
136 |
+
this.changeVisibility(tagArea, this.visible)
|
137 |
+
tagArea.remove()
|
138 |
+
}
|
139 |
+
|
140 |
+
gradioApp()
|
141 |
+
.getElementById('txt2img_toprow')
|
142 |
+
.after(this.render())
|
143 |
+
}
|
144 |
+
|
145 |
+
async readFile(filepath) {
|
146 |
+
const response = await fetch(`file=${filepath}?${new Date().getTime()}`);
|
147 |
+
|
148 |
+
return await response.text();
|
149 |
+
}
|
150 |
+
|
151 |
+
async parseFiles() {
|
152 |
+
const text = await this.readFile(this.PATH_FILE);
|
153 |
+
if (text === '') { return {} }
|
154 |
+
|
155 |
+
const paths = text.split(/\r\n|\n/)
|
156 |
+
|
157 |
+
const tags = {}
|
158 |
+
for (const path of paths) {
|
159 |
+
const filename = path.split('/').pop().split('.').slice(0, -1).join('.')
|
160 |
+
const data = await this.readFile(path)
|
161 |
+
yaml.loadAll(data, function (doc) {
|
162 |
+
tags[filename] = doc
|
163 |
+
})
|
164 |
+
}
|
165 |
+
|
166 |
+
return tags
|
167 |
+
}
|
168 |
+
|
169 |
+
// Render
|
170 |
+
render() {
|
171 |
+
const row = document.createElement('div')
|
172 |
+
row.style.display = 'flex'
|
173 |
+
row.style.alignItems = 'center'
|
174 |
+
row.style.gap = '10px'
|
175 |
+
|
176 |
+
const dropDown = this.renderDropdown()
|
177 |
+
dropDown.style.flex = '1'
|
178 |
+
dropDown.style.minWidth = '1'
|
179 |
+
row.appendChild(dropDown)
|
180 |
+
|
181 |
+
const settings = document.createElement('div')
|
182 |
+
const checkbox = EPSElementBuilder.checkbox('负面', {
|
183 |
+
onChange: (checked) => { this.toNegative = checked }
|
184 |
+
})
|
185 |
+
settings.style.flex = '1'
|
186 |
+
settings.appendChild(checkbox)
|
187 |
+
|
188 |
+
row.appendChild(settings)
|
189 |
+
|
190 |
+
const container = EPSElementBuilder.areaContainer(this.AREA_ID)
|
191 |
+
|
192 |
+
container.appendChild(row)
|
193 |
+
container.appendChild(this.renderContent())
|
194 |
+
|
195 |
+
return container
|
196 |
+
}
|
197 |
+
|
198 |
+
renderDropdown() {
|
199 |
+
const dropDown = EPSElementBuilder.dropDown(
|
200 |
+
this.SELECT_ID,
|
201 |
+
Object.keys(this.tags), {
|
202 |
+
onChange: (selected) => {
|
203 |
+
const content = gradioApp().getElementById(this.CONTENT_ID)
|
204 |
+
Array.from(content.childNodes).forEach((node) => {
|
205 |
+
const visible = node.id === `easy-prompt-selector-container-${selected}`
|
206 |
+
this.changeVisibility(node, visible)
|
207 |
+
})
|
208 |
+
}
|
209 |
+
}
|
210 |
+
)
|
211 |
+
|
212 |
+
return dropDown
|
213 |
+
}
|
214 |
+
|
215 |
+
renderContent() {
|
216 |
+
const content = document.createElement('div')
|
217 |
+
content.id = this.CONTENT_ID
|
218 |
+
|
219 |
+
Object.keys(this.tags).forEach((key) => {
|
220 |
+
const values = this.tags[key]
|
221 |
+
|
222 |
+
const fields = EPSElementBuilder.tagFields()
|
223 |
+
fields.id = `easy-prompt-selector-container-${key}`
|
224 |
+
fields.style.display = 'none'
|
225 |
+
fields.style.flexDirection = 'row'
|
226 |
+
fields.style.marginTop = '10px'
|
227 |
+
|
228 |
+
this.renderTagButtons(values, key).forEach((group) => {
|
229 |
+
fields.appendChild(group)
|
230 |
+
})
|
231 |
+
|
232 |
+
content.appendChild(fields)
|
233 |
+
})
|
234 |
+
|
235 |
+
return content
|
236 |
+
}
|
237 |
+
|
238 |
+
renderTagButtons(tags, prefix = '') {
|
239 |
+
if (Array.isArray(tags)) {
|
240 |
+
return tags.map((tag) => this.renderTagButton(tag, tag, 'secondary'))
|
241 |
+
} else {
|
242 |
+
return Object.keys(tags).map((key) => {
|
243 |
+
const values = tags[key]
|
244 |
+
const randomKey = `${prefix}:${key}`
|
245 |
+
|
246 |
+
if (typeof values === 'string') { return this.renderTagButton(key, values, 'secondary') }
|
247 |
+
|
248 |
+
const fields = EPSElementBuilder.tagFields()
|
249 |
+
fields.style.flexDirection = 'column'
|
250 |
+
|
251 |
+
fields.append(this.renderTagButton(key, `@${randomKey}@`))
|
252 |
+
|
253 |
+
const buttons = EPSElementBuilder.tagFields()
|
254 |
+
buttons.id = 'buttons'
|
255 |
+
fields.append(buttons)
|
256 |
+
this.renderTagButtons(values, randomKey).forEach((button) => {
|
257 |
+
buttons.appendChild(button)
|
258 |
+
})
|
259 |
+
|
260 |
+
return fields
|
261 |
+
})
|
262 |
+
}
|
263 |
+
}
|
264 |
+
|
265 |
+
renderTagButton(title, value, color = 'primary') {
|
266 |
+
return EPSElementBuilder.tagButton({
|
267 |
+
title,
|
268 |
+
onClick: (e) => {
|
269 |
+
e.preventDefault();
|
270 |
+
|
271 |
+
this.addTag(value, this.toNegative || e.metaKey || e.ctrlKey)
|
272 |
+
},
|
273 |
+
onRightClick: (e) => {
|
274 |
+
e.preventDefault();
|
275 |
+
|
276 |
+
this.removeTag(value, this.toNegative || e.metaKey || e.ctrlKey)
|
277 |
+
},
|
278 |
+
color
|
279 |
+
})
|
280 |
+
}
|
281 |
+
|
282 |
+
// Util
|
283 |
+
changeVisibility(node, visible) {
|
284 |
+
node.style.display = visible ? 'flex' : 'none'
|
285 |
+
}
|
286 |
+
|
287 |
+
addTag(tag, toNegative = false) {
|
288 |
+
const id = toNegative ? 'txt2img_neg_prompt' : 'txt2img_prompt'
|
289 |
+
const textarea = gradioApp().getElementById(id).querySelector('textarea')
|
290 |
+
|
291 |
+
if (textarea.value.trim() === '') {
|
292 |
+
textarea.value = tag
|
293 |
+
} else if (textarea.value.trim().endsWith(',')) {
|
294 |
+
textarea.value += ' ' + tag
|
295 |
+
} else {
|
296 |
+
textarea.value += ', ' + tag
|
297 |
+
}
|
298 |
+
|
299 |
+
updateInput(textarea)
|
300 |
+
}
|
301 |
+
|
302 |
+
removeTag(tag, toNegative = false) {
|
303 |
+
const id = toNegative ? 'txt2img_neg_prompt' : 'txt2img_prompt'
|
304 |
+
const textarea = gradioApp().getElementById(id).querySelector('textarea')
|
305 |
+
|
306 |
+
if (textarea.value.trimStart().startsWith(tag)) {
|
307 |
+
const matched = textarea.value.match(new RegExp(`${tag.replace(/[-\/\\^$*+?.()|\[\]{}]/g, '\\$&') },*`))
|
308 |
+
textarea.value = textarea.value.replace(matched[0], '').trimStart()
|
309 |
+
} else {
|
310 |
+
textarea.value = textarea.value.replace(`, ${tag}`, '')
|
311 |
+
}
|
312 |
+
|
313 |
+
updateInput(textarea)
|
314 |
+
}
|
315 |
+
}
|
316 |
+
|
317 |
+
onUiLoaded(async () => {
|
318 |
+
yaml = window.jsyaml
|
319 |
+
const easyPromptSelector = new EasyPromptSelector(yaml, gradioApp())
|
320 |
+
|
321 |
+
const button = EPSElementBuilder.openButton({
|
322 |
+
onClick: () => {
|
323 |
+
const tagArea = gradioApp().querySelector(`#${easyPromptSelector.AREA_ID}`)
|
324 |
+
easyPromptSelector.changeVisibility(tagArea, easyPromptSelector.visible = !easyPromptSelector.visible)
|
325 |
+
}
|
326 |
+
})
|
327 |
+
|
328 |
+
const reloadButton = gradioApp().getElementById('easy_prompt_selector_reload_button')
|
329 |
+
reloadButton.addEventListener('click', async () => {
|
330 |
+
await easyPromptSelector.init()
|
331 |
+
})
|
332 |
+
|
333 |
+
const txt2imgActionColumn = gradioApp().getElementById('txt2img_actions_column')
|
334 |
+
const container = document.createElement('div')
|
335 |
+
container.classList.add('easy_prompt_selector_container')
|
336 |
+
container.appendChild(button)
|
337 |
+
container.appendChild(reloadButton)
|
338 |
+
|
339 |
+
txt2imgActionColumn.appendChild(container)
|
340 |
+
|
341 |
+
await easyPromptSelector.init()
|
342 |
+
})
|