nx_hub_emit.nx source
↩ module page · 189 lines · 6976 B
1// nx_hub_emit.nx -- sovereign emitter: content/hub.cards -> dist/hub/index.html
2//
3// Built per operator directive 2026-06-30 ("build the apis that will do
4// what you'd do in shell"). Data-driven: the card set lives in the
5// pipe-delimited manifest content/hub.cards (the SSOT); editing it + re-
6// running this organ regenerates the s-class hub page. Reuses the site's
7// .app-grid / .app-card CSS (so it matches the homepage) + a no-dependency
8// client-side search filter. "soon" cards render disabled (no dead link).
9//
10// File-I/O only -> imports the leaf x86_64 syscall layer ONLY (builds
11// clean; see [[nishi-serving-syscall-dup-gotcha]]). Run from the
12// nishi-pages checkout (so content/ + dist/ resolve); dist/hub must exist.
13//
14// Build: ./nxc2.exe --target x86_64 runtime/nx_hub_emit.nx > /tmp/he.s
15// wsl bash -c "gcc -no-pie /tmp/he.s -o /tmp/nx_hub_emit"
16// Run: cd nishi-pages && mkdir -p dist/hub && /tmp/nx_hub_emit
17//
18// nx_capability_claims:
19// needs: [x86_64_syscalls, pipe_delimited_parse]
20// provides: [hub_page_emit, data_driven_cards, client_side_card_search]
21// safety: [html_escape_text_fields, no_floating_point, fixed_output_path]
22// verdict: [exit_0_ok]
23// license: ORIGINAL
24// kind: nishi_pages_specialist
25// layer: L3 (transform over L1 syscalls)
26// raci: [R=nishi_pages_builder, A=elder_west, C=hub_cards_owner, I=conductor]
27
28import "nx_syscalls_x86_64.nx"
29const K_MAGIC_262144: i64 = 262144
30
31// Append NUL-terminated s to buf at off; return new off.
32func he_puts(buf: *u8, off: i64, s: *u8) -> i64 {
33 var o: i64 = off
34 var i: i64 = 0
35 while s[i] != 0 {
36 buf[o] = s[i]
37 o = o + 1
38 i = i + 1
39 }
40 return o
41}
42
43// Append n raw bytes from src to buf at off; return new off.
44func he_putb(buf: *u8, off: i64, src: *u8, n: i64) -> i64 {
45 var o: i64 = off
46 var i: i64 = 0
47 while i < n {
48 buf[o] = src[i]
49 o = o + 1
50 i = i + 1
51 }
52 return o
53}
54
55// Append src[0..n), HTML-escaping & < > (UTF-8 multibyte passes through).
56func he_put_esc(buf: *u8, off: i64, src: *u8, n: i64) -> i64 {
57 var o: i64 = off
58 var i: i64 = 0
59 while i < n {
60 let c: i64 = src[i] as i64
61 if c == 0x26 {
62 o = he_puts(buf, o, "&" as *u8)
63 } else {
64 if c == 0x3c {
65 o = he_puts(buf, o, "<" as *u8)
66 } else {
67 if c == 0x3e {
68 o = he_puts(buf, o, ">" as *u8)
69 } else {
70 buf[o] = src[i]
71 o = o + 1
72 }
73 }
74 }
75 i = i + 1
76 }
77 return o
78}
79
80// True if buf[off..off+n) equals the NUL-terminated literal lit.
81func he_eq(buf: *u8, off: i64, n: i64, lit: *u8) -> i64 {
82 var i: i64 = 0
83 while lit[i] != 0 {
84 if i >= n { return 0 }
85 if (buf[off + i] as i64) != (lit[i] as i64) { return 0 }
86 i = i + 1
87 }
88 if i != n { return 0 }
89 return 1
90}
91
92// Split buf[ls..le) on '|' into foff[]/flen[] (up to 6 fields). Returns
93// the field count.
94func he_split(buf: *u8, ls: i64, le: i64, foff: *i64, flen: *i64) -> i64 {
95 var fi: i64 = 0
96 var s: i64 = ls
97 var i: i64 = ls
98 while i < le {
99 if (buf[i] as i64) == 0x7c {
100 if fi < 6 {
101 foff[fi] = s
102 flen[fi] = i - s
103 fi = fi + 1
104 }
105 s = i + 1
106 }
107 i = i + 1
108 }
109 if fi < 6 {
110 foff[fi] = s
111 flen[fi] = le - s
112 fi = fi + 1
113 }
114 return fi
115}
116
117func main() -> i64 {
118 let len_p: *i64 = sys_mmap(8) as *i64
119 let data: *u8 = sys_read_file_x86_64("content/hub.cards" as *u8, len_p)
120 if data == (0 as *u8) { return 5 }
121 let dn: i64 = len_p[0]
122
123 let out: *u8 = sys_mmap(K_MAGIC_262144)
124 var o: i64 = 0
125
126 o = he_puts(out, o, "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>Hub · Nishi Family</title><link rel=\"stylesheet\" href=\"/assets/style.css\"><style>.app-soon{opacity:.55}.app-soon h3::after{content:\" \\2022 soon\";font-size:.7em;color:#8a8d98}</style></head><body><header class=\"site-header\"><a class=\"site-title\" href=\"/\">Nishi Family</a><nav><a href=\"/hub\">Hub</a><a href=\"/games\">Games</a><a href=\"/video/\">Video</a><a href=\"/pages/about\">About</a></nav></header><main><section class=\"hero\"><h1>Nishi Family Hub</h1><p class=\"tagline\">Everything the family runs, in one place.</p></section><section class=\"apps\"><div class=\"app-grid\" id=\"hubgrid\">" as *u8)
127
128 let foff: *i64 = sys_mmap(64) as *i64
129 let flen: *i64 = sys_mmap(64) as *i64
130
131 var ls: i64 = 0
132 while ls < dn {
133 // Find end of this line (first \n at/after ls, else dn).
134 var le: i64 = 0 - 1
135 var k: i64 = ls
136 while k < dn {
137 if le < 0 {
138 if (data[k] as i64) == 0x0a { le = k }
139 }
140 k = k + 1
141 }
142 if le < 0 { le = dn }
143
144 // Skip blank lines and comments (#...).
145 var skip: i64 = 0
146 if le <= ls { skip = 1 }
147 if skip == 0 {
148 if (data[ls] as i64) == 0x23 { skip = 1 }
149 }
150
151 if skip == 0 {
152 let nf: i64 = he_split(data, ls, le, foff, flen)
153 if nf >= 5 {
154 let is_soon: i64 = he_eq(data, foff[0], flen[0], "soon" as *u8)
155 // <a class="app-card[ app-soon]" href="..." data-kw="title blurb keywords">
156 o = he_puts(out, o, "<a class=\"app-card" as *u8)
157 if is_soon == 1 { o = he_puts(out, o, " app-soon" as *u8) }
158 o = he_puts(out, o, "\" href=\"" as *u8)
159 if is_soon == 1 {
160 o = he_puts(out, o, "#" as *u8)
161 } else {
162 o = he_putb(out, o, ((data as i64) + foff[3]) as *u8, flen[3])
163 }
164 o = he_puts(out, o, "\"><span class=\"app-emoji\">" as *u8)
165 o = he_putb(out, o, ((data as i64) + foff[1]) as *u8, flen[1])
166 o = he_puts(out, o, "</span><h3>" as *u8)
167 o = he_put_esc(out, o, ((data as i64) + foff[2]) as *u8, flen[2])
168 o = he_puts(out, o, "</h3><p>" as *u8)
169 o = he_put_esc(out, o, ((data as i64) + foff[4]) as *u8, flen[4])
170 o = he_puts(out, o, "</p></a>" as *u8)
171 }
172 }
173
174 ls = le + 1
175 }
176
177 o = he_puts(out, o, "</div></section></main><footer><p>© 2026 Elder West. Served by NishiVM — sovereign substrate.</p></footer></body></html>" as *u8)
178
179 let fd: i64 = sys_openat_wr("dist/hub/index.html" as *u8, 420)
180 if fd < 0 { return 6 }
181 sys_write(fd, out, o)
182 sys_close(fd)
183
184 let banner: *u8 = "nx_hub_emit: wrote dist/hub/index.html\n" as *u8
185 var bn: i64 = 0
186 while banner[bn] != 0 { bn = bn + 1 }
187 sys_write(1, banner, bn)
188 return 0
189}