Phase 5: Page layouts as s-expressions — components, fragments, error pages

Add 9 new shared s-expression components (cart-mini, auth-menu,
account-nav-item, calendar-entry-nav, calendar-link-nav, market-link-nav,
post-card, base-shell, error-page) and wire them into all fragment route
handlers. 404/403 error pages now render entirely via s-expressions as a
full-page proof-of-concept, with Jinja fallback on failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 15:25:11 +00:00
parent 04419a1ec6
commit 8013317b41
9 changed files with 687 additions and 41 deletions

View File

@@ -57,6 +57,18 @@ def _error_page(message: str) -> str:
)
def _sexp_error_page(errnum: str, message: str, image: str | None = None) -> str:
"""Render an error page via s-expressions. Bypasses Jinja entirely."""
from shared.sexp.page import render_page
return render_page(
'(~error-page :title title :message message :image image :asset-url "/static")',
title=f"{errnum} Error",
message=message,
image=image,
)
def errors(app):
def _info(e):
return {
@@ -82,10 +94,17 @@ def errors(app):
errnum='404'
)
else:
html = await render_template(
"_types/root/exceptions/_.html",
errnum='404',
)
# Render via s-expressions (Phase 5 proof-of-concept)
try:
html = _sexp_error_page(
"404", "NOT FOUND",
image="/static/errors/404.gif",
)
except Exception:
html = await render_template(
"_types/root/exceptions/_.html",
errnum='404',
)
return await make_response(html, 404)
@@ -98,10 +117,16 @@ def errors(app):
errnum='403'
)
else:
html = await render_template(
"_types/root/exceptions/_.html",
errnum='403',
)
try:
html = _sexp_error_page(
"403", "FORBIDDEN",
image="/static/errors/403.gif",
)
except Exception:
html = await render_template(
"_types/root/exceptions/_.html",
errnum='403',
)
return await make_response(html, 403)