Web extension module for def-forms + modifier-key clicks + CSSX SSR fix
Move defhandler/defquery/defaction/defpage/defrelation from hardcoded evaluator dispatch to web/web-forms.sx extension module, registered via register-special-form!. Adapters updated to use definition-form? and dynamically extended form-name lists. Fix modifier-key clicks (ctrl-click → new tab) in three click handlers: bindBoostLink, bindClientRouteClick, and orchestration.sx bind-event. Add event-modifier-key? primitive (eventModifierKey_p for transpiler). Fix CSSX SSR: ~cssx/flush no longer drains the collected bucket on the server, so the shell template correctly emits CSSX rules in <head>. Add missing server-side DOM stubs (create-text-node, dom-append, etc.) and SSR passthrough for portal/error-boundary/promise-delayed. Passive event listeners for touch/wheel/scroll to fix touchpad scrolling. 97/97 Playwright demo tests + 4/4 isomorphic SSR tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -125,11 +125,9 @@ let io_batch_mode = ref false
|
||||
let io_queue : (int * string * value list) list ref = ref []
|
||||
let io_counter = ref 0
|
||||
|
||||
(** Module-level scope stacks — shared between make_server_env (aser
|
||||
scope-push!/pop!) and step-sf-context (via get-primitive "scope-peek"). *)
|
||||
(** Request cookies — set by Python bridge before each page render.
|
||||
get-cookie reads from here on the server; set-cookie is a no-op
|
||||
(server can't set response cookies from SX — that's the framework's job). *)
|
||||
(* Request cookies — set by Python bridge before each page render.
|
||||
get-cookie reads from here on the server; set-cookie is a no-op
|
||||
(server can't set response cookies from SX — that's the framework's job). *)
|
||||
let _request_cookies : (string, string) Hashtbl.t = Hashtbl.create 8
|
||||
|
||||
let () = Sx_primitives.register "get-cookie" (fun args ->
|
||||
@@ -144,6 +142,8 @@ let () = Sx_primitives.register "set-cookie" (fun _args ->
|
||||
(* No-op on server — cookies are set via HTTP response headers *)
|
||||
Nil)
|
||||
|
||||
(* Module-level scope stacks — shared between make_server_env (aser
|
||||
scope-push!/pop!) and step-sf-context (via get-primitive "scope-peek"). *)
|
||||
let _scope_stacks : (string, value list) Hashtbl.t = Hashtbl.create 8
|
||||
|
||||
let () = Sx_primitives.register "scope-push!" (fun args ->
|
||||
@@ -496,6 +496,10 @@ let make_server_env () =
|
||||
bind "dom-text-content" (fun _args -> String "");
|
||||
bind "dom-set-text-content" (fun _args -> Nil);
|
||||
bind "dom-body" (fun _args -> Nil);
|
||||
bind "dom-create-element" (fun _args -> Nil);
|
||||
bind "dom-append" (fun _args -> Nil);
|
||||
bind "create-text-node" (fun _args -> Nil);
|
||||
bind "render-to-dom" (fun _args -> Nil);
|
||||
|
||||
(* Raw HTML — platform primitives for adapter-html.sx *)
|
||||
bind "make-raw-html" (fun args ->
|
||||
@@ -617,6 +621,16 @@ let make_server_env () =
|
||||
Sx_ref.eval_expr m.m_body (Env body_env)
|
||||
| _ -> raise (Eval_error "expand-macro: expected (macro args env)"));
|
||||
|
||||
(* Expose register-special-form! and *custom-special-forms* to SX code
|
||||
(used by web-forms.sx and adapter form-classification functions) *)
|
||||
bind "register-special-form!" (fun args ->
|
||||
match args with
|
||||
| [String name; handler] ->
|
||||
ignore (Sx_ref.register_special_form (String name) handler);
|
||||
Nil
|
||||
| _ -> raise (Eval_error "register-special-form!: expected (name handler)"));
|
||||
ignore (env_bind env "*custom-special-forms*" Sx_ref.custom_special_forms);
|
||||
|
||||
(* Register <> as a special form — evaluates all children, returns list *)
|
||||
ignore (Sx_ref.register_special_form (String "<>") (NativeFn ("<>", fun args ->
|
||||
List (List.map (fun a -> Sx_ref.eval_expr a (Env env)) args))));
|
||||
@@ -725,10 +739,8 @@ let make_server_env () =
|
||||
bind "set-response-status" (fun args -> io_request "set-response-status" args);
|
||||
bind "set-response-header" (fun args -> io_request "set-response-header" args);
|
||||
|
||||
(* Application constructs — no-ops in the kernel, but needed so
|
||||
handler/page files load successfully (their define forms get evaluated) *)
|
||||
ignore (Sx_ref.register_special_form (String "defhandler") (NativeFn ("defhandler", fun _args -> Nil)));
|
||||
ignore (Sx_ref.register_special_form (String "defpage") (NativeFn ("defpage", fun _args -> Nil)));
|
||||
(* defhandler/defpage/defquery/defaction/defrelation are registered by
|
||||
web-forms.sx via register-special-form!, no longer hardcoded here. *)
|
||||
|
||||
bind "cond-scheme?" (fun args ->
|
||||
match args with
|
||||
@@ -1504,6 +1516,7 @@ let cli_mode mode =
|
||||
Filename.concat base "render.sx";
|
||||
Filename.concat web_base "adapter-html.sx";
|
||||
Filename.concat web_base "adapter-sx.sx";
|
||||
Filename.concat web_base "web-forms.sx";
|
||||
] in
|
||||
(* Load spec files for all CLI modes that need rendering *)
|
||||
(if mode = "aser" || mode = "aser-slot" || mode = "render" then
|
||||
@@ -1588,6 +1601,7 @@ let test_mode () =
|
||||
Filename.concat web_base "signals.sx";
|
||||
Filename.concat web_base "adapter-html.sx";
|
||||
Filename.concat web_base "adapter-sx.sx";
|
||||
Filename.concat web_base "web-forms.sx";
|
||||
] in
|
||||
cli_load_files env files;
|
||||
(* Register JIT *)
|
||||
|
||||
Reference in New Issue
Block a user