Reifen/Version/Wartungsplan: erste Runde des Sammel-Feedbacks behoben

- Archiv-Knopf jetzt in Zeile mit "Montiert" (stale margin-top entfernt),
  bekommt einen sichtbaren Hintergrund
- companion-app: Wechseltermin-Datumsfeld (appearance-Reset gegen
  natives Safari-Chrome, Breite auf Inhalt geschrumpft)
- Mein Audi/Reifen-Box: "montiert: Sommerräder" links statt Marke/Modell,
  "offen" durch "-" ersetzt
- "Servicebuch" ueberall in "Wartungsplan" umbenannt (nur Anzeigetext,
  beide Codebasen)
- Version-Kachel: tote Fahrzeugdaten-/Position-/Dashboard-Zeilen entfernt
  (inkl. der nie aktualisierten CONFIG.version-Konstante)

Version 2026.8.28.1, live im Testcontainer verifiziert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-28 09:43:40 +02:00
parent bdd8cb89a3
commit edefdd66d8
9 changed files with 129 additions and 31 deletions
+80
View File
@@ -3939,6 +3939,86 @@ reading source): the battery list's `<div class="leaf">` now computes `cursor: a
a genuinely navigable trip row (`<button class="leaf">`) still correctly computes `cursor: pointer` -
confirming the fix is properly scoped, not a blanket removal.
## AB. First round of a large owner batch: Reifen/Version/Wartungsplan fixes (2026.8.28.1)
Owner sent 12 items in one message. Worked through the quick, well-specified ones first; the two larger
ones (combined export/import popup, Setup-menu sensor field redesign) are being scoped with the owner before
implementation - see the open items this creates below.
- **Archiv-Knopf nicht in Zeile mit "Montiert"** (Reifen-Seite, panel only - no companion-app equivalent):
root cause was `.montiert`'s own `margin-top:var(--sp-4)` (added 2026-08-16 round eight, when the pill sat
alone under the headline block) - once the archive icon button joined it on the same flex row (section R),
that leftover margin threw off `align-items:center`'s vertical centering between the two children, even
though both were genuinely siblings in the same row. Moved the spacing to the wrapping row div instead of
one child. Verified live: both centers compute to the identical `584.08px` Y-coordinate.
- **Archiv-Icon soll ein Knopf mit Hintergrund sein**: new `.archivbtn` class (panel), permanent
`background: var(--tile-2)` instead of the generic `.iconbtn`'s hover/active-only background.
- **companion-app: Wechseltermin-Datumsfeld sah aus wie ein gestreckter Kreis statt einer Box**: root cause
is the standard cross-browser gap - `input[type="date"]` needs an explicit `appearance:none` reset or
Safari/WKWebView (this app's iOS shell) keeps its own heavily-rounded native chrome regardless of author
CSS on `background`/`border`/`border-radius`. Neither `.dm-eingabe` nor the shared `@audi-dash/ui` `Feld`
component reset it. Fixed locally in `companion-app/src/stile/screens.css` (not in the shared design-system
package - no other field type shows this, scoped to `input[type="date"].dm-eingabe` only) with
`-webkit-appearance:none`/`appearance:none` plus `width:fit-content` (owner's explicit ask: shrink to the
actual date text instead of the shared `Feld`'s `min-width:132px`). **Not live-verified** - no running
companion-app session with backend auth this round; verified via `tsc --noEmit` and the full suite only,
same limitation as several other companion-app-only changes this session.
- **Mein Audi/Reifen-Box restructured**: was `Reifen · Sommer montiert` as the tile header, then
`<dt>Continental SportContact</dt><dd>275/30 R20 97Y XL</dd>` as the first row. Header simplified to just
"Reifen"; the row's left side now reads `montiert: Sommerräder` (owner's exact wording, `${r.aktiv}räder`)
instead of the brand/model, keeping the size (`275/30 R20 97Y XL`) as the right-hand value, per the owner's
explicit "montiert... shall be on the left side". companion-app's equivalent "Mein Audi" tile
(`MeinAudi.tsx`) is structurally a single-line `NaviKachel` (`${aktiv} · ${km} km`), not the panel's
multi-row `.rows` list - it never had "Sommer montiert" or brand/model text to begin with, so nothing to
port here (documented structural difference, not a skipped fix).
- **"offen" caption removed from the Wechseltermin row**: `vereinbart ? "vereinbart" : "offen"``... : ""`
- owner: "the '-' is sufficient". Same structural note as above - companion-app's tile has no equivalent
caption to change.
- **"Servicebuch" renamed to "Wartungsplan" everywhere it's user-visible**, both codebases - tile/page
headings, empty-state text, confirm-delete dialogs, CSV export button label, the "Mein Audi" Service
NaviKachel's summary text. Deliberately NOT renamed: the `ServicebuchEintrag` TS type, the `Servicebuch()`
component/function names (panel and companion-app), the `sbuch` route key, and every code comment - same
precedent as the 2026-08-19 "Zusammen"→"Summe" rename (display-label fix, not a data-model rename).
- **"Version" tile cleaned up**: removed the "Fahrzeugdaten"/"Position" rows (both hardcoded to the literal
string "FMM003", carried no live information) and the "Dashboard" row, whose value came from `CONFIG.version`
- a frontend constant hardcoded to `"1.0"` at `profilZuConfig()` (confirmed via grep: no other reader),
never updated since. Removed that dead constant along with its row rather than leaving an orphan. Only
"Installiert" (the real `APP_VERSION` from the backend) remains.
Verified: `node --check` clean on the panel, `tsc --noEmit` clean and full suite green (146/146) on
companion-app. Manifest bumped to `2026.8.28.1` (new day, per this project's `YYYY.M.D.N` scheme - the prior
session's work had reached `.27.22`). `audi_ha_test` restarted, confirmed clean via log
(`Audi Dashboard 2026.8.28.1 eingerichtet`). Live-verified via the panel's own shadow DOM: Reifen tile shows
`montiert: Sommerräder | 275/30 R20 97Y XL` and `15.10.2026 | `; Version tile shows only "Installiert"; the
Archiv button and "Montiert" share one Y-center exactly, with a visible background disc behind the icon
(screenshot taken).
**Open, not yet started - scoped with the owner before implementation:**
- **Item 8 (informational, answered in chat, not yet acted on)**: both the live path
(`tankerkennung.py``ablage.distanz_seit_letzter_tankung()`) and the import path
(`historienimport.py`'s `_distanz_zum_vorherigen()`) compute "distance since last refuel" as an odometer
delta (current/refuel `KM_SENSOR` reading minus the previous refuel's own recorded `odometer_km`) - a
derived calculation, not read from any vehicle sensor. Owner pointed out a real FMM003 sensor,
`sensor.testzone_fmm003_calculated_distance_covered_after_the_latest_refueling` (device-side, presumably
more accurate/authoritative than our own subtraction), that reports this directly and "can be used instead
of the calculation" - not yet wired in, would mirror the `TANK_LITER_SENSOR` precedent (section Q): new
optional sensor role, preferred over the calculation when mapped, calculation stays as the fallback when
unmapped. Owner to confirm before this is built.
- **Item 9**: combine the "Fahrzeugprofil" tile (`data-profil="export"`/`"import"`, JSON) and "Daten ausgeben"
tile (`data-csv="fahrten"`/`"tanken"`/`"service"`, CSV export only, no import) into one area with two
buttons - "Datensatz sichern" opening a popup with 4 individual export actions (Fahrzeugprofil,
Fahrten/Tankvorgänge/Wartungsplan as CSV), "Datensatz laden" the same 4 in reverse. The pre-existing,
separate "Backup" tile (`data-backup="jetzt"`/`"export"`/`"import"`, a combined profil+fahrten+tank JSON
bundle plus the scheduled-backup mechanism) was NOT named by the owner and stays untouched. Three of the
four export actions and one of the four import actions already exist and just need moving into the new
popup; CSV **import** for Fahrten/Tankvorgänge/Wartungsplan does not exist anywhere yet and needs new
backend parsing (inverse of the existing CSV export format: `;`-delimited, de-DE number format).
- **Item 11**: Setup-Menü sensor role fields - replace the current description text under each field's
headline (e.g. "on = Fahrt läuft", "Optional, vom CAN...") with the live sensor's current output value and
unit inline next to the headline (`[on/off]`, `[%]`, `[l]`, `[km]`...), and show the matched entity's ID
suffix (the part after `fmm003_...`) as "the expected sensor" - not yet investigated in the code
(`entitaeten.py`'s `FELDER` catalog / the Setup popup's row-rendering in the panel).
---
## Working conventions (observed — keep them)
+1 -1
View File
@@ -83,7 +83,7 @@ export const TITEL: Record<SeitenName, string> = {
battliste: "Messwerte",
service: "Service",
werkstatt: "Werkstatt",
sbuch: "Servicebuch",
sbuch: "Wartungsplan",
reifen: "Reifen",
vers: "Versicherung & Steuer",
beitrag: "Beitrag",
+1 -1
View File
@@ -55,7 +55,7 @@ export function MeinAudi({ geheZu }: { geheZu: (name: SeitenName, id?: string) =
}
beiKlick={() => geheZu("battverlauf")}
/>
<NaviKachel titel="Service" wert="Termine und Servicebuch" beiKlick={() => geheZu("service")} />
<NaviKachel titel="Service" wert="Termine und Wartungsplan" beiKlick={() => geheZu("service")} />
<NaviKachel
titel="Reifen"
wert={`${fahrzeug.reifen.aktiv} · ${deOderStrich(aktiverSatz.km)} km`}
+7 -7
View File
@@ -140,7 +140,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
{oelFussnote && <div className="dm-tgprognose">{oelFussnote}</div>}
</>
) : (
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Servicebuch</div>
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Wartungsplan</div>
)}
</div>
@@ -156,7 +156,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
{inspFussnote && <div className="dm-tgprognose">{inspFussnote}</div>}
</>
) : (
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Servicebuch</div>
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Wartungsplan</div>
)}
</div>
@@ -167,7 +167,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
{huGueltig ? (
<div className="dm-tgwert">{datum(fahrzeug.hu)}</div>
) : (
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Servicebuch</div>
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Wartungsplan</div>
)}
</div>
@@ -183,7 +183,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
</div>
)}
<p className="dm-fussnote">
Die eigene Prognose rechnet ab dem letzten Eintrag der jeweiligen Art im Servicebuch neu
Die eigene Prognose rechnet ab dem letzten Eintrag der jeweiligen Art im Wartungsplan neu
und nutzt die seither tatsächlich gefahrene Strecke. Sie ersetzt die Meldung des
Fahrzeugs nicht, sondern ergänzt sie.
</p>
@@ -196,7 +196,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
beiKlick={() => geheZu("werkstatt")}
/>
<NaviKachel
titel="Servicebuch"
titel="Wartungsplan"
wert={`${de(buch.length)} Eintrag${buch.length === 1 ? "" : "e"}`}
beiKlick={() => geheZu("sbuch")}
/>
@@ -324,7 +324,7 @@ export function Servicebuch() {
}
const entfernen = async (index: number) => {
if (!bestaetigen("Diesen Servicebuch-Eintrag löschen?")) return
if (!bestaetigen("Diesen Wartungsplan-Eintrag löschen?")) return
await schreiben(buch.filter((_, i) => i !== index))
}
@@ -397,7 +397,7 @@ export function Servicebuch() {
{buch.length === 0 && !neuOffen && (
<Leerzustand
titel="Servicebuch ist leer"
titel="Wartungsplan ist leer"
text="Trag hier ein, was am Fahrzeug gemacht wurde. Aus den Ölwechseln entsteht die eigene Wartungsprognose."
aktion={{ beschriftung: "Eintrag hinzufügen", beiKlick: () => setzeNeuOffen(true) }}
/>
+1 -1
View File
@@ -176,7 +176,7 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string)
</span>
</>
) : (
<span className="dm-fussnote">kein Eintrag im Servicebuch</span>
<span className="dm-fussnote">kein Eintrag im Wartungsplan</span>
)}
</button>
<div className="dm-servicezeile">
+16
View File
@@ -24,6 +24,22 @@
color: var(--fg3);
}
/* type="date": ohne appearance-Reset behält Safari/WKWebView (die iOS-Hülle
dieser App) sein eigenes, stark abgerundetes natives Eingabe-Chrome statt
der Box-Optik aus @audi-dash/ui's Feld - beim Nutzer sichtbar als
"gestreckter Kreis statt Box" (Reifen.tsx, Wechseltermin). @audi-dash/ui
selbst bleibt unangetastet (kein appearance-Reset dort, andere Felder
zeigen den Fehler nicht) - der Reset sitzt hier, spezifisch für type=date,
höhere Selektor-Spezifität als das geteilte ".ads-feld input" schlägt
dessen feste min-width:132px, die hier breiter ist als "27.08.2026"
braucht. */
input[type="date"].dm-eingabe {
-webkit-appearance: none;
appearance: none;
min-width: 0;
width: fit-content;
}
/* Eine Zeile aus einer bearbeitbaren Liste (z. B. Selbstbeteiligung im
Vertrag) - zwei Eingaben plus Löschen-Knopf in einer Reihe, deckungsgleich
mit .feld/.zeile-loeschen im Panel (audi-dashboard.css). */
@@ -116,7 +116,6 @@ function profilZuConfig(p) {
fahrzeuguntertitel: p.fahrzeug.untertitel,
kennzeichen: p.fahrzeug.kennzeichen,
startbild: p.einstellungen.uebersichtsbild,
version: "1.0",
tankvolumen: p.fahrzeug.tankvolumen_liter,
nachtVon: p.einstellungen.nacht_von,
nachtBis: p.einstellungen.nacht_bis,
@@ -1394,7 +1393,7 @@ function terminText(t) {
const restKmAbs = restKm !== null ? Math.abs(restKm) : null;
const haupt = t.zuerst === "km" && restKm !== null ? "in " + de(restKmAbs) + " km" : dedat(t.datum);
const neben = t.zuerst === "km" ? "voraussichtlich " + dedat(t.kmDatum) + " · " + dedat(t.datum)
: (t.km !== null ? "in " + de(restKmAbs) + " km" : (t.art === "Hauptuntersuchung" ? "" : "aus dem Servicebuch fortgeschrieben"));
: (t.km !== null ? "in " + de(restKmAbs) + " km" : (t.art === "Hauptuntersuchung" ? "" : "aus dem Wartungsplan fortgeschrieben"));
return { haupt, neben, restKm };
}
@@ -1468,7 +1467,7 @@ function vHome() {
</div>`;
if (!t) return `<div class="tile" style="margin-bottom:22px">
<span class="label">Nächster Service:</span>
<div class="wert" style="margin-top:8px">kein Eintrag im Servicebuch</div>
<div class="wert" style="margin-top:8px">kein Eintrag im Wartungsplan</div>
${kmZeile}
</div>`;
// naechsterTermin() waehlt bereits ueber alle drei Arten hinweg (Oelwechsel,
@@ -1656,13 +1655,13 @@ function vAudi() {
<dd>${eur(CAR.versicherung.beitrag + CAR.steuer.betrag)} </dd></div>
</dl></button>
<button class="tile tilebtn" data-go="reifen:x">
<span class="label">Reifen · ${r.aktiv} montiert</span>
<span class="label">Reifen</span>
<svg class="go" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg>
<dl class="rows" style="margin-top:10px">
<div class="row"><dt>${esc(a.marke)} ${esc(a.modell)}</dt><dd>${esc(a.mass)}</dd></div>
<div class="row"><dt>montiert: ${r.aktiv}räder</dt><dd>${esc(a.mass)}</dd></div>
<div class="row"><dt>Laufleistung</dt><dd>${a.km != null ? de(a.km) + " km" : "unbekannt"}</dd></div>
<div class="row" style="border-bottom:0"><dt>Wechseltermin</dt>
<dd>${dedat(new Date(r.wechsel.datum))}<small>${r.wechsel.vereinbart ? "vereinbart" : "offen"}</small></dd></div>
<dd>${dedat(new Date(r.wechsel.datum))}<small>${r.wechsel.vereinbart ? "vereinbart" : ""}</small></dd></div>
</dl></button>`;
}
@@ -2039,7 +2038,7 @@ function vService() {
const iv = intervalle(t);
if (!iv && !hatMeldung) return `<div class="termingruppe">
<div class="tg-kopf">${kopf}</div>
<div class="tg-wert leer">kein Eintrag im Servicebuch</div>
<div class="tg-wert leer">kein Eintrag im Wartungsplan</div>
</div>`;
const anzeige = iv ? (iv.hersteller || iv.eigen) : null;
// Forecast-Zeile aus dem kundeneigenen Intervall in "Einrichten" mit
@@ -2119,13 +2118,13 @@ function vService() {
href="https://maps.google.com/?q=${encodeURIComponent(sv.autohaus.adresse)}"
style="display:block;text-decoration:none;text-align:center">In Karten öffnen</a>
</div>
<div class="tile"><span class="label">Servicebuch</span>
<div class="tile"><span class="label">Wartungsplan</span>
${sv.buch.length ? sv.buch.map((e, i) => swipeZeile(`sbuch:${i}`, `<button class="leaf" style="padding-left:0" data-go="sbuch:${i}">
<span class="k">${esc(e.art)}<small>${esc(e.werkstatt)}</small></span>
<span class="v">${e.datum}<small>${de(e.km)} km${e.kosten != null ? " · " + eur(e.kosten) + " €" : ""}</small></span>
<svg class="chev" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg></button>`)).join("") : `<div class="leer" style="margin-top:12px">
<strong>Noch keine Einträge</strong>
<p>Aus dem Servicebuch berechnet die App die Fälligkeit von Inspektion, Ölwechsel und Hauptuntersuchung. Der erste Eintrag ist die Grundlage für jede Prognose.</p>
<p>Aus dem Wartungsplan berechnet die App die Fälligkeit von Inspektion, Ölwechsel und Hauptuntersuchung. Der erste Eintrag ist die Grundlage für jede Prognose.</p>
</div>`}
<button class="aktion primaer" data-sbneu>Eintrag hinzufügen</button>
</div>`;
@@ -2653,14 +2652,11 @@ function vEinst() {
Tabellenprogrammen aufgehen.</span>
<button class="aktion" data-csv="fahrten">Fahrten als CSV</button>
<button class="aktion" data-csv="tanken">Tankvorgänge als CSV</button>
<button class="aktion" data-csv="service">Servicebuch als CSV</button>
<button class="aktion" data-csv="service">Wartungsplan als CSV</button>
</div>
<div class="tile"><span class="label">Version</span>
<dl class="rows">
<div class="row"><dt>Dashboard</dt><dd>${esc(CONFIG.version)}</dd></div>
<div class="row"><dt>Installiert</dt><dd>${esc(APP_VERSION || "unbekannt")}<small>Home-Assistant-Integration</small></dd></div>
<div class="row"><dt>Fahrzeugdaten</dt><dd>FMM003<small>Zündung · Batterie</small></dd></div>
<div class="row" style="border-bottom:0"><dt>Position</dt><dd>FMM003<small>GPS-Tracker</small></dd></div>
<div class="row" style="border-bottom:0"><dt>Installiert</dt><dd>${esc(APP_VERSION || "unbekannt")}<small>Home-Assistant-Integration</small></dd></div>
</dl>
</div>
<div class="tile"><span class="label">Integration-Update</span>
@@ -2958,9 +2954,9 @@ function vReifen() {
<button type="button" data-bildersetzen="${radDatei}">Foto ersetzen</button>
<button type="button" class="loeschen" data-bildloeschen="${radDatei}">Löschen</button>
</div>` : ""}
<div style="display:flex;justify-content:space-between;align-items:center">
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:var(--sp-4)">
<button data-satz="${k === "sommer" ? "Sommer" : "Winter"}" class="montiert ${aktiv ? "on" : ""}">Montiert</button>
<button class="iconbtn" data-reifenarchivieren="${k}" aria-label="Neue Räder anlegen">${ciSVG(CI.packageS, 18)}</button>
<button class="iconbtn archivbtn" data-reifenarchivieren="${k}" aria-label="Neue Räder anlegen">${ciSVG(CI.packageS, 18)}</button>
</div>
<div style="margin-top:12px">
${feld(k, "marke", "Hersteller")}
@@ -3418,14 +3414,14 @@ function swipeZeile(id, inhaltHtml) {
const LOESCH_TEXT = {
trip: "Fahrt löschen?",
fill: "Tankvorgang löschen?",
sbuch: "Servicebuch-Eintrag löschen?",
sbuch: "Wartungsplan-Eintrag löschen?",
bild: "Foto löschen?",
batt: "Messwert löschen?",
};
const LOESCH_HINWEIS = {
trip: "Die Fahrt wird aus dem Fahrtenbuch entfernt. Das lässt sich nicht widerrufen.",
fill: "Der Tankvorgang und der zugehörige Beleg werden entfernt. Das lässt sich nicht widerrufen.",
sbuch: "Der Eintrag wird aus dem Servicebuch entfernt. Das lässt sich nicht widerrufen.",
sbuch: "Der Eintrag wird aus dem Wartungsplan entfernt. Das lässt sich nicht widerrufen.",
bild: "Das Foto wird vom Server gelöscht. Der Platz bleibt bestehen und kann neu belegt werden.",
batt: "Der Tageseintrag wird aus der Messwertliste entfernt. Das lässt sich nicht widerrufen.",
};
@@ -4077,7 +4073,7 @@ function render() {
else if (route.name === "werkstatt") { head = ["Service", "Autohaus"]; v.innerHTML = vWerkstatt(); }
else if (route.name === "battverlauf") { head = ["Fahrzeug", "Batteriespannung"]; v.innerHTML = vBatterieverlauf(); bvZeichnen(); }
else if (route.name === "battliste") { head = ["Batteriespannung", "Messwerte"]; v.innerHTML = vBatterieliste(); }
else if (route.name === "sbuch") { head = ["Servicebuch", "Eintrag"]; v.innerHTML = vSbuch(); }
else if (route.name === "sbuch") { head = ["Wartungsplan", "Eintrag"]; v.innerHTML = vSbuch(); }
else if (route.name === "standort") { head = ["Fahrzeug", "Standort"]; v.innerHTML = vStandort(); }
else { const t = TABS.find((x) => x.id === route.name); head = [t.eyebrow, t.id === "audi" ? "Mein Audi" : t.title];
v.innerHTML = { audi: vAudi, trips: vTrips, stat: vStat, fuel: vFuel }[route.name](); }
@@ -182,6 +182,12 @@
}
.iconbtn:hover { background: var(--tile-2); color: var(--fg); }
.iconbtn:active { background: var(--tile-2); color: var(--fg); transform: scale(.92); }
/* "Neue Räder anlegen" auf der Reifen-Seite: bewusst dauerhaft mit
sichtbarem Hintergrund statt nur bei Hover/Tap wie .iconbtn sonst - sitzt
direkt neben dem gefüllten "Montiert"-Knopf und wirkt ohne eigene Fläche
zu unscheinbar, um dort als eigenständiger Knopf erkannt zu werden. */
.archivbtn { background: var(--tile-2); color: var(--fg2); }
.archivbtn:hover, .archivbtn:active { color: var(--fg); }
/* D4 war urspruenglich: die Ringe sind Marke, kein Knopf. Seit die
iOS-Auflage in Telefonbreite das Zahnrad ausblendet, sind sie dort aber der
einzige Zugang zu den Einstellungen - und als reines div mit
@@ -1131,7 +1137,7 @@ button.leaf:active { background: var(--tile-2); }
überlagert (das saß nur dort, solange Überschrift+km diesen Platz noch
nicht beanspruchten). */
.montiert {
position: relative; margin-top: var(--sp-4);
position: relative;
display: inline-flex; align-items: center; justify-content: center;
font-family: inherit; font-size: 12px; letter-spacing: normal;
color: var(--fg2);
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.8.27.22",
"version": "2026.8.28.1",
"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"],