mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-24 14:42:11 -03:00
Enhance LoraCard with preset controls for usage tips and update modal styles
This commit is contained in:
@@ -594,6 +594,7 @@ body.modal-open {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-item.usage-tips,
|
||||||
.info-item.notes {
|
.info-item.notes {
|
||||||
grid-column: 1 / -1 !important; /* Make notes section full width */
|
grid-column: 1 / -1 !important; /* Make notes section full width */
|
||||||
}
|
}
|
||||||
@@ -843,4 +844,73 @@ body.modal-open {
|
|||||||
background: var(--lora-accent);
|
background: var(--lora-accent);
|
||||||
color: white;
|
color: white;
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update Preset Controls styles */
|
||||||
|
.preset-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-2);
|
||||||
|
margin-bottom: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-controls select,
|
||||||
|
.preset-controls input {
|
||||||
|
padding: var(--space-1);
|
||||||
|
background: var(--bg-color);
|
||||||
|
border: 1px solid var(--lora-border);
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-tag {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--lora-surface);
|
||||||
|
border: 1px solid var(--lora-border);
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
padding: calc(var(--space-1) * 0.5) var(--space-1);
|
||||||
|
gap: var(--space-1);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-tag span {
|
||||||
|
color: var(--lora-accent);
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-tag i {
|
||||||
|
color: var(--text-color);
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-tag:hover {
|
||||||
|
background: oklch(var(--lora-accent) / 0.1);
|
||||||
|
border-color: var(--lora-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preset-tag i:hover {
|
||||||
|
color: var(--lora-error);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-preset-btn {
|
||||||
|
padding: calc(var(--space-1) * 0.5) var(--space-2);
|
||||||
|
background: var(--lora-accent);
|
||||||
|
color: var(--lora-text);
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--border-radius-xs);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-preset-btn:hover {
|
||||||
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { showToast } from '../utils/uiHelpers.js';
|
import { showToast } from '../utils/uiHelpers.js';
|
||||||
import { modalManager } from '../managers/ModalManager.js';
|
import { modalManager } from '../managers/ModalManager.js';
|
||||||
import { resetAndReload } from '../api/loraApi.js';
|
|
||||||
import { state } from '../state/index.js';
|
import { state } from '../state/index.js';
|
||||||
|
|
||||||
export function createLoraCard(lora) {
|
export function createLoraCard(lora) {
|
||||||
@@ -129,7 +128,7 @@ export function showLoraModal(lora) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<label>File Name</label>
|
<label>File Name</label>
|
||||||
<span>${lora.file_name || 'N/A'}</span>
|
<span id="file-name">${lora.file_name || 'N/A'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<label>Location</label>
|
<label>Location</label>
|
||||||
@@ -142,10 +141,20 @@ export function showLoraModal(lora) {
|
|||||||
<div class="info-item usage-tips">
|
<div class="info-item usage-tips">
|
||||||
<label>Usage Tips</label>
|
<label>Usage Tips</label>
|
||||||
<div class="editable-field">
|
<div class="editable-field">
|
||||||
<div class="usage-tips-content" contenteditable="true" spellcheck="false">${lora.usage_tips || 'Save usage tips here..'}</div>
|
<div class="preset-controls">
|
||||||
<button class="save-btn" onclick="saveUsageTips('${lora.file_path}')">
|
<select id="preset-selector">
|
||||||
<i class="fas fa-save"></i>
|
<option value="">Add preset parameter...</option>
|
||||||
</button>
|
<option value="strength_min">Strength Min</option>
|
||||||
|
<option value="strength_max">Strength Max</option>
|
||||||
|
<option value="strength">Strength</option>
|
||||||
|
<option value="clip_skip">Clip Skip</option>
|
||||||
|
</select>
|
||||||
|
<input type="number" id="preset-value" step="0.01" placeholder="Value" style="display:none;">
|
||||||
|
<button class="add-preset-btn">Add</button>
|
||||||
|
</div>
|
||||||
|
<div class="preset-tags">
|
||||||
|
${renderPresetTags(parsePresets(lora.usage_tips))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${renderTriggerWords(escapedWords)}
|
${renderTriggerWords(escapedWords)}
|
||||||
@@ -195,25 +204,57 @@ function setupEditableFields() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Add these functions to handle saving the editable fields
|
const presetSelector = document.getElementById('preset-selector');
|
||||||
window.saveUsageTips = async function(filePath) {
|
const presetValue = document.getElementById('preset-value');
|
||||||
const content = document.querySelector('.usage-tips-content').textContent;
|
const addPresetBtn = document.querySelector('.add-preset-btn');
|
||||||
try {
|
const presetTags = document.querySelector('.preset-tags');
|
||||||
await saveModelMetadata(filePath, { usage_tips: content });
|
|
||||||
|
|
||||||
// Update the corresponding lora card's dataset
|
presetSelector.addEventListener('change', function() {
|
||||||
const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
|
const selected = this.value;
|
||||||
if (loraCard) {
|
if (selected) {
|
||||||
loraCard.dataset.usage_tips = content;
|
presetValue.style.display = 'inline-block';
|
||||||
|
presetValue.min = selected.includes('strength') ? 0 : 1;
|
||||||
|
presetValue.max = selected.includes('strength') ? 1 : 12;
|
||||||
|
presetValue.step = selected.includes('strength') ? 0.01 : 1;
|
||||||
|
if (selected === 'clip_skip') {
|
||||||
|
presetValue.type = 'number';
|
||||||
|
presetValue.step = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
presetValue.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
showToast('Usage tips saved successfully', 'success');
|
addPresetBtn.addEventListener('click', async function() {
|
||||||
} catch (error) {
|
const key = presetSelector.value;
|
||||||
showToast('Failed to save usage tips', 'error');
|
const value = presetValue.value;
|
||||||
}
|
|
||||||
};
|
if (!key || !value) return;
|
||||||
|
|
||||||
|
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}"]`);
|
||||||
|
const currentPresets = parsePresets(loraCard.dataset.usage_tips);
|
||||||
|
|
||||||
|
currentPresets[key] = parseFloat(value);
|
||||||
|
const newPresetsJson = JSON.stringify(currentPresets);
|
||||||
|
|
||||||
|
await saveModelMetadata(filePath, {
|
||||||
|
usage_tips: newPresetsJson
|
||||||
|
});
|
||||||
|
|
||||||
|
loraCard.dataset.usage_tips = newPresetsJson;
|
||||||
|
presetTags.innerHTML = renderPresetTags(currentPresets);
|
||||||
|
|
||||||
|
presetSelector.value = '';
|
||||||
|
presetValue.value = '';
|
||||||
|
presetValue.style.display = 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
window.saveNotes = async function(filePath) {
|
window.saveNotes = async function(filePath) {
|
||||||
const content = document.querySelector('.notes-content').textContent;
|
const content = document.querySelector('.notes-content').textContent;
|
||||||
@@ -225,7 +266,7 @@ window.saveNotes = async function(filePath) {
|
|||||||
if (loraCard) {
|
if (loraCard) {
|
||||||
loraCard.dataset.notes = content;
|
loraCard.dataset.notes = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
showToast('Notes saved successfully', 'success');
|
showToast('Notes saved successfully', 'success');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showToast('Failed to save notes', 'error');
|
showToast('Failed to save notes', 'error');
|
||||||
@@ -426,4 +467,47 @@ export function scrollToTop(button) {
|
|||||||
behavior: 'smooth'
|
behavior: 'smooth'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parsePresets(usageTips) {
|
||||||
|
if (!usageTips || usageTips === 'Save usage tips here..') return {};
|
||||||
|
try {
|
||||||
|
return JSON.parse(usageTips);
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPresetTags(presets) {
|
||||||
|
return Object.entries(presets).map(([key, value]) => `
|
||||||
|
<div class="preset-tag" data-key="${key}">
|
||||||
|
<span>${formatPresetKey(key)}: ${value}</span>
|
||||||
|
<i class="fas fa-times" onclick="removePreset('${key}')"></i>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPresetKey(key) {
|
||||||
|
return key.split('_').map(word =>
|
||||||
|
word.charAt(0).toUpperCase() + word.slice(1)
|
||||||
|
).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.removePreset = async function(key) {
|
||||||
|
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}"]`);
|
||||||
|
const currentPresets = parsePresets(loraCard.dataset.usage_tips);
|
||||||
|
|
||||||
|
delete currentPresets[key];
|
||||||
|
const newPresetsJson = JSON.stringify(currentPresets);
|
||||||
|
|
||||||
|
await saveModelMetadata(filePath, {
|
||||||
|
usage_tips: newPresetsJson
|
||||||
|
});
|
||||||
|
|
||||||
|
loraCard.dataset.usage_tips = newPresetsJson;
|
||||||
|
document.querySelector('.preset-tags').innerHTML = renderPresetTags(currentPresets);
|
||||||
|
};
|
||||||
@@ -15,7 +15,7 @@ class LoraMetadata:
|
|||||||
sha256: str # SHA256 hash of the file
|
sha256: str # SHA256 hash of the file
|
||||||
base_model: str # Base model (SD1.5/SD2.1/SDXL/etc.)
|
base_model: str # Base model (SD1.5/SD2.1/SDXL/etc.)
|
||||||
preview_url: str # Preview image URL
|
preview_url: str # Preview image URL
|
||||||
usage_tips: str = "" # Usage tips for the model
|
usage_tips: str = "{}" # Usage tips for the model, json string
|
||||||
notes: str = "" # Additional notes
|
notes: str = "" # Additional notes
|
||||||
from_civitai: bool = True # Whether the lora is from Civitai
|
from_civitai: bool = True # Whether the lora is from Civitai
|
||||||
civitai: Optional[Dict] = None # Civitai API data if available
|
civitai: Optional[Dict] = None # Civitai API data if available
|
||||||
|
|||||||
Reference in New Issue
Block a user