diff --git a/hosts/ocaml/bin/sx_server.ml b/hosts/ocaml/bin/sx_server.ml index 9e4ab1b6..66816226 100644 --- a/hosts/ocaml/bin/sx_server.ml +++ b/hosts/ocaml/bin/sx_server.ml @@ -1499,9 +1499,7 @@ let http_render_page env path = | [e] -> e | [] -> Nil | _ -> List (Symbol "<>" :: body_exprs) in Sx_render.render_to_html_streaming body_expr env with e -> - Printf.eprintf "[http-ssr] failed: %s\n%!" (Printexc.to_string e); - (* Fallback: minimal layout structure so client can mount *) - "
" + Printf.eprintf "[http-ssr] failed: %s\n%!" (Printexc.to_string e); "" in let t3 = Unix.gettimeofday () in (* Phase 3: Shell — render directly to buffer for zero-copy output *) @@ -1621,25 +1619,14 @@ let http_inject_shell_statics env static_dir sx_sxc = | _ -> () ) env.bindings; let raw_defs = Buffer.contents buf in - (* Escape scans for (case-insensitive) to - close the tag. Replace which + the HTML parser uses to close the tag prematurely. *) + let component_defs = "" in + (* Cache the raw defs for the /static/sx-components.sx endpoint *) + Hashtbl.replace static_cache "/static/sx-components.sx" + (Printf.sprintf "HTTP/1.1 200 OK\r\nContent-Type: text/sx; charset=utf-8\r\nContent-Length: %d\r\nCache-Control: public, max-age=31536000, immutable\r\n\r\n%s" + (String.length raw_defs) raw_defs); let component_hash = Digest.string component_defs |> Digest.to_hex in (* Compute file hashes for cache busting *) let sx_js_hash = file_hash (static_dir ^ "/scripts/sx-browser.js") in @@ -1704,7 +1691,16 @@ let http_inject_shell_statics env static_dir sx_sxc = ignore (env_bind env "__shell-body-scripts" Nil); ignore (env_bind env "__shell-inline-css" Nil); ignore (env_bind env "__shell-inline-head-js" Nil); - ignore (env_bind env "__shell-init-sx" Nil); + (* init-sx: trigger client-side render when sx-root is empty (SSR failed). + The boot code hydrates existing islands but doesn't do fresh render. + This script forces a render from page-sx after boot completes. *) + ignore (env_bind env "__shell-init-sx" (String + "document.addEventListener('sx:boot-done', function() { \ + var root = document.getElementById('sx-root'); \ + if (root && !root.innerHTML.trim() && typeof SX !== 'undefined' && SX.renderPage) { \ + SX.renderPage(); \ + } \ + });")); Printf.eprintf "[sx-http] Shell statics: defs=%d hash=%s css=%d js=%s wasm=%s\n%!" (String.length component_defs) component_hash (String.length sx_css) sx_js_hash wasm_hash diff --git a/hosts/ocaml/lib/sx_render.ml b/hosts/ocaml/lib/sx_render.ml index 92bc351a..d8201501 100644 --- a/hosts/ocaml/lib/sx_render.ml +++ b/hosts/ocaml/lib/sx_render.ml @@ -258,21 +258,8 @@ and render_list_to_html head args env = | Component c when c.c_affinity = "client" -> "" (* skip client-only *) | Component _ -> render_component v args env | Island _i -> - (* Islands: try SSR via the SX render-to-html from adapter-html.sx. - It handles deref/signal/computed through the CEK correctly, - and renders island bodies with hydration markers. - On failure, emit a placeholder — client hydrates from SX source. *) - (try - let call_expr = List (Symbol name :: args) in - let quoted = List [Symbol "quote"; call_expr] in - let render_call = List [Symbol "render-to-html"; quoted; Env env] in - let result = Sx_ref.eval_expr render_call (Env env) in - (match result with - | String s | RawHTML s -> s - | _ -> value_to_string result) - with _e -> - (* Placeholder — client will hydrate this island *) - Printf.sprintf "" _i.i_name) + (* Islands are client-rendered — emit placeholder for hydration *) + Printf.sprintf "" _i.i_name | Macro m -> let expanded = expand_macro m args env in do_render_to_html expanded env @@ -516,16 +503,10 @@ and render_list_buf buf head args env = | Component c when c.c_affinity = "client" -> () | Component _ -> render_component_buf buf v args env | Island _i -> - (try - let call_expr = List (Symbol name :: args) in - let quoted = List [Symbol "quote"; call_expr] in - let render_call = List [Symbol "render-to-html"; quoted; Env env] in - let result = Sx_ref.eval_expr render_call (Env env) in - (match result with - | String s | RawHTML s -> Buffer.add_string buf s - | _ -> Buffer.add_string buf (value_to_string result)) - with _e -> - Buffer.add_string buf (Printf.sprintf "" _i.i_name)) + (* Islands are client-rendered — emit placeholder for hydration. + SSR of island bodies is unreliable (client-only symbols like + signals, DOM refs) and can cascade errors. *) + Buffer.add_string buf (Printf.sprintf "" _i.i_name) | Macro m -> let expanded = expand_macro m args env in render_to_buf buf expanded env