| 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() |
| }) |
|
|