Split-slide: lettertype schaalt niet op naar de vrije ruimte na verdelen over meerdere slides #1279

Closed
opened 2026-08-05 19:01:47 +00:00 by brenno · 6 comments
Owner

Symptoom

Een overvolle split-slide (bullets + afbeelding) kun je met de "verdeel over
meerdere slides"-actie opknippen. Na het splitsen groeit het lettertype op de
resulterende deel-slides niet mee
om de vrijgekomen ruimte te vullen — de tekst
blijft klein en er blijft ruimte onbenut.

Reproductie (Proefpresentatie.md, slide 5 "Dit is een hele volle slide"):
een _class: split-slide met een zeer lange bullet-lijst (~59 bullets, elk een
volle zin) en --image-width: 40%. Splits hem; de deel-slides blijven klein
renderen. (Bevestigd: gewone bullets, geen ocideck_list_style: richText — dus
het bulletsImage-pad, niet het richText-pad.)

Belangrijk vooraf: dit raakt een bewust, getest invariant

Dit is geen schaal die uit de bronslide wordt gekopieerd. Er is geen scale-veld
op Slide; continuesSplit is enkel een bool
(lib/models/slide.dart:398). De tekst blijft klein omdat de
per-pagina auto-fit bij het renderen wordt geklemd op het minimum van de hele
split-run
— de schaal van de vólste pagina. Dat is expliciet ontworpen en getest
gedrag, gedocumenteerd in lib/models/slide.dart:392 ("one
shared font scale — the size of the fullest half") en vastgelegd in
test/shared_split_fit_scale_test.dart:91.
De bouwer moet dus eerst de intentie bevestigen (bewaker/productowner) vóór hij
de klem loslaat.

Oorzaak (bestand:regel)

De eigen auto-fit wérkt, maar wordt onderdrukt:

  • bulletsImageSlideFitScale
    (lib/services/slide_layout_metrics.dart:698)
    berekent per slide een correcte inpassing op de smalle tekstkolom.
  • Bij render wordt die geklemd:
    lib/widgets/slides/previews/bullets_image_preview.dart:131
    resolvedScale = fitScaleOverride != null ? math.min(fitScaleOverride!, scale) : scale.
    math.min staat niet toe dat een dunnere pagina groter wordt dan de gedeelde
    waarde. (Idem bullets_previews.dart:237 en :619.)
  • fitScaleOverride = sharedSplitFitScale = minimum over de run, berekend in
    buildSplitRunLayoutIndex
    (lib/services/split_run.dart:230, lidmeting
    splitRunMemberScalebulletsImageSlideFitScale, :255).
  • Een losse bulletsImage-slide krijgt fitScaleOverride == null en rendert wél
    op volle auto-fit (tot kBulletsMaxScale = 3.2) — dát zijn de split-slides die
    elders wél groot tonen. Zodra de slide in een run zit, klemt math.min.

Rode haring: de --split-text-scale: 1.00 / font-size: 1.00em in het .md-bestand
is een ándere grootheid en niet de oorzaak van de native render. Geschreven door
_splitTextScale (markdown_service_helpers.dart:235),
een grove bucket (blijft 1.00 bij >12 bullets) die alleen de HTML/Marp-export-CSS
voedt. De parser leest hem nooit terug
(markdown_service_parse/markdown_service_parse_directives.dart
heeft geen tak voor scale; --image-width wél). Native render negeert hem.

Oplossingsrichtingen

Product/ontwerp-spanning: de gebruiker wil per-pagina auto-fit; het ontwerp wil bewust
één uniforme grootte over de run.

(A) Wat de opdracht letterlijk vraagt — per-deel auto-fit. Stop met klemmen voor
split-runs: buildSplitRunLayoutIndex
(split_run.dart:230) niet langer op het minimum pinnen,
óf fitScaleOverride niet doorgeven. Kleinste centrale ingreep zit in split_run.dart
(raakt dan alle 6 call-sites: preview_panel.dart:452, slide_list_panel.dart:586,
slide_preview.dart, audience_window.dart:374, presenter_overlays.dart:568,
slide_rasterizer.dart:242). Breekt het gedocumenteerde invariant + de tests en
maakt pagina 2 groter dan pagina 1 (inconsistente lijstgrootte binnen één verhaal).
Niet doen zonder productbesluit.

(B) Ontwerp-behoudend (aanbevolen) — laat de split grotere pagina's maken. Houd de
uniforme grootte, maar geef bulletsImage een smaller per-pagina-doel in
splitBulletSlidePages
(lib/utils/bullet_fixes.dart:106) i.p.v. de
volle-breedte-drempel kSingleColumnBulletWarningCount = 8. De smalle split-kolom
(afbeelding neemt ruimte) houdt minder bullets comfortabel; minder bullets per pagina
tilt de gedeelde schaal op, vult de ruimte, en blijft consistent. Route:
DeckNotifier.splitSlide
(lib/state/deck_provider_slides.dart:99) →
splitBulletSlidePagessplitBulletsIntoPages
(lib/services/bullet_pagination.dart:53).

Advies: bevestig intentie (bewaker/productowner); voorkeur (B).

Regressietest

  • Raakt: test/shared_split_fit_scale_test.dart
    — met name 'a bullets+image run shares the fuller page's scale' (:91) en de
    bullets-variant (:73), die nu expliciet vastleggen dat de dunne pagina op de vólste
    klemt (sparseScale > fullScale, shared == fullScale). Ook
    test/split_run_quality_test.dart.
  • Eerst-rood: bij (A) inverteer :73/:113 (dunne pagina rendert op eigen, grotere
    schaal). Bij (B) een nieuwe test die na splitSlide op een overvolle bulletsImage
    aantoont dat de gedeelde schaal boven een comfortabele vloer uitkomt (groter dan vóór
    de fix) — met huidige code faalt dat.
  • Repo-regel (render-heuristiek tegen echte render): bewijs via een échte
    widget-render, niet alleen de metric. test/bullets_image_preview_test.dart
    rendert de widget al; lees de effectieve fontSize in de boom op een vervolgpagina en
    toets dat die groeit. Ronde afsluiten met visuele controle (skill beeldkeuring).

Geraakte poorten / ketens

  • l10n: geen nieuwe zichtbare tekst verwacht; wél 31 vertalingen als er een nieuwe
    kwaliteitsmelding/label bij komt.
  • class/file-size ratchet: split_run.dart (284 r.) en bullet_fixes.dart ruim
    onder 1000; methoden ≤150. Houd (A) centraal in split_run.dart om call-site-churn en
    de "UI-imports in lib/services ≤8"-poort te vermijden.
  • coverage: bestaande, gedekte bestanden + toegevoegde test.

Open vragen / nuances voor de bouwer

  • Oppervlak-discrepantie: HTML-export gebruikt de per-slide _splitTextScale (ruime
    pagina groeit) terwijl native + PNG/PDF-raster
    (slide_rasterizer.dart:242) de run-minimum
    gebruiken (ruime pagina geklemd). Als het verschil per oppervlak zichtbaar was, is dát
    mogelijk de eigenlijke verwarring — verifieer op welk oppervlak de melding geldt.
  • Vol of ruim? De ~59 bullets zijn lange volzinnen; bij drempel 8 ontstaan pagina's
    die ook los nog vol zijn. Controleer met beeldkeuring of de deel-slides écht ruimte
    overhouden (dan helpt (A)) of gewoon te vol blijven voor de kolom (dan is (B) de kern).
  • Ontwerpbotsing: richting (A) gaat recht in tegen een expliciet, getest invariant
    (slide.dart:392 + shared_split_fit_scale_test.dart). Vereist
    een bewuste productkeuze vóór bouwen — dit issue is bouwklaar op de analyse, maar de
    richtingkeuze is de eerste stap.
## Symptoom Een overvolle `split`-slide (bullets + afbeelding) kun je met de "verdeel over meerdere slides"-actie opknippen. Na het splitsen **groeit het lettertype op de resulterende deel-slides niet mee** om de vrijgekomen ruimte te vullen — de tekst blijft klein en er blijft ruimte onbenut. **Reproductie** (`Proefpresentatie.md`, slide 5 "Dit is een hele volle slide"): een `_class: split`-slide met een zeer lange bullet-lijst (~59 bullets, elk een volle zin) en `--image-width: 40%`. Splits hem; de deel-slides blijven klein renderen. (Bevestigd: gewone bullets, **geen** `ocideck_list_style: richText` — dus het bulletsImage-pad, niet het richText-pad.) ## Belangrijk vooraf: dit raakt een bewust, getest invariant Dit is **geen** schaal die uit de bronslide wordt gekopieerd. Er is geen scale-veld op `Slide`; `continuesSplit` is enkel een `bool` ([lib/models/slide.dart:398](lib/models/slide.dart)). De tekst blijft klein omdat de per-pagina auto-fit bij het renderen **wordt geklemd op het minimum van de hele split-run** — de schaal van de vólste pagina. Dat is expliciet ontworpen en getest gedrag, gedocumenteerd in [lib/models/slide.dart:392](lib/models/slide.dart) ("one shared font scale — the size of the fullest half") en vastgelegd in [test/shared_split_fit_scale_test.dart:91](test/shared_split_fit_scale_test.dart). De bouwer moet dus eerst de **intentie** bevestigen (bewaker/productowner) vóór hij de klem loslaat. ## Oorzaak (bestand:regel) De eigen auto-fit wérkt, maar wordt onderdrukt: - `bulletsImageSlideFitScale` ([lib/services/slide_layout_metrics.dart:698](lib/services/slide_layout_metrics.dart)) berekent per slide een correcte inpassing op de smalle tekstkolom. - Bij render wordt die geklemd: [lib/widgets/slides/previews/bullets_image_preview.dart:131](lib/widgets/slides/previews/bullets_image_preview.dart) `resolvedScale = fitScaleOverride != null ? math.min(fitScaleOverride!, scale) : scale`. `math.min` staat niet toe dat een dunnere pagina *groter* wordt dan de gedeelde waarde. (Idem `bullets_previews.dart:237` en `:619`.) - `fitScaleOverride` = `sharedSplitFitScale` = **minimum over de run**, berekend in `buildSplitRunLayoutIndex` ([lib/services/split_run.dart:230](lib/services/split_run.dart), lidmeting `splitRunMemberScale` → `bulletsImageSlideFitScale`, `:255`). - Een **losse** bulletsImage-slide krijgt `fitScaleOverride == null` en rendert wél op volle auto-fit (tot `kBulletsMaxScale = 3.2`) — dát zijn de split-slides die elders wél groot tonen. Zodra de slide in een run zit, klemt `math.min`. **Rode haring:** de `--split-text-scale: 1.00` / `font-size: 1.00em` in het .md-bestand is een ándere grootheid en niet de oorzaak van de native render. Geschreven door `_splitTextScale` ([markdown_service_helpers.dart:235](lib/services/markdown_service_helpers.dart)), een grove bucket (blijft 1.00 bij >12 bullets) die **alleen** de HTML/Marp-export-CSS voedt. De parser leest hem nooit terug ([markdown_service_parse/markdown_service_parse_directives.dart](lib/services/markdown_parse/markdown_service_parse_directives.dart) heeft geen tak voor scale; `--image-width` wél). Native render negeert hem. ## Oplossingsrichtingen Product/ontwerp-spanning: de gebruiker wil per-pagina auto-fit; het ontwerp wil bewust één uniforme grootte over de run. **(A) Wat de opdracht letterlijk vraagt — per-deel auto-fit.** Stop met klemmen voor split-runs: `buildSplitRunLayoutIndex` ([split_run.dart:230](lib/services/split_run.dart)) niet langer op het minimum pinnen, óf `fitScaleOverride` niet doorgeven. Kleinste centrale ingreep zit in `split_run.dart` (raakt dan alle 6 call-sites: `preview_panel.dart:452`, `slide_list_panel.dart:586`, `slide_preview.dart`, `audience_window.dart:374`, `presenter_overlays.dart:568`, `slide_rasterizer.dart:242`). **Breekt** het gedocumenteerde invariant + de tests en maakt pagina 2 groter dan pagina 1 (inconsistente lijstgrootte binnen één verhaal). Niet doen zonder productbesluit. **(B) Ontwerp-behoudend (aanbevolen) — laat de split grotere pagina's maken.** Houd de uniforme grootte, maar geef `bulletsImage` een smaller per-pagina-doel in `splitBulletSlidePages` ([lib/utils/bullet_fixes.dart:106](lib/utils/bullet_fixes.dart)) i.p.v. de volle-breedte-drempel `kSingleColumnBulletWarningCount = 8`. De smalle split-kolom (afbeelding neemt ruimte) houdt minder bullets comfortabel; minder bullets per pagina tilt de gedeelde schaal op, vult de ruimte, en blijft consistent. Route: `DeckNotifier.splitSlide` ([lib/state/deck_provider_slides.dart:99](lib/state/deck_provider_slides.dart)) → `splitBulletSlidePages` → `splitBulletsIntoPages` ([lib/services/bullet_pagination.dart:53](lib/services/bullet_pagination.dart)). **Advies:** bevestig intentie (bewaker/productowner); voorkeur (B). ## Regressietest - **Raakt:** [test/shared_split_fit_scale_test.dart](test/shared_split_fit_scale_test.dart) — met name 'a bullets+image run shares the fuller page's scale' (`:91`) en de bullets-variant (`:73`), die nu expliciet vastleggen dat de dunne pagina op de vólste klemt (`sparseScale > fullScale`, `shared == fullScale`). Ook `test/split_run_quality_test.dart`. - **Eerst-rood:** bij (A) inverteer `:73`/`:113` (dunne pagina rendert op eigen, grotere schaal). Bij (B) een nieuwe test die na `splitSlide` op een overvolle bulletsImage aantoont dat de gedeelde schaal boven een comfortabele vloer uitkomt (groter dan vóór de fix) — met huidige code faalt dat. - **Repo-regel (render-heuristiek tegen echte render):** bewijs via een échte widget-render, niet alleen de metric. [test/bullets_image_preview_test.dart](test/bullets_image_preview_test.dart) rendert de widget al; lees de effectieve `fontSize` in de boom op een vervolgpagina en toets dat die groeit. Ronde afsluiten met visuele controle (skill `beeldkeuring`). ## Geraakte poorten / ketens - **l10n:** geen nieuwe zichtbare tekst verwacht; wél 31 vertalingen als er een nieuwe kwaliteitsmelding/label bij komt. - **class/file-size ratchet:** `split_run.dart` (284 r.) en `bullet_fixes.dart` ruim onder 1000; methoden ≤150. Houd (A) centraal in `split_run.dart` om call-site-churn en de "UI-imports in lib/services ≤8"-poort te vermijden. - **coverage:** bestaande, gedekte bestanden + toegevoegde test. ## Open vragen / nuances voor de bouwer - **Oppervlak-discrepantie:** HTML-export gebruikt de per-slide `_splitTextScale` (ruime pagina groeit) terwijl native + PNG/PDF-raster ([slide_rasterizer.dart:242](lib/widgets/slides/slide_rasterizer.dart)) de run-minimum gebruiken (ruime pagina geklemd). Als het verschil per oppervlak zichtbaar was, is dát mogelijk de eigenlijke verwarring — verifieer op welk oppervlak de melding geldt. - **Vol of ruim?** De ~59 bullets zijn lange volzinnen; bij drempel 8 ontstaan pagina's die ook los nog vol zijn. Controleer met beeldkeuring of de deel-slides écht ruimte overhouden (dan helpt (A)) of gewoon te vol blijven voor de kolom (dan is (B) de kern). - **Ontwerpbotsing:** richting (A) gaat recht in tegen een expliciet, getest invariant ([slide.dart:392](lib/models/slide.dart) + `shared_split_fit_scale_test.dart`). Vereist een bewuste productkeuze vóór bouwen — dit issue is bouwklaar op de analyse, maar de richtingkeuze is de eerste stap.
Author
Owner

Opgepakt. Tak: fix/1279-split-fit-scale. Verwachte reikwijdte: lib/utils/bullet_fixes.dart, lib/services/bullet_pagination.dart (richting B), test/shared_split_fit_scale_test.dart, nieuwe regressietest. Eerste stap: richtingkeuze A/B via productowner/bewaker.

Opgepakt. Tak: fix/1279-split-fit-scale. Verwachte reikwijdte: lib/utils/bullet_fixes.dart, lib/services/bullet_pagination.dart (richting B), test/shared_split_fit_scale_test.dart, nieuwe regressietest. Eerste stap: richtingkeuze A/B via productowner/bewaker.
Author
Owner

Opgepakt. Tak: fix/1279-split-autofit. Richting B (ontwerp-behoudend): de split geeft bulletsImage een smaller per-pagina-doel zodat de smalle kolom niet overvol raakt en de gedeelde schaal omhoog komt — het uniforme-schaal-invariant (shared_split_fit_scale_test) blijft. Reikwijdte: bullet_fixes.dart / bullet_pagination.dart + regressietest tegen echte render.

Opgepakt. Tak: fix/1279-split-autofit. Richting B (ontwerp-behoudend): de split geeft bulletsImage een smaller per-pagina-doel zodat de smalle kolom niet overvol raakt en de gedeelde schaal omhoog komt — het uniforme-schaal-invariant (shared_split_fit_scale_test) blijft. Reikwijdte: bullet_fixes.dart / bullet_pagination.dart + regressietest tegen echte render.
Author
Owner

Richting B gebouwd en gekeurd (tak fix/1279-split-fit-scale). Kern: splitBulletSlidePages schaalt het paginadoel voor bulletsImage-slides mee met de smalle tekstkolom (bulletsImageTextColumnFraction) — bij 40% beeld vijf i.p.v. acht bullets per pagina, met kMinPageBullets als vloer. Het uniforme-run-invariant (slide.dart:392) blijft ongemoeid; de run wint grootte doordat de volste pagina minder draagt.

Beeldkeuring op het reproductiegeval (Proefpresentatie.md slide 5): na splitsen 20% -> 52% ontwerpgrootte op de maatgevende pagina, leesbaar, en de run rendert uniform. De keurder meende dat zelfs de volste pagina maar ~60% vulde en wees naar een render-hoogteschatter die zou overschatten. Dat heb ik met een echte widget-rendermeting getoetst: een werkelijk volle pagina (5 lange bullets) vult 94% van de tekstkolomhoogte op zijn eigen fit-schaal (solo-schaal 0.71). Er is dus GEEN schatteroverschatting; de lege ruimte op de dunnere pagina's is de bewuste prijs van de uniforme run (je kunt niet elke pagina vullen en tegelijk één grootte houden). Het paginadoel verder verlagen helpt niet — dat maakt alleen meer pagina's.

Aparte observatie van de keuring, GEEN onderdeel van deze fix: na een aanbevolen splitsing verzesvoudigt het aantal tekstdichtheid-waarschuwingen (elke deel-slide meldt lange bullets/veel woorden). Terecht in inhoud maar het voelt als straf op gehoorzaamheid; kandidaat voor een eigen issue. Regressietests eerst-rood; docs bijgewerkt. Poort draait; sluit ik na groen + merge op main.

Richting B gebouwd en gekeurd (tak fix/1279-split-fit-scale). Kern: splitBulletSlidePages schaalt het paginadoel voor bulletsImage-slides mee met de smalle tekstkolom (bulletsImageTextColumnFraction) — bij 40% beeld vijf i.p.v. acht bullets per pagina, met kMinPageBullets als vloer. Het uniforme-run-invariant (slide.dart:392) blijft ongemoeid; de run wint grootte doordat de volste pagina minder draagt. Beeldkeuring op het reproductiegeval (Proefpresentatie.md slide 5): na splitsen 20% -> 52% ontwerpgrootte op de maatgevende pagina, leesbaar, en de run rendert uniform. De keurder meende dat zelfs de volste pagina maar ~60% vulde en wees naar een render-hoogteschatter die zou overschatten. Dat heb ik met een echte widget-rendermeting getoetst: een werkelijk volle pagina (5 lange bullets) vult 94% van de tekstkolomhoogte op zijn eigen fit-schaal (solo-schaal 0.71). Er is dus GEEN schatteroverschatting; de lege ruimte op de dunnere pagina's is de bewuste prijs van de uniforme run (je kunt niet elke pagina vullen en tegelijk één grootte houden). Het paginadoel verder verlagen helpt niet — dat maakt alleen meer pagina's. Aparte observatie van de keuring, GEEN onderdeel van deze fix: na een aanbevolen splitsing verzesvoudigt het aantal tekstdichtheid-waarschuwingen (elke deel-slide meldt lange bullets/veel woorden). Terecht in inhoud maar het voelt als straf op gehoorzaamheid; kandidaat voor een eigen issue. Regressietests eerst-rood; docs bijgewerkt. Poort draait; sluit ik na groen + merge op main.
Author
Owner

Gemerged op main via PR #1292 (merge-commit abcfab80). Richting B: paginadoel schaalt mee met de tekstkolom naast de afbeelding. Geverifieerd op main met make check (groen, 8641 tests, geen golden-drift). Niet in deze fix: de aparte observatie dat een aanbevolen splitsing het aantal kwaliteitswaarschuwingen laat oplopen — apart ingediend als #1289.

Gemerged op main via PR #1292 (merge-commit abcfab80). Richting B: paginadoel schaalt mee met de tekstkolom naast de afbeelding. Geverifieerd op main met make check (groen, 8641 tests, geen golden-drift). Niet in deze fix: de aparte observatie dat een aanbevolen splitsing het aantal kwaliteitswaarschuwingen laat oplopen — apart ingediend als #1289.
Author
Owner

Opgelost op main (squash van PR #1291). Aparte per-pagina-drempel kSplitImageColumnBulletTarget=5 voor de bulletsImage-split (i.p.v. 8): de smalle tekstkolom naast de afbeelding raakt niet overvol, de gedeelde split-schaal komt omhoog en de ruimte wordt benut — uniforme-schaal-invariant intact (shared_split_fit_scale_test groen). Regressietest tegen echte render, eerst-rood bewezen (16,12pt met oude drempel faalt de vloer). CI groen. Empirisch: 8/pag→schaal ~0.71, 5/pag→~0.79 (pagina's 8→12).

Opgelost op main (squash van PR #1291). Aparte per-pagina-drempel kSplitImageColumnBulletTarget=5 voor de bulletsImage-split (i.p.v. 8): de smalle tekstkolom naast de afbeelding raakt niet overvol, de gedeelde split-schaal komt omhoog en de ruimte wordt benut — uniforme-schaal-invariant intact (shared_split_fit_scale_test groen). Regressietest tegen echte render, eerst-rood bewezen (16,12pt met oude drempel faalt de vloer). CI groen. Empirisch: 8/pag→schaal ~0.71, 5/pag→~0.79 (pagina's 8→12).
Author
Owner

Correctie op mijn vorige reactie: #1279 is niet via PR #1291 geland maar via PR #1292 (tak fix/1279-split-fit-scale, merge abcfab80) — een parallelle sessie landde dezelfde richting-B-fix ('paginadoel volgt de tekstkolom naast de afbeelding') net eerder, met eigen regressietest. Mijn PR #1291 (fix/1279-split-autofit) is daardoor een duplicaat en sluit ik. Het issue blijft terecht gesloten.

Correctie op mijn vorige reactie: #1279 is niet via PR #1291 geland maar via **PR #1292** (tak fix/1279-split-fit-scale, merge abcfab80) — een parallelle sessie landde dezelfde richting-B-fix ('paginadoel volgt de tekstkolom naast de afbeelding') net eerder, met eigen regressietest. Mijn PR #1291 (fix/1279-split-autofit) is daardoor een duplicaat en sluit ik. Het issue blijft terecht gesloten.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
LibreKAT/Ocideck#1279
No description provided.