code wiki / _hdl_build / nx_site_build.nx

nx_site_build.nx source

↩ module page · 531 lines · 27226 B

1// nx_site_build.nx -- R1 WHOLE-SITE emitter. From ONE .site blueprint, emit a multi-PAGE site with a SHARED 2// cross-page nav, INTERNAL links between pages, and its own sitemap.xml + robots.txt -- all through the gorgeous 3// S-class kit (nx_sclass_kit sk_*), so every page is s-class-by-construction. Closes the whole-site gaps the 4// factory census named: shared nav, internal <a href> links, and wired sitemap/robots EMISSION over our output. 5// Reuses the andelinwest .site blueprint verbatim (title|/header|brand|cta|href /hero|headline|sub|cta|href 6// /search|action|placeholder /cards+card|title|body /steps+step| /trust+sig| /footer|): the page LIST is derived 7// from the cards (like nx_site_pages), but upgraded to the premium kit + real navigation + a self-emitted sitemap. 8// usage: nx_site_build <config.site> <docroot> <domain> [theme:0|1] 9// emits: <docroot>/index.html + <docroot>/practice/<slug>.html (one per card) + sitemap.xml + robots.txt 10// SCALE: one buffer is mmap'd ONCE and reused per page (no per-iteration mmap -> no bump-allocator starvation). 11// license_tier: ORIGINAL 12import "nx_sclass_kit.nx" 13import "nx_nishi_editorial.nx" 14import "nx_syscalls.nx" 15const SB_MAGIC_1024: i64 = 1024 16const SB_MAGIC_131072: i64 = 131072 17const SB_MAGIC_262144: i64 = 262144 18 19const SB_ICON: *u8 = "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 3l8 4v5c0 4-3.5 7-8 9-4.5-2-8-5-8-9V7l8-4z\"/></svg>" 20 21func sb_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 22func sb_wn(v: i64) -> i64 { 23 if v==0 { sys_write(1,"0" as *u8,1); return 0 } 24 var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m } 25 let t: *u8=sys_mmap(24); var k: i64=0 26 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 27 let o2: *u8=sys_mmap(24); var w: i64=0; var q: i64=k-1 28 while q>=0 { o2[w]=t[q]; w=w+1; q=q-1 } 29 sys_write(1,o2,w); return 0 30} 31func sb_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 32func sb_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 } 33 34// copy text[start..end) into arena at ap (trim a trailing CR), NUL-terminate, return new ap. 35func sb_copy(arena: *u8, ap: i64, text: *u8, start: i64, end: i64) -> i64 { 36 var o: i64=ap; var j: i64=start 37 while j<end { arena[o]=text[j]; o=o+1; j=j+1 } 38 if o>ap { if (arena[o-1] as i64)==13 { o=o-1 } } 39 arena[o]=0 as u8; o=o+1 40 return o 41} 42// does text[ls..le) start with key|? return value-start idx, or -1. 43func sb_lm(text: *u8, ls: i64, le: i64, key: *u8, klen: i64) -> i64 { 44 if ls+klen+1 > le { return 0 - 1 } 45 var i: i64=0 46 while i<klen { if (text[ls+i] as i64)!=(key[i] as i64) { return 0 - 1 } i=i+1 } 47 if (text[ls+klen] as i64)!=124 { return 0 - 1 } 48 return ls+klen+1 49} 50// value of the first "key|value" line -> out (NUL-term, trailing CR trimmed). return len, or -1. 51func sb_cg(text: *u8, tn: i64, key: *u8, out: *u8, cap: i64) -> i64 { 52 let klen: i64 = sb_slen(key) 53 var ls: i64=0; var i: i64=0 54 while i<=tn { 55 var eol: i64=0 56 if i==tn { eol=1 } 57 if i<tn { if (text[i] as i64)==10 { eol=1 } } 58 if eol==1 { 59 let vs: i64 = sb_lm(text, ls, i, key, klen) 60 if vs>=0 { 61 var o: i64=0; var j: i64=vs 62 while j<i { if o<cap-1 { out[o]=text[j]; o=o+1 } j=j+1 } 63 if o>0 { if (out[o-1] as i64)==13 { o=o-1 } } 64 out[o]=0 as u8 65 return o 66 } 67 ls=i+1 68 } 69 i=i+1 70 } 71 out[0]=0 as u8 72 return 0 - 1 73} 74// idx-th '|'-separated field of NUL-term val -> out (NUL-term). return len. 75func sb_fld(val: *u8, idx: i64, out: *u8) -> i64 { 76 var f: i64=0; var i: i64=0; var o: i64=0 77 while val[i]!=(0 as u8) { 78 if (val[i] as i64)==124 { f=f+1 } else { if f==idx { out[o]=val[i]; o=o+1 } } 79 i=i+1 80 } 81 out[o]=0 as u8 82 return o 83} 84// slug: lowercase, space->'-', keep [a-z0-9-]. "Family Law" -> "family-law". return len. 85func sb_slug(title: *u8, out: *u8) -> i64 { 86 var o: i64=0; var i: i64=0 87 while title[i]!=(0 as u8) { 88 var c: i64 = title[i] as i64 89 if c>=65 { if c<=90 { c=c+32 } } 90 if c==32 { out[o]=45 as u8; o=o+1 } else { 91 var keep: i64=0 92 if c>=97 { if c<=122 { keep=1 } } 93 if c>=48 { if c<=57 { keep=1 } } 94 if c==45 { keep=1 } 95 if keep==1 { out[o]=c as u8; o=o+1 } 96 } 97 i=i+1 98 } 99 out[o]=0 as u8 100 return o 101} 102 103// collect every "card|title|body" line: fill ct/cb/cs (arena ptrs). return count. 104func sb_cards(text: *u8, tn: i64, ct: *i64, cb: *i64, cs: *i64, arena: *u8, apP: *i64, maxc: i64) -> i64 { 105 var cnt: i64=0; var ls: i64=0; var i: i64=0 106 while i<=tn { 107 var eol: i64=0 108 if i==tn { eol=1 } 109 if i<tn { if (text[i] as i64)==10 { eol=1 } } 110 if eol==1 { 111 let vs: i64 = sb_lm(text, ls, i, "card" as *u8, 4) 112 if vs>=0 { if cnt<maxc { 113 var p1: i64=vs 114 while p1<i { if (text[p1] as i64)==124 { break } p1=p1+1 } 115 var ap: i64 = apP[0] 116 ct[cnt] = (arena + ap) as i64 117 ap = sb_copy(arena, ap, text, vs, p1) 118 cb[cnt] = (arena + ap) as i64 119 ap = sb_copy(arena, ap, text, p1+1, i) 120 cs[cnt] = (arena + ap) as i64 121 let sl: i64 = sb_slug(ct[cnt] as *u8, (arena + ap) as *u8) 122 ap = ap + sl + 1 123 apP[0] = ap 124 cnt = cnt + 1 125 } } 126 ls=i+1 127 } 128 i=i+1 129 } 130 return cnt 131} 132// collect the single value of every "key|value" line (steps, sigs). return count. 133func sb_single(text: *u8, tn: i64, key: *u8, klen: i64, ptrs: *i64, arena: *u8, apP: *i64, maxc: i64) -> i64 { 134 var cnt: i64=0; var ls: i64=0; var i: i64=0 135 while i<=tn { 136 var eol: i64=0 137 if i==tn { eol=1 } 138 if i<tn { if (text[i] as i64)==10 { eol=1 } } 139 if eol==1 { 140 let vs: i64 = sb_lm(text, ls, i, key, klen) 141 if vs>=0 { if cnt<maxc { 142 var ap: i64 = apP[0] 143 ptrs[cnt] = (arena + ap) as i64 144 ap = sb_copy(arena, ap, text, vs, i) 145 apP[0] = ap 146 cnt = cnt + 1 147 } } 148 ls=i+1 149 } 150 i=i+1 151 } 152 return cnt 153} 154 155// build "/practice/<slug>.html" into out. return len. 156func sb_phref(out: *u8, slug: *u8) -> i64 { 157 var o: i64 = sb_cat(out, 0, "/practice/" as *u8) 158 o = sb_cat(out, o, slug); o = sb_cat(out, o, ".html" as *u8) 159 out[o]=0 as u8 160 return o 161} 162// emit the SHARED nav (Home + one item per card) into buf. hrefbuf is reused scratch. return new off. 163func emit_nav(buf: *u8, off: i64, ncards: i64, ct: *i64, cs: *i64, hrefbuf: *u8) -> i64 { 164 var o: i64 = sk_nav_item(buf, off, "Home" as *u8, "/" as *u8) 165 var i: i64=0 166 while i<ncards { 167 sb_phref(hrefbuf, cs[i] as *u8) 168 o = sk_nav_item(buf, o, ct[i] as *u8, hrefbuf) 169 i=i+1 170 } 171 return o 172} 173// emit the trust band from the sig[] signals. return new off. 174func emit_trust(buf: *u8, off: i64, nsigs: i64, sigs: *i64) -> i64 { 175 if nsigs<=0 { return off } 176 var o: i64 = sk_trust_open(buf, off, "Why us" as *u8, "Trusted, by construction" as *u8, "Every page ships accessible, fast, and sovereign -- by construction." as *u8) 177 var i: i64=0 178 while i<nsigs { o = sk_trust_item(buf, o, sigs[i] as *u8, "Verified." as *u8); i=i+1 } 179 o = sk_trust_close(buf, o) 180 return o 181} 182 183// sitemap.xml + robots.txt for the emitted page set. Extracted so BOTH presentation tiers emit the same 184// SEO surface from one place -- a second copy is how the two tiers would silently drift apart. 185func sb_emit_sitemap_robots(buf: *u8, pathbuf: *u8, docroot: *u8, domain: *u8, ncards: i64, cs: *i64) -> i64 { 186 var o: i64 = sb_cat(buf, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n" as *u8) 187 o = sb_cat(buf, o, "<url><loc>https://" as *u8); o = sb_cat(buf, o, domain); o = sb_cat(buf, o, "/</loc><priority>1.0</priority></url>\n" as *u8) 188 var i: i64 = 0 189 while i < ncards { 190 o = sb_cat(buf, o, "<url><loc>https://" as *u8); o = sb_cat(buf, o, domain); o = sb_cat(buf, o, "/practice/" as *u8); o = sb_cat(buf, o, cs[i] as *u8); o = sb_cat(buf, o, ".html</loc><priority>0.8</priority></url>\n" as *u8) 191 i=i+1 192 } 193 o = sb_cat(buf, o, "</urlset>\n" as *u8) 194 var smo: i64 = sb_cat(pathbuf, 0, docroot); smo = sb_cat(pathbuf, smo, "/sitemap.xml" as *u8); pathbuf[smo]=0 as u8 195 let smfd: i64 = sys_openat_wr(pathbuf, 0x1a4) 196 if smfd >= 0 { sys_write(smfd, buf, o); sys_close(smfd) } 197 198 o = sb_cat(buf, 0, "User-agent: *\nAllow: /\nSitemap: https://" as *u8); o = sb_cat(buf, o, domain); o = sb_cat(buf, o, "/sitemap.xml\n" as *u8) 199 var rbo: i64 = sb_cat(pathbuf, 0, docroot); rbo = sb_cat(pathbuf, rbo, "/robots.txt" as *u8); pathbuf[rbo]=0 as u8 200 let rbfd: i64 = sys_openat_wr(pathbuf, 0x1a4) 201 if rbfd >= 0 { sys_write(rbfd, buf, o); sys_close(rbfd) } 202 return 0 203} 204 205// emit the shared editorial site nav (Home + one entry per practice area + Consult). 206func sb_ed_nav(buf: *u8, off: i64, brand: *u8, ncards: i64, ct: *i64, cs: *i64, hrefbuf: *u8, cur: i64) -> i64 { 207 var o: i64 = ed_firm_nav_open(buf, off, brand, "/" as *u8) 208 var home_cur: i64 = 0 209 if cur < 0 { home_cur = 1 } 210 o = ed_firm_nav_item(buf, o, "Home" as *u8, "/" as *u8, home_cur) 211 var i: i64 = 0 212 while i < ncards { 213 sb_phref(hrefbuf, cs[i] as *u8) 214 var isc: i64 = 0 215 if i == cur { isc = 1 } 216 o = ed_firm_nav_item(buf, o, ct[i] as *u8, hrefbuf, isc) 217 i = i + 1 218 } 219 o = ed_firm_nav_item(buf, o, "Consult" as *u8, "/#consult" as *u8, 0) 220 return ed_firm_nav_close(buf, o) 221} 222 223// THE WHOLE-SITE BUILD as a callable core (composed by the CLI main below AND the site-edit 224// daemon): read the .site blueprint at `config`, emit index + practice pages + sitemap + robots 225// into `docroot`. Returns ncards (>=1) on success, negative on failure. NO sys_exit here. 226func sb_build_site(config: *u8, docroot: *u8, domain: *u8, theme: i64) -> i64 { 227 let szp: *i64 = sys_mmap(16) as *i64 228 let text: *u8 = sys_read_file(config, szp) 229 if (text as i64) == 0 { sb_w("SITE-BUILD FAIL: cannot read config\n" as *u8); return 0 - 1 } 230 let tn: i64 = szp[0] 231 232 // ---- parse scalars (data-driven; content is the caller's) ---- 233 let titlebase: *u8 = sys_mmap(512); sb_cg(text, tn, "title" as *u8, titlebase, 511) 234 let hdr: *u8 = sys_mmap(512); sb_cg(text, tn, "header" as *u8, hdr, 511) 235 let brand: *u8 = sys_mmap(160); sb_fld(hdr, 0, brand) 236 let hcta: *u8 = sys_mmap(160); sb_fld(hdr, 1, hcta) 237 let hhref: *u8 = sys_mmap(160); sb_fld(hdr, 2, hhref) 238 let hero: *u8 = sys_mmap(SB_MAGIC_1024); sb_cg(text, tn, "hero" as *u8, hero, 1023) 239 let hl: *u8 = sys_mmap(512); sb_fld(hero, 0, hl) 240 let hsub: *u8 = sys_mmap(512); sb_fld(hero, 1, hsub) 241 let search: *u8 = sys_mmap(512); sb_cg(text, tn, "search" as *u8, search, 511) 242 let saction: *u8 = sys_mmap(160); sb_fld(search, 0, saction) 243 let splace: *u8 = sys_mmap(256); sb_fld(search, 1, splace) 244 let footer: *u8 = sys_mmap(512); sb_cg(text, tn, "footer" as *u8, footer, 511) 245 246 // ---- collect cards / steps / sigs into one arena ---- 247 let arena: *u8 = sys_mmap(SB_MAGIC_131072) 248 let apP: *i64 = sys_mmap(16) as *i64; apP[0]=0 249 let ct: *i64 = sys_mmap(8*32) as *i64 250 let cb: *i64 = sys_mmap(8*32) as *i64 251 let cs: *i64 = sys_mmap(8*32) as *i64 252 let ncards: i64 = sb_cards(text, tn, ct, cb, cs, arena, apP, 32) 253 let steps: *i64 = sys_mmap(8*8) as *i64 254 let nsteps: i64 = sb_single(text, tn, "step" as *u8, 4, steps, arena, apP, 3) 255 let sigs: *i64 = sys_mmap(8*16) as *i64 256 let nsigs: i64 = sb_single(text, tn, "sig" as *u8, 3, sigs, arena, apP, 12) 257 258 // ---- shared scratch (hoisted ONCE; reused per page) ---- 259 let buf: *u8 = sys_mmap(SB_MAGIC_262144) 260 let pathbuf: *u8 = sys_mmap(512) 261 let hrefbuf: *u8 = sys_mmap(256) 262 let numbuf: *u8 = sys_mmap(8) 263 let stepttl: *u8 = sys_mmap(32) 264 let titlebuf2: *u8 = sys_mmap(320) 265 266 sys_mkdir(docroot, 0x1ed) 267 var pdo: i64 = sb_cat(pathbuf, 0, docroot); pdo = sb_cat(pathbuf, pdo, "/practice" as *u8); pathbuf[pdo]=0 as u8 268 sys_mkdir(pathbuf, 0x1ed) 269 270 var i: i64 = 0 271 272 // ================= EDITORIAL TIER ================= 273 // `tier|editorial` selects the editorial presentation (nx_nishi_editorial -- the system behind /kitchen). 274 // ABSENT -> the original s-class path below runs untouched, so every other site this emitter builds keeps 275 // its current bytes. The site GRAPH (cards -> pages, nav, sitemap, robots) is shared: only presentation forks. 276 let tierv: *u8 = sys_mmap(64) 277 let has_tier: i64 = sb_cg(text, tn, "tier" as *u8, tierv, 63) 278 var ed_on: i64 = 0 279 if has_tier > 0 { if (tierv[0] as i64) == 101 { ed_on = 1 } } 280 if ed_on == 1 { 281 let eyeb: *u8 = sys_mmap(256); sb_cg(text, tn, "eyebrow" as *u8, eyeb, 255) 282 let hrow: *u8 = sys_mmap(SB_MAGIC_1024); sb_cg(text, tn, "hero" as *u8, hrow, 1023) 283 let eh1: *u8 = sys_mmap(512); sb_fld(hrow, 0, eh1) 284 let eem: *u8 = sys_mmap(256); sb_fld(hrow, 1, eem) 285 let ethe: *u8 = sys_mmap(768); sb_fld(hrow, 2, ethe) 286 let artv: *u8 = sys_mmap(512); sb_cg(text, tn, "art" as *u8, artv, 511) 287 let artk: *u8 = sys_mmap(16); sb_fld(artv, 0, artk) 288 let artl: *u8 = sys_mmap(256); sb_fld(artv, 1, artl) 289 var akind: i64 = 1 290 if (artk[0] as i64) >= 49 { akind = (artk[0] as i64) - 48 } 291 let ephone: *u8 = sys_mmap(128); sb_cg(text, tn, "phone" as *u8, ephone, 127) 292 let email: *u8 = sys_mmap(160); sb_cg(text, tn, "email" as *u8, email, 159) 293 let eaddr: *u8 = sys_mmap(256); sb_cg(text, tn, "address" as *u8, eaddr, 255) 294 let ehours: *u8 = sys_mmap(256); sb_cg(text, tn, "hours" as *u8, ehours, 255) 295 let eintake: *u8 = sys_mmap(256); sb_cg(text, tn, "intake" as *u8, eintake, 255) 296 let edisc: *u8 = sys_mmap(SB_MAGIC_1024); sb_cg(text, tn, "disclaimer" as *u8, edisc, 1023) 297 let ehint: *u8 = sys_mmap(256); sb_cg(text, tn, "hint" as *u8, ehint, 255) 298 // attorneys: name|role|bio|monogram|pending 299 let atts: *i64 = sys_mmap(8*12) as *i64 300 let natt: i64 = sb_single(text, tn, "attorney" as *u8, 8, atts, arena, apP, 12) 301 let afn: *u8 = sys_mmap(256) 302 let afr: *u8 = sys_mmap(256) 303 let afb: *u8 = sys_mmap(SB_MAGIC_1024) 304 let afm: *u8 = sys_mmap(16) 305 let afp: *u8 = sys_mmap(16) 306 let cbody: *u8 = sys_mmap(SB_MAGIC_1024) 307 let csubs: *u8 = sys_mmap(SB_MAGIC_1024) 308 let onesub: *u8 = sys_mmap(256) 309 let secn: *u8 = sys_mmap(8) 310 var sn: i64 = 1 311 312 // ---------------- index ---------------- 313 // `paper|1` opts the whole site into the paper reading tier (measured-to-paper contrast, stillness, 314 // book measure). Absent -> the standard editorial palette, so no existing site changes. 315 let paperv: *u8 = sys_mmap(32) 316 let has_paper: i64 = sb_cg(text, tn, "paper" as *u8, paperv, 31) 317 var paper_on: i64 = 0 318 if has_paper > 0 { if (paperv[0] as i64) == 49 { paper_on = 1 } } 319 320 var o: i64 = ed_doc_open(buf, 0, titlebase) 321 o = ed_firm_css(buf, o) 322 if paper_on == 1 { o = ed_paper_css(buf, o) } 323 o = sb_ed_nav(buf, o, brand, ncards, ct, cs, hrefbuf, 0 - 1) 324 o = ed_firm_mast_open(buf, o, eyeb, eh1, eem, ethe, hcta, hhref, "Our expertise" as *u8, "#expertise" as *u8, ehint) 325 o = ed_artpanel(buf, o, akind, artl) 326 o = ed_firm_mast_close(buf, o) 327 o = ed_firm_search(buf, o, saction, splace, "Search this site" as *u8) 328 329 secn[0]=48 as u8; secn[1]=(48+sn) as u8; secn[2]=0 as u8; sn=sn+1 330 o = ed_sec_open(buf, o, secn, "How we help" as *u8) 331 o = ed_raw(buf, o, "<div class='grid' id='expertise'>" as *u8) 332 i = 0 333 while i < ncards { 334 sb_fld(cb[i] as *u8, 0, cbody) 335 sb_fld(cb[i] as *u8, 1, csubs) 336 sb_phref(hrefbuf, cs[i] as *u8) 337 o = ed_cell_open(buf, o, "Practice area" as *u8, ct[i] as *u8, cbody, hrefbuf) 338 // sub-items are a comma list in the card's 3rd field; emitted only when present, so a plain 339 // card never ships an empty <ul>. 340 if (csubs[0] as i64) != 0 { 341 o = ed_subs_open(buf, o) 342 var sp: i64 = 0; var so2: i64 = 0 343 while 1 == 1 { 344 let ch: i64 = csubs[sp] as i64 345 if ch == 44 { onesub[so2]=0 as u8; if so2>0 { o = ed_sub(buf, o, onesub, hrefbuf) } so2=0 } 346 if ch == 0 { onesub[so2]=0 as u8; if so2>0 { o = ed_sub(buf, o, onesub, hrefbuf) } break } 347 if ch != 44 { if ch != 32 { onesub[so2]=ch as u8; so2=so2+1 } } 348 if ch == 32 { if so2>0 { onesub[so2]=ch as u8; so2=so2+1 } } 349 sp=sp+1 350 } 351 o = ed_subs_close(buf, o) 352 } 353 o = ed_cell_close(buf, o, "Read more" as *u8, hrefbuf) 354 i=i+1 355 } 356 o = ed_raw(buf, o, "</div>" as *u8) 357 o = ed_sec_close(buf, o) 358 359 if nsteps > 0 { 360 secn[0]=48 as u8; secn[1]=(48+sn) as u8; secn[2]=0 as u8; sn=sn+1 361 o = ed_sec_open(buf, o, secn, "What working with us looks like" as *u8) 362 o = ed_raw(buf, o, "<ol class='steps'>" as *u8) 363 i = 0 364 while i < nsteps { 365 o = ed_raw(buf, o, "<li><span>" as *u8) 366 o = ed_esc(buf, o, steps[i] as *u8) 367 o = ed_raw(buf, o, "</span></li>" as *u8) 368 i=i+1 369 } 370 o = ed_raw(buf, o, "</ol>" as *u8) 371 o = ed_sec_close(buf, o) 372 } 373 374 if natt > 0 { 375 secn[0]=48 as u8; secn[1]=(48+sn) as u8; secn[2]=0 as u8; sn=sn+1 376 o = ed_sec_open(buf, o, secn, "Attorneys" as *u8) 377 o = ed_raw(buf, o, "<div class='roster' id='attorneys'>" as *u8) 378 i = 0 379 while i < natt { 380 sb_fld(atts[i] as *u8, 0, afn) 381 sb_fld(atts[i] as *u8, 1, afr) 382 sb_fld(atts[i] as *u8, 2, afb) 383 sb_fld(atts[i] as *u8, 3, afm) 384 sb_fld(atts[i] as *u8, 4, afp) 385 var pend: i64 = 0 386 if (afp[0] as i64) == 49 { pend = 1 } 387 o = ed_person(buf, o, afn, afr, afb, afm, pend) 388 i=i+1 389 } 390 o = ed_raw(buf, o, "</div>" as *u8) 391 o = ed_sec_close(buf, o) 392 } 393 394 secn[0]=48 as u8; secn[1]=(48+sn) as u8; secn[2]=0 as u8 395 o = ed_sec_open(buf, o, secn, "Request a consultation" as *u8) 396 o = ed_consult(buf, o, ephone, email, eaddr, ehours, eintake, "/" as *u8, "Request a consultation" as *u8, "We reply to every enquiry. Sending this does not create an attorney-client relationship." as *u8) 397 o = ed_sec_close(buf, o) 398 o = ed_ctabar(buf, o, hcta, "Free initial consultation" as *u8, "Request a consultation" as *u8, "#consult" as *u8, ephone) 399 o = ed_raw(buf, o, "<footer class='colophon'><p>" as *u8) 400 o = ed_esc(buf, o, footer) 401 o = ed_raw(buf, o, "</p><p>" as *u8) 402 o = ed_esc(buf, o, edisc) 403 o = ed_raw(buf, o, "</p></footer>" as *u8) 404 o = ed_doc_close(buf, o) 405 var eio: i64 = sb_cat(pathbuf, 0, docroot); eio = sb_cat(pathbuf, eio, "/index.html" as *u8); pathbuf[eio]=0 as u8 406 let eifd: i64 = sys_openat_wr(pathbuf, 0x1a4) 407 if eifd >= 0 { sys_write(eifd, buf, o); sys_close(eifd) } 408 409 // ---------------- one page per practice area ---------------- 410 var epc: i64 = 0 411 while epc < ncards { 412 sb_fld(cb[epc] as *u8, 0, cbody) 413 var ept: i64 = sb_cat(titlebuf2, 0, ct[epc] as *u8); ept = sb_cat(titlebuf2, ept, " -- " as *u8); ept = sb_cat(titlebuf2, ept, brand); titlebuf2[ept]=0 as u8 414 o = ed_doc_open(buf, 0, titlebuf2) 415 o = ed_firm_css(buf, o) 416 if paper_on == 1 { o = ed_paper_css(buf, o) } 417 o = sb_ed_nav(buf, o, brand, ncards, ct, cs, hrefbuf, epc) 418 o = ed_firm_mast_open(buf, o, eyeb, ct[epc] as *u8, "" as *u8, cbody, hcta, "#consult" as *u8, "All practice areas" as *u8, "/#expertise" as *u8, ehint) 419 o = ed_artpanel(buf, o, akind, artl) 420 o = ed_firm_mast_close(buf, o) 421 o = ed_firm_search(buf, o, saction, splace, "Search this site" as *u8) 422 o = ed_sec_open(buf, o, "01" as *u8, "Other ways we help" as *u8) 423 o = ed_raw(buf, o, "<div class='grid'>" as *u8) 424 i = 0 425 while i < ncards { 426 if i != epc { 427 sb_fld(cb[i] as *u8, 0, csubs) 428 sb_phref(hrefbuf, cs[i] as *u8) 429 o = ed_cell_open(buf, o, "Practice area" as *u8, ct[i] as *u8, csubs, hrefbuf) 430 o = ed_cell_close(buf, o, "Read more" as *u8, hrefbuf) 431 } 432 i=i+1 433 } 434 o = ed_raw(buf, o, "</div>" as *u8) 435 o = ed_sec_close(buf, o) 436 o = ed_sec_open(buf, o, "02" as *u8, "Request a consultation" as *u8) 437 o = ed_consult(buf, o, ephone, email, eaddr, ehours, eintake, "/" as *u8, "Request a consultation" as *u8, "We reply to every enquiry. Sending this does not create an attorney-client relationship." as *u8) 438 o = ed_sec_close(buf, o) 439 o = ed_ctabar(buf, o, hcta, "Free initial consultation" as *u8, "Request a consultation" as *u8, "#consult" as *u8, ephone) 440 o = ed_raw(buf, o, "<footer class='colophon'><p>" as *u8) 441 o = ed_esc(buf, o, footer) 442 o = ed_raw(buf, o, "</p><p>" as *u8) 443 o = ed_esc(buf, o, edisc) 444 o = ed_raw(buf, o, "</p></footer>" as *u8) 445 o = ed_doc_close(buf, o) 446 var epo: i64 = sb_cat(pathbuf, 0, docroot); epo = sb_cat(pathbuf, epo, "/practice/" as *u8); epo = sb_cat(pathbuf, epo, cs[epc] as *u8); epo = sb_cat(pathbuf, epo, ".html" as *u8); pathbuf[epo]=0 as u8 447 let epfd: i64 = sys_openat_wr(pathbuf, 0x1a4) 448 if epfd >= 0 { sys_write(epfd, buf, o); sys_close(epfd) } 449 epc=epc+1 450 } 451 452 sb_emit_sitemap_robots(buf, pathbuf, docroot, domain, ncards, cs) 453 sb_w("SITE-BUILD OK [editorial]: index + " as *u8); sb_wn(ncards); sb_w(" practice pages + " as *u8); sb_wn(natt); sb_w(" attorneys + sitemap.xml + robots.txt -> " as *u8); sb_w(docroot); sb_w("\n" as *u8) 454 return ncards 455 } 456 457 // ================= INDEX ================= 458 var o: i64 = sk_open_site(buf, 0, titlebase, brand, theme, "/" as *u8) 459 o = emit_nav(buf, o, ncards, ct, cs, hrefbuf) 460 o = sk_header_close(buf, o) 461 o = sk_hero(buf, o, brand, hl, " " as *u8, hsub, hcta, hhref, "Explore services" as *u8, "#services" as *u8, "Free consultation -- no obligation." as *u8) 462 o = sk_search(buf, o, saction, splace) 463 o = sk_section_open(buf, o, "services" as *u8, "What we do" as *u8, "How we help" as *u8, "Choose the area that fits your matter -- each opens a dedicated page." as *u8) 464 i = 0 465 while i < ncards { sb_phref(hrefbuf, cs[i] as *u8); o = sk_card_link(buf, o, SB_ICON, ct[i] as *u8, cb[i] as *u8, hrefbuf); i=i+1 } 466 o = sk_section_close(buf, o) 467 if nsteps > 0 { 468 o = sk_section_open(buf, o, "how" as *u8, "Simple process" as *u8, "Resolve it in 3 steps" as *u8, "From first contact to resolution." as *u8) 469 i = 0 470 while i < nsteps { 471 numbuf[0]=(49+i) as u8; numbuf[1]=0 as u8 472 var so: i64 = sb_cat(stepttl, 0, "Step " as *u8); so = sb_cat(stepttl, so, numbuf); stepttl[so]=0 as u8 473 o = sk_card(buf, o, numbuf, stepttl, steps[i] as *u8) 474 i=i+1 475 } 476 o = sk_section_close(buf, o) 477 } 478 o = emit_trust(buf, o, nsigs, sigs) 479 o = sk_cta(buf, o, "Get started" as *u8, hcta, "Most matters start with one short call." as *u8, hcta, hhref, "Browse services" as *u8, "#services" as *u8) 480 o = sk_close(buf, o, footer, brand) 481 var io: i64 = sb_cat(pathbuf, 0, docroot); io = sb_cat(pathbuf, io, "/index.html" as *u8); pathbuf[io]=0 as u8 482 let ifd: i64 = sys_openat_wr(pathbuf, 0x1a4) 483 if ifd >= 0 { sys_write(ifd, buf, o); sys_close(ifd) } 484 485 // ================= PRACTICE PAGES ================= 486 var pc: i64 = 0 487 while pc < ncards { 488 o = 0 489 var pt: i64 = sb_cat(titlebuf2, 0, ct[pc] as *u8); pt = sb_cat(titlebuf2, pt, " -- " as *u8); pt = sb_cat(titlebuf2, pt, brand); titlebuf2[pt]=0 as u8 490 o = sk_open_site(buf, o, titlebuf2, brand, theme, "/" as *u8) 491 o = emit_nav(buf, o, ncards, ct, cs, hrefbuf) 492 o = sk_header_close(buf, o) 493 o = sk_hero(buf, o, brand, ct[pc] as *u8, " " as *u8, cb[pc] as *u8, hcta, hhref, "All services" as *u8, "/#services" as *u8, "Serving Utah -- free initial consultation." as *u8) 494 o = sk_search(buf, o, saction, splace) 495 o = sk_section_open(buf, o, "related" as *u8, "More help" as *u8, "Related services" as *u8, "Other ways we help Utah families and businesses." as *u8) 496 i = 0 497 while i < ncards { 498 if i != pc { sb_phref(hrefbuf, cs[i] as *u8); o = sk_card_link(buf, o, SB_ICON, ct[i] as *u8, cb[i] as *u8, hrefbuf) } 499 i=i+1 500 } 501 o = sk_section_close(buf, o) 502 o = emit_trust(buf, o, nsigs, sigs) 503 o = sk_cta(buf, o, "Get started" as *u8, hcta, "Tell us what happened -- we will call you back, usually the same day." as *u8, hcta, hhref, "Back to home" as *u8, "/" as *u8) 504 o = sk_close(buf, o, footer, brand) 505 var po: i64 = sb_cat(pathbuf, 0, docroot); po = sb_cat(pathbuf, po, "/practice/" as *u8); po = sb_cat(pathbuf, po, cs[pc] as *u8); po = sb_cat(pathbuf, po, ".html" as *u8); pathbuf[po]=0 as u8 506 let pfd: i64 = sys_openat_wr(pathbuf, 0x1a4) 507 if pfd >= 0 { sys_write(pfd, buf, o); sys_close(pfd) } 508 pc=pc+1 509 } 510 511 // ================= sitemap.xml + robots.txt ================= 512 sb_emit_sitemap_robots(buf, pathbuf, docroot, domain, ncards, cs) 513 514 sb_w("SITE-BUILD OK: index + " as *u8); sb_wn(ncards); sb_w(" practice pages + sitemap.xml (" as *u8); sb_wn(ncards+1); sb_w(" urls) + robots.txt -> " as *u8); sb_w(docroot); sb_w("\n" as *u8) 515 return ncards 516} 517 518func main(argc: i64, argv: *i64) -> i64 { 519 if argc < 4 { 520 sb_w("usage: nx_site_build <config.site> <docroot> <domain> [theme:0|1]\n" as *u8) 521 sys_exit(2); return 2 522 } 523 let config: *u8 = argv[1] as *u8 524 let docroot: *u8 = argv[2] as *u8 525 let domain: *u8 = argv[3] as *u8 526 var theme: i64 = 0 527 if argc > 4 { let a4: *u8 = argv[4] as *u8; if (a4[0] as i64) == 49 { theme = 1 } } 528 let rc: i64 = sb_build_site(config, docroot, domain, theme) 529 if rc < 0 { sys_exit(1); return 1 } 530 sys_exit(0); return 0 531}