nx_cms_visual_builder.nx source
↩ module page · 293 lines · 16478 B
1// nx_cms_visual_builder.nx -- Converts site blueprints into editable forms and rebuilds them from posted data without client-side JavaScript.
2const K_MAGIC_250000: i64 = 250000
3// nx_cms_visual_builder.nx -- the VISUAL (no-JS) site editor: the .site blueprint rendered as a per-field
4// FORM (title / header / hero / search / card rows / steps / trust / footer, all prefilled) instead of a raw
5// config textarea, plus the server-side REBUILD of the blueprint from the posted form fields. Closes the
6// radar's "visual no-code builder UI" opportunity at v1: field-level WYSIWYG editing with ZERO client JS --
7// real <form> posts carrying the ?s= session; ALL validation/versioning stays in the daemon's existing
8// save path. PURE emit/parse library (no imports, no syscalls; streams caller-buffer -> caller-buffer) so
9// nx_siteedit_daemon composes it without any duplicate-import risk -- the nx_sclass_kit idiom.
10// license_tier: ORIGINAL (no main -- a library)
11
12func vb_puts(buf: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){buf[o]=s[i]; o=o+1; i=i+1} return o }
13func vb_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
14
15// ---- blueprint (line/field) lookup: "key|f0|f1|..." ----
16// start offset of the idx-th line whose key matches (key = chars before the first '|'); -1 absent.
17func vb_line_at(cfg: *u8, n: i64, key: *u8, idx: i64) -> i64 {
18 let kl: i64 = vb_slen(key)
19 var seen: i64 = 0
20 var ls: i64 = 0
21 while ls < n {
22 var le: i64 = ls
23 var sc: i64 = 1
24 while sc == 1 { if le >= n { sc = 0 } else { if (cfg[le] as i64) == 10 { sc = 0 } else { le = le + 1 } } }
25 var m: i64 = 1
26 if ls + kl + 1 > le { m = 0 }
27 if m == 1 {
28 var i: i64 = 0
29 while i < kl { if (cfg[ls + i] as i64) != (key[i] as i64) { m = 0; i = kl } else { i = i + 1 } }
30 if m == 1 { if (cfg[ls + kl] as i64) != 124 { m = 0 } }
31 }
32 if m == 1 {
33 if seen == idx { return ls }
34 seen = seen + 1
35 }
36 ls = le + 1
37 }
38 return 0 - 1
39}
40// end (exclusive) of the line starting at ls
41func vb_line_end(cfg: *u8, n: i64, ls: i64) -> i64 {
42 var le: i64 = ls
43 var sc: i64 = 1
44 while sc == 1 { if le >= n { sc = 0 } else { if (cfg[le] as i64) == 10 { sc = 0 } else { if (cfg[le] as i64) == 13 { sc = 0 } else { le = le + 1 } } } }
45 return le
46}
47// start of field fidx within line [ls..le) (fields split on '|'; field 0 = after the key's '|').
48func vb_fld_start(cfg: *u8, ls: i64, le: i64, fidx: i64) -> i64 {
49 var bars: i64 = 0 - 1
50 var i: i64 = ls
51 while i < le {
52 if (cfg[i] as i64) == 124 {
53 bars = bars + 1
54 if bars == fidx { return i + 1 }
55 }
56 i = i + 1
57 }
58 return 0 - 1
59}
60// end (exclusive) of the field starting at fs within [..le)
61func vb_fld_end(cfg: *u8, fs: i64, le: i64) -> i64 {
62 var i: i64 = fs
63 while i < le { if (cfg[i] as i64) == 124 { return i } i = i + 1 }
64 return le
65}
66// emit cfg[a..b) attribute-escaped (& < ") into body
67func vb_emit_esc(src: *u8, a: i64, b2: i64, body: *u8, off: i64) -> i64 {
68 var o: i64 = off
69 var i: i64 = a
70 while i < b2 {
71 let c: i64 = src[i] as i64
72 if c == 38 { o = vb_puts(body, o, "&" as *u8) } else {
73 if c == 60 { o = vb_puts(body, o, "<" as *u8) } else {
74 if c == 34 { o = vb_puts(body, o, """ as *u8) } else {
75 body[o] = c as u8
76 o = o + 1
77 }
78 }
79 }
80 i = i + 1
81 }
82 return o
83}
84// one labeled input, prefilled from blueprint line `key` (occurrence idx) field fidx.
85func vb_input(cfg: *u8, n: i64, meta: *i64, body: *u8, off: i64) -> i64 {
86 // meta packs the call (arg-count discipline): [0]=key ptr [1]=idx [2]=fidx [3]=label ptr [4]=name ptr
87 let key: *u8 = meta[0] as *u8
88 let idx: i64 = meta[1]
89 let fidx: i64 = meta[2]
90 let label: *u8 = meta[3] as *u8
91 let name: *u8 = meta[4] as *u8
92 var o: i64 = vb_puts(body, off, "<label>" as *u8)
93 o = vb_puts(body, o, label)
94 o = vb_puts(body, o, "<input name=" as *u8)
95 o = vb_puts(body, o, name)
96 o = vb_puts(body, o, " value=\"" as *u8)
97 let ls: i64 = vb_line_at(cfg, n, key, idx)
98 if ls >= 0 {
99 let le: i64 = vb_line_end(cfg, n, ls)
100 let fs: i64 = vb_fld_start(cfg, ls, le, fidx)
101 if fs >= 0 { o = vb_emit_esc(cfg, fs, vb_fld_end(cfg, fs, le), body, o) }
102 }
103 o = vb_puts(body, o, "\"></label>" as *u8)
104 return o
105}
106func vb_meta(meta: *i64, key: *u8, idx: i64, fidx: i64, label: *u8, name: *u8) -> i64 {
107 meta[0] = key as i64
108 meta[1] = idx
109 meta[2] = fidx
110 meta[3] = label as i64
111 meta[4] = name as i64
112 return 0
113}
114
115// ---- THE FORM PAGE BODY (caller wraps with its response helper). sess spliced raw into the action URL. ----
116func vb_form_page(cfg: *u8, n: i64, sraw: *u8, srn: i64, body: *u8, off: i64) -> i64 {
117 var o: i64 = vb_puts(body, off, "<style>fieldset{border:1px solid rgb(214,214,222);border-radius:9px;margin:0 0 14px;padding:10px 14px}legend{font-weight:600;font-size:.9rem;padding:0 6px}label{display:block;font-size:.8rem;color:rgb(90,96,115);margin:.35rem 0}input{display:block;width:100%;box-sizing:border-box;padding:8px;border:1px solid rgb(204,204,204);border-radius:6px;font:inherit;margin-top:2px}.vrow{display:grid;grid-template-columns:1fr 2fr;gap:10px}</style>" as *u8)
118 o = vb_puts(body, o, "<form method=post action=\"/site/vsave?s=" as *u8)
119 var si: i64 = 0
120 while si < srn { body[o] = sraw[si]; o = o + 1; si = si + 1 }
121 o = vb_puts(body, o, "\">" as *u8)
122 let meta: *i64 = (body as i64 + K_MAGIC_250000) as *i64 // scratch tail of the caller's big body buffer (past any page)
123 // identity
124 o = vb_puts(body, o, "<fieldset><legend>Site</legend>" as *u8)
125 vb_meta(meta, "title" as *u8, 0, 0, "Browser tab title" as *u8, "title" as *u8); o = vb_input(cfg, n, meta, body, o)
126 vb_meta(meta, "footer" as *u8, 0, 0, "Footer (firm, location, phone)" as *u8, "footer" as *u8); o = vb_input(cfg, n, meta, body, o)
127 o = vb_puts(body, o, "</fieldset><fieldset><legend>Header</legend>" as *u8)
128 vb_meta(meta, "header" as *u8, 0, 0, "Brand name" as *u8, "brand" as *u8); o = vb_input(cfg, n, meta, body, o)
129 vb_meta(meta, "header" as *u8, 0, 1, "Header button text" as *u8, "hcta" as *u8); o = vb_input(cfg, n, meta, body, o)
130 vb_meta(meta, "header" as *u8, 0, 2, "Header button link" as *u8, "hhref" as *u8); o = vb_input(cfg, n, meta, body, o)
131 o = vb_puts(body, o, "</fieldset><fieldset><legend>Hero</legend>" as *u8)
132 vb_meta(meta, "hero" as *u8, 0, 0, "Big headline" as *u8, "heroh" as *u8); o = vb_input(cfg, n, meta, body, o)
133 vb_meta(meta, "hero" as *u8, 0, 1, "Subtext" as *u8, "herosub" as *u8); o = vb_input(cfg, n, meta, body, o)
134 o = vb_puts(body, o, "</fieldset><fieldset><legend>On-site search</legend>" as *u8)
135 vb_meta(meta, "search" as *u8, 0, 0, "Search action path" as *u8, "searcha" as *u8); o = vb_input(cfg, n, meta, body, o)
136 vb_meta(meta, "search" as *u8, 0, 1, "Search placeholder" as *u8, "searchp" as *u8); o = vb_input(cfg, n, meta, body, o)
137 o = vb_puts(body, o, "</fieldset><fieldset><legend>Service cards (each becomes a page; leave a name blank to drop the card)</legend>" as *u8)
138 // 8 card rows (name + one-line description)
139 vb_meta(meta, "card" as *u8, 0, 0, "Card 1 name" as *u8, "card1t" as *u8); o = vb_puts(body, o, "<div class=vrow>" as *u8); o = vb_input(cfg, n, meta, body, o)
140 vb_meta(meta, "card" as *u8, 0, 1, "Card 1 description" as *u8, "card1b" as *u8); o = vb_input(cfg, n, meta, body, o); o = vb_puts(body, o, "</div>" as *u8)
141 vb_meta(meta, "card" as *u8, 1, 0, "Card 2 name" as *u8, "card2t" as *u8); o = vb_puts(body, o, "<div class=vrow>" as *u8); o = vb_input(cfg, n, meta, body, o)
142 vb_meta(meta, "card" as *u8, 1, 1, "Card 2 description" as *u8, "card2b" as *u8); o = vb_input(cfg, n, meta, body, o); o = vb_puts(body, o, "</div>" as *u8)
143 vb_meta(meta, "card" as *u8, 2, 0, "Card 3 name" as *u8, "card3t" as *u8); o = vb_puts(body, o, "<div class=vrow>" as *u8); o = vb_input(cfg, n, meta, body, o)
144 vb_meta(meta, "card" as *u8, 2, 1, "Card 3 description" as *u8, "card3b" as *u8); o = vb_input(cfg, n, meta, body, o); o = vb_puts(body, o, "</div>" as *u8)
145 vb_meta(meta, "card" as *u8, 3, 0, "Card 4 name" as *u8, "card4t" as *u8); o = vb_puts(body, o, "<div class=vrow>" as *u8); o = vb_input(cfg, n, meta, body, o)
146 vb_meta(meta, "card" as *u8, 3, 1, "Card 4 description" as *u8, "card4b" as *u8); o = vb_input(cfg, n, meta, body, o); o = vb_puts(body, o, "</div>" as *u8)
147 vb_meta(meta, "card" as *u8, 4, 0, "Card 5 name" as *u8, "card5t" as *u8); o = vb_puts(body, o, "<div class=vrow>" as *u8); o = vb_input(cfg, n, meta, body, o)
148 vb_meta(meta, "card" as *u8, 4, 1, "Card 5 description" as *u8, "card5b" as *u8); o = vb_input(cfg, n, meta, body, o); o = vb_puts(body, o, "</div>" as *u8)
149 vb_meta(meta, "card" as *u8, 5, 0, "Card 6 name" as *u8, "card6t" as *u8); o = vb_puts(body, o, "<div class=vrow>" as *u8); o = vb_input(cfg, n, meta, body, o)
150 vb_meta(meta, "card" as *u8, 5, 1, "Card 6 description" as *u8, "card6b" as *u8); o = vb_input(cfg, n, meta, body, o); o = vb_puts(body, o, "</div>" as *u8)
151 vb_meta(meta, "card" as *u8, 6, 0, "Card 7 name" as *u8, "card7t" as *u8); o = vb_puts(body, o, "<div class=vrow>" as *u8); o = vb_input(cfg, n, meta, body, o)
152 vb_meta(meta, "card" as *u8, 6, 1, "Card 7 description" as *u8, "card7b" as *u8); o = vb_input(cfg, n, meta, body, o); o = vb_puts(body, o, "</div>" as *u8)
153 vb_meta(meta, "card" as *u8, 7, 0, "Card 8 name" as *u8, "card8t" as *u8); o = vb_puts(body, o, "<div class=vrow>" as *u8); o = vb_input(cfg, n, meta, body, o)
154 vb_meta(meta, "card" as *u8, 7, 1, "Card 8 description" as *u8, "card8b" as *u8); o = vb_input(cfg, n, meta, body, o); o = vb_puts(body, o, "</div>" as *u8)
155 o = vb_puts(body, o, "</fieldset><fieldset><legend>The 3-step process</legend>" as *u8)
156 vb_meta(meta, "step" as *u8, 0, 0, "Step 1" as *u8, "step1" as *u8); o = vb_input(cfg, n, meta, body, o)
157 vb_meta(meta, "step" as *u8, 1, 0, "Step 2" as *u8, "step2" as *u8); o = vb_input(cfg, n, meta, body, o)
158 vb_meta(meta, "step" as *u8, 2, 0, "Step 3" as *u8, "step3" as *u8); o = vb_input(cfg, n, meta, body, o)
159 o = vb_puts(body, o, "</fieldset><fieldset><legend>Trust points</legend>" as *u8)
160 vb_meta(meta, "sig" as *u8, 0, 0, "Trust point 1" as *u8, "sig1" as *u8); o = vb_input(cfg, n, meta, body, o)
161 vb_meta(meta, "sig" as *u8, 1, 0, "Trust point 2" as *u8, "sig2" as *u8); o = vb_input(cfg, n, meta, body, o)
162 vb_meta(meta, "sig" as *u8, 2, 0, "Trust point 3" as *u8, "sig3" as *u8); o = vb_input(cfg, n, meta, body, o)
163 o = vb_puts(body, o, "</fieldset><p><button type=submit style=\"padding:10px 18px;border:0;border-radius:7px;background:rgb(42,77,143);color:rgb(255,255,255);font-size:.95rem\">Save</button></p></form>" as *u8)
164 return o
165}
166
167// ---- REBUILD: posted form fields -> a .site blueprint (validation stays with the caller). ----
168// find field `name` in the urlencoded body; returns value start or -1; end via vb_v_end.
169func vb_v_start(fb: *u8, fn2: i64, name: *u8) -> i64 {
170 let nl: i64 = vb_slen(name)
171 var i: i64 = 0
172 while i + nl + 1 <= fn2 {
173 var atb: i64 = 0
174 if i == 0 { atb = 1 } else { if (fb[i - 1] as i64) == 38 { atb = 1 } }
175 if atb == 1 {
176 var m: i64 = 1
177 var j: i64 = 0
178 while j < nl { if (fb[i + j] as i64) != (name[j] as i64) { m = 0; j = nl } else { j = j + 1 } }
179 if m == 1 { if (fb[i + nl] as i64) == 61 { return i + nl + 1 } }
180 }
181 i = i + 1
182 }
183 return 0 - 1
184}
185func vb_v_end(fb: *u8, fn2: i64, vs: i64) -> i64 {
186 var i: i64 = vs
187 while i < fn2 { if (fb[i] as i64) == 38 { return i } i = i + 1 }
188 return fn2
189}
190func vb_hexv(c: i64) -> i64 {
191 if c >= 48 { if c <= 57 { return c - 48 } }
192 if c >= 97 { if c <= 102 { return c - 87 } }
193 if c >= 65 { if c <= 70 { return c - 55 } }
194 return 0 - 1
195}
196// urldecode fb[vs..ve) into out; '|' and newlines in VALUES become spaces (they would corrupt the line format).
197func vb_dec_emit(fb: *u8, vs: i64, ve: i64, out: *u8, off: i64) -> i64 {
198 var o: i64 = off
199 var i: i64 = vs
200 while i < ve {
201 var c: i64 = fb[i] as i64
202 var adv: i64 = 1
203 if c == 43 { c = 32 }
204 if c == 37 { if i + 2 < ve {
205 let h1: i64 = vb_hexv(fb[i+1] as i64)
206 let h2: i64 = vb_hexv(fb[i+2] as i64)
207 if h1 >= 0 { if h2 >= 0 { c = h1 * 16 + h2; adv = 3 } }
208 } }
209 if c == 124 { c = 32 }
210 if c == 10 { c = 32 }
211 if c == 13 { c = 32 }
212 out[o] = c as u8
213 o = o + 1
214 i = i + adv
215 }
216 return o
217}
218// nonempty check on the RAW value
219func vb_vhas(fb: *u8, fn2: i64, name: *u8) -> i64 {
220 let vs: i64 = vb_v_start(fb, fn2, name)
221 if vs < 0 { return 0 }
222 if vb_v_end(fb, fn2, vs) > vs { return 1 }
223 return 0
224}
225// emit "<pfx><decoded value>" if the field is nonempty; sep=1 prepends '|'.
226func vb_emit_val(fb: *i64, name: *u8, pfx: *u8, out: *u8, off: i64) -> i64 {
227 // fb packs (ptr,len) to stay under the arg-count discipline
228 let fp: *u8 = fb[0] as *u8
229 let fl: i64 = fb[1]
230 var o: i64 = off
231 o = vb_puts(out, o, pfx)
232 let vs: i64 = vb_v_start(fp, fl, name)
233 if vs >= 0 { o = vb_dec_emit(fp, vs, vb_v_end(fp, fl, vs), out, o) }
234 return o
235}
236// a full "card|t|b\n" line iff the name field is nonempty
237func vb_card_line(fbp: *i64, tname: *u8, bname: *u8, out: *u8, off: i64) -> i64 {
238 let fp: *u8 = fbp[0] as *u8
239 let fl: i64 = fbp[1]
240 if vb_vhas(fp, fl, tname) == 0 { return off }
241 var o: i64 = vb_emit_val(fbp, tname, "card|" as *u8, out, off)
242 o = vb_emit_val(fbp, bname, "|" as *u8, out, o)
243 o = vb_puts(out, o, "\n" as *u8)
244 return o
245}
246func vb_single_line(fbp: *i64, name: *u8, pfx: *u8, out: *u8, off: i64) -> i64 {
247 let fp: *u8 = fbp[0] as *u8
248 let fl: i64 = fbp[1]
249 if vb_vhas(fp, fl, name) == 0 { return off }
250 var o: i64 = vb_emit_val(fbp, name, pfx, out, off)
251 o = vb_puts(out, o, "\n" as *u8)
252 return o
253}
254// THE REBUILD: form body -> blueprint text in out. Returns length (0 = nothing usable).
255func vb_rebuild(fb: *u8, fn2: i64, out: *u8) -> i64 {
256 let fbp: *i64 = (out as i64 + K_MAGIC_250000) as *i64 // pack scratch at the caller buffer's far tail
257 fbp[0] = fb as i64
258 fbp[1] = fn2
259 var o: i64 = 0
260 o = vb_single_line(fbp, "title" as *u8, "title|" as *u8, out, o)
261 if vb_vhas(fb, fn2, "brand" as *u8) == 1 {
262 o = vb_emit_val(fbp, "brand" as *u8, "header|" as *u8, out, o)
263 o = vb_emit_val(fbp, "hcta" as *u8, "|" as *u8, out, o)
264 o = vb_emit_val(fbp, "hhref" as *u8, "|" as *u8, out, o)
265 o = vb_puts(out, o, "\n" as *u8)
266 }
267 if vb_vhas(fb, fn2, "heroh" as *u8) == 1 {
268 o = vb_emit_val(fbp, "heroh" as *u8, "hero|" as *u8, out, o)
269 o = vb_emit_val(fbp, "herosub" as *u8, "|" as *u8, out, o)
270 o = vb_puts(out, o, "\n" as *u8)
271 }
272 if vb_vhas(fb, fn2, "searcha" as *u8) == 1 {
273 o = vb_emit_val(fbp, "searcha" as *u8, "search|" as *u8, out, o)
274 o = vb_emit_val(fbp, "searchp" as *u8, "|" as *u8, out, o)
275 o = vb_puts(out, o, "\n" as *u8)
276 }
277 o = vb_card_line(fbp, "card1t" as *u8, "card1b" as *u8, out, o)
278 o = vb_card_line(fbp, "card2t" as *u8, "card2b" as *u8, out, o)
279 o = vb_card_line(fbp, "card3t" as *u8, "card3b" as *u8, out, o)
280 o = vb_card_line(fbp, "card4t" as *u8, "card4b" as *u8, out, o)
281 o = vb_card_line(fbp, "card5t" as *u8, "card5b" as *u8, out, o)
282 o = vb_card_line(fbp, "card6t" as *u8, "card6b" as *u8, out, o)
283 o = vb_card_line(fbp, "card7t" as *u8, "card7b" as *u8, out, o)
284 o = vb_card_line(fbp, "card8t" as *u8, "card8b" as *u8, out, o)
285 o = vb_single_line(fbp, "step1" as *u8, "step|" as *u8, out, o)
286 o = vb_single_line(fbp, "step2" as *u8, "step|" as *u8, out, o)
287 o = vb_single_line(fbp, "step3" as *u8, "step|" as *u8, out, o)
288 o = vb_single_line(fbp, "sig1" as *u8, "sig|" as *u8, out, o)
289 o = vb_single_line(fbp, "sig2" as *u8, "sig|" as *u8, out, o)
290 o = vb_single_line(fbp, "sig3" as *u8, "sig|" as *u8, out, o)
291 o = vb_single_line(fbp, "footer" as *u8, "footer|" as *u8, out, o)
292 return o
293}