220 lines
6.2 KiB
JavaScript
220 lines
6.2 KiB
JavaScript
class Converter {
|
||
static round(value) {
|
||
return Math.round(value * 10000) / 10000;
|
||
}
|
||
|
||
static convertStr(srt) {
|
||
return srt.replace(/:/g, ":").replace(/(/g, "(").replace(/)/g, ")");
|
||
}
|
||
|
||
static convertStr2Array(str) {
|
||
// 匹配各种括号中的内容,包括括号本身
|
||
const bracketRegex = /(\(|\)|<|>|\[|\])/g;
|
||
|
||
// 将字符串按照各种括号分割成数组
|
||
const splitByBracket = (str) => {
|
||
const arr = [];
|
||
let start = 0;
|
||
let depth = 0;
|
||
let match;
|
||
while ((match = bracketRegex.exec(str)) !== null) {
|
||
if (depth === 0 && match.index > start) {
|
||
arr.push(str.substring(start, match.index));
|
||
start = match.index;
|
||
}
|
||
if (match[0] === "(" || match[0] === "<" || match[0] === "[") {
|
||
depth++;
|
||
} else if (match[0] === ")" || match[0] === ">" || match[0] === "]") {
|
||
depth--;
|
||
}
|
||
if (depth === 0) {
|
||
arr.push(str.substring(start, match.index + 1));
|
||
start = match.index + 1;
|
||
}
|
||
}
|
||
if (start < str.length) {
|
||
arr.push(str.substring(start));
|
||
}
|
||
return arr;
|
||
};
|
||
|
||
// 将字符串按照逗号和各种括号分割成数组
|
||
const splitByComma = (str) => {
|
||
const arr = [];
|
||
let start = 0;
|
||
let inBracket = false;
|
||
for (let i = 0; i < str.length; i++) {
|
||
if (str[i] === "," && !inBracket) {
|
||
arr.push(str.substring(start, i).trim());
|
||
start = i + 1;
|
||
} else if (str[i].match(bracketRegex)) {
|
||
inBracket = !inBracket;
|
||
}
|
||
}
|
||
arr.push(str.substring(start).trim());
|
||
return arr;
|
||
};
|
||
|
||
// 清洗字符串并输出数组
|
||
const cleanStr = (str) => {
|
||
let arr = splitByBracket(str);
|
||
arr = arr.flatMap((s) => splitByComma(s));
|
||
return arr.filter((s) => s !== "");
|
||
};
|
||
|
||
return cleanStr(str)
|
||
.filter((item) => {
|
||
const pattern = /^[,\s, ]+$/;
|
||
return !pattern.test(item);
|
||
})
|
||
.filter(Boolean)
|
||
.sort((a, b) => {
|
||
return a.includes("<") && !b.includes("<")
|
||
? 1
|
||
: b.includes("<") && !a.includes("<")
|
||
? -1
|
||
: 0;
|
||
});
|
||
}
|
||
|
||
static convertArray2Str(array) {
|
||
const newArray = array.map((item) => {
|
||
if (item.includes("<")) return item;
|
||
const newItem = item
|
||
.replace(/\s+/g, " ")
|
||
.replace([/\,|\.|\。/g, ","])
|
||
.replace([/\“|\‘|\”|\"|\\|\//g, ""])
|
||
.replace(/\, /g, ",")
|
||
.replace(/\,\,/g, ",")
|
||
.replace(/\,/g, ", ");
|
||
return Converter.convertStr2Array(newItem).join(", ");
|
||
});
|
||
return newArray.join(", ");
|
||
}
|
||
|
||
static convert(input) {
|
||
const re_attention = /\{|\[|\}|\]|[^\{\}\[\]]+/gmu;
|
||
|
||
let text = Converter.convertStr(input);
|
||
text = Converter.convertStr2Array(text);
|
||
text = Converter.convertArray2Str(text);
|
||
|
||
let res = [];
|
||
|
||
const curly_bracket_multiplier = 1.05;
|
||
const square_bracket_multiplier = 1 / 1.05;
|
||
|
||
const brackets = {
|
||
"{": { stack: [], multiplier: curly_bracket_multiplier },
|
||
"[": { stack: [], multiplier: square_bracket_multiplier },
|
||
};
|
||
|
||
function multiply_range(start_position, multiplier) {
|
||
for (let pos = start_position; pos < res.length; pos++) {
|
||
res[pos][1] = Converter.round(res[pos][1] * multiplier);
|
||
}
|
||
}
|
||
|
||
for (const match of text.matchAll(re_attention)) {
|
||
let word = match[0];
|
||
|
||
if (word in brackets) {
|
||
brackets[word].stack.push(res.length);
|
||
} else if (word == "}" || word == "]") {
|
||
const bracket = brackets[word === "}" ? "{" : "["];
|
||
if (bracket.stack.length > 0) {
|
||
multiply_range(bracket.stack.pop(), bracket.multiplier);
|
||
}
|
||
} else {
|
||
res.push([word, 1.0]);
|
||
}
|
||
}
|
||
|
||
for (const bracketType in brackets) {
|
||
for (const pos of brackets[bracketType].stack) {
|
||
multiply_range(pos, brackets[bracketType].multiplier);
|
||
}
|
||
}
|
||
|
||
if (res.length == 0) {
|
||
res = [["", 1.0]];
|
||
}
|
||
|
||
let i = 0;
|
||
while (i + 1 < res.length) {
|
||
if (res[i][1] == res[i + 1][1]) {
|
||
res[i][0] += res[i + 1][0];
|
||
res.splice(i + 1, 1);
|
||
} else {
|
||
i += 1;
|
||
}
|
||
}
|
||
|
||
let result = "";
|
||
for (const [word, value] of res) {
|
||
result += value === 1.0 ? word : `(${word}:${value.toString()})`;
|
||
}
|
||
return result;
|
||
}
|
||
|
||
static dispatchInputEvent(target) {
|
||
let inputEvent = new Event("input");
|
||
Object.defineProperty(inputEvent, "target", { value: target });
|
||
target.dispatchEvent(inputEvent);
|
||
}
|
||
|
||
static onClickConvert(type) {
|
||
const default_prompt = "";
|
||
const default_negative = "";
|
||
|
||
const prompt = gradioApp().querySelector(
|
||
`#${type}_prompt > label > textarea`
|
||
);
|
||
const result = Converter.convert(prompt.value);
|
||
prompt.value =
|
||
result.match(/^masterpiece, best quality,/) === null
|
||
? default_prompt + result
|
||
: result;
|
||
Converter.dispatchInputEvent(prompt);
|
||
const negprompt = gradioApp().querySelector(
|
||
`#${type}_neg_prompt > label > textarea`
|
||
);
|
||
const negResult = Converter.convert(negprompt.value);
|
||
negprompt.value =
|
||
negResult.match(/^lowres,/) === null
|
||
? negResult.length === 0
|
||
? default_negative
|
||
: default_negative + negResult
|
||
: negResult;
|
||
Converter.dispatchInputEvent(negprompt);
|
||
}
|
||
|
||
static createButton(id, innerHTML, onClick) {
|
||
const button = document.createElement("button");
|
||
button.id = id;
|
||
button.type = "button";
|
||
button.innerHTML = innerHTML;
|
||
button.className = "lg secondary gradio-button tool svelte-1ipelgc";
|
||
button.addEventListener("click", onClick);
|
||
return button;
|
||
}
|
||
|
||
static addPromptButton(type) {
|
||
const generateBtn = gradioApp().querySelector(`#${type}_generate`);
|
||
const actionsColumn = gradioApp().querySelector(`#${type}_style_create`);
|
||
const nai2local = gradioApp().querySelector(`#${type}_nai2localconvert`);
|
||
if (!generateBtn || !actionsColumn || nai2local) return;
|
||
const convertBtn = Converter.createButton(
|
||
`${type}_nai2localconvert`,
|
||
"🪄",
|
||
() => Converter.onClickConvert(type)
|
||
);
|
||
actionsColumn.parentNode.append(convertBtn);
|
||
}
|
||
}
|
||
|
||
onUiUpdate(() => {
|
||
Converter.addPromptButton("txt2img");
|
||
Converter.addPromptButton("img2img");
|
||
});
|