diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-18 22:06:53 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-18 22:06:53 -0500 |
| commit | e110f7afac89322a2af4f3c4ebafe303be044cc2 (patch) | |
| tree | f2b24d43f3c38044bd6127e11be43b157a7db0e5 /scripts/theme-studio/face_specs.py | |
| parent | 64153c8d995f1603986f3b44ccbdf9ddb21dfd55 (diff) | |
| download | dotemacs-e110f7afac89322a2af4f3c4ebafe303be044cc2.tar.gz dotemacs-e110f7afac89322a2af4f3c4ebafe303be044cc2.zip | |
refactor(theme-studio): cut the face model over to weight/slant/objects
I replaced the legacy bold/italic/underline/strike booleans with the final model shape across both sides of the tool. weight (light/normal/medium/semibold/bold/heavy) and slant (normal/italic/oblique) replace the bold/italic flags, underline becomes {style: line|wave, color}, strike becomes {color}, and null means unset.
A single migration converts a legacy face on the way in, mirrored as migrateLegacyFace in app-core.js and migrate_legacy in face_specs.py so the JS and Python models can't drift. It runs on import (applyImported, mergePackagesInto) and on every seed that face_spec touches. The captured-snapshot seed (default_faces.seed) narrows the same way it did before. Only bold and italic survive, as weight "bold" and slant "italic", so the generated themes stay byte-identical.
The B/I/U/S toggle buttons keep working through a transitional bridge (legacyStyleOn / toggleLegacyStyle). The weight/slant dropdowns and underline/strike controls that replace them land next. The live previews read the new shape, with a weight name mapped to a numeric CSS font-weight.
The cutover is proven emit-neutral two ways. An ERT test asserts the migrated shapes emit the same attributes as the legacy booleans, and deep-migrating every face in dupre, distinguished, sterling, now, theme, and WIP then running build-theme yields byte-identical output. Full suite green: Python 59, Node 200, ERT 41, plus the browser hash gates.
Diffstat (limited to 'scripts/theme-studio/face_specs.py')
| -rw-r--r-- | scripts/theme-studio/face_specs.py | 53 |
1 files changed, 43 insertions, 10 deletions
diff --git a/scripts/theme-studio/face_specs.py b/scripts/theme-studio/face_specs.py index 697eec50f..5fa038068 100644 --- a/scripts/theme-studio/face_specs.py +++ b/scripts/theme-studio/face_specs.py @@ -5,20 +5,20 @@ from __future__ import annotations from typing import Any -# The full per-face attribute model. inherit and height live here (every tier -# can set them now, not just packages). bold/italic/underline/strike stay as the -# legacy booleans for this phase; the weight/slant/underline-object cutover lands -# with the editor widgets that force it. distant-fg, family, overline, inverse, -# and extend are added in their final shape (no legacy form to migrate). +# The full per-face attribute model, in its final shape. weight and slant replace +# the legacy bold/italic booleans (weight is one of light/normal/medium/semibold/ +# bold/heavy; slant is normal/italic/oblique). underline and strike are objects: +# underline is {style: line|wave, color} and strike is {color}; null means unset. +# inherit and height are no longer package-only — every tier can set them. STYLE_DEFAULTS: dict[str, Any] = { "fg": None, "bg": None, "distant-fg": None, "family": None, - "bold": False, - "italic": False, - "underline": False, - "strike": False, + "weight": None, + "slant": None, + "underline": None, + "strike": None, "overline": None, "box": None, "inverse": False, @@ -32,10 +32,43 @@ STYLE_DEFAULTS: dict[str, Any] = { PACKAGE_DEFAULTS: dict[str, Any] = dict(STYLE_DEFAULTS) +def migrate_legacy(spec: dict[str, Any]) -> dict[str, Any]: + """Convert a face spec's legacy boolean style fields to the new shape. + + bold -> weight "bold", italic -> slant "italic", underline true -> + {style: line, color: null}, strike true -> {color: null}. An explicit + weight/slant already present wins over the legacy flag. Specs already in the + new shape pass through unchanged, so this is safe to apply to any input. The + JS side mirrors this in app-core.js migrateLegacyFace; keep them in step. + """ + out = dict(spec) + if "bold" in out: + bold = out.pop("bold") + if bold and not out.get("weight"): + out["weight"] = "bold" + if "italic" in out: + italic = out.pop("italic") + if italic and not out.get("slant"): + out["slant"] = "italic" + if "underline" in out: + underline = out["underline"] + if underline is True: + out["underline"] = {"style": "line", "color": None} + elif underline is False: + out["underline"] = None + if "strike" in out: + strike = out["strike"] + if strike is True: + out["strike"] = {"color": None} + elif strike is False: + out["strike"] = None + return out + + def face_spec(spec: dict[str, Any] | None = None, *, package: bool = False) -> dict[str, Any]: out = dict(PACKAGE_DEFAULTS if package else STYLE_DEFAULTS) if spec: - out.update(spec) + out.update(migrate_legacy(spec)) return out |
