Add register_sx_layout infrastructure, convert account/federation/orders
Phase 0: Add _ctx_to_env() and render_to_sx_with_env() to shared/sx/helpers.py, register_sx_layout() to shared/sx/layouts.py, and ~root-header/~root-mobile wrapper defcomps to layout.sx. Convert built-in "root" layout to .sx. Phases 1-3: Convert account (65→19 lines), federation (105→97 lines), and orders (88→21 lines) to use register_sx_layout with .sx defcomps that read ctx values as free variables from the evaluation environment. No more Python building SX strings via SxExpr(await root_header_sx(ctx)). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
17
federation/sx/layouts.sx
Normal file
17
federation/sx/layouts.sx
Normal file
@@ -0,0 +1,17 @@
|
||||
;; Federation layout defcomps — read ctx values from env free variables.
|
||||
;; `actor` is injected into env by the layout registration in __init__.py.
|
||||
|
||||
;; Full page: root header + social header in header-child
|
||||
(defcomp ~social-layout-full ()
|
||||
(<> (~root-header)
|
||||
(~header-child-sx
|
||||
:inner (~federation-social-header
|
||||
:nav (~federation-social-nav :actor actor)))))
|
||||
|
||||
;; OOB (HTMX): social header oob + root header oob
|
||||
(defcomp ~social-layout-oob ()
|
||||
(<> (~oob-header-sx
|
||||
:parent-id "root-header-child"
|
||||
:row (~federation-social-header
|
||||
:nav (~federation-social-nav :actor actor)))
|
||||
(~root-header :oob true)))
|
||||
@@ -17,7 +17,7 @@ def _load_federation_page_files() -> None:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Layouts
|
||||
# Layouts — .sx defcomps read free variables from env
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _register_federation_layouts() -> None:
|
||||
@@ -25,36 +25,30 @@ def _register_federation_layouts() -> None:
|
||||
register_custom_layout("social", _social_full, _social_oob)
|
||||
|
||||
|
||||
async def _social_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx, render_to_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
def _actor_data(ctx: dict) -> dict | None:
|
||||
actor = ctx.get("actor")
|
||||
actor_data = _serialize_actor(actor) if actor else None
|
||||
nav = await render_to_sx("federation-social-nav", actor=actor_data)
|
||||
social_hdr = await render_to_sx("federation-social-header", nav=SxExpr(nav))
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
child = await header_child_sx(social_hdr)
|
||||
return "(<> " + root_hdr + " " + child + ")"
|
||||
if not actor:
|
||||
return None
|
||||
from services.federation_page import _serialize_actor
|
||||
return _serialize_actor(actor)
|
||||
|
||||
|
||||
async def _social_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, _ctx_to_env
|
||||
env = _ctx_to_env(ctx)
|
||||
env["actor"] = kw.get("actor") or _actor_data(ctx)
|
||||
return await render_to_sx_with_env("social-layout-full", env)
|
||||
|
||||
|
||||
async def _social_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, render_to_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
actor = ctx.get("actor")
|
||||
actor_data = _serialize_actor(actor) if actor else None
|
||||
nav = await render_to_sx("federation-social-nav", actor=actor_data)
|
||||
social_hdr = await render_to_sx("federation-social-header", nav=SxExpr(nav))
|
||||
child_oob = await render_to_sx("oob-header-sx",
|
||||
parent_id="root-header-child",
|
||||
row=SxExpr(social_hdr))
|
||||
root_hdr_oob = await root_header_sx(ctx, oob=True)
|
||||
return "(<> " + child_oob + " " + root_hdr_oob + ")"
|
||||
from shared.sx.helpers import render_to_sx_with_env, _ctx_to_env
|
||||
env = _ctx_to_env(ctx, oob=True)
|
||||
env["actor"] = kw.get("actor") or _actor_data(ctx)
|
||||
return await render_to_sx_with_env("social-layout-oob", env)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Serializers and helpers — still used by layouts and route handlers
|
||||
# Helpers still used by route handlers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _serialize_actor(actor) -> dict | None:
|
||||
@@ -78,16 +72,12 @@ def _serialize_remote_actor(a) -> dict:
|
||||
async def _social_page(ctx: dict, actor, *, content: str,
|
||||
title: str = "Rose Ash", meta_html: str = "") -> str:
|
||||
"""Build a full social page with social header."""
|
||||
from shared.sx.helpers import render_to_sx, root_header_sx, header_child_sx, full_page_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.sx.helpers import render_to_sx_with_env, _ctx_to_env, full_page_sx
|
||||
from markupsafe import escape
|
||||
|
||||
actor_data = _serialize_actor(actor)
|
||||
nav = await render_to_sx("federation-social-nav", actor=actor_data)
|
||||
social_hdr = await render_to_sx("federation-social-header", nav=SxExpr(nav))
|
||||
hdr = await root_header_sx(ctx)
|
||||
child = await header_child_sx(social_hdr)
|
||||
header_rows = "(<> " + hdr + " " + child + ")"
|
||||
env = _ctx_to_env(ctx)
|
||||
env["actor"] = _serialize_actor(actor) if actor else None
|
||||
header_rows = await render_to_sx_with_env("social-layout-full", env)
|
||||
return await full_page_sx(ctx, header_rows=header_rows, content=content,
|
||||
meta_html=meta_html or f'<title>{escape(title)}</title>')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user