mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
Refactor model name editing functionality in LoraModal; update styles for improved user interaction and accessibility
This commit is contained in:
@@ -593,56 +593,60 @@
|
|||||||
|
|
||||||
/* Model name field styles - complete replacement */
|
/* Model name field styles - complete replacement */
|
||||||
.model-name-field {
|
.model-name-field {
|
||||||
display: flex;
|
display: none;
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2);
|
|
||||||
width: calc(100% - 40px); /* Reduce width to avoid overlap with close button */
|
|
||||||
position: relative; /* Add position relative for absolute positioning of save button */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-name-field h2 {
|
/* New Model Name Header Styles */
|
||||||
|
.model-name-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: calc(100% - 40px); /* Avoid overlap with close button */
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-name-content {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: var(--space-1);
|
padding: var(--space-1);
|
||||||
border-radius: var(--border-radius-xs);
|
border-radius: var(--border-radius-xs);
|
||||||
transition: background-color 0.2s;
|
font-size: 1.5em !important;
|
||||||
flex: 1;
|
font-weight: 600;
|
||||||
font-size: 1.5em !important; /* Increased and forced size */
|
|
||||||
font-weight: 600; /* Make it bolder */
|
|
||||||
min-height: 1.5em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
color: var(--text-color); /* Ensure correct color */
|
color: var(--text-color);
|
||||||
}
|
border: 1px solid transparent;
|
||||||
|
|
||||||
.model-name-field h2:hover {
|
|
||||||
background: oklch(var(--lora-accent) / 0.1);
|
|
||||||
cursor: text;
|
|
||||||
}
|
|
||||||
|
|
||||||
.model-name-field h2:focus {
|
|
||||||
outline: none;
|
outline: none;
|
||||||
background: var(--bg-color);
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-name-content:focus {
|
||||||
border: 1px solid var(--lora-accent);
|
border: 1px solid var(--lora-accent);
|
||||||
|
background: var(--bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-name-field .save-btn {
|
.edit-model-name-btn {
|
||||||
position: absolute;
|
background: transparent;
|
||||||
right: 10px; /* Position closer to the end of the field */
|
border: none;
|
||||||
top: 50%;
|
color: var(--text-color);
|
||||||
transform: translateY(-50%);
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s;
|
cursor: pointer;
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
margin-left: var(--space-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-name-field:hover .save-btn,
|
.edit-model-name-btn.visible,
|
||||||
.model-name-field h2:focus ~ .save-btn {
|
.model-name-header:hover .edit-model-name-btn {
|
||||||
opacity: 1;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure close button is accessible */
|
.edit-model-name-btn:hover {
|
||||||
.modal-content .close {
|
opacity: 0.8 !important;
|
||||||
z-index: 10; /* Ensure close button is above other elements */
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .edit-model-name-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tab System Styling */
|
/* Tab System Styling */
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ export function showLoraModal(lora) {
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<button class="close" onclick="modalManager.closeModal('loraModal')">×</button>
|
<button class="close" onclick="modalManager.closeModal('loraModal')">×</button>
|
||||||
<header class="modal-header">
|
<header class="modal-header">
|
||||||
<div class="editable-field model-name-field">
|
<div class="model-name-header">
|
||||||
<h2 class="model-name-content" contenteditable="true" spellcheck="false">${lora.model_name}</h2>
|
<h2 class="model-name-content" contenteditable="true" spellcheck="false">${lora.model_name}</h2>
|
||||||
<button class="save-btn" onclick="saveModelName('${lora.file_path}')">
|
<button class="edit-model-name-btn" title="Edit model name">
|
||||||
<i class="fas fa-save"></i>
|
<i class="fas fa-pencil-alt"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
${renderCompactTags(lora.tags || [])}
|
${renderCompactTags(lora.tags || [])}
|
||||||
@@ -122,6 +122,7 @@ export function showLoraModal(lora) {
|
|||||||
setupTabSwitching();
|
setupTabSwitching();
|
||||||
setupTagTooltip();
|
setupTagTooltip();
|
||||||
setupTriggerWordsEditMode();
|
setupTriggerWordsEditMode();
|
||||||
|
setupModelNameEditing();
|
||||||
|
|
||||||
// If we have a model ID but no description, fetch it
|
// If we have a model ID but no description, fetch it
|
||||||
if (lora.civitai?.modelId && !lora.modelDescription) {
|
if (lora.civitai?.modelId && !lora.modelDescription) {
|
||||||
@@ -405,61 +406,18 @@ function setupEditableFields() {
|
|||||||
|
|
||||||
editableFields.forEach(field => {
|
editableFields.forEach(field => {
|
||||||
field.addEventListener('focus', function() {
|
field.addEventListener('focus', function() {
|
||||||
if (this.textContent === 'Add your notes here...' ||
|
if (this.textContent === 'Add your notes here...') {
|
||||||
this.textContent === 'Save usage tips here..') {
|
|
||||||
this.textContent = '';
|
this.textContent = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
field.addEventListener('blur', function() {
|
field.addEventListener('blur', function() {
|
||||||
if (this.textContent.trim() === '') {
|
if (this.textContent.trim() === '') {
|
||||||
if (this.classList.contains('model-name-content')) {
|
if (this.classList.contains('notes-content')) {
|
||||||
// Restore original model name if empty
|
|
||||||
const filePath = document.querySelector('.modal-content')
|
|
||||||
.querySelector('.file-path').textContent +
|
|
||||||
document.querySelector('.modal-content')
|
|
||||||
.querySelector('#file-name').textContent + '.safetensors';
|
|
||||||
const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
|
|
||||||
if (loraCard) {
|
|
||||||
this.textContent = loraCard.dataset.model_name;
|
|
||||||
}
|
|
||||||
} else if (this.classList.contains('usage-tips-content')) {
|
|
||||||
this.textContent = 'Save usage tips here..';
|
|
||||||
} else {
|
|
||||||
this.textContent = 'Add your notes here...';
|
this.textContent = 'Add your notes here...';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add input validation for model name
|
|
||||||
if (field.classList.contains('model-name-content')) {
|
|
||||||
field.addEventListener('input', function() {
|
|
||||||
// Limit model name length
|
|
||||||
if (this.textContent.length > 100) {
|
|
||||||
this.textContent = this.textContent.substring(0, 100);
|
|
||||||
// Place cursor at the end
|
|
||||||
const range = document.createRange();
|
|
||||||
const sel = window.getSelection();
|
|
||||||
range.setStart(this.childNodes[0], 100);
|
|
||||||
range.collapse(true);
|
|
||||||
sel.removeAllRanges();
|
|
||||||
sel.addRange(range);
|
|
||||||
|
|
||||||
showToast('Model name is limited to 100 characters', 'warning');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
field.addEventListener('keydown', function(e) {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
e.preventDefault();
|
|
||||||
const filePath = document.querySelector('.modal-content')
|
|
||||||
.querySelector('.file-path').textContent +
|
|
||||||
document.querySelector('.modal-content')
|
|
||||||
.querySelector('#file-name').textContent + '.safetensors';
|
|
||||||
saveModelName(filePath);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const presetSelector = document.getElementById('preset-selector');
|
const presetSelector = document.getElementById('preset-selector');
|
||||||
@@ -473,7 +431,7 @@ function setupEditableFields() {
|
|||||||
presetValue.style.display = 'inline-block';
|
presetValue.style.display = 'inline-block';
|
||||||
presetValue.min = selected.includes('strength') ? 0 : 1;
|
presetValue.min = selected.includes('strength') ? 0 : 1;
|
||||||
presetValue.max = selected.includes('strength') ? 1 : 12;
|
presetValue.max = selected.includes('strength') ? 1 : 12;
|
||||||
presetValue.step = selected.includes('strength') ? 0.01 : 1;
|
presetValue.step = selected.includes('strength') ? 0.5 : 1;
|
||||||
if (selected === 'clip_skip') {
|
if (selected === 'clip_skip') {
|
||||||
presetValue.type = 'number';
|
presetValue.type = 'number';
|
||||||
presetValue.step = 1;
|
presetValue.step = 1;
|
||||||
@@ -795,7 +753,7 @@ export function scrollToTop(button) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parsePresets(usageTips) {
|
function parsePresets(usageTips) {
|
||||||
if (!usageTips || usageTips === 'Save usage tips here..') return {};
|
if (!usageTips) return {};
|
||||||
try {
|
try {
|
||||||
return JSON.parse(usageTips);
|
return JSON.parse(usageTips);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -852,17 +810,6 @@ function formatFileSize(bytes) {
|
|||||||
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tag copy functionality
|
|
||||||
window.copyTag = async function(tag) {
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(tag);
|
|
||||||
showToast('Tag copied to clipboard', 'success');
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Copy failed:', err);
|
|
||||||
showToast('Copy failed', 'error');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// New function to render compact tags with tooltip
|
// New function to render compact tags with tooltip
|
||||||
function renderCompactTags(tags) {
|
function renderCompactTags(tags) {
|
||||||
if (!tags || tags.length === 0) return '';
|
if (!tags || tags.length === 0) return '';
|
||||||
@@ -1162,3 +1109,89 @@ window.copyTriggerWord = async function(word) {
|
|||||||
showToast('Copy failed', 'error');
|
showToast('Copy failed', 'error');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// New function to handle model name editing
|
||||||
|
function setupModelNameEditing() {
|
||||||
|
const modelNameContent = document.querySelector('.model-name-content');
|
||||||
|
const editBtn = document.querySelector('.edit-model-name-btn');
|
||||||
|
|
||||||
|
if (!modelNameContent || !editBtn) return;
|
||||||
|
|
||||||
|
// Show edit button on hover
|
||||||
|
const modelNameHeader = document.querySelector('.model-name-header');
|
||||||
|
modelNameHeader.addEventListener('mouseenter', () => {
|
||||||
|
editBtn.classList.add('visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
modelNameHeader.addEventListener('mouseleave', () => {
|
||||||
|
if (!modelNameContent.getAttribute('data-editing')) {
|
||||||
|
editBtn.classList.remove('visible');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle edit button click
|
||||||
|
editBtn.addEventListener('click', () => {
|
||||||
|
modelNameContent.setAttribute('data-editing', 'true');
|
||||||
|
modelNameContent.focus();
|
||||||
|
|
||||||
|
// Place cursor at the end
|
||||||
|
const range = document.createRange();
|
||||||
|
const sel = window.getSelection();
|
||||||
|
if (modelNameContent.childNodes.length > 0) {
|
||||||
|
range.setStart(modelNameContent.childNodes[0], modelNameContent.textContent.length);
|
||||||
|
range.collapse(true);
|
||||||
|
sel.removeAllRanges();
|
||||||
|
sel.addRange(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
editBtn.classList.add('visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle focus out
|
||||||
|
modelNameContent.addEventListener('blur', function() {
|
||||||
|
this.removeAttribute('data-editing');
|
||||||
|
editBtn.classList.remove('visible');
|
||||||
|
|
||||||
|
if (this.textContent.trim() === '') {
|
||||||
|
// Restore original model name if empty
|
||||||
|
const filePath = document.querySelector('.modal-content')
|
||||||
|
.querySelector('.file-path').textContent +
|
||||||
|
document.querySelector('.modal-content')
|
||||||
|
.querySelector('#file-name').textContent + '.safetensors';
|
||||||
|
const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
|
||||||
|
if (loraCard) {
|
||||||
|
this.textContent = loraCard.dataset.model_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle enter key
|
||||||
|
modelNameContent.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
const filePath = document.querySelector('.modal-content')
|
||||||
|
.querySelector('.file-path').textContent +
|
||||||
|
document.querySelector('.modal-content')
|
||||||
|
.querySelector('#file-name').textContent + '.safetensors';
|
||||||
|
saveModelName(filePath);
|
||||||
|
this.blur();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Limit model name length
|
||||||
|
modelNameContent.addEventListener('input', function() {
|
||||||
|
// Limit model name length
|
||||||
|
if (this.textContent.length > 100) {
|
||||||
|
this.textContent = this.textContent.substring(0, 100);
|
||||||
|
// Place cursor at the end
|
||||||
|
const range = document.createRange();
|
||||||
|
const sel = window.getSelection();
|
||||||
|
range.setStart(this.childNodes[0], 100);
|
||||||
|
range.collapse(true);
|
||||||
|
sel.removeAllRanges();
|
||||||
|
sel.addRange(range);
|
||||||
|
|
||||||
|
showToast('Model name is limited to 100 characters', 'warning');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user