Hyperscript: focus command, diagnostic test output, blur keyword

Parser/compiler/runtime for focus command. Tokenizer: focus, blur,
precedes, follows, ignoring, case keywords. Test spec: per-test
failure output for diagnosis.

374/831 (45%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-12 12:38:05 +00:00
parent 1783f4805a
commit f60d22e86e
12 changed files with 277 additions and 190 deletions

View File

@@ -1838,6 +1838,47 @@ let parse_http_headers data =
) (match lines with _ :: rest -> rest | [] -> []);
!headers
(* IO-aware eval for rendering — handles perform (text-measure, sleep, import).
Used by aser and SSR so components can call measure-text via perform. *)
let eval_with_io_render expr env =
let state = ref (Sx_ref.cek_step_loop (Sx_ref.make_cek_state expr (Env env) Nil)) in
while sx_truthy (Sx_ref.cek_suspended_p !state) do
let request = Sx_ref.cek_io_request !state in
let op = match request with
| Dict d -> (match Hashtbl.find_opt d "op" with Some (String s) -> s | Some (Symbol s) -> s | _ -> "")
| _ -> "" in
let args = match request with
| Dict d -> (match Hashtbl.find_opt d "args" with Some v -> v | None -> Nil)
| _ -> Nil in
let result = match op with
| "text-measure" ->
let size = match args with
| List [_font; Number sz; _text] -> sz
| List [_font; Number sz] -> sz
| _ -> 16.0 in
let text = match args with
| List [_font; _sz; String t] -> t
| _ -> "" in
let char_width = size *. 0.6 in
let width = char_width *. (float_of_int (String.length text)) in
let d = Hashtbl.create 4 in
Hashtbl.replace d "width" (Number width);
Hashtbl.replace d "height" (Number size);
Hashtbl.replace d "ascent" (Number (size *. 0.8));
Hashtbl.replace d "descent" (Number (size *. 0.2));
Dict d
| "io-sleep" | "sleep" ->
let ms = match args with
| List (Number n :: _) -> n | Number n -> n | _ -> 0.0 in
Unix.sleepf (ms /. 1000.0); Nil
| "import" -> Nil
| _ -> Nil
in
state := Sx_ref.cek_step_loop (Sx_ref.cek_resume !state result)
done;
if sx_truthy (Sx_ref.cek_terminal_p !state) then Sx_ref.cek_value !state
else Nil
(** Render a page. Routing + AJAX detection in SX (request-handler.sx),
render pipeline (aser → SSR → shell) in OCaml for reliable env access. *)
let http_render_page env path headers =
@@ -1889,7 +1930,7 @@ let http_render_page env path headers =
(* AJAX: return SX wire format (aser output) with text/sx content type *)
let body_result =
let call = List [Symbol "aser"; List [Symbol "quote"; wrapped]; Env env] in
Sx_ref.eval_expr call (Env env) in
eval_with_io_render call env in
let body_str = match body_result with
| String s | SxExpr s -> s | _ -> serialize_value body_result in
let t1 = Unix.gettimeofday () in
@@ -1903,7 +1944,7 @@ let http_render_page env path headers =
let t1 = Unix.gettimeofday () in
let body_result =
let call = List [Symbol "aser"; List [Symbol "quote"; full_ast]; Env env] in
Sx_ref.eval_expr call (Env env) in
eval_with_io_render call env in
let body_str = match body_result with
| String s | SxExpr s -> s | _ -> serialize_value body_result in
let t2 = Unix.gettimeofday () in
@@ -1913,7 +1954,7 @@ let http_render_page env path headers =
if env_has env "render-to-html" then
let render_call = List [Symbol "render-to-html";
List [Symbol "quote"; body_expr]; Env env] in
(match Sx_ref.eval_expr render_call (Env env) with
(match eval_with_io_render render_call env with
| String s | RawHTML s -> s | v -> Sx_runtime.value_to_str v)
else Sx_render.sx_render_to_html env body_expr env
with e -> Printf.eprintf "[http-ssr] failed for %s: %s\n%!" path (Printexc.to_string e); "" in