code wiki / _hdl_build / nx_sitegen_freeform.nx

nx_sitegen_freeform.nx source

↩ module page · 172 lines · 8806 B

1// nx_sitegen_freeform.nx -- AUTO-BUILDER, FREE-FORM CANVAS page profile (the power-user capability tier). Unlike 2// the responsive block-walk (structured) and the guided wizard, this lets elements be PLACED FREELY on a canvas 3// (x/y/width), like Wix's free-form editor -- but, unlike Wix, the result stays SOVEREIGN (built by the engine), 4// PORTABLE (the `el|..` lines are plain data that round-trips), and INJECTION-SAFE (every text + href escaped via 5// hs_escape). The free-form tier trades the structural publish-gate (by design -- it's a free canvas) but KEEPS 6// escaping + portability + sovereignty. Data format, one element per line: 7// el|<heading|text|button|image>|x|y|w|text|href 8// se_build_freeform = the free-form endpoint: parse the el| doc, build the canvas page. license_tier: ORIGINAL 9import "nx_html_sanitize.nx" 10import "nx_syscalls.nx" 11const K_MAGIC_131072: i64 = 131072 12 13func ff_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 } 14func ff_wnum(fd: i64, v: i64) -> i64 { 15 let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0 16 if m==0{t[0]=48 as u8;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} 17 var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 18} 19// escape an untrusted field (text or href) and write it to fd 20func ff_esc(fd: i64, src: *u8) -> i64 { 21 if (src as i64) == 0 { return 0 } 22 var sl: i64 = 0 23 while src[sl] != (0 as u8) { sl = sl + 1 } 24 let dst: *u8 = sys_mmap(sl*8 + 16) 25 let n: i64 = hs_escape(src, sl, dst, sl*8 + 15) 26 sys_write(fd, dst, n) 27 return 0 28} 29 30func ff_linematch(doc: *u8, ls: i64, le: i64, key: *u8, klen: i64) -> i64 { 31 if ls+klen+1 > le { return 0-1 } 32 var i: i64=0 33 while i<klen { if (doc[ls+i] as i64)!=(key[i] as i64){return 0-1} i=i+1 } 34 if (doc[ls+klen] as i64)!=124 { return 0-1 } 35 return ls+klen+1 36} 37func ff_range_eq(s: *u8, a: i64, b: i64, lit: *u8) -> i64 { 38 var e: i64 = b 39 if e>a { if (s[e-1] as i64)==13 { e=e-1 } } 40 var i: i64=a; var k: i64=0 41 while i<e { if lit[k]==(0 as u8){return 0} if (s[i] as i64)!=(lit[k] as i64){return 0} i=i+1; k=k+1 } 42 if lit[k]==(0 as u8){return 1} 43 return 0 44} 45func ff_atoi(doc: *u8, a: i64, b: i64) -> i64 { 46 var v: i64=0; var j: i64=a 47 while j<b { let c: i64=doc[j] as i64; if c>=48 { if c<=57 { v=v*10+(c-48) } } j=j+1 } 48 return v 49} 50func ff_kind(doc: *u8, a: i64, b: i64) -> i64 { 51 if ff_range_eq(doc,a,b,"heading" as *u8)==1 { return 1 } 52 if ff_range_eq(doc,a,b,"text" as *u8)==1 { return 2 } 53 if ff_range_eq(doc,a,b,"button" as *u8)==1 { return 3 } 54 if ff_range_eq(doc,a,b,"image" as *u8)==1 { return 4 } 55 return 2 56} 57func ff_cpy(arena: *u8, ap: i64, doc: *u8, a: i64, b: i64) -> i64 { 58 var o: i64=ap; var j: i64=a 59 while j<b { arena[o]=doc[j]; o=o+1; j=j+1 } 60 if o>ap { if (arena[o-1] as i64)==13 { o=o-1 } } 61 arena[o]=0 as u8; o=o+1 62 return o 63} 64 65// parse a free-form doc: title|.. + el|kind|x|y|w|text|href lines. fields copied to arena. return element count. 66func ff_parse(doc: *u8, n: i64, kinds: *i64, xs: *i64, ys: *i64, ws: *i64, texts: *i64, hrefs: *i64, maxn: i64, arena: *u8, title_out: *u8, tcap: i64) -> i64 { 67 let fs: *i64 = sys_mmap(8*8) as *i64 68 let fe: *i64 = sys_mmap(8*8) as *i64 69 var ap: i64 = 0 70 var cnt: i64 = 0 71 var ls: i64 = 0 72 var i: i64 = 0 73 title_out[0] = 0 as u8 74 while i <= n { 75 var eol: i64=0 76 if i==n { eol=1 } 77 if i<n { if (doc[i] as i64)==10 { eol=1 } } 78 if eol==1 { 79 let tv: i64 = ff_linematch(doc, ls, i, "title" as *u8, 5) 80 if tv >= 0 { 81 var o: i64=0; var j: i64=tv 82 while j<i { if o<tcap-1 { title_out[o]=doc[j]; o=o+1 } j=j+1 } 83 if o>0 { if (title_out[o-1] as i64)==13 { o=o-1 } } 84 title_out[o]=0 as u8 85 } else { 86 let ev: i64 = ff_linematch(doc, ls, i, "el" as *u8, 2) 87 if ev >= 0 { 88 if cnt < maxn { 89 // split [ev,i) into up to 6 fields by '|' 90 var f: i64=0 91 while f<6 { fs[f]=i; fe[f]=i; f=f+1 } 92 var nf: i64=0 93 var fstart: i64=ev 94 var cur: i64=ev 95 while cur <= i { 96 var fb: i64=0 97 if cur==i { fb=1 } 98 if cur<i { if (doc[cur] as i64)==124 { fb=1 } } 99 if fb==1 { 100 if nf<6 { fs[nf]=fstart; fe[nf]=cur; nf=nf+1 } 101 fstart=cur+1 102 } 103 cur=cur+1 104 } 105 kinds[cnt] = ff_kind(doc, fs[0], fe[0]) 106 xs[cnt] = ff_atoi(doc, fs[1], fe[1]) 107 ys[cnt] = ff_atoi(doc, fs[2], fe[2]) 108 ws[cnt] = ff_atoi(doc, fs[3], fe[3]) 109 texts[cnt] = (arena + ap) as i64 110 ap = ff_cpy(arena, ap, doc, fs[4], fe[4]) 111 hrefs[cnt] = (arena + ap) as i64 112 ap = ff_cpy(arena, ap, doc, fs[5], fe[5]) 113 cnt = cnt + 1 114 } 115 } 116 } 117 ls = i + 1 118 } 119 i = i + 1 120 } 121 return cnt 122} 123 124// build a free-form canvas page to fd from the parsed element arrays. returns element count. 125func ff_build(fd: i64, title: *u8, n: i64, kinds: *i64, xs: *i64, ys: *i64, ws: *i64, texts: *i64, hrefs: *i64) -> i64 { 126 ff_w(fd, "<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\">\n" as *u8) 127 ff_w(fd, "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n" as *u8) 128 ff_w(fd, "<title>" as *u8); ff_esc(fd, title); ff_w(fd, "</title>\n<style>\n" as *u8) 129 ff_w(fd, "*{box-sizing:border-box}body{margin:0;font:16px/1.5 system-ui,-apple-system,Segoe UI,sans-serif;color:#1b2330;background:#eef2f7}\n" as *u8) 130 ff_w(fd, ".canvas{position:relative;width:1000px;min-height:700px;margin:20px auto;background:#fff;border:1px solid #d7deea;box-shadow:0 2px 14px rgba(0,0,0,.06)}\n" as *u8) 131 ff_w(fd, ".el{position:absolute}.el h2{margin:0}.el p{margin:0}\n" as *u8) 132 ff_w(fd, ".btn{display:inline-block;padding:10px 16px;background:#1a6dff;color:#fff;text-decoration:none;border-radius:8px}img{max-width:100%;display:block}\n" as *u8) 133 ff_w(fd, "@media(max-width:1040px){.canvas{width:100%}}\n" as *u8) // honest: a fixed canvas degrades on small screens 134 ff_w(fd, "</style></head><body>\n<div class=\"canvas\">\n" as *u8) 135 var i: i64 = 0 136 while i < n { 137 ff_w(fd, "<div class=\"el\" style=\"left:" as *u8); ff_wnum(fd, xs[i]) 138 ff_w(fd, "px;top:" as *u8); ff_wnum(fd, ys[i]) 139 ff_w(fd, "px;width:" as *u8); ff_wnum(fd, ws[i]); ff_w(fd, "px\">" as *u8) 140 let k: i64 = kinds[i] 141 if k == 1 { ff_w(fd, "<h2>" as *u8); ff_esc(fd, texts[i] as *u8); ff_w(fd, "</h2>" as *u8) } 142 if k == 2 { ff_w(fd, "<p>" as *u8); ff_esc(fd, texts[i] as *u8); ff_w(fd, "</p>" as *u8) } 143 if k == 3 { ff_w(fd, "<a class=\"btn\" href=\"" as *u8); ff_esc(fd, hrefs[i] as *u8); ff_w(fd, "\">" as *u8); ff_esc(fd, texts[i] as *u8); ff_w(fd, "</a>" as *u8) } 144 if k == 4 { ff_w(fd, "<img src=\"" as *u8); ff_esc(fd, hrefs[i] as *u8); ff_w(fd, "\" alt=\"" as *u8); ff_esc(fd, texts[i] as *u8); ff_w(fd, "\">" as *u8) } 145 ff_w(fd, "</div>\n" as *u8) 146 i = i + 1 147 } 148 ff_w(fd, "</div>\n</body></html>\n" as *u8) 149 return n 150} 151 152// the FREE-FORM endpoint: parse an el| doc + build the canvas page into out; returns built length. 153func se_build_freeform(payload: *u8, n: i64, out: *u8, cap: i64) -> i64 { 154 let kinds: *i64 = sys_mmap(8*128) as *i64 155 let xs: *i64 = sys_mmap(8*128) as *i64 156 let ys: *i64 = sys_mmap(8*128) as *i64 157 let ws: *i64 = sys_mmap(8*128) as *i64 158 let texts: *i64 = sys_mmap(8*128) as *i64 159 let hrefs: *i64 = sys_mmap(8*128) as *i64 160 let arena: *u8 = sys_mmap(K_MAGIC_131072) 161 let title: *u8 = sys_mmap(512) 162 let cnt: i64 = ff_parse(payload, n, kinds, xs, ys, ws, texts, hrefs, 128, arena, title, 511) 163 let path: *u8 = "/tmp/nx_ff_build.html" as *u8 164 let fd: i64 = sys_openat_wr(path, 0x1a4) 165 if fd >= 0 { ff_build(fd, title, cnt, kinds, xs, ys, ws, texts, hrefs); sys_close(fd) } 166 let rfd: i64 = sys_openat_rd(path) 167 if rfd < 0 { return 0 - 1 } 168 var total: i64 = 0; var go: i64 = 1 169 while go == 1 { let r: i64 = sys_read(rfd, out + total, cap - total); if r <= 0 { go = 0 } if r > 0 { total = total + r } if total >= cap { go = 0 } } 170 sys_close(rfd) 171 return total 172}