feat(import): presentaties uit PowerPoint, Keynote en Impress (#772) #807

Merged
brenno merged 6 commits from feat/keiko-presentatie-import-772 into main 2026-07-24 16:35:25 +00:00
Owner

Importeren van PowerPoint (.pptx), LibreOffice Impress (.odp) en Apple Keynote (.key) naar echte OciDeck-decks. Sluit aan op de bestaande Uitbreidingen-module Importeren (#789): het menu-item hangt achter dezelfde importModuleRevealProvider als de OpenKAT-bron ernaast.

Sluit #772.

Waar dit vandaan komt

Het losse project Keiko deed dit al, maar met een eigen Markdown-writer. Die is bewust niet meegekomen — dat contract heeft OciDeck al. De import bouwt Slide/Deck via Slide.create + copyWith en laat MarkdownService/FileService serialiseren.

Geen enkele nieuwe afhankelijkheid. Keiko's pubspec noemde protobuf, maar dat pakket wordt nergens geïmporteerd: de IWA-laag heeft een eigen, schema-vrije wire-decoder. pubspec.yaml is onaangeroerd, dus geen SBOM-ronde.

Wat het doet

  • Geen één-op-één-kopie, en dat staat vooraf. OciDecks diamodel is eenvoudiger; wat niet past wordt een zichtbare "niet overgenomen"-notitiedia. Een eenmalige waarschuwing zegt dat vóór de import, en raadt een aparte importmap aan omdat de conversiekwaliteit per bron verschilt.
  • Grote lijsten en tabellen worden niet gesnoeid. Boven 8 bullets of 12 tabelrijen blijft álle data in het deck en wordt alleen de wéérgave begrensd (#672), met een telling erbij. Bewust op bronvolgorde: een importeur heeft geen grond om te bepalen wat de belangrijkste rijen zijn.
  • Eén bestand opent in een tab; meerdere gaan door een wachtrij met een eigen doelmap, voortgang per bestand en een samenvatting. Een mislukt bestand stopt de rij niet.
  • Werkt op bytes — geen dart:io in de servicelaag — dus ook in de browserversie, op de bulkroute na (die schrijft bestanden).

De bewaker keurde de eerste versie af

Terecht: de techniek deugde, de belofte niet. Wat daaruit is gerepareerd, elk met een test:

  • Video verdampte. De bytes werden weggegooid en het deck hield een pad over naar een bestand dat nooit is weggeschreven. Ingebedde media reist nu als mem:-pad mee; copyMediaToProject kreeg de route die de afbeeldingskant al had.
  • De bouwer gooide zwijgend weg — een inleidende alinea boven een bullet-lijst, de derde afbeelding, opsommingspunten naast een tabel, en álle afbeeldingen op een vrije-Markdown-dia. Nu komt elk geval op de notitiedia, mét een tegenproef per geval zodat we geen verlies mélden dat er niet is.
  • Een zachte regelafbreking vrat tekst. PowerPoints <a:br/> werd een \n midden in een bullet; na opslaan en heropenen verhuisde de vervolgregel of verdween hij. Gevonden door de nieuwe rondgangstest (bouwen → serialiseren → terugparsen), die er niet was.
  • Bijlagenamen kwamen ongefilterd uit het archief: rapport.pdf.command landde zo in de projectmap. Beeld heeft nu dezelfde witte lijst als video.
  • Een geneutraliseerde koppeling noemt nu zijn oorspronkelijke doel, en de Keynote-notitie beweerde ten onrechte dat tabellen en grafieken niet overkwamen.

Drie fouten die géén poort zag zijn onderweg opgelost: de zip-bom-melding drukte de hele bytelijst af, safe_extensions/importer_logger waren dode resp. dubbele code, en archive 4.x geeft op rommel een leeg archief in plaats van te struikelen — waardoor een kapot bestand las als "geen dia's gevonden". Dat laatste is bij de bron geweigerd, dus alle drie de formaten profiteren.

Wat er bewust niet in zit

  • #806 — foutmeldingen en notitiedia-tekst zijn Nederlands ongeacht de taal. Echte fout, maar vraagt eigen ontwerpkeuzes (reden-codes, een vertaalnaad in de bouwer) plus een vertaalronde. De bewaker vroeg om een vastgelegd besluit in plaats van stilzwijgen; dat issue ís dat besluit.
  • #803 — sneltoetsteksten ontsnappen aan de vertaalpoort. Bestond al, kwam boven bij een refactor hier.
  • Themakleuren komen niet over. De parsers leveren een SourceTheme, maar niemand leest hem; een geïmporteerd deck krijgt OciDecks eigen opmaak. Zo staat het ook in de gids.

Eerlijk over de beproeving

Alle testfixtures bouwen hun archief zelf. Er staat geen enkel bestand in de repo dat PowerPoint, Impress of Keynote werkelijk heeft weggeschreven, dus de parsers zijn tegen nagebouwde invoer beproefd en niet tegen de echte wereld. Dat staat als punt 11 in docs/design/VERIFICATION.md.

Poort

make check groen (exit 0), 6500 tests, dekking 86,5%, geen basislijn verhoogd. Eén refactor was nodig om onder het klasseplafond te blijven: _exportGate is top-level gemaakt, volgens het precedent dat in dat bestand al stond.

Docs: USER_GUIDE, FAQ, KNOWN_LIMITATIONS, CHANGELOG, SOURCE_MAP en VERIFICATION.

🤖 Generated with Claude Code

Importeren van **PowerPoint (.pptx)**, **LibreOffice Impress (.odp)** en **Apple Keynote (.key)** naar echte OciDeck-decks. Sluit aan op de bestaande Uitbreidingen-module *Importeren* (#789): het menu-item hangt achter dezelfde `importModuleRevealProvider` als de OpenKAT-bron ernaast. Sluit #772. ## Waar dit vandaan komt Het losse project **Keiko** deed dit al, maar met een eigen Markdown-writer. Die is bewust **niet** meegekomen — dat contract heeft OciDeck al. De import bouwt `Slide`/`Deck` via `Slide.create` + `copyWith` en laat `MarkdownService`/`FileService` serialiseren. **Geen enkele nieuwe afhankelijkheid.** Keiko's pubspec noemde `protobuf`, maar dat pakket wordt nergens geïmporteerd: de IWA-laag heeft een eigen, schema-vrije wire-decoder. `pubspec.yaml` is onaangeroerd, dus geen SBOM-ronde. ## Wat het doet - **Geen één-op-één-kopie, en dat staat vooraf.** OciDecks diamodel is eenvoudiger; wat niet past wordt een zichtbare "niet overgenomen"-notitiedia. Een eenmalige waarschuwing zegt dat vóór de import, en raadt een aparte importmap aan omdat de conversiekwaliteit per bron verschilt. - **Grote lijsten en tabellen worden niet gesnoeid.** Boven 8 bullets of 12 tabelrijen blijft álle data in het deck en wordt alleen de wéérgave begrensd (#672), met een telling erbij. Bewust op bronvolgorde: een importeur heeft geen grond om te bepalen wat de belangrijkste rijen zijn. - **Eén bestand** opent in een tab; **meerdere** gaan door een wachtrij met een eigen doelmap, voortgang per bestand en een samenvatting. Een mislukt bestand stopt de rij niet. - Werkt op bytes — geen `dart:io` in de servicelaag — dus ook in de browserversie, op de bulkroute na (die schrijft bestanden). ## De bewaker keurde de eerste versie af Terecht: de techniek deugde, de belofte niet. Wat daaruit is gerepareerd, elk met een test: - **Video verdampte.** De bytes werden weggegooid en het deck hield een pad over naar een bestand dat nooit is weggeschreven. Ingebedde media reist nu als `mem:`-pad mee; `copyMediaToProject` kreeg de route die de afbeeldingskant al had. - **De bouwer gooide zwijgend weg** — een inleidende alinea boven een bullet-lijst, de derde afbeelding, opsommingspunten naast een tabel, en álle afbeeldingen op een vrije-Markdown-dia. Nu komt elk geval op de notitiedia, mét een tegenproef per geval zodat we geen verlies mélden dat er niet is. - **Een zachte regelafbreking vrat tekst.** PowerPoints `<a:br/>` werd een `\n` midden in een bullet; na opslaan en heropenen verhuisde de vervolgregel of verdween hij. Gevonden door de nieuwe rondgangstest (bouwen → serialiseren → terugparsen), die er niet was. - **Bijlagenamen kwamen ongefilterd uit het archief**: `rapport.pdf.command` landde zo in de projectmap. Beeld heeft nu dezelfde witte lijst als video. - Een geneutraliseerde koppeling noemt nu zijn oorspronkelijke doel, en de Keynote-notitie beweerde ten onrechte dat tabellen en grafieken niet overkwamen. Drie fouten die géén poort zag zijn onderweg opgelost: de zip-bom-melding drukte de hele bytelijst af, `safe_extensions`/`importer_logger` waren dode resp. dubbele code, en `archive` 4.x geeft op rommel een *leeg* archief in plaats van te struikelen — waardoor een kapot bestand las als "geen dia's gevonden". Dat laatste is bij de bron geweigerd, dus alle drie de formaten profiteren. ## Wat er bewust niet in zit - **#806** — foutmeldingen en notitiedia-tekst zijn Nederlands ongeacht de taal. Echte fout, maar vraagt eigen ontwerpkeuzes (reden-codes, een vertaalnaad in de bouwer) plus een vertaalronde. De bewaker vroeg om een vastgelegd besluit in plaats van stilzwijgen; dat issue ís dat besluit. - **#803** — sneltoetsteksten ontsnappen aan de vertaalpoort. Bestond al, kwam boven bij een refactor hier. - **Themakleuren komen niet over.** De parsers leveren een `SourceTheme`, maar niemand leest hem; een geïmporteerd deck krijgt OciDecks eigen opmaak. Zo staat het ook in de gids. ## Eerlijk over de beproeving Alle testfixtures bouwen hun archief **zelf**. Er staat geen enkel bestand in de repo dat PowerPoint, Impress of Keynote werkelijk heeft weggeschreven, dus de parsers zijn tegen nagebouwde invoer beproefd en niet tegen de echte wereld. Dat staat als punt 11 in `docs/design/VERIFICATION.md`. ## Poort `make check` groen (exit 0), 6500 tests, dekking 86,5%, geen basislijn verhoogd. Eén refactor was nodig om onder het klasseplafond te blijven: `_exportGate` is top-level gemaakt, volgens het precedent dat in dat bestand al stond. Docs: `USER_GUIDE`, `FAQ`, `KNOWN_LIMITATIONS`, `CHANGELOG`, `SOURCE_MAP` en `VERIFICATION`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Het presentatie-importsubsysteem uit het losse Keiko-project, herbouwd op
OciDecks eigen model. Parsers voor .pptx, .odp en .key (de Keynote-IWA-laag
met snappy en een eigen, schema-vrije protobuf-wire-decoder), een
format-neutraal tussenmodel, een classifier die op OciDecks échte SlideType
mikt, en een deck-builder die `Slide.create` + copyWith gebruikt — Keiko's
eigen Markdown-writer is bewust NIET meegekomen, want dat contract heeft
OciDeck al. Alles werkt op bytes, dus zonder `dart:io` en dus ook op web.

**Geen enkele nieuwe afhankelijkheid**: Keiko's pubspec noemde `protobuf`,
maar dat pakket wordt nergens geïmporteerd.

Bewust geen één-op-één-kopie: wat niet in OciDecks eenvoudiger diamodel past
wordt een zichtbare "niet overgenomen"-notitiedia, en een eenmalige
waarschuwing zegt dat vooraf. Grote lijsten en tabellen worden niet gesnoeid
maar krijgen een niet-destructieve weergavelimiet (#672): de gegevens blijven,
alleen de weergave is begrensd. Eén bestand opent in een tab; meerdere
bestanden gaan door een wachtrij met een eigen doelmap, waarbij de interface
aanraadt die apart te houden omdat de conversiekwaliteit per bron verschilt.

Sluit aan op de bestaande module Importeren (#789): het menu-item hangt achter
dezelfde `importModuleRevealProvider` als de OpenKAT-bron ernaast.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Menu-actie, waarschuwing, wachtrijdialoog en de resultaatmeldingen. De
terminologie volgt per taal wat de repo al gebruikte voor "dia", "map" en
"presentatie", zodat de nieuwe teksten niet naast de bestaande gaan staan.

Twee dingen om te weten: de telbare fragmenten (bv. "3 dia's") staan zonder
pluralisatie in de zin, dus talen met getalsafhankelijke naamvallen dragen de
vorm die bij de meeste getallen klopt — echt oplossen vraagt pluralisatie in
de interface, geen betere vertaling. En tlh is best-effort in het bestaande
register van dat bestand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De klasse-ratchet viel: het menu-item en de dispatch van de
presentatie-import brachten `_MainLayoutState` op 1458 regels, negen
boven haar plafond van 1449.

`_exportGate` is zuiver — deckState, readiness, quality en l10n komen
allemaal als parameter binnen, er wordt niets van de State gelezen — dus
staat ze nu top-level in hetzelfde bestand, met dezelfde afweging als
`_createDeckFromDialog` er vlak onder. De klasse zakt daarmee naar 1422;
het plafond blijft staan waar het stond.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
USER_GUIDE krijgt een sectie bij de importmodule: wat er wel en niet overkomt,
waarom een aparte map, en het verschil tussen één bestand en een wachtrij. Ook
de browsertabel klopt weer (de bulkroute schrijft bestanden en kan daar dus
niet). FAQ noemt de drie formaten mét de asymmetrie: pptx gaat er ook uit,
.key en .odp niet. KNOWN_LIMITATIONS zegt dat importeren een conversie is en
geen kopie.

VERIFICATION krijgt er een punt bij, en dat is de eerlijkste regel van deze
wijziging: alle testfixtures bouwen hun archief zélf: er staat geen enkel
bestand in de repo dat PowerPoint, Impress of Keynote werkelijk heeft
weggeschreven. De parsers zijn dus tegen nagebouwde invoer beproefd, niet
tegen de echte wereld.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De bewaker keurde deze tak af, en terecht: de techniek deugde maar de belofte
niet. Vijf dingen, elk met een test.

**Video verdampte.** `SourceVideo.bytes` droeg de film, maar de bouwer zette
alleen `media/clip.mp4` in het deck en materialiseerde niets — een pad naar een
bestand dat nooit is weggeschreven, terwijl de bron na de import weg was.
Ingebedde video reist nu als `mem:`-pad mee, net als beeld; `copyMediaToProject`
kreeg daarvoor dezelfde route die de afbeeldingskant al had.

**De bouwer gooide zwijgend weg.** De classifier meldde wat híj niet kwijt kon,
maar daarna sneuvelden een inleidende alinea boven een bullet-lijst, de derde
afbeelding, opsommingspunten naast een tabel — zonder één woord. Dat is precies
de belofte waarop deze functie verkocht wordt. Nu komt elk van die gevallen op
de notitiedia, met een tegenproef per geval zodat we geen verlies mélden dat er
niet is. Onderweg bleek een vrije-Markdown-dia álle afbeeldingen te laten
vallen; die telt nu mee.

**Een zachte regelafbreking vrat tekst.** PowerPoints `<a:br/>` werd een `\n`
midden in een bullet; na opslaan en heropenen verhuisde de vervolgregel naar
een ander veld, en bij een tweede zo'n punt verdween hij. Bullets zijn nu één
regel — de afbreking gaat verloren, de woorden niet.

**Een geneutraliseerde koppeling liet geen spoor na.** Het schema onschadelijk
maken blijft, maar het doel wordt nu in de notitie genoemd; anders ziet de
gebruiker niet eens wát er stond.

**Bijlagenamen kwamen ongefilterd uit het archief.** `rapport.pdf.command`
landde zo in de projectmap. Beeld heeft nu dezelfde witte lijst als video, en
het commentaar dat het tegendeel beweerde is rechtgezet.

Plus: de Keynote-notitiedia beweerde dat tabellen, grafieken en media niet
overkwamen terwijl de reconstructor ze wel degelijk invult. En er is eindelijk
een rondgangstest — bouwen, serialiseren, terugparsen — want dat is de enige
toets die bewijst dat de gebruiker met zijn .md verder kan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(media): audio erfde de video-terugval, en de kernfix was ongedekt (#772)
All checks were successful
scans / scans (pull_request) Successful in 3m12s
77b6b88389
De dekkingsmeting wees uit dat juist de reparatie van de vorige commit — het
materialiseren van een ingebedde `mem:`-video naar de `media/`-map — door geen
enkele test werd geraakt, terwijl de boodschap "elk met een test" beloofde.
Nu wél, tot op schijf: de bytes staan er, met de goede naam.

Dat legde meteen een tweede fout bloot. `_mediaToProject` gaf video én audio
dezelfde terugval mee, dus een audiobestand zonder extensie in de store landde
als `.mp4`. De terugval is nu per soort.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brenno merged commit e3e3c29b7a into main 2026-07-24 16:35:25 +00:00
brenno deleted branch feat/keiko-presentatie-import-772 2026-07-24 16:35:26 +00:00
Sign in to join this conversation.
No description provided.