diff --git a/BENUTZERHANDBUCH.md b/BENUTZERHANDBUCH.md index 00de90c..f27c1a9 100644 --- a/BENUTZERHANDBUCH.md +++ b/BENUTZERHANDBUCH.md @@ -76,6 +76,7 @@ Position wird gemerkt. | Statuspunkt | Zeigt den Zustand der Datenquellen, siehe unten | | Pfeil-Symbol (nur bei neuem Release) | Öffnet die Release-Seite im Browser | | Zahnrad | Öffnet die Einstellungen | +| `ⓘ` | Öffnet ein Menü mit Benutzerhandbuch, Disclaimer und Lizenz — Klick auf einen Eintrag zeigt den Text in einem eigenen Fenster | | `–` | Blendet das Fenster aus (Rückkehr über das Tray-Symbol) | **Anzeigebereich** diff --git a/DISCLAIMER.md b/DISCLAIMER.md index a778613..d49ce42 100644 --- a/DISCLAIMER.md +++ b/DISCLAIMER.md @@ -40,7 +40,8 @@ Abrechnung ist ausschließlich Anthropic maßgeblich. Die Anwendung liest lokal vorhandene Claude-Code-Transkripte und optional das lokale Zugriffstoken, um die genannte Serverabfrage durchzuführen. Details dazu stehen im Abschnitt „Datenschutz und Sicherheit“ in -[`BENUTZERHANDBUCH.md`](BENUTZERHANDBUCH.md) sowie in [`README.md`](README.md). +[`BENUTZERHANDBUCH.md`](BENUTZERHANDBUCH.md) sowie in +[`README.md`](https://git.conatum.net/swinks/claude-live-dashboard/src/branch/main/README.md). ## Lizenz und Nutzung auf eigene Verantwortung diff --git a/README.md b/README.md index d93f6e9..a8a5a54 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ ELECTRON_OZONE_PLATFORM_HINT=wayland ./start.sh # nativ Wayland, ohne Position | Kopfzeile ziehen | Fenster verschieben (Position wird gemerkt) | | Statuspunkt (links vom Zahnrad) | Fehler-/Ereignis-Log öffnen | | ⚙ | Einstellungen öffnen | +| ⓘ | Menü mit Benutzerhandbuch/Disclaimer/Lizenz — öffnet den jeweiligen Text in einem eigenen Fenster | | „Details“ | Modell-Aufschlüsselung, Tempo und Datengrundlage aufklappen | | `–` | Ausblenden — Rückkehr über das Tray-Symbol | | Tray-Klick | Ein-/Ausblenden | diff --git a/package.json b/package.json index a655dc8..c1e09be 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "config.json", "BENUTZERHANDBUCH.md", "DISCLAIMER.md", + "LICENSE.txt", "!**/*.map" ], "win": { diff --git a/src/main/index.js b/src/main/index.js index d62c729..27ed87d 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -34,11 +34,23 @@ 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. */ +/** Feste Whitelist — der Renderer kann nur diese 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') }, + license: { title: 'Lizenz', file: path.join(ROOT, 'LICENSE.txt') }, +}; + +/** + * Ordnet Dateinamen, wie sie in den Markdown-Texten oben verlinkt sind, dem + * passenden INFO_DOCS-Schlüssel zu — für Links, die innerhalb der Anwendung + * bleiben sollen (siehe will-navigate-Abfangen in openInfoDoc()). + */ +const INFO_LINK_TARGETS = { + 'BENUTZERHANDBUCH.md': 'manual', + 'DISCLAIMER.md': 'disclaimer', + 'LICENSE.txt': 'license', }; /** Wartezeit vor der einmaligen Update-Prüfung — verzögert, damit sie den @@ -384,6 +396,32 @@ function escapeHtml(text) { .replace(/>/g, '>'); } +/** + * Behandelt einen Link-Klick im Info-Dialog. Ohne dieses Abfangen navigiert + * Electron das Fenster selbst zum verlinkten Ziel — bei einem relativen Link + * auf eine .md-Datei bleibt das Fenster dann leer und ohne Titelleiste hängen + * (kein Schließen-Button mehr). Externe http(s)-Links öffnen im Standard- + * browser, interne Verweise auf die drei bekannten Dokumente wechseln den + * Inhalt desselben Dialogs; alles andere wird verworfen. + */ +function handleInfoLink(url) { + let parsed; + try { + parsed = new URL(url); + } catch { + return; + } + if (parsed.protocol === 'http:' || parsed.protocol === 'https:') { + shell.openExternal(url); + return; + } + if (parsed.protocol === 'file:') { + const base = path.basename(decodeURIComponent(parsed.pathname)); + const kind = INFO_LINK_TARGETS[base]; + if (kind) openInfoDoc(kind); + } +} + /** * Benutzerhandbuch-/Disclaimer-Dialog — ein Fenster für beide Dokumente * (Singleton wie oben), Titel und Inhalt werden bei jedem Öffnen aktualisiert. @@ -438,6 +476,18 @@ function openInfoDoc(kind) { }, }); + // Linkklicks im gerenderten Markdown nie im Fenster selbst navigieren + // lassen (siehe handleInfoLink()) — sonst bleibt ein leeres, unschließbares + // Fenster zurück. + infoWin.webContents.on('will-navigate', (event, url) => { + event.preventDefault(); + handleInfoLink(url); + }); + infoWin.webContents.setWindowOpenHandler(({ url }) => { + handleInfoLink(url); + return { action: 'deny' }; + }); + infoWin.loadFile(path.join(__dirname, '..', 'info', 'index.html')); infoWin.webContents.once('did-finish-load', send); infoWin.once('ready-to-show', () => infoWin.show()); @@ -503,6 +553,7 @@ function registerIpc() { }); ipcMain.on('open-manual', () => openInfoDoc('manual')); ipcMain.on('open-disclaimer', () => openInfoDoc('disclaimer')); + ipcMain.on('open-license', () => openInfoDoc('license')); ipcMain.on('info:close', () => infoWin && infoWin.close()); ipcMain.handle('settings:load', () => ({ diff --git a/src/preload.js b/src/preload.js index 260eba1..94e2dd5 100644 --- a/src/preload.js +++ b/src/preload.js @@ -30,4 +30,6 @@ contextBridge.exposeInMainWorld('dashboard', { openManual: () => ipcRenderer.send('open-manual'), /** Öffnet den Disclaimer-Dialog. */ openDisclaimer: () => ipcRenderer.send('open-disclaimer'), + /** Öffnet den Lizenz-Dialog. */ + openLicense: () => ipcRenderer.send('open-license'), }); diff --git a/src/renderer/app.js b/src/renderer/app.js index 99d2738..7ab86e4 100644 --- a/src/renderer/app.js +++ b/src/renderer/app.js @@ -53,6 +53,7 @@ const el = { infoMenu: $('info-menu'), infoMenuManual: $('info-menu-manual'), infoMenuDisclaimer: $('info-menu-disclaimer'), + infoMenuLicense: $('info-menu-license'), btnHide: $('btn-hide'), btnSettings: $('btn-settings'), btnExpand: $('btn-expand'), @@ -501,6 +502,10 @@ el.infoMenuDisclaimer.addEventListener('click', () => { el.infoMenu.hidden = true; window.dashboard.openDisclaimer(); }); +el.infoMenuLicense.addEventListener('click', () => { + el.infoMenu.hidden = true; + window.dashboard.openLicense(); +}); window.dashboard.onState(render); diff --git a/src/renderer/index.html b/src/renderer/index.html index 2c62954..9b340d2 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -35,6 +35,7 @@