feat(modal): make version name editable in model modal (#931)

This commit is contained in:
Will Miao
2026-05-26 20:16:35 +08:00
parent 7416080cfb
commit 37ccd29fc0
13 changed files with 167 additions and 12 deletions

View File

@@ -1180,6 +1180,7 @@
"editModelName": "Modellname bearbeiten",
"editFileName": "Dateiname bearbeiten",
"editBaseModel": "Basis-Modell bearbeiten",
"editVersionName": "Versionsname bearbeiten",
"viewOnCivitai": "Auf Civitai anzeigen",
"viewOnCivitaiText": "Auf Civitai anzeigen",
"viewCreatorProfile": "Ersteller-Profil anzeigen",

View File

@@ -1180,6 +1180,7 @@
"editModelName": "Edit model name",
"editFileName": "Edit file name",
"editBaseModel": "Edit base model",
"editVersionName": "Edit version name",
"viewOnCivitai": "View on Civitai",
"viewOnCivitaiText": "View on Civitai",
"viewCreatorProfile": "View Creator Profile",

View File

@@ -1180,6 +1180,7 @@
"editModelName": "Editar nombre del modelo",
"editFileName": "Editar nombre de archivo",
"editBaseModel": "Editar modelo base",
"editVersionName": "Editar nombre de versión",
"viewOnCivitai": "Ver en Civitai",
"viewOnCivitaiText": "Ver en Civitai",
"viewCreatorProfile": "Ver perfil del creador",

View File

@@ -1180,6 +1180,7 @@
"editModelName": "Modifier le nom du modèle",
"editFileName": "Modifier le nom de fichier",
"editBaseModel": "Modifier le modèle de base",
"editVersionName": "Modifier le nom de la version",
"viewOnCivitai": "Voir sur Civitai",
"viewOnCivitaiText": "Voir sur Civitai",
"viewCreatorProfile": "Voir le profil du créateur",

View File

@@ -1180,6 +1180,7 @@
"editModelName": "ערוך שם מודל",
"editFileName": "ערוך שם קובץ",
"editBaseModel": "ערוך מודל בסיס",
"editVersionName": "ערוך שם גרסה",
"viewOnCivitai": "הצג ב-Civitai",
"viewOnCivitaiText": "הצג ב-Civitai",
"viewCreatorProfile": "הצג פרופיל יוצר",

View File

@@ -1180,6 +1180,7 @@
"editModelName": "モデル名を編集",
"editFileName": "ファイル名を編集",
"editBaseModel": "ベースモデルを編集",
"editVersionName": "バージョン名を編集",
"viewOnCivitai": "Civitaiで表示",
"viewOnCivitaiText": "Civitaiで表示",
"viewCreatorProfile": "作成者プロフィールを表示",

View File

@@ -1180,6 +1180,7 @@
"editModelName": "모델명 편집",
"editFileName": "파일명 편집",
"editBaseModel": "베이스 모델 편집",
"editVersionName": "버전명 편집",
"viewOnCivitai": "Civitai에서 보기",
"viewOnCivitaiText": "Civitai에서 보기",
"viewCreatorProfile": "제작자 프로필 보기",

View File

@@ -1180,6 +1180,7 @@
"editModelName": "Редактировать название модели",
"editFileName": "Редактировать имя файла",
"editBaseModel": "Редактировать базовую модель",
"editVersionName": "Редактировать название версии",
"viewOnCivitai": "Посмотреть на Civitai",
"viewOnCivitaiText": "Посмотреть на Civitai",
"viewCreatorProfile": "Посмотреть профиль создателя",

View File

@@ -1180,6 +1180,7 @@
"editModelName": "编辑模型名称",
"editFileName": "编辑文件名",
"editBaseModel": "编辑基础模型",
"editVersionName": "编辑版本名称",
"viewOnCivitai": "在 Civitai 查看",
"viewOnCivitaiText": "在 Civitai 查看",
"viewCreatorProfile": "查看创作者主页",

View File

@@ -1180,6 +1180,7 @@
"editModelName": "編輯模型名稱",
"editFileName": "編輯檔案名稱",
"editBaseModel": "編輯基礎模型",
"editVersionName": "編輯版本名稱",
"viewOnCivitai": "在 Civitai 查看",
"viewOnCivitaiText": "在 Civitai 查看",
"viewCreatorProfile": "查看創作者個人檔案",

View File

@@ -255,25 +255,28 @@
transform: translateY(-2px);
}
/* File name copy styles */
.file-name-wrapper {
/* Editable inline field styles (file name, version name, etc.) */
.file-name-wrapper,
.version-name-wrapper {
display: flex;
align-items: center;
gap: 8px;
padding: 4px;
padding: 4px 0;
border-radius: var(--border-radius-xs);
transition: background-color 0.2s;
position: relative;
}
.file-name-content {
padding: 2px 4px;
.file-name-content,
.version-name-content {
padding: 2px 4px 2px 0;
border-radius: var(--border-radius-xs);
border: 1px solid transparent;
flex: 1;
}
.file-name-wrapper.editing .file-name-content {
.file-name-wrapper.editing .file-name-content,
.version-name-wrapper.editing .version-name-content {
border: 1px solid var(--lora-accent);
background: var(--bg-color);
outline: none;
@@ -283,7 +286,8 @@
.edit-model-name-btn,
.edit-file-name-btn,
.edit-base-model-btn,
.edit-model-description-btn {
.edit-model-description-btn,
.edit-version-name-btn {
background: transparent;
border: none;
color: var(--text-color);
@@ -299,9 +303,11 @@
.edit-file-name-btn.visible,
.edit-base-model-btn.visible,
.edit-model-description-btn.visible,
.edit-version-name-btn.visible,
.model-name-header:hover .edit-model-name-btn,
.file-name-wrapper:hover .edit-file-name-btn,
.base-model-display:hover .edit-base-model-btn,
.version-name-wrapper:hover .edit-version-name-btn,
.model-name-header:hover .edit-model-description-btn {
opacity: 0.5;
}
@@ -309,14 +315,16 @@
.edit-model-name-btn:hover,
.edit-file-name-btn:hover,
.edit-base-model-btn:hover,
.edit-model-description-btn:hover {
.edit-model-description-btn:hover,
.edit-version-name-btn:hover {
opacity: 0.8 !important;
background: rgba(0, 0, 0, 0.05);
}
[data-theme="dark"] .edit-model-name-btn:hover,
[data-theme="dark"] .edit-file-name-btn:hover,
[data-theme="dark"] .edit-base-model-btn:hover {
[data-theme="dark"] .edit-base-model-btn:hover,
[data-theme="dark"] .edit-version-name-btn:hover {
background: rgba(255, 255, 255, 0.05);
}
@@ -338,7 +346,7 @@
}
.base-model-content {
padding: 2px 4px;
padding: 2px 4px 2px 0;
border-radius: var(--border-radius-xs);
border: 1px solid transparent;
color: var(--text-color);

View File

@@ -66,6 +66,12 @@ function updateModalFilePathReferences(newFilePath) {
fileNameContent.setAttribute('data-file-path', newFilePath);
}
const versionNameContent = scopedQuery('.version-name-content');
if (versionNameContent && versionNameContent.dataset) {
versionNameContent.dataset.filePath = newFilePath;
versionNameContent.setAttribute('data-file-path', newFilePath);
}
const editTagsBtn = scopedQuery('.edit-tags-btn');
if (editTagsBtn) {
editTagsBtn.dataset.filePath = newFilePath;
@@ -516,3 +522,127 @@ export function setupFileNameEditing(filePath) {
editBtn.classList.remove('visible');
}
}
/**
* Set up version name editing functionality
* @param {string} filePath - File path
*/
export function setupVersionNameEditing(filePath) {
const versionNameContent = document.querySelector('.version-name-content');
const editBtn = document.querySelector('.edit-version-name-btn');
if (!versionNameContent || !editBtn) return;
// Store the file path in a data attribute for later use
versionNameContent.dataset.filePath = filePath;
// Show edit button on hover
const versionNameWrapper = document.querySelector('.version-name-wrapper');
versionNameWrapper.addEventListener('mouseenter', () => {
editBtn.classList.add('visible');
});
versionNameWrapper.addEventListener('mouseleave', () => {
if (!versionNameWrapper.classList.contains('editing')) {
editBtn.classList.remove('visible');
}
});
// Handle edit button click
editBtn.addEventListener('click', () => {
versionNameWrapper.classList.add('editing');
versionNameContent.setAttribute('contenteditable', 'true');
// Store original value for comparison later
versionNameContent.dataset.originalValue = versionNameContent.textContent.trim();
versionNameContent.focus();
// Place cursor at the end
const range = document.createRange();
const sel = window.getSelection();
if (versionNameContent.childNodes.length > 0) {
range.setStart(versionNameContent.childNodes[0], versionNameContent.textContent.length);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
editBtn.classList.add('visible');
});
// Handle keyboard events in edit mode
versionNameContent.addEventListener('keydown', function(e) {
if (!this.getAttribute('contenteditable')) return;
if (e.key === 'Enter') {
e.preventDefault();
this.blur(); // Trigger save on Enter
} else if (e.key === 'Escape') {
e.preventDefault();
// Restore original value
this.textContent = this.dataset.originalValue;
exitEditMode();
}
});
// Limit version name length
versionNameContent.addEventListener('input', function() {
if (!this.getAttribute('contenteditable')) return;
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('toast.models.nameTooLong', {}, 'warning');
}
});
// Handle focus out - save changes
versionNameContent.addEventListener('blur', async function() {
if (!this.getAttribute('contenteditable')) return;
const newVersionName = this.textContent.trim();
const originalValue = this.dataset.originalValue;
// Basic validation
if (!newVersionName) {
// Restore original value if empty
this.textContent = originalValue;
showToast('toast.models.nameCannotBeEmpty', {}, 'error');
exitEditMode();
return;
}
if (newVersionName === originalValue) {
// No changes, just exit edit mode
exitEditMode();
return;
}
try {
// Resolve current file path from modal state
const filePath = getActiveModalFilePath(this.dataset.filePath);
await getModelApiClient().saveModelMetadata(filePath, { civitai: { name: newVersionName } });
showToast('toast.models.nameUpdatedSuccessfully', {}, 'success');
} catch (error) {
console.error('Error updating version name:', error);
this.textContent = originalValue; // Restore original version name
showToast('toast.models.nameUpdateFailed', {}, 'error');
} finally {
exitEditMode();
}
});
function exitEditMode() {
versionNameContent.removeAttribute('contenteditable');
versionNameWrapper.classList.remove('editing');
editBtn.classList.remove('visible');
}
}

View File

@@ -11,7 +11,8 @@ import { setupTabSwitching } from './ModelDescription.js';
import {
setupModelNameEditing,
setupBaseModelEditing,
setupFileNameEditing
setupFileNameEditing,
setupVersionNameEditing
} from './ModelMetadata.js';
import { setupTagEditMode } from './ModelTags.js';
import { getModelApiClient } from '../../api/modelApiFactory.js';
@@ -466,7 +467,12 @@ export async function showModelModal(model, modelType) {
<div class="info-grid">
<div class="info-item">
<label>${translate('modals.model.metadata.version', {}, 'Version')}</label>
<span>${modelWithFullData.civitai?.name || 'N/A'}</span>
<div class="version-name-wrapper">
<span class="version-name-content">${modelWithFullData.civitai?.name || 'N/A'}</span>
<button class="edit-version-name-btn" title="${translate('modals.model.actions.editVersionName', {}, 'Edit version name')}">
<i class="fas fa-pencil-alt"></i>
</button>
</div>
</div>
<div class="info-item">
<label>${translate('modals.model.metadata.fileName', {}, 'File Name')}</label>
@@ -660,6 +666,7 @@ export async function showModelModal(model, modelType) {
setupTagTooltip();
setupTagEditMode(modelType);
setupModelNameEditing(modelWithFullData.file_path);
setupVersionNameEditing(modelWithFullData.file_path);
setupBaseModelEditing(modelWithFullData.file_path);
setupFileNameEditing(modelWithFullData.file_path);
setupEventHandlers(modelWithFullData.file_path, modelType);