Add fake frame for electron window

This commit is contained in:
2026-01-13 18:41:22 +01:00
parent 8dc4cc72e8
commit 8ddda451f3
3 changed files with 132 additions and 4 deletions

View File

@@ -2,6 +2,10 @@
box-sizing: border-box; box-sizing: border-box;
} }
:root {
--window-frame-height: 36px;
}
p { p {
margin: 0; margin: 0;
} }
@@ -1142,6 +1146,71 @@ button:disabled:hover {
background: transparent; background: transparent;
} }
.window-frame {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: var(--window-frame-height);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
background: rgba(15, 23, 42, 0.9);
border-bottom: 1px solid rgba(30, 41, 59, 0.7);
z-index: 5;
-webkit-app-region: drag;
}
.broadcast-body.has-window-frame #broadcast-canvas,
.broadcast-body.has-window-frame .broadcast-script-layer {
top: var(--window-frame-height);
}
.window-frame-title {
font-size: 12px;
letter-spacing: 0.3px;
color: #cbd5f5;
text-transform: uppercase;
}
.window-frame-controls {
display: flex;
align-items: center;
gap: 6px;
-webkit-app-region: no-drag;
}
.window-control {
width: 30px;
height: 24px;
padding: 0;
border-radius: 6px;
background: rgba(148, 163, 184, 0.2);
border: 1px solid rgba(148, 163, 184, 0.25);
color: #e2e8f0;
font-size: 16px;
line-height: 1;
box-shadow: none;
}
.window-control:hover {
background: rgba(148, 163, 184, 0.35);
}
.window-control:active {
transform: none;
}
.window-control-close {
background: rgba(239, 68, 68, 0.3);
border-color: rgba(239, 68, 68, 0.4);
}
.window-control-close:hover {
background: rgba(239, 68, 68, 0.5);
}
.panel { .panel {
margin-top: 24px; margin-top: 24px;
padding: 16px; padding: 16px;

View File

@@ -1,9 +1,11 @@
import { BroadcastRenderer } from "./broadcast/renderer.js"; import { BroadcastRenderer } from "./broadcast/renderer.js";
import { connectTwitchChat } from "./broadcast/twitchChat.js"; import { connectTwitchChat } from "./broadcast/twitchChat.js";
import { setUpElectronWindowResizeListener } from "./electron.js"; import { setUpElectronWindowFrame, setUpElectronWindowResizeListener } from "./electron.js";
const canvas = document.getElementById("broadcast-canvas"); const canvas = document.getElementById("broadcast-canvas");
const scriptLayer = document.getElementById("broadcast-script-layer"); const scriptLayer = document.getElementById("broadcast-script-layer");
setUpElectronWindowFrame();
const renderer = new BroadcastRenderer({ canvas, scriptLayer, broadcaster, showToast }); const renderer = new BroadcastRenderer({ canvas, scriptLayer, broadcaster, showToast });
const disconnectChat = connectTwitchChat(broadcaster, ({ channel, displayName, message }) => { const disconnectChat = connectTwitchChat(broadcaster, ({ channel, displayName, message }) => {
console.log(`[twitch:${broadcaster}] ${displayName}: ${message}`); console.log(`[twitch:${broadcaster}] ${displayName}: ${message}`);

View File

@@ -1,9 +1,63 @@
export function setUpElectronWindowResizeListener(canvas) { function canManageElectronWindow() {
if ( return (
typeof window !== "undefined" && typeof window !== "undefined" &&
window.store && window.store &&
typeof window.store.setWindowSize === "function" typeof window.store.setWindowSize === "function"
);
}
function getWindowFrameHeight() {
const height = getComputedStyle(document.documentElement)
.getPropertyValue("--window-frame-height")
.trim();
const parsed = Number.parseInt(height.replace("px", ""), 10);
return Number.isNaN(parsed) ? 0 : parsed;
}
export function setUpElectronWindowFrame() {
if (
typeof window === "undefined" ||
!window.store ||
typeof window.store.minimizeWindow !== "function"
) { ) {
return false;
}
document.body.classList.add("has-window-frame");
const frame = document.createElement("div");
frame.className = "window-frame";
frame.setAttribute("role", "presentation");
frame.innerHTML = `
<div class="window-frame-title">Imgfloat</div>
<div class="window-frame-controls">
<button class="window-control" type="button" data-window-action="minimize" aria-label="Minimize">
&minus;
</button>
<button class="window-control window-control-close" type="button" data-window-action="close" aria-label="Close">
&times;
</button>
</div>
`;
document.body.appendChild(frame);
frame.querySelectorAll("[data-window-action]").forEach((button) => {
button.addEventListener("click", () => {
const action = button.dataset.windowAction;
if (action === "minimize") {
window.store.minimizeWindow();
}
if (action === "close") {
window.store.closeWindow();
}
});
});
return true;
}
export function setUpElectronWindowResizeListener(canvas) {
if (canManageElectronWindow()) {
console.info("Electron environment detected, setting up resize listener."); console.info("Electron environment detected, setting up resize listener.");
} else { } else {
console.info("Not running in Electron environment, skipping resize listener setup."); console.info("Not running in Electron environment, skipping resize listener setup.");
@@ -12,9 +66,12 @@ export function setUpElectronWindowResizeListener(canvas) {
const resize = () => { const resize = () => {
const rect = canvas.getBoundingClientRect(); const rect = canvas.getBoundingClientRect();
const frameHeight = document.body.classList.contains("has-window-frame")
? getWindowFrameHeight()
: 0;
window.store.setWindowSize( window.store.setWindowSize(
Math.ceil(rect.width), Math.ceil(rect.width),
Math.ceil(rect.height) Math.ceil(rect.height + frameHeight)
); );
}; };