SX docs: configurable shell, SX-native event handlers, nav fixes

- Configurable page shell (~sx-page-shell kwargs + SX_SHELL app config)
  so each app controls its own assets — sx docs loads only sx-browser.js
- SX-evaluated sx-on:* handlers (eval-expr instead of new Function)
  with DOM primitives registered in PRIMITIVES table
- data-init boot mode for pure SX initialization scripts
- Jiggle animation on links while fetching
- Nav: 3-column grid for centered alignment, is-leaf sizing,
  fix map-indexed param order (index, item), guard mod-by-zero
- Async route eval failure now falls back to server fetch
  instead of silently rendering nothing
- Remove duplicate h1 title from ~doc-page
- Re-bootstrap sx-ref.js + sx-browser.js

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 11:00:59 +00:00
parent 31a6e708fc
commit 8a5c115557
20 changed files with 5763 additions and 3046 deletions

View File

@@ -293,7 +293,12 @@ async def oob_page_sx(*, oobs: str = "", filter: str = "", aside: str = "",
async def full_page_sx(ctx: dict, *, header_rows: str,
filter: str = "", aside: str = "",
content: str = "", menu: str = "",
meta_html: str = "", meta: str = "") -> str:
meta_html: str = "", meta: str = "",
head_scripts: list[str] | None = None,
inline_css: str | None = None,
inline_head_js: str | None = None,
init_sx: str | None = None,
body_scripts: list[str] | None = None) -> str:
"""Build a full page using sx_page() with ~app-body.
meta_html: raw HTML injected into the <head> shell (legacy).
@@ -313,7 +318,10 @@ async def full_page_sx(ctx: dict, *, header_rows: str,
# Wrap body + meta in a fragment so sx.js renders both;
# auto-hoist moves meta/title/link elements to <head>.
body_sx = _sx_fragment(meta, body_sx)
return await sx_page(ctx, body_sx, meta_html=meta_html)
return await sx_page(ctx, body_sx, meta_html=meta_html,
head_scripts=head_scripts, inline_css=inline_css,
inline_head_js=inline_head_js, init_sx=init_sx,
body_scripts=body_scripts)
def _build_component_ast(__name: str, **kwargs: Any) -> list:
@@ -518,8 +526,12 @@ def components_for_request(source: str = "",
if val.has_children:
param_strs.extend(["&rest", "children"])
params_sx = "(" + " ".join(param_strs) + ")"
body_sx = serialize(val.body, pretty=True)
parts.append(f"(defcomp ~{val.name} {params_sx} {body_sx})")
body_sx = serialize(val.body, indent=1, pretty=True)
head = f"(defcomp ~{val.name} {params_sx}"
if "\n" in body_sx:
parts.append(f"{head}\n {body_sx})")
else:
parts.append(f"{head} {body_sx})")
elif isinstance(val, Macro):
if val.name in loaded:
continue
@@ -527,8 +539,12 @@ def components_for_request(source: str = "",
if val.rest_param:
param_strs.extend(["&rest", val.rest_param])
params_sx = "(" + " ".join(param_strs) + ")"
body_sx = serialize(val.body, pretty=True)
parts.append(f"(defmacro {val.name} {params_sx} {body_sx})")
body_sx = serialize(val.body, indent=1, pretty=True)
head = f"(defmacro {val.name} {params_sx}"
if "\n" in body_sx:
parts.append(f"{head}\n {body_sx})")
else:
parts.append(f"{head} {body_sx})")
return "\n".join(parts)
@@ -752,7 +768,12 @@ def _sx_literal(v: object) -> str:
async def sx_page(ctx: dict, page_sx: str, *,
meta_html: str = "") -> str:
meta_html: str = "",
head_scripts: list[str] | None = None,
inline_css: str | None = None,
inline_head_js: str | None = None,
init_sx: str | None = None,
body_scripts: list[str] | None = None) -> str:
"""Return a minimal HTML shell that boots the page from sx source.
The browser loads component definitions and page sx, then sx.js
@@ -817,8 +838,21 @@ async def sx_page(ctx: dict, page_sx: str, *,
if isinstance(page_sx, SxExpr):
page_sx = "".join([page_sx])
return await render_to_html(
"sx-page-shell",
# Per-app shell config: check explicit args, then app config, then defaults
from quart import current_app as _app
_shell_cfg = _app.config.get("SX_SHELL", {})
if head_scripts is None:
head_scripts = _shell_cfg.get("head_scripts")
if inline_css is None:
inline_css = _shell_cfg.get("inline_css")
if inline_head_js is None:
inline_head_js = _shell_cfg.get("inline_head_js")
if init_sx is None:
init_sx = _shell_cfg.get("init_sx")
if body_scripts is None:
body_scripts = _shell_cfg.get("body_scripts")
shell_kwargs: dict[str, Any] = dict(
title=_html_escape(title),
asset_url=asset_url,
meta_html=meta_html,
@@ -832,6 +866,17 @@ async def sx_page(ctx: dict, page_sx: str, *,
sx_js_hash=_script_hash("sx-browser.js"),
body_js_hash=_script_hash("body.js"),
)
if head_scripts is not None:
shell_kwargs["head_scripts"] = head_scripts
if inline_css is not None:
shell_kwargs["inline_css"] = inline_css
if inline_head_js is not None:
shell_kwargs["inline_head_js"] = inline_head_js
if init_sx is not None:
shell_kwargs["init_sx"] = init_sx
if body_scripts is not None:
shell_kwargs["body_scripts"] = body_scripts
return await render_to_html("sx-page-shell", **shell_kwargs)
_SX_STREAMING_RESOLVE = """\