;; htmx 4.0 compatibility — demo page
(defcomp
  ()
  (~docs/page
    :title "htmx.sx"
    (p
      (~tw :tokens "text-lg text-gray-600 mb-2")
      "Every "
      (code "hx-")
      " attribute is syntactic sugar for a hyperscript event handler. "
      "The htmx compat layer translates attributes to the same runtime calls "
      "the "
      (code "_=\"...\"")
      " compiler emits. Same bytecode. Zero duplication.")
    (p
      (~tw :tokens "text-sm text-gray-500 mb-8")
      "htmx 4.0 compatible: "
      (code "hx-action")
      "/"
      (code "hx-method")
      ", "
      (code ":inherited")
      " modifiers, swap aliases, "
      (code "hx-status:XXX")
      ", "
      (code "hx-sync")
      ", SSE in core.")
    (~docs/section
      :title "The Translation"
      :id "translation"
      (p
        "An htmx element and its hyperscript equivalent compile to identical runtime calls:")
      (~hyperscript/translation
        :htmx-html "<button hx-get=\"/api/items\"\n        hx-target=\"#list\"\n        hx-swap=\"innerHTML\"\n        hx-indicator=\"#spinner\"\n        hx-confirm=\"Sure?\"\n        hx-push-url=\"true\">"
        :hs-source "on click\n  confirm 'Sure?'\n  add .htmx-request to #spinner\n  fetch /api/items\n  put the result into #list\n  push url '/api/items'\n  remove .htmx-request from #spinner"
        :description "A button that fetches, swaps, confirms, indicates, and pushes history")
      (p
        (~tw :tokens "text-sm text-gray-600 mt-4")
        "Both go through: "
        (code "hs-on")
        " → "
        (code "perform io-fetch")
        " → "
        (code "dom-set-inner-html")
        ". The htmx path skips parsing/compilation and "
        "calls the runtime primitives directly."))
    (~docs/section
      :title "How It Works"
      :id "how-it-works"
      (~docs/note
        (p
          (strong "Architecture: ")
          "htmx-activate! scans "
          (code "hx-*")
          " attributes, "
          "builds a handler function from the same runtime primitives the hyperscript compiler emits "
          "(hs-on, dom-add-class, perform io-fetch, etc.), and registers it via "
          (code "hs-on")
          ". "
          "No separate runtime. No duplication."))
      (p "The activation pipeline:")
      (ol
        (~tw :tokens "list-decimal pl-6 space-y-1 text-sm text-gray-700 mb-4")
        (li
          (code "hs-activate!(el)")
          " reads "
          (code "_=\"...\"")
          " → parse → compile → eval")
        (li
          (code "htmx-activate!(el)")
          " reads "
          (code "hx-*")
          " → build handler → "
          (code "hs-on"))
        (li "Both produce the same runtime calls")
        (li
          (code "hs-boot-subtree!")
          " runs both on every DOM subtree (page load + after swaps)"))
      (~docs/code
        :src (highlight
          "(define htmx-activate!\n  (fn (el)\n    (when (not (dom-get-attr el \"hx-ignore\"))\n      (let ((verb-info (hx-verb-info el)))\n        (when (and verb-info (not (dom-get-data el \"hx-active\")))\n          (dom-set-data el \"hx-active\" true)\n          (let ((method (first verb-info))\n                (url    (nth verb-info 1)))\n            (let ((trigger (hx-parse-trigger\n                             (dom-get-attr el \"hx-trigger\") el))\n                  (handler (hx-make-handler el method url)))\n              (hx-register-trigger! el trigger handler))))))))"
          "lisp")))
    (~docs/section
      :title "What's Shared (Zero Duplication)"
      :id "shared"
      (~docs/table
        :headers ("Capability" "Runtime Function" "Used By")
        :rows (list
          (list "Event dispatch" "hs-on / dom-listen" "both")
          (list "HTTP fetch" "perform io-fetch" "both")
          (list
            "DOM swap"
            "dom-set-inner-html / dom-insert-adjacent-html"
            "both")
          (list "CSS transitions" "dom-add-class / dom-remove-class" "both")
          (list "History API" "perform io-push-state" "both")
          (list "Indicator toggle" "dom-add-class .htmx-request" "both")
          (list "Confirm dialog" "perform io-confirm" "both")
          (list "Wait/settle" "hs-wait" "both")
          (list "Subtree boot" "hs-boot-subtree!" "both"))))
    (~docs/section
      :title "Live Demos"
      :id "demos"
      (p
        "These demos use "
        (code "_=\"...\"")
        " hyperscript syntax. "
        "The equivalent "
        (code "hx-*")
        " attributes would produce identical behavior "
        "through the same runtime.")
      (~docs/subsection
        :title "Toggle (Class Manipulation)"
        (~hyperscript/translation
          :htmx-html "<button hx-get=\"\" hx-swap=\"none\"\n        hx-trigger=\"click\">"
          :hs-source "on click toggle .bg-violet-600 on me\n  toggle .text-white on me"
          :description "Toggle class on click")
        (div
          (~tw :tokens "flex gap-3 items-center mt-3 mb-6")
          (button
            (~tw
              :tokens "px-4 py-2 border border-gray-300 rounded-lg text-sm transition-colors duration-200")
            :_ "on click toggle .bg-violet-600 on me then toggle .text-white on me"
            "Click to Toggle")))
      (~docs/subsection
        :title "Counter (State + DOM Update)"
        (~hyperscript/translation
          :htmx-html "<button hx-get=\"/api/increment\"\n        hx-target=\"#count\"\n        hx-swap=\"innerHTML\">"
          :hs-source "on click\n  get the innerHTML of #demo-count\n  set x to it as Int\n  set the innerHTML of #demo-count\n    to (x + 1)"
          :description "Increment a counter")
        (div
          (~tw :tokens "flex gap-3 items-center mt-3 mb-6")
          (button
            (~tw
              :tokens "px-4 py-2 border border-violet-300 rounded-lg text-sm bg-violet-50 hover:bg-violet-100")
            :_ "on click get the innerHTML of #demo-count then set x to it as Int then set the innerHTML of #demo-count to (x + 1)"
            "+1")
          (span (~tw :tokens "text-lg font-mono") :id "demo-count" "0")))
      (~docs/subsection
        :title "Show/Hide (Indicator Pattern)"
        (~hyperscript/translation
          :htmx-html "<button hx-get=\"/api/data\"\n        hx-indicator=\"#demo-spinner\">"
          :hs-source "on click\n  add .htmx-request to #demo-spinner\n  wait 1s\n  remove .htmx-request from #demo-spinner"
          :description "Show indicator during request")
        (style
          "\n          #demo-spinner { opacity: 0; transition: opacity 0.2s; }\n          #demo-spinner.htmx-request { opacity: 1; }\n        ")
        (div
          (~tw :tokens "flex gap-3 items-center mt-3 mb-6")
          (button
            (~tw :tokens "px-4 py-2 border border-gray-300 rounded-lg text-sm")
            :_ "on click add .htmx-request to #demo-spinner then wait 1s then remove .htmx-request from #demo-spinner"
            "Load Data")
          (span
            :id "demo-spinner"
            (~tw :tokens "text-sm text-violet-600")
            "Loading...")))
      (~docs/subsection
        :title "Swap Modes"
        (~hyperscript/translation
          :htmx-html "<div hx-get=\"/fragment\"\n     hx-target=\"#demo-swap-target\"\n     hx-swap=\"beforeend\">"
          :hs-source "on click\n  fetch /fragment\n  put the result at the end of\n    #demo-swap-target"
          :description "Append content (hx-swap=\"beforeend\" = v4 alias \"append\")")
        (div
          (~tw :tokens "mt-3 mb-6")
          (div
            :id "demo-swap-target"
            (~tw
              :tokens "border border-dashed border-gray-300 rounded-lg p-3 min-h-12 text-sm text-gray-500")
            (div (~tw :tokens "text-gray-400") "Target area"))
          (button
            (~tw
              :tokens "mt-2 px-4 py-2 border border-gray-300 rounded-lg text-sm")
            :_ "on click put '<div class=\"py-1 text-violet-700\">Appended at ' + Date() + '</div>' at the end of #demo-swap-target"
            "Append Item"))))
    (~docs/section
      :title "htmx 4.0 Features"
      :id "v4"
      (~docs/subsection
        :title "Swap Aliases"
        (p "v4 adds short aliases that normalize to canonical names:")
        (~docs/table
          :headers ("v4 Alias" "Canonical" "Meaning")
          :rows (list
            (list
              (code "before")
              (code "beforebegin")
              "Insert before element")
            (list (code "after") (code "afterend") "Insert after element")
            (list
              (code "prepend")
              (code "afterbegin")
              "Insert as first child")
            (list (code "append") (code "beforeend") "Insert as last child")
            (list
              (code "innerMorph")
              (code "innerMorph")
              "Morph children (idiomorph)")
            (list
              (code "outerMorph")
              (code "outerMorph")
              "Morph entire element")
            (list
              (code "textContent")
              (code "textContent")
              "Set text, no HTML parsing"))))
      (~docs/subsection
        :title "hx-action / hx-method"
        (p "v4 alternative to " (code "hx-get") "/" (code "hx-post") ":")
        (~docs/code
          :src (highlight
            ";; These are equivalent:\n(button :hx-post \"/api/submit\")\n(button :hx-action \"/api/submit\" :hx-method \"post\")"
            "lisp")))
      (~docs/subsection
        :title "Explicit Inheritance"
        (p
          "v4 requires "
          (code ":inherited")
          " modifier for attribute inheritance:")
        (~docs/code
          :src (highlight
            ";; v4: parent target inherited by children\n(div :hx-target:inherited \"#results\"\n  (button :hx-get \"/search\" \"Search\")\n  (button :hx-get \"/filter\" \"Filter\"))"
            "lisp"))
        (p
          (~tw :tokens "text-sm text-gray-500 mt-2")
          "Both buttons target "
          (code "#results")
          " via inherited attribute, "
          "not implicit inheritance."))
      (~docs/subsection
        :title "Status-Based Swap (hx-status:XXX)"
        (p "Override swap behavior per HTTP status code:")
        (~docs/code
          :src (highlight
            "(form :hx-post \"/api/submit\"\n      :hx-target \"#result\"\n      :hx-status:422 \"swap:innerHTML target:#errors\"\n      :hx-status:5xx \"swap:none\")"
            "lisp"))
        (p
          (~tw :tokens "text-sm text-gray-500 mt-2")
          "Specificity: exact ("
          (code "422")
          ") > two-digit ("
          (code "50x")
          ") > one-digit ("
          (code "5xx")
          ")."))
      (~docs/subsection
        :title "Request Sync (hx-sync)"
        (p "Synchronize concurrent requests:")
        (~docs/table
          :headers ("Strategy" "Behavior")
          :rows (list
            (list (code "drop") "Skip if request already in flight (default)")
            (list (code "abort") "Cancel existing request, start new")
            (list (code "replace") "Cancel existing, replace with new")
            (list
              (code "queue last")
              "Queue latest request until current completes")))
        (~docs/code
          :src (highlight
            ";; Search: cancel stale requests\n(input :hx-get \"/search\"\n       :hx-trigger \"keyup changed delay:500ms\"\n       :hx-sync \"this:replace\")"
            "lisp")))
      (~docs/subsection
        :title "Server-Sent Events"
        (p "SSE is built into core in v4 (no extension needed):")
        (~docs/code
          :src (highlight
            "(div :hx-sse-connect \"/events\"\n     :hx-sse-swap \"message:#notifications,alert:#alerts:outerHTML\")"
            "lisp"))
        (p
          (~tw :tokens "text-sm text-gray-500 mt-2")
          "Each event type maps to a target element and swap mode.")))
    (~docs/section
      :title "Swap Modifiers"
      :id "swap-modifiers"
      (p
        "The "
        (code "hx-swap")
        " value accepts space-separated modifiers after the mode:")
      (~docs/table
        :headers ("Modifier" "Example" "Effect")
        :rows (list
          (list (code "swap:") (code "swap:100ms") "Delay before swap")
          (list
            (code "settle:")
            (code "settle:200ms")
            "Delay between swap and settle")
          (list
            (code "scroll:")
            (code "scroll:top")
            "Auto-scroll after swap")
          (list
            (code "transition:")
            (code "transition:true")
            "Use View Transitions API")
          (list
            (code "strip:")
            (code "strip:true")
            "Remove outer wrapper before swap")
          (list
            (code "target:")
            (code "target:#alt")
            "Override target inline")
          (list
            (code "ignoreTitle:")
            (code "ignoreTitle:true")
            "Don't update page title")))
      (~docs/code
        :src (highlight
          "(button :hx-get \"/page\"\n        :hx-swap \"innerHTML swap:50ms settle:100ms scroll:top transition:true\")"
          "lisp")))
    (~docs/section
      :title "Implementation"
      :id "implementation"
      (p "The total implementation:")
      (ul
        (~tw :tokens "list-disc pl-6 space-y-1 text-sm text-gray-700")
        (li (code "lib/hyperscript/htmx.sx") " — ~400 lines, 55 functions")
        (li
          "2 lines changed in "
          (code "integration.sx")
          " to hook into "
          (code "hs-activate!"))
        (li "57 tests across 11 suites")
        (li "Runtime cost: zero — it " (em "is") " the hyperscript runtime"))
      (~docs/note
        (p
          (strong "The insight: ")
          "htmx and hyperscript are not two competing libraries. "
          "htmx is a declarative attribute syntax for a subset of what hyperscript can express. "
          "The 'htmx compat layer' is just a thin attribute scanner "
          "that builds the same handler functions the hyperscript compiler produces.")))))