Baustein 2: Inhalte einlesen und pruefen, Handouts, Suche, Glossar

Die fachlichen Inhalte liegen als Dateien unter content/ und werden beim
Start eingelesen, geprueft und in den Speicher geladen.

- Inhaltslader mit Zod-Schemata und Querverweispruefung
- Fehlermeldungen nennen Datei und Zeile, gesammelt statt beim ersten
  Fehler geworfen; die Anwendung startet bei Fehlern nicht
- npm run inhalte:pruefen prueft ohne Start
- Handout-Ansicht mit festem Aufbau, Uebersicht nach Themenbloecken
- Volltextsuche mit Gewichtung und Trefferausschnitt
- Gelesen-Markierung je Handout
- Glossar mit Suche und automatischer Verlinkung in den Handouts
- Beispielinhalte: 4 Wochen, 12 Handouts, 24 Merkkarten, 4 Aufgaben,
  4 Checklisten, 2 Lernzielkontrollen, 24 Glossareintraege
- rahmenplan.yaml aus FIAusbV 2020 und KMK-Rahmenlehrplan, mit Fundstellen
- gray-matter entfernt, YAML-Kopf wird selbst herausgeloest
- npm run screenshots nimmt die Bilder fuer die Anleitung auf
- 39 neue Tests, insgesamt 68

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-10 07:56:38 +02:00
co-authored by Claude Opus 5
parent e856349cd4
commit 1af5613e9b
70 changed files with 4779 additions and 124 deletions
+211 -3
View File
@@ -323,9 +323,7 @@ select:disabled {
color: #f2f8fa;
text-decoration: none;
letter-spacing: -0.01em;
display: inline-flex;
align-items: center;
gap: 0.5rem;
white-space: nowrap;
}
.marke:hover { color: #fff; }
.marke .marke-akzent { color: #e5a552; }
@@ -488,3 +486,213 @@ table.tabelle th {
white-space: nowrap;
border: 0;
}
/* --- Handouts --- */
.handout-kuerzel {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 2.6rem;
padding: 0.1rem 0.5rem;
border-radius: var(--radius-klein);
background: var(--petrol);
color: var(--text-auf-petrol);
font-weight: 700;
font-size: 0.85rem;
letter-spacing: 0.02em;
flex-shrink: 0;
}
.handout-kuerzel-gross {
font-size: 1rem;
padding: 0.25rem 0.7rem;
}
.handout-kachel {
display: block;
height: 100%;
background: var(--flaeche);
border: 1px solid var(--rand-zart);
border-radius: var(--radius);
padding: 0.9rem 1rem;
text-decoration: none;
color: var(--text);
}
.handout-kachel:hover {
background: var(--flaeche-kraeftig);
border-color: var(--rand);
color: var(--text);
}
.handout-treffer {
display: flex;
gap: 0.75rem;
align-items: flex-start;
background: var(--flaeche);
border: 1px solid var(--rand-zart);
border-radius: var(--radius);
padding: 0.8rem 1rem;
text-decoration: none;
color: var(--text);
}
.handout-treffer:hover {
background: var(--flaeche-kraeftig);
color: var(--text);
}
.handout-worum {
font-size: 1.05rem;
color: var(--text-leise);
margin: 0;
}
.handout-zuordnung dl {
margin: 0;
display: grid;
gap: 0.6rem;
}
.handout-zuordnung dt {
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-leise);
font-weight: 650;
margin-bottom: 0.2rem;
}
.handout-zuordnung dd {
margin: 0;
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
}
.zuordnung-marke {
display: inline-flex;
gap: 0.35rem;
align-items: baseline;
background: var(--grund);
border: 1px solid var(--rand);
border-radius: var(--radius-klein);
padding: 0.15rem 0.5rem;
font-size: 0.87rem;
}
.zuordnung-marke strong {
color: var(--petrol);
}
.fliesstext > *:first-child { margin-top: 0; }
.fliesstext > *:last-child { margin-bottom: 0; }
.fliesstext ul,
.fliesstext ol {
padding-left: 1.3rem;
margin: 0 0 1em;
}
.fliesstext li + li { margin-top: 0.4rem; }
.fliesstext table {
border-collapse: collapse;
width: 100%;
font-size: 0.94rem;
}
.fliesstext th,
.fliesstext td {
border: 1px solid var(--rand-zart);
padding: 0.45rem 0.65rem;
text-align: left;
vertical-align: top;
}
.fliesstext th {
background: var(--flaeche);
font-weight: 650;
}
.merksatz {
border-left: 5px solid var(--bernstein);
background: var(--warnung-flaeche);
border-radius: 0 var(--radius) var(--radius) 0;
padding: 0.9rem 1.1rem;
}
.merksatz-kennzeichnung {
display: block;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 700;
color: var(--warnung);
margin-bottom: 0.2rem;
}
.merksatz p {
margin: 0;
font-size: 1.08rem;
font-weight: 600;
}
.pruefungsfrage {
border: 1px solid var(--rand);
border-radius: var(--radius);
background: var(--grund);
}
.pruefungsfrage > summary {
cursor: pointer;
padding: 0.7rem 1rem;
font-weight: 650;
list-style: none;
display: flex;
align-items: center;
gap: 0.5rem;
min-height: 44px;
}
.pruefungsfrage > summary::-webkit-details-marker { display: none; }
.pruefungsfrage > summary::before {
content: "▸";
color: var(--petrol);
font-size: 0.9rem;
}
.pruefungsfrage[open] > summary::before { content: "▾"; }
.pruefungsfrage[open] > summary { border-bottom: 1px solid var(--rand-zart); }
.pruefungsfrage > div { padding: 0.9rem 1rem; }
a.glossarbegriff {
color: inherit;
text-decoration: underline dotted var(--petrol);
text-decoration-thickness: 1.5px;
text-underline-offset: 3px;
cursor: help;
}
a.glossarbegriff:hover {
color: var(--petrol);
text-decoration-style: solid;
}
/* --- Glossar --- */
.glossar {
display: grid;
gap: 0.75rem;
margin: 0;
}
.glossar-eintrag {
scroll-margin-top: 1rem;
}
.glossar-eintrag dt {
font-weight: 700;
font-size: 1.05rem;
margin-bottom: 0.3rem;
}
.glossar-langform {
display: block;
font-weight: 400;
font-size: 0.85rem;
color: var(--text-leise);
}
.glossar-eintrag dd { margin: 0; }
.glossar-eintrag:target {
outline: 3px solid var(--bernstein);
outline-offset: 2px;
}
@media print {
.pruefungsfrage > div { display: block !important; }
.handout-zuordnung { border: 1px solid #999; }
a.glossarbegriff { text-decoration: none; }
}