Teilen-Blatt, HIG-Blaetter, geteilte Ansichtseinstellungen (2026.8.31.1)

Share-Erweiterung: ein PDF aus Mail landet ueber Teilen -> DataMetric360 als
Tankbeleg. Alles Native liegt versioniert unter companion-app/native/, weil
ios/ gitignored ist und npx cap add ios es sonst wieder frisst;
scripts/ios-teilen-einrichten.mjs haengt es bei jedem Bau ins Xcode-Projekt
und ist in ios-signieren.sh eingehaengt. Der pbxproj-Teil ist auf keinem Mac
erprobt - er bricht vor dem Schreiben ab, und docs/SHARE_EXTENSION.md
beschreibt dieselben Handgriffe fuer Xcode.

"In den Kalender uebernehmen" ging in der App nie: WKWebView ignoriert das
download-Attribut, der Klick lief ins Leere. Auf dem Geraet jetzt
@capacitor/filesystem plus @capacitor/share.

useTheme/useBoden legten je Aufrufer eigenen Zustand an - fuenf Kopien. Der
Schalter aenderte nur seine eigene, data-theme in App.tsx blieb stehen, die
Tag/Nacht-Umschaltung wirkte erst nach einem Neustart. Jetzt ein Speicher je
Einstellung ueber useSyncExternalStore. Zwei Regressionstests, gegen den
alten Stand als fehlschlagend nachgewiesen.

Alle Popups nach Apple HIG: neue Bausteine Sheet und ActionSheet, blickdicht
und mit abgedunkeltem Schleier, in beiden Codebasen. Popup portalierte nach
document.body - ausserhalb von [data-theme] - und war deshalb im Tagmodus
fast unsichtbar; Portal zielt jetzt auf .ads-root.

ImagePlaceholder merkte sich "fehlgeschlagen" ohne die Adresse: nach einem
leeren Galerieplatz zeigten auch die mit Foto nur noch den Platzhalter.
Vier Regressionstests, ebenfalls gegen den alten Stand geprueft.

Bildadressen tragen jetzt einen Cache-Brecher - HA liefert /local/ mit 31
Tagen Cache-Vorgabe aus, im Panel eingefuegte Radfotos erschienen in der App
deshalb nicht.

uebersichtsbild: Panel speichert den Namen, die App verglich gegen den
Dateinamen - der Vergleich traf nie zu. Kanonisch ist der Name.

Weiter: Navigationspfeil statt gleichschenkligem Dreieck auf der Streckenlinie
(die Kerbe unterscheidet Kopf und Ende), CI-Symbol tour-s als Fahrten-Icon,
Datumsfelder zeigen ihren Wert ohne erstes Antippen, feste Beispielnamen im
Setup, Kopf-Gegengewicht fuer mittige Titel, Standort-Blatt mit festem
Fussabstand, NSLocationWhenInUseUsageDescription, watchPosition fuer die
Live-Ortung, Art-Pille wieder als Knopf, Zwischenablage fuer Tankbelege.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-31 14:24:52 +02:00
parent 26f72930c3
commit b1411625aa
58 changed files with 2846 additions and 368 deletions
@@ -599,8 +599,12 @@ button.leaf:active { background: var(--tile-2); }
.szene.bleed::before {
content: "";
position: absolute; left: 0; right: 0; bottom: 0;
height: 42%;
background: linear-gradient(to bottom, transparent, var(--canvas) 78%);
/* 26/96 statt 42/78: der Verlauf soll die harte Bildkante ausblenden,
nicht das Fahrzeug. Mit 42% Hoehe und Vollton ab 78% war das untere
Drittel des Fotos praktisch einfarbig - Raeder und Schweller lagen
darunter (gemeldet vom Eigentuemer, 2026-08-31). */
height: 26%;
background: linear-gradient(to bottom, transparent, var(--canvas) 96%);
pointer-events: none;
z-index: 1;
}
@@ -668,7 +672,9 @@ button.leaf:active { background: var(--tile-2); }
.marke { display: inline-flex; align-items: center; gap: 6px; }
.marke-logo { display: inline-flex; align-items: center; }
.marke-logo img { height: 13px; width: auto; }
/* Auf Zeilenhoehe (16px) statt 13px: das Markenlogo stand neben Text
derselben Zeile und wirkte daneben zu klein. */
.marke-logo img { height: 16px; width: auto; }
.marke-logo .ph { display: none; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--fg3); }
.marke-logo.logo-fehlt img { display: none; }
.marke-logo.logo-fehlt .ph { display: inline; }
@@ -1011,19 +1017,23 @@ button.leaf:active { background: var(--tile-2); }
gezaehlt, das Blatt war auf dem Geraet rund 16px hoeher als noetig und
schob sich entsprechend weiter hoch. Am Rechner faellt das nicht auf,
dort ist der Einzug 0. */
padding: 0 20px 18px;
padding: 0 20px 34px;
transform: translateY(calc(100% - var(--standort-peek)));
transition: transform .28s cubic-bezier(.22,.61,.36,1);
}
/* Aufgezogen rastet das Blatt auf der mittleren Hoehe ein, nicht auf seiner
Inhaltshoehe. Apple beschreibt genau das als "detents": Hoehen, auf denen
ein Blatt natuerlich zur Ruhe kommt - large ist das voll ausgefahrene
Blatt, medium etwa die Haelfte davon. Vorher war die Rasthoehe schlicht
die Hoehe des Inhalts, dadurch schlossen die Knoepfe unten buendig mit der
Menueleiste ab. Der Ueberschuss liegt unter den Knoepfen und haelt sie von
der Leiste frei. Die eingeklappte Hoehe bleibt unberuehrt: die Verschiebung
rechnet mit 100% der jeweiligen Blatthoehe minus dem Guckwert. */
.standortmenu.offen { transform: translateY(0); min-height: 50%; }
/* Aufgezogen bleibt unter den Knoepfen ein fester Abstand stehen, statt dass
sie buendig mit der Menueleiste abschliessen.
Vorher stand hier min-height:50% - Apples "medium detent". Gemessen ergab
das 89px (Panel) bzw. 76px (App) Luft unter dem letzten Knopf, und das war
sichtbar zu viel: das Blatt zog ueber die Haelfte des Bildschirms auf,
obwohl sein Inhalt dort laengst zu Ende war. Eine Raste ist nur dann
richtig, wenn zwischen ihr und der Inhaltshoehe kein grosses Loch klafft.
Jetzt bestimmt der Inhalt die Hoehe, und die 34px Fussabstand halten die
Knoepfe von der Leiste frei - hergeleitet aus der Beanstandung, nicht
geraten: 18px waren zu wenig, 76-89px zu viel. */
.standortmenu.offen { transform: translateY(0); }
.standortmenu-griffzone { padding: 10px 0 14px; touch-action: none; cursor: grab; position: relative; }
/* Der Griff ist jetzt ein Knopf (Tastaturzugang zum Menue). Die Knopf-
Voreinstellungen des Browsers werden deshalb zurueckgesetzt; die
@@ -1090,38 +1100,62 @@ button.leaf:active { background: var(--tile-2); }
.standort-pille[disabled] { opacity: .4; pointer-events: none; }
/* ------------------------------------------------------- Bilderverwaltung */
.bildmenu-catcher { position: fixed; inset: 0; z-index: 1000; }
/* ---------------------------------------------------------- Blätter
Apples Vorgabe kennt für etwas Modales auf dem Telefon kein frei
schwebendes Fenster: es kommt von der unteren Kante. Das Aktionsblatt
(eine kurze Liste von Wegen) und das Formularblatt (eine Eingabe) sind
die beiden Formen; die Karte in der Bildschirmmitte war keine von
beiden und stand ausserdem ohne abgedunkelten Grund über der voll
sichtbaren Seite.
Der Schleier gehört dazu und ist nicht Geschmack: modal heisst, dass
der Inhalt dahinter nicht bedienbar ist - das soll man sehen. */
.bildmenu-catcher {
position: absolute; inset: 0; z-index: 1000;
background: rgba(0,0,0,.45);
animation: sheet-fade .18s ease-out;
}
.bildmenu {
position: absolute; z-index: 1001;
background: var(--tile-2);
border-radius: 14px;
padding: var(--sp-2);
left: 10px; right: 10px;
bottom: max(10px, env(safe-area-inset-bottom));
top: auto;
transform: none;
background: var(--tile-deckend, var(--canvas));
border-radius: 16px;
overflow: hidden;
padding: 0;
display: flex; flex-direction: column;
min-width: 160px;
animation: sheet-rein .24s cubic-bezier(.22,.61,.36,1);
}
.bildmenu.rad { top: 90px; left: 22px; }
/* "Bild der Übersicht": das Vorschaubild füllt die ganze Kachelbreite, seine
Unterkante liegt daher immer nah an der Kachel-Unterkante - das Menü von
dort aus verankern statt mit einem festen top-Wert, der bei jedem
Bildseitenverhältnis neu nachgemessen werden müsste. */
.bildmenu.ansicht { bottom: var(--sp-5); left: 50%; transform: translateX(-50%); }
/* Die beiden Lage-Varianten sind gegenstandslos geworden - ein Blatt
kommt immer von unten. Die Klassen bleiben im Markup stehen, damit die
Aufrufstellen unberührt bleiben; hier tragen sie nichts mehr bei. */
.bildmenu.rad, .bildmenu.ansicht { left: 10px; right: 10px; top: auto; transform: none; }
.bildmenu button {
display: block; width: 100%; text-align: left;
background: none; border: none;
color: var(--fg); font-family: inherit; font-size: 15px;
padding: 13px 10px; min-height: 46px; border-radius: 10px;
color: var(--fg); font-family: inherit; font-size: 16px;
padding: 16px 18px; min-height: 52px; border-radius: 0;
border-bottom: 1px solid var(--line);
cursor: pointer;
}
.bildmenu button:last-child { border-bottom: none; }
.bildmenu button:active { background: var(--shade); }
.bildmenu button.loeschen { color: var(--bad); }
/* Formularblatt (siehe Kasten oben): unten angeschlagen, nur oben rund,
blickdicht. Stand vorher als Karte 60px unter dem oberen Rand - ausser
Daumenreichweite und ohne sichtbaren Weg hinaus. */
.sdpopup {
position: absolute; z-index: 1001;
left: var(--sp-5); right: var(--sp-5); top: 60px;
background: var(--tile-2);
border-radius: 14px;
padding: var(--sp-4);
left: 0; right: 0; bottom: 0; top: auto;
background: var(--tile-deckend, var(--canvas));
border-radius: 16px 16px 0 0;
padding: var(--sp-4) 20px max(20px, env(safe-area-inset-bottom));
animation: blatt-hoch .28s cubic-bezier(.22,.61,.36,1);
}
@keyframes blatt-hoch { from { transform: translateY(100%) } to { transform: none } }
/* Beleg-Fenster: PDF hineinziehen (Rechner) oder einfügen (Telefon). */
.beleg-catcher {
@@ -1129,15 +1163,20 @@ button.leaf:active { background: var(--tile-2); }
background: rgba(0,0,0,.45);
animation: sheet-fade .18s ease-out;
}
/* Formularblatt (siehe Kasten oben). Traegt drei Fenster: Beleg
hochladen, Daten importieren, Datensatz sichern/laden. */
.beleg-popup {
position: absolute; z-index: 1001;
left: var(--sp-5); right: var(--sp-5);
top: 50%; transform: translateY(-50%);
/* Wie .setup-popup: --tile-2 ist im Nacht-Theme transluzent, ein frei
schwebendes Fenster braucht einen blickdichten Grund. */
left: 0; right: 0; bottom: 0;
top: auto; transform: none;
max-height: 88%;
overflow-y: auto;
/* --tile-2 ist im Nacht-Theme transluzent; ein Blatt ueber der Seite
braucht einen blickdichten Grund. */
background: var(--tile-deckend, var(--canvas));
border-radius: var(--r-tile);
padding: var(--sp-4);
border-radius: 16px 16px 0 0;
padding: var(--sp-4) 20px max(20px, env(safe-area-inset-bottom));
animation: blatt-hoch .28s cubic-bezier(.22,.61,.36,1);
}
.beleg-drop {
margin-top: var(--sp-3);