From 960cf6b9818e2df0fdd4a416796e3df690a0050f Mon Sep 17 00:00:00 2001 From: Haoming Date: Mon, 12 Feb 2024 12:25:04 +0800 Subject: [PATCH] improved compatibility for Forge and SD.Next and removed bloatwares :p --- README.md | 11 +- README_ZH.md | 13 +- javascript/prompt_format.js | 230 ++++++------------------------------ 3 files changed, 48 insertions(+), 206 deletions(-) diff --git a/README.md b/README.md index 1fad397..317c731 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ This is an Extension for the [Automatic1111 Webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui), which helps formatting prompts. +> Also supports [SD.Next](https://github.com/vladmandic/automatic) and [Forge](https://github.com/lllyasviel/stable-diffusion-webui-forge) out of the box! +

Sometimes, when you type too fast or copy prompts from all over the places, you end up with duplicated **spaces** or **commas**. This simple Extension helps removing them whenever you click **Generate**. @@ -15,17 +17,14 @@ Sometimes, when you type too fast or copy prompts from all over the places, you - **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** - - *Some newer anime checkpoints claim to eliminate the need of using underscores* +- [x] Toggle `Remove Underscores` to replace `_` with `space` + - Some newer anime checkpoints claim to eliminate the need of using underscores - [x] Respect line breaks - `Remove Duplicates` only checks within the same line -- [x] Pressing `Ctrl + \` to quickly escape the **parentheses** of the hovered tag *(the words where the caret is)* - - Normally, **parentheses** are used to increase the weight of a prompt. Therefore, tags like `mejiro mcqueen (umamusume)` will need to be escaped to `mejiro mcqueen \(umamusume\)`. - [x] Toggle between auto formatting and manual formatting - In `Auto`: The process is ran whenever you press **Generate** - In `Manual`: The process is only ran when you press the **Format** button -- [x] Use `Shift + ScrollWheel` to quickly move the hovered tag *(determined by **commas**)* around the prompt -- [x] **[New]** You can now toggle which features to enable/disable by default in `System` section of the **Settings** tab +- [x] Toggle which above features is enabled/disabled by default in `System` section of the **Settings** tab ## Note 1. Since the formatting in `Auto` mode is triggered at the same time as the generation, diff --git a/README_ZH.md b/README_ZH.md index f04c753..2e5f606 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -3,6 +3,8 @@ 這是一個[Automatic1111 Webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui)的插件,用來幫忙校正咒語。 +> 亦支援 [SD.Next](https://github.com/vladmandic/automatic) 與 [Forge](https://github.com/lllyasviel/stable-diffusion-webui-forge) ! +

有時候如果打字太快或是從各處東拼西湊咒語,常會造成多個重複的空格或逗點。這個擴充可以幫忙移除它們。 @@ -15,20 +17,17 @@ - **注意:** 只對單詞類咒語有效 - **例.** `1girl, solo, smile, 1girl` 會變成 `1girl, solo, smile` - **例.** `a girl smiling, a girl standing` 則不變 -- [x] 開啟`Remove Underscores`會將 `_` 換成 **空格** - - *一些較新的動漫模型聲稱不用再加底線* +- [x] 開啟`Remove Underscores`會將 `_` 換成 `空格` + - 一些較新的動漫模型聲稱不用再加底線 - [x] 保留咒語的換行 - 上述的`Remove Duplicates`只在同一行中有效 -- [x] 按下`Ctrl + \`來跳脫目前游標所在的單字 - - 平時,**括弧**是用來強調單字。所以若使用像是`mejiro mcqueen (umamusume)`的咒語,便必須跳脫成`mejiro mcqueen \(umamusume\)` - [x] 按下`Auto Format`以在手動與自動間切換 - `自動`: 每次按下 **生成 (Generate)** 時處裡 - `手動`: 手動按下 **Format** 時才處裡 -- [x] 使用`Shift + 滾輪`來橫移目前游標所在的單字 *(由**逗點**決定)* -- [x] **[New]** 可以在 **Settings** 頁面的 `System` 區開啟/關閉某些預設功能 +- [x] 在 **Settings** 頁面的 `System` 區 開啟/關閉 上述預設功能 ## 注意 -1. 由於`自動`校正和生成是同時觸發,除非有先用過`手動`校正,不然當下所產的第一章圖片之內部資料並不會被更新。 +1. 由於`自動`校正和生成是同時觸發,除非有先用過`手動`校正,不然當下所產的第一張圖片之內部資料並不會被更新。 2. 有些擴充 *(如. [tagcomplete](https://github.com/DominikDoom/a1111-sd-webui-tagcomplete))* 追蹤文字的編輯事件,意即文字校正會導致它們啟動。 你可以到 **Settings** 頁面的 `System` 區關閉咒語的自動編輯。 diff --git a/javascript/prompt_format.js b/javascript/prompt_format.js index 96f33c2..419790b 100644 --- a/javascript/prompt_format.js +++ b/javascript/prompt_format.js @@ -3,10 +3,13 @@ class LeFormatter { // ===== Cache Embedding & LoRA Prompts ===== static cachedCards = null; - static cache_Cards() { + static cacheCards() { this.cachedCards = []; - const extras = document.getElementById('txt2img_extra_tabs').querySelectorAll('span.name'); + const extras = document.getElementById('txt2img_extra_tabs')?.querySelectorAll('span.name'); + if (extras == null) + return; + extras.forEach((card) => { if (card.textContent.includes('_')) this.cachedCards.push(card.textContent); @@ -14,26 +17,21 @@ class LeFormatter { } // ===== UI Related ===== - static manualButton({ onClick }) { - const button = gradioApp().getElementById('txt2img_extra_refresh').cloneNode(); - + 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.fontWeight = 'var(--button-small-text-weight)'; - button.style.fontSize = 'var(--button-small-text-size)'; + 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 injectButton(id, { onClick }) { - const button = gradioApp().getElementById(id); - button.addEventListener('click', onClick); - } - static checkbox(text, default_value, { onChange }) { const label = gradioApp().getElementById('tab_settings').querySelector('input[type=checkbox]').parentNode.cloneNode(true); label.removeAttribute('id'); @@ -55,12 +53,7 @@ class LeFormatter { } // ===== Main Format Logics ===== - static formatPipeline(id, dedupe, removeUnderscore, autoRefresh) { - const textArea = gradioApp().getElementById(id)?.querySelector('textarea'); - - if (textArea == null) - return; - + static formatPipeline(textArea, dedupe, removeUnderscore, autoRefresh) { const lines = textArea.value.split('\n'); for (let i = 0; i < lines.length; i++) @@ -77,7 +70,7 @@ class LeFormatter { return tag; if (this.cachedCards == null) - this.cache_Cards(); + this.cacheCards(); // [start:end:step] OR const chucks = tag.split(':').map(c => c.trim()); @@ -144,142 +137,6 @@ class LeFormatter { return input.split(',').map(word => word.trim()).filter(word => word !== '').join(', '); } - static grabBrackets(str, index) { - var openBracket = -1; - var closeBracket = -1; - - for (let i = index; i >= 0; i--) { - if (str[i] === '(') { - openBracket = i; - break; - } - if (str[i] === ')' && i !== index) { - break; - } - } - - for (let i = index; i < str.length; i++) { - if (str[i] === ')') { - closeBracket = i; - break; - } - if (str[i] === '(' && i !== index) { - break; - } - } - - if (openBracket !== -1 && closeBracket !== -1 && openBracket !== closeBracket) - return [openBracket, closeBracket]; - else - return null; - } - - static injectTagShift(id) { - const textArea = gradioApp().getElementById(id)?.querySelector('textarea'); - - if (textArea == null) - return; - - textArea.addEventListener('wheel', (event) => { - if (event.shiftKey) { - event.preventDefault(); - - if (textArea.selectionStart !== textArea.selectionEnd) - return; - - if (event.deltaY === 0) - return; - - const shift = event.deltaY < 0 ? 1 : -1; - const tags = textArea.value.split(',').map(t => t.trim()); - - var cursor = textArea.selectionStart; - - var index = 0; - - for (let i = 0; i < textArea.selectionStart; i++) { - if (textArea.value[i] === ',') - index++; - } - - if (index === 0 && shift === -1) - return; - if (index === tags.length - 1 && shift === 1) - return; - - const shifted = []; - - if (shift < 0) { - for (let i = 0; i < index - 1; i++) - shifted.push(tags[i]); - - shifted.push(tags[index]); - shifted.push(tags[index - 1]); - - cursor -= tags[index - 1].length + 2; - - for (let i = index + 1; i < tags.length; i++) - shifted.push(tags[i]); - } else { - for (let i = 0; i < index; i++) - shifted.push(tags[i]); - - shifted.push(tags[index + 1]); - shifted.push(tags[index]); - - cursor -= tags[index + 1].length * -1 - 2; - - for (let i = index + 2; i < tags.length; i++) - shifted.push(tags[i]); - } - - textArea.value = shifted.join(', '); - - textArea.selectionStart = cursor; - textArea.selectionEnd = cursor; - - updateInput(textArea); - } - }); - } - - static injectBracketEscape(id) { - const textArea = gradioApp().getElementById(id)?.querySelector('textarea'); - - if (textArea == null) - return; - - textArea.addEventListener('keydown', (event) => { - if (event.ctrlKey && event.key === '\\') { - event.preventDefault(); - - let cursorPosition = textArea.selectionStart; - - if (textArea.selectionStart !== textArea.selectionEnd) - cursorPosition++; - - let result = LeFormatter.grabBrackets(textArea.value, cursorPosition); - - if (result) { - const original = textArea.value; - - if (result[0] !== 0 && textArea.value[result[0] - 1] === '\\' && textArea.value[result[1] - 1] === '\\') { - textArea.value = original.slice(0, result[0] - 1) + original.slice(result[0] - 1, result[1]).replace(/\\/g, '') + original.slice(result[1]); - textArea.selectionStart = result[0] - 1; - textArea.selectionEnd = result[1] - 1; - } - else { - textArea.value = original.slice(0, result[0]) + '\\' + original.slice(result[0], result[1]) + '\\' + original.slice(result[1]); - textArea.selectionStart = result[0]; - textArea.selectionEnd = result[1] + 3; - } - - updateInput(textArea); - } - } - }); - } - // ===== Load Settings ===== static shouldRefresh() { const config = gradioApp().getElementById('setting_pf_disableupdateinput').querySelector('input[type=checkbox]'); @@ -300,19 +157,34 @@ class LeFormatter { const config = gradioApp().getElementById('setting_pf_startwithrmudscr').querySelector('input[type=checkbox]'); return config.checked; } + + // ===== Cache All Prompt Fields ===== + 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) => { + const textArea = gradioApp().getElementById(id)?.querySelector('textarea'); + if (textArea != null) + textareas.push(textArea); + }); + + return textareas; + } } onUiLoaded(async () => { - const Modes = ['txt', 'img']; + const promptFields = LeFormatter.getPromptFields(); var autoRun = LeFormatter.defaultAuto(); var dedupe = LeFormatter.defaultDedupe(); var removeUnderscore = LeFormatter.defaultRemoveUnderscore(); + const refresh = LeFormatter.shouldRefresh(); - const manualBtn = LeFormatter.manualButton({ + const manualBtn = LeFormatter.button({ onClick: () => { - const ids = ['txt2img_prompt', 'txt2img_neg_prompt', 'img2img_prompt', 'img2img_neg_prompt', 'hires_prompt', 'hires_neg_prompt']; - ids.forEach((id) => LeFormatter.formatPipeline(id, dedupe, removeUnderscore, true)); + promptFields.forEach((field) => LeFormatter.formatPipeline(field, dedupe, removeUnderscore, true)); } }); @@ -330,9 +202,7 @@ onUiLoaded(async () => { }); const underlineCB = LeFormatter.checkbox('Remove Underscores', removeUnderscore, { - onChange: (checked) => { - removeUnderscore = checked; - } + onChange: (checked) => { removeUnderscore = checked; } }); const formatter = document.createElement('div'); @@ -348,36 +218,10 @@ onUiLoaded(async () => { const tools = document.getElementById('quicksettings'); tools.after(formatter); - Modes.forEach((mode) => { - - LeFormatter.injectButton(mode + '2img_generate', { - onClick: () => { - if (!autoRun) - return; - - const ids = [mode + '2img_prompt', mode + '2img_neg_prompt']; - ids.forEach((ID) => LeFormatter.formatPipeline(ID, dedupe, removeUnderscore, LeFormatter.shouldRefresh())); - } + ['txt', 'img'].forEach((mode) => { + gradioApp().getElementById(`${mode}2img_generate`).addEventListener('click', () => { + if (autoRun) + promptFields.forEach((field) => LeFormatter.formatPipeline(field, dedupe, removeUnderscore, refresh)); }); - - LeFormatter.injectBracketEscape(mode + '2img_prompt'); - LeFormatter.injectBracketEscape(mode + '2img_neg_prompt'); - LeFormatter.injectTagShift(mode + '2img_prompt'); - LeFormatter.injectTagShift(mode + '2img_neg_prompt'); }); - - LeFormatter.injectButton('txt2img_generate', { - onClick: () => { - if (!autoRun) - return; - - const hires_id = ['hires_prompt', 'hires_neg_prompt']; - hires_id.forEach((hID) => LeFormatter.formatPipeline(hID, dedupe, removeUnderscore, LeFormatter.shouldRefresh())); - } - }); - - LeFormatter.injectBracketEscape('hires_prompt'); - LeFormatter.injectBracketEscape('hires_neg_prompt'); - LeFormatter.injectTagShift('hires_prompt'); - LeFormatter.injectTagShift('hires_neg_prompt'); });