Add fake frame

This commit is contained in:
2026-01-13 18:40:36 +01:00
parent c2a90fdb64
commit 3940f06cd0
4 changed files with 126 additions and 40 deletions

View File

@@ -3,6 +3,10 @@
color: white; color: white;
} }
:root {
--window-frame-height: 36px;
}
p { p {
margin: 0; margin: 0;
} }
@@ -30,6 +34,65 @@ body {
padding: clamp(24px, 4vw, 48px); padding: clamp(24px, 4vw, 48px);
} }
.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);
-webkit-app-region: drag;
}
.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);
}
.channels-shell { .channels-shell {
width: 100%; width: 100%;
display: flex; display: flex;

View File

@@ -6,6 +6,22 @@
<link rel="stylesheet" href="./css/index.css" /> <link rel="stylesheet" href="./css/index.css" />
</head> </head>
<body class="channels-body"> <body class="channels-body">
<div class="window-frame" role="presentation">
<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>
</div>
<div class="channels-shell"> <div class="channels-shell">
<header class="channels-header"> <header class="channels-header">
<div class="brand"> <div class="brand">
@@ -54,6 +70,7 @@
const form = document.getElementById("channel-search-form"); const form = document.getElementById("channel-search-form");
const input = document.getElementById("channel-search"); const input = document.getElementById("channel-search");
const domainInput = document.getElementById("domain-input"); const domainInput = document.getElementById("domain-input");
const windowActionButtons = document.querySelectorAll("[data-window-action]");
window.store.loadBroadcaster().then((value) => { window.store.loadBroadcaster().then((value) => {
if (value && input.value === "") { if (value && input.value === "") {
@@ -82,13 +99,25 @@
const fallbackDomain = domainInput.placeholder || ""; const fallbackDomain = domainInput.placeholder || "";
const domain = domainInput.value.trim() || fallbackDomain; const domain = domainInput.value.trim() || fallbackDomain;
if (!channel) { if (!channel) {
return return;
}; }
const params = new URLSearchParams({ broadcaster: channel }); const params = new URLSearchParams({ broadcaster: channel });
window.store.saveDomain(domain); window.store.saveDomain(domain);
window.location.href = `${domain}/view/${encodeURIComponent(channel)}/broadcast`; window.location.href = `${domain}/view/${encodeURIComponent(channel)}/broadcast`;
}); });
windowActionButtons.forEach((button) => {
button.addEventListener("click", () => {
const action = button.dataset.windowAction;
if (action === "minimize") {
window.store.minimizeWindow();
}
if (action === "close") {
window.store.closeWindow();
}
});
});
</script> </script>
</body> </body>
</html> </html>

View File

@@ -24,49 +24,29 @@ function resolveDefaultDomain() {
return normalizeDomain(buildTimeDomain); return normalizeDomain(buildTimeDomain);
} }
function createWindowOptionsForPlatform(platform) { function createWindowOptions() {
switch (platform) { return {
case "darwin": width: INITIAL_WINDOW_WIDTH_PX,
case "linux": height: INITIAL_WINDOW_HEIGHT_PX,
return { transparent: true,
width: INITIAL_WINDOW_WIDTH_PX, frame: false,
height: INITIAL_WINDOW_HEIGHT_PX, backgroundColor: "#00000000",
transparent: true, alwaysOnTop: false,
frame: true, icon: path.join(__dirname, "../res/icon/appicon.ico"),
backgroundColor: "#00000000", webPreferences: {
alwaysOnTop: false, backgroundThrottling: false,
icon: path.join(__dirname, "../res/icon/appicon.ico"), preload: path.join(__dirname, "preload.js"),
webPreferences: { },
backgroundThrottling: false, };
preload: path.join(__dirname, "preload.js"),
},
};
case "win32":
return {
width: INITIAL_WINDOW_WIDTH_PX,
height: INITIAL_WINDOW_HEIGHT_PX,
transparent: true,
frame: false,
backgroundColor: "#00000000",
alwaysOnTop: false,
icon: path.join(__dirname, "../res/icon/appicon.ico"),
webPreferences: {
backgroundThrottling: false,
preload: path.join(__dirname, "preload.js"),
},
};
default:
throw new Error(`Unsupported platform: ${platform}`);
}
} }
function createWindow(version) { function createWindow(version) {
const windowOptions = createWindowOptionsForPlatform(process.platform); const windowOptions = createWindowOptions();
const win = new BrowserWindow(windowOptions); const win = new BrowserWindow(windowOptions);
win.setMenu(null); win.setMenu(null);
win.setFullScreenable(false) win.setFullScreenable(false);
win.setFullScreen(false) win.setFullScreen(false);
win.setResizable(false) win.setResizable(false);
win.setTitle(`Imgfloat Client v${version}`); win.setTitle(`Imgfloat Client v${version}`);
return win; return win;
@@ -78,6 +58,18 @@ ipcMain.handle("set-window-size", (_, width, height) => {
} }
}); });
ipcMain.handle("minimize-window", () => {
if (ELECTRON_WINDOW && !ELECTRON_WINDOW.isDestroyed()) {
ELECTRON_WINDOW.minimize();
}
});
ipcMain.handle("close-window", () => {
if (ELECTRON_WINDOW && !ELECTRON_WINDOW.isDestroyed()) {
ELECTRON_WINDOW.close();
}
});
ipcMain.handle("save-broadcaster", (_, broadcaster) => { ipcMain.handle("save-broadcaster", (_, broadcaster) => {
const store = readStore(STORE_PATH); const store = readStore(STORE_PATH);
store.lastBroadcaster = broadcaster; store.lastBroadcaster = broadcaster;

View File

@@ -7,4 +7,6 @@ contextBridge.exposeInMainWorld("store", {
loadDomain: () => ipcRenderer.invoke("load-domain"), loadDomain: () => ipcRenderer.invoke("load-domain"),
loadDefaultDomain: () => ipcRenderer.invoke("load-default-domain"), loadDefaultDomain: () => ipcRenderer.invoke("load-default-domain"),
setWindowSize: (width, height) => ipcRenderer.invoke("set-window-size", width, height), setWindowSize: (width, height) => ipcRenderer.invoke("set-window-size", width, height),
minimizeWindow: () => ipcRenderer.invoke("minimize-window"),
closeWindow: () => ipcRenderer.invoke("close-window"),
}); });