@@ -277,3 +277,7 @@ Note: Uses [ImagesGrid](https://github.com/LEv145/images-grid-comfy-plugin) for
|
||||
My general text to image workflow, note that it uses several nodes from different custom nodes.
|
||||
|
||||

|
||||
|
||||
# Known Issues
|
||||
|
||||
* Embedding highlighting does not work on linux.
|
||||
|
||||
@@ -16,6 +16,8 @@ import torch
|
||||
from server import PromptServer
|
||||
from aiohttp import web
|
||||
|
||||
import folder_paths
|
||||
|
||||
|
||||
class ConfigReader:
|
||||
"""
|
||||
@@ -329,5 +331,20 @@ class MessageHolder:
|
||||
|
||||
return int(message.strip())
|
||||
except ValueError:
|
||||
cls.logger.log(f"failed to parse '${message}' as ${'comma separated list of ints' if asList else 'int'}", "ERROR")
|
||||
cls.logger.log(f"failed to parse '{message}' as {'comma separated list of ints' if asList else 'int'}", "ERROR")
|
||||
return [1] if asList else 1
|
||||
|
||||
|
||||
API_PREFIX = "/sn0w"
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get(f"{API_PREFIX}/loras")
|
||||
async def get_loras(request):
|
||||
loras = folder_paths.get_filename_list("loras")
|
||||
return web.json_response(list(map(lambda a: os.path.splitext(a)[0], loras)))
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get(f"{API_PREFIX}/embeddings")
|
||||
async def get_embeddings(request):
|
||||
embeddings = folder_paths.get_filename_list("embeddings")
|
||||
return web.json_response(list(map(lambda a: os.path.splitext(a)[0], embeddings)))
|
||||
|
||||
+28
-10
@@ -9,6 +9,10 @@ from ..sn0w import ConfigReader, Logger, Utility, MessageHolder
|
||||
routes = MessageHolder.routes
|
||||
API_PREFIX = MessageHolder.API_PREFIX
|
||||
|
||||
# File paths
|
||||
CHARACTER_FILE_PATH = "web/settings/characters.json"
|
||||
CUSTOM_CHARACTER_FILE_PATH = "web/settings/custom_characters.json"
|
||||
|
||||
|
||||
@routes.post(f"{API_PREFIX}/update_characters")
|
||||
async def handle_update_characters(request):
|
||||
@@ -38,15 +42,32 @@ class CharacterSelectNode:
|
||||
|
||||
cls.load_characters(dir_path)
|
||||
|
||||
@classmethod
|
||||
def get_base_dir(cls):
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
if os.path.basename(dir_path) == "src":
|
||||
dir_path = os.path.dirname(dir_path)
|
||||
return dir_path
|
||||
|
||||
@classmethod
|
||||
def check_initialize(cls):
|
||||
if not cls.final_characters or cls.cached_default_character_setting != ConfigReader.get_setting(
|
||||
"sn0w.DisableDefaultCharacters", False
|
||||
):
|
||||
cls.cached_default_character_setting = ConfigReader.get_setting("sn0w.DisableDefaultCharacters", False)
|
||||
cls.initialize()
|
||||
elif cls.cached_sorting_setting != ConfigReader.get_setting("sn0w.SortBySeries", False):
|
||||
cls.sort_characters()
|
||||
|
||||
@classmethod
|
||||
def load_characters(cls, dir_path):
|
||||
character_data = []
|
||||
if not cls.cached_default_character_setting:
|
||||
json_path = os.path.join(dir_path, "web/settings/characters.json")
|
||||
json_path = os.path.join(dir_path, CHARACTER_FILE_PATH)
|
||||
with open(json_path, "r", encoding="utf-8") as file:
|
||||
character_data = json.load(file)
|
||||
|
||||
custom_json_path = os.path.join(dir_path, "web/settings/custom_characters.json")
|
||||
custom_json_path = os.path.join(dir_path, CUSTOM_CHARACTER_FILE_PATH)
|
||||
if os.path.exists(custom_json_path):
|
||||
with open(custom_json_path, "r", encoding="utf-8") as file:
|
||||
custom_character_data = json.load(file)
|
||||
@@ -64,7 +85,10 @@ class CharacterSelectNode:
|
||||
|
||||
@staticmethod
|
||||
def extract_series_name(character_name):
|
||||
return character_name.split("(")[-1].split(")")[0].strip()
|
||||
series = character_name.split("(")[-1].split(")")[0].strip()
|
||||
if character_name == series:
|
||||
Logger().log(f"{character_name} has no series name.", "WARNING")
|
||||
return series
|
||||
|
||||
@classmethod
|
||||
def sort_characters(cls, force_sort=False):
|
||||
@@ -84,13 +108,7 @@ class CharacterSelectNode:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
if not cls.final_characters: # Check if initialization is needed
|
||||
cls.initialize()
|
||||
elif cls.cached_default_character_setting != ConfigReader.get_setting("sn0w.DisableDefaultCharacters", False):
|
||||
cls.cached_default_character_setting = not cls.cached_default_character_setting
|
||||
cls.initialize()
|
||||
else:
|
||||
cls.sort_characters()
|
||||
cls.check_initialize()
|
||||
character_names = ["None"] + list(cls.final_characters)
|
||||
return {
|
||||
"required": {
|
||||
|
||||
+4
-2
@@ -333,7 +333,8 @@ export class SettingUtils {
|
||||
}
|
||||
|
||||
if (!nodeDef) {
|
||||
throw new Error(`Node definition with name ${nodeName} not found`);
|
||||
console.error(`Node definition with name ${nodeName} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the specific node in the graph by name
|
||||
@@ -346,7 +347,8 @@ export class SettingUtils {
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
throw new Error(`Node with name ${nodeName} not found in the graph`);
|
||||
console.warn(`Node with name ${nodeName} not found in the graph`);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let nodeNum in graphCanvas.graph._nodes) {
|
||||
|
||||
+260
-76
@@ -1,5 +1,6 @@
|
||||
import { SettingUtils } from './sn0w.js';
|
||||
import { app } from '../../../scripts/app.js';
|
||||
import { api } from '../../../scripts/api.js';
|
||||
|
||||
app.registerExtension({
|
||||
name: 'sn0w.Textbox',
|
||||
@@ -11,6 +12,13 @@ app.registerExtension({
|
||||
return textbox;
|
||||
};
|
||||
|
||||
nodeType.prototype.updateHighlightType = function () {
|
||||
const inputEl = this.inputEl.inputEl;
|
||||
const overlayEl = this.overlayEl;
|
||||
setTextColors(inputEl, overlayEl);
|
||||
setTimeout(() => syncText(this.inputEl.inputEl, this.overlayEl), 50);
|
||||
}
|
||||
|
||||
nodeType.prototype.populate = function () {
|
||||
this.inputEl = this.widgets[0];
|
||||
|
||||
@@ -41,54 +49,58 @@ app.registerExtension({
|
||||
|
||||
this.inputEl.inputEl.style.background = 'transparent';
|
||||
|
||||
// Sync text initially and on input
|
||||
this.inputEl.inputEl.addEventListener('input', () => {
|
||||
syncText(this.inputEl.inputEl, this.overlayEl);
|
||||
setOverlayStyle(this.inputEl, this.overlayEl);
|
||||
});
|
||||
addEventListeners(this);
|
||||
addObversers(this);
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
setOverlayPosition(this.inputEl, this.overlayEl);
|
||||
});
|
||||
|
||||
observer.observe(this.inputEl.inputEl, {
|
||||
attributes: true,
|
||||
attributeFilter: ['style'],
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true,
|
||||
});
|
||||
|
||||
const parentObserver = new MutationObserver(() => {
|
||||
if (!document.contains(this.inputEl.inputEl)) {
|
||||
this.overlayEl.remove();
|
||||
}
|
||||
});
|
||||
|
||||
parentObserver.observe(this.inputEl.inputEl.parentNode, {
|
||||
childList: true,
|
||||
});
|
||||
|
||||
this.inputEl.inputEl.addEventListener('keydown', (event) => {
|
||||
if (
|
||||
event.ctrlKey &&
|
||||
(event.key === 'ArrowUp' || event.key === 'ArrowDown')
|
||||
) {
|
||||
setTimeout(() => {
|
||||
syncText(this.inputEl.inputEl, this.overlayEl);
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
setOverlayPosition(this.inputEl, this.overlayEl);
|
||||
setOverlayStyle(this.inputEl, this.overlayEl);
|
||||
}, 10);
|
||||
setOverlayPosition(this.inputEl, this.overlayEl);
|
||||
setOverlayStyle(this.inputEl, this.overlayEl);
|
||||
} else {
|
||||
console.error('Parent node of input element is not available.');
|
||||
}
|
||||
};
|
||||
|
||||
function addEventListeners(node) {
|
||||
node.inputEl.inputEl.addEventListener('input', () => {
|
||||
syncText(node.inputEl.inputEl, node.overlayEl);
|
||||
setOverlayStyle(node.inputEl, node.overlayEl);
|
||||
});
|
||||
|
||||
node.inputEl.inputEl.addEventListener('keydown', (event) => {
|
||||
if (
|
||||
event.ctrlKey &&
|
||||
(event.key === 'ArrowUp' || event.key === 'ArrowDown')
|
||||
) {
|
||||
setTimeout(() => {
|
||||
syncText(node.inputEl.inputEl, node.overlayEl);
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addObversers(node) {
|
||||
const observer = new MutationObserver(() => {
|
||||
setOverlayPosition(node.inputEl, node.overlayEl);
|
||||
});
|
||||
|
||||
observer.observe(node.inputEl.inputEl, {
|
||||
attributes: true,
|
||||
attributeFilter: ['style'],
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true,
|
||||
});
|
||||
|
||||
const parentObserver = new MutationObserver(() => {
|
||||
if (!document.contains(node.inputEl.inputEl)) {
|
||||
node.overlayEl.remove();
|
||||
}
|
||||
});
|
||||
|
||||
parentObserver.observe(node.inputEl.inputEl.parentNode, {
|
||||
childList: true,
|
||||
});
|
||||
}
|
||||
|
||||
async function setTextHighlightType(inputEl) {
|
||||
const highlightGradient = await SettingUtils.getSetting('sn0w.TextboxGradientColors');
|
||||
if (highlightGradient === null) {
|
||||
@@ -159,7 +171,85 @@ app.registerExtension({
|
||||
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
||||
}
|
||||
|
||||
async function syncText(inputEl, overlayEl) {
|
||||
function charToType(char) {
|
||||
switch(char) {
|
||||
case '<':
|
||||
return '-lora';
|
||||
case 'e':
|
||||
return '-embedding';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function extractLoraName(text, currentIndex) {
|
||||
const loraPrefix = "lora:";
|
||||
const startIndex = text.lastIndexOf(loraPrefix, currentIndex) + loraPrefix.length;
|
||||
|
||||
// Find the end of the LoRA name (next space, comma, or ".safetensors")
|
||||
let endIndex = text.indexOf(' ', startIndex);
|
||||
const commaIndex = text.indexOf(',', startIndex);
|
||||
const safetensorsIndex = text.indexOf('.safetensors', startIndex);
|
||||
|
||||
if (endIndex === -1 || (commaIndex !== -1 && commaIndex < endIndex)) {
|
||||
endIndex = commaIndex;
|
||||
}
|
||||
if (endIndex === -1 || (safetensorsIndex !== -1 && safetensorsIndex < endIndex)) {
|
||||
endIndex = safetensorsIndex;
|
||||
}
|
||||
|
||||
// If there's no space, comma, or ".safetensors" after the LoRA name, consider the end of the text
|
||||
const loraName = endIndex === -1 ? text.slice(startIndex) : text.slice(startIndex, endIndex);
|
||||
return loraName;
|
||||
}
|
||||
|
||||
function validateName(validFiles, name) {
|
||||
if (!validFiles) {
|
||||
console.error('Valid names not defined or not an array.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (validFiles.includes(name)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function processTags(tags) {
|
||||
let returnArray = [];
|
||||
tags.forEach(tag => {
|
||||
returnArray.push(processTag(tag));
|
||||
})
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
function processTag(tag) {
|
||||
let trimmedTag = tag.trim();
|
||||
|
||||
// Remove HTML tags
|
||||
trimmedTag = trimmedTag.replace(/<[^>]*>/g, '');
|
||||
|
||||
// Remove the first character if it is a parenthesis
|
||||
if (trimmedTag.startsWith('(')) {
|
||||
trimmedTag = trimmedTag.substring(1).trim();
|
||||
}
|
||||
|
||||
// Remove everything after a colon
|
||||
const colonIndex = trimmedTag.indexOf(':');
|
||||
if (colonIndex !== -1) {
|
||||
trimmedTag = trimmedTag.substring(0, colonIndex).trim();
|
||||
}
|
||||
|
||||
return trimmedTag;
|
||||
}
|
||||
|
||||
const charPairs = {
|
||||
'(': ')',
|
||||
'<': '>',
|
||||
};
|
||||
|
||||
async function syncText(inputEl, overlayEl, tries = 1) {
|
||||
const text = inputEl.value;
|
||||
overlayEl.textContent = text;
|
||||
|
||||
@@ -169,12 +259,14 @@ app.registerExtension({
|
||||
const loraColor = colors ? colors[0] : undefined;
|
||||
|
||||
if (!colors || !errorColor || !loraColor || shouldHighlightGradient == undefined) {
|
||||
setTimeout(() => syncText(inputEl, overlayEl), 5);
|
||||
if (tries < 5) {
|
||||
setTimeout(() => syncText(inputEl, overlayEl, tries++), tries * 5);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let uniqueIdCounter = 0;
|
||||
const generateUniqueId = () => `unique-span-${uniqueIdCounter++}`;
|
||||
const generateUniqueId = (type = "") => `span-${uniqueIdCounter++}${type}`;
|
||||
|
||||
let nestingLevel = 0;
|
||||
let highlightedText = '';
|
||||
@@ -182,8 +274,27 @@ app.registerExtension({
|
||||
let spanStack = [];
|
||||
const uniqueIdMap = new Map();
|
||||
|
||||
const extractTags = (text) => {
|
||||
let tags = text.split(',').map(tag => tag.trim());
|
||||
tags = processTags(tags);
|
||||
const tagCounts = new Map();
|
||||
|
||||
tags.forEach(tag => {
|
||||
tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1);
|
||||
});
|
||||
|
||||
return { tags, tagCounts };
|
||||
};
|
||||
|
||||
const { tags, tagCounts } = extractTags(text);
|
||||
|
||||
/*
|
||||
* This loop iterates over each character in the `text` string to apply syntax highlighting and handle special cases.
|
||||
* - lastIndex: index of the last processed character, used to slice text segments.
|
||||
* - spanStack: stack to manage opened spans, storing their ids, start positions, original colors, and characters.
|
||||
*/
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const char = text[i];
|
||||
const char = text[i].toLowerCase();
|
||||
|
||||
// Handle escape characters
|
||||
if (char === '\\' && i + 1 < text.length && (text[i + 1] === '(' || text[i + 1] === ')')) {
|
||||
@@ -194,53 +305,81 @@ app.registerExtension({
|
||||
// Handle wrong escape characters
|
||||
if (char === '/' && i + 1 < text.length && (text[i + 1] === '(' || text[i + 1] === ')')) {
|
||||
highlightedText += text.slice(lastIndex, i) + `<span style="background-color: ${errorColor};">/</span>`;
|
||||
console.error(`Try replacing "${char}" at char ${i} with "\\"`);
|
||||
console.error(`Replace "${char}" at char ${i} with "\\"`);
|
||||
lastIndex = i + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let color = colors[nestingLevel % colors.length];
|
||||
let uniqueId = generateUniqueId();
|
||||
let uniqueId = generateUniqueId(charToType(char));
|
||||
if (inputEl.highlightGradient === true) {
|
||||
color = `id-${uniqueId}`;
|
||||
}
|
||||
switch(char) {
|
||||
case '(':
|
||||
case '<':
|
||||
highlightedText += text.slice(lastIndex, i) + `<span id="${uniqueId}" style="background-color: ${color};">${escapeHtml(char)}`;
|
||||
spanStack.push({ id: uniqueId, start: highlightedText.length, originalSpan: `<span id="${uniqueId}" style="background-color: ${color};">`, nestingLevel });
|
||||
spanStack.push({ id: uniqueId, start: highlightedText.length, originalSpan: `<span id="${uniqueId}" style="background-color: ${color};">`, nestingLevel, originalColor: color, originalChar: char });
|
||||
nestingLevel++;
|
||||
lastIndex = i + 1;
|
||||
break;
|
||||
case '<':
|
||||
uniqueId = `${uniqueId}-lora`;
|
||||
color = `id-${uniqueId}`;
|
||||
highlightedText += text.slice(lastIndex, i) + `<span id="${uniqueId}" style="background-color: ${color};">${escapeHtml(char)}`;
|
||||
spanStack.push({ id: uniqueId, start: highlightedText.length - 3, originalSpan: `<span id="${uniqueId}" style="background-color: ${color};">`, nestingLevel });
|
||||
nestingLevel++;
|
||||
lastIndex = i + 1;
|
||||
case 'e':
|
||||
let embeddingColor = colors[0];
|
||||
const embeddingPrefix = "embedding:";
|
||||
|
||||
// Check if the text starts with "embedding:" at position i
|
||||
if (text.toLowerCase().startsWith(embeddingPrefix, i)) {
|
||||
// Find the end of the embedding (next space or comma)
|
||||
let endIndex = i + embeddingPrefix.length;
|
||||
while (endIndex < text.length && text[endIndex] !== ' ' && text[endIndex] !== ',') {
|
||||
endIndex++;
|
||||
}
|
||||
|
||||
// Get the full embedding text
|
||||
const embeddingText = text.slice(i, endIndex);
|
||||
if (validateName(inputEl.validEmbeddings, embeddingText.split(":")[1]) === false) {
|
||||
embeddingColor = errorColor;
|
||||
}
|
||||
const wrappedEmbedding = `<span id="${uniqueId}" style="background-color: ${embeddingColor};">${escapeHtml(embeddingText)}</span>`;
|
||||
highlightedText += text.slice(lastIndex, i) + wrappedEmbedding;
|
||||
lastIndex = i + embeddingText.length;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
case '>':
|
||||
if (nestingLevel > 0) {
|
||||
highlightedText += text.slice(lastIndex, i) + `${escapeHtml(char)}</span>`;
|
||||
const { id } = spanStack.pop();
|
||||
nestingLevel--;
|
||||
const { id, originalColor, originalChar } = spanStack[spanStack.length - 1];
|
||||
if (charPairs[originalChar] === char) {
|
||||
spanStack.pop();
|
||||
highlightedText += text.slice(lastIndex, i) + `${escapeHtml(char)}</span>`;
|
||||
nestingLevel--;
|
||||
|
||||
if (inputEl.highlightGradient === true || id.endsWith('lora')) {
|
||||
// Check for the strength
|
||||
const strengthText = text.slice(Math.max(0, i - 10), i);
|
||||
const match = strengthText.match(/(\d+(\.\d+)?)\s*$/);
|
||||
if (match) {
|
||||
const strength = parseFloat(match[1]);
|
||||
const clampedStrength = Math.max(0, Math.min(2, strength));
|
||||
const normalizedStrength = clampedStrength / 2;
|
||||
const newColor = interpolateColor(colors[0], colors[colors.length - 1], easeInOutCubic(normalizedStrength));
|
||||
uniqueIdMap.set(id, newColor);
|
||||
} else {
|
||||
uniqueIdMap.set(id, interpolateColor(colors[0], colors[colors.length - 1], 0.5));
|
||||
// Extract and validate the LoRA name
|
||||
if (id.endsWith('lora')) {
|
||||
const loraName = extractLoraName(text, i);
|
||||
if (validateName(inputEl.validLoras, loraName) === false) {
|
||||
uniqueIdMap.set(id, [errorColor, originalColor]);
|
||||
lastIndex = i + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (inputEl.highlightGradient === true || id.endsWith('lora')) {
|
||||
// Check for the strength
|
||||
const strengthText = text.slice(Math.max(0, i - 10), i);
|
||||
const match = strengthText.match(/(\d+(\.\d+)?)\s*$/);
|
||||
if (match) {
|
||||
const strength = parseFloat(match[1]);
|
||||
const clampedStrength = Math.max(0, Math.min(2, strength));
|
||||
const normalizedStrength = clampedStrength / 2;
|
||||
const newColor = interpolateColor(colors[0], colors[colors.length - 1], easeInOutCubic(normalizedStrength));
|
||||
uniqueIdMap.set(id, [newColor, originalColor]);
|
||||
} else {
|
||||
uniqueIdMap.set(id, [interpolateColor(colors[0], colors[colors.length - 1], 0.5), originalColor]);
|
||||
}
|
||||
}
|
||||
lastIndex = i + 1;
|
||||
}
|
||||
lastIndex = i + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -257,8 +396,7 @@ app.registerExtension({
|
||||
const errorSpanTag = `<span id="${id}" style="background-color: ${errorColor};">`;
|
||||
|
||||
if (originalSpan) {
|
||||
const newText = highlightedText.slice(start - (originalSpan.length + 1), highlightedText.length).replace(originalSpan, errorSpanTag);
|
||||
highlightedText = highlightedText.slice(0, start - originalSpan.length + 1) + newText;
|
||||
highlightedText = highlightedText.replace(originalSpan, errorSpanTag);
|
||||
highlightedText += `</span>`;
|
||||
}
|
||||
}
|
||||
@@ -266,10 +404,25 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
// Apply the updated colors to the highlighted text
|
||||
uniqueIdMap.forEach((newColor, id) => {
|
||||
highlightedText = highlightedText.replace(`background-color: id-${id}`, `background-color: ${newColor}`);
|
||||
uniqueIdMap.forEach((colors, id) => {
|
||||
const [newColor, originalColor] = [colors[0], colors[1]];
|
||||
highlightedText = highlightedText.replace(`id="${id}" style="background-color: ${originalColor};"`, `id="${id}" style="background-color: ${newColor};"`);
|
||||
});
|
||||
|
||||
// Highlight duplicate tags
|
||||
highlightedText.split(/(,)/).map(segment => {
|
||||
if (segment === ',') {
|
||||
return segment;
|
||||
}
|
||||
|
||||
const trimmedSegment = processTag(segment);
|
||||
|
||||
// Check for duplicates
|
||||
if (tagCounts.get(trimmedSegment) > 1) {
|
||||
highlightedText = highlightedText.replaceAll(trimmedSegment, `<span style="background-color: ${errorColor};">${trimmedSegment}</span>`)
|
||||
}
|
||||
}).join('');
|
||||
|
||||
overlayEl.innerHTML = highlightedText;
|
||||
}
|
||||
|
||||
@@ -305,9 +458,27 @@ app.registerExtension({
|
||||
overlayEl.style.wordWrap = 'break-word';
|
||||
}
|
||||
|
||||
async function setValidFiles(inputEl) {
|
||||
inputEl.validLoras = await getValidFiles("loras");
|
||||
inputEl.validEmbeddings = await getValidFiles("embeddings");
|
||||
}
|
||||
|
||||
async function getValidFiles(type) {
|
||||
const apiRequest = await api.fetchApi(`${SettingUtils.API_PREFIX}/${type}`, {
|
||||
method: "GET",
|
||||
})
|
||||
if (!apiRequest.ok) {
|
||||
console.error('API request failed:', apiRequest.statusText);
|
||||
} else {
|
||||
const responseBody = await apiRequest.json();
|
||||
return responseBody;
|
||||
}
|
||||
}
|
||||
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
this.populate();
|
||||
setTextColors(this.inputEl.inputEl, this.overlayEl);
|
||||
setValidFiles(this.inputEl.inputEl);
|
||||
this.getTextboxText = this.getTextboxText.bind(this);
|
||||
};
|
||||
}
|
||||
@@ -321,6 +492,12 @@ const settingsDefinitions = [
|
||||
type: SettingUtils.createMultilineSetting,
|
||||
defaultValue: '#559c22\n#229c57\n#229c8b\n#226f9c\n#22479c',
|
||||
attrs: { tooltip: 'A list of either rgb or hex colors, one color per line.' },
|
||||
onChange: () => {
|
||||
const nodes = app.graph._nodes.filter(node => node.type === 'Copy/Paste Textbox');
|
||||
nodes.forEach(node => {
|
||||
node.updateHighlightType();
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sn0w.TextboxGradientColors',
|
||||
@@ -328,6 +505,12 @@ const settingsDefinitions = [
|
||||
type: 'boolean',
|
||||
defaultValue: false,
|
||||
tooltip: 'Makes the textbox highlighting be a gradient between the first and last color based on the strength of the selection.',
|
||||
onChange: () => {
|
||||
const nodes = app.graph._nodes.filter(node => node.type === 'Copy/Paste Textbox');
|
||||
nodes.forEach(node => {
|
||||
node.updateHighlightType();
|
||||
});
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
@@ -342,6 +525,7 @@ const registerSetting = (settingDefinition) => {
|
||||
defaultValue: settingDefinition.defaultValue,
|
||||
tooltip: settingDefinition.tooltip,
|
||||
attrs: settingDefinition.attrs,
|
||||
onChange: settingDefinition.onChange,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user