code wiki / _hdl_build / nx_editor_canvas.nx
nx_editor_canvas.nx source
↩ module page · 102 lines · 5191 B
1// nx_editor_canvas.nx -- the EDITOR engine: the sovereign, gateable CORE of a visual editor, as data+logic so it can
2// be PROVEN without a browser. A canvas is a flat i64 array of N elements, 5 fields each: [kind, x, y, w, h].
3// Operations: snap-all-to-grid, align (L/R/T/B/center-h/middle-v), distribute-horizontally (even left edges), nudge,
4// and one-deep undo (snapshot/restore). The surface is rendered NO-JS (ec_render); editing = server ops (apply an op,
5// re-render), never a client-side JS canvas -- a JS drag-drop canvas is the JS-heavy approach the Nishi ecosystem
6// REJECTS, not a maturity bar to chase. SOVEREIGN + no-JS + governed. REUSE: nx_brand_snap (bs_snap).
7// license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_brand_snap.nx"
10
11const EC_STRIDE: i64 = 5 // [kind, x, y, w, h]
12
13// snap every element's x/y/w onto the grid.
14func ec_snap_all(c: *i64, n: i64, grid: i64) -> i64 {
15 var i: i64 = 0
16 while i < n {
17 c[i*EC_STRIDE + 1] = bs_snap(c[i*EC_STRIDE + 1], grid)
18 c[i*EC_STRIDE + 2] = bs_snap(c[i*EC_STRIDE + 2], grid)
19 c[i*EC_STRIDE + 3] = bs_snap(c[i*EC_STRIDE + 3], grid)
20 i = i + 1
21 }
22 return 0
23}
24// align all elements: mode 0=left 1=right 2=top 3=bottom 4=center-h 5=middle-v (to the bounding box).
25func ec_align(c: *i64, n: i64, mode: i64) -> i64 {
26 if n < 1 { return 0 }
27 var minx: i64 = c[1]; var maxr: i64 = c[1] + c[3]
28 var miny: i64 = c[2]; var maxb: i64 = c[2] + c[4]
29 var i: i64 = 1
30 while i < n {
31 let x: i64 = c[i*EC_STRIDE+1]; let r: i64 = x + c[i*EC_STRIDE+3]
32 let y: i64 = c[i*EC_STRIDE+2]; let b: i64 = y + c[i*EC_STRIDE+4]
33 if x < minx { minx = x }
34 if r > maxr { maxr = r }
35 if y < miny { miny = y }
36 if b > maxb { maxb = b }
37 i = i + 1
38 }
39 i = 0
40 while i < n {
41 if mode == 0 { c[i*EC_STRIDE+1] = minx }
42 if mode == 1 { c[i*EC_STRIDE+1] = maxr - c[i*EC_STRIDE+3] }
43 if mode == 2 { c[i*EC_STRIDE+2] = miny }
44 if mode == 3 { c[i*EC_STRIDE+2] = maxb - c[i*EC_STRIDE+4] }
45 if mode == 4 { c[i*EC_STRIDE+1] = (minx + maxr)/2 - c[i*EC_STRIDE+3]/2 }
46 if mode == 5 { c[i*EC_STRIDE+2] = (miny + maxb)/2 - c[i*EC_STRIDE+4]/2 }
47 i = i + 1
48 }
49 return 0
50}
51// distribute left edges evenly between the leftmost and rightmost element (elements given in visual x-order).
52func ec_distribute_h(c: *i64, n: i64) -> i64 {
53 if n < 2 { return 0 }
54 var minx: i64 = c[1]; var maxx: i64 = c[1]
55 var i: i64 = 1
56 while i < n { let x: i64 = c[i*EC_STRIDE+1]; if x < minx { minx = x } if x > maxx { maxx = x } i = i + 1 }
57 let span: i64 = maxx - minx
58 i = 0
59 while i < n { c[i*EC_STRIDE+1] = minx + (i*span)/(n-1); i = i + 1 }
60 return 0
61}
62// move one element.
63func ec_nudge(c: *i64, idx: i64, dx: i64, dy: i64) -> i64 {
64 c[idx*EC_STRIDE+1] = c[idx*EC_STRIDE+1] + dx
65 c[idx*EC_STRIDE+2] = c[idx*EC_STRIDE+2] + dy
66 return 0
67}
68// snapshot / restore (one-deep undo; a history stack is the follow-on).
69func ec_save(c: *i64, n: i64, hist: *i64) -> i64 { var i: i64 = 0; while i < n*EC_STRIDE { hist[i] = c[i]; i = i + 1 } return 0 }
70func ec_undo(c: *i64, n: i64, hist: *i64) -> i64 { var i: i64 = 0; while i < n*EC_STRIDE { c[i] = hist[i]; i = i + 1 } return 0 }
71
72// ---- NO-JS render: the canvas as absolutely-positioned HTML via CSS only (ZERO JavaScript) ----
73// The sovereign editor's surface is server-rendered + CSS-positioned -- editing is server ops (apply an engine op,
74// re-render), never a client-side JS canvas. This is the no-JS-unless-mandatory doctrine made literal.
75func ec_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
76func ec_wn(fd: i64, v: i64) -> i64 {
77 if v == 0 { sys_write(fd, "0" as *u8, 1); return 0 }
78 var m: i64 = v; if m < 0 { sys_write(fd, "-" as *u8, 1); m = 0 - m }
79 let d: *u8 = sys_mmap(24); var k: i64 = 0
80 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
81 var i: i64 = k - 1
82 while i >= 0 { let o: *u8 = sys_mmap(1); o[0] = d[i]; sys_write(fd, o, 1); i = i - 1 }
83 return 0
84}
85func ec_kindlabel(k: i64) -> *u8 { if k==1 { return "heading" as *u8 } if k==2 { return "text" as *u8 } if k==3 { return "button" as *u8 } if k==4 { return "image" as *u8 } return "block" as *u8 }
86// render the canvas to an fd as absolutely-positioned divs (CSS only, NO JavaScript). Token-driven borders.
87func ec_render(c: *i64, n: i64, fd: i64) -> i64 {
88 ec_w(fd, "<div class=\"nx-canvas\" style=\"position:relative;min-height:480px;border:1px solid var(--nx-color-line)\">\n" as *u8)
89 var i: i64 = 0
90 while i < n {
91 ec_w(fd, "<div class=\"nx-el\" style=\"position:absolute;left:" as *u8)
92 ec_wn(fd, c[i*EC_STRIDE+1]); ec_w(fd, "px;top:" as *u8); ec_wn(fd, c[i*EC_STRIDE+2])
93 ec_w(fd, "px;width:" as *u8); ec_wn(fd, c[i*EC_STRIDE+3]); ec_w(fd, "px;height:" as *u8); ec_wn(fd, c[i*EC_STRIDE+4])
94 ec_w(fd, "px;border:1px solid var(--nx-color-line);padding:4px\">" as *u8)
95 ec_w(fd, ec_kindlabel(c[i*EC_STRIDE+0]))
96 ec_w(fd, "</div>\n" as *u8)
97 i = i + 1
98 }
99 ec_w(fd, "</div>\n" as *u8)
100 return 0
101}
102