fix(editor): alinhamento seleção/Enter — paridade de layout textarea×overlay

Dois defeitos de geometria entre o textarea transparente (desenha caret e
seleção) e o overlay #hl (desenha o texto visível):

1. Largura: no Windows/Chrome o scrollbar clássico do textarea (~15-17px)
   reduz sua área de texto; o overlay (overflow:hidden) não — cada linha
   quebrava em colunas diferentes e a seleção ficava com 'buracos' e
   deslocamento progressivo. Agora: scrollbar-gutter:stable no textarea
   (largura constante, sem saltar ao cruzar 1 tela) + --sbw medido por JS
   espelhado no padding-right do overlay a cada render.
2. Negrito sintetizado: tk-h/tk-strong/tk-mark usavam font-weight 700; sem
   face bold na fonte monoespaçada, o Chrome alonga os glifos (~1-2px/char)
   e o overlay acumulava offset por linha (visível no print: seleção parava
   no fim do trecho negrito). font-synthesis:none no overlay + pseudo-negrito
   por text-shadow (.35px) — peso visual preservado, avanço idêntico.

Também: renderEditorVisual reassincroniza scrollTop/scrollLeft de hl/gutter
após replaceChildren (antes o Enter em nota longa zerava o scroll do overlay
— 'Enter não atualiza a visualização').

Verificado headless: com scrollbar clássico simulado (15px), largura de texto
textarea==overlay (1078==1078), seleção contínua até o fim correto da linha
quebrada; Enter com doc rolado mantém ta/hl/gutter em 5000; wysiwyg/preview
sem regressão. sw v14.
This commit is contained in:
lgcosta committed 2026-11-05 12:08:42 -03:00
1 parent f4d6361b62
commit 82417d2e79
3 files changed
+15 -5

No files matched your search

+8
View File
@@ -1157,6 +1157,10 @@
const frag = document.createDocumentFragment();
for (const r of rowEls) frag.appendChild(r);
E.hl.replaceChildren(frag);
// paridade de largura com o textarea: com scrollbar-gutter:stable o valor é
// constante por plataforma (clássico ~15-17px; overlay 0) e o espelha aqui
const sbw = E.editor.offsetWidth - E.editor.clientWidth;
E.hl.style.setProperty('--sbw', sbw > 0 ? sbw + 'px' : '0px');
if (lines.length !== lastGutterCount) {
const gf = document.createDocumentFragment();
for (let i = 1; i <= lines.length; i++) {
@@ -1172,6 +1176,10 @@
const rows = E.gutterInner.children;
for (let i = 0; i < rowEls.length; i++) rows[i].style.height = E.hl.children[i].offsetHeight + 'px';
}
// replaceChildren zera o scroll do overlay/gutter — reassincroniza com o textarea
E.hl.scrollTop = E.editor.scrollTop;
E.hl.scrollLeft = E.editor.scrollLeft;
E.gutter.scrollTop = E.editor.scrollTop;
updateMarker();
}