From 3701a3df31770ec03ffb412ddbba66ecb7db3068 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Sat, 24 Jan 2026 19:59:37 +0000 Subject: [PATCH] Add 0.5px overlap to background rects for sub-pixel gap elimination Background rects now extend 0.5px in both width and height to create a slight overlap, eliminating visible sub-pixel gaps when viewing SVG screenshots at high zoom levels. --- Screenshot 2026-01-24 at 19.45.24.png | Bin 21052 -> 0 bytes pyproject.toml | 2 +- src/textual_webterm/svg_exporter.py | 5 +++-- tests/test_svg_exporter.py | 6 +++--- 4 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 Screenshot 2026-01-24 at 19.45.24.png diff --git a/Screenshot 2026-01-24 at 19.45.24.png b/Screenshot 2026-01-24 at 19.45.24.png deleted file mode 100644 index 211ddc8926e334176b862742dc1cc20895de7c7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21052 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 TjcdLC?7}d_-oVtMonrn4h&1hq diff --git a/pyproject.toml b/pyproject.toml index d973557..bf3a4b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "textual-webterm" -version = "0.3.12" +version = "0.3.13" description = "Serve terminal sessions over the web" authors = ["Will McGugan "] license = "MIT" diff --git a/src/textual_webterm/svg_exporter.py b/src/textual_webterm/svg_exporter.py index 5fe4e5d..8549ee0 100644 --- a/src/textual_webterm/svg_exporter.py +++ b/src/textual_webterm/svg_exporter.py @@ -207,11 +207,12 @@ def render_terminal_svg( char_cols = 2 # Wide character # Background rect if not default + # Add 0.5px overlap in both directions to eliminate sub-pixel gaps at high zoom if bg != background: - bg_width = char_cols * char_width + bg_width = char_cols * char_width + 0.5 row_bg_rects.append( f'' ) diff --git a/tests/test_svg_exporter.py b/tests/test_svg_exporter.py index b16b0fb..2ec9b04 100644 --- a/tests/test_svg_exporter.py +++ b/tests/test_svg_exporter.py @@ -300,15 +300,15 @@ class TestRenderTerminalSvg: self._char("", bg="red"), # Placeholder inherits bg ]] svg = render_terminal_svg(buffer, width=80, height=24, char_width=10.0) - # Background should span 2 columns (20px width) + # Background should span 2 columns (20px width + 0.5px overlap) assert f'fill="{ANSI_COLORS["red"]}"' in svg - # Verify rect width is for 2 columns + # Verify rect width is for 2 columns plus overlap import re rect_match = re.search(r']*width="(\d+\.?\d*)"[^>]*fill="{}"/>' .format(ANSI_COLORS["red"]), svg) assert rect_match is not None width = float(rect_match.group(1)) - assert width == 20.0 # 2 columns * 10.0 char_width + assert width == 20.5 # 2 columns * 10.0 char_width + 0.5 overlap def test_background_same_as_terminal_bg_no_rect(self) -> None: """Background same as terminal background doesn't create extra rect."""