feat(slideshow): add image fit setting (fill vs black bars)

object-fit was hardcoded to 'cover', which crops portrait photos heavily. Add a
global `slideshow_fit` setting (Settings -> Slideshow): 'cover' fills + crops,
'contain' shows the whole image with black bars (no crop). Default 'cover'
(unchanged). Stored in app_settings (no migration), resolved server-side into
the slideshow settings + /state poll so a running projector picks it up live.
This commit is contained in:
Luca
2026-06-20 12:06:34 +02:00
parent 759784a4d1
commit b5c73e05bd
7 changed files with 65 additions and 11 deletions
+3
View File
@@ -299,6 +299,9 @@ router.put('/slideshow', adminAuth, requirePermission('settings.edit'), async (r
const push = (key, value) => updates.push({ setting_key: key, setting_value: JSON.stringify(value), setting_type: 'slideshow' });
const has = (k) => Object.prototype.hasOwnProperty.call(req.body, k);
if (has('slideshow_fit')) {
push('slideshow_fit', req.body.slideshow_fit === 'contain' ? 'contain' : 'cover');
}
if (has('slideshow_watermark_enabled')) push('slideshow_watermark_enabled', !!req.body.slideshow_watermark_enabled);
if (has('slideshow_watermark_source')) {
const v = ['logo', 'logo_dark', 'favicon', 'event'].includes(req.body.slideshow_watermark_source) ? req.body.slideshow_watermark_source : 'logo';
+5
View File
@@ -301,11 +301,16 @@ async function slideshowSettings(event) {
watermark = { url, position: position || 'bottom-right', opacity: opacity ?? 60, style: style || 'white', size: size ?? 12 };
}
}
// Image fit is a global setting (Settings → Slideshow): 'cover' fills + crops,
// 'contain' shows the whole image with black bars (no crop).
const fitRaw = await getAppSetting('slideshow_fit', 'cover');
const fit = fitRaw === 'contain' ? 'contain' : 'cover';
return {
interval_ms: event.show_interval_ms || 5000,
transition: event.show_transition || 'crossfade',
transition_ms: event.show_transition_ms || 800,
colorfilter: event.show_colorfilter || 'none',
fit,
watermark,
};
}