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:
1 parent
f4d6361b62
commit
82417d2e79
3 files changed
+15
-5
No files matched your search
+6
-4
@@ -336,10 +336,11 @@ body.no-gutter #gutter { display: none; }
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
padding: var(--pad-y) var(--pad-x);
|
||||
padding: var(--pad-y) calc(var(--pad-x) + var(--sbw, 0px)) var(--pad-y) var(--pad-x);
|
||||
font-family: var(--editor-font);
|
||||
font-size: var(--editor-size);
|
||||
line-height: var(--line-h);
|
||||
font-synthesis: none; /* negrito sintetizado alonga glifos e desloca a quebra vs textarea */
|
||||
color: var(--fg);
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
@@ -367,6 +368,7 @@ body.no-gutter #gutter { display: none; }
|
||||
border: 0;
|
||||
resize: none;
|
||||
overflow: auto;
|
||||
scrollbar-gutter: stable; /* reserva a barra clássica sempre: largura de texto constante p/ casar com o overlay */
|
||||
padding: var(--pad-y) var(--pad-x);
|
||||
font-family: var(--editor-font);
|
||||
font-size: var(--editor-size);
|
||||
@@ -381,8 +383,8 @@ body.no-gutter #gutter { display: none; }
|
||||
#editor::placeholder { color: var(--muted); }
|
||||
|
||||
/* tokens de colorização */
|
||||
.tk-h { color: var(--tk-heading); font-weight: 700; }
|
||||
.tk-strong { color: var(--tk-strong); font-weight: 700; }
|
||||
.tk-h { color: var(--tk-heading); font-weight: 700; text-shadow: .35px 0 currentColor; }
|
||||
.tk-strong { color: var(--tk-strong); font-weight: 700; text-shadow: .35px 0 currentColor; }
|
||||
.tk-em { color: var(--tk-em); font-style: italic; }
|
||||
.tk-strike { color: var(--tk-strike); text-decoration: line-through; }
|
||||
.tk-code { color: var(--tk-code); background: var(--tk-code-bg); }
|
||||
@@ -391,7 +393,7 @@ body.no-gutter #gutter { display: none; }
|
||||
.tk-link { color: var(--tk-link); text-decoration: underline; }
|
||||
.tk-url { color: var(--tk-url); text-decoration: underline; }
|
||||
.tk-tag { color: var(--tk-tag); font-weight: 600; }
|
||||
.tk-mark { color: var(--tk-mark); font-weight: 700; }
|
||||
.tk-mark { color: var(--tk-mark); font-weight: 700; text-shadow: .35px 0 currentColor; }
|
||||
.tk-quote { color: var(--tk-quote); font-style: italic; }
|
||||
.tk-html { color: var(--tk-html); }
|
||||
.tk-attr { color: var(--tk-attr); }
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user