178 lines
6.1 KiB
JavaScript
178 lines
6.1 KiB
JavaScript
import { NotesApi } from "../api/notesApi.js";
|
|
|
|
export class AppendMenu {
|
|
constructor(textArea, onContentChange) {
|
|
this.textArea = textArea;
|
|
this.onContentChange = onContentChange;
|
|
this.container = null;
|
|
this.button = null;
|
|
this.submenu = null;
|
|
this.isDisabled = true;
|
|
}
|
|
|
|
createMenuElements() {
|
|
// Append submenu container
|
|
this.container = document.createElement('div');
|
|
this.container.className = 'notes-submenu-container';
|
|
|
|
this.button = document.createElement('button');
|
|
this.button.innerHTML = '<i class="pi pi-plus-circle"></i>';
|
|
this.button.className = 'notes-btn append-loras';
|
|
this.button.title = 'Append';
|
|
this.button.onclick = () => this.toggleMenu();
|
|
this.button.disabled = this.isDisabled;
|
|
|
|
// Create submenu
|
|
this.submenu = document.createElement('div');
|
|
this.submenu.className = 'notes-submenu';
|
|
this.submenu.style.display = 'none';
|
|
|
|
// Create submenu items
|
|
const rawOption = document.createElement('div');
|
|
rawOption.className = 'notes-submenu-item';
|
|
rawOption.textContent = 'Append raw LoRA filenames list';
|
|
rawOption.onclick = () => this.selectOption('raw');
|
|
|
|
const tagOption = document.createElement('div');
|
|
tagOption.className = 'notes-submenu-item';
|
|
tagOption.textContent = "Append LoRAs in '<lora:name>' format";
|
|
tagOption.onclick = () => this.selectOption('tag');
|
|
|
|
const metaOption = document.createElement('div');
|
|
metaOption.className = 'notes-submenu-item';
|
|
metaOption.textContent = "Append LoRAs in '<lora:name>, metadata' format";
|
|
metaOption.onclick = () => this.selectOption('meta');
|
|
|
|
this.submenu.appendChild(rawOption);
|
|
this.submenu.appendChild(tagOption);
|
|
this.submenu.appendChild(metaOption);
|
|
|
|
this.container.appendChild(this.button);
|
|
this.container.appendChild(this.submenu);
|
|
|
|
return this.container;
|
|
}
|
|
|
|
setDisabled(disabled) {
|
|
this.isDisabled = disabled;
|
|
if (this.button) {
|
|
this.button.disabled = disabled;
|
|
}
|
|
}
|
|
|
|
toggleMenu() {
|
|
if (this.submenu.style.display === 'none') {
|
|
this.submenu.style.display = 'block';
|
|
// Add click listener to close menu when clicking outside
|
|
document.addEventListener('click', this.closeMenuHandler);
|
|
} else {
|
|
this.submenu.style.display = 'none';
|
|
document.removeEventListener('click', this.closeMenuHandler);
|
|
}
|
|
}
|
|
|
|
closeMenuHandler = (event) => {
|
|
if (!this.container.contains(event.target)) {
|
|
this.submenu.style.display = 'none';
|
|
document.removeEventListener('click', this.closeMenuHandler);
|
|
}
|
|
}
|
|
|
|
selectOption(type) {
|
|
this.submenu.style.display = 'none';
|
|
document.removeEventListener('click', this.closeMenuHandler);
|
|
|
|
switch(type) {
|
|
case 'raw':
|
|
this.appendLorasList();
|
|
break;
|
|
case 'tag':
|
|
this.appendLorasTags();
|
|
break;
|
|
case 'meta':
|
|
this.appendLorasWithMetadata();
|
|
break;
|
|
}
|
|
}
|
|
|
|
async appendLorasList() {
|
|
try {
|
|
const loras = await NotesApi.getLorasList();
|
|
|
|
// Normalize backslashes to forward slashes
|
|
const normalizedLoras = loras.map(lora => lora.replace(/\\/g, '/'));
|
|
const lorasText = normalizedLoras.join('\n');
|
|
|
|
this.insertTextIntoTextarea(lorasText);
|
|
} catch (error) {
|
|
console.error('Error appending LoRAs:', error);
|
|
alert('Failed to append LoRAs list');
|
|
}
|
|
}
|
|
|
|
async appendLorasTags() {
|
|
try {
|
|
const loras = await NotesApi.getLorasList();
|
|
|
|
// Format each LoRA as <lora:name:1.0>, stripping .safetensors extension and normalizing slashes
|
|
const lorasTags = loras.map(lora => {
|
|
const nameWithoutExt = lora.replace(/\.safetensors$/i, '');
|
|
const normalizedPath = nameWithoutExt.replace(/\\/g, '/');
|
|
return `<lora:${normalizedPath}>`;
|
|
});
|
|
const lorasText = lorasTags.join('\n');
|
|
|
|
this.insertTextIntoTextarea(lorasText);
|
|
} catch (error) {
|
|
console.error('Error appending LoRAs tags:', error);
|
|
alert('Failed to append LoRAs tags');
|
|
}
|
|
}
|
|
|
|
async appendLorasWithMetadata() {
|
|
try {
|
|
const loras = await NotesApi.getLorasWithMetadata();
|
|
|
|
// Format each LoRA with metadata as <lora:name:1.0>, metadata
|
|
const lorasWithMeta = 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');
|
|
|
|
this.insertTextIntoTextarea(lorasText);
|
|
} catch (error) {
|
|
console.error('Error appending LoRAs with metadata:', error);
|
|
alert('Failed to append LoRAs with metadata');
|
|
}
|
|
}
|
|
|
|
insertTextIntoTextarea(text) {
|
|
if (!this.textArea) return;
|
|
|
|
// 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 + text;
|
|
|
|
// 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
|
|
if (this.onContentChange) {
|
|
this.onContentChange();
|
|
}
|
|
}
|
|
} |