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, "&" as *u8) }
22 if c == 60 { o = ht_put(out, o, "<" as *u8) }
23 if c == 62 { o = ht_put(out, o, ">" as *u8) }
24 if c == 34 { o = ht_put(out, o, """ 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}