diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/LICENSE b/LICENSE index 5534522..91b74be 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Haoming +Copyright (c) 2024 Haoming Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. diff --git a/README.md b/README.md index 317c731..f2da99c 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Sometimes, when you type too fast or copy prompts from all over the places, you - [x] Remove duplicated **spaces** and **commas** - [x] Fix misplaced **brackets** and **commas** - [x] Toggle `Remove Duplicates` to remove identical tags found in the prompts - - **Note:** Only works for tag-based prompt, not sentence-based prompt + - **Note:** Only works for tag-based prompt, not sentence-based prompt - **eg.** `1girl, solo, smile, 1girl` will become `1girl, solo, smile` - **eg.** `a girl smiling, a girl standing` will not be changed - [x] Toggle `Remove Underscores` to replace `_` with `space` @@ -25,10 +25,11 @@ Sometimes, when you type too fast or copy prompts from all over the places, you - In `Auto`: The process is ran whenever you press **Generate** - In `Manual`: The process is only ran when you press the **Format** button - [x] Toggle which above features is enabled/disabled by default in `System` section of the **Settings** tab +- [x] Pressing `Alt` + `Shift` + `F` can also trigger formatting ## Note -1. Since the formatting in `Auto` mode is triggered at the same time as the generation, -the immediate image will not have its metadata updated (unless you already did manual formatting beforehand). +1. Since the formatting in `Auto` mode is triggered at the same time as the generation, +the immediate image will not have its metadata updated (unless you already did manual formatting beforehand). 2. Some Extensions *(**eg.** [tagcomplete](https://github.com/DominikDoom/a1111-sd-webui-tagcomplete))* listen to the text editing event, meaning the formatting will cause them to trigger. You can disable updating the actual prompts in the `System` section of the **Settings** tab. diff --git a/README_ZH.md b/README_ZH.md index 2e5f606..15bf870 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -25,6 +25,7 @@ - `自動`: 每次按下 **生成 (Generate)** 時處裡 - `手動`: 手動按下 **Format** 時才處裡 - [x] 在 **Settings** 頁面的 `System` 區 開啟/關閉 上述預設功能 +- [x] 按下 `Alt` + `Shift` + `F` 亦可觸發格式化 ## 注意 1. 由於`自動`校正和生成是同時觸發,除非有先用過`手動`校正,不然當下所產的第一張圖片之內部資料並不會被更新。 diff --git a/javascript/prompt_format.js b/javascript/prompt_format.js index 5fce7ba..5fa784c 100644 --- a/javascript/prompt_format.js +++ b/javascript/prompt_format.js @@ -16,42 +16,6 @@ class LeFormatter { }); } - // ===== UI Related ===== - static button({ onClick }) { - const button = document.createElement('button'); - button.id = 'manual-format'; - button.classList.add(['lg', 'secondary', 'gradio-button']); - - button.textContent = 'Format'; - button.style.padding = '2px 8px'; - button.style.borderRadius = '0.2em'; - button.style.border = 'var(--button-border-width) solid var(--button-secondary-border-color)'; - button.style.background = 'var(--button-secondary-background-fill)'; - - button.addEventListener('click', onClick); - return button; - } - - static checkbox(text, default_value, { onChange }) { - const label = gradioApp().getElementById('tab_settings').querySelector('input[type=checkbox]').parentNode.cloneNode(true); - label.removeAttribute('id'); - - const checkbox = label.children[0]; - - checkbox.checked = default_value; - checkbox.addEventListener('change', (event) => { - onChange(event.target.checked); - }); - - const span = label.children[1]; - span.textContent = text; - - label.style.display = 'flex'; - label.style.alignItems = 'center'; - label.style.margin = '2px 8px'; - return label; - } - // ===== Main Format Logics ===== static formatPipeline(textArea, dedupe, removeUnderscore, autoRefresh) { const lines = textArea.value.split('\n'); @@ -138,33 +102,44 @@ class LeFormatter { } // ===== Load Settings ===== + /** @returns {boolean} */ static shouldRefresh() { const config = gradioApp().getElementById('setting_pf_disableupdateinput').querySelector('input[type=checkbox]'); return !config.checked; } + /** @returns {boolean} */ static defaultAuto() { const config = gradioApp().getElementById('setting_pf_startinauto').querySelector('input[type=checkbox]'); return config.checked; } + /** @returns {boolean} */ static defaultDedupe() { const config = gradioApp().getElementById('setting_pf_startwithdedupe').querySelector('input[type=checkbox]'); return config.checked; } + /** @returns {boolean} */ static defaultRemoveUnderscore() { const config = gradioApp().getElementById('setting_pf_startwithrmudscr').querySelector('input[type=checkbox]'); return config.checked; } // ===== Cache All Prompt Fields ===== + /** @returns {Array} */ static getPromptFields() { - // Expandable ID List in 1 place - const ids = ['txt2img_prompt', 'txt2img_neg_prompt', 'img2img_prompt', 'img2img_neg_prompt', 'hires_prompt', 'hires_neg_prompt']; const textareas = []; - ids.forEach((id) => { + // Expandable ID List in 1 place + [ + 'txt2img_prompt', + 'txt2img_neg_prompt', + 'img2img_prompt', + 'img2img_neg_prompt', + 'hires_prompt', + 'hires_neg_prompt' + ].forEach((id) => { const textArea = gradioApp().getElementById(id)?.querySelector('textarea'); if (textArea != null) textareas.push(textArea); @@ -176,11 +151,11 @@ class LeFormatter { onUiLoaded(async () => { const promptFields = LeFormatter.getPromptFields(); + const refresh = LeFormatter.shouldRefresh(); var autoRun = LeFormatter.defaultAuto(); var dedupe = LeFormatter.defaultDedupe(); var removeUnderscore = LeFormatter.defaultRemoveUnderscore(); - const refresh = LeFormatter.shouldRefresh(); document.addEventListener('keydown', (e) => { if (e.altKey && e.shiftKey && e.code === 'KeyF') { @@ -189,55 +164,41 @@ onUiLoaded(async () => { } }); - const manualBtn = LeFormatter.button({ - onClick: () => { + const formatter = LeFormatterUI.setupUIs( + () => { promptFields.forEach((field) => LeFormatter.formatPipeline(field, dedupe, removeUnderscore, true)); - } + }, + autoRun, dedupe, removeUnderscore + ); + + formatter.checkboxs[0].addEventListener("change", (e) => { + autoRun = e.target.checked; + formatter.btn.style.display = autoRun ? 'none' : 'flex'; }); - manualBtn.style.display = autoRun ? 'none' : 'block'; - - const autoCB = LeFormatter.checkbox('Auto Format', autoRun, { - onChange: (checked) => { - autoRun = checked; - manualBtn.style.display = autoRun ? 'none' : 'block'; - } + formatter.checkboxs[1].addEventListener("change", (e) => { + dedupe = e.target.checked; }); - const dedupeCB = LeFormatter.checkbox('Remove Duplicates', dedupe, { - onChange: (checked) => { dedupe = checked; } + formatter.checkboxs[2].addEventListener("change", (e) => { + removeUnderscore = e.target.checked; }); - const underlineCB = LeFormatter.checkbox('Remove Underscores', removeUnderscore, { - onChange: (checked) => { removeUnderscore = checked; } - }); - - const formatter = document.createElement('div'); - formatter.id = 'le-formatter'; - formatter.style.display = 'flex'; - formatter.style.flex.direction = 'row'; - - formatter.appendChild(autoCB); - formatter.appendChild(manualBtn); - formatter.appendChild(dedupeCB); - formatter.appendChild(underlineCB); - const tools = document.getElementById('quicksettings'); tools.after(formatter); ['txt', 'img'].forEach((mode) => { const generateButton = gradioApp().getElementById(`${mode}2img_generate`); const enqueueButton = gradioApp().getElementById(`${mode}2img_enqueue`); - - const handleClick = () => { - if (autoRun) { + + generateButton?.addEventListener('click', () => { + if (autoRun) promptFields.forEach((field) => LeFormatter.formatPipeline(field, dedupe, removeUnderscore, refresh)); - } - }; - - generateButton.addEventListener('click', handleClick); - if (enqueueButton) { - enqueueButton.addEventListener('click', handleClick); - } + }); + + enqueueButton?.addEventListener('click', () => { + if (autoRun) + promptFields.forEach((field) => LeFormatter.formatPipeline(field, dedupe, removeUnderscore, refresh)); + }); }); }); diff --git a/javascript/prompt_format_UI.js b/javascript/prompt_format_UI.js new file mode 100644 index 0000000..8700d37 --- /dev/null +++ b/javascript/prompt_format_UI.js @@ -0,0 +1,61 @@ +class LeFormatterUI { + + /** @param {Function} onClick @returns {Element} */ + static #button(onClick) { + const button = document.createElement('button'); + button.textContent = 'Format'; + + button.id = 'manual-format'; + button.classList.add(['lg', 'secondary', 'gradio-button']); + + button.addEventListener('click', onClick); + return button; + } + + /** @param {boolean} default_value @param {string} text @returns {Element} */ + static #checkbox(default_value, text) { + const label = gradioApp().getElementById('tab_settings').querySelector('input[type=checkbox]').parentNode.cloneNode(true); + label.removeAttribute('id'); + label.classList.add("pf-checkbox"); + + const checkbox = label.children[0]; + checkbox.checked = default_value; + + const span = label.children[1]; + span.textContent = text; + + return label; + } + + /** + * @param {Function} onManual + * @param {boolean} autoRun @param {boolean} dedupe @param {boolean} removeUnderscore + * @returns {Element} + * */ + static setupUIs(onManual, autoRun, dedupe, removeUnderscore) { + const formatter = document.createElement('div'); + formatter.id = 'le-formatter'; + + const manualBtn = this.#button(onManual); + manualBtn.style.display = autoRun ? 'none' : 'flex'; + + const autoCB = this.#checkbox(autoRun, 'Auto Format'); + const dedupeCB = this.#checkbox(dedupe, 'Remove Duplicates'); + const underscoreCB = this.#checkbox(removeUnderscore, 'Remove Underscores'); + + formatter.appendChild(autoCB); + formatter.appendChild(manualBtn); + formatter.appendChild(dedupeCB); + formatter.appendChild(underscoreCB); + + formatter.btn = manualBtn; + formatter.checkboxs = [ + autoCB.children[0], + dedupeCB.children[0], + underscoreCB.children[0] + ]; + + return formatter; + } + +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..a154090 --- /dev/null +++ b/style.css @@ -0,0 +1,23 @@ +#le-formatter { + display: flex; + flex-direction: row; + user-select: none; +} + +#manual-format { + height: 24px; + padding: 2px 8px; + border-radius: 0.2em; + border: var(--button-border-width) solid var(--button-secondary-border-color); + background: var(--button-secondary-background-fill); + + display: flex; + justify-content: center; + align-items: center; +} + +.pf-checkbox { + display: flex; + align-items: center; + margin: 2px 8px; +}