Add 21 new interactive examples to sx docs site (27 total)
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m26s

Loading: lazy loading, infinite scroll, progress bar
Forms: active search, inline validation, value select, reset on submit
Records: edit row, bulk update
Swap/DOM: swap positions, select filter, tabs
Display: animations, dialogs, keyboard shortcuts
HTTP: PUT/PATCH, JSON encoding, vals & headers
Resilience: loading states, request abort (sync replace), retry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 02:26:10 +00:00
parent e6b0849ce3
commit 002cc49f2c
4 changed files with 2060 additions and 0 deletions

View File

@@ -901,6 +901,27 @@ def _examples_content_sx(slug: str) -> str:
"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)()
@@ -1121,6 +1142,802 @@ def _example_oob_swaps_sx() -> str:
)
def _example_lazy_loading_sx() -> str:
c_sx = _example_code('(div\n'
' :sx-get "/examples/api/lazy"\n'
' :sx-trigger "load"\n'
' :sx-swap "innerHTML"\n'
' (div :class "animate-pulse" "Loading..."))')
c_handler = _example_code('@bp.get("/examples/api/lazy")\n'
'async def api_lazy():\n'
' now = datetime.now().strftime(...)\n'
' return sx_response(\n'
' f\'(~lazy-result :time "{now}")\')',
language="python")
return (
f'(~doc-page :title "Lazy Loading"'
f' (p :class "text-stone-600 mb-6"'
f' "Use sx-trigger=\\"load\\" to fetch content as soon as the element enters the DOM. '
f'Great for deferring expensive content below the fold.")'
f' (~example-card :title "Demo"'
f' :description "Content loads automatically when the page renders."'
f' (~example-demo (~lazy-loading-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("lazy-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("lazy-wire")})'
)
def _example_infinite_scroll_sx() -> str:
c_sx = _example_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...")')
c_handler = _example_code('@bp.get("/examples/api/scroll")\n'
'async 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)',
language="python")
return (
f'(~doc-page :title "Infinite Scroll"'
f' (p :class "text-stone-600 mb-6"'
f' "A sentinel element at the bottom uses sx-trigger=\\"intersect once\\" '
f'to load the next page when scrolled into view. Each response appends items and a new sentinel.")'
f' (~example-card :title "Demo"'
f' :description "Scroll down in the container to load more items (5 pages total)."'
f' (~example-demo (~infinite-scroll-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("scroll-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("scroll-wire")})'
)
def _example_progress_bar_sx() -> str:
c_sx = _example_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")')
c_handler = _example_code('@bp.post("/examples/api/progress/start")\n'
'async 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}")\')',
language="python")
return (
f'(~doc-page :title "Progress Bar"'
f' (p :class "text-stone-600 mb-6"'
f' "Start a server-side job, then poll for progress using sx-trigger=\\"load delay:500ms\\" on each response. '
f'The bar fills up and stops when complete.")'
f' (~example-card :title "Demo"'
f' :description "Click start to begin a simulated job."'
f' (~example-demo (~progress-bar-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("progress-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("progress-wire")})'
)
def _example_active_search_sx() -> str:
c_sx = _example_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...")')
c_handler = _example_code('@bp.get("/examples/api/search")\n'
'async 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}")\')',
language="python")
return (
f'(~doc-page :title "Active Search"'
f' (p :class "text-stone-600 mb-6"'
f' "An input with sx-trigger=\\"keyup delay:300ms changed\\" debounces keystrokes and only fires when the value changes. '
f'The server filters a list of programming languages.")'
f' (~example-card :title "Demo"'
f' :description "Type to search through 20 programming languages."'
f' (~example-demo (~active-search-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("search-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("search-wire")})'
)
def _example_inline_validation_sx() -> str:
c_sx = _example_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")')
c_handler = _example_code('@bp.get("/examples/api/validate")\n'
'async 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 ...)\')',
language="python")
return (
f'(~doc-page :title "Inline Validation"'
f' (p :class "text-stone-600 mb-6"'
f' "Validate an email field on blur. The server checks format and whether it is taken, '
f'returning green or red feedback inline.")'
f' (~example-card :title "Demo"'
f' :description "Enter an email and click away (blur) to validate."'
f' (~example-demo (~inline-validation-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("validate-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("validate-wire")})'
)
def _example_value_select_sx() -> str:
c_sx = _example_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"))')
c_handler = _example_code('@bp.get("/examples/api/values")\n'
'async 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 ...))\')',
language="python")
return (
f'(~doc-page :title "Value Select"'
f' (p :class "text-stone-600 mb-6"'
f' "Two linked selects: pick a category and the second select updates with matching items via sx-get.")'
f' (~example-card :title "Demo"'
f' :description "Select a category to populate the item dropdown."'
f' (~example-demo (~value-select-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("values-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("values-wire")})'
)
def _example_reset_on_submit_sx() -> str:
c_sx = _example_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"))')
c_handler = _example_code('@bp.post("/examples/api/reset-submit")\n'
'async 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 "...")\')',
language="python")
return (
f'(~doc-page :title "Reset on Submit"'
f' (p :class "text-stone-600 mb-6"'
f' "Use sx-on:afterSwap=\\"this.reset()\\" to clear form inputs after a successful submission.")'
f' (~example-card :title "Demo"'
f' :description "Submit a message — the input resets after each send."'
f' (~example-demo (~reset-on-submit-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("reset-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("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
)
c_sx = _example_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")')
c_handler = _example_code('@bp.get("/examples/api/editrow/<id>")\n'
'async 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>")\n'
'async def api_editrow_save(id):\n'
' form = await request.form\n'
' return sx_response(\n'
' f\'(~edit-row-view :id ... :name ...)\')',
language="python")
return (
f'(~doc-page :title "Edit Row"'
f' (p :class "text-stone-600 mb-6"'
f' "Click edit to replace a table row with input fields. Save or cancel swaps back the display row. '
f'Uses sx-include to gather form values from the row.")'
f' (~example-card :title "Demo"'
f' :description "Click edit on any row to modify it inline."'
f' (~example-demo (~edit-row-demo :rows (list {rows_sx}))))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("editrow-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("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
)
c_sx = _example_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")')
c_handler = _example_code('@bp.post("/examples/api/bulk")\n'
'async 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)',
language="python")
return (
f'(~doc-page :title "Bulk Update"'
f' (p :class "text-stone-600 mb-6"'
f' "Select rows with checkboxes and use Activate/Deactivate buttons. '
f'sx-include gathers checkbox values from the form.")'
f' (~example-card :title "Demo"'
f' :description "Check some rows, then click Activate or Deactivate."'
f' (~example-demo (~bulk-update-demo :users (list {users_sx}))))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("bulk-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("bulk-wire")})'
)
def _example_swap_positions_sx() -> str:
c_sx = _example_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")')
c_handler = _example_code('@bp.post("/examples/api/swap-log")\n'
'async 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)',
language="python")
return (
f'(~doc-page :title "Swap Positions"'
f' (p :class "text-stone-600 mb-6"'
f' "Demonstrates different swap modes: beforeend appends, afterbegin prepends, '
f'and none skips the main swap while still processing OOB updates.")'
f' (~example-card :title "Demo"'
f' :description "Try each button to see different swap behaviours."'
f' (~example-demo (~swap-positions-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("swap-wire")})'
)
def _example_select_filter_sx() -> str:
c_sx = _example_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")')
c_handler = _example_code('@bp.get("/examples/api/dashboard")\n'
'async 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" ...))\')',
language="python")
return (
f'(~doc-page :title "Select Filter"'
f' (p :class "text-stone-600 mb-6"'
f' "sx-select lets the client pick a specific section from the server response by CSS selector. '
f'The server always returns the full dashboard — the client filters.")'
f' (~example-card :title "Demo"'
f' :description "Different buttons select different parts of the same server response."'
f' (~example-demo (~select-filter-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("filter-wire")})'
)
def _example_tabs_sx() -> str:
c_sx = _example_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")')
c_handler = _example_code('@bp.get("/examples/api/tabs/<tab>")\n'
'async def api_tabs(tab: str):\n'
' content = TAB_CONTENT[tab]\n'
' return sx_response(content)',
language="python")
return (
f'(~doc-page :title "Tabs"'
f' (p :class "text-stone-600 mb-6"'
f' "Tab navigation using sx-push-url to update the browser URL. '
f'Back/forward buttons navigate between previously visited tabs.")'
f' (~example-card :title "Demo"'
f' :description "Click tabs to switch content. Watch the browser URL change."'
f' (~example-demo (~tabs-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("tabs-wire")})'
)
def _example_animations_sx() -> str:
c_sx = _example_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!")))')
c_handler = _example_code('@bp.get("/examples/api/animate")\n'
'async 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}")\')',
language="python")
return (
f'(~doc-page :title "Animations"'
f' (p :class "text-stone-600 mb-6"'
f' "CSS animations play on swap. The component injects a style tag with a keyframe animation '
f'and applies the class. Each click picks a random background colour.")'
f' (~example-card :title "Demo"'
f' :description "Click to swap in content with a fade-in animation."'
f' (~example-demo (~animations-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("anim-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("anim-wire")})'
)
def _example_dialogs_sx() -> str:
c_sx = _example_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")')
c_handler = _example_code('@bp.get("/examples/api/dialog")\n'
'async 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")\n'
'async def api_dialog_close():\n'
' return sx_response("")',
language="python")
return (
f'(~doc-page :title "Dialogs"'
f' (p :class "text-stone-600 mb-6"'
f' "Open a modal dialog by swapping in the dialog component. Close by swapping in empty content. '
f'Pure sx — no JavaScript library needed.")'
f' (~example-card :title "Demo"'
f' :description "Click to open a modal dialog."'
f' (~example-demo (~dialogs-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("dialog-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("dialog-wire")})'
)
def _example_keyboard_shortcuts_sx() -> str:
c_sx = _example_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...")')
c_handler = _example_code('@bp.get("/examples/api/keyboard")\n'
'async 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]}")\')',
language="python")
return (
f'(~doc-page :title "Keyboard Shortcuts"'
f' (p :class "text-stone-600 mb-6"'
f' "Use sx-trigger with keyup event filters and from:body to listen for global keyboard shortcuts. '
f'The filter prevents firing when typing in inputs.")'
f' (~example-card :title "Demo"'
f' :description "Press s, n, or h on your keyboard."'
f' (~example-demo (~keyboard-shortcuts-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("kbd-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("kbd-wire")})'
)
def _example_put_patch_sx() -> str:
from content.pages import PROFILE_DEFAULT
c_sx = _example_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)"))')
c_handler = _example_code('@bp.put("/examples/api/putpatch")\n'
'async def api_put():\n'
' form = await request.form\n'
' # Full replacement\n'
' return sx_response(\'(~pp-view ...)\')',
language="python")
n, e, r = PROFILE_DEFAULT["name"], PROFILE_DEFAULT["email"], PROFILE_DEFAULT["role"]
return (
f'(~doc-page :title "PUT / PATCH"'
f' (p :class "text-stone-600 mb-6"'
f' "sx-put replaces the entire resource. This example shows a profile card with an Edit All button '
f'that sends a PUT with all fields.")'
f' (~example-card :title "Demo"'
f' :description "Click Edit All to replace the full profile via PUT."'
f' (~example-demo (~put-patch-demo :name "{n}" :email "{e}" :role "{r}")))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("pp-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("pp-wire")})'
)
def _example_json_encoding_sx() -> str:
c_sx = _example_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"))')
c_handler = _example_code('@bp.post("/examples/api/json-echo")\n'
'async 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}")\')',
language="python")
return (
f'(~doc-page :title "JSON Encoding"'
f' (p :class "text-stone-600 mb-6"'
f' "Use sx-encoding=\\"json\\" to send form data as a JSON body instead of URL-encoded form data. '
f'The server echoes back what it received.")'
f' (~example-card :title "Demo"'
f' :description "Submit the form and see the JSON body the server received."'
f' (~example-demo (~json-encoding-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("json-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("json-wire")})'
)
def _example_vals_and_headers_sx() -> str:
c_sx = _example_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")')
c_handler = _example_code('@bp.get("/examples/api/echo-vals")\n'
'async 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")\n'
'async 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 (...))\')',
language="python")
return (
f'(~doc-page :title "Vals & Headers"'
f' (p :class "text-stone-600 mb-6"'
f' "sx-vals adds extra key/value pairs to the request parameters. '
f'sx-headers adds custom HTTP headers. The server echoes back what it received.")'
f' (~example-card :title "Demo"'
f' :description "Click each button to see what the server receives."'
f' (~example-demo (~vals-headers-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("vals-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("vals-wire")})'
)
def _example_loading_states_sx() -> str:
c_sx = _example_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")')
c_handler = _example_code('@bp.get("/examples/api/slow")\n'
'async def api_slow():\n'
' await asyncio.sleep(2)\n'
' return sx_response(\n'
' f\'(~loading-result :time "{now}")\')',
language="python")
return (
f'(~doc-page :title "Loading States"'
f' (p :class "text-stone-600 mb-6"'
f' "sx.js adds the .sx-request CSS class to any element that has an active request. '
f'Use pure CSS to show spinners, disable buttons, or change opacity during loading.")'
f' (~example-card :title "Demo"'
f' :description "Click the button — it shows a spinner during the 2-second request."'
f' (~example-demo (~loading-states-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("loading-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("loading-wire")})'
)
def _example_sync_replace_sx() -> str:
c_sx = _example_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...")')
c_handler = _example_code('@bp.get("/examples/api/slow-search")\n'
'async 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}")\')',
language="python")
return (
f'(~doc-page :title "Request Abort"'
f' (p :class "text-stone-600 mb-6"'
f' "sx-sync=\\"replace\\" aborts any in-flight request before sending a new one. '
f'This prevents stale responses from overwriting newer ones, even with random server delays.")'
f' (~example-card :title "Demo"'
f' :description "Type quickly — only the latest result appears despite random 0.5-2s server delays."'
f' (~example-demo (~sync-replace-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("sync-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("sync-wire")})'
)
def _example_retry_sx() -> str:
c_sx = _example_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")')
c_handler = _example_code('@bp.get("/examples/api/flaky")\n'
'async 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} ...)\')',
language="python")
return (
f'(~doc-page :title "Retry"'
f' (p :class "text-stone-600 mb-6"'
f' "sx-retry=\\"exponential:1000:8000\\" retries failed requests with exponential backoff '
f'starting at 1s up to 8s. The endpoint fails the first 2 attempts and succeeds on the 3rd.")'
f' (~example-card :title "Demo"'
f' :description "Click the button — watch it retry automatically after failures."'
f' (~example-demo (~retry-demo)))'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "S-expression")'
f' {c_sx}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Component")'
f' {_placeholder("retry-comp")}'
f' (h3 :class "text-lg font-semibold text-stone-700 mt-6" "Server handler")'
f' {c_handler}'
f' (div :class "flex items-center justify-between mt-6"'
f' (h3 :class "text-lg font-semibold text-stone-700" "Wire response")'
f' {_clear_components_btn()})'
f' {_placeholder("retry-wire")})'
)
# ---------------------------------------------------------------------------
# Essays
# ---------------------------------------------------------------------------