# Waffensachkunde – Lernsoftware (Anwendungsgerüst) Barrierefreie Desktop-Lernsoftware zur Vorbereitung auf die Waffensachkundeprüfung. Läuft vollständig offline; es werden keine Daten übertragen. Dieses Verzeichnis enthält das **Anwendungsgerüst**; die Lerninhalte kommen aus einem anderen Arbeitsstrang (`content/`, `data-pipeline/`). Das Datenbankschema gehört dagegen hierher: Grundschema und alle Migrationsschritte stehen in `src/main/schema.ts` (siehe den Baum unten). Bis Fassung 0.27.2 nahm dieser Satz es mit aus — wer es suchte, suchte unter `content/` und `data-pipeline/`, wo keine einzige Schemadefinition liegt. --- ## Technischer Stack | Baustein | Version | Zweck | | ----------------- | -------------- | ---------------------------------------- | | Electron | 43.4.1 | Desktop-Laufzeit | | React | 19.2.8 | Oberfläche im Renderer-Prozess | | Vite | 7.3.6 | Bundler | | electron-vite | 5.0.0 | Bindeglied Vite ↔ Electron | | TypeScript | 6.0.3 | `strict` plus zusätzliche Verschärfungen | | better-sqlite3 | 13.0.3 | Lokale Datenbank des Lernstands | | electron-builder | 26.15.3 | Paketierung (NSIS, DMG) | | Vitest | 4.1.11 | Unit-Tests | | Playwright | 1.62.1 | E2E-Tests gegen die echte Anwendung | | axe-core | 4.13.0 | Automatisierte Barrierefreiheitsprüfung | | ESLint / Prettier | 9.39.5 / 3.9.6 | Statische Analyse und Formatierung | --- ## Verzeichnisstruktur ``` app/ ├── src/ │ ├── main/ Main-Prozess (Node, voller Systemzugriff) – 32 Dateien │ │ ├── index.ts Einstiegspunkt, Lebenszyklus der Anwendung │ │ ├── fenster.ts Hauptfenster inkl. gehärteter webPreferences │ │ ├── sicherheit.ts CSP-Header, Berechtigungssperren, Navigationsschutz │ │ ├── menue.ts Deutschsprachiges Anwendungsmenü │ │ ├── ipc.ts Registrierung der typisierten IPC-Handler │ │ ├── einstellungen.ts Nutzereinstellungen (JSON in userData) │ │ ├── datenbank.ts Rauchtest des nativen SQLite-Moduls (In-Memory) │ │ ├── schema.ts Grundschema und die neun Migrationsschritte │ │ ├── lernstand.ts Der Anwendungskern: Profile, Antworten, Sitzung, Plan │ │ ├── pruefung.ts Prüfungssimulation: Bogen, Auswertung, Verlauf │ │ ├── katalog.ts, Laden und Prüfen der Inhalte aus content/ │ │ │ erklaerungen.ts, │ │ │ glossar.ts, │ │ │ normtexte.ts, │ │ │ themen.ts │ │ ├── sicherung.ts, Sichern und Einspielen des Lernstands │ │ │ sicherung-dialoge.ts, │ │ │ selbstsicherung.ts, │ │ │ profil-uebernehmen.ts │ │ └── druck.ts, PDF-Ausgabe: Lernbericht, Fehlerprotokoll, Fragenliste │ │ dokument-export.ts, │ │ fragendruck-export.ts, │ │ lernbericht-export.ts │ │ │ ├── preload/ Die EINZIGE Brücke zwischen Main und Renderer │ │ ├── index.ts contextBridge-Freigabe der typisierten API │ │ └── index.d.ts Deklaration von `window.lernApp` │ │ │ ├── renderer/ React-Oberfläche (Sandbox, kein Node-Zugriff) │ │ ├── index.html lang="de", CSP-Platzhalter, Titel │ │ └── src/ │ │ ├── main.tsx React-Einstiegspunkt │ │ ├── App.tsx Ansichtswahl und die Tore davor │ │ ├── bridge.ts Typisierter Zugriff auf window.lernApp │ │ ├── components/ Bausteine, nach Bereich gegliedert: │ │ │ lernen/ (40), pruefung/ (13), profil/ (3), │ │ │ suche/ (2), ueber/ (4), hilfe/ (1) │ │ ├── hooks/ 29 Haken – Katalog, Lernstand, Sitzung, │ │ │ Prüfung, Thema, Textabstand, Tastenkürzel │ │ ├── lernen/ Bewertung, Sitzungsaufbau, Sprachausgabe │ │ ├── pruefung/ Auftrag, Bogen, Bewertung, Zeit │ │ └── styles/ tokens.css (3 Themes) und sechs Bereichsdateien │ │ │ └── shared/ Von allen drei Prozessen genutzte Typen und Logik │ ├── ipc.ts IPC-Vertrag (44 Kanäle, Anfrage-/Antworttypen) │ ├── fsrs.ts Das Wiedervorlageverfahren │ ├── lernstand.ts, Verträge des Kerns │ │ lernplan.ts, reife.ts │ ├── katalog.ts, Verträge der Inhalte │ │ erklaerungen.ts, glossar.ts, normtexte.ts, themen.ts │ ├── druck/ Die Dokumentbausteine, ohne Electron │ ├── theme.ts Theme-Typen und Auflösungslogik │ └── csp.ts Content-Security-Policy (eine Quelle der Wahrheit) │ ├── tests/ Unit-Tests (Vitest, jsdom) – 79 Testdateien │ ├── setup.ts Test-Setup, matchMedia-Stub │ ├── lernstand.test.ts Der Kern: Profile, Wiedervorlage, Übersicht, Plan │ ├── pruefung.test.ts Bogen, Auswertung, unterbrochener Lauf │ ├── renderer-*.test.tsx Die Oberfläche je Bereich, mit axe-Lauf │ ├── dokumentation.test.ts Fassungsnummern, Changelog, tote Verweise │ └── … Druck, Sicherung, Suche, Glossar, Normtexte, Symbol │ ├── e2e/ E2E-Tests (Playwright gegen echtes Electron) – 23 Prüfdateien │ ├── electron-hilfe.ts Start der gebauten App mit frischem Profil │ ├── axe-hilfe.ts axe-core-Injektion für Electron │ ├── konsolenwache.ts Macht Konsolenfehler der Anwendung rot │ ├── gepackt.spec.ts Rauchtest gegen das gebaute Installationspaket │ └── barrierefreiheit*.spec.ts axe je Ansicht und Farbschema, Landmarken, Fokus │ ├── build-resources/ Programmsymbol und die Grafiken des Store-Pakets ├── tools/ Symbol bauen, Paketstand messen, Gate-Stempel ├── electron.vite.config.ts Build für main / preload / renderer ├── electron-builder.yml Paketierung Windows (NSIS, AppX) und macOS (DMG) ├── eslint.config.mjs ESLint Flat Config ├── playwright.config.ts E2E-Konfiguration ├── vitest.config.ts Unit-Test-Konfiguration samt Abdeckungsschwellen └── tsconfig.*.json Getrennte Projekte für node / web / tests / e2e ``` Nach `npm run build` entsteht zusätzlich `out/` (`out/main`, `out/preload`, `out/renderer`), nach `npm run dist:*` das Verzeichnis `release/`. Beides ist nicht versioniert. --- ## npm-Skripte | Skript | Wirkung | | ---------------------- | -------------------------------------------------------------------------------- | | `npm run dev` | Startet Vite-Dev-Server und Electron mit Hot Reload. | | `npm run build` | Typprüfung, danach Produktionsbundles nach `out/`. | | `npm start` | Zeigt einen bereits gebauten Stand (`electron-vite preview`). | | `npm test` | Unit-Tests einmalig (Vitest). | | `npm run test:watch` | Unit-Tests im Beobachtungsmodus. | | `npm run test:e2e` | Baut die App und führt danach die Playwright-Tests aus. | | `npm run lint` | ESLint über das gesamte Projekt. | | `npm run lint:fix` | ESLint mit automatischer Korrektur. | | `npm run typecheck` | TypeScript für alle vier Teilprojekte (node, web, tests, e2e). | | `npm run format` | Prettier schreibt alle Dateien. | | `npm run format:check` | Prettier prüft ohne zu schreiben (für CI). | | `npm run dist:win` | Windows-Installer (NSIS) nach `release/`. | | `npm run dist:mac` | macOS-Abbild (DMG) nach `release/` – muss auf macOS laufen. | | `npm run rebuild` | Baut native Module für Electron neu. Normalerweise **nicht** nötig, siehe unten. | --- ## Sicherheitsmodell Diese Schalter sind nicht verhandelbar und werden von `tests/sicherheit.test.ts` (Quellprüfung) und `e2e/anwendung.spec.ts` (Verhaltensprüfung) abgesichert: - `contextIsolation: true` – Renderer und Preload laufen in getrennten JS-Kontexten. - `nodeIntegration: false` – im Renderer gibt es kein `require`, `process`, `Buffer`. - `sandbox: true` – der Renderer-Prozess läuft in der Chromium-Sandbox. - `webviewTag: false`, `nodeIntegrationInWorker/-InSubFrames: false`, `webSecurity: true`. Weitere Härtung in `src/main/sicherheit.ts`: alle Berechtigungsanfragen werden abgelehnt, Navigation nach außen wird blockiert, neue Fenster werden nicht in Electron geöffnet, sondern an den Systembrowser übergeben. **Genau ein Kanal führt nach außen**, und er wird immer von Hand angestoßen: `system:unterstuetzung` öffnet die Unterstützungsseite im Standardbrowser (`shell.openExternal`). Der Renderer schickt die Adresse mit, damit prüfbar bleibt, dass die Oberfläche genau die anfordert, die sie anzeigt – geöffnet wird aber ausschließlich die eine Adresse aus `src/shared/unterstuetzung.ts`. Ist dort nichts eingetragen, öffnet dieser Weg nichts, und die Oberfläche zeigt das Angebot gar nicht erst an. Die Anwendung lädt nach wie vor von sich aus nichts: kein `fetch`, kein `net.request`, kein `https.get`. **IPC ausschließlich über `contextBridge`.** Kanäle werden an genau einer Stelle definiert (`src/shared/ipc.ts`, Interface `IpcVertrag`); Anfrage- und Antworttyp werden auf beiden Seiten daraus abgeleitet. Einen neuen Kanal ergänzt man so: 1. Eintrag in `IpcVertrag` und in `IPC_KANAELE` (Allowlist). 2. Handler in `src/main/ipc.ts` über die Hilfsfunktion `behandeln()`. 3. Methode in `LernAppBridge` und Umsetzung in `src/preload/index.ts`. **Content-Security-Policy** steht in `src/shared/csp.ts` und wird zweifach ausgeliefert: als ``-Element im HTML und als HTTP-Kopf aus dem Main-Prozess. Beide greifen auch bei `file://` im gepackten Build, und beide gelten gleichzeitig – ausgewertet wird die Schnittmenge. Im Produktionsbuild gilt `default-src 'none'` ohne jedes `unsafe-*`. Die ``-Fassung lässt `frame-ancestors` bewusst weg: Diese Direktive wirkt ausschließlich im HTTP-Kopf, im ``-Element verwirft Chromium sie mit einem Konsolenfehler bei jedem Start. Dass sie über den Kopf ankommt, misst `e2e/anwendung.spec.ts` im echten Fenster. --- ## Barrierefreiheit Barrierefreiheit ist **Definition of Done**, nicht Nacharbeit. ### Verbindliche Regeln | Bereich | Regel | | ------------ | ----------------------------------------------------------------------- | | Zielniveau | WCAG 2.2 Stufe AA vollständig; AAA als Zielwert, wo erreichbar. | | Textkontrast | Minimum 4,5:1, Zielwert 7:1. | | UI-Kontrast | Ränder, Bedienelemente und Fokusring mindestens 3:1. | | Sprache | `lang="de"` am ``; alle sichtbaren Texte auf Deutsch. | | Tastatur | Jede Funktion ohne Maus bedienbar; sichtbarer `:focus-visible`-Ring. | | Skip-Link | Erster Tabstopp springt zum Hauptinhalt. | | Struktur | Genau eine `