Remove textLength attribute - fixes cursor positioning issues

The textLength with lengthAdjust='spacing' approach was causing visual
positioning problems. While x coordinates were calculated correctly,
the browser's spacing adjustments shifted subsequent text visually,
causing cursor and text to appear offset.

Removed textLength entirely. Accepting slight visual gaps in horizontal
box-drawing lines is preferable to cursor misalignment.

Version bump to 0.3.10
This commit is contained in:
GitHub Copilot
2026-01-24 19:36:39 +00:00
parent b896464c81
commit 583ece5ce9
4 changed files with 12 additions and 13 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "textual-webterm" name = "textual-webterm"
version = "0.3.9" version = "0.3.10"
description = "Serve terminal sessions over the web" description = "Serve terminal sessions over the web"
authors = ["Will McGugan <will@textualize.io>"] authors = ["Will McGugan <will@textualize.io>"]
license = "MIT" license = "MIT"
+4 -7
View File
@@ -222,13 +222,10 @@ def render_terminal_svg(
if classes: if classes:
attrs.append(f'class="{" ".join(classes)}"') attrs.append(f'class="{" ".join(classes)}"')
# For horizontal box-drawing spans, use textLength to ensure correct width # Note: textLength with lengthAdjust="spacing" was tried but causes
# This prevents gaps caused by font rendering of ─ being narrower than char_width # visual positioning issues. The browser adds spacing between chars
# Use "mostly" check to handle occasional corrupted chars (like U+FFFD) # which shifts subsequent text visually even though x coords are correct.
if _is_mostly_horizontal_box_drawing(text) and len(text) > 1: # Accepting slight gaps in horizontal lines is preferable to cursor misalignment.
span_width = columns * char_width
attrs.append(f'textLength="{span_width:.1f}"')
attrs.append('lengthAdjust="spacing"')
parts.append(f'<tspan {" ".join(attrs)}>{_escape_xml(text)}</tspan>') parts.append(f'<tspan {" ".join(attrs)}>{_escape_xml(text)}</tspan>')
x += columns * char_width x += columns * char_width
+7 -5
View File
@@ -829,8 +829,8 @@ class TestEdgeCases:
assert "" in svg assert "" in svg
assert "" in svg assert "" in svg
def test_horizontal_lines_use_textlength(self) -> None: def test_horizontal_lines_render_without_textlength(self) -> None:
"""Horizontal line spans use textLength for correct width.""" """Horizontal lines render without textLength (removed due to positioning issues)."""
buffer = [[ buffer = [[
self._char(""), self._char(""),
self._char(""), self._char(""),
@@ -839,9 +839,11 @@ class TestEdgeCases:
self._char(""), self._char(""),
]] ]]
svg = render_terminal_svg(buffer, width=5, height=1) svg = render_terminal_svg(buffer, width=5, height=1)
# Horizontal lines should have textLength attribute # Horizontal lines should NOT have textLength (causes visual offset issues)
assert 'textLength="24.0"' in svg assert 'textLength=' not in svg
assert 'lengthAdjust="spacing"' in svg assert 'lengthAdjust=' not in svg
# But the characters should still be present
assert "" in svg or "───" in svg
def test_ansi_bright_colors(self) -> None: def test_ansi_bright_colors(self) -> None:
"""All bright ANSI colors render.""" """All bright ANSI colors render."""