feat: Add save metadata route and update checkpoint card functionality

This commit is contained in:
Will Miao
2025-04-12 11:18:21 +08:00
parent 10a4fe04d1
commit e84a8a72c5
7 changed files with 250 additions and 120 deletions

View File

@@ -4,6 +4,7 @@
*/
import { showToast } from '../../utils/uiHelpers.js';
import { BASE_MODELS } from '../../utils/constants.js';
import { updateLoraCard } from '../../utils/cardUpdater.js';
/**
* 保存模型元数据到服务器
@@ -32,13 +33,17 @@ export async function saveModelMetadata(filePath, data) {
/**
* 设置模型名称编辑功能
* @param {string} filePath - 文件路径
*/
export function setupModelNameEditing() {
export function setupModelNameEditing(filePath) {
const modelNameContent = document.querySelector('.model-name-content');
const editBtn = document.querySelector('.edit-model-name-btn');
if (!modelNameContent || !editBtn) return;
// Store the file path in a data attribute for later use
modelNameContent.dataset.filePath = filePath;
// Show edit button on hover
const modelNameHeader = document.querySelector('.model-name-header');
modelNameHeader.addEventListener('mouseenter', () => {
@@ -76,10 +81,7 @@ export function setupModelNameEditing() {
if (this.textContent.trim() === '') {
// Restore original model name if empty
const filePath = document.querySelector('#loraModal .modal-content')
.querySelector('.file-path').textContent +
document.querySelector('#loraModal .modal-content')
.querySelector('#file-name').textContent + '.safetensors';
const filePath = this.dataset.filePath;
const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
if (loraCard) {
this.textContent = loraCard.dataset.model_name;
@@ -91,10 +93,7 @@ export function setupModelNameEditing() {
modelNameContent.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
const filePath = document.querySelector('#loraModal .modal-content')
.querySelector('.file-path').textContent +
document.querySelector('#loraModal .modal-content')
.querySelector('#file-name').textContent + '.safetensors';
const filePath = this.dataset.filePath;
saveModelName(filePath);
this.blur();
}
@@ -144,21 +143,9 @@ async function saveModelName(filePath) {
await saveModelMetadata(filePath, { model_name: newModelName });
// Update the corresponding lora card's dataset and display
const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
if (loraCard) {
loraCard.dataset.model_name = newModelName;
const titleElement = loraCard.querySelector('.card-title');
if (titleElement) {
titleElement.textContent = newModelName;
}
}
updateLoraCard(filePath, { model_name: newModelName });
showToast('Model name updated successfully', 'success');
// Reload the page to reflect the sorted order
setTimeout(() => {
window.location.reload();
}, 1500);
} catch (error) {
showToast('Failed to update model name', 'error');
}
@@ -166,13 +153,17 @@ async function saveModelName(filePath) {
/**
* 设置基础模型编辑功能
* @param {string} filePath - 文件路径
*/
export function setupBaseModelEditing() {
export function setupBaseModelEditing(filePath) {
const baseModelContent = document.querySelector('.base-model-content');
const editBtn = document.querySelector('.edit-base-model-btn');
if (!baseModelContent || !editBtn) return;
// Store the file path in a data attribute for later use
baseModelContent.dataset.filePath = filePath;
// Show edit button on hover
const baseModelDisplay = document.querySelector('.base-model-display');
baseModelDisplay.addEventListener('mouseenter', () => {
@@ -270,11 +261,8 @@ export function setupBaseModelEditing() {
// Only save if the value has actually changed
if (valueChanged || baseModelContent.textContent.trim() !== originalValue) {
// Get file path for saving
const filePath = document.querySelector('#loraModal .modal-content')
.querySelector('.file-path').textContent +
document.querySelector('#loraModal .modal-content')
.querySelector('#file-name').textContent + '.safetensors';
// Get file path from the dataset
const filePath = baseModelContent.dataset.filePath;
// Save the changes, passing the original value for comparison
saveBaseModel(filePath, originalValue);
@@ -325,10 +313,7 @@ async function saveBaseModel(filePath, originalValue) {
await saveModelMetadata(filePath, { base_model: newBaseModel });
// Update the corresponding lora card's dataset
const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
if (loraCard) {
loraCard.dataset.base_model = newBaseModel;
}
updateLoraCard(filePath, { base_model: newBaseModel });
showToast('Base model updated successfully', 'success');
} catch (error) {
@@ -338,13 +323,17 @@ async function saveBaseModel(filePath, originalValue) {
/**
* 设置文件名编辑功能
* @param {string} filePath - 文件路径
*/
export function setupFileNameEditing() {
export function setupFileNameEditing(filePath) {
const fileNameContent = document.querySelector('.file-name-content');
const editBtn = document.querySelector('.edit-file-name-btn');
if (!fileNameContent || !editBtn) return;
// Store the original file path
fileNameContent.dataset.filePath = filePath;
// Show edit button on hover
const fileNameWrapper = document.querySelector('.file-name-wrapper');
fileNameWrapper.addEventListener('mouseenter', () => {
@@ -441,9 +430,8 @@ export function setupFileNameEditing() {
}
try {
// Get the full file path
const filePath = document.querySelector('#loraModal .modal-content')
.querySelector('.file-path').textContent + originalValue + '.safetensors';
// Get the file path from the dataset
const filePath = this.dataset.filePath;
// Call API to rename the file
const response = await fetch('/api/rename_lora', {
@@ -462,12 +450,9 @@ export function setupFileNameEditing() {
if (result.success) {
showToast('File name updated successfully', 'success');
// Update the LoRA card with new file path
const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
if (loraCard) {
const newFilePath = filePath.replace(originalValue, newFileName);
loraCard.dataset.filepath = newFilePath;
}
// Get the new file path and update the card
const newFilePath = filePath.replace(originalValue, newFileName);
updateLoraCard(filePath, {}, newFilePath);
// Reload the page after a short delay to reflect changes
setTimeout(() => {

View File

@@ -18,6 +18,7 @@ import {
saveModelMetadata
} from './ModelMetadata.js';
import { renderCompactTags, setupTagTooltip, formatFileSize } from './utils.js';
import { updateLoraCard } from '../../utils/cardUpdater.js';
/**
* 显示LoRA模型弹窗
@@ -152,14 +153,14 @@ export function showLoraModal(lora) {
`;
modalManager.showModal('loraModal', content);
setupEditableFields();
setupEditableFields(lora.file_path);
setupShowcaseScroll();
setupTabSwitching();
setupTagTooltip();
setupTriggerWordsEditMode();
setupModelNameEditing();
setupBaseModelEditing();
setupFileNameEditing();
setupModelNameEditing(lora.file_path);
setupBaseModelEditing(lora.file_path);
setupFileNameEditing(lora.file_path);
// If we have a model ID but no description, fetch it
if (lora.civitai?.modelId && !lora.modelDescription) {
@@ -188,10 +189,7 @@ window.saveNotes = async function(filePath) {
await saveModelMetadata(filePath, { notes: content });
// Update the corresponding lora card's dataset
const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
if (loraCard) {
loraCard.dataset.notes = content;
}
updateLoraCard(filePath, { notes: content });
showToast('Notes saved successfully', 'success');
} catch (error) {
@@ -199,7 +197,7 @@ window.saveNotes = async function(filePath) {
}
};
function setupEditableFields() {
function setupEditableFields(filePath) {
const editableFields = document.querySelectorAll('.editable-field [contenteditable]');
editableFields.forEach(field => {
@@ -247,11 +245,6 @@ function setupEditableFields() {
if (!key || !value) return;
const filePath = document.querySelector('#loraModal .modal-content')
.querySelector('.file-path').textContent +
document.querySelector('#loraModal .modal-content')
.querySelector('#file-name').textContent + '.safetensors';
const loraCard = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
const currentPresets = parsePresets(loraCard.dataset.usage_tips);
@@ -262,7 +255,9 @@ function setupEditableFields() {
usage_tips: newPresetsJson
});
loraCard.dataset.usage_tips = newPresetsJson;
// Update the card with the new usage tips
updateLoraCard(filePath, { usage_tips: newPresetsJson });
presetTags.innerHTML = renderPresetTags(currentPresets);
presetSelector.value = '';
@@ -280,10 +275,6 @@ function setupEditableFields() {
return;
}
e.preventDefault();
const filePath = document.querySelector('#loraModal .modal-content')
.querySelector('.file-path').textContent +
document.querySelector('#loraModal .modal-content')
.querySelector('#file-name').textContent + '.safetensors';
await saveNotes(filePath);
}
});