@vedivad/typst-web-service 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -14
- package/dist/index.d.ts +12 -7
- package/dist/index.js +89 -57
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# @vedivad/typst-web-service
|
|
2
2
|
|
|
3
|
-
Editor-agnostic Typst services for the web — compile, render, analyze, and
|
|
3
|
+
Editor-agnostic Typst services for the web — compile, render, analyze, and
|
|
4
|
+
format via WASM.
|
|
4
5
|
|
|
5
|
-
Four independent classes
|
|
6
|
+
Four independent classes. Import only what you need.
|
|
7
|
+
|
|
8
|
+
`TypstCompiler.create()` and `TypstAnalyzer.create()` are async because they
|
|
9
|
+
initialize worker-backed WASM services up front. `TypstRenderer.create()` and
|
|
10
|
+
`TypstFormatter.create()` are sync wrappers; their WASM work is awaited by
|
|
11
|
+
methods like `renderSvgPages()` and `format()`.
|
|
6
12
|
|
|
7
13
|
## Install
|
|
8
14
|
|
|
@@ -18,19 +24,19 @@ npm install @vedivad/typst-web-service
|
|
|
18
24
|
import { TypstCompiler, TypstRenderer } from "@vedivad/typst-web-service";
|
|
19
25
|
|
|
20
26
|
const compiler = await TypstCompiler.create();
|
|
21
|
-
const renderer =
|
|
27
|
+
const renderer = TypstRenderer.create();
|
|
22
28
|
|
|
23
29
|
// Populate the VFS, then compile
|
|
24
30
|
await compiler.setText("/main.typ", "= Hello, Typst");
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
27
|
-
const pages = await renderer.renderSvgPages(
|
|
31
|
+
const firstResult = await compiler.compile();
|
|
32
|
+
if (firstResult.vector) {
|
|
33
|
+
const pages = await renderer.renderSvgPages(firstResult.vector);
|
|
28
34
|
document.querySelector("#preview")!.innerHTML = pages
|
|
29
35
|
.map((page) => `<div class="page">${page.svg}</div>`)
|
|
30
36
|
.join("");
|
|
31
37
|
}
|
|
32
38
|
|
|
33
|
-
//
|
|
39
|
+
// firstResult.diagnostics are returned in deterministic order
|
|
34
40
|
// (path, start position, end position, message)
|
|
35
41
|
|
|
36
42
|
// Multi-file
|
|
@@ -38,7 +44,7 @@ await compiler.setMany({
|
|
|
38
44
|
"/main.typ": '#import "template.typ": greet\n#greet("World")',
|
|
39
45
|
"/template.typ": "#let greet(name) = [Hello, #name!]",
|
|
40
46
|
});
|
|
41
|
-
const
|
|
47
|
+
const multiFileResult = await compiler.compile();
|
|
42
48
|
|
|
43
49
|
// PDF export — operates on the same VFS state
|
|
44
50
|
const pdf = await compiler.compilePdf();
|
|
@@ -54,7 +60,7 @@ Requires a bundler that supports WASM imports (e.g. Vite + `vite-plugin-wasm`).
|
|
|
54
60
|
```ts
|
|
55
61
|
import { TypstFormatter } from "@vedivad/typst-web-service";
|
|
56
62
|
|
|
57
|
-
const formatter =
|
|
63
|
+
const formatter = TypstFormatter.create({ tab_spaces: 2, max_width: 80 });
|
|
58
64
|
const formatted = await formatter.format(source);
|
|
59
65
|
const rangeResult = await formatter.formatRange(source, start, end);
|
|
60
66
|
```
|
|
@@ -83,14 +89,13 @@ const analyzer = await TypstAnalyzer.create({ wasmUrl: tinymistWasmUrl });
|
|
|
83
89
|
await analyzer.didChange("untitled:project/main.typ", source);
|
|
84
90
|
const completions = await analyzer.completion(
|
|
85
91
|
"untitled:project/main.typ",
|
|
86
|
-
|
|
87
|
-
character,
|
|
92
|
+
source,
|
|
93
|
+
{ line, character },
|
|
88
94
|
);
|
|
89
|
-
const hover = await analyzer.hover(
|
|
90
|
-
"untitled:project/main.typ",
|
|
95
|
+
const hover = await analyzer.hover("untitled:project/main.typ", source, {
|
|
91
96
|
line,
|
|
92
97
|
character,
|
|
93
|
-
);
|
|
98
|
+
});
|
|
94
99
|
|
|
95
100
|
analyzer.destroy();
|
|
96
101
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -401,11 +401,16 @@ declare class TypstProject {
|
|
|
401
401
|
*/
|
|
402
402
|
setText(path: Path, content: string): Promise<void>;
|
|
403
403
|
/**
|
|
404
|
-
* Add or overwrite a JSON file.
|
|
405
|
-
*
|
|
404
|
+
* Add or overwrite a JSON file. The analyzer does not track data files, but
|
|
405
|
+
* if `path` was previously tracked as text the analyzer document is closed
|
|
406
|
+
* and the text entry retired so the three views stay consistent.
|
|
406
407
|
*/
|
|
407
408
|
setJson(path: Path, value: unknown): Promise<void>;
|
|
408
|
-
/**
|
|
409
|
+
/**
|
|
410
|
+
* Add or overwrite a binary file. If `path` was previously tracked as text,
|
|
411
|
+
* the analyzer document is closed and the text entry retired in the same
|
|
412
|
+
* call so the compiler / analyzer / project views stay consistent.
|
|
413
|
+
*/
|
|
409
414
|
setBinary(path: Path, content: ArrayBuffer | ArrayBufferView): Promise<void>;
|
|
410
415
|
/**
|
|
411
416
|
* Batch set multiple files. Strings route to both compiler and analyzer;
|
|
@@ -446,15 +451,15 @@ declare class TypstProject {
|
|
|
446
451
|
private requireAnalyzer;
|
|
447
452
|
/**
|
|
448
453
|
* Request completion for `path` at `position`, using `source` as the
|
|
449
|
-
* current document state.
|
|
450
|
-
*
|
|
454
|
+
* current document state. Pure analyzer query — neither the compiler VFS
|
|
455
|
+
* nor project tracking (`files`/`getText`) is touched. Throws when no
|
|
451
456
|
* analyzer is attached.
|
|
452
457
|
*/
|
|
453
458
|
completion(path: Path, source: string, position: LspPosition): Promise<LspCompletionResponse>;
|
|
454
459
|
/**
|
|
455
460
|
* Request hover for `path` at `position`, using `source` as the current
|
|
456
|
-
* document state.
|
|
457
|
-
* when no analyzer is attached.
|
|
461
|
+
* document state. Pure analyzer query — neither the compiler VFS nor
|
|
462
|
+
* project tracking is touched. Throws when no analyzer is attached.
|
|
458
463
|
*/
|
|
459
464
|
hover(path: Path, source: string, position: LspPosition): Promise<LspHover | null>;
|
|
460
465
|
/**
|
package/dist/index.js
CHANGED
|
@@ -43,12 +43,13 @@ var TypstAnalyzer = class _TypstAnalyzer {
|
|
|
43
43
|
return analyzer;
|
|
44
44
|
}
|
|
45
45
|
async didOpen(uri, content) {
|
|
46
|
-
this.content.set(uri, content);
|
|
47
46
|
await this.proxy.didOpen(uri, content);
|
|
47
|
+
this.content.set(uri, content);
|
|
48
48
|
}
|
|
49
49
|
async didClose(uri) {
|
|
50
|
-
if (!this.content.
|
|
50
|
+
if (!this.content.has(uri)) return;
|
|
51
51
|
await this.proxy.didClose(uri);
|
|
52
|
+
this.content.delete(uri);
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
54
55
|
* Notify the analyzer that a document has changed. Skips the RPC when the
|
|
@@ -60,9 +61,9 @@ var TypstAnalyzer = class _TypstAnalyzer {
|
|
|
60
61
|
return;
|
|
61
62
|
}
|
|
62
63
|
if (this.content.get(uri) === content) return;
|
|
63
|
-
this.content.set(uri, content);
|
|
64
64
|
const version = ++this.versionCounter;
|
|
65
65
|
await this.proxy.didChange(uri, version, content);
|
|
66
|
+
this.content.set(uri, content);
|
|
66
67
|
}
|
|
67
68
|
/**
|
|
68
69
|
* Batch document changes. Splits inputs into opens (first-time URIs) and
|
|
@@ -72,17 +73,19 @@ var TypstAnalyzer = class _TypstAnalyzer {
|
|
|
72
73
|
async didChangeMany(docs) {
|
|
73
74
|
const opens = [];
|
|
74
75
|
const changes = [];
|
|
76
|
+
const updates = [];
|
|
75
77
|
for (const [uri, content] of Object.entries(docs)) {
|
|
76
78
|
if (!this.content.has(uri)) {
|
|
77
79
|
opens.push({ uri, content });
|
|
78
|
-
|
|
80
|
+
updates.push([uri, content]);
|
|
79
81
|
} else if (this.content.get(uri) !== content) {
|
|
80
82
|
changes.push({ uri, version: ++this.versionCounter, content });
|
|
81
|
-
|
|
83
|
+
updates.push([uri, content]);
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
if (opens.length === 0 && changes.length === 0) return;
|
|
85
87
|
await this.proxy.didChangeMany(opens, changes);
|
|
88
|
+
for (const [uri, content] of updates) this.content.set(uri, content);
|
|
86
89
|
}
|
|
87
90
|
/**
|
|
88
91
|
* Batch document closes. Filters to currently-open URIs and sends the set
|
|
@@ -91,10 +94,11 @@ var TypstAnalyzer = class _TypstAnalyzer {
|
|
|
91
94
|
async didCloseMany(uris) {
|
|
92
95
|
const toClose = [];
|
|
93
96
|
for (const uri of uris) {
|
|
94
|
-
if (this.content.
|
|
97
|
+
if (this.content.has(uri)) toClose.push(uri);
|
|
95
98
|
}
|
|
96
99
|
if (toClose.length === 0) return;
|
|
97
100
|
await this.proxy.didCloseMany(toClose);
|
|
101
|
+
for (const uri of toClose) this.content.delete(uri);
|
|
98
102
|
}
|
|
99
103
|
/**
|
|
100
104
|
* Request completion at `position` for `uri`, with `content` as the current
|
|
@@ -107,15 +111,16 @@ var TypstAnalyzer = class _TypstAnalyzer {
|
|
|
107
111
|
return this.proxy.completion(uri, position);
|
|
108
112
|
}
|
|
109
113
|
const isOpen = this.content.has(uri);
|
|
110
|
-
this.content.set(uri, content);
|
|
111
114
|
const version = ++this.versionCounter;
|
|
112
|
-
|
|
115
|
+
const result = await this.proxy.completionWithDoc(
|
|
113
116
|
uri,
|
|
114
117
|
version,
|
|
115
118
|
content,
|
|
116
119
|
position,
|
|
117
120
|
isOpen ? "change" : "open"
|
|
118
121
|
);
|
|
122
|
+
this.content.set(uri, content);
|
|
123
|
+
return result;
|
|
119
124
|
}
|
|
120
125
|
/**
|
|
121
126
|
* Request hover at `position` for `uri`, with `content` as the current
|
|
@@ -128,15 +133,16 @@ var TypstAnalyzer = class _TypstAnalyzer {
|
|
|
128
133
|
return this.proxy.hover(uri, position);
|
|
129
134
|
}
|
|
130
135
|
const isOpen = this.content.has(uri);
|
|
131
|
-
this.content.set(uri, content);
|
|
132
136
|
const version = ++this.versionCounter;
|
|
133
|
-
|
|
137
|
+
const result = await this.proxy.hoverWithDoc(
|
|
134
138
|
uri,
|
|
135
139
|
version,
|
|
136
140
|
content,
|
|
137
141
|
position,
|
|
138
142
|
isOpen ? "change" : "open"
|
|
139
143
|
);
|
|
144
|
+
this.content.set(uri, content);
|
|
145
|
+
return result;
|
|
140
146
|
}
|
|
141
147
|
destroy() {
|
|
142
148
|
this.proxy[Comlink.releaseProxy]();
|
|
@@ -162,7 +168,7 @@ import * as Comlink2 from "comlink";
|
|
|
162
168
|
var DEFAULT_FONTS = [
|
|
163
169
|
"https://cdn.jsdelivr.net/npm/roboto-font@0.1.0/fonts/Roboto/roboto-regular-webfont.ttf"
|
|
164
170
|
];
|
|
165
|
-
var
|
|
171
|
+
var defaultWasmUrl = () => `https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-web-compiler@${"0.7.0-rc2"}/pkg/typst_ts_web_compiler_bg.wasm`;
|
|
166
172
|
var TypstCompiler = class _TypstCompiler {
|
|
167
173
|
proxy;
|
|
168
174
|
worker;
|
|
@@ -177,7 +183,7 @@ var TypstCompiler = class _TypstCompiler {
|
|
|
177
183
|
const worker = options.worker ?? createWorker();
|
|
178
184
|
const proxy = Comlink2.wrap(worker);
|
|
179
185
|
await proxy.init(
|
|
180
|
-
options.wasmUrl ??
|
|
186
|
+
options.wasmUrl ?? defaultWasmUrl(),
|
|
181
187
|
options.fonts ?? DEFAULT_FONTS,
|
|
182
188
|
options.packages ?? true
|
|
183
189
|
);
|
|
@@ -205,8 +211,8 @@ var TypstCompiler = class _TypstCompiler {
|
|
|
205
211
|
*/
|
|
206
212
|
async setText(path, source) {
|
|
207
213
|
if (this.content.get(path) === source) return;
|
|
214
|
+
await this.proxy.mapShadow(path, this.encoder.encode(source));
|
|
208
215
|
this.content.set(path, source);
|
|
209
|
-
return this.proxy.mapShadow(path, this.encoder.encode(source));
|
|
210
216
|
}
|
|
211
217
|
/** Add or overwrite a JSON file in the virtual compiler filesystem. */
|
|
212
218
|
setJson(path, value, replacer, space) {
|
|
@@ -220,38 +226,42 @@ var TypstCompiler = class _TypstCompiler {
|
|
|
220
226
|
*/
|
|
221
227
|
async setMany(files) {
|
|
222
228
|
const encoded = {};
|
|
229
|
+
const textUpdates = [];
|
|
230
|
+
const binaryInvalidations = [];
|
|
223
231
|
for (const [path, content] of Object.entries(files)) {
|
|
224
232
|
if (typeof content === "string") {
|
|
225
233
|
if (this.content.get(path) === content) continue;
|
|
226
|
-
|
|
234
|
+
textUpdates.push([path, content]);
|
|
227
235
|
encoded[path] = this.encoder.encode(content);
|
|
228
236
|
} else {
|
|
229
|
-
|
|
237
|
+
binaryInvalidations.push(path);
|
|
230
238
|
encoded[path] = content;
|
|
231
239
|
}
|
|
232
240
|
}
|
|
233
241
|
if (Object.keys(encoded).length === 0) return;
|
|
234
|
-
|
|
242
|
+
await this.proxy.mapShadowMany(encoded);
|
|
243
|
+
for (const [path, content] of textUpdates) this.content.set(path, content);
|
|
244
|
+
for (const path of binaryInvalidations) this.content.delete(path);
|
|
235
245
|
}
|
|
236
246
|
/** Add or overwrite a binary file in the virtual compiler filesystem. */
|
|
237
|
-
setBinary(path, content) {
|
|
238
|
-
this.content.delete(path);
|
|
247
|
+
async setBinary(path, content) {
|
|
239
248
|
const bytes = content instanceof ArrayBuffer ? new Uint8Array(content) : new Uint8Array(
|
|
240
249
|
content.buffer,
|
|
241
250
|
content.byteOffset,
|
|
242
251
|
content.byteLength
|
|
243
252
|
);
|
|
244
|
-
|
|
253
|
+
await this.proxy.mapShadow(path, bytes);
|
|
254
|
+
this.content.delete(path);
|
|
245
255
|
}
|
|
246
256
|
/** Remove a file from the virtual compiler filesystem. */
|
|
247
|
-
remove(path) {
|
|
257
|
+
async remove(path) {
|
|
258
|
+
await this.proxy.unmapShadow(path);
|
|
248
259
|
this.content.delete(path);
|
|
249
|
-
return this.proxy.unmapShadow(path);
|
|
250
260
|
}
|
|
251
261
|
/** Clear all virtual files from the compiler filesystem. */
|
|
252
|
-
clear() {
|
|
262
|
+
async clear() {
|
|
263
|
+
await this.proxy.resetShadow();
|
|
253
264
|
this.content.clear();
|
|
254
|
-
return this.proxy.resetShadow();
|
|
255
265
|
}
|
|
256
266
|
destroy() {
|
|
257
267
|
this.proxy[Comlink2.releaseProxy]();
|
|
@@ -300,7 +310,6 @@ var CompileScheduler = class {
|
|
|
300
310
|
}
|
|
301
311
|
debounceTimer = null;
|
|
302
312
|
maxWaitTimer = null;
|
|
303
|
-
lastFireTime = 0;
|
|
304
313
|
schedule(callback) {
|
|
305
314
|
if (this.debounceTimer) {
|
|
306
315
|
clearTimeout(this.debounceTimer);
|
|
@@ -309,22 +318,19 @@ var CompileScheduler = class {
|
|
|
309
318
|
const delay = Math.max(0, this.options.debounceMs ?? 0);
|
|
310
319
|
this.debounceTimer = setTimeout(() => {
|
|
311
320
|
this.debounceTimer = null;
|
|
312
|
-
this.
|
|
321
|
+
this.clearMaxWait();
|
|
322
|
+
callback();
|
|
313
323
|
}, delay);
|
|
314
324
|
const maxWait = this.options.maxWaitMs;
|
|
315
325
|
if (maxWait != null && maxWait > 0 && !this.maxWaitTimer) {
|
|
316
|
-
const wait = Math.max(
|
|
317
|
-
0,
|
|
318
|
-
maxWait - (performance.now() - this.lastFireTime)
|
|
319
|
-
);
|
|
320
326
|
this.maxWaitTimer = setTimeout(() => {
|
|
321
327
|
this.maxWaitTimer = null;
|
|
322
328
|
if (this.debounceTimer) {
|
|
323
329
|
clearTimeout(this.debounceTimer);
|
|
324
330
|
this.debounceTimer = null;
|
|
325
|
-
|
|
331
|
+
callback();
|
|
326
332
|
}
|
|
327
|
-
},
|
|
333
|
+
}, maxWait);
|
|
328
334
|
}
|
|
329
335
|
}
|
|
330
336
|
/** Cancel any pending scheduled fire without calling the callback. */
|
|
@@ -333,15 +339,14 @@ var CompileScheduler = class {
|
|
|
333
339
|
clearTimeout(this.debounceTimer);
|
|
334
340
|
this.debounceTimer = null;
|
|
335
341
|
}
|
|
342
|
+
this.clearMaxWait();
|
|
343
|
+
}
|
|
344
|
+
clearMaxWait() {
|
|
336
345
|
if (this.maxWaitTimer) {
|
|
337
346
|
clearTimeout(this.maxWaitTimer);
|
|
338
347
|
this.maxWaitTimer = null;
|
|
339
348
|
}
|
|
340
349
|
}
|
|
341
|
-
fire(callback) {
|
|
342
|
-
this.lastFireTime = performance.now();
|
|
343
|
-
callback();
|
|
344
|
-
}
|
|
345
350
|
};
|
|
346
351
|
|
|
347
352
|
// src/project.ts
|
|
@@ -454,7 +459,6 @@ var TypstProject = class {
|
|
|
454
459
|
async setText(path, content) {
|
|
455
460
|
const p = normalizePath(path);
|
|
456
461
|
if (this.contentByPath.get(p) === content) return;
|
|
457
|
-
this.contentByPath.set(p, content);
|
|
458
462
|
await Promise.all([
|
|
459
463
|
this.compiler.setText(p, content),
|
|
460
464
|
this.analyzer?.didChange(
|
|
@@ -462,19 +466,37 @@ var TypstProject = class {
|
|
|
462
466
|
content
|
|
463
467
|
) ?? Promise.resolve()
|
|
464
468
|
]);
|
|
469
|
+
this.contentByPath.set(p, content);
|
|
465
470
|
this.scheduleCompile();
|
|
466
471
|
}
|
|
467
472
|
/**
|
|
468
|
-
* Add or overwrite a JSON file.
|
|
469
|
-
*
|
|
473
|
+
* Add or overwrite a JSON file. The analyzer does not track data files, but
|
|
474
|
+
* if `path` was previously tracked as text the analyzer document is closed
|
|
475
|
+
* and the text entry retired so the three views stay consistent.
|
|
470
476
|
*/
|
|
471
477
|
async setJson(path, value) {
|
|
472
|
-
|
|
478
|
+
const p = normalizePath(path);
|
|
479
|
+
const wasText = this.contentByPath.has(p);
|
|
480
|
+
await Promise.all([
|
|
481
|
+
this.compiler.setJson(p, value),
|
|
482
|
+
wasText ? this.analyzer?.didClose(pathToAnalyzerUri(p, this.analyzerUriRoot)) : void 0
|
|
483
|
+
]);
|
|
484
|
+
this.contentByPath.delete(p);
|
|
473
485
|
this.scheduleCompile();
|
|
474
486
|
}
|
|
475
|
-
/**
|
|
487
|
+
/**
|
|
488
|
+
* Add or overwrite a binary file. If `path` was previously tracked as text,
|
|
489
|
+
* the analyzer document is closed and the text entry retired in the same
|
|
490
|
+
* call so the compiler / analyzer / project views stay consistent.
|
|
491
|
+
*/
|
|
476
492
|
async setBinary(path, content) {
|
|
477
|
-
|
|
493
|
+
const p = normalizePath(path);
|
|
494
|
+
const wasText = this.contentByPath.has(p);
|
|
495
|
+
await Promise.all([
|
|
496
|
+
this.compiler.setBinary(p, content),
|
|
497
|
+
wasText ? this.analyzer?.didClose(pathToAnalyzerUri(p, this.analyzerUriRoot)) : void 0
|
|
498
|
+
]);
|
|
499
|
+
this.contentByPath.delete(p);
|
|
478
500
|
this.scheduleCompile();
|
|
479
501
|
}
|
|
480
502
|
/**
|
|
@@ -484,24 +506,37 @@ var TypstProject = class {
|
|
|
484
506
|
* always go through (no content cache, so no dedup).
|
|
485
507
|
*/
|
|
486
508
|
async setMany(files) {
|
|
509
|
+
const canonical = /* @__PURE__ */ new Map();
|
|
510
|
+
for (const [path, content] of Object.entries(files)) {
|
|
511
|
+
canonical.set(normalizePath(path), content);
|
|
512
|
+
}
|
|
487
513
|
const normalized = {};
|
|
488
514
|
const analyzerDocs = {};
|
|
489
|
-
|
|
490
|
-
|
|
515
|
+
const textUpdates = [];
|
|
516
|
+
const analyzerCloses = [];
|
|
517
|
+
const binaryRetirements = [];
|
|
518
|
+
for (const [p, content] of canonical) {
|
|
491
519
|
if (typeof content !== "string") {
|
|
492
520
|
normalized[p] = content;
|
|
521
|
+
if (this.contentByPath.has(p)) {
|
|
522
|
+
analyzerCloses.push(pathToAnalyzerUri(p, this.analyzerUriRoot));
|
|
523
|
+
binaryRetirements.push(p);
|
|
524
|
+
}
|
|
493
525
|
continue;
|
|
494
526
|
}
|
|
495
527
|
if (this.contentByPath.get(p) === content) continue;
|
|
496
|
-
|
|
528
|
+
textUpdates.push([p, content]);
|
|
497
529
|
normalized[p] = content;
|
|
498
530
|
analyzerDocs[pathToAnalyzerUri(p, this.analyzerUriRoot)] = content;
|
|
499
531
|
}
|
|
500
532
|
if (Object.keys(normalized).length === 0) return;
|
|
501
533
|
await Promise.all([
|
|
502
534
|
this.compiler.setMany(normalized),
|
|
503
|
-
this.analyzer?.didChangeMany(analyzerDocs) ?? Promise.resolve()
|
|
535
|
+
this.analyzer?.didChangeMany(analyzerDocs) ?? Promise.resolve(),
|
|
536
|
+
analyzerCloses.length > 0 ? this.analyzer?.didCloseMany(analyzerCloses) ?? Promise.resolve() : Promise.resolve()
|
|
504
537
|
]);
|
|
538
|
+
for (const [p, content] of textUpdates) this.contentByPath.set(p, content);
|
|
539
|
+
for (const p of binaryRetirements) this.contentByPath.delete(p);
|
|
505
540
|
this.scheduleCompile();
|
|
506
541
|
}
|
|
507
542
|
/**
|
|
@@ -510,11 +545,12 @@ var TypstProject = class {
|
|
|
510
545
|
*/
|
|
511
546
|
async remove(path) {
|
|
512
547
|
const p = normalizePath(path);
|
|
513
|
-
const wasText = this.contentByPath.
|
|
548
|
+
const wasText = this.contentByPath.has(p);
|
|
514
549
|
await Promise.all([
|
|
515
550
|
this.compiler.remove(p),
|
|
516
551
|
wasText ? this.analyzer?.didClose(pathToAnalyzerUri(p, this.analyzerUriRoot)) : void 0
|
|
517
552
|
]);
|
|
553
|
+
this.contentByPath.delete(p);
|
|
518
554
|
this.scheduleCompile();
|
|
519
555
|
}
|
|
520
556
|
/** Clear all files from both compiler VFS and analyzer document set. */
|
|
@@ -523,11 +559,11 @@ var TypstProject = class {
|
|
|
523
559
|
this.contentByPath.keys(),
|
|
524
560
|
(p) => pathToAnalyzerUri(p, this.analyzerUriRoot)
|
|
525
561
|
);
|
|
526
|
-
this.contentByPath.clear();
|
|
527
562
|
await Promise.all([
|
|
528
563
|
this.compiler.clear(),
|
|
529
564
|
uris.length > 0 ? this.analyzer?.didCloseMany(uris) : void 0
|
|
530
565
|
]);
|
|
566
|
+
this.contentByPath.clear();
|
|
531
567
|
this.scheduleCompile();
|
|
532
568
|
}
|
|
533
569
|
/**
|
|
@@ -587,31 +623,27 @@ var TypstProject = class {
|
|
|
587
623
|
}
|
|
588
624
|
/**
|
|
589
625
|
* Request completion for `path` at `position`, using `source` as the
|
|
590
|
-
* current document state.
|
|
591
|
-
*
|
|
626
|
+
* current document state. Pure analyzer query — neither the compiler VFS
|
|
627
|
+
* nor project tracking (`files`/`getText`) is touched. Throws when no
|
|
592
628
|
* analyzer is attached.
|
|
593
629
|
*/
|
|
594
630
|
completion(path, source, position) {
|
|
595
631
|
const analyzer = this.requireAnalyzer("completion");
|
|
596
|
-
const p = normalizePath(path);
|
|
597
|
-
this.contentByPath.set(p, source);
|
|
598
632
|
return analyzer.completion(
|
|
599
|
-
pathToAnalyzerUri(
|
|
633
|
+
pathToAnalyzerUri(normalizePath(path), this.analyzerUriRoot),
|
|
600
634
|
source,
|
|
601
635
|
position
|
|
602
636
|
);
|
|
603
637
|
}
|
|
604
638
|
/**
|
|
605
639
|
* Request hover for `path` at `position`, using `source` as the current
|
|
606
|
-
* document state.
|
|
607
|
-
* when no analyzer is attached.
|
|
640
|
+
* document state. Pure analyzer query — neither the compiler VFS nor
|
|
641
|
+
* project tracking is touched. Throws when no analyzer is attached.
|
|
608
642
|
*/
|
|
609
643
|
hover(path, source, position) {
|
|
610
644
|
const analyzer = this.requireAnalyzer("hover");
|
|
611
|
-
const p = normalizePath(path);
|
|
612
|
-
this.contentByPath.set(p, source);
|
|
613
645
|
return analyzer.hover(
|
|
614
|
-
pathToAnalyzerUri(
|
|
646
|
+
pathToAnalyzerUri(normalizePath(path), this.analyzerUriRoot),
|
|
615
647
|
source,
|
|
616
648
|
position
|
|
617
649
|
);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/analyzer.ts","../src/rpc.ts","../src/identifiers.ts","../src/compiler.ts","../src/formatter.ts","../src/compile-scheduler.ts","../src/project.ts","../src/renderer.ts"],"sourcesContent":["import * as Comlink from \"comlink\";\nimport type {\n LspCompletionResponse,\n LspHover,\n LspPosition,\n} from \"./analyzer-types.js\";\nimport type { AnalyzerWorker } from \"./analyzer-worker.js\";\nimport { createAnalyzerWorker } from \"./rpc.js\";\n\nexport interface TypstAnalyzerOptions {\n /**\n * Explicit Worker instance. When omitted, an inlined blob worker is created automatically.\n * Use this for Vite apps:\n * `await TypstAnalyzer.create({ worker: new Worker(new URL('typst-web-service/analyzer-worker', import.meta.url), { type: 'module' }) })`\n */\n worker?: Worker;\n /**\n * URL to the tinymist WASM binary.\n * Required — there is no default CDN URL for tinymist-web.\n */\n wasmUrl: string;\n}\n\n/**\n * Manages a tinymist language server in a Web Worker. Provides LSP-based\n * completion and hover for Typst documents.\n *\n * const analyzer = await TypstAnalyzer.create({ wasmUrl: '...' });\n */\nexport class TypstAnalyzer {\n private readonly proxy: Comlink.Remote<AnalyzerWorker>;\n private readonly worker: Worker;\n private versionCounter = 0;\n /**\n * Last content pushed to the worker per URI. Presence is the source of\n * truth for \"is this URI opened on the worker?\"; value drives own-RPC dedup.\n */\n private readonly content = new Map<string, string>();\n\n private constructor(worker: Worker, proxy: Comlink.Remote<AnalyzerWorker>) {\n this.worker = worker;\n this.proxy = proxy;\n }\n\n static async create(options: TypstAnalyzerOptions): Promise<TypstAnalyzer> {\n const worker = options.worker ?? createAnalyzerWorker();\n const proxy = Comlink.wrap<AnalyzerWorker>(worker);\n const absoluteWasmUrl = new URL(options.wasmUrl, globalThis.location?.href)\n .href;\n\n const analyzer = new TypstAnalyzer(worker, proxy);\n\n await proxy.init(absoluteWasmUrl);\n\n return analyzer;\n }\n\n async didOpen(uri: string, content: string): Promise<void> {\n this.content.set(uri, content);\n await this.proxy.didOpen(uri, content);\n }\n\n async didClose(uri: string): Promise<void> {\n if (!this.content.delete(uri)) return;\n await this.proxy.didClose(uri);\n }\n\n /**\n * Notify the analyzer that a document has changed. Skips the RPC when the\n * content matches what the worker last saw.\n */\n async didChange(uri: string, content: string): Promise<void> {\n if (!this.content.has(uri)) {\n await this.didOpen(uri, content);\n return;\n }\n if (this.content.get(uri) === content) return;\n this.content.set(uri, content);\n const version = ++this.versionCounter;\n await this.proxy.didChange(uri, version, content);\n }\n\n /**\n * Batch document changes. Splits inputs into opens (first-time URIs) and\n * changes (already-open URIs) and sends them in a single worker roundtrip.\n * Skips unchanged documents; returns without an RPC if nothing is pending.\n */\n async didChangeMany(docs: Record<string, string>): Promise<void> {\n const opens: Array<{ uri: string; content: string }> = [];\n const changes: Array<{ uri: string; version: number; content: string }> =\n [];\n for (const [uri, content] of Object.entries(docs)) {\n if (!this.content.has(uri)) {\n opens.push({ uri, content });\n this.content.set(uri, content);\n } else if (this.content.get(uri) !== content) {\n changes.push({ uri, version: ++this.versionCounter, content });\n this.content.set(uri, content);\n }\n }\n if (opens.length === 0 && changes.length === 0) return;\n await this.proxy.didChangeMany(opens, changes);\n }\n\n /**\n * Batch document closes. Filters to currently-open URIs and sends the set\n * in a single worker roundtrip.\n */\n async didCloseMany(uris: string[]): Promise<void> {\n const toClose: string[] = [];\n for (const uri of uris) {\n if (this.content.delete(uri)) toClose.push(uri);\n }\n if (toClose.length === 0) return;\n await this.proxy.didCloseMany(toClose);\n }\n\n /**\n * Request completion at `position` for `uri`, with `content` as the current\n * document state. Bundles the didChange notification with the request in one\n * worker roundtrip; degrades to a plain completion request when the worker\n * already has this exact content.\n */\n async completion(\n uri: string,\n content: string,\n position: LspPosition,\n ): Promise<LspCompletionResponse> {\n if (this.content.get(uri) === content) {\n return this.proxy.completion(uri, position);\n }\n const isOpen = this.content.has(uri);\n this.content.set(uri, content);\n const version = ++this.versionCounter;\n return this.proxy.completionWithDoc(\n uri,\n version,\n content,\n position,\n isOpen ? \"change\" : \"open\",\n );\n }\n\n /**\n * Request hover at `position` for `uri`, with `content` as the current\n * document state. Bundles the didChange notification with the request in one\n * worker roundtrip; degrades to a plain hover request when the worker\n * already has this exact content.\n */\n async hover(\n uri: string,\n content: string,\n position: LspPosition,\n ): Promise<LspHover | null> {\n if (this.content.get(uri) === content) {\n return this.proxy.hover(uri, position);\n }\n const isOpen = this.content.has(uri);\n this.content.set(uri, content);\n const version = ++this.versionCounter;\n return this.proxy.hoverWithDoc(\n uri,\n version,\n content,\n position,\n isOpen ? \"change\" : \"open\",\n );\n }\n\n destroy(): void {\n this.proxy[Comlink.releaseProxy]();\n this.worker.terminate();\n }\n}\n","// Injected at build time by tsup (see tsup.config.ts)\ndeclare const __WORKER_CODE__: string;\ndeclare const __ANALYZER_WORKER_CODE__: string;\n\n/** Create a Worker from an inlined code string, auto-revoking the blob URL on terminate. */\nfunction createBlobWorker(code: string): Worker {\n const blob = new Blob([code], { type: \"application/javascript\" });\n const url = URL.createObjectURL(blob);\n const worker = new Worker(url);\n const origTerminate = worker.terminate.bind(worker);\n worker.terminate = () => {\n origTerminate();\n URL.revokeObjectURL(url);\n };\n return worker;\n}\n\n/** Create a blob Worker from the inlined compiler worker code. */\nexport function createWorker(): Worker {\n return createBlobWorker(__WORKER_CODE__);\n}\n\n/** Create a blob Worker from the inlined analyzer worker code. */\nexport function createAnalyzerWorker(): Worker {\n return createBlobWorker(__ANALYZER_WORKER_CODE__);\n}\n","/**\n * Two addressing schemes live in this codebase. This file is the only place\n * that knows how they relate. Keeping the vocabulary and conversions colocated\n * means a new contributor can read one file and understand the model.\n *\n * 1. Path — \"/main.typ\". Always leading-slash, always forward slashes.\n * Used by the compiler VFS, the `TypstProject` public API,\n * and the `typstFilePath` facet. Addresses a file within\n * the project.\n *\n * 2. AnalyzerUri — \"untitled:project/main.typ\". What tinymist's LSP expects.\n * Derived from a Path plus the analyzer URI root.\n *\n * The types below are type aliases, not branded/opaque types. That's\n * deliberate: callers can pass string literals where a Path is expected\n * without wrapping, and the aliases only serve as self-documenting\n * signatures. Ingress points still need to call `normalizePath` to defend\n * against un-normalized input — the alias does not imply the string has\n * been normalized.\n */\n\n/** `/path/to/file.typ` — leading-slash, forward slashes only. */\nexport type Path = string;\n\n/** `untitled:project/path/to/file.typ` — what tinymist's LSP consumes. */\nexport type AnalyzerUri = string;\n\n/** Ensure a path starts with a leading slash. Idempotent. */\nexport function normalizePath(path: string): Path {\n return path.startsWith(\"/\") ? path : `/${path}`;\n}\n\n/**\n * Normalize an analyzer URI root. Ensures leading slash, strips trailing\n * slashes. Special-cases \"/\" → \"\" so URIs don't end up with a leading\n * `untitled://`.\n */\nexport function normalizeRoot(rootPath: string): string {\n const root = normalizePath(rootPath);\n return root === \"/\" ? \"\" : root.replace(/\\/+$/, \"\");\n}\n\n/**\n * Build the analyzer URI for a given project path. `root` is expected to\n * already be normalized (see `normalizeRoot`).\n *\n * pathToAnalyzerUri(\"/main.typ\", \"/project\") -> \"untitled:project/main.typ\"\n */\nexport function pathToAnalyzerUri(path: Path, root: string): AnalyzerUri {\n const bare = root.replace(/^\\//, \"\");\n return `untitled:${bare}${normalizePath(path)}`;\n}\n","import * as Comlink from \"comlink\";\nimport { createWorker } from \"./rpc.js\";\nimport type { DiagnosticMessage } from \"./types.js\";\nimport type { CompilerWorker } from \"./compiler-worker.js\";\n\ndeclare const __TYPST_TS_WEB_COMPILER_VERSION__: string;\n\nexport interface CompileResult {\n diagnostics: DiagnosticMessage[];\n /** Vector artifact bytes from the compiler, usable with TypstRenderer for SVG rendering. */\n vector?: Uint8Array;\n}\n\nexport interface TypstCompilerOptions {\n /**\n * Explicit Worker instance. When omitted, an inlined blob worker is created automatically.\n * Use this for Vite apps to get proper source maps:\n * `await TypstCompiler.create({ worker: new Worker(new URL('typst-web-service/compiler-worker', import.meta.url)) })`\n */\n worker?: Worker;\n /**\n * URL to the typst-ts-web-compiler WASM binary.\n * Defaults to the matching version on jsDelivr CDN.\n * Override with a local asset URL for offline support or faster load:\n * `new URL('@myriaddreamin/typst-ts-web-compiler/pkg/typst_ts_web_compiler_bg.wasm', import.meta.url).href`\n */\n wasmUrl?: string;\n /** Font URLs to load into the Typst compiler. Defaults to Roboto from jsDelivr. */\n fonts?: string[];\n /**\n * Enable fetching @preview/ packages from packages.typst.org on demand.\n * Default: true.\n */\n packages?: boolean;\n}\n\nconst DEFAULT_FONTS = [\n \"https://cdn.jsdelivr.net/npm/roboto-font@0.1.0/fonts/Roboto/roboto-regular-webfont.ttf\",\n];\n\nconst DEFAULT_WASM_URL = `https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-web-compiler@${__TYPST_TS_WEB_COMPILER_VERSION__}/pkg/typst_ts_web_compiler_bg.wasm`;\n\n/**\n * Manages a Typst compiler worker. Create one instance and share it across\n * all extensions (linter, autocomplete, preview, etc.).\n *\n * await TypstCompiler.create() // blob worker, defaults\n * await TypstCompiler.create({ wasmUrl: '...' }) // blob worker, custom WASM\n * await TypstCompiler.create({ worker: myWorker }) // explicit Worker (Vite)\n * await TypstCompiler.create({ worker: myWorker, fonts: [...] }) // explicit Worker + options\n */\nexport class TypstCompiler {\n private readonly proxy: Comlink.Remote<CompilerWorker>;\n private readonly worker: Worker;\n private readonly encoder = new TextEncoder();\n /** Last text content pushed per path. Drives own-RPC dedup. Invalidated by binary writes. */\n private readonly content = new Map<string, string>();\n\n private constructor(worker: Worker, proxy: Comlink.Remote<CompilerWorker>) {\n this.worker = worker;\n this.proxy = proxy;\n }\n\n static async create(\n options: TypstCompilerOptions = {},\n ): Promise<TypstCompiler> {\n const worker = options.worker ?? createWorker();\n const proxy = Comlink.wrap<CompilerWorker>(worker);\n\n await proxy.init(\n options.wasmUrl ?? DEFAULT_WASM_URL,\n options.fonts ?? DEFAULT_FONTS,\n options.packages ?? true,\n );\n\n return new TypstCompiler(worker, proxy);\n }\n\n /**\n * Compile whatever is currently in the VFS (populated via\n * setText/setBinary/setJson/setMany). Defaults to compiling \"/main.typ\";\n * override with `entry`.\n */\n compile(entry?: string): Promise<CompileResult> {\n return this.proxy.compile(entry);\n }\n\n /**\n * Compile to PDF. Operates on whatever is currently in the VFS (populated\n * via setText/setBinary/setJson/setMany). Defaults to compiling \"/main.typ\";\n * override with `entry`.\n */\n compilePdf(entry?: string): Promise<Uint8Array> {\n return this.proxy.compilePdf(entry);\n }\n\n /**\n * Add or overwrite a text file in the virtual compiler filesystem. Skips\n * the worker RPC when `source` matches the last value pushed for `path`.\n */\n async setText(path: string, source: string): Promise<void> {\n if (this.content.get(path) === source) return;\n this.content.set(path, source);\n return this.proxy.mapShadow(path, this.encoder.encode(source));\n }\n\n /** Add or overwrite a JSON file in the virtual compiler filesystem. */\n setJson(\n path: string,\n value: unknown,\n replacer?: (this: unknown, key: string, value: unknown) => unknown,\n space?: string | number,\n ): Promise<void> {\n return this.setText(path, JSON.stringify(value, replacer, space));\n }\n\n /**\n * Add or overwrite multiple files in the virtual compiler filesystem in a\n * single worker roundtrip. Strings are UTF-8 encoded; Uint8Arrays are\n * passed through. Text entries unchanged since their last push are skipped;\n * binary entries always push and invalidate the text cache for their path.\n */\n async setMany(files: Record<string, string | Uint8Array>): Promise<void> {\n const encoded: Record<string, Uint8Array> = {};\n for (const [path, content] of Object.entries(files)) {\n if (typeof content === \"string\") {\n if (this.content.get(path) === content) continue;\n this.content.set(path, content);\n encoded[path] = this.encoder.encode(content);\n } else {\n this.content.delete(path);\n encoded[path] = content;\n }\n }\n if (Object.keys(encoded).length === 0) return;\n return this.proxy.mapShadowMany(encoded);\n }\n\n /** Add or overwrite a binary file in the virtual compiler filesystem. */\n setBinary(\n path: string,\n content: ArrayBuffer | ArrayBufferView,\n ): Promise<void> {\n this.content.delete(path);\n const bytes =\n content instanceof ArrayBuffer\n ? new Uint8Array(content)\n : new Uint8Array(\n content.buffer,\n content.byteOffset,\n content.byteLength,\n );\n return this.proxy.mapShadow(path, bytes);\n }\n\n /** Remove a file from the virtual compiler filesystem. */\n remove(path: string): Promise<void> {\n this.content.delete(path);\n return this.proxy.unmapShadow(path);\n }\n\n /** Clear all virtual files from the compiler filesystem. */\n clear(): Promise<void> {\n this.content.clear();\n return this.proxy.resetShadow();\n }\n\n destroy(): void {\n this.proxy[Comlink.releaseProxy]();\n this.worker.terminate();\n }\n}\n","export interface FormatConfig {\n /** Number of spaces per indentation level. Default: 2 */\n tab_spaces?: number;\n /** Maximum line width. Default: 80 */\n max_width?: number;\n /** Maximum consecutive blank lines allowed. */\n blank_lines_upper_bound?: number;\n /** Collapse consecutive whitespace in markup to a single space. */\n collapse_markup_spaces?: boolean;\n /** Sort import items alphabetically. */\n reorder_import_items?: boolean;\n /** Wrap text in markup to fit within max_width. Implies collapse_markup_spaces. */\n wrap_text?: boolean;\n}\n\nexport interface FormatRangeResult {\n /** Start index (UTF-16) of the actual formatted range. */\n start: number;\n /** End index (UTF-16) of the actual formatted range. */\n end: number;\n /** The formatted text for the range. */\n text: string;\n}\n\ntype TypstyleModule = typeof import(\"@typstyle/typstyle-wasm-bundler\");\n\nlet typstylePromise: Promise<TypstyleModule> | null = null;\n\nfunction getTypstyle(): Promise<TypstyleModule> {\n if (!typstylePromise) {\n typstylePromise = import(\"@typstyle/typstyle-wasm-bundler\").catch((err) => {\n typstylePromise = null;\n throw err;\n });\n }\n return typstylePromise;\n}\n\n/**\n * Typst code formatter powered by typstyle.\n *\n * Runs on the main thread — typstyle is lightweight and fast.\n * The WASM module is loaded lazily on first format call.\n * Requires a bundler that supports WASM imports (e.g. Vite, webpack).\n *\n * const formatter = TypstFormatter.create({ tab_spaces: 2, max_width: 80 });\n * const formatted = await formatter.format(source);\n */\nexport class TypstFormatter {\n private config: FormatConfig;\n\n private constructor(config: FormatConfig = {}) {\n this.config = config;\n // Eagerly start loading WASM so it's ready by first use.\n // Swallow preload errors — they'll surface on first format() call.\n getTypstyle().catch(() => {});\n }\n\n static create(config: FormatConfig = {}): TypstFormatter {\n return new TypstFormatter(config);\n }\n\n /** Format an entire Typst source string. */\n async format(source: string): Promise<string> {\n const typstyle = await getTypstyle();\n return typstyle.format(source, this.config);\n }\n\n /** Format a range within a Typst source string. Indices are UTF-16 code units. */\n async formatRange(\n source: string,\n start: number,\n end: number,\n ): Promise<FormatRangeResult> {\n const typstyle = await getTypstyle();\n const result = typstyle.format_range(source, start, end, this.config);\n return { start: result.start, end: result.end, text: result.text };\n }\n}\n","interface CompileSchedulerOptions {\n /** Minimum idle time (ms) after the last request before firing. Default: 0. */\n debounceMs?: number;\n /**\n * Maximum time (ms) the debounce may keep deferring during continuous\n * requests. Guarantees a fire at least this often so users see progress\n * while typing. Default: 0 (no cap).\n */\n maxWaitMs?: number;\n}\n\n/**\n * Debounce helper with a max-wait ceiling for coalescing compile requests. A\n * burst of `schedule(cb)` calls within `debounceMs` collapses into one fire;\n * during sustained bursts, `maxWaitMs` caps how long the debounce may keep\n * deferring.\n */\nexport class CompileScheduler {\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private maxWaitTimer: ReturnType<typeof setTimeout> | null = null;\n private lastFireTime = 0;\n\n constructor(private readonly options: CompileSchedulerOptions = {}) {}\n\n schedule(callback: () => void): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n\n const delay = Math.max(0, this.options.debounceMs ?? 0);\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null;\n this.fire(callback);\n }, delay);\n\n const maxWait = this.options.maxWaitMs;\n if (maxWait != null && maxWait > 0 && !this.maxWaitTimer) {\n const wait = Math.max(\n 0,\n maxWait - (performance.now() - this.lastFireTime),\n );\n this.maxWaitTimer = setTimeout(() => {\n this.maxWaitTimer = null;\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n this.fire(callback);\n }\n }, wait);\n }\n }\n\n /** Cancel any pending scheduled fire without calling the callback. */\n cancel(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n if (this.maxWaitTimer) {\n clearTimeout(this.maxWaitTimer);\n this.maxWaitTimer = null;\n }\n }\n\n private fire(callback: () => void): void {\n this.lastFireTime = performance.now();\n callback();\n }\n}\n","import type { TypstAnalyzer } from \"./analyzer.js\";\nimport type {\n LspCompletionResponse,\n LspHover,\n LspPosition,\n} from \"./analyzer-types.js\";\nimport { CompileScheduler } from \"./compile-scheduler.js\";\nimport type { CompileResult, TypstCompiler } from \"./compiler.js\";\nimport {\n normalizePath,\n normalizeRoot,\n type Path,\n pathToAnalyzerUri,\n} from \"./identifiers.js\";\n\nexport interface TypstProjectOptions {\n compiler: TypstCompiler;\n /**\n * Optional analyzer. When provided, text file operations also sync with the\n * analyzer so completions / hover reflect the current state.\n */\n analyzer?: TypstAnalyzer;\n /** Default entry file path. Default: \"/main.typ\". */\n entry?: string;\n /**\n * Prefix used to build the `untitled:` URIs handed to the analyzer.\n * Default: \"/project\". A path of `/main.typ` becomes\n * `untitled:project/main.typ`. Only affects URI construction — the compiler\n * and project VFS use the raw paths unchanged.\n */\n analyzerUriRoot?: string;\n /**\n * Scheduling for auto-compiles after VFS mutations. Mutations debounce by\n * `debounceMs`; `maxWaitMs` caps how long the debounce can keep deferring\n * during sustained edits so the user still sees progress.\n */\n autoCompile?: AutoCompileOptions;\n}\n\nexport interface AutoCompileOptions {\n /**\n * Idle time (ms) after the last VFS mutation before a compile fires.\n * Default: 0 — compile fires on the next macrotask. Set higher (e.g. 150) to\n * coalesce rapid edits.\n */\n debounceMs?: number;\n /**\n * Maximum time (ms) the debounce is allowed to defer a compile during\n * sustained mutation bursts. Default: 0 (no cap — pure debounce).\n */\n maxWaitMs?: number;\n}\n\nconst DEFAULT_ENTRY = \"/main.typ\";\nconst DEFAULT_ROOT = \"/project\";\n\n/**\n * Coordinates a compiler + analyzer pair for multi-file Typst projects.\n *\n * Owns the project's virtual filesystem state. Editors push incremental\n * `setText` updates as the user types; the project mirrors those edits to\n * both the compiler's shadow VFS and the analyzer's open-document set, then\n * compiles or services LSP requests against the current state.\n *\n * const project = new TypstProject({ compiler, analyzer });\n * await project.setMany({ \"/main.typ\": \"...\", \"/utils.typ\": \"...\" });\n * const result = await project.compile();\n */\nexport type CompileListener = (result: CompileResult) => void;\n\nfunction errorAsCompileResult(\n err: unknown,\n paths: readonly string[],\n): CompileResult {\n const message = err instanceof Error ? err.message : String(err);\n return {\n diagnostics: paths.map((path) => ({\n package: \"\",\n path,\n severity: \"Error\",\n range: { startLine: 0, startCol: 0, endLine: 0, endCol: 1 },\n message,\n })),\n };\n}\n\nexport class TypstProject {\n private readonly compiler: TypstCompiler;\n private readonly analyzer?: TypstAnalyzer;\n private readonly analyzerUriRoot: string;\n /**\n * Tracked text files: path → latest content observed. Presence in this map\n * is the source of truth for \"is this a tracked text file?\"; insertion\n * order drives the `files` getter. Per-sink dedup lives in the compiler and\n * analyzer.\n */\n private readonly contentByPath = new Map<Path, string>();\n private readonly compileListeners = new Set<CompileListener>();\n private readonly scheduler: CompileScheduler;\n private compileVersion = 0;\n private _lastResult: CompileResult | undefined;\n private _entry: Path;\n private destroyed = false;\n\n private invokeListener(\n listener: CompileListener,\n result: CompileResult,\n ): void {\n try {\n listener(result);\n } catch (err) {\n console.error(\"[typst] compile listener threw:\", err);\n }\n }\n\n constructor(options: TypstProjectOptions) {\n this.compiler = options.compiler;\n this.analyzer = options.analyzer;\n this.analyzerUriRoot = normalizeRoot(\n options.analyzerUriRoot ?? DEFAULT_ROOT,\n );\n this._entry = normalizePath(options.entry ?? DEFAULT_ENTRY);\n this.scheduler = new CompileScheduler({\n debounceMs: options.autoCompile?.debounceMs,\n maxWaitMs: options.autoCompile?.maxWaitMs,\n });\n }\n\n /**\n * Schedule an auto-compile after VFS mutations. Coalesces rapid calls via\n * the configured debounce/throttle. Errors surface through `onCompile`\n * listeners via a synthetic diagnostic; callers awaiting a specific compile\n * should call `compile()` directly.\n */\n private scheduleCompile(): void {\n if (this.destroyed) return;\n this.scheduler.schedule(() => {\n this.compile().catch((err) => console.error(\"[typst]\", err));\n });\n }\n\n /** Current entry file path. Assign to change the sticky entry used by subsequent `compile()` calls. */\n get entry(): Path {\n return this._entry;\n }\n\n set entry(path: Path) {\n const next = normalizePath(path);\n if (next === this._entry) return;\n this._entry = next;\n this.scheduleCompile();\n }\n\n /** Whether an analyzer is attached. */\n get hasAnalyzer(): boolean {\n return this.analyzer !== undefined;\n }\n\n /**\n * Most recent compile result, or `undefined` before the first compile has\n * settled. Useful for lazy-mounted UI that subscribes after boot and needs\n * an initial value.\n */\n get lastResult(): CompileResult | undefined {\n return this._lastResult;\n }\n\n /**\n * Snapshot of tracked text file paths, in insertion order. Updated by\n * `setText`, `setMany`, `remove`, and `clear`. Returns a fresh array — mutate\n * freely without affecting project state.\n */\n get files(): Path[] {\n return [...this.contentByPath.keys()];\n }\n\n /**\n * Current text content for a tracked file, or `undefined` if the path was\n * never written via `setText`/`setMany` (or was removed). Read-through to the\n * project's sync cache — lets consumers avoid shadowing the VFS themselves.\n */\n getText(path: Path): string | undefined {\n return this.contentByPath.get(normalizePath(path));\n }\n\n /**\n * Add or overwrite a text file. Goes to the compiler's VFS and, when an\n * analyzer is attached, to the analyzer as a document change. No-op when\n * the tracked path already has this exact content — skips both worker RPCs\n * and the auto-scheduled compile.\n */\n async setText(path: Path, content: string): Promise<void> {\n const p = normalizePath(path);\n if (this.contentByPath.get(p) === content) return;\n this.contentByPath.set(p, content);\n await Promise.all([\n this.compiler.setText(p, content),\n this.analyzer?.didChange(\n pathToAnalyzerUri(p, this.analyzerUriRoot),\n content,\n ) ?? Promise.resolve(),\n ]);\n this.scheduleCompile();\n }\n\n /**\n * Add or overwrite a JSON file. Compiler-only — the analyzer does not track\n * data files.\n */\n async setJson(path: Path, value: unknown): Promise<void> {\n await this.compiler.setJson(normalizePath(path), value);\n this.scheduleCompile();\n }\n\n /** Add or overwrite a binary file. Compiler-only. */\n async setBinary(\n path: Path,\n content: ArrayBuffer | ArrayBufferView,\n ): Promise<void> {\n await this.compiler.setBinary(normalizePath(path), content);\n this.scheduleCompile();\n }\n\n /**\n * Batch set multiple files. Strings route to both compiler and analyzer;\n * Uint8Array entries go to the compiler only. Strings matching the last\n * tracked content for their path are skipped on both sinks. Binary entries\n * always go through (no content cache, so no dedup).\n */\n async setMany(files: Record<Path, string | Uint8Array>): Promise<void> {\n const normalized: Record<Path, string | Uint8Array> = {};\n const analyzerDocs: Record<string, string> = {};\n for (const [path, content] of Object.entries(files)) {\n const p = normalizePath(path);\n if (typeof content !== \"string\") {\n normalized[p] = content;\n continue;\n }\n if (this.contentByPath.get(p) === content) continue;\n this.contentByPath.set(p, content);\n normalized[p] = content;\n analyzerDocs[pathToAnalyzerUri(p, this.analyzerUriRoot)] = content;\n }\n if (Object.keys(normalized).length === 0) return;\n await Promise.all([\n this.compiler.setMany(normalized),\n this.analyzer?.didChangeMany(analyzerDocs) ?? Promise.resolve(),\n ]);\n this.scheduleCompile();\n }\n\n /**\n * Remove a file. Always removed from the compiler's VFS; also closed on the\n * analyzer when it was previously tracked as text.\n */\n async remove(path: Path): Promise<void> {\n const p = normalizePath(path);\n const wasText = this.contentByPath.delete(p);\n await Promise.all([\n this.compiler.remove(p),\n wasText\n ? this.analyzer?.didClose(pathToAnalyzerUri(p, this.analyzerUriRoot))\n : undefined,\n ]);\n this.scheduleCompile();\n }\n\n /** Clear all files from both compiler VFS and analyzer document set. */\n async clear(): Promise<void> {\n const uris = Array.from(this.contentByPath.keys(), (p) =>\n pathToAnalyzerUri(p, this.analyzerUriRoot),\n );\n this.contentByPath.clear();\n await Promise.all([\n this.compiler.clear(),\n uris.length > 0 ? this.analyzer?.didCloseMany(uris) : undefined,\n ]);\n this.scheduleCompile();\n }\n\n /**\n * Subscribe to compile results. Fires after every `compile()` whose result is\n * still current (stale results from out-of-order concurrent compiles are\n * dropped). If a compile has already settled, the most recent result is\n * delivered synchronously so late-mounted listeners aren't stuck blank until\n * the next compile. Returns an unsubscribe function.\n */\n onCompile(listener: CompileListener): () => void {\n this.compileListeners.add(listener);\n if (this._lastResult !== undefined) {\n this.invokeListener(listener, this._lastResult);\n }\n return () => {\n this.compileListeners.delete(listener);\n };\n }\n\n /**\n * Compile the current VFS state using the sticky entry. Errors from the\n * underlying compiler are converted into a synthetic error diagnostic so\n * callers and listeners always receive a `CompileResult`. Listeners are\n * notified only for the most recent compile — results from an earlier call\n * that resolves after a later one are suppressed.\n *\n * VFS mutations (`setText`, `remove`, etc.) auto-schedule a debounced\n * compile; call this directly only when you need an awaitable handle on the\n * result (e.g., to flush before rendering to PDF).\n */\n async compile(): Promise<CompileResult> {\n this.scheduler.cancel();\n const version = ++this.compileVersion;\n let result: CompileResult;\n try {\n result = await this.compiler.compile(this._entry);\n } catch (err) {\n // Spread the synthetic error across every tracked text path so the\n // diagnostic is visible no matter which file the user is viewing.\n // Falls back to the entry if nothing is tracked yet.\n const paths =\n this.contentByPath.size > 0\n ? [...this.contentByPath.keys()]\n : [this._entry];\n result = errorAsCompileResult(err, paths);\n }\n if (version === this.compileVersion) {\n this._lastResult = result;\n for (const listener of this.compileListeners) {\n this.invokeListener(listener, result);\n }\n }\n return result;\n }\n\n /** Compile the current VFS state to PDF using the sticky entry. */\n compilePdf(): Promise<Uint8Array> {\n return this.compiler.compilePdf(this._entry);\n }\n\n private requireAnalyzer(operation: string): TypstAnalyzer {\n if (!this.analyzer) {\n throw new Error(`TypstProject: ${operation} requires an analyzer`);\n }\n return this.analyzer;\n }\n\n /**\n * Request completion for `path` at `position`, using `source` as the\n * current document state. One analyzer roundtrip; compiler is not touched —\n * the compile sync path writes to the compiler separately. Throws when no\n * analyzer is attached.\n */\n completion(\n path: Path,\n source: string,\n position: LspPosition,\n ): Promise<LspCompletionResponse> {\n const analyzer = this.requireAnalyzer(\"completion\");\n const p = normalizePath(path);\n this.contentByPath.set(p, source);\n return analyzer.completion(\n pathToAnalyzerUri(p, this.analyzerUriRoot),\n source,\n position,\n );\n }\n\n /**\n * Request hover for `path` at `position`, using `source` as the current\n * document state. One analyzer roundtrip; compiler is not touched. Throws\n * when no analyzer is attached.\n */\n hover(\n path: Path,\n source: string,\n position: LspPosition,\n ): Promise<LspHover | null> {\n const analyzer = this.requireAnalyzer(\"hover\");\n const p = normalizePath(path);\n this.contentByPath.set(p, source);\n return analyzer.hover(\n pathToAnalyzerUri(p, this.analyzerUriRoot),\n source,\n position,\n );\n }\n\n /**\n * Tear down the project and the services it owns. Destroys the attached\n * compiler and analyzer, drops all listeners, and clears VFS tracking state.\n * Idempotent — calling twice is a no-op. After destruction, further calls on\n * the project are not supported; construct a new one.\n *\n * If you need to share a compiler or analyzer across projects, destroy them\n * yourself and don't call this method — the project does not provide an\n * ownership toggle.\n */\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.scheduler.cancel();\n this.compileListeners.clear();\n this.contentByPath.clear();\n this._lastResult = undefined;\n this.compiler.destroy();\n this.analyzer?.destroy();\n }\n}\n","/** Minimal interface for the built TypstRenderer instance. */\nexport interface RendererInstance {\n free(): void;\n create_session(): RendererSession;\n manipulate_data(\n session: RendererSession,\n action: string,\n data: Uint8Array,\n ): void;\n svg_data(session: RendererSession): string;\n}\n\n/** Minimal interface for a TypstRenderer session. */\nexport interface RendererSession {\n free(): void;\n}\n\ntype RendererWasmModule = typeof import(\"@myriaddreamin/typst-ts-renderer\");\n\ndeclare const __TYPST_TS_RENDERER_VERSION__: string;\n\nconst DEFAULT_RENDERER_WASM_URL = `https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-renderer@${__TYPST_TS_RENDERER_VERSION__}/pkg/typst_ts_renderer_bg.wasm`;\n\nlet rendererModulePromise: Promise<RendererWasmModule> | null = null;\n\nfunction getRendererModule(): Promise<RendererWasmModule> {\n if (!rendererModulePromise) {\n rendererModulePromise = import(\"@myriaddreamin/typst-ts-renderer\").catch(\n (err) => {\n rendererModulePromise = null;\n throw err;\n },\n );\n }\n return rendererModulePromise;\n}\n\nexport interface TypstRendererOptions {\n /** URL to the typst-ts-renderer WASM binary. Defaults to jsDelivr CDN. */\n wasmUrl?: string;\n}\n\nexport interface RenderedSvgPage {\n /** Zero-based page index within the document. */\n index: number;\n /** Page width in typographic points. */\n width: number;\n /** Page height in typographic points. */\n height: number;\n /** Standalone SVG string for just this page. */\n svg: string;\n}\n\n/**\n * Converts Typst vector artifacts to SVG strings.\n *\n * The renderer WASM module is loaded lazily on first use.\n *\n * const renderer = TypstRenderer.create();\n * const svg = await renderer.renderSvg(vector);\n */\nexport class TypstRenderer {\n private wasmUrl: string;\n private instance: Promise<RendererInstance> | null = null;\n\n private constructor(options: TypstRendererOptions = {}) {\n this.wasmUrl = options.wasmUrl ?? DEFAULT_RENDERER_WASM_URL;\n // Eagerly start loading the WASM module so it's ready by first use.\n getRendererModule().catch(() => {});\n }\n\n static create(options: TypstRendererOptions = {}): TypstRenderer {\n return new TypstRenderer(options);\n }\n\n private getInstance(): Promise<RendererInstance> {\n if (!this.instance) {\n this.instance = this.#init().catch((err) => {\n this.instance = null;\n throw err;\n });\n }\n return this.instance;\n }\n\n async #init(): Promise<RendererInstance> {\n const mod = await getRendererModule();\n await mod.default({ module_or_path: this.wasmUrl });\n return new mod.TypstRendererBuilder().build();\n }\n\n /** Free the underlying WASM renderer instance. */\n async destroy(): Promise<void> {\n const instance = this.instance;\n this.instance = null;\n if (instance) {\n (await instance).free();\n }\n }\n\n /** Render a Typst vector artifact to an SVG string. */\n async renderSvg(vector: Uint8Array): Promise<string> {\n const renderer = await this.getInstance();\n const session = renderer.create_session();\n try {\n renderer.manipulate_data(session, \"reset\", vector);\n return renderer.svg_data(session);\n } finally {\n session.free();\n }\n }\n\n /**\n * Render a Typst vector artifact into one self-contained SVG string per\n * physical page. The merged SVG is split by `<g class=\"typst-page\">`\n * children; each group's `data-page-width` / `data-page-height` give the\n * page-local viewBox. Shared `<defs>` / `<style>` are duplicated into each\n * page so the output SVGs render independently. Returns an empty array if\n * the document has no page groups.\n */\n async renderSvgPages(vector: Uint8Array): Promise<RenderedSvgPage[]> {\n return splitMergedSvgPages(await this.renderSvg(vector));\n }\n}\n\n// Parsing must use \"text/html\", not \"image/svg+xml\": Typst's merged SVG\n// output has repeatedly failed XML-strict parsing. HTML mode tolerates it\n// and still produces real SVGSVGElement nodes for inline SVG.\nfunction splitMergedSvgPages(svg: string): RenderedSvgPage[] {\n const doc = new DOMParser().parseFromString(svg, \"text/html\");\n const root = doc.querySelector(\"svg\");\n if (!root) return [];\n\n const children = Array.from(root.children);\n const pageGroups = children.filter(\n (el) =>\n el.tagName.toLowerCase() === \"g\" && el.classList.contains(\"typst-page\"),\n );\n if (pageGroups.length === 0) return [];\n\n const sharedHtml = children\n .filter((el) => !el.classList.contains(\"typst-page\"))\n .map((el) => el.outerHTML)\n .join(\"\");\n\n const namespaceAttrs = Array.from(root.attributes)\n .filter((attr) => attr.name === \"xmlns\" || attr.name.startsWith(\"xmlns:\"))\n .map((attr) => `${attr.name}=\"${attr.value}\"`)\n .join(\" \");\n\n return pageGroups.flatMap((group, index) => {\n const width = Number(group.getAttribute(\"data-page-width\")) || 0;\n const height = Number(group.getAttribute(\"data-page-height\")) || 0;\n if (width <= 0 || height <= 0) return [];\n\n const clone = group.cloneNode(true) as Element;\n clone.removeAttribute(\"transform\");\n\n return [\n {\n index,\n width,\n height,\n svg:\n `<svg ${namespaceAttrs} viewBox=\"0 0 ${width} ${height}\" ` +\n `width=\"${width}\" height=\"${height}\">` +\n `${sharedHtml}${clone.outerHTML}</svg>`,\n },\n ];\n });\n}\n"],"mappings":"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","names":["Comlink"]}
|
|
1
|
+
{"version":3,"sources":["../src/analyzer.ts","../src/rpc.ts","../src/identifiers.ts","../src/compiler.ts","../src/formatter.ts","../src/compile-scheduler.ts","../src/project.ts","../src/renderer.ts"],"sourcesContent":["import * as Comlink from \"comlink\";\nimport type {\n LspCompletionResponse,\n LspHover,\n LspPosition,\n} from \"./analyzer-types.js\";\nimport type { AnalyzerWorker } from \"./analyzer-worker.js\";\nimport { createAnalyzerWorker } from \"./rpc.js\";\n\nexport interface TypstAnalyzerOptions {\n /**\n * Explicit Worker instance. When omitted, an inlined blob worker is created automatically.\n * Use this for Vite apps:\n * `await TypstAnalyzer.create({ worker: new Worker(new URL('typst-web-service/analyzer-worker', import.meta.url), { type: 'module' }) })`\n */\n worker?: Worker;\n /**\n * URL to the tinymist WASM binary.\n * Required — there is no default CDN URL for tinymist-web.\n */\n wasmUrl: string;\n}\n\n/**\n * Manages a tinymist language server in a Web Worker. Provides LSP-based\n * completion and hover for Typst documents.\n *\n * const analyzer = await TypstAnalyzer.create({ wasmUrl: '...' });\n */\nexport class TypstAnalyzer {\n private readonly proxy: Comlink.Remote<AnalyzerWorker>;\n private readonly worker: Worker;\n private versionCounter = 0;\n /**\n * Last content pushed to the worker per URI. Presence is the source of\n * truth for \"is this URI opened on the worker?\"; value drives own-RPC dedup.\n */\n private readonly content = new Map<string, string>();\n\n private constructor(worker: Worker, proxy: Comlink.Remote<AnalyzerWorker>) {\n this.worker = worker;\n this.proxy = proxy;\n }\n\n static async create(options: TypstAnalyzerOptions): Promise<TypstAnalyzer> {\n const worker = options.worker ?? createAnalyzerWorker();\n const proxy = Comlink.wrap<AnalyzerWorker>(worker);\n const absoluteWasmUrl = new URL(options.wasmUrl, globalThis.location?.href)\n .href;\n\n const analyzer = new TypstAnalyzer(worker, proxy);\n\n await proxy.init(absoluteWasmUrl);\n\n return analyzer;\n }\n\n async didOpen(uri: string, content: string): Promise<void> {\n await this.proxy.didOpen(uri, content);\n this.content.set(uri, content);\n }\n\n async didClose(uri: string): Promise<void> {\n if (!this.content.has(uri)) return;\n await this.proxy.didClose(uri);\n this.content.delete(uri);\n }\n\n /**\n * Notify the analyzer that a document has changed. Skips the RPC when the\n * content matches what the worker last saw.\n */\n async didChange(uri: string, content: string): Promise<void> {\n if (!this.content.has(uri)) {\n await this.didOpen(uri, content);\n return;\n }\n if (this.content.get(uri) === content) return;\n const version = ++this.versionCounter;\n await this.proxy.didChange(uri, version, content);\n this.content.set(uri, content);\n }\n\n /**\n * Batch document changes. Splits inputs into opens (first-time URIs) and\n * changes (already-open URIs) and sends them in a single worker roundtrip.\n * Skips unchanged documents; returns without an RPC if nothing is pending.\n */\n async didChangeMany(docs: Record<string, string>): Promise<void> {\n const opens: Array<{ uri: string; content: string }> = [];\n const changes: Array<{ uri: string; version: number; content: string }> =\n [];\n const updates: Array<[string, string]> = [];\n for (const [uri, content] of Object.entries(docs)) {\n if (!this.content.has(uri)) {\n opens.push({ uri, content });\n updates.push([uri, content]);\n } else if (this.content.get(uri) !== content) {\n changes.push({ uri, version: ++this.versionCounter, content });\n updates.push([uri, content]);\n }\n }\n if (opens.length === 0 && changes.length === 0) return;\n await this.proxy.didChangeMany(opens, changes);\n for (const [uri, content] of updates) this.content.set(uri, content);\n }\n\n /**\n * Batch document closes. Filters to currently-open URIs and sends the set\n * in a single worker roundtrip.\n */\n async didCloseMany(uris: string[]): Promise<void> {\n const toClose: string[] = [];\n for (const uri of uris) {\n if (this.content.has(uri)) toClose.push(uri);\n }\n if (toClose.length === 0) return;\n await this.proxy.didCloseMany(toClose);\n for (const uri of toClose) this.content.delete(uri);\n }\n\n /**\n * Request completion at `position` for `uri`, with `content` as the current\n * document state. Bundles the didChange notification with the request in one\n * worker roundtrip; degrades to a plain completion request when the worker\n * already has this exact content.\n */\n async completion(\n uri: string,\n content: string,\n position: LspPosition,\n ): Promise<LspCompletionResponse> {\n if (this.content.get(uri) === content) {\n return this.proxy.completion(uri, position);\n }\n const isOpen = this.content.has(uri);\n const version = ++this.versionCounter;\n const result = await this.proxy.completionWithDoc(\n uri,\n version,\n content,\n position,\n isOpen ? \"change\" : \"open\",\n );\n this.content.set(uri, content);\n return result;\n }\n\n /**\n * Request hover at `position` for `uri`, with `content` as the current\n * document state. Bundles the didChange notification with the request in one\n * worker roundtrip; degrades to a plain hover request when the worker\n * already has this exact content.\n */\n async hover(\n uri: string,\n content: string,\n position: LspPosition,\n ): Promise<LspHover | null> {\n if (this.content.get(uri) === content) {\n return this.proxy.hover(uri, position);\n }\n const isOpen = this.content.has(uri);\n const version = ++this.versionCounter;\n const result = await this.proxy.hoverWithDoc(\n uri,\n version,\n content,\n position,\n isOpen ? \"change\" : \"open\",\n );\n this.content.set(uri, content);\n return result;\n }\n\n destroy(): void {\n this.proxy[Comlink.releaseProxy]();\n this.worker.terminate();\n }\n}\n","// Injected at build time by tsup (see tsup.config.ts)\ndeclare const __WORKER_CODE__: string;\ndeclare const __ANALYZER_WORKER_CODE__: string;\n\n/** Create a Worker from an inlined code string, auto-revoking the blob URL on terminate. */\nfunction createBlobWorker(code: string): Worker {\n const blob = new Blob([code], { type: \"application/javascript\" });\n const url = URL.createObjectURL(blob);\n const worker = new Worker(url);\n const origTerminate = worker.terminate.bind(worker);\n worker.terminate = () => {\n origTerminate();\n URL.revokeObjectURL(url);\n };\n return worker;\n}\n\n/** Create a blob Worker from the inlined compiler worker code. */\nexport function createWorker(): Worker {\n return createBlobWorker(__WORKER_CODE__);\n}\n\n/** Create a blob Worker from the inlined analyzer worker code. */\nexport function createAnalyzerWorker(): Worker {\n return createBlobWorker(__ANALYZER_WORKER_CODE__);\n}\n","/**\n * Two addressing schemes live in this codebase. This file is the only place\n * that knows how they relate. Keeping the vocabulary and conversions colocated\n * means a new contributor can read one file and understand the model.\n *\n * 1. Path — \"/main.typ\". Always leading-slash, always forward slashes.\n * Used by the compiler VFS, the `TypstProject` public API,\n * and the `typstFilePath` facet. Addresses a file within\n * the project.\n *\n * 2. AnalyzerUri — \"untitled:project/main.typ\". What tinymist's LSP expects.\n * Derived from a Path plus the analyzer URI root.\n *\n * The types below are type aliases, not branded/opaque types. That's\n * deliberate: callers can pass string literals where a Path is expected\n * without wrapping, and the aliases only serve as self-documenting\n * signatures. Ingress points still need to call `normalizePath` to defend\n * against un-normalized input — the alias does not imply the string has\n * been normalized.\n */\n\n/** `/path/to/file.typ` — leading-slash, forward slashes only. */\nexport type Path = string;\n\n/** `untitled:project/path/to/file.typ` — what tinymist's LSP consumes. */\nexport type AnalyzerUri = string;\n\n/** Ensure a path starts with a leading slash. Idempotent. */\nexport function normalizePath(path: string): Path {\n return path.startsWith(\"/\") ? path : `/${path}`;\n}\n\n/**\n * Normalize an analyzer URI root. Ensures leading slash, strips trailing\n * slashes. Special-cases \"/\" → \"\" so URIs don't end up with a leading\n * `untitled://`.\n */\nexport function normalizeRoot(rootPath: string): string {\n const root = normalizePath(rootPath);\n return root === \"/\" ? \"\" : root.replace(/\\/+$/, \"\");\n}\n\n/**\n * Build the analyzer URI for a given project path. `root` is expected to\n * already be normalized (see `normalizeRoot`).\n *\n * pathToAnalyzerUri(\"/main.typ\", \"/project\") -> \"untitled:project/main.typ\"\n */\nexport function pathToAnalyzerUri(path: Path, root: string): AnalyzerUri {\n const bare = root.replace(/^\\//, \"\");\n return `untitled:${bare}${normalizePath(path)}`;\n}\n","import * as Comlink from \"comlink\";\nimport { createWorker } from \"./rpc.js\";\nimport type { DiagnosticMessage } from \"./types.js\";\nimport type { CompilerWorker } from \"./compiler-worker.js\";\n\ndeclare const __TYPST_TS_WEB_COMPILER_VERSION__: string;\n\nexport interface CompileResult {\n diagnostics: DiagnosticMessage[];\n /** Vector artifact bytes from the compiler, usable with TypstRenderer for SVG rendering. */\n vector?: Uint8Array;\n}\n\nexport interface TypstCompilerOptions {\n /**\n * Explicit Worker instance. When omitted, an inlined blob worker is created automatically.\n * Use this for Vite apps to get proper source maps:\n * `await TypstCompiler.create({ worker: new Worker(new URL('typst-web-service/compiler-worker', import.meta.url)) })`\n */\n worker?: Worker;\n /**\n * URL to the typst-ts-web-compiler WASM binary.\n * Defaults to the matching version on jsDelivr CDN.\n * Override with a local asset URL for offline support or faster load:\n * `new URL('@myriaddreamin/typst-ts-web-compiler/pkg/typst_ts_web_compiler_bg.wasm', import.meta.url).href`\n */\n wasmUrl?: string;\n /** Font URLs to load into the Typst compiler. Defaults to Roboto from jsDelivr. */\n fonts?: string[];\n /**\n * Enable fetching @preview/ packages from packages.typst.org on demand.\n * Default: true.\n */\n packages?: boolean;\n}\n\nconst DEFAULT_FONTS = [\n \"https://cdn.jsdelivr.net/npm/roboto-font@0.1.0/fonts/Roboto/roboto-regular-webfont.ttf\",\n];\n\nconst defaultWasmUrl = () =>\n `https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-web-compiler@${__TYPST_TS_WEB_COMPILER_VERSION__}/pkg/typst_ts_web_compiler_bg.wasm`;\n\n/**\n * Manages a Typst compiler worker. Create one instance and share it across\n * all extensions (linter, autocomplete, preview, etc.).\n *\n * await TypstCompiler.create() // blob worker, defaults\n * await TypstCompiler.create({ wasmUrl: '...' }) // blob worker, custom WASM\n * await TypstCompiler.create({ worker: myWorker }) // explicit Worker (Vite)\n * await TypstCompiler.create({ worker: myWorker, fonts: [...] }) // explicit Worker + options\n */\nexport class TypstCompiler {\n private readonly proxy: Comlink.Remote<CompilerWorker>;\n private readonly worker: Worker;\n private readonly encoder = new TextEncoder();\n /** Last text content pushed per path. Drives own-RPC dedup. Invalidated by binary writes. */\n private readonly content = new Map<string, string>();\n\n private constructor(worker: Worker, proxy: Comlink.Remote<CompilerWorker>) {\n this.worker = worker;\n this.proxy = proxy;\n }\n\n static async create(\n options: TypstCompilerOptions = {},\n ): Promise<TypstCompiler> {\n const worker = options.worker ?? createWorker();\n const proxy = Comlink.wrap<CompilerWorker>(worker);\n\n await proxy.init(\n options.wasmUrl ?? defaultWasmUrl(),\n options.fonts ?? DEFAULT_FONTS,\n options.packages ?? true,\n );\n\n return new TypstCompiler(worker, proxy);\n }\n\n /**\n * Compile whatever is currently in the VFS (populated via\n * setText/setBinary/setJson/setMany). Defaults to compiling \"/main.typ\";\n * override with `entry`.\n */\n compile(entry?: string): Promise<CompileResult> {\n return this.proxy.compile(entry);\n }\n\n /**\n * Compile to PDF. Operates on whatever is currently in the VFS (populated\n * via setText/setBinary/setJson/setMany). Defaults to compiling \"/main.typ\";\n * override with `entry`.\n */\n compilePdf(entry?: string): Promise<Uint8Array> {\n return this.proxy.compilePdf(entry);\n }\n\n /**\n * Add or overwrite a text file in the virtual compiler filesystem. Skips\n * the worker RPC when `source` matches the last value pushed for `path`.\n */\n async setText(path: string, source: string): Promise<void> {\n if (this.content.get(path) === source) return;\n await this.proxy.mapShadow(path, this.encoder.encode(source));\n this.content.set(path, source);\n }\n\n /** Add or overwrite a JSON file in the virtual compiler filesystem. */\n setJson(\n path: string,\n value: unknown,\n replacer?: (this: unknown, key: string, value: unknown) => unknown,\n space?: string | number,\n ): Promise<void> {\n return this.setText(path, JSON.stringify(value, replacer, space));\n }\n\n /**\n * Add or overwrite multiple files in the virtual compiler filesystem in a\n * single worker roundtrip. Strings are UTF-8 encoded; Uint8Arrays are\n * passed through. Text entries unchanged since their last push are skipped;\n * binary entries always push and invalidate the text cache for their path.\n */\n async setMany(files: Record<string, string | Uint8Array>): Promise<void> {\n const encoded: Record<string, Uint8Array> = {};\n const textUpdates: Array<[string, string]> = [];\n const binaryInvalidations: string[] = [];\n for (const [path, content] of Object.entries(files)) {\n if (typeof content === \"string\") {\n if (this.content.get(path) === content) continue;\n textUpdates.push([path, content]);\n encoded[path] = this.encoder.encode(content);\n } else {\n binaryInvalidations.push(path);\n encoded[path] = content;\n }\n }\n if (Object.keys(encoded).length === 0) return;\n await this.proxy.mapShadowMany(encoded);\n for (const [path, content] of textUpdates) this.content.set(path, content);\n for (const path of binaryInvalidations) this.content.delete(path);\n }\n\n /** Add or overwrite a binary file in the virtual compiler filesystem. */\n async setBinary(\n path: string,\n content: ArrayBuffer | ArrayBufferView,\n ): Promise<void> {\n const bytes =\n content instanceof ArrayBuffer\n ? new Uint8Array(content)\n : new Uint8Array(\n content.buffer,\n content.byteOffset,\n content.byteLength,\n );\n await this.proxy.mapShadow(path, bytes);\n this.content.delete(path);\n }\n\n /** Remove a file from the virtual compiler filesystem. */\n async remove(path: string): Promise<void> {\n await this.proxy.unmapShadow(path);\n this.content.delete(path);\n }\n\n /** Clear all virtual files from the compiler filesystem. */\n async clear(): Promise<void> {\n await this.proxy.resetShadow();\n this.content.clear();\n }\n\n destroy(): void {\n this.proxy[Comlink.releaseProxy]();\n this.worker.terminate();\n }\n}\n","export interface FormatConfig {\n /** Number of spaces per indentation level. Default: 2 */\n tab_spaces?: number;\n /** Maximum line width. Default: 80 */\n max_width?: number;\n /** Maximum consecutive blank lines allowed. */\n blank_lines_upper_bound?: number;\n /** Collapse consecutive whitespace in markup to a single space. */\n collapse_markup_spaces?: boolean;\n /** Sort import items alphabetically. */\n reorder_import_items?: boolean;\n /** Wrap text in markup to fit within max_width. Implies collapse_markup_spaces. */\n wrap_text?: boolean;\n}\n\nexport interface FormatRangeResult {\n /** Start index (UTF-16) of the actual formatted range. */\n start: number;\n /** End index (UTF-16) of the actual formatted range. */\n end: number;\n /** The formatted text for the range. */\n text: string;\n}\n\ntype TypstyleModule = typeof import(\"@typstyle/typstyle-wasm-bundler\");\n\nlet typstylePromise: Promise<TypstyleModule> | null = null;\n\nfunction getTypstyle(): Promise<TypstyleModule> {\n if (!typstylePromise) {\n typstylePromise = import(\"@typstyle/typstyle-wasm-bundler\").catch((err) => {\n typstylePromise = null;\n throw err;\n });\n }\n return typstylePromise;\n}\n\n/**\n * Typst code formatter powered by typstyle.\n *\n * Runs on the main thread — typstyle is lightweight and fast.\n * The WASM module is loaded lazily on first format call.\n * Requires a bundler that supports WASM imports (e.g. Vite, webpack).\n *\n * const formatter = TypstFormatter.create({ tab_spaces: 2, max_width: 80 });\n * const formatted = await formatter.format(source);\n */\nexport class TypstFormatter {\n private config: FormatConfig;\n\n private constructor(config: FormatConfig = {}) {\n this.config = config;\n // Eagerly start loading WASM so it's ready by first use.\n // Swallow preload errors — they'll surface on first format() call.\n getTypstyle().catch(() => {});\n }\n\n static create(config: FormatConfig = {}): TypstFormatter {\n return new TypstFormatter(config);\n }\n\n /** Format an entire Typst source string. */\n async format(source: string): Promise<string> {\n const typstyle = await getTypstyle();\n return typstyle.format(source, this.config);\n }\n\n /** Format a range within a Typst source string. Indices are UTF-16 code units. */\n async formatRange(\n source: string,\n start: number,\n end: number,\n ): Promise<FormatRangeResult> {\n const typstyle = await getTypstyle();\n const result = typstyle.format_range(source, start, end, this.config);\n return { start: result.start, end: result.end, text: result.text };\n }\n}\n","interface CompileSchedulerOptions {\n /** Minimum idle time (ms) after the last request before firing. Default: 0. */\n debounceMs?: number;\n /**\n * Maximum time (ms) the debounce may keep deferring during continuous\n * requests. Guarantees a fire at least this often so users see progress\n * while typing. Default: 0 (no cap).\n */\n maxWaitMs?: number;\n}\n\n/**\n * Debounce helper with a max-wait ceiling for coalescing compile requests. A\n * burst of `schedule(cb)` calls within `debounceMs` collapses into one fire;\n * during sustained bursts, `maxWaitMs` caps how long the debounce may keep\n * deferring.\n */\nexport class CompileScheduler {\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private maxWaitTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(private readonly options: CompileSchedulerOptions = {}) {}\n\n schedule(callback: () => void): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n\n const delay = Math.max(0, this.options.debounceMs ?? 0);\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null;\n this.clearMaxWait();\n callback();\n }, delay);\n\n const maxWait = this.options.maxWaitMs;\n if (maxWait != null && maxWait > 0 && !this.maxWaitTimer) {\n this.maxWaitTimer = setTimeout(() => {\n this.maxWaitTimer = null;\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n callback();\n }\n }, maxWait);\n }\n }\n\n /** Cancel any pending scheduled fire without calling the callback. */\n cancel(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.clearMaxWait();\n }\n\n private clearMaxWait(): void {\n if (this.maxWaitTimer) {\n clearTimeout(this.maxWaitTimer);\n this.maxWaitTimer = null;\n }\n }\n}\n","import type { TypstAnalyzer } from \"./analyzer.js\";\nimport type {\n LspCompletionResponse,\n LspHover,\n LspPosition,\n} from \"./analyzer-types.js\";\nimport { CompileScheduler } from \"./compile-scheduler.js\";\nimport type { CompileResult, TypstCompiler } from \"./compiler.js\";\nimport {\n normalizePath,\n normalizeRoot,\n type Path,\n pathToAnalyzerUri,\n} from \"./identifiers.js\";\n\nexport interface TypstProjectOptions {\n compiler: TypstCompiler;\n /**\n * Optional analyzer. When provided, text file operations also sync with the\n * analyzer so completions / hover reflect the current state.\n */\n analyzer?: TypstAnalyzer;\n /** Default entry file path. Default: \"/main.typ\". */\n entry?: string;\n /**\n * Prefix used to build the `untitled:` URIs handed to the analyzer.\n * Default: \"/project\". A path of `/main.typ` becomes\n * `untitled:project/main.typ`. Only affects URI construction — the compiler\n * and project VFS use the raw paths unchanged.\n */\n analyzerUriRoot?: string;\n /**\n * Scheduling for auto-compiles after VFS mutations. Mutations debounce by\n * `debounceMs`; `maxWaitMs` caps how long the debounce can keep deferring\n * during sustained edits so the user still sees progress.\n */\n autoCompile?: AutoCompileOptions;\n}\n\nexport interface AutoCompileOptions {\n /**\n * Idle time (ms) after the last VFS mutation before a compile fires.\n * Default: 0 — compile fires on the next macrotask. Set higher (e.g. 150) to\n * coalesce rapid edits.\n */\n debounceMs?: number;\n /**\n * Maximum time (ms) the debounce is allowed to defer a compile during\n * sustained mutation bursts. Default: 0 (no cap — pure debounce).\n */\n maxWaitMs?: number;\n}\n\nconst DEFAULT_ENTRY = \"/main.typ\";\nconst DEFAULT_ROOT = \"/project\";\n\n/**\n * Coordinates a compiler + analyzer pair for multi-file Typst projects.\n *\n * Owns the project's virtual filesystem state. Editors push incremental\n * `setText` updates as the user types; the project mirrors those edits to\n * both the compiler's shadow VFS and the analyzer's open-document set, then\n * compiles or services LSP requests against the current state.\n *\n * const project = new TypstProject({ compiler, analyzer });\n * await project.setMany({ \"/main.typ\": \"...\", \"/utils.typ\": \"...\" });\n * const result = await project.compile();\n */\nexport type CompileListener = (result: CompileResult) => void;\n\nfunction errorAsCompileResult(\n err: unknown,\n paths: readonly string[],\n): CompileResult {\n const message = err instanceof Error ? err.message : String(err);\n return {\n diagnostics: paths.map((path) => ({\n package: \"\",\n path,\n severity: \"Error\",\n range: { startLine: 0, startCol: 0, endLine: 0, endCol: 1 },\n message,\n })),\n };\n}\n\nexport class TypstProject {\n private readonly compiler: TypstCompiler;\n private readonly analyzer?: TypstAnalyzer;\n private readonly analyzerUriRoot: string;\n /**\n * Tracked text files: path → latest content observed. Presence in this map\n * is the source of truth for \"is this a tracked text file?\"; insertion\n * order drives the `files` getter. Per-sink dedup lives in the compiler and\n * analyzer.\n */\n private readonly contentByPath = new Map<Path, string>();\n private readonly compileListeners = new Set<CompileListener>();\n private readonly scheduler: CompileScheduler;\n private compileVersion = 0;\n private _lastResult: CompileResult | undefined;\n private _entry: Path;\n private destroyed = false;\n\n private invokeListener(\n listener: CompileListener,\n result: CompileResult,\n ): void {\n try {\n listener(result);\n } catch (err) {\n console.error(\"[typst] compile listener threw:\", err);\n }\n }\n\n constructor(options: TypstProjectOptions) {\n this.compiler = options.compiler;\n this.analyzer = options.analyzer;\n this.analyzerUriRoot = normalizeRoot(\n options.analyzerUriRoot ?? DEFAULT_ROOT,\n );\n this._entry = normalizePath(options.entry ?? DEFAULT_ENTRY);\n this.scheduler = new CompileScheduler({\n debounceMs: options.autoCompile?.debounceMs,\n maxWaitMs: options.autoCompile?.maxWaitMs,\n });\n }\n\n /**\n * Schedule an auto-compile after VFS mutations. Coalesces rapid calls via\n * the configured debounce/throttle. Errors surface through `onCompile`\n * listeners via a synthetic diagnostic; callers awaiting a specific compile\n * should call `compile()` directly.\n */\n private scheduleCompile(): void {\n if (this.destroyed) return;\n this.scheduler.schedule(() => {\n this.compile().catch((err) => console.error(\"[typst]\", err));\n });\n }\n\n /** Current entry file path. Assign to change the sticky entry used by subsequent `compile()` calls. */\n get entry(): Path {\n return this._entry;\n }\n\n set entry(path: Path) {\n const next = normalizePath(path);\n if (next === this._entry) return;\n this._entry = next;\n this.scheduleCompile();\n }\n\n /** Whether an analyzer is attached. */\n get hasAnalyzer(): boolean {\n return this.analyzer !== undefined;\n }\n\n /**\n * Most recent compile result, or `undefined` before the first compile has\n * settled. Useful for lazy-mounted UI that subscribes after boot and needs\n * an initial value.\n */\n get lastResult(): CompileResult | undefined {\n return this._lastResult;\n }\n\n /**\n * Snapshot of tracked text file paths, in insertion order. Updated by\n * `setText`, `setMany`, `remove`, and `clear`. Returns a fresh array — mutate\n * freely without affecting project state.\n */\n get files(): Path[] {\n return [...this.contentByPath.keys()];\n }\n\n /**\n * Current text content for a tracked file, or `undefined` if the path was\n * never written via `setText`/`setMany` (or was removed). Read-through to the\n * project's sync cache — lets consumers avoid shadowing the VFS themselves.\n */\n getText(path: Path): string | undefined {\n return this.contentByPath.get(normalizePath(path));\n }\n\n /**\n * Add or overwrite a text file. Goes to the compiler's VFS and, when an\n * analyzer is attached, to the analyzer as a document change. No-op when\n * the tracked path already has this exact content — skips both worker RPCs\n * and the auto-scheduled compile.\n */\n async setText(path: Path, content: string): Promise<void> {\n const p = normalizePath(path);\n if (this.contentByPath.get(p) === content) return;\n await Promise.all([\n this.compiler.setText(p, content),\n this.analyzer?.didChange(\n pathToAnalyzerUri(p, this.analyzerUriRoot),\n content,\n ) ?? Promise.resolve(),\n ]);\n this.contentByPath.set(p, content);\n this.scheduleCompile();\n }\n\n /**\n * Add or overwrite a JSON file. The analyzer does not track data files, but\n * if `path` was previously tracked as text the analyzer document is closed\n * and the text entry retired so the three views stay consistent.\n */\n async setJson(path: Path, value: unknown): Promise<void> {\n const p = normalizePath(path);\n const wasText = this.contentByPath.has(p);\n await Promise.all([\n this.compiler.setJson(p, value),\n wasText\n ? this.analyzer?.didClose(pathToAnalyzerUri(p, this.analyzerUriRoot))\n : undefined,\n ]);\n this.contentByPath.delete(p);\n this.scheduleCompile();\n }\n\n /**\n * Add or overwrite a binary file. If `path` was previously tracked as text,\n * the analyzer document is closed and the text entry retired in the same\n * call so the compiler / analyzer / project views stay consistent.\n */\n async setBinary(\n path: Path,\n content: ArrayBuffer | ArrayBufferView,\n ): Promise<void> {\n const p = normalizePath(path);\n const wasText = this.contentByPath.has(p);\n await Promise.all([\n this.compiler.setBinary(p, content),\n wasText\n ? this.analyzer?.didClose(pathToAnalyzerUri(p, this.analyzerUriRoot))\n : undefined,\n ]);\n this.contentByPath.delete(p);\n this.scheduleCompile();\n }\n\n /**\n * Batch set multiple files. Strings route to both compiler and analyzer;\n * Uint8Array entries go to the compiler only. Strings matching the last\n * tracked content for their path are skipped on both sinks. Binary entries\n * always go through (no content cache, so no dedup).\n */\n async setMany(files: Record<Path, string | Uint8Array>): Promise<void> {\n const canonical = new Map<Path, string | Uint8Array>();\n for (const [path, content] of Object.entries(files)) {\n canonical.set(normalizePath(path), content);\n }\n\n const normalized: Record<Path, string | Uint8Array> = {};\n const analyzerDocs: Record<string, string> = {};\n const textUpdates: Array<[Path, string]> = [];\n const analyzerCloses: string[] = [];\n const binaryRetirements: Path[] = [];\n for (const [p, content] of canonical) {\n if (typeof content !== \"string\") {\n normalized[p] = content;\n if (this.contentByPath.has(p)) {\n analyzerCloses.push(pathToAnalyzerUri(p, this.analyzerUriRoot));\n binaryRetirements.push(p);\n }\n continue;\n }\n if (this.contentByPath.get(p) === content) continue;\n textUpdates.push([p, content]);\n normalized[p] = content;\n analyzerDocs[pathToAnalyzerUri(p, this.analyzerUriRoot)] = content;\n }\n if (Object.keys(normalized).length === 0) return;\n await Promise.all([\n this.compiler.setMany(normalized),\n this.analyzer?.didChangeMany(analyzerDocs) ?? Promise.resolve(),\n analyzerCloses.length > 0\n ? (this.analyzer?.didCloseMany(analyzerCloses) ?? Promise.resolve())\n : Promise.resolve(),\n ]);\n for (const [p, content] of textUpdates) this.contentByPath.set(p, content);\n for (const p of binaryRetirements) this.contentByPath.delete(p);\n this.scheduleCompile();\n }\n\n /**\n * Remove a file. Always removed from the compiler's VFS; also closed on the\n * analyzer when it was previously tracked as text.\n */\n async remove(path: Path): Promise<void> {\n const p = normalizePath(path);\n const wasText = this.contentByPath.has(p);\n await Promise.all([\n this.compiler.remove(p),\n wasText\n ? this.analyzer?.didClose(pathToAnalyzerUri(p, this.analyzerUriRoot))\n : undefined,\n ]);\n this.contentByPath.delete(p);\n this.scheduleCompile();\n }\n\n /** Clear all files from both compiler VFS and analyzer document set. */\n async clear(): Promise<void> {\n const uris = Array.from(this.contentByPath.keys(), (p) =>\n pathToAnalyzerUri(p, this.analyzerUriRoot),\n );\n await Promise.all([\n this.compiler.clear(),\n uris.length > 0 ? this.analyzer?.didCloseMany(uris) : undefined,\n ]);\n this.contentByPath.clear();\n this.scheduleCompile();\n }\n\n /**\n * Subscribe to compile results. Fires after every `compile()` whose result is\n * still current (stale results from out-of-order concurrent compiles are\n * dropped). If a compile has already settled, the most recent result is\n * delivered synchronously so late-mounted listeners aren't stuck blank until\n * the next compile. Returns an unsubscribe function.\n */\n onCompile(listener: CompileListener): () => void {\n this.compileListeners.add(listener);\n if (this._lastResult !== undefined) {\n this.invokeListener(listener, this._lastResult);\n }\n return () => {\n this.compileListeners.delete(listener);\n };\n }\n\n /**\n * Compile the current VFS state using the sticky entry. Errors from the\n * underlying compiler are converted into a synthetic error diagnostic so\n * callers and listeners always receive a `CompileResult`. Listeners are\n * notified only for the most recent compile — results from an earlier call\n * that resolves after a later one are suppressed.\n *\n * VFS mutations (`setText`, `remove`, etc.) auto-schedule a debounced\n * compile; call this directly only when you need an awaitable handle on the\n * result (e.g., to flush before rendering to PDF).\n */\n async compile(): Promise<CompileResult> {\n this.scheduler.cancel();\n const version = ++this.compileVersion;\n let result: CompileResult;\n try {\n result = await this.compiler.compile(this._entry);\n } catch (err) {\n // Spread the synthetic error across every tracked text path so the\n // diagnostic is visible no matter which file the user is viewing.\n // Falls back to the entry if nothing is tracked yet.\n const paths =\n this.contentByPath.size > 0\n ? [...this.contentByPath.keys()]\n : [this._entry];\n result = errorAsCompileResult(err, paths);\n }\n if (version === this.compileVersion) {\n this._lastResult = result;\n for (const listener of this.compileListeners) {\n this.invokeListener(listener, result);\n }\n }\n return result;\n }\n\n /** Compile the current VFS state to PDF using the sticky entry. */\n compilePdf(): Promise<Uint8Array> {\n return this.compiler.compilePdf(this._entry);\n }\n\n private requireAnalyzer(operation: string): TypstAnalyzer {\n if (!this.analyzer) {\n throw new Error(`TypstProject: ${operation} requires an analyzer`);\n }\n return this.analyzer;\n }\n\n /**\n * Request completion for `path` at `position`, using `source` as the\n * current document state. Pure analyzer query — neither the compiler VFS\n * nor project tracking (`files`/`getText`) is touched. Throws when no\n * analyzer is attached.\n */\n completion(\n path: Path,\n source: string,\n position: LspPosition,\n ): Promise<LspCompletionResponse> {\n const analyzer = this.requireAnalyzer(\"completion\");\n return analyzer.completion(\n pathToAnalyzerUri(normalizePath(path), this.analyzerUriRoot),\n source,\n position,\n );\n }\n\n /**\n * Request hover for `path` at `position`, using `source` as the current\n * document state. Pure analyzer query — neither the compiler VFS nor\n * project tracking is touched. Throws when no analyzer is attached.\n */\n hover(\n path: Path,\n source: string,\n position: LspPosition,\n ): Promise<LspHover | null> {\n const analyzer = this.requireAnalyzer(\"hover\");\n return analyzer.hover(\n pathToAnalyzerUri(normalizePath(path), this.analyzerUriRoot),\n source,\n position,\n );\n }\n\n /**\n * Tear down the project and the services it owns. Destroys the attached\n * compiler and analyzer, drops all listeners, and clears VFS tracking state.\n * Idempotent — calling twice is a no-op. After destruction, further calls on\n * the project are not supported; construct a new one.\n *\n * If you need to share a compiler or analyzer across projects, destroy them\n * yourself and don't call this method — the project does not provide an\n * ownership toggle.\n */\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.scheduler.cancel();\n this.compileListeners.clear();\n this.contentByPath.clear();\n this._lastResult = undefined;\n this.compiler.destroy();\n this.analyzer?.destroy();\n }\n}\n","/** Minimal interface for the built TypstRenderer instance. */\nexport interface RendererInstance {\n free(): void;\n create_session(): RendererSession;\n manipulate_data(\n session: RendererSession,\n action: string,\n data: Uint8Array,\n ): void;\n svg_data(session: RendererSession): string;\n}\n\n/** Minimal interface for a TypstRenderer session. */\nexport interface RendererSession {\n free(): void;\n}\n\ntype RendererWasmModule = typeof import(\"@myriaddreamin/typst-ts-renderer\");\n\ndeclare const __TYPST_TS_RENDERER_VERSION__: string;\n\nconst DEFAULT_RENDERER_WASM_URL = `https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-renderer@${__TYPST_TS_RENDERER_VERSION__}/pkg/typst_ts_renderer_bg.wasm`;\n\nlet rendererModulePromise: Promise<RendererWasmModule> | null = null;\n\nfunction getRendererModule(): Promise<RendererWasmModule> {\n if (!rendererModulePromise) {\n rendererModulePromise = import(\"@myriaddreamin/typst-ts-renderer\").catch(\n (err) => {\n rendererModulePromise = null;\n throw err;\n },\n );\n }\n return rendererModulePromise;\n}\n\nexport interface TypstRendererOptions {\n /** URL to the typst-ts-renderer WASM binary. Defaults to jsDelivr CDN. */\n wasmUrl?: string;\n}\n\nexport interface RenderedSvgPage {\n /** Zero-based page index within the document. */\n index: number;\n /** Page width in typographic points. */\n width: number;\n /** Page height in typographic points. */\n height: number;\n /** Standalone SVG string for just this page. */\n svg: string;\n}\n\n/**\n * Converts Typst vector artifacts to SVG strings.\n *\n * The renderer WASM module is loaded lazily on first use.\n *\n * const renderer = TypstRenderer.create();\n * const svg = await renderer.renderSvg(vector);\n */\nexport class TypstRenderer {\n private wasmUrl: string;\n private instance: Promise<RendererInstance> | null = null;\n\n private constructor(options: TypstRendererOptions = {}) {\n this.wasmUrl = options.wasmUrl ?? DEFAULT_RENDERER_WASM_URL;\n // Eagerly start loading the WASM module so it's ready by first use.\n getRendererModule().catch(() => {});\n }\n\n static create(options: TypstRendererOptions = {}): TypstRenderer {\n return new TypstRenderer(options);\n }\n\n private getInstance(): Promise<RendererInstance> {\n if (!this.instance) {\n this.instance = this.#init().catch((err) => {\n this.instance = null;\n throw err;\n });\n }\n return this.instance;\n }\n\n async #init(): Promise<RendererInstance> {\n const mod = await getRendererModule();\n await mod.default({ module_or_path: this.wasmUrl });\n return new mod.TypstRendererBuilder().build();\n }\n\n /** Free the underlying WASM renderer instance. */\n async destroy(): Promise<void> {\n const instance = this.instance;\n this.instance = null;\n if (instance) {\n (await instance).free();\n }\n }\n\n /** Render a Typst vector artifact to an SVG string. */\n async renderSvg(vector: Uint8Array): Promise<string> {\n const renderer = await this.getInstance();\n const session = renderer.create_session();\n try {\n renderer.manipulate_data(session, \"reset\", vector);\n return renderer.svg_data(session);\n } finally {\n session.free();\n }\n }\n\n /**\n * Render a Typst vector artifact into one self-contained SVG string per\n * physical page. The merged SVG is split by `<g class=\"typst-page\">`\n * children; each group's `data-page-width` / `data-page-height` give the\n * page-local viewBox. Shared `<defs>` / `<style>` are duplicated into each\n * page so the output SVGs render independently. Returns an empty array if\n * the document has no page groups.\n */\n async renderSvgPages(vector: Uint8Array): Promise<RenderedSvgPage[]> {\n return splitMergedSvgPages(await this.renderSvg(vector));\n }\n}\n\n// Parsing must use \"text/html\", not \"image/svg+xml\": Typst's merged SVG\n// output has repeatedly failed XML-strict parsing. HTML mode tolerates it\n// and still produces real SVGSVGElement nodes for inline SVG.\nfunction splitMergedSvgPages(svg: string): RenderedSvgPage[] {\n const doc = new DOMParser().parseFromString(svg, \"text/html\");\n const root = doc.querySelector(\"svg\");\n if (!root) return [];\n\n const children = Array.from(root.children);\n const pageGroups = children.filter(\n (el) =>\n el.tagName.toLowerCase() === \"g\" && el.classList.contains(\"typst-page\"),\n );\n if (pageGroups.length === 0) return [];\n\n const sharedHtml = children\n .filter((el) => !el.classList.contains(\"typst-page\"))\n .map((el) => el.outerHTML)\n .join(\"\");\n\n const namespaceAttrs = Array.from(root.attributes)\n .filter((attr) => attr.name === \"xmlns\" || attr.name.startsWith(\"xmlns:\"))\n .map((attr) => `${attr.name}=\"${attr.value}\"`)\n .join(\" \");\n\n return pageGroups.flatMap((group, index) => {\n const width = Number(group.getAttribute(\"data-page-width\")) || 0;\n const height = Number(group.getAttribute(\"data-page-height\")) || 0;\n if (width <= 0 || height <= 0) return [];\n\n const clone = group.cloneNode(true) as Element;\n clone.removeAttribute(\"transform\");\n\n return [\n {\n index,\n width,\n height,\n svg:\n `<svg ${namespaceAttrs} viewBox=\"0 0 ${width} ${height}\" ` +\n `width=\"${width}\" height=\"${height}\">` +\n `${sharedHtml}${clone.outerHTML}</svg>`,\n },\n ];\n });\n}\n"],"mappings":"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","names":["Comlink"]}
|