Refactor trigger words and metadata editing styles

- Removed outdated styles from trigger words CSS and consolidated into a new shared edit-metadata CSS file.
- Updated JavaScript components for trigger words and model tags to utilize the new metadata styles.
- Adjusted class names and structure in the HTML to align with the new styling conventions.
- Enhanced the UI for editing tags and trigger words, ensuring consistency across components.
This commit is contained in:
Will Miao
2025-06-13 20:19:10 +08:00
parent 65c783c024
commit dc0a49f96d
6 changed files with 409 additions and 613 deletions

View File

@@ -73,7 +73,7 @@
background: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: var(--border-radius-sm);
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15); /* Enhanced shadow for better visibility */
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
padding: 10px 14px;
max-width: 400px;
z-index: 10;
@@ -88,7 +88,7 @@
opacity: 1;
visibility: visible;
transform: translateY(0);
pointer-events: auto; /* Enable interactions when visible */
pointer-events: auto;
}
.tooltip-content {
@@ -139,259 +139,10 @@
opacity: 0.5;
}
.edit-tags-btn:hover {
opacity: 0.8 !important;
background: rgba(0, 0, 0, 0.05);
}
[data-theme="dark"] .edit-tags-btn:hover {
background: rgba(255, 255, 255, 0.05);
}
/* Edit mode active state */
.model-tags-container.edit-mode {
width: 100%;
display: block; /* Change to block display in edit mode */
flex-basis: 100%; /* Take full width in flex contexts */
grid-column: 1 / -1; /* Ensure it spans full width in grid layouts */
}
/* Fix for tags edit container width and position */
.tags-edit-container {
padding: var(--space-2);
background: rgba(0, 0, 0, 0.03);
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: var(--border-radius-sm);
margin-top: var(--space-2);
width: 100%; /* Ensure it takes full width */
min-width: 100%; /* Force minimum width */
max-width: 100%; /* Limit maximum width */
box-sizing: border-box; /* Include padding in width calculation */
position: relative; /* For proper positioning of elements inside */
display: block; /* Force block display */
}
[data-theme="dark"] .tags-edit-container {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--lora-border);
}
.tags-edit-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid var(--lora-border);
width: 100%; /* Ensure header takes full width */
}
/* Style for the edit button when positioned in the header */
.edit-header-btn {
display: inline-flex !important; /* Always show */
opacity: 0.8 !important;
color: var(--lora-accent) !important;
margin-left: auto; /* Push to right */
}
.tags-edit-content {
margin-bottom: var(--space-1);
width: 100%; /* Ensure full width */
display: block; /* Force block display */
}
.tags-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: flex-start;
margin-bottom: var(--space-2);
width: 100%; /* Ensure full width */
}
.tag-edit-tag {
display: inline-flex;
align-items: center;
background: var(--bg-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius-xs);
padding: 4px 8px;
position: relative;
}
.tag-edit-content {
color: var(--lora-accent) !important;
font-size: 0.85em;
line-height: 1.4;
word-break: break-word;
}
/* Delete button for tag */
.delete-tag-btn {
position: absolute;
top: -5px;
right: -5px;
width: 16px;
height: 16px;
background: var(--lora-error);
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 9px;
transition: transform 0.2s ease;
}
.delete-tag-btn:hover {
transform: scale(1.1);
}
/* Edit controls */
.tags-edit-controls {
display: flex;
justify-content: flex-end;
gap: var(--space-2);
margin-top: var(--space-2);
margin-bottom: var(--space-2);
}
.tags-edit-controls button {
padding: 3px 8px;
border-radius: var(--border-radius-xs);
border: 1px solid var(--border-color);
background: var(--bg-color);
color: var(--text-color);
font-size: 0.85em;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.2s ease;
}
.tags-edit-controls button:hover {
background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1);
border-color: var(--lora-accent);
}
.save-tags-btn {
background: var(--lora-accent) !important;
color: white !important;
border-color: var(--lora-accent) !important;
}
.save-tags-btn:hover {
opacity: 0.9;
}
/* Add tag form */
.add-tag-form {
display: flex;
gap: var(--space-1);
position: relative;
width: 100%; /* Ensure full width */
}
.new-tag-input {
flex: 1;
padding: 4px 8px;
border-radius: var(--border-radius-xs);
border: 1px solid var(--border-color);
background: var(--bg-color);
color: var(--text-color);
font-size: 0.9em;
}
.new-tag-input:focus {
border-color: var(--lora-accent);
outline: none;
}
/* Tag Suggestions Dropdown Styles */
.tag-suggestions-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: var(--bg-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius-sm);
margin-top: 4px;
z-index: 100;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
overflow: hidden;
display: flex;
flex-direction: column;
}
.tag-suggestions-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
}
.tag-suggestions-header span {
font-size: 0.9em;
font-weight: 500;
color: var(--text-color);
}
.tag-suggestions-header small {
font-size: 0.8em;
opacity: 0.7;
}
.tag-suggestions-container {
max-height: 200px;
overflow-y: auto;
padding: 10px;
display: flex;
flex-wrap: wrap;
gap: 8px;
align-content: flex-start;
}
.tag-suggestion-item {
display: inline-flex;
align-items: center;
justify-content: space-between;
padding: 5px 10px;
cursor: pointer;
transition: all 0.2s ease;
border-radius: var(--border-radius-xs);
background: var(--lora-surface);
border: 1px solid var(--lora-border);
max-width: 150px;
}
.tag-suggestion-item:hover {
background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1);
border-color: var(--lora-accent);
}
.tag-suggestion-item.already-added {
opacity: 0.7;
cursor: default;
}
.tag-suggestion-item.already-added:hover {
background: var(--lora-surface);
border-color: var(--lora-border);
}
.tag-suggestion-text {
color: var(--lora-accent);
font-size: 0.9em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 4px;
max-width: 100px;
display: block;
flex-basis: 100%;
grid-column: 1 / -1;
}

View File

@@ -20,32 +20,6 @@
margin-bottom: 6px;
}
.edit-trigger-words-btn {
background: transparent;
border: none;
color: var(--text-color);
opacity: 0.5;
cursor: pointer;
padding: 2px 5px;
border-radius: var(--border-radius-xs);
transition: all 0.2s ease;
}
.edit-trigger-words-btn:hover {
opacity: 0.8;
background: rgba(0, 0, 0, 0.05);
}
[data-theme="dark"] .edit-trigger-words-btn:hover {
background: rgba(255, 255, 255, 0.05);
}
/* Edit mode active state */
.trigger-words.edit-mode .edit-trigger-words-btn {
opacity: 0.8;
color: var(--lora-accent);
}
.trigger-words-content {
margin-bottom: var(--space-1);
}
@@ -65,7 +39,7 @@
font-size: 0.9em;
}
/* Update Trigger Words styles */
/* Trigger word tags in display mode */
.trigger-word-tag {
display: inline-flex;
align-items: center;
@@ -79,15 +53,13 @@
position: relative;
}
/* Update trigger word content color to use theme accent */
.trigger-word-content {
color: var(--lora-accent) !important; /* Override general span color */
color: var(--lora-accent) !important;
font-size: 0.85em;
line-height: 1.4;
word-break: break-word;
}
/* Keep the hover effect using accent color */
.trigger-word-tag:hover {
background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1);
border-color: var(--lora-accent);
@@ -102,234 +74,8 @@
transition: opacity 0.2s;
}
/* Delete button for trigger word */
.delete-trigger-word-btn {
position: absolute;
top: -5px;
right: -5px;
width: 16px;
height: 16px;
background: var(--lora-error);
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 9px;
transition: transform 0.2s ease;
}
.delete-trigger-word-btn:hover {
transform: scale(1.1);
}
/* Edit controls */
.trigger-words-edit-controls {
display: flex;
justify-content: flex-end;
gap: var(--space-2);
margin-top: var(--space-2);
}
.trigger-words-edit-controls button {
padding: 3px 8px;
border-radius: var(--border-radius-xs);
border: 1px solid var(--border-color);
background: var(--bg-color);
color: var(--text-color);
font-size: 0.85em;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.2s ease;
}
.trigger-words-edit-controls button:hover {
background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1);
border-color: var(--lora-accent);
}
.trigger-words-edit-controls button i {
font-size: 0.8em;
}
.save-trigger-words-btn {
background: var(--lora-accent) !important;
color: white !important;
border-color: var(--lora-accent) !important;
}
.save-trigger-words-btn:hover {
opacity: 0.9;
}
/* Add trigger word form */
.add-trigger-word-form {
margin-top: var(--space-2);
display: flex;
gap: var(--space-1);
position: relative; /* Added for dropdown positioning */
}
.new-trigger-word-input {
flex: 1;
padding: 4px 8px;
border-radius: var(--border-radius-xs);
border: 1px solid var(--border-color);
background: var(--bg-color);
color: var(--text-color);
font-size: 0.9em;
}
.new-trigger-word-input:focus {
border-color: var(--lora-accent);
outline: none;
}
.confirm-add-trigger-word-btn,
.cancel-add-trigger-word-btn {
padding: 4px 8px;
border-radius: var(--border-radius-xs);
border: 1px solid var(--border-color);
background: var (--bg-color);
color: var(--text-color);
font-size: 0.85em;
cursor: pointer;
transition: all 0.2s ease;
}
.confirm-add-trigger-word-btn {
background: var(--lora-accent);
color: white;
border-color: var(--lora-accent);
}
.confirm-add-trigger-word-btn:hover {
opacity: 0.9;
}
.cancel-add-trigger-word-btn:hover {
background: rgba(0, 0, 0, 0.05);
}
[data-theme="dark"] .cancel-add-trigger-word-btn:hover {
background: rgba(255, 255, 255, 0.05);
}
/* Trained Words Loading Indicator */
.trained-words-loading {
display: flex;
align-items: center;
justify-content: center;
margin: var(--space-1) 0;
color: var(--text-color);
opacity: 0.7;
font-size: 0.9em;
gap: 8px;
}
.trained-words-loading i {
color: var(--lora-accent);
}
/* Trained Words Dropdown Styles */
.trained-words-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: var(--bg-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius-sm);
margin-top: 4px;
z-index: 100;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
overflow: hidden;
display: flex;
flex-direction: column;
}
.trained-words-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
}
.trained-words-header span {
font-size: 0.9em;
font-weight: 500;
color: var(--text-color);
}
.trained-words-header small {
font-size: 0.8em;
opacity: 0.7;
}
.trained-words-container {
max-height: 200px;
overflow-y: auto;
padding: 10px;
display: flex;
flex-wrap: wrap;
gap: 8px;
align-content: flex-start;
}
.trained-word-item {
display: inline-flex;
align-items: center;
justify-content: space-between;
padding: 5px 10px;
cursor: pointer;
transition: all 0.2s ease;
border-radius: var(--border-radius-xs);
background: var(--lora-surface);
border: 1px solid var(--lora-border);
max-width: 150px;
}
.trained-word-item:hover {
background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1);
border-color: var(--lora-accent);
}
.trained-word-item.already-added {
opacity: 0.7;
cursor: default;
}
.trained-word-item.already-added:hover {
background: var(--lora-surface);
border-color: var(--lora-border);
}
.trained-word-text {
color: var(--lora-accent);
font-size: 0.9em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 4px;
max-width: 100px;
}
.trained-word-meta {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.trained-word-freq {
color: var (--text-color);
color: var(--text-color);
font-size: 0.75em;
background: rgba(0, 0, 0, 0.05);
border-radius: 10px;
@@ -343,23 +89,6 @@
background: rgba(255, 255, 255, 0.05);
}
.added-indicator {
color: var(--lora-accent);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75em;
}
.no-trained-words {
padding: 16px 12px;
text-align: center;
color: var(--text-color);
opacity: 0.7;
font-style: italic;
font-size: 0.9em;
}
/* Class tokens styling */
.class-tokens-container {
padding: 10px;
@@ -380,10 +109,4 @@
padding: 2px 5px;
border-radius: 8px;
white-space: nowrap;
}
.dropdown-separator {
height: 1px;
background: var(--lora-border);
margin: 5px 10px;
}

View File

@@ -0,0 +1,321 @@
/* Common Metadata Edit UI Components */
/* Used by both tag editing and trigger words editing interfaces */
/* Edit Button */
.metadata-edit-btn {
background: transparent;
border: none;
color: var(--text-color);
opacity: 0.5;
cursor: pointer;
padding: 2px 5px;
border-radius: var(--border-radius-xs);
transition: all 0.2s ease;
}
.metadata-edit-btn:hover {
opacity: 0.8;
background: rgba(0, 0, 0, 0.05);
}
[data-theme="dark"] .metadata-edit-btn:hover {
background: rgba(255, 255, 255, 0.05);
}
/* Edit mode active state */
.edit-mode .metadata-edit-btn {
opacity: 0.8;
color: var(--lora-accent);
}
/* Edit Container */
.metadata-edit-container {
padding: var(--space-2);
background: rgba(0, 0, 0, 0.03);
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: var(--border-radius-sm);
margin-top: var(--space-2);
width: 100%;
box-sizing: border-box;
position: relative;
display: block;
}
[data-theme="dark"] .metadata-edit-container {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--lora-border);
}
/* Edit Header */
.metadata-edit-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid var(--lora-border);
width: 100%;
}
/* Style for the edit button when positioned in the header */
.metadata-header-btn {
display: inline-flex !important;
opacity: 0.8 !important;
color: var(--lora-accent) !important;
margin-left: auto;
}
/* Edit Content */
.metadata-edit-content {
margin-bottom: var(--space-1);
width: 100%;
display: block;
}
/* Items Container */
.metadata-items {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: flex-start;
margin-bottom: var(--space-2);
width: 100%;
}
/* Individual Item */
.metadata-item {
display: inline-flex;
align-items: center;
background: var(--bg-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius-xs);
padding: 4px 8px;
position: relative;
}
.metadata-item-content {
color: var(--lora-accent) !important;
font-size: 0.85em;
line-height: 1.4;
word-break: break-word;
}
/* Delete Button */
.metadata-delete-btn {
position: absolute;
top: -5px;
right: -5px;
width: 16px;
height: 16px;
background: var(--lora-error);
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 9px;
transition: transform 0.2s ease;
}
.metadata-delete-btn:hover {
transform: scale(1.1);
}
/* Edit Controls */
.metadata-edit-controls {
display: flex;
justify-content: flex-end;
gap: var(--space-2);
margin-top: var(--space-2);
margin-bottom: var(--space-2);
}
.metadata-edit-controls button {
padding: 3px 8px;
border-radius: var(--border-radius-xs);
border: 1px solid var(--border-color);
background: var(--bg-color);
color: var(--text-color);
font-size: 0.85em;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.2s ease;
}
.metadata-edit-controls button:hover {
background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1);
border-color: var(--lora-accent);
}
.metadata-save-btn,
.save-tags-btn {
background: var(--lora-accent) !important;
color: white !important;
border-color: var(--lora-accent) !important;
}
.metadata-save-btn:hover,
.save-tags-btn:hover {
opacity: 0.9;
}
/* Add Form */
.metadata-add-form {
display: flex;
gap: var(--space-1);
position: relative;
width: 100%;
}
.metadata-input {
flex: 1;
padding: 4px 8px;
border-radius: var(--border-radius-xs);
border: 1px solid var(--border-color);
background: var(--bg-color);
color: var(--text-color);
font-size: 0.9em;
}
.metadata-input:focus {
border-color: var(--lora-accent);
outline: none;
}
/* Suggestions Dropdown */
.metadata-suggestions-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: var(--bg-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius-sm);
margin-top: 4px;
z-index: 100;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
overflow: hidden;
display: flex;
flex-direction: column;
}
.metadata-suggestions-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
}
.metadata-suggestions-header span {
font-size: 0.9em;
font-weight: 500;
color: var(--text-color);
}
.metadata-suggestions-header small {
font-size: 0.8em;
opacity: 0.7;
}
.metadata-suggestions-container {
max-height: 200px;
overflow-y: auto;
padding: 10px;
display: flex;
flex-wrap: wrap;
gap: 8px;
align-content: flex-start;
}
.metadata-suggestion-item {
display: inline-flex;
align-items: center;
justify-content: space-between;
padding: 5px 10px;
cursor: pointer;
transition: all 0.2s ease;
border-radius: var(--border-radius-xs);
background: var(--lora-surface);
border: 1px solid var(--lora-border);
max-width: 150px;
}
.metadata-suggestion-item:hover {
background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1);
border-color: var(--lora-accent);
}
.metadata-suggestion-item.already-added {
opacity: 0.7;
cursor: default;
}
.metadata-suggestion-item.already-added:hover {
background: var(--lora-surface);
border-color: var(--lora-border);
}
.metadata-suggestion-text {
color: var(--lora-accent) !important;
font-size: 0.9em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 4px;
max-width: 100px;
}
.metadata-suggestion-meta {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.added-indicator {
color: var(--lora-accent);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75em;
}
/* No suggestions message */
.no-suggestions {
padding: 16px 12px;
text-align: center;
color: var(--text-color);
opacity: 0.7;
font-style: italic;
font-size: 0.9em;
}
/* Loading indicator */
.metadata-loading {
display: flex;
align-items: center;
justify-content: center;
margin: var(--space-1) 0;
color: var(--text-color);
opacity: 0.7;
font-size: 0.9em;
gap: 8px;
}
.metadata-loading i {
color: var(--lora-accent);
}
/* Dropdown separator */
.dropdown-separator {
height: 1px;
background: var(--lora-border);
margin: 5px 10px;
}

View File

@@ -19,6 +19,7 @@
@import 'components/lora-modal/preset-tags.css';
@import 'components/lora-modal/showcase.css';
@import 'components/lora-modal/triggerwords.css';
@import 'components/shared/edit-metadata.css';
@import 'components/support-modal.css';
@import 'components/search-filter.css';
@import 'components/bulk.css';

View File

@@ -39,7 +39,7 @@ export function setupTagEditMode() {
// Toggle edit mode UI elements
const compactTagsDisplay = tagsSection.querySelector('.model-tags-compact');
const tagsEditContainer = tagsSection.querySelector('.tags-edit-container');
const tagsEditContainer = tagsSection.querySelector('.metadata-edit-container');
if (isEditMode) {
// Enter edit mode
@@ -57,11 +57,11 @@ export function setupTagEditMode() {
// If edit container doesn't exist yet, create it
if (!tagsEditContainer) {
const editContainer = document.createElement('div');
editContainer.className = 'tags-edit-container';
editContainer.className = 'metadata-edit-container';
// Move the edit button inside the container header for better visibility
const editBtnClone = editBtn.cloneNode(true);
editBtnClone.classList.add('edit-header-btn');
editBtnClone.classList.add('metadata-header-btn');
// Create edit UI with edit button in the header
editContainer.innerHTML = createTagEditUI(originalTags, editBtnClone.outerHTML);
@@ -71,7 +71,7 @@ export function setupTagEditMode() {
setupTagInput();
// Create and add preset suggestions dropdown
const tagForm = editContainer.querySelector('.add-tag-form');
const tagForm = editContainer.querySelector('.metadata-add-form');
const suggestionsDropdown = createSuggestionsDropdown(originalTags);
tagForm.appendChild(suggestionsDropdown);
@@ -79,7 +79,7 @@ export function setupTagEditMode() {
setupDeleteButtons();
// Transfer click event from original button to the cloned one
const newEditBtn = editContainer.querySelector('.edit-header-btn');
const newEditBtn = editContainer.querySelector('.metadata-header-btn');
if (newEditBtn) {
newEditBtn.addEventListener('click', function() {
editBtn.click();
@@ -144,28 +144,28 @@ export function setupTagEditMode() {
*/
function createTagEditUI(currentTags, editBtnHTML = '') {
return `
<div class="tags-edit-content">
<div class="tags-edit-header">
<div class="metadata-edit-content">
<div class="metadata-edit-header">
<label>Edit Tags</label>
${editBtnHTML}
</div>
<div class="tags-tags">
<div class="metadata-items">
${currentTags.map(tag => `
<div class="tag-edit-tag" data-tag="${tag}">
<span class="tag-edit-content">${tag}</span>
<button class="delete-tag-btn">
<div class="metadata-item" data-tag="${tag}">
<span class="metadata-item-content">${tag}</span>
<button class="metadata-delete-btn">
<i class="fas fa-times"></i>
</button>
</div>
`).join('')}
</div>
<div class="tags-edit-controls">
<div class="metadata-edit-controls">
<button class="save-tags-btn" title="Save changes">
<i class="fas fa-save"></i> Save
</button>
</div>
<div class="add-tag-form">
<input type="text" class="new-tag-input" placeholder="Type to add or click suggestions below">
<div class="metadata-add-form">
<input type="text" class="metadata-input" placeholder="Type to add or click suggestions below">
</div>
</div>
`;
@@ -178,11 +178,11 @@ function createTagEditUI(currentTags, editBtnHTML = '') {
*/
function createSuggestionsDropdown(existingTags = []) {
const dropdown = document.createElement('div');
dropdown.className = 'tag-suggestions-dropdown';
dropdown.className = 'metadata-suggestions-dropdown';
// Create header
const header = document.createElement('div');
header.className = 'tag-suggestions-header';
header.className = 'metadata-suggestions-header';
header.innerHTML = `
<span>Suggested Tags</span>
<small>Click to add</small>
@@ -191,17 +191,17 @@ function createSuggestionsDropdown(existingTags = []) {
// Create tag container
const container = document.createElement('div');
container.className = 'tag-suggestions-container';
container.className = 'metadata-suggestions-container';
// Add each preset tag as a suggestion
PRESET_TAGS.forEach(tag => {
const isAdded = existingTags.includes(tag);
const item = document.createElement('div');
item.className = `tag-suggestion-item ${isAdded ? 'already-added' : ''}`;
item.className = `metadata-suggestion-item ${isAdded ? 'already-added' : ''}`;
item.title = tag;
item.innerHTML = `
<span class="tag-suggestion-text">${tag}</span>
<span class="metadata-suggestion-text">${tag}</span>
${isAdded ? '<span class="added-indicator"><i class="fas fa-check"></i></span>' : ''}
`;
@@ -210,7 +210,7 @@ function createSuggestionsDropdown(existingTags = []) {
addNewTag(tag);
// Also populate the input field for potential editing
const input = document.querySelector('.new-tag-input');
const input = document.querySelector('.metadata-input');
if (input) input.value = tag;
// Focus on the input
@@ -232,7 +232,7 @@ function createSuggestionsDropdown(existingTags = []) {
* Set up tag input behavior
*/
function setupTagInput() {
const tagInput = document.querySelector('.new-tag-input');
const tagInput = document.querySelector('.metadata-input');
if (tagInput) {
tagInput.addEventListener('keydown', function(e) {
@@ -249,10 +249,10 @@ function setupTagInput() {
* Set up delete buttons for tags
*/
function setupDeleteButtons() {
document.querySelectorAll('.delete-tag-btn').forEach(btn => {
document.querySelectorAll('.metadata-delete-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
e.stopPropagation();
const tag = this.closest('.tag-edit-tag');
const tag = this.closest('.metadata-item');
tag.remove();
// Update status of items in the suggestion dropdown
@@ -269,7 +269,7 @@ function addNewTag(tag) {
tag = tag.trim().toLowerCase();
if (!tag) return;
const tagsContainer = document.querySelector('.tags-tags');
const tagsContainer = document.querySelector('.metadata-items');
if (!tagsContainer) return;
// Validation: Check length
@@ -279,7 +279,7 @@ function addNewTag(tag) {
}
// Validation: Check total number
const currentTags = tagsContainer.querySelectorAll('.tag-edit-tag');
const currentTags = tagsContainer.querySelectorAll('.metadata-item');
if (currentTags.length >= 30) {
showToast('Maximum 30 tags allowed', 'error');
return;
@@ -294,17 +294,17 @@ function addNewTag(tag) {
// Create new tag
const newTag = document.createElement('div');
newTag.className = 'tag-edit-tag';
newTag.className = 'metadata-item';
newTag.dataset.tag = tag;
newTag.innerHTML = `
<span class="tag-edit-content">${tag}</span>
<button class="delete-tag-btn">
<span class="metadata-item-content">${tag}</span>
<button class="metadata-delete-btn">
<i class="fas fa-times"></i>
</button>
`;
// Add event listener to delete button
const deleteBtn = newTag.querySelector('.delete-tag-btn');
const deleteBtn = newTag.querySelector('.metadata-delete-btn');
deleteBtn.addEventListener('click', function(e) {
e.stopPropagation();
newTag.remove();
@@ -323,16 +323,16 @@ function addNewTag(tag) {
* Update status of items in the suggestions dropdown
*/
function updateSuggestionsDropdown() {
const dropdown = document.querySelector('.tag-suggestions-dropdown');
const dropdown = document.querySelector('.metadata-suggestions-dropdown');
if (!dropdown) return;
// Get all current tags
const currentTags = document.querySelectorAll('.tag-edit-tag');
const currentTags = document.querySelectorAll('.metadata-item');
const existingTags = Array.from(currentTags).map(tag => tag.dataset.tag);
// Update status of each item in dropdown
dropdown.querySelectorAll('.tag-suggestion-item').forEach(item => {
const tagText = item.querySelector('.tag-suggestion-text').textContent;
dropdown.querySelectorAll('.metadata-suggestion-item').forEach(item => {
const tagText = item.querySelector('.metadata-suggestion-text').textContent;
const isAdded = existingTags.includes(tagText);
if (isAdded) {
@@ -360,11 +360,11 @@ function updateSuggestionsDropdown() {
// Restore click event if not already set
if (!item.onclick) {
item.onclick = () => {
const tag = item.querySelector('.tag-suggestion-text').textContent;
const tag = item.querySelector('.metadata-suggestion-text').textContent;
addNewTag(tag);
// Also populate the input field
const input = document.querySelector('.new-tag-input');
const input = document.querySelector('.metadata-input');
if (input) input.value = tag;
// Focus the input
@@ -393,7 +393,7 @@ async function saveTags() {
if (!editBtn) return;
const filePath = editBtn.dataset.filePath;
const tagElements = document.querySelectorAll('.tag-edit-tag');
const tagElements = document.querySelectorAll('.metadata-item');
const tags = Array.from(tagElements).map(tag => tag.dataset.tag);
// Get original tags to compare

View File

@@ -39,17 +39,17 @@ async function fetchTrainedWords(filePath) {
*/
function createSuggestionDropdown(trainedWords, classTokens, existingWords = []) {
const dropdown = document.createElement('div');
dropdown.className = 'trained-words-dropdown';
dropdown.className = 'metadata-suggestions-dropdown';
// Create header
const header = document.createElement('div');
header.className = 'trained-words-header';
header.className = 'metadata-suggestions-header';
// No suggestions case
if ((!trainedWords || trainedWords.length === 0) && !classTokens) {
header.innerHTML = '<span>No suggestions available</span>';
dropdown.appendChild(header);
dropdown.innerHTML += '<div class="no-trained-words">No trained words or class tokens found in this model. You can manually enter trigger words.</div>';
dropdown.innerHTML += '<div class="no-suggestions">No trained words or class tokens found in this model. You can manually enter trigger words.</div>';
return dropdown;
}
@@ -62,7 +62,7 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
if (classTokens) {
// Add class tokens header
const classTokensHeader = document.createElement('div');
classTokensHeader.className = 'trained-words-header';
classTokensHeader.className = 'metadata-suggestions-header';
classTokensHeader.innerHTML = `
<span>Class Token</span>
<small>Add to your prompt for best results</small>
@@ -75,11 +75,11 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
// Create a special item for the class token
const tokenItem = document.createElement('div');
tokenItem.className = `trained-word-item class-token-item ${existingWords.includes(classTokens) ? 'already-added' : ''}`;
tokenItem.className = `metadata-suggestion-item class-token-item ${existingWords.includes(classTokens) ? 'already-added' : ''}`;
tokenItem.title = `Class token: ${classTokens}`;
tokenItem.innerHTML = `
<span class="trained-word-text">${classTokens}</span>
<div class="trained-word-meta">
<span class="metadata-suggestion-text">${classTokens}</span>
<div class="metadata-suggestion-meta">
<span class="token-badge">Class Token</span>
${existingWords.includes(classTokens) ?
'<span class="added-indicator"><i class="fas fa-check"></i></span>' : ''}
@@ -93,7 +93,7 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
addNewTriggerWord(classTokens);
// Also populate the input field for potential editing
const input = document.querySelector('.new-trigger-word-input');
const input = document.querySelector('.metadata-input');
if (input) input.value = classTokens;
// Focus on the input
@@ -125,18 +125,18 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
// Create tag container for trained words
const container = document.createElement('div');
container.className = 'trained-words-container';
container.className = 'metadata-suggestions-container';
// Add each trained word as a tag
trainedWords.forEach(([word, frequency]) => {
const isAdded = existingWords.includes(word);
const item = document.createElement('div');
item.className = `trained-word-item ${isAdded ? 'already-added' : ''}`;
item.className = `metadata-suggestion-item ${isAdded ? 'already-added' : ''}`;
item.title = word; // Show full word on hover if truncated
item.innerHTML = `
<span class="trained-word-text">${word}</span>
<div class="trained-word-meta">
<span class="metadata-suggestion-text">${word}</span>
<div class="metadata-suggestion-meta">
<span class="trained-word-freq">${frequency}</span>
${isAdded ? '<span class="added-indicator"><i class="fas fa-check"></i></span>' : ''}
</div>
@@ -148,7 +148,7 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
addNewTriggerWord(word);
// Also populate the input field for potential editing
const input = document.querySelector('.new-trigger-word-input');
const input = document.querySelector('.metadata-input');
if (input) input.value = word;
// Focus on the input
@@ -165,7 +165,7 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
dropdown.appendChild(container);
} else if (!classTokens) {
// If we have neither class tokens nor trained words
dropdown.innerHTML += '<div class="no-trained-words">No word suggestions found in this model. You can manually enter trigger words.</div>';
dropdown.innerHTML += '<div class="no-suggestions">No word suggestions found in this model. You can manually enter trigger words.</div>';
}
return dropdown;
@@ -182,7 +182,7 @@ export function renderTriggerWords(words, filePath) {
<div class="info-item full-width trigger-words">
<div class="trigger-words-header">
<label>Trigger Words</label>
<button class="edit-trigger-words-btn" data-file-path="${filePath}" title="Edit trigger words">
<button class="edit-trigger-words-btn metadata-edit-btn" data-file-path="${filePath}" title="Edit trigger words">
<i class="fas fa-pencil-alt"></i>
</button>
</div>
@@ -190,13 +190,13 @@ export function renderTriggerWords(words, filePath) {
<span class="no-trigger-words">No trigger word needed</span>
<div class="trigger-words-tags" style="display:none;"></div>
</div>
<div class="trigger-words-edit-controls" style="display:none;">
<button class="save-trigger-words-btn" title="Save changes">
<div class="metadata-edit-controls" style="display:none;">
<button class="metadata-save-btn" title="Save changes">
<i class="fas fa-save"></i> Save
</button>
</div>
<div class="add-trigger-word-form" style="display:none;">
<input type="text" class="new-trigger-word-input" placeholder="Type to add or click suggestions below">
<div class="metadata-add-form" style="display:none;">
<input type="text" class="metadata-input" placeholder="Type to add or click suggestions below">
</div>
</div>
`;
@@ -205,7 +205,7 @@ export function renderTriggerWords(words, filePath) {
<div class="info-item full-width trigger-words">
<div class="trigger-words-header">
<label>Trigger Words</label>
<button class="edit-trigger-words-btn" data-file-path="${filePath}" title="Edit trigger words">
<button class="edit-trigger-words-btn metadata-edit-btn" data-file-path="${filePath}" title="Edit trigger words">
<i class="fas fa-pencil-alt"></i>
</button>
</div>
@@ -217,20 +217,20 @@ export function renderTriggerWords(words, filePath) {
<span class="trigger-word-copy">
<i class="fas fa-copy"></i>
</span>
<button class="delete-trigger-word-btn" style="display:none;" onclick="event.stopPropagation();">
<button class="metadata-delete-btn" style="display:none;" onclick="event.stopPropagation();">
<i class="fas fa-times"></i>
</button>
</div>
`).join('')}
</div>
</div>
<div class="trigger-words-edit-controls" style="display:none;">
<button class="save-trigger-words-btn" title="Save changes">
<div class="metadata-edit-controls" style="display:none;">
<button class="metadata-save-btn" title="Save changes">
<i class="fas fa-save"></i> Save
</button>
</div>
<div class="add-trigger-word-form" style="display:none;">
<input type="text" class="new-trigger-word-input" placeholder="Type to add or click suggestions below">
<div class="metadata-add-form" style="display:none;">
<input type="text" class="metadata-input" placeholder="Type to add or click suggestions below">
</div>
</div>
`;
@@ -257,8 +257,8 @@ export function setupTriggerWordsEditMode() {
// Toggle edit mode UI elements
const triggerWordTags = triggerWordsSection.querySelectorAll('.trigger-word-tag');
const editControls = triggerWordsSection.querySelector('.trigger-words-edit-controls');
const addForm = triggerWordsSection.querySelector('.add-trigger-word-form');
const editControls = triggerWordsSection.querySelector('.metadata-edit-controls');
const addForm = triggerWordsSection.querySelector('.metadata-add-form');
const noTriggerWords = triggerWordsSection.querySelector('.no-trigger-words');
const tagsContainer = triggerWordsSection.querySelector('.trigger-words-tags');
@@ -284,7 +284,7 @@ export function setupTriggerWordsEditMode() {
triggerWordTags.forEach(tag => {
tag.onclick = null;
const copyIcon = tag.querySelector('.trigger-word-copy');
const deleteBtn = tag.querySelector('.delete-trigger-word-btn');
const deleteBtn = tag.querySelector('.metadata-delete-btn');
if (copyIcon) copyIcon.style.display = 'none';
if (deleteBtn) {
@@ -300,7 +300,7 @@ export function setupTriggerWordsEditMode() {
// Load trained words and display dropdown when entering edit mode
// Add loading indicator
const loadingIndicator = document.createElement('div');
loadingIndicator.className = 'trained-words-loading';
loadingIndicator.className = 'metadata-loading';
loadingIndicator.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Loading suggestions...';
addForm.appendChild(loadingIndicator);
@@ -354,13 +354,13 @@ export function setupTriggerWordsEditMode() {
}
// Remove dropdown if present
const dropdown = document.querySelector('.trained-words-dropdown');
const dropdown = document.querySelector('.metadata-suggestions-dropdown');
if (dropdown) dropdown.remove();
}
});
// Set up input for adding trigger words
const triggerWordInput = document.querySelector('.new-trigger-word-input');
const triggerWordInput = document.querySelector('.metadata-input');
if (triggerWordInput) {
// Add keydown event to input
@@ -374,13 +374,13 @@ export function setupTriggerWordsEditMode() {
}
// Set up save button
const saveBtn = document.querySelector('.save-trigger-words-btn');
const saveBtn = document.querySelector('.metadata-save-btn');
if (saveBtn) {
saveBtn.addEventListener('click', saveTriggerWords);
}
// Set up delete buttons
document.querySelectorAll('.delete-trigger-word-btn').forEach(btn => {
document.querySelectorAll('.metadata-delete-btn').forEach(btn => {
// Remove any existing listeners to avoid duplication
btn.removeEventListener('click', deleteTriggerWord);
btn.addEventListener('click', deleteTriggerWord);
@@ -410,7 +410,7 @@ function resetTriggerWordsUIState(section) {
triggerWordTags.forEach(tag => {
const word = tag.dataset.word;
const copyIcon = tag.querySelector('.trigger-word-copy');
const deleteBtn = tag.querySelector('.delete-trigger-word-btn');
const deleteBtn = tag.querySelector('.metadata-delete-btn');
// Restore click-to-copy functionality
tag.onclick = () => copyTriggerWord(word);
@@ -456,7 +456,7 @@ function restoreOriginalTriggerWords(section, originalWords) {
<span class="trigger-word-copy">
<i class="fas fa-copy"></i>
</span>
<button class="delete-trigger-word-btn" style="display:none;" onclick="event.stopPropagation();">
<button class="metadata-delete-btn" style="display:none;" onclick="event.stopPropagation();">
<i class="fas fa-times"></i>
</button>
`;
@@ -525,13 +525,13 @@ function addNewTriggerWord(word) {
<span class="trigger-word-copy" style="display:none;">
<i class="fas fa-copy"></i>
</span>
<button class="delete-trigger-word-btn" onclick="event.stopPropagation();">
<button class="metadata-delete-btn" onclick="event.stopPropagation();">
<i class="fas fa-times"></i>
</button>
`;
// Add event listener to delete button
const deleteBtn = newTag.querySelector('.delete-trigger-word-btn');
const deleteBtn = newTag.querySelector('.metadata-delete-btn');
deleteBtn.addEventListener('click', deleteTriggerWord);
tagsContainer.appendChild(newTag);
@@ -544,7 +544,7 @@ function addNewTriggerWord(word) {
* Update status of items in the trained words dropdown
*/
function updateTrainedWordsDropdown() {
const dropdown = document.querySelector('.trained-words-dropdown');
const dropdown = document.querySelector('.metadata-suggestions-dropdown');
if (!dropdown) return;
// Get all current trigger words
@@ -552,8 +552,8 @@ function updateTrainedWordsDropdown() {
const existingWords = Array.from(currentTags).map(tag => tag.dataset.word);
// Update status of each item in dropdown
dropdown.querySelectorAll('.trained-word-item').forEach(item => {
const wordText = item.querySelector('.trained-word-text').textContent;
dropdown.querySelectorAll('.metadata-suggestion-item').forEach(item => {
const wordText = item.querySelector('.metadata-suggestion-text').textContent;
const isAdded = existingWords.includes(wordText);
if (isAdded) {
@@ -562,7 +562,7 @@ function updateTrainedWordsDropdown() {
// Add indicator if it doesn't exist
let indicator = item.querySelector('.added-indicator');
if (!indicator) {
const meta = item.querySelector('.trained-word-meta');
const meta = item.querySelector('.metadata-suggestion-meta');
indicator = document.createElement('span');
indicator.className = 'added-indicator';
indicator.innerHTML = '<i class="fas fa-check"></i>';
@@ -582,11 +582,11 @@ function updateTrainedWordsDropdown() {
// Restore click event if not already set
if (!item.onclick) {
item.onclick = () => {
const word = item.querySelector('.trained-word-text').textContent;
const word = item.querySelector('.metadata-suggestion-text').textContent;
addNewTriggerWord(word);
// Also populate the input field
const input = document.querySelector('.new-trigger-word-input');
const input = document.querySelector('.metadata-input');
if (input) input.value = word;
// Focus the input