| import { app } from "../../scripts/app.js"; |
|
|
| |
|
|
| app.registerExtension({ |
| name: "Comfy.EditAttention", |
| init() { |
| const editAttentionDelta = app.ui.settings.addSetting({ |
| id: "Comfy.EditAttention.Delta", |
| name: "Ctrl+up/down precision", |
| type: "slider", |
| attrs: { |
| min: 0.01, |
| max: 0.5, |
| step: 0.01, |
| }, |
| defaultValue: 0.05, |
| }); |
|
|
| function incrementWeight(weight, delta) { |
| const floatWeight = parseFloat(weight); |
| if (isNaN(floatWeight)) return weight; |
| const newWeight = floatWeight + delta; |
| if (newWeight < 0) return "0"; |
| return String(Number(newWeight.toFixed(10))); |
| } |
|
|
| function findNearestEnclosure(text, cursorPos) { |
| let start = cursorPos, end = cursorPos; |
| let openCount = 0, closeCount = 0; |
|
|
| |
| while (start >= 0) { |
| start--; |
| if (text[start] === "(" && openCount === closeCount) break; |
| if (text[start] === "(") openCount++; |
| if (text[start] === ")") closeCount++; |
| } |
| if (start < 0) return false; |
|
|
| openCount = 0; |
| closeCount = 0; |
|
|
| |
| while (end < text.length) { |
| if (text[end] === ")" && openCount === closeCount) break; |
| if (text[end] === "(") openCount++; |
| if (text[end] === ")") closeCount++; |
| end++; |
| } |
| if (end === text.length) return false; |
|
|
| return { start: start + 1, end: end }; |
| } |
|
|
| function addWeightToParentheses(text) { |
| const parenRegex = /^\((.*)\)$/; |
| const parenMatch = text.match(parenRegex); |
|
|
| const floatRegex = /:([+-]?(\d*\.)?\d+([eE][+-]?\d+)?)/; |
| const floatMatch = text.match(floatRegex); |
|
|
| if (parenMatch && !floatMatch) { |
| return `(${parenMatch[1]}:1.0)`; |
| } else { |
| return text; |
| } |
| }; |
|
|
| function editAttention(event) { |
| const inputField = event.composedPath()[0]; |
| const delta = parseFloat(editAttentionDelta.value); |
|
|
| if (inputField.tagName !== "TEXTAREA") return; |
| if (!(event.key === "ArrowUp" || event.key === "ArrowDown")) return; |
| if (!event.ctrlKey && !event.metaKey) return; |
|
|
| event.preventDefault(); |
|
|
| let start = inputField.selectionStart; |
| let end = inputField.selectionEnd; |
| let selectedText = inputField.value.substring(start, end); |
|
|
| |
| if (!selectedText) { |
| const nearestEnclosure = findNearestEnclosure(inputField.value, start); |
| if (nearestEnclosure) { |
| start = nearestEnclosure.start; |
| end = nearestEnclosure.end; |
| selectedText = inputField.value.substring(start, end); |
| } else { |
| |
| const delimiters = " .,\\/!?%^*;:{}=-_`~()\r\n\t"; |
| |
| while (!delimiters.includes(inputField.value[start - 1]) && start > 0) { |
| start--; |
| } |
| |
| while (!delimiters.includes(inputField.value[end]) && end < inputField.value.length) { |
| end++; |
| } |
|
|
| selectedText = inputField.value.substring(start, end); |
| if (!selectedText) return; |
| } |
| } |
|
|
| |
| if (selectedText[selectedText.length - 1] === " ") { |
| selectedText = selectedText.substring(0, selectedText.length - 1); |
| end -= 1; |
| } |
|
|
| |
| if (inputField.value[start - 1] === "(" && inputField.value[end] === ")") { |
| start -= 1; |
| end += 1; |
| selectedText = inputField.value.substring(start, end); |
| } |
|
|
| |
| if (selectedText[0] !== "(" || selectedText[selectedText.length - 1] !== ")") { |
| selectedText = `(${selectedText})`; |
| } |
|
|
| |
| selectedText = addWeightToParentheses(selectedText); |
|
|
| |
| const weightDelta = event.key === "ArrowUp" ? delta : -delta; |
| const updatedText = selectedText.replace(/\((.*):(\d+(?:\.\d+)?)\)/, (match, text, weight) => { |
| weight = incrementWeight(weight, weightDelta); |
| if (weight == 1) { |
| return text; |
| } else { |
| return `(${text}:${weight})`; |
| } |
| }); |
|
|
| inputField.setRangeText(updatedText, start, end, "select"); |
| } |
| window.addEventListener("keydown", editAttention); |
| }, |
| }); |
|
|