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