Kilometerstand und Service standen als zwei identisch aussehende Kacheln
nebeneinander - 166x113px, gleiche 29px-Zahl, gleiche Farbe -, obwohl nur
die rechte ein Knopf war (erkennbar allein an einem 12px-Chevron) und die
beiden Zahlen Grundverschiedenes meinen: ein gemessener Ist-Wert gegen
einen taeglich schrumpfenden Countdown.
Neu, nach Abstimmung mit dem Nutzer und belegt an beiden Regelwerken
(Apple HIG "Layout"/"Widgets" live gelesen, Audi-CI aus dem
projekteigenen AUDI_CI_ARCHIV):
Naechster Service: >
9.029 km
bis zum Oelwechsel · vsl. 15.04.2027
------------------------------------
Kilometerstand 20.845 km
Das .duo-Raster entfaellt; der Block liegt in einer .tile.flat, also in
derselben Ebene wie die Reichweite darueber, statt in einem eigenen
Kachelcontainer. Der Chevron sitzt nur am .serviceblock-Knopf, nicht ueber
der ganzen Flaeche - damit bleibt erkennbar, welcher Teil bedienbar ist
und welcher reine Anzeige (HIG: "differentiate controls from content").
"Naechster Service" meint jede Serviceart; naechsterTermin() waehlte
ohnehin schon ueber Oelwechsel/Inspektion/Hauptuntersuchung aus, neu ist
die Beschriftung samt ART_BIS fuer den richtigen Artikel ("bis zum
Oelwechsel", aber "bis zur Inspektion"). Ohne Restkilometer
(Hauptuntersuchung) rueckt das Datum selbst ins Zahlenfeld, dann bei 30px
statt 40px.
Von der Aenderung verwaist und mitentfernt: mmjjjj() und die
.duo-Regeln in beiden Stylesheets.
Live geprueft bei 375px und 1280px: Block und Wertezeile fluchten mit der
Kachelkante, Chevron buendig rechts, Tippflaeche 88px; Klick auf den Block
oeffnet "Service", Klick auf die Kilometerstand-Zeile navigiert nicht.
Keine neuen Konsolenfehler.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gemeldet: das Datum der Oelwechsel-Prognose steht in einer eigenen Zeile.
Nachgemessen: das <small> in der rechten Spalte war 29px hoch bei 217px
Breite (zwei Zeilen); .row ist ein Flex aus dt/dd, und dd bekommt nur den
Rest neben dem Titel - fuer "9.029 km, voraussichtlich am 14.04.2027"
reicht das bei 12,5px nicht.
Statt an Schriftgroesse oder Text zu drehen, haengt die Fussnote jetzt als
eigenes Element (.row-fussnote) neben dt/dd und bricht per flex:1 0 100%
auf eine eigene Zeile ueber die volle Kachelbreite um. flex-wrap:wrap ist
per :has(.row-fussnote) auf genau diese Zeilen eingegrenzt, damit die
uebrigen .row-Verwendungen zweispaltig bleiben.
Die verschachtelte fm-/nicht-fm-Verzweigung im Markup faellt dabei zu
einem hatMeldung-Zweig fuer den Wert plus einem separaten fussnote-String
zusammen; die Anzeigelogik selbst ist unveraendert.
Live geprueft bei 375px und 1280px: beide Fussnoten einzeilig (14px),
307px bzw. 636px breit; alle 17 .row auf "Mein Audi" weiterhin
zweispaltig; keine neuen Konsolenfehler.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Auf Nachfrage, wie Apple HIG und die Audi-CI die beiden Kacheln loesen
wuerden - beide laufen auf dieselbe Antwort hinaus.
Die Oelwechsel-Kachel presste zwei verschiedene Groessen in ein
Zahlenfeld: Restkilometer und Prognosedatum, getrennt durch einen
Schraegstrich. Zwei Einheiten, davon eine erfundene ("tkm" schreibt
niemand - die Mein-Audi-Servicebox nannte dieselbe Zahl schon immer
"9.029 km"), zwei Schraegstriche mit zwei Bedeutungen, und auf 375px
zweizeilig umgebrochen.
Apple HIG "Layout" ("don't obscure essential information by crowding it
with nonessential details") und "Typography" (Hierarchie ueber Groesse
und Farbe) verlangen die Trennung; die Audi-CI definiert .fig
ausdruecklich als "large numeric figures" - eine Zahl, nicht zwei.
- Wert gross (9.029 km), Kontext leise darunter (vsl. 04/2027)
- Echte Einheit statt "tkm"
- Kachel ist jetzt ein Knopf mit Chevron zur Service-Seite; vorher eine
Sackgasse, obwohl es die Seite gibt
- mmjj() dadurch verwaist, ersetzt durch mmjjjj() mit vollem Jahr
- HU-/Inspektionszweig faellt mit dem Oelwechselzweig zusammen, weil sie
nach der Trennung dieselbe Form haben
companion-app hatte diesen Fehler nicht (dort standen die Termine schon
als Label/Wert/Zusatz). Dort stattdessen der benachbarte
Gruppierungsfehler behoben: die Service-Zeilen hingen innerhalb der
Kilometerstand-Kachel und fuehrten nirgendwohin - jetzt eigene,
antippbare Kachel zur Service-Seite.
Live geprueft im Panel (Tag und Nacht, 375px): beide Kacheln einzeilig,
Tippen oeffnet Service. companion-app: typecheck sauber, 95/95 Tests,
Build erfolgreich.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fuenf gemeldete Punkte plus ein Theme-Bug beim Nachpruefen:
- "Dauer" ist kein Eingabefeld mehr - stattdessen Ankunftszeit, die Dauer
wird aus Start/Ankunft berechnet und live angezeigt (fahrtZeitraum()/
dauerText()), inklusive Fahrten ueber Mitternacht.
- Art-Werte werden grossgeschrieben angezeigt (Privat/Arbeitsweg), der
gespeicherte Wert bleibt klein (artText()-Helfer).
- Arbeitsweg-Pille ist nicht mehr rot - sieht jetzt aus wie Privat.
- "tippen zum Umschalten" unter der Pille entfernt.
- Bild-Platzhalter (Einstellungen) war im Tag-Theme unsichtbar: --tile wird
dort vom iOS-Overlay auf reines Weiss gesetzt, identisch mit Canvas/
Kacheln. Auf --ios-fill umgestellt, in beiden Themes sichtbar grau.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sechs gemeldete Punkte plus ein Fund beim Nachlesen des Ladevorgangs:
- HECKKLAPPENSCHLOSS_SENSOR ueberall entfernt (einstellungen.py,
entitaeten.py-Katalog, _sicherheitscheck()). HECKKLAPPE_SENSOR bleibt.
- Bild-Platzhalter in den Einstellungen war halb abgeschnitten: die bare
.carfix-Regel der Desktop-Container-Query traf auch den Platzhalter-Div,
dessen inset:0 von der festen Hoehe uebersteuert wurde. Auf .bildbox
eingegrenzt.
- Inspektion bekommt eine eigene "voraussichtlich am ..."-Zeile (nur Datum).
oelwechselPrognose() dafuer auf den gemeinsamen Kern servicePrognose()
zurueckgefuehrt.
- "Neue Fahrt" bietet jetzt alle Felder, die die Einzelfahrt anzeigt, und die
Einzelfahrt laesst sich bearbeiten - gemeinsames fahrtFelder()/
fahrtFormularWerte() nach dem Muster von tankFelder(). Backend: Service
audi_dashboard_fahrt_aktualisieren, profil.fahrt_bearbeiten() (edited_fields
schuetzt gegen die Automatik, nicht gegen den Nutzer). Die Art-Pille
speichert jetzt ebenfalls statt nur lokal umzuschalten.
- Titelzeile steht auf dem Handy buendig zu den Kacheln: .back:not(.on) gibt
seine Breite und den Gap auf allen Breiten frei, .topbar links 16px.
- Ladebildschirm: nachladeAnstossen() und datenLaden() stiegen bei fehlendem
HASS aus, ohne eine naechste Runde zu planen - genau die Sackgasse, aus der
bisher nur ein Menueklick half. Beide planen jetzt weiter, der Timer wird
beim Abhaengen gestoppt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ctrl+V does not reach the dialog either - Chrome only delivers a paste
gesture when there is a paste target, and the dialog has no input field.
So the shortcut is no longer mentioned anywhere; the computer now offers
exactly two routes, dragging and the file picker, and the phone keeps the
clipboard button. The paste listener stays as a silent bonus for browsers
that do deliver the event, and the now-orphaned tastenkuerzelEinfuegen()
is gone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The "paste from clipboard" button could never work on a desktop:
clipboard.read() only ever exposes text, HTML and images by spec, so a
file copied in Explorer is not offered through it - and the dialog then
wrongly blamed the file for not being a PDF. It is now hidden on
pointer-coarse devices only, where it does work; on a PC the drop zone
names Strg+V, which is the path that carries the actual file.
Detection also relied solely on the MIME type, but a file copied or
dragged from Explorer can arrive with an empty type and was rejected -
istPdf() now falls back to the extension.
Price per litre now shows 2 decimals everywhere (list, receipt detail,
both entry forms, CSV) instead of 3.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pull-to-refresh gesture logic was already correct (verified by driving
real pointer events through it); what was missing was overscroll-behavior
on the scroll container, so on real touch hardware the browser's own
native overscroll could take the gesture over before it hit the threshold.
"Beleg hochladen" now opens a dialog instead of the native file picker:
drag a PDF in at the computer, paste it from the clipboard on the phone
(copied out of a mail), file picker kept as a third route. All three
sources share one upload path.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Read Tab Bars, Sidebars, Sheets, Alerts, Action Sheets, Toolbars, Buttons,
Pickers, Loading, Feedback, Privacy, and Undo/Redo off developer.apple.com
and cross-checked each against the actual code. profilSpeichern() (the
shared save path for ~25 fields) had no error handling at any call site,
so a failed backend write was an unhandled promise rejection with zero
user feedback - fixed centrally, plus the same for serviceRufen(). Also
added a spinner to the "Ladt ..." bootstrap screen and gave the
Anzugsmoment/km-correction popups the same primary-button styling the
Setup popup already used.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaced the hand-drawn placeholder battery icon with the real Audi CI
battery-12v-l/-s (user supplied the actual SVGs) next to "Ruhespannung",
matching the existing poi-l/poi-s size-variant convention.
Added a km-correction control to the Sommerräder/Winterräder tiles: a
pencil icon (Audi CI edit-s) opens an inline field to manually correct
the tire-set's accumulated km. The tricky part was persistence -
configCarZuProfil() deliberately never writes km back (a stale browser
copy could otherwise clobber a since-elapsed automatic increment), so
this needed its own backend service, audi_dashboard_reifen_km_setzen,
that overwrites only the stored counter and leaves referenz_odo_km
alone - the existing odometer-delta tracking in reifenzaehler.py then
continues accumulating from the corrected value on its own, no other
backend change required.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tile headline now reads "Sommerräder"/"Winterräder" and renders as an
actual headline (new .tile-headline class, 20px/var(--fg)) instead of the
small muted .label styling used for other in-tile section titles.
Headline + km now sit above "Montiert" instead of the wheel photo
overlaying it: new .radkopf-row (flex) puts headline/km and the photo
side by side at the tile's top; Montiert dropped position:absolute (no
longer needed once the photo stopped sharing its space) and is now a
normal-flow pill below the headline block.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reported "whole tab bar disappears when hiding labels" bug was real,
just one level deeper than first checked. .tabbar.ohne .tab span{display:
none} targeted the label span, but the icon is also wrapped in a <span
class="tabpille"> - a bare "span" type selector doesn't care about class,
so the icon's own wrapper collapsed too. Confirmed by measuring the <svg>
directly (0x0 bounding rect) after a live screenshot showed all 5 tab
buttons empty. Fixed with :not(.tabpille) in both stylesheets.
The Modell dropdown was still bright despite the color-scheme hint from
the last commit: a <select>'s opened option list is rendered by the
browser/OS as its own surface, entirely outside the page's paint tree -
confirmed when a screenshot call hung 30s trying to capture it open.
color-scheme only gets partial credit there; explicit background-color/
color on <option> is what Chrome/Firefox/Edge actually honor for the
popup rows. Added that, verified via getComputedStyle since the open
popup itself can't be screenshotted by this tooling.
Swapped the wheel photo (now right-aligned via margin-left:auto on the
now-correctly-square .bildbox.radbild) and Montiert (already left) per
request.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
.bildbox.radbild lost to a same-specificity, later-declared .bildbox rule,
so the wheel photo stayed a full-width 4:3 box despite the earlier crop
fix - only which slice showed ever changed. Fixed with a compound selector
and moved "Montiert" to top-left to match the now-correctly-sized photo.
Native <select> popups had no color-scheme hint and rendered in the
browser's default light palette regardless of the app's dark theme -
this is what made "Modell" and other dropdowns flash bright white.
bildWeiter() (Mein Audi image-cycle tap) referenced a CSS class that was
never emitted (.platzhalter-datei vs. the real .platzhalter-aktion),
throwing and aborting before the page-dot indicator update - the photo
advanced but the dots never moved.
Merged the separate "Fahrzeugbilder" upload grid into "Bild der
Übersicht": pick a view from the existing dropdown, tap its preview to
upload/replace/delete - same generic popup plumbing, no new mechanism.
Removed the now-dead BILDER_UPLOAD_SLOTS/.bildgrid/.bildslot/.carfix.mini.
Back arrow changed from red to the neutral headline color, matching every
other navigation-color decision in this project.
Investigated but could not reproduce: user-reported "whole tab bar
disappears" when hiding labels. Traced the exact commit that added the
icon+label highlight and confirmed it's correctly scoped to :not(.ohne);
live DOM/computed-style testing across toggle, navigation, and both
mobile/desktop widths showed the tab bar staying visible throughout.
Left unchanged pending a repro from the user.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Battery voltage stat cutoff 13.2V -> 12.8V (values above that are already
alternator output, not the battery); fixed the battery chart's Y-axis
"stretching" on zoom by computing it once from the full dataset; removed
two explanatory paragraphs from the battery detail view; Tankfuellung now
shows "X % / X l"; removed the "noch etwa X l im Tank" suffix; fuel bar
color now matches the headline instead of a red/yellow/green gradient;
active/primary buttons are grey again, not red (destructive stays red per
HIG); select fields gained an Apple-HIG pull-down chevron (appearance:none
had removed the native one with nothing replacing it); selected menu icon
is white on desktop, matching mobile; Reifenfoto crops from the right edge;
"Montiert" button rebuilt as a compact top-right pill with a 44px invisible
tap target; tab-bar active highlight now covers icon+label, not just the
icon; removed two leftover explanatory texts from Einstellungen.
Root-caused the map pins looking "transparent": the CI poi-car/poi icons
are pure contour forms (nonzero-fill-rule ring + thin detail lines, only
13-40% of their own bounding box actually filled). Fixed by extracting each
icon's own first sub-path (the true outer balloon silhouette, verified by
rasterizing it in isolation) as a solid-filled layer behind the original
icon, applied to both the vehicle and station markers.
Read Color/Typography/Layout/Buttons/Materials on developer.apple.com and
applied the user-approved subset (11px text floor, 44px tap targets);
declined items (font weight, materials) documented with rationale.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The station marker was a red circleMarker. It is now the CI poi pin
(poi-l/poi-s via tankstellenMarkerSVG) - the plain pin rather than poi-car, so
the vehicle and the station stay distinguishable on a map.
While testing it turned out nothing in the backend ever writes station_lat /
station_lon, so that marker had never been reachable in practice. The receipt
address is now geocoded in the frontend via Nominatim /search
(adresseAufloesen, the forward counterpart to the existing reverse lookup),
with results cached in localStorage - negative ones too, since a station
address does not move and Nominatim's terms ask for caching. Verified live:
"Shell, SÖLDEN" resolves to 46.9756 / 11.0111.
The map card gained a centre button (CI.gps, which is byte-identical to the
delivered gps-s.svg), disabled until the coordinates arrive. .mapbox needed
position:relative so the control anchors to the card instead of an ancestor.
Only in the Einzelbeleg, as requested - the Standort fullscreen already has
its own centre controls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Installieren.cmd (double-click) wraps install.ps1, which automates steps 1-3
of ANLEITUNG.md: find the HA config share, copy pyscript/, data/ ->
audi_dashboard/ and www/, merge the configuration.yaml snippet, optionally
write ha_token.txt. The .cmd wrapper exists because double-clicking a .ps1
opens it in an editor and the execution policy blocks it; the bypass applies
to that one call only.
Designed to be safe to re-run:
- Never overwrites fahrzeugprofil.json, fahrten.jsonl, tankvorgaenge.jsonl,
entitaeten.json or ha_token.txt, so re-running on a live install keeps the
vehicle profile, trips, fills and sensor mapping.
- configuration.yaml is backed up to .bak first; the inserted part sits
between marker lines and gets replaced instead of appended twice.
- If pyscript: or panel_custom: are already there from another source, the
file is left untouched and the script prints what to merge by hand -
duplicate top-level keys would be invalid YAML and HA would not start.
- Aborts before writing if the target has no configuration.yaml.
Steps that cannot be automated from a Windows share (HA restart, pip install
pypdf, sensor mapping) are collected into a closing to-do list. -Pruefen shows
what would happen without writing.
install.ps1 is stored UTF-8 *with* BOM - PowerShell 5.1 reads scripts as ANSI
otherwise and mangles the umlauts; the files it writes stay BOM-less.
Verified against a fake config tree: fresh install, re-run idempotency,
existing-data preservation, foreign-key refusal, wrong-directory abort, and
the merged configuration.yaml parses as valid YAML.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The "grey box around the icons" report turned out not to be a defect: the box
on "10.000 km"/"1 Jahr"/"15" is the wanted pattern, and every value the user
can change should carry it. All .feld inputs and selects now get the grey iOS
fill; inputs with their own visual language (checkbox/radio/range/file) keep
the transparent base rule, so the switches are unaffected.
Fuel stations are now displayed as "Shell, Pascalstr. 8, Ingolstadt" instead of
the bare operator name. New _marke()/_ist_strasse()/_tankstelle() helpers are
shared by both parser paths so Shell and non-Shell receipts format the same.
The brand is only matched against the receipt header - searching the whole text
would let "Total" hit a totals line. Missing parts are dropped instead of
leaving empty comma slots, and without a known brand the operator name takes
its place. station_address still carries the full street and postcode.
test_bekannte_stationen updated to the new format; suite stays green.
installationspaket/ is versioned from now on (user request). It contains no
real vehicle data - only the example profile with empty FIN/plate placeholders.
Keep it in sync whenever pyscript/ or www/ changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>