|
class EPSElementBuilder { |
|
|
|
static baseButton(text, { size = 'sm', color = 'primary' }) { |
|
const button = gradioApp().getElementById('txt2img_generate').cloneNode() |
|
button.id = '' |
|
button.classList.remove('gr-button-lg', 'gr-button-primary', 'lg', 'primary') |
|
button.classList.add( |
|
|
|
`gr-button-${size}`, |
|
`gr-button-${color}`, |
|
|
|
size, |
|
color |
|
) |
|
button.textContent = text |
|
|
|
return button |
|
} |
|
|
|
static tagFields() { |
|
const fields = document.createElement('div') |
|
fields.style.display = 'flex' |
|
fields.style.flexDirection = 'row' |
|
fields.style.flexWrap = 'wrap' |
|
fields.style.minWidth = 'min(320px, 100%)' |
|
fields.style.maxWidth = '100%' |
|
fields.style.flex = '1 calc(50% - 20px)' |
|
fields.style.borderWidth = '1px' |
|
fields.style.borderColor = 'var(--block-border-color,#374151)' |
|
fields.style.borderRadius = 'var(--block-radius,8px)' |
|
fields.style.padding = '8px' |
|
fields.style.height = 'fit-content' |
|
|
|
return fields |
|
} |
|
|
|
|
|
static openButton({ onClick }) { |
|
const button = EPSElementBuilder.baseButton('🔯提示词', { size: 'sm', color: 'secondary' }) |
|
button.classList.add('easy_prompt_selector_button') |
|
button.addEventListener('click', onClick) |
|
|
|
return button |
|
} |
|
|
|
static areaContainer(id = undefined) { |
|
const container = gradioApp().getElementById('txt2img_results').cloneNode() |
|
container.id = id |
|
container.style.gap = 0 |
|
container.style.display = 'none' |
|
|
|
return container |
|
} |
|
|
|
static tagButton({ title, onClick, onRightClick, color = 'primary' }) { |
|
const button = EPSElementBuilder.baseButton(title, { color }) |
|
button.style.height = '2rem' |
|
button.style.flexGrow = '0' |
|
button.style.margin = '2px' |
|
|
|
button.addEventListener('click', onClick) |
|
button.addEventListener('contextmenu', onRightClick) |
|
|
|
return button |
|
} |
|
|
|
static dropDown(id, options, { onChange }) { |
|
const select = document.createElement('select') |
|
select.id = id |
|
|
|
|
|
select.classList.add('gr-box', 'gr-input') |
|
|
|
|
|
select.style.color = 'var(--body-text-color)' |
|
select.style.backgroundColor = 'var(--body-background-fill)' |
|
select.style.borderColor = 'var(--block-border-color)' |
|
select.style.borderRadius = 'var(--block-radius)' |
|
select.style.margin = '2px' |
|
select.addEventListener('change', (event) => { onChange(event.target.value) }) |
|
|
|
const none = ['空'] |
|
none.concat(options).forEach((key) => { |
|
const option = document.createElement('option') |
|
option.value = key |
|
option.textContent = key |
|
select.appendChild(option) |
|
}) |
|
|
|
return select |
|
} |
|
|
|
static checkbox(text, { onChange }) { |
|
const label = document.createElement('label') |
|
label.style.display = 'flex' |
|
label.style.alignItems = 'center' |
|
|
|
const checkbox = gradioApp().querySelector('input[type=checkbox]').cloneNode() |
|
checkbox.checked = false |
|
checkbox.addEventListener('change', (event) => { |
|
onChange(event.target.checked) |
|
}) |
|
|
|
const span = document.createElement('span') |
|
span.style.marginLeft = 'var(--size-2, 8px)' |
|
span.textContent = text |
|
|
|
label.appendChild(checkbox) |
|
label.appendChild(span) |
|
|
|
return label |
|
} |
|
} |
|
|
|
class EasyPromptSelector { |
|
PATH_FILE = 'tmp/easyPromptSelector.txt' |
|
AREA_ID = 'easy-prompt-selector' |
|
SELECT_ID = 'easy-prompt-selector-select' |
|
CONTENT_ID = 'easy-prompt-selector-content' |
|
TO_NEGATIVE_PROMPT_ID = 'easy-prompt-selector-to-negative-prompt' |
|
|
|
constructor(yaml, gradioApp) { |
|
this.yaml = yaml |
|
this.gradioApp = gradioApp |
|
this.visible = false |
|
this.toNegative = false |
|
this.tags = undefined |
|
} |
|
|
|
async init() { |
|
this.tags = await this.parseFiles() |
|
|
|
const tagArea = gradioApp().querySelector(`#${this.AREA_ID}`) |
|
if (tagArea != null) { |
|
this.visible = false |
|
this.changeVisibility(tagArea, this.visible) |
|
tagArea.remove() |
|
} |
|
|
|
gradioApp() |
|
.getElementById('txt2img_toprow') |
|
.after(this.render()) |
|
} |
|
|
|
async readFile(filepath) { |
|
const response = await fetch(`file=${filepath}?${new Date().getTime()}`); |
|
|
|
return await response.text(); |
|
} |
|
|
|
async parseFiles() { |
|
const text = await this.readFile(this.PATH_FILE); |
|
if (text === '') { return {} } |
|
|
|
const paths = text.split(/\r\n|\n/) |
|
|
|
const tags = {} |
|
for (const path of paths) { |
|
const filename = path.split('/').pop().split('.').slice(0, -1).join('.') |
|
const data = await this.readFile(path) |
|
yaml.loadAll(data, function (doc) { |
|
tags[filename] = doc |
|
}) |
|
} |
|
|
|
return tags |
|
} |
|
|
|
|
|
render() { |
|
const row = document.createElement('div') |
|
row.style.display = 'flex' |
|
row.style.alignItems = 'center' |
|
row.style.gap = '10px' |
|
|
|
const dropDown = this.renderDropdown() |
|
dropDown.style.flex = '1' |
|
dropDown.style.minWidth = '1' |
|
row.appendChild(dropDown) |
|
|
|
const settings = document.createElement('div') |
|
const checkbox = EPSElementBuilder.checkbox('负面', { |
|
onChange: (checked) => { this.toNegative = checked } |
|
}) |
|
settings.style.flex = '1' |
|
settings.appendChild(checkbox) |
|
|
|
row.appendChild(settings) |
|
|
|
const container = EPSElementBuilder.areaContainer(this.AREA_ID) |
|
|
|
container.appendChild(row) |
|
container.appendChild(this.renderContent()) |
|
|
|
return container |
|
} |
|
|
|
renderDropdown() { |
|
const dropDown = EPSElementBuilder.dropDown( |
|
this.SELECT_ID, |
|
Object.keys(this.tags), { |
|
onChange: (selected) => { |
|
const content = gradioApp().getElementById(this.CONTENT_ID) |
|
Array.from(content.childNodes).forEach((node) => { |
|
const visible = node.id === `easy-prompt-selector-container-${selected}` |
|
this.changeVisibility(node, visible) |
|
}) |
|
} |
|
} |
|
) |
|
|
|
return dropDown |
|
} |
|
|
|
renderContent() { |
|
const content = document.createElement('div') |
|
content.id = this.CONTENT_ID |
|
|
|
Object.keys(this.tags).forEach((key) => { |
|
const values = this.tags[key] |
|
|
|
const fields = EPSElementBuilder.tagFields() |
|
fields.id = `easy-prompt-selector-container-${key}` |
|
fields.style.display = 'none' |
|
fields.style.flexDirection = 'row' |
|
fields.style.marginTop = '10px' |
|
|
|
this.renderTagButtons(values, key).forEach((group) => { |
|
fields.appendChild(group) |
|
}) |
|
|
|
content.appendChild(fields) |
|
}) |
|
|
|
return content |
|
} |
|
|
|
renderTagButtons(tags, prefix = '') { |
|
if (Array.isArray(tags)) { |
|
return tags.map((tag) => this.renderTagButton(tag, tag, 'secondary')) |
|
} else { |
|
return Object.keys(tags).map((key) => { |
|
const values = tags[key] |
|
const randomKey = `${prefix}:${key}` |
|
|
|
if (typeof values === 'string') { return this.renderTagButton(key, values, 'secondary') } |
|
|
|
const fields = EPSElementBuilder.tagFields() |
|
fields.style.flexDirection = 'column' |
|
|
|
fields.append(this.renderTagButton(key, `@${randomKey}@`)) |
|
|
|
const buttons = EPSElementBuilder.tagFields() |
|
buttons.id = 'buttons' |
|
fields.append(buttons) |
|
this.renderTagButtons(values, randomKey).forEach((button) => { |
|
buttons.appendChild(button) |
|
}) |
|
|
|
return fields |
|
}) |
|
} |
|
} |
|
|
|
renderTagButton(title, value, color = 'primary') { |
|
return EPSElementBuilder.tagButton({ |
|
title, |
|
onClick: (e) => { |
|
e.preventDefault(); |
|
|
|
this.addTag(value, this.toNegative || e.metaKey || e.ctrlKey) |
|
}, |
|
onRightClick: (e) => { |
|
e.preventDefault(); |
|
|
|
this.removeTag(value, this.toNegative || e.metaKey || e.ctrlKey) |
|
}, |
|
color |
|
}) |
|
} |
|
|
|
|
|
changeVisibility(node, visible) { |
|
node.style.display = visible ? 'flex' : 'none' |
|
} |
|
|
|
addTag(tag, toNegative = false) { |
|
const id = toNegative ? 'txt2img_neg_prompt' : 'txt2img_prompt' |
|
const textarea = gradioApp().getElementById(id).querySelector('textarea') |
|
|
|
if (textarea.value.trim() === '') { |
|
textarea.value = tag |
|
} else if (textarea.value.trim().endsWith(',')) { |
|
textarea.value += ' ' + tag |
|
} else { |
|
textarea.value += ', ' + tag |
|
} |
|
|
|
updateInput(textarea) |
|
} |
|
|
|
removeTag(tag, toNegative = false) { |
|
const id = toNegative ? 'txt2img_neg_prompt' : 'txt2img_prompt' |
|
const textarea = gradioApp().getElementById(id).querySelector('textarea') |
|
|
|
if (textarea.value.trimStart().startsWith(tag)) { |
|
const matched = textarea.value.match(new RegExp(`${tag.replace(/[-\/\\^$*+?.()|\[\]{}]/g, '\\$&') },*`)) |
|
textarea.value = textarea.value.replace(matched[0], '').trimStart() |
|
} else { |
|
textarea.value = textarea.value.replace(`, ${tag}`, '') |
|
} |
|
|
|
updateInput(textarea) |
|
} |
|
} |
|
|
|
onUiLoaded(async () => { |
|
yaml = window.jsyaml |
|
const easyPromptSelector = new EasyPromptSelector(yaml, gradioApp()) |
|
|
|
const button = EPSElementBuilder.openButton({ |
|
onClick: () => { |
|
const tagArea = gradioApp().querySelector(`#${easyPromptSelector.AREA_ID}`) |
|
easyPromptSelector.changeVisibility(tagArea, easyPromptSelector.visible = !easyPromptSelector.visible) |
|
} |
|
}) |
|
|
|
const reloadButton = gradioApp().getElementById('easy_prompt_selector_reload_button') |
|
reloadButton.addEventListener('click', async () => { |
|
await easyPromptSelector.init() |
|
}) |
|
|
|
const txt2imgActionColumn = gradioApp().getElementById('txt2img_actions_column') |
|
const container = document.createElement('div') |
|
container.classList.add('easy_prompt_selector_container') |
|
container.appendChild(button) |
|
container.appendChild(reloadButton) |
|
|
|
txt2imgActionColumn.appendChild(container) |
|
|
|
await easyPromptSelector.init() |
|
}) |
|
|