Commit Graph
11 Commits
Author SHA1 Message Date
ki 67e1b465d5 Merge fix/BUG-01-02-resize-pointer: Pointer-Events + rAF
BUG-01: Resize-Handle griff nicht — HTML5-draggable auf Items feuerte
dragstart bevor mousedown auf dem Handle ankam. Komplett raus, eigene
Pointer-Events für Move+Resize mit stopPropagation auf dem Resize-Handle.

BUG-02: Resize-Drag war zäh — renderGrid() pro mousemove. Jetzt rAF +
nur CSS-Geometrie-Update während Drag.

Tests: 7/7 grün.
2026-08-29 17:49:11 +04:00
ki b9d269876b Merge fix/BUG-04-add-no-repack: first-fit für neue Items
Bug-04: Add-Item packte das ganze Layout neu um — ein 1x1 hello
konnte einen 2x1 spotify aus seiner Position drängen. Mit first_fit
landet das neue Item in der ersten freien Zelle, andere bleiben
unverändert. Voller Grid → 409 mit Auto-Pack-Hinweis.

Tests: 5/5 grün.
2026-08-29 17:48:48 +04:00
ki c2a44b80ea BUG-01+02: Resize via Pointer-Events + rAF (kein renderGrid-per-Move)
BUG-01: Resize-Handle griff nicht
  Items hatten `draggable=true` (HTML5-DnD). Der Resize-Handle war Kind des
  Items, also fired der Browser `dragstart` am Parent, sobald die Maus
  überhaupt auf irgendeinem Kind war. `e.preventDefault()` im
  `startResize()` mousedown-Handler kam zu spät — HTML5-DnD hatte den
  Move-Drag bereits übernommen.

BUG-02: Resize-Drag war zäh
  Pro mousemove rief der alte Code `renderGrid()` auf, das die komplette
  Cell- und Item-Struktur neu aufbaute. Bei 60 events/s × 100+ DOM-Ops
  pro Render entstand sichtbarer Jank.

Fix:
- HTML5-draggable komplett raus.
- Eigene Pointer-Events (`pointerdown`/`pointermove`/`pointerup`)
  für Move (startItemPointer) und Resize (startResizePointer).
- Resize-Handle: `pointer-events:auto` auf Handle,
  `pointer-events:none` auf `::before` (Dreieck-Deko), eigene
  Listener, `stopPropagation()` damit Item-Handler nicht mitfeuert.
- `touch-action:none` auf Handle und Item-Body → Touch-Devices
  scrollen nicht versehentlich beim Drag.
- Beide Aktionen nutzen requestAnimationFrame, und nur die
  CSS-Geometrie (grid-column/grid-row) wird aktualisiert — kein
  renderGrid() während Drag. Erst beim Drop / Resize-End kommt
  der volle re-render + save.
- Resize nutzt `setPointerCapture` damit der Move auch dann
  weiterläuft, wenn die Maus den Handle kurz verlässt.

Beweis: tests/test_pointer_events.js (Node-basiert, ohne Browser)
  1. Resize stopPropagation wird aufgerufen
  2. Item-Body pointerdown erreicht nur Item-Handler (nicht Resize)
  3. CSS pointer-events korrekt gesetzt
  4. renderGrid() in keiner onMove-Funktion (0/2)
  5. requestAnimationFrame wird genutzt (3 calls)
  6. draggable=true komplett entfernt (0 assignments)
  7. touch-action:none auf Resize-Handle
  ALLE 7 grün.

Manuelle Verifikation gegen laufenden Server (?demo=1):
  GET /?demo=1 → 200, 91685 bytes
  grep draggable=true: 0
  grep startItemPointer|startResizePointer: 6
  grep pointerdown: 3
  grep requestAnimationFrame: 3
  grep HTML5-drag handlers: 0 (nur 1 in Kommentar)
  grep renderGrid(): 12, davon 0 in onMove-Handlern

Closes #3 #4
2026-08-29 17:31:40 +04:00
ki c6919c5104 BUG-04: Add-Item ohne Re-Pack (first-fit)
Beim Hinzufügen eines neuen Widgets via /api/layout/add rief der Server
layout_mod.pack() auf alle Items auf — pack() sortiert nach Fläche
absteigend und platziert scan-line greedy. Ein 1x1 hello konnte dabei
einen 2x1 spotify aus seiner Position drängen, weil die Sort-Reihenfolge
sich ändert sobald ein neues Item im Mix ist.

Reproduktion vor dem Fix:
  Bestehende config: c1@(0,0) w1@(2,0) st1@(0,2) sp1@(2,2) sv1@(2,3)
  Add hello (1x1) → w1 wurde nach (0,2) verschoben, hello landete bei (2,0).
  Siehe RED-Test in tests/test_add_route_no_repack.py.

Fix:
- layout.py: neue Funktion first_fit(item, others) — platziert ein Item in
  der ersten freien scan-line-Zelle OHNE andere Items zu verändern.
- admin.py /api/layout/add nutzt first_fit. Wenn kein Platz: HTTP 409 mit
  {ok:false, error:'kein Platz für WxH-Item', hint:'use_auto_pack'}.
- templates/index.html addItem(): 409 als 'warn'-Toast mit Auto-Pack-Hinweis.

Akzeptanzkriterien (BUG-04):
- bestehende Items bleiben bei Add unverändert an (x,y)
- neues Item landet in erster freier Zelle
- voller Grid → 409, kein bestehendes Item verschoben
- Auto-Pack bleibt als expliziter User-Wunsch erhalten (BUG-06)

Tests:
- tests/test_layout_firstfit.py: 3 unit tests (empty, gappy, full grid)
- tests/test_add_route_no_repack.py: 2 integration tests gegen /api/layout/add
  mit gemocktem dashboard-Modul + Flask test_client

Closes #6
2026-08-29 17:20:22 +04:00
ki af9a99a379 Add classic design, set as default
Classic = original flat white design (matches reference screenshot)
2026-08-26 15:15:09 +04:00
ki 49de61bb26 Linear design system: new admin UI + display design switcher (8 designs)
Admin UI: Linear.app dark theme — near-black canvas, indigo-violet accent, Inter font
Display: Minimal, Minimal Color, Bold, Analog Clock, Magazine, Cards, Kiosk, Classic
2026-08-26 15:08:17 +04:00
ki a2c4158ba8 Add theme switchers: Admin UI (A/B/C) + Display (5 themes)
Admin UI: Dark Minimal / Retro Terminal / Warm Editorial via header buttons
Display: Classic White / Dark / Sepia / Nord / Terminal via Settings tab
Both switchers persist via localStorage / config.json
2026-08-26 14:41:34 +04:00
ki 99fb5692ef Add 3 alternative admin UI designs: Dark Minimal, Retro Terminal, Warm Editorial 2026-08-26 14:35:39 +04:00
ki 782eb61558 dashboard: remove debug grid lines and item borders from render 2026-08-26 14:16:59 +04:00
ki 502e4459c3 docs: add README with full architecture and plugin guide 2026-08-26 14:15:21 +04:00
ki 28124c5617 Initial commit: epaper-dashboard for 7.3" ACeP 7-Color display
- dashboard.py: plugin-based renderer with 4x4 grid layout
- admin.py: web UI with layout editor + plugin configs
- layout.py: pack algorithm, item placement, grid system
- plugins/: clock, weather, system, spotify, strava, gmail, minimax, hello
- network_watchdog.py: WiFi AP/client mode management
- waveshare_epd_init.py: vendor driver stub
2026-08-26 14:12:43 +04:00