39b1f74b56
Gegenprobe lief nicht im Browser, sondern im echten 1.3.0-AppImage ueber den WebKit-Remote-Inspektor. Gleicher Fehlerfall vorher/nachher: Kachel 469 -> 389 px bei 1000 px Bereich, Platzhalter erreicht jetzt exakt den rechten Rand (603 px). Dazu die Messfalle festgehalten: im Client gegen style.width/style.transform messen, nie gegen getBoundingClientRect() - bei Fenster im Hintergrund friert WebKitGTK die Animationsuhr ein und der width-Uebergang bleibt stehen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
225 lines
12 KiB
Markdown
225 lines
12 KiB
Markdown
---
|
||
phase: quick-260922-vdk
|
||
plan: 01
|
||
subsystem: ui
|
||
tags: [react, react-grid-layout, ref-callback, resize-observer, dashboard, vitest]
|
||
|
||
requires: []
|
||
provides:
|
||
- "Dashboard-Raster misst seine Breite auch beim Wechsel Leerzustand -> gefuellt (Ref-Rueckruf statt Einmal-Effekt)"
|
||
- "Fenster-Horcher als Netz zusaetzlich zum ResizeObserver"
|
||
affects: [dashboard-grid, dashboard]
|
||
|
||
actuals:
|
||
tokens: 2392
|
||
tasks: 2
|
||
commits: 2
|
||
|
||
tech-stack:
|
||
added: []
|
||
patterns:
|
||
- "Ref-Rueckruf (callback ref) statt useEffect mit leerer Abhaengigkeitsliste, wenn eine Messung den tatsaechlich eingehaengten Knoten ueber Aus-/Einhaengen hinweg verfolgen muss"
|
||
|
||
key-files:
|
||
created: []
|
||
modified:
|
||
- apps/web/src/components/dashboard/dashboard-grid.tsx
|
||
- apps/web/src/components/dashboard/dashboard-grid.test.tsx
|
||
- CHANGELOG.md
|
||
|
||
key-decisions:
|
||
- "Ref-Rueckruf measureRef ersetzt containerRef + Einmal-Effekt; misst synchron in der Commit-Phase, kein useLayoutEffect noetig"
|
||
- "Kein Aufraeum-Rueckgabewert aus measureRef, damit React 19 den Rueckruf beim Aushaengen mit null aufruft (das ist der Aufraeumpfad)"
|
||
- "Fenster-Horcher zusaetzlich zum ResizeObserver, nicht als Ersatz"
|
||
- "Startwert bleibt 1200 (nur bis zur ersten Commit-Phase relevant)"
|
||
- "FREE_PLACEMENT_COMPACTOR/preventCollision, BREAKPOINTS, COLS, rowHeight, margin, applyConstraintMinima unveraendert"
|
||
|
||
patterns-established:
|
||
- "quick-260922-vdk Kommentarblock in dashboard-grid.tsx erklaert das Warum der Ref-Rueckruf-Messung"
|
||
|
||
requirements-completed: [QUICK-260922-VDK]
|
||
|
||
coverage:
|
||
- id: D1
|
||
description: "Dashboard-Raster misst seine Breite auch aus dem Leerzustand heraus (Ref-Rueckruf, synchrone Erstmessung, Fenster-Horcher)"
|
||
requirement: "QUICK-260922-VDK"
|
||
verification:
|
||
- kind: unit
|
||
ref: "apps/web/src/components/dashboard/dashboard-grid.test.tsx#quick-260922-vdk Test 10: Leerzustand -> gefuellt misst die tatsaechliche Breite statt beim Startwert 1200 stehenzubleiben"
|
||
status: pass
|
||
- kind: unit
|
||
ref: "apps/web/src/components/dashboard/dashboard-grid.test.tsx#quick-260922-vdk Test 11: Fenstergroesse aendert sich -> der Fenster-Horcher misst neu"
|
||
status: pass
|
||
human_judgment: true
|
||
rationale: "Die Unit-Tests beweisen die gemessene Breite in jsdom; ob im echten Browser rechts kein toter Streifen mehr bleibt und die Rasterbreite sichtbar der Breite des Inhaltsbereichs entspricht, verlangt einen Browser-Rundgang (Prueflliste unten)."
|
||
- id: D2
|
||
description: "Ziehverhalten unveraendert: belegtes Feld bleibt blockiert, nichts wird zur Seite geschoben"
|
||
verification:
|
||
- kind: unit
|
||
ref: "apps/web/src/components/dashboard/dashboard-grid.test.tsx#quick-260916-dyv Test 7: Compactor-Pin"
|
||
status: pass
|
||
- kind: unit
|
||
ref: "apps/web/src/components/dashboard/dashboard-grid.test.tsx#quick-260916-dyv Test 6: dragConfig-Pin"
|
||
status: pass
|
||
human_judgment: false
|
||
|
||
duration: ~9min
|
||
completed: 2026-09-22
|
||
status: complete
|
||
---
|
||
|
||
# Quick-Aufgabe 260922-vdk: Dashboard-Raster misst seine Breite auch aus dem Leerzustand heraus Summary
|
||
|
||
**Messung an einem Ref-Rueckruf (`measureRef`) statt an einem Einmal-Effekt: der Beobachter folgt dem eingehaengten `<div>` ueber Leerzustand <-> gefuellt hinweg, misst synchron in der Commit-Phase, plus Fenster-Horcher als Netz — 20-Spalten/`md`-Fehlmessung nach 1200 px behoben.**
|
||
|
||
## Performance
|
||
|
||
- **Duration:** ~9 min
|
||
- **Completed:** 2026-09-22
|
||
- **Tasks:** 2/2
|
||
- **Files modified:** 3
|
||
|
||
## Accomplishments
|
||
- Dashboard-Raster misst jetzt in jedem Fall die tatsaechliche Breite des Inhaltsbereichs, auch wenn `DashboardGrid` mit null Kacheln einhaengt und die erste Kachel erst spaeter erscheint
|
||
- Fenster-Horcher als Sicherheitsnetz zusaetzlich zum ResizeObserver
|
||
- Zwei neue Regressionstests, nachweislich zuerst rot
|
||
- Ziehverhalten (`FREE_PLACEMENT_COMPACTOR` mit `preventCollision`), Raster-Konstanten und Leerzustand unveraendert (per Test 4/6/7/9/9b bestaetigt)
|
||
|
||
## Task Commits
|
||
|
||
Each task was committed atomically:
|
||
|
||
1. **Aufgabe 1: Messung an den Knoten haengen (Ref-Rueckruf, synchrone Erstmessung, Fenster-Horcher)** - `d9f2af3` (fix)
|
||
2. **Aufgabe 2: Changelog-Stichpunkt, volle Tore, Zaehler, Pruefliste** - `cf67c8a` (docs)
|
||
|
||
_Ledger: `plan_head_before` = `3e8c0f4` (letzter Commit vor diesem Plan — die bereits committete Akte). `git rev-list --count 3e8c0f4..HEAD` = 2, deckt sich mit den zwei oben genannten Commits._
|
||
|
||
**Plan metadata:** wird vom Orchestrator nach diesem SUMMARY committet (STATE.md/ROADMAP.md ausserhalb dieses Plans).
|
||
|
||
## Rot-Nachweis (Aufgabe 1, vor der Aenderung)
|
||
|
||
Testlauf vor dem Umbau von `dashboard-grid.tsx` (nur die Test-Datei war schon geaendert):
|
||
|
||
```
|
||
FAIL src/components/dashboard/dashboard-grid.test.tsx > DashboardGrid > quick-260922-vdk Test 10: ...
|
||
AssertionError: expected 1200 to be 1000 // Object.is equality
|
||
|
||
FAIL src/components/dashboard/dashboard-grid.test.tsx > DashboardGrid > quick-260922-vdk Test 11: ...
|
||
AssertionError: expected 1000 to be 1600 // Object.is equality
|
||
```
|
||
|
||
10 von 12 Faellen gruen, genau die zwei neuen rot — wie erwartet: Test 10 blieb beim Startwert 1200 (der frueh zurueckspringende Leerzustand rendert den gemessenen Knoten nie, der Effekt mit leerer Abhaengigkeitsliste sieht ihn also nie), Test 11 blieb bei 1000, weil es vorher keinen Fenster-Horcher gab.
|
||
|
||
Nach dem Umbau: alle 12 Faelle gruen (`pnpm --filter @tessera/web exec vitest run src/components/dashboard/dashboard-grid.test.tsx`).
|
||
|
||
## Gemessene Zahlen (nachher, alle Tore)
|
||
|
||
| Tor | Ausgangsmessung (22.09., vor der Aenderung) | Nachher (gemessen) |
|
||
|---|---|---|
|
||
| `dashboard-grid.test.tsx` | 10 Faelle | 12 Faelle, alle gruen |
|
||
| Web Tests gesamt | 81 Dateien / 659 Tests | 81 Dateien / 661 Tests, alle gruen |
|
||
| `pnpm type-check` | — | 4/4 ohne Befund |
|
||
| `pnpm lint` | — | 5/5 ohne Befund der Stufe `error` |
|
||
| Biome web Warnungen | 53 | 53 (unveraendert) |
|
||
| `as unknown as` in web | 6 | 6 (unveraendert) |
|
||
|
||
Keine Abweichung — alle erwarteten Zahlen aus dem Plan treffen exakt zu.
|
||
|
||
## Files Created/Modified
|
||
- `apps/web/src/components/dashboard/dashboard-grid.tsx` - `measureRef`-Ref-Rueckruf ersetzt `containerRef` + Einmal-Effekt; `applyWidth`-Wächter (verwirft 0/nicht-endliche Werte); Fenster-Horcher (`resize`-Listener); deutscher Warum-Kommentarblock `quick-260922-vdk`
|
||
- `apps/web/src/components/dashboard/dashboard-grid.test.tsx` - zwei neue Faelle (Test 10: Leerzustand -> gefuellt misst 1000; Test 11: resize-Ereignis misst 1600), `stubResizeObserver`-Import, `vi.unstubAllGlobals()` im `afterEach`
|
||
- `CHANGELOG.md` - neuer Abschnitt „### Behoben" unter „## Unveroeffentlicht" mit einem Stichpunkt
|
||
|
||
## Decisions Made
|
||
Keine neuen Entscheidungen — die im Plan gebundenen Entscheidungen (Ref-Rueckruf statt Einmal-Effekt, synchron vor dem ersten Zeichnen, Fenster-Horcher als Netz, Ziehverhalten unveraendert, Startwert 1200 bleibt, nur `apps/web`) wurden wortgleich umgesetzt.
|
||
|
||
## Deviations from Plan
|
||
|
||
None - plan executed exactly as written.
|
||
|
||
## Issues Encountered
|
||
None.
|
||
|
||
## Pruefliste fuer den Browser-Rundgang (Orchestrator, lokal, Playwright-MCP, nicht Testserver)
|
||
|
||
Breiten werden am DOM gemessen (`getBoundingClientRect` der Elemente), **nie** per `fetch` aus der Seite heraus — das taeuscht in beide Richtungen.
|
||
|
||
- [ ] (a) Dashboard eines Benutzers ohne Kacheln oeffnen (oder alle Kacheln entfernen und neu laden) -> Leerzustand mit Bildmarke erscheint
|
||
- [ ] (b) Bearbeiten -> „Widget hinzufuegen" -> Uhr: die Kachel erscheint, und beim Ziehen reicht der Platzhalter bis an den rechten Rand des Inhaltsbereichs — kein toter Streifen
|
||
- [ ] (c) messen: Breite von `.react-grid-layout` gleicht der Breite des umgebenden Inhaltsbereichs (Abweichung hoechstens der Rand von 8 px); vorher lag sie bei 1200 px unabhaengig von der Fensterbreite
|
||
- [ ] (d) eine zweite Kachel auf ein belegtes Feld ziehen -> sie springt zurueck, nichts wird zur Seite geschoben (unveraendert gewollt); Groesse ziehen stoppt am Nachbarn
|
||
- [ ] (e) Fenster schmaler und wieder breiter ziehen -> das Raster folgt, Kacheln bleiben heil
|
||
- [ ] (f) Seite mit vorhandenen Kacheln neu laden -> weiterhin richtig (der bisher schon funktionierende Pfad), und beim ersten Zeichnen ist kein Sprung von schmal auf breit zu sehen
|
||
- [ ] (g) letzte Kachel entfernen -> Leerzustand erscheint, danach eine neue Kachel hinzufuegen -> wieder volle Breite (der Beobachter wurde sauber getrennt und neu angehaengt)
|
||
|
||
Entscheidend: Punkt (b)/(c) — auf einem beim Oeffnen leeren Dashboard fuellt die erste Kachel den Inhaltsbereich bis zum rechten Rand, und die gemessene Rasterbreite stimmt mit der Breite des Inhaltsbereichs ueberein.
|
||
|
||
## Threat Flags
|
||
|
||
Keine neue Vertrauensgrenze, keine neuen Pakete. Alle sechs T-VDK-Punkte aus dem Plan-Threat-Model sind mit Tests bzw. Struktur-Toren abgedeckt (siehe Rot-Nachweis und gemessene Zahlen oben); nichts Neues gefunden.
|
||
|
||
## Next Phase Readiness
|
||
Kein laufender Meilenstein, keine Folge-Phase direkt abhaengig. Naechster Schritt laut STATE.md bleibt: Widget-Modul-Kopplung (`WIDGET_MODULE_MAP`) und danach das Proxmox-Modul — unabhaengig von dieser Quick-Aufgabe.
|
||
|
||
## Self-Check: PASSED
|
||
|
||
- FOUND: apps/web/src/components/dashboard/dashboard-grid.tsx
|
||
- FOUND: apps/web/src/components/dashboard/dashboard-grid.test.tsx
|
||
- FOUND: CHANGELOG.md
|
||
- FOUND: .planning/quick/260922-vdk-dashboard-raster-misst-seine-breite-nich/260922-vdk-SUMMARY.md
|
||
- FOUND commit: d9f2af3
|
||
- FOUND commit: cf67c8a
|
||
|
||
---
|
||
*Phase: quick-260922-vdk*
|
||
*Completed: 2026-09-22*
|
||
|
||
## Rundgang durch den Orchestrator (22.09.2026, echter Linux-Client, nicht der Browser)
|
||
|
||
Der Nutzer hat den Fehler im **Linux-Client** gemeldet und ausdruecklich gesagt, ein
|
||
Browser-Test bringe nichts. Geprueft wurde deshalb im echten Paket: `Tessera-1.3.0.AppImage`
|
||
aus dem Gitea-Release, auf dem Entwicklungsrechner gestartet (`DISPLAY=:10`,
|
||
`WEBKIT_INSPECTOR_HTTP_SERVER=127.0.0.1:9230`), gesteuert ueber den WebKit-Remote-Inspektor
|
||
(Treiber `scratchpad/wk.mjs`, Target-Protokoll: `Target.sendMessageToTarget` +
|
||
`Runtime.evaluate`). Server: lokaler Stack.
|
||
|
||
**Ausgangsmessung VOR dem Fix** (gleiche Sitzung, gleicher Client):
|
||
|
||
| Weg | Bereich | Kachel | Rasterbreite laut Rechnung |
|
||
|-----|---------|--------|----------------------------|
|
||
| Neuladen MIT Kachel | 1176 px | 459 px | 1176 px — richtig |
|
||
| Seitenleiste auf/zu | 1000 ↔ 1176 px | folgt | richtig |
|
||
| **Leeres Dashboard, dann Kachel hinzufuegen** | 1000 px | **469 px** | **1200 px — falsch** |
|
||
|
||
Der dritte Weg ist der Fehlerfall: `DashboardGrid` haengt mit null Kacheln ein, der
|
||
gemessene `<div>` existiert nicht, der Einmal-Effekt bricht ab und laeuft nie wieder.
|
||
|
||
**Nach dem Fix, derselbe Weg** (Kachel entfernt, gespeichert, neu geladen -> leeres
|
||
Dashboard -> Kalender hinzugefuegt):
|
||
|
||
- `width`-Eigenschaft an `Responsive`: **1000** (= echte Bereichsbreite), Breakpoint `md`,
|
||
20 Spalten — aus dem React-Fiber ausgelesen, nicht geraten.
|
||
- Kachel `style.width`: **389 px** = `8 × 41,6 + 56`, exakt der Sollwert fuer 1000 px.
|
||
- Ziehen nach rechts (synthetische Maus-Ereignisse, jeweils mit Wartezeit, damit React
|
||
dazwischen rendert): Platzhalter laeuft 8 → 107 → 256 → 405 → 554 → **603** und bleibt
|
||
dort. `1000 − 8 − 389 = 603` — **der Platzhalter erreicht jetzt exakt den rechten Rand**.
|
||
Die Kachel wird auch dort abgelegt (`tileX 603`).
|
||
|
||
**Messfalle, dokumentiert damit sie niemanden noch einmal kostet:** `getBoundingClientRect()`
|
||
und `getComputedStyle().width` lieferten im Client weiter 469 px, obwohl `style.width`
|
||
bereits 389 px war. Grund: `.react-grid-item` hat `transition: width .2s`, und das
|
||
Client-Fenster lag im Hintergrund — WebKitGTK friert die Animationsuhr dann ein, der
|
||
Uebergang bleibt auf dem Startwert stehen (`getAnimations()` meldete eine laufende
|
||
`CSSTransition` auf `width`). **Im Client gegen die gesetzten Werte messen
|
||
(`style.width`, `style.transform`) oder gegen die React-Eigenschaften, nie gegen die
|
||
gemalte Box** — sonst misst man die eingefrorene Animation statt des Ergebnisses.
|
||
|
||
**Nicht angefasst, wie zugesagt:** belegte Plaetze bleiben gesperrt, nichts weicht aus
|
||
(`FREE_PLACEMENT_COMPACTOR` mit `preventCollision` unveraendert) — ausdrueckliche Ansage des
|
||
Nutzers am 22.09.
|
||
|
||
**Testreste der Pruefung:** lokaler Testbenutzer `clienttest` und die zeitweise auf
|
||
`NODE_ENV=development` gesetzte lokale API (WebKitGTK nimmt `secure`-Kekse ueber `http` nicht
|
||
an, Chromium macht fuer `localhost` eine Ausnahme) — beides nach der Pruefung wieder
|
||
zurueckgebaut.
|