Add custom SVG exporter, remove Rich from screenshot rendering

- Created svg_exporter.py with direct pyte-to-SVG rendering
- Eliminates Rich's export_svg() quirks (clip path count mismatch)
- Added 63 comprehensive tests for SVG exporter
- Removed Rich imports from local_server.py, terminal_session.py,
  app_session.py, and cli.py
- Replaced RichHandler with standard logging.basicConfig
- Replaced @rich.repr.auto with standard __repr__ methods
- Rich is no longer directly imported (still transitive via textual-serve)

Bump version to 0.3.0
This commit is contained in:
GitHub Copilot
2026-01-24 17:11:20 +00:00
parent d4acdbb4f1
commit d5a060d6aa
9 changed files with 934 additions and 165 deletions
+1 -20
View File
@@ -9,7 +9,6 @@ from textual_webterm.config import App, Config
from textual_webterm.local_server import (
LocalClientConnector,
LocalServer,
_rewrite_svg_fonts,
)
@@ -238,8 +237,7 @@ class TestLocalServerHelpers:
response = await server._handle_screenshot(request)
assert response.content_type == "image/svg+xml"
assert "<svg" in response.text
assert "ui-monospace" in response.text
assert "cdnjs.cloudflare.com" not in response.text
assert "ui-monospace" in response.text # Custom exporter uses ui-monospace font
assert created["called"][0] == "known"
assert created["called"][1:] == (132, 45)
@@ -490,23 +488,6 @@ class TestLocalServerMoreCoverage:
assert "data-font-size" in resp.text
assert "<title>Known</title>" in resp.text
def test_rewrite_svg_fonts_removes_font_face_and_forces_stack(self):
svg = (
'<svg xmlns="http://www.w3.org/2000/svg">'
'<style>@font-face{src:url(https://cdnjs.cloudflare.com/x);} text{font-family:Fira Code;}</style>'
'<text>hi</text>'
'</svg>'
)
out = _rewrite_svg_fonts(svg)
assert "@font-face" not in out
assert "cdnjs.cloudflare.com" not in out
assert "ui-monospace" in out
def test_rewrite_svg_fonts_injects_style_if_missing(self):
svg = '<svg xmlns="http://www.w3.org/2000/svg"><text>hi</text></svg>'
out = _rewrite_svg_fonts(svg)
assert "ui-monospace" in out
@pytest.mark.asyncio
async def test_cached_screenshot_etag_returns_304(self, server_with_no_apps):
request = MagicMock()