diff --git a/hosts/ocaml/bin/sx_server.ml b/hosts/ocaml/bin/sx_server.ml index 3ca70881..93540f20 100644 --- a/hosts/ocaml/bin/sx_server.ml +++ b/hosts/ocaml/bin/sx_server.ml @@ -3740,18 +3740,23 @@ let http_mode port = (try Unix.close client with _ -> ()) in - (* Check if request has SX-Request or HX-Request header (AJAX navigation) *) + (* Check if request has SX-Request header (SX AJAX navigation — return SX wire format) + or HX-Request header (htmx AJAX — return rendered HTML). *) + let has_substring s sub = + let slen = String.length s and sublen = String.length sub in + if sublen > slen then false + else let rec check i = if i > slen - sublen then false + else if String.sub s i sublen = sub then true else check (i + 1) + in check 0 + in let is_sx_request data = let lower = String.lowercase_ascii data in - let has_substring s sub = - let slen = String.length s and sublen = String.length sub in - if sublen > slen then false - else let rec check i = if i > slen - sublen then false - else if String.sub s i sublen = sub then true else check (i + 1) - in check 0 - in has_substring lower "sx-request" || has_substring lower "hx-request" in + let _is_hx_request data = + let lower = String.lowercase_ascii data in + has_substring lower "hx-request" && not (has_substring lower "sx-request") + in (* Non-blocking event loop with render worker pool. - Main loop: Unix.select on listen socket + all connected clients @@ -3785,14 +3790,23 @@ let http_mode port = match work with | Some (fd, path, headers) -> let is_ajax = headers <> [] in - let cache_key = if is_ajax then "ajax:" ^ path else path in + let is_htmx = List.exists (fun (k,_) -> String.lowercase_ascii k = "hx-request") headers in + let cache_key = if is_ajax then (if is_htmx then "htmx:" else "ajax:") ^ path else path in let response = try match http_render_page env path headers with | Some body -> - let ct = if is_ajax then "text/sx; charset=utf-8" + (* htmx requests get HTML; SX requests get SX wire format *) + let final_body = if is_htmx then + (try + let exprs = Sx_parser.parse_all body in + let expr = match exprs with [e] -> e | [] -> Nil | _ -> List (Symbol "<>" :: exprs) in + Sx_render.sx_render_to_html env expr env + with _ -> body) + else body in + let ct = if is_ajax && not is_htmx then "text/sx; charset=utf-8" else "text/html; charset=utf-8" in - let resp = http_response ~content_type:ct body in + let resp = http_response ~content_type:ct final_body in Hashtbl.replace response_cache cache_key resp; resp | None -> http_response ~status:404 "