Tankstellen-Nadel, doppeltes Tanken-Symbol, Tankstellenname, packageClassList

Fuenf Meldungen des Eigentuemers, vier davon behoben:

1. Tankstellen-Nadel war bei 32 px ein dunkler Fleck (poi zeichnet nur
   einen Ring). Gefuellter Punkt in currentColor zwischen Silhouette und
   Symbol - die Geometrie stand im Pfad. Rot wurde ausdruecklich
   abgelehnt, die Umsetzung aendert keine Farbe.

2. Tanken-Symbol im Standort-Blatt wurde gefuellt UND umrandet, weil das
   CI-Symbol zusaetzlich stroke-Attribute mitbekam. Jetzt ueber ciSVG
   wie das Parkplatz-Symbol daneben; in der App dafuer imRaster an
   SymbolTanken (der 0,84-Faktor gilt nur in der Reiterleiste).

3. Tankstellenname: der Bestand hat zwei Bauformen - aeltere Belege
   tragen den Betreiber in station_name und die Anschrift daneben.
   Neu tankstelleTeile() liest beide Felder, entfernt Firmierungen,
   kuerzt Strassen und trennt die Hausnummer auch ohne Leerzeichen.
   Uebersicht: Marke, Strasse, Ort. Einzelbeleg: zwei Zeilen.
   adresseTeilen() hat keinen Aufrufer mehr und ist entfernt.

4. Zwischenablage und Kalendereintrag hatten denselben Grund: Capacitor 8
   registriert nur, was in packageClassList steht, und cap sync traegt
   dort ausschliesslich npm-Pakete ein. Beide app-eigenen Plugins lagen
   im Programm und wurden nie registriert. ios-teilen-einrichten.mjs
   traegt sie jetzt nach, ios-signieren.sh bricht ab, wenn sie im
   fertigen .ipa fehlen.

5. Geteilter Beleg nur mit Datum und Uhrzeit bleibt offen - der Weg
   nutzt denselben belegLesen()-Pfad wie eine gewaehlte Datei, es fehlt
   der konkrete Beleg oder die Protokollzeile.

tsc sauber, 246 Tests, vite build sauber, Panel als Modul geparst,
audi_ha_test auf 2026.9.4.20 ohne Traceback. Panel und App liefern fuer
die Tankstelle byteweise dasselbe.
This commit is contained in:
2026-09-04 16:34:46 +02:00
parent cefb235f6d
commit a99ae432fc
14 changed files with 543 additions and 78 deletions
@@ -1 +1 @@
{"version":"2026.9.4.19","sha256":"58969a387bfa72c3d452c04b689e8ec01440c9a2a89b7a097d43fe0418092ae6","bytes":322568,"gebaut":"2026-09-04T14:06:53Z"}
{"version":"2026.9.4.20","sha256":"95213bb0998806d4b59b4d520d2dd93a4f1c7ce48991e6f77b38e9dbca8247a8","bytes":322945,"gebaut":"2026-09-04T14:28:33Z"}
@@ -981,7 +981,16 @@ function tankstellenMarkerSVG(px = 32) {
const box = gross ? 48 : 24;
const sil = gross ? CI.poiLSil : CI.poiSSil;
const icon = gross ? CI.poiL : CI.poiS;
return `<svg viewBox="0 0 ${box} ${box}" width="${px}" height="${px}" fill="none" aria-hidden="true">${sil}${icon}</svg>`;
// Der Ring im poi-Icon ist bei Kartengroesse zu duenn, um sich von der
// dunklen Silhouette abzuheben - die Nadel las sich als schwarzer Fleck
// (Nutzerbefund 04.09.2026). Der gefuellte Punkt schliesst genau den
// Kreis, der im Pfad ohnehin steht (poi-s: 11.5/8.5 r3.5, poi-l:
// 23.5/17.5 r7.5) - erfunden ist daran nichts, und keine Farbe aendert
// sich: currentColor ist dasselbe Weiss wie die uebrigen Konturlinien.
const punkt = gross
? '<circle cx="23.5" cy="17.5" r="6.2" fill="currentColor"/>'
: '<circle cx="11.5" cy="8.5" r="2.9" fill="currentColor"/>';
return `<svg viewBox="0 0 ${box} ${box}" width="${px}" height="${px}" fill="none" aria-hidden="true">${sil}${punkt}${icon}</svg>`;
}
/* Fahrzustand fürs Menü: kommt aus der Zündung (CAR.zuendung, vom Backend aus
@@ -1322,23 +1331,86 @@ function markeErkennen(name) {
return null;
}
function adresseTeilen(adresse) {
const roh = (adresse || "").trim();
if (!roh) return { strasse: "", ort: "" };
const komma = roh.lastIndexOf(",");
if (komma < 0) return { strasse: roh, ort: "" };
return { strasse: roh.slice(0, komma).trim(), ort: roh.slice(komma + 1).trim() };
/* Kennzeichen einer Firmierung. Ein Teil, der so etwas enthaelt, ist der
BETREIBER und gehoert nach Vorgabe des Eigentuemers nicht in die Anzeige. */
const FIRMA_MUSTER = /\b(gmbh|mbh|ohg|kg|ag|e\.\s?k\.|inh\.|& co)\b/i;
const PLZ_ORT_MUSTER = /^(\d{4,5})\s+(.+)$/;
/* Greedy bis zum letzten Nicht-Ziffernzeichen, damit auch die
zusammengeschriebene Form der Belege trifft: "Nürnberger Str.74". */
const STRASSE_NR_MUSTER = /^(.*\D)\s*(\d+\s*[a-zA-Z]?)$/;
/* "Nürnberger Straße" -> "Nürnberger Str.". Die Grossschreibung des Treffers
entscheidet, sonst stuende mitten im Wort ein grosses S. */
function strasseKuerzen(strasse) {
return strasse.replace(/stra(?:ß|ss)e\b\.?/gi, (t) => (t[0] === t[0].toUpperCase() ? "Str." : "str."));
}
/* Oben Kopfmarke und Strasse mit Hausnummer, unten Postleitzahl und Ort.
Ohne erkennbare Marke steht oben nur die Strasse - die Firmierung des
Betreibers tritt NICHT an ihre Stelle. Ohne Anschrift bleibt der
gespeicherte Name die einzige Auskunft und wird unveraendert gezeigt. */
/* Zerlegt, was ein Beleg ueber die Tankstelle hergibt.
Der Bestand hat zwei Bauformen, am 04.09.2026 an den echten Datensaetzen
nachgesehen:
bis 2025: station_name = "TC Sengül GmbH" (der Betreiber!)
station_address = "Nürnberger Str.74, 91522 Ansbach"
ab 08.2026: station_name = "Shell, Pascalstr. 8, Ingolstadt"
station_address = null
Die Markenerkennung lief bis hierher nur ueber den Namen - bei den aelteren
Belegen steht die Marke dort gar nicht, deshalb blieb in der Uebersicht die
Firmierung stehen ("...GmbH", Nutzerbefund 04.09.2026), waehrend die
Anschrift daneben ungenutzt blieb. Gelesen werden jetzt beide Felder. */
function tankstelleTeile(name, adresse) {
const teile = { marke: markeErkennen(name) || "", strasse: "", hausnummer: "", plz: "", ort: "", roh: (name || "").trim() };
const quellen = (adresse || "").split(",");
for (const stueck of (name || "").split(",")) {
const t = stueck.trim();
if (teile.marke && t.toLowerCase() === teile.marke.toLowerCase()) continue;
quellen.push(t);
}
for (const stueck of quellen) {
const t = stueck.trim();
if (!t || FIRMA_MUSTER.test(t)) continue;
const plzOrt = PLZ_ORT_MUSTER.exec(t);
if (plzOrt) {
if (!teile.plz) teile.plz = plzOrt[1];
if (!teile.ort) teile.ort = plzOrt[2].trim();
continue;
}
const mitNummer = STRASSE_NR_MUSTER.exec(t);
if (mitNummer) {
if (!teile.strasse) {
teile.strasse = strasseKuerzen(mitNummer[1].trim());
teile.hausnummer = mitNummer[2].replace(/\s+/g, "");
}
continue;
}
/* Weder Postleitzahl noch Hausnummer: der Ort. Eine Strasse ohne Nummer
laesst sich davon nicht sicher unterscheiden - im Zweifel der Ort, weil
er in der Uebersicht die wichtigere Angabe ist. */
if (!teile.ort) teile.ort = t;
}
return teile;
}
/* Zeile der Uebersicht: Marke, Strasse ohne Hausnummer, Ort ohne Postleitzahl
(Vorgabe des Eigentuemers, 04.09.2026). Ergibt die Zerlegung nichts, bleibt
der rohe Name stehen - ihn wegzulassen hiesse, eine bekannte Tankstelle
"unbekannt" zu nennen. */
function tankstelleKurz(name, adresse) {
const t = tankstelleTeile(name, adresse);
return [t.marke, t.strasse, t.ort].filter(Boolean).join(", ") || t.roh || "unbekannt";
}
/* Die beiden Zeilen im Einzelbeleg: oben Marke und Strasse mit Hausnummer,
unten Postleitzahl und Ort - geschrieben wie eine Anschrift, das Komma
trennt nur die Marke ab. `oben` ist nie leer. */
function tankstelleZeilen(name, adresse) {
const { strasse, ort } = adresseTeilen(adresse);
if (!strasse && !ort) return { oben: (name || "").trim() || "unbekannt", unten: "" };
const oben = [markeErkennen(name) || "", strasse].filter(Boolean).join(", ");
return { oben: oben || "unbekannt", unten: ort };
const t = tankstelleTeile(name, adresse);
const strasse = [t.strasse, t.hausnummer].filter(Boolean).join(" ");
let oben = [t.marke, strasse].filter(Boolean).join(", ");
let unten = [t.plz, t.ort].filter(Boolean).join(" ");
if (!oben && !unten) return { oben: t.roh || "unbekannt", unten: "" };
if (!oben) { oben = unten; unten = ""; }
return { oben, unten };
}
/* Suchbegriffe fuer die Geokodierung, in der Reihenfolge des Versuchs: erst
@@ -1768,7 +1840,13 @@ function vStandort() {
: `${ciSVG(CI.parking)}<span>${zustand.seitTs != null ? "Geparkt seit " + dauerSeitText(zustand.seitTs) : "Fahrzeug steht, Parkdauer unbekannt"}</span>`}
</div>
<div class="standortmenu-werte">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${ICONS.fuel}</svg>
<!-- CI-Symbole sind Flaechen und tragen ihr fill selbst. Die
Strichattribute von oben zogen zusaetzlich eine 1,5er Kontur um
jede Flaeche - das Symbol wurde gefuellt UND umrandet gezeichnet
und wirkte neben dem Parkplatz-Symbol dicker und dunkler
(Nutzerbefund 04.09.2026). Dieselbe Falle wie beim Zahnrad in
.profilbtn svg. Jetzt derselbe Weg wie fuer CI.parking daneben. -->
${ciSVG(CI.fuelStationS, 16)}
<span>${de(CAR.tankPct)} %</span>
<span>${CAR.reichweite != null ? de(CAR.reichweite) + " km" : "Reichweite unbekannt"}</span>
</div>
@@ -4674,7 +4752,7 @@ function vFuel() {
<span>Ø ${de(schnittpreis(fm), 2)} /l</span></span></button>
<div class="acc-b ${mi === 0 ? "on" : ""}" id="fm${j}${m}">`;
fm.forEach((f) => { const preis = literpreis(f); h += swipeZeile(`fill:${f.tank_id}`, `<button class="leaf" data-go="fill:${f.tank_id}">
<span class="k">${dedat(new Date(f.ts))}<small>${esc(f.station_name || "")}</small></span>
<span class="k">${dedat(new Date(f.ts))}<small>${esc(tankstelleKurz(f.station_name, f.station_address))}</small></span>
<span class="v">${f.liters != null ? de(f.liters, 2) + " l" : ""}<small>${preis != null ? de(preis, 2) + " €/l" : ""}</small></span>
<svg class="chev" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg></button>`); });
h += `</div></div>`;
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.9.4.19",
"version": "2026.9.4.20",
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
"codeowners": [