diff --git a/docs/PLAN.md b/docs/PLAN.md index ba7b4e32..61761d43 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -271,6 +271,7 @@ Größe vor dem Start messen: `store.mjs` (570), `app.js` (504/527) und `store.t | DS-X-12 | Bewegungstoken angewendet: ein Druck-Token (`--g-press-scale`) für Knöpfe, Filterchips, Segmente und Tabs; der Füllbalken wächst per `transform: scaleX()` statt über die Breite, die Erlaubnisliste für Layout-Animation in `motion.test.ts` ist leer; der Lauf-Ring atmet nur unter `--g-ambient-play` (Lebendig), in Ruhig steht er still und ein Zustandswechsel gibt einen einmaligen Ping (`usePulseOnChange`); eine dokumentierte Regel für reduzierte Bewegung am Kopf von `motion.css`; das doppelte `g-enter` entfällt | Implementierer · Claude; Root nimmt ab | DS-X-03 (#1606), DS-X-07 (#1683); DS-Blueprint-Schnitt 16.10. (Zeile DS-X-12) | Server | B | Erledigt: #1693 (merged 2026-10-18) | red-first Vitest-Tests; `vitest run src/design`, typecheck, contrast-check, `plan-check --offline` und prepush Exit 0; ein unabhängiger Stufe-B-Review (Root beauftragt) | – | | DS-X-21 | Ein Symbolsatz auf 20-px-Raster: `Icon` in `src/design/icons/` (ohne Beschriftung für Hilfstechnik verborgen, mit `label` als `role="img"`, Farbe über `currentColor`) und 24 Lucide-Pfade als Daten (`glyphs.ts`), dazu Entsprechungen für die 14 klassischen Symbole, die Seitenleisten-Symbole und den Auswahl-Pfeil; Lizenztext `LUCIDE-LIZENZ.txt` neben den Symbolen und Eintrag in `docs/THIRD_PARTY_NOTICES.md` | Implementierer · Claude; Root nimmt ab | DS-Blueprint-Schnitt 16.10. (DS-X-21), Root-Entscheidung 17.10.: Pfade kopieren, kein Paket | Server | B + Lizenzprüfung | Entwurfs-PR offen (17.10.): kein Aufrufer umgestellt, `src/components/Icon.tsx` bleibt bis KR-15; Galerie zeigt den Satz unter „Symbole“ | Tests rot→grün; `vitest run src/design`, typecheck, `contrast-check`, `plan-check --offline` und prepush Exit 0; Bilder der Galerie hell und dunkel; ein Stufe-B-Review einer Nicht-Anthropic-Familie, Root prüft den Lizenzhinweis | – | | DS-X-32 | Datenfarben `--g-viz-1` bis `--g-viz-6` in `src/design/tokens.css` (hell und dunkel), eigene Farbtöne abseits der Zustandsfarben; `scripts/contrast-check.mjs` prüft jede Datenfarbe als Nicht-Text mit mindestens 3:1 auf Grund, Chrome und Glas (auch in allen Glas-Varianten), hält sie voneinander und von den Zustandsfarben auseinander und meldet „Token fehlt“; die Galerie-Seite Farben zeigt die sechs Farbfelder; keine Diagramm-Bausteine (das ist DS-X-33) | Implementierer · Claude; Root nimmt ab | DS-X-02; DS-Blueprint-Schnitt 16.10. (Zeile DS-X-32) | Server | B | Erledigt: #1702 (merged 2026-10-18), Branch `claude/ds-x-32`; die sechs Datenfarben stehen in `tokens.css`, `contrast-check` prüft sie und die Galerie-Seite Farben zeigt sie, Diagramm-Bausteine folgen mit DS-X-33 | red-first Gate-Tests in `scripts/lib/contrast-gate.test.mjs`; `contrast-check`, typecheck, `plan-check --offline` und prepush Exit 0; Bilder der Farben-Seite hell und dunkel; ein unabhängiger Stufe-B-Review (Root beauftragt) | – | +| DS-X-33 | Diagramm-Bausteine in `src/design/data/`: `Sparkline`, beschriftete `BarList` und `ChartFrame` mit Titel, Beschreibung, Legende und verpflichtender Tabellenansicht aus denselben Daten; ausschließlich Datenfarben, leere und unbekannte Werte als Wörter, statisch; Register und deutsche Texte ergänzt; keine bestehenden Ansichten umgestellt | Implementierer · Codex; Root nimmt ab | DS-X-32 (#1702, `4a9c8a5b`); DS-Blueprint-Schnitt 16.10. | Server | B | In Prüfung: Entwurfs-PR `codex/ds-x-33`; nach Merge stehen die drei Bausteine samt Tabellenansicht bereit | vier benannte Tests rot→grün; Registerabdeckung, typecheck, contrast-check, `plan-check --offline` und eigene prepush Exit 0; betrachtete Bilder hell/dunkel mit offener Tabelle; unabhängiger Nicht-OpenAI-Review durch Root ausstehend | – | | DS-X-26 | Popover (Fabrik-Designsystem, Welle 4): `src/design/layout/Popover.tsx` setzt `aria-expanded`/`aria-controls` am Auslöser, schließt bei einem Pointerdown außerhalb genau einmal, mit Esc (Fokus zurück zum Auslöser) und beim Verlassen per Tab, misst einmal beim Öffnen, ob es nach oben klappt (keine CSS-Anker-Positionierung), und geht über den Austrittsübergang aus DS-X-07 (bei reduzierter Bewegung sofort); `ProjectPicker` und das kompakte Darstellungsmenü nutzen es mit unverändertem Verhalten und Aussehen; Eintrag im Register; kein Menü-Baustein, kein Tooltip (DS-X-27) | Implementierer · Claude; Root nimmt ab | DS-X-07 (#1683); DS-Blueprint-Schnitt 16.10. (Zeile DS-X-26) | Server | B | Erledigt: #1705 (merged 2026-10-18); `ProjectPicker` und das kompakte Darstellungsmenü nutzen das Popover mit unverändertem Verhalten und Aussehen, kein Menü-Baustein und kein Tooltip (DS-X-27) | red-first Vitest-Tests (`popover.test.tsx`); die bestehenden Tests von `ProjectPicker` und Darstellungsmenü bleiben unverändert grün; `vitest run src/design src/shell`, typecheck, contrast-check, `plan-check --offline` und prepush Exit 0; Bildschirmfotos beider Menüs hell und dunkel; ein unabhängiger Stufe-B-Review (Root beauftragt) | – | | DS-X-28 | Toast-Warteschlange (Fabrik-Designsystem, Welle 4): `useToast()` in `src/design/layout/useToast.tsx` liefert `push` (gibt eine Kennung zurück), `dismiss(id)` und den einzubindenden `region`; höchstens drei Hinweise sind sichtbar, weitere warten; ein optionaler Aktionsknopf ruft seine Funktion genau einmal auf; Hinweise verlassen den Bildschirm über `useExitTransition` (bei reduzierter Bewegung sofort); `ToastRegion` bleibt abwärtskompatibel, Register-Eintrag angepasst; keine Bildschirmeinbindung | Implementierer · Claude; Root nimmt ab | DS-X-07; DS-Blueprint-Schnitt 16.10. (Zeile DS-X-28) | Server | B | Erledigt: #1704 (merged 2026-10-18), Branch `claude/ds-x-28`; keine Bildschirmeinbindung | red-first Vitest-Tests (`toast.queue.test.tsx`); `vitest run src/design`, typecheck, contrast-check, `plan-check --offline` und prepush Exit 0; ein unabhängiger Stufe-B-Review (Root beauftragt) | – | | DS-T-03 | Vertrauensfehler P1-b/e: die Kartenlampe sagt, was bewiesen ist (`lampFor.ts`, `lamps/texts.ts`, `AgentCard.tsx`, `FlowTab.tsx`) | Implementierer · Claude; Root nimmt ab | Lampenform aus DS-T-02 (#1610) | Server | B | Erledigt: #1624 (merged 2026-10-16); unbekannte Spalte oder unbekannter Worker-Status zeigt auf Karte und Fabrik-Knoten die graue Unbekannt-Lampe statt „Läuft“, die grüne Lampe heißt „Test bestanden“ mit dem Alter des Testlaufs; welcher Stand getestet wurde, bleibt ein späteres Nahtpaket | fünf Test-First-Tests rot an der Merge-Base, grün am Kopf (`red-first.sh` Exit 0); typecheck, `node scripts/contrast-check.mjs` und prepush Exit 0; zwei Stufe-B-Reviews verschiedener Nicht-Autor-Familien: Subscription + OpenRouter; Root-Abnahme | – | diff --git a/docs/design/BUILDING.md b/docs/design/BUILDING.md index 0c1766e2..483ee6cc 100644 --- a/docs/design/BUILDING.md +++ b/docs/design/BUILDING.md @@ -19,7 +19,7 @@ holds the truth and, where one exists, the test that fails when it is broken. Vo | Enter or pick a value | `inputs/`: `Input`, `Textarea`, `Select`; `forms/`: `Field` (label, hint, error by id), `Checkbox`, `RadioGroup`, `SwitchField` | | Show a measure or identity | `inputs/`: `Lamp`, `Meter`, `Avatar`, `Kbd` | | Show a state | `state/`: `StateMark`, `StateGlyph`; `fabrik/`: `Orb` | -| Show data, proof, "nothing here" | `data/`: `Table`, `KeyValue`, `ProofChip`, `HonestState`, `DevNote` | +| Show data, proof, "nothing here" | `data/`: `Table`, `KeyValue`, `ProofChip`, `HonestState`, `DevNote`, `Sparkline`, `BarList`, `ChartFrame` | | Navigate, overlay, notify | `layout/`: `Tabs`, `TabPanel`, `Sheet`, `ToastRegion` | | Lists and labels | `surface/`: `ListRow`, `Separator`, `Badge` | | The Halle | `hall/` | diff --git a/src/design/data/BarList.tsx b/src/design/data/BarList.tsx new file mode 100644 index 00000000..b55426b7 --- /dev/null +++ b/src/design/data/BarList.tsx @@ -0,0 +1,18 @@ +import { chartColour, chartEmpty, chartValue, known, type ChartProps } from "./charts"; +import "./charts.css"; + +export function BarList({ label, data }: ChartProps) { + if (!data?.length) return

{chartEmpty(data)}

; + const max = Math.max(...data.map(row => known(row.value) ? Math.abs(row.value) : 0)) || 1; + return ; +} diff --git a/src/design/data/ChartFrame.tsx b/src/design/data/ChartFrame.tsx new file mode 100644 index 00000000..be4b7f68 --- /dev/null +++ b/src/design/data/ChartFrame.tsx @@ -0,0 +1,36 @@ +import { useId } from "react"; +import { de } from "../../i18n/de"; +import { BarList } from "./BarList"; +import { Sparkline } from "./Sparkline"; +import "./data.css"; +import { chartColour, chartEmpty, chartValue, type ChartProps } from "./charts"; +import "./charts.css"; + +export interface ChartFrameProps { + title: string; + caption: string; + kind: "sparkline" | "bars"; + data: ChartProps["data"]; +} + +/** One dataset drives both the graphic and its mandatory table twin. */ +export function ChartFrame({ title, caption, kind, data }: ChartFrameProps) { + const id = useId(); + const legend = kind === "sparkline" ? [title] : (data ?? []).map(row => row.label); + return
+

{title}

{caption}

+ {!!data?.length &&