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:
@@ -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 = """\
|
||||
|
||||
Reference in New Issue
Block a user