code wiki / (root) / nx_cms_visual_builder.nx

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, "&amp;" as *u8) } else { 73 if c == 60 { o = vb_puts(body, o, "&lt;" as *u8) } else { 74 if c == 34 { o = vb_puts(body, o, "&quot;" 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}