- Fix highlight() returning SxExpr so syntax-highlighted code renders as DOM elements instead of leaking SX source text into the page - Add Specs section that reads and displays canonical SX spec files from shared/sx/ref/ with syntax highlighting - Add "The Reflexive Web" essay on SX becoming a complete LISP with AI as native participant - Change logo from (<x>) to (<sx>) everywhere - Unify all backgrounds to bg-stone-100, center code blocks - Skip component/style cookie cache in dev mode so .sx edits are visible immediately on refresh without clearing localStorage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
194 lines
6.5 KiB
Python
194 lines
6.5 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
|
|
],
|
|
}
|
|
|
|
|
|
_SPEC_FILES = {
|
|
"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", "Rendering evaluated expressions to DOM, HTML, or SX wire format."),
|
|
}
|
|
|
|
|
|
def _spec_data(slug: str) -> dict:
|
|
"""Return spec file source and highlighted version for display."""
|
|
import os
|
|
from content.highlight import highlight as _highlight
|
|
|
|
ref_dir = os.path.join(os.path.dirname(__file__), "..", "..", "shared", "sx", "ref")
|
|
# Normalise — inside container shared is at /app/shared
|
|
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 = _SPEC_FILES[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": "SX Core Specification", "spec-files": specs}
|
|
|
|
info = _SPEC_FILES.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,
|
|
}
|