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}