;; Example page defcomps — one per example.
;; Each calls ~example-page-content with static string data.
;; Replaces all _example_*_sx() builders in essays.py.

(defcomp ~example-click-to-load ()
  (~example-page-content
    :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."))

(defcomp ~example-form-submission ()
  (~example-page-content
    :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"))

(defcomp ~example-polling ()
  (~example-page-content
    :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."))

(defcomp ~example-delete-row ()
  (~example-page-content
    :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 (~delete-demo :items (list
      (list "1" "Implement dark mode")
      (list "2" "Fix login bug")
      (list "3" "Write documentation")
      (list "4" "Deploy to production")
      (list "5" "Add unit tests")))
    :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."))

(defcomp ~example-inline-edit ()
  (~example-page-content
    :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"))

(defcomp ~example-oob-swaps ()
  (~example-page-content
    :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."))

(defcomp ~example-lazy-loading ()
  (~example-page-content
    :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"))

(defcomp ~example-infinite-scroll ()
  (~example-page-content
    :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"))

(defcomp ~example-progress-bar ()
  (~example-page-content
    :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"))

(defcomp ~example-active-search ()
  (~example-page-content
    :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"))

(defcomp ~example-inline-validation ()
  (~example-page-content
    :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"))

(defcomp ~example-value-select ()
  (~example-page-content
    :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"))

(defcomp ~example-reset-on-submit ()
  (~example-page-content
    :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"))

(defcomp ~example-edit-row ()
  (~example-page-content
    :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 (~edit-row-demo :rows (list
      (list "1" "Widget A" "19.99" "142")
      (list "2" "Widget B" "24.50" "89")
      (list "3" "Widget C" "12.00" "305")
      (list "4" "Widget D" "45.00" "67")))
    :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"))

(defcomp ~example-bulk-update ()
  (~example-page-content
    :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 (~bulk-update-demo :users (list
      (list "1" "Alice Chen" "alice@example.com" "active")
      (list "2" "Bob Rivera" "bob@example.com" "inactive")
      (list "3" "Carol Zhang" "carol@example.com" "active")
      (list "4" "Dan Okafor" "dan@example.com" "inactive")
      (list "5" "Eve Larsson" "eve@example.com" "active")))
    :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"))

(defcomp ~example-swap-positions ()
  (~example-page-content
    :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"))

(defcomp ~example-select-filter ()
  (~example-page-content
    :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"))

(defcomp ~example-tabs ()
  (~example-page-content
    :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"))

(defcomp ~example-animations ()
  (~example-page-content
    :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"))

(defcomp ~example-dialogs ()
  (~example-page-content
    :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"))

(defcomp ~example-keyboard-shortcuts ()
  (~example-page-content
    :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"))

(defcomp ~example-put-patch ()
  (~example-page-content
    :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 (~put-patch-demo :name "Ada Lovelace" :email "ada@example.com" :role "Engineer")
    :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"))

(defcomp ~example-json-encoding ()
  (~example-page-content
    :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"))

(defcomp ~example-vals-and-headers ()
  (~example-page-content
    :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"))

(defcomp ~example-loading-states ()
  (~example-page-content
    :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"))

(defcomp ~example-sync-replace ()
  (~example-page-content
    :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"))

(defcomp ~example-retry ()
  (~example-page-content
    :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"))
