Refactor model name editing functionality in LoraModal; update styles for improved user interaction and accessibility

This commit is contained in:
Will Miao
2025-03-13 22:11:51 +08:00
parent dd81c86540
commit d3e4534673
2 changed files with 134 additions and 97 deletions

View File

@@ -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 */

View File

@@ -10,10 +10,10 @@ export function showLoraModal(lora) {
<div class="modal-content"> <div class="modal-content">
<button class="close" onclick="modalManager.closeModal('loraModal')">&times;</button> <button class="close" onclick="modalManager.closeModal('loraModal')">&times;</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 '';
@@ -1161,4 +1108,90 @@ window.copyTriggerWord = async function(word) {
console.error('Copy failed:', err); console.error('Copy failed:', err);
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');
}
});
}