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 d6a267f..385a312 100644 Binary files a/Demo.jpg and b/Demo.jpg differ 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