diff --git a/README.md b/README.md index 87970fe..3f2c816 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,10 @@ Sometimes, when you type too fast or copy prompts from all over the places, you - [x] Pressing `Alt` + `Shift` + `F` can also trigger formatting - [x] Assign "[alias](#tag-alias)" that counts as duplicates for the specified tags - [x] Exclude specific tags from `Remove Underscores` -- [x] Click `Reload Cached Cards & Alias` to force a reload - - By default, the `ExtraNetwork` cards are cached once at the start, to be excluded from `Remove Underscores`. If you add more cards while the Webui is still running, you may click this to re-cache again. +- [x] Click `Reload` to cache new cards + - By default, the `ExtraNetwork` cards are cached once at the start, to be excluded from `Remove Underscores`. If you added more cards while the Webui is already running, click this button to re-cache again. ### Tag Alias - -

New 🔥

- - In the `Prompt Format` settings, there is a new field for **Tag Alias** - You can assign other tags that count as the same as the main tag, and thus get removed during `Remove Duplicates` - The syntax is in the format of `main tag: alias1, alias2, alias3` diff --git a/README_ZH.md b/README_ZH.md index e882d55..d8b2159 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -29,13 +29,10 @@ - [x] 按下 `Alt` + `Shift` + `F` 亦可觸發格式化 - [x] 為指定單字新增 "[同義詞](#同義詞)" - [x] 將指定字詞除外 `Remove Underscores` 的影響 -- [x] 點擊 `Reload Cached Cards & Alias` 以重新載入 - - 在一開始, `ExtraNetwork` 中的卡片會被緩存一次以防被 `Remove Underscores` 影響。如果你在 Webui 仍在運行時加入更多的卡片,點擊此按鈕來重新緩存。 +- [x] 點擊 `Reload` 以緩存卡片 + - 在 Webui 剛開啟時, `ExtraNetwork` 中的卡片會被緩存一次以防被 `Remove Underscores` 影響。如果你在 Webui 已運行時加入更多的卡片,點擊此按鈕來重新緩存。 ### 同義詞 - -

新功能 🔥

- - 在 `Prompt Format` 的設定裡,有個新的 **Tag Alias** 欄位 - 你可以在此把其它字詞設為主單字的同義詞,使其在 `Remove Duplicates` 中被當作重複字而刪去 - 格式為 `main tag: alias1, alias2, alias3` diff --git a/javascript/prompt_format_Configs.js b/javascript/pf_configs.js similarity index 71% rename from javascript/prompt_format_Configs.js rename to javascript/pf_configs.js index 479a0db..5fc310d 100644 --- a/javascript/prompt_format_Configs.js +++ b/javascript/pf_configs.js @@ -1,4 +1,4 @@ -class LeFormatterConfig { +class pfConfigs { constructor() { this.refresh = this.#shouldRefresh(); @@ -7,7 +7,6 @@ class LeFormatterConfig { this.removeUnderscore = this.#defaultRemoveUnderscore(); this.comma = this.#appendComma(); this.promptFields = this.#getPromptFields(); - this.button = this.#createReloadButton(); } /** @returns {boolean} */ @@ -40,56 +39,47 @@ class LeFormatterConfig { return config.checked; } - // ===== Cache All Prompt Fields ===== - /** @returns {HTMLTextAreaElement[]} */ + /** + * Cache All Prompt Fields + * @returns {HTMLTextAreaElement[]} + */ #getPromptFields() { const textareas = []; - // Expandable ID List in 1 place - [ + /** Expandable List of IDs in 1 place */ + const IDs = [ 'txt2img_prompt', 'txt2img_neg_prompt', 'img2img_prompt', 'img2img_neg_prompt', 'hires_prompt', 'hires_neg_prompt' - ].forEach((id) => { + ]; + + for (const id of IDs) { const textArea = document.getElementById(id)?.querySelector('textarea'); if (textArea != null) textareas.push(textArea); - }); + } - // ADetailer - [ + const ADetailer = [ "script_txt2img_adetailer_ad_main_accordion", "script_img2img_adetailer_ad_main_accordion" - ].forEach((id) => { + ]; + + for (const id of ADetailer) { const fields = document.getElementById(id)?.querySelectorAll('textarea'); - if (fields != null) - fields.forEach((textArea) => { - if (textArea.placeholder.length > 0) - textareas.push(textArea); - }); - }); + if (fields == null) + continue; + for (const textArea of fields) { + if (textArea.placeholder.length > 0) + textareas.push(textArea); + } + } return textareas; } - /** @returns {HTMLButtonElement} */ - #createReloadButton() { - const button = document.getElementById('settings_show_all_pages').cloneNode(false); - const page = document.getElementById('column_settings_pf'); - - button.id = "setting_pf_reload"; - button.textContent = "Reload Cached Cards & Alias"; - - button.style.borderRadius = "1em"; - button.style.margin = "1em 0em 0em 0em"; - - page.appendChild(button); - return button; - } - /** @returns {string[]} */ static cacheCards() { const extras = document.getElementById('txt2img_extra_tabs'); @@ -97,16 +87,15 @@ class LeFormatterConfig { return []; const cards = []; - extras.querySelectorAll('span.name').forEach((card) => { + for (const card of extras.querySelectorAll('span.name')) { if (card.textContent.includes('_')) cards.push(card.textContent); - }); + } const config = document.getElementById('setting_pf_exclusion').querySelector('input').value; if (config.trim()) { - config.split(",").forEach((tag) => { + for (const tag of config.split(",")) cards.push(tag.trim()); - }); } return cards; @@ -117,22 +106,21 @@ class LeFormatterConfig { const alias = new Map(); const config = document.getElementById('setting_pf_alias').querySelector('textarea').value; - if (!config.trim()) return alias; - config.split("\n").forEach((line) => { + for (const line of config.split("\n")) { const [tag, words] = line.split(":"); const mainTag = tag.trim(); - words.split(",").map(part => part.trim()).forEach((word) => { + for (const word of words.split(",").map(part => part.trim())) { if (word === mainTag) - return; + continue; const pattern = this.#parseRegExp(word); alias.set(pattern, mainTag); - }); - }); + } + } return alias; } @@ -141,7 +129,6 @@ class LeFormatterConfig { static #parseRegExp(input) { const startAnchor = input.startsWith('^'); const endAnchor = input.endsWith('$'); - return new RegExp(`${startAnchor ? '' : '^'}${input}${endAnchor ? '' : '$'}`); } diff --git a/javascript/prompt_format_UI.js b/javascript/pf_ui.js similarity index 52% rename from javascript/prompt_format_UI.js rename to javascript/pf_ui.js index 0d18016..b1916c8 100644 --- a/javascript/prompt_format_UI.js +++ b/javascript/pf_ui.js @@ -1,26 +1,22 @@ -class LeFormatterUI { +class pfUI { - /** @param {Function} onClick @returns {HTMLButtonElement} */ - static #button(onClick) { + /** @param {string} text @param {string} tip @returns {HTMLButtonElement} */ + static #button(text, tip) { const button = document.createElement('button'); - button.textContent = 'Format'; - - button.id = 'manual-format'; button.classList.add(['lg', 'secondary', 'gradio-button']); - - button.addEventListener('click', onClick); + button.textContent = text; + if (tip) button.title = tip; return button; } - /** @param {boolean} default_value @param {string} text @returns {HTMLDivElement} */ - static #checkbox(default_value, text) { + /** @param {boolean} value @param {string} text @returns {HTMLLabelElement} */ + static #checkbox(value, text) { const label = document.getElementById('tab_settings').querySelector('input[type=checkbox]').parentNode.cloneNode(true); - label.removeAttribute('id'); label.classList.add("pf-checkbox"); + label.removeAttribute('id'); const checkbox = label.children[0]; - checkbox.checked = default_value; - + checkbox.checked = value; const span = label.children[1]; span.textContent = text; @@ -28,16 +24,19 @@ class LeFormatterUI { } /** - * @param {Function} onManual - * @param {boolean} autoRun @param {boolean} dedupe @param {boolean} removeUnderscore + * @param {boolean} autoRun + * @param {boolean} dedupe + * @param {boolean} removeUnderscore * @returns {HTMLDivElement} - * */ - static setupUIs(onManual, autoRun, dedupe, removeUnderscore) { + */ + static setupUIs(autoRun, dedupe, removeUnderscore) { const formatter = document.createElement('div'); formatter.id = 'le-formatter'; - const manualBtn = this.#button(onManual); + const manualBtn = this.#button('Format', null); manualBtn.style.display = autoRun ? 'none' : 'flex'; + const refreshBtn = this.#button('Reload', 'Reload Cached Cards & Alias'); + refreshBtn.style.display = removeUnderscore ? 'flex' : 'none'; const autoCB = this.#checkbox(autoRun, 'Auto Format'); const dedupeCB = this.#checkbox(dedupe, 'Remove Duplicates'); @@ -47,13 +46,13 @@ class LeFormatterUI { formatter.appendChild(manualBtn); formatter.appendChild(dedupeCB); formatter.appendChild(underscoreCB); + formatter.appendChild(refreshBtn); - formatter.btn = manualBtn; - formatter.checkboxs = [ - autoCB.children[0], - dedupeCB.children[0], - underscoreCB.children[0] - ]; + formatter.manual = manualBtn; + formatter.refresh = refreshBtn; + formatter.auto = autoCB.children[0]; + formatter.dedupe = dedupeCB.children[0]; + formatter.underscore = underscoreCB.children[0]; return formatter; } diff --git a/javascript/prompt_format.js b/javascript/prompt_format.js index e825dbc..d60324b 100644 --- a/javascript/prompt_format.js +++ b/javascript/prompt_format.js @@ -1,11 +1,27 @@ class LeFormatter { - static #cachedCards = null; - static #alias = null; + static #cachedCardsInternal = null; + static #aliasInternal = null; static forceReload() { - this.#alias = LeFormatterConfig.getTagAlias(); - this.#cachedCards = LeFormatterConfig.cacheCards(); + this.#cachedCardsInternal = null; + this.#aliasInternal = null; + } + + /** @returns {string[]} */ + static get #cachedCards() { + if (this.#cachedCardsInternal == null) + this.#cachedCardsInternal = pfConfigs.cacheCards(); + + return this.#cachedCardsInternal; + } + + /** @returns {Map} */ + static get #alias() { + if (this.#aliasInternal == null) + this.#aliasInternal = pfConfigs.getTagAlias(); + + return this.#aliasInternal; } /** @@ -25,7 +41,7 @@ class LeFormatter { textArea.value = lines.join('\n'); else { const val = lines.join(',\n'); - textArea.value = val.replace(/\n,\n/g, "\n\n"); + textArea.value = val.replace(/\n,\n/g, '\n\n'); } if (autoRefresh) @@ -35,12 +51,7 @@ class LeFormatter { /** @param {string} input @param {boolean} dedupe @param {boolean} removeUnderscore @returns {string} */ static #formatString(input, dedupe, removeUnderscore) { // Remove Duplicate - if (dedupe) { - if (this.#alias == null) - this.#alias = LeFormatterConfig.getTagAlias(); - - input = this.#dedupe(input); - } + input = dedupe ? this.#dedupe(input) : input; // Fix Commas inside Brackets input = input @@ -50,22 +61,17 @@ class LeFormatter { .replace(/\[\s*,+/g, ',['); // Sentence -> Tags - var tags = input.split(','); + let tags = input.split(','); // Remove Underscore - if (removeUnderscore) { - if (this.#cachedCards == null) - this.#cachedCards = LeFormatterConfig.cacheCards(); - - tags = this.#removeUnderscore(tags); - } + tags = removeUnderscore ? this.#removeUnderscore(tags) : tags; // Remove Stray Brackets const patterns = /^\(+$|^\)+$|^\[+$|^\]+$/; tags = tags.filter(word => !patterns.test(word)); // Remove extra Spaces - input = tags.join(', ').replace(/\s{2,}/g, ' '); + input = tags.join(', ').replace(/\s+/g, ' '); // Fix Bracket & Space input = input @@ -85,22 +91,22 @@ class LeFormatter { static #dedupe(input) { const chunks = input.split(','); + const KEYWORD = /^(AND|BREAK)$/; const uniqueSet = new Set(); const resultArray = []; - const KEYWORD = /^(AND|BREAK)$/; - chunks.forEach((tag) => { + for (const tag of chunks) { const cleanedTag = tag.replace(/\[|\]|\(|\)/g, '').replace(/\s+/g, ' ').trim(); if (KEYWORD.test(cleanedTag)) { resultArray.push(tag); - return; + continue; } - var substitute = null; + let substitute = null; for (const [pattern, mainTag] of this.#alias) { if (substitute != null) - return; + continue; if (pattern.test(cleanedTag)) substitute = mainTag; } @@ -108,39 +114,39 @@ class LeFormatter { if ((substitute == null) && (!uniqueSet.has(cleanedTag))) { uniqueSet.add(cleanedTag); resultArray.push(tag); - return; + continue; } if ((substitute != null) && (!uniqueSet.has(substitute))) { uniqueSet.add(substitute); resultArray.push(tag.replace(cleanedTag, substitute)); - return; + continue; } resultArray.push(tag.replace(cleanedTag, '')); - }); + } return resultArray.join(', '); } - /** @param {Array { + for (const tag of tags) { if (!tag.trim()) - return; + continue; // [start:end:step] OR const chucks = tag.split(':').map(c => c.trim()); for (let i = 0; i < chucks.length; i++) { if (!this.#cachedCards.includes(chucks[i])) - chucks[i] = chucks[i].replace(/_/g, ' '); + chucks[i] = chucks[i].replaceAll('_', ' '); } result.push(chucks.join(':').trim()); - }); + } return result; } @@ -148,51 +154,59 @@ class LeFormatter { onUiLoaded(() => { - const config = new LeFormatterConfig(); - config.button.onclick = () => { LeFormatter.forceReload(); } + const config = new pfConfigs(); + const formatter = pfUI.setupUIs(config.autoRun, config.dedupe, config.removeUnderscore); document.addEventListener('keydown', (e) => { if (e.altKey && e.shiftKey && e.code === 'KeyF') { e.preventDefault(); - config.promptFields.forEach((field) => LeFormatter.formatPipeline(field, config.dedupe, config.removeUnderscore, true, config.comma)); + for (const field of config.promptFields) + LeFormatter.formatPipeline(field, config.dedupe, config.removeUnderscore, true, config.comma); } }); - const formatter = LeFormatterUI.setupUIs( - () => { - config.promptFields.forEach((field) => LeFormatter.formatPipeline(field, config.dedupe, config.removeUnderscore, true, config.comma)); - }, - config.autoRun, config.dedupe, config.removeUnderscore - ); - - formatter.checkboxs[0].addEventListener("change", (e) => { - config.autoRun = e.target.checked; - formatter.btn.style.display = config.autoRun ? 'none' : 'flex'; + formatter.auto.addEventListener("change", () => { + config.autoRun = formatter.auto.checked; + formatter.manual.style.display = config.autoRun ? 'none' : 'flex'; }); - formatter.checkboxs[1].addEventListener("change", (e) => { - config.dedupe = e.target.checked; + formatter.dedupe.addEventListener("change", () => { + config.dedupe = formatter.dedupe.checked; }); - formatter.checkboxs[2].addEventListener("change", (e) => { - config.removeUnderscore = e.target.checked; + formatter.underscore.addEventListener("change", () => { + config.removeUnderscore = formatter.underscore.checked; + formatter.refresh.style.display = config.removeUnderscore ? 'flex' : 'none'; + }); + + formatter.manual.addEventListener("click", () => { + for (const field of config.promptFields) + LeFormatter.formatPipeline(field, config.dedupe, config.removeUnderscore, config.refresh, config.comma); + }); + + formatter.refresh.addEventListener("click", () => { + LeFormatter.forceReload(); }); const tools = document.getElementById('quicksettings'); tools.after(formatter); - ['txt', 'img'].forEach((mode) => { - // Expandable ID List in 1 place - [ - `${mode}2img_generate`, - `${mode}2img_enqueue`, - ].forEach((id) => { - const button = document.getElementById(id); - button?.addEventListener('click', () => { - if (config.autoRun) - config.promptFields.forEach((field) => LeFormatter.formatPipeline(field, config.dedupe, config.removeUnderscore, config.refresh, config.comma)); - }); + /** Expandable List of IDs in 1 place */ + const IDs = [ + 'txt2img_generate', + 'txt2img_enqueue', + 'img2img_generate', + 'img2img_enqueue' + ]; + + for (const id of IDs) { + const button = document.getElementById(id); + button?.addEventListener('click', () => { + if (config.autoRun) { + for (const field of config.promptFields) + LeFormatter.formatPipeline(field, config.dedupe, config.removeUnderscore, config.refresh, config.comma); + } }); - }); + } }); diff --git a/scripts/pf_settings.py b/scripts/pf_settings.py index 21b1578..a3f09e6 100644 --- a/scripts/pf_settings.py +++ b/scripts/pf_settings.py @@ -1,61 +1,39 @@ from modules.script_callbacks import on_ui_settings -from modules.shared import OptionInfo, opts -import gradio as gr - -section = ("pf", "Prompt Format") def on_settings(): + from modules.shared import OptionInfo, opts + import gradio as gr + + args = {"section": ("pf", "Prompt Format"), "category_id": "system"} opts.add_option( "pf_disableupdateinput", - OptionInfo( - False, - "Disable automatic input updates", - section=section, - category_id="system", - ).html( - """ - (enable this if you have Extension, - such as tagcomplete, - that subscribes to text editing event) - """ + OptionInfo(False, "Disable the automatic updates of the prompts", **args).info( + 'enable this if you have Extensions, such as tagcomplete, that subscribe to text editing events' ), ) opts.add_option( "pf_startinauto", - OptionInfo(True, "Start in Auto Mode", section=section, category_id="system"), + OptionInfo(True, "Launch in Auto Mode", **args), ) opts.add_option( "pf_startwithdedupe", - OptionInfo( - True, - "Launch with Remove Duplicates", - section=section, - category_id="system", - ), + OptionInfo(True, "Launch with Remove Duplicates", **args), ) opts.add_option( "pf_startwithrmudscr", - OptionInfo( - False, - "Launch with Remove Underscores", - section=section, - category_id="system", - ), + OptionInfo(True, "Launch with Remove Underscores", **args), ) opts.add_option( "pf_appendcomma", - OptionInfo( - True, - "Append a comma at the end of a line", - section=section, - category_id="system", - ).info("only active when there are more than one line"), + OptionInfo(True, "Append a comma at the end of each line", **args).info( + "only active when there are multiple lines" + ), ) opts.add_option( @@ -66,11 +44,10 @@ def on_settings(): component=gr.Textbox, component_args={ "placeholder": "score_9, score_8_up, score_7_up", - "lines": 1, "max_lines": 1, + "lines": 1, }, - section=section, - category_id="system", + **args ), ) @@ -82,18 +59,17 @@ def on_settings(): component=gr.Textbox, component_args={ "placeholder": "1girl: girl, woman, lady\nadult: \\d*\\s*(y\\.?o\\.?|[Yy]ear[s]? [Oo]ld)", - "lines": 8, - "max_lines": 64, + "max_lines": 16, + "lines": 4, }, - section=section, - category_id="system", + **args ) + .link("RegExr", "https://regexr.com/") .info( - """treat tags on the right as duplicates, and substitute them with the main tag on the left) - (based on regular expression, meaning you may need to escape some symbols) - (comma is not supported in pattern""" - ) - .link("RegExr", "https://regexr.com/"), + """treat tags on the right as duplicates of the main tag on the left) + (based on regular expression, meaning you need to escape special characters) + (comma is not allowed""" + ), ) diff --git a/style.css b/style.css index a154090..0d54551 100644 --- a/style.css +++ b/style.css @@ -4,7 +4,7 @@ user-select: none; } -#manual-format { +#le-formatter button { height: 24px; padding: 2px 8px; border-radius: 0.2em; @@ -16,7 +16,7 @@ align-items: center; } -.pf-checkbox { +#le-formatter .pf-checkbox { display: flex; align-items: center; margin: 2px 8px;