diff --git a/dev/scripts/dossier_page/build.py b/dev/scripts/dossier_page/build.py index 4b630df..8324f71 100644 --- a/dev/scripts/dossier_page/build.py +++ b/dev/scripts/dossier_page/build.py @@ -17,6 +17,14 @@ document (template.html with the data inlined) to --out, by default infra/gitea/custom/public/assets/dossier/index.html (gitignored: the data is rebuilt, never committed). --json also writes the raw data. +Design: lex is a fhirworx (HTI-5) page built from the system's sources, so +it follows them on every rebuild. The page inlines assets/css/fhirworx.css +(palette, fonts, base classes), the fhirworx dark palette from the Gitea +theme (infra/gitea/theme/theme-fhirworx-dark.css) mapped onto the same token +names (opt-in, as in Gitea), the logo (assets/icons/logo.svg, as an SVG +symbol) and the favicon set (assets/icons). The template uses only those +tokens. + AMA license: CPT descriptors never ship. The RVU-file short descriptors are not read at all, and descriptor_change events carry a fixed note in place of the RVU-file text. @@ -25,6 +33,7 @@ place of the RVU-file text. from __future__ import annotations import argparse +import base64 import html import json import os @@ -805,12 +814,130 @@ def _fill(tpl: str, marker: str, value: str, count: int = 1) -> str: return tpl.replace(marker, value) +# ── the fhirworx design system, from its sources ────────────────────────── +FHIRWORX_CSS = ( + ROOT / "assets/css/fhirworx.css" +) # single source of truth for the palette +GITEA_DARK = ( + ROOT / "infra/gitea/theme/theme-fhirworx-dark.css" +) # the system's dark palette +ICONS = ROOT / "assets/icons" + +# fhirworx.css token -> the Gitea fhirworx-dark variable that plays the same +# role. chart-1..3 (the indigo scale) have no dark counterpart in the system, +# so they keep their fhirworx.css values; the template picks the step that +# reads on the dark card. +DARK_ROLES = { + "background": "--fw-background", + "foreground": "--fw-foreground", + "card": "--fw-card", + "card-foreground": "--fw-foreground", + "popover": "--fw-card", + "popover-foreground": "--fw-foreground", + "primary": "--fw-primary", + "primary-foreground": "--fw-primary-foreground", + "secondary": "--fw-secondary", + "secondary-foreground": "--color-text-light", + "muted": "--fw-secondary", + "muted-foreground": "--fw-muted-foreground", + "accent": "--fw-secondary", + "accent-foreground": "--fw-foreground", + "destructive": "--fw-destructive", + "destructive-foreground": "--fw-primary-foreground", + "border": "--fw-border", + "input": "--fw-border", + "ring": "--fw-primary", + "sidebar": "--fw-nav-bg", + "sidebar-foreground": "--fw-nav-text", + "sidebar-primary": "--fw-link", + "sidebar-primary-foreground": "--fw-primary-foreground", + "sidebar-accent": "--fw-nav-hover-bg", + "sidebar-accent-foreground": "--fw-nav-text", + "sidebar-border": "--fw-nav-hover-bg", + "sidebar-ring": "--fw-link", + "chart-4": "--color-green", + "chart-5": "--fw-chart-5", +} + + +def _root_vars(path: Path) -> dict[str, str]: + """Custom properties of the stylesheet's first :root block (raw values).""" + m = re.search(r"^:root\s*\{(.*?)^\}", path.read_text(encoding="utf-8"), re.S | re.M) + if not m: + raise SystemExit(f"{path}: no :root block") + body = re.sub(r"/\*.*?\*/", "", m.group(1), flags=re.S) + return {k: v.strip() for k, v in re.findall(r"(--[\w-]+)\s*:\s*([^;]+);", body)} + + +def _resolve(raw: dict[str, str], name: str, where: Path, depth: int = 0) -> str: + """The value of one custom property, following var(--x) references.""" + if depth > 8 or name not in raw: + raise SystemExit(f"{where}: cannot resolve {name}") + return re.sub( + r"var\((--[\w-]+)\)", + lambda r: _resolve(raw, r.group(1), where, depth + 1), + raw[name], + ) + + +def _dark_palette() -> str: + light = _root_vars(FHIRWORX_CSS) + dark = _root_vars(GITEA_DARK) + lines = [] + for token, src in DARK_ROLES.items(): + if f"--{token}" not in light: + raise SystemExit( + f"{FHIRWORX_CSS}: token --{token} is gone; update DARK_ROLES" + ) + value = _resolve(dark, src, GITEA_DARK) + if not re.fullmatch(r"#[0-9A-Fa-f]{6}", value): + raise SystemExit(f"{GITEA_DARK}: {src} is {value!r}, expected a hex colour") + lines.append(f" --{token}: {value}; /* {src} */") + return ':root[data-theme="dark"] {\n' + "\n".join(lines) + "\n}" + + +def _data_uri(path: Path, mime: str) -> str: + return f"data:{mime};base64," + base64.b64encode(path.read_bytes()).decode("ascii") + + +def _logo_symbol() -> str: + """assets/icons/logo.svg as an SVG