Listenkoepfe vereinheitlicht, Zeilen mit Luft, Kalenderdateien mit Namen (2026.9.2.21-.23)

KOEPFE. Jahr und Monat tragen dasselbe Format (13px, Zweitfarbe, gemischt) -
vorher drei Elemente in drei Formaten. Die Versalien aus .20 waren falsch: das
ist der iOS-Stil vor 13. Vom Eigentuemer aus drei gerenderten Vorschlaegen
gewaehlt.

ZEILEN. padding-left 20px statt 0 - die Spalte des Aufklapp-Pfeils, damit die
Zeilen auf derselben Kante stehen wie der Text der Koepfe. Nachgemessen bei
375px: Beschriftungsspalte 157px, laengster Ortsname 142,7px.

GALERIE. Der gezeigte Platz ueberlebt den Seitenwechsel - er lag in der
Komponente und starb mit ihr. Im Panel ist er seit jeher modulweit.

KALENDER. Jeder Termin hiess "-.ics": in dateiname() fehlte das \w in der
Zeichenklasse, es ueberlebten nur w/ae/oe/ue/ss und der Bindestrich. Zwei
Termine ueberschrieben einander damit. Jetzt mit Umschrift und Rueckfall, und
ueber Share.files statt Share.url.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 21:15:50 +02:00
parent d889353cbe
commit 36a6c21bfd
8 changed files with 162 additions and 37 deletions
+54
View File
@@ -8284,3 +8284,57 @@ Projekts aus den Apple-Richtlinien.
- **Hochladen über einen eigenen HTTP-Endpunkt** statt über den Dienst: hebt die 3-MB-Grenze des
Panels auf (WebSocket, aiohttp-Vorgabe 4 MiB) und spart die Base64-Aufblähung. Gemessen: über
REST gehen 12 MB durch, über den WebSocket bricht die Verbindung ab 3,2 MB Rohbild wortlos ab.
### Nachtrag .21-.23: gleiche Köpfe, Luft an den Zeilen, und `-.ics`
**Die Listenköpfe tragen jetzt dasselbe Format** (Vorschlag B von dreien, vom Eigentümer gewählt):
Jahr und Monat beide 13px in der Zweitfarbe, gemischt geschrieben, ohne Sperrung; die Fahrtzeile
bleibt 16px. Vorher standen drei Elemente in drei Formaten — 16px weiß, 11px grau gesperrt in
Versalien, wieder 16px weiß. Die Versalien aus .20 waren mein Fehlgriff: sie sind der iOS-Stil vor
13, heute schreibt Apple Abschnittsköpfe gemischt. Unterschieden werden die Ebenen über die
Position, nicht über die Schrift. Tanken teilt die Regel; seine rechte Spalte bleibt bewusst
anders (Ansage des Eigentümers).
Die drei Vorschläge lagen als gerendertes Muster vor (echte App-CSS, Audi Type, eigenständige HTML)
— das ist der Weg, der bei Gestaltungsfragen zu einer Entscheidung führt, statt sie zu beschreiben.
**Die Zeilen haben wieder Luft**: `padding-left: 20px` statt 0. Das ist nicht geraten, sondern die
Spalte des Aufklapp-Pfeils — die Zeilen stehen damit auf derselben Kante wie der Text der
Kopfzeilen, wie iOS es in gruppierten Listen macht. Bei 375px behält die Beschriftungsspalte 157px,
der längste Ortsname der Testdaten braucht 142,7px. Von den 58px aus .19 bleiben 38 gewonnen.
**Die Galerie merkt sich ihren Platz.** `useState(0)` setzte ihn bei jeder Rückkehr auf die
Seitenansicht zurück. Im Panel war das nie aufgefallen, weil `bild` dort eine modulweite Variable
ist und den Seitenwechsel überlebt; in der App lag der Zustand in der Komponente und starb mit ihr.
**Jeder Kalendertermin hieß `-.ics`.** In `dateiname()` stand `[^wäöüÄÖÜß-]`, gemeint war
`[^\wäöüÄÖÜß-]` — ohne das `\w` überlebten nur w, ä, ö, ü, ß und der Bindestrich:
| Titel | vorher | jetzt |
|---|---|---|
| RS4 Avant competition: Ölwechsel | `-ö-w-.ics` | `rs4-avant-competition-oelwechsel.ics` |
| Audi RS4: Inspektion | `-.ics` | `audi-rs4-inspektion.ics` |
| Wechseltermin | `-.ics` | `wechseltermin.ics` |
Zwei verschiedene Termine überschrieben einander damit im Zwischenspeicher. Umlaute werden jetzt
umgeschrieben statt weggeworfen (ein „ö" im Dateinamen muss über die Teilen-Kette prozentkodiert
werden — eine Fehlerquelle weniger), und die Übergabe läuft über `files` statt `url`.
**Nicht bestätigt ist, dass das den Kalender-Export repariert.** Der Verdacht bleibt, dass iOS
„Kalender" im Teilen-Blatt gar nicht als Ziel führt; der verlässliche Weg wäre EventKit, also
nativ. Steht zur Prüfung beim Eigentümer.
### Was am 02.09. abends offen blieb
- **Fahrt bleibt offen, wenn das Trip-Signal ihr Ende nie meldet.** Im Testcontainer nachgewiesen:
Trip-Status ging 16:33:15 auf `on` und nie wieder auf `off`, Zündung ging 16:54:21 aus, der
Kilometerstand stand ab 16:52 (209.480 → 209.490, also 10 km). Im Laufzeitspeicher steht seither
`fahrt_start_ts: 2026-09-02T16:32:18` — die Fahrt ist nie in der Liste gelandet. Der FMM003 hätte
`trip=false` nach seinem eigenen 900-s-Timeout senden müssen. **Vorschlag, noch nicht gebaut**
(der Eigentümer prüft zuerst den Dongle): bleibt das Trip-Signal an, während die Zündung länger
als 900s + Puffer aus ist und der Kilometerstand steht, die Fahrt zum Zündungsende schließen.
- **Zwischenablage:** `navigator.clipboard.read()` gibt in WebKit nur Text, HTML und Bilder heraus.
Ein PDF ist dort nie zu holen — der Knopf kann nicht funktionieren. Entweder nativ über
`UIPasteboard` oder er verschwindet auf dem Telefon.
- **Teilen legt keinen Tankvorgang an** (Abschnitt BQ) — die drei OTA-Teile und der native Umbau
stehen weiter aus.
+23 -1
View File
@@ -33,9 +33,31 @@ interface AusstattungGruppe {
serie?: string[]
}
/** Zuletzt gezeigter Galerieplatz - modulweit, damit er den Seitenwechsel
ueberlebt (siehe Begruendung in der Komponente). */
let gemerkterBildIndex = 0
export function MeinAudi({ geheZu }: { geheZu: (name: SeitenName, id?: string) => void }) {
const { fahrzeug, einstellungen } = useDaten()
const [bildIndex, setzeBildIndex] = useState(0)
/* Der Platz in der Galerie ueberlebt den Seitenwechsel.
useState(0) setzte ihn bei jeder Rueckkehr auf die Seitenansicht zurueck
- wer sich das Cockpit ansah, im Menue wechselte und zurueckkam, fing
wieder vorn an (gemeldet vom Eigentuemer, 02.09.2026). Im Panel ist das
nie aufgefallen, weil `bild` dort eine modulweite Variable ist und den
Seitenwechsel deshalb ueberlebt; hier liegt der Zustand in der
Komponente und stirbt mit ihr.
Modulweit statt im Speicher: der Platz ist eine Ansichtssache, kein
Datum. Er soll die Sitzung ueberleben, nicht den Neustart. */
const [bildIndex, setzeBildIndexIntern] = useState(gemerkterBildIndex)
const setzeBildIndex = (naechster: number | ((vorher: number) => number)) => {
setzeBildIndexIntern((vorher) => {
const wert = typeof naechster === "function" ? naechster(vorher) : naechster
gemerkterBildIndex = wert
return wert
})
}
if (!fahrzeug || !einstellungen) return null
const aktiverSatz =
+26 -2
View File
@@ -79,8 +79,29 @@ function hash(text: string): number {
return wert
}
/**
* Dateiname für den Termin.
*
* Hier stand `[^wäöüÄÖÜß-]` - gemeint war `[^\\wäöüÄÖÜß-]`, also "alles
* außer Wortzeichen". Ohne das `\\w` überlebten nur w, ä, ö, ü, ß und der
* Bindestrich: aus "Wechseltermin" wurde `-.ics`, aus "Audi RS4: Inspektion"
* ebenfalls `-.ics`. Ein Name ohne Stamm, und zwei verschiedene Termine
* überschrieben einander im Zwischenspeicher.
*
* Jetzt mit Umlaut-Umschrift statt Wegwerfen (ein "ö" im Dateinamen muss über
* die Teilen-Kette prozentkodiert werden und ist eine Fehlerquelle mehr),
* zusammengefassten Bindestrichen und einem Rückfall, falls vom Titel nichts
* übrig bleibt.
*/
function dateiname(titel: string): string {
return `${titel.replace(/[^wäöüÄÖÜß-]+/g, "-").toLowerCase()}.ics`
const stamm = titel
.replace(/ä/g, "ae").replace(/ö/g, "oe").replace(/ü/g, "ue")
.replace(/Ä/g, "Ae").replace(/Ö/g, "Oe").replace(/Ü/g, "Ue")
.replace(/ß/g, "ss")
.replace(/[^A-Za-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
.toLowerCase()
return `${stamm || "termin"}.ics`
}
export async function kalenderDateiLaden(
@@ -102,7 +123,10 @@ export async function kalenderDateiLaden(
directory: Directory.Cache,
encoding: Encoding.UTF8,
})
await Share.share({ title: titel, url: geschrieben.uri })
/* files statt url: beides landet im Plugin als URL in den activityItems,
aber `files` ist der dokumentierte Weg für lokale Dateien - `url` ist
für Links gedacht. dialogTitle ist Android-only und schadet hier nicht. */
await Share.share({ title: titel, files: [geschrieben.uri], dialogTitle: titel })
return
}
+28 -16
View File
@@ -1092,27 +1092,39 @@ button.dm-sicherheitszeile:active { background: var(--tile-2); }
margin-left: 0;
}
/* Der Monat ist ein Abschnittskopf, kein kleingeratener Eintrag.
/* Jahr und Monat tragen DENSELBEN Kopf, die Fahrtzeile trägt den Inhalt.
Vorher unterschied er sich von der Fahrtzeile nur durch Größe (13 gegen
16px) und Farbe - beim Lesen wirkte die Liste dadurch zweistufig statt
dreistufig, und genau danach hat der Eigentümer gefragt (02.09.2026).
Versalien plus Sperrung sind die Form, die iOS für Abschnittsköpfe
benutzt und die in dieser Oberfläche schon über dem Seitentitel steht
(.ads-eyebrow).
In der Liste standen drei Elemente in drei Formaten: 2026 in 16px weiß,
September in 11px grau gesperrt in Versalien, der Ortsname wieder in 16px
weiß. Der Eigentümer hat das am 02.09.2026 als unprofessionell bezeichnet -
zu Recht: zwei Köpfe, die dasselbe tun, sollen auch gleich aussehen.
11px und nicht 10: das ist die Untergrenze, die dieses Projekt aus den
Apple-Richtlinien übernommen hat. In beiden Oberflächen dieselben Werte,
damit der Kopf nicht je Gerät anders aussieht. */
.dm-liste .ads-accordion--lvl2 .ads-acc-h__k {
font-size: 11px;
letter-spacing: .18em;
text-transform: uppercase;
color: var(--fg3);
Beide jetzt 13px in der Zweitfarbe, gemischt geschrieben und ohne Sperrung.
Das ist zugleich die heutige Apple-Form: Abschnittsköpfe klein und in
Sekundärfarbe, aber gemischt - Versalien sind der Stil vor iOS 13. Der
Inhalt bleibt größer als sein Kopf, wie es die Richtlinien verlangen.
Unterschieden werden die beiden Ebenen über die Position, nicht über die
Schrift.
Von drei Vorschlägen (Monat als eigener Abschnitt ohne Aufklappen / beide
Köpfe gleich / Jahr als Seitenüberschrift) hat der Eigentümer diesen
gewählt: das Zusammenklappen ganzer Jahre bleibt erhalten. */
.dm-liste .ads-acc-h__k {
font-size: 13px;
color: var(--fg2);
}
/* 20px statt 0: die Zeilen standen sonst 34px vom Bildschirmrand entfernt und
damit sichtbar enger als alles andere in der Kachel (gemeldet 02.09.2026).
20px ist nicht geraten, sondern die Spalte des Aufklapp-Pfeils - die Zeilen
stehen damit auf derselben Kante wie der Text der Kopfzeilen, so wie iOS es
in gruppierten Listen macht.
Nachgemessen bei 375px: die Beschriftungsspalte behält 157px, der längste
Ortsname der Testdaten ("Ingolstadt → Adelschlag") braucht 142,7px. Von den
58px, die vorher verschenkt waren, bleiben 38 gewonnen. */
.dm-liste .dm-blatt {
padding-left: 0;
padding-left: 20px;
}
.dm-blatt {
@@ -1 +1 @@
{"version":"2026.9.2.20","sha256":"04aa82b37c019eadf0c9c23bc27e4931b9371dba512ddd979adc1ea70f482924","bytes":266578,"gebaut":"2026-09-02T14:34:34Z"}
{"version":"2026.9.2.23","sha256":"a4b55f9576980ea8f52a5c7782bc0879ff7e4c6561e19f844c44dad903bb87c5","bytes":266680,"gebaut":"2026-09-02T19:03:48Z"}
@@ -917,25 +917,38 @@ img.carfix.klein { aspect-ratio: auto; height: auto; }
ohne Platz zu kosten. Beschränkt auf .liste, weil das linke Polster von
.leaf anderswo (Batteriewerte, Servicebuch) gewollt ist. */
.liste .lvl2 { margin-left: 0; }
/* Der Monat ist ein Abschnittskopf, kein kleingeratener Eintrag.
/* Jahr und Monat tragen DENSELBEN Kopf, die Fahrtzeile traegt den Inhalt.
Vorher unterschied er sich von der Fahrtzeile nur durch Groesse (13 gegen
16px) und Farbe - beim Lesen wirkte die Liste dadurch zweistufig statt
dreistufig, und genau danach hat der Eigentuemer gefragt (02.09.2026).
Versalien plus Sperrung sind die Form, die iOS fuer Abschnittskoepfe
benutzt und die in dieser Oberflaeche schon ueber dem Seitentitel steht
(.eyebrow).
In der Liste standen drei Elemente in drei Formaten: 2026 in 16px weiss,
September in 11px grau gesperrt in Versalien, der Ortsname wieder in 16px
weiss. Der Eigentuemer hat das am 02.09.2026 als unprofessionell
bezeichnet - zu Recht: zwei Koepfe, die dasselbe tun, sollen auch gleich
aussehen.
11px und nicht 10: das ist die Untergrenze, die dieses Projekt aus den
Apple-Richtlinien uebernommen hat. In beiden Oberflaechen dieselben
Werte, damit der Kopf nicht je Geraet anders aussieht. */
.liste .lvl2 .acc-h .k {
font-size: 11px;
letter-spacing: .18em;
text-transform: uppercase;
color: var(--fg3);
Beide jetzt 13px in der Zweitfarbe, gemischt geschrieben und ohne
Sperrung. Das ist zugleich die heutige Apple-Form: Abschnittskoepfe
klein und in Sekundaerfarbe, aber gemischt - Versalien sind der Stil vor
iOS 13. Der Inhalt bleibt groesser als sein Kopf, wie es die Richtlinien
verlangen. Unterschieden werden die beiden Ebenen ueber die Position,
nicht ueber die Schrift.
Von drei Vorschlaegen (Monat als eigener Abschnitt ohne Aufklappen /
beide Koepfe gleich / Jahr als Seitenueberschrift) hat der Eigentuemer
diesen gewaehlt: das Zusammenklappen ganzer Jahre bleibt erhalten. */
.liste .acc-h .k {
font-size: 13px;
color: var(--fg2);
}
.liste .leaf { padding-left: 0; }
/* 20px statt 0: die Zeilen standen sonst 34px vom Bildschirmrand entfernt und
damit sichtbar enger als alles andere in der Kachel (gemeldet 02.09.2026).
20px ist nicht geraten, sondern die Spalte des Aufklapp-Pfeils - die
Zeilen stehen damit auf derselben Kante wie der Text der Kopfzeilen, so
wie iOS es in gruppierten Listen macht.
Nachgemessen bei 375px: die Beschriftungsspalte behaelt 157px, der
laengste Ortsname der Testdaten ("Ingolstadt -> Adelschlag") braucht
142,7px. Von den 58px, die vorher verschenkt waren, bleiben 38 gewonnen. */
.liste .leaf { padding-left: 20px; }
.tile-h {
display: block; width: 100%;
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.9.2.20",
"version": "2026.9.2.23",
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
"codeowners": ["@paul"],