Download sd-webui-lobe-theme/src/script/formatPrompt.ts from imspidey/ThemeSD: direct link, hf CLI and curl.
- Browser
- Download file 8.49 kB
-
https://huggingface.co/imspidey/ThemeSD/resolve/main/sd-webui-lobe-theme/src/script/formatPrompt.ts
- Command line
-
hf download hf://imspidey/ThemeSD/sd-webui-lobe-theme/src/script/formatPrompt.ts
-
curl -L -o formatPrompt.ts https://huggingface.co/imspidey/ThemeSD/resolve/main/sd-webui-lobe-theme/src/script/formatPrompt.ts
8.49 kB
| /** | |
| * 转换器工具类 | |
| */ | |
| export const Converter = { | |
| /** | |
| * 添加转换按钮 | |
| * @param type - 组件类型 | |
| */ | |
| addPromptButton(type: string): void { | |
| console.debug('🤯 [formatPrompt] inject', type); | |
| const actionsColumn: HTMLElement | null = gradioApp().querySelector( | |
| `#${type}_tools > div.form`, | |
| ); | |
| const formatBtn: HTMLElement | null = gradioApp().querySelector(`#${type}_formatconvert`); | |
| if (!actionsColumn || formatBtn) return; | |
| const convertButton: HTMLElement = Converter.createButton(`${type}_formatconvert`, '🪄', () => | |
| Converter.onClickConvert(type)); | |
| actionsColumn.append(convertButton); | |
| }, | |
| /** | |
| * 将输入的字符串转换成特定格式的字符串 | |
| * @param input 输入的字符串 | |
| * @returns 转换后的字符串 | |
| */ | |
| convert(input: string): string { | |
| const re_attention = /\{|\[|\}|\]|[^{}[\]]+/gmu; | |
| let text = Converter.convertStr(input); | |
| const textArray = Converter.convertStr2Array(text); | |
| text = Converter.convertArray2Str(textArray); | |
| let res: [string, number][] = []; | |
| const curly_bracket_multiplier = 1.05; | |
| const square_bracket_multiplier = 1 / 1.05; | |
| const brackets: Record<string, { multiplier: number; stack: number[] }> = { | |
| '[': { multiplier: square_bracket_multiplier, stack: [] }, | |
| '{': { multiplier: curly_bracket_multiplier, stack: [] }, | |
| }; | |
| /** | |
| * 将指定范围内的数字乘以指定倍数 | |
| * @param start_position 起始位置 | |
| * @param multiplier 倍数 | |
| */ | |
| function multiply_range(start_position: number, multiplier: number) { | |
| 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]); | |
| } | |
| } | |
| for (const bracketType of Object.keys(brackets)) { | |
| for (const pos of brackets[bracketType].stack) { | |
| multiply_range(pos, brackets[bracketType].multiplier); | |
| } | |
| } | |
| if (res.length === 0) { | |
| res = [['', 1]]; | |
| } | |
| let index = 0; | |
| while (index + 1 < res.length) { | |
| if (res[index][1] === res[index + 1][1]) { | |
| res[index][0] += res[index + 1][0]; | |
| res.splice(index + 1, 1); | |
| } else { | |
| index += 1; | |
| } | |
| } | |
| let result = ''; | |
| for (const [word, value] of res) { | |
| result += value === 1 ? word : `(${word}:${value.toString()})`; | |
| } | |
| return result; | |
| }, | |
| /** | |
| * 将数组转换成字符串 | |
| * @param array 数组 | |
| * @returns 转换后的字符串 | |
| */ | |
| convertArray2Str(array: string[]): string { | |
| const newArray = array.map((item) => { | |
| if (item.includes('<')) return item; | |
| const newItem = item | |
| .replaceAll(/\s+/g, ' ') | |
| .replaceAll(/,|\.\|。/g, ',') | |
| .replaceAll(/“|‘|”|"|\/'/g, '') | |
| .replaceAll(', ', ',') | |
| .replaceAll(',,', ',') | |
| .replaceAll(',', ', '); | |
| return Converter.convertStr2Array(newItem).join(', '); | |
| }); | |
| return newArray.join(', '); | |
| }, | |
| /** | |
| * 将字符串中的中文冒号和括号转换成英文冒号和括号 | |
| * @param srt 字符串 | |
| * @returns 转换后的字符串 | |
| */ | |
| convertStr(srt: string): string { | |
| return srt.replaceAll(':', ':').replaceAll('(', '(').replaceAll(')', ')'); | |
| }, | |
| /** | |
| * 将字符串按照括号分割成数组 | |
| * @param str 字符串 | |
| * @returns 分割后的数组 | |
| */ | |
| convertStr2Array(string_: string): string[] { | |
| // 匹配各种括号中的内容,包括括号本身 | |
| const bracketRegex = /([()<>[\]])/g; | |
| /** | |
| * 将字符串按照各种括号分割成数组 | |
| * @param str 字符串 | |
| * @returns 分割后的数组 | |
| */ | |
| const splitByBracket = (string__: string): string[] => { | |
| const array: string[] = []; | |
| let start = 0; | |
| let depth = 0; | |
| let match; | |
| while ((match = bracketRegex.exec(string__)) !== null) { | |
| if (depth === 0 && match.index > start) { | |
| array.push(string__.slice(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) { | |
| array.push(string__.slice(start, match.index + 1)); | |
| start = match.index + 1; | |
| } | |
| } | |
| if (start < string__.length) { | |
| array.push(string__.slice(Math.max(0, start))); | |
| } | |
| return array; | |
| }; | |
| /** | |
| * 将字符串按照逗号和各种括号分割成数组 | |
| * @param str 字符串 | |
| * @returns 分割后的数组 | |
| */ | |
| const splitByComma = (string__: string): string[] => { | |
| const array: string[] = []; | |
| let start = 0; | |
| let inBracket = false; | |
| for (let index = 0; index < string__.length; index++) { | |
| if (string__[index] === ',' && !inBracket) { | |
| array.push(string__.slice(start, index).trim()); | |
| start = index + 1; | |
| } else if (bracketRegex.test(string__[index])) { | |
| inBracket = !inBracket; | |
| } | |
| } | |
| array.push(string__.slice(Math.max(0, start)).trim()); | |
| return array; | |
| }; | |
| /** | |
| * 清洗字符串并输出数组 | |
| * @param str 字符串 | |
| * @returns 清洗后的数组 | |
| */ | |
| const cleanString = (string__: string): string[] => { | |
| let array = splitByBracket(string__); | |
| array = array.flatMap((s) => splitByComma(s)); | |
| return array.filter((s) => s !== ''); | |
| }; | |
| return cleanString(string_) | |
| .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; | |
| }); | |
| }, | |
| /** | |
| * 创建转换按钮 | |
| * @param id 按钮 id | |
| * @param innerHTML 按钮文本 | |
| * @param onClick 点击事件处理函数 | |
| * @returns 新建的按钮元素 | |
| */ | |
| createButton(id: string, innerHTML: string, onClick: () => void): HTMLButtonElement { | |
| const button = document.createElement('button'); | |
| button.id = id; | |
| button.type = 'button'; | |
| button.innerHTML = innerHTML; | |
| button.title = 'Format prompt~🪄'; | |
| button.className = 'lg secondary gradio-button tool svelte-cmf5ev'; | |
| button.addEventListener('click', onClick); | |
| return button; | |
| }, | |
| /** | |
| * 触发 input 事件 | |
| * @param target 目标元素 | |
| */ | |
| dispatchInputEvent(target: EventTarget) { | |
| let inputEvent = new Event('input'); | |
| Object.defineProperty(inputEvent, 'target', { value: target }); | |
| target.dispatchEvent(inputEvent); | |
| }, | |
| /** | |
| * 点击转换按钮的事件处理函数 | |
| * @param type 类型 | |
| */ | |
| onClickConvert(type: string) { | |
| const default_prompt = ''; | |
| const default_negative = ''; | |
| const prompt = gradioApp().querySelector( | |
| `#${type}_prompt > label > textarea`, | |
| ) as HTMLTextAreaElement; | |
| 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`, | |
| ) as HTMLTextAreaElement; | |
| const negResult = Converter.convert(negprompt.value); | |
| negprompt.value = | |
| negResult.match(/^lowres,/) === null ? | |
| negResult.length === 0 ? | |
| default_negative : | |
| default_negative + negResult : | |
| negResult; | |
| Converter.dispatchInputEvent(negprompt); | |
| }, | |
| /** | |
| * 将数字四舍五入到小数点后四位 | |
| * @param value 数字 | |
| * @returns 四舍五入后的数字 | |
| */ | |
| round(value: number): number { | |
| return Math.round(value * 10_000) / 10_000; | |
| }, | |
| }; | |
| export default () => { | |
| console.time('🤯 [formatPrompt] inject'); | |
| Converter.addPromptButton('txt2img'); | |
| Converter.addPromptButton('img2img'); | |
| console.timeEnd('🤯 [formatPrompt] inject'); | |
| }; | |