code wiki / _hdl_build / nx_wiki_shell.nx
nx_wiki_shell.nx source
↩ module page · 545 lines · 29420 B
1// nx_wiki_shell.nx -- THE AUTOMATIC PAGE BUILDER: the standard wiki-page furniture as reusable
2// emitters, so EVERY Nishi wiki page clears the MediaWiki/Wikipedia feature bar measured by
3// nx_wiki_exceed. Each function appends to a caller buffer and returns the new offset.
4//
5// Features emitted (the census signals): responsive-viewport, print-css (media="print"),
6// lang-links (hreflang=), permalink (rel="bookmark") [sh_head]; navbar (<nav), search (type="search"),
7// wikilinks (href="/wiki/) [sh_nav]; toc (class="toc") [sh_toc_*]; section-anchors (<h2 id=)
8// [sh_section]; infobox (class="infobox") [sh_infobox_*]; references (class="references") [sh_refs_*];
9// categories (class="catlinks"), seealso (id="seealso"), edit-affordance (class="wiki-edit") [sh_footer].
10// The page supplies its own <h1> + freshness chip + body. Sovereign: nx_syscalls. license_tier: ORIGINAL
11
12import "nx_syscalls.nx"
13import "nx_wiki_backlinks.nx"
14import "nx_native_config.nx"
15
16func sh_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != 0 as u8 { n = n + 1 } return n }
17func sh_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != 0 as u8 { dst[off + i] = s[i]; i = i + 1 } return off + i }
18func sh_catn(dst: *u8, off: i64, v: i64) -> i64 {
19 var m: i64 = v; var o: i64 = off
20 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m }
21 let t: *u8 = sys_mmap(28); var k: i64 = 0
22 if m == 0 { t[0] = 48 as u8; k = 1 }
23 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
24 var i: i64 = 0
25 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 }
26 return o + k
27}
28
29// HEAD: charset + responsive viewport + title + permalink + interlanguage link + main CSS + PRINT CSS.
30func sh_head(buf: *u8, off: i64, title: *u8, slug: *u8) -> i64 {
31 var w: i64 = off
32 w = sh_cat(buf, w, "<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n" as *u8)
33 w = sh_cat(buf, w, "<title>" as *u8); w = sh_cat(buf, w, title); w = sh_cat(buf, w, " — Nishi Wiki</title>\n" as *u8)
34 w = sh_cat(buf, w, "<link rel=\"bookmark\" href=\"/wiki/" as *u8); w = sh_cat(buf, w, slug); w = sh_cat(buf, w, ".html\">\n" as *u8)
35 w = sh_cat(buf, w, "<link rel=\"alternate\" hreflang=\"en\" href=\"/wiki/" as *u8); w = sh_cat(buf, w, slug); w = sh_cat(buf, w, ".html\">\n" as *u8)
36 w = sh_cat(buf, w, "<style>" as *u8)
37 w = sh_cat(buf, w, "body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;max-width:920px;margin:0 auto;padding:0 24px 4vh;color:#14141e;line-height:1.6}" as *u8)
38 w = sh_cat(buf, w, "h1{font-size:2.2rem;margin:.6rem 0 .1rem}h2{color:#2a4d8f;margin-top:2rem;font-size:1.25rem;border-bottom:2px solid #eef2fb;padding-bottom:.2rem}h2 a{color:inherit;text-decoration:none}h3{font-size:1rem;margin:1rem 0 .3rem}" as *u8)
39 w = sh_cat(buf, w, ".fresh{display:inline-block;background:#eef7ee;border:1px solid #bcdcbc;color:#225522;border-radius:6px;padding:6px 12px;font-size:.85rem;margin:.4rem 0}" as *u8)
40 w = sh_cat(buf, w, ".lede{background:#1b1b26;color:#e8eef8;border-radius:8px;padding:16px 20px;font-size:1rem}.lede strong{color:#9ecbff}" as *u8)
41 w = sh_cat(buf, w, ".why{background:#eef2fb;border-left:4px solid #2a4d8f;border-radius:4px;padding:12px 16px;font-size:.92rem;margin:1rem 0}" as *u8)
42 w = sh_cat(buf, w, "table{border-collapse:collapse;width:100%;margin:.8rem 0;font-size:.9rem}th,td{border:1px solid #dcdce4;padding:5px 8px;text-align:left}th{background:#f4f4f8}" as *u8)
43 w = sh_cat(buf, w, "code{background:#f0f0f4;padding:1px 5px;border-radius:3px;font-size:.9em}pre{background:#1b1b26;color:#d8e8d8;padding:10px 13px;border-radius:6px;overflow-x:auto;font-size:.85rem}" as *u8)
44 w = sh_cat(buf, w, ".ev{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:.85rem;background:#1b1b26;color:#d8e8d8;padding:7px 10px;border-radius:5px;white-space:pre-wrap}" as *u8)
45 w = sh_cat(buf, w, "ul{padding-left:1.2rem}li{margin:.25rem 0}.foot{margin-top:3rem;font-size:.8rem;color:#888;border-top:1px solid #eee;padding-top:1rem}a{color:#2a4d8f}" as *u8)
46 w = sh_cat(buf, w, "nav.wiki-nav{position:sticky;top:0;background:#fff;border-bottom:1px solid #e4e4ee;display:flex;gap:14px;align-items:center;padding:10px 0;margin-bottom:1rem;flex-wrap:wrap;z-index:9}.wiki-nav a{font-weight:600;text-decoration:none}.wiki-nav form{margin-left:auto}.wiki-nav input{border:1px solid #ccd;border-radius:6px;padding:5px 9px;font-size:.9rem}" as *u8)
47 w = sh_cat(buf, w, "nav.toc{background:#fafaff;border:1px solid #e4e4ee;border-radius:8px;padding:10px 16px;margin:1rem 0;font-size:.9rem}nav.toc b{display:block;margin-bottom:.3rem;color:#555}nav.toc ol{margin:0;padding-left:1.3rem}" as *u8)
48 w = sh_cat(buf, w, "aside.infobox{float:right;width:280px;margin:0 0 1rem 1.2rem;border:1px solid #dcdce4;border-radius:8px;background:#fbfbfe;font-size:.85rem}aside.infobox .ib-h{background:#2a4d8f;color:#fff;padding:7px 11px;border-radius:8px 8px 0 0;font-weight:700}aside.infobox table{margin:0}aside.infobox td{border:none;border-top:1px solid #eee}" as *u8)
49 w = sh_cat(buf, w, "section.references{font-size:.9rem}section.references ol{padding-left:1.3rem}#seealso ul{columns:2}.catlinks{margin-top:1.4rem;padding:8px 12px;background:#f4f4f8;border-radius:6px;font-size:.85rem}.catlinks a{margin-right:10px}.wiki-edit{float:right;font-size:.8rem;border:1px solid #ccd;border-radius:6px;padding:2px 9px;text-decoration:none}" as *u8)
50 w = sh_cat(buf, w, "@media (max-width:680px){aside.infobox{float:none;width:auto;margin:0 0 1rem}#seealso ul{columns:1}}" as *u8)
51 w = sh_cat(buf, w, "</style>\n" as *u8)
52 w = sh_cat(buf, w, "<style media=\"print\">nav.wiki-nav,.wiki-edit,.wiki-nav form{display:none}body{max-width:none}a{color:#000;text-decoration:none}aside.infobox{float:none}</style>\n" as *u8)
53 w = sh_cat(buf, w, "</head><body>\n" as *u8)
54 return w
55}
56
57// NAV bar: internal wikilinks + an on-page search box + an edit affordance.
58func sh_nav(buf: *u8, off: i64, slug: *u8) -> i64 {
59 var w: i64 = off
60 w = sh_cat(buf, w, "<nav class=\"wiki-nav\"><a href=\"/wiki/start.html\">☖ AI Start</a> <a href=\"/wiki/charter.html\">Charter</a> <a href=\"/wiki/start.html#tasks\">Task streams</a> <a href=\"/wiki/start.html#docs\">Docs</a>" as *u8)
61 w = sh_cat(buf, w, "<a class=\"wiki-edit\" href=\"/wiki/edit?p=" as *u8); w = sh_cat(buf, w, slug); w = sh_cat(buf, w, "\">✎ edit</a>" as *u8)
62 w = sh_cat(buf, w, "<form action=\"/wiki/search.html\" method=\"get\"><input type=\"search\" name=\"q\" placeholder=\"Search the wiki\" aria-label=\"Search\"></form></nav>\n" as *u8)
63 return w
64}
65
66// TABLE OF CONTENTS (page lists its own sections).
67func sh_toc_open(buf: *u8, off: i64) -> i64 { return sh_cat(buf, off, "<nav class=\"toc\" aria-label=\"Contents\"><b>Contents</b><ol>\n" as *u8) }
68func sh_toc_item(buf: *u8, off: i64, id: *u8, title: *u8) -> i64 {
69 var w: i64 = off
70 w = sh_cat(buf, w, "<li><a href=\"#" as *u8); w = sh_cat(buf, w, id); w = sh_cat(buf, w, "\">" as *u8); w = sh_cat(buf, w, title); w = sh_cat(buf, w, "</a></li>\n" as *u8)
71 return w
72}
73func sh_toc_close(buf: *u8, off: i64) -> i64 { return sh_cat(buf, off, "</ol></nav>\n" as *u8) }
74
75// ANCHORED section heading (deep-linkable).
76func sh_section(buf: *u8, off: i64, id: *u8, title: *u8) -> i64 {
77 var w: i64 = off
78 w = sh_cat(buf, w, "<h2 id=\"" as *u8); w = sh_cat(buf, w, id); w = sh_cat(buf, w, "\"><a href=\"#" as *u8); w = sh_cat(buf, w, id); w = sh_cat(buf, w, "\">" as *u8); w = sh_cat(buf, w, title); w = sh_cat(buf, w, "</a></h2>\n" as *u8)
79 return w
80}
81
82// INFOBOX (structured summary).
83func sh_infobox_open(buf: *u8, off: i64, title: *u8) -> i64 {
84 var w: i64 = off
85 w = sh_cat(buf, w, "<aside class=\"infobox\"><div class=\"ib-h\">" as *u8); w = sh_cat(buf, w, title); w = sh_cat(buf, w, "</div><table>\n" as *u8)
86 return w
87}
88func sh_infobox_row(buf: *u8, off: i64, k: *u8, v: *u8) -> i64 {
89 var w: i64 = off
90 w = sh_cat(buf, w, "<tr><td><strong>" as *u8); w = sh_cat(buf, w, k); w = sh_cat(buf, w, "</strong></td><td>" as *u8); w = sh_cat(buf, w, v); w = sh_cat(buf, w, "</td></tr>\n" as *u8)
91 return w
92}
93func sh_infobox_close(buf: *u8, off: i64) -> i64 { return sh_cat(buf, off, "</table></aside>\n" as *u8) }
94
95// REFERENCES section (the page appends <li> items between open/close).
96func sh_refs_open(buf: *u8, off: i64) -> i64 { return sh_cat(buf, off, "<section class=\"references\"><h2 id=\"refs\"><a href=\"#refs\">References & evidence</a></h2><ol>\n" as *u8) }
97func sh_refs_close(buf: *u8, off: i64) -> i64 { return sh_cat(buf, off, "</ol></section>\n" as *u8) }
98
99// =============================================================================
100// WALKABILITY FURNITURE (wiki nav-tree arc): dense bidirectional link-tree +
101// learning paths, the S-class fix for the flat/dead-end F-class nav. The curated
102// tree/path/see-also/tags come from knowledge/registry/wiki_links.tsv (loaded by
103// sh_links_load below); forward+back links stay COMPUTED from page bodies.
104// =============================================================================
105
106// ---- wiki_links.tsv reader (curated tree/path/related/tags) -----------------
107// Loaded ONCE per page into an NxWikiLinks; every furniture emitter reads it.
108// Columns: slug<TAB>parent<TAB>prev<TAB>next<TAB>related<TAB>tags<TAB>diataxis
109const SH_WL_MAXROWS: i64 = 64
110const SH_WL_MAXBYTES: i64 = 65536
111const SH_WL_NCOLS: i64 = 7
112
113struct NxWikiLinks {
114 raw: *u8 // mutable copy of the file (TABs/NLs overwritten with NUL)
115 nrows: i64
116 // per-row, per-col: pointer (as i64) to a NUL-terminated field inside raw ('-'/empty => 0)
117 cells: *i64 // SH_WL_MAXROWS * SH_WL_NCOLS i64s
118 valid: i64
119}
120
121func sh_wl_field(wl: *NxWikiLinks, row: i64, col: i64) -> *u8 {
122 if wl.valid != 1 { return 0 as *u8 }
123 if row < 0 { return 0 as *u8 }
124 if row >= wl.nrows { return 0 as *u8 }
125 if col < 0 { return 0 as *u8 }
126 if col >= SH_WL_NCOLS { return 0 as *u8 }
127 return wl.cells[row * SH_WL_NCOLS + col] as *u8
128}
129
130// streq for NUL-terminated strings (one may be 0 => not equal unless both 0-len).
131func sh_streq(a: *u8, b: *u8) -> i64 {
132 if (a as i64) == 0 { return 0 }
133 if (b as i64) == 0 { return 0 }
134 var i: i64 = 0
135 while a[i] != 0 as u8 {
136 if a[i] != b[i] { return 0 }
137 i = i + 1
138 }
139 if b[i] != 0 as u8 { return 0 }
140 return 1
141}
142
143// find the row index for a slug; -1 if absent.
144func sh_wl_row_of(wl: *NxWikiLinks, slug: *u8) -> i64 {
145 var r: i64 = 0
146 while r < wl.nrows {
147 let s: *u8 = sh_wl_field(wl, r, 0)
148 if sh_streq(s, slug) == 1 { return r }
149 r = r + 1
150 }
151 return 0 - 1
152}
153
154// the native nav-tree store prefix (no TSV; authored by nx_wiki_links_seed).
155func sh_wikilinks_prefix() -> *u8 { return "knowledge/store/wiki-links-\x00" as *u8 }
156// map a named field of a decoded row to a cell pointer; "-"/empty/absent -> 0 (the old '-'=>empty rule).
157func sh_wl_cell(keys: *i64, vals: *i64, nf: i64, name: *u8) -> i64 {
158 let v: *u8 = ncfg_field(keys, vals, nf, name)
159 if (v as i64) == 0 { return 0 }
160 if v[0] == 0 as u8 { return 0 }
161 if v[0] == 45 as u8 { if v[1] == 0 as u8 { return 0 } }
162 return v as i64
163}
164// Load the curated wiki nav tree into wl from the NATIVE seg_store (no TSV). `path` is vestigial
165// (kept for source-compat with the existing callers); the tree lives in knowledge/store/wiki-links-
166// via nx_native_config (tag "wl"). Columns are looked up BY NAME and stored in wl.cells in the same
167// fixed order (slug/parent/prev/next/related/tags/diataxis), so every furniture emitter
168// (sh_wl_field / sh_wl_row_of / sh_breadcrumb / sh_tree / sh_related / sh_footer_wl) is UNCHANGED.
169// A "-"/empty field maps to 0 (root / endpoint / none). Returns nrows (>=0), or -1 if the native
170// store is absent (fail-closed: emit no nav rather than read a stale TSV).
171func sh_links_load(wl: *NxWikiLinks, path: *u8) -> i64 {
172 wl.valid = 0
173 wl.nrows = 0
174 wl.cells = sys_mmap(SH_WL_MAXROWS * SH_WL_NCOLS * 8) as *i64
175 wl.raw = 0 as *u8
176 let hh: *i64 = ncfg_open(sh_wikilinks_prefix())
177 if (hh as i64) == 0 { return 0 - 1 }
178 let cnt: i64 = ncfg_count(hh, "wl\x00" as *u8)
179 let rk: *i64 = sys_mmap(8 * 8) as *i64
180 let rv: *i64 = sys_mmap(8 * 8) as *i64
181 var rows: i64 = 0
182 var i: i64 = 0
183 while i < cnt {
184 if rows < SH_WL_MAXROWS {
185 let rf: i64 = ncfg_row(hh, "wl\x00" as *u8, i, rk, rv, 8)
186 if rf > 0 {
187 wl.cells[rows * SH_WL_NCOLS + 0] = sh_wl_cell(rk, rv, rf, "slug\x00" as *u8)
188 wl.cells[rows * SH_WL_NCOLS + 1] = sh_wl_cell(rk, rv, rf, "parent\x00" as *u8)
189 wl.cells[rows * SH_WL_NCOLS + 2] = sh_wl_cell(rk, rv, rf, "prev\x00" as *u8)
190 wl.cells[rows * SH_WL_NCOLS + 3] = sh_wl_cell(rk, rv, rf, "next\x00" as *u8)
191 wl.cells[rows * SH_WL_NCOLS + 4] = sh_wl_cell(rk, rv, rf, "related\x00" as *u8)
192 wl.cells[rows * SH_WL_NCOLS + 5] = sh_wl_cell(rk, rv, rf, "tags\x00" as *u8)
193 wl.cells[rows * SH_WL_NCOLS + 6] = sh_wl_cell(rk, rv, rf, "diataxis\x00" as *u8)
194 rows = rows + 1
195 }
196 }
197 i = i + 1
198 }
199 wl.nrows = rows
200 wl.valid = 1
201 return rows
202}
203
204// pretty title from a slug: capitalize first letter, '_' -> ' '. Falls back to
205// the raw slug if absent. Writes into dst at off; returns new off.
206func sh_title_of(dst: *u8, off: i64, slug: *u8) -> i64 {
207 var o: i64 = off
208 if (slug as i64) == 0 { return o }
209 var i: i64 = 0
210 while slug[i] != 0 as u8 {
211 var ch: u8 = slug[i]
212 if ch == 95 as u8 { ch = 32 as u8 } // '_' -> ' '
213 if i == 0 { if ch >= 97 as u8 { if ch <= 122 as u8 { ch = (ch - 32) as u8 } } }
214 dst[o] = ch; o = o + 1
215 i = i + 1
216 }
217 return o
218}
219
220// ---- A. BREADCRUMB: Home > parent-chain > current ---------------------------
221// Walks parent edges from slug up to the root, emitting a reversed trail. Cycle-
222// guarded by SH_WL_MAXROWS hops. Marker class="nx-breadcrumb".
223func sh_breadcrumb(buf: *u8, off: i64, wl: *NxWikiLinks, slug: *u8) -> i64 {
224 var w: i64 = off
225 w = sh_cat(buf, w, "<nav class=\"nx-breadcrumb\" aria-label=\"Breadcrumb\"><a href=\"/wiki/start.html\">🏠 Home</a>" as *u8)
226 // collect the parent chain (slug -> parent -> ...root), bounded
227 let chain: *i64 = sys_mmap(SH_WL_MAXROWS * 8) as *i64
228 var depth: i64 = 0
229 var cur: *u8 = slug
230 var hops: i64 = 0
231 while hops < SH_WL_MAXROWS {
232 let r: i64 = sh_wl_row_of(wl, cur)
233 if r < 0 { hops = SH_WL_MAXROWS } else {
234 chain[depth] = r
235 depth = depth + 1
236 let par: *u8 = sh_wl_field(wl, r, 1)
237 if (par as i64) == 0 { hops = SH_WL_MAXROWS } else { cur = par }
238 }
239 hops = hops + 1
240 }
241 // emit from root (chain[depth-1]) down to current (chain[0]); current is plain text
242 var k: i64 = depth - 1
243 while k >= 0 {
244 let rr: i64 = chain[k]
245 let sg: *u8 = sh_wl_field(wl, rr, 0)
246 w = sh_cat(buf, w, " › " as *u8)
247 if k == 0 {
248 w = sh_cat(buf, w, "<span aria-current=\"page\">" as *u8)
249 w = sh_title_of(buf, w, sg)
250 w = sh_cat(buf, w, "</span>" as *u8)
251 } else {
252 w = sh_cat(buf, w, "<a href=\"/wiki/" as *u8); w = sh_cat(buf, w, sg); w = sh_cat(buf, w, ".html\">" as *u8)
253 w = sh_title_of(buf, w, sg)
254 w = sh_cat(buf, w, "</a>" as *u8)
255 }
256 k = k - 1
257 }
258 w = sh_cat(buf, w, "</nav>\n" as *u8)
259 return w
260}
261
262// ---- B. PREV / NEXT learning-path pager -------------------------------------
263// Reads the prev/next columns for slug. Marker class="nx-prevnext".
264func sh_prevnext(buf: *u8, off: i64, wl: *NxWikiLinks, slug: *u8) -> i64 {
265 let r: i64 = sh_wl_row_of(wl, slug)
266 if r < 0 { return off }
267 let prev: *u8 = sh_wl_field(wl, r, 2)
268 let next: *u8 = sh_wl_field(wl, r, 3)
269 var w: i64 = off
270 w = sh_cat(buf, w, "<nav class=\"nx-prevnext\" aria-label=\"Learning path\">" as *u8)
271 if (prev as i64) != 0 {
272 w = sh_cat(buf, w, "<a class=\"nx-pn-prev\" rel=\"prev\" href=\"/wiki/" as *u8); w = sh_cat(buf, w, prev)
273 w = sh_cat(buf, w, ".html\">← " as *u8); w = sh_title_of(buf, w, prev); w = sh_cat(buf, w, "</a>" as *u8)
274 } else {
275 w = sh_cat(buf, w, "<span class=\"nx-pn-prev nx-pn-none\"></span>" as *u8)
276 }
277 if (next as i64) != 0 {
278 w = sh_cat(buf, w, "<a class=\"nx-pn-next\" rel=\"next\" href=\"/wiki/" as *u8); w = sh_cat(buf, w, next)
279 w = sh_cat(buf, w, ".html\">" as *u8); w = sh_title_of(buf, w, next); w = sh_cat(buf, w, " →</a>" as *u8)
280 } else {
281 w = sh_cat(buf, w, "<span class=\"nx-pn-next nx-pn-none\"></span>" as *u8)
282 }
283 w = sh_cat(buf, w, "</nav>\n" as *u8)
284 return w
285}
286
287// ---- C. RELATED (curated see-also) ------------------------------------------
288// Reads the comma-separated related column for slug. Marker class="nx-related".
289func sh_related(buf: *u8, off: i64, wl: *NxWikiLinks, slug: *u8) -> i64 {
290 let r: i64 = sh_wl_row_of(wl, slug)
291 if r < 0 { return off }
292 let rel: *u8 = sh_wl_field(wl, r, 4)
293 if (rel as i64) == 0 { return off }
294 var w: i64 = off
295 w = sh_cat(buf, w, "<aside class=\"nx-related\"><h2>Related pages</h2><ul>\n" as *u8)
296 // split rel on ','
297 var i: i64 = 0
298 var seg_start: i64 = 0
299 var done: i64 = 0
300 while done == 0 {
301 let ch: u8 = rel[i]
302 var boundary: i64 = 0
303 if ch == 0 as u8 { boundary = 1; done = 1 }
304 if ch == 44 as u8 { boundary = 1 } // ','
305 if boundary == 1 {
306 let seglen: i64 = i - seg_start
307 if seglen > 0 {
308 // emit a temp NUL-terminated slug
309 let t: *u8 = sys_mmap(seglen + 1)
310 var j: i64 = 0
311 while j < seglen { t[j] = rel[seg_start + j]; j = j + 1 }
312 t[seglen] = 0 as u8
313 w = sh_cat(buf, w, "<li><a href=\"/wiki/" as *u8); w = sh_cat(buf, w, t)
314 w = sh_cat(buf, w, ".html\">" as *u8); w = sh_title_of(buf, w, t); w = sh_cat(buf, w, "</a></li>\n" as *u8)
315 }
316 seg_start = i + 1
317 }
318 i = i + 1
319 if i > SH_WL_MAXBYTES { done = 1 }
320 }
321 w = sh_cat(buf, w, "</ul></aside>\n" as *u8)
322 return w
323}
324
325// ---- D. TREE (this page's siblings/children from parent edges) --------------
326// Emits the parent's child list (or, if slug is root, the root's children),
327// highlighting the current page. Marker class="nx-tree". Pure read of wl.
328func sh_tree(buf: *u8, off: i64, wl: *NxWikiLinks, slug: *u8) -> i64 {
329 let r: i64 = sh_wl_row_of(wl, slug)
330 if r < 0 { return off }
331 let par: *u8 = sh_wl_field(wl, r, 1)
332 // the "branch parent" is this page's parent, or this page itself if it is root
333 var branch: *u8 = par
334 if (branch as i64) == 0 { branch = slug } // root: show its own children
335 var w: i64 = off
336 w = sh_cat(buf, w, "<nav class=\"nx-tree\" aria-label=\"Section\"><b>" as *u8)
337 w = sh_title_of(buf, w, branch)
338 w = sh_cat(buf, w, "</b><ul>\n" as *u8)
339 // root link of the branch first
340 w = sh_cat(buf, w, "<li><a href=\"/wiki/" as *u8); w = sh_cat(buf, w, branch)
341 w = sh_cat(buf, w, ".html\">" as *u8); w = sh_title_of(buf, w, branch); w = sh_cat(buf, w, "</a><ul>\n" as *u8)
342 // every page whose parent == branch
343 var c: i64 = 0
344 while c < wl.nrows {
345 let cp: *u8 = sh_wl_field(wl, c, 1)
346 if sh_streq(cp, branch) == 1 {
347 let cs: *u8 = sh_wl_field(wl, c, 0)
348 if sh_streq(cs, slug) == 1 {
349 w = sh_cat(buf, w, "<li class=\"nx-tree-cur\"><strong>" as *u8); w = sh_title_of(buf, w, cs); w = sh_cat(buf, w, "</strong></li>\n" as *u8)
350 } else {
351 w = sh_cat(buf, w, "<li><a href=\"/wiki/" as *u8); w = sh_cat(buf, w, cs)
352 w = sh_cat(buf, w, ".html\">" as *u8); w = sh_title_of(buf, w, cs); w = sh_cat(buf, w, "</a></li>\n" as *u8)
353 }
354 }
355 c = c + 1
356 }
357 w = sh_cat(buf, w, "</ul></li>\n</ul></nav>\n" as *u8)
358 return w
359}
360
361// ---- E. BACKLINKS ("what links here"): THIN wrapper over the existing engine -
362// nx_wiki_backlinks_panel computes the reverse-link set from page bodies (ground
363// truth, never hand-maintained) and emits an <aside class="nx-backlinks">. This
364// wrapper just splices its NUL-terminated output into the caller buffer at off.
365const SH_BL_CAP: i64 = 32768
366func sh_backlinks(buf: *u8, off: i64, store: *NxWikiDocStore, slug: *u8) -> i64 {
367 if (store as i64) == 0 { return off }
368 let scratch: *u8 = sys_mmap(SH_BL_CAP)
369 let used: *i64 = sys_mmap(8) as *i64
370 let rc: i64 = nx_wiki_backlinks_panel(store, slug, sh_slen(slug), scratch, SH_BL_CAP, used)
371 if rc != NX_WBL_OK { return off }
372 var w: i64 = off
373 var i: i64 = 0
374 while i < used[0] { buf[w] = scratch[i]; w = w + 1; i = i + 1 }
375 return w
376}
377
378// ---- live-corpus store loader (so a page emitter can feed sh_backlinks) ------
379// Ingests the framed corpus snapshot (###PAGE <url> <blen>\n<body>) -- the SAME
380// transport nx_wiki_walkability/nx_ims_monitor read -- into a doc store. Returns
381// the store (valid) or 0 on failure. Read-only.
382const SH_CORPUS_PATH: *u8 = "knowledge/status/ims_live_corpus.txt"
383const SH_CORPUS_CAP: i64 = 4194304
384const SH_CORPUS_MARKN: i64 = 8 // len("###PAGE ")
385func sh_corpus_store() -> *NxWikiDocStore {
386 let nbox: *i64 = sys_mmap(16) as *i64
387 nbox[0] = 0
388 let buf: *u8 = sys_read_file(SH_CORPUS_PATH, nbox)
389 if (buf as i64) == 0 { return 0 as *NxWikiDocStore }
390 let total: i64 = nbox[0]
391 if total < 1 { return 0 as *NxWikiDocStore }
392 if total > SH_CORPUS_CAP { return 0 as *NxWikiDocStore }
393 let store: *NxWikiDocStore = sys_mmap(512) as *NxWikiDocStore
394 nx_wiki_doc_store_init(store, 64, 16384, 16384, SH_CORPUS_CAP)
395 let mk: *u8 = "###PAGE \x00" as *u8
396 var i: i64 = 0
397 while i < total {
398 var ismark: i64 = 0
399 if i + SH_CORPUS_MARKN <= total {
400 var mi: i64 = 0
401 var ok: i64 = 1
402 while mi < SH_CORPUS_MARKN { if buf[i + mi] != mk[mi] { ok = 0 } mi = mi + 1 }
403 ismark = ok
404 }
405 if ismark == 1 {
406 let url_off: i64 = i + SH_CORPUS_MARKN
407 var u: i64 = url_off
408 var du: i64 = 0
409 while du == 0 { if u >= total { du = 1 } else { if buf[u] == 0x20 as u8 { du = 1 } else { u = u + 1 } } }
410 let url_n: i64 = u - url_off
411 var bi: i64 = u + 1
412 var blen: i64 = 0
413 var db: i64 = 0
414 while db == 0 {
415 if bi >= total { db = 1 } else {
416 let c: i64 = buf[bi] as i64
417 if c < 0x30 { db = 1 } else { if c > 0x39 { db = 1 } else { blen = blen * 10 + (c - 0x30); bi = bi + 1 } }
418 }
419 }
420 var he: i64 = bi
421 var dh: i64 = 0
422 while dh == 0 { if he >= total { dh = 1 } else { if buf[he] == 0x0A as u8 { dh = 1 } else { he = he + 1 } } }
423 let body_off: i64 = he + 1
424 var body_n: i64 = blen
425 if body_off + body_n > total { body_n = total - body_off }
426 let url_p: *u8 = (buf as i64 + url_off) as *u8
427 let body_p: *u8 = (buf as i64 + body_off) as *u8
428 nx_wiki_doc_store_add(store, url_p, url_n, url_p, url_n, body_p, body_n)
429 i = body_off + body_n
430 } else {
431 i = i + 1
432 }
433 }
434 return store
435}
436
437// ---- COMBINED walk furniture: one call emits the whole walkable nav block ----
438// breadcrumb + tree + prev/next + related + backlinks, in reading order. store
439// may be 0 (backlinks skipped). Wrapped in <div class="nx-walk"> for styling.
440func sh_walk_furniture(buf: *u8, off: i64, wl: *NxWikiLinks, store: *NxWikiDocStore, slug: *u8) -> i64 {
441 var w: i64 = off
442 w = sh_cat(buf, w, "<div class=\"nx-walk\">\n" as *u8)
443 w = sh_breadcrumb(buf, w, wl, slug)
444 w = sh_tree(buf, w, wl, slug)
445 w = sh_prevnext(buf, w, wl, slug)
446 w = sh_related(buf, w, wl, slug)
447 w = sh_backlinks(buf, w, store, slug)
448 w = sh_cat(buf, w, "</div>\n" as *u8)
449 return w
450}
451
452// ---- FOOTER (refactored): see-also + categories READ from wiki_links.tsv ----
453// see-also = the curated related[] for this slug; categories = the curated tags[].
454// Falls back to the slug itself if the page is absent from the registry.
455func sh_footer_wl(buf: *u8, off: i64, wl: *NxWikiLinks, slug: *u8, epoch: i64) -> i64 {
456 var w: i64 = off
457 let r: i64 = sh_wl_row_of(wl, slug)
458 // SEE ALSO from related[]
459 w = sh_cat(buf, w, "<section id=\"seealso\"><h2><a href=\"#seealso\">See also</a></h2><ul>" as *u8)
460 var any_rel: i64 = 0
461 if r >= 0 {
462 let rel: *u8 = sh_wl_field(wl, r, 4)
463 if (rel as i64) != 0 {
464 var i: i64 = 0
465 var seg_start: i64 = 0
466 var done: i64 = 0
467 while done == 0 {
468 let ch: u8 = rel[i]
469 var boundary: i64 = 0
470 if ch == 0 as u8 { boundary = 1; done = 1 }
471 if ch == 44 as u8 { boundary = 1 }
472 if boundary == 1 {
473 let seglen: i64 = i - seg_start
474 if seglen > 0 {
475 let t: *u8 = sys_mmap(seglen + 1)
476 var j: i64 = 0
477 while j < seglen { t[j] = rel[seg_start + j]; j = j + 1 }
478 t[seglen] = 0 as u8
479 w = sh_cat(buf, w, "<li><a href=\"/wiki/" as *u8); w = sh_cat(buf, w, t)
480 w = sh_cat(buf, w, ".html\">" as *u8); w = sh_title_of(buf, w, t); w = sh_cat(buf, w, "</a></li>" as *u8)
481 any_rel = 1
482 }
483 seg_start = i + 1
484 }
485 i = i + 1
486 if i > SH_WL_MAXBYTES { done = 1 }
487 }
488 }
489 }
490 if any_rel == 0 {
491 w = sh_cat(buf, w, "<li><a href=\"/wiki/start.html\">AI Start Here (capstone)</a></li>" as *u8)
492 }
493 w = sh_cat(buf, w, "</ul></section>\n" as *u8)
494 // CATEGORIES from tags[]
495 w = sh_cat(buf, w, "<div class=\"catlinks\"><strong>Categories:</strong> " as *u8)
496 var any_tag: i64 = 0
497 if r >= 0 {
498 let tg: *u8 = sh_wl_field(wl, r, 5)
499 if (tg as i64) != 0 {
500 var i2: i64 = 0
501 var seg2: i64 = 0
502 var d2: i64 = 0
503 while d2 == 0 {
504 let ch2: u8 = tg[i2]
505 var b2: i64 = 0
506 if ch2 == 0 as u8 { b2 = 1; d2 = 1 }
507 if ch2 == 44 as u8 { b2 = 1 }
508 if b2 == 1 {
509 let sl2: i64 = i2 - seg2
510 if sl2 > 0 {
511 let t2: *u8 = sys_mmap(sl2 + 1)
512 var j2: i64 = 0
513 while j2 < sl2 { t2[j2] = tg[seg2 + j2]; j2 = j2 + 1 }
514 t2[sl2] = 0 as u8
515 w = sh_cat(buf, w, "<a href=\"/wiki/start.html\">" as *u8); w = sh_cat(buf, w, t2); w = sh_cat(buf, w, "</a> " as *u8)
516 any_tag = 1
517 }
518 seg2 = i2 + 1
519 }
520 i2 = i2 + 1
521 if i2 > SH_WL_MAXBYTES { d2 = 1 }
522 }
523 }
524 }
525 if any_tag == 0 { w = sh_cat(buf, w, "<a href=\"/wiki/start.html\">Nishi ecosystem</a>" as *u8) }
526 w = sh_cat(buf, w, "</div>\n" as *u8)
527 // PROVENANCE (unchanged)
528 w = sh_cat(buf, w, "<p class=\"foot\">Built by the sovereign Nishi page builder (<code>nx_wiki_shell</code>) — no nginx, no OpenSSL, no libc. Permalink <a rel=\"bookmark\" href=\"/wiki/" as *u8)
529 w = sh_cat(buf, w, slug); w = sh_cat(buf, w, ".html\">/wiki/" as *u8); w = sh_cat(buf, w, slug)
530 w = sh_cat(buf, w, ".html</a> · regenerate to refresh (epoch=" as *u8); w = sh_catn(buf, w, epoch)
531 w = sh_cat(buf, w, ").</p>\n</body></html>\n" as *u8)
532 return w
533}
534
535// FOOTER: see-also + categories + provenance. (permalink + edit live in head/nav.)
536func sh_footer(buf: *u8, off: i64, slug: *u8, epoch: i64) -> i64 {
537 var w: i64 = off
538 w = sh_cat(buf, w, "<section id=\"seealso\"><h2><a href=\"#seealso\">See also</a></h2><ul><li><a href=\"/wiki/start.html\">AI Start Here (capstone)</a></li><li><a href=\"/wiki/charter.html\">Graceful Evolution Charter</a></li><li><a href=\"/wiki/start.html#tasks\">Task streams (resume any work)</a></li></ul></section>\n" as *u8)
539 w = sh_cat(buf, w, "<div class=\"catlinks\"><strong>Categories:</strong> <a href=\"/wiki/start.html\">Nishi ecosystem</a> <a href=\"/wiki/start.html#docs\">Documentation</a> <a href=\"/wiki/charter.html\">Doctrine</a></div>\n" as *u8)
540 w = sh_cat(buf, w, "<p class=\"foot\">Built by the sovereign Nishi page builder (<code>nx_wiki_shell</code>) — no nginx, no OpenSSL, no libc. Permalink <a rel=\"bookmark\" href=\"/wiki/" as *u8)
541 w = sh_cat(buf, w, slug); w = sh_cat(buf, w, ".html\">/wiki/" as *u8); w = sh_cat(buf, w, slug)
542 w = sh_cat(buf, w, ".html</a> · regenerate to refresh (epoch=" as *u8); w = sh_catn(buf, w, epoch)
543 w = sh_cat(buf, w, ").</p>\n</body></html>\n" as *u8)
544 return w
545}