Files
audi-app/companion-app/src/symbole.tsx
T
tobias 4a58021670 Paritaetsrunden 2 und 3, Designpruefung umgesetzt (2026.8.30.8)
Drei zusammenhaengende Runden, alle live bei 375x812 gegen audi_ha_test
geprueft und in beiden Codebasen angewandt.

Paritaetsrunde 2: der gemeinsame Rahmen war das eigentliche Problem -
Seitenrand, Kachelabstaende, Kopfleiste, Tableiste und vier Bausteine des
Design-Systems trugen noch den Stand vor der iOS-Entscheidung. Dazu sieben
Bildschirme neu aufgebaut. Zwei Panel-Fehler dabei mitbehoben: teaser()
zeigte unter "Letzte Fahrt" den aeltesten Datensatz, und "Daten bearbeiten"
war ein toter Knopf.

Paritaetsrunde 3: das Panel hat nie Audi Type gerendert. @font-face in einem
Shadow Root wird ignoriert - Schriftschnitte registriert der Browser pro
Dokument, nie pro Shadow Tree. Die drei Schnitte liegen jetzt in
audi-dashboard-schriften.css und werden ins Dokument gehaengt. Damit erledigt
sich eine ganze Reihe von "die Schrift sieht anders aus"-Eindruecken: die
beiden Anwendungen zeigten tatsaechlich verschiedene Schriften.
Ausserdem "Daten bearbeiten" im Panel gebaut und portiert, das Zeilenmenue
entfernt und die letzten neun Bildschirme angeglichen.

Designpruefung: die fuenf Punkte der Reihenfolge. Beim vierten hat das Messen
den Befund veraendert - gezaehlt waren die deklarierten Groessen, wirksam war
laengst eine saubere Sieben-Schritt-Skala mit 27 Ausreissern; die sind jetzt
auf den naechsten Schritt gezogen, keiner verschiebt sich um mehr als 1px.

Zuletzt: die Standortvorschau zeichnete die falsche Nadel (das Panel wechselt
den Icon-Satz ab 34px, die App nahm immer den grossen), und der Kopfabstand
der App ist auf den sicheren Bereich reduziert - die 56px des Panels liegen
dort unter der Kopfleiste von Home Assistant, in der nativen Huelle steht
darueber nichts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 18:31:46 +02:00

316 lines
11 KiB
TypeScript

/**
* Symbole als React-Bausteine. Aus dem alten Panel übernommen (dort als
* SVG-Fragmente in Zeichenketten), damit App und Panel identisch aussehen.
* Bewusst keine Symbolbibliothek: es sind eine Handvoll, und jede Bibliothek
* wäre größer als der gesamte Rest.
*/
import type { ReactElement } from "react"
// Ausdrücklich `| undefined`, weil exactOptionalPropertyTypes eingeschaltet ist:
// ein durchgereichtes `groesse={undefined}` muss zulässig bleiben.
interface SymbolProps {
groesse?: number | undefined
}
/** `voll`: Fuellflaeche statt Kontur, fuer Symbole aus dem Audi-CI-Icon-Satz,
deren Pfade ihr eigenes fill="currentColor" mitbringen - ohne stroke="none"
hier wuerde der geerbte Kontur-Stroke trotzdem um jede Flaeche gezeichnet.
Entspricht der .voll-Klasse im Panel (audi-dashboard-app.js). */
function Rahmen({
groesse = 24,
voll = false,
kinder,
}: SymbolProps & { voll?: boolean; kinder: ReactElement }) {
return (
<svg
width={groesse}
height={groesse}
viewBox="0 0 24 24"
fill={voll ? "currentColor" : "none"}
stroke={voll ? "none" : "currentColor"}
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
{kinder}
</svg>
)
}
export function SymbolUebersicht({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
kinder={
<g>
<path d="M4 17a9 9 0 0 1 18 0" />
<path d="M13 17l5.5-5" />
<circle cx="13" cy="17" r="1.3" />
</g>
}
/>
)
}
// Wagen von vorn - dasselbe Symbol wie in der myAudi-App (Audi-Symbolsatz),
// dasselbe Pfad wie ICONS.audi im Panel. Fläche statt Kontur (voll), wie dort
// (siehe ICONS_VOLL). War hier bis 2026-08-29 ein grobes Platzhalter-Viereck -
// nie durch das echte Symbol ersetzt worden (gefunden beim Parität-Audit).
export function SymbolAudi({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
voll
kinder={
<path d="M5.46 4.05a80 80 0 0 1 13.08 0l.24.02.14.2A55 55 0 0 1 21.9 9.2l2.1 2.1v8.2c0 .83-.67 1.5-1.5 1.5h-2a1.5 1.5 0 0 1-1.5-1.5V19l-14-.04v.54c0 .83-.67 1.5-1.5 1.5h-2A1.5 1.5 0 0 1 0 19.5v-8.2l2.1-2.1a55 55 0 0 1 2.98-4.93l.14-.2zM2.7 10 1 11.7v.3h4v1H1v6.5c0 .28.22.5.5.5h2a.5.5 0 0 0 .5-.5v-1.55L20 18v1.5c0 .28.22.5.5.5h2a.5.5 0 0 0 .5-.5V13h-4v-1h4v-.3L21.3 10zm17.94-1a45 45 0 0 0-2.43-3.98 78 78 0 0 0-12.44 0L4.51 7c-.3.5-.7 1.16-1.16 2.01h17.3ZM7.3 12h9.42l1.36 1.36-.1.3c-.31 1-.6 1.78-.88 2.31-.13.27-.27.5-.41.67-.13.15-.36.36-.68.36H8a.9.9 0 0 1-.68-.36c-.14-.17-.28-.4-.41-.67a17 17 0 0 1-.89-2.32l-.09-.29zm.42 1-.64.64c.28.84.52 1.47.73 1.89a2.4 2.4 0 0 0 .3.47h7.8l.02-.01c.07-.08.16-.23.28-.46.21-.42.45-1.05.73-1.9L16.3 13H7.71Z" />
}
/>
)
}
export function SymbolFahrten({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
kinder={
<g>
<circle cx="6" cy="6" r="2.2" />
<circle cx="18" cy="18" r="2.2" />
<path d="M8 7.5c4 1.5 4.5 3 3 5s-1 3.5 4.5 5" />
</g>
}
/>
)
}
// CI-Symbol polls-s statt der vorherigen handgezeichneten Balken (Parität
// zum Panel, siehe ICONS.stat dort). Anders als die handgezeichneten
// Symbole (die im 24er-Raster deutlich Rand lassen) ist das CI-Symbol
// randlos gezeichnet und wirkte dadurch in der Tableiste sichtbar größer
// als "Fahrten"/"Tanken" - dieselbe zentrierte Stauchung (Faktor 0.77, um
// (12,12), auf Nutzerwunsch 10% größer als die erste Fassung) wie im Panel
// gleicht das aus, siehe ICONS.stat dort.
export function SymbolStatistik({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
voll
kinder={
<g transform="translate(2.76,2.76) scale(0.77)">
<path d="M9 0h6v21H9zm1 1v19h4V1zm7 4h6v16h-6zm1 1v14h4V6zM1 9h6v12H1zm1 1v10h4V10zM1 23h22v1H1z" />
</g>
}
/>
)
}
// CI-Symbol list-s: Sprung vom Diagramm zur Werteliste (Batteriespannung).
export function SymbolListe({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
voll
kinder={<path d="M5 8H3V7h2zm2-1h14v1H7zm-4 4h2v1H3zm4 0h9v1H7zm-4 4h2v1H3zm4 0h5v1H7z" />}
/>
)
}
// CI-Symbol package-s: "Neue Räder anlegen" (Reifensatz archivieren).
export function SymbolPaket({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
voll
kinder={
<path d="m12 .96 11 4.71v12.66l-11 4.71-11-4.71V5.67zM2 6.76v10.91l9.5 4.07V10.83zm10.5 4.07v10.91l9.5-4.07V6.76zM21.23 6 12 2.04 8.41 3.58l9.2 3.97zM7.15 4.12 2.77 6 12 9.96l4.34-1.86-9.2-3.98Z" />
}
/>
)
}
export function SymbolTanken({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
kinder={
<g>
<path d="M4.5 20V6.2c0-.9.8-1.7 1.7-1.7h5.6c1 0 1.7.8 1.7 1.7V20" />
<path d="M3 20h12.5" />
<path d="M4.5 11h10.5" />
<path d="M17 20v-8.5a2 2 0 0 0-2-2" />
<path d="M17 11.5V7.8l-2-2.3" />
</g>
}
/>
)
}
export function SymbolZurueck({ groesse }: SymbolProps) {
return <Rahmen groesse={groesse} kinder={<path d="M15 5l-7 7 7 7" />} />
}
export function SymbolZahnrad({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
kinder={
<g>
<circle cx="12" cy="12" r="3" />
<path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9L17 7M7 17l-2.1 2.1" />
</g>
}
/>
)
}
/* Das Zahnrad der Kachel-Ecken (`.zahnrad` im Panel) — ein gefülltes Symbol
aus dem Audi-CI-Satz, verbatim übernommen. Nicht zu verwechseln mit
SymbolZahnrad oben, dem Kontur-Symbol der Seitenleiste. */
export function SymbolZahnradVoll({ groesse }: SymbolProps) {
return (
<RahmenFlaeche
groesse={groesse}
kinder={
<path
fill="currentColor"
d="M9.4 2h4.2v2.84c.37.13.75.27 1.11.44L16.7 3.3l3 3.01-1.98 1.99c.17.36.31.74.44 1.11H21v4.3h-2.84c-.13.37-.27.75-.44 1.11l2 2-3.03 2.89-1.98-1.98c-.36.17-.74.31-1.11.44V21H9.4v-2.84a9 9 0 0 1-1.11-.44L6.3 19.7l-3-3.01 1.98-1.99a9 9 0 0 1-.44-1.11H2V9.4h2.84c.13-.37.27-.75.44-1.11L3.3 6.3l3.01-3L8.3 5.28c.36-.17.74-.31 1.11-.44V2Zm1 1v2.56l-.34.11c-.61.2-1.16.4-1.6.66l-.34.2L6.3 4.71 4.7 6.3l1.83 1.82-.2.34c-.27.44-.45.99-.66 1.6l-.11.34H3v2.2h2.56l.11.34c.2.61.4 1.16.66 1.6l.2.34-1.82 1.82 1.59 1.6 1.82-1.83.34.2c.44.27.99.45 1.6.66l.34.11V20h2.2v-2.56l.34-.11a8.3 8.3 0 0 0 1.6-.66l.34-.2 1.83 1.83 1.57-1.5-1.81-1.82.2-.34c.27-.44.45-.99.66-1.6l.11-.34H20v-2.3h-2.56l-.11-.34a8.2 8.2 0 0 0-.66-1.6l-.2-.34 1.82-1.82-1.59-1.6-1.82 1.83-.34-.2a8.2 8.2 0 0 0-1.6-.66l-.34-.11V3zm-2.9 8.5c0-2.18 1.82-4 4-4s4 1.82 4 4-1.82 4-4 4-4-1.82-4-4m4-3c-1.62 0-3 1.38-3 3s1.38 3 3 3 3-1.38 3-3-1.38-3-3-3"
/>
}
/>
)
}
/* battery-12v-s aus dem Audi-CI-Satz, verbatim wie im Panel (CI.battery12vS) —
steht vor der Ruhespannungs-Skala auf der Batterieseite. */
export function SymbolBatterie({ groesse }: SymbolProps) {
return (
<RahmenFlaeche
groesse={groesse}
kinder={
<path
fill="currentColor"
d="M5 5h4v2h6V5h4v2h3v12H2V7h3zm1 2h2V6H6zM3 8v10h18V8zm15-1V6h-2v1zm-1 6v2h-1v-2h-2v-1h2v-2h1v2h2v1zm-8 0H5v-1h4z"
/>
}
/>
)
}
export function SymbolSonne({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
kinder={
<g>
<circle cx="12" cy="12" r="4.5" />
<path d="M12 2v2.4M12 19.6V22M2 12h2.4M19.6 12H22M4.7 4.7l1.7 1.7M17.6 17.6l1.7 1.7M19.3 4.7l-1.7 1.7M6.4 17.6l-1.7 1.7" />
</g>
}
/>
)
}
export function SymbolMond({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
kinder={<path d="M19.5 14.4A8.4 8.4 0 1 1 9.6 4.5a6.7 6.7 0 0 0 9.9 9.9z" />}
/>
)
}
/* SymbolMehr (die drei Punkte) ist mit dem „···"-Knopf der Listenzeilen
entfallen - es gab keine zweite Verwendung. */
/**
* oil-change-s/inspection-s aus dem Audi-CI-Icon-Satz (vom Nutzer als SVG
* geliefert, verbatim übernommen — 24er-Grid, wie im Panel als CI.oilChangeS/
* CI.inspectionS). Anders als die generischen Rahmen()-Symbole oben sind das
* gefüllte Flächen-Icons, deshalb kein stroke-Wrapper.
*/
function RahmenFlaeche({ groesse = 24, kinder }: SymbolProps & { kinder: ReactElement }) {
return (
<svg width={groesse} height={groesse} viewBox="0 0 24 24" fill="none" aria-hidden="true">
{kinder}
</svg>
)
}
export function SymbolOelwechsel({ groesse }: SymbolProps) {
return (
<RahmenFlaeche
groesse={groesse}
kinder={
<path
fill="currentColor"
d="M9 8H7.5V7h4v1H10v2h2.77l.94 1.41 7.93-2.48 2.21 2.22-.7.7-1.79-1.78-1.03.32L15.25 17H4v-6.8L2.8 9H0V8h3.2l2 2H9zm-4 3v5h9.75l3.92-5.1-5.38 1.69L12.23 11z"
/>
}
/>
)
}
export function SymbolInspektion({ groesse }: SymbolProps) {
return (
<RahmenFlaeche
groesse={groesse}
kinder={
<path
fill="currentColor"
d="M3.3 0H5v3.3l.7.7h2.6l.7-.7V0h1.7L13 2.3v4.4l-3 3V21c0 .69-.3 1.42-.76 1.98A2.57 2.57 0 0 1 7.3 24h-.6c-.79 0-1.47-.42-1.93-.98A3.22 3.22 0 0 1 4 21V9.7l-3-3V2.3zm.4 1L2 2.7v3.6l3 3V21c0 .47.2.98.53 1.38.34.4.76.62 1.17.62h.6c.4 0 .82-.24 1.16-.65.34-.42.54-.94.54-1.35V9.3l3-3V2.7L10.3 1H10v2.7L8.7 5H5.3L4 3.7V1zm12.44-1h3.72L21 3.42V6.7l-1 1V14h1v7.5a2.5 2.5 0 0 1-2.5 2.5h-1a2.5 2.5 0 0 1-2.5-2.5V14h1V7.7l-1-1V3.43zM16 15v6.5c0 .82.68 1.5 1.5 1.5h1c.82 0 1.5-.68 1.5-1.5V15zm3-1V7.3l1-1V3.57L19.14 1h-2.28L16 3.58V6.3l1 1V14z"
/>
}
/>
)
}
// edit-s aus dem Audi-CI-Icon-Satz (vom Nutzer als SVG geliefert, verbatim
// übernommen wie oil-change-s/inspection-s oben) - für den km-Korrektur-
// Stift bei Anzugsmoment/Reifenkilometern (Vorlage: CI.editS im Panel).
export function SymbolEdit({ groesse }: SymbolProps) {
return (
<RahmenFlaeche
groesse={groesse}
kinder={
<path
fill="currentColor"
d="m14.14 1.06 2.4 1.1.04.02c.23.16.42.39.52.65.1.27.13.6-.06.91l-.91 1.94 2.94 1.38-2.82 5.95-.9-.42 2.38-5.05-2.02-.95L9.5 19.8 5 23.57v-5.88l7.56-16.14.02-.03c.16-.23.39-.42.65-.52a1 1 0 0 1 .9.06Zm1.08 4.2.94-2 .02-.03v-.04a.4.4 0 0 0-.13-.16l-2.4-1.1-.02-.01h-.04a.4.4 0 0 0-.16.13L12.53 4l2.7 1.27Zm-3.12-.37L6.16 17.56l2.7 1.27L14.8 6.16 12.1 4.9ZM6 18.6v2.84l2.18-1.81L6 18.59Z"
/>
}
/>
)
}
/* Datenstand/Aktualisieren — die beiden Pfeile aus der Kopfzeile des Panels
(`#sync`), verbatim übernommen. Fläche statt Kontur, wie dort. */
export function SymbolAktualisieren({ groesse }: SymbolProps) {
return (
<RahmenFlaeche
groesse={groesse}
kinder={
<path
fill="currentColor"
d="m18.65 4.95 2.56 2.55-2.56 2.55-.7-.7L19.29 8H7.2A5.24 5.24 0 0 0 2 13.38V14H1v-.6A6.24 6.24 0 0 1 7.2 7h12.1l-1.34-1.35.7-.7ZM23 11.59A6.24 6.24 0 0 1 16.8 18H4.7l1.35 1.35-.7.7-2.56-2.55 2.56-2.55.7.7L4.71 17H16.8a5.24 5.24 0 0 0 5.2-5.38V11h1v.6Z"
/>
}
/>
)
}
export function SymbolWarnung({ groesse }: SymbolProps) {
return (
<Rahmen
groesse={groesse}
kinder={
<g>
<path d="M12 4.5 2.5 20h19z" />
<path d="M12 10v4.5" />
<circle cx="12" cy="17.3" r="0.9" fill="currentColor" stroke="none" />
</g>
}
/>
)
}