Fahrt beenden auf Knopfdruck, Schieberegler, Zuendungspause (2026.9.3.5-.7)
Vier Themen aus einer Sitzung. 1. Die Fahrt endet wieder bei uns statt im Dongle (Abschnitt BW). Der Ignition-OFF-Timeout des FMM003 (900 s) und sein Schlaf-Timeout (ebenfalls 900 s) starten beide beim Zuendungs-Aus und fallen in derselben Sekunde - am 02.09. zweimal beobachtet, einmal ging das Trip-Ende verloren, einmal kam es eine Sekunde vor dem Schlaf an. Ausloeser ist jetzt die Zuendung, die Wartezeit laeuft in Home Assistant. ZUENDUNG_NACHLAUF_S = 180 wird in beiden Wegen abgezogen (ueber das Trip-Signal 1080 s, weil dessen Ende selbst an der verzoegerten Zuendung haengt). 2. Schieberegler, neu in beiden Codebasen. Schrittweite 5 Minuten; der Daumen war im Tagmodus weiss auf weiss und traegt jetzt einen Ring aus --line-strong - ein Token, das genau dort sichtbar ist, wo es gebraucht wird. 3. Knopf "Fahrt beenden" in der Zuletzt-Kachel (Abschnitt BX). Schliesst auf das letzte Lebenszeichen, nicht auf "jetzt", und kennzeichnet das Ende als vorlaeufig. Ein spaet eintreffendes Zuendungs-Aus zieht es nach - innerhalb von sechs Stunden, nur nach vorn, und nur bei einer vorlaeufigen Fahrt. ts_end wandert bewusst NICHT in edited_fields, sonst blockierte der Schutz fuer Handeingaben genau diese Korrektur. 4. Das OTA-Buendel kann auf dem Mac gar nicht entstehen (Abschnitt BV): npm run ota ist eine Windows-PowerShell-Datei, ios-signieren.sh baut ein frisches dist/ und fasst das Buendel nie an. Ausgeliefert war deshalb eine Oberflaeche ohne den Versionshinweis unter richtiger Nummer. Verifiziert: Backend 27/27 (5 Signalwechsel, 14 Zuendungspause, 8 Fahrtende), companion-app tsc sauber und 179/179, Panel als Modul geparst, audi_ha_test auf 2026.9.3.7 sauber gestartet. Live am laufenden Panel und ohne Rueckstand belegt: Wartezeit samt Abbruch, die 180-s-Rechnung, der Regler im Tagmodus und der Knopf von der laufenden Fahrt bis zum verworfenen Kurzvorgang - Fahrten vorher 16, nachher 16. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
.ads-schieber {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-4);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 28px hoch, obwohl die Spur nur 4px misst: das ist die Trefferfläche des
|
||||
Daumens. Ein 4px hohes Element wäre auf dem Telefon nicht zu greifen. */
|
||||
.ads-schieber input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
height: 28px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ads-schieber input[type="range"]:disabled {
|
||||
opacity: .4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Die beiden Spur-Regeln lassen sich NICHT zusammenfassen: trifft ein Browser
|
||||
auf einen Selektor, den er nicht kennt, verwirft er die ganze Regel. */
|
||||
.ads-schieber input[type="range"]::-webkit-slider-runnable-track {
|
||||
height: 4px;
|
||||
border-radius: var(--r-pill);
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
var(--fg) var(--ads-fuell, 0%),
|
||||
var(--ios-fill) var(--ads-fuell, 0%)
|
||||
);
|
||||
}
|
||||
|
||||
.ads-schieber input[type="range"]::-moz-range-track {
|
||||
height: 4px;
|
||||
border-radius: var(--r-pill);
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
var(--fg) var(--ads-fuell, 0%),
|
||||
var(--ios-fill) var(--ads-fuell, 0%)
|
||||
);
|
||||
}
|
||||
|
||||
/* margin-top zentriert den Daumen auf der 4px-Spur: (4 - 28) / 2 = -12. */
|
||||
.ads-schieber input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
box-sizing: border-box;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-top: -12px;
|
||||
/* Der Ring ist der Trick fuer den Tagmodus: --line-strong ist dort
|
||||
rgba(0,0,0,.22) und zeichnet den weissen Daumen sauber gegen die helle
|
||||
Kachel ab, im Nachtmodus dagegen rgba(255,255,255,.20) - also auf Weiss
|
||||
unsichtbar, wo er nicht gebraucht wird. Ein Token, zwei Themen.
|
||||
Dazu ein kraeftigerer Schatten: der allein trug im Tagmodus nicht. */
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .22), 0 4px 10px rgba(0, 0, 0, .16);
|
||||
}
|
||||
|
||||
.ads-schieber input[type="range"]::-moz-range-thumb {
|
||||
box-sizing: border-box;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .22), 0 4px 10px rgba(0, 0, 0, .16);
|
||||
}
|
||||
|
||||
.ads-schieber input[type="range"]:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.ads-schieber input[type="range"]:focus-visible::-webkit-slider-thumb {
|
||||
box-shadow: 0 0 0 3px var(--line-strong), 0 3px 8px rgba(0, 0, 0, .16);
|
||||
}
|
||||
|
||||
.ads-schieber input[type="range"]:focus-visible::-moz-range-thumb {
|
||||
box-shadow: 0 0 0 3px var(--line-strong), 0 3px 8px rgba(0, 0, 0, .16);
|
||||
}
|
||||
|
||||
/* Feste Mindestbreite und Ziffern gleicher Laufweite: sonst ruckelt der Regler
|
||||
seitlich, sobald der Wert von 9 auf 10 springt. */
|
||||
.ads-schieber__wert {
|
||||
flex: 0 0 auto;
|
||||
min-width: 62px;
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--fg2);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import * as React from "react";
|
||||
|
||||
/**
|
||||
* Schieberegler für einen Zahlenwert - das iOS-Muster für "irgendwo zwischen
|
||||
* wenig und viel", wo die genaue Ziffer zweitrangig ist.
|
||||
*
|
||||
* Bewusst ein echtes `input[type=range]` und kein nachgebauter Regler aus
|
||||
* divs: das bringt Tastaturbedienung, VoiceOver und die Wischgeste des Systems
|
||||
* mit, ohne dass wir sie nachbauen (und dabei die Hälfte vergessen). Gemalt
|
||||
* wird trotzdem selbst, denn sonst sähe der Regler im Panel wie Chrome und in
|
||||
* der App wie WebKit aus - zwei Codebasen, ein Look.
|
||||
*
|
||||
* Der gefüllte Teil der Spur kommt aus der CSS-Variablen `--ads-fuell`, weil
|
||||
* ein `input[type=range]` seinen Wert nicht an das Stylesheet weiterreicht.
|
||||
* Sobald man `::-webkit-slider-runnable-track` selbst gestaltet, entfällt auch
|
||||
* die automatische Füllung von `accent-color` - der Anteil muss also von hier
|
||||
* kommen.
|
||||
*
|
||||
* Farben wie beim Balken (ProgressBar): `--fg` auf `--ios-fill`. Der
|
||||
* Markenrot-Ton bleibt den Stellen vorbehalten, die wirklich Aufmerksamkeit
|
||||
* verlangen; eine Einstellung gehört nicht dazu.
|
||||
*/
|
||||
export interface SliderProps {
|
||||
wert: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
schritt?: number;
|
||||
onChange: (wert: number) => void;
|
||||
/** Wird rechts neben dem Regler gezeigt, z. B. "15 Min." */
|
||||
anzeige?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
id?: string;
|
||||
"aria-label"?: string;
|
||||
"aria-describedby"?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Slider({
|
||||
wert,
|
||||
min = 0,
|
||||
max = 100,
|
||||
schritt = 1,
|
||||
onChange,
|
||||
anzeige,
|
||||
disabled,
|
||||
id,
|
||||
className,
|
||||
...rest
|
||||
}: SliderProps) {
|
||||
const spanne = max - min;
|
||||
// Geklemmt, damit ein Wert außerhalb der Grenzen die Spur nicht überfüllt -
|
||||
// gespeicherte Werte können älter sein als die heutigen Grenzen.
|
||||
const anteil = spanne > 0 ? Math.min(100, Math.max(0, ((wert - min) / spanne) * 100)) : 0;
|
||||
const classes = ["ads-schieber"];
|
||||
if (className) classes.push(className);
|
||||
|
||||
return (
|
||||
<div className={classes.join(" ")}>
|
||||
<input
|
||||
type="range"
|
||||
id={id}
|
||||
min={min}
|
||||
max={max}
|
||||
step={schritt}
|
||||
value={wert}
|
||||
disabled={disabled}
|
||||
onChange={(e) => onChange(Number(e.target.value))}
|
||||
style={{ "--ads-fuell": `${anteil}%` } as React.CSSProperties}
|
||||
{...rest}
|
||||
/>
|
||||
{anzeige !== undefined && <output className="ads-schieber__wert">{anzeige}</output>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { Slider } from "./Slider";
|
||||
export type { SliderProps } from "./Slider";
|
||||
@@ -16,6 +16,9 @@ export type { ActionButtonProps } from "./components/ActionButton";
|
||||
export { Switch } from "./components/Switch";
|
||||
export type { SwitchProps } from "./components/Switch";
|
||||
|
||||
export { Slider } from "./components/Slider";
|
||||
export type { SliderProps } from "./components/Slider";
|
||||
|
||||
export { Seg } from "./components/Seg";
|
||||
export type { SegProps, SegOption } from "./components/Seg";
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
@import "./components/StatusRow/StatusRow.css";
|
||||
@import "./components/ActionButton/ActionButton.css";
|
||||
@import "./components/Switch/Switch.css";
|
||||
@import "./components/Slider/Slider.css";
|
||||
@import "./components/Seg/Seg.css";
|
||||
@import "./components/RowList/RowList.css";
|
||||
@import "./components/Accordion/LeafRow.css";
|
||||
|
||||
Reference in New Issue
Block a user