From 5b55b75a9aabdcb4ef353cd3a1f09b872bf55e8c Mon Sep 17 00:00:00 2001 From: giles Date: Mon, 30 Mar 2026 18:06:22 +0000 Subject: [PATCH] AJAX on main thread, fix double-push in click delegation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AJAX requests (SX-Request: true) now render on main thread instead of queueing behind slow full-page renders in worker pool - Remove pushState from click handler — handle-history does it after swap succeeds, preventing double-push that triggered popstate handler Co-Authored-By: Claude Opus 4.6 (1M context) --- hosts/ocaml/bin/sx_server.ml | 29 +++++++++++++++++++++++------ shared/static/wasm/sx-platform.js | 6 ++---- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/hosts/ocaml/bin/sx_server.ml b/hosts/ocaml/bin/sx_server.ml index 36bf0acf..81a7fad6 100644 --- a/hosts/ocaml/bin/sx_server.ml +++ b/hosts/ocaml/bin/sx_server.ml @@ -2238,12 +2238,29 @@ let http_mode port = match Hashtbl.find_opt response_cache cache_key with | Some cached -> write_response fd cached; true | None -> - let headers = if is_ajax then parse_http_headers data else [] in - Mutex.lock render_mutex; - render_queue := !render_queue @ [(fd, path, headers)]; - Condition.signal render_cond; - Mutex.unlock render_mutex; - false + if is_ajax then begin + (* AJAX: render on main thread — aser only, fast, no SSR. + Avoids queueing behind slow full-page renders. *) + let headers = parse_http_headers data in + let response = + try match http_render_page env path headers with + | Some body -> + let resp = http_response ~content_type:"text/sx; charset=utf-8" body in + Hashtbl.replace response_cache cache_key resp; resp + | None -> http_response ~status:404 "nil" + with e -> + Printf.eprintf "[ajax] Error for %s: %s\n%!" path (Printexc.to_string e); + http_response ~status:500 "nil" + in + write_response fd response; true + end else begin + (* Full page: queue to render worker *) + Mutex.lock render_mutex; + render_queue := !render_queue @ [(fd, path, [])]; + Condition.signal render_cond; + Mutex.unlock render_mutex; + false + end end else if String.length path > 8 && String.sub path 0 8 = "/static/" then begin write_response fd (serve_static_file static_dir path); true end else begin diff --git a/shared/static/wasm/sx-platform.js b/shared/static/wasm/sx-platform.js index 0a96a7f4..82f7a3a2 100644 --- a/shared/static/wasm/sx-platform.js +++ b/shared/static/wasm/sx-platform.js @@ -464,10 +464,8 @@ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return; e.preventDefault(); var url = el.getAttribute("href") || el.getAttribute("sx-get"); - // Push URL first (execute-request doesn't handle URL updates) - if (el.getAttribute("sx-push-url")) { - history.pushState({}, "", url); - } + // Don't push URL here — execute-request's handle-history does it. + // Double-push causes popstate handler to clobber the SX swap. // Store the element reference for SX to pick up window.__sxClickEl = el; try {