parent
b7b9c07ca5
commit
960cf6b981
11
README.md
11
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!
|
||||
|
||||
<p align="center"><img src="sample.jpg" width=512></p>
|
||||
|
||||
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,
|
||||
|
|
|
|||
13
README_ZH.md
13
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) !
|
||||
|
||||
<p align="center"><img src="sample.jpg" width=512></p>
|
||||
|
||||
有時候如果打字太快或是從各處東拼西湊咒語,常會造成多個重複的空格或逗點。這個擴充可以幫忙移除它們。
|
||||
|
|
@ -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` 區關閉咒語的自動編輯。
|
||||
|
|
|
|||
|
|
@ -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 <lora:name:str>
|
||||
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');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue