code wiki / _hdl_build / nx_hub_template.nx

nx_hub_template.nx source

↩ module page · 230 lines · 11550 B

1// nx_hub_template.nx -- the S-CLASS PERSONAL-HUB template emitter (blog / organizer / hub) for the autonomous 2// builder. Operator 2026-06-29: migrate bradrwest.com into a blog/organizer/hub; publish videos from phone; 3// coordinate via shared calendars with coach + friends (jensendwest.com too); video chat. Grounded in 4// knowledge/library/hub_* (blog/portal/CMS/calendaring/iCalendar/video-hosting/web-conferencing). PURE transform 5// ht_emit_hub(cfg,n,out) -> out_n: a data-driven `.hub` blueprint -> a MOBILE-FIRST hub page with sections: 6// header/profile · Blog · Videos (+ "publish from your phone") · Calendar (events with coach/friends) · Friends 7// (link + video-chat). HTML-ESCAPES all text values (defensive at the boundary -- migrated/user content is 8// untrusted). `.hub` line format (pipe-delimited): name|<n> · tagline|<t> · post|<title>|<date>|<body> · 9// video|<title>|<url> · event|<date>|<title>|<with> · friend|<name>|<url> · footer|<text>. license_tier: ORIGINAL 10import "nx_syscalls.nx" 11const K_MAGIC_1048576: i64 = 1048576 12 13func ht_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 14func ht_put(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 } 15 16// HTML-escape cfg[off..off+len) into out at o (&<>" -> entities). returns new o. 17func ht_esc(out: *u8, o: i64, cfg: *u8, off: i64, len: i64) -> i64 { 18 var i: i64 = 0 19 while i < len { 20 let c: i64 = cfg[off + i] as i64 21 if c == 38 { o = ht_put(out, o, "&amp;" as *u8) } 22 if c == 60 { o = ht_put(out, o, "&lt;" as *u8) } 23 if c == 62 { o = ht_put(out, o, "&gt;" as *u8) } 24 if c == 34 { o = ht_put(out, o, "&quot;" as *u8) } 25 if c != 38 { if c != 60 { if c != 62 { if c != 34 { out[o] = c as u8; o = o + 1 } } } } 26 i = i + 1 27 } 28 return o 29} 30 31func ht_eol(cfg: *u8, n: i64, start: i64) -> i64 { 32 var i: i64 = start 33 var f: i64 = 0 34 while f == 0 { if i >= n { f = 1 } else { if (cfg[i] as i64) == 10 { f = 1 } else { i = i + 1 } } } 35 return i 36} 37 38// idx-th '|'-delimited field of line cfg[ls..le): set off/len. 1 if present, 0 if not. 39func ht_line_field(cfg: *u8, ls: i64, le: i64, idx: i64, offb: *i64, lenb: *i64) -> i64 { 40 var fi: i64 = 0 41 var s: i64 = ls 42 var i: i64 = ls 43 while i <= le { 44 var sep: i64 = 0 45 if i == le { sep = 1 } 46 if i < le { if (cfg[i] as i64) == 124 { sep = 1 } } 47 if sep == 1 { 48 if fi == idx { offb[0] = s; lenb[0] = i - s; return 1 } 49 fi = fi + 1 50 s = i + 1 51 } 52 i = i + 1 53 } 54 return 0 55} 56 57// does the slice cfg[off..off+len) equal the null-terminated tag? 58func ht_tageq(cfg: *u8, off: i64, len: i64, tag: *u8) -> i64 { 59 let tl: i64 = ht_slen(tag) 60 if tl != len { return 0 } 61 var i: i64 = 0 62 while i < len { if (cfg[off + i] as i64) != (tag[i] as i64) { return 0 } i = i + 1 } 63 return 1 64} 65 66// emit the idx-th field of the FIRST line whose field0 == tag, HTML-escaped. returns new o (unchanged if absent). 67func ht_emit_first(cfg: *u8, n: i64, tag: *u8, idx: i64, out: *u8, o0: i64) -> i64 { 68 var o: i64 = o0 69 let ob: *i64 = sys_mmap(8) as *i64 70 let lb: *i64 = sys_mmap(8) as *i64 71 var cur: i64 = 0 72 var done: i64 = 0 73 while done == 0 { 74 if cur >= n { done = 1 } else { 75 let le: i64 = ht_eol(cfg, n, cur) 76 if ht_line_field(cfg, cur, le, 0, ob, lb) == 1 { 77 if ht_tageq(cfg, ob[0], lb[0], tag) == 1 { 78 if ht_line_field(cfg, cur, le, idx, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 79 done = 1 80 } 81 } 82 cur = le + 1 83 } 84 } 85 return o 86} 87 88func ht_render_posts(cfg: *u8, n: i64, out: *u8, o0: i64) -> i64 { 89 var o: i64 = o0 90 let ob: *i64 = sys_mmap(8) as *i64 91 let lb: *i64 = sys_mmap(8) as *i64 92 var cur: i64 = 0 93 while cur < n { 94 let le: i64 = ht_eol(cfg, n, cur) 95 if ht_line_field(cfg, cur, le, 0, ob, lb) == 1 { 96 if ht_tageq(cfg, ob[0], lb[0], "post" as *u8) == 1 { 97 o = ht_put(out, o, "<article><h3>" as *u8) 98 if ht_line_field(cfg, cur, le, 1, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 99 o = ht_put(out, o, "</h3><time>" as *u8) 100 if ht_line_field(cfg, cur, le, 2, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 101 o = ht_put(out, o, "</time><p>" as *u8) 102 if ht_line_field(cfg, cur, le, 3, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 103 o = ht_put(out, o, "</p></article>" as *u8) 104 } 105 } 106 cur = le + 1 107 } 108 return o 109} 110 111func ht_render_videos(cfg: *u8, n: i64, out: *u8, o0: i64) -> i64 { 112 var o: i64 = o0 113 let ob: *i64 = sys_mmap(8) as *i64 114 let lb: *i64 = sys_mmap(8) as *i64 115 var cur: i64 = 0 116 while cur < n { 117 let le: i64 = ht_eol(cfg, n, cur) 118 if ht_line_field(cfg, cur, le, 0, ob, lb) == 1 { 119 if ht_tageq(cfg, ob[0], lb[0], "video" as *u8) == 1 { 120 o = ht_put(out, o, "<a class=tile href=\"" as *u8) 121 if ht_line_field(cfg, cur, le, 2, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 122 o = ht_put(out, o, "\">\xE2\x96\xB6 " as *u8) 123 if ht_line_field(cfg, cur, le, 1, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 124 o = ht_put(out, o, "</a>" as *u8) 125 } 126 } 127 cur = le + 1 128 } 129 return o 130} 131 132func ht_render_events(cfg: *u8, n: i64, out: *u8, o0: i64) -> i64 { 133 var o: i64 = o0 134 let ob: *i64 = sys_mmap(8) as *i64 135 let lb: *i64 = sys_mmap(8) as *i64 136 var cur: i64 = 0 137 while cur < n { 138 let le: i64 = ht_eol(cfg, n, cur) 139 if ht_line_field(cfg, cur, le, 0, ob, lb) == 1 { 140 if ht_tageq(cfg, ob[0], lb[0], "event" as *u8) == 1 { 141 o = ht_put(out, o, "<div class=evt><time>" as *u8) 142 if ht_line_field(cfg, cur, le, 1, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 143 o = ht_put(out, o, "</time><b>" as *u8) 144 if ht_line_field(cfg, cur, le, 2, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 145 o = ht_put(out, o, "</b><span> with " as *u8) 146 if ht_line_field(cfg, cur, le, 3, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 147 o = ht_put(out, o, "</span></div>" as *u8) 148 } 149 } 150 cur = le + 1 151 } 152 return o 153} 154 155func ht_render_friends(cfg: *u8, n: i64, out: *u8, o0: i64) -> i64 { 156 var o: i64 = o0 157 let ob: *i64 = sys_mmap(8) as *i64 158 let lb: *i64 = sys_mmap(8) as *i64 159 var cur: i64 = 0 160 while cur < n { 161 let le: i64 = ht_eol(cfg, n, cur) 162 if ht_line_field(cfg, cur, le, 0, ob, lb) == 1 { 163 if ht_tageq(cfg, ob[0], lb[0], "friend" as *u8) == 1 { 164 o = ht_put(out, o, "<div class=fr><a href=\"" as *u8) 165 if ht_line_field(cfg, cur, le, 2, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 166 o = ht_put(out, o, "\">" as *u8) 167 if ht_line_field(cfg, cur, le, 1, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 168 o = ht_put(out, o, "</a><a class=chat href=\"/meet?with=" as *u8) 169 if ht_line_field(cfg, cur, le, 1, ob, lb) == 1 { o = ht_esc(out, o, cfg, ob[0], lb[0]) } 170 o = ht_put(out, o, "\">\xF0\x9F\x93\xB9 Video chat</a></div>" as *u8) 171 } 172 } 173 cur = le + 1 174 } 175 return o 176} 177 178func ht_emit_hub(cfg: *u8, n: i64, out: *u8) -> i64 { 179 var o: i64 = 0 180 o = ht_put(out, o, "<!DOCTYPE html><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>" as *u8) 181 o = ht_emit_first(cfg, n, "name" as *u8, 1, out, o) 182 o = ht_put(out, o, " \xE2\x80\x94 hub</title>" as *u8) 183 o = ht_put(out, o, "<style>body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;margin:0;color:#1c1c1e;background:#f6f7f9;line-height:1.5}.hero{background:linear-gradient(135deg,#0a6,#075);color:#fff;padding:2.4rem 1.2rem;text-align:center}.hero h1{margin:.2rem 0;font-size:1.9rem}.hero p{margin:.2rem 0;opacity:.92}nav{display:flex;gap:.3rem;justify-content:center;flex-wrap:wrap;padding:.6rem;background:#fff;position:sticky;top:0;box-shadow:0 1px 6px rgba(0,0,0,.06)}nav a{text-decoration:none;color:#075;font-weight:600;padding:.3rem .7rem;border-radius:8px}section{max-width:760px;margin:1.1rem auto;padding:0 1rem}h2{font-size:1.3rem}article,.tile,.evt,.fr{background:#fff;border-radius:12px;padding:.9rem 1rem;margin:.6rem 0;box-shadow:0 1px 5px rgba(0,0,0,.05)}article h3{margin:.1rem 0}time{color:#888;font-size:.85rem;display:block}.tile{display:block;text-decoration:none;color:#075;font-weight:600}.cta{display:block;text-align:center;background:#0a6;color:#fff;text-decoration:none;padding:.85rem;border-radius:10px;font-weight:700;margin:.7rem 0}.evt b{display:block}.fr{display:flex;justify-content:space-between;align-items:center}.fr a{text-decoration:none;color:#075;font-weight:600}.fr a.chat{background:#0a6;color:#fff;padding:.4rem .8rem;border-radius:8px;font-size:.9rem}footer{text-align:center;color:#888;padding:1.5rem;font-size:.85rem}</style>" as *u8) 184 o = ht_put(out, o, "<header class=hero><h1>" as *u8) 185 o = ht_emit_first(cfg, n, "name" as *u8, 1, out, o) 186 o = ht_put(out, o, "</h1><p>" as *u8) 187 o = ht_emit_first(cfg, n, "tagline" as *u8, 1, out, o) 188 o = ht_put(out, o, "</p></header><nav><a href=\"#blog\">Blog</a><a href=\"#videos\">Videos</a><a href=\"#cal\">Calendar</a><a href=\"#friends\">Friends</a></nav>" as *u8) 189 190 o = ht_put(out, o, "<section id=blog><h2>\xF0\x9F\x93\x93 Blog</h2>" as *u8) 191 o = ht_render_posts(cfg, n, out, o) 192 o = ht_put(out, o, "</section>" as *u8) 193 194 o = ht_put(out, o, "<section id=videos><h2>\xF0\x9F\x8E\xAC Videos</h2>" as *u8) 195 o = ht_render_videos(cfg, n, out, o) 196 o = ht_put(out, o, "<a class=cta href=\"/upload\">\xF0\x9F\x93\xB1 Publish a video from your phone</a></section>" as *u8) 197 198 o = ht_put(out, o, "<section id=cal><h2>\xF0\x9F\x93\x85 Calendar</h2>" as *u8) 199 o = ht_render_events(cfg, n, out, o) 200 o = ht_put(out, o, "<a class=cta href=\"/schedule\">\xE2\x9E\x95 Add to a shared calendar</a></section>" as *u8) 201 202 o = ht_put(out, o, "<section id=friends><h2>\xF0\x9F\xA4\x9D Friends</h2>" as *u8) 203 o = ht_render_friends(cfg, n, out, o) 204 o = ht_put(out, o, "</section>" as *u8) 205 206 o = ht_put(out, o, "<footer>" as *u8) 207 o = ht_emit_first(cfg, n, "footer" as *u8, 1, out, o) 208 o = ht_put(out, o, "</footer>" as *u8) 209 return o 210} 211 212func main(argc: i64, argv: *i64) -> i64 { 213 if argc < 3 { sys_write(2, "usage: nx_hub_template <config.hub> <out.html>\n" as *u8, 47); return 1 } 214 let cfgpath: *u8 = argv[1] as *u8 215 let outpath: *u8 = argv[2] as *u8 216 let szb: *i64 = sys_mmap(16) as *i64 217 szb[0] = 0 218 let cfg: *u8 = sys_read_file(cfgpath, szb) 219 if (cfg as i64) == 0 { sys_write(2, "hub: cannot read config\n" as *u8, 24); return 2 } 220 let out: *u8 = sys_mmap(K_MAGIC_1048576) 221 let on: i64 = ht_emit_hub(cfg, szb[0], out) 222 let fd: i64 = sys_openat_wr(outpath, 0x1a4) 223 if fd < 0 { sys_write(2, "hub: cannot write html\n" as *u8, 23); return 3 } 224 sys_write(fd, out, on) 225 sys_close(fd) 226 sys_write(1, "HUB -> " as *u8, 7) 227 sys_write(1, outpath, ht_slen(outpath)) 228 sys_write(1, "\n" as *u8, 1) 229 return 0 230}