@vedivad/typst-web-service 0.15.4 → 0.17.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 CHANGED
@@ -1,14 +1,8 @@
1
1
  # @vedivad/typst-web-service
2
2
 
3
- Editor-agnostic Typst services for the web compile, render, analyze, and
4
- format via WASM.
3
+ Editor-agnostic Typst engine for the web: compile, render, format, autocomplete, hover, and syntax-highlight, all from a single Typst engine compiled to WebAssembly and run in a Web Worker.
5
4
 
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()`.
5
+ Everything goes through one class, `TypstProject`. It owns an in-memory file system, mirrors your files into the worker, schedules compiles, and forwards engine queries.
12
6
 
13
7
  ## Install
14
8
 
@@ -16,98 +10,98 @@ methods like `renderSvgPages()` and `format()`.
16
10
  npm install @vedivad/typst-web-service
17
11
  ```
18
12
 
19
- > Most users should install `@vedivad/codemirror-typst` instead, which re-exports everything from this package and adds CodeMirror 6 integration.
13
+ > Most users should install `@vedivad/codemirror-typst` instead, which re-exports everything here and adds CodeMirror 6 integration.
14
+
15
+ ## Prerequisites
16
+
17
+ A bundler with WebAssembly support (e.g. [Vite](https://vite.dev) with [`vite-plugin-wasm`](https://github.com/nicolo-ribaudo/vite-plugin-wasm)). The engine wasm ships inside this package and is loaded into a worker at runtime; there are no separate binaries to wire up.
20
18
 
21
- ## Compilation
19
+ ## Compile and render
22
20
 
23
21
  ```ts
24
- import { TypstCompiler, TypstRenderer } from "@vedivad/typst-web-service";
25
-
26
- const compiler = await TypstCompiler.create();
27
- const renderer = TypstRenderer.create();
28
-
29
- // Populate the VFS, then compile
30
- await compiler.setText("/main.typ", "= Hello, Typst");
31
- const firstResult = await compiler.compile();
32
- if (firstResult.vector) {
33
- const pages = await renderer.renderSvgPages(firstResult.vector);
34
- document.querySelector("#preview")!.innerHTML = pages
35
- .map((page) => `<div class="page">${page.svg}</div>`)
36
- .join("");
37
- }
22
+ import { TypstProject } from "@vedivad/typst-web-service";
38
23
 
39
- // firstResult.diagnostics are returned in deterministic order
40
- // (path, start position, end position, message)
24
+ const project = await TypstProject.create();
41
25
 
42
- // Multi-file
43
- await compiler.setMany({
44
- "/main.typ": '#import "template.typ": greet\n#greet("World")',
26
+ await project.setMany({
27
+ "/main.typ": '#import "/template.typ": greet\n#greet("World")',
45
28
  "/template.typ": "#let greet(name) = [Hello, #name!]",
46
29
  });
47
- const multiFileResult = await compiler.compile();
48
30
 
49
- // PDF export operates on the same VFS state
50
- const pdf = await compiler.compilePdf();
51
- const blob = new Blob([pdf.slice()], { type: "application/pdf" });
31
+ const result = await project.compile();
32
+ // result.diagnostics: errors/warnings (deterministic order)
33
+ // result.pages: per-page dimensions (compile lays out; SVG is rendered on demand)
34
+
35
+ const pages = await project.renderedPages(0, result.pages.length);
36
+ document.querySelector("#preview")!.innerHTML = pages
37
+ .map((page) => `<div class="page">${page.svg}</div>`)
38
+ .join("");
39
+
40
+ // Export the last compile to PDF
41
+ const pdf = await project.exportPdf();
42
+ if (pdf) {
43
+ const blob = new Blob([pdf.slice()], { type: "application/pdf" });
44
+ }
45
+
46
+ project.destroy();
47
+ ```
48
+
49
+ `compile()` returns the fresh result and also fires `onCompile` listeners; subscribe for reactive updates:
52
50
 
53
- compiler.destroy();
51
+ ```ts
52
+ const unsubscribe = project.onCompile((result) => {
53
+ /* result.pages, result.diagnostics */
54
+ });
54
55
  ```
55
56
 
56
- ## Formatting
57
+ Rendering is on demand so a viewer can virtualize: `renderPage(index)` returns one page's SVG, `renderedPages(start, end)` returns `{ index, width, height, svg }` for a range.
58
+
59
+ ## Fonts
57
60
 
58
- Requires a bundler that supports WASM imports (e.g. Vite + `vite-plugin-wasm`).
61
+ The engine bundles Typst's default fonts (Libertinus Serif for body, New Computer Modern Math for equations, DejaVu Sans Mono for raw/code), so documents render out of the box. Use `addFont` to register families the engine does not ship, such as other scripts (CJK) or a brand font:
59
62
 
60
63
  ```ts
61
- import { TypstFormatter } from "@vedivad/typst-web-service";
64
+ const bytes = new Uint8Array(await (await fetch(fontUrl)).arrayBuffer());
65
+ await project.addFont(bytes); // TTF/OTF or a TTC collection
66
+ ```
67
+
68
+ Added fonts persist for the project's lifetime, and adding one schedules a recompile.
62
69
 
63
- const formatter = TypstFormatter.create({ tab_spaces: 2, max_width: 80 });
64
- const formatted = await formatter.format(source);
65
- const rangeResult = await formatter.formatRange(source, start, end);
70
+ ## Compile scheduling
71
+
72
+ VFS mutations (`setText`, `setMany`, `setBinary`, `remove`, `clear`, entry change) auto-schedule a debounced compile. Configure it per project; call `compile()` to flush immediately.
73
+
74
+ ```ts
75
+ const project = await TypstProject.create({
76
+ entry: "/main.typ", // default compile entry (default: "/main.typ")
77
+ autoCompile: { debounceMs: 300, maxWaitMs: 2000 },
78
+ });
66
79
  ```
67
80
 
68
- Config options ([typstyle docs](https://github.com/typstyle-rs/typstyle)):
81
+ | Option | Default | Behavior |
82
+ | ------------------------ | ------- | ------------------------------------------------------------------------------------ |
83
+ | `autoCompile.debounceMs` | `0` | Coalesce a burst of mutations into one compile, firing once they pause. |
84
+ | `autoCompile.maxWaitMs` | `0` | Force a compile at least this often during sustained mutation. Needs `debounceMs`>0. |
69
85
 
70
- | Option | Type | Default | Description |
71
- | ------------------------- | --------- | ------- | ----------------------------------- |
72
- | `tab_spaces` | `number` | `2` | Spaces per indentation level |
73
- | `max_width` | `number` | `80` | Maximum line width |
74
- | `blank_lines_upper_bound` | `number` | -- | Max consecutive blank lines |
75
- | `collapse_markup_spaces` | `boolean` | -- | Collapse whitespace in markup |
76
- | `reorder_import_items` | `boolean` | -- | Sort import items alphabetically |
77
- | `wrap_text` | `boolean` | -- | Wrap text to fit within `max_width` |
86
+ `@preview` packages are fetched over HTTP on demand: when a source imports `@preview/...`, the referenced package is downloaded and pushed into the VFS before the compile runs. Sources with no such imports never hit the network.
78
87
 
79
- ## Completion and hover with tinymist
88
+ ## Editor intelligence
80
89
 
81
- `TypstAnalyzer` runs a [tinymist](https://github.com/Myriad-Dreamin/tinymist) language server in a Web Worker. The `wasmUrl` option must point to the `tinymist_bg.wasm` binary from the `tinymist-web` package.
90
+ Completions, hover, formatting, and highlighting come from the engine's own `typst-ide` and `typst-syntax`; there is no separate language server. The IDE methods take the live editor buffer (`source`) so they work whether or not the project's VFS is already current. Offsets are CodeMirror (UTF-16) positions.
82
91
 
83
92
  ```ts
84
- import { TypstAnalyzer } from "@vedivad/typst-web-service";
85
- import tinymistWasmUrl from "tinymist-web/pkg/tinymist_bg.wasm?url";
86
-
87
- const analyzer = await TypstAnalyzer.create({ wasmUrl: tinymistWasmUrl });
88
-
89
- await analyzer.didChange("untitled:project/main.typ", source);
90
- const completions = await analyzer.completion(
91
- "untitled:project/main.typ",
92
- source,
93
- { line, character },
94
- );
95
- const hover = await analyzer.hover("untitled:project/main.typ", source, {
96
- line,
97
- character,
98
- });
93
+ const completions = await project.completion("/main.typ", source, offset);
94
+ const hover = await project.hover("/main.typ", source, offset);
95
+ const formatted = await project.format("/main.typ", source);
99
96
 
100
- analyzer.destroy();
97
+ // Syntax highlighting: spans for a viewport, or ready HTML for a snippet.
98
+ const spans = await project.highlight(source, viewportFrom, viewportTo);
99
+ const html = await project.highlightHtml(codeSnippet);
101
100
  ```
102
101
 
103
- ## Service classes
102
+ ## Offsets
104
103
 
105
- | Class | Runs on | WASM loading | Purpose |
106
- | ---------------- | ----------- | ----------------------- | ---------------------------------------------------------- |
107
- | `TypstCompiler` | Web Worker | CDN (automatic) | `compile()` -> diagnostics + vector, `compilePdf()` -> PDF |
108
- | `TypstRenderer` | Main thread | CDN (automatic) | `renderSvg(vector)` or `renderSvgPages(vector)` |
109
- | `TypstFormatter` | Main thread | Bundler (static import) | `format(source)`, `formatRange(source, start, end)` |
110
- | `TypstAnalyzer` | Web Worker | User-provided `wasmUrl` | Completion + hover via tinymist |
104
+ The engine speaks UTF-8 byte offsets; editors speak UTF-16. `cmOffsetToByte` and `byteToCmOffset` convert a single position; the project's own methods accept and return CodeMirror offsets, so you only need these if you work with raw engine offsets directly.
111
105
 
112
106
  ## License
113
107