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, "<" as *u8) } else { if c == 62 { gw(fd, ">" as *u8) } else { if c == 38 { gw(fd, "&" 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>🧠 " as *u8); gwn(fd, ncount); gw(fd, " nodes</h2><div style=\"font-size:11px;color:#8b949e\">click a node to jump · Ctrl+F to search · 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}