feat(ui): frameless modern window frame with dark titlebar controls and sleek dark scrollbars
Build Windows Installer / build (push) Successful in 18m51s

This commit is contained in:
m4xx1m committed 2026-11-12 20:40:36 +03:00
1 parent 375cff78cb
commit 99d8e97f83
5 files changed
+197 -6

No files matched your search

+72 -1
View File
@@ -124,7 +124,7 @@ function renderShell() {
const root = document.getElementById('app')!;
const verText = state.appInfo?.version ? `v${state.appInfo.version}` : 'v2.0';
root.innerHTML = `
<header class="topbar">
<header class="topbar" id="topbar">
<div class="brand">
<div class="brand-icon">▶</div>
<span class="brand-title">ConvertGUI</span>
@@ -140,6 +140,17 @@ function renderShell() {
<div class="topbar-right">
<button id="ffmpegStatusBtn" class="status-pill warn">${tr.ffmpegMissing}</button>
<button id="langBtn">${state.lang.toUpperCase()}</button>
<div class="win-controls" id="winControls">
<button id="winMinBtn" class="win-btn" title="Свернуть">
<svg width="10" height="1" viewBox="0 0 10 1"><rect width="10" height="1" fill="currentColor"/></svg>
</button>
<button id="winMaxBtn" class="win-btn" title="Развернуть">
<svg id="winMaxIcon" width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.2"><rect x="0.6" y="0.6" width="8.8" height="8.8"/></svg>
</button>
<button id="winCloseBtn" class="win-btn close" title="Закрыть">
<svg width="10" height="10" viewBox="0 0 10 10" stroke="currentColor" stroke-width="1.2"><line x1="0.5" y1="0.5" x2="9.5" y2="9.5"/><line x1="9.5" y1="0.5" x2="0.5" y2="9.5"/></svg>
</button>
</div>
</div>
</header>
@@ -899,6 +910,7 @@ function bindGlobalEvents() {
window.addEventListener('resize', () => {
fitVideoStage();
void syncMaximizedUI();
});
window.addEventListener('keydown', (e) => {
@@ -1285,7 +1297,66 @@ function bindEvents() {
if (state.donePath) void api?.OpenOutputFile(state.donePath);
});
// Window titlebar controls (frameless mode)
el<'button'>('winMinBtn')?.addEventListener('click', async () => {
if (window.runtime?.WindowMinimise) {
window.runtime.WindowMinimise();
} else {
await api?.WindowMinimise();
}
});
const toggleMaximize = async () => {
if (window.runtime?.WindowToggleMaximise) {
window.runtime.WindowToggleMaximise();
} else {
await api?.WindowToggleMaximise();
}
await syncMaximizedUI();
};
el<'button'>('winMaxBtn')?.addEventListener('click', () => {
void toggleMaximize();
});
el<'button'>('winCloseBtn')?.addEventListener('click', async () => {
if (window.runtime?.WindowClose) {
window.runtime.WindowClose();
} else {
await api?.WindowClose();
}
});
el<'header'>('topbar')?.addEventListener('dblclick', (e) => {
const target = e.target as HTMLElement;
if (target.closest('button, input, select, .file-pill')) return;
void toggleMaximize();
});
bindWheelControls();
void syncMaximizedUI();
}
async function syncMaximizedUI() {
const api = appApi();
let isMax = false;
try {
if (window.runtime?.WindowIsMaximised) {
isMax = await window.runtime.WindowIsMaximised();
} else if (api?.WindowIsMaximised) {
isMax = await api.WindowIsMaximised();
}
} catch {
// ignore
}
document.body.classList.toggle('maximized', isMax);
const maxBtn = el<'button'>('winMaxBtn');
if (maxBtn) {
maxBtn.innerHTML = isMax
? `<svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.1"><rect x="2.5" y="0.5" width="7" height="7"/><polyline points="2.5,2.5 0.5,2.5 0.5,9.5 7.5,9.5 7.5,7.5"/></svg>`
: `<svg id="winMaxIcon" width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.2"><rect x="0.6" y="0.6" width="8.8" height="8.8"/></svg>`;
maxBtn.title = isMax ? 'Восстановить' : 'Развернуть';
}
}
function setupWailsEvents() {