通过Ctrl+↑/↓快速调整输入光标所在的Tag权重。支持所有Prompt输入框。
// ==UserScript==
// @name NovelAI 快捷键权重调整
// @namespace https://novelai.net
// @match https://novelai.net/image
// @icon https://novelai.net/_next/static/media/goose_blue.1580a990.svg
// @license MIT
// @version 1.5
// @author Takoro
// @description 通过Ctrl+↑/↓快速调整输入光标所在的Tag权重。支持所有Prompt输入框。
// ==/UserScript==
(function() {
'use strict';
function getActiveInputElement() {
const selection = window.getSelection();
if (!selection.rangeCount) return null;
const node = selection.focusNode;
const pElement = node.nodeType === 3 ? node.parentElement.closest('p') : node.closest('p');
if (pElement && (
pElement.closest('.prompt-input-box-prompt') ||
pElement.closest('.prompt-input-box-base-prompt') ||
pElement.closest('.prompt-input-box-negative-prompt') ||
pElement.closest('.prompt-input-box-undesired-content') ||
pElement.closest('[class*="character-prompt-input"]')
)) {
return pElement;
}
return null;
}
function getSelectedTagInfo(inputElement) {
if (!inputElement) return null;
const selection = window.getSelection();
if (!selection.rangeCount) return null;
const range = selection.getRangeAt(0);
const node = range.startContainer;
const offset = range.startOffset;
const fullText = inputElement.textContent || '';
let globalOffset = 0;
if (node.nodeType === 3) {
const treeWalker = document.createTreeWalker(
inputElement,
NodeFilter.SHOW_TEXT
);
let currentNode;
while ((currentNode = treeWalker.nextNode())) {
if (currentNode === node) break;
globalOffset += currentNode.length;
}
globalOffset += offset;
} else {
globalOffset = offset;
}
let start = globalOffset;
while (start > 0 && fullText[start - 1] !== ',' && fullText[start - 1] !== '\n') {
start--;
}
let end = globalOffset;
while (end < fullText.length && fullText[end] !== ',' && fullText[end] !== '\n') {
end++;
}
if (fullText[start] === ',' || fullText[start] === '\n') start++;
if (fullText[end - 1] === ',') end--;
const tagText = fullText.slice(start, end).trim();
return tagText ? { tagText, start, end, fullText } : null;
}
// 权重解析函数
function parseWeight(text) {
const cleanText = text.replace(/:{2,}/g, '::').replace(/:+$/, '');
const weightMatch = cleanText.match(/^(-?[\d.]+)::(.+?)(?:::|$)/);
if (weightMatch) {
const weight = parseFloat(weightMatch[1]);
return {
weight: isNaN(weight) ? 1.0 : weight,
tag: weightMatch[2].trim()
};
}
return { weight: 1.0, tag: text.trim() };
}
function adjustWeight(text, direction) {
const { weight, tag } = parseWeight(text);
let newWeight = weight + (direction * 0.1);
newWeight = Math.round(newWeight * 10) / 10;
if (Math.abs(newWeight - 1.0) < 0.001) {
return tag;
}
return `${newWeight}::${tag}::`;
}
function modifyInputText(inputElement, newText, start, end, fullText) {
const newContent = fullText.slice(0, start) + newText + fullText.slice(end);
if (inputElement.childNodes.length === 1 && inputElement.firstChild.nodeType === 3) {
inputElement.firstChild.textContent = newContent;
} else {
const newTextNode = document.createTextNode(newContent);
inputElement.innerHTML = '';
inputElement.appendChild(newTextNode);
}
const newRange = document.createRange();
newRange.setStart(inputElement.firstChild, start);
newRange.setEnd(inputElement.firstChild, start + newText.length);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(newRange);
const inputEvent = new Event('input', { bubbles: true });
inputElement.dispatchEvent(inputEvent);
}
function handleKeydown(event) {
const inputElement = getActiveInputElement();
if (!inputElement) return;
if (!event.ctrlKey || (event.key !== 'ArrowUp' && event.key !== 'ArrowDown')) return;
event.preventDefault();
event.stopPropagation();
const tagInfo = getSelectedTagInfo(inputElement);
if (!tagInfo) return;
const direction = (event.key === 'ArrowUp') ? 1 : -1;
const newText = adjustWeight(tagInfo.tagText, direction);
modifyInputText(inputElement, newText, tagInfo.start, tagInfo.end, tagInfo.fullText);
}
function init() {
const checkInterval = setInterval(() => {
const inputElement = getActiveInputElement();
if (inputElement) {
clearInterval(checkInterval);
document.addEventListener('keydown', handleKeydown, true);
}
}, 500);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址