"""Panel widths: three numbers per panel, in three files, that must agree. `set_layout` drops a CSS variable it does not recognise, and it drops it silently -- an older browser sending a key a newer release removed must not fail the whole request. The cost of that kindness is that a panel whose width is missing from `LAYOUT_BOUNDS` is one whose drag handle appears to work, moves the edge, and forgets by the next page load. Nothing anywhere says so. So the three are pinned here: the allowlist entry, the `data-resize-min` on the handle, and the `--*-width-min` token the CSS clamps with. """ from __future__ import annotations import re from pathlib import Path import lembas from lembas.api.preferences import LAYOUT_BOUNDS ROOT = Path(lembas.__file__).parent TOKENS = (ROOT / "web/static/css/tokens.css").read_text(encoding="utf-8") TEMPLATES = ROOT / "web/templates" # The panels with a drag handle, and the template each handle lives in. PANELS = { "--terminal-width": "chat/_terminal.html", "--canvas-width": "chat/_canvas.html", } # 1rem, everywhere in this application. REM = 16 def _resize_min(template: str) -> int: text = (TEMPLATES / template).read_text(encoding="utf-8") found = re.search(r'data-resize-min="(\d+)"', text) assert found, f"{template} has a resize handle with no minimum" return int(found.group(1)) def _token_min(name: str) -> int: found = re.search(rf"{re.escape(name)}-min:\s*([\d.]+)rem", TOKENS) assert found, f"{name}-min is not declared in tokens.css" return int(float(found.group(1)) * REM) def test_every_dragged_panel_is_in_the_allowlist(): """Without the entry the drag is silently discarded on the way to the account, so the width survives in one browser and vanishes in the next.""" missing = [name for name in PANELS if name not in LAYOUT_BOUNDS] assert not missing, f"not in LAYOUT_BOUNDS: {missing}" def test_the_three_minimums_agree(): for name, template in PANELS.items(): assert LAYOUT_BOUNDS[name][0] == _resize_min(template) == _token_min(name), name def test_no_bound_lets_a_panel_become_unreachable(): """A width outside these is a panel somebody cannot see well enough to drag back, which is the other half of what the allowlist is for.""" for name, (low, high) in LAYOUT_BOUNDS.items(): assert 0 < low < high, name def test_the_canvas_starts_wider_than_the_terminal(): """A source line is longer than eighty columns once nothing is re-wrapping it, and this one holds prose as well.""" widths = { name: float(re.search(rf"{re.escape(name)}:\s*([\d.]+)rem", TOKENS).group(1)) for name in PANELS } assert widths["--canvas-width"] > widths["--terminal-width"] # --- Breakpoints ------------------------------------------------------------- # A media query cannot read a custom property, so the three widths this # application breaks at are literals in three stylesheets with nothing tying # them to the tokens that name them. Which is fine until somebody adds a fourth # in passing, and then there are four breakpoints and a comment describing # three. def _breakpoints_used() -> set[str]: """Widths that appear in an `@media` condition, and nowhere else. Scoped to the condition on purpose: `max-width` is also an ordinary declaration -- `.composer__dir` is capped at 11rem, `.picker__menu` at 14rem -- and a pattern that reads every one of them calls two dozen component caps "breakpoints" and fails on all of them. """ import re used: set[str] = set() for name in ("app.css", "chat.css", "admin.css"): text = (ROOT / "web/static/css" / name).read_text(encoding="utf-8") for condition in re.findall(r"@media([^{]*)\{", text): used.update(re.findall(r"max-width:\s*([\d.]+rem)", condition)) return used def test_every_breakpoint_is_one_of_the_declared_ones(): import re declared = set(re.findall(r"--bp-[a-z]+:\s*([\d.]+rem)", TOKENS)) assert declared, "no --bp-* tokens declared" used = _breakpoints_used() assert used <= declared, ( f"breakpoints used but not declared in tokens.css: {sorted(used - declared)}" ) def test_no_breakpoint_is_declared_and_never_used(): """The other direction: a token naming a width nothing breaks at is the same clutter as a colour nothing paints with.""" import re declared = set(re.findall(r"--bp-[a-z]+:\s*([\d.]+rem)", TOKENS)) assert declared <= _breakpoints_used(), ( f"declared and unused: {sorted(declared - _breakpoints_used())}" )