diff --git a/shared/sx/async_eval.py b/shared/sx/async_eval.py index 7eed7b3..b810129 100644 --- a/shared/sx/async_eval.py +++ b/shared/sx/async_eval.py @@ -1013,6 +1013,8 @@ async def async_eval_to_sx( return result if result is None or result is NIL: return SxExpr("") + if isinstance(result, str): + return SxExpr(result) return SxExpr(serialize(result)) @@ -1042,6 +1044,8 @@ async def async_eval_slot_to_sx( return result if result is None or result is NIL: return SxExpr("") + if isinstance(result, str): + return SxExpr(result) return SxExpr(serialize(result)) else: import logging @@ -1059,6 +1063,9 @@ async def async_eval_slot_to_sx( return result if result is None or result is NIL: return SxExpr("") + if isinstance(result, str): + # Plain strings from page helpers are already sx source — don't quote. + return SxExpr(result) return SxExpr(serialize(result)) diff --git a/shared/sx/helpers.py b/shared/sx/helpers.py index 6d7fba0..1d89727 100644 --- a/shared/sx/helpers.py +++ b/shared/sx/helpers.py @@ -347,8 +347,19 @@ async def _render_to_sx_with_env(__name: str, extra_env: dict, **kwargs: Any) -> """ from .jinja_bridge import get_component_env, _get_request_context from .async_eval import async_eval_slot_to_sx + from .types import Symbol, Keyword, NIL as _NIL + + # Build AST with extra_env entries as keyword args so _aser_component + # binds them as params (otherwise it defaults all params to NIL). + comp_sym = Symbol(__name if __name.startswith("~") else f"~{__name}") + ast: list = [comp_sym] + for k, v in extra_env.items(): + ast.append(Keyword(k)) + ast.append(v if v is not None else _NIL) + for k, v in kwargs.items(): + ast.append(Keyword(k.replace("_", "-"))) + ast.append(v if v is not None else _NIL) - ast = _build_component_ast(__name, **kwargs) env = dict(get_component_env()) env.update(extra_env) ctx = _get_request_context() diff --git a/sx/sxc/pages/utils.py b/sx/sxc/pages/utils.py index f08e5de..9012cb9 100644 --- a/sx/sxc/pages/utils.py +++ b/sx/sxc/pages/utils.py @@ -6,7 +6,7 @@ from shared.sx.helpers import ( ) -def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None) -> str: +def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None) -> SxExpr: """Build nav link items as sx.""" parts = [] for label, href in items: @@ -15,7 +15,7 @@ def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None) -> s is_selected="true" if current == label else None, select_colours="aria-selected:bg-violet-200 aria-selected:text-violet-900", )) - return "(<> " + " ".join(parts) + ")" + return SxExpr("(<> " + " ".join(parts) + ")") def _doc_nav_sx(items: list[tuple[str, str]], current: str) -> str: