diff --git a/static/css/components/card.css b/static/css/components/card.css index 838383d0..b13236a9 100644 --- a/static/css/components/card.css +++ b/static/css/components/card.css @@ -86,23 +86,39 @@ min-height: 0; /* Fix for potential flexbox sizing issue in Firefox */ } +/* Smaller text for medium density */ +.medium-density .model-name { + font-size: 0.95em; + max-height: 2.6em; +} + +.medium-density .base-model-label { + font-size: 0.85em; + max-width: 120px; +} + +.medium-density .card-actions i { + font-size: 0.98em; + padding: 4px; +} + /* Smaller text for compact mode */ -.compact-mode .model-name { +.compact-density .model-name { font-size: 0.9em; max-height: 2.4em; } -.compact-mode .base-model-label { +.compact-density .base-model-label { font-size: 0.8em; max-width: 110px; } -.compact-mode .card-actions i { +.compact-density .card-actions i { font-size: 0.95em; padding: 3px; } -.compact-mode .model-info { +.compact-density .model-info { padding-bottom: 2px; } diff --git a/static/css/components/modal.css b/static/css/components/modal.css index 1661229d..77f13808 100644 --- a/static/css/components/modal.css +++ b/static/css/components/modal.css @@ -682,4 +682,15 @@ input:checked + .toggle-slider:before { [data-theme="dark"] .warning-text { color: var(--lora-warning, #f39c12); +} + +/* Add styles for density description list */ +.density-description { + margin: 8px 0; + padding-left: 20px; + font-size: 0.9em; +} + +.density-description li { + margin-bottom: 4px; } \ No newline at end of file diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index e3b8c299..f0c7c240 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -31,6 +31,16 @@ export class SettingsManager { if (state.global.settings.compactMode === undefined) { state.global.settings.compactMode = false; } + + // Convert old boolean compactMode to new displayDensity string + if (typeof state.global.settings.displayDensity === 'undefined') { + if (state.global.settings.compactMode === true) { + state.global.settings.displayDensity = 'compact'; + } else { + state.global.settings.displayDensity = 'default'; + } + // We can delete the old setting, but keeping it for backwards compatibility + } } initialize() { @@ -82,10 +92,10 @@ export class SettingsManager { autoplayOnHoverCheckbox.checked = state.global.settings.autoplayOnHover || false; } - // Set compact mode setting - const compactModeCheckbox = document.getElementById('compactMode'); - if (compactModeCheckbox) { - compactModeCheckbox.checked = state.global.settings.compactMode || false; + // Set display density setting + const displayDensitySelect = document.getElementById('displayDensity'); + if (displayDensitySelect) { + displayDensitySelect.value = state.global.settings.displayDensity || 'default'; } // Load default lora root @@ -219,6 +229,11 @@ export class SettingsManager { // Update frontend state if (settingKey === 'default_lora_root') { state.global.settings.default_loras_root = value; + } else if (settingKey === 'display_density') { + state.global.settings.displayDensity = value; + + // Also update compactMode for backwards compatibility + state.global.settings.compactMode = (value !== 'default'); } else { // For any other settings that might be added in the future state.global.settings[settingKey] = value; @@ -229,22 +244,38 @@ export class SettingsManager { try { // For backend settings, make API call - const payload = {}; - payload[settingKey] = value; - - const response = await fetch('/api/settings', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(payload) - }); + if (settingKey === 'default_lora_root') { + const payload = {}; + payload[settingKey] = value; + + const response = await fetch('/api/settings', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload) + }); - if (!response.ok) { - throw new Error('Failed to save setting'); + if (!response.ok) { + throw new Error('Failed to save setting'); + } + + showToast(`Settings updated: ${settingKey.replace(/_/g, ' ')}`, 'success'); } - showToast(`Settings updated: ${settingKey.replace(/_/g, ' ')}`, 'success'); + // Apply frontend settings immediately + this.applyFrontendSettings(); + + // Recalculate layout when display density changes + if (settingKey === 'display_density' && state.virtualScroller) { + state.virtualScroller.calculateLayout(); + + let densityName = "Default"; + if (value === 'medium') densityName = "Medium"; + if (value === 'compact') densityName = "Compact"; + + showToast(`Display Density set to ${densityName}`, 'success'); + } } catch (error) { showToast('Failed to save setting: ' + error.message, 'error'); @@ -419,8 +450,17 @@ export class SettingsManager { videoParent.replaceChild(videoClone, video); }); - // For show_only_sfw, there's no immediate action needed as it affects content loading - // The setting will take effect on next reload + // Apply display density class to grid + const grid = document.querySelector('.card-grid'); + if (grid) { + const density = state.global.settings.displayDensity || 'default'; + + // Remove all density classes first + grid.classList.remove('default-density', 'medium-density', 'compact-density'); + + // Add the appropriate density class + grid.classList.add(`${density}-density`); + } } } diff --git a/static/js/utils/VirtualScroller.js b/static/js/utils/VirtualScroller.js index caee0f19..82790539 100644 --- a/static/js/utils/VirtualScroller.js +++ b/static/js/utils/VirtualScroller.js @@ -88,22 +88,40 @@ export class VirtualScroller { // Calculate available content width (excluding padding) const availableContentWidth = containerWidth - paddingLeft - paddingRight; - // Get compact mode setting - const compactMode = state.global.settings?.compactMode || false; + // Get display density setting + const displayDensity = state.global.settings?.displayDensity || 'default'; // Set exact column counts and grid widths to match CSS container widths let maxColumns, maxGridWidth; - // Match exact column counts and CSS container width values + // Match exact column counts and CSS container width values based on density if (window.innerWidth >= 3000) { // 4K - maxColumns = compactMode ? 10 : 8; + if (displayDensity === 'default') { + maxColumns = 8; + } else if (displayDensity === 'medium') { + maxColumns = 9; + } else { // compact + maxColumns = 10; + } maxGridWidth = 2400; // Match exact CSS container width for 4K } else if (window.innerWidth >= 2000) { // 2K/1440p - maxColumns = compactMode ? 8 : 6; + if (displayDensity === 'default') { + maxColumns = 6; + } else if (displayDensity === 'medium') { + maxColumns = 7; + } else { // compact + maxColumns = 8; + } maxGridWidth = 1800; // Match exact CSS container width for 2K } else { // 1080p - maxColumns = compactMode ? 7 : 5; + if (displayDensity === 'default') { + maxColumns = 5; + } else if (displayDensity === 'medium') { + maxColumns = 6; + } else { // compact + maxColumns = 7; + } maxGridWidth = 1400; // Match exact CSS container width for 1080p } @@ -145,7 +163,7 @@ export class VirtualScroller { leftOffset: this.leftOffset, paddingLeft, paddingRight, - compactMode, + displayDensity, maxColumns, baseCardWidth, rowGap: this.rowGap @@ -154,12 +172,9 @@ export class VirtualScroller { // Update grid element max-width to match available width this.gridElement.style.maxWidth = `${actualGridWidth}px`; - // Add or remove compact-mode class for style adjustments - if (compactMode) { - this.gridElement.classList.add('compact-mode'); - } else { - this.gridElement.classList.remove('compact-mode'); - } + // Add or remove density classes for style adjustments + this.gridElement.classList.remove('default-density', 'medium-density', 'compact-density'); + this.gridElement.classList.add(`${displayDensity}-density`); // Update spacer height this.updateSpacerHeight(); diff --git a/templates/components/modals.html b/templates/components/modals.html index 773df854..142ff8e4 100644 --- a/templates/components/modals.html +++ b/templates/components/modals.html @@ -161,18 +161,24 @@
- +
-
- +
+
- Display more cards per row (7 on 1080p, 8 on 2K, 10 on 4K). Warning: May cause performance issues (lag and lower FPS) on systems with limited resources. + Choose how many cards to display per row: +
    +
  • Default: 5 (1080p), 6 (2K), 8 (4K)
  • +
  • Medium: 6 (1080p), 7 (2K), 9 (4K)
  • +
  • Compact: 7 (1080p), 8 (2K), 10 (4K)
  • +
+ Warning: Higher densities may cause performance issues on systems with limited resources.