fix(preview): de drie resterende oorzaken uit #782 — checklist/scopematrix, tijdlijn en cockpit #795

Merged
brenno merged 6 commits from fix/preview-nulbreedte-rest into main 2026-07-24 12:46:08 +00:00
Owner

Dit maakt #782 af. De eerste rij van die tabel is in PR #784 gerepareerd; hier staan de andere drie, elk als eigen commit omdat het drie ongerelateerde oorzaken zijn.

Wat er is nagekeken

Alle 24 slidetypes gerenderd op breedte 0, 0,5, 1 en 4 — en de tijdlijn en cockpit bovendien per indeling en per metertype. Wat eruit kwam:

Vindplaats Oorzaak Wanneer
checklist, scopeMatrix LinearProgressIndicator eist minHeight > 0, en die dikte is van de breedte afgeleid breedte 0
timeline deling door een van de breedte afgeleide maat → Infinity/NaN, en het afronden daarvan gooit breedte 0
timeline clamp van de verbindingslijn kruist op een kaart die smaller is dan haar eigen marge breedte 0,5 / 1 / 4
cockpit hoekstraal min randdikte wordt negatief, wat RRect weigert elke meter korter dan ~15 px

De tweede tijdlijnoorzaak stond nog niet in de issue. Die kwam pas boven water toen de eerste weg was: op breedte nul werd ze afgedekt, en op 0,5 sloeg ze alsnog toe. De cockpitoorzaak is evenmin strikt een nulbreedte-geval — de ondergrens van één pixel op de randdikte wint op élke korte meter.

Wat er is gewijzigd

Elke maat kreeg de ondergrens die hij mist: een haarlijn voor de balkdikte, één verdieping voor de tijdlijn, een scherpe hoek voor de cockpitrand, en een bovengrens die tegen de ondergrens aan wordt gehouden voor de verbindingslijn — die twee liggen rekenkundig op hetzelfde punt maar verschillen in de laatste bit, en dat bleek genoeg om ze te laten kruisen.

Boven die grenzen verandert er niets aan de maatvoering van een echte dia. De toets op een gewone breedte verandert er niets staat daaronder, en de goldens zijn ongewijzigd.

Toetsen

test/preview_degenerate_width_test.dart groeit van 10 naar 34 toetsen:

  • per oorzaak een toets mét de inhoud die hem uitlokt (drie tijdlijnindelingen × vier breedtes, zeven metertypes, de lege cockpit met vier meters naast elkaar);
  • daarnaast één sweep over álle 24 slidetypes op vier ontaarde breedtes, die alle omgevallen types in één melding noemt. Dat is het vangnet voor het volgende slidetype dat dezelfde deling of clamp opnieuw uitschrijft.

Elke toets is één keer rood gezien tegen de onherstelde code — de cockpit-sweep door de ondergrens tijdelijk terug te draaien.

De toets houdt het onderscheid uit #784 aan: een overloop van een paar pixels is bij deze breedtes de juiste uitkomst en blijft toegestaan; een ArgumentError of een gefaalde assertie niet.

Poorten

  • make check groen, 6300 toetsen (exit 0, niet door tail gepijpt).
  • make check-secrets groen — gitleaks en trufflehog, werkboom én historie.
  • make sast groen — semgrep, 3 regels over 709 bestanden, 0 bevindingen.
  • DAST niet gedraaid. ZAP is DAST voor webapps en komt niet door een CanvasKit-canvas; dit raakt alleen de renderlaag van de previews en geen geserveerd oppervlak.
  • Bewaker niet aangeroepen, expliciet. Dit raakt het bestandsformaat niet, de opslag niet, geen afhankelijkheid, geen uitgaand verkeer en geen publieke belofte — het zijn ondergrenzen in de renderberekening.

Weging

Onveranderd ten opzichte van de issue: een gebruikerspad hiernaartoe is niet aangetoond. Het venster heeft een ondergrens van 1000×650 en de panelen hebben hun eigen vloer. Dit is hardening met een reproductie. De reden om het toch te doen staat in de issue en geldt nog: de volgende animatie of nieuwe layout breekt die aanname stilzwijgend, en de melding wijst dan nergens naar de oorzaak.

Closes #782

Dit maakt #782 af. De eerste rij van die tabel is in PR #784 gerepareerd; hier staan de andere drie, elk als eigen commit omdat het drie ongerelateerde oorzaken zijn. ## Wat er is nagekeken Alle 24 slidetypes gerenderd op breedte 0, 0,5, 1 en 4 — en de tijdlijn en cockpit bovendien per indeling en per metertype. Wat eruit kwam: | Vindplaats | Oorzaak | Wanneer | | --- | --- | --- | | `checklist`, `scopeMatrix` | `LinearProgressIndicator` eist `minHeight > 0`, en die dikte is van de breedte afgeleid | breedte 0 | | `timeline` | deling door een van de breedte afgeleide maat → Infinity/NaN, en het afronden daarvan gooit | breedte 0 | | `timeline` | clamp van de verbindingslijn kruist op een kaart die smaller is dan haar eigen marge | breedte 0,5 / 1 / 4 | | `cockpit` | hoekstraal min randdikte wordt negatief, wat `RRect` weigert | elke meter korter dan ~15 px | De tweede tijdlijnoorzaak stond nog niet in de issue. Die kwam pas boven water toen de eerste weg was: op breedte nul werd ze afgedekt, en op 0,5 sloeg ze alsnog toe. De cockpitoorzaak is evenmin strikt een nulbreedte-geval — de ondergrens van één pixel op de randdikte wint op élke korte meter. ## Wat er is gewijzigd Elke maat kreeg de ondergrens die hij mist: een haarlijn voor de balkdikte, één verdieping voor de tijdlijn, een scherpe hoek voor de cockpitrand, en een bovengrens die tegen de ondergrens aan wordt gehouden voor de verbindingslijn — die twee liggen rekenkundig op hetzelfde punt maar verschillen in de laatste bit, en dat bleek genoeg om ze te laten kruisen. Boven die grenzen verandert er niets aan de maatvoering van een echte dia. De toets `op een gewone breedte verandert er niets` staat daaronder, en de goldens zijn ongewijzigd. ## Toetsen `test/preview_degenerate_width_test.dart` groeit van 10 naar 34 toetsen: - per oorzaak een toets mét de inhoud die hem uitlokt (drie tijdlijnindelingen × vier breedtes, zeven metertypes, de lege cockpit met vier meters naast elkaar); - daarnaast één sweep over álle 24 slidetypes op vier ontaarde breedtes, die alle omgevallen types in één melding noemt. Dat is het vangnet voor het volgende slidetype dat dezelfde deling of clamp opnieuw uitschrijft. Elke toets is één keer rood gezien tegen de onherstelde code — de cockpit-sweep door de ondergrens tijdelijk terug te draaien. De toets houdt het onderscheid uit #784 aan: een overloop van een paar pixels is bij deze breedtes de juiste uitkomst en blijft toegestaan; een `ArgumentError` of een gefaalde assertie niet. ## Poorten - `make check` groen, 6300 toetsen (exit 0, niet door `tail` gepijpt). - `make check-secrets` groen — gitleaks en trufflehog, werkboom én historie. - `make sast` groen — semgrep, 3 regels over 709 bestanden, 0 bevindingen. - **DAST niet gedraaid.** ZAP is DAST voor webapps en komt niet door een CanvasKit-canvas; dit raakt alleen de renderlaag van de previews en geen geserveerd oppervlak. - **Bewaker niet aangeroepen, expliciet.** Dit raakt het bestandsformaat niet, de opslag niet, geen afhankelijkheid, geen uitgaand verkeer en geen publieke belofte — het zijn ondergrenzen in de renderberekening. ## Weging Onveranderd ten opzichte van de issue: **een gebruikerspad hiernaartoe is niet aangetoond.** Het venster heeft een ondergrens van 1000×650 en de panelen hebben hun eigen vloer. Dit is hardening met een reproductie. De reden om het toch te doen staat in de issue en geldt nog: de volgende animatie of nieuwe layout breekt die aanname stilzwijgend, en de melding wijst dan nergens naar de oorzaak. Closes #782
`LinearProgressIndicator` eist `minHeight > 0`, en de checklist- en
scopematrix-preview leidden die dikte af van de breedte waarop de dia wordt
opgemaakt. Een preview wordt vaker gemeten dan getekend — een inklappend
paneel of een animatie die bij nul begint levert breedte nul — en dan gaat er
een assertie af die de dia niet noemt en de breedte al helemaal niet.

De dikte krijgt een haarlijn als ondergrens. Bij die breedte is er toch niets
te zien, want de balk is zelf nul breed; boven een halve pixel verandert er
niets aan de maatvoering van een echte dia.

Refs #782

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Twee oorzaken in dezelfde berekening, allebei zichtbaar zodra de preview op
een breedte wordt gemeten die te klein is om op te maken.

De verdiepingstelling deelt door een maat die van de breedte is afgeleid. Op
nul levert die deling Infinity — of NaN als de teller ook nul is — en het
afronden daarvan gooit `Unsupported operation: Infinity or NaN toInt`, een
melding die de tijdlijn niet noemt. Bij die breedte valt er niets te verdelen,
dus is het antwoord één verdieping.

De verbindingslijn tussen kaart en rail zet zich tien pixels binnen de
kaartrand. Een kaart die smaller is dan haar eigen marge liet de bovengrens
van die clamp onder de ondergrens zakken — dezelfde foutklasse als #714. De
marge is nu hoogstens de halve kaart, en de bovengrens wordt tegen de
ondergrens aan gehouden: rekenkundig vallen ze dan samen, maar in de laatste
bit niet, en dat bleek genoeg om ze alsnog te laten kruisen.

Die tweede kwam pas boven water op breedte 0,5, 1 en 4 — op nul alleen bleef
ze staan. De regressietoets loopt daarom alle drie de tijdlijnindelingen langs
bij elk van die vier breedtes.

Refs #782

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De binnenste haarlijn van de instrumentrand trekt de randdikte van de
hoekstraal af. De straal volgt de metergrootte, maar de dikte heeft een vaste
pixel als vloer — op een meter die korter is dan zo'n vijftien pixels wint die
vloer en wordt het verschil negatief, wat `RRect` weigert. De assertie die dan
afgaat noemt de cockpit niet, en gaat per meter opnieuw af.

Een preview die op nulbreedte wordt gemeten is de reproductie, maar de rand
tekent voor elk metertype opnieuw, dus de toets loopt ze alle zeven langs plus
de lege cockpit — die valt terug op vier voorbeeldmeters naast elkaar, en dat
is het geval waarin één meter al kort wordt zonder dat de dia zelf ontaard is.

Refs #782

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CONTRIBUTING: één taal per commentaarblok, en wie een bestaand blok bewerkt
volgt de taal die er al staat. Het blok boven de haarlijn is Engels; de
Nederlandse toevoeging maakte er een mengeling van, wat de commentaartaalpoort
terecht rood zette.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De vier toetsen hierboven noemen elk hun eigen oorzaak en zetten er de inhoud
bij die hem uitlokt. Deze doet het omgekeerde en loopt met de standaardinhoud
van `Slide.create` alle types langs. Wat ze vangt is geen oorzaak maar een
verzuim: het volgende slidetype dat erbij komt en dezelfde deling of clamp
opnieuw uitschrijft.

Bewust één toets en geen 24, met alle omgevallen types in de melding — wie een
type toevoegt wil weten wát er omvalt, niet 24 losse regels waarvan er één rood
is. Nagemeten door de cockpit-ondergrens tijdelijk terug te draaien: de toets
noemt dan de cockpit op alle vier de breedtes.

Refs #782

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Refs #782

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brenno merged commit 5856984b7f into main 2026-07-24 12:46:08 +00:00
Sign in to join this conversation.
No description provided.