Fix register_sx_layout: use async_eval_slot_to_sx to expand component bodies

async_eval_to_sx serializes component calls without expanding their bodies,
so free variables from _ctx_to_env were passed through as unresolved symbols
to the client. Switch to async_eval_slot_to_sx which expands the top-level
component body server-side, resolving free variables during expansion.

Also inline ~root-header/~root-mobile into layout defcomps rather than using
wrapper defcomps (nested ~component calls in _aser are serialized without
expansion, so wrapper defcomps would still leave free vars unresolved).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 14:48:43 +00:00
parent 45c5e4a0db
commit e4bfd46c48
2 changed files with 16 additions and 19 deletions

View File

@@ -427,15 +427,20 @@ async def render_to_sx_with_env(__name: str, extra_env: dict, **kwargs: Any) ->
"""Like ``render_to_sx`` but merges *extra_env* into the evaluation
environment before eval. Used by ``register_sx_layout`` so .sx
defcomps can read ctx values as free variables.
Uses ``async_eval_slot_to_sx`` (not ``async_eval_to_sx``) so the
top-level component body is expanded server-side — free variables
from *extra_env* are resolved during expansion rather than being
serialized as unresolved symbols for the client.
"""
from .jinja_bridge import get_component_env, _get_request_context
from .async_eval import async_eval_to_sx
from .async_eval import async_eval_slot_to_sx
ast = _build_component_ast(__name, **kwargs)
env = dict(get_component_env())
env.update(extra_env)
ctx = _get_request_context()
return await async_eval_to_sx(ast, env, ctx)
return await async_eval_slot_to_sx(ast, env, ctx)
async def render_to_sx(__name: str, **kwargs: Any) -> str: