feat(ui): downloader-style top menu bar (Datei / Einstellungen / Hilfe)
Adds a custom HTML menu bar above the tab bar, replicating the Real-Debrid Downloader's look: click-to-open triggers, hover-switch between open menus, click-outside / Escape to close, right-flyout submenus. - Datei: Dateien hinzufügen, Ordner hinzufügen, Sicherung submenu (Export/Import backup), Neustart, Beenden. - Einstellungen: "Einstellungen öffnen" (jumps to the settings tab) plus an inline live grid with the max-parallel-uploads spinner and a speed-limit checkbox + MB/s spinner, both two-way-synced with the Settings tab fields via saveGlobalSettings (parallelUploadCount / globalMaxSpeedKbs). - Hilfe: Log-Ordner öffnen, Diagnose-Paket exportieren, Suche Aktualisierungen. Wiring reuses existing handlers (addFiles/addFolder buttons, doBackupExport/ Import, openLogFolder, createSupportBundle, checkForUpdate) and two new tiny IPC handlers app:restart (app.relaunch+quit) / app:quit. initMenuBar() is wrapped in try/catch in setupListeners so a menu fault can never break core app init. CSS maps the downloader's menu styles onto this app's theme vars. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cafe777377
commit
e5d7917cf3
9
main.js
9
main.js
@ -2180,6 +2180,15 @@ ipcMain.handle('app:get-version', () => {
|
|||||||
return app.getVersion();
|
return app.getVersion();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('app:restart', () => {
|
||||||
|
app.relaunch();
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('app:quit', () => {
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
// --- Hoster settings ---
|
// --- Hoster settings ---
|
||||||
ipcMain.handle('get-hoster-settings', () => {
|
ipcMain.handle('get-hoster-settings', () => {
|
||||||
const config = configStore.load();
|
const config = configStore.load();
|
||||||
|
|||||||
@ -56,6 +56,8 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
installUpdate: () => ipcRenderer.invoke('app:install-update'),
|
installUpdate: () => ipcRenderer.invoke('app:install-update'),
|
||||||
abortUpdate: () => ipcRenderer.invoke('app:abort-update'),
|
abortUpdate: () => ipcRenderer.invoke('app:abort-update'),
|
||||||
getVersion: () => ipcRenderer.invoke('app:get-version'),
|
getVersion: () => ipcRenderer.invoke('app:get-version'),
|
||||||
|
restartApp: () => ipcRenderer.invoke('app:restart'),
|
||||||
|
quitApp: () => ipcRenderer.invoke('app:quit'),
|
||||||
onUpdateAvailable: (callback) => {
|
onUpdateAvailable: (callback) => {
|
||||||
ipcRenderer.on('app:update-available', (_event, data) => callback(data));
|
ipcRenderer.on('app:update-available', (_event, data) => callback(data));
|
||||||
},
|
},
|
||||||
|
|||||||
164
renderer/app.js
164
renderer/app.js
@ -291,6 +291,169 @@ function _isHistoryTabActive() {
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// --- Top menu bar (Datei / Einstellungen / Hilfe) ---
|
||||||
|
function initMenuBar() {
|
||||||
|
const menuBar = document.getElementById('menuBar');
|
||||||
|
if (!menuBar) return;
|
||||||
|
let openMenu = null;
|
||||||
|
|
||||||
|
const dropdowns = {};
|
||||||
|
menuBar.querySelectorAll('[data-menu-dropdown]').forEach(d => { dropdowns[d.dataset.menuDropdown] = d; });
|
||||||
|
const triggers = {};
|
||||||
|
menuBar.querySelectorAll('[data-menu-trigger]').forEach(t => { triggers[t.dataset.menuTrigger] = t; });
|
||||||
|
|
||||||
|
function closeMenus() {
|
||||||
|
openMenu = null;
|
||||||
|
for (const k in dropdowns) dropdowns[k].style.display = 'none';
|
||||||
|
for (const k in triggers) triggers[k].classList.remove('open');
|
||||||
|
menuBar.querySelectorAll('.menu-submenu-dropdown').forEach(s => { s.style.display = 'none'; });
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMenuNamed(name) {
|
||||||
|
if (openMenu === name) return;
|
||||||
|
closeMenus();
|
||||||
|
openMenu = name;
|
||||||
|
if (dropdowns[name]) dropdowns[name].style.display = '';
|
||||||
|
if (triggers[name]) triggers[name].classList.add('open');
|
||||||
|
if (name === 'einstellungen') _syncMenuSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
menuBar.querySelectorAll('[data-menu-trigger]').forEach(trigger => {
|
||||||
|
const name = trigger.dataset.menuTrigger;
|
||||||
|
trigger.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (openMenu === name) closeMenus(); else openMenuNamed(name);
|
||||||
|
});
|
||||||
|
trigger.addEventListener('mouseenter', () => { if (openMenu && openMenu !== name) openMenuNamed(name); });
|
||||||
|
});
|
||||||
|
|
||||||
|
menuBar.querySelectorAll('.menu-submenu').forEach(sm => {
|
||||||
|
const sub = sm.querySelector('.menu-submenu-dropdown');
|
||||||
|
sm.addEventListener('mouseenter', () => { if (sub) sub.style.display = ''; });
|
||||||
|
sm.addEventListener('mouseleave', () => { if (sub) sub.style.display = 'none'; });
|
||||||
|
});
|
||||||
|
|
||||||
|
menuBar.querySelectorAll('[data-menu-action]').forEach(item => {
|
||||||
|
item.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const action = item.dataset.menuAction;
|
||||||
|
closeMenus();
|
||||||
|
_handleMenuAction(action);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', (e) => { if (openMenu && !e.target.closest('.menu-bar')) closeMenus(); });
|
||||||
|
document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && openMenu) closeMenus(); });
|
||||||
|
|
||||||
|
_initMenuSettingsControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _handleMenuAction(action) {
|
||||||
|
switch (action) {
|
||||||
|
case 'add-files': document.getElementById('addFilesBtn')?.click(); break;
|
||||||
|
case 'add-folder': document.getElementById('addFolderBtn')?.click(); break;
|
||||||
|
case 'backup-export': doBackupExport(); break;
|
||||||
|
case 'backup-import': doBackupImport(); break;
|
||||||
|
case 'restart': if (confirm('Anwendung neu starten?')) window.api.restartApp(); break;
|
||||||
|
case 'quit': window.api.quitApp(); break;
|
||||||
|
case 'open-settings': document.querySelector('.tab[data-view="settings"]')?.click(); break;
|
||||||
|
case 'open-log-folder': window.api.openLogFolder(); break;
|
||||||
|
case 'support-bundle': {
|
||||||
|
showCopyToast('Diagnose-Paket wird erstellt…');
|
||||||
|
try {
|
||||||
|
const res = await window.api.createSupportBundle();
|
||||||
|
if (res && res.ok) showCopyToast(`Diagnose-Paket gespeichert (${(res.bytes / 1024).toFixed(1)} KB)`);
|
||||||
|
else if (res && res.canceled) showCopyToast('Abgebrochen');
|
||||||
|
else showCopyToast(`Fehler: ${(res && res.error) || 'unbekannt'}`);
|
||||||
|
} catch (err) { showCopyToast(`Fehler: ${err.message || err}`); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'check-updates': {
|
||||||
|
showCopyToast('Suche nach Updates…');
|
||||||
|
try {
|
||||||
|
const result = await window.api.checkForUpdate();
|
||||||
|
if (result && result.available) { showUpdateBanner(result); showCopyToast('Update gefunden!'); }
|
||||||
|
else showCopyToast('Kein Update verfügbar');
|
||||||
|
} catch { showCopyToast('Fehler beim Prüfen'); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _syncMenuSettings() {
|
||||||
|
const gs = config.globalSettings || {};
|
||||||
|
const pInput = document.getElementById('menuParallelInput');
|
||||||
|
if (pInput) pInput.value = String(gs.parallelUploadCount ?? 0);
|
||||||
|
const speedKbs = gs.globalMaxSpeedKbs || 0;
|
||||||
|
const enabled = speedKbs > 0;
|
||||||
|
const sCheck = document.getElementById('menuSpeedLimitCheck');
|
||||||
|
const sInput = document.getElementById('menuSpeedInput');
|
||||||
|
const sSpinner = document.getElementById('menuSpeedSpinner');
|
||||||
|
if (sCheck) sCheck.checked = enabled;
|
||||||
|
if (sInput) sInput.value = enabled ? String(+(speedKbs / 1024).toFixed(2)) : '0';
|
||||||
|
if (sSpinner) sSpinner.classList.toggle('disabled', !enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _menuSaveParallel(v) {
|
||||||
|
const n = Math.max(0, Math.min(100, parseInt(v, 10) || 0));
|
||||||
|
const gs = { ...(config.globalSettings || {}), parallelUploadCount: n };
|
||||||
|
config.globalSettings = gs;
|
||||||
|
window.api.saveGlobalSettings(gs).catch(() => {});
|
||||||
|
const mirror = document.getElementById('parallelUploadCountInput');
|
||||||
|
if (mirror) mirror.value = String(n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _menuSaveSpeedMbs(mbs, enabled) {
|
||||||
|
const kbs = enabled ? Math.max(0, Math.round((parseFloat(mbs) || 0) * 1024)) : 0;
|
||||||
|
const gs = { ...(config.globalSettings || {}), globalMaxSpeedKbs: kbs };
|
||||||
|
config.globalSettings = gs;
|
||||||
|
window.api.saveGlobalSettings(gs).catch(() => {});
|
||||||
|
const mirror = document.getElementById('globalMaxSpeedMbsInput');
|
||||||
|
if (mirror) mirror.value = kbs > 0 ? String(+(kbs / 1024).toFixed(2)) : '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
function _initMenuSettingsControls() {
|
||||||
|
const grid = document.getElementById('menuSettingsGrid');
|
||||||
|
if (grid) grid.addEventListener('click', (e) => e.stopPropagation());
|
||||||
|
|
||||||
|
const pInput = document.getElementById('menuParallelInput');
|
||||||
|
if (pInput) {
|
||||||
|
const save = () => { pInput.value = String(_menuSaveParallel(pInput.value)); };
|
||||||
|
pInput.addEventListener('change', save);
|
||||||
|
pInput.addEventListener('blur', save);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sInput = document.getElementById('menuSpeedInput');
|
||||||
|
const sCheck = document.getElementById('menuSpeedLimitCheck');
|
||||||
|
const sSpinner = document.getElementById('menuSpeedSpinner');
|
||||||
|
if (sCheck) sCheck.addEventListener('change', () => {
|
||||||
|
if (sSpinner) sSpinner.classList.toggle('disabled', !sCheck.checked);
|
||||||
|
_menuSaveSpeedMbs(sInput ? sInput.value : 0, sCheck.checked);
|
||||||
|
});
|
||||||
|
if (sInput) {
|
||||||
|
const save = () => _menuSaveSpeedMbs(sInput.value, sCheck ? sCheck.checked : true);
|
||||||
|
sInput.addEventListener('change', save);
|
||||||
|
sInput.addEventListener('blur', save);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('#menuSettingsGrid [data-spin]').forEach(btn => {
|
||||||
|
btn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const kind = btn.dataset.spin;
|
||||||
|
if (kind === 'parallel-up' || kind === 'parallel-down') {
|
||||||
|
const cur = parseInt(pInput.value, 10) || 0;
|
||||||
|
pInput.value = String(_menuSaveParallel(cur + (kind === 'parallel-up' ? 1 : -1)));
|
||||||
|
} else if (kind === 'speed-up' || kind === 'speed-down') {
|
||||||
|
const cur = parseFloat(sInput.value) || 0;
|
||||||
|
const next = Math.max(0, cur + (kind === 'speed-up' ? 1 : -1));
|
||||||
|
sInput.value = String(next);
|
||||||
|
_menuSaveSpeedMbs(next, sCheck ? sCheck.checked : true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// --- Hoster selection ---
|
// --- Hoster selection ---
|
||||||
function accountHasCreds(name, account) {
|
function accountHasCreds(name, account) {
|
||||||
if (!account) return false;
|
if (!account) return false;
|
||||||
@ -4464,6 +4627,7 @@ window.addEventListener('beforeunload', () => {
|
|||||||
|
|
||||||
// --- Setup Listeners ---
|
// --- Setup Listeners ---
|
||||||
function setupListeners() {
|
function setupListeners() {
|
||||||
|
try { initMenuBar(); } catch (err) { console.error('menu bar init failed', err); }
|
||||||
document.getElementById('addFilesBtn').addEventListener('click', pickFiles);
|
document.getElementById('addFilesBtn').addEventListener('click', pickFiles);
|
||||||
document.getElementById('addFolderBtn').addEventListener('click', pickFolder);
|
document.getElementById('addFolderBtn').addEventListener('click', pickFolder);
|
||||||
document.getElementById('startUploadBtn').addEventListener('click', startUpload);
|
document.getElementById('startUploadBtn').addEventListener('click', startUpload);
|
||||||
|
|||||||
@ -7,6 +7,65 @@
|
|||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<nav class="menu-bar" id="menuBar">
|
||||||
|
<div class="menu-bar-item" data-menu="datei">
|
||||||
|
<button class="menu-bar-trigger" data-menu-trigger="datei">Datei</button>
|
||||||
|
<div class="menu-dropdown" data-menu-dropdown="datei" style="display:none">
|
||||||
|
<button class="menu-dropdown-item" data-menu-action="add-files"><span>Dateien hinzufügen</span></button>
|
||||||
|
<button class="menu-dropdown-item" data-menu-action="add-folder"><span>Ordner hinzufügen</span></button>
|
||||||
|
<div class="menu-separator"></div>
|
||||||
|
<div class="menu-submenu" data-submenu="sicherung">
|
||||||
|
<button class="menu-submenu-trigger">Sicherung</button>
|
||||||
|
<div class="menu-submenu-dropdown" style="display:none">
|
||||||
|
<button class="menu-dropdown-item" data-menu-action="backup-export"><span>Exportieren</span></button>
|
||||||
|
<button class="menu-dropdown-item" data-menu-action="backup-import"><span>Importieren</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="menu-separator"></div>
|
||||||
|
<button class="menu-dropdown-item" data-menu-action="restart"><span>Neustart</span></button>
|
||||||
|
<button class="menu-dropdown-item" data-menu-action="quit"><span>Beenden</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="menu-bar-item" data-menu="einstellungen">
|
||||||
|
<button class="menu-bar-trigger" data-menu-trigger="einstellungen">Einstellungen</button>
|
||||||
|
<div class="menu-dropdown" data-menu-dropdown="einstellungen" style="display:none">
|
||||||
|
<button class="menu-dropdown-item" data-menu-action="open-settings"><span>Einstellungen öffnen</span></button>
|
||||||
|
<div class="menu-separator"></div>
|
||||||
|
<div class="menu-settings-grid" id="menuSettingsGrid">
|
||||||
|
<span>Max. parallele Uploads</span>
|
||||||
|
<span></span>
|
||||||
|
<div class="menu-spinner">
|
||||||
|
<input type="text" inputmode="numeric" id="menuParallelInput">
|
||||||
|
<div class="menu-spinner-arrows">
|
||||||
|
<button data-spin="parallel-up">▲</button>
|
||||||
|
<button data-spin="parallel-down">▼</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span></span>
|
||||||
|
<span>Geschwindigkeitslimit</span>
|
||||||
|
<input type="checkbox" id="menuSpeedLimitCheck">
|
||||||
|
<div class="menu-spinner" id="menuSpeedSpinner">
|
||||||
|
<input type="text" inputmode="decimal" id="menuSpeedInput">
|
||||||
|
<div class="menu-spinner-arrows">
|
||||||
|
<button data-spin="speed-up">▲</button>
|
||||||
|
<button data-spin="speed-down">▼</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="menu-speed-unit">MB/s</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="menu-bar-item" data-menu="hilfe">
|
||||||
|
<button class="menu-bar-trigger" data-menu-trigger="hilfe">Hilfe</button>
|
||||||
|
<div class="menu-dropdown" data-menu-dropdown="hilfe" style="display:none">
|
||||||
|
<button class="menu-dropdown-item" data-menu-action="open-log-folder"><span>Log-Ordner öffnen</span></button>
|
||||||
|
<button class="menu-dropdown-item" data-menu-action="support-bundle"><span>Diagnose-Paket exportieren</span></button>
|
||||||
|
<div class="menu-separator"></div>
|
||||||
|
<button class="menu-dropdown-item" data-menu-action="check-updates"><span>Suche Aktualisierungen</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<nav class="tab-bar">
|
<nav class="tab-bar">
|
||||||
<button class="tab active" data-view="upload">Upload</button>
|
<button class="tab active" data-view="upload">Upload</button>
|
||||||
<button class="tab" data-view="accounts">Accounts</button>
|
<button class="tab" data-view="accounts">Accounts</button>
|
||||||
|
|||||||
@ -34,6 +34,165 @@ body {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Menu Bar (Datei / Einstellungen / Hilfe) */
|
||||||
|
.menu-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0;
|
||||||
|
user-select: none;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 2px 10px;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(0, 0, 0, 0.06));
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 200;
|
||||||
|
}
|
||||||
|
.menu-bar-item { position: relative; }
|
||||||
|
.menu-bar-trigger {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text);
|
||||||
|
padding: 5px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background 0.1s;
|
||||||
|
}
|
||||||
|
.menu-bar-trigger:hover,
|
||||||
|
.menu-bar-trigger.open { background: var(--bg-card-hover); }
|
||||||
|
.menu-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
min-width: max-content;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 0;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.menu-dropdown-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text);
|
||||||
|
padding: 7px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: left;
|
||||||
|
transition: background 0.1s;
|
||||||
|
}
|
||||||
|
.menu-dropdown-item:hover { background: var(--bg-card-hover); }
|
||||||
|
.menu-dropdown-item .shortcut {
|
||||||
|
color: var(--text-dim);
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.menu-separator { height: 1px; background: var(--border); margin: 4px 8px; }
|
||||||
|
.menu-submenu { position: relative; }
|
||||||
|
.menu-submenu-trigger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text);
|
||||||
|
padding: 7px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: left;
|
||||||
|
transition: background 0.1s;
|
||||||
|
}
|
||||||
|
.menu-submenu-trigger:hover { background: var(--bg-card-hover); }
|
||||||
|
.menu-submenu-trigger::after {
|
||||||
|
content: "\25B6";
|
||||||
|
font-size: 8px;
|
||||||
|
color: var(--text-dim);
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
.menu-submenu-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
left: 100%;
|
||||||
|
z-index: 1001;
|
||||||
|
min-width: max-content;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 0;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
.menu-settings-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto auto auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px 8px;
|
||||||
|
padding: 6px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.menu-spinner {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
.menu-spinner.disabled { opacity: 0.45; pointer-events: none; }
|
||||||
|
.menu-spinner input[type="text"] {
|
||||||
|
width: 46px;
|
||||||
|
background: var(--bg-input);
|
||||||
|
color: var(--text);
|
||||||
|
border: none;
|
||||||
|
padding: 0 6px;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.menu-spinner-arrows { display: flex; flex-direction: column; border-left: 1px solid var(--border); }
|
||||||
|
.menu-spinner-arrows button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: none;
|
||||||
|
color: var(--text-dim);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
width: 18px;
|
||||||
|
flex: 1;
|
||||||
|
font-size: 8px;
|
||||||
|
line-height: 1;
|
||||||
|
transition: background 0.1s;
|
||||||
|
}
|
||||||
|
.menu-spinner-arrows button:hover { background: var(--bg-card-hover); color: var(--text); }
|
||||||
|
.menu-spinner-arrows button:first-child { border-bottom: 1px solid var(--border); }
|
||||||
|
.menu-settings-grid input[type="checkbox"] {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
accent-color: var(--accent);
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.menu-speed-unit { color: var(--text-dim); font-size: 12px; white-space: nowrap; }
|
||||||
|
|
||||||
/* Tab Bar */
|
/* Tab Bar */
|
||||||
.tab-bar {
|
.tab-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user