Replace env free-variable threading with IO-primitive auto-fetch macros
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m38s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m38s
Layout components now self-resolve context (cart-mini, auth-menu, nav-tree, rights, URLs) via new IO primitives (root-header-ctx, select-colours, account-nav-ctx, app-rights) and defmacro wrappers (~root-header-auto, ~auth-header-row-auto, ~root-mobile-auto). This eliminates _ctx_to_env(), HELPER_CSS_CLASSES, and verbose :key threading across all 10 services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,34 +16,6 @@ from .page import SEARCH_HEADERS_MOBILE, SEARCH_HEADERS_DESKTOP
|
||||
from .parser import SxExpr
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Pre-computed CSS classes for inline sx built by Python helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
# These :class strings appear in post_header_sx / post_admin_header_sx etc.
|
||||
# They're static — scan once at import time so they aren't re-scanned per request.
|
||||
|
||||
_HELPER_CLASS_SOURCES = [
|
||||
':class "flex flex-col sm:flex-row sm:items-center gap-2 border-r border-stone-200 mr-2 sm:max-w-2xl"',
|
||||
':class "relative nav-group"',
|
||||
':class "justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black p-3"',
|
||||
':class "!bg-stone-500 !text-white"',
|
||||
':class "fa fa-cog"',
|
||||
':class "fa fa-shield-halved"',
|
||||
':class "text-white"',
|
||||
':class "justify-center cursor-pointer flex flex-row items-center gap-2 rounded !bg-stone-500 !text-white p-3"',
|
||||
]
|
||||
|
||||
|
||||
def _scan_helper_classes() -> frozenset[str]:
|
||||
"""Scan the static class strings from helper functions once."""
|
||||
from .css_registry import scan_classes_from_sx
|
||||
combined = " ".join(_HELPER_CLASS_SOURCES)
|
||||
return frozenset(scan_classes_from_sx(combined))
|
||||
|
||||
|
||||
HELPER_CSS_CLASSES: frozenset[str] = _scan_helper_classes()
|
||||
|
||||
|
||||
def call_url(ctx: dict, key: str, path: str = "/") -> str:
|
||||
"""Call a URL helper from context (e.g., blog_url, account_url)."""
|
||||
fn = ctx.get(key)
|
||||
@@ -141,12 +113,8 @@ async def _post_nav_items_sx(ctx: dict) -> str:
|
||||
container_nav = str(ctx.get("container_nav") or "").strip()
|
||||
# Skip empty fragment wrappers like "(<> )"
|
||||
if container_nav and container_nav.replace("(<>", "").replace(")", "").strip():
|
||||
parts.append(
|
||||
f'(div :id "entries-calendars-nav-wrapper"'
|
||||
f' :class "flex flex-col sm:flex-row sm:items-center gap-2'
|
||||
f' border-r border-stone-200 mr-2 sm:max-w-2xl"'
|
||||
f' {container_nav})'
|
||||
)
|
||||
parts.append(await render_to_sx("container-nav-wrapper",
|
||||
content=SxExpr(container_nav)))
|
||||
|
||||
# Admin cog
|
||||
admin_nav = ctx.get("post_admin_nav")
|
||||
@@ -157,15 +125,9 @@ async def _post_nav_items_sx(ctx: dict) -> str:
|
||||
from quart import request
|
||||
admin_href = call_url(ctx, "blog_url", f"/{slug}/admin/")
|
||||
is_admin_page = ctx.get("is_admin_section") or "/admin" in request.path
|
||||
sel_cls = "!bg-stone-500 !text-white" if is_admin_page else ""
|
||||
base_cls = ("justify-center cursor-pointer flex flex-row"
|
||||
" items-center gap-2 rounded bg-stone-200 text-black p-3")
|
||||
admin_nav = (
|
||||
f'(div :class "relative nav-group"'
|
||||
f' (a :href "{admin_href}"'
|
||||
f' :class "{base_cls} {sel_cls}"'
|
||||
f' (i :class "fa fa-cog" :aria-hidden "true")))'
|
||||
)
|
||||
admin_nav = await render_to_sx("admin-cog-button",
|
||||
href=admin_href,
|
||||
is_admin_page=is_admin_page or None)
|
||||
if admin_nav:
|
||||
parts.append(admin_nav)
|
||||
return "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
@@ -282,10 +244,8 @@ async def post_admin_header_sx(ctx: dict, slug: str, *, oob: bool = False,
|
||||
selected: str = "", admin_href: str = "") -> str:
|
||||
"""Post admin header row as sx wire format."""
|
||||
# Label
|
||||
label_parts = ['(i :class "fa fa-shield-halved" :aria-hidden "true")', '" admin"']
|
||||
if selected:
|
||||
label_parts.append(f'(span :class "text-white" "{escape(selected)}")')
|
||||
label_sx = "(<> " + " ".join(label_parts) + ")"
|
||||
label_sx = await render_to_sx("post-admin-label",
|
||||
selected=str(escape(selected)) if selected else None)
|
||||
|
||||
nav_sx = await _post_admin_nav_items_sx(ctx, slug, selected) or None
|
||||
|
||||
@@ -385,44 +345,6 @@ def _build_component_ast(__name: str, **kwargs: Any) -> list:
|
||||
return ast
|
||||
|
||||
|
||||
def _ctx_to_env(ctx: dict, *, oob: bool = False) -> dict:
|
||||
"""Convert template context dict → SX evaluation env dict.
|
||||
|
||||
Applies ``_as_sx()`` to HTML fragments, ``call_url()`` to URL helpers,
|
||||
extracts rights/admin flags. Returns kebab-case keys matching SX
|
||||
symbol conventions so .sx defcomps can read them as free variables.
|
||||
"""
|
||||
rights = ctx.get("rights") or {}
|
||||
is_admin = rights.get("admin") if isinstance(rights, dict) else getattr(rights, "admin", False)
|
||||
env = {
|
||||
# Root header values (match ~header-row-sx &key params)
|
||||
"cart-mini": _as_sx(ctx.get("cart_mini")),
|
||||
"blog-url": call_url(ctx, "blog_url", ""),
|
||||
"site-title": ctx.get("base_title", ""),
|
||||
"app-label": ctx.get("app_label", ""),
|
||||
"nav-tree": _as_sx(ctx.get("nav_tree")),
|
||||
"auth-menu": _as_sx(ctx.get("auth_menu")),
|
||||
"nav-panel": _as_sx(ctx.get("nav_panel")),
|
||||
"settings-url": call_url(ctx, "blog_url", "/settings/") if is_admin else "",
|
||||
"is-admin": is_admin,
|
||||
"oob": oob,
|
||||
# URL helpers (pre-resolved to strings)
|
||||
"account-url": call_url(ctx, "account_url", ""),
|
||||
"events-url": call_url(ctx, "events_url", ""),
|
||||
"market-url": call_url(ctx, "market_url", ""),
|
||||
"cart-url": call_url(ctx, "cart_url", ""),
|
||||
# Common values
|
||||
"select-colours": ctx.get("select_colours", ""),
|
||||
"rights": rights,
|
||||
# Fragments (used by various services)
|
||||
"container-nav": _as_sx(ctx.get("container_nav")),
|
||||
"account-nav": _as_sx(ctx.get("account_nav")),
|
||||
# Post context
|
||||
"post": ctx.get("post") or {},
|
||||
}
|
||||
return env
|
||||
|
||||
|
||||
async def render_to_sx_with_env(__name: str, extra_env: dict, **kwargs: Any) -> str:
|
||||
"""Like ``render_to_sx`` but merges *extra_env* into the evaluation
|
||||
environment before eval. Used by ``register_sx_layout`` so .sx
|
||||
@@ -584,8 +506,6 @@ def sx_response(source: str, status: int = 200,
|
||||
cumulative_classes: set[str] = set()
|
||||
if registry_loaded():
|
||||
new_classes = scan_classes_from_sx(source)
|
||||
# Include pre-computed helper classes (menu bars, admin nav, etc.)
|
||||
new_classes.update(HELPER_CSS_CLASSES)
|
||||
if comp_defs:
|
||||
# Scan only the component definitions actually being sent
|
||||
new_classes.update(scan_classes_from_sx(comp_defs))
|
||||
@@ -717,8 +637,6 @@ def sx_page(ctx: dict, page_sx: str, *,
|
||||
for val in _COMPONENT_ENV.values():
|
||||
if isinstance(val, Component) and val.css_classes:
|
||||
classes.update(val.css_classes)
|
||||
# Include pre-computed helper classes (menu bars, admin nav, etc.)
|
||||
classes.update(HELPER_CSS_CLASSES)
|
||||
# Page sx is unique per request — scan it
|
||||
classes.update(scan_classes_from_sx(page_sx))
|
||||
# Always include body classes
|
||||
|
||||
Reference in New Issue
Block a user