fix(callouts): markeringen terug in de rasterexport, en zichtbaar op donker (#1847) #1857

Merged
brenno merged 1 commit from fix/1847-callouts-uit-de-export into main 2026-08-29 18:51:31 +00:00
Owner

De beeldkeuring van #1847 gelopen — pin/gebied/pijl × licht/donker/druk/grijswaarden, onthullen, zoom, volle diabreedte én slidestrook. Twee bevindingen zijn hier gerepareerd; vier andere zijn issues geworden (#1853, #1854, #1855, #1856).

1. De markeringen ontbraken in élke rasterexport

De zwaarste. PDF, PPTX en ODP kregen de dia zonder spelden, gebieden of pijlen — zichtbaar in de app, weg in het bestand, en niets dat erover klaagde.

CalloutOverlay tekent niets zolang de intrinsieke beeldmaat onbekend is, en die maat kwam altijd via een setState. De rasterizer laadt de dia-afbeeldingen voor en vangt dan één frame zodra de boom niet meer hoeft te verven — dat frame was er al vóór de tweede ronde. Staat het beeld in de cache (precies wat het voorladen doet), dan levert resolveIntrinsicSize de maat nu synchroon af.

Nagemeten op het echte product, want de fout was niet uit de code af te lezen: dezelfde dia, dezelfde export, vóór de reparatie geen enkele markering en erna alle drie op hun plek.

Wat een test hier wél en niet houdt. De tíming is onder de testbinding niet na te bootsen: daar komt het extra frame vanzelf op tijd, en een rasterizer-toets bleef groen mét én zónder de fout — dat is met een mutatie nagegaan, niet aangenomen. Wat wél vastligt is de eigenschap waar de export op steunt: een gecachet beeld antwoordt synchroon. callout_export_frame_test.dart valt om zodra dat weer een frame kost. Daarnaast houdt callout_raster_export_frame_test.dart de bodem vast — markeringen bereiken de gerasterde pixels überhaupt — door de echte rasterizer te draaien en de pixels te tellen.

2. De markering was op een donkere afbeelding niet te zien

§6 belooft een niet-optionele two-tone rand, juist omdat de pixels eronder willekeurig zijn. De invulling was: rand altijd donker, vulling het thema-accent. Met het uitgerolde profiel zijn dat twee dónkere tonen. Gemeten tegen zwart:

contrast
vulling #003399 op rgb(25,25,25) 1,50:1
rand #111111 op rgb(25,25,25) 1,16:1
ondergrens die deze app zelf hanteert 3,5

In grijswaarden bleef er een zwevende letter over zonder markering eromheen — precies wat punt 3 van de acceptatielijst verbiedt.

Er zit nu een witte ring tussen de vulling en de donkere rand: één lichte en één donkere toon, dus contrast tegen élke ondergrond, en niet meer leunend op kleur. De HTML-export deed dit al zo (border:2px solid #fff plus accentring); dit brengt Flutter in lijn in plaats van andersom.

Daarbij een ondergrens op de straal. Op slidestrook-breedte (~121 pt slot) kwam de markering op 2,7 pt uit en verdween ze in het beeld: de strook toonde dan een dia waarvan niet te zien was dát er verwijzingen op staan. De letter is daar toch niet te lezen — een navigatiestrook is geen leesoppervlak — maar de markering moet een markering blijven.

De rest van de keuring

Acceptatiepunt Oordeel
Markering op de juiste plek, volle breedte én strook goed — nagerekend tegen het cover-model; wel #1853 over afgeknipte doelen
Two-tone zichtbaar op licht / donker / druk was niet goed op donker — hier gerepareerd
Onderscheidbaar in grijswaarden was niet goed — zelfde reparatie
Onthullen: alleen zichtbare regels en hun markeringen goed, stap voor stap doorgelopen
Zoom > 100%: markering schaalt mee met het beeld goed; de markering houdt bewust een vaste schermmaat. De regelaar is alleen niet te bereiken zonder pinch → #1856

Wat issues zijn geworden

  • #1853 — een afgeknipt doel verdwijnt zonder een woord; bij een gebied verdwijnt het hele gebied zodra één rand buiten valt. Er zitten drie geldige antwoorden aan vast, dus dat is een besluit en geen stille reparatie.
  • #1854 — de plaatsingsstage toont een andere uitsnede dan de dia (verhouding ~1,42 tegen ~0,71), plus: de stage toont alleen de geselecteerde verwijzing, en de bijsnijddialoog toont er geen.
  • #1855 — de HTML-export verliest de naast-elkaar-opmaak van een callout-dia (de tekst ligt óp het beeld), schrijft afgeknipte markeringen wél weg waar Flutter ze laat vallen, en gebruikt een vaste 16:9 voor de beeldverhouding.
  • #1856 — de zoomregelaar is met muis of toetsenbord niet te bereiken; de letterbox-balken zijn zuiver zwart in elk thema.

Test plan

  • make check groen
  • make check-secrets groen (gitleaks: no leaks; trufflehog: 0 geverifieerd)
  • make sast groen (semgrep, 0 bevindingen)
  • Nieuw: callout_export_frame_test.dart (de synchrone eigenschap, met mutatieproef) en callout_raster_export_frame_test.dart (echte rasterizer, pixels geteld)
  • Met de hand nagemeten op een echte PDF- en PPTX-export, vóór en na
  • Met eigen ogen gekeurd op licht, donker en de slidestrook

Bewaker

Geen bestandsformaat, geen opslag, geen afhankelijkheid, geen uitgaand verkeer. Wel een publieke belofte: de exportdialoog zegt "De export gebruikt exact de weergave uit de editor". Die belofte was onwaar en is nu waar voor de rasterexports; voor de HTML-export staat wat er nog scheef is in #1855 in plaats van dat het stil blijft.

🤖 Generated with Claude Code

De beeldkeuring van #1847 gelopen — pin/gebied/pijl × licht/donker/druk/grijswaarden, onthullen, zoom, volle diabreedte én slidestrook. Twee bevindingen zijn hier gerepareerd; vier andere zijn issues geworden (#1853, #1854, #1855, #1856). ## 1. De markeringen ontbraken in élke rasterexport De zwaarste. PDF, PPTX en ODP kregen de dia **zonder** spelden, gebieden of pijlen — zichtbaar in de app, weg in het bestand, en niets dat erover klaagde. `CalloutOverlay` tekent niets zolang de intrinsieke beeldmaat onbekend is, en die maat kwam altijd via een `setState`. De rasterizer laadt de dia-afbeeldingen voor en vangt dan één frame zodra de boom niet meer hoeft te verven — dat frame was er al vóór de tweede ronde. Staat het beeld in de cache (precies wat het voorladen doet), dan levert `resolveIntrinsicSize` de maat nu synchroon af. **Nagemeten op het echte product**, want de fout was niet uit de code af te lezen: dezelfde dia, dezelfde export, vóór de reparatie geen enkele markering en erna alle drie op hun plek. **Wat een test hier wél en niet houdt.** De tíming is onder de testbinding niet na te bootsen: daar komt het extra frame vanzelf op tijd, en een rasterizer-toets bleef groen mét én zónder de fout — dat is met een mutatie nagegaan, niet aangenomen. Wat wél vastligt is de eigenschap waar de export op steunt: **een gecachet beeld antwoordt synchroon**. `callout_export_frame_test.dart` valt om zodra dat weer een frame kost. Daarnaast houdt `callout_raster_export_frame_test.dart` de bodem vast — markeringen bereiken de gerasterde pixels überhaupt — door de echte rasterizer te draaien en de pixels te tellen. ## 2. De markering was op een donkere afbeelding niet te zien §6 belooft een niet-optionele two-tone rand, juist omdat de pixels eronder willekeurig zijn. De invulling was: rand altijd donker, vulling het thema-accent. Met het uitgerolde profiel zijn dat twee dónkere tonen. Gemeten tegen zwart: | | contrast | |---|---| | vulling `#003399` op `rgb(25,25,25)` | **1,50:1** | | rand `#111111` op `rgb(25,25,25)` | **1,16:1** | | ondergrens die deze app zelf hanteert | 3,5 | In grijswaarden bleef er een zwevende letter over zonder markering eromheen — precies wat punt 3 van de acceptatielijst verbiedt. Er zit nu een **witte ring** tussen de vulling en de donkere rand: één lichte en één donkere toon, dus contrast tegen élke ondergrond, en niet meer leunend op kleur. De HTML-export deed dit al zo (`border:2px solid #fff` plus accentring); dit brengt Flutter in lijn in plaats van andersom. Daarbij een **ondergrens op de straal**. Op slidestrook-breedte (~121 pt slot) kwam de markering op 2,7 pt uit en verdween ze in het beeld: de strook toonde dan een dia waarvan niet te zien was dát er verwijzingen op staan. De letter is daar toch niet te lezen — een navigatiestrook is geen leesoppervlak — maar de markering moet een markering blijven. ## De rest van de keuring | Acceptatiepunt | Oordeel | |---|---| | Markering op de juiste plek, volle breedte én strook | goed — nagerekend tegen het cover-model; wel #1853 over afgeknipte doelen | | Two-tone zichtbaar op licht / donker / druk | **was niet goed op donker** — hier gerepareerd | | Onderscheidbaar in grijswaarden | **was niet goed** — zelfde reparatie | | Onthullen: alleen zichtbare regels en hun markeringen | goed, stap voor stap doorgelopen | | Zoom > 100%: markering schaalt mee met het beeld | goed; de markering houdt bewust een vaste schermmaat. De regelaar is alleen niet te bereiken zonder pinch → #1856 | ## Wat issues zijn geworden - **#1853** — een afgeknipt doel verdwijnt zonder een woord; bij een gebied verdwijnt het hele gebied zodra één rand buiten valt. Er zitten drie geldige antwoorden aan vast, dus dat is een besluit en geen stille reparatie. - **#1854** — de plaatsingsstage toont een andere uitsnede dan de dia (verhouding ~1,42 tegen ~0,71), plus: de stage toont alleen de geselecteerde verwijzing, en de bijsnijddialoog toont er geen. - **#1855** — de HTML-export verliest de naast-elkaar-opmaak van een callout-dia (de tekst ligt óp het beeld), schrijft afgeknipte markeringen wél weg waar Flutter ze laat vallen, en gebruikt een vaste 16:9 voor de beeldverhouding. - **#1856** — de zoomregelaar is met muis of toetsenbord niet te bereiken; de letterbox-balken zijn zuiver zwart in elk thema. ## Test plan - [x] `make check` groen - [x] `make check-secrets` groen (gitleaks: no leaks; trufflehog: 0 geverifieerd) - [x] `make sast` groen (semgrep, 0 bevindingen) - [x] Nieuw: `callout_export_frame_test.dart` (de synchrone eigenschap, met mutatieproef) en `callout_raster_export_frame_test.dart` (echte rasterizer, pixels geteld) - [x] Met de hand nagemeten op een echte PDF- en PPTX-export, vóór en na - [x] Met eigen ogen gekeurd op licht, donker en de slidestrook ### Bewaker Geen bestandsformaat, geen opslag, geen afhankelijkheid, geen uitgaand verkeer. Wel een publieke belofte: de exportdialoog zegt "De export gebruikt exact de weergave uit de editor". Die belofte was onwaar en is nu waar voor de rasterexports; voor de HTML-export staat wat er nog scheef is in #1855 in plaats van dat het stil blijft. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(callouts): markeringen terug in de rasterexport, en zichtbaar op donker (#1847)
All checks were successful
scans / scans (pull_request) Successful in 2m8s
static-gate / static-gate (pull_request) Successful in 5m5s
bbca6ad664
Twee bevindingen uit de beeldkeuring, allebei nagemeten op het echte product.

**De markeringen ontbraken in élke rasterexport.** PDF, PPTX en ODP kregen de
dia zonder spelden, gebieden of pijlen. De overlay tekent niets zolang de
intrinsieke beeldmaat onbekend is, en die kwam altijd via een `setState`; de
rasterizer laadt de beelden voor en vangt dán één frame, en dat frame was er
al voordat de tweede ronde kwam. Staat het beeld in de cache — precies wat het
voorladen doet — dan levert `resolveIntrinsicSize` de maat nu synchroon af.

Geen widgettest houdt de tíming vast: onder de testbinding komt het extra
frame vanzelf op tijd, dus daar was de fout niet te zien (dat is nagegaan, met
een mutatie). Wat wél vastligt is de eigenschap waar de export op steunt: een
gecachet beeld antwoordt synchroon. Die toets valt om zodra dat weer een
frame kost. De uitkomst zelf is met de hand nagemeten: dezelfde dia, dezelfde
export, vóór de reparatie geen enkele markering en erna alle drie.

**De markering was op donker niet te zien.** De rand was altijd donker en de
vulling het thema-accent: met het uitgerolde profiel 1,5:1 voor de vulling en
1,16:1 voor de rand tegen zwart, waar deze app zelf 3,5 als vloer hanteert. In
grijswaarden bleef er een zwevende letter over. Er zit nu een witte ring tussen
vulling en rand — één lichte en één donkere toon. De HTML-export deed dat al zo.

Daarbij een ondergrens op de straal: op slidestrook-breedte kwam de markering
op 2,7 pt uit en verdween ze, zodat de strook een dia toonde waarvan niet te
zien was dát er verwijzingen op staan.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
brenno merged commit c4f221f471 into main 2026-08-29 18:51:31 +00:00
Sign in to join this conversation.
No description provided.