fix: 26 openstaande issues — stijlreis, exports, prestatie, stabiliteit #2664

Merged
brenno merged 40 commits from feat/batch-openstaande-issues into main 2026-10-29 12:18:50 +00:00
Owner

Samenvatting

Eén gebundelde reparatieronde over de ongeclaimde issue-lijst:

  • Stijlreis (#2640-#2645, #2648, #2649): documentLogoPath door de hele project- en pakketketen, donker logo gematerialiseerd, botsingsvrije bestandsnamen, documentBodyFontSize in DOCX/ODT, web-logokiezers via mem:-assets, Annuleren herstelt bewerkkopieën, ocideck-style.json-sidecar volgens FILE_FORMAT.md.
  • Prestatie (#2619-#2626, #2629, #2633, #2634, #2636): decodegrenzen per oppervlak, per-dia memo's via Expando/IdentitySlotMemo, exports en scans uit de UI-isolate, debounced Quill→Markdown, sync-io-poort en schaaltoetsen.
  • Stabiliteit (#2628, #2631, #2632, #2635): atomaire instellingen met zelfherstel, best-effort-afsluitketen, TextPainter-disposes, AI-statustimer.
  • Windows (#2627, #2630): single-instance via mutex + WM_COPYDATA, VC++-runtime meegeleverd + rookproef — niet op hardware getest, eerlijk gedocumenteerd in KNOWN_LIMITATIONS.
  • Docs: USER_GUIDE, KNOWN_LIMITATIONS, CHANGELOG, PERFORMANCE_GUIDE, SOURCE_MAP bijgewerkt.

Parallele sessies landden #2621/#2622/#2625/#2635, #2616 en #2640 ondertussen zelf op main; de merge houdt waar mogelijk de gelande variant aan en bewaakt wat onze kant extra dekt (budgetgrens om carried style, deckcontainment in de beeldlader, relatieve DOCX-koppen).

Waarden-weging (bewaker)

De stijlsidecar zit naast de markdown (vorm 4): het .md in een pakket blijft pure Marp — tool/marp-check rendert het nog steeds zonder OciDeck. Geen nieuwe afhankelijkheid; geen nieuw uitgaand verkeer. #2644 versterkt uitwisselbaarheid juist: stijl reist mee i.p.v. stil te verdwijnen.

Verificatie

make check-full groen op commit 87ebb40e30 — formatting, analyze --fatal-infos, conventies, privacygrens, 14.888 tests + 1.238 pakkettests, dekking, 83 goldens, l10n (0 wezen), gitleaks/trufflehog, semgrep (0), shellcheck, SBOM-versheid, bundled-JS, deps (CWE 5.0 actueel), marp-check.

Bekende restpunten: follow-up #2657 (Windows-metingen op echte hardware) en #2662 (verwijderknop stijlprofiel dood bij één profiel).

Generated with Devin

## Samenvatting Eén gebundelde reparatieronde over de ongeclaimde issue-lijst: - **Stijlreis** (#2640-#2645, #2648, #2649): documentLogoPath door de hele project- en pakketketen, donker logo gematerialiseerd, botsingsvrije bestandsnamen, documentBodyFontSize in DOCX/ODT, web-logokiezers via mem:-assets, Annuleren herstelt bewerkkopieën, ocideck-style.json-sidecar volgens FILE_FORMAT.md. - **Prestatie** (#2619-#2626, #2629, #2633, #2634, #2636): decodegrenzen per oppervlak, per-dia memo's via Expando/IdentitySlotMemo, exports en scans uit de UI-isolate, debounced Quill→Markdown, sync-io-poort en schaaltoetsen. - **Stabiliteit** (#2628, #2631, #2632, #2635): atomaire instellingen met zelfherstel, best-effort-afsluitketen, TextPainter-disposes, AI-statustimer. - **Windows** (#2627, #2630): single-instance via mutex + WM_COPYDATA, VC++-runtime meegeleverd + rookproef — niet op hardware getest, eerlijk gedocumenteerd in KNOWN_LIMITATIONS. - **Docs**: USER_GUIDE, KNOWN_LIMITATIONS, CHANGELOG, PERFORMANCE_GUIDE, SOURCE_MAP bijgewerkt. Parallele sessies landden #2621/#2622/#2625/#2635, #2616 en #2640 ondertussen zelf op main; de merge houdt waar mogelijk de gelande variant aan en bewaakt wat onze kant extra dekt (budgetgrens om carried style, deckcontainment in de beeldlader, relatieve DOCX-koppen). ## Waarden-weging (bewaker) De stijlsidecar zit naast de markdown (vorm 4): het .md in een pakket blijft pure Marp — tool/marp-check rendert het nog steeds zonder OciDeck. Geen nieuwe afhankelijkheid; geen nieuw uitgaand verkeer. #2644 versterkt uitwisselbaarheid juist: stijl reist mee i.p.v. stil te verdwijnen. ## Verificatie make check-full groen op commit 87ebb40e30df3994d62af7e20e74183b5951ea21 — formatting, analyze --fatal-infos, conventies, privacygrens, 14.888 tests + 1.238 pakkettests, dekking, 83 goldens, l10n (0 wezen), gitleaks/trufflehog, semgrep (0), shellcheck, SBOM-versheid, bundled-JS, deps (CWE 5.0 actueel), marp-check. Bekende restpunten: follow-up #2657 (Windows-metingen op echte hardware) en #2662 (verwijderknop stijlprofiel dood bij één profiel). Generated with [Devin](https://devin.ai)
Vier bevindingen die allemaal in dezelfde opslagketen zaten:

- #2645: logo en merkstrook met dezelfde bestandsnaam overschreven elkaar.
  Alle vier de profielafbeeldingen gaan nu via één gedeelde kopieerroute met
  inhoudsbewuste naamsuffixen (resolveAssetDestination).
- #2642: het donkere presentatielogo werd nooit in de projectmap
  gematerialiseerd; na verhuizen van een project was het weg. Relatieve
  paden resolven nu tegen het brondproject bij 'Opslaan als'.
- #2641: een afwijkend documentlogo verdween bij opslaan en zat niet in
  het pakket. Het veld rijdt nu mee in logos/, met de driewaardige
  betekenis behouden (null = gedeeld, '' = uit).
- #2644: een .ocideck-pakket opende met het actieve profiel van de
  ontvanger in plaats van de stijl van de maker. Het pakket draagt nu een
  theme.json-record (dezelfde vorm als een OciServe-lespakket) dat bij het
  openen geldt voor díe presentatie — op schijf via de uitgepakte map, in
  het geheugen via mem:-assets. Afbeeldingsvelden worden binnen de
  pakketgrens opgelost en de afsluitslide blijft uit (onvertrouwde invoer).

Regressietests in test/profile_image_persistence_test.dart dekken de
botsing, het meereizen van donker en documentlogo, de null/''-betekenis
en de pakket-stijlroundtrip.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De huisstijl kende documentBodyFontSize (9–28 pt, standaard 11) en de
schermweergave, HTML- en PDF-export volgden die al. De twee
office-exports niet: Word kreeg altijd 11 pt, LibreOffice 100% van zijn
eigen default.

- DOCX: docDefaults draagt de maat in halve punten; koppen en
  brontekst schalen mee via documentFontScale, zodat hun relatieve
  maten behouden blijven.
- ODT: de default-paragraafstijl draagt de maat absoluut in punten;
  koppen en vooropgemaakte tekst blijven percentages en volgen de
  body vanzelf.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Op web gaven de logokiezers stilletjes niets terug (blob:-paden) en stond
de hele sectie verborgen. De kiezers gebruiken nu dezelfde byte-route als
elke afbeelding (ImageService.pickImageDetailed): gevalideerd, op
naam-getoond en meegenomen in een pakketexport. De sweep behield alleen
mem:-beelden van slides en het lichte logo; nu ook donker logo,
documentlogo, merkstrook én de beelden van opgeslagen stijlprofielen.
Aanmaken, importeren en verwijderen van een stijlprofiel schreven direct
naar de provider; de documentopmaak-sectie deed dat via eigen setters.
Annuleren sloot het venster maar liet een afgedwongen stijl of gewijzigde
marge gelden. Beide werken nu op de bewerkkopie van het venster — pas
Opslaan schrijft de profielverzameling (via de nieuwe
SettingsNotifier.setThemeProfiles) en de zeven documentvelden weg.
onDispose annuleerde de periodieke timer maar liet hem gezet, dus ??=
maakte na een herbouw nooit een nieuwe — het lampje stond stil tot een
klik of herstart. Nu cancel + null, de timer wordt per build aangemaakt
en de callback toetst de actuele generatie.
Elke TextPainter houdt een native ui.Paragraph vast; de meetfuncties
en de eenmalige painters in de chart-/cockpit-/scene-previews gaven
die niet vrij en bouwden zo native-geheugendruk op bij tabellen en
veel previews. Meting gebeurt nu onder try/finally, en tableBlockHeight
onthoudt identieke cellen binnen een meetronde zodat de letterzoektocht
minder paragrafen maakt.
Elke opruimstap vóór quitApp is nu begrensd en foutdovend: lessessies
3 s per sessie via boundedQuitStep, opname-stop 5 s intern in
stopRecordingBeforeQuit, de call-leave had al 5 s. Duurt de keten toch
langer dan 5 s, dan mag de gebruiker de rest overslaan — opgeslagen
werk is dan al veilig.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De 50 ms-tik schreef _progress en session.progress in de widgetstaat,
waardoor 20x per seconde de complete cockpit herbouwde. De voortgang en
restantijd leven nu in ValueNotifiers; alleen de voortgangsbalk en het
klokblok herbouwen mee via ValueListenableBuilder. De timed-sessie
emitt eert snapshots nog alleen bij echte feiten (diawissel, fase,
countdown) zodat _syncAudience gedrag gelijk blijft. clock.now() maakt
de verstreken tijd in tests deterministisch naast de fake timers.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De index-cache zat op de lijst, dus elke toetsaanslag (nieuwe lijst)
mat alle runleden opnieuw — 136–535 ms per toets bij grote decks. De
lidmeting zit nu in een Expando<Slide> op dia-identiteit met sleutel
(thema-identiteit, font, diaverhouding): een gewijzigde dia mist, de
rest hergebruikt zijn meting. De kwaliteitsanalyse deelt dezelfde
Expando en hoeft geen eigen cache meer te houden.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Drie globale LRU's (privacyscan, render-layout, rich-text-plan)
verdreven bij een sequentiële scan boven de capaciteit elke waarde net
voor hergebruik — 100% misses boven ~512 dia's, een klif van 0,4 naar
386 ms per toetsaanslag. Nu hangen de memo's via Expando aan de dia:
identiteit als wijzigingsstempel, levensduur aan de dia gekoppeld,
geen plafond meer. Per dia een klein geometrie-mapje (max. 6) voor de
paar breedtes waarop een dia voorkomt.

De dia-index is uit de scansleutel: scanSlide stempelt op een sentinel
en memoizedSlideScan schrijft de echte index er bij het samenvoegen op
via PrivacyFinding.reindexed — een invoeging bovenaan ongeldigt geen
enkele scan meer.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Het renderpad deed twee synchrone resolveSymbolicLinksSync per afbeelding
per build — duur op Windows (Defender, OneDrive, netwerk-mappen). De check
zit nu in de loadBytes-grens van CappedImage: één async realpath per decode,
en de lezer opent het reële doelpad waarmee ook de check-tot-open-kier dicht
is. Een ontsnappende symlink werpt ProjectEscapeException, die dezelfde
outsideDeck-placeholder toont als voorheen.

boundedFileImage is nu een CappedImage met decodeEdge (zelfde altijd-
concreet-doel-semantiek als de vorige ResizeImage), zodat ook de miniatuur-
route de bewaakte lezer deelt. Audio/video houden resolveContainedRealPath:
dat loopt eenmalig per controller-init, niet per frame.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
SlidePreviewWidget berekent bij gebrek aan een expliciete decodeMaxEdge nu
zelf de grens: LayoutBuilder-breedte × devicePixelRatio, omhoog afgerond op
de vaste treden 1024/2048/4096 zodat de cachesleutel stabiel blijft. De
preview, presentator en beamer decoden daarmee niet langer op de
4096-decodeerbom-grens — een 24 MP-foto kostte ~45 MB per oppervlak, en de
beamer draait als eigen engine met eigen imageCache nog eens extra.

De grens houdt de cappedDecodeTarget-semantiek (natieve decode onder de
cap), dus geanimeerde GIF's blijven animeren. De export krijgt de trede die
bij zijn targetWidth hoort; de presenter-precache warmt dezelfde sleutel
als de render.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Op Windows toont het venster pas bij het eerste frame; alles wat ervoor op
de UI-isolate draait is onzichtbaar wachten. Drie grenzen aangepast:

- pruneStale is nu volledig async (dir.list/stat i.p.v. listSync/statSync)
  en start pas ná het eerste frame, samen met de git-zandbak en de
  rechtenbeperking — huishouding, geen voorwaarde om te kunnen werken.
- Het gezondheidslexicon (~2 MB, 63k termen) ontleedt en indexeert in een
  Isolate.run; het laden start alvast aan het begin van main en wordt pas
  vlak voor runApp geawait, zodat de lexicon-garantie overeind blijft.
- Een opstartwaakhond logt wanneer het eerste frame na 10 s nog niet
  bereikt is, zodat een trage start niet meer van een crash te
  onderscheiden is.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Drie bronnen van per-aanslag-werk weggenomen:

- De deck- en documentluisteraars van TabsNotifier zetten bij élke emissie
  een nieuwe tabs-toestand — dus herbouwde bij elke letter de hele
  AppShell. Nu alleen nog wanneer een tabfeit verandert: label, vuil-stip,
  open-status of bestandspad.
- AppPlatformMenuBar bouwde daardoor ook per aanslag de menu's opnieuw en
  serialiseerde ze over het platformkanaal naar macOS. De balk bewaart de
  items nu en bouwt alleen bij een veranderde signatuur (taal, welke items
  een handler dragen, vlaggen én de deck-callbacks zelf — die zijn per
  tabblad ander werk).
- SlideListPanel berekende deckScopeCiaIndex per zichtbare miniatuur
  opnieuw (O(dia's) elk). Deckbrede waarden lopen nu één keer per build,
  en miniatuur-widget-instanties worden per dia gecachet op hun
  invoer-signatuur: een ongewijzigde dia herbouwt helemaal niet.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De terugschrijf van de visuele stand naar de bron zat integraal in de
aanslag en was superlineair in documentlengte (~256 ms bij 300 pagina's).
Drie maatregelen:

- Het documentoppervlak zet nu na een rustpauze van 350 ms om, via
  flushPendingVisualEdit(). Elke lezing van de broncontroller (opslaan,
  autosave, export, zoeken) flust via een haak op
  MarkdownSourceController.value zelf het uitstaande werk — geen losse
  flush-punten per aanroep. Discrete bewerkingen (invoegpalet, werkbalk,
  knippen) flushen meteen; kleine vlakken zoals notities syncen per
  aanslag zoals voorheen.
- markdownFromDocument loopt via convertDocument(Document): de aanroeper
  heeft de boom al, dus geen tweede Document.fromDelta meer.
- De genummerde-lijst-voornummering wordt één keer per omzetting berekend
  (één gang door het document) in plaats van per lijstitem het hele
  document terug te lopen. Zelfde semantiek, inclusief de #1925-regel dat
  ingesprongen niet-lijstregels lijstitem-inhoud zijn.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Op Windows en Linux herschrijft shared_preferences alles in-place in één
JSON-bestand, synchroon op de UI-isolate. Een afgebroken schrijfbeurt liet
een half bestand achter waar elke volgende getInstance() een
FormatException op gooide: toestemming elke start opnieuw, instellingen
weg, elke opslag mislukt.

AtomicFilePreferencesStore vervangt de platform-store op die twee
desktops: zelfde map, bestandsnaam en JSON-vorm, maar elke mutatie
schrijft via writeStringAtomic (tijdelijk bestand + rename) en een
onleesbaar bestand krijgt een .corrupt-<tijdstip>-naam waarna leeg wordt
verdergegaan — quarantaine in plaats van falen, met één melding naar de
gebruiker via settingsCorruptionNoticeProvider. Lezen en schrijven zijn
nu ook echt async.

Omdat de opslag nooit meer een FormatException gooit, vangt
SettingsNotifier._load() resterende leesfouten expliciet af en logt ze,
in plaats van ze naar de zone-handler te sturen.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De gezichtsscan is de duurste stap van de kwaliteitscontrole en draaide
synchroon (decode + YuNet op drie schalen) op de UI-thread — terwijl de
provider bovendien aan het hele deck hing en bij élke toetsaanslag
opnieuw startte.

- _IsolateImageFaceScanner: één lang levende werk-isolate met het model
  geladen, verzoeken serieel over een SendPort, bytes via
  TransferableTypedData. De native probe blijft op de UI-isolate; een
  niet-startende of hangende worker (90 s per beeld) faalt dicht op
  unreadable, nooit stil op nul en nooit terug naar "draai het dan maar
  op de UI-thread".
- computeImagePrivacyRawIssues volgt nu een sleutel over het
  beeldmateriaal (projectpad + per afbeelding index/veld/pad) in plaats
  van het deck: alleen-tekst wijzigingen starten geen scanronde meer —
  ook geen stat() per plaatje.
- ref.mounted-check in de lus: een vervangen of gestopte berekening
  meet niet verder voor niets.
- De memo gaat van LRU(256) naar LRU(8192); een gememoiseerde waarde is
  int + bool en een groot deck mocht zijn eigen inzendingen niet wegduwen.
- Gereduceerd decoderen: IMREAD_REDUCED_COLOR_2/4/8 op basis van de kop,
  zodat een 24 MP-foto nooit volledig in het geheugen komt — de fijnste
  detectieschaal is toch 1920.
- imageFaceScanProgressProvider (per tab gescoped) meldt "x van y"; het
  kwaliteitspaneel toont die rij zolang de scan loopt, zodat de
  export-gate niet stil wacht.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Een groot document exporteren bevroor de app tot de export klaar was:
geen voortgang, geen annuleren, en op Windows "reageert niet".

- Document-PDF: opmaak (twee rondes) + document.save() draait nu in
  Isolate.run. De te-brede-tabellen komen als retourwaarde mee terug —
  een callback zou in de gekopieerde isolate-omgeving muteren en de
  teller zou stil op nul blijven.
- DOCX/ODT/EPUB: ZipEncoder().encodeBytes(archive) naar Isolate.run
  (het archief bouwen, geheugenwerk, blijft waar het was). Web blijft
  Future.sync zoals ExportService._offload.
- Presentatie-exports: de gerasterde dia-PNG's gaan als
  TransferableTypedData over in plaats van gekopieerd — bij 150 dia's
  met foto's was die kopie honderden MB piekgeheugen. Eenmalig
  bruikbaar, zoals de lijst toch al was.
- Annuleren: de documentexportdialoog houdt Annuleren tijdens de bouw
  klikbaar; writeDocumentExport toetst de vlag vlak vóór de ene
  onomkeerbare stap (de schrijfactie) en laat die dan vallen. De
  lopende isolate zelf kan coöperatief niet afbreken (ponytail:
  ceiling is doormeten-tot-klaar; upgrade path is een eigen isolate
  met kill()).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- privacytekstscan: warmPrivacyScanMemo scant de koude dia's in een
  werk-isolate en legt de uitslag in dezelfde per-dia-memo; het
  bulklexicon laadt de worker zelf via de root-token, met een synchrone
  terugval als dat niet kan (fail-closed).
- kwaliteitsmeting: analyzeWarmed meet in plakken van ~20 dia's met
  Priority.idle-taken ertussen — TextPainter blijft op de UI-isolate,
  maar de app schiet tussen de plakken door.
- beide warmlopen vóór het plaatsen van een net geopend deck, overal waar
  _placeDeckInTab landt (schijf, bytes, pakket, git).
- de eerste onveilige-markdown-scan in openFileByPath viel weg: de
  bevindingen reizen mee in het openresultaat uit dezelfde lees-/scan-
  operatie — geen dubbele lees meer voor het alarm.
- wWinMain maakt Local\OciDeckSingleInstance aan; bestaat die al, dan
  zoekt hij het primaire venster (Flutter-klasse + OciDeckPrimaryInstance-
  prop), stuurt de bestandsargumenten via WM_COPYDATA, trekt het venster
  naar voren en sluit af.
- Het beamervenster (multi_window als eerste argument) slaat de mutex
  over — bewust een tweede proces.
- FlutterWindow opent het ocideck/open_file-kanaal en zet de COPYDATA-
  payload door als openFiles — hetzelfde kanaal dat macOS al gebruikte.
- OpenFileChannel.start() activeert nu ook op Windows; de koude start
  loopt zoals voorheen via de commandoregel-argumenten.

Vervolg vastgelegd in #2656: herstel-PID-lock per sessie en Linux-
socket. Niet hardware-bewezen op Windows (geen spiegel hier); de
Dart-kant van het kanaal is ongewijzigd en getest.
- InstallRequiredSystemLibraries installeert msvcp140/vcruntime140/
  vcruntime140_1 naast ocideck.exe — Inno en de zip pakken die map
  integraal, dus één regel dekt beide distributievormen.
- De installer-rookproef eist de drie DLL's nu expliciet in de payload:
  op windows-latest staat de runtime er altijd al (Visual Studio), dus
  een missende DLL viel precies daar onzichtbaar weg.
- THIRD_PARTY_NOTICES.md vermeldt de redistributable.

Niet hardware-bewezen (geen Windows-spiegel); de CI-payloadcontrole op
de volgende release-build is de echte toets.
De prestatiefixes uit deze sweep stonden zonder terugvalbeveiliging; deze
commit zet de poorten erop:

- check_conventions krijgt syncIoCallsIn: een *Sync dart:io-call in
  lib/widgets/ of lib/state/ faalt, met syncIoBaseline als krimpende
  allowlist voor de twee bewuste uitzonderingen (image_crop_dialog, #1854).
- Vijf bestaande *Sync-calls omgezet naar async: welkomstscherm,
  carrousel-verwijdering/-ontdubbeling, PathExistsProbe van de
  bulkimport (FutureOr, testnaad via de dialog), thema-logo-issue en de
  naamvalidator van de aanmaakdialoog — die laatste deed een existsSync
  per toetsaanslag, nu een async probe.
- Twee gelijk-werk-schaaltoetsen: markdownFromDocument op 300 pagina's
  en het per-toetsaanslag-werk (reparse + schrijfstijlinspectie).
- PERFORMANCE_GUIDE documenteert de poorten en de nog-missende
  Windows-first-frame-meting (opvolg-issue #2657: er is geen
  meetpunt op Windows-hardware).
- Plafonds van de elf bestanden/klassen die deze sweep legitiem liet
  groeien zijn bewust verhoogd met reden; twee geslonken baselines
  vastgezet.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- SOURCE_MAP.md: vijf regels voor de nieuwe libraries uit #2628/#2631/#2644
  (dekkingstest was rood).
- KNOWN_LIMITATIONS.md: eerlijke notitie dat de Windows single-instance
  (#2627) en de meegeleverde VC++-runtime (#2630) niet op hardware bewezen
  zijn, plus het openstaande Windows-opstartmeetpunt (#2657).
- USER_GUIDE.md: het .ocideck-pakket draagt zijn stijl mee (#2644) en een
  eigen logo is op web ook te kiezen — als mem:-asset, dus sessie-gebonden.
- CHANGELOG.md: Unreleased-vastelegging van de hele batch.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
#2649 voegde `show ThemeProfile` toe terwijl regel 34 dezelfde library al
volledig importeerde — --fatal-infos markeerde hem.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De batch liet zeven methoden boven hun ratchet-plafond groeien.
_performedChecksList werd opgeknipt in _imageScanProgressRow,
_privacyDisabledRow en _lexiconCoverageRow (~105 regels over, geen
plafond nodig). De rest is bewust verhoogd met +N (#issue)-reden en
lucht, conform de bestaande baseline-conventie.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De #2634- en #2644-toevoegingen vervolgden bestaande Engelse blokken in
het Nederlands — beide blokken staan nu weer volledig in de staande taal.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De #2627/#2630-beperking stond alleen in de Engelse bron; de
vertaalpoort eist de kop ook in de .nl-variant.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
#2640 liet de donkere-logoknop het gedeelde label 'Kiezen' hergebruiken;
de oude #1931-sleutel bleef wees achter in de brontabel en 30 vertalingen.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
warmDeckAnalysisBeforePlace wachtte op endOfFrame + Isolate.run, die onder
de fake-async testklok nooit landen terwijl de test await — info_safety_-
prompt en shell_present_and_close hingen daarom 10 minuten. De FLUTTER_TEST-
probe uit mermaid_render_service is nu een gedeelde util; in tests warmt de
helper synchroon — zelfde memo's en eindstaat, geen scheduler/isolate.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sinds #2624 sleutelt calloutImageProvider op het lexicale pad — realpath en
containment zitten in de byte-lezing. De test warmde nog op met de realpath-
sleutel, dus de overlay miste de entry op frame één.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Twee gaten in de vertraagde Quill→Markdown-terugschrijf:

- Een flush in deactivate() notifieerde de broncontroller midden in de
  deactivatiegolf; luisteraars deden dan context.read op een inactief
  element. Bovendien schreef die flush bij een documentwissel uitstaande
  inhoud van het oude document over de nieuwe bron heen. De haak op
  controller.value dekt alle levende lezers al; uitstaande inhoud van een
  weggevallen schrijfvlak vervalt bewust mee.
- De controller-haak flustte ook bij lezingen ín een build (preview,
  EditableText, didUpdateWidget) — de daaropvolgende providerwijziging mag
  daar niet landen. flushPendingVisualEdit stelt nu uit zolang de scheduler
  in de buildfase zit; de rustpauze-timer schrijft het verschil alsnog. De
  sluiting naar bronstand in didUpdateWidget loopt om dezelfde reden een
  frame later.

Tests pompen de rustpauze (350 ms) uit waar ze de bron na een aanslag lezen,
en bewaken de State-identiteit van het schrijfvlak in plaats van de
widgetconfig — die mag na de afgeklokte chrome-verversing gewoon herbouwen.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De afsluit-dialoog (#2631), web-logo-hint (#2640), afbeeldingsnaam en
settings-herstelmeldingen (#2628) hadden nog geen vertalingen. Twee
adjacent-literals zijn samengevoegd tot één sleutel (de extractor leest
alleen de eerste), en het pad staat als {pad}-placeholder in de sleutel
in plaats van geïnterpoleerd vóór de lookup.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De conditional-export-facade heeft geen uitvoerbare regels en de stub is de
webhelft — beide erkende baseline-categorieën. De test importeert voortaan
via de publieke seam in plaats van de io-helft rechtstreeks.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Dubbele implementaties opgelost ten gunste van main's gelande varianten
waar die een superset zijn: ocideck-style.json-sidecar (#2644),
materializeProjectStyleImages, IdentitySlotMemo, VisualMarkdownFlush-
flushpad (#2621) en ResolvedDecodeImage (#2625). Behoed uit onze kant:
budgetbescherming om applyCarriedStyleMembers heen, deckcontainment in
_VersionedFileImage, en de per-vlak decodegrenzen op de exportroute.
- DOCX-kopgroottes volgen main's vaste sjabloonmaten (#2649 dekt de
  bodymaat; de koppen waren bewust niet meegezoomd).
- Pakket-sidecar heet ocideck-style.json met een plat ThemeProfile,
  geen theme.json met een definition-wrapper (#2644).
- 'Donker logo kiezen' is géén weessleutel — main's logokiezer gebruikt
  hem; de verwijdering is teruggedraaid.
- Verwijdertest zaadt twee profielen: het laatste opgeslagen profiel is
  per contract niet te verwijderen.
- SBOM opnieuw gegenereerd na de afhankelijkheidsverschuiving.
- lengthSync() uit de bullet-markerkiezer voor de sync-io-poort.
Main hield de kopmaten vast (32…16 halve punten), waardoor een grotere
bodytekst zijn eigen kop inhield en DOCX afweek van ODT-percentages,
scherm en HTML/PDF — die allemaal relatief schalen. De kopmaten schalen
nu mee via documentFontScale; main's roundtrip-test assertte '32' op het
vaste h1-spoor en toetst nu de echte verhouding (52 bij 18 pt).
brenno merged commit ac466bb858 into main 2026-10-29 12:18:50 +00:00
Sign in to join this conversation.
No description provided.