Files
mono/sx/sxc/pages/essays.py
giles 36a0bd8577 Move sx docs markup from Python to .sx files (Phase 2)
Migrate ~2,500 lines of SX markup from Python string concatenation in
essays.py to proper .sx defcomp definitions:

- docs-content.sx: 8 defcomps for docs pages (intro, getting-started,
  components, evaluator, primitives, css, server-rendering, home)
- protocols.sx: 6 defcomps for protocol documentation pages
- essays.sx: 9 essay defcomps (pure content, no params)
- examples.sx: template defcomp receiving data values, calls highlight
  internally — Python passes raw code strings, never SX
- reference.sx: 6 defcomps for data-driven reference pages

essays.py reduced from 2,699 to 619 lines. Docs/protocol/essay
functions become one-liners returning component names. Example functions
use sx_call to pass data values to the template. Reference functions
pass data-built component trees via SxExpr.

renders.py: removed _code, _example_code, _placeholder,
_clear_components_btn (now handled by .sx templates).
helpers.py: removed inline hero code building, uses ~sx-home-content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 00:22:17 +00:00

620 lines
36 KiB
Python

"""All content generator functions and dispatchers for sx docs pages.
Content markup lives in .sx files (sx/sx/*.sx). Python functions here are thin
dispatchers that either return a component name or pass data values via sx_call.
"""
from __future__ import annotations
from shared.sx.helpers import sx_call, SxExpr
from .utils import _attr_table_sx, _primitives_section_sx, _headers_table_sx
# ---------------------------------------------------------------------------
# Dispatcher functions — route slugs to content builders
# ---------------------------------------------------------------------------
async def _docs_content_sx(slug: str) -> str:
"""Route to the right docs content builder."""
import inspect
builders = {
"introduction": _docs_introduction_sx,
"getting-started": _docs_getting_started_sx,
"components": _docs_components_sx,
"evaluator": _docs_evaluator_sx,
"primitives": _docs_primitives_sx,
"css": _docs_css_sx,
"server-rendering": _docs_server_rendering_sx,
}
builder = builders.get(slug, _docs_introduction_sx)
result = builder()
return await result if inspect.isawaitable(result) else result
async def _reference_content_sx(slug: str) -> str:
import inspect
builders = {
"attributes": _reference_attrs_sx,
"headers": _reference_headers_sx,
"events": _reference_events_sx,
"js-api": _reference_js_api_sx,
}
result = builders.get(slug or "", _reference_attrs_sx)()
return await result if inspect.isawaitable(result) else result
def _protocol_content_sx(slug: str) -> str:
builders = {
"wire-format": _protocol_wire_format_sx,
"fragments": _protocol_fragments_sx,
"resolver-io": _protocol_resolver_io_sx,
"internal-services": _protocol_internal_services_sx,
"activitypub": _protocol_activitypub_sx,
"future": _protocol_future_sx,
}
return builders.get(slug, _protocol_wire_format_sx)()
def _examples_content_sx(slug: str) -> str:
builders = {
"click-to-load": _example_click_to_load_sx,
"form-submission": _example_form_submission_sx,
"polling": _example_polling_sx,
"delete-row": _example_delete_row_sx,
"inline-edit": _example_inline_edit_sx,
"oob-swaps": _example_oob_swaps_sx,
"lazy-loading": _example_lazy_loading_sx,
"infinite-scroll": _example_infinite_scroll_sx,
"progress-bar": _example_progress_bar_sx,
"active-search": _example_active_search_sx,
"inline-validation": _example_inline_validation_sx,
"value-select": _example_value_select_sx,
"reset-on-submit": _example_reset_on_submit_sx,
"edit-row": _example_edit_row_sx,
"bulk-update": _example_bulk_update_sx,
"swap-positions": _example_swap_positions_sx,
"select-filter": _example_select_filter_sx,
"tabs": _example_tabs_sx,
"animations": _example_animations_sx,
"dialogs": _example_dialogs_sx,
"keyboard-shortcuts": _example_keyboard_shortcuts_sx,
"put-patch": _example_put_patch_sx,
"json-encoding": _example_json_encoding_sx,
"vals-and-headers": _example_vals_and_headers_sx,
"loading-states": _example_loading_states_sx,
"sync-replace": _example_sync_replace_sx,
"retry": _example_retry_sx,
}
return builders.get(slug, _example_click_to_load_sx)()
def _essay_content_sx(slug: str) -> str:
builders = {
"sx-sucks": _essay_sx_sucks,
"why-sexps": _essay_why_sexps,
"htmx-react-hybrid": _essay_htmx_react_hybrid,
"on-demand-css": _essay_on_demand_css,
"client-reactivity": _essay_client_reactivity,
"sx-native": _essay_sx_native,
"sx-manifesto": _essay_sx_manifesto,
"tail-call-optimization": _essay_tail_call_optimization,
"continuations": _essay_continuations,
}
return builders.get(slug, _essay_sx_sucks)()
# ---------------------------------------------------------------------------
# Docs content — self-contained .sx components (sx/sx/docs-content.sx)
# ---------------------------------------------------------------------------
def _docs_introduction_sx() -> str:
return "(~docs-introduction-content)"
def _docs_getting_started_sx() -> str:
return "(~docs-getting-started-content)"
def _docs_components_sx() -> str:
return "(~docs-components-content)"
def _docs_evaluator_sx() -> str:
return "(~docs-evaluator-content)"
def _docs_primitives_sx() -> str:
prims = _primitives_section_sx()
return sx_call("docs-primitives-content", prims=SxExpr(prims))
def _docs_css_sx() -> str:
return "(~docs-css-content)"
def _docs_server_rendering_sx() -> str:
return "(~docs-server-rendering-content)"
# ---------------------------------------------------------------------------
# Protocol content — self-contained .sx components (sx/sx/protocols.sx)
# ---------------------------------------------------------------------------
def _protocol_wire_format_sx() -> str:
return "(~protocol-wire-format-content)"
def _protocol_fragments_sx() -> str:
return "(~protocol-fragments-content)"
def _protocol_resolver_io_sx() -> str:
return "(~protocol-resolver-io-content)"
def _protocol_internal_services_sx() -> str:
return "(~protocol-internal-services-content)"
def _protocol_activitypub_sx() -> str:
return "(~protocol-activitypub-content)"
def _protocol_future_sx() -> str:
return "(~protocol-future-content)"
# ---------------------------------------------------------------------------
# Essay content — self-contained .sx components (sx/sx/essays.sx)
# ---------------------------------------------------------------------------
def _essay_sx_sucks() -> str:
return "(~essay-sx-sucks)"
def _essay_why_sexps() -> str:
return "(~essay-why-sexps)"
def _essay_htmx_react_hybrid() -> str:
return "(~essay-htmx-react-hybrid)"
def _essay_on_demand_css() -> str:
return "(~essay-on-demand-css)"
def _essay_client_reactivity() -> str:
return "(~essay-client-reactivity)"
def _essay_sx_native() -> str:
return "(~essay-sx-native)"
def _essay_sx_manifesto() -> str:
return "(~essay-sx-manifesto)"
def _essay_tail_call_optimization() -> str:
return "(~essay-tail-call-optimization)"
def _essay_continuations() -> str:
return "(~essay-continuations)"
# ---------------------------------------------------------------------------
# Reference pages — data-driven, page layouts in .sx (sx/sx/reference.sx)
# ---------------------------------------------------------------------------
def _reference_index_sx() -> str:
return "(~reference-index-content)"
def _reference_attr_detail_sx(slug: str) -> str:
from content.pages import ATTR_DETAILS
detail = ATTR_DETAILS.get(slug)
if not detail:
return sx_call("reference-attr-not-found", slug=slug)
demo_name = detail.get("demo")
demo = SxExpr(f"(~{demo_name})") if demo_name else None
wire_placeholder_id = None
if "handler" in detail:
wire_id = slug.replace(":", "-").replace("*", "star")
wire_placeholder_id = f"ref-wire-{wire_id}"
return sx_call("reference-attr-detail-content",
title=slug,
description=detail["description"],
demo=demo,
example_code=detail["example"],
handler_code=detail.get("handler"),
wire_placeholder_id=wire_placeholder_id)
def _reference_attrs_sx() -> str:
from content.pages import REQUEST_ATTRS, BEHAVIOR_ATTRS, SX_UNIQUE_ATTRS
return sx_call("reference-attrs-content",
req_table=SxExpr(_attr_table_sx("Request Attributes", REQUEST_ATTRS)),
beh_table=SxExpr(_attr_table_sx("Behavior Attributes", BEHAVIOR_ATTRS)),
uniq_table=SxExpr(_attr_table_sx("Unique to sx", SX_UNIQUE_ATTRS)))
def _reference_headers_sx() -> str:
from content.pages import REQUEST_HEADERS, RESPONSE_HEADERS
return sx_call("reference-headers-content",
req_table=SxExpr(_headers_table_sx("Request Headers", REQUEST_HEADERS)),
resp_table=SxExpr(_headers_table_sx("Response Headers", RESPONSE_HEADERS)))
def _reference_events_sx() -> str:
from content.pages import EVENTS
rows = []
for name, desc in EVENTS:
rows.append(sx_call("doc-two-col-row", name=name, description=desc))
rows_sx = "(<> " + " ".join(rows) + ")"
table = sx_call("doc-two-col-table",
intro="sx fires custom DOM events at various points in the request lifecycle.",
col1="Event", col2="Description", rows=SxExpr(rows_sx))
return sx_call("reference-events-content", table=SxExpr(table))
def _reference_js_api_sx() -> str:
from content.pages import JS_API
rows = []
for name, desc in JS_API:
rows.append(sx_call("doc-two-col-row", name=name, description=desc))
rows_sx = "(<> " + " ".join(rows) + ")"
table = sx_call("doc-two-col-table",
intro="The client-side sx.js library exposes a public API for programmatic use.",
col1="Method", col2="Description", rows=SxExpr(rows_sx))
return sx_call("reference-js-api-content", table=SxExpr(table))
# ---------------------------------------------------------------------------
# Example pages — template in .sx (sx/sx/examples.sx), data values from Python
# ---------------------------------------------------------------------------
def _build_example(title, description, demo_description, demo, sx_code,
handler_code, *, comp_placeholder_id=None,
wire_placeholder_id=None, wire_note=None,
comp_heading=None, handler_heading=None) -> str:
"""Build an example page by passing data values to the .sx template."""
kw = dict(
title=title, description=description,
demo_description=demo_description,
demo=SxExpr(demo),
sx_code=sx_code, handler_code=handler_code,
)
if comp_placeholder_id:
kw["comp_placeholder_id"] = comp_placeholder_id
if wire_placeholder_id:
kw["wire_placeholder_id"] = wire_placeholder_id
if wire_note:
kw["wire_note"] = wire_note
if comp_heading:
kw["comp_heading"] = comp_heading
if handler_heading:
kw["handler_heading"] = handler_heading
return sx_call("example-page-content", **kw)
def _example_click_to_load_sx() -> str:
return _build_example(
title="Click to Load",
description="The simplest sx interaction: click a button, fetch content from the server, swap it in.",
demo_description="Click the button to load server-rendered content.",
demo="(~click-to-load-demo)",
sx_code='(button\n :sx-get "/examples/api/click"\n :sx-target "#click-result"\n :sx-swap "innerHTML"\n "Load content")',
handler_code='@bp.get("/examples/api/click")\nasync def api_click():\n now = datetime.now().strftime(...)\n return sx_response(\n f\'(~click-result :time "{now}")\')',
comp_placeholder_id="click-comp",
wire_placeholder_id="click-wire",
wire_note="The server responds with content-type text/sx. New CSS rules are prepended as a style tag. Clear the component cache to see component definitions included in the wire response.")
def _example_form_submission_sx() -> str:
return _build_example(
title="Form Submission",
description="Forms with sx-post submit via AJAX and swap the response into a target.",
demo_description="Enter a name and submit.",
demo="(~form-demo)",
sx_code='(form\n :sx-post "/examples/api/form"\n :sx-target "#form-result"\n :sx-swap "innerHTML"\n (input :type "text" :name "name")\n (button :type "submit" "Submit"))',
handler_code='@bp.post("/examples/api/form")\nasync def api_form():\n form = await request.form\n name = form.get("name", "")\n return sx_response(\n f\'(~form-result :name "{name}")\')',
comp_placeholder_id="form-comp",
wire_placeholder_id="form-wire")
def _example_polling_sx() -> str:
return _build_example(
title="Polling",
description='Use sx-trigger with "every" to poll the server at regular intervals.',
demo_description="This div polls the server every 2 seconds.",
demo="(~polling-demo)",
sx_code='(div\n :sx-get "/examples/api/poll"\n :sx-trigger "load, every 2s"\n :sx-swap "innerHTML"\n "Loading...")',
handler_code='@bp.get("/examples/api/poll")\nasync def api_poll():\n poll_count["n"] += 1\n now = datetime.now().strftime("%H:%M:%S")\n count = min(poll_count["n"], 10)\n return sx_response(\n f\'(~poll-result :time "{now}" :count {count})\')',
comp_placeholder_id="poll-comp",
wire_placeholder_id="poll-wire",
wire_note="Updates every 2 seconds — watch the time and count change.")
def _example_delete_row_sx() -> str:
from content.pages import DELETE_DEMO_ITEMS
items_sx = " ".join(f'(list "{id}" "{name}")' for id, name in DELETE_DEMO_ITEMS)
return _build_example(
title="Delete Row",
description='sx-delete with sx-swap "outerHTML" and an empty response removes the row from the DOM.',
demo_description="Click delete to remove a row. Uses sx-confirm for confirmation.",
demo=f"(~delete-demo :items (list {items_sx}))",
sx_code='(button\n :sx-delete "/api/delete/1"\n :sx-target "#row-1"\n :sx-swap "outerHTML"\n :sx-confirm "Delete this item?"\n "delete")',
handler_code='@bp.delete("/examples/api/delete/<item_id>")\nasync def api_delete(item_id: str):\n # Empty response — outerHTML swap removes the row\n return Response("", status=200,\n content_type="text/sx")',
comp_placeholder_id="delete-comp",
wire_placeholder_id="delete-wire",
wire_note="Empty body — outerHTML swap replaces the target element with nothing.")
def _example_inline_edit_sx() -> str:
return _build_example(
title="Inline Edit",
description="Click edit to swap a display view for an edit form. Save swaps back.",
demo_description="Click edit, modify the text, save or cancel.",
demo="(~inline-edit-demo)",
sx_code=';; View mode — shows text + edit button\n(~inline-view :value "some text")\n\n;; Edit mode — returned by server on click\n(~inline-edit-form :value "some text")',
handler_code='@bp.get("/examples/api/edit")\nasync def api_edit_form():\n value = request.args.get("value", "")\n return sx_response(\n f\'(~inline-edit-form :value "{value}")\')\n\n@bp.post("/examples/api/edit")\nasync def api_edit_save():\n form = await request.form\n value = form.get("value", "")\n return sx_response(\n f\'(~inline-view :value "{value}")\')',
comp_placeholder_id="edit-comp",
comp_heading="Components",
handler_heading="Server handlers",
wire_placeholder_id="edit-wire")
def _example_oob_swaps_sx() -> str:
return _build_example(
title="Out-of-Band Swaps",
description="sx-swap-oob lets a single response update multiple elements anywhere in the DOM.",
demo_description="One request updates both Box A (via sx-target) and Box B (via sx-swap-oob).",
demo="(~oob-demo)",
sx_code=';; Button targets Box A\n(button\n :sx-get "/examples/api/oob"\n :sx-target "#oob-box-a"\n :sx-swap "innerHTML"\n "Update both boxes")',
handler_code='@bp.get("/examples/api/oob")\nasync def api_oob():\n now = datetime.now().strftime("%H:%M:%S")\n return sx_response(\n f\'(<>\'\n f\' (p "Box A updated at {now}")\'\n f\' (div :id "oob-box-b"\'\n f\' :sx-swap-oob "innerHTML"\'\n f\' (p "Box B updated at {now}")))\')',
wire_placeholder_id="oob-wire",
wire_note="The fragment contains both the main content and an OOB element. sx.js splits them: main content goes to sx-target, OOB elements find their targets by ID.")
def _example_lazy_loading_sx() -> str:
return _build_example(
title="Lazy Loading",
description='Use sx-trigger="load" to fetch content as soon as the element enters the DOM. Great for deferring expensive content below the fold.',
demo_description="Content loads automatically when the page renders.",
demo="(~lazy-loading-demo)",
sx_code='(div\n :sx-get "/examples/api/lazy"\n :sx-trigger "load"\n :sx-swap "innerHTML"\n (div :class "animate-pulse" "Loading..."))',
handler_code='@bp.get("/examples/api/lazy")\nasync def api_lazy():\n now = datetime.now().strftime(...)\n return sx_response(\n f\'(~lazy-result :time "{now}")\')',
comp_placeholder_id="lazy-comp",
wire_placeholder_id="lazy-wire")
def _example_infinite_scroll_sx() -> str:
return _build_example(
title="Infinite Scroll",
description='A sentinel element at the bottom uses sx-trigger="intersect once" to load the next page when scrolled into view. Each response appends items and a new sentinel.',
demo_description="Scroll down in the container to load more items (5 pages total).",
demo="(~infinite-scroll-demo)",
sx_code='(div :id "scroll-sentinel"\n :sx-get "/examples/api/scroll?page=2"\n :sx-trigger "intersect once"\n :sx-target "#scroll-items"\n :sx-swap "beforeend"\n "Loading more...")',
handler_code='@bp.get("/examples/api/scroll")\nasync def api_scroll():\n page = int(request.args.get("page", 2))\n items = [f"Item {i}" for i in range(...)]\n # Include next sentinel if more pages\n return sx_response(items_sx + sentinel_sx)',
comp_placeholder_id="scroll-comp",
wire_placeholder_id="scroll-wire")
def _example_progress_bar_sx() -> str:
return _build_example(
title="Progress Bar",
description='Start a server-side job, then poll for progress using sx-trigger="load delay:500ms" on each response. The bar fills up and stops when complete.',
demo_description="Click start to begin a simulated job.",
demo="(~progress-bar-demo)",
sx_code=';; Start the job\n(button\n :sx-post "/examples/api/progress/start"\n :sx-target "#progress-target"\n :sx-swap "innerHTML")\n\n;; Each response re-polls via sx-trigger="load"\n(div :sx-get "/api/progress/status?job=ID"\n :sx-trigger "load delay:500ms"\n :sx-target "#progress-target"\n :sx-swap "innerHTML")',
handler_code='@bp.post("/examples/api/progress/start")\nasync def api_progress_start():\n job_id = str(uuid4())[:8]\n _jobs[job_id] = 0\n return sx_response(\n f\'(~progress-status :percent 0 :job-id "{job_id}")\')',
comp_placeholder_id="progress-comp",
wire_placeholder_id="progress-wire")
def _example_active_search_sx() -> str:
return _build_example(
title="Active Search",
description='An input with sx-trigger="keyup delay:300ms changed" debounces keystrokes and only fires when the value changes. The server filters a list of programming languages.',
demo_description="Type to search through 20 programming languages.",
demo="(~active-search-demo)",
sx_code='(input :type "text" :name "q"\n :sx-get "/examples/api/search"\n :sx-trigger "keyup delay:300ms changed"\n :sx-target "#search-results"\n :sx-swap "innerHTML"\n :placeholder "Search...")',
handler_code='@bp.get("/examples/api/search")\nasync def api_search():\n q = request.args.get("q", "").lower()\n results = [l for l in LANGUAGES if q in l.lower()]\n return sx_response(\n f\'(~search-results :items (...) :query "{q}")\')',
comp_placeholder_id="search-comp",
wire_placeholder_id="search-wire")
def _example_inline_validation_sx() -> str:
return _build_example(
title="Inline Validation",
description="Validate an email field on blur. The server checks format and whether it is taken, returning green or red feedback inline.",
demo_description="Enter an email and click away (blur) to validate.",
demo="(~inline-validation-demo)",
sx_code='(input :type "text" :name "email"\n :sx-get "/examples/api/validate"\n :sx-trigger "blur"\n :sx-target "#email-feedback"\n :sx-swap "innerHTML"\n :placeholder "user@example.com")',
handler_code='@bp.get("/examples/api/validate")\nasync def api_validate():\n email = request.args.get("email", "")\n if "@" not in email:\n return sx_response(\'(~validation-error ...)\')\n return sx_response(\'(~validation-ok ...)\')',
comp_placeholder_id="validate-comp",
wire_placeholder_id="validate-wire")
def _example_value_select_sx() -> str:
return _build_example(
title="Value Select",
description="Two linked selects: pick a category and the second select updates with matching items via sx-get.",
demo_description="Select a category to populate the item dropdown.",
demo="(~value-select-demo)",
sx_code='(select :name "category"\n :sx-get "/examples/api/values"\n :sx-trigger "change"\n :sx-target "#value-items"\n :sx-swap "innerHTML"\n (option "Languages")\n (option "Frameworks")\n (option "Databases"))',
handler_code='@bp.get("/examples/api/values")\nasync def api_values():\n cat = request.args.get("category", "")\n items = VALUE_SELECT_DATA.get(cat, [])\n return sx_response(\n f\'(~value-options :items (list ...))\')',
comp_placeholder_id="values-comp",
wire_placeholder_id="values-wire")
def _example_reset_on_submit_sx() -> str:
return _build_example(
title="Reset on Submit",
description='Use sx-on:afterSwap="this.reset()" to clear form inputs after a successful submission.',
demo_description="Submit a message — the input resets after each send.",
demo="(~reset-on-submit-demo)",
sx_code='(form :id "reset-form"\n :sx-post "/examples/api/reset-submit"\n :sx-target "#reset-result"\n :sx-swap "innerHTML"\n :sx-on:afterSwap "this.reset()"\n (input :type "text" :name "message")\n (button :type "submit" "Send"))',
handler_code='@bp.post("/examples/api/reset-submit")\nasync def api_reset_submit():\n form = await request.form\n msg = form.get("message", "")\n return sx_response(\n f\'(~reset-message :message "{msg}" :time "...")\')',
comp_placeholder_id="reset-comp",
wire_placeholder_id="reset-wire")
def _example_edit_row_sx() -> str:
from content.pages import EDIT_ROW_DATA
rows_sx = " ".join(
f'(list "{r["id"]}" "{r["name"]}" "{r["price"]}" "{r["stock"]}")'
for r in EDIT_ROW_DATA
)
return _build_example(
title="Edit Row",
description="Click edit to replace a table row with input fields. Save or cancel swaps back the display row. Uses sx-include to gather form values from the row.",
demo_description="Click edit on any row to modify it inline.",
demo=f"(~edit-row-demo :rows (list {rows_sx}))",
sx_code='(button\n :sx-get "/examples/api/editrow/1"\n :sx-target "#erow-1"\n :sx-swap "outerHTML"\n "edit")\n\n;; Save sends form data via POST\n(button\n :sx-post "/examples/api/editrow/1"\n :sx-target "#erow-1"\n :sx-swap "outerHTML"\n :sx-include "#erow-1"\n "save")',
handler_code='@bp.get("/examples/api/editrow/<id>")\nasync def api_editrow_form(id):\n row = EDIT_ROW_DATA[id]\n return sx_response(\n f\'(~edit-row-form :id ... :name ...)\')\n\n@bp.post("/examples/api/editrow/<id>")\nasync def api_editrow_save(id):\n form = await request.form\n return sx_response(\n f\'(~edit-row-view :id ... :name ...)\')',
comp_placeholder_id="editrow-comp",
wire_placeholder_id="editrow-wire")
def _example_bulk_update_sx() -> str:
from content.pages import BULK_USERS
users_sx = " ".join(
f'(list "{u["id"]}" "{u["name"]}" "{u["email"]}" "{u["status"]}")'
for u in BULK_USERS
)
return _build_example(
title="Bulk Update",
description="Select rows with checkboxes and use Activate/Deactivate buttons. sx-include gathers checkbox values from the form.",
demo_description="Check some rows, then click Activate or Deactivate.",
demo=f"(~bulk-update-demo :users (list {users_sx}))",
sx_code='(button\n :sx-post "/examples/api/bulk?action=activate"\n :sx-target "#bulk-table"\n :sx-swap "innerHTML"\n :sx-include "#bulk-form"\n "Activate")',
handler_code='@bp.post("/examples/api/bulk")\nasync def api_bulk():\n action = request.args.get("action")\n form = await request.form\n ids = form.getlist("ids")\n # Update matching users\n return sx_response(updated_rows)',
comp_placeholder_id="bulk-comp",
wire_placeholder_id="bulk-wire")
def _example_swap_positions_sx() -> str:
return _build_example(
title="Swap Positions",
description="Demonstrates different swap modes: beforeend appends, afterbegin prepends, and none skips the main swap while still processing OOB updates.",
demo_description="Try each button to see different swap behaviours.",
demo="(~swap-positions-demo)",
sx_code=';; Append to end\n(button :sx-post "/api/swap-log?mode=beforeend"\n :sx-target "#swap-log" :sx-swap "beforeend"\n "Add to End")\n\n;; Prepend to start\n(button :sx-post "/api/swap-log?mode=afterbegin"\n :sx-target "#swap-log" :sx-swap "afterbegin"\n "Add to Start")\n\n;; No swap — OOB counter update only\n(button :sx-post "/api/swap-log?mode=none"\n :sx-target "#swap-log" :sx-swap "none"\n "Silent Ping")',
handler_code='@bp.post("/examples/api/swap-log")\nasync def api_swap_log():\n mode = request.args.get("mode")\n # OOB counter updates on every request\n oob = f\'(span :id "swap-counter" :sx-swap-oob "innerHTML" "Count: {n}")\'\n return sx_response(entry + oob)',
wire_placeholder_id="swap-wire")
def _example_select_filter_sx() -> str:
return _build_example(
title="Select Filter",
description="sx-select lets the client pick a specific section from the server response by CSS selector. The server always returns the full dashboard — the client filters.",
demo_description="Different buttons select different parts of the same server response.",
demo="(~select-filter-demo)",
sx_code=';; Pick just the stats section from the response\n(button\n :sx-get "/examples/api/dashboard"\n :sx-target "#filter-target"\n :sx-swap "innerHTML"\n :sx-select "#dash-stats"\n "Stats Only")\n\n;; No sx-select — get the full response\n(button\n :sx-get "/examples/api/dashboard"\n :sx-target "#filter-target"\n :sx-swap "innerHTML"\n "Full Dashboard")',
handler_code='@bp.get("/examples/api/dashboard")\nasync def api_dashboard():\n # Returns header + stats + footer\n # Client uses sx-select to pick sections\n return sx_response(\n \'(<> (div :id "dash-header" ...) \'\n \' (div :id "dash-stats" ...) \'\n \' (div :id "dash-footer" ...))\')',
wire_placeholder_id="filter-wire")
def _example_tabs_sx() -> str:
return _build_example(
title="Tabs",
description="Tab navigation using sx-push-url to update the browser URL. Back/forward buttons navigate between previously visited tabs.",
demo_description="Click tabs to switch content. Watch the browser URL change.",
demo="(~tabs-demo)",
sx_code='(button\n :sx-get "/examples/api/tabs/tab1"\n :sx-target "#tab-content"\n :sx-swap "innerHTML"\n :sx-push-url "/examples/tabs?tab=tab1"\n "Overview")',
handler_code='@bp.get("/examples/api/tabs/<tab>")\nasync def api_tabs(tab: str):\n content = TAB_CONTENT[tab]\n return sx_response(content)',
wire_placeholder_id="tabs-wire")
def _example_animations_sx() -> str:
return _build_example(
title="Animations",
description="CSS animations play on swap. The component injects a style tag with a keyframe animation and applies the class. Each click picks a random background colour.",
demo_description="Click to swap in content with a fade-in animation.",
demo="(~animations-demo)",
sx_code='(button\n :sx-get "/examples/api/animate"\n :sx-target "#anim-target"\n :sx-swap "innerHTML"\n "Load with animation")\n\n;; Component uses CSS animation class\n(defcomp ~anim-result (&key color time)\n (div :class "sx-fade-in ..."\n (style ".sx-fade-in { animation: sxFadeIn 0.5s }")\n (p "Faded in!")))',
handler_code='@bp.get("/examples/api/animate")\nasync def api_animate():\n colors = ["bg-violet-100", "bg-emerald-100", ...]\n color = random.choice(colors)\n return sx_response(\n f\'(~anim-result :color "{color}" :time "{now}")\')',
comp_placeholder_id="anim-comp",
wire_placeholder_id="anim-wire")
def _example_dialogs_sx() -> str:
return _build_example(
title="Dialogs",
description="Open a modal dialog by swapping in the dialog component. Close by swapping in empty content. Pure sx — no JavaScript library needed.",
demo_description="Click to open a modal dialog.",
demo="(~dialogs-demo)",
sx_code='(button\n :sx-get "/examples/api/dialog"\n :sx-target "#dialog-container"\n :sx-swap "innerHTML"\n "Open Dialog")\n\n;; Dialog closes by swapping empty content\n(button\n :sx-get "/examples/api/dialog/close"\n :sx-target "#dialog-container"\n :sx-swap "innerHTML"\n "Close")',
handler_code='@bp.get("/examples/api/dialog")\nasync def api_dialog():\n return sx_response(\n \'(~dialog-modal :title "Confirm"\'\n \' :message "Are you sure?")\')\n\n@bp.get("/examples/api/dialog/close")\nasync def api_dialog_close():\n return sx_response("")',
comp_placeholder_id="dialog-comp",
wire_placeholder_id="dialog-wire")
def _example_keyboard_shortcuts_sx() -> str:
return _build_example(
title="Keyboard Shortcuts",
description="Use sx-trigger with keyup event filters and from:body to listen for global keyboard shortcuts. The filter prevents firing when typing in inputs.",
demo_description="Press s, n, or h on your keyboard.",
demo="(~keyboard-shortcuts-demo)",
sx_code='(div :id "kbd-target"\n :sx-get "/examples/api/keyboard?key=s"\n :sx-trigger "keyup[key==\'s\'&&!event.target.matches(\'input,textarea\')] from:body"\n :sx-swap "innerHTML"\n "Press a shortcut key...")',
handler_code='@bp.get("/examples/api/keyboard")\nasync def api_keyboard():\n key = request.args.get("key", "")\n actions = {"s": "Search", "n": "New item", "h": "Help"}\n return sx_response(\n f\'(~kbd-result :key "{key}" :action "{actions[key]}")\')',
comp_placeholder_id="kbd-comp",
wire_placeholder_id="kbd-wire")
def _example_put_patch_sx() -> str:
from content.pages import PROFILE_DEFAULT
n, e, r = PROFILE_DEFAULT["name"], PROFILE_DEFAULT["email"], PROFILE_DEFAULT["role"]
return _build_example(
title="PUT / PATCH",
description="sx-put replaces the entire resource. This example shows a profile card with an Edit All button that sends a PUT with all fields.",
demo_description="Click Edit All to replace the full profile via PUT.",
demo=f'(~put-patch-demo :name "{n}" :email "{e}" :role "{r}")',
sx_code=';; Replace entire resource\n(form :sx-put "/examples/api/putpatch"\n :sx-target "#pp-target" :sx-swap "innerHTML"\n (input :name "name") (input :name "email")\n (button "Save All (PUT)"))',
handler_code='@bp.put("/examples/api/putpatch")\nasync def api_put():\n form = await request.form\n # Full replacement\n return sx_response(\'(~pp-view ...)\')',
comp_placeholder_id="pp-comp",
wire_placeholder_id="pp-wire")
def _example_json_encoding_sx() -> str:
return _build_example(
title="JSON Encoding",
description='Use sx-encoding="json" to send form data as a JSON body instead of URL-encoded form data. The server echoes back what it received.',
demo_description="Submit the form and see the JSON body the server received.",
demo="(~json-encoding-demo)",
sx_code='(form\n :sx-post "/examples/api/json-echo"\n :sx-target "#json-result"\n :sx-swap "innerHTML"\n :sx-encoding "json"\n (input :name "name" :value "Ada")\n (input :type "number" :name "age" :value "36")\n (button "Submit as JSON"))',
handler_code='@bp.post("/examples/api/json-echo")\nasync def api_json_echo():\n data = await request.get_json()\n body = json.dumps(data, indent=2)\n ct = request.content_type\n return sx_response(\n f\'(~json-result :body "{body}" :content-type "{ct}")\')',
comp_placeholder_id="json-comp",
wire_placeholder_id="json-wire")
def _example_vals_and_headers_sx() -> str:
return _build_example(
title="Vals & Headers",
description="sx-vals adds extra key/value pairs to the request parameters. sx-headers adds custom HTTP headers. The server echoes back what it received.",
demo_description="Click each button to see what the server receives.",
demo="(~vals-headers-demo)",
sx_code=';; Send extra values with the request\n(button\n :sx-get "/examples/api/echo-vals"\n :sx-vals "{\\"source\\": \\"button\\"}"\n "Send with vals")\n\n;; Send custom headers\n(button\n :sx-get "/examples/api/echo-headers"\n :sx-headers "{\\"X-Custom-Token\\": \\"abc123\\"}"\n "Send with headers")',
handler_code='@bp.get("/examples/api/echo-vals")\nasync def api_echo_vals():\n vals = dict(request.args)\n return sx_response(\n f\'(~echo-result :label "values" :items (...))\')\n\n@bp.get("/examples/api/echo-headers")\nasync def api_echo_headers():\n custom = {k: v for k, v in request.headers\n if k.startswith("X-")}\n return sx_response(\n f\'(~echo-result :label "headers" :items (...))\')',
comp_placeholder_id="vals-comp",
wire_placeholder_id="vals-wire")
def _example_loading_states_sx() -> str:
return _build_example(
title="Loading States",
description="sx.js adds the .sx-request CSS class to any element that has an active request. Use pure CSS to show spinners, disable buttons, or change opacity during loading.",
demo_description="Click the button — it shows a spinner during the 2-second request.",
demo="(~loading-states-demo)",
sx_code=';; .sx-request class added during request\n(style ".sx-loading-btn.sx-request {\n opacity: 0.7; pointer-events: none; }\n.sx-loading-btn.sx-request .sx-spinner {\n display: inline-block; }\n.sx-loading-btn .sx-spinner {\n display: none; }")\n\n(button :class "sx-loading-btn"\n :sx-get "/examples/api/slow"\n :sx-target "#loading-result"\n (span :class "sx-spinner animate-spin" "...")\n "Load slow endpoint")',
handler_code='@bp.get("/examples/api/slow")\nasync def api_slow():\n await asyncio.sleep(2)\n return sx_response(\n f\'(~loading-result :time "{now}")\')',
comp_placeholder_id="loading-comp",
wire_placeholder_id="loading-wire")
def _example_sync_replace_sx() -> str:
return _build_example(
title="Request Abort",
description='sx-sync="replace" aborts any in-flight request before sending a new one. This prevents stale responses from overwriting newer ones, even with random server delays.',
demo_description="Type quickly — only the latest result appears despite random 0.5-2s server delays.",
demo="(~sync-replace-demo)",
sx_code='(input :type "text" :name "q"\n :sx-get "/examples/api/slow-search"\n :sx-trigger "keyup delay:200ms changed"\n :sx-target "#sync-result"\n :sx-swap "innerHTML"\n :sx-sync "replace"\n "Type to search...")',
handler_code='@bp.get("/examples/api/slow-search")\nasync def api_slow_search():\n delay = random.uniform(0.5, 2.0)\n await asyncio.sleep(delay)\n q = request.args.get("q", "")\n return sx_response(\n f\'(~sync-result :query "{q}" :delay "{delay_ms}")\')',
comp_placeholder_id="sync-comp",
wire_placeholder_id="sync-wire")
def _example_retry_sx() -> str:
return _build_example(
title="Retry",
description='sx-retry="exponential:1000:8000" retries failed requests with exponential backoff starting at 1s up to 8s. The endpoint fails the first 2 attempts and succeeds on the 3rd.',
demo_description="Click the button — watch it retry automatically after failures.",
demo="(~retry-demo)",
sx_code='(button\n :sx-get "/examples/api/flaky"\n :sx-target "#retry-result"\n :sx-swap "innerHTML"\n :sx-retry "exponential:1000:8000"\n "Call flaky endpoint")',
handler_code='@bp.get("/examples/api/flaky")\nasync def api_flaky():\n _flaky["n"] += 1\n if _flaky["n"] % 3 != 0:\n return Response("", status=503)\n return sx_response(\n f\'(~retry-result :attempt {n} ...)\')',
comp_placeholder_id="retry-comp",
wire_placeholder_id="retry-wire")