From 61d8512c3afaaedb82d76a62937ae8c0907e4e89 Mon Sep 17 00:00:00 2001 From: Haoming Date: Thu, 1 Jun 2023 17:53:01 +0800 Subject: [PATCH] New Feature & Optimization Added Brackets Escape --- .gitattributes | 1 + Demo.jpg | Bin 8927 -> 130 bytes README.md | 27 ++++--- javascript/prompt_format.js | 140 +++++++++++++++++++++++++----------- 4 files changed, 115 insertions(+), 53 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4fae6dc --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.jpg filter=lfs diff=lfs merge=lfs -text diff --git a/Demo.jpg b/Demo.jpg index d6a267f580299f3a5a33c2cd11cb468d31d30019..385a3126d121bc9b0a68aafd7974ff8001755816 100644 GIT binary patch literal 130 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 diff --git a/README.md b/README.md index b2ea175..e65924a 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,23 @@ # SD Webui Prompt Format This is an Extension for the [Automatic1111 Webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui), which helps formatting prompts. -

+

-## What is This ? -- Sometimes, when you type too fast or copy prompts from all over the places, you end up with duplicated **spaces** or **commas**. -- This simple script helps removing them whenever you click **Generate**. -- Works in both `txt2img` and `img2img`. -- You can also toggle `Remove Duplicates` to remove duplicated tags found in the prompts. *(This is optional, since you may want duplicate tags to strengthen the concept sometimes.)* - - **Note:** Only works for tag-based prompt but not for sentence-based prompt +> The above demo was achieved in just 1 process + +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**. + +## Feature List +- [x] Works in both `txt2img` and `img2img` +- [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 - **eg.** `1girl, solo, smile, 1girl` will become `1girl, solo, smile` - **eg.** `a girl smiling, a girl standing` will not be changed -- You can also toggle `Remove Underscores` to replace all `_` with spaces. *(Some newer anime checkpoints claim to fix the need of using underscores in tags)* -- Now respect line breaks too - - **Note:** Dedupe only works within the same line \ No newline at end of file +- [x] Toggle `Remove Underscores` to replace `_` with **space** + - *Some newer anime checkpoints claim to eliminate the need of using underscores* +- [x] Respect line breaks + - **Note:** `Remove Duplicates` only checks within the same line +- [x] **[New]** Pressing `Ctrl + \` to quickly escape the **brackets** of the hovered tag + - Normally, **brackets** *(parentheses)* are used to increase the weight of a prompt. Therefore, for tags like `mejiro mcqueen (umamusume)`, you will need to escape it like `mejiro mcqueen \(umamusume\)`. \ No newline at end of file diff --git a/javascript/prompt_format.js b/javascript/prompt_format.js index 9d0519b..74a3cbe 100644 --- a/javascript/prompt_format.js +++ b/javascript/prompt_format.js @@ -27,6 +27,70 @@ class LeFormatter { return label } + static injectBracketEscape(id) { + const textarea = gradioApp().getElementById(id).querySelector('textarea') + + textarea.addEventListener('keydown', (event) => { + if (event.ctrlKey && event.key === '\\') { + event.preventDefault() + + let cursorPosition = textarea.selectionStart; + + if (textarea.selectionStart !== textarea.selectionEnd) + cursorPosition++ + + let result = pf_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) + } + } + }) + } + +} + +function pf_GrabBrackets(str, index) { + let openBracket = -1; + let 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 } function fixBracketComma(input) { @@ -38,7 +102,13 @@ function fixBracketSpace(input) { } function fixBracketEmpty(input) { - return input.replace(/\(\)/g, '').replace(/\[\]/g, '') + let temp = input.replace(/\(\s+\)/g, '').replace(/\[\s+\]/g, '') + + while (temp.includes('()')) + temp = temp.replace(/\(\s*\)/g, '') + while (temp.includes('[]')) + temp = temp.replace(/\[\s*\]/g, '') + return temp } function formatString(input, dedupe, deunderline) { @@ -48,10 +118,9 @@ function formatString(input, dedupe, deunderline) { } onUiLoaded(async () => { - var dedupe = false - var deunderline = false + let dedupe = false + let deunderline = false - // Checkbox const dedupeCB = LeFormatter.checkbox('Remove Duplicates', { onChange: (checked) => { dedupe = checked } }) @@ -60,7 +129,7 @@ onUiLoaded(async () => { onChange: (checked) => { deunderline = checked } }) - let formatter = document.createElement('div') + const formatter = document.createElement('div') formatter.id = 'le-formatter' formatter.style.display = 'flex'; formatter.style.flex.direction = 'row'; @@ -71,49 +140,34 @@ onUiLoaded(async () => { const tools = document.getElementById('quicksettings') tools.after(formatter) - // Formatter - LeFormatter.injectButton('txt2img_generate', { - onClick: () => { - const ids = ['txt2img_prompt', 'txt2img_neg_prompt'] - const textAreas = [gradioApp().getElementById(ids[0]).querySelector('textarea'), gradioApp().getElementById(ids[1]).querySelector('textarea')] + const Modes = ['txt', 'img'] - var lines = [textAreas[0].value.split('\n'), textAreas[1].value.split('\n')] + Modes.forEach((mode) => { - for (let i = 0; i < lines[0].length; i++) - lines[0][i] = formatString(lines[0][i], dedupe, deunderline) + LeFormatter.injectButton(mode + '2img_generate', { + onClick: () => { + const ids = [mode + '2img_prompt', mode + '2img_neg_prompt'] + const textAreas = [gradioApp().getElementById(ids[0]).querySelector('textarea'), gradioApp().getElementById(ids[1]).querySelector('textarea')] - for (let i = 0; i < lines[1].length; i++) - lines[1][i] = formatString(lines[1][i], dedupe, deunderline) + let lines = [textAreas[0].value.split('\n'), textAreas[1].value.split('\n')] + + for (let i = 0; i < lines[0].length; i++) + lines[0][i] = formatString(lines[0][i], dedupe, deunderline) + + for (let i = 0; i < lines[1].length; i++) + lines[1][i] = formatString(lines[1][i], dedupe, deunderline) - textAreas[0].value = lines[0].join('\n') - updateInput(textAreas[0]) + textAreas[0].value = lines[0].join('\n') + updateInput(textAreas[0]) + + textAreas[1].value = lines[1].join('\n') + updateInput(textAreas[1]) + } + }) + + LeFormatter.injectBracketEscape(mode + '2img_prompt') + LeFormatter.injectBracketEscape(mode + '2img_neg_prompt') - textAreas[1].value = lines[1].join('\n') - updateInput(textAreas[1]) - } }) - - LeFormatter.injectButton('img2img_generate', { - onClick: () => { - const ids = ['img2img_prompt', 'img2img_neg_prompt'] - const textAreas = [gradioApp().getElementById(ids[0]).querySelector('textarea'), gradioApp().getElementById(ids[1]).querySelector('textarea')] - - var lines = [textAreas[0].value.split('\n'), textAreas[1].value.split('\n')] - - for (let i = 0; i < lines[0].length; i++) - lines[0][i] = formatString(lines[0][i], dedupe) - - for (let i = 0; i < lines[1].length; i++) - lines[1][i] = formatString(lines[1][i], dedupe) - - - textAreas[0].value = lines[0].join('\n') - updateInput(textAreas[0]) - - textAreas[1].value = lines[1].join('\n') - updateInput(textAreas[1]) - } - }) - }) \ No newline at end of file