nx_browser_buyguard.nx source
↩ module page · 136 lines · 6557 B
1// nx_browser_buyguard.nx -- the Nishi BROWSER's "before you buy" pass, on REAL page HTML (operator: "part
2// of the nishi browser ... showing what you have vs what it will cost and help with the opportunity costs").
3// Composes the browser's ACTUAL pipeline organ nx_html_to_text (fetch -> html_to_text -> render): the browser
4// hands this the page HTML; the pass strips it to text (the real renderer path), finds the price
5// (px_find_price), runs the grounded purchase-decision engine (nx_purchase: opportunity cost EXACT via
6// nx_debt), and emits a zero-JS overlay the browser shows -- WHAT YOU HAVE vs WHAT IT TRULY COSTS. Advisory
7// only (autonomy). No price on the page -> ALLOW + empty overlay (does nothing). license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_money.nx"
10import "nx_debt_payoff.nx"
11import "nx_adhd.nx"
12import "nx_html_to_text.nx"
13import "nx_price_extract.nx"
14import "nx_purchase.nx"
15
16// profile array indices (the person's real numbers; the intake survey fills these).
17const BGW_AVAIL: i64 = 0 // available discretionary cash (cents)
18const BGW_BAL: i64 = 1 // highest-interest debt balance (cents)
19const BGW_APR: i64 = 2 // its APR (scaled)
20const BGW_PAY: i64 = 3 // monthly payment (cents)
21const BGW_WAGE: i64 = 4 // hourly wage (cents)
22const BGW_GOAL: i64 = 5 // savings/buffer goal (cents)
23const BGW_THRESH: i64 = 6 // impulse pause threshold (cents)
24
25func bgw_cat(out: *u8, off: i64, s: *u8) -> i64 {
26 var o: i64 = off; var i: i64 = 0
27 while s[i] != (0 as u8) { out[o] = s[i]; o = o + 1; i = i + 1 }
28 return o
29}
30func bgw_n(out: *u8, off: i64, v: i64) -> i64 {
31 if v == 0 { out[off] = 0x30 as u8; return off + 1 }
32 let tmp: *u8 = sys_mmap(24); var m: i64 = v; var k: i64 = 0
33 while m > 0 { tmp[k] = (0x30 + (m % 10)) as u8; m = m / 10; k = k + 1 }
34 var o: i64 = off; var i: i64 = k - 1
35 while i >= 0 { out[o] = tmp[i]; o = o + 1; i = i - 1 }
36 return o
37}
38func bgw_money(out: *u8, off: i64, cents: i64) -> i64 {
39 var o: i64 = off; var c: i64 = cents
40 if c < 0 { out[o] = 0x2d as u8; o = o + 1; c = 0 - c }
41 out[o] = 0x24 as u8; o = o + 1
42 let dollars: i64 = c / 100
43 let cc: i64 = c - dollars * 100
44 o = bgw_n(out, o, dollars)
45 out[o] = 0x2e as u8; o = o + 1
46 out[o] = (0x30 + (cc / 10)) as u8; o = o + 1
47 out[o] = (0x30 + (cc % 10)) as u8; o = o + 1
48 return o
49}
50
51// Run the before-you-buy pass on a page's HTML using the person's profile. Writes the zero-JS overlay HTML
52// (NUL-terminated) to out; returns the advisory decision (ADHD_ALLOW/PAUSE/WARN). No price -> ALLOW, empty.
53func bgw_scan(html: *u8, html_len: i64, prof: *i64, out: *u8, cap: i64) -> i64 {
54 let text: *u8 = sys_mmap(html_len + 8192)
55 let tn: i64 = nx_html_to_text(html, html_len, text, html_len + 8192)
56 let price: i64 = px_find_price(text, tn)
57 if price < 0 { out[0] = 0 as u8; return ADHD_ALLOW }
58 let avail: i64 = prof[BGW_AVAIL]
59 let bal: i64 = prof[BGW_BAL]
60 let apr: i64 = prof[BGW_APR]
61 let pay: i64 = prof[BGW_PAY]
62 let wage: i64 = prof[BGW_WAGE]
63 let thresh: i64 = prof[BGW_THRESH]
64 let saved: i64 = pur_interest_saved(bal, apr, pay, price)
65 let truecost: i64 = pur_true_cost(price, saved)
66 let hours: i64 = pur_hours(price, wage)
67 let decision: i64 = pur_recommend(price, 1, thresh, avail)
68 var o: i64 = 0
69 o = bgw_cat(out, o, "<div class=\"nishi-buyguard\"><strong>Before you buy</strong> — this page is asking " as *u8)
70 o = bgw_money(out, o, price)
71 o = bgw_cat(out, o, ". You have " as *u8)
72 o = bgw_money(out, o, avail)
73 o = bgw_cat(out, o, ". It truly costs " as *u8)
74 o = bgw_money(out, o, truecost)
75 o = bgw_cat(out, o, " — that " as *u8)
76 o = bgw_money(out, o, price)
77 o = bgw_cat(out, o, " would save " as *u8)
78 o = bgw_money(out, o, saved)
79 o = bgw_cat(out, o, " in interest on your debt, and it is " as *u8)
80 o = bgw_n(out, o, hours)
81 o = bgw_cat(out, o, " hours of your life. " as *u8)
82 if decision == ADHD_PAUSE { o = bgw_cat(out, o, "Sleep on it 24h?" as *u8) }
83 else { if decision == ADHD_WARN { o = bgw_cat(out, o, "This is over your budget." as *u8) } else { o = bgw_cat(out, o, "Within your means." as *u8) } }
84 o = bgw_cat(out, o, "</div>" as *u8)
85 out[o] = 0 as u8
86 return decision
87}
88
89// find the byte index JUST AFTER the first "<body...>" open tag, so the overlay is inserted as flow
90// content where the layout engine expects it. Returns 0 (prepend) if there is no <body> tag.
91func bgw_find_body(raw: *u8, raw_len: i64) -> i64 {
92 var i: i64 = 0
93 while i + 5 <= raw_len {
94 if raw[i] == (0x3c as u8) { // '<'
95 let c1: i64 = raw[i+1] as i64
96 // case-insensitive "body"
97 if (c1 == 0x62) | (c1 == 0x42) {
98 let c2: i64 = raw[i+2] as i64; let c3: i64 = raw[i+3] as i64; let c4: i64 = raw[i+4] as i64
99 if (c2==0x6f)|(c2==0x4f) { if (c3==0x64)|(c3==0x44) { if (c4==0x79)|(c4==0x59) {
100 var j: i64 = i + 5
101 while j < raw_len { if raw[j] == (0x3e as u8) { return j + 1 } j = j + 1 }
102 return i // unterminated <body -> insert at the tag
103 } } }
104 }
105 }
106 i = i + 1
107 }
108 return 0
109}
110
111// INJECT the before-you-buy overlay into a page's HTML so the browser lays it out as a top-of-page box.
112// out = raw[0..body) + overlay-div + raw[body..end]. No price on the page -> out = an untouched copy
113// of raw (no overlay, the browser shows the page exactly as fetched). Returns the new length, NUL-term.
114// PURE (no network): the browser calls this on the fetched body right before br_layout. license-safe to gate.
115func bgw_inject(raw: *u8, raw_len: i64, prof: *i64, out: *u8, cap: i64) -> i64 {
116 let overlay: *u8 = sys_mmap(8192)
117 bgw_scan(raw, raw_len, prof, overlay, 8192)
118 if overlay[0] == (0 as u8) {
119 var c: i64 = 0
120 while c < raw_len { if c < cap - 1 { out[c] = raw[c] } c = c + 1 }
121 var en: i64 = raw_len
122 if en > cap - 1 { en = cap - 1 }
123 out[en] = 0 as u8
124 return en
125 }
126 let ins: i64 = bgw_find_body(raw, raw_len)
127 var o: i64 = 0
128 var i: i64 = 0
129 while i < ins { if o < cap - 1 { out[o] = raw[i]; o = o + 1 } i = i + 1 }
130 var k: i64 = 0
131 while overlay[k] != (0 as u8) { if o < cap - 1 { out[o] = overlay[k]; o = o + 1 } k = k + 1 }
132 var j: i64 = ins
133 while j < raw_len { if o < cap - 1 { out[o] = raw[j]; o = o + 1 } j = j + 1 }
134 out[o] = 0 as u8
135 return o
136}