Fix #167: gutter renumbers to visual rows when line wrap is on #190
@@ -270,6 +270,10 @@ body {
|
|||||||
.btn-icon.wrap-toggle[aria-pressed="true"] { background: var(--accent); color: var(--bg); border-color: var(--accent); }
|
.btn-icon.wrap-toggle[aria-pressed="true"] { background: var(--accent); color: var(--bg); border-color: var(--accent); }
|
||||||
/* #152: wrap ON must break long unbroken tokens mid-word and allow no horizontal scrolling */
|
/* #152: wrap ON must break long unbroken tokens mid-word and allow no horizontal scrolling */
|
||||||
html[data-wrap] .codebody { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all; overflow-x: hidden; }
|
html[data-wrap] .codebody { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all; overflow-x: hidden; }
|
||||||
|
/* #167: overflow:hidden zeroes the flex auto-minimum, so the codebody must
|
||||||
|
be told to fill the space left of the gutter or it collapses and the
|
||||||
|
gutter (flex-shrink:0) consumes the whole row */
|
||||||
|
html[data-wrap] .code .codebody { flex: 1 1 auto; min-width: 0; }
|
||||||
html[data-wrap] .editor { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all; overflow-x: hidden; }
|
html[data-wrap] .editor { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all; overflow-x: hidden; }
|
||||||
/* #152: with wrap on nothing may scroll horizontally, including the code container and mobile floats */
|
/* #152: with wrap on nothing may scroll horizontally, including the code container and mobile floats */
|
||||||
html[data-wrap] .code { overflow-x: hidden; }
|
html[data-wrap] .code { overflow-x: hidden; }
|
||||||
@@ -284,6 +288,12 @@ html[data-wrap] .float { overflow-x: hidden; }
|
|||||||
font-family: var(--font-mono); font-size: var(--code-fs); line-height: var(--code-lh);
|
font-family: var(--font-mono); font-size: var(--code-fs); line-height: var(--code-lh);
|
||||||
padding: 14px 0; display: flex; overflow-x: auto;
|
padding: 14px 0; display: flex; overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
/* #167: the gutter must not drive the flex layout — its content width
|
||||||
|
(row count × number width) shrinks the code column, which re-wraps lines,
|
||||||
|
which grows the gutter: a feedback loop. Pin the gutter with a fixed
|
||||||
|
basis, take its width out of the negotiation, and let the codebody take
|
||||||
|
the rest. */
|
||||||
|
.code .gutter { flex: 0 0 auto; width: 3ch; min-width: 3ch; overflow: visible; }
|
||||||
.code .gutter { flex-shrink: 0; }
|
.code .gutter { flex-shrink: 0; }
|
||||||
/* gutter/code share line metrics; the editor gutter keeps its own padding (#50) */
|
/* gutter/code share line metrics; the editor gutter keeps its own padding (#50) */
|
||||||
.code .gutter { padding-top: 0; padding-bottom: 0; }
|
.code .gutter { padding-top: 0; padding-bottom: 0; }
|
||||||
@@ -786,3 +796,5 @@ button[type="submit"]:focus-visible,
|
|||||||
.toggle-inline { display: inline-flex; }
|
.toggle-inline { display: inline-flex; }
|
||||||
.wrap-normal { word-break: normal; overflow-wrap: break-word; }
|
.wrap-normal { word-break: normal; overflow-wrap: break-word; }
|
||||||
.created-banner { display: block; }
|
.created-banner { display: block; }
|
||||||
|
/* #167: gutter rows for wrapped paste view — one row per visual code line */
|
||||||
|
.gutline { display: block; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// #167: with line wrapping on, a logical line can occupy several visual
|
// #167: with line wrapping on, a logical line can occupy several visual
|
||||||
// lines; the gutter must show one number per VISUAL line. Per-line spans
|
// lines; the gutter must show one number per VISUAL line. Per-line spans
|
||||||
// give each logical line its own box so offsetTop order stays correct even
|
// give each logical line its own box so its height identifies the wrapped
|
||||||
// when highlighting spans cross no line boundaries.
|
// row count; blank gutter rows keep numbers aligned with line starts.
|
||||||
(function () {
|
(function () {
|
||||||
var body = document.getElementById('codebody');
|
var body = document.getElementById('codebody');
|
||||||
var gutter = document.getElementById('gutter');
|
var gutter = document.getElementById('gutter');
|
||||||
@@ -31,9 +31,9 @@
|
|||||||
// Each logical line block occupies rows = height / line-height when
|
// Each logical line block occupies rows = height / line-height when
|
||||||
// wrapped; its number sits on the first row and the remaining rows get
|
// wrapped; its number sits on the first row and the remaining rows get
|
||||||
// blank gutter lines so numbers stay aligned with line starts.
|
// blank gutter lines so numbers stay aligned with line starts.
|
||||||
// The code column width must not change while measuring (the gutter is
|
// The gutter has a fixed CSS width now (.code .gutter), so its row
|
||||||
// flex-shrink:0 so its own row count never affects it), and renumber
|
// count never changes the code column width: measurement is stable
|
||||||
// must be idempotent to avoid a ResizeObserver feedback loop.
|
// with no measurement/layout feedback.
|
||||||
var lh = parseFloat(getComputedStyle(body).lineHeight) || 1;
|
var lh = parseFloat(getComputedStyle(body).lineHeight) || 1;
|
||||||
gutter.textContent = '';
|
gutter.textContent = '';
|
||||||
var frag = document.createDocumentFragment();
|
var frag = document.createDocumentFragment();
|
||||||
@@ -61,19 +61,11 @@
|
|||||||
renumber();
|
renumber();
|
||||||
|
|
||||||
// Re-renumber on toggle (theme.js toggles data-wrap on <html>) and on any
|
// Re-renumber on toggle (theme.js toggles data-wrap on <html>) and on any
|
||||||
// size change (paste of lots of text, window resize, zoom). Only width
|
// size change (paste of lots of text, window resize, zoom).
|
||||||
// changes of the code column affect wrapping, so observe width only —
|
|
||||||
// height changes caused by our own renumbering must not re-trigger.
|
|
||||||
var mo = new MutationObserver(renumber);
|
var mo = new MutationObserver(renumber);
|
||||||
mo.observe(document.documentElement, { attributes: true, attributeFilter: ['data-wrap'] });
|
mo.observe(document.documentElement, { attributes: true, attributeFilter: ['data-wrap'] });
|
||||||
var lastW = body.getBoundingClientRect().width;
|
|
||||||
if (window.ResizeObserver) {
|
if (window.ResizeObserver) {
|
||||||
var ro = new ResizeObserver(function () {
|
var ro = new ResizeObserver(function () { renumber(); });
|
||||||
var w = body.getBoundingClientRect().width;
|
|
||||||
if (Math.abs(w - lastW) < 0.5) return;
|
|
||||||
lastW = w;
|
|
||||||
renumber();
|
|
||||||
});
|
|
||||||
ro.observe(body);
|
ro.observe(body);
|
||||||
} else {
|
} else {
|
||||||
window.addEventListener('resize', renumber);
|
window.addEventListener('resize', renumber);
|
||||||
|
|||||||
Reference in New Issue
Block a user