@@ -120,6 +120,7 @@ export function showLoraModal(lora) {
setupShowcaseScroll();
setupTabSwitching();
setupTagTooltip();
+ setupTriggerWordsEditMode();
// If we have a model ID but no description, fetch it
if (lora.civitai?.modelId && !lora.modelDescription) {
@@ -488,35 +489,75 @@ async function saveModelMetadata(filePath, data) {
}
}
-function renderTriggerWords(words) {
+function renderTriggerWords(words, filePath) {
if (!words.length) return `
-
- No trigger word needed
+
+
+
+
+
+ No trigger word needed
+
+
+
+
+
+
+
+
+
+
+
`;
return `
-
-
- ${words.map(word => `
-
- ${word}
-
-
-
-
- `).join('')}
+
+
+
+
+
+
+ ${words.map(word => `
+
+ ${word}
+
+
+
+
+
+ `).join('')}
+
+
+
+
+
+
+
+
+
+
`;
}
-function renderShowcaseImages(images) {
- return renderShowcaseContent(images);
-}
-
export function toggleShowcase(element) {
const carousel = element.nextElementSibling;
const isCollapsed = carousel.classList.contains('collapsed');
@@ -738,4 +779,243 @@ function setupTagTooltip() {
tooltip.classList.remove('visible');
});
}
-}
\ No newline at end of file
+}
+
+// Set up trigger words edit mode
+function setupTriggerWordsEditMode() {
+ const editBtn = document.querySelector('.edit-trigger-words-btn');
+ if (!editBtn) return;
+
+ editBtn.addEventListener('click', function() {
+ const triggerWordsSection = this.closest('.trigger-words');
+ const isEditMode = triggerWordsSection.classList.toggle('edit-mode');
+
+ // Toggle edit mode UI elements
+ const triggerWordTags = triggerWordsSection.querySelectorAll('.trigger-word-tag');
+ const editControls = triggerWordsSection.querySelector('.trigger-words-edit-controls');
+ const noTriggerWords = triggerWordsSection.querySelector('.no-trigger-words');
+ const tagsContainer = triggerWordsSection.querySelector('.trigger-words-tags');
+
+ if (isEditMode) {
+ this.innerHTML = ''; // Change to cancel icon
+ this.title = "Cancel editing";
+ editControls.style.display = 'flex';
+
+ // If we have no trigger words yet, hide the "No trigger word needed" text
+ // and show the empty tags container
+ if (noTriggerWords) {
+ noTriggerWords.style.display = 'none';
+ if (tagsContainer) tagsContainer.style.display = 'flex';
+ }
+
+ // Disable click-to-copy and show delete buttons
+ triggerWordTags.forEach(tag => {
+ tag.onclick = null;
+ tag.querySelector('.trigger-word-copy').style.display = 'none';
+ tag.querySelector('.delete-trigger-word-btn').style.display = 'block';
+ });
+ } else {
+ this.innerHTML = ''; // Change back to edit icon
+ this.title = "Edit trigger words";
+ editControls.style.display = 'none';
+
+ // If we have no trigger words, show the "No trigger word needed" text
+ // and hide the empty tags container
+ const currentTags = triggerWordsSection.querySelectorAll('.trigger-word-tag');
+ if (noTriggerWords && currentTags.length === 0) {
+ noTriggerWords.style.display = '';
+ if (tagsContainer) tagsContainer.style.display = 'none';
+ }
+
+ // Restore original state
+ triggerWordTags.forEach(tag => {
+ const word = tag.dataset.word;
+ tag.onclick = () => copyTriggerWord(word);
+ tag.querySelector('.trigger-word-copy').style.display = 'flex';
+ tag.querySelector('.delete-trigger-word-btn').style.display = 'none';
+ });
+
+ // Hide add form if open
+ triggerWordsSection.querySelector('.add-trigger-word-form').style.display = 'none';
+ }
+ });
+
+ // Set up add trigger word button
+ const addBtn = document.querySelector('.add-trigger-word-btn');
+ if (addBtn) {
+ addBtn.addEventListener('click', function() {
+ const triggerWordsSection = this.closest('.trigger-words');
+ const addForm = triggerWordsSection.querySelector('.add-trigger-word-form');
+ addForm.style.display = 'flex';
+ addForm.querySelector('input').focus();
+ });
+ }
+
+ // Set up confirm and cancel add buttons
+ const confirmAddBtn = document.querySelector('.confirm-add-trigger-word-btn');
+ const cancelAddBtn = document.querySelector('.cancel-add-trigger-word-btn');
+ const triggerWordInput = document.querySelector('.new-trigger-word-input');
+
+ if (confirmAddBtn && triggerWordInput) {
+ confirmAddBtn.addEventListener('click', function() {
+ addNewTriggerWord(triggerWordInput.value);
+ });
+
+ // Add keydown event to input
+ triggerWordInput.addEventListener('keydown', function(e) {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ addNewTriggerWord(this.value);
+ }
+ });
+ }
+
+ if (cancelAddBtn) {
+ cancelAddBtn.addEventListener('click', function() {
+ const addForm = this.closest('.add-trigger-word-form');
+ addForm.style.display = 'none';
+ addForm.querySelector('input').value = '';
+ });
+ }
+
+ // Set up save button
+ const saveBtn = document.querySelector('.save-trigger-words-btn');
+ if (saveBtn) {
+ saveBtn.addEventListener('click', saveTriggerWords);
+ }
+
+ // Set up delete buttons
+ document.querySelectorAll('.delete-trigger-word-btn').forEach(btn => {
+ btn.addEventListener('click', function(e) {
+ e.stopPropagation();
+ const tag = this.closest('.trigger-word-tag');
+ tag.remove();
+ });
+ });
+}
+
+// Function to add a new trigger word
+function addNewTriggerWord(word) {
+ word = word.trim();
+ if (!word) return;
+
+ const triggerWordsSection = document.querySelector('.trigger-words');
+ let tagsContainer = document.querySelector('.trigger-words-tags');
+
+ // Ensure tags container exists and is visible
+ if (tagsContainer) {
+ tagsContainer.style.display = 'flex';
+ } else {
+ // Create tags container if it doesn't exist
+ const contentDiv = triggerWordsSection.querySelector('.trigger-words-content');
+ if (contentDiv) {
+ tagsContainer = document.createElement('div');
+ tagsContainer.className = 'trigger-words-tags';
+ contentDiv.appendChild(tagsContainer);
+ }
+ }
+
+ if (!tagsContainer) return;
+
+ // Hide "no trigger words" message if it exists
+ const noTriggerWordsMsg = triggerWordsSection.querySelector('.no-trigger-words');
+ if (noTriggerWordsMsg) {
+ noTriggerWordsMsg.style.display = 'none';
+ }
+
+ // Validation: Check length
+ if (word.split(/\s+/).length > 30) {
+ showToast('Trigger word should not exceed 30 words', 'error');
+ return;
+ }
+
+ // Validation: Check total number
+ const currentTags = tagsContainer.querySelectorAll('.trigger-word-tag');
+ if (currentTags.length >= 10) {
+ showToast('Maximum 10 trigger words allowed', 'error');
+ return;
+ }
+
+ // Validation: Check for duplicates
+ const existingWords = Array.from(currentTags).map(tag => tag.dataset.word);
+ if (existingWords.includes(word)) {
+ showToast('This trigger word already exists', 'error');
+ return;
+ }
+
+ // Create new tag
+ const newTag = document.createElement('div');
+ newTag.className = 'trigger-word-tag';
+ newTag.dataset.word = word;
+ newTag.innerHTML = `
+ ${word}
+
+
+
+
+ `;
+
+ // Add event listener to delete button
+ const deleteBtn = newTag.querySelector('.delete-trigger-word-btn');
+ deleteBtn.addEventListener('click', function() {
+ newTag.remove();
+ });
+
+ tagsContainer.appendChild(newTag);
+
+ // Clear and hide the input form
+ const triggerWordInput = document.querySelector('.new-trigger-word-input');
+ triggerWordInput.value = '';
+ document.querySelector('.add-trigger-word-form').style.display = 'none';
+}
+
+// Function to save updated trigger words
+async function saveTriggerWords() {
+ const filePath = document.querySelector('.edit-trigger-words-btn').dataset.filePath;
+ const triggerWordTags = document.querySelectorAll('.trigger-word-tag');
+ const words = Array.from(triggerWordTags).map(tag => tag.dataset.word);
+
+ try {
+ // Special format for updating nested civitai.trainedWords
+ await saveModelMetadata(filePath, {
+ civitai: { trainedWords: words }
+ });
+
+ // Update UI
+ const editBtn = document.querySelector('.edit-trigger-words-btn');
+ editBtn.click(); // Exit edit mode
+
+ // Update the LoRA card's dataset
+ const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
+ if (loraCard && loraCard.dataset.civitai) {
+ const civitaiData = JSON.parse(loraCard.dataset.civitai);
+ civitaiData.trainedWords = words;
+ loraCard.dataset.civitai = JSON.stringify(civitaiData);
+ }
+
+ // If we saved an empty array and there's a no-trigger-words element, show it
+ const noTriggerWords = document.querySelector('.no-trigger-words');
+ const tagsContainer = document.querySelector('.trigger-words-tags');
+ if (words.length === 0 && noTriggerWords) {
+ noTriggerWords.style.display = '';
+ if (tagsContainer) tagsContainer.style.display = 'none';
+ }
+
+ showToast('Trigger words updated successfully', 'success');
+ } catch (error) {
+ showToast('Failed to update trigger words', 'error');
+ }
+}
+
+// Add copy trigger word function
+window.copyTriggerWord = async function(word) {
+ try {
+ await navigator.clipboard.writeText(word);
+ showToast('Trigger word copied', 'success');
+ } catch (err) {
+ console.error('Copy failed:', err);
+ showToast('Copy failed', 'error');
+ }
+};
\ No newline at end of file