Info-Menü mit Benutzerhandbuch/Disclaimer-Dialog ergaenzen
Neues (i)-Symbol in der Titelleiste öffnet ein Dropdown mit den Einträgen "Benutzerhandbuch" und "Disclaimer". Klick auf einen Eintrag zeigt den jeweiligen Text als modalen, scrollbaren Dialog (marked wandelt die .md-Dateien zur Laufzeit im Main-Prozess in HTML um). Beide Dateien sind jetzt auch in build.files gelistet, damit sie im gepackten Release verfügbar sind. Claude-Session: https://claude.ai/code/session_01VeqoaAZDVSCU4QBtiYDC5C
This commit is contained in:
Generated
+15
@@ -8,6 +8,9 @@
|
|||||||
"name": "claude-live-dashboard",
|
"name": "claude-live-dashboard",
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"marked": "^18.0.11"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^43.2.0",
|
"electron": "^43.2.0",
|
||||||
"electron-builder": "^25.1.8"
|
"electron-builder": "^25.1.8"
|
||||||
@@ -3048,6 +3051,18 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/marked": {
|
||||||
|
"version": "18.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked/-/marked-18.0.11.tgz",
|
||||||
|
"integrity": "sha512-HnslJfsZkRPBDJRHvVtAaWlZHEpSu7u8LgQuJCELjRKuWR+hpq4A7sLq3p8HaI9ypVoXDXxV34CsQJEe1+J5Aw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"marked": "bin/marked.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
|
|||||||
@@ -35,6 +35,8 @@
|
|||||||
"src/**/*",
|
"src/**/*",
|
||||||
"assets/**/*",
|
"assets/**/*",
|
||||||
"config.json",
|
"config.json",
|
||||||
|
"BENUTZERHANDBUCH.md",
|
||||||
|
"DISCLAIMER.md",
|
||||||
"!**/*.map"
|
"!**/*.map"
|
||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
@@ -97,5 +99,8 @@
|
|||||||
},
|
},
|
||||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"marked": "^18.0.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/** Brücke für den Benutzerhandbuch-/Disclaimer-Dialog. Bewusst getrennt vom Widget-Preload. */
|
||||||
|
|
||||||
|
const { contextBridge, ipcRenderer } = require('electron');
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('infoBridge', {
|
||||||
|
/** Registriert den Empfänger für Titel/Inhalt, gesendet bei jedem Öffnen. */
|
||||||
|
onContent(callback) {
|
||||||
|
ipcRenderer.on('info:content', (_event, data) => callback(data));
|
||||||
|
},
|
||||||
|
/** Schließt das Fenster. */
|
||||||
|
close: () => ipcRenderer.send('info:close'),
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Benutzerhandbuch-/Disclaimer-Dialog. Zeigt nur an, was der Main-Prozess
|
||||||
|
* schickt — Datei lesen und Markdown→HTML wandeln passiert dort (siehe
|
||||||
|
* `openInfoDoc()` in `src/main/index.js`).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const $ = (id) => document.getElementById(id);
|
||||||
|
|
||||||
|
const el = {
|
||||||
|
title: $('title'),
|
||||||
|
content: $('content'),
|
||||||
|
close: $('btn-close'),
|
||||||
|
};
|
||||||
|
|
||||||
|
window.infoBridge.onContent(({ title, html }) => {
|
||||||
|
document.title = title;
|
||||||
|
el.title.textContent = title;
|
||||||
|
el.content.innerHTML = html;
|
||||||
|
el.content.scrollTop = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
el.close.addEventListener('click', () => window.infoBridge.close());
|
||||||
|
document.addEventListener('keydown', (ev) => {
|
||||||
|
if (ev.key === 'Escape') window.infoBridge.close();
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'self'; script-src 'self';" />
|
||||||
|
<title>Info</title>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="titlebar">
|
||||||
|
<span class="title" id="title">Info</span>
|
||||||
|
<span class="spacer"></span>
|
||||||
|
<button class="icon-btn" id="btn-close" title="Schließen" aria-label="Schließen">✕</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="content" id="content"></main>
|
||||||
|
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
/* Benutzerhandbuch-/Disclaimer-Fenster — gleiche Farbwelt wie das Widget,
|
||||||
|
plus Basis-Typografie für aus Markdown gerendertes HTML. */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
|
||||||
|
--surface: #1e1e22;
|
||||||
|
--surface-raised: rgba(255, 255, 255, 0.05);
|
||||||
|
|
||||||
|
--text-primary: #ffffff;
|
||||||
|
--text-secondary: #c3c2b7;
|
||||||
|
--text-muted: #898781;
|
||||||
|
--hairline: rgba(255, 255, 255, 0.1);
|
||||||
|
|
||||||
|
--accent: #3987e5;
|
||||||
|
|
||||||
|
--font: system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||||
|
--font-mono: ui-monospace, "Cascadia Mono", Consolas, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--surface);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-family: var(--font);
|
||||||
|
font-size: 13px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Titelleiste ─────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.titlebar {
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 9px 10px 9px 14px;
|
||||||
|
border-bottom: 1px solid var(--hairline);
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn {
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 22px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn:hover {
|
||||||
|
background: var(--surface-raised);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Inhalt (aus Markdown gerendertes HTML) ─────────────────────────────── */
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 16px 20px 22px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h1,
|
||||||
|
.content h2,
|
||||||
|
.content h3 {
|
||||||
|
color: var(--text-primary);
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h1 {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
font-size: 18px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid var(--hairline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h2 {
|
||||||
|
margin: 24px 0 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h3 {
|
||||||
|
margin: 18px 0 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content p {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content ul,
|
||||||
|
.content ol {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
padding-left: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content li {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content a {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content code {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 12px;
|
||||||
|
background: var(--surface-raised);
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 1px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content pre {
|
||||||
|
background: var(--surface-raised);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content pre code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid var(--hairline);
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 0 16px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content th,
|
||||||
|
.content td {
|
||||||
|
border: 1px solid var(--hairline);
|
||||||
|
padding: 6px 8px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content th {
|
||||||
|
color: var(--text-primary);
|
||||||
|
background: var(--surface-raised);
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const { app, BrowserWindow, Tray, Menu, ipcMain, screen, nativeImage, shell, Notification } = require('electron');
|
const { app, BrowserWindow, Tray, Menu, ipcMain, screen, nativeImage, shell, Notification } = require('electron');
|
||||||
|
const { marked } = require('marked');
|
||||||
|
|
||||||
const { Store, loadConfig } = require('./store');
|
const { Store, loadConfig } = require('./store');
|
||||||
const { Collector } = require('./collector');
|
const { Collector } = require('./collector');
|
||||||
@@ -30,6 +31,16 @@ const SETTINGS_HEIGHT = 660;
|
|||||||
const ERRORS_WIDTH = 420;
|
const ERRORS_WIDTH = 420;
|
||||||
const ERRORS_HEIGHT = 480;
|
const ERRORS_HEIGHT = 480;
|
||||||
|
|
||||||
|
const INFO_WIDTH = 560;
|
||||||
|
const INFO_HEIGHT = 680;
|
||||||
|
|
||||||
|
/** Feste Whitelist — der Renderer kann nur diese beiden Schlüssel anfragen,
|
||||||
|
* nie einen beliebigen Dateipfad. */
|
||||||
|
const INFO_DOCS = {
|
||||||
|
manual: { title: 'Benutzerhandbuch', file: path.join(ROOT, 'BENUTZERHANDBUCH.md') },
|
||||||
|
disclaimer: { title: 'Disclaimer', file: path.join(ROOT, 'DISCLAIMER.md') },
|
||||||
|
};
|
||||||
|
|
||||||
/** Wartezeit vor der einmaligen Update-Prüfung — verzögert, damit sie den
|
/** Wartezeit vor der einmaligen Update-Prüfung — verzögert, damit sie den
|
||||||
* Start nicht verlangsamt und nicht mit dem ersten Poll konkurriert. */
|
* Start nicht verlangsamt und nicht mit dem ersten Poll konkurriert. */
|
||||||
const UPDATE_CHECK_DELAY_MS = 60 * 1000;
|
const UPDATE_CHECK_DELAY_MS = 60 * 1000;
|
||||||
@@ -37,6 +48,7 @@ const UPDATE_CHECK_DELAY_MS = 60 * 1000;
|
|||||||
let win = null;
|
let win = null;
|
||||||
let settingsWin = null;
|
let settingsWin = null;
|
||||||
let errorsWin = null;
|
let errorsWin = null;
|
||||||
|
let infoWin = null;
|
||||||
let tray = null;
|
let tray = null;
|
||||||
let collector = null;
|
let collector = null;
|
||||||
let store = null;
|
let store = null;
|
||||||
@@ -364,6 +376,76 @@ function openErrorLog() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Escaped Text für die Einbettung in HTML — nur für die Fehlermeldung unten. */
|
||||||
|
function escapeHtml(text) {
|
||||||
|
return String(text)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Benutzerhandbuch-/Disclaimer-Dialog — ein Fenster für beide Dokumente
|
||||||
|
* (Singleton wie oben), Titel und Inhalt werden bei jedem Öffnen aktualisiert.
|
||||||
|
* Modal relativ zum Hauptfenster, ausschließlich mit Schließen-Button.
|
||||||
|
*/
|
||||||
|
function openInfoDoc(kind) {
|
||||||
|
const doc = INFO_DOCS[kind];
|
||||||
|
if (!doc) return;
|
||||||
|
|
||||||
|
let html;
|
||||||
|
try {
|
||||||
|
const raw = fs.readFileSync(doc.file, 'utf8');
|
||||||
|
html = marked.parse(raw);
|
||||||
|
} catch (err) {
|
||||||
|
html = `<p>Datei konnte nicht gelesen werden: ${escapeHtml(err.message)}</p>`;
|
||||||
|
}
|
||||||
|
const send = () => infoWin.webContents.send('info:content', { title: doc.title, html });
|
||||||
|
|
||||||
|
if (infoWin && !infoWin.isDestroyed()) {
|
||||||
|
infoWin.show();
|
||||||
|
infoWin.focus();
|
||||||
|
send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = win && !win.isDestroyed()
|
||||||
|
? screen.getDisplayMatching(win.getBounds())
|
||||||
|
: screen.getPrimaryDisplay();
|
||||||
|
const area = target.workArea;
|
||||||
|
|
||||||
|
infoWin = new BrowserWindow({
|
||||||
|
width: INFO_WIDTH,
|
||||||
|
height: INFO_HEIGHT,
|
||||||
|
x: Math.round(area.x + (area.width - INFO_WIDTH) / 2),
|
||||||
|
y: Math.round(area.y + Math.max(0, (area.height - INFO_HEIGHT) / 2)),
|
||||||
|
minWidth: 420,
|
||||||
|
minHeight: 360,
|
||||||
|
frame: false,
|
||||||
|
resizable: true,
|
||||||
|
skipTaskbar: false,
|
||||||
|
modal: true,
|
||||||
|
parent: win && !win.isDestroyed() ? win : undefined,
|
||||||
|
alwaysOnTop: true,
|
||||||
|
show: false,
|
||||||
|
backgroundColor: '#1e1e22',
|
||||||
|
icon: path.join(ROOT, 'assets', 'icon.png'),
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, '..', 'info-preload.js'),
|
||||||
|
contextIsolation: true,
|
||||||
|
nodeIntegration: false,
|
||||||
|
sandbox: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
infoWin.loadFile(path.join(__dirname, '..', 'info', 'index.html'));
|
||||||
|
infoWin.webContents.once('did-finish-load', send);
|
||||||
|
infoWin.once('ready-to-show', () => infoWin.show());
|
||||||
|
infoWin.on('closed', () => {
|
||||||
|
infoWin = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Übernimmt geänderte Einstellungen ohne Neustart und schreibt sie in die
|
* Übernimmt geänderte Einstellungen ohne Neustart und schreibt sie in die
|
||||||
* Datei. Unbekannte Felder und die `_`-Kommentare bleiben erhalten, weil die
|
* Datei. Unbekannte Felder und die `_`-Kommentare bleiben erhalten, weil die
|
||||||
@@ -419,6 +501,9 @@ function registerIpc() {
|
|||||||
const info = collector?.state?.update;
|
const info = collector?.state?.update;
|
||||||
if (info?.available && info.htmlUrl) shell.openExternal(info.htmlUrl);
|
if (info?.available && info.htmlUrl) shell.openExternal(info.htmlUrl);
|
||||||
});
|
});
|
||||||
|
ipcMain.on('open-manual', () => openInfoDoc('manual'));
|
||||||
|
ipcMain.on('open-disclaimer', () => openInfoDoc('disclaimer'));
|
||||||
|
ipcMain.on('info:close', () => infoWin && infoWin.close());
|
||||||
|
|
||||||
ipcMain.handle('settings:load', () => ({
|
ipcMain.handle('settings:load', () => ({
|
||||||
config,
|
config,
|
||||||
|
|||||||
@@ -26,4 +26,8 @@ contextBridge.exposeInMainWorld('dashboard', {
|
|||||||
openErrorLog: () => ipcRenderer.send('open-error-log'),
|
openErrorLog: () => ipcRenderer.send('open-error-log'),
|
||||||
/** Öffnet die Release-Seite eines gefundenen neuen Updates im Browser. */
|
/** Öffnet die Release-Seite eines gefundenen neuen Updates im Browser. */
|
||||||
openReleasePage: () => ipcRenderer.send('open-release-page'),
|
openReleasePage: () => ipcRenderer.send('open-release-page'),
|
||||||
|
/** Öffnet den Benutzerhandbuch-Dialog. */
|
||||||
|
openManual: () => ipcRenderer.send('open-manual'),
|
||||||
|
/** Öffnet den Disclaimer-Dialog. */
|
||||||
|
openDisclaimer: () => ipcRenderer.send('open-disclaimer'),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ const el = {
|
|||||||
staleBadge: $('stale-badge'),
|
staleBadge: $('stale-badge'),
|
||||||
btnEndpointStatus: $('btn-endpoint-status'),
|
btnEndpointStatus: $('btn-endpoint-status'),
|
||||||
btnUpdateAvailable: $('btn-update-available'),
|
btnUpdateAvailable: $('btn-update-available'),
|
||||||
|
infoWrap: $('info-wrap'),
|
||||||
|
btnInfo: $('btn-info'),
|
||||||
|
infoMenu: $('info-menu'),
|
||||||
|
infoMenuManual: $('info-menu-manual'),
|
||||||
|
infoMenuDisclaimer: $('info-menu-disclaimer'),
|
||||||
btnHide: $('btn-hide'),
|
btnHide: $('btn-hide'),
|
||||||
btnSettings: $('btn-settings'),
|
btnSettings: $('btn-settings'),
|
||||||
btnExpand: $('btn-expand'),
|
btnExpand: $('btn-expand'),
|
||||||
@@ -475,6 +480,28 @@ el.btnSettings.addEventListener('click', () => window.dashboard.openSettings());
|
|||||||
el.btnEndpointStatus.addEventListener('click', () => window.dashboard.openErrorLog());
|
el.btnEndpointStatus.addEventListener('click', () => window.dashboard.openErrorLog());
|
||||||
el.btnUpdateAvailable.addEventListener('click', () => window.dashboard.openReleasePage());
|
el.btnUpdateAvailable.addEventListener('click', () => window.dashboard.openReleasePage());
|
||||||
|
|
||||||
|
el.btnInfo.addEventListener('click', () => {
|
||||||
|
el.infoMenu.hidden = !el.infoMenu.hidden;
|
||||||
|
});
|
||||||
|
document.addEventListener('click', (ev) => {
|
||||||
|
if (!el.infoMenu.hidden && !el.infoWrap.contains(ev.target)) {
|
||||||
|
el.infoMenu.hidden = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.addEventListener('keydown', (ev) => {
|
||||||
|
if (ev.key === 'Escape' && !el.infoMenu.hidden) {
|
||||||
|
el.infoMenu.hidden = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
el.infoMenuManual.addEventListener('click', () => {
|
||||||
|
el.infoMenu.hidden = true;
|
||||||
|
window.dashboard.openManual();
|
||||||
|
});
|
||||||
|
el.infoMenuDisclaimer.addEventListener('click', () => {
|
||||||
|
el.infoMenu.hidden = true;
|
||||||
|
window.dashboard.openDisclaimer();
|
||||||
|
});
|
||||||
|
|
||||||
window.dashboard.onState(render);
|
window.dashboard.onState(render);
|
||||||
|
|
||||||
// Reihenfolge: erst die Ansicht auf die wiederhergestellte Fenstergröße
|
// Reihenfolge: erst die Ansicht auf die wiederhergestellte Fenstergröße
|
||||||
|
|||||||
@@ -30,6 +30,13 @@
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<div class="info-wrap" id="info-wrap">
|
||||||
|
<button class="icon-btn" id="btn-info" title="Info" aria-label="Info">ⓘ</button>
|
||||||
|
<div class="info-menu" id="info-menu" hidden>
|
||||||
|
<button class="info-menu-item" id="info-menu-manual">Benutzerhandbuch</button>
|
||||||
|
<button class="info-menu-item" id="info-menu-disclaimer">Disclaimer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button class="icon-btn" id="btn-hide" title="Ausblenden (über das Tray-Symbol zurückholen)">–</button>
|
<button class="icon-btn" id="btn-hide" title="Ausblenden (über das Tray-Symbol zurückholen)">–</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
@@ -157,6 +157,47 @@ body {
|
|||||||
color: var(--status-update);
|
color: var(--status-update);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-wrap {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 4px);
|
||||||
|
right: 0;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 148px;
|
||||||
|
padding: 4px;
|
||||||
|
background: var(--surface-solid);
|
||||||
|
border: 1px solid var(--hairline);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-menu-item {
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-menu-item:hover {
|
||||||
|
background: var(--surface-raised);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Meter ───────────────────────────────────────────────────────────── */
|
/* ── Meter ───────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
.meters {
|
.meters {
|
||||||
|
|||||||
Reference in New Issue
Block a user