code wiki / _hdl_build / nx_site_pages.nx

nx_site_pages.nx source

↩ module page · 156 lines · 8027 B

1// nx_site_pages.nx -- DATA-DRIVEN multi-page site generator (operator: finish the andelinwest WEBSITE -- the 2// visitor content off the root domain). The homepage `.site` config already lists the practice areas as cards; 3// the onsite search links each to /practice/<slug>, but those pages did not exist (dangling search results). This 4// organ reuses the PROVEN composer (nx_site_compose compose_build -- the same block-walk that builds the homepage, 5// so practice pages match the brand by construction) to emit, into a per-domain docroot served by the content 6// router (nx_host_router hr_serve3): 7// /index.html -- the homepage (compose the original config) 8// /practice/<slug>.html -- one branded landing page per card; slug = lowercased title, spaces->'-', so 9// "Family Law" -> family-law EXACTLY matches the search URL (no dangling link). 10// Nishi emits the HTML (never hand-authored); pure composition over existing organs. The live /search ENDPOINT 11// (dynamic query->results) + the deploy are separate rungs. license_tier: ORIGINAL 12import "nx_site_compose.nx" 13import "nx_syscalls.nx" 14const K_MAGIC_16384: i64 = 16384 15 16func sp_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 17func sp_cat(out: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { out[o] = s[i]; o = o + 1; i = i + 1 } return o } 18 19// lowercase + space->'-' + keep [a-z0-9-]; "Family Law" -> "family-law". null-terminated. returns length. 20func sp_slug(title: *u8, out: *u8) -> i64 { 21 var o: i64 = 0; var i: i64 = 0 22 while title[i] != (0 as u8) { 23 var c: i64 = title[i] as i64 24 if c >= 65 { if c <= 90 { c = c + 32 } } 25 if c == 32 { out[o] = 45 as u8; o = o + 1 } else { 26 var keep: i64 = 0 27 if c >= 97 { if c <= 122 { keep = 1 } } 28 if c >= 48 { if c <= 57 { keep = 1 } } 29 if c == 45 { keep = 1 } 30 if keep == 1 { out[o] = c as u8; o = o + 1 } 31 } 32 i = i + 1 33 } 34 out[o] = 0 as u8 35 return o 36} 37 38// extract the idx-th '|'-separated field of val into out (null-terminated). returns length. 39func sp_field(val: *u8, idx: i64, out: *u8) -> i64 { 40 var f: i64 = 0; var i: i64 = 0; var o: i64 = 0 41 while val[i] != (0 as u8) { 42 if val[i] == (124 as u8) { f = f + 1 } else { if f == idx { out[o] = val[i]; o = o + 1 } } 43 i = i + 1 44 } 45 out[o] = 0 as u8 46 return o 47} 48 49// copy text[start..end) into out (null-terminated, trailing CR trimmed). returns length. 50func sp_slice(text: *u8, start: i64, end: i64, out: *u8) -> i64 { 51 var o: i64 = 0; var j: i64 = start 52 while j < end { out[o] = text[j]; o = o + 1; j = j + 1 } 53 if o > 0 { if (out[o - 1] as i64) == 13 { o = o - 1 } } 54 out[o] = 0 as u8 55 return o 56} 57 58// build a practice .site config = the homepage config with ONLY the title + hero lines swapped to this practice 59// (every other block copied verbatim, so the practice page passes the same s-class compliance as the homepage). 60// returns length. 61func sp_build_practice(text: *u8, tn: i64, ctitle: *u8, cdesc: *u8, firm: *u8, cta: *u8, href: *u8, out: *u8) -> i64 { 62 var o: i64 = 0 63 var ls: i64 = 0; var i: i64 = 0 64 while i <= tn { 65 var eol: i64 = 0 66 if i == tn { eol = 1 } 67 if i < tn { if (text[i] as i64) == 10 { eol = 1 } } 68 if eol == 1 { 69 if cmp_line_match(text, ls, i, "title" as *u8, 5) >= 0 { 70 o = sp_cat(out, o, "title|" as *u8); o = sp_cat(out, o, ctitle); o = sp_cat(out, o, " -- " as *u8); o = sp_cat(out, o, firm) 71 } else { if cmp_line_match(text, ls, i, "hero" as *u8, 4) >= 0 { 72 o = sp_cat(out, o, "hero|" as *u8); o = sp_cat(out, o, ctitle); out[o] = 124 as u8; o = o + 1 73 o = sp_cat(out, o, cdesc); out[o] = 124 as u8; o = o + 1 74 o = sp_cat(out, o, cta); out[o] = 124 as u8; o = o + 1 75 o = sp_cat(out, o, href) 76 } else { 77 var j: i64 = ls 78 while j < i { out[o] = text[j]; o = o + 1; j = j + 1 } 79 } } 80 if i < tn { out[o] = 10 as u8; o = o + 1 } 81 ls = i + 1 82 } 83 i = i + 1 84 } 85 return o 86} 87 88// GENERATE the homepage + a practice page per card from `config_path` into `docroot`. Returns the practice-page 89// count (>=0), or -1 if the config is unreadable. 90func sp_gen_file(config_path: *u8, docroot: *u8) -> i64 { 91 let szp: *i64 = sys_mmap(16) as *i64 92 let text: *u8 = sys_read_file(config_path, szp) 93 if (text as i64) == 0 { return 0 - 1 } 94 let tn: i64 = szp[0] 95 let dummy: *u8 = "web_assets/game_template.html" as *u8 // unused for the legal page type 96 97 sys_mkdir(docroot, 0x1ed) 98 let prdir: *u8 = sys_mmap(512); var pdo: i64 = sp_cat(prdir, 0, docroot); pdo = sp_cat(prdir, pdo, "/practice" as *u8); prdir[pdo] = 0 as u8 99 sys_mkdir(prdir, 0x1ed) 100 101 // homepage 102 let ipath: *u8 = sys_mmap(512); var io: i64 = sp_cat(ipath, 0, docroot); io = sp_cat(ipath, io, "/index.html" as *u8); ipath[io] = 0 as u8 103 let ifd: i64 = sys_openat_wr(ipath, 0x1a4) 104 if ifd >= 0 { compose_build(text, tn, ifd, dummy); sys_close(ifd) } 105 106 // shared brand chrome (firm / cta label / cta href / footer) -- data-driven from the config 107 let hdr: *u8 = sys_mmap(512); cmp_get(text, tn, "header" as *u8, hdr, 511) 108 let firm: *u8 = sys_mmap(128); sp_field(hdr, 0, firm) 109 let cta: *u8 = sys_mmap(128); sp_field(hdr, 1, cta) 110 let href: *u8 = sys_mmap(128); sp_field(hdr, 2, href) 111 let ftr: *u8 = sys_mmap(512); cmp_get(text, tn, "footer" as *u8, ftr, 511) 112 113 var count: i64 = 0 114 var ls: i64 = 0; var i: i64 = 0 115 while i <= tn { 116 var eol: i64 = 0 117 if i == tn { eol = 1 } 118 if i < tn { if (text[i] as i64) == 10 { eol = 1 } } 119 if eol == 1 { 120 let vs: i64 = cmp_line_match(text, ls, i, "card" as *u8, 4) // value start after "card|", or -1 121 if vs >= 0 { 122 var p1: i64 = vs 123 while p1 < i { if (text[p1] as i64) == 124 { break } p1 = p1 + 1 } 124 let ctitle: *u8 = sys_mmap(128); sp_slice(text, vs, p1, ctitle) 125 let cdesc: *u8 = sys_mmap(512); sp_slice(text, p1 + 1, i, cdesc) 126 let slug: *u8 = sys_mmap(128); sp_slug(ctitle, slug) 127 128 // build the practice .site config = the FULL homepage config with the title + hero lines swapped to 129 // THIS practice (keeps every block, so it is as s-class-compliant as the homepage by construction) 130 let pc: *u8 = sys_mmap(K_MAGIC_16384) 131 let pl: i64 = sp_build_practice(text, tn, ctitle, cdesc, firm, cta, href, pc) 132 133 let ppath: *u8 = sys_mmap(512); var po: i64 = sp_cat(ppath, 0, prdir); po = sp_cat(ppath, po, "/" as *u8); po = sp_cat(ppath, po, slug); po = sp_cat(ppath, po, ".html" as *u8); ppath[po] = 0 as u8 134 let pfd: i64 = sys_openat_wr(ppath, 0x1a4) 135 if pfd >= 0 { compose_build(pc, pl, pfd, dummy); sys_close(pfd); count = count + 1 } 136 } 137 ls = i + 1 138 } 139 i = i + 1 140 } 141 return count 142} 143 144func main(argc: i64, argv: *i64) -> i64 { 145 if argc < 3 { 146 sp_w("usage: nx_site_pages <config.site> <docroot>\n" as *u8) 147 sys_exit(2); return 2 148 } 149 let r: i64 = sp_gen_file(argv[1] as *u8, argv[2] as *u8) 150 if r < 0 { sp_w("SITE-PAGES FAIL: cannot read config\n" as *u8); sys_exit(1); return 1 } 151 sp_w("SITE-PAGES OK: homepage + " as *u8) 152 let db: *u8 = sys_mmap(8); var v: i64 = r; var k: i64 = 0; if v == 0 { db[0] = 48 as u8; k = 1 } else { while v > 0 { db[k] = (48 + (v % 10)) as u8; v = v / 10; k = k + 1 } } 153 let rv: *u8 = sys_mmap(8); var x: i64 = 0; while x < k { rv[x] = db[k - 1 - x]; x = x + 1 } sys_write(1, rv, k) 154 sp_w(" practice pages generated into the docroot (served by the content router; search /practice/<slug> resolves)\n" as *u8) 155 sys_exit(0); return 0 156}