code wiki / _hdl_build / nx_editor_loop.nx

nx_editor_loop.nx source

↩ module page · 80 lines · 4804 B

1// nx_editor_loop.nx -- the NO-JS edit LOOP: the Nishi ecosystem MANAGING a layout with zero JavaScript. The editor 2// surface is rendered (ec_render) with a NATIVE HTML <form> of op buttons; submitting POSTs an op; the server APPLIES 3// the engine op (el_apply -> nx_editor_canvas) and RE-RENDERS. Every edit is a server round-trip -- no client JS, 4// and more governed than drag-drop (each op passes through our code). The daemon route (/editor/apply) is the 5// deployment wiring; this is the sovereign engine of it. REUSE: nx_editor_canvas. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_editor_canvas.nx" 8 9// null-terminated string equality. 10func el_streq(a: *u8, b: *u8) -> i64 { 11 var i: i64 = 0 12 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 13 if b[i] != (0 as u8) { return 0 } 14 return 1 15} 16// The snap grid the posted `snap` op uses. It was a bare 8 inside el_apply; naming it is what lets the 17// stack-backed path below use THE SAME grid instead of a second hand-typed copy that can drift from it. 18const EL_GRID: i64 = 8 19 20// apply a posted op to the canvas via the engine. Returns 1 if applied, 0 if the op is unknown (rejected, no-op). 21// KEPT, UNCHANGED, for every caller that holds a bare canvas array. el_apply_st below is the additive 22// stack-backed sibling; neither signature moved. 23func el_apply(c: *i64, n: i64, op: *u8) -> i64 { 24 if el_streq(op, "align-left" as *u8) == 1 { ec_align(c, n, 0); return 1 } 25 if el_streq(op, "align-right" as *u8) == 1 { ec_align(c, n, 1); return 1 } 26 if el_streq(op, "align-top" as *u8) == 1 { ec_align(c, n, 2); return 1 } 27 if el_streq(op, "snap" as *u8) == 1 { ec_snap_all(c, n, EL_GRID); return 1 } 28 if el_streq(op, "distribute" as *u8) == 1 { ec_distribute_h(c, n); return 1 } 29 return 0 30} 31 32// THE SAME POSTED VOCABULARY, RECORDED ON THE OPERATION LOG. This is what makes the no-JS loop undoable: 33// every posted op becomes one entry on the stack, so `undo` and `redo` are ops the form can post like any 34// other, and they go back as many steps as the user took rather than one. Returns 1 if applied, 0 if the op 35// is unknown OR the engine refused it -- and a refusal is NOT silently reported as applied, so a POST that 36// could not be honoured re-renders unchanged instead of lying about it. 37func el_apply_st(st: *i64, op: *u8) -> i64 { 38 if el_streq(op, "align-left" as *u8) == 1 { if ecs_align(st, 0) == ECS_OK { return 1 } return 0 } 39 if el_streq(op, "align-right" as *u8) == 1 { if ecs_align(st, 1) == ECS_OK { return 1 } return 0 } 40 if el_streq(op, "align-top" as *u8) == 1 { if ecs_align(st, 2) == ECS_OK { return 1 } return 0 } 41 if el_streq(op, "snap" as *u8) == 1 { if ecs_snap(st, EL_GRID) == ECS_OK { return 1 } return 0 } 42 if el_streq(op, "distribute" as *u8) == 1 { if ecs_distribute_h(st) == ECS_OK { return 1 } return 0 } 43 if el_streq(op, "undo" as *u8) == 1 { if ecs_undo(st) == ECS_OK { return 1 } return 0 } 44 if el_streq(op, "redo" as *u8) == 1 { if ecs_redo(st) == ECS_OK { return 1 } return 0 } 45 return 0 46} 47 48// The op buttons, EXTRACTED so the snapshot form and the stack-backed form cannot drift apart in what they 49// offer. Adding an op here reaches both surfaces; typing it twice is how one surface silently loses a verb. 50func el_emit_ops(fd: i64) -> i64 { 51 ec_w(fd, "<button name=\"op\" value=\"align-left\">Align left</button>\n" as *u8) 52 ec_w(fd, "<button name=\"op\" value=\"align-right\">Align right</button>\n" as *u8) 53 ec_w(fd, "<button name=\"op\" value=\"align-top\">Align top</button>\n" as *u8) 54 ec_w(fd, "<button name=\"op\" value=\"snap\">Snap to grid</button>\n" as *u8) 55 ec_w(fd, "<button name=\"op\" value=\"distribute\">Distribute</button>\n" as *u8) 56 return 0 57} 58 59// render the canvas + a NATIVE HTML ops form (no JS). Submitting an op is a server round-trip. 60// BYTE-FOR-BYTE what it emitted before: the buttons moved into el_emit_ops and nothing else changed. 61func el_emit_form(c: *i64, n: i64, fd: i64) -> i64 { 62 ec_render(c, n, fd) 63 ec_w(fd, "<form method=\"post\" action=\"/editor/apply\">\n" as *u8) 64 el_emit_ops(fd) 65 ec_w(fd, "</form>\n" as *u8) 66 return 0 67} 68 69// The stack-backed surface: the same form plus Undo and Redo, which the snapshot form cannot offer because 70// a one-deep snapshot has nothing to redo. STILL ZERO JAVASCRIPT -- undo is a POST like every other op, 71// which is the whole claim this loop exists to make. 72func el_emit_form_st(st: *i64, fd: i64) -> i64 { 73 ecs_render(st, fd) 74 ec_w(fd, "<form method=\"post\" action=\"/editor/apply\">\n" as *u8) 75 el_emit_ops(fd) 76 ec_w(fd, "<button name=\"op\" value=\"undo\">Undo</button>\n" as *u8) 77 ec_w(fd, "<button name=\"op\" value=\"redo\">Redo</button>\n" as *u8) 78 ec_w(fd, "</form>\n" as *u8) 79 return 0 80}