- Nur Formatierung und Importreihenfolge, keine Verhaltensaenderung
- Betrifft ausschliesslich Befunde, die der Merge neu eingebracht hat
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Dashboard-Reiter stehen in der aufgeklappten Seitenleiste als Liste
unter "Dashboard" (Ziehen sortiert senkrecht, Umbenennen/Loeschen/Neu
im Bearbeitungsmodus); eingeklappt und auf dem Handy wie bisher in der
Kopfzeile. Die Kopfzeile zeigt sonst den Namen des Dashboards.
- Begruessung einzeilig auf Hoehe der Befehlsleiste.
- Kachel-Groesse: kollidiert die gezogene Groesse mit einem Nachbarn, wird
nur die freie Achse uebernommen - vorher verwarf react-grid-layout mit
preventCollision die ganze Aenderung, sobald die Maus eine Zeile nach
unten wackelte (Favoriten liessen sich deshalb oft nicht schmaler ziehen).
- Ansicht: belegte Spalten mittig, Begruessung/Befehlsleiste ruecken mit.
- Kalender: einzeilige Terminliste mit "Heute"/"Morgen" in Akzentfarbe,
Ort im Tooltip, Standardhoehe 16 statt 12.
- Hintergrund "Bluete" im Dunkelmodus weggelassen (Farbstufen), Ersatz Nebel.
- Kacheln: Symbol-Chip in der Akzentfarbe, leichtes Anheben beim Zeigen,
gestaffeltes Einblenden beim Laden (ohne bei reduzierter Bewegung).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Begruessung nach Tageszeit mit Datum, Befehlsleiste oben rechts statt
schwebendem Knopf, einheitliche Kachel-Kopfzeilen mit Symbol, Kalender
mit Pfeilknoepfen, runden Tagen und Terminpunkten, Favoriten als
36-px-Zeilen bzw. App-Starter-Kacheln, Notiz-Kaestchen im Akzent,
leerer Zustand mit drei Vorschlaegen.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Einheitliche Kachel-Kopfzeile, ruhigere Notiz-/Kalender-/Favoriten-
Darstellung, 12 px Raster-Abstand, gestrichelte Kontur und Griff im
Bearbeitungsmodus. Neuer Knopf Hintergrund: Keiner, fuenf eingebaute
(Nebel, Salbei, Bluete, Duenen, Mosaik) oder eigenes Bild aus den
Bilderrahmen-Bildern; Prototyp speichert im localStorage je Benutzer.
Mit Hintergrund werden Kacheln Mica-artig durchscheinend (Kontrast
>= 4,5:1 auch im schlechtesten Fall).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- tenant-selector: Komponenten statisch statt im Test dynamisch importiert
(Laden zaehlte in die 5-s-Frist des ersten Tests), SUPER_ADMIN/ADMIN in
zwei it aufgetrennt
- marketplace/marketplace-filters: gleiche Umstellung; userEvent an die
falsche Uhr gekoppelt statt auf shouldAdvanceTime zu warten
- proxmox-widget: Rendern wartet das erste Laden in act() ab (81 Warnungen weg)
- Todo 2026-09-23 nach completed/
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- ProxmoxServerPicker gemeinsam fuer Kachel und Einstellungsformular
- Bearbeitungsmodus: entprelltes Titelfeld, Server auswaehlen statt Liste, sofort gespeichert
- ProxmoxWidgetConfigForm unter Einstellungen > Dashboard, Titel-Zusatz in der Kopfzeile
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Auswahl-Bauteil: Reihenfolge nach position, Produktwort, Aufraeumen geloeschter Kennungen
- Einstellungsformular: Laden, Fehler, Leer, onChange fuer Titel und Auswahl
- Einstellungsbereich: Zweig proxmox mit Titel-Zusatz in der Kopfzeile
- Kachel im Bearbeitungsmodus: entprellter Titel, Auswahl statt Liste, schliesst beim Verlassen
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Zeilen nach down, warn, ok, idle, orphan mit genau einer Kennzahl, unbekannt statt 0
- config.serverIds filtert Liste und Balken, nur geloeschte Kennungen: eigener Satz
- Ansichtsmodus Links auf /modules/proxmox, Bearbeitungsmodus ohne Links
- Groessenstufen per Container-Query, Nachladen alle 60 s, pausiert bei verborgenem Tab
- formatPercent/formatCount einmal in components/proxmox, ServerCard nutzt sie
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Typ proxmox am Ende von WIDGET_TYPES, WIDGET_MODULE_SLUGS = { proxmox: 'proxmox' }
- gemeinsame Statusteile (proxmox-status, HealthBar, status-styles) nach components/proxmox/
- HealthBar variant compact: 6 px, ohne Legende, aria-hidden
- Registry 3/4/8/8, Katalog-/Registry-/API-Tests auf die erste Modul-Kachel umgestellt
- ProxmoxWidget liest nur listServers, zeigt Lade-, Leer-, Fehlerzustand
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- eine Kachel ohne Bauteil (entfernter Typ oder gesperrtes Modul) zeigt
statt des rohen Typnamens den Satz `widgets.unavailable`, zentriert und
grau; Schluessel in de.json und en.json
- Entwicklerdoku: neuer Abschnitt "Eine Kachel zum Modul" im
Modul-Walkthrough — die drei verbliebenen Stellen und was eine Kachel
mit moduleSlug automatisch tut
- Changelog unter "Unveroeffentlicht - Geaendert"
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Befund aus dem Browser-Rundgang: example.com setzt `margin: 15vh` — mit
3000 px Vorschauhoehe lag die Ueberschrift bei y 450, in der Kachel mit
720 px Rahmenhoehe bei y 108. Der in der Vorschau gewaehlte Ausschnitt
zeigte in der Kachel also etwas anderes. Die Kachel nutzt jetzt dieselbe
Layouthoehe wie die Vorschau (3000), damit vh-relative Seiten identisch
umbrechen; der Rest wird ohnehin weggeschnitten.
Vorschau: `overflow-x-hidden`, die Eckgriffe am rechten Rand erzeugten
einen 4-px-Querbalken.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Resolver: crop (x/y/w/h, geklemmt auf 1280er-Seite, x verschoben statt abgewiesen), zoom (50..150, groesste Stufe <= n), readOnly (nur echtes true)
- xframe-crop.ts: reine Geometrie - computeCropLayout (contain + zentriert, scale 0 bis gemessen), applyCropDrag (Verschieben, vier Ecken, Gegenecke bleibt)
- Kachel: Clip + verschobener, skalierter <iframe> bei fester Layoutbreite 1280, ResizeObserver am Koerper; Zoom-Zweig mit Prozentmassen; 100 % wie bisher; readOnly-Flaeche nur im Ansichtsmodus
- Formular: Checkbox Ausschnitt (ein Aufruf mit crop + readOnly), Vorschau 1280 px breit mit derselben Sandbox und pointer-events none, Rahmen mit vier Griffen (Pointer-Events, Capture-Waechter fuer jsdom), Zahlenfelder, Zoom-Auswahl nur ohne Ausschnitt, Nur anzeigen, dauerhafter Hinweis
- Rahmen als <fieldset> statt div role=group (Biome useSemanticElements, kein biome-ignore)
- Test-Helfer stubResizeObserver, 13 neue Schluessel de/en, Ausschnitt auf der Umlaut-Allowlist; Web-Tests 604 -> 640
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- xframe-config.ts: Resolver (https-Pruefung via isHttpsUrl des Bilderrahmens,
Titel bis 100 Zeichen, Neuladen 0/60/300/600/1800/3600 s geklemmt),
XFRAME_SANDBOX ohne allow-top-navigation und allow-modals; 12 Tests zuerst rot
- xframe-widget.tsx: genau ein <iframe> (sandbox, allow="", no-referrer, lazy),
Kopfleiste mit Titel oder Ecksymbol "In neuem Tab oeffnen", Neuladen ueber
key-Wechsel mit Timer-Raeumung, transparente Flaeche im Bearbeitungsmodus
damit die Kachel Ziehgriff bleibt; 12 Tests zuerst rot
- xframe-config-form.tsx: Adresse/Titel mit Uebernahme bei Blur/Enter, http wird
mit Meldung abgewiesen und nicht gespeichert, Intervall-Auswahl, dauerhafter
Hinweis auf verweigertes Einbetten; 8 Tests
- Panel-Zweig samt "— Titel" in der Kopfzeile, Registry (12x12, Fenster-Symbol),
Katalog, Seite, DTO @IsIn, de/en widgets.xframe (15 Schluessel), Umlaut-Allowlist
"neuem"; der Server ruft die Adresse nie ab
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Befunde aus dem Browser-Rundgang am 21.09.2026:
- Die Grossansicht lag in einem `react-grid-item` mit CSS-`transform`; ein
transformierter Vorfahr wird fuer `position: fixed` zum Bezugsrahmen, der
Dialog war deshalb auf die Kachelflaeche (531x216) beschraenkt statt den
Viewport zu fuellen. Jetzt per `createPortal` in `document.body`, wie der
Kalender-Tooltip.
- Wechselintervall "1 Minuten" -> ICU-Plural (`one {# Minute}`), de + en; der
Formular-Test nutzt dafuer den echten `createTranslator` von next-intl auf
der echten de.json statt eines `{n}`-Ersatzes.
- Standardgroesse 8x8 (216 px hoch) war zu flach fuer ein Foto -> 8x12 wie die
Kalender-Vorgabe.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- picture-frame-config.ts: Eintragstyp als Vereinigung (upload | url) in EINER
geordneten Liste, resolvePictureFrameConfig laesst alles ausser https weg
(T-PI9-07), Intervall 0 oder 5..3600 s, pickNextIndex (Zufall nie dasselbe)
- dashboard-images-api.ts: Upload als FormData-Feld image ohne eigenen
Content-Type, 413 -> deutsche Meldung, Proxy-Pfad fuer <img src>
- PictureFrameWidget: Leerzustand, <img referrerPolicy=no-referrer> (Browser
laedt Fremdbilder, Server nie), object-contain/cover, Unterschrift-Streifen,
Wechsel per Timer mit Raeumung, kaputte Bilder verlassen den Umlauf,
Grossansicht nur ausserhalb des Bearbeitungsmodus mit Fokus-Rueckgabe und
Pause des Wechsels; im Bearbeitungsmodus kein Knopf (Karte bleibt Griff)
- PictureFrameConfigForm im WidgetSettingsPanel: Ausschnitt, Intervall,
Reihenfolge, Liste mit Vorschau/Unterschrift/Pfeilen/Entfernen (Upload wird
auch serverseitig geloescht), Datei hochladen, https-Adresse hinzufuegen
- Registry (4x4 min, 8x8 Vorgabe), Katalog, Seite, Uebersetzungen de/en
(widgets.pictureFrame, 28 Schluessel), bestehende Tests auf acht Typen
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Restposten 3a (doppeltes Ladefenster): lastFetchWindowRef merkt sich das
zuletzt TATSAECHLICH geholte from/to-Paar; ein Monatswechsel, dessen
berechnetes Fenster damit uebereinstimmt, ueberspringt fetchEvents. Der
5-Minuten-Auffrischer (force=true) umgeht den Sperrgriff immer, sonst
friert die Anzeige ein. computeFetchWindow und die Tagesgrenzen-Rundung
bleiben unangetastet.
Restposten 3b (Quellenliste je Monatswechsel): hasSourcesRef merkt sich
das Ergebnis; fetchSources laeuft nur beim Aufbau (Merkung leer) oder
erzwungen (Auffrischer) — eine neu eingerichtete Quelle wird weiterhin
binnen fuenf Minuten bemerkt.
Restposten 4 (t-Identitaet): neue translations-identity.test.tsx rendert
eine Testkomponente unter dem ECHTEN NextIntlClientProvider und beweist
per Referenzgleichheit, dass t bei einem lokalen Zustandswechsel
dasselbe Funktionsobjekt bleibt — bestaetigt durch den use-intl-4.13.0-
Quelltext (translate entsteht in einem useMemo, dessen Abhaengigkeiten
ausschliesslich aus dem root-staendigen Intl-Kontext stammen). Die
Faustregel aus 260921-gof ("t gehoert in keine Abhaengigkeitsliste")
bleibt als Konvention in Ordnung; die zugrunde liegende Annahme ("t ist
bei jedem Render frisch") ist damit ausdruecklich WIDERLEGT statt ein
drittes Mal weitergetragen. Die vier verbliebenen Stellen
(marketplace/page.tsx, admin/users/page.tsx,
calendar-settings-panel.tsx, calendar-source-form.tsx) bleiben deshalb
unveraendert.
Vier neue zaehlende Testfaelle in calendar-widget.test.tsx (Aufbau je 1,
abweichendes Fenster +1/+0, identisches Fenster +0/+0, erzwungener Lauf
+1/+1) plus ein Test, dass ein uebersprungener Lauf den Ladezustand
sauber beendet und geladene Termine nicht leert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
header.tsx, account-settings-form.tsx, favorites-widget.tsx (zweimal):
alle vier tragen bereits alt="", sind also schon aus dem
Zugaenglichkeitsbaum genommen; aria-hidden sagt dasselbe nur
ausdruecklich. Ehrliche Einordnung: richtige Auszeichnung, verbessert
fuer keinen Menschen etwas — der Befund verschwindet, weil die Regel
ein verborgenes Element nicht mehr betrachtet. onError ist ein
Ladefehler, keine Bedienung: hier gab es nie einen Tastaturweg zu
schaffen. Keine Unterdrueckung, kein biome-ignore.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
calculator-widget.tsx Speicherzeile und favorites-widget.tsx
Ansichtsumschalter waren schlichte <div> mit aria-label, das die Rolle
generic stillschweigend verwarf. role="toolbar" ergaenzt (geprueft
sauber; role="group"/"region" loesen useSemanticElements neu aus).
Taschenrechner: fest verdrahteter Text wandert in
calculator.memoryLabel; bei dieser Gelegenheit auch Anzeigefeld
(displayLabel) und Rueckschritt-Taste (backspaceLabel) in den Katalog
gezogen (D-05), da diese Datei ohnehin geaendert wird.
Favoriten: die bisherige Beschriftung ("Favoriten") war sachlich
falsch fuer einen Listen/Kachel-Umschalter — neuer, zutreffender
Schluessel favorites.viewModeLabel statt des wiederverwendeten
favorites.name.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
- Zelle rendert als <button type="button"> NUR wenn hasEvents gilt,
sonst unveraendert als <div> ohne Handler (D-01) — 42 neue Tab-Stopps
waeren eine Verschlechterung
- onFocus/onBlur mit demselben Rumpf wie onMouseEnter/onMouseLeave,
damit die Termin-Einblendung auch per Tastatur erscheint/verschwindet
- cellClass unveraendert uebernommen, nur w-full text-left ergaenzt
(D-04, kein optischer Unterschied); widgetNoDrag bleibt erhalten
- aria-label nennt Datum und Terminzahl ueber neue Katalogschluessel
widgets.calendar.dayEventsOne/dayEventsMany (Mehrzahl-Konvention wie
configMaxEventsOne/Many, D-05)
- Neue Tests: Tag mit Terminen ist <button> und reagiert auf
Fokus/Weggehen wie auf Maus-Hover; Tag ohne Termine bleibt <div>;
Einzahl-/Mehrzahl-Beschriftung; echter Tab-Stopp nachgewiesen
Nach diesem Umbau: klick-regeln 5 (nur die vier <img onError> +
Taschenrechner-Rahmen), semantic 0, a11y gesamt 14, errors 0 — Aufgabe
1 des Plans vollstaendig.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
- NoteCheckbox traegt jetzt ein echtes onChange und gibt sein eigenes
DOM-Element an onToggle weiter; readOnly entfaellt (D-01) — war bisher
nur da, um Reacts Warnung ueber ein gesteuertes Feld ohne onChange zu
unterdruecken
- Index-Ermittlung bleibt wortgleich (alle Kaestchen im Behaelter
einsammeln, indexOf auf dem ausloesenden Element), wandert aber vom
Behaelter-onClick in handleCheckboxToggle, das den Behaelter ueber ein
ref statt event.currentTarget findet
- previewOptions als useMemo mit leerer Abhaengigkeitsliste, Rueckruf
ueber ein ref erreicht — identitaetsstabil wie die alte Modulkonstante,
T-JT4-03: rehypePlugins: [[rehypeSanitize]] unveraendert erhalten
- Tests: echte Tastaturbetaetigung (Leertaste auf fokussiertem
Kaestchen) UND echter Klick loesen onToggle/PATCH aus; neue Tests
belegen readOnly/disabled entfallen
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
- Wirkungslose eslint-disable-Zeile in stopwatch-widget.tsx ersetzt durch
Sachhinweis: Zeilen halten keinen Zustand, Rundennummer wird aus Laenge
und Position berechnet. Keine neue Unterdrueckung, ARRAYKEY bleibt bei 19.
- Neuer Testfall in stopwatch-widget.test.tsx: zwei Runden nacheinander,
neuere Runde steht oben, Rundennummern 2/1 stimmen zu ihrer eigenen Zeit.
- Neue MergeTab.test.tsx: Entfernen der mittleren Datei laesst genau erste
und dritte Datei mit eigenem Namen und eigenem Entfernen-Knopf uebrig.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
Befunde 7-15 der Biome-Regel useExhaustiveDependencies (quick-260921-gof):
- VehicleTable, TenderDetail, DigestIntervalForm, SourceConfigForm,
favorites-widget: Ersatz-Fehlertext aus useTranslations wird jetzt vor
dem Effekt/Rueckruf in eine Konstante gezogen und diese Konstante in
die Abhaengigkeitsliste aufgenommen - `t` selbst kommt nirgends mehr
in eine Liste. In diesem Projekt ist belegt, dass `t` bei jedem
Durchlauf eine frische Funktion ist (Testattrappen), eine `t`-
Abhaengigkeit haette den jeweiligen Mount-Abruf zur Schleife gemacht.
- RssFeedListForm.tsx und SavedSearchBar.tsx: die Ladefunktionen waren
gewoehnliche Funktionen im Rumpf (bei jedem Durchlauf neu) - jetzt in
einen stabilen Rueckruf mit der Text-Konstante als einziger
Abhaengigkeit eingepackt.
- ResultsList.tsx: Befund 8 (t) wie oben, Befund 15 (refreshKey) in den
Effekt verschoben, der `load` aufruft, statt in `load` selbst zu
stehen - eine begruendete `biome-ignore`-Zeile (erste im Projekt)
haelt fest, dass der Auffrisch-Ausloeser der Elternseite ohne diese
Abhaengigkeit wirkungslos waere.
- Sieben Testdateien um eine Zaehlprobe erweitert: erneutes Zeichnen mit
unveraenderten Props darf keinen weiteren Abruf ausloesen; ResultsList
zusaetzlich um eine Probe, dass ein refreshKey-Bump genau einen
weiteren Abruf ausloest.
- DigestIntervalForm hat keine Testdatei - nur am laufenden System auf
Meine Quellen geprueft (siehe SUMMARY).
- Wirkungslose eslint-disable-Zeilen fuer diese Regel entfallen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
Befunde 1-6 der Biome-Regel useExhaustiveDependencies (quick-260921-gof):
- calendar-widget.tsx: useMemo um resolveCalendarConfig() entfernt
(reine Funktion, spart nichts). showToday setzt monthDate jetzt
identitaetserhaltend, wenn der aktuelle Monat schon angezeigt wird -
erst danach durfte der Ladeeffekt von monthDate.getTime() auf
monthDate umgestellt werden, sonst haette jeder Druck auf den
Monatsknopf im laufenden Monat einen Termin-Abruf bis zum
Exchange-Server ausgeloest (D-04).
- stopwatch-widget.tsx: neue reine Hilfsfunktion computeElapsedFrom()
fuer den Takt-Effekt, der jetzt nur noch drei Einzelwerte statt des
ganzen sw-Objekts liest - eine sw-Abhaengigkeit haette den 100-ms-Takt
bei jeder aufgezeichneten Runde ab- und wiederaufgebaut.
- Testerweiterungen als Rueckfallsicherungen: 2x weiterblaettern -> 3
Abrufe, 3x Monatsknopf im laufenden Monat -> kein Zusatzabruf; Runde
waehrend die Stoppuhr laeuft unterbricht den Takt nicht, genau 1 PATCH
je Klick.
- Wirkungslose eslint-disable-Zeilen fuer diese Regel entfallen (kein
ESLint mehr im Projekt).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
Vier der fuenf noch offenen Regeln aus Teillieferung B bereinigt:
- noRedundantRoles (4): ueberfluessige role-Angaben auf button/time/select
entfernt (maschineller --unsafe-Fix, gelesen).
- useAriaPropsForRole (1): entfaellt automatisch mit obigem Fix -- das
<select role="combobox"> in search-widget.tsx verlangte die fehlenden
ARIA-Attribute nur wegen der ueberfluessigen Rolle.
- useSemanticElements (4): admin-sidebar/settings-sidebar tragen role=
"navigation" jetzt am <nav> statt am <aside> (kein doppeltes Landmark
mehr); widget-wrapper.tsx ist jetzt ein echtes <article> statt
div role="article"; DropZone.tsx trennt die Datei-Entfernen-Schaltflaeche
als Geschwister ab, damit die Drop-Flaeche selbst ein echtes <button>
werden kann (ein <button> darf kein zweites <button> verschachteln).
Die Drop-Flaeche traegt darum jetzt Klick- UND Drag-Handler direkt am
<button>, sonst waere sie ein "statisches" Element mit Ereignis-Handlern
geworden (die zurueckgestellten Regeln noStaticElementInteractions /
noNoninteractiveElementInteractions waeren neu angeschlagen -- geprueft,
bleiben bei 5/11).
- noNoninteractiveTabindex (1): calculator-widget.tsx traegt jetzt
tabIndex={-1} statt {0}. Die Zifferntasten sind bereits echte <button>
und damit selbst Teil der Tab-Reihenfolge; Tastendruecke erreichen
handleKeyboard weiterhin per Bubbling, sobald eine Taste fokussiert ist.
Verhalten unveraendert, nur ein wirkungsloser Tab-Stopp auf dem Container
selbst entfaellt.
Verbleibend: noLabelWithoutControl (22), naechster Schritt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
useButtonType: "Widget hinzufuegen"-, Bearbeiten-Modus- und Katalog-Buttons
(kein Formular in diesen Dateien) erhalten type="button".
noSvgWithoutTitle: Widget-Icons in der Katalog-Kachel (immer neben dem
Widget-Namen) und Buttons mit bestehendem aria-label/title werden dekorativ
(aria-hidden="true"); der bislang unbeschriftete Such-Button im Such-Widget
erhaelt aria-label={t('search.searchButton')} (neuer Schluessel, siehe
vorherige i18n-Festschreibung).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
- Aufgabe 2: vier sichere Biome-Regeln (useImportType pfadgebunden auf
apps/web+packages, noUselessEscapeInRegex, useConst,
useExponentiationOperator) sowie fuenf ungesicherte Regeln
(useNodejsImportProtocol, useLiteralKeys, useOptionalChain, useTemplate,
useParseIntRadix) angewendet und den gesamten Diff von Hand gelesen
(ldap.service.ts zeichenweise gegen Gross-/Kleinschreibung der
AD-Merkmale, auth.service.ts/jwt.strategy.ts gegen Durchwinken bei
fehlender Sitzung geprueft)
- noUselessSwitchCase bleibt bewusst stehen (tender-normalizer.service.ts:60,
die Fallmarke dokumentiert Absicht)
- Toter Code (D-03): fuenf folgenlose Auffangvariablen entfernt, eine
nicht benutzte Funktion (forSystemQuery, Pruefskript) entfernt, ein
positionsgebundener Dekoratorparameter umbenannt (current-user.decorator.ts),
fuenf Symptomfunde entfernt und als Folgeaufgaben zu melden (siehe unten)
- Sechs weitere, im Plan nicht namentlich gelistete aber
gleich-kategorische Dead-Code-Fundstellen in Testdateien zusaetzlich
bereinigt (groups.service.spec.ts, cert-manager.test.tsx,
ldap.service.spec.ts, prisma-tenant.extension.spec.ts x3) — noetig, um
die vom Plan selbst verlangten Nullstaende bei noUnusedVariables/
noUnusedImports/noUnusedFunctionParameters zu erreichen
Dekoratordaten aus apps/api unveraendert (593 Zeilen, sha256 6e1583f1...).
Endstand 620 Befunde (541 echt, 79 Test) statt der im Plan geschaetzten
621/542 — eine Differenz von 1, weil das Streichen des Namens aus
`catch (e: any)` in calendar.service.ts (Symptom-Fix) den dort ebenfalls
gemeldeten noExplicitAny-Befund miteliminiert; das ist eine erwuenschte
Nebenwirkung, keine Regression. Fehlerstufe 0, beide Testlaeufe
punktgleich gruen (69/1124, 66/459), pnpm type-check 4/4, pnpm lint
--force 5/5.
Folgeaufgaben aus D-03 (nicht in diesem Vorgang behoben):
- force-password-change.interceptor.ts: Freigabeliste prueft nur den Pfad,
nicht die HTTP-Methode
- change-password/page.tsx: nach erzwungenem Wechsel bleibt die Person auf
der Seite stehen (keine Weiterleitung, keine Aktualisierung der
Benutzerablage)
- VehicleTable.tsx: Loeschschaltflaeche hat keinen Besetztzustand, laesst
sich doppelt ausloesen
- SplitTab.tsx: downloadAllAsZip erhielt eine ungenutzte
Uebersetzungsfunktion, Hinweis auf fest verdrahtete Texte im Zip-Pfad
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
- Plaketten-Wrapper calendar-day-badges rendert buildDayBadges(day.events) statt der alten Einzel-Plakette
- Ein Kalender: unveraendert eine Plakette in Kalenderfarbe/Akzentfarbe (Tests 3/3b/3c bleiben gruen)
- Zwei/drei Kalender: je ein kleiner Kreis pro Kalender in Startreihenfolge (Test 3d)
- Vier oder mehr Kalender: zwei Kreise plus grauer Restkreis bg-muted-foreground/text-background mit Summe (Test 3e)
- CHANGELOG-Stichpunkt erweitert (mehrere Kalender am selben Tag)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- groupDayBySource gruppiert Tagestermine nach sourceId (nicht Farbe), Reihenfolge = erstes Auftreten, Farbe = Farbe des ersten Termins der Gruppe
- buildDayBadges baut daraus bis zu drei Plaketten-Kreise, ab dem vierten Kalender einen grauen Restkreis mit Summe
- Tests 8/9 in calendar-month.test.ts (TDD: RED bestaetigt vor Implementierung)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- groupEventsByDate sortiert jede Tagesgruppe jetzt nach Start aufsteigend
- Zaehl-Plakette im Monatsraster traegt die Farbe des Kalenders des fruehesten Termins (weisse Schrift), ohne Farbe unveraendert bg-primary
- Tooltip-Zeilen bekommen denselben Farbpunkt wie die Liste "Naechste Termine" (Rueckfall var(--muted-foreground))
- 3 neue Tests: calendar-month Test 2b, calendar-widget Test 3b/3c
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- useTheme (next-themes) + mounted-Guard wie changelog-view.tsx
- data-color-mode am Wurzel-Div folgt resolvedTheme statt fest 'auto'
- zwei neue Tests (dark/light) via vi.hoisted-Themenzustand
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Titel-Span im Tooltip: min-w-0 break-words statt truncate
- Breite (288px) und Rand-Klemmung aus TOOLTIP_WIDTH_PX/TOOLTIP_EDGE_PX
- Test 4c: belegt Klassen und Tooltip-Breite
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- WIDGET_CONSTRAINTS: Minima aus dem Innenaufbau gerechnet (clock 2/2, search 6/2,
calendar 3/3, note 4/4, calculator 3/9, favorites 3/3, link 3/2, stopwatch 4/3),
Vorgaben unveraendert; Test A pinnt alle 32 Werte per toEqual
- dashboard-grid.tsx: effectiveLayouts (useMemo) ueberschreibt minW/minH jedes
gespeicherten Eintrags in jedem Breakpoint aus den Konstanten und hebt zu
kleine w/h auf das Minimum an — RGL 2.2.3 nimmt gespeicherte Eintraege
woertlich (synchronizeLayoutWithChildren) und liest data-grid nur ohne Eintrag
- dashboard-grid.test.tsx: Test 5 angepasst, Test 9 (Ueberschreibung je
Breakpoint, unbekannter Typ unveraendert) und Test 9b (h 8 -> 9, w >= minW bleibt)
- stopwatch-widget.tsx: Bedienleiste px-2 py-1 text-xs, Zeile gap-1 py-1, damit
Stop + Runde + Reset in 4 Spalten passen und die Kachel auf 4x3 schrumpfen kann
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
<img src={fav.iconUrl}> hotlinked the external favicon directly; sites
that send Cross-Origin-Resource-Policy: same-origin (e.g. claude.ai)
get blocked by the browser (net::ERR_BLOCKED_BY_RESPONSE.NotSameOrigin),
leaving only the letter fallback. Points src at the new same-origin
/api-proxy/favorites/:id/icon route instead (same pattern already used
for the user avatar image). Render guard and onError fallback unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- dashboard-grid.test: add noCompactor to react-grid-layout mock
- note-widget.test: enable edit mode before typing (onChange is undefined
when isEditing=false), replace native dispatchEvent with fireEvent.change
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CR-01: fix SSRF bypass — isPrivateIpv6 now delegates ::ffff:<ipv4> to
isPrivateIpv4, covering 172.16-31.x and 169.254.x ranges
- CR-02: add ParseUUIDPipe to GET /favorites widgetId param + service guard
so missing widgetId returns 400 instead of leaking all user favorites
- WR-01: link-widget — replace raw 'link.error' key with t('link.error') (4 sites)
- WR-02: favorites-widget — fix load-path error to use t('favorites.error')
- WR-03: widget-catalog-modal — move aria-hidden from outer wrapper to backdrop
- WR-04: calculator — remove duplicate M button (MR clone); MC/MR/M+/M−/MS remain
- WR-05: schema — add FavoriteLink→WidgetInstance FK with onDelete:Cascade
- IN-01: create-widget.dto.ts — update comment from four to eight supported types
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fetchFavorites called with instanceId on mount
- Link renders as anchor with target="_blank" rel="noreferrer" (T-08-12)
- Empty + edit mode shows add form; createFavorite called on submit
- Single-link enforcement: add form hidden when link exists (D-06)
- Edit mode: updateFavorite called with id and new title
- View toggle: list default, tile container on gridView click
- Letter fallback: iconUrl null shows first uppercase letter