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