code wiki / _hdl_build / nx_mindmap.nx

nx_mindmap.nx source

↩ module page · 237 lines · 13226 B

1// nx_mindmap.nx -- DATA-DRIVEN sovereign tree/mind-map -> NAVIGABLE HTML (the Nishi Visualization Suite, made 2// data-driven + navigable so you can LOGICALLY PARSE any node/parent dataset, not a hardcoded chart). Ingests 3// node_id <TAB> type <TAB> parent_id <TAB> axis <TAB> sovereignty <TAB> label <TAB> safety (parent="-" = root) 4// computes a LAYERED tree layout (x = depth from root, y = sibling order), and emits ONE self-contained HTML 5// page: an inline native SVG (no mermaid.js/d3.js) with every node a rounded rect COLOR-CODED BY SOVEREIGNTY 6// (per-node id anchor + full label on hover), plus a SIDEBAR INDEX grouped by sovereignty -- click a node to 7// jump to it (and :target highlights it), Ctrl+F to search, native browser scroll/zoom to pan. All emitted by 8// the organ; the only browser dependency is native scroll/zoom/find (no app-JS). Point it at any tree (family 9// genealogy, arcs, workstreams). usage: nx_mindmap <input.tsv> <output.html>. license_tier: ORIGINAL 10import "nx_syscalls.nx" 11 12const MM_MAXN: i64 = 256 13const MM_NODE_W: i64 = 300 14const MM_NODE_H: i64 = 30 15const MM_COL_W: i64 = 340 16const MM_ROW_H: i64 = 44 17const MM_MARGIN: i64 = 30 18 19func mm_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 20func mm_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while 1 == 1 { if a[i] != b[i] { return 0 } if a[i] == (0 as u8) { return 1 } i = i + 1 } return 1 } 21func mm_atoi(s: *u8) -> i64 { var v: i64 = 0; var i: i64 = 0; while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } return v } 22// take the first comma-separated value in place (lineage parents may be multi -> primary lineage) 23func mm_first(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { if (s[i] as i64) == 44 { s[i] = 0 as u8; return 0 } i = i + 1 } return 0 } 24func mm_known_sov(sov: *u8) -> i64 { 25 if mm_streq(sov, "sovereign" as *u8) == 1 { return 1 } 26 if mm_streq(sov, "sovereign-emu" as *u8) == 1 { return 1 } 27 if mm_streq(sov, "building" as *u8) == 1 { return 1 } 28 if mm_streq(sov, "third-party" as *u8) == 1 { return 1 } 29 if mm_streq(sov, "mixed" as *u8) == 1 { return 1 } 30 if mm_streq(sov, "origin" as *u8) == 1 { return 1 } 31 if mm_streq(sov, "future" as *u8) == 1 { return 1 } 32 return 0 33} 34func gw(fd: i64, s: *u8) -> i64 { sys_write(fd, s, mm_slen(s)); return 0 } 35func gwn(fd: i64, v: i64) -> i64 { 36 let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) } 37 let t: *u8 = sys_mmap(28); var k: i64 = 0 38 if m == 0 { t[0] = 48 as u8; k = 1 } 39 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 40 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 41 sys_write(fd, bb, k); return 0 42} 43func gw_esc(fd: i64, s: *u8, cap: i64) -> i64 { 44 var i: i64 = 0 45 while s[i] != (0 as u8) { 46 if i >= cap { gw(fd, "\xE2\x80\xA6" as *u8); return 0 } 47 let c: i64 = s[i] as i64 48 if c == 60 { gw(fd, "&lt;" as *u8) } else { if c == 62 { gw(fd, "&gt;" as *u8) } else { if c == 38 { gw(fd, "&amp;" as *u8) } else { let b: *u8 = sys_mmap(1); b[0] = c as u8; sys_write(fd, b, 1) } } } 49 i = i + 1 50 } 51 return 0 52} 53func mm_color(sov: *u8) -> *u8 { 54 if mm_streq(sov, "sovereign" as *u8) == 1 { return "#1b5e20" as *u8 } 55 if mm_streq(sov, "third-party" as *u8) == 1 { return "#b71c1c" as *u8 } 56 if mm_streq(sov, "building" as *u8) == 1 { return "#e65100" as *u8 } 57 if mm_streq(sov, "sovereign-emu" as *u8) == 1 { return "#00695c" as *u8 } 58 if mm_streq(sov, "mixed" as *u8) == 1 { return "#4a148c" as *u8 } 59 if mm_streq(sov, "origin" as *u8) == 1 { return "#0d47a1" as *u8 } 60 if mm_streq(sov, "future" as *u8) == 1 { return "#424242" as *u8 } 61 return "#37474f" as *u8 62} 63 64func main(argc: i64, argv: *i64) -> i64 { 65 if argc < 3 { sys_write(2, "usage: nx_mindmap <input.tsv> <output.html>\n" as *u8, 44); return 1 } 66 let tsv: *u8 = argv[1] as *u8 67 let outp: *u8 = argv[2] as *u8 68 // optional column map (default = genesis_lineage schema: id=0 parent=2 color=4 label=5) 69 var id_col: i64 = 0 70 var parent_col: i64 = 2 71 var color_col: i64 = 4 72 var label_col: i64 = 5 73 if argc >= 7 { id_col = mm_atoi(argv[3] as *u8); parent_col = mm_atoi(argv[4] as *u8); color_col = mm_atoi(argv[5] as *u8); label_col = mm_atoi(argv[6] as *u8) } 74 75 let lenbox: *i64 = sys_mmap(16) as *i64; lenbox[0] = 0 76 let buf: *u8 = sys_read_file(tsv, lenbox) 77 if (buf as i64) == 0 { sys_write(2, "nx_mindmap: cannot read input\n" as *u8, 30); return 2 } 78 let n: i64 = lenbox[0] 79 80 let ids: *i64 = sys_mmap(MM_MAXN * 8) as *i64 81 let parents: *i64 = sys_mmap(MM_MAXN * 8) as *i64 82 let sovs: *i64 = sys_mmap(MM_MAXN * 8) as *i64 83 let labels: *i64 = sys_mmap(MM_MAXN * 8) as *i64 84 var ncount: i64 = 0 85 86 var i: i64 = 0 87 while i < n { 88 let linestart: i64 = i 89 var le: i64 = linestart 90 var f: i64 = 1 91 while f == 1 { if le >= n { f = 0 } else { if (buf[le] as i64) == 10 { f = 0 } else { le = le + 1 } } } 92 let lineend: i64 = le 93 i = lineend + 1 94 if lineend > linestart { 95 if (buf[linestart] as i64) != 35 { 96 let fields: *i64 = sys_mmap(16 * 8) as *i64 97 var nf: i64 = 0 98 var fstart: i64 = linestart 99 var q: i64 = linestart 100 while q <= lineend { 101 if q == lineend { 102 if nf < 16 { fields[nf] = ((buf as i64) + fstart) as i64; nf = nf + 1 } 103 if lineend < n { buf[lineend] = 0 as u8 } 104 q = lineend + 1 105 } else { 106 if (buf[q] as i64) == 9 { 107 if nf < 16 { fields[nf] = ((buf as i64) + fstart) as i64; nf = nf + 1 } 108 buf[q] = 0 as u8 109 fstart = q + 1 110 } 111 q = q + 1 112 } 113 } 114 if nf >= 6 { 115 if ncount < MM_MAXN { 116 ids[ncount] = fields[id_col] 117 let pp: *u8 = fields[parent_col] as *u8 118 mm_first(pp) 119 parents[ncount] = pp as i64 120 sovs[ncount] = fields[color_col] 121 labels[ncount] = fields[label_col] 122 ncount = ncount + 1 123 } 124 } 125 } 126 } 127 } 128 129 let depths: *i64 = sys_mmap(MM_MAXN * 8) as *i64 130 var d0: i64 = 0 131 while d0 < ncount { 132 var d: i64 = 0; var cur: i64 = d0; var go: i64 = 1; var guard: i64 = 0 133 while go == 1 { 134 if guard > MM_MAXN { go = 0 } 135 let pid: *u8 = parents[cur] as *u8 136 if mm_streq(pid, "-" as *u8) == 1 { go = 0 } 137 else { 138 var pidx: i64 = 0 - 1; var j: i64 = 0 139 while j < ncount { if mm_streq(ids[j] as *u8, pid) == 1 { pidx = j; j = ncount } else { j = j + 1 } } 140 if pidx < 0 { go = 0 } else { d = d + 1; cur = pidx; guard = guard + 1 } 141 } 142 } 143 depths[d0] = d; d0 = d0 + 1 144 } 145 146 let xs: *i64 = sys_mmap(MM_MAXN * 8) as *i64 147 let ys: *i64 = sys_mmap(MM_MAXN * 8) as *i64 148 let dcount: *i64 = sys_mmap(MM_MAXN * 8) as *i64 149 var z: i64 = 0; while z < MM_MAXN { dcount[z] = 0; z = z + 1 } 150 var maxd: i64 = 0; var maxrows: i64 = 0 151 var k: i64 = 0 152 while k < ncount { 153 let d: i64 = depths[k] 154 xs[k] = MM_MARGIN + d * MM_COL_W 155 ys[k] = MM_MARGIN + 20 + dcount[d] * MM_ROW_H 156 dcount[d] = dcount[d] + 1 157 if d > maxd { maxd = d } 158 if dcount[d] > maxrows { maxrows = dcount[d] } 159 k = k + 1 160 } 161 let svg_w: i64 = MM_MARGIN * 2 + (maxd + 1) * MM_COL_W 162 let svg_h: i64 = MM_MARGIN * 2 + 20 + maxrows * MM_ROW_H 163 164 __syscall(263, AT_FDCWD, outp as i64, 0, 0, 0, 0) 165 let fd: i64 = sys_openat_wr(outp, 0x1a4) 166 if fd < 0 { sys_write(2, "nx_mindmap: cannot open output\n" as *u8, 31); return 3 } 167 168 // ---- HTML head + style ---- 169 gw(fd, "<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Nishi mind-map</title>\n" as *u8) 170 gw(fd, "<style>body{margin:0;background:#0d1117;color:#e6edf3;font-family:monospace;display:flex;height:100vh}aside{width:320px;flex:0 0 320px;overflow:auto;padding:10px;border-right:1px solid #30363d;box-sizing:border-box}main{flex:1;overflow:auto}a{color:#9bd1ff;text-decoration:none;display:block;padding:1px 2px;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}a:hover{background:#161b22}.d{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:5px;vertical-align:middle}h3{font-size:13px;margin:12px 0 4px;color:#e6edf3}h2{font-size:15px;margin:4px 0}:target rect{stroke:#fff;stroke-width:3.5}</style></head><body>\n" as *u8) 171 172 // ---- sidebar index, grouped by sovereignty ---- 173 gw(fd, "<aside><h2>&#129504; " as *u8); gwn(fd, ncount); gw(fd, " nodes</h2><div style=\"font-size:11px;color:#8b949e\">click a node to jump &middot; Ctrl+F to search &middot; scroll/zoom the chart</div>\n" as *u8) 174 let groups: *i64 = sys_mmap(8 * 8) as *i64 175 groups[0] = "sovereign" as *u8 as i64; groups[1] = "sovereign-emu" as *u8 as i64; groups[2] = "building" as *u8 as i64 176 groups[3] = "third-party" as *u8 as i64; groups[4] = "mixed" as *u8 as i64; groups[5] = "origin" as *u8 as i64; groups[6] = "future" as *u8 as i64 177 var g: i64 = 0 178 while g < 7 { 179 let gname: *u8 = groups[g] as *u8 180 // count 181 var cnt: i64 = 0; var c2: i64 = 0 182 while c2 < ncount { if mm_streq(sovs[c2] as *u8, gname) == 1 { cnt = cnt + 1 } c2 = c2 + 1 } 183 if cnt > 0 { 184 gw(fd, "<h3><span class=d style=\"background:" as *u8); gw(fd, mm_color(gname)); gw(fd, "\"></span>" as *u8); gw(fd, gname); gw(fd, " (" as *u8); gwn(fd, cnt); gw(fd, ")</h3>\n" as *u8) 185 var p: i64 = 0 186 while p < ncount { 187 if mm_streq(sovs[p] as *u8, gname) == 1 { 188 gw(fd, "<a href=\"#n" as *u8); gwn(fd, p); gw(fd, "\"><b>" as *u8); gw_esc(fd, ids[p] as *u8, 22); gw(fd, "</b> " as *u8); gw_esc(fd, labels[p] as *u8, 40); gw(fd, "</a>\n" as *u8) 189 } 190 p = p + 1 191 } 192 } 193 g = g + 1 194 } 195 var ocnt: i64 = 0; var oc: i64 = 0 196 while oc < ncount { if mm_known_sov(sovs[oc] as *u8) == 0 { ocnt = ocnt + 1 } oc = oc + 1 } 197 if ocnt > 0 { 198 gw(fd, "<h3><span class=d style=\"background:#37474f\"></span>other (" as *u8); gwn(fd, ocnt); gw(fd, ")</h3>\n" as *u8) 199 var op: i64 = 0 200 while op < ncount { 201 if mm_known_sov(sovs[op] as *u8) == 0 { 202 gw(fd, "<a href=\"#n" as *u8); gwn(fd, op); gw(fd, "\"><b>" as *u8); gw_esc(fd, ids[op] as *u8, 22); gw(fd, "</b> " as *u8); gw_esc(fd, labels[op] as *u8, 40); gw(fd, "</a>\n" as *u8) 203 } 204 op = op + 1 205 } 206 } 207 gw(fd, "</aside>\n" as *u8) 208 209 // ---- main canvas: inline SVG ---- 210 gw(fd, "<main><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" as *u8); gwn(fd, svg_w); gw(fd, "\" height=\"" as *u8); gwn(fd, svg_h); gw(fd, "\" font-family=\"monospace\">\n" as *u8) 211 gw(fd, "<rect width=\"100%\" height=\"100%\" fill=\"#0d1117\"/>\n" as *u8) 212 var e: i64 = 0 213 while e < ncount { 214 let pid: *u8 = parents[e] as *u8 215 if mm_streq(pid, "-" as *u8) != 1 { 216 var pidx: i64 = 0 - 1; var j: i64 = 0 217 while j < ncount { if mm_streq(ids[j] as *u8, pid) == 1 { pidx = j; j = ncount } else { j = j + 1 } } 218 if pidx >= 0 { 219 gw(fd, "<line x1=\"" as *u8); gwn(fd, xs[pidx] + MM_NODE_W); gw(fd, "\" y1=\"" as *u8); gwn(fd, ys[pidx] + MM_NODE_H / 2) 220 gw(fd, "\" x2=\"" as *u8); gwn(fd, xs[e]); gw(fd, "\" y2=\"" as *u8); gwn(fd, ys[e] + MM_NODE_H / 2); gw(fd, "\" stroke=\"#475569\" stroke-width=\"1.5\"/>\n" as *u8) 221 } 222 } 223 e = e + 1 224 } 225 var m: i64 = 0 226 while m < ncount { 227 gw(fd, "<g id=\"n" as *u8); gwn(fd, m); gw(fd, "\"><title>" as *u8); gw_esc(fd, labels[m] as *u8, 200); gw(fd, "</title>\n" as *u8) 228 gw(fd, "<rect x=\"" as *u8); gwn(fd, xs[m]); gw(fd, "\" y=\"" as *u8); gwn(fd, ys[m]); gw(fd, "\" width=\"" as *u8); gwn(fd, MM_NODE_W); gw(fd, "\" height=\"" as *u8); gwn(fd, MM_NODE_H); gw(fd, "\" rx=\"6\" fill=\"" as *u8); gw(fd, mm_color(sovs[m] as *u8)); gw(fd, "\" stroke=\"#30363d\"/>\n" as *u8) 229 gw(fd, "<text x=\"" as *u8); gwn(fd, xs[m] + 8); gw(fd, "\" y=\"" as *u8); gwn(fd, ys[m] + 20); gw(fd, "\" font-size=\"12\" fill=\"#ffffff\"><tspan font-weight=\"bold\">" as *u8); gw_esc(fd, ids[m] as *u8, 18); gw(fd, "</tspan> " as *u8); gw_esc(fd, labels[m] as *u8, 32); gw(fd, "</text></g>\n" as *u8) 230 m = m + 1 231 } 232 gw(fd, "</svg></main></body></html>\n" as *u8) 233 sys_close(fd) 234 235 gw(1, "[nx_mindmap] wrote " as *u8); gw(1, outp); gw(1, " (" as *u8); gwn(1, ncount); gw(1, " nodes, " as *u8); gwn(1, maxd + 1); gw(1, " levels)\n" as *u8) 236 return 0 237}