code wiki / (root) / nx_render_journal.nx

nx_render_journal.nx source

↩ module page · 226 lines · 7860 B

1// nx_render_journal.nx -- substrate-native UX/CX intelligence layer. 2// 3// First-class observability for every Nishi app deployed via the 4// WSLg-X11 pioneer pattern. REPLACES screen-scraping / pixel-level 5// session recording (Hotjar, FullStory, LogRocket -- ALL refused as 6// surveillance) with substrate-side structured intent logging: 7// each paint/dispatch/navigation op records its OWN intent the 8// moment it happens. The user's screen is never observed; only the 9// substrate's actions are. Substrate honesty over surveillance. 10// 11// Patterns absorbed from best-in-class (without copying their 12// snooping-flavoured implementations): 13// OpenTelemetry traces -> kind + attributes + timestamp per event 14// Honeycomb wide events -> rich attribute set per row 15// Sentry RUM -> error-fingerprint via semantic tag 16// Chrome DevTools -> queryable tree state at any moment 17// React DevTools -> component-intent introspection 18// eBPF -> instrumentation by composition not snooping 19// 20// Patterns explicitly REFUSED: 21// Hotjar / FullStory pixel-stream session-replay 22// Screenshot-every-N-seconds (snoops user content) 23// Keystroke-by-keystroke recording outside form-input boundaries 24// Mouse-trail capture beyond event handler hit-tests 25// 26// API: 27// nx_journal_new() -> *RenderJournal 28// nx_journal_log_rect(j, intent, x, y, w, h, color) 29// nx_journal_log_event(j, intent, kind, x, y) 30// nx_journal_log_nav(j, intent, from_idx, to_idx) 31// nx_journal_dump_to_fd(j, fd) -- structured text 32// nx_journal_dump_to_file(j, path) -- to /tmp/*.log 33// 34// license_tier: ORIGINAL 35// lineage_id: nishi_render_journal_q10 36 37import "nx_syscalls.nx" 38 39const NX_JOURNAL_MAX_ENTRIES: i64 = 1024 40const NX_JOURNAL_KIND_RECT: i64 = 1 41const NX_JOURNAL_KIND_EVENT: i64 = 2 42const NX_JOURNAL_KIND_NAV: i64 = 3 43 44struct RenderJournalEntry { 45 kind: i64, // 1=RECT, 2=EVENT, 3=NAV 46 intent: *u8, // C-style NUL-terminated intent tag 47 a: i64, // x or kind-specific param 0 48 b: i64, // y or kind-specific param 1 49 c: i64, // w or kind-specific param 2 50 d: i64, // h or kind-specific param 3 51 color: i64, 52 timestamp: i64 53} 54 55const NX_JOURNAL_ENTRY_BYTES: i64 = 64 56 57struct RenderJournal { 58 entries: *RenderJournalEntry, 59 count: i64, 60 capacity: i64 61} 62 63const NX_JOURNAL_BYTES: i64 = 32 64 65func nx_journal_new() -> *RenderJournal { 66 let raw: *u8 = sys_mmap(NX_JOURNAL_BYTES + 16) 67 let j: *RenderJournal = raw as *RenderJournal 68 j.entries = (sys_mmap(NX_JOURNAL_MAX_ENTRIES * NX_JOURNAL_ENTRY_BYTES + 16)) 69 as *RenderJournalEntry 70 j.count = 0 71 j.capacity = NX_JOURNAL_MAX_ENTRIES 72 return j 73} 74 75func _journal_at(j: *RenderJournal, idx: i64) -> *RenderJournalEntry { 76 return ((j.entries as i64) + idx * NX_JOURNAL_ENTRY_BYTES) as *RenderJournalEntry 77} 78 79func nx_journal_log_rect(j: *RenderJournal, intent: *u8, 80 x: i64, y: i64, w: i64, h: i64, 81 color: i64) -> i64 { 82 if j.count >= j.capacity { return 0 - 1 } 83 let e: *RenderJournalEntry = _journal_at(j, j.count) 84 e.kind = NX_JOURNAL_KIND_RECT 85 e.intent = intent 86 e.a = x; e.b = y; e.c = w; e.d = h 87 e.color = color 88 e.timestamp = j.count // monotonic ordinal for now 89 j.count = j.count + 1 90 return 0 91} 92 93func nx_journal_log_event(j: *RenderJournal, intent: *u8, 94 event_kind: i64, x: i64, y: i64) -> i64 { 95 if j.count >= j.capacity { return 0 - 1 } 96 let e: *RenderJournalEntry = _journal_at(j, j.count) 97 e.kind = NX_JOURNAL_KIND_EVENT 98 e.intent = intent 99 e.a = event_kind 100 e.b = x; e.c = y 101 e.d = 0 102 e.color = 0 103 e.timestamp = j.count 104 j.count = j.count + 1 105 return 0 106} 107 108func nx_journal_log_nav(j: *RenderJournal, intent: *u8, 109 from_idx: i64, to_idx: i64) -> i64 { 110 if j.count >= j.capacity { return 0 - 1 } 111 let e: *RenderJournalEntry = _journal_at(j, j.count) 112 e.kind = NX_JOURNAL_KIND_NAV 113 e.intent = intent 114 e.a = from_idx 115 e.b = to_idx 116 e.c = 0; e.d = 0 117 e.color = 0 118 e.timestamp = j.count 119 j.count = j.count + 1 120 return 0 121} 122 123// Write decimal of v (signed) to buf, advancing *off. 124func _journal_put_dec(buf: *u8, off: *i64, v: i64) -> i64 { 125 var x: i64 = v 126 if x < 0 { 127 buf[*off] = 0x2D 128 *off = *off + 1 129 x = 0 - x 130 } 131 if x == 0 { 132 buf[*off] = 0x30; *off = *off + 1; return 0 133 } 134 let tmp: *u8 = sys_mmap(24) 135 var n: i64 = 0 136 while x > 0 { tmp[n] = (0x30 + (x % 10)) as u8; x = x / 10; n = n + 1 } 137 while n > 0 { n = n - 1; buf[*off] = tmp[n]; *off = *off + 1 } 138 return 0 139} 140 141func _journal_put_hex8(buf: *u8, off: *i64, v: i64) -> i64 { 142 buf[*off] = 0x30; *off = *off + 1 143 buf[*off] = 0x78; *off = *off + 1 144 var i: i64 = 7 145 while i >= 0 { 146 let nib: i64 = (v >> (i * 4)) & 0xF 147 if nib < 10 { buf[*off] = (0x30 + nib) as u8 } 148 else { buf[*off] = (0x57 + nib) as u8 } 149 *off = *off + 1 150 i = i - 1 151 } 152 return 0 153} 154 155func _journal_put_str(buf: *u8, off: *i64, s: *u8) -> i64 { 156 var i: i64 = 0 157 while s[i] != 0 { buf[*off] = s[i]; *off = *off + 1; i = i + 1 } 158 return 0 159} 160 161// Dump to fd as one-line-per-entry structured text. Format: 162// N RECT intent (x,y,w,h) color=0xRRGGBB 163// N EVENT intent kind=K pos=(x,y) 164// N NAV intent from=F to=T 165func nx_journal_dump_to_fd(j: *RenderJournal, fd: i64) -> i64 { 166 let line: *u8 = sys_mmap(512) 167 var idx: i64 = 0 168 while idx < j.count { 169 let e: *RenderJournalEntry = _journal_at(j, idx) 170 var o: i64 = 0 171 let o_p: *i64 = sys_mmap(16) as *i64 172 *o_p = 0 173 _journal_put_dec(line, o_p, e.timestamp) 174 _journal_put_str(line, o_p, " " as *u8) 175 176 if e.kind == NX_JOURNAL_KIND_RECT { 177 _journal_put_str(line, o_p, "RECT " as *u8) 178 _journal_put_str(line, o_p, e.intent) 179 _journal_put_str(line, o_p, " (" as *u8) 180 _journal_put_dec(line, o_p, e.a) 181 _journal_put_str(line, o_p, "," as *u8) 182 _journal_put_dec(line, o_p, e.b) 183 _journal_put_str(line, o_p, "," as *u8) 184 _journal_put_dec(line, o_p, e.c) 185 _journal_put_str(line, o_p, "," as *u8) 186 _journal_put_dec(line, o_p, e.d) 187 _journal_put_str(line, o_p, ") color=" as *u8) 188 _journal_put_hex8(line, o_p, e.color) 189 } 190 if e.kind == NX_JOURNAL_KIND_EVENT { 191 _journal_put_str(line, o_p, "EVENT " as *u8) 192 _journal_put_str(line, o_p, e.intent) 193 _journal_put_str(line, o_p, " kind=" as *u8) 194 _journal_put_dec(line, o_p, e.a) 195 _journal_put_str(line, o_p, " pos=(" as *u8) 196 _journal_put_dec(line, o_p, e.b) 197 _journal_put_str(line, o_p, "," as *u8) 198 _journal_put_dec(line, o_p, e.c) 199 _journal_put_str(line, o_p, ")" as *u8) 200 } 201 if e.kind == NX_JOURNAL_KIND_NAV { 202 _journal_put_str(line, o_p, "NAV " as *u8) 203 _journal_put_str(line, o_p, e.intent) 204 _journal_put_str(line, o_p, " from=" as *u8) 205 _journal_put_dec(line, o_p, e.a) 206 _journal_put_str(line, o_p, " to=" as *u8) 207 _journal_put_dec(line, o_p, e.b) 208 } 209 line[*o_p] = 0x0A; *o_p = *o_p + 1 210 sys_write(fd, line, *o_p) 211 idx = idx + 1 212 } 213 return 0 214} 215 216func nx_journal_dump_to_file(j: *RenderJournal, path: *u8) -> i64 { 217 let fd: i64 = sys_openat_wr(path, 0x1A4) 218 if fd < 0 { return 0 - 1 } 219 nx_journal_dump_to_fd(j, fd) 220 sys_close(fd) 221 return 0 222} 223 224func main() -> i64 { 225 return 0 226}