Add Lora buttons
This commit is contained in:
+46
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
from aiohttp import web
|
||||
from server import PromptServer
|
||||
import folder_paths
|
||||
|
||||
WEB_DIRECTORY = "./js"
|
||||
|
||||
@@ -76,4 +77,49 @@ async def delete_note(request):
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
@PromptServer.instance.routes.get("/notes_sidebar/loras")
|
||||
async def get_loras_list(request):
|
||||
try:
|
||||
loras = folder_paths.get_filename_list("loras")
|
||||
return web.json_response({"loras": loras})
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
@PromptServer.instance.routes.get("/notes_sidebar/loras_with_metadata")
|
||||
async def get_loras_with_metadata(request):
|
||||
try:
|
||||
loras = folder_paths.get_filename_list("loras")
|
||||
|
||||
loras_with_metadata = []
|
||||
for lora in loras:
|
||||
# Get the full path to the lora file
|
||||
lora_full_path = folder_paths.get_full_path("loras", lora)
|
||||
|
||||
metadata = ""
|
||||
if lora_full_path:
|
||||
# Get the directory and base name
|
||||
lora_dir = os.path.dirname(lora_full_path)
|
||||
base_name = os.path.splitext(os.path.basename(lora_full_path))[0]
|
||||
txt_file_path = os.path.join(lora_dir, f"{base_name}.txt")
|
||||
|
||||
if os.path.exists(txt_file_path):
|
||||
try:
|
||||
with open(txt_file_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.read().strip().split('\n')
|
||||
# Join multiple lines with " | "
|
||||
metadata = " | ".join(line.strip() for line in lines if line.strip())
|
||||
except Exception as e:
|
||||
print(f"Error reading metadata for {lora}: {e}")
|
||||
|
||||
loras_with_metadata.append({
|
||||
"name": lora,
|
||||
"metadata": metadata
|
||||
})
|
||||
|
||||
print(f"Processed Lora: {lora}, Metadata: {metadata}")
|
||||
|
||||
return web.json_response({"loras": loras_with_metadata})
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']
|
||||
+152
-3
@@ -108,6 +108,9 @@ class NotesSidebar {
|
||||
this.saveBtn = null;
|
||||
this.renameBtn = null;
|
||||
this.deleteBtn = null;
|
||||
this.appendLorasBtn = null;
|
||||
this.appendLorasTagsBtn = null;
|
||||
this.appendLorasMetaBtn = null;
|
||||
this.isInitialized = false;
|
||||
this.sidebarElement = null;
|
||||
|
||||
@@ -170,7 +173,7 @@ class NotesSidebar {
|
||||
}
|
||||
|
||||
updateButtonState() {
|
||||
if (!this.saveBtn || !this.renameBtn || !this.deleteBtn) return;
|
||||
if (!this.saveBtn || !this.renameBtn || !this.deleteBtn || !this.appendLorasBtn || !this.appendLorasTagsBtn || !this.appendLorasMetaBtn) return;
|
||||
|
||||
const hasActiveNote = !!this.activeNote;
|
||||
const hasUnsavedChanges = hasActiveNote && this.unsavedChanges.get(this.activeNote);
|
||||
@@ -178,9 +181,12 @@ class NotesSidebar {
|
||||
// Save button: enabled only when there's an active note AND unsaved changes
|
||||
this.saveBtn.disabled = !hasActiveNote || !hasUnsavedChanges;
|
||||
|
||||
// Rename and delete buttons: enabled only when there's an active note
|
||||
// Rename, delete, and append buttons: enabled only when there's an active note
|
||||
this.renameBtn.disabled = !hasActiveNote;
|
||||
this.deleteBtn.disabled = !hasActiveNote;
|
||||
this.appendLorasBtn.disabled = !hasActiveNote;
|
||||
this.appendLorasTagsBtn.disabled = !hasActiveNote;
|
||||
this.appendLorasMetaBtn.disabled = !hasActiveNote;
|
||||
}
|
||||
|
||||
async init() {
|
||||
@@ -284,8 +290,32 @@ class NotesSidebar {
|
||||
this.deleteBtn.onclick = () => this.deleteActiveNote();
|
||||
this.deleteBtn.disabled = true;
|
||||
|
||||
this.appendLorasBtn = document.createElement('button');
|
||||
this.appendLorasBtn.innerHTML = '<i class="pi pi-plus-circle"></i>';
|
||||
this.appendLorasBtn.className = 'notes-btn append-loras';
|
||||
this.appendLorasBtn.title = 'Append LoRAs List';
|
||||
this.appendLorasBtn.onclick = () => this.appendLorasList();
|
||||
this.appendLorasBtn.disabled = true;
|
||||
|
||||
this.appendLorasTagsBtn = document.createElement('button');
|
||||
this.appendLorasTagsBtn.innerHTML = '<i class="pi pi-tags"></i>';
|
||||
this.appendLorasTagsBtn.className = 'notes-btn append-loras-tags';
|
||||
this.appendLorasTagsBtn.title = 'Append LoRAs as Tags';
|
||||
this.appendLorasTagsBtn.onclick = () => this.appendLorasTags();
|
||||
this.appendLorasTagsBtn.disabled = true;
|
||||
|
||||
this.appendLorasMetaBtn = document.createElement('button');
|
||||
this.appendLorasMetaBtn.innerHTML = '<i class="pi pi-info-circle"></i>';
|
||||
this.appendLorasMetaBtn.className = 'notes-btn append-loras-meta';
|
||||
this.appendLorasMetaBtn.title = 'Append LoRAs with Metadata';
|
||||
this.appendLorasMetaBtn.onclick = () => this.appendLorasWithMetadata();
|
||||
this.appendLorasMetaBtn.disabled = true;
|
||||
|
||||
leftButtonContainer.appendChild(this.saveBtn);
|
||||
rightButtonContainer.appendChild(this.renameBtn);
|
||||
leftButtonContainer.appendChild(this.renameBtn);
|
||||
leftButtonContainer.appendChild(this.appendLorasBtn);
|
||||
leftButtonContainer.appendChild(this.appendLorasTagsBtn);
|
||||
leftButtonContainer.appendChild(this.appendLorasMetaBtn);
|
||||
rightButtonContainer.appendChild(this.deleteBtn);
|
||||
|
||||
buttonContainer.appendChild(leftButtonContainer);
|
||||
@@ -548,6 +578,125 @@ class NotesSidebar {
|
||||
const hasUnsavedChanges = currentContent !== savedContent;
|
||||
this.store.setUnsavedChanges(this.activeNote, hasUnsavedChanges);
|
||||
}
|
||||
|
||||
async appendLorasList() {
|
||||
try {
|
||||
const response = await fetch('/notes_sidebar/loras');
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok && data.loras) {
|
||||
// Normalize backslashes to forward slashes
|
||||
const normalizedLoras = data.loras.map(lora => lora.replace(/\\/g, '/'));
|
||||
const lorasText = normalizedLoras.join('\n');
|
||||
|
||||
// Add newline before appending if current content doesn't end with one
|
||||
const separator = this.textArea.value && !this.textArea.value.endsWith('\n') ? '\n' : '';
|
||||
const textToInsert = separator + lorasText;
|
||||
|
||||
// Focus textarea to ensure it's active
|
||||
this.textArea.focus();
|
||||
|
||||
// Move cursor to end
|
||||
this.textArea.setSelectionRange(this.textArea.value.length, this.textArea.value.length);
|
||||
|
||||
// Use document.execCommand to preserve undo history
|
||||
document.execCommand('insertText', false, textToInsert);
|
||||
|
||||
// Trigger change handling
|
||||
this.onTextAreaChange();
|
||||
} else {
|
||||
console.error('Error fetching LoRAs:', data.error || 'Unknown error');
|
||||
alert('Failed to fetch LoRAs list');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error appending LoRAs:', error);
|
||||
alert('Failed to append LoRAs list');
|
||||
}
|
||||
}
|
||||
|
||||
async appendLorasTags() {
|
||||
try {
|
||||
const response = await fetch('/notes_sidebar/loras');
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok && data.loras) {
|
||||
// Format each LoRA as <lora:name:1.0>, stripping .safetensors extension and normalizing slashes
|
||||
const lorasTags = data.loras.map(lora => {
|
||||
const nameWithoutExt = lora.replace(/\.safetensors$/i, '');
|
||||
const normalizedPath = nameWithoutExt.replace(/\\/g, '/');
|
||||
return `<lora:${normalizedPath}>`;
|
||||
});
|
||||
const lorasText = lorasTags.join('\n');
|
||||
|
||||
// Add newline before appending if current content doesn't end with one
|
||||
const separator = this.textArea.value && !this.textArea.value.endsWith('\n') ? '\n' : '';
|
||||
const textToInsert = separator + lorasText;
|
||||
|
||||
// Focus textarea to ensure it's active
|
||||
this.textArea.focus();
|
||||
|
||||
// Move cursor to end
|
||||
this.textArea.setSelectionRange(this.textArea.value.length, this.textArea.value.length);
|
||||
|
||||
// Use document.execCommand to preserve undo history
|
||||
document.execCommand('insertText', false, textToInsert);
|
||||
|
||||
// Trigger change handling
|
||||
this.onTextAreaChange();
|
||||
} else {
|
||||
console.error('Error fetching LoRAs:', data.error || 'Unknown error');
|
||||
alert('Failed to fetch LoRAs list');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error appending LoRAs tags:', error);
|
||||
alert('Failed to append LoRAs tags');
|
||||
}
|
||||
}
|
||||
|
||||
async appendLorasWithMetadata() {
|
||||
try {
|
||||
const response = await fetch('/notes_sidebar/loras_with_metadata');
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok && data.loras) {
|
||||
// Format each LoRA with metadata as <lora:name:1.0>, metadata
|
||||
const lorasWithMeta = data.loras.map(lora => {
|
||||
const nameWithoutExt = lora.name.replace(/\.safetensors$/i, '');
|
||||
const normalizedPath = nameWithoutExt.replace(/\\/g, '/');
|
||||
const baseTag = `<lora:${normalizedPath}>`;
|
||||
|
||||
if (lora.metadata) {
|
||||
return `${baseTag}, ${lora.metadata}`;
|
||||
} else {
|
||||
return baseTag;
|
||||
}
|
||||
});
|
||||
const lorasText = lorasWithMeta.join('\n');
|
||||
|
||||
// Add newline before appending if current content doesn't end with one
|
||||
const separator = this.textArea.value && !this.textArea.value.endsWith('\n') ? '\n' : '';
|
||||
const textToInsert = separator + lorasText;
|
||||
|
||||
// Focus textarea to ensure it's active
|
||||
this.textArea.focus();
|
||||
|
||||
// Move cursor to end
|
||||
this.textArea.setSelectionRange(this.textArea.value.length, this.textArea.value.length);
|
||||
|
||||
// Use document.execCommand to preserve undo history
|
||||
document.execCommand('insertText', false, textToInsert);
|
||||
|
||||
// Trigger change handling
|
||||
this.onTextAreaChange();
|
||||
} else {
|
||||
console.error('Error fetching LoRAs with metadata:', data.error || 'Unknown error');
|
||||
alert('Failed to fetch LoRAs with metadata');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error appending LoRAs with metadata:', error);
|
||||
alert('Failed to append LoRAs with metadata');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to validate filename safety
|
||||
|
||||
@@ -146,4 +146,16 @@
|
||||
|
||||
.notes-btn.rename {
|
||||
background: #FF9800;
|
||||
}
|
||||
|
||||
.notes-btn.append-loras {
|
||||
background: #9C27B0;
|
||||
}
|
||||
|
||||
.notes-btn.append-loras-tags {
|
||||
background: #673AB7;
|
||||
}
|
||||
|
||||
.notes-btn.append-loras-meta {
|
||||
background: #3F51B5;
|
||||
}
|
||||
Reference in New Issue
Block a user