Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/probe_typstoutline.mjs

4.5 KiB, 1 run

created by r2519314175:101, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1// probe_typstoutline.mjs — where a section rail's entries could come from.
2//
3// The live view is asked for a toggled rail of hyperlinked sections. A rail needs
4// three things per entry and the document has to answer all three: the words, the
5// level, and THE PAGE. This asks every route the vendored pair offers, and reports
6// what each one actually hands back.
7//
8// It asserts nothing and is not a verifier.
9//
10// eval "$(bash dev/world.sh 5 --env)"
11// node dev/probe_typstoutline.mjs
12import fs from 'node:fs';
13import { open, scratch } from './harness.mjs';
14
15const WORK = scratch('typstoutline');
16fs.mkdirSync(WORK, { recursive: true });
17
18const DOC = `#set page(width: 200pt, height: 160pt, margin: 14pt)
19#set text(size: 9pt)
20= Front matter
21#lorem(40)
22= Chapter one
23== Section one point one
24#lorem(120)
25== Section one point two
26#lorem(120)
27= Chapter two
28#lorem(200)
29== Section two point one
30#lorem(60)
31`;
32
33const s = await open({ name: 'typstoutline' });
34const p = s.page;
35await p.waitForTimeout(1200);
36
37const r = await p.evaluate(async (src) => {
38 const out = {};
39 const m = await import('/pkg/oxedyne_daimond.js');
40 m.set_locked_packs('');
41 await m.write_file('outprobe/main.typ', src);
42 const c = await m.typst_compile_project_vector('outprobe/main.typ');
43 if (!c.vector) return { error: String(c.error || 'nothing').slice(0, 400) };
44
45 // ── 1. the renderer, on the artifact the view already holds ──
46 const g = await import('/vendor/typst/typst_ts_renderer.mjs');
47 await g.default('/vendor/typst/typst_ts_renderer_bg.wasm');
48 const rr = await new g.TypstRendererBuilder().build();
49 const ses = rr.session_from_artifact(c.vector, 'vector');
50 out.pages = ses.pages_info.page_count;
51 try {
52 const cu = rr.get_customs(ses);
53 out.customs = cu ? cu.map((x) => {
54 try { return [String(x[0]), (x[1] && x[1].length) || 0]; }
55 catch (e) { return String(x).slice(0, 80); }
56 }) : null;
57 } catch (e) { out.customs = 'threw: ' + (e && e.message); }
58 // What one page's markup carries that could name a heading.
59 const info = ses.pages_info;
60 const pg0 = info.page(0);
61 out.page0 = { h: pg0.height_pt, off: pg0.page_off };
62 ses.free();
63 const ses2 = rr.session_from_artifact(c.vector, 'vector');
64 const svg = ses2.render_in_window(0, 0, info.width(), 1e6);
65 ses2.free();
66 out.svgKB = +(svg.length / 1024).toFixed(1);
67 out.svgHas = {};
68 for (const k of ['data-tid', 'typst-group', 'typst-text', 'tsel', 'pseudo-link',
69 'handleTypstLocation', 'typst-page', 'data-page', '<a ', 'xlink:href']) {
70 out.svgHas[k] = svg.split(k).length - 1;
71 }
72 out.svgFirstA = (svg.match(/<a [^>]{0,200}/) || [''])[0];
73 out.svgPageGroups = (svg.match(/class="typst-page[^"]*"[^>]{0,140}/g) || []).slice(0, 4);
74
75 // ── 2. the compiler, asked about the document rather than for it ──
76 // A SECOND compiler off the SAME wasm module, so this costs no second 27 MB.
77 const heap0 = window.DaimondTypst.heapMB();
78 const cg = await import('/vendor/typst/typst_ts_web_compiler.mjs');
79 const init = await cg.default('/vendor/typst/typst_ts_web_compiler_bg.wasm');
80 const b = new cg.TypstCompilerBuilder();
81 b.set_dummy_access_model();
82 for (const n of ['LibertinusSerif-Regular.otf', 'LibertinusSerif-Bold.otf',
83 'LibertinusSerif-Italic.otf', 'LibertinusSerif-BoldItalic.otf',
84 'NewCMMath-Regular.otf']) {
85 try {
86 const resp = await fetch('/vendor/typst/fonts/' + n);
87 if (resp.ok) await b.add_raw_font(new Uint8Array(await resp.arrayBuffer()));
88 } catch (e) { /* whichever are there */ }
89 }
90 const comp = await b.build();
91 comp.reset_shadow();
92 comp.add_source('/main.typ', src);
93 out.query = {};
94 const ask = (sel, field) => {
95 const t0 = performance.now();
96 try {
97 const j = comp.query('/main.typ', undefined, sel, field);
98 return { ms: +(performance.now() - t0).toFixed(1), out: String(j).slice(0, 900) };
99 } catch (e) { return { ms: +(performance.now() - t0).toFixed(1), err: String(e && e.message).slice(0, 200) }; }
100 };
101 out.query.heading = ask('heading', undefined);
102 out.query.headingBody = ask('heading', 'body');
103 out.query.headingLoc = ask('heading', 'location');
104 out.query.headingPage = ask('heading', 'page');
105 out.query.metadata = ask('<toc>', undefined);
106 out.heapAfter = { before: heap0, after: window.DaimondTypst.heapMB(),
107 second: init && init.memory ? init.memory.buffer.byteLength / 1048576 : 0 };
108 return out;
109}, DOC);
110
111if (r.error) { console.log('REFUSED: ' + r.error); await s.close(); process.exit(1); }
112fs.writeFileSync(`${WORK}/probe.json`, JSON.stringify(r, null, 2));
113console.log(JSON.stringify(r, null, 2).slice(0, 12000));
114console.log('\nwritten to ' + WORK);
115await s.close();