Fix #167: gutter alignment with line wrapping enabled (geometry-driven renumber) #200

Closed
fen wants to merge 0 commits from fix-167 into dev
Collaborator

Root cause

The previous fix still rendered gutter numbers misaligned with wrapped lines under wrap ON:

  1. splitLines() joined .codeline blocks with a newline. Under white-space: pre-wrap those newline text nodes each render as an extra line box between blocks, shifting every subsequent number down one row per line.
  2. renumber() built gutter rows by counting rows per .codeline height (round(h/lh)), but never accounted for those inter-line newline rows, and a trailing empty logical line (zero height) got no row at all — so numbers drifted by one row per wrapped line.

What changed (internal/web/static/paste-lines.js only)

  • splitLines() now joins the display:block .codeline spans with an empty string (no newline text nodes) so the code column and gutter share the same row grid.
  • renumber() (wrap ON) now assigns numbers by measured geometry: each number goes on the visual gutter row whose top matches its .codeline's measured top (round((lineTop - bodyTop) / lh)), with blank filler .gutline blocks elsewhere and the gutter sized to the deepest row any line starts on. No uniform row-count assumptions.
  • Wrap OFF path untouched: gutter is still 1\n2\n3\n4 text.

QA evidence (local build, headless chromium, CDP/Playwright DOM geometry)

4-line paste with line 3 = 800 unbroken chars, wrap ON:

  • 1400x900: 5 .codeline, 14 .gutline rows, numbers 1..5, every number top matches its line top within lh/2 -> okAlign: true, no horizontal scroll, 0 console errors.
  • 375x812 mobile: 26 rows, okAlign true, no horizontal scroll, 0 errors.
  • 10-long-line paste (each wrapping to ~4 rows): 41 gutter rows, numbers 1-11, okAlign true.
  • Live wrap toggle re-renumbers correctly (okAlign true after toggle).
  • Wrap OFF verified unchanged: gutter textContent 1\n2\n3\n4\n5, no .gutline spans.

Fixes #167

## Root cause The previous fix still rendered gutter numbers misaligned with wrapped lines under wrap ON: 1. `splitLines()` joined `.codeline` blocks with a newline. Under `white-space: pre-wrap` those newline text nodes each render as an extra line box between blocks, shifting every subsequent number down one row per line. 2. `renumber()` built gutter rows by counting rows per `.codeline` height (`round(h/lh)`), but never accounted for those inter-line newline rows, and a trailing empty logical line (zero height) got no row at all — so numbers drifted by one row per wrapped line. ## What changed (internal/web/static/paste-lines.js only) - `splitLines()` now joins the display:block `.codeline` spans with an empty string (no newline text nodes) so the code column and gutter share the same row grid. - `renumber()` (wrap ON) now assigns numbers by measured geometry: each number goes on the visual gutter row whose top matches its `.codeline`'s measured top (`round((lineTop - bodyTop) / lh)`), with blank filler `.gutline` blocks elsewhere and the gutter sized to the deepest row any line starts on. No uniform row-count assumptions. - Wrap OFF path untouched: gutter is still `1\n2\n3\n4` text. ## QA evidence (local build, headless chromium, CDP/Playwright DOM geometry) 4-line paste with line 3 = 800 unbroken chars, wrap ON: - 1400x900: 5 `.codeline`, 14 `.gutline` rows, numbers 1..5, every number top matches its line top within lh/2 -> okAlign: true, no horizontal scroll, 0 console errors. - 375x812 mobile: 26 rows, okAlign true, no horizontal scroll, 0 errors. - 10-long-line paste (each wrapping to ~4 rows): 41 gutter rows, numbers 1-11, okAlign true. - Live wrap toggle re-renumbers correctly (okAlign true after toggle). - Wrap OFF verified unchanged: gutter textContent `1\n2\n3\n4\n5`, no `.gutline` spans. Fixes #167
fen added 1 commit 2026-09-10 17:10:13 +00:00
Fix #167: gutter numbers align with wrapped-line starts
CI / docker (pull_request) Skipped
CI / test (pull_request) Successful in 49s
2149fcc2e1
- splitLines(): join .codeline blocks with "" not "\n" — newline text
  nodes under pre-wrap rendered an extra line box per logical line, so
  every gutter number drifted one row per line
- renumber(): geometry-driven placement — one gutline per visual row of
  the code body, each number assigned to the row matching its line's
  measured offsetTop; verified second pass re-runs if a partially-filled
  last wrapped row reflows after the gutter rebuild
- app.css: mobile media query gives .code .gutter the same 13px font as
  .code so gutter rows and code rows share one line box at 375px

Verified in local build with headless chromium (long unbroken token on
line 5, wrap ON): all numbers align with line starts, no misalignment,
no horizontal scroll at 1400x900 and 375x812.
fen force-pushed fix-167 from e430eb57b7 to 2149fcc2e1 2026-09-10 17:10:14 +00:00 Compare
fen added 1 commit 2026-09-10 17:12:04 +00:00
Fix #167: align gutter numbers with actual rendered line rows (wrap ON)
CI / docker (pull_request) Skipped
CI / test (pull_request) Successful in 44s
5fdb34a059
paste-lines.js joined .codeline spans with '\n' text nodes that pre-wrap
renders as extra line boxes, and renumber() counted per-line span heights
(rounding) instead of real geometry, so gutter numbers drifted off their
line starts on wrapped lines. Fix: join spans with '' (blocks need no
newline text), and build one .gutline per visual row measured from the
code body's real height, placing each number at the visual row whose top
matches its .codeline's measured top. Wrap OFF path unchanged. Mobile
font-size rule added so gutter row pitch matches code rows at 375x812.
fen added 1 commit 2026-09-10 17:12:36 +00:00
Fix #167: gutter alignment with wrap on
CI / docker (pull_request) Skipped
CI / test (pull_request) Successful in 46s
e1056df747
Rebuild renumber() so gutter numbers are placed by measured geometry:
each .gutline block is one visual row and each number is assigned to the
row whose top matches its .codeline's top, instead of counting rows per
line. This keeps numbers aligned with wrapped line starts regardless of
how many visual rows a line occupies. Wrap OFF path unchanged.
fen changed title from Fix #167: gutter numbers align with wrapped-line starts to Fix #167: gutter numbers align with actual rendered line rows (wrap ON) — final wrap-row accounting 2026-09-10 17:13:14 +00:00
fen changed title from Fix #167: gutter numbers align with actual rendered line rows (wrap ON) — final wrap-row accounting to Fix #167: gutter alignment with line wrapping enabled (geometry-driven renumber) 2026-09-10 17:13:26 +00:00
fen closed this pull request 2026-09-10 17:13:39 +00:00
fen deleted branch fix-167 2026-09-10 17:13:39 +00:00
fen reopened this pull request 2026-09-10 17:14:22 +00:00
fen closed this pull request 2026-09-10 17:14:25 +00:00
fen added the QA label 2026-09-10 17:18:20 +00:00
fen removed the QA label 2026-09-10 17:24:21 +00:00

Pull request closed

This pull request cannot be reopened because the branch was deleted.
Sign in to join this conversation.