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}