Migrate all apps to defpage declarative page routes
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m41s

Replace Python GET page handlers with declarative defpage definitions in .sx
files across all 8 apps (sx docs, orders, account, market, cart, federation,
events, blog). Each app now has sxc/pages/ with setup functions, layout
registrations, page helpers, and .sx defpage declarations.

Core infrastructure: add g I/O primitive, PageDef support for auth/layout/
data/content/filter/aside/menu slots, post_author auth level, and custom
layout registration. Remove ~1400 lines of render_*_page/render_*_oob
boilerplate. Update all endpoint references in routes, sx_components, and
templates to defpage_* naming.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 14:52:34 +00:00
parent 5b4cacaf19
commit c243d17eeb
108 changed files with 3598 additions and 2851 deletions

View File

@@ -6,8 +6,6 @@ import os
from shared.sx.jinja_bridge import load_sx_dir, watch_sx_dir
from shared.sx.helpers import (
sx_call, SxExpr, get_asset_url,
root_header_sx, full_page_sx,
oob_header_sx, oob_page_sx,
)
from content.highlight import highlight
@@ -17,10 +15,6 @@ load_sx_dir(_sxc_dir)
watch_sx_dir(_sxc_dir)
def _full_page(ctx: dict, **kwargs) -> str:
"""full_page_sx wrapper."""
return full_page_sx(ctx, **kwargs)
def _code(code: str, language: str = "lisp") -> str:
"""Build a ~doc-code component with highlighted content."""
@@ -168,13 +162,6 @@ def _main_nav_sx(current_section: str | None = None) -> str:
return _nav_items_sx(MAIN_NAV, current_section)
def _header_stack_sx(ctx: dict, section_nav: str | None = None) -> str:
"""Full header stack: root header + sx menu row."""
hdr = root_header_sx(ctx)
sx_row = _sx_header_sx(section_nav)
return "(<> " + hdr + " " + sx_row + ")"
def _sub_row_sx(sub_label: str, sub_href: str, sub_nav: str,
selected: str = "") -> str:
"""Build the level-2 sub-section menu-row."""
@@ -186,104 +173,6 @@ def _sub_row_sx(sub_label: str, sub_href: str, sub_nav: str,
)
def _section_header_stack_sx(ctx: dict, main_nav: str, sub_nav: str,
sub_label: str, sub_href: str,
selected: str = "") -> str:
"""Header stack with main nav + sub-section nav row."""
hdr = root_header_sx(ctx)
sub_row = _sub_row_sx(sub_label, sub_href, sub_nav, selected)
sx_row = _sx_header_sx(main_nav, child=sub_row)
return "(<> " + hdr + " " + sx_row + ")"
# ---------------------------------------------------------------------------
# OOB helpers — rebuild header rows for AJAX navigation
# ---------------------------------------------------------------------------
async def _section_oob_sx(section: str, sub_label: str, sub_href: str,
sub_nav: str, content: str,
selected: str = "") -> str:
"""Generic OOB response: rebuild both header rows + content."""
from shared.sx.page import get_template_context
ctx = await get_template_context()
root_hdr = root_header_sx(ctx)
main_nav = _main_nav_sx(section)
sub_row = _sub_row_sx(sub_label, sub_href, sub_nav, selected)
sx_row = _sx_header_sx(main_nav, child=sub_row)
rows = "(<> " + root_hdr + " " + sx_row + ")"
header_oob = oob_header_sx("root-header-child", "sx-header-child", rows)
return oob_page_sx(oobs=header_oob, content=content)
async def home_oob_sx() -> str:
"""OOB response for home page navigation."""
from shared.sx.page import get_template_context
ctx = await get_template_context()
root_hdr = root_header_sx(ctx)
main_nav = _main_nav_sx()
sx_row = _sx_header_sx(main_nav)
rows = "(<> " + root_hdr + " " + sx_row + ")"
header_oob = oob_header_sx("root-header-child", "sx-header-child", rows)
hero_code = highlight('(div :class "p-4 bg-white rounded shadow"\n'
' (h1 :class "text-2xl font-bold" "Hello")\n'
' (button :sx-get "/api/data"\n'
' :sx-target "#result"\n'
' "Load data"))', "lisp")
content = (
f'(div :id "main-content"'
f' (~sx-hero {hero_code})'
f' (~sx-philosophy)'
f' (~sx-how-it-works)'
f' (~sx-credits))'
)
return oob_page_sx(oobs=header_oob, content=content)
async def docs_oob_sx(slug: str) -> str:
"""OOB response for docs section navigation."""
from content.pages import DOCS_NAV
current = next((label for label, href in DOCS_NAV if href.endswith(slug)), None)
sub_nav = _docs_nav_sx(current)
return await _section_oob_sx("Docs", "Docs", "/docs/introduction", sub_nav,
_docs_content_sx(slug), selected=current or "")
async def reference_oob_sx(slug: str) -> str:
"""OOB response for reference section navigation."""
from content.pages import REFERENCE_NAV
current = next((label for label, href in REFERENCE_NAV
if href.rstrip("/").endswith(slug or "reference")), "Attributes")
sub_nav = _reference_nav_sx(current)
return await _section_oob_sx("Reference", "Reference", "/reference/", sub_nav,
_reference_content_sx(slug), selected=current or "")
async def protocol_oob_sx(slug: str) -> str:
"""OOB response for protocols section navigation."""
from content.pages import PROTOCOLS_NAV
current = next((label for label, href in PROTOCOLS_NAV if href.endswith(slug)), None)
sub_nav = _protocols_nav_sx(current)
return await _section_oob_sx("Protocols", "Protocols", "/protocols/wire-format", sub_nav,
_protocol_content_sx(slug), selected=current or "")
async def examples_oob_sx(slug: str) -> str:
"""OOB response for examples section navigation."""
from content.pages import EXAMPLES_NAV
current = next((label for label, href in EXAMPLES_NAV if href.endswith(slug)), None)
sub_nav = _examples_nav_sx(current)
return await _section_oob_sx("Examples", "Examples", "/examples/click-to-load", sub_nav,
_examples_content_sx(slug), selected=current or "")
async def essay_oob_sx(slug: str) -> str:
"""OOB response for essays section navigation."""
from content.pages import ESSAYS_NAV
current = next((label for label, href in ESSAYS_NAV if href.endswith(slug)), None)
sub_nav = _essays_nav_sx(current)
return await _section_oob_sx("Essays", "Essays", "/essays/sx-sucks", sub_nav,
_essay_content_sx(slug), selected=current or "")
# ---------------------------------------------------------------------------
# Content builders — return sx source strings
@@ -352,40 +241,6 @@ def _headers_table_sx(title: str, headers: list[tuple[str, str, str]]) -> str:
)
# ---------------------------------------------------------------------------
# Page renderers — async functions returning full HTML
# ---------------------------------------------------------------------------
async def render_home_page_sx(ctx: dict) -> str:
"""Full page: home."""
main_nav = _main_nav_sx()
hdr = _header_stack_sx(ctx, main_nav)
hero_code = highlight('(div :class "p-4 bg-white rounded shadow"\n'
' (h1 :class "text-2xl font-bold" "Hello")\n'
' (button :sx-get "/api/data"\n'
' :sx-target "#result"\n'
' "Load data"))', "lisp")
content = (
f'(div :id "main-content"'
f' (~sx-hero {hero_code})'
f' (~sx-philosophy)'
f' (~sx-how-it-works)'
f' (~sx-credits))'
)
return _full_page(ctx, header_rows=hdr, content=content)
async def render_docs_page_sx(ctx: dict, slug: str) -> str:
"""Full page: docs section."""
from content.pages import DOCS_NAV
current = next((label for label, href in DOCS_NAV if href.endswith(slug)), None)
main_nav = _main_nav_sx("Docs")
sub_nav = _docs_nav_sx(current)
hdr = _section_header_stack_sx(ctx, main_nav, sub_nav, "Docs", "/docs/introduction",
selected=current or "")
content = _docs_content_sx(slug)
return _full_page(ctx, header_rows=hdr, content=content)
def _docs_content_sx(slug: str) -> str:
"""Route to the right docs content builder."""
@@ -613,19 +468,6 @@ def _docs_server_rendering_sx() -> str:
# Reference pages
# ---------------------------------------------------------------------------
async def render_reference_page_sx(ctx: dict, slug: str) -> str:
"""Full page: reference section."""
from content.pages import REFERENCE_NAV
current = next((label for label, href in REFERENCE_NAV
if href.rstrip("/").endswith(slug or "reference")), "Attributes")
main_nav = _main_nav_sx("Reference")
sub_nav = _reference_nav_sx(current)
hdr = _section_header_stack_sx(ctx, main_nav, sub_nav, "Reference", "/reference/",
selected=current or "")
content = _reference_content_sx(slug)
return _full_page(ctx, header_rows=hdr, content=content)
def _reference_content_sx(slug: str) -> str:
builders = {
"": _reference_attrs_sx,
@@ -712,18 +554,6 @@ def _reference_js_api_sx() -> str:
# Protocol pages
# ---------------------------------------------------------------------------
async def render_protocol_page_sx(ctx: dict, slug: str) -> str:
"""Full page: protocols section."""
from content.pages import PROTOCOLS_NAV
current = next((label for label, href in PROTOCOLS_NAV if href.endswith(slug)), None)
main_nav = _main_nav_sx("Protocols")
sub_nav = _protocols_nav_sx(current)
hdr = _section_header_stack_sx(ctx, main_nav, sub_nav, "Protocols", "/protocols/wire-format",
selected=current or "")
content = _protocol_content_sx(slug)
return _full_page(ctx, header_rows=hdr, content=content)
def _protocol_content_sx(slug: str) -> str:
builders = {
"wire-format": _protocol_wire_format_sx,
@@ -887,18 +717,6 @@ def _protocol_future_sx() -> str:
# Examples pages
# ---------------------------------------------------------------------------
async def render_examples_page_sx(ctx: dict, slug: str) -> str:
"""Full page: examples section."""
from content.pages import EXAMPLES_NAV
current = next((label for label, href in EXAMPLES_NAV if href.endswith(slug)), None)
main_nav = _main_nav_sx("Examples")
sub_nav = _examples_nav_sx(current)
hdr = _section_header_stack_sx(ctx, main_nav, sub_nav, "Examples", "/examples/click-to-load",
selected=current or "")
content = _examples_content_sx(slug)
return _full_page(ctx, header_rows=hdr, content=content)
def _examples_content_sx(slug: str) -> str:
builders = {
"click-to-load": _example_click_to_load_sx,
@@ -1948,18 +1766,6 @@ def _example_retry_sx() -> str:
# Essays
# ---------------------------------------------------------------------------
async def render_essay_page_sx(ctx: dict, slug: str) -> str:
"""Full page: essays section."""
from content.pages import ESSAYS_NAV
current = next((label for label, href in ESSAYS_NAV if href.endswith(slug)), None)
main_nav = _main_nav_sx("Essays")
sub_nav = _essays_nav_sx(current)
hdr = _section_header_stack_sx(ctx, main_nav, sub_nav, "Essays", "/essays/sx-sucks",
selected=current or "")
content = _essay_content_sx(slug)
return _full_page(ctx, header_rows=hdr, content=content)
def _essay_content_sx(slug: str) -> str:
builders = {
"sx-sucks": _essay_sx_sucks,