code wiki / (root) / nx_gen_ui_emit_lifecycle_test_20260907.nx

nx_gen_ui_emit_lifecycle_test_20260907.nx source

↩ module page · 317 lines · 41508 B

1// nx_gen_ui_emit.nx -- EMITS the /gen companion app page (elara_ui.html) from DATA (knowledge/gen_ui.conf). 2// gen G19 (2026-08-30, operator: "we should always be emitting our uis and web pages from the first byte up -- make 3// sure /gen isnt handcrafted"). The served page was a hand-authored file since seq264; this organ now OWNS it: every 4// string, default, endpoint, cadence and theme token is a conf row, the structure lives here, and nx_gen_ui_gate 5// proves (a) two emits are byte-identical, (b) required behaviours are present, (c) a missing key REFUSES by name and 6// writes nothing, (d) the SERVED file is the EMITTED file. Output carries the NX-DERIVED stamp so no reader mistakes it 7// for authored content. ASCII source: non-ASCII glyphs are HTML entities or JS String.fromCodePoint. 8// nx_gen_ui_emit <conf> <out.html> exit: 0 emitted | 2 usage | 3 REFUSED (missing keys, named) | 4 unreadable/write-fail 9// Contract symbol: gu_emit. license_tier: ORIGINAL expect_exit: 0 10import "nx_syscalls.nx" 11 12const UE_MAXKEYS: i64 = 160 13const UE_CONF_CAP: i64 = 65536 14const UE_MISS_CAP: i64 = 4096 15const UE_MODE_FILE: i64 = 420 16const UE_EXIT_OK: i64 = 0 17const UE_EXIT_USAGE: i64 = 2 18const UE_EXIT_REFUSED: i64 = 3 19const UE_EXIT_IO: i64 = 4 20const UE_CH_PIPE: i64 = 124 21const UE_CH_HASH: i64 = 35 22const UE_CH_NL: i64 = 10 23const UE_CH_CR: i64 = 13 24const UE_CH_COMMA: i64 = 44 25 26func ue_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 27func ue_w(fd: i64, s: *u8) -> i64 { sys_write(fd, s, ue_len(s)); return 0 } 28func ue_eq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] == b[i] { if a[i] == (0 as u8) { return 1 } i = i + 1 } return 0 } 29 30// key|value rows -> parallel offset arrays. Mutates buf in place (NUL at the pipe and at end of line). 31func ue_parse(buf: *u8, n: i64, keys: *i64, vals: *i64) -> i64 { 32 var k: i64 = 0 33 var i: i64 = 0 34 while i < n { 35 let ls: i64 = i 36 var eol: i64 = i 37 var scan: i64 = 1 38 while scan == 1 { if eol >= n { scan = 0 } else { if buf[eol] == (UE_CH_NL as u8) { scan = 0 } else { eol = eol + 1 } } } 39 var e2: i64 = eol 40 if e2 > ls { if buf[e2 - 1] == (UE_CH_CR as u8) { e2 = e2 - 1 } } 41 var skip: i64 = 0 42 if e2 == ls { skip = 1 } else { if buf[ls] == (UE_CH_HASH as u8) { skip = 1 } } 43 if skip == 0 { 44 var p: i64 = ls 45 var ps: i64 = 1 46 while ps == 1 { if p >= e2 { ps = 0 } else { if buf[p] == (UE_CH_PIPE as u8) { ps = 0 } else { p = p + 1 } } } 47 if p < e2 { if k < UE_MAXKEYS { 48 buf[p] = 0 as u8 49 buf[e2] = 0 as u8 50 keys[k] = ls 51 vals[k] = p + 1 52 k = k + 1 53 } } 54 } 55 i = eol + 1 56 } 57 return k 58} 59 60// lookup; a missing key is RECORDED by name (never defaulted) so the refusal can list every offender at once. 61func ue_get(buf: *u8, keys: *i64, vals: *i64, nk: i64, key: *u8, miss: *i64, mlist: *u8) -> *u8 { 62 var i: i64 = 0 63 while i < nk { 64 if ue_eq(((buf as i64) + keys[i]) as *u8, key) == 1 { return ((buf as i64) + vals[i]) as *u8 } 65 i = i + 1 66 } 67 var o: i64 = ue_len(mlist) 68 if o + ue_len(key) + 2 < UE_MISS_CAP { 69 if o > 0 { mlist[o] = 32 as u8; o = o + 1 } 70 var j: i64 = 0 71 while key[j] != (0 as u8) { mlist[o] = key[j]; o = o + 1; j = j + 1 } 72 mlist[o] = 0 as u8 73 } 74 miss[0] = miss[0] + 1 75 return "" as *u8 76} 77 78// Release labels are plain text; links are local root-relative paths without fragments. 79func ue_text(fd: i64, s: *u8) -> i64 { 80 var i: i64 = 0 81 while s[i] != (0 as u8) { 82 let c: i64 = s[i] as i64 83 if c == 38 { ue_w(fd, "&amp;" as *u8) } else { if c == 60 { ue_w(fd, "&lt;" as *u8) } else { if c == 62 { ue_w(fd, "&gt;" as *u8) } else { if c == 34 { ue_w(fd, "&quot;" as *u8) } else { if c == 39 { ue_w(fd, "&#39;" as *u8) } else { sys_write(fd, ((s as i64) + i) as *u8, 1) } } } } } 84 i = i + 1 85 } 86 return 0 87} 88func ue_local_href(s: *u8) -> i64 { 89 if s[0] != (47 as u8) { return 0 } 90 if (s[1] == (0 as u8)) | (s[1] == (47 as u8)) { return 0 } 91 var i: i64 = 1 92 while s[i] != (0 as u8) { 93 let c: i64 = s[i] as i64 94 var allowed: i64 = 0 95 if (c >= 48) & (c <= 57) { allowed = 1 } 96 if (c >= 65) & (c <= 90) { allowed = 1 } 97 if (c >= 97) & (c <= 122) { allowed = 1 } 98 if (c == 45) | (c == 95) | (c == 47) { allowed = 1 } 99 if allowed == 0 { return 0 } 100 if c == 47 { if s[i - 1] == (47 as u8) { return 0 } } 101 i = i + 1 102 } 103 return 1 104} 105 106// <option>a</option><option>b</option> from a comma list 107func ue_options(fd: i64, csv: *u8) -> i64 { 108 var i: i64 = 0 109 var start: i64 = 0 110 var scan: i64 = 1 111 while scan == 1 { 112 let c: i64 = csv[i] as i64 113 if (c == UE_CH_COMMA) | (c == 0) { 114 if i > start { ue_w(fd, "<option>" as *u8); sys_write(fd, ((csv as i64) + start) as *u8, i - start); ue_w(fd, "</option>" as *u8) } 115 start = i + 1 116 } 117 if c == 0 { scan = 0 } else { i = i + 1 } 118 } 119 return 0 120} 121 122// THE CONTRACT: emit the whole page to fd from the parsed conf. Returns 0, or -1 when any key was missing (nothing partial 123// is ever written because the caller resolves every key BEFORE opening the output). 124func gu_emit(fd: i64, buf: *u8, keys: *i64, vals: *i64, nk: i64, miss: *i64, mlist: *u8) -> i64 { 125 let title: *u8 = ue_get(buf, keys, vals, nk, "title" as *u8, miss, mlist) 126 let name: *u8 = ue_get(buf, keys, vals, nk, "name" as *u8, miss, mlist) 127 let initial: *u8 = ue_get(buf, keys, vals, nk, "initial" as *u8, miss, mlist) 128 let pchip: *u8 = ue_get(buf, keys, vals, nk, "pchip_default" as *u8, miss, mlist) 129 let pact: *u8 = ue_get(buf, keys, vals, nk, "pact_default" as *u8, miss, mlist) 130 let placeholder: *u8 = ue_get(buf, keys, vals, nk, "placeholder" as *u8, miss, mlist) 131 let empty_h: *u8 = ue_get(buf, keys, vals, nk, "empty_headline" as *u8, miss, mlist) 132 let empty_b: *u8 = ue_get(buf, keys, vals, nk, "empty_body" as *u8, miss, mlist) 133 let tab_chat: *u8 = ue_get(buf, keys, vals, nk, "tab_chat" as *u8, miss, mlist) 134 let tab_gallery: *u8 = ue_get(buf, keys, vals, nk, "tab_gallery" as *u8, miss, mlist) 135 let tab_studio: *u8 = ue_get(buf, keys, vals, nk, "tab_studio" as *u8, miss, mlist) 136 let studio_prompt: *u8 = ue_get(buf, keys, vals, nk, "studio_prompt" as *u8, miss, mlist) 137 let width: *u8 = ue_get(buf, keys, vals, nk, "width" as *u8, miss, mlist) 138 let height: *u8 = ue_get(buf, keys, vals, nk, "height" as *u8, miss, mlist) 139 let count: *u8 = ue_get(buf, keys, vals, nk, "count" as *u8, miss, mlist) 140 let count_max: *u8 = ue_get(buf, keys, vals, nk, "count_max" as *u8, miss, mlist) 141 let steps: *u8 = ue_get(buf, keys, vals, nk, "steps" as *u8, miss, mlist) 142 let cfg: *u8 = ue_get(buf, keys, vals, nk, "cfg" as *u8, miss, mlist) 143 let seed: *u8 = ue_get(buf, keys, vals, nk, "seed" as *u8, miss, mlist) 144 let samplers: *u8 = ue_get(buf, keys, vals, nk, "samplers" as *u8, miss, mlist) 145 let hint_speed: *u8 = ue_get(buf, keys, vals, nk, "hint_speed" as *u8, miss, mlist) 146 let hint_neg: *u8 = ue_get(buf, keys, vals, nk, "hint_neg" as *u8, miss, mlist) 147 let api_base: *u8 = ue_get(buf, keys, vals, nk, "api_base" as *u8, miss, mlist) 148 let login_path: *u8 = ue_get(buf, keys, vals, nk, "login_path" as *u8, miss, mlist) 149 let gallery_href: *u8 = ue_get(buf, keys, vals, nk, "gallery_href" as *u8, miss, mlist) 150 let release_href: *u8 = ue_get(buf, keys, vals, nk, "release_href" as *u8, miss, mlist) 151 let release_label: *u8 = ue_get(buf, keys, vals, nk, "release_label" as *u8, miss, mlist) 152 let gallery_text: *u8 = ue_get(buf, keys, vals, nk, "gallery_link_text" as *u8, miss, mlist) 153 let poll_ms: *u8 = ue_get(buf, keys, vals, nk, "poll_ms" as *u8, miss, mlist) 154 let timeout_ms: *u8 = ue_get(buf, keys, vals, nk, "timeout_ms" as *u8, miss, mlist) 155 let state_ms: *u8 = ue_get(buf, keys, vals, nk, "state_ms" as *u8, miss, mlist) 156 let storage_key: *u8 = ue_get(buf, keys, vals, nk, "storage_key" as *u8, miss, mlist) 157 let chat_keep: *u8 = ue_get(buf, keys, vals, nk, "chat_keep" as *u8, miss, mlist) 158 let recent_gallery: *u8 = ue_get(buf, keys, vals, nk, "recent_gallery" as *u8, miss, mlist) 159 let recent_avatar: *u8 = ue_get(buf, keys, vals, nk, "recent_avatar" as *u8, miss, mlist) 160 let max_input_h: *u8 = ue_get(buf, keys, vals, nk, "max_input_h" as *u8, miss, mlist) 161 let lost_text: *u8 = ue_get(buf, keys, vals, nk, "lost_text" as *u8, miss, mlist) 162 let noanswer: *u8 = ue_get(buf, keys, vals, nk, "noanswer_prefix" as *u8, miss, mlist) 163 let chat_poll_ms: *u8 = ue_get(buf, keys, vals, nk, "chat_poll_ms" as *u8, miss, mlist) 164 let chat_timeout_ms: *u8 = ue_get(buf, keys, vals, nk, "chat_timeout_ms" as *u8, miss, mlist) 165 // CC4 the swipe loop: labels for regenerate / previous / next / edit and the edit prompt, all data 166 let swipe_prev: *u8 = ue_get(buf, keys, vals, nk, "swipe_prev" as *u8, miss, mlist) 167 let swipe_next: *u8 = ue_get(buf, keys, vals, nk, "swipe_next" as *u8, miss, mlist) 168 let regen_label: *u8 = ue_get(buf, keys, vals, nk, "regen_label" as *u8, miss, mlist) 169 let edit_label: *u8 = ue_get(buf, keys, vals, nk, "edit_label" as *u8, miss, mlist) 170 let edit_prompt: *u8 = ue_get(buf, keys, vals, nk, "edit_prompt" as *u8, miss, mlist) 171 // the conversation album (2026-08-30, operator: ONE /gallery; /gen keeps only her photos by conversation day) 172 let album_empty: *u8 = ue_get(buf, keys, vals, nk, "album_empty" as *u8, miss, mlist) 173 let c_bg: *u8 = ue_get(buf, keys, vals, nk, "c_bg" as *u8, miss, mlist) 174 let c_bg2: *u8 = ue_get(buf, keys, vals, nk, "c_bg2" as *u8, miss, mlist) 175 let c_surf: *u8 = ue_get(buf, keys, vals, nk, "c_surf" as *u8, miss, mlist) 176 let c_surf2: *u8 = ue_get(buf, keys, vals, nk, "c_surf2" as *u8, miss, mlist) 177 let c_line: *u8 = ue_get(buf, keys, vals, nk, "c_line" as *u8, miss, mlist) 178 let c_ink: *u8 = ue_get(buf, keys, vals, nk, "c_ink" as *u8, miss, mlist) 179 let c_mut: *u8 = ue_get(buf, keys, vals, nk, "c_mut" as *u8, miss, mlist) 180 let c_dim: *u8 = ue_get(buf, keys, vals, nk, "c_dim" as *u8, miss, mlist) 181 let c_rose: *u8 = ue_get(buf, keys, vals, nk, "c_rose" as *u8, miss, mlist) 182 let c_rose2: *u8 = ue_get(buf, keys, vals, nk, "c_rose2" as *u8, miss, mlist) 183 let c_rose_d: *u8 = ue_get(buf, keys, vals, nk, "c_rose_d" as *u8, miss, mlist) 184 let c_you: *u8 = ue_get(buf, keys, vals, nk, "c_you" as *u8, miss, mlist) 185 let c_you_ink: *u8 = ue_get(buf, keys, vals, nk, "c_you_ink" as *u8, miss, mlist) 186 let c_ok: *u8 = ue_get(buf, keys, vals, nk, "c_ok" as *u8, miss, mlist) 187 let c_warn: *u8 = ue_get(buf, keys, vals, nk, "c_warn" as *u8, miss, mlist) 188 let c_bad: *u8 = ue_get(buf, keys, vals, nk, "c_bad" as *u8, miss, mlist) 189 if miss[0] > 0 { return 0 - 1 } 190 if (ue_local_href(release_href) == 0) | (release_label[0] == (0 as u8)) { return 0 - 2 } 191 if fd < 0 { return 0 } 192 193 // ---- head + tokens ---- 194 ue_w(fd, "<!doctype html>\n<!-- NX-DERIVED: regenerated artefact, not authored content -- emitted by nx_gen_ui_emit from knowledge/gen_ui.conf; edit the conf, never this file -->\n<html lang=en>\n<head>\n<meta charset=utf-8>\n<meta name=viewport content=\"width=device-width,initial-scale=1,viewport-fit=cover\">\n<meta name=theme-color content=\"" as *u8); ue_w(fd, c_bg); ue_w(fd, "\">\n<title>" as *u8); ue_w(fd, title); ue_w(fd, "</title>\n<style>\n:root{--bg:" as *u8) 195 ue_w(fd, c_bg); ue_w(fd, ";--bg2:" as *u8); ue_w(fd, c_bg2); ue_w(fd, ";--surf:" as *u8); ue_w(fd, c_surf); ue_w(fd, ";--surf2:" as *u8); ue_w(fd, c_surf2) 196 ue_w(fd, ";--line:" as *u8); ue_w(fd, c_line); ue_w(fd, ";--ink:" as *u8); ue_w(fd, c_ink); ue_w(fd, ";--mut:" as *u8); ue_w(fd, c_mut); ue_w(fd, ";--dim:" as *u8); ue_w(fd, c_dim) 197 ue_w(fd, ";--rose:" as *u8); ue_w(fd, c_rose); ue_w(fd, ";--rose2:" as *u8); ue_w(fd, c_rose2); ue_w(fd, ";--rose-d:" as *u8); ue_w(fd, c_rose_d) 198 ue_w(fd, ";--you:" as *u8); ue_w(fd, c_you); ue_w(fd, ";--you-ink:" as *u8); ue_w(fd, c_you_ink); ue_w(fd, ";--ok:" as *u8); ue_w(fd, c_ok); ue_w(fd, ";--warn:" as *u8); ue_w(fd, c_warn); ue_w(fd, ";--bad:" as *u8); ue_w(fd, c_bad) 199 ue_w(fd, ";--sh:0 8px 30px rgba(0,0,0,.45)}\n" as *u8) 200 // ---- static structure CSS (the design lives here; the tokens above are the data) ---- 201 ue_w(fd, "*{box-sizing:border-box}html,body{height:100%}body{margin:0;font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif;color:var(--ink);background:radial-gradient(1200px 600px at 80% -10%,rgba(240,119,158,.10),transparent 60%),radial-gradient(900px 500px at -10% 110%,rgba(84,120,214,.10),transparent 55%),var(--bg);display:flex;flex-direction:column;height:100dvh;overflow:hidden;-webkit-font-smoothing:antialiased}\na{color:var(--rose2);text-decoration:none}button{font:inherit;cursor:pointer;border:0;background:none;color:inherit}\n" as *u8) 202 ue_w(fd, "#pres{flex:0 0 auto;display:flex;align-items:center;gap:12px;padding:12px 16px;background:linear-gradient(180deg,rgba(21,26,39,.96),rgba(16,20,31,.82));border-bottom:1px solid var(--line);backdrop-filter:blur(10px);box-shadow:0 4px 20px rgba(0,0,0,.25);cursor:pointer;user-select:none}\n#av{flex:0 0 auto;width:46px;height:46px;border-radius:50%;background:linear-gradient(135deg,var(--rose),#c76bd6);background-size:cover;background-position:center top;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:1.2rem;color:#fff;box-shadow:0 0 0 2px rgba(240,119,158,.35),var(--sh);position:relative}\n#av::after{content:\"\";position:absolute;right:-1px;bottom:-1px;width:12px;height:12px;border-radius:50%;background:var(--ok);border:2.5px solid var(--bg2);box-shadow:0 0 8px var(--ok)}\n" as *u8) 203 ue_w(fd, "#pmeta{flex:1;min-width:0}#pname{font-weight:650;font-size:1.02rem;letter-spacing:.2px;display:flex;align-items:center;gap:8px}#pchip{font-size:.7rem;font-weight:600;padding:2px 9px;border-radius:999px;background:rgba(240,119,158,.16);color:var(--rose2);border:1px solid rgba(240,119,158,.28);white-space:nowrap}#pact{font-size:.8rem;color:var(--mut);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#pcar{color:var(--dim);font-size:1.1rem;transition:transform .25s;flex:0 0 auto}#pres.open #pcar{transform:rotate(180deg)}\n#pdet{flex:0 0 auto;overflow:hidden;max-height:0;transition:max-height .3s ease;background:linear-gradient(180deg,rgba(16,20,31,.9),rgba(10,13,20,.9));border-bottom:1px solid var(--line)}#pres.open + #pdet{max-height:220px}#pdet .in{padding:12px 16px 16px}\n" as *u8) 204 ue_w(fd, ".meter{margin:9px 0}.meter .lab{display:flex;justify-content:space-between;font-size:.72rem;color:var(--mut);margin-bottom:4px}.meter .lab b{color:var(--ink);font-weight:600}.bar{height:7px;border-radius:999px;background:#0c1017;overflow:hidden;border:1px solid var(--line)}.bar>i{display:block;height:100%;border-radius:999px;transition:width .6s cubic-bezier(.2,.8,.2,1)}.f-bond>i{background:linear-gradient(90deg,var(--rose-d),var(--rose))}.f-warm>i{background:linear-gradient(90deg,#8a4a2a,#ffb27a)}.f-energy>i{background:linear-gradient(90deg,#1f6d57,var(--ok))}.f-calm>i{background:linear-gradient(90deg,#3a5fb0,#8fb6ff)}\n" as *u8) 205 ue_w(fd, "main{flex:1 1 auto;position:relative;overflow:hidden}.tab{position:absolute;inset:0;display:flex;flex-direction:column;overflow:hidden}.tab[hidden]{display:none}\n#log{flex:1 1 auto;overflow-y:auto;padding:16px 14px 8px;display:flex;flex-direction:column;gap:10px;scroll-behavior:smooth}.empty{margin:auto;text-align:center;color:var(--dim);max-width:280px;padding:24px}.empty .big{font-size:2.2rem;margin-bottom:8px}\n.msg{max-width:80%;padding:10px 14px;border-radius:20px;line-height:1.42;font-size:.96rem;word-wrap:break-word;white-space:pre-wrap;animation:pop .22s ease}@keyframes pop{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}\n.msg.elara{align-self:flex-start;background:linear-gradient(160deg,#2a1c28,#20222f);border:1px solid rgba(240,119,158,.22);border-bottom-left-radius:7px;color:#f6e9ef}.msg.you{align-self:flex-end;background:linear-gradient(160deg,var(--you),#1c2740);color:var(--you-ink);border-bottom-right-radius:7px}\n" as *u8) 206 ue_w(fd, ".msg .imgs{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.msg .imgs img{width:150px;max-width:44vw;border-radius:13px;cursor:zoom-in;background:#0c1017;box-shadow:var(--sh)}.msg.only-img{background:none!important;border:0!important;padding:0}\n.typing{align-self:flex-start;padding:12px 16px;border-radius:20px;border-bottom-left-radius:7px;background:#20222f;border:1px solid rgba(240,119,158,.18);display:flex;gap:5px}.typing i{width:7px;height:7px;border-radius:50%;background:var(--rose2);opacity:.5;animation:bl 1s infinite}.typing i:nth-child(2){animation-delay:.15s}.typing i:nth-child(3){animation-delay:.3s}@keyframes bl{0%,100%{opacity:.35;transform:translateY(0)}50%{opacity:1;transform:translateY(-3px)}}\n.msg .tools{display:flex;gap:6px;margin-top:7px;font-size:.72rem;align-items:center}.msg .tools button{padding:2px 9px;border-radius:999px;background:rgba(255,255,255,.06);border:1px solid var(--line);color:var(--mut)}.msg .tools button:active{transform:scale(.95)}.msg .tools span{color:var(--dim)}\n.dayh{grid-column:1/-1;font-size:.72rem;color:var(--mut);letter-spacing:.4px;text-transform:uppercase;margin:10px 2px 0}\n" as *u8) 207 ue_w(fd, "#comp{flex:0 0 auto;display:flex;align-items:flex-end;gap:9px;padding:10px 12px calc(10px + env(safe-area-inset-bottom));border-top:1px solid var(--line);background:linear-gradient(180deg,rgba(16,20,31,.7),rgba(21,26,39,.95))}#ci{flex:1;min-height:44px;max-height:" as *u8); ue_w(fd, max_input_h); ue_w(fd, "px;resize:none;padding:11px 15px;border-radius:22px;background:var(--surf2);border:1px solid var(--line);color:var(--ink);font-size:.98rem;line-height:1.35;outline:none}#ci:focus{border-color:rgba(240,119,158,.5)}#ci::placeholder{color:var(--dim)}\n#send{flex:0 0 auto;width:44px;height:44px;border-radius:50%;background:linear-gradient(135deg,var(--rose),#d15fa0);color:#fff;font-size:1.2rem;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 14px rgba(240,119,158,.4);transition:transform .12s}#send:active{transform:scale(.9)}#send[disabled]{opacity:.5;box-shadow:none}\n" as *u8) 208 ue_w(fd, "#gwrap{flex:1;overflow-y:auto;padding:12px}#grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(108px,1fr));gap:8px}#grid img{width:100%;aspect-ratio:3/4;object-fit:cover;border-radius:12px;background:var(--surf);cursor:zoom-in;box-shadow:0 3px 12px rgba(0,0,0,.35);transition:transform .15s}#grid img:active{transform:scale(.97)}.ghint{color:var(--dim);text-align:center;padding:40px 20px;font-size:.9rem}\n#stwrap{flex:1;overflow-y:auto;padding:16px 16px calc(20px + env(safe-area-inset-bottom))}.fld{margin:12px 0}.fld label{display:block;font-size:.74rem;color:var(--mut);margin-bottom:5px;text-transform:uppercase;letter-spacing:.4px}.fld textarea,.fld input,.fld select{width:100%;padding:10px 12px;border-radius:10px;background:var(--surf2);border:1px solid var(--line);color:var(--ink);font-size:.95rem;outline:none}.fld textarea:focus,.fld input:focus,.fld select:focus{border-color:rgba(240,119,158,.5)}.grid3{display:grid;grid-template-columns:repeat(3,1fr);gap:9px}\n" as *u8) 209 ue_w(fd, "#go{width:100%;margin-top:16px;padding:14px;border-radius:12px;font-size:1rem;font-weight:650;background:linear-gradient(135deg,var(--rose),#c76bd6);color:#fff;box-shadow:0 6px 20px rgba(240,119,158,.35)}#go:active{transform:scale(.99)}#go[disabled]{opacity:.6}#sst{font-size:.82rem;color:var(--mut);min-height:1.2em;margin-top:10px;text-align:center}.hint{font-size:.76rem;color:var(--dim);margin-top:6px}\n#release-nav{flex:0 0 auto;padding:.4em 1em;color:var(--mut);background:var(--bg2);border-top:1px solid var(--line);font-size:.82rem;overflow-wrap:anywhere}#release-nav a{color:var(--ink);text-decoration:underline}\n#bar{flex:0 0 auto;display:flex;border-top:1px solid var(--line);background:linear-gradient(180deg,rgba(16,20,31,.9),rgba(10,13,20,.98));padding-bottom:env(safe-area-inset-bottom)}#bar button{flex:1;padding:9px 0 8px;display:flex;flex-direction:column;align-items:center;gap:2px;color:var(--dim);font-size:.66rem;font-weight:600;letter-spacing:.3px;transition:color .15s}#bar button .ic{font-size:1.25rem;line-height:1}#bar button.on{color:var(--rose2)}#bar button.on .ic{filter:drop-shadow(0 0 8px rgba(240,119,158,.6))}\n" as *u8) 210 ue_w(fd, "#lb{position:fixed;inset:0;background:rgba(4,6,11,.94);display:none;align-items:center;justify-content:center;z-index:50;flex-direction:column;gap:14px;padding:20px;backdrop-filter:blur(4px)}#lb.on{display:flex}#lb img{max-width:96vw;max-height:80vh;border-radius:14px;box-shadow:var(--sh)}#lb .row{display:flex;gap:10px}#lb .row a,#lb .row button{padding:9px 18px;border-radius:10px;background:var(--surf2);border:1px solid var(--line);color:var(--ink);font-size:.9rem}\n#toast{position:fixed;left:50%;bottom:78px;transform:translateX(-50%) translateY(20px);opacity:0;background:var(--surf2);border:1px solid var(--line);color:var(--ink);padding:10px 18px;border-radius:999px;font-size:.85rem;box-shadow:var(--sh);transition:.3s;z-index:60;pointer-events:none;max-width:90vw}#toast.on{opacity:1;transform:translateX(-50%) translateY(0)}\n@media(min-width:620px){.msg{max-width:66%}#log{padding:20px 24px 8px}#grid{grid-template-columns:repeat(auto-fill,minmax(130px,1fr))}}\n</style>\n</head>\n<body>\n" as *u8) 211 212 // ---- presence header + meters ---- 213 ue_w(fd, "<header id=pres onclick=\"togglePres()\"><div id=av>" as *u8); ue_w(fd, initial); ue_w(fd, "</div><div id=pmeta><div id=pname>" as *u8); ue_w(fd, name) 214 ue_w(fd, " <span id=pchip>" as *u8); ue_w(fd, pchip); ue_w(fd, "</span></div><div id=pact>" as *u8); ue_w(fd, pact); ue_w(fd, "</div></div><div id=pcar>&#8964;</div></header>\n" as *u8) 215 ue_w(fd, "<div id=pdet><div class=in><div class=\"meter f-bond\"><div class=lab><span>Bond</span><b id=m-bond>&mdash;</b></div><div class=bar><i id=b-bond style=width:0></i></div></div><div class=\"meter f-warm\"><div class=lab><span>Warmth</span><b id=m-warm>&mdash;</b></div><div class=bar><i id=b-warm style=width:0></i></div></div><div class=\"meter f-energy\"><div class=lab><span>Energy</span><b id=m-energy>&mdash;</b></div><div class=bar><i id=b-energy style=width:0></i></div></div><div class=\"meter f-calm\"><div class=lab><span>Calm</span><b id=m-calm>&mdash;</b></div><div class=bar><i id=b-calm style=width:0></i></div></div></div></div>\n" as *u8) 216 217 // ---- tabs ---- 218 ue_w(fd, "<main>\n<section id=t-chat class=tab><div id=log></div><div id=comp><textarea id=ci rows=1 placeholder=\"" as *u8); ue_w(fd, placeholder); ue_w(fd, "\"></textarea><button id=send onclick=send()>&#10148;</button></div></section>\n" as *u8) 219 ue_w(fd, "<section id=t-gallery class=tab hidden><div id=gwrap><div id=grid></div><div id=ghint class=ghint></div></div></section>\n" as *u8) 220 ue_w(fd, "<section id=t-studio class=tab hidden><div id=stwrap><div class=fld><label>Prompt</label><textarea id=p rows=3>" as *u8); ue_w(fd, studio_prompt); ue_w(fd, "</textarea></div><div class=grid3><div class=fld><label>Width</label><input id=w type=number value=" as *u8); ue_w(fd, width) 221 ue_w(fd, "></div><div class=fld><label>Height</label><input id=h type=number value=" as *u8); ue_w(fd, height); ue_w(fd, "></div><div class=fld><label>Count</label><input id=cn type=number value=" as *u8); ue_w(fd, count); ue_w(fd, " min=1 max=" as *u8); ue_w(fd, count_max) 222 ue_w(fd, "></div></div><div class=grid3><div class=fld><label>Steps</label><input id=stp type=number value=\"" as *u8); ue_w(fd, steps); ue_w(fd, "\" placeholder=default></div><div class=fld><label>CFG</label><input id=cfg value=" as *u8); ue_w(fd, cfg); ue_w(fd, "></div><div class=fld><label>Seed</label><input id=sd type=number value=" as *u8); ue_w(fd, seed) 223 ue_w(fd, "></div></div><div class=fld><label>Sampler</label><select id=sm>" as *u8); ue_options(fd, samplers); ue_w(fd, "</select></div><button id=go onclick=gen()>Generate</button><div id=sst>" as *u8); ue_w(fd, hint_speed); ue_w(fd, "</div><div class=hint>" as *u8); ue_w(fd, hint_neg); ue_w(fd, "</div></div></section>\n</main>\n" as *u8) 224 ue_w(fd, "<div id=release-nav><a href=\"" as *u8); ue_text(fd, release_href); ue_w(fd, "#release-summary\">" as *u8); ue_text(fd, release_label); ue_w(fd, "</a> &middot; <a href=\"" as *u8); ue_text(fd, release_href); ue_w(fd, "#worklog\">Release history and work log</a></div>\n" as *u8) 225 ue_w(fd, "<nav id=bar><button data-tab=chat class=on onclick=\"tab('chat')\"><span class=ic>&#x1F4AC;</span>" as *u8); ue_w(fd, tab_chat); ue_w(fd, "</button><button data-tab=gallery onclick=\"tab('gallery')\"><span class=ic>&#x1F5BC;&#xFE0F;</span>" as *u8); ue_w(fd, tab_gallery) 226 ue_w(fd, "</button><button data-tab=studio onclick=\"tab('studio')\"><span class=ic>&#x2728;</span>" as *u8); ue_w(fd, tab_studio); ue_w(fd, "</button></nav>\n" as *u8) 227 ue_w(fd, "<div id=lb onclick=\"if(event.target.id=='lb')closeLb()\"><img id=lbimg src=\"\"><div class=row><a id=lbdl download>Download</a><button onclick=closeLb()>Close</button></div></div>\n<div id=toast></div>\n" as *u8) 228 229 // ---- script (presentation + fetch plumbing; the daemon is the authority for every state it renders) ---- 230 ue_w(fd, "<script>\nvar API='" as *u8); ue_w(fd, api_base); ue_w(fd, "';var LOGIN='" as *u8); ue_w(fd, login_path); ue_w(fd, "';var GAL='" as *u8); ue_w(fd, gallery_href); ue_w(fd, "';var GALTXT='" as *u8); ue_w(fd, gallery_text) 231 ue_w(fd, "';var POLL_MS=" as *u8); ue_w(fd, poll_ms); ue_w(fd, ";var TIMEOUT_MS=" as *u8); ue_w(fd, timeout_ms); ue_w(fd, ";var STATE_MS=" as *u8); ue_w(fd, state_ms); ue_w(fd, ";var SKEY='" as *u8); ue_w(fd, storage_key) 232 ue_w(fd, "';var KEEP=" as *u8); ue_w(fd, chat_keep); ue_w(fd, ";var NGAL=" as *u8); ue_w(fd, recent_gallery); ue_w(fd, ";var NAV=" as *u8); ue_w(fd, recent_avatar); ue_w(fd, ";var MAXH=" as *u8); ue_w(fd, max_input_h) 233 ue_w(fd, ";var LOST='" as *u8); ue_w(fd, lost_text); ue_w(fd, "';var NOANS='" as *u8); ue_w(fd, noanswer); ue_w(fd, "';var NAME='" as *u8); ue_w(fd, name); ue_w(fd, "';var CHAT_POLL_MS=" as *u8); ue_w(fd, chat_poll_ms); ue_w(fd, ";var CHAT_TIMEOUT_MS=" as *u8); ue_w(fd, chat_timeout_ms); ue_w(fd, ";\n" as *u8) 234 ue_w(fd, "var SWIPE_PREV='" as *u8); ue_w(fd, swipe_prev); ue_w(fd, "';var SWIPE_NEXT='" as *u8); ue_w(fd, swipe_next); ue_w(fd, "';var REGEN_LABEL='" as *u8); ue_w(fd, regen_label); ue_w(fd, "';var EDIT_LABEL='" as *u8); ue_w(fd, edit_label); ue_w(fd, "';var EDIT_PROMPT='" as *u8); ue_w(fd, edit_prompt); ue_w(fd, "';var ALBUM_EMPTY='" as *u8); ue_w(fd, album_empty); ue_w(fd, "';\n" as *u8) 235 ue_w(fd, "var $=function(i){return document.getElementById(i)};function cp(c){return String.fromCodePoint(c)}\nfunction esc(s){var d=document.createElement('div');d.textContent=s;return d.innerHTML}\nfunction toast(t){var e=$('toast');e.textContent=t;e.classList.add('on');clearTimeout(e._t);e._t=setTimeout(function(){e.classList.remove('on')},2200)}\n" as *u8) 236 ue_w(fd, "var LIFE={active:false,epoch:0,timer:null,reads:new Set(),waiters:[]};\nfunction lifeAbort(){return new DOMException('Page suspended','AbortError')}\nfunction lifeCurrent(epoch){return LIFE.active&&epoch===LIFE.epoch}\nfunction lifeWait(){if(LIFE.active)return Promise.resolve();return new Promise(function(resolve){LIFE.waiters.push(resolve)})}\nfunction lifePause(){if(!LIFE.active)return;LIFE.active=false;LIFE.epoch++;if(LIFE.timer!==null){clearTimeout(LIFE.timer);LIFE.timer=null}LIFE.reads.forEach(function(c){c.abort()})}\nasync function stateTick(epoch){await loadState();if(lifeCurrent(epoch)&&LIFE.timer===null)LIFE.timer=setTimeout(function(){LIFE.timer=null;stateTick(epoch)},STATE_MS)}\nfunction lifeResume(){if(document.hidden||LIFE.active)return;LIFE.active=true;var epoch=++LIFE.epoch;var pending=LIFE.waiters.splice(0);pending.forEach(function(resolve){resolve()});stateTick(epoch);loadAvatar();if(!$('t-gallery').hidden)loadGallery()}\ndocument.addEventListener('visibilitychange',function(){if(document.hidden)lifePause();else lifeResume()});\nwindow.addEventListener('pagehide',lifePause);window.addEventListener('pageshow',lifeResume);\nasync function jread(path,resume){for(;;){if(!LIFE.active){if(!resume)throw lifeAbort();await lifeWait()}var epoch=LIFE.epoch;var controller=new AbortController();LIFE.reads.add(controller);try{var r=await fetch(API+path,{signal:controller.signal});if(!lifeCurrent(epoch))throw lifeAbort();if(r.status==401){location=LOGIN;throw 0}var data=await r.json();if(!lifeCurrent(epoch))throw lifeAbort();return data}catch(e){if(!(resume&&e&&e.name==='AbortError'&&!lifeCurrent(epoch)))throw e}finally{LIFE.reads.delete(controller)}}}\nasync function jpost(path,body){var r=await fetch(API+path,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});if(r.status==401){location=LOGIN;throw 0}return r}\n" as *u8) 237 ue_w(fd, "function tab(name){['chat','gallery','studio'].forEach(function(n){$('t-'+n).hidden=(n!=name)});document.querySelectorAll('#bar button').forEach(function(b){b.classList.toggle('on',b.dataset.tab==name)});if(name=='gallery')loadGallery();if(name=='chat')scrollLog()}\nfunction togglePres(){$('pres').classList.toggle('open')}\n" as *u8) 238 ue_w(fd, "var MOODICON={'deeply in love':cp(0x1F497),'warm and affectionate':cp(0x1F970),'giddy and playful':cp(0x1F604),'calm and content':cp(0x1F60C),'hurt and guarded':cp(0x1F97A)};\nfunction setBar(id,v){$('b-'+id).style.width=Math.max(0,Math.min(100,v))+'%';$('m-'+id).textContent=Math.round(v)}\n" as *u8) 239 ue_w(fd, "async function loadState(){var epoch=LIFE.epoch;try{var j=await jread('/api/state');if(!lifeCurrent(epoch))return;var mi=MOODICON[j.mood]||cp(0x1F4AB);$('pchip').textContent=mi+' '+j.mood;$('pact').textContent=j.activity;setBar('bond',j.affection);setBar('warm',j.oxytocin);setBar('energy',j.dopamine);setBar('calm',100-j.cortisol)}catch(e){}}\n" as *u8) 240 ue_w(fd, "async function loadAvatar(){var epoch=LIFE.epoch;try{var j=await jread('/api/recent?n='+NAV);if(!lifeCurrent(epoch))return;if(j&&j.cids&&j.cids.length){$('av').style.backgroundImage='url('+API+'/img/'+j.cids[0]+')';$('av').textContent=''}}catch(e){}}\n" as *u8) 241 ue_w(fd, "var CHAT=[];try{CHAT=JSON.parse(localStorage.getItem(SKEY)||'[]');if(!Array.isArray(CHAT))CHAT=[];CHAT=CHAT.slice(-KEEP)}catch(e){CHAT=[]}\nfunction saveChat(){if(CHAT.length>KEEP)CHAT.splice(0,CHAT.length-KEEP);try{localStorage.setItem(SKEY,JSON.stringify(CHAT))}catch(e){}}\nfunction scrollLog(){var l=$('log');l.scrollTop=l.scrollHeight}\n" as *u8) 242 // CC4: the LAST Elara bubble carries the swipe toolbar (prev k/n next, regenerate, edit); every other bubble is plain 243 ue_w(fd, "function bubble(m){var d=document.createElement('div');var onlyImg=(!m.text&&m.images&&m.images.length);d.className='msg '+(m.role=='you'?'you':'elara')+(onlyImg?' only-img':'');var html=m.text?esc(m.text):'';if(m.images&&m.images.length){html+='<div class=imgs>'+m.images.map(function(c){return '<img src=\"'+API+'/img/'+c+'\" data-c=\"'+c+'\" onclick=\"openLb(this.dataset.c)\">'}).join('')+'</div>'}if(m.role=='elara'&&m===lastElara()){var n=(m.alts&&m.alts.length)||1;var k=(m.sel||0)+1;html+='<div class=tools>'+(n>1?'<button onclick=\"swipe(-1)\">'+SWIPE_PREV+'</button><span>'+k+'/'+n+'</span><button onclick=\"swipe(1)\">'+SWIPE_NEXT+'</button>':'')+'<button onclick=\"regen()\">'+REGEN_LABEL+'</button><button onclick=\"editLast()\">'+EDIT_LABEL+'</button></div>'}d.innerHTML=html;return d}\n" as *u8) 244 ue_w(fd, "function lastElara(){for(var i=CHAT.length-1;i>=0;i--){if(CHAT[i].role=='elara')return CHAT[i]}return null}\n" as *u8) 245 ue_w(fd, "function renderChat(){var l=$('log');l.innerHTML='';if(!CHAT.length){l.innerHTML='<div class=empty><div class=big>'+cp(0x1F497)+'</div>" as *u8); ue_w(fd, empty_h); ue_w(fd, "<br>" as *u8); ue_w(fd, empty_b); ue_w(fd, "</div>';return}CHAT.forEach(function(m){l.appendChild(bubble(m))});scrollLog()}\n" as *u8) 246 ue_w(fd, "function addMsg(m){CHAT.push(m);saveChat();if(!$('t-chat').hidden){renderChat()}}\nvar typingEl=null;function showTyping(){typingEl=document.createElement('div');typingEl.className='typing';typingEl.innerHTML='<i></i><i></i><i></i>';$('log').appendChild(typingEl);scrollLog()}function hideTyping(){if(typingEl){typingEl.remove();typingEl=null}}\n" as *u8) 247 // ONE job-poll loop for chat and regenerate (the async lane: submit returns a job, /api/batch/<job> reads pending or the reply) 248 ue_w(fd, "async function awaitJob(js){if(!js.job){throw 1}var j=null;var t0=Date.now();while(!j){await new Promise(function(rs){setTimeout(rs,CHAT_POLL_MS)});var pr=await jread('/api/batch/'+js.job,true);if(pr.done===0){if(Date.now()-t0>CHAT_TIMEOUT_MS){j={error:'no reply inside the chat budget'}}}else{j=pr}}return j}\nfunction replyText(j){return ((j.choices&&j.choices[0]&&j.choices[0].message&&j.choices[0].message.content)||'').trim()}\n" as *u8) 249 ue_w(fd, "async function send(){var el=$('ci');var t=el.value.trim();if(!t)return;el.value='';el.style.height='auto';$('send').disabled=true;addMsg({role:'you',text:t});showTyping();try{var j=await awaitJob(await (await jpost('/api/chat_async',{message:t})).json());hideTyping();var content=replyText(j);if(!content&&j.error){content=NOANS+' '+j.error+')'}var images=j.images||[];addMsg({role:'elara',text:content,images:images,alts:[content],sel:0});if(images.length){loadAvatar();toast(NAME+' sent you '+images.length+' photo'+(images.length>1?'s':''))}loadState()}catch(e){hideTyping();if(e!==0)addMsg({role:'elara',text:LOST})}$('send').disabled=false;$('ci').focus()}\n" as *u8) 250 // CC4 the swipe loop: regenerate appends a sibling (never replaces the log), swipe selects one, edit is additive -- the daemon owns the truth 251 ue_w(fd, "async function regen(){var m=lastElara();if(!m)return;$('send').disabled=true;showTyping();try{var j=await awaitJob(await (await jpost('/api/chat_regen_async',{})).json());hideTyping();var content=replyText(j);if(!content){toast(NOANS+' '+(j.error||'empty reply')+')')}else{m.alts=m.alts||[m.text];m.alts.push(content);m.sel=m.alts.length-1;m.text=content;m.images=j.images||[];saveChat();renderChat()}loadState()}catch(e){hideTyping();if(e!==0)toast(LOST)}$('send').disabled=false}\n" as *u8) 252 ue_w(fd, "async function swipe(d){var m=lastElara();if(!m||!m.alts)return;var k=(m.sel||0)+d;if(k<0||k>=m.alts.length)return;try{var r=await (await jpost('/api/chat_select',{index:k})).json();if(r.error){toast(r.error);return}m.sel=k;m.text=m.alts[k];saveChat();renderChat()}catch(e){if(e!==0)toast(LOST)}}\n" as *u8) 253 ue_w(fd, "async function editLast(){var m=lastElara();if(!m)return;var t=prompt(EDIT_PROMPT,m.text);if(t===null)return;t=t.trim();if(!t)return;try{var r=await (await jpost('/api/chat_edit',{text:t})).json();if(r.error){toast(r.error);return}m.alts=m.alts||[m.text];m.alts[m.sel||0]=t;m.text=t;saveChat();renderChat()}catch(e){if(e!==0)toast(LOST)}}\n" as *u8) 254 ue_w(fd, "$('ci').addEventListener('keydown',function(e){if(e.key=='Enter'&&!e.shiftKey){e.preventDefault();send()}});$('ci').addEventListener('input',function(){this.style.height='auto';this.style.height=Math.min(MAXH,this.scrollHeight)+'px'});\n" as *u8) 255 // THE CONVERSATION ALBUM replaces the recent-grid (one /gallery estate-wide): her photos grouped by day, newest first, 256 // with the family gallery linked prominently -- the daemon owns the grouping (/api/album). 257 ue_w(fd, "async function loadGallery(){var epoch=LIFE.epoch;try{var j=await jread('/api/album');if(!lifeCurrent(epoch))return;var g=$('grid'),h=$('ghint');g.innerHTML='';var link='<a href=\"'+GAL+'\">'+GALTXT+'</a>';if(!j.days||!j.days.length){h.innerHTML=ALBUM_EMPTY+' '+link;return}h.innerHTML=link;j.days.forEach(function(d){var hd=document.createElement('div');hd.className='dayh';hd.textContent=d.day;g.appendChild(hd);d.cids.forEach(function(c){var im=document.createElement('img');im.src=API+'/img/'+c;im.loading='lazy';im.onclick=function(){openLb(c)};g.appendChild(im)})})}catch(e){}}\n" as *u8) 258 ue_w(fd, "async function gen(){var body={prompt:$('p').value,negative:'',width:+$('w').value,height:+$('h').value,steps:+$('stp').value,cfg:$('cfg').value,seed:+$('sd').value,count:+$('cn').value,sampler:$('sm').value};if(!body.prompt.trim()){$('sst').textContent='Enter a prompt.';return}$('go').disabled=true;$('sst').textContent='Starting batch of '+body.count+'...';try{var jb=await (await jpost('/api/batch',body)).json();if(!jb.job){$('sst').textContent='Error: '+(jb.error||'no job');$('go').disabled=false;return}var t0=Date.now();var poll=async function(){try{var r=await jread('/api/batch/'+jb.job,true);if(r.cids){$('sst').textContent=r.count+' image'+(r.count>1?'s':'')+' done in '+((Date.now()-t0)/1000).toFixed(0)+'s.';loadAvatar();toast('Batch of '+r.count+' -> Gallery');tab('gallery');$('go').disabled=false;return}if(r.error){$('sst').textContent='Error: '+r.error;$('go').disabled=false;return}$('sst').textContent='Rendering... '+((Date.now()-t0)/1000).toFixed(0)+'s';if(Date.now()-t0>TIMEOUT_MS){$('sst').textContent='Timed out.';$('go').disabled=false;return}setTimeout(poll,POLL_MS)}catch(e){if(e!==0){$('sst').textContent='Poll error.';$('go').disabled=false}}};setTimeout(poll,POLL_MS)}catch(e){if(e!==0){$('sst').textContent='Connection error.';$('go').disabled=false}}}\n" as *u8) 259 ue_w(fd, "function openLb(c){$('lbimg').src=API+'/img/'+c;$('lbdl').href=API+'/img/'+c;$('lbdl').download=c+'.png';$('lb').classList.add('on')}function closeLb(){$('lb').classList.remove('on');$('lbimg').src=''}document.addEventListener('keydown',function(e){if(e.key=='Escape')closeLb()});\nrenderChat();lifeResume();\n</script>\n</body>\n</html>\n" as *u8) 260 return 0 261} 262 263func main(argc: i64, argv: *i64) -> i64 { 264 if argc < 3 { ue_w(2, "usage: nx_gen_ui_emit <conf> <out.html>\n" as *u8); sys_exit(UE_EXIT_USAGE); return UE_EXIT_USAGE } 265 let conf: *u8 = argv[1] as *u8 266 let outp: *u8 = argv[2] as *u8 267 let lp: *i64 = sys_mmap(16) as *i64 268 lp[0] = 0 269 let raw: *u8 = sys_read_file(conf, lp) 270 if (raw as i64) == 0 { ue_w(1, "GEN-UI-EMIT REFUSED conf-unreadable " as *u8); ue_w(1, conf); ue_w(1, "\n" as *u8); sys_exit(UE_EXIT_IO); return UE_EXIT_IO } 271 let n: i64 = lp[0] 272 if n <= 0 { ue_w(1, "GEN-UI-EMIT REFUSED conf-empty\n" as *u8); sys_exit(UE_EXIT_IO); return UE_EXIT_IO } 273 if n >= UE_CONF_CAP { ue_w(1, "GEN-UI-EMIT REFUSED conf-over-cap\n" as *u8); sys_exit(UE_EXIT_IO); return UE_EXIT_IO } 274 // copy into a writable, NUL-padded arena (the parser writes NULs in place) 275 let buf: *u8 = sys_mmap(UE_CONF_CAP + 16) 276 var ci: i64 = 0 277 while ci < n { buf[ci] = raw[ci]; ci = ci + 1 } 278 buf[n] = 0 as u8 279 let keys: *i64 = sys_mmap(UE_MAXKEYS * 8) as *i64 280 let vals: *i64 = sys_mmap(UE_MAXKEYS * 8) as *i64 281 let nk: i64 = ue_parse(buf, n, keys, vals) 282 let miss: *i64 = sys_mmap(16) as *i64 283 miss[0] = 0 284 let mlist: *u8 = sys_mmap(UE_MISS_CAP) 285 mlist[0] = 0 as u8 286 // DRY RESOLVE FIRST (fd = -1): every key is looked up before any byte is written, so a refusal leaves no partial page 287 let dry: i64 = gu_emit(0 - 1, buf, keys, vals, nk, miss, mlist) 288 if dry == (0 - 2) { ue_w(1, "GEN-UI-EMIT REFUSED invalid release_href or empty release_label (nothing written)\n" as *u8); sys_exit(UE_EXIT_REFUSED); return UE_EXIT_REFUSED } 289 if dry < 0 { 290 ue_w(1, "GEN-UI-EMIT REFUSED missing-keys=" as *u8) 291 let nb: *u8 = sys_mmap(32); var m: i64 = miss[0]; var k: i64 = 0; let t: *u8 = sys_mmap(32) 292 if m == 0 { t[0] = 48 as u8; k = 1 } while m > 0 { t[k] = (48 + (m % 10)) as u8; k = k + 1; m = m / 10 } 293 var q: i64 = 0; while k > 0 { k = k - 1; nb[q] = t[k]; q = q + 1 } nb[q] = 0 as u8 294 ue_w(1, nb); ue_w(1, " keys: " as *u8); ue_w(1, mlist); ue_w(1, " (nothing written)\n" as *u8) 295 sys_exit(UE_EXIT_REFUSED); return UE_EXIT_REFUSED 296 } 297 // atomic: write <out>.tmp then rename 298 let tmp: *u8 = sys_mmap(1024) 299 var o: i64 = 0 300 var i: i64 = 0 301 while outp[i] != (0 as u8) { tmp[o] = outp[i]; o = o + 1; i = i + 1 } 302 tmp[o] = 46 as u8; tmp[o+1] = 116 as u8; tmp[o+2] = 109 as u8; tmp[o+3] = 112 as u8; tmp[o+4] = 0 as u8 303 let fd: i64 = sys_openat_wr(tmp, UE_MODE_FILE) 304 if fd < 0 { ue_w(1, "GEN-UI-EMIT REFUSED write-fail " as *u8); ue_w(1, tmp); ue_w(1, "\n" as *u8); sys_exit(UE_EXIT_IO); return UE_EXIT_IO } 305 miss[0] = 0 306 gu_emit(fd, buf, keys, vals, nk, miss, mlist) 307 sys_close(fd) 308 let rr: i64 = sys_renameat(tmp, outp) 309 if rr < 0 { ue_w(1, "GEN-UI-EMIT REFUSED rename-fail\n" as *u8); sys_exit(UE_EXIT_IO); return UE_EXIT_IO } 310 ue_w(1, "GEN-UI-EMIT OK keys=" as *u8) 311 let kb: *u8 = sys_mmap(32); var kk: i64 = nk; var kn: i64 = 0; let kt: *u8 = sys_mmap(32) 312 if kk == 0 { kt[0] = 48 as u8; kn = 1 } while kk > 0 { kt[kn] = (48 + (kk % 10)) as u8; kn = kn + 1; kk = kk / 10 } 313 var kq: i64 = 0; while kn > 0 { kn = kn - 1; kb[kq] = kt[kn]; kq = kq + 1 } kb[kq] = 0 as u8 314 ue_w(1, kb); ue_w(1, " out=" as *u8); ue_w(1, outp); ue_w(1, " (NX-DERIVED stamped; edit knowledge/gen_ui.conf, never the page)\n" as *u8) 315 sys_exit(UE_EXIT_OK) 316 return UE_EXIT_OK 317}