Fix _aser_call and sx_call list serialization: use (list ...) for data arrays

Data lists (dicts, strings, numbers) were wrapped in (<> ...) fragments
which the client rendered as empty DocumentFragments instead of iterable
arrays. This broke map/filter over cards, tag_groups, and authors in
blog index and similar components.

- _aser_call: data lists → (list ...), rendered content (SxExpr) → (<> ...)
- sx_call: all list kwargs → (list ...)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 20:11:11 +00:00
parent 959e63d440
commit 0456b3d25c
2 changed files with 29 additions and 10 deletions

View File

@@ -1253,15 +1253,25 @@ async def _aser_call(
extra_class = val.class_name
else:
parts.append(f":{arg.name}")
# Plain list (e.g. from map) → wrap as fragment to
# avoid ambiguity with function application on re-parse
# Plain list → serialize for the client.
# Rendered items (SxExpr) → wrap in (<> ...) fragment.
# Data items (dicts, strings, numbers) → (list ...)
# so the client gets an iterable array, not a
# DocumentFragment that breaks map/filter.
if isinstance(val, list):
items = [serialize(v) for v in val
if v is not NIL and v is not None]
parts.append(
"(<> " + " ".join(items) + ")" if items
else "nil"
)
live = [v for v in val
if v is not NIL and v is not None]
items = [serialize(v) for v in live]
if not items:
parts.append("nil")
elif any(isinstance(v, SxExpr) for v in live):
parts.append(
"(<> " + " ".join(items) + ")"
)
else:
parts.append(
"(list " + " ".join(items) + ")"
)
else:
parts.append(serialize(val))
i += 2

View File

@@ -404,13 +404,22 @@ def sx_call(component_name: str, **kwargs: Any) -> str:
Values are serialized: strings are quoted, None becomes nil,
bools become true/false, numbers stay as-is.
List values use ``(list ...)`` so the client gets an iterable array
rather than a rendered fragment.
"""
from .parser import serialize
from .parser import serialize, SxExpr
name = component_name if component_name.startswith("~") else f"~{component_name}"
parts = [name]
for key, val in kwargs.items():
parts.append(f":{key.replace('_', '-')}")
parts.append(serialize(val))
if isinstance(val, list):
items = [serialize(v) for v in val if v is not None]
if not items:
parts.append("nil")
else:
parts.append("(list " + " ".join(items) + ")")
else:
parts.append(serialize(val))
return "(" + " ".join(parts) + ")"