Kaputte Links im Info-Dialog beheben, Lizenz-Menüpunkt ergaenzen

Klicks auf Links im gerenderten Markdown (z. B. BENUTZERHANDBUCH.md,
LICENSE.txt) navigierten das Fenster selbst dorthin und liessen einen
leeren, unschliessbaren Dialog zurueck. Main-Prozess faengt jetzt jede
Navigation im Info-Fenster ab: externe http(s)-Links oeffnen im
Standardbrowser, interne Verweise auf Benutzerhandbuch/Disclaimer/
Lizenz wechseln stattdessen den Inhalt desselben Dialogs.

Neuer dritter Menüpunkt "Lizenz" (LICENSE.txt) im Info-Dropdown. Der
README.md-Link in DISCLAIMER.md zeigt jetzt auf die Repo-Seite auf
Gitea statt auf einen kaputten relativen Pfad.

Claude-Session: https://claude.ai/code/session_01VeqoaAZDVSCU4QBtiYDC5C
This commit is contained in:
Winkler, Stefan
2026-09-01 11:58:58 +02:00
parent bb7089f560
commit f8a286f219
8 changed files with 66 additions and 3 deletions
+1
View File
@@ -76,6 +76,7 @@ Position wird gemerkt.
| Statuspunkt | Zeigt den Zustand der Datenquellen, siehe unten | | Statuspunkt | Zeigt den Zustand der Datenquellen, siehe unten |
| Pfeil-Symbol (nur bei neuem Release) | Öffnet die Release-Seite im Browser | | Pfeil-Symbol (nur bei neuem Release) | Öffnet die Release-Seite im Browser |
| Zahnrad | Öffnet die Einstellungen | | 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) | | `` | Blendet das Fenster aus (Rückkehr über das Tray-Symbol) |
**Anzeigebereich** **Anzeigebereich**
+2 -1
View File
@@ -40,7 +40,8 @@ Abrechnung ist ausschließlich Anthropic maßgeblich.
Die Anwendung liest lokal vorhandene Claude-Code-Transkripte und optional das Die Anwendung liest lokal vorhandene Claude-Code-Transkripte und optional das
lokale Zugriffstoken, um die genannte Serverabfrage durchzuführen. Details lokale Zugriffstoken, um die genannte Serverabfrage durchzuführen. Details
dazu stehen im Abschnitt „Datenschutz und Sicherheit“ in 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 ## Lizenz und Nutzung auf eigene Verantwortung
+1
View File
@@ -75,6 +75,7 @@ ELECTRON_OZONE_PLATFORM_HINT=wayland ./start.sh # nativ Wayland, ohne Position
| Kopfzeile ziehen | Fenster verschieben (Position wird gemerkt) | | Kopfzeile ziehen | Fenster verschieben (Position wird gemerkt) |
| Statuspunkt (links vom Zahnrad) | Fehler-/Ereignis-Log öffnen | | Statuspunkt (links vom Zahnrad) | Fehler-/Ereignis-Log öffnen |
| ⚙ | Einstellungen öffnen | | ⚙ | Einstellungen öffnen |
| ⓘ | Menü mit Benutzerhandbuch/Disclaimer/Lizenz — öffnet den jeweiligen Text in einem eigenen Fenster |
| „Details“ | Modell-Aufschlüsselung, Tempo und Datengrundlage aufklappen | | „Details“ | Modell-Aufschlüsselung, Tempo und Datengrundlage aufklappen |
| `` | Ausblenden — Rückkehr über das Tray-Symbol | | `` | Ausblenden — Rückkehr über das Tray-Symbol |
| Tray-Klick | Ein-/Ausblenden | | Tray-Klick | Ein-/Ausblenden |
+1
View File
@@ -37,6 +37,7 @@
"config.json", "config.json",
"BENUTZERHANDBUCH.md", "BENUTZERHANDBUCH.md",
"DISCLAIMER.md", "DISCLAIMER.md",
"LICENSE.txt",
"!**/*.map" "!**/*.map"
], ],
"win": { "win": {
+53 -2
View File
@@ -34,11 +34,23 @@ const ERRORS_HEIGHT = 480;
const INFO_WIDTH = 560; const INFO_WIDTH = 560;
const INFO_HEIGHT = 680; const INFO_HEIGHT = 680;
/** Feste Whitelist — der Renderer kann nur diese beiden Schlüssel anfragen, /** Feste Whitelist — der Renderer kann nur diese Schlüssel anfragen, nie
* nie einen beliebigen Dateipfad. */ * einen beliebigen Dateipfad. */
const INFO_DOCS = { const INFO_DOCS = {
manual: { title: 'Benutzerhandbuch', file: path.join(ROOT, 'BENUTZERHANDBUCH.md') }, manual: { title: 'Benutzerhandbuch', file: path.join(ROOT, 'BENUTZERHANDBUCH.md') },
disclaimer: { title: 'Disclaimer', file: path.join(ROOT, 'DISCLAIMER.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 /** Wartezeit vor der einmaligen Update-Prüfung — verzögert, damit sie den
@@ -384,6 +396,32 @@ function escapeHtml(text) {
.replace(/>/g, '>'); .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 * Benutzerhandbuch-/Disclaimer-Dialog — ein Fenster für beide Dokumente
* (Singleton wie oben), Titel und Inhalt werden bei jedem Öffnen aktualisiert. * (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.loadFile(path.join(__dirname, '..', 'info', 'index.html'));
infoWin.webContents.once('did-finish-load', send); infoWin.webContents.once('did-finish-load', send);
infoWin.once('ready-to-show', () => infoWin.show()); infoWin.once('ready-to-show', () => infoWin.show());
@@ -503,6 +553,7 @@ function registerIpc() {
}); });
ipcMain.on('open-manual', () => openInfoDoc('manual')); ipcMain.on('open-manual', () => openInfoDoc('manual'));
ipcMain.on('open-disclaimer', () => openInfoDoc('disclaimer')); ipcMain.on('open-disclaimer', () => openInfoDoc('disclaimer'));
ipcMain.on('open-license', () => openInfoDoc('license'));
ipcMain.on('info:close', () => infoWin && infoWin.close()); ipcMain.on('info:close', () => infoWin && infoWin.close());
ipcMain.handle('settings:load', () => ({ ipcMain.handle('settings:load', () => ({
+2
View File
@@ -30,4 +30,6 @@ contextBridge.exposeInMainWorld('dashboard', {
openManual: () => ipcRenderer.send('open-manual'), openManual: () => ipcRenderer.send('open-manual'),
/** Öffnet den Disclaimer-Dialog. */ /** Öffnet den Disclaimer-Dialog. */
openDisclaimer: () => ipcRenderer.send('open-disclaimer'), openDisclaimer: () => ipcRenderer.send('open-disclaimer'),
/** Öffnet den Lizenz-Dialog. */
openLicense: () => ipcRenderer.send('open-license'),
}); });
+5
View File
@@ -53,6 +53,7 @@ const el = {
infoMenu: $('info-menu'), infoMenu: $('info-menu'),
infoMenuManual: $('info-menu-manual'), infoMenuManual: $('info-menu-manual'),
infoMenuDisclaimer: $('info-menu-disclaimer'), infoMenuDisclaimer: $('info-menu-disclaimer'),
infoMenuLicense: $('info-menu-license'),
btnHide: $('btn-hide'), btnHide: $('btn-hide'),
btnSettings: $('btn-settings'), btnSettings: $('btn-settings'),
btnExpand: $('btn-expand'), btnExpand: $('btn-expand'),
@@ -501,6 +502,10 @@ el.infoMenuDisclaimer.addEventListener('click', () => {
el.infoMenu.hidden = true; el.infoMenu.hidden = true;
window.dashboard.openDisclaimer(); window.dashboard.openDisclaimer();
}); });
el.infoMenuLicense.addEventListener('click', () => {
el.infoMenu.hidden = true;
window.dashboard.openLicense();
});
window.dashboard.onState(render); window.dashboard.onState(render);
+1
View File
@@ -35,6 +35,7 @@
<div class="info-menu" id="info-menu" hidden> <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-manual">Benutzerhandbuch</button>
<button class="info-menu-item" id="info-menu-disclaimer">Disclaimer</button> <button class="info-menu-item" id="info-menu-disclaimer">Disclaimer</button>
<button class="info-menu-item" id="info-menu-license">Lizenz</button>
</div> </div>
</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>