@@ -1,6 +1,12 @@
|
||||
name: Run Character Tag Validator
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'web/settings/characters.json'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'web/settings/characters.json'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -12,7 +18,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.10
|
||||
python-version: 3.10.9
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -11,3 +11,4 @@ src/custom_schedulers/mixed_decay_growth.py
|
||||
src/prompt_combine_testing.py
|
||||
web/settings/sn0w_settings.json
|
||||
testing/test_scheduler.py
|
||||
update-dev.bat
|
||||
|
||||
@@ -69,13 +69,16 @@ class CharacterSelectNode:
|
||||
|
||||
custom_json_path = os.path.join(dir_path, CUSTOM_CHARACTER_FILE_PATH)
|
||||
if not os.path.exists(custom_json_path):
|
||||
cls.logger.log(f"Custom character file doesn't exist. Please create the json file at: {custom_json_path}", "WARNING")
|
||||
cls.logger.log(
|
||||
f"Custom character file doesn't exist. Please create the json file at: {custom_json_path}", "WARNING"
|
||||
)
|
||||
else:
|
||||
with open(custom_json_path, "r", encoding="utf-8") as file:
|
||||
custom_character_data = json.load(file)
|
||||
for custom_character in custom_character_data:
|
||||
for character in character_data:
|
||||
if custom_character["name"] == character["name"]:
|
||||
character["associated_string"] += ", " + custom_character["associated_string"]
|
||||
character["prompt"] += ", " + custom_character["prompt"]
|
||||
break
|
||||
else:
|
||||
|
||||
@@ -3,18 +3,7 @@ import { app } from '../../../scripts/app.js';
|
||||
import { api } from '../../../scripts/api.js';
|
||||
|
||||
async function fetchExtensionSettings() {
|
||||
try {
|
||||
const response = await api.fetchApi(
|
||||
'/extensions/ComfyUI-Sn0w-Scripts/settings/sn0w_settings.json'
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok ' + response.statusText);
|
||||
}
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
return SettingUtils.fetchApi("/extensions/ComfyUI-Sn0w-Scripts/settings/sn0w_settings.json");
|
||||
}
|
||||
|
||||
const sn0wSettings = await fetchExtensionSettings();
|
||||
|
||||
+48
-17
@@ -62,35 +62,66 @@ app.registerExtension({
|
||||
|
||||
// Combine the existing custom loaders with the fetched ones
|
||||
loraLoaders = loraLoaders.concat(...customLoraLoadersArrays);
|
||||
addLoraLoaders(loraLoaders);
|
||||
|
||||
const original_getNodeMenuOptions = app.canvas.getNodeMenuOptions;
|
||||
app.canvas.getNodeMenuOptions = function (node) {
|
||||
const options = original_getNodeMenuOptions.apply(this, arguments);
|
||||
|
||||
// Collect all Lora widgets.
|
||||
const loraWidgets = node.widgets.filter(widget => widget.name.includes("lora") && widget.type === "combo");
|
||||
const totalLoras = loraWidgets.length;
|
||||
|
||||
if (loraLoaders.includes(node.type)) {
|
||||
const settingUtils = new SettingUtils();
|
||||
const nullIndex = options.indexOf(null);
|
||||
let optionsArr = []
|
||||
|
||||
// Check if the filename is in the existingList
|
||||
const selectedLora = node.widgets[0].value;
|
||||
const pathArray = selectedLora.split('\\');
|
||||
const filename = pathArray[pathArray.length - 1];
|
||||
const isFavourite = existingList.includes(filename);
|
||||
loraWidgets.forEach(widget => {
|
||||
const selectedLora = widget.value;
|
||||
const pathArray = selectedLora.split('\\');
|
||||
const filename = pathArray[pathArray.length - 1];
|
||||
if (filename == "None") {
|
||||
return;
|
||||
}
|
||||
const isFavourite = existingList.includes(filename);
|
||||
|
||||
// Create the new menu item
|
||||
const newMenuItem = {
|
||||
content: isFavourite ? 'Unfavourite Lora ☆' : 'Favourite Lora ★',
|
||||
disabled: false,
|
||||
callback: () => {
|
||||
SettingUtils.toggleFavourite(existingList, filename, favouriteLoraId);
|
||||
settingUtils.refreshComboInSingleNode(app, node.title);
|
||||
},
|
||||
};
|
||||
const newMenuItem = {
|
||||
content: (isFavourite ? 'Unfavourite ' : 'Favourite ') + filename.split(".")[0] + (isFavourite ? ' ☆' : ' ★'),
|
||||
disabled: false,
|
||||
callback: () => {
|
||||
SettingUtils.toggleFavourite(existingList, filename, favouriteLoraId);
|
||||
settingUtils.refreshComboInSingleNode(app, node.title);
|
||||
},
|
||||
};
|
||||
|
||||
optionsArr.push(newMenuItem);
|
||||
});
|
||||
|
||||
let menuItem
|
||||
if (optionsArr.length > 0) {
|
||||
if (totalLoras === 1) {
|
||||
menuItem = optionsArr[0]
|
||||
} else if (totalLoras > 1) {
|
||||
menuItem = {
|
||||
content: "Favourite",
|
||||
disabled: false,
|
||||
has_submenu: true,
|
||||
submenu: {
|
||||
options: optionsArr
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
menuItem = {
|
||||
content: "No lora selected",
|
||||
disabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
if (nullIndex !== -1) {
|
||||
options.splice(nullIndex, 0, newMenuItem);
|
||||
options.splice(nullIndex, 0, menuItem);
|
||||
} else {
|
||||
options.push(newMenuItem);
|
||||
options.push(menuItem);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
|
||||
@@ -23,13 +23,13 @@ app.registerExtension({
|
||||
};
|
||||
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
this.inputs[3].type = ['STRING', 'CONDITIONING'];
|
||||
this.inputs[4].type = ['STRING', 'CONDITIONING'];
|
||||
//this.inputs[3].type = ['STRING', 'CONDITIONING'];
|
||||
//this.inputs[4].type = ['STRING', 'CONDITIONING'];
|
||||
};
|
||||
|
||||
nodeType.prototype.onConfigure = function () {
|
||||
this.inputs[3].type = ['STRING', 'CONDITIONING'];
|
||||
this.inputs[4].type = ['STRING', 'CONDITIONING'];
|
||||
//this.inputs[3].type = ['STRING', 'CONDITIONING'];
|
||||
//this.inputs[4].type = ['STRING', 'CONDITIONING'];
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
+35
-9
@@ -19,6 +19,23 @@ export class SettingUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static async fetchApi(route, options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (!options.headers) {
|
||||
options.headers = {};
|
||||
}
|
||||
const url = `${location.protocol}//${location.host}${route}`;
|
||||
try {
|
||||
const response = await fetch(url, options);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Callback function to add or remove the filename from the settings list
|
||||
static async toggleFavourite(existingList, filename, setting) {
|
||||
try {
|
||||
@@ -47,35 +64,33 @@ export class SettingUtils {
|
||||
const textarea = document.createElement('textarea');
|
||||
|
||||
// Generate a unique ID for associating the label with the textarea
|
||||
const uniqueId = `${name.replaceAll(' ', '').replace('[', '').replace(']', '-')}`;
|
||||
const uniqueId = `${name.replaceAll(' ', '').replaceAll('[', '').replaceAll(']', '-')}`;
|
||||
label.setAttribute('for', uniqueId);
|
||||
label.textContent = name;
|
||||
|
||||
textarea.id = uniqueId;
|
||||
textarea.value = value;
|
||||
const maxLines = 10;
|
||||
const lineHeight = 15;
|
||||
// Function to set the height based on the number of lines
|
||||
const adjustHeight = () => {
|
||||
textarea.style.height = ''; // Allow to shrink
|
||||
const lines = textarea.value.split('\n').length;
|
||||
const scrollHeight = textarea.scrollHeight + 3;
|
||||
if (lines > maxLines) {
|
||||
const height = (scrollHeight / lines) * maxLines
|
||||
textarea.setAttribute(
|
||||
'style',
|
||||
`width: 100%; height: ${lineHeight * maxLines}px; resize: none;`
|
||||
`width: 100%; height: ${height}px; resize: none;`
|
||||
);
|
||||
return;
|
||||
}
|
||||
const height = lines * lineHeight;
|
||||
textarea.setAttribute('style', `width: 100%; height: ${height}px; resize: none;`);
|
||||
textarea.setAttribute('style', `width: 100%; height: ${scrollHeight}px; resize: none;`);
|
||||
};
|
||||
|
||||
adjustHeight();
|
||||
textarea.onchange = () => setter(textarea.value);
|
||||
textarea.addEventListener('input', adjustHeight);
|
||||
|
||||
// Apply event handler
|
||||
textarea.onchange = () => setter(textarea.value);
|
||||
|
||||
// Apply additional attributes
|
||||
for (const [key, val] of Object.entries(attrs)) {
|
||||
textarea.setAttribute(key, val);
|
||||
@@ -94,6 +109,17 @@ export class SettingUtils {
|
||||
label.className = 'comfy-tooltip-indicator';
|
||||
}
|
||||
|
||||
// Update height when settings are opened
|
||||
const dialog = document.getElementById('comfy-settings-dialog');
|
||||
const observer = new MutationObserver(mutations => {
|
||||
mutations.forEach(mutation => {
|
||||
if (mutation.type === 'attributes' && mutation.attributeName === 'open') {
|
||||
adjustHeight();
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe(dialog, { attributes: true });
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
||||
@@ -108,7 +134,7 @@ export class SettingUtils {
|
||||
const label = document.createElement('label');
|
||||
|
||||
// Generate a unique ID for the setting container
|
||||
const uniqueId = `${name.replaceAll(' ', '').replace('[', '').replace(']', '-')}`;
|
||||
const uniqueId = `${name.replaceAll(' ', '').replaceAll('[', '').replaceAll(']', '-')}`;
|
||||
label.setAttribute('for', uniqueId);
|
||||
label.textContent = name;
|
||||
|
||||
|
||||
+3
-9
@@ -228,7 +228,7 @@ app.registerExtension({
|
||||
let trimmedTag = tag.trim();
|
||||
|
||||
// Remove HTML tags
|
||||
trimmedTag = trimmedTag.replace(/<[^>]*>/g, '');
|
||||
trimmedTag = trimmedTag.replace(/<|>/g, "");
|
||||
|
||||
// Remove the first character if it is a parenthesis
|
||||
if (trimmedTag.startsWith('(')) {
|
||||
@@ -464,15 +464,9 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
async function getValidFiles(type) {
|
||||
const apiRequest = await api.fetchApi(`${SettingUtils.API_PREFIX}/${type}`, {
|
||||
return await SettingUtils.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 () {
|
||||
|
||||
@@ -159,6 +159,11 @@
|
||||
"associated_string": "shylily, shylily \\(bikini\\), indie virtual youtuber",
|
||||
"prompt": "ahoge, blue hair, large breasts, long hair, multicolored hair, purple eyes, two-tone hair, virtual youtuber, white hair, animal ears, facial mark, orca girl"
|
||||
},
|
||||
{
|
||||
"name": "Akuma Nihmune (Vtuber)",
|
||||
"associated_string": "akuma nihmune, indie virtual youtuber",
|
||||
"prompt": "purple hair, demon horns, mole under eye, heart tattoo, ear piercing, earrings"
|
||||
},
|
||||
{
|
||||
"name": "Hasumi (Blue Archive)",
|
||||
"associated_string": "hasumi \\(blue archive\\), blue archive",
|
||||
|
||||
Reference in New Issue
Block a user