nx_archive_site_viewer.nx source
↩ module page · 279 lines · 22820 B
1// nx_archive_site_viewer.nx -- DOMAIN-GENERAL time-machine viewer with LOCAL MEDIA (no broken images). Reads the
2// target host from web_assets/archive/_target.txt (default page3.com), fetches its Wayback CDX, picks the EARLIEST
3// status-200 capture, reconstructs it from the capture's OWN original URL, then FETCHES every referenced image from
4// the archive, stores them under web_assets/archive/<host>/media/, and rewrites the page's <img src> to those local
5// copies (transparent-pixel placeholder for any image the archive doesn't have -> never a broken icon). Output:
6// web_assets/archive/<host>/{index.html,page.html,media/*}. Served by nx_archive_server. Fetch stack, cache-first,
7// status-aware retry. license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_x509_trust_store.nx"
10import "nx_trust_store_load_from_certdata.nx"
11import "nx_https_fetch_follow.nx"
12import "nx_cdx_parse.nx"
13import "nx_medrec.nx" // mr_canon_hash -- the url->media join key, gate-proven (MEDREC T12/T13)
14
15const K_MAGIC_4194304: i64 = 4194304
16const K_MAGIC_1024: i64 = 1024
17const K_MAGIC_4096: i64 = 4096
18const K_MAGIC_2048: i64 = 2048
19const K_MAGIC_65536: i64 = 65536
20
21func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
22func gn(v: i64) -> i64 { if v==0 { sys_write(1,"0" as *u8,1); return 0 } var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m } let t: *u8=sys_mmap(24); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } let o: *u8=sys_mmap(24); var w: i64=0; var q: i64=k-1; while q>=0 { o[w]=t[q]; w=w+1; q=q-1 } sys_write(1,o,w); return 0 }
23func ap(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { buf[off+i]=s[i]; i=i+1 } return off+i }
24func apn(buf: *u8, off: i64, v: i64) -> i64 { if v==0 { buf[off]=0x30 as u8; return off+1 } var m: i64=v; if m<0 { buf[off]=0x2d as u8; off=off+1; m=0-m } let t: *u8=sys_mmap(24); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } var o: i64=off; var q: i64=k-1; while q>=0 { buf[o]=t[q]; o=o+1; q=q-1 } return o }
25func apslice(dst: *u8, off: i64, src: *u8, so: i64, sl: i64) -> i64 { var i: i64=0; while i<sl { dst[off+i]=src[so+i]; i=i+1 } return off+sl }
26func dstarts(buf: *u8, off: i64, n: i64, lit: *u8, litlen: i64) -> i64 { if off+litlen>n { return 0 } var i: i64=0; while i<litlen { var a: i64=buf[off+i] as i64; if a>=0x41 { if a<=0x5a { a=a+0x20 } } if a != (lit[i] as i64) { return 0 } i=i+1 } return 1 }
27func esc_html(dst: *u8, doff: i64, src: *u8, n: i64) -> i64 { var o: i64=doff; var i: i64=0; while i<n { let c: i64=src[i] as i64; if c==0x3c { o=ap(dst,o,"<" as *u8) } else { if c==0x3e { o=ap(dst,o,">" as *u8) } else { if c==0x26 { o=ap(dst,o,"&" as *u8) } else { dst[o]=c as u8; o=o+1 } } } i=i+1 } return o }
28func save_file(path: *u8, buf: *u8, n: i64) -> i64 { let fd: i64=sys_openat_wr(path, 0x1a4); if fd<0 { return 0 } var off: i64=0; while off<n { let w: i64=sys_write(fd, ((buf as i64)+off) as *u8, n-off); if w<=0 { sys_close(fd); return 0 } off=off+w } sys_close(fd); return 1 }
29func read_file(path: *u8, buf: *u8, cap: i64, lenbox: *i64) -> i64 { let fd: i64=sys_openat_rd(path); if fd<0 { return 0 } let n: i64=sys_read(fd, buf, cap); sys_close(fd); if n<=0 { return 0 } lenbox[0]=n; return n }
30func is_image(buf: *u8, n: i64) -> i64 { if n<4 { return 0 } let a: i64=buf[0] as i64; let b: i64=buf[1] as i64; let c: i64=buf[2] as i64; let d: i64=buf[3] as i64; if a==0x47 { if b==0x49 { if c==0x46 { return 1 } } } if a==0xff { if b==0xd8 { if c==0xff { return 1 } } } if a==0x89 { if b==0x50 { if c==0x4e { if d==0x47 { return 1 } } } } if a==0x42 { if b==0x4d { return 1 } } return 0 }
31func fetch_retry(url: *u8, store: *TrustStore, out: *u8, cap: i64, st: *i64, tries: i64) -> i64 { var t: i64=0; var n: i64=0-1; while t<tries { n=nx_https_fetch_follow(url, store, out, cap, 6, st); if n>0 { if st[0]==200 { t=tries } else { t=t+1 } } else { t=t+1 } } return n }
32func mkpath(dst: *u8, a: *u8, b: *u8) -> i64 { var o: i64=ap(dst,0,a); o=ap(dst,o,b); dst[o]=0 as u8; return o }
33func next_imgsrc(buf: *u8, n: i64, from: i64, out2: *i64) -> i64 { var i: i64=from; while i<n { if dstarts(buf, i, n, "src=" as *u8, 4)==1 { if i+4<n { if (buf[i+4] as i64)==0x22 { let us: i64=i+5; var ue: i64=us; var go: i64=1; while go==1 { if ue<n { if (buf[ue] as i64)==0x22 { go=0 } else { ue=ue+1 } } else { go=0 } } out2[0]=us; out2[1]=ue-us; return ue+1 } } } i=i+1 } return 0-1 }
34// lowercase extension (<=4 alnum) of url[off,off+len) into extbuf; returns length (0 if none).
35func ext_of(buf: *u8, off: i64, len: i64, extbuf: *u8) -> i64 {
36 var dot: i64=0-1; var i: i64=off
37 while i<off+len { if (buf[i] as i64)==0x2e { dot=i } i=i+1 }
38 if dot<0 { return 0 }
39 var o: i64=0; var j: i64=dot+1
40 while j<off+len { if o<4 { var c: i64=buf[j] as i64; var ok: i64=0; if c>=0x30 { if c<=0x39 { ok=1 } } if c>=0x41 { if c<=0x5a { c=c+0x20; ok=1 } } if c>=0x61 { if c<=0x7a { ok=1 } } if ok==1 { extbuf[o]=c as u8; o=o+1 } else { j=off+len } } j=j+1 }
41 return o
42}
43
44func main() -> i64 {
45 gw("=== nx_archive_site_viewer (local-media): time machine for any domain ===\n" as *u8)
46 sys_mkdir("web_assets" as *u8, 0x1ed)
47 sys_mkdir("web_assets/archive" as *u8, 0x1ed)
48 let cap: i64=K_MAGIC_4194304
49 let lb: *i64=sys_mmap(8) as *i64
50 let st: *i64=sys_mmap(8) as *i64
51
52 let dbuf: *u8=sys_mmap(256)
53 var domain_len: i64=0
54 if read_file("web_assets/archive/_target.txt" as *u8, dbuf, 250, lb)>0 { var di: i64=0; var go: i64=1; while go==1 { if di<lb[0] { let c: i64=dbuf[di] as i64; if c<=0x20 { go=0 } else { di=di+1 } } else { go=0 } } domain_len=di }
55 if domain_len==0 { domain_len=ap(dbuf, 0, "page3.com" as *u8) }
56 dbuf[domain_len]=0 as u8
57 gw("host = " as *u8); gw(dbuf); gw("\n" as *u8)
58
59 let ddir: *u8=sys_mmap(512)
60 var dl: i64=ap(ddir, 0, "web_assets/archive/" as *u8); dl=ap(ddir, dl, dbuf); ddir[dl]=0 as u8
61 sys_mkdir(ddir, 0x1ed)
62 let mdir: *u8=sys_mmap(512); mkpath(mdir, ddir, "/media" as *u8); sys_mkdir(mdir, 0x1ed)
63
64 let r: i64=nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 512, K_MAGIC_4194304)
65 if r<=0 { gw("trust store load failed\n" as *u8); return 1 }
66 let store: *TrustStore=r as *TrustStore
67 let cdxbuf: *u8=sys_mmap(cap)
68 let pth: *u8=sys_mmap(K_MAGIC_1024)
69
70 mkpath(pth, ddir, "/_cache_cdx.txt" as *u8)
71 var ncdx: i64=read_file(pth, cdxbuf, cap, lb)
72 if ncdx>0 { gw("[1] CDX cache (" as *u8); gn(ncdx); gw(" B)\n" as *u8) } else {
73 let cu: *u8=sys_mmap(K_MAGIC_1024); var co: i64=ap(cu, 0, "https://web.archive.org/cdx/search/cdx?url=" as *u8); co=ap(cu, co, dbuf); co=ap(cu, co, "&limit=150" as *u8); cu[co]=0 as u8
74 gw("[1] fetch CDX " as *u8); gw(cu); gw("\n" as *u8)
75 ncdx=fetch_retry(cu, store, cdxbuf, cap, st, 6)
76 if ncdx<=0 { gw(" CDX fail\n" as *u8); return 1 }
77 if st[0]!=200 { gw(" rate-limited; retry shortly\n" as *u8); return 1 }
78 save_file(pth, cdxbuf, ncdx)
79 }
80
81 let RS: *i64=sys_mmap(K_MAGIC_4096) as *i64
82 let RE: *i64=sys_mmap(K_MAGIC_4096) as *i64
83 let nrows: i64=cdx_rows(cdxbuf, ncdx, RS, RE, 480)
84 let dates: *i64=sys_mmap(K_MAGIC_4096) as *i64
85 let tso: *i64=sys_mmap(K_MAGIC_4096) as *i64
86 let tsl: *i64=sys_mmap(K_MAGIC_4096) as *i64
87 let oro: *i64=sys_mmap(K_MAGIC_4096) as *i64
88 let orl: *i64=sys_mmap(K_MAGIC_4096) as *i64
89 let b2: *i64=sys_mmap(16) as *i64
90 var i: i64=0
91 var m: i64=0
92 while i<nrows {
93 cdx_field(cdxbuf, RS[i], RE[i], 4, b2)
94 var ok2: i64=0
95 if b2[1]==3 { if (cdxbuf[b2[0]] as i64)==0x32 { if (cdxbuf[b2[0]+1] as i64)==0x30 { if (cdxbuf[b2[0]+2] as i64)==0x30 { ok2=1 } } } }
96 if ok2==1 { cdx_field(cdxbuf, RS[i], RE[i], 1, b2); dates[m]=cdx_ts8(cdxbuf, b2[0], b2[1]); tso[m]=b2[0]; tsl[m]=b2[1]; cdx_field(cdxbuf, RS[i], RE[i], 2, b2); oro[m]=b2[0]; orl[m]=b2[1]; m=m+1 }
97 i=i+1
98 }
99 if m<=0 { gw(" no captures\n" as *u8); return 1 }
100 var q: i64=0; var qd: i64=dates[0]; var jj: i64=1
101 while jj<m { if dates[jj]<qd { qd=dates[jj]; q=jj } jj=jj+1 }
102 gw("[2] earliest capture " as *u8); gn(dates[q]); gw("\n" as *u8)
103
104 let cont: *u8=sys_mmap(cap)
105 mkpath(pth, ddir, "/_cache_page.txt" as *u8)
106 var ncont: i64=read_file(pth, cont, cap, lb)
107 if ncont>0 { gw("[3] page cache (" as *u8); gn(ncont); gw(" B)\n" as *u8) } else {
108 let url: *u8=sys_mmap(K_MAGIC_2048); var uo: i64=ap(url, 0, "https://web.archive.org/web/" as *u8); uo=apslice(url, uo, cdxbuf, tso[q], tsl[q]); uo=ap(url, uo, "id_/" as *u8); uo=apslice(url, uo, cdxbuf, oro[q], orl[q]); url[uo]=0 as u8
109 gw("[3] fetch page\n" as *u8)
110 ncont=fetch_retry(url, store, cont, cap, st, 6)
111 if ncont<=0 { gw(" page fail\n" as *u8); return 1 }
112 if st[0]!=200 { gw(" rate-limited; retry shortly\n" as *u8); return 1 }
113 save_file(pth, cont, ncont)
114 }
115
116 // -- LOCAL MEDIA: fetch each <img src>, store under <host>/media/, record local name (or 0 if absent) --
117 let names: *i64=sys_mmap(64*8) as *i64
118 let namel: *i64=sys_mmap(64*8) as *i64
119 let uoffs_a: *i64=sys_mmap(64*8) as *i64
120 let ulens_a: *i64=sys_mmap(64*8) as *i64
121 let imgbuf: *u8=sys_mmap(cap)
122 let o2: *i64=sys_mmap(16) as *i64
123 let ext: *u8=sys_mmap(16)
124 var from: i64=0
125 var idx: i64=0
126 // base of the capture's original URL (up to last '/') for resolving relative <img src>
127 var obase_off: i64=oro[q]
128 var obls: i64=oro[q]+orl[q]
129 var ox: i64=oro[q]
130 while ox<oro[q]+orl[q] { if (cdxbuf[ox] as i64)==0x2f { obls=ox } ox=ox+1 }
131 var obase_len: i64=obls-oro[q]+1
132 gw("[4] localizing media ...\n" as *u8)
133 var more: i64=1
134 while more==1 {
135 let p: i64=next_imgsrc(cont, ncont, from, o2)
136 if p<0 { more=0 } else {
137 from=p
138 if idx<24 {
139 let uoff: i64=o2[0]; let ulen: i64=o2[1]
140 names[idx]=0; namel[idx]=0
141 var dup: i64=0-1; var dj: i64=0
142 while dj<idx { if ulens_a[dj]==ulen { var eq: i64=1; var dk: i64=0; while dk<ulen { if (cont[uoffs_a[dj]+dk] as i64) != (cont[uoff+dk] as i64) { eq=0; dk=ulen } else { dk=dk+1 } } if eq==1 { dup=dj; dj=idx } } dj=dj+1 }
143 uoffs_a[idx]=uoff; ulens_a[idx]=ulen
144 if dup>=0 { names[idx]=names[dup]; namel[idx]=namel[dup] } else {
145 let el: i64=ext_of(cont, uoff, ulen, ext); ext[el]=0 as u8
146 // *NAME BY THE JOIN KEY, NOT BY PAGE POSITION. media/img<idx> is unstable across runs --
147 // idx shifts the moment the page's markup changes -- and it shares nothing with the
148 // harvester's digest-addressed store, so every render re-fetched bytes already on disk.
149 // mr_canon_hash is path-only and gate-proven (MEDREC T12/T13), so hashing the page's RAW
150 // relative src yields the same key as the harvester's absolute CDX url. One key space,
151 // one store, two producers.
152 let ukey: i64=mr_canon_hash(cont, uoff, ulen)
153 let lname: *u8=sys_mmap(64)
154 var lo: i64=ap(lname, 0, "media/u" as *u8); lo=apn(lname, lo, ukey)
155 if el>0 { lname[lo]=0x2e as u8; lo=lo+1; lo=ap(lname, lo, ext) }
156 lname[lo]=0 as u8
157 let sp: *u8=sys_mmap(512); var spo: i64=ap(sp, 0, ddir); sp[spo]=0x2f as u8; spo=spo+1; spo=ap(sp, spo, lname); sp[spo]=0 as u8
158 var fn: i64=read_file(sp, imgbuf, cap, lb)
159 if fn>0 { fn=lb[0] } else {
160 // resolve the absolute image URL
161 let rimg: *u8=sys_mmap(K_MAGIC_2048); var ro: i64=0
162 if dstarts(cont, uoff, uoff+ulen, "http" as *u8, 4)==1 { ro=apslice(rimg, 0, cont, uoff, ulen) } else { ro=apslice(rimg, 0, cdxbuf, obase_off, obase_len); var us2: i64=uoff; if ulen>=2 { if (cont[uoff] as i64)==0x2e { if (cont[uoff+1] as i64)==0x2f { us2=uoff+2 } } } if us2==uoff { if (cont[uoff] as i64)==0x2f { us2=uoff+1 } } ro=apslice(rimg, ro, cont, us2, ulen-(us2-uoff)) }
163 rimg[ro]=0 as u8
164 // *BYURL SHORT-CIRCUIT -- the entire point of the reverse index.
165 // nx_arcmine harvests this host exhaustively FROM THE CDX INDEX and stores the bytes
166 // content-addressed. This viewer was re-fetching every one of them, because its cache
167 // is keyed by page position (media/img<idx>.ext) while the harvester's is keyed by
168 // DIGEST -- two disjoint stores holding identical bytes. mr_canon_hash joins them, and
169 // that join is gate-proven (MEDREC T12/T13: the absolute CDX url, the relative <img
170 // src>, and a bare-host+query form all agree; a different path does not).
171 // A page whose media is already harvested now renders with ZERO network calls -- which
172 // REMOVES the ~12s job deadline rather than working around it.
173 fn=0
174 var vl: i64=0
175 while rimg[vl]!=(0 as u8) { vl=vl+1 }
176 let bq: *u8=sys_mmap(512); var bqo: i64=ap(bq, 0, ddir); bq[bqo]=0x2f as u8; bqo=bqo+1
177 bqo=ap(bq, bqo, "byurl/" as *u8); bqo=apn(bq, bqo, mr_canon_hash(rimg, 0, vl)); bqo=ap(bq, bqo, ".txt" as *u8); bq[bqo]=0 as u8
178 let brb: *u8=sys_mmap(256)
179 var bn: i64=read_file(bq, brb, 256, lb)
180 if bn>0 {
181 bn=lb[0]
182 var bl: i64=0
183 while bl<bn { if brb[bl]==(0x0a as u8) { bn=bl; bl=bn } else { bl=bl+1 } }
184 let mp2: *u8=sys_mmap(512); var mo2: i64=ap(mp2, 0, ddir); mp2[mo2]=0x2f as u8; mo2=mo2+1
185 mo2=ap(mp2, mo2, "media/" as *u8); mo2=apslice(mp2, mo2, brb, 0, bn); mp2[mo2]=0 as u8
186 let hf: i64=read_file(mp2, imgbuf, cap, lb)
187 if hf>0 { fn=lb[0]; if is_image(imgbuf, fn)==0 { fn=0 } }
188 if fn>0 { gw(" img" as *u8); gn(idx); gw(" LOCAL via byurl (no fetch)\n" as *u8) }
189 }
190 // primary: the image AT this capture's timestamp
191 if fn==0 {
192 let iu: *u8=sys_mmap(K_MAGIC_2048); var io: i64=ap(iu, 0, "https://web.archive.org/web/" as *u8); io=apslice(iu, io, cdxbuf, tso[q], tsl[q]); io=ap(iu, io, "id_/" as *u8); io=ap(iu, io, rimg); iu[io]=0 as u8
193 fn=fetch_retry(iu, store, imgbuf, cap, st, 3)
194 if fn>0 { if st[0]==200 { if is_image(imgbuf, fn)==1 { } else { fn=0 } } else { fn=0 } } else { fn=0 }
195 }
196 // CROSS-TEMPORAL RECOVERY: gap at this date -> find the image at ANY date it was captured
197 if fn==0 {
198 let cq: *u8=sys_mmap(K_MAGIC_2048); var qo: i64=ap(cq, 0, "https://web.archive.org/cdx/search/cdx?url=" as *u8); qo=ap(cq, qo, rimg); qo=ap(cq, qo, "&limit=1&filter=statuscode:200" as *u8); cq[qo]=0 as u8
199 let cr: *u8=sys_mmap(K_MAGIC_65536)
200 let cn: i64=fetch_retry(cq, store, cr, K_MAGIC_65536, st, 2)
201 if cn>10 { if st[0]==200 {
202 let R1: *i64=sys_mmap(64) as *i64; let R2: *i64=sys_mmap(64) as *i64
203 let nr: i64=cdx_rows(cr, cn, R1, R2, 4)
204 if nr>0 {
205 let tb: *i64=sys_mmap(16) as *i64
206 cdx_field(cr, R1[0], R2[0], 1, tb)
207 let ru: *u8=sys_mmap(K_MAGIC_2048); var rio: i64=ap(ru, 0, "https://web.archive.org/web/" as *u8); rio=apslice(ru, rio, cr, tb[0], tb[1]); rio=ap(ru, rio, "id_/" as *u8); rio=ap(ru, rio, rimg); ru[rio]=0 as u8
208 fn=fetch_retry(ru, store, imgbuf, cap, st, 2)
209 if fn>0 { if st[0]==200 { if is_image(imgbuf, fn)==1 { gw(" img" as *u8); gn(idx); gw(" RECOVERED from another date\n" as *u8) } else { fn=0 } } else { fn=0 } } else { fn=0 }
210 }
211 } }
212 }
213 if fn>0 {
214 save_file(sp, imgbuf, fn)
215 // *THE VIEWER IS NOW A PRODUCER OF THE SHARED INDEX, not merely a consumer.
216 // It just fetched exactly the images a REAL PAGE references -- the highest-value
217 // subset there is, and precisely the set the index-order harvest is slowest to
218 // reach, because that walks the CDX keyspace alphabetically while a page cites
219 // whatever it cites. Recording the fetch here makes the NEXT render of this page
220 // network-free without waiting for the sweep to catch up. Demand-driven where the
221 // harvest is breadth-driven; both fill the same index.
222 let bd2: *u8=sys_mmap(512); var bd2o: i64=ap(bd2, 0, ddir); bd2[bd2o]=0x2f as u8; bd2o=bd2o+1
223 bd2o=ap(bd2, bd2o, "byurl" as *u8); bd2[bd2o]=0 as u8
224 sys_mkdir(bd2, 0x1ed)
225 let bw: *u8=sys_mmap(512); var bwo: i64=ap(bw, 0, bd2); bw[bwo]=0x2f as u8; bwo=bwo+1
226 bwo=apn(bw, bwo, ukey); bwo=ap(bw, bwo, ".txt" as *u8); bw[bwo]=0 as u8
227 let bv: *u8=sys_mmap(128); var bvo: i64=ap(bv, 0, "u" as *u8); bvo=apn(bv, bvo, ukey)
228 if el>0 { bv[bvo]=0x2e as u8; bvo=bvo+1; bvo=ap(bv, bvo, ext) }
229 bv[bvo]=0x0a as u8; bvo=bvo+1
230 save_file(bw, bv, bvo)
231 }
232 }
233 if fn>0 { names[idx]=lname as i64; namel[idx]=lo; gw(" img" as *u8); gn(idx); gw(" ok " as *u8); gn(fn); gw("B\n" as *u8) } else { gw(" img" as *u8); gn(idx); gw(" miss\n" as *u8) }
234 }
235 idx=idx+1
236 } else { more=0 }
237 }
238 }
239 let nmedia: i64=idx
240
241 // -- page.html: rewrite each <img src> to its local copy (or transparent placeholder) --
242 let pbuf: *u8=sys_mmap(cap)
243 var po: i64=ap(pbuf, 0, "<!DOCTYPE html><meta charset=utf-8><style>html,body{margin:0;padding:8px;background:#fff;color:#000;font-family:Times,Georgia,serif}img{max-width:100%;height:auto}table{max-width:100%}</style>" as *u8)
244 var ri: i64=0
245 var rj: i64=0
246 while rj<ncont {
247 var handled: i64=0
248 if dstarts(cont, rj, ncont, "src=" as *u8, 4)==1 { if rj+4<ncont { if (cont[rj+4] as i64)==0x22 {
249 po=ap(pbuf, po, "src=" as *u8); pbuf[po]=0x22 as u8; po=po+1
250 var nm: i64=0
251 if ri<nmedia { nm=names[ri] }
252 if nm != 0 { po=apslice(pbuf, po, nm as *u8, 0, namel[ri]) } else { po=ap(pbuf, po, "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" as *u8) }
253 ri=ri+1
254 rj=rj+5
255 var go: i64=1
256 while go==1 { if rj<ncont { if (cont[rj] as i64)==0x22 { go=0 } else { rj=rj+1 } } else { go=0 } }
257 handled=1
258 } } }
259 if handled==0 { pbuf[po]=cont[rj] as u8; po=po+1; rj=rj+1 }
260 }
261 mkpath(pth, ddir, "/page.html" as *u8)
262 if save_file(pth, pbuf, po)==0 { gw("page.html fail\n" as *u8); return 1 }
263
264 // -- index.html dashboard --
265 let ob: *u8=sys_mmap(cap)
266 var o: i64=0
267 o=ap(ob,o,"<!DOCTYPE html><html><head><meta charset=utf-8><title>Nishi Time Machine - " as *u8); o=ap(ob,o,dbuf); o=ap(ob,o,"</title><style>body{font-family:system-ui,Arial,sans-serif;margin:0;background:#0e0f13;color:#e7e7ea}.wrap{max-width:1040px;margin:0 auto;padding:28px}h1{font-size:26px;margin:0 0 4px}.sub{color:#8a8f9a;margin:0 0 18px}.prov{background:#171a21;border:1px solid #2a2d36;border-radius:10px;padding:16px 18px;line-height:1.6}.prov b{color:#7bd88f}h3{color:#cdd}.tl{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0;max-height:140px;overflow:auto;background:#0b0c10;border:1px solid #2a2d36;border-radius:8px;padding:10px}.tl a{background:#23262f;border:1px solid #343846;border-radius:5px;padding:3px 8px;font-size:12px;color:#7bd8f0;text-decoration:none}.tl a:hover{background:#2f5d40;color:#fff}iframe{width:100%;height:540px;border:1px solid #2a2d36;border-radius:10px;background:#fff}pre{white-space:pre-wrap;word-break:break-all;background:#0b0c10;border:1px solid #2a2d36;border-radius:10px;padding:14px;font-size:12px;color:#9fe6c8;max-height:320px;overflow:auto}</style></head><body><div class=wrap>" as *u8)
268 o=ap(ob,o,"<h1>🕐 Nishi Time Machine</h1><p class=sub>" as *u8); o=ap(ob,o,dbuf); o=ap(ob,o," — reconstructed with LOCAL media (served by our own stack; no broken images)</p>" as *u8)
269 o=ap(ob,o,"<div class=prov><b>What we pulled</b><br>CDX index " as *u8); o=apn(ob,o,ncdx); o=ap(ob,o," B · <b>" as *u8); o=apn(ob,o,m); o=ap(ob,o,"</b> captures · earliest <b>" as *u8); o=apn(ob,o,dates[q]); o=ap(ob,o,"</b> · reconstructed page " as *u8); o=apn(ob,o,ncont); o=ap(ob,o," B · <b>" as *u8); o=apn(ob,o,nmedia); o=ap(ob,o,"</b> media localized</div>" as *u8)
270 o=ap(ob,o,"<h3>Scroll the history — click any date to load that snapshot</h3><div class=tl>" as *u8)
271 var d: i64=0; while d<m { if d<130 { o=ap(ob,o,"<a target=tm href='https://web.archive.org/web/" as *u8); o=apslice(ob,o,cdxbuf,tso[d],tsl[d]); o=ap(ob,o,"/" as *u8); o=apslice(ob,o,cdxbuf,oro[d],orl[d]); o=ap(ob,o,"'>" as *u8); o=apn(ob,o,dates[d]); o=ap(ob,o,"</a>" as *u8) } d=d+1 }
272 o=ap(ob,o,"</div><p class=sub>Default frame below = OUR sovereign reconstruction (local media). Click a date to load that snapshot from the archive.</p><iframe name=tm src=page.html></iframe>" as *u8)
273 o=ap(ob,o,"<h3>Raw archived HTML</h3><pre>" as *u8); o=esc_html(ob, o, cont, ncont); o=ap(ob,o,"</pre><div class=prov>Source: Internet Archive Wayback Machine · fetched, decoded & served sovereignly by Nishi</div></div></body></html>" as *u8)
274 mkpath(pth, ddir, "/index.html" as *u8)
275 if save_file(pth, ob, o)==0 { gw("index.html fail\n" as *u8); return 1 }
276
277 gw("\nSITE-VIEWER DONE: " as *u8); gn(nmedia); gw(" media localized -> web_assets/archive/" as *u8); gw(dbuf); gw("/index.html\n" as *u8)
278 return 0
279}