Opslaan vanuit Visueel dupliceert frontmatter in de body (theme:/--- lekken als tekst en kop) #2142

Closed
opened 2026-09-20 21:57:56 +00:00 by brenno · 0 comments
Owner

Symptoom

Opslaan vanuit de visuele stand op een document met frontmatter (een gekozen stijl, TLP of documentvelden) corrupt de bron: de frontmatter-regels worden gedupliceerd ín de body. Zichtbaar als: theme: rvs verschijnt als tekst/kop in het document en een gewone tekstregel wordt door een losse --- eronder als (setext-)kop weergegeven. De corruptie wordt op schijf geschreven én via notifier.replaceSource terug in de editor getrokken — vandaar dat het "opeens bij Opslaan" optreedt.

Oorzaak

saveDocumentWithDestination → _patchVisualSave (lib/widgets/shell/document_save_actions.dart:126):

final baseline = MarkdownQuillCodec.markdownFromDocument(
  MarkdownQuillCodec.documentFromMarkdown(
    normalizeRichTextMarkdown(savedSource),   // ← HELE bron, mét frontmatter
  ),
);
final patched = patchVisualEdits(
  original: savedSource,
  baseline: baseline,
  current: currentSource,                     // ← frontMatter + roundTrip(body)
);

De baseline wordt berekend over de hele savedSource inclusief het frontmatter-blok, maar current (= document.source) is frontMatter + roundTrip(body) — de frontmatter is daar intact. De codec kent geen YAML-frontmatter: theme: rvs\n--- parseert als setext-kop, dus de baseline vermangelt het blok tot ---\n\n\n## theme: rvs\n…. De regel-diff tussen die vermangelde baseline en de intacte current ziet het frontmatter-verschil als een gebruikersbewerking, en _applyEdits (lib/utils/source_patcher.dart) plant de theme:- en ----regels op een afgeleide plek midden in de body.

Reproductie (bewezen met de pure functies)

const saved = '---\ntheme: rvs\n---\n\n# Titel\n\nEerste regel.\n';
patchVisualEdits(
  original: saved,
  baseline: roundTrip(saved),                       // zoals _patchVisualSave
  current: '---\ntheme: rvs\n---\n\n'
      + roundTrip('# Titel\n\nEerste regelx.\n'),   // zoals de notifier hem houdt
);

levert:

---
theme: rvs
---
# Titel
theme: rvs     ← gelekt in de body
---            ← maakt "theme: rvs" een setext-kop

Eerste regelx.

Voorwaarde: state.visualEdited == true — dus alleen bij documenten die in Visueel bewerkt zijn én frontmatter dragen. Precies de combinatie "document met gekozen stijl, getypt, opgeslagen".

Oplossingsrichting

Splits de frontmatter buiten de patch:

final savedSplit = splitDocumentFrontMatter(savedSource);
final currSplit  = splitDocumentFrontMatter(currentSource);
final patchedBody = patchVisualEdits(
  original: savedSplit.body,
  baseline: roundTrip(savedSplit.body),   // alleen de body — zoals de editor hem laadt
  current: currSplit.body,
);
return MarkdownDocument.parse(currSplit.block + patchedBody);
  • Baseline over savedSplit.body, niet over de hele bron — dat is ook exact wat de editor in _controller.text had.
  • Neem currSplit.block (de huidige frontmatter) terug in het resultaat, niet savedSplit.block: zo overleeft een tussentijdse stijl-/TLP-wijziging de opslag.
  • Alternatief-minimum: frontmatter van savedSource en currentSource strippen vóór de diff en achteraf weer voorplakken — zelfde idee, minder netjes.
  • Regressietest: document met theme:-frontmatter + één visuele aanslag → opgeslagen bron begint met ongewijzigd frontmatter, bevat geen tweede theme:-regel in de body, en bevat de bewerking. Dezelfde testvariant voor tlp:/vrije velden, en voor "visueel bewerkt maar géén frontmatter" (moet ongewijzigd blijven werken).
## Symptoom Opslaan vanuit de visuele stand op een document **met frontmatter** (een gekozen stijl, TLP of documentvelden) corrupt de bron: de frontmatter-regels worden gedupliceerd ín de body. Zichtbaar als: `theme: rvs` verschijnt als tekst/kop in het document en een gewone tekstregel wordt door een losse `---` eronder als (setext-)kop weergegeven. De corruptie wordt op schijf geschreven én via `notifier.replaceSource` terug in de editor getrokken — vandaar dat het "opeens bij Opslaan" optreedt. ## Oorzaak `saveDocumentWithDestination` → `_patchVisualSave` (`lib/widgets/shell/document_save_actions.dart:126`): ```dart final baseline = MarkdownQuillCodec.markdownFromDocument( MarkdownQuillCodec.documentFromMarkdown( normalizeRichTextMarkdown(savedSource), // ← HELE bron, mét frontmatter ), ); final patched = patchVisualEdits( original: savedSource, baseline: baseline, current: currentSource, // ← frontMatter + roundTrip(body) ); ``` De baseline wordt berekend over de **hele** `savedSource` inclusief het frontmatter-blok, maar `current` (= `document.source`) is `frontMatter + roundTrip(body)` — de frontmatter is daar intact. De codec kent geen YAML-frontmatter: `theme: rvs\n---` parseert als **setext-kop**, dus de baseline vermangelt het blok tot `---\n\n\n## theme: rvs\n…`. De regel-diff tussen die vermangelde baseline en de intacte `current` ziet het frontmatter-verschil als een gebruikersbewerking, en `_applyEdits` (`lib/utils/source_patcher.dart`) plant de `theme:`- en `---`-regels op een afgeleide plek midden in de body. ## Reproductie (bewezen met de pure functies) ```dart const saved = '---\ntheme: rvs\n---\n\n# Titel\n\nEerste regel.\n'; patchVisualEdits( original: saved, baseline: roundTrip(saved), // zoals _patchVisualSave current: '---\ntheme: rvs\n---\n\n' + roundTrip('# Titel\n\nEerste regelx.\n'), // zoals de notifier hem houdt ); ``` levert: ``` --- theme: rvs --- # Titel theme: rvs ← gelekt in de body --- ← maakt "theme: rvs" een setext-kop Eerste regelx. ``` Voorwaarde: `state.visualEdited == true` — dus alleen bij documenten die in Visueel bewerkt zijn én frontmatter dragen. Precies de combinatie "document met gekozen stijl, getypt, opgeslagen". ## Oplossingsrichting Splits de frontmatter **buiten** de patch: ```dart final savedSplit = splitDocumentFrontMatter(savedSource); final currSplit = splitDocumentFrontMatter(currentSource); final patchedBody = patchVisualEdits( original: savedSplit.body, baseline: roundTrip(savedSplit.body), // alleen de body — zoals de editor hem laadt current: currSplit.body, ); return MarkdownDocument.parse(currSplit.block + patchedBody); ``` - Baseline over `savedSplit.body`, niet over de hele bron — dat is ook exact wat de editor in `_controller.text` had. - Neem `currSplit.block` (de *huidige* frontmatter) terug in het resultaat, niet `savedSplit.block`: zo overleeft een tussentijdse stijl-/TLP-wijziging de opslag. - Alternatief-minimum: frontmatter van `savedSource` en `currentSource` strippen vóór de diff en achteraf weer voorplakken — zelfde idee, minder netjes. - Regressietest: document met `theme:`-frontmatter + één visuele aanslag → opgeslagen bron begint met ongewijzigd frontmatter, bevat geen tweede `theme:`-regel in de body, en bevat de bewerking. Dezelfde testvariant voor `tlp:`/vrije velden, en voor "visueel bewerkt maar géén frontmatter" (moet ongewijzigd blijven werken).
brenno 2026-09-20 23:36:22 +00:00
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#2142
No description provided.