URL restructure, 404 page, trailing slash normalization, layout fixes
- Rename /reactive-islands/ → /reactive/, /reference/ → /hypermedia/reference/, /examples/ → /hypermedia/examples/ across all .sx and .py files - Add 404 error page (not-found.sx) working on both server refresh and client-side SX navigation via orchestration.sx error response handling - Add trailing slash redirect (GET only, excludes /api/, /static/, /internal/) - Remove blue sky-500 header bar from SX docs layout (conditional on header-rows) - Fix 405 on API endpoints from trailing slash redirect hitting POST/PUT/DELETE - Fix client-side 404: orchestration.sx now swaps error response content instead of silently dropping it - Add new plan files and home page component Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -501,6 +501,41 @@ def _render_lake(args: list, env: dict[str, Any]) -> str:
|
||||
return f'<{lake_tag} data-sx-lake="{_escape_attr(lake_id)}">{body}</{lake_tag}>'
|
||||
|
||||
|
||||
def _render_marsh(args: list, env: dict[str, Any]) -> str:
|
||||
"""Render a reactive server-morphable marsh slot.
|
||||
|
||||
(marsh :id "name" :tag "div" :transform fn children...)
|
||||
→ <div data-sx-marsh="name">children</div>
|
||||
|
||||
Marshes are zones where reactivity and hypermedia interpenetrate.
|
||||
Like lakes but content is parsed as SX on the client and re-evaluated
|
||||
in the island's signal scope. :transform is consumed but not used
|
||||
server-side (it's a client-side concern).
|
||||
"""
|
||||
marsh_id = ""
|
||||
marsh_tag = "div"
|
||||
children: list[Any] = []
|
||||
i = 0
|
||||
while i < len(args):
|
||||
arg = args[i]
|
||||
if isinstance(arg, Keyword) and i + 1 < len(args):
|
||||
kname = arg.name
|
||||
kval = _eval(args[i + 1], env)
|
||||
if kname == "id":
|
||||
marsh_id = str(kval) if kval is not None and kval is not NIL else ""
|
||||
elif kname == "tag":
|
||||
marsh_tag = str(kval) if kval is not None and kval is not NIL else "div"
|
||||
elif kname == "transform":
|
||||
pass # Client-side only; skip
|
||||
i += 2
|
||||
else:
|
||||
children.append(arg)
|
||||
i += 1
|
||||
|
||||
body = "".join(_render(c, env) for c in children)
|
||||
return f'<{marsh_tag} data-sx-marsh="{_escape_attr(marsh_id)}">{body}</{marsh_tag}>'
|
||||
|
||||
|
||||
def _render_list(expr: list, env: dict[str, Any]) -> str:
|
||||
"""Render a list expression — could be an HTML element, special form,
|
||||
component call, or data list."""
|
||||
@@ -530,6 +565,10 @@ def _render_list(expr: list, env: dict[str, Any]) -> str:
|
||||
if name == "lake":
|
||||
return _render_lake(expr[1:], env)
|
||||
|
||||
# --- marsh → reactive server-morphable slot within island --------
|
||||
if name == "marsh":
|
||||
return _render_marsh(expr[1:], env)
|
||||
|
||||
# --- html: prefix → force tag rendering --------------------------
|
||||
if name.startswith("html:"):
|
||||
return _render_element(name[5:], expr[1:], env)
|
||||
|
||||
Reference in New Issue
Block a user