/** * Einstiegspunkt des Main-Prozesses. */ import { app, BrowserWindow, dialog } from 'electron'; import type { CspModus } from '../shared/csp'; import { energieUeberwachen, energieVergessen } from './energie'; import { hauptfensterErstellen } from './fenster'; import { hilfsmittelBeobachten, ipcAbmelden, ipcRegistrieren } from './ipc'; import { menueEinrichten } from './menue'; import { fehlerZeile, protokollEinrichten, protokollieren } from './protokoll'; import { sicherheitEinrichten } from './sicherheit'; import { datenbankStatus } from './datenbank'; const MODUS: CspModus = app.isPackaged ? 'production' : 'development'; app.setName('Waffensachkunde Lernsoftware'); /* Die Taskleisten-Identität unter Windows. Ohne sie trägt das laufende Fenster eine andere Kennung als die Verknüpfung, die das Installationsprogramm anlegt: Windows gruppiert das Programm dann nicht zuverlässig mit dem angehefteten Symbol, und „An Taskleiste anheften“ kann ein zweites, doppeltes Symbol erzeugen. Die Kennung muss dieselbe sein wie `appId` in `electron-builder.yml` – deshalb steht sie hier wörtlich und nicht abgeleitet: Zwei Stellen, aber nur eine davon liest der Bau, und ein Auseinanderlaufen fiele erst am fertigen Paket auf. `tests/paketstand.test.ts` vergleicht beide. Nur außerhalb des Store-Pakets: Dort vergibt Windows die Identität aus dem AppX-Manifest, und ein eigener Aufruf überschriebe sie mit einer falschen. */ if (process.platform === 'win32' && !process.windowsStore) { app.setAppUserModelId('de.willerding.waffensachkunde'); } // Nur eine Instanz: eine zweite Instanz fokussiert das bestehende Fenster. if (!app.requestSingleInstanceLock()) { app.quit(); } else { app.on('second-instance', () => { const [fenster] = BrowserWindow.getAllWindows(); if (fenster) { if (fenster.isMinimized()) { fenster.restore(); } fenster.focus(); } }); /* Was sonst niemand sähe, kommt in eine Datei. Fehler des Hauptprozesses landeten bis 0.22.0 ausschließlich in der Konsole – die im gepackten Programm niemand sieht. Wer „es stürzt manchmal ab“ melden wollte, hatte nichts in der Hand. Die Datei geht nirgendwohin; sie liegt neben dem Lernstand und wird von Hand mitgeschickt oder gar nicht. Einzelheiten und die Reinigungsregel stehen in `protokoll.ts`. Vor `whenReady`, damit auch ein Fehler beim Hochfahren noch hineinkommt. */ protokollEinrichten(app.getPath('userData')); process.on('uncaughtException', (fehler: unknown) => { console.error('[fehler] Unbehandelte Ausnahme:', fehler); protokollieren('fehler', `Unbehandelte Ausnahme: ${fehlerZeile(fehler)}`); }); process.on('unhandledRejection', (grund: unknown) => { console.error('[fehler] Unbehandeltes Versprechen:', grund); protokollieren('fehler', `Unbehandeltes Versprechen: ${fehlerZeile(grund)}`); }); app.whenReady().then( () => { sicherheitEinrichten(MODUS); menueEinrichten(); ipcRegistrieren(); hilfsmittelBeobachten(); energieUeberwachen(); // Rauchtest für better-sqlite3 einmal beim Start protokollieren. const status = datenbankStatus(); console.info(`[datenbank] ${status.meldung}`); hauptfensterErstellen(); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { hauptfensterErstellen(); } }); }, (fehler: unknown) => { /* Nicht wortlos beenden. Vorher stand der Grund nur in der Konsole – die niemand sieht, der das Programm über sein Symbol startet. Ein Fenster, das aufgeht und sofort wieder verschwindet, ist keine Fehlermeldung. `showErrorBox` geht auch ohne fertige Anwendung und wird vom Screenreader gelesen. */ const grund = fehler instanceof Error ? fehler.message : String(fehler); console.error('[start] Anwendung konnte nicht initialisiert werden:', fehler); protokollieren('start', `Anwendung konnte nicht starten: ${fehlerZeile(fehler)}`); dialog.showErrorBox( 'Waffensachkunde – Lernsoftware', [ 'Das Programm konnte nicht gestartet werden.', grund, 'Ihr Lernstand ist davon nicht betroffen; er liegt als Datei auf Ihrer Festplatte.', ].join('\n\n'), ); app.quit(); }, ); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); app.on('will-quit', () => { energieVergessen(); ipcAbmelden(); }); }