From 217ecea74a959a13a48c8bc91c9ff452b32e0679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kr=C3=BChlmann?= Date: Thu, 1 Jan 2026 16:46:38 +0100 Subject: [PATCH] Fixed sized --- src/main/node/app.js | 101 ++++++++++++++++++------ src/main/resources/static/js/landing.js | 15 +--- 2 files changed, 80 insertions(+), 36 deletions(-) diff --git a/src/main/node/app.js b/src/main/node/app.js index 3ad0317..3418c9b 100644 --- a/src/main/node/app.js +++ b/src/main/node/app.js @@ -2,8 +2,8 @@ const { app, BrowserWindow } = require('electron'); function createWindow() { const url = process.env.ELECTRON_START_URL || "https://imgfloat.kruhlmann.dev/channels"; - const width = Number.parseInt(process.env.ELECTRON_WINDOW_WIDTH, 10) || 1920; - const height = Number.parseInt(process.env.ELECTRON_WINDOW_HEIGHT, 10) || 1080; + const width = Number.parseInt(process.env.ELECTRON_WINDOW_WIDTH, 10) || 960; + const height = Number.parseInt(process.env.ELECTRON_WINDOW_HEIGHT, 10) || 640; const win = new BrowserWindow({ width: width, @@ -17,6 +17,65 @@ function createWindow() { } }); + let canvasSizeInterval; + + const clearCanvasSizeInterval = () => { + if (canvasSizeInterval) { + clearInterval(canvasSizeInterval); + canvasSizeInterval = undefined; + } + }; + + const lockWindowToCanvas = async () => { + if (win.isDestroyed()) { + return false; + } + try { + const size = await win.webContents.executeJavaScript(`(() => { + const canvas = document.getElementById('broadcast-canvas'); + if (!canvas || !canvas.width || !canvas.height) { + return null; + } + return { width: Math.round(canvas.width), height: Math.round(canvas.height) }; + })();`); + + if (size?.width && size?.height) { + const [currentWidth, currentHeight] = win.getSize(); + if (currentWidth !== size.width || currentHeight !== size.height) { + win.setSize(size.width, size.height, false); + } + win.setMinimumSize(size.width, size.height); + win.setMaximumSize(size.width, size.height); + win.setResizable(false); + return true; + } + } catch (error) { + // Best-effort sizing; ignore errors from early navigation states. + } + return false; + }; + + const handleNavigation = (navigationUrl) => { + try { + const { pathname } = new URL(navigationUrl); + const isBroadcast = /\\/view\\/[^/]+\\/broadcast\\/?$/.test(pathname); + + if (isBroadcast) { + clearCanvasSizeInterval(); + canvasSizeInterval = setInterval(lockWindowToCanvas, 750); + lockWindowToCanvas(); + } else { + clearCanvasSizeInterval(); + win.setResizable(true); + win.setMinimumSize(320, 240); + win.setMaximumSize(10000, 10000); + win.setSize(width, height, false); + } + } catch { + // Ignore malformed URLs while navigating. + } + }; + win.loadURL(url); win.webContents.on('did-finish-load', () => { @@ -26,6 +85,10 @@ function createWindow() { user-select: none; } + body { + transition: opacity 120ms ease; + } + a, button, input, select, textarea, option, [role="button"], [role="textbox"] { -webkit-app-region: no-drag; user-select: auto; @@ -33,42 +96,32 @@ function createWindow() { `); win.webContents.executeJavaScript(`(() => { - const overlayId = 'imgfloat-drag-overlay'; - if (!document.getElementById(overlayId)) { - const overlay = document.createElement('div'); - overlay.id = overlayId; - Object.assign(overlay.style, { - position: 'fixed', - inset: '0', - background: 'rgba(0, 0, 0, 0)', - transition: 'background 120ms ease', - pointerEvents: 'none', - zIndex: '2147483647', - webkitAppRegion: 'drag' - }); - document.documentElement.appendChild(overlay); + const body = document.body; + if (!body) { + return; } - - const overlay = document.getElementById(overlayId); + const defaultOpacity = getComputedStyle(body).opacity || '1'; let timeout; window.__imgfloatShowDragOverlay = () => { - if (!overlay) { - return; - } - - overlay.style.background = 'rgba(0, 0, 0, 0.35)'; + body.style.opacity = '0.9'; clearTimeout(timeout); timeout = setTimeout(() => { - overlay.style.background = 'rgba(0, 0, 0, 0)'; + body.style.opacity = defaultOpacity; }, 150); }; })();`); + + handleNavigation(win.webContents.getURL()); }); win.on('move', () => { win.webContents.executeJavaScript('window.__imgfloatShowDragOverlay && window.__imgfloatShowDragOverlay();'); }); + + win.webContents.on('did-navigate', (_event, navigationUrl) => handleNavigation(navigationUrl)); + win.webContents.on('did-navigate-in-page', (_event, navigationUrl) => handleNavigation(navigationUrl)); + win.on('closed', clearCanvasSizeInterval); } app.whenReady().then(() => { diff --git a/src/main/resources/static/js/landing.js b/src/main/resources/static/js/landing.js index f33e3e7..13138e7 100644 --- a/src/main/resources/static/js/landing.js +++ b/src/main/resources/static/js/landing.js @@ -8,13 +8,6 @@ document.addEventListener("DOMContentLoaded", () => { return; } - function keepInputFocused() { - if (document.activeElement !== searchInput) { - searchInput.focus({ preventScroll: true }); - searchInput.select(); - } - } - let channels = []; function updateSuggestions(term) { @@ -44,10 +37,10 @@ document.addEventListener("DOMContentLoaded", () => { } } + searchInput.focus({ preventScroll: true }); + searchInput.select(); + searchInput.addEventListener("input", (event) => updateSuggestions(event.target.value || "")); - searchInput.addEventListener("blur", () => { - requestAnimationFrame(keepInputFocused); - }); searchForm.addEventListener("submit", (event) => { event.preventDefault(); @@ -59,7 +52,5 @@ document.addEventListener("DOMContentLoaded", () => { window.location.href = `/view/${encodeURIComponent(broadcaster)}/broadcast`; }); - keepInputFocused(); - window.addEventListener("focus", keepInputFocused); loadChannels(); });