Files
mono/sx/sxc/pages/helpers.py
giles 3197022299 Restructure Specs section into Architecture, Core, and Adapters pages
- Add Architecture intro page explaining the spec's two-layer design
  (core language + selectable adapters) with dependency graph
- Split specs into Core (parser, eval, primitives, render) and
  Adapters (DOM, HTML, SX wire, SxEngine) overview pages
- Add individual detail pages for all adapter and engine specs
- Update nav with Architecture landing, Core, Adapters, and all
  individual spec file links

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 11:55:59 +00:00

210 lines
7.3 KiB
Python

"""Page helper registration for sx docs.
All helpers return data values (dicts, lists) — no sx_call(), no SxExpr.
Markup composition lives entirely in .sx files.
"""
from __future__ import annotations
def _register_sx_helpers() -> None:
"""Register Python data helpers as page helpers."""
from shared.sx.pages import register_page_helpers
from content.highlight import highlight as _highlight
register_page_helpers("sx", {
"highlight": _highlight,
"primitives-data": _primitives_data,
"reference-data": _reference_data,
"attr-detail-data": _attr_detail_data,
"spec-data": _spec_data,
})
def _primitives_data() -> dict:
"""Return the PRIMITIVES dict for the primitives docs page."""
from content.pages import PRIMITIVES
return PRIMITIVES
def _reference_data(slug: str) -> dict:
"""Return reference table data for a given slug.
Returns a dict whose keys become SX env bindings:
- attributes: req-attrs, beh-attrs, uniq-attrs
- headers: req-headers, resp-headers
- events: events-list
- js-api: js-api-list
"""
from content.pages import (
REQUEST_ATTRS, BEHAVIOR_ATTRS, SX_UNIQUE_ATTRS,
REQUEST_HEADERS, RESPONSE_HEADERS,
EVENTS, JS_API, ATTR_DETAILS,
)
if slug == "attributes":
return {
"req-attrs": [
{"name": a, "desc": d, "exists": e,
"href": f"/reference/attributes/{a}" if e and a in ATTR_DETAILS else None}
for a, d, e in REQUEST_ATTRS
],
"beh-attrs": [
{"name": a, "desc": d, "exists": e,
"href": f"/reference/attributes/{a}" if e and a in ATTR_DETAILS else None}
for a, d, e in BEHAVIOR_ATTRS
],
"uniq-attrs": [
{"name": a, "desc": d, "exists": e,
"href": f"/reference/attributes/{a}" if e and a in ATTR_DETAILS else None}
for a, d, e in SX_UNIQUE_ATTRS
],
}
elif slug == "headers":
return {
"req-headers": [
{"name": n, "value": v, "desc": d}
for n, v, d in REQUEST_HEADERS
],
"resp-headers": [
{"name": n, "value": v, "desc": d}
for n, v, d in RESPONSE_HEADERS
],
}
elif slug == "events":
return {
"events-list": [
{"name": n, "desc": d}
for n, d in EVENTS
],
}
elif slug == "js-api":
return {
"js-api-list": [
{"name": n, "desc": d}
for n, d in JS_API
],
}
# Default — return attrs data for fallback
return {
"req-attrs": [
{"name": a, "desc": d, "exists": e,
"href": f"/reference/attributes/{a}" if e and a in ATTR_DETAILS else None}
for a, d, e in REQUEST_ATTRS
],
"beh-attrs": [
{"name": a, "desc": d, "exists": e,
"href": f"/reference/attributes/{a}" if e and a in ATTR_DETAILS else None}
for a, d, e in BEHAVIOR_ATTRS
],
"uniq-attrs": [
{"name": a, "desc": d, "exists": e,
"href": f"/reference/attributes/{a}" if e and a in ATTR_DETAILS else None}
for a, d, e in SX_UNIQUE_ATTRS
],
}
_CORE_SPECS = {
"parser": ("parser.sx", "Parser", "Tokenization and parsing of SX source text into AST."),
"evaluator": ("eval.sx", "Evaluator", "Tree-walking evaluation of SX expressions."),
"primitives": ("primitives.sx", "Primitives", "All built-in pure functions and their signatures."),
"renderer": ("render.sx", "Renderer", "Shared rendering registries and utilities used by all adapters."),
}
_ADAPTER_SPECS = {
"adapter-dom": ("adapter-dom.sx", "DOM Adapter", "Renders SX expressions to live DOM nodes. Browser-only."),
"adapter-html": ("adapter-html.sx", "HTML Adapter", "Renders SX expressions to HTML strings. Server-side."),
"adapter-sx": ("adapter-sx.sx", "SX Wire Adapter", "Serializes SX for client-side rendering. Component calls stay unexpanded."),
"engine": ("engine.sx", "SxEngine", "Fetch/swap/history engine for browser-side SX. Like HTMX but native to SX."),
}
_ALL_SPECS = {**_CORE_SPECS, **_ADAPTER_SPECS}
def _spec_data(slug: str) -> dict:
"""Return spec file source and metadata for display."""
import os
ref_dir = os.path.join(os.path.dirname(__file__), "..", "..", "shared", "sx", "ref")
if not os.path.isdir(ref_dir):
ref_dir = "/app/shared/sx/ref"
base = {"spec-not-found": None, "spec-title": None, "spec-desc": None,
"spec-filename": None, "spec-source": None, "spec-files": None}
if slug == "core":
specs = []
for key in ("parser", "evaluator", "primitives", "renderer"):
filename, title, desc = _CORE_SPECS[key]
filepath = os.path.join(ref_dir, filename)
source = _read_spec(filepath)
specs.append({
"title": title, "desc": desc, "filename": filename,
"source": source, "href": f"/specs/{key}",
})
return {**base, "spec-title": "Core Language", "spec-files": specs}
if slug == "adapters":
specs = []
for key in ("adapter-dom", "adapter-html", "adapter-sx", "engine"):
filename, title, desc = _ADAPTER_SPECS[key]
filepath = os.path.join(ref_dir, filename)
source = _read_spec(filepath)
specs.append({
"title": title, "desc": desc, "filename": filename,
"source": source, "href": f"/specs/{key}",
})
return {**base, "spec-title": "Adapters & Engine", "spec-files": specs}
info = _ALL_SPECS.get(slug)
if not info:
return {**base, "spec-not-found": True}
filename, title, desc = info
filepath = os.path.join(ref_dir, filename)
source = _read_spec(filepath)
return {**base,
"spec-title": title, "spec-desc": desc,
"spec-filename": filename, "spec-source": source}
def _read_spec(filepath: str) -> str:
"""Read a spec file, returning empty string if missing."""
try:
with open(filepath, encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
return ";; spec file not found"
def _attr_detail_data(slug: str) -> dict:
"""Return attribute detail data for a specific attribute slug.
Returns a dict whose keys become SX env bindings:
- attr-title, attr-description, attr-example, attr-handler
- attr-demo (component call or None)
- attr-wire-id (wire placeholder id or None)
- attr-not-found (truthy if not found)
"""
from content.pages import ATTR_DETAILS
from shared.sx.helpers import SxExpr
detail = ATTR_DETAILS.get(slug)
if not detail:
return {"attr-not-found": True}
demo_name = detail.get("demo")
wire_id = None
if "handler" in detail:
wire_id = f"ref-wire-{slug.replace(':', '-').replace('*', 'star')}"
return {
"attr-not-found": None,
"attr-title": slug,
"attr-description": detail["description"],
"attr-example": detail["example"],
"attr-handler": detail.get("handler"),
"attr-demo": SxExpr(f"(~{demo_name})") if demo_name else None,
"attr-wire-id": wire_id,
}