diff --git a/static/css/components/lora-modal.css b/static/css/components/lora-modal.css
index 0e3245c0..c4278a29 100644
--- a/static/css/components/lora-modal.css
+++ b/static/css/components/lora-modal.css
@@ -573,6 +573,59 @@
flex: 2; /* 分配更多空间给base model */
}
+/* Base model display and editing styles */
+.base-model-display {
+ display: flex;
+ align-items: center;
+ position: relative;
+}
+
+.base-model-content {
+ padding: 2px 4px;
+ border-radius: var(--border-radius-xs);
+ border: 1px solid transparent;
+ color: var(--text-color);
+ flex: 1;
+}
+
+.edit-base-model-btn {
+ background: transparent;
+ border: none;
+ color: var(--text-color);
+ opacity: 0;
+ cursor: pointer;
+ padding: 2px 5px;
+ border-radius: var(--border-radius-xs);
+ transition: all 0.2s ease;
+ margin-left: var(--space-1);
+}
+
+.edit-base-model-btn.visible,
+.base-model-display:hover .edit-base-model-btn {
+ opacity: 0.5;
+}
+
+.edit-base-model-btn:hover {
+ opacity: 0.8 !important;
+ background: rgba(0, 0, 0, 0.05);
+}
+
+[data-theme="dark"] .edit-base-model-btn:hover {
+ background: rgba(255, 255, 255, 0.05);
+}
+
+.base-model-selector {
+ width: 100%;
+ padding: 3px 5px;
+ background: var(--bg-color);
+ border: 1px solid var(--lora-accent);
+ border-radius: var(--border-radius-xs);
+ color: var(--text-color);
+ font-size: 0.9em;
+ outline: none;
+ margin-right: var(--space-1);
+}
+
.size-wrapper {
flex: 1;
border-left: 1px solid var(--lora-border);
diff --git a/static/js/components/LoraModal.js b/static/js/components/LoraModal.js
index b8254bff..bca3041e 100644
--- a/static/js/components/LoraModal.js
+++ b/static/js/components/LoraModal.js
@@ -1,7 +1,7 @@
import { showToast } from '../utils/uiHelpers.js';
import { state } from '../state/index.js';
import { modalManager } from '../managers/ModalManager.js';
-import { NSFW_LEVELS } from '../utils/constants.js';
+import { NSFW_LEVELS, BASE_MODELS } from '../utils/constants.js';
export function showLoraModal(lora) {
const escapedWords = lora.civitai?.trainedWords?.length ?
@@ -43,7 +43,12 @@ export function showLoraModal(lora) {
-
${lora.base_model || 'N/A'}
+
+ ${lora.base_model || 'N/A'}
+
+
@@ -124,6 +129,7 @@ export function showLoraModal(lora) {
setupTagTooltip();
setupTriggerWordsEditMode();
setupModelNameEditing();
+ setupBaseModelEditing();
// If we have a model ID but no description, fetch it
if (lora.civitai?.modelId && !lora.modelDescription) {
@@ -1225,3 +1231,148 @@ function setupModelNameEditing() {
}
});
}
+
+// Add save model base model function
+window.saveBaseModel = async function(filePath) {
+ const baseModelElement = document.querySelector('.base-model-content');
+ const newBaseModel = baseModelElement.textContent.trim();
+
+ try {
+ 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;
+ }
+
+ showToast('Base model updated successfully', 'success');
+ } catch (error) {
+ showToast('Failed to update base model', 'error');
+ }
+};
+
+// New function to handle base model editing
+function setupBaseModelEditing() {
+ const baseModelContent = document.querySelector('.base-model-content');
+ const editBtn = document.querySelector('.edit-base-model-btn');
+
+ if (!baseModelContent || !editBtn) return;
+
+ // Show edit button on hover
+ const baseModelDisplay = document.querySelector('.base-model-display');
+ baseModelDisplay.addEventListener('mouseenter', () => {
+ editBtn.classList.add('visible');
+ });
+
+ baseModelDisplay.addEventListener('mouseleave', () => {
+ if (!baseModelDisplay.classList.contains('editing')) {
+ editBtn.classList.remove('visible');
+ }
+ });
+
+ // Handle edit button click
+ editBtn.addEventListener('click', () => {
+ baseModelDisplay.classList.add('editing');
+
+ // Create dropdown selector to replace the base model content
+ const currentValue = baseModelContent.textContent.trim();
+ const dropdown = document.createElement('select');
+ dropdown.className = 'base-model-selector';
+
+ // Add options from BASE_MODELS constants
+ const baseModelCategories = {
+ 'Stable Diffusion 1.x': [BASE_MODELS.SD_1_4, BASE_MODELS.SD_1_5, BASE_MODELS.SD_1_5_LCM, BASE_MODELS.SD_1_5_HYPER],
+ 'Stable Diffusion 2.x': [BASE_MODELS.SD_2_0, BASE_MODELS.SD_2_1],
+ 'Stable Diffusion 3.x': [BASE_MODELS.SD_3, BASE_MODELS.SD_3_5, BASE_MODELS.SD_3_5_MEDIUM, BASE_MODELS.SD_3_5_LARGE, BASE_MODELS.SD_3_5_LARGE_TURBO],
+ 'SDXL': [BASE_MODELS.SDXL, BASE_MODELS.SDXL_LIGHTNING, BASE_MODELS.SDXL_HYPER],
+ 'Video Models': [BASE_MODELS.SVD, BASE_MODELS.WAN_VIDEO, BASE_MODELS.HUNYUAN_VIDEO],
+ 'Other Models': [
+ BASE_MODELS.FLUX_1_D, BASE_MODELS.FLUX_1_S, BASE_MODELS.AURAFLOW,
+ BASE_MODELS.PIXART_A, BASE_MODELS.PIXART_E, BASE_MODELS.HUNYUAN_1,
+ BASE_MODELS.LUMINA, BASE_MODELS.KOLORS, BASE_MODELS.NOOBAI,
+ BASE_MODELS.ILLUSTRIOUS, BASE_MODELS.PONY, BASE_MODELS.UNKNOWN
+ ]
+ };
+
+ // Create option groups for better organization
+ Object.entries(baseModelCategories).forEach(([category, models]) => {
+ const group = document.createElement('optgroup');
+ group.label = category;
+
+ models.forEach(model => {
+ const option = document.createElement('option');
+ option.value = model;
+ option.textContent = model;
+ option.selected = model === currentValue;
+ group.appendChild(option);
+ });
+
+ dropdown.appendChild(group);
+ });
+
+ // Replace content with dropdown
+ baseModelContent.style.display = 'none';
+ baseModelDisplay.insertBefore(dropdown, editBtn);
+
+ // Hide edit button during editing
+ editBtn.style.display = 'none';
+
+ // Focus the dropdown
+ dropdown.focus();
+
+ // Handle dropdown change
+ dropdown.addEventListener('change', function() {
+ const selectedModel = this.value;
+ baseModelContent.textContent = selectedModel;
+ });
+
+ // Function to save changes and exit edit mode
+ const saveAndExit = function() {
+ // Check if dropdown still exists and remove it
+ if (dropdown && dropdown.parentNode === baseModelDisplay) {
+ baseModelDisplay.removeChild(dropdown);
+ }
+
+ // Show the content and edit button
+ baseModelContent.style.display = '';
+ editBtn.style.display = '';
+
+ // Remove editing class
+ baseModelDisplay.classList.remove('editing');
+
+ // 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';
+
+ // Save the changes
+ saveBaseModel(filePath);
+
+ // Remove this event listener
+ document.removeEventListener('click', outsideClickHandler);
+ };
+
+ // Handle outside clicks to save and exit
+ const outsideClickHandler = function(e) {
+ // If click is outside the dropdown and base model display
+ if (!baseModelDisplay.contains(e.target)) {
+ saveAndExit();
+ }
+ };
+
+ // Add delayed event listener for outside clicks
+ setTimeout(() => {
+ document.addEventListener('click', outsideClickHandler);
+ }, 0);
+
+ // Also handle dropdown blur event
+ dropdown.addEventListener('blur', function(e) {
+ // Only save if the related target is not the edit button or inside the baseModelDisplay
+ if (!baseModelDisplay.contains(e.relatedTarget)) {
+ saveAndExit();
+ }
+ });
+ });
+}