- isValidBgUrl akzeptiert jetzt https:// zusätzlich zu data:/blob: (http bleibt
ausgeschlossen wegen Mixed-Content)
- CSP img-src 'self' https: data: blob: in allen 3 Manifesten, damit remote
Hintergründe deterministisch laden statt still am CSP-Default zu haengen
- Upload-Bilder werden vor dem Speichern per Canvas auf die Bildschirmkante
(max 2560px) verkleinert und als WebP re-kodiert -> schont chrome.storage.local
- URL-Feld: Platzhalter lokalisierbar (data-i18n-placeholder) + Tracking-Hinweis,
dass ein per URL geladenes Bild bei jedem Oeffnen vom fremden Server kommt
- i18n DE/EN: bg_url.desc + bg_invalid_url an https angepasst, 2 neue Keys
Neuer Pin-Button (custom SVG, kein Emoji) im Board-Header sperrt die Position eines
Boards. Bei gesperrtem Board (.board.locked):
- der Drag-Handle wird per CSS ausgeblendet (Flos Wunsch: Handle weg statt nur inaktiv),
- ein zweiter Guard in drag.js onDown verweigert zusaetzlich jeden Drag.
Schuetzt vor versehentlichem Verschieben (ergaenzt den 3px-Bewegungs-Schwellwert). locked
wird wie blurred persistiert, im Export/Import durchgereicht und mit ins Trash-Board geklont.
i18n DE/EN ergaenzt.
Der Drain laesst renderBoards() aus, solange ein Board- oder Bookmark-Drag laeuft
(replaceChildren wuerde den Drag abreissen) — holte den Render danach aber nie nach,
sodass der gespeicherte Eintrag bis zu einem unabhaengigen Fremd-Render unsichtbar blieb.
Der ausgelassene Render wird jetzt gemerkt (_renderDeferredByDrag) und drag.js ruft am
Ende jedes Drags (onUp/onCancel/Bookmark-dragend) flushQuickSaveRenderIfDeferred nach.
Idempotent: ohne ausstehenden Render kein Extra-Render bei normalen Drags.
Zwei Befunde aus der Integrations-Review:
- Race: der Page-Drain und der Worker machen je ein read-modify-write auf
'quicksave_pending' ohne kontextuebergreifende Atomizitaet. Ein Worker-Append im
await-Fenster des Drains konnte einen bereits gedrainten Eintrag in der Queue belassen,
den ein Folge-Drain erneut in die Inbox schrieb (Duplikat). Jede eingespielte Bookmark
traegt jetzt die Pending-id als srcId; ein erneut auftauchender Eintrag wird uebersprungen
statt doppelt eingefuegt. boards-Write bleibt vor der Queue-Bereinigung -> kein Verlust.
- Validierung: der Drain hat e.url ohne isSafeUrl gepusht, anders als jeder andere
Bookmark-Schreibpfad. isSafeUrl (jetzt im DOM-freien quicksave-core, http/https/ftp)
filtert unsichere/leere Protokolle vor dem Schreiben ins Board.
Beim Trash-Import sortierte combined.sort+slice(-N) rein nach deletedAt: brachte ein
Backup neuere Eintraege mit, fielen aeltere LOKALE Eintraege aus dem Cap — und die sind
die einzige Kopie der geloeschten Daten (Datenverlust). Jetzt haben lokale Eintraege
Vorrang (alle behalten, sind bereits auf TRASH_MAX_ENTRIES gekappt), Importe fuellen nur
den Rest mit den neuesten auf.
Ein reiner Klick/Tap auf den Drag-Handle (ohne echtes Verschieben) hat in onUp den
gegen die Viewport geclampten --board-x/y-Wert zurueckgelesen und als board.pos
persistiert. Bei einem off-screen geclampten Board (nach Fenster-Verkleinerung oder
Import von breiterem Screen) zerstoerte das die wahre Position. Jetzt zaehlt erst eine
Bewegung > 3px als Drag; ohne Bewegung bleibt board.pos unangetastet.
- Render + neuer debounced Resize-Handler clampen --board-x/y gegen den
aktuellen Viewport: ein auf breiterem Fenster platziertes Board rendert
nie mehr off-screen (und damit per Drag unerreichbar). board.pos bleibt
unveraendert, bei spaeterer Verbreiterung wird die Originalposition erreicht.
- drag.js: cleanup() + pointercancel-Listener. Die Klasse .board.dragging
klebte bei Touch-Interrupt/Browser-Geste sonst dauerhaft und legte den
app.js-Sync-Guard (Quick-Save-Render) still.
- main.css: '.board.blurred { position: relative }' entfernt — lag im
utilities-Layer und schlug das absolute Free-Layout (geblurrtes Board fiel
aus seiner Position + war nicht mehr drag-bar).
- data.js: board.pos wird beim JSON-Import durchgereicht (safePos-Validierung
via Number.isFinite), sonst Verlust des frei gesetzten Layouts beim Restore.
- settings.js: _makeTrap bricht ab, wenn ein .dialog-overlay offen ist, damit
der Dialog-keydown-Handler Escape/Tab allein behandelt (kein Doppelschluss,
Fokusfalle bleibt dicht)
- dialog.js: aria-labelledby/-describedby zeigen auf instanz-eindeutige IDs
(Date.now + Modul-Zaehler) statt feste dialogTitle/dialogBody, damit kurz
gestapelte Dialoge dem Screenreader nicht den falschen Titel liefern
- boards.js: Bookmark-keydown reagiert bei role=link nur noch auf Enter, Space
entfernt (Space ist Button-Semantik)