fix(plakken): een geplakte opsomming houdt zijn inspringing #1562

Merged
brenno merged 2 commits from fix/plakken-behoudt-inspringing into main 2026-08-18 20:34:22 +00:00
Owner

Drie gebreken in dezelfde plakroute lieten een geneste opsomming plat of scheef
binnenkomen. Ze kwamen boven bij het natrekken van #1556.

Waarom dit niet eerder gevonden was

De melding beschreef "new document"<source> → twee panelen. Dat is
documentmodus, en die heeft een eigen plakafhandeling (_smartPaste
parseClipboardTablesanitizeMarkdownPaste). De deck-broneditor, die er
sterk op lijkt, heeft er géén — daar valt een plak ongewijzigd binnen. Toetsen op
het verkeerde van die twee gaf een "niet reproduceerbaar" dat niet klopte.

De drie gebreken

1. Dunne spaties in de inspringing werden gewist (#1560). _invisibleChars
verwijderde U+2009 overal. Van acht spatiesoorten was dat de enige die sneuvelde:

teken vóór
U+0020, U+00A0, U+2002, U+2003, U+2007, U+202F, U+3000 blijft blijft
U+2009 dunne spatie weg blijft

Springt een webeditor met dunne spaties in, dan kwam elke bullet op het
hoofdniveau — letterlijk de klacht uit #1556. De motivering ("ruis van
nieuwssites") gaat op voor een dunne spatie midden in een zin, niet voor één aan
het begin van een regel: daar is het de structuur. Hij wordt daar nu een gewone
spatie, zoals een NBSP al werd; verderop in de regel verdwijnt hij nog steeds.

2. U+2028/U+2029 werden gewist in plaats van gelezen (#1560). Het zijn
regel- en alineascheiders; ze stonden op dezelfde lijst. Drie opsommingsregels
werden zo één regel. Ze worden nu \n, net als \r\n en \r een regel hoger.

3. trim() nam de inspringing van de eerste regel mee (#1561). Kopieer een
stuk uit het midden van een geneste lijst en dat eerste item sprong naar het
hoofdniveau terwijl de rest bleef staan. Nu gaan alleen lege regels aan de
randen eraf.

En de tabelherkenning greep te breed (#1557)

parseClipboardTable behandelt elke tab als kolomscheiding ("no one types tabs
into a cell") en telt komma's en puntkomma's zodra elke regel evenveel velden
geeft. Een ingesprongen opsomming voldoet aan allebei — met tabs, of met
toevallig één komma per regel. Zo'n lijst werd dan een tabel.

Er staat nu een wachtpost vóór de scheidingsdetectie: begint élke niet-lege
regel met een opsommingsteken of een genummerd item, dan is het geen tabel. Een
markdown-tabel wordt eerder herkend en raakt dit niet.

Toetsing

Elk gebrek is één keer teruggezet om te zien dat zijn test valt — alle vier
vielen, en alleen die vier:

  • inspringing met dunne spatie U+2009 blijft staan
  • U+2028 en U+2029 gelden als regeleinde, niet als ruis
  • een fragment dat ingesprongen begint houdt zijn inspringing
  • an indented bullet list is not a table

Als tegenproef staan de zeven andere spatiesoorten erbij, plus een dunne spatie
midden in een zin (moet blijven verdwijnen) en een echte TSV-plak (moet nog
steeds een tabel zijn) — zodat de reparatie niet te breed grijpt.

Wat hier bewust níet in zit

Het niveau-plafond van de visuele opsommingseditor (#1558) staat los: dat is geen
plakprobleem maar een botsing tussen de 2-spaties-conventie van de parser en
_maxLevel = 4, en de reparatie vraagt een ontwerpkeuze. Blijft open.

Of het bronprogramma van de indiener werkelijk dunne spaties gebruikt, is nog
niet bevestigd; de vraag om de ruwe klembordinhoud staat uit. Deze reparaties
staan daar los van — het zijn gebreken ongeacht zijn antwoord.

Bewaker

Bewust overgeslagen: raakt het bestandsformaat niet, de opslag niet, geen
afhankelijkheid, geen uitgaand verkeer, geen publieke belofte. Het gaat om wat
er bij het plakken in de tekst belandt.

Poorten

make check groen (exit 0, 9799 tests, dekking 87,0%, per-bestandsvloer 0),
make check-secrets groen (geen lekken), make sast groen (0 bevindingen).
DAST niet gedraaid — geen geserveerd oppervlak geraakt.

Closes #1557
Closes #1560
Closes #1561

Drie gebreken in dezelfde plakroute lieten een geneste opsomming plat of scheef binnenkomen. Ze kwamen boven bij het natrekken van #1556. ## Waarom dit niet eerder gevonden was De melding beschreef *"new document"* → `<source>` → twee panelen. Dat is **documentmodus**, en die heeft een eigen plakafhandeling (`_smartPaste` → `parseClipboardTable` → `sanitizeMarkdownPaste`). De **deck**-broneditor, die er sterk op lijkt, heeft er géén — daar valt een plak ongewijzigd binnen. Toetsen op het verkeerde van die twee gaf een "niet reproduceerbaar" dat niet klopte. ## De drie gebreken **1. Dunne spaties in de inspringing werden gewist (#1560).** `_invisibleChars` verwijderde U+2009 overal. Van acht spatiesoorten was dat de enige die sneuvelde: | teken | vóór | ná | | --- | --- | --- | | U+0020, U+00A0, U+2002, U+2003, U+2007, U+202F, U+3000 | blijft | blijft | | **U+2009 dunne spatie** | **weg** | blijft | Springt een webeditor met dunne spaties in, dan kwam elke bullet op het hoofdniveau — letterlijk de klacht uit #1556. De motivering ("ruis van nieuwssites") gaat op voor een dunne spatie midden in een zin, niet voor één aan het begin van een regel: daar is het de structuur. Hij wordt daar nu een gewone spatie, zoals een NBSP al werd; verderop in de regel verdwijnt hij nog steeds. **2. U+2028/U+2029 werden gewist in plaats van gelezen (#1560).** Het zijn regel- en alineascheiders; ze stonden op dezelfde lijst. Drie opsommingsregels werden zo één regel. Ze worden nu `\n`, net als `\r\n` en `\r` een regel hoger. **3. `trim()` nam de inspringing van de eerste regel mee (#1561).** Kopieer een stuk uit het midden van een geneste lijst en dat eerste item sprong naar het hoofdniveau terwijl de rest bleef staan. Nu gaan alleen lege regels aan de randen eraf. ## En de tabelherkenning greep te breed (#1557) `parseClipboardTable` behandelt elke tab als kolomscheiding ("no one types tabs into a cell") en telt komma's en puntkomma's zodra elke regel evenveel velden geeft. Een ingesprongen opsomming voldoet aan allebei — met tabs, of met toevallig één komma per regel. Zo'n lijst werd dan een tabel. Er staat nu een wachtpost vóór de scheidingsdetectie: begint élke niet-lege regel met een opsommingsteken of een genummerd item, dan is het geen tabel. Een markdown-tabel wordt eerder herkend en raakt dit niet. ## Toetsing Elk gebrek is één keer teruggezet om te zien dat zijn test valt — alle vier vielen, en alleen die vier: - `inspringing met dunne spatie U+2009 blijft staan` - `U+2028 en U+2029 gelden als regeleinde, niet als ruis` - `een fragment dat ingesprongen begint houdt zijn inspringing` - `an indented bullet list is not a table` Als tegenproef staan de zeven andere spatiesoorten erbij, plus een dunne spatie midden in een zin (moet blijven verdwijnen) en een echte TSV-plak (moet nog steeds een tabel zijn) — zodat de reparatie niet te breed grijpt. ## Wat hier bewust níet in zit Het niveau-plafond van de visuele opsommingseditor (#1558) staat los: dat is geen plakprobleem maar een botsing tussen de 2-spaties-conventie van de parser en `_maxLevel = 4`, en de reparatie vraagt een ontwerpkeuze. Blijft open. Of het bronprogramma van de indiener werkelijk dunne spaties gebruikt, is nog niet bevestigd; de vraag om de ruwe klembordinhoud staat uit. Deze reparaties staan daar los van — het zijn gebreken ongeacht zijn antwoord. ## Bewaker Bewust overgeslagen: raakt het bestandsformaat niet, de opslag niet, geen afhankelijkheid, geen uitgaand verkeer, geen publieke belofte. Het gaat om wat er bij het plakken in de tekst belandt. ## Poorten `make check` groen (exit 0, 9799 tests, dekking 87,0%, per-bestandsvloer 0), `make check-secrets` groen (geen lekken), `make sast` groen (0 bevindingen). DAST niet gedraaid — geen geserveerd oppervlak geraakt. Closes #1557 Closes #1560 Closes #1561
Drie gebreken in dezelfde plakroute (documentmodus), die een geneste lijst
plat of scheef lieten binnenkomen (#1556).

De schoonmaak wiste een dunne spatie (U+2009) overal, ook waar die de
inspringing was. Van acht spatiesoorten was dat de enige die sneuvelde, en het
gevolg was elke bullet op het hoofdniveau — precies wat de indiener beschreef.
In de inspringing wordt hij nu een gewone spatie, net als een NBSP al werd;
midden in een zin blijft hij verdwijnen, want daar is het wel ruis (#1560).

De regelscheiders U+2028 en U+2029 stonden in diezelfde lijst en werden dus
gewist in plaats van gelezen. Alle regels plakten daardoor aaneen. Ze worden nu
een regeleinde, wat ze betekenen (#1560).

trim() nam ook de leidende witruimte van de eerste regel mee, dus een selectie
die midden in een lijst begon kwam scheef binnen. Nu gaan alleen lege regels
aan de randen eraf (#1561).

En de tabelherkenning greep te breed: een tab telt altijd als kolomscheiding, en
komma's tellen zodra elke regel evenveel velden geeft. Een ingesprongen
opsomming voldoet aan allebei en werd dan een tabel. Begint elke niet-lege regel
met een opsommingsteken of een genummerd item, dan is het nu geen tabel (#1557).

Elk gebrek heeft een test die valt zodra de reparatie eruit gaat; de zeven
andere spatiesoorten en een echte TSV-plak staan erbij als tegenproef, zodat de
reparatie niet te breed grijpt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs: de plakreparatie in de changelog
All checks were successful
scans / scans (pull_request) Successful in 2m47s
static-gate / static-gate (pull_request) Successful in 6m16s
c9422b34bd
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
brenno merged commit 36bec4a2a9 into main 2026-08-18 20:34:22 +00:00
Sign in to join this conversation.
No description provided.