adjustable card/font size

This commit is contained in:
Simon
2026-06-30 16:26:20 +00:00
parent 2e6e74b959
commit 5d739bec12
6 changed files with 120 additions and 7 deletions

View File

@@ -559,6 +559,38 @@ body.theme-light .input-row input:focus {
background: var(--bg-tertiary); background: var(--bg-tertiary);
} }
.setting-item input[type="range"] {
width: 100%;
appearance: none;
-webkit-appearance: none;
height: 6px;
border-radius: 999px;
background: var(--bg-tertiary);
border: 1px solid var(--border);
cursor: pointer;
margin: 6px 0;
}
.setting-item input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--text-primary);
border: none;
cursor: pointer;
}
.setting-item input[type="range"]::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--text-primary);
border: none;
cursor: pointer;
}
.setting-item select { .setting-item select {
width: 100%; width: 100%;
padding: 9px 12px; padding: 9px 12px;
@@ -872,11 +904,11 @@ body.theme-light .setting-item select option {
} }
.video-card h4 { .video-card h4 {
font-size: 16px; font-size: calc(16px * var(--card-font-scale, 1));
} }
.video-card p { .video-card p {
font-size: 13px; font-size: calc(13px * var(--card-font-scale, 1));
} }
} }
@@ -989,7 +1021,7 @@ body.theme-light .setting-item select option {
} }
.video-card h4 { .video-card h4 {
font-size: 15px; font-size: calc(15px * var(--card-font-scale, 1));
font-weight: 500; font-weight: 500;
padding: 12px 12px 10px; padding: 12px 12px 10px;
line-height: 1.3; line-height: 1.3;
@@ -1022,7 +1054,7 @@ body.theme-light .setting-item select option {
} }
.video-card p { .video-card p {
font-size: 12px; font-size: calc(12px * var(--card-font-scale, 1));
color: var(--text-secondary); color: var(--text-secondary);
padding: 0 12px 12px 12px; padding: 0 12px 12px 12px;
margin: 0; margin: 0;
@@ -1040,7 +1072,7 @@ body.theme-light .setting-item select option {
} }
.video-tag { .video-tag {
font-size: 11px; font-size: calc(11px * var(--card-font-scale, 1));
color: var(--text-secondary); color: var(--text-secondary);
background: var(--bg-tertiary); background: var(--bg-tertiary);
border: 1px solid var(--border); border: 1px solid var(--border);

View File

@@ -98,6 +98,14 @@
<option value="compact">Compact</option> <option value="compact">Compact</option>
</select> </select>
</div> </div>
<div class="setting-item">
<label for="card-size-range">Card Size</label>
<input type="range" id="card-size-range" min="0.7" max="1.5" step="0.1" value="1">
</div>
<div class="setting-item">
<label for="text-size-range">Text Size</label>
<input type="range" id="text-size-range" min="0.8" max="1.4" step="0.1" value="1">
</div>
<div class="setting-item"> <div class="setting-item">
<label for="feed-end-select">Reels: On Video End</label> <label for="feed-end-select">Reels: On Video End</label>
<select id="feed-end-select"> <select id="feed-end-select">

View File

@@ -8,6 +8,9 @@ window.App = window.App || {};
App.ui.applyPreferredQuality(); App.ui.applyPreferredQuality();
App.ui.applyFeedEndBehavior(); App.ui.applyFeedEndBehavior();
App.ui.applyDensity(); App.ui.applyDensity();
// Set the text-size CSS variable before the first pack so initial card
// heights are measured at the user's chosen size.
document.documentElement.style.setProperty('--card-font-scale', App.storage.getFontScale());
App.ui.renderMenu(); App.ui.renderMenu();
App.favorites.renderBar(); App.favorites.renderBar();
App.ui.bindGlobalHandlers(); App.ui.bindGlobalHandlers();

View File

@@ -57,6 +57,30 @@ App.session = App.session || {};
localStorage.setItem('density', nextDensity === 'compact' ? 'compact' : 'comfortable'); localStorage.setItem('density', nextDensity === 'compact' ? 'compact' : 'comfortable');
}; };
// User-tunable card width / text size multipliers (default 1.0). Clamped so a
// stale or hand-edited value can never break the layout.
const clampScale = function(value, min, max, fallback) {
const n = parseFloat(value);
if (!isFinite(n)) return fallback;
return Math.min(max, Math.max(min, n));
};
App.storage.getCardScale = function() {
return clampScale(localStorage.getItem('cardScale'), 0.7, 1.5, 1);
};
App.storage.setCardScale = function(next) {
localStorage.setItem('cardScale', clampScale(next, 0.7, 1.5, 1));
};
App.storage.getFontScale = function() {
return clampScale(localStorage.getItem('fontScale'), 0.8, 1.4, 1);
};
App.storage.setFontScale = function(next) {
localStorage.setItem('fontScale', clampScale(next, 0.8, 1.4, 1));
};
App.storage.getServerEntries = function() { App.storage.getServerEntries = function() {
const config = App.storage.getConfig(); const config = App.storage.getConfig();
if (!config.servers || !Array.isArray(config.servers)) return []; if (!config.servers || !Array.isArray(config.servers)) return [];

View File

@@ -28,6 +28,29 @@ App.ui = App.ui || {};
if (select) select.value = density; if (select) select.value = density;
}; };
// Card Size: re-packs the virtual grid (column count derives from the scaled
// minimum card width in videos.js).
App.ui.applyCardScale = function() {
const scale = App.storage.getCardScale();
const range = document.getElementById('card-size-range');
if (range) range.value = scale;
if (App.virtualGrid && typeof App.virtualGrid.relayout === 'function') {
App.virtualGrid.relayout();
}
};
// Text Size: drives the --card-font-scale CSS variable; a re-pack follows so
// card heights account for the new text size.
App.ui.applyFontScale = function() {
const scale = App.storage.getFontScale();
document.documentElement.style.setProperty('--card-font-scale', scale);
const range = document.getElementById('text-size-range');
if (range) range.value = scale;
if (App.virtualGrid && typeof App.virtualGrid.relayout === 'function') {
App.virtualGrid.relayout();
}
};
// Toast helper for playback + network errors. // Toast helper for playback + network errors.
App.ui.showError = function(message) { App.ui.showError = function(message) {
const toast = document.getElementById('error-toast'); const toast = document.getElementById('error-toast');
@@ -305,6 +328,24 @@ App.ui = App.ui || {};
}; };
} }
const cardSizeRange = document.getElementById('card-size-range');
if (cardSizeRange) {
cardSizeRange.value = App.storage.getCardScale();
cardSizeRange.oninput = () => {
App.storage.setCardScale(cardSizeRange.value);
App.ui.applyCardScale();
};
}
const textSizeRange = document.getElementById('text-size-range');
if (textSizeRange) {
textSizeRange.value = App.storage.getFontScale();
textSizeRange.oninput = () => {
App.storage.setFontScale(textSizeRange.value);
App.ui.applyFontScale();
};
}
const feedEndSelect = document.getElementById('feed-end-select'); const feedEndSelect = document.getElementById('feed-end-select');
if (feedEndSelect) { if (feedEndSelect) {
feedEndSelect.value = App.storage.getFeedEndBehavior(); feedEndSelect.value = App.storage.getFeedEndBehavior();

View File

@@ -565,6 +565,7 @@ App.videos = App.videos || {};
const measureMetrics = function() { const measureMetrics = function() {
const el = grid(); const el = grid();
if (!el) return false; if (!el) return false;
const phone = window.matchMedia('(max-width: 480px)').matches;
const mobile = window.matchMedia('(max-width: 768px)').matches; const mobile = window.matchMedia('(max-width: 768px)').matches;
const large = window.matchMedia('(min-width: 1600px)').matches; const large = window.matchMedia('(min-width: 1600px)').matches;
gap = mobile ? 12 : (large ? 24 : 16); gap = mobile ? 12 : (large ? 24 : 16);
@@ -580,8 +581,12 @@ App.videos = App.videos || {};
if (inner <= 0) return false; if (inner <= 0) return false;
// Density toggle (see enhance.js / settings) tunes the minimum card // Density toggle (see enhance.js / settings) tunes the minimum card
// width, so "compact" packs more columns at the same viewport width. // width, so "compact" packs more columns at the same viewport width.
const minCardW = document.body.dataset.density === 'compact' ? 210 : 260; // The user's Card Size setting scales that minimum on top of density.
cols = mobile ? 2 : Math.max(1, Math.floor((inner + gap) / (minCardW + gap))); const cardScale = (App.storage && App.storage.getCardScale) ? App.storage.getCardScale() : 1;
const minCardW = (document.body.dataset.density === 'compact' ? 210 : 260) * cardScale;
// One card per row on phones, two on larger handsets/tablets, and a
// size-driven count on desktop.
cols = phone ? 1 : (mobile ? 2 : Math.max(1, Math.floor((inner + gap) / (minCardW + gap))));
colWidth = (inner - gap * (cols - 1)) / cols; colWidth = (inner - gap * (cols - 1)) / cols;
return true; return true;
}; };