@drawmotive/textgraph 0.2.2-alpha.3 → 0.2.2-alpha.5

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.
Files changed (40) hide show
  1. package/README.md +21 -3
  2. package/docs/api-v1.md +14 -2
  3. package/docs/react.md +7 -0
  4. package/generated/wasm/DrawMotive.TextGraph.Bridge.wasm +0 -0
  5. package/generated/wasm/Graphics.Core.wasm +0 -0
  6. package/generated/wasm/Grpc.Core.Api.wasm +0 -0
  7. package/generated/wasm/HarfBuzzSharp.wasm +0 -0
  8. package/generated/wasm/Math.Core.wasm +0 -0
  9. package/generated/wasm/Microsoft.Extensions.Logging.Abstractions.wasm +0 -0
  10. package/generated/wasm/Polly.Core.wasm +0 -0
  11. package/generated/wasm/RBush.wasm +0 -0
  12. package/generated/wasm/SkiaSharp.wasm +0 -0
  13. package/generated/wasm/System.Collections.Concurrent.wasm +0 -0
  14. package/generated/wasm/System.Console.wasm +0 -0
  15. package/generated/wasm/System.IO.Compression.Brotli.wasm +0 -0
  16. package/generated/wasm/System.IO.Compression.wasm +0 -0
  17. package/generated/wasm/System.Linq.wasm +0 -0
  18. package/generated/wasm/System.Net.Http.wasm +0 -0
  19. package/generated/wasm/System.Private.CoreLib.wasm +0 -0
  20. package/generated/wasm/System.Runtime.InteropServices.wasm +0 -0
  21. package/generated/wasm/System.Runtime.Intrinsics.wasm +0 -0
  22. package/generated/wasm/System.Runtime.wasm +0 -0
  23. package/generated/wasm/System.Text.Json.wasm +0 -0
  24. package/generated/wasm/System.Threading.Thread.wasm +0 -0
  25. package/generated/wasm/dotnet.boot.js +23 -28
  26. package/generated/wasm/dotnet.native.js +2 -2
  27. package/generated/wasm/dotnet.native.wasm +0 -0
  28. package/generated/wasm/themes.css +1 -1
  29. package/generated/wasm-manifest.js +38 -44
  30. package/generated/wasm-manifest.json +38 -44
  31. package/package.json +7 -7
  32. package/src/index.d.ts +5 -1
  33. package/src/index.js +8 -2
  34. package/src/react/index.d.ts +3 -1
  35. package/src/react/index.js +15 -9
  36. package/src/runtime/font-cache.js +36 -0
  37. package/src/runtime/font-resources.js +11 -2
  38. package/src/runtime/render-resources.js +4 -2
  39. package/src/runtime/rendering.js +8 -7
  40. package/generated/wasm/ZstdSharp.wasm +0 -0
package/README.md CHANGED
@@ -51,10 +51,24 @@ For existing documentation, use the
51
51
  npm install @drawmotive/textgraph@0.2.2-alpha.3
52
52
  ```
53
53
 
54
- Requires **Node.js 22+** or a modern browser with WebAssembly and Web Crypto.
54
+ Supports **Node.js 22** or a modern browser with WebAssembly and Web Crypto.
55
55
  Browser applications need HTTPS or localhost. React support is an optional entry
56
56
  in this package, compatible with React 18.2+ and 19.
57
57
 
58
+ ## Supported environments
59
+
60
+ | Layer | Supported environment | Verification boundary |
61
+ | --- | --- | --- |
62
+ | Node runtime and package tools | Node.js 22; npm 10 | Vite samples require Node 22.12+ on the 22 line |
63
+ | Desktop build and Node rendering | Linux, Windows, macOS | CI targets all three systems with Node 22 |
64
+ | Browser, React and module Worker rendering | Current Chromium, Firefox and WebKit engines | Playwright tests the SDK and samples on Linux; WebKit is not a Safari device certification |
65
+ | React entry | React 18.2+ in the 18 line, or React 19 | React is an optional peer dependency |
66
+
67
+ The standalone audit on 2026-10-01 ran Linux x64 with Node 22.23.2 and npm
68
+ 10.9.8. The support and CI targets above do not claim that Windows, macOS
69
+ or every browser was executed in that audit. Browser hosts must provide
70
+ WebAssembly, JavaScript modules, Web Crypto and a secure origin.
71
+
58
72
  ## Render a PNG in Node.js
59
73
 
60
74
  Save as `render.mjs`, then run `node render.mjs`:
@@ -232,8 +246,8 @@ publish anything. Samples are not included in the npm package.
232
246
  - **Available:** flowcharts and directed graphs, automatic layout, PNG and SVG rendering,
233
247
  and source validation.
234
248
  - **Not available yet:** mind maps, sequence diagrams, and slides.
235
- - **Fonts:** Noto Sans and Fuzzy Bubbles are included. Custom font descriptors are
236
- supported; the optional `@drawmotive/textgraph-fonts` package is not published.
249
+ - **Fonts:** Noto Sans and Fuzzy Bubbles are included. Custom font descriptors and
250
+ the optional published `@drawmotive/textgraph-fonts@0.2.2-alpha.2` package are supported.
237
251
  See [font configuration](https://github.com/drawmotive/textgraph/blob/main/docs/api-v1.md#fonts-and-language-packs).
238
252
 
239
253
  ## Project links
@@ -243,6 +257,10 @@ publish anything. Samples are not included in the npm package.
243
257
  [Report an issue](https://github.com/drawmotive/textgraph/issues) ·
244
258
  [MIT license](https://github.com/drawmotive/textgraph/blob/main/LICENSE)
245
259
 
260
+ For public checkout commands and pull requests, see [Contributing](CONTRIBUTING.md).
261
+ Redistribution notices are indexed in [NOTICE](NOTICE).
262
+ For vulnerability reports, see [Security reporting](SECURITY.md).
263
+
246
264
  ### Try it in a browser
247
265
 
248
266
  [Open the runnable HTML/JavaScript example](https://textgraph.dev/examples/textgraph/) to edit source, see its PNG and copy the integration code without installing anything. For visual editing, see [the DrawMotive editor](https://textgraph.dev/editor/).
package/docs/api-v1.md CHANGED
@@ -75,11 +75,23 @@ Use `languagePacks: []` with `fallback: false` to explicitly disable automatic f
75
75
 
76
76
  Catalog fonts carry actual Unicode coverage, language hints, byte sizes and SHA-256.
77
77
  They are verified before native installation. Catalog URLs are cached per instance;
78
- HTTP font URLs include the content hash as a stable `v` parameter. HTTP caching remains
79
- browser/server-owned; there is no new IndexedDB or Service Worker. Offline operation
78
+ HTTP font URLs include the content hash as a stable `v` parameter. The SDK automatically
79
+ stores verified bundled and optional font bytes in browser/Worker Cache Storage
80
+ (`textgraph-fonts-v1`) and reuses them across instances and page reloads, independent
81
+ of server cache headers. Cached bytes are verified again before use; damaged entries
82
+ are replaced from the configured source. Storage denial or quota errors fall back
83
+ to normal loading. Local files and descriptors without hashes are not persisted. Offline operation
80
84
  requires the complete runtime and font package on a local filesystem or local server;
81
85
  an online page cannot fetch an uncached font after losing network access.
82
86
 
87
+ For interactive previews, pass `onPreview` to `renderPng` or `renderSvg`. When visible
88
+ labels need optional fonts, the callback receives a provisional successful image
89
+ using installed fonts before catalog/font downloads. Display it with a font-loading
90
+ notice; missing glyphs or different text geometry can appear temporarily. The returned
91
+ promise still resolves to the final output after fonts are installed and layout is
92
+ recomputed. Calls without the callback retain final-output semantics. Font-loading
93
+ failures reject so the host can keep the provisional image and offer a retry.
94
+
83
95
  `renderPng(source, { language: "ja" })` and `renderSvg(source, { language: "ja" })` can disambiguate Han-only labels. Without a hint,
84
96
  kana selects Japanese for that label and Han-only labels use Chinese. Grapheme-aware
85
97
  emoji selection includes flags, ZWJ sequences, modifiers and keycaps. Font selection
package/docs/react.md CHANGED
@@ -38,10 +38,17 @@ Use your deployed base path instead of `/` for subdirectory hosting. The [React/
38
38
  | `alt` | Image description; defaults to `TextGraph diagram`. |
39
39
  | `renderOptions` | Defaults to `scale: 1`; `padding` and `maxWidth` use SDK defaults. Set `scale: 2` explicitly for higher raster density. |
40
40
  | `loading` | React content shown with `role="status"`; defaults to `Rendering diagram…`. |
41
+ | `fontsLoading` | Content shown beside the provisional image while optional fonts load; defaults to `Fonts are still loading…`. |
41
42
  | Other image attributes | Passed to the successful `<img>`, including `className`, `style`, `width`, and `height`. |
42
43
 
43
44
  The component owns `src`, encoding, and cancellation. While new input renders, old images are replaced by loading content. A stale result cannot replace newer input. DSL diagnostics and operational failures appear as escaped text with `role="alert"`; corrected input renders again. Changing only `alt` or image styles does not rerun layout.
44
45
 
46
+ When optional fonts are needed, the component displays a provisional image first,
47
+ shows `fontsLoading`, and replaces the image after loading and measuring the fonts.
48
+ If loading fails, the preview remains beside the error. The SDK automatically caches
49
+ verified font bytes across instances and reloads in browser Cache Storage; no host
50
+ cache setup is required.
51
+
45
52
  Images use the render result's logical `displayWidth` and `displayHeight`, so increasing `scale` adds pixels without enlarging the diagram. `maxWidth` limits raster pixels rather than CSS width. Styles default to `maxWidth: '100%'` and `height: 'auto'`; supplied styles merge over these defaults, and explicit image dimensions override the logical dimensions.
46
53
 
47
54
  Older runtimes omit display dimensions. The component then divides raster dimensions by the requested scale; this preserves logical size unless `maxWidth` has lowered the effective density. Deploy matching updated runtime assets for accurate sizing in that case. PNG DPI metadata does not control CSS size.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,7 +1,7 @@
1
1
  export const config = /*json-start*/{
2
2
  "mainAssemblyName": "DrawMotive.TextGraph.Bridge.dll",
3
3
  "resources": {
4
- "hash": "sha256-8FmwOmBZ+SFhsg4Z1Q4eyPRkYNbS6XkVYj2B2RWM2Uw=",
4
+ "hash": "sha256-NkSXf6BaW3IAk8E3aIFzyrGG28CgnWT4/WC1onNX/Z0=",
5
5
  "jsModuleNative": [
6
6
  {
7
7
  "name": "dotnet.native.js"
@@ -15,7 +15,7 @@ export const config = /*json-start*/{
15
15
  "wasmNative": [
16
16
  {
17
17
  "name": "dotnet.native.wasm",
18
- "hash": "sha256-3DvnYxhVVGZUnnhjVRAjnr+yRc4RUw3tH5mtWSs4OCk="
18
+ "hash": "sha256-Tqb9aNQTpsiBcpNevNGjnyxzrpZE0n77Sx4Rcn4UFhc="
19
19
  }
20
20
  ],
21
21
  "coreAssembly": [
@@ -27,7 +27,7 @@ export const config = /*json-start*/{
27
27
  {
28
28
  "virtualPath": "System.Private.CoreLib.wasm",
29
29
  "name": "System.Private.CoreLib.wasm",
30
- "hash": "sha256-R3WPOu0FW1nr0l4R5IUtgsh9K4LqI8nG4E1P2qaGZ04="
30
+ "hash": "sha256-Iaa0Sf9pQ/5qOAIAghx8wjz/9jodVQVYvksdfYoyQo8="
31
31
  }
32
32
  ],
33
33
  "assembly": [
@@ -39,12 +39,12 @@ export const config = /*json-start*/{
39
39
  {
40
40
  "virtualPath": "Grpc.Core.Api.wasm",
41
41
  "name": "Grpc.Core.Api.wasm",
42
- "hash": "sha256-cAsCPThXAaxkhablgLVwz/pEW8al1JTVs552cOIPDLY="
42
+ "hash": "sha256-X6Xe6EsTbgz2wWCfm/jWGDbHliLWTyZ4/q8tq0mEXuA="
43
43
  },
44
44
  {
45
45
  "virtualPath": "HarfBuzzSharp.wasm",
46
46
  "name": "HarfBuzzSharp.wasm",
47
- "hash": "sha256-0rnI3MbFX+T2XsQ+WPS3Gama89dTzWD0iUGVJBwTRXc="
47
+ "hash": "sha256-ANRkrpepe+vbsz6PkjgjWbgkW2W68G7iYG9gU7qx+zs="
48
48
  },
49
49
  {
50
50
  "virtualPath": "MagicOnion.Abstractions.wasm",
@@ -109,7 +109,7 @@ export const config = /*json-start*/{
109
109
  {
110
110
  "virtualPath": "Microsoft.Extensions.Logging.Abstractions.wasm",
111
111
  "name": "Microsoft.Extensions.Logging.Abstractions.wasm",
112
- "hash": "sha256-zydm7jLPaVif+FqW1aRZRQKUMTpfVGl3nqwspcQxoDs="
112
+ "hash": "sha256-20141PpUKEltX8UAUL75vqQKTz7O4t65bVFpNxnVtq0="
113
113
  },
114
114
  {
115
115
  "virtualPath": "Microsoft.Extensions.Options.wasm",
@@ -139,7 +139,7 @@ export const config = /*json-start*/{
139
139
  {
140
140
  "virtualPath": "Polly.Core.wasm",
141
141
  "name": "Polly.Core.wasm",
142
- "hash": "sha256-li+Zk1bbR406FjygbR4vHFxUYBMg6YMRm3hUkOYwuzo="
142
+ "hash": "sha256-dXCCNoxEuqdFCoVDH1ZfKecXtEldfinkUKjwj18RqKE="
143
143
  },
144
144
  {
145
145
  "virtualPath": "R3.wasm",
@@ -154,7 +154,7 @@ export const config = /*json-start*/{
154
154
  {
155
155
  "virtualPath": "SkiaSharp.wasm",
156
156
  "name": "SkiaSharp.wasm",
157
- "hash": "sha256-MpeE81pK73isgx4BVEiPEljrY/wUyoYXGq5+xbHAMYs="
157
+ "hash": "sha256-bRa2qn2QJTeC0dSx2LmKFUS1lm/ZeqoaJ31c24l8NtI="
158
158
  },
159
159
  {
160
160
  "virtualPath": "SkiaSharp.HarfBuzz.wasm",
@@ -166,35 +166,30 @@ export const config = /*json-start*/{
166
166
  "name": "Stateless.wasm",
167
167
  "hash": "sha256-EWplLf93o2pQw4CGMvCTYO234EMwTX1tSqc2xJ8JbCw="
168
168
  },
169
- {
170
- "virtualPath": "ZstdSharp.wasm",
171
- "name": "ZstdSharp.wasm",
172
- "hash": "sha256-KR3cAHwB6xsvw0WXeCcP8OnTHPehbTBzJ5VbX5vlOVU="
173
- },
174
169
  {
175
170
  "virtualPath": "Graphics.Core.wasm",
176
171
  "name": "Graphics.Core.wasm",
177
- "hash": "sha256-pURePVdy3OdXKVxBOGxozwxpgUuNp+gDbRR4K0yOjII="
172
+ "hash": "sha256-RkBXYEmkqaTS5u4oaD9LgCGW9BeM6Yx6y7Faf0anA0A="
178
173
  },
179
174
  {
180
175
  "virtualPath": "Math.Core.wasm",
181
176
  "name": "Math.Core.wasm",
182
- "hash": "sha256-rAwRf96kfhvzX87bTnypm9ukbRUO04ILuNsSzbrRCdY="
177
+ "hash": "sha256-pdy4LK0A0uhbm44JzBQPm/7P1SQ1cZNhcvycGoJBxsk="
183
178
  },
184
179
  {
185
180
  "virtualPath": "RBush.wasm",
186
181
  "name": "RBush.wasm",
187
- "hash": "sha256-EaDqY2JWw7Fb0CUJ6KczHvuqtvJKpQ3h3NAR6bDkghw="
182
+ "hash": "sha256-hB+961dw2Pyftv3Lyp+nlvtuEj0yUB5d/Fzby23Rq2k="
188
183
  },
189
184
  {
190
185
  "virtualPath": "DrawMotive.TextGraph.Bridge.wasm",
191
186
  "name": "DrawMotive.TextGraph.Bridge.wasm",
192
- "hash": "sha256-ojZGmKsqH6SfJ2X8Kgdym9LOxMDXGoZuUNcdj59ZL2c="
187
+ "hash": "sha256-FPqTWFElDDiTkAPoR5NrVyDpUDK4hTEj5naELw5Y21c="
193
188
  },
194
189
  {
195
190
  "virtualPath": "System.Collections.Concurrent.wasm",
196
191
  "name": "System.Collections.Concurrent.wasm",
197
- "hash": "sha256-BXUr4+prcuxklm+r7szsDktFIfPfcb8/02SPaRyz960="
192
+ "hash": "sha256-Jeoyh28M8wBA9+BQ0e4ANv2Jq6ITNW88s/DsalFTOII="
198
193
  },
199
194
  {
200
195
  "virtualPath": "System.Collections.Immutable.wasm",
@@ -239,7 +234,7 @@ export const config = /*json-start*/{
239
234
  {
240
235
  "virtualPath": "System.Console.wasm",
241
236
  "name": "System.Console.wasm",
242
- "hash": "sha256-BAKmU+lUWgxhtkmZ8jieCZeWo80RWxGZs2ooZ6iqZ/o="
237
+ "hash": "sha256-wWvOkB7Asdsz3ljcee0YzCbdSqWCXg1y/VElLjmc4IA="
243
238
  },
244
239
  {
245
240
  "virtualPath": "System.Diagnostics.StackTrace.wasm",
@@ -254,12 +249,12 @@ export const config = /*json-start*/{
254
249
  {
255
250
  "virtualPath": "System.IO.Compression.Brotli.wasm",
256
251
  "name": "System.IO.Compression.Brotli.wasm",
257
- "hash": "sha256-F4xiJc7ffAvcTm67k1NojAE82TOqJutN2GLp8nguyLY="
252
+ "hash": "sha256-DcPCQCYlBqXrd+tiArKhLDeuqXNSS6lnKVwPOoQ9NZg="
258
253
  },
259
254
  {
260
255
  "virtualPath": "System.IO.Compression.wasm",
261
256
  "name": "System.IO.Compression.wasm",
262
- "hash": "sha256-LEpM7vWA7l1pAc9qqrubbXdboEwopOLFK3a7VMdGJLg="
257
+ "hash": "sha256-smvpD00XQvKdSiwuc7hnhCoRz2Nk9VsSTg7MQ9QDipY="
263
258
  },
264
259
  {
265
260
  "virtualPath": "System.IO.Pipelines.wasm",
@@ -274,7 +269,7 @@ export const config = /*json-start*/{
274
269
  {
275
270
  "virtualPath": "System.Linq.wasm",
276
271
  "name": "System.Linq.wasm",
277
- "hash": "sha256-4a6K3TpQt5VM+yAnqrNXot7Ym22WYMkZGioVgUU8xQA="
272
+ "hash": "sha256-fuRhzw+pO117bLAf4pgfy9YBNt5KfAQ9s+2VhePWmsU="
278
273
  },
279
274
  {
280
275
  "virtualPath": "System.Memory.wasm",
@@ -284,7 +279,7 @@ export const config = /*json-start*/{
284
279
  {
285
280
  "virtualPath": "System.Net.Http.wasm",
286
281
  "name": "System.Net.Http.wasm",
287
- "hash": "sha256-VTLLDv0CufL90HZn7zDPWv02ruTg8G9m+SwcJyD1i3Q="
282
+ "hash": "sha256-ARrgegBDSux9EF+zqRrsot7EbG1Q9fVGFdJPdHm/r54="
288
283
  },
289
284
  {
290
285
  "virtualPath": "System.Net.Primitives.wasm",
@@ -334,12 +329,12 @@ export const config = /*json-start*/{
334
329
  {
335
330
  "virtualPath": "System.Runtime.InteropServices.wasm",
336
331
  "name": "System.Runtime.InteropServices.wasm",
337
- "hash": "sha256-JOphI8UBDfRYPMoPVWX6vtbIvIl2X5JrfC/mHtvh7ks="
332
+ "hash": "sha256-Q+92Kf8z4HWVuCYT5hM1wtDe4wQSltcDE8cvVbCH8fU="
338
333
  },
339
334
  {
340
335
  "virtualPath": "System.Runtime.Intrinsics.wasm",
341
336
  "name": "System.Runtime.Intrinsics.wasm",
342
- "hash": "sha256-eakqOzOkp0Mj/WG0i0fuK0e4F/dENzwC+AF5SXmatt0="
337
+ "hash": "sha256-Amor9SnGJljEQGEHCbNnu8SJdtb2TN+4vW5fSBgwcMc="
343
338
  },
344
339
  {
345
340
  "virtualPath": "System.Runtime.Loader.wasm",
@@ -359,7 +354,7 @@ export const config = /*json-start*/{
359
354
  {
360
355
  "virtualPath": "System.Runtime.wasm",
361
356
  "name": "System.Runtime.wasm",
362
- "hash": "sha256-k2UA6JqOpAVySatRrwRGNVQnnfCX79dzJL1D9a7vC2M="
357
+ "hash": "sha256-f0aumL0L3t2Dj97l3TzNxbOS4K9q93bDGqeAdICfb/A="
363
358
  },
364
359
  {
365
360
  "virtualPath": "System.Security.Cryptography.wasm",
@@ -379,7 +374,7 @@ export const config = /*json-start*/{
379
374
  {
380
375
  "virtualPath": "System.Text.Json.wasm",
381
376
  "name": "System.Text.Json.wasm",
382
- "hash": "sha256-sUgA2ZxaAqrbzqji4LJpam1l3LMSaZlQyabY9brJXSg="
377
+ "hash": "sha256-VDv7svOvBBRLoEC3jt8c/LiVS4CCJyY8vXuy2/npWGg="
383
378
  },
384
379
  {
385
380
  "virtualPath": "System.Text.RegularExpressions.wasm",
@@ -394,7 +389,7 @@ export const config = /*json-start*/{
394
389
  {
395
390
  "virtualPath": "System.Threading.Thread.wasm",
396
391
  "name": "System.Threading.Thread.wasm",
397
- "hash": "sha256-z/9sp9813ITXlJUi0QmOUPMRNX77BVzO6yU0bIKss/w="
392
+ "hash": "sha256-xmNkGMgtSroWqZ6CxqqzoILHxIWocNCS8InItRUqIUE="
398
393
  },
399
394
  {
400
395
  "virtualPath": "System.Threading.ThreadPool.wasm",
@@ -30,11 +30,11 @@ Module['ready'] = new Promise((resolve, reject) => {
30
30
 
31
31
  // --pre-jses are emitted after the Module integration code, so that they can
32
32
  // refer to Module (if they choose; they can also define Module)
33
- // include: /home/admin/.dotnet/packs/Microsoft.NETCore.App.Runtime.Mono.browser-wasm/10.0.12/runtimes/browser-wasm/native/src/es6/dotnet.es6.pre.js
33
+ // include: /home/mason/dotnet/packs/Microsoft.NETCore.App.Runtime.Mono.browser-wasm/10.0.12/runtimes/browser-wasm/native/src/es6/dotnet.es6.pre.js
34
34
  if (_nativeModuleLoaded) throw new Error("Native module already loaded");
35
35
  _nativeModuleLoaded = true;
36
36
  createDotnetRuntime = Module = moduleArg(Module);
37
- // end include: /home/admin/.dotnet/packs/Microsoft.NETCore.App.Runtime.Mono.browser-wasm/10.0.12/runtimes/browser-wasm/native/src/es6/dotnet.es6.pre.js
37
+ // end include: /home/mason/dotnet/packs/Microsoft.NETCore.App.Runtime.Mono.browser-wasm/10.0.12/runtimes/browser-wasm/native/src/es6/dotnet.es6.pre.js
38
38
 
39
39
 
40
40
  // Sometimes an existing Module object exists with properties
Binary file
@@ -85,7 +85,7 @@
85
85
  --textgraph-edge-label-self-loop-padding: 8px;
86
86
  --textgraph-edge-label-center-nudge-limit: 12px;
87
87
  --textgraph-edge-label-font-size: medium;
88
- --textgraph-edge-label-padding: 2px 2px 2px 2px;
88
+ --textgraph-edge-label-padding: 3px 6px;
89
89
  --textgraph-edge-label-opacity: 0.88;
90
90
  --textgraph-preview-export-padding: 48px;
91
91
  --textgraph-preview-group-corner-radius: 4px;
@@ -1,7 +1,7 @@
1
1
  export default {
2
2
  "schemaVersion": 1,
3
3
  "packageName": "@drawmotive/textgraph",
4
- "packageVersion": "0.2.2-alpha.3",
4
+ "packageVersion": "0.2.2-alpha.5",
5
5
  "abiVersion": "1.0.0",
6
6
  "protocolVersion": 1,
7
7
  "runtimeModule": "wasm/dotnet.js",
@@ -27,7 +27,7 @@ export default {
27
27
  },
28
28
  "targetFramework": "net10.0",
29
29
  "privateSource": {
30
- "commit": "ee444cb0e43081011079193af05a9707c77739fa",
30
+ "commit": "f7627ef2646772503fb3d8165ee312d4f411566c",
31
31
  "project": "DrawMotive.TextGraph.Bridge"
32
32
  },
33
33
  "entryAssembly": "wasm/DrawMotive.TextGraph.Bridge.wasm",
@@ -52,7 +52,7 @@ export default {
52
52
  "path": "wasm/DrawMotive.TextGraph.Bridge.wasm",
53
53
  "mediaType": "application/wasm",
54
54
  "bytes": 87317,
55
- "sha256": "a2364698ab2a1fa49f2765fc2a07729bd2cec4c0d71a866e50d71d8f9f592f67"
55
+ "sha256": "14fa935851250c38939003e847936b5720e95032b8853123e676842f0e58db57"
56
56
  },
57
57
  {
58
58
  "path": "wasm/ExCSS.wasm",
@@ -75,20 +75,20 @@ export default {
75
75
  {
76
76
  "path": "wasm/Graphics.Core.wasm",
77
77
  "mediaType": "application/wasm",
78
- "bytes": 3976473,
79
- "sha256": "a5445e3d5772dce757295c41386c68cf0c69814b8da7e8036d14782b4c8e8c82"
78
+ "bytes": 4040985,
79
+ "sha256": "4640576049a4a9a4d2e6ee28683f4b802196f4178ce98c7acbb15a7f46a70340"
80
80
  },
81
81
  {
82
82
  "path": "wasm/Grpc.Core.Api.wasm",
83
83
  "mediaType": "application/wasm",
84
84
  "bytes": 7957,
85
- "sha256": "700b023d385701ac6485a6e580b570cffa445bc6a5d494d5b39e7670e20f0cb6"
85
+ "sha256": "5fa5dee84b136e0cf6c1609f9bf8d61836c79622d64f2678feaf2dab49845ee0"
86
86
  },
87
87
  {
88
88
  "path": "wasm/HarfBuzzSharp.wasm",
89
89
  "mediaType": "application/wasm",
90
90
  "bytes": 30485,
91
- "sha256": "d2b9c8dcc6c55fe4f65ec43e58f4b719a99af3d753cd60f4894195241c134577"
91
+ "sha256": "00d464ae97a97bebdbb33e8f92382359b8245b65baf06ee2606f6053bab1fb3b"
92
92
  },
93
93
  {
94
94
  "path": "wasm/MagicOnion.Abstractions.wasm",
@@ -118,7 +118,7 @@ export default {
118
118
  "path": "wasm/Math.Core.wasm",
119
119
  "mediaType": "application/wasm",
120
120
  "bytes": 3241241,
121
- "sha256": "ac0c117fdea47e1bf35fcedb4e7ca99bdba46d150ed3820bb8db12cdbad109d6"
121
+ "sha256": "a5dcb82cad00d2e85b9b8e09cc140f9bfecfd5243571936172fc9c1a8241c6c9"
122
122
  },
123
123
  {
124
124
  "path": "wasm/MemoryPack.Core.wasm",
@@ -166,7 +166,7 @@ export default {
166
166
  "path": "wasm/Microsoft.Extensions.Logging.Abstractions.wasm",
167
167
  "mediaType": "application/wasm",
168
168
  "bytes": 19221,
169
- "sha256": "cf2766ee32cf69589ff85a96d5a459450294313a5f5469779eac2ca5c431a03b"
169
+ "sha256": "db4d78d4fa5428496d5fc50050bef9bea40a4f3ecee2deb96d51693719d5b6ad"
170
170
  },
171
171
  {
172
172
  "path": "wasm/Microsoft.Extensions.Logging.wasm",
@@ -214,7 +214,7 @@ export default {
214
214
  "path": "wasm/Polly.Core.wasm",
215
215
  "mediaType": "application/wasm",
216
216
  "bytes": 62741,
217
- "sha256": "962f999356db478d3a163ca06d1e2f1c5c54601320e983119b785490e630bb3a"
217
+ "sha256": "757082368c44baa7450a85431f565f29e717b4495d7e29e450a8f08f5f11a8a1"
218
218
  },
219
219
  {
220
220
  "path": "wasm/Polly.wasm",
@@ -238,7 +238,7 @@ export default {
238
238
  "path": "wasm/RBush.wasm",
239
239
  "mediaType": "application/wasm",
240
240
  "bytes": 24341,
241
- "sha256": "11a0ea636256c3b15bd02509e8a7331efbaab6f24aa50de1dcd011e9b0e4821c"
241
+ "sha256": "841fbdeb5770d8fc9fb6fdcbca9fa796fb6e123d32501e5dfc5cdbcb6dd1ab69"
242
242
  },
243
243
  {
244
244
  "path": "wasm/SkiaSharp.HarfBuzz.wasm",
@@ -249,8 +249,8 @@ export default {
249
249
  {
250
250
  "path": "wasm/SkiaSharp.wasm",
251
251
  "mediaType": "application/wasm",
252
- "bytes": 97557,
253
- "sha256": "329784f35a4aef78ac831e0154488f1258eb63fc14ca86171aae7ec5b1c0318b"
252
+ "bytes": 102677,
253
+ "sha256": "6d16b6aa7d90253782d1d4b1d8b98a1544b5966fd97aaa1a277d5cdb897c36d2"
254
254
  },
255
255
  {
256
256
  "path": "wasm/Stateless.wasm",
@@ -261,8 +261,8 @@ export default {
261
261
  {
262
262
  "path": "wasm/System.Collections.Concurrent.wasm",
263
263
  "mediaType": "application/wasm",
264
- "bytes": 39189,
265
- "sha256": "05752be3ea6b72ec64966fabeeccec0e4b4521f3df71bf3fd3648f691cb3f7ad"
264
+ "bytes": 36117,
265
+ "sha256": "25ea32876f0cf30040f7e050d1ee0036fd89aba213356f3cb3f0ec6a51533882"
266
266
  },
267
267
  {
268
268
  "path": "wasm/System.Collections.Immutable.wasm",
@@ -316,7 +316,7 @@ export default {
316
316
  "path": "wasm/System.Console.wasm",
317
317
  "mediaType": "application/wasm",
318
318
  "bytes": 15125,
319
- "sha256": "0402a653e9545a0c61b64999f2389e099796a3cd115b1199b36a2867a8aa67fa"
319
+ "sha256": "c16bce901ec0b1db33de58dc79ed18cc26dd4aa5825e0d72fd51252e399ce080"
320
320
  },
321
321
  {
322
322
  "path": "wasm/System.Diagnostics.StackTrace.wasm",
@@ -333,14 +333,14 @@ export default {
333
333
  {
334
334
  "path": "wasm/System.IO.Compression.Brotli.wasm",
335
335
  "mediaType": "application/wasm",
336
- "bytes": 5397,
337
- "sha256": "178c6225cedf7c0bdc4e6ebb9353688c013cd933aa26eb4dd862e9f2782ec8b6"
336
+ "bytes": 6933,
337
+ "sha256": "0dc3c240262506a5eb77eb6202b2a12c37aea973524ba967295c0f3a843d3598"
338
338
  },
339
339
  {
340
340
  "path": "wasm/System.IO.Compression.wasm",
341
341
  "mediaType": "application/wasm",
342
342
  "bytes": 13077,
343
- "sha256": "2c4a4ceef580ee5d6901cf6aaabb9b6d775ba04c28a4e2c52b76bb54c74624b8"
343
+ "sha256": "b26be90f4d1742f29d4a2c2e73b867842a11cf6364f55b124e0ecc43d4038a96"
344
344
  },
345
345
  {
346
346
  "path": "wasm/System.IO.Pipelines.wasm",
@@ -357,8 +357,8 @@ export default {
357
357
  {
358
358
  "path": "wasm/System.Linq.wasm",
359
359
  "mediaType": "application/wasm",
360
- "bytes": 105749,
361
- "sha256": "e1ae8add3a50b7954cfb2027aab357a2ded89b6d9660c9191a2a1581453cc500"
360
+ "bytes": 104213,
361
+ "sha256": "7ee461cf0fa93b5d7b6cb01fe2981fcbd60136de4a7c043db3ed9585e3d69ac5"
362
362
  },
363
363
  {
364
364
  "path": "wasm/System.Memory.wasm",
@@ -370,7 +370,7 @@ export default {
370
370
  "path": "wasm/System.Net.Http.wasm",
371
371
  "mediaType": "application/wasm",
372
372
  "bytes": 142101,
373
- "sha256": "5532cb0efd02b9f2fdd07667ef30cf5afd36aee4e0f06f66f92c1c2720f58b74"
373
+ "sha256": "011ae07a00434aec7d105fb3a91aeca2dec46c6d50f5f54615d24f7479bfaf9e"
374
374
  },
375
375
  {
376
376
  "path": "wasm/System.Net.Primitives.wasm",
@@ -393,8 +393,8 @@ export default {
393
393
  {
394
394
  "path": "wasm/System.Private.CoreLib.wasm",
395
395
  "mediaType": "application/wasm",
396
- "bytes": 2177817,
397
- "sha256": "47758f3aed055b59ebd25e11e4852d82c87d2b82ea23c9c6e04d4fdaa686674e"
396
+ "bytes": 2173721,
397
+ "sha256": "21a6b449ff6943fe6a380200821c7cc23cfff63a1d550558be4b1d7d8a32428f"
398
398
  },
399
399
  {
400
400
  "path": "wasm/System.Private.Uri.wasm",
@@ -442,13 +442,13 @@ export default {
442
442
  "path": "wasm/System.Runtime.InteropServices.wasm",
443
443
  "mediaType": "application/wasm",
444
444
  "bytes": 8981,
445
- "sha256": "24ea6123c5010df4583cca0f5565fabed6c8bc89765f926b7c2fe61edbe1ee4b"
445
+ "sha256": "43ef7629ff33e07595b82613e61335c2d0dee3041296d70313c72f55b087f1f5"
446
446
  },
447
447
  {
448
448
  "path": "wasm/System.Runtime.Intrinsics.wasm",
449
449
  "mediaType": "application/wasm",
450
- "bytes": 5397,
451
- "sha256": "79a92a3b33a4a74323fd61b48b47ee2b47b817f744373c02f8017949799ab6dd"
450
+ "bytes": 4885,
451
+ "sha256": "026a2bf529c62658c440610709b367bbc48976d6f64cdfb8bd6e5f48183070c7"
452
452
  },
453
453
  {
454
454
  "path": "wasm/System.Runtime.Loader.wasm",
@@ -472,7 +472,7 @@ export default {
472
472
  "path": "wasm/System.Runtime.wasm",
473
473
  "mediaType": "application/wasm",
474
474
  "bytes": 16661,
475
- "sha256": "936500e89a8ea4057249ab51af04463554279df097efd77324bd43f5aeef0b63"
475
+ "sha256": "7f46ae98bd0bdedd838fdee5dd3ccdc5b392e0af6af776c31aa78074809f6ff0"
476
476
  },
477
477
  {
478
478
  "path": "wasm/System.Security.Cryptography.wasm",
@@ -496,7 +496,7 @@ export default {
496
496
  "path": "wasm/System.Text.Json.wasm",
497
497
  "mediaType": "application/wasm",
498
498
  "bytes": 251669,
499
- "sha256": "b14800d99c5a02aadbcea8e2e0b2696a6d65dcb312699950c9a6d8f5bac95d28"
499
+ "sha256": "543bfbb2f3af04144ba040b78edf1cfcb8954b808227263cbd7bb2dbf9e95868"
500
500
  },
501
501
  {
502
502
  "path": "wasm/System.Text.RegularExpressions.wasm",
@@ -514,7 +514,7 @@ export default {
514
514
  "path": "wasm/System.Threading.Thread.wasm",
515
515
  "mediaType": "application/wasm",
516
516
  "bytes": 4885,
517
- "sha256": "cfff6ca7df35dc84d7949522d1098e50f311357efb055cceeb25346c82acb3fc"
517
+ "sha256": "c6636418c82d4aba16a99e82c6aab3a082c7c485a870d092f089c8b5152a2141"
518
518
  },
519
519
  {
520
520
  "path": "wasm/System.Threading.ThreadPool.wasm",
@@ -546,17 +546,11 @@ export default {
546
546
  "bytes": 4885,
547
547
  "sha256": "bcf126d8aaa05eaaee1b09f3ef5f224a0983a01f089a0ee21f71733b9edbd118"
548
548
  },
549
- {
550
- "path": "wasm/ZstdSharp.wasm",
551
- "mediaType": "application/wasm",
552
- "bytes": 396565,
553
- "sha256": "291ddc007c01eb1b2fc3459778270ff0e9d31cf7a16d307327955b5f9be53955"
554
- },
555
549
  {
556
550
  "path": "wasm/dotnet.boot.js",
557
551
  "mediaType": "text/javascript",
558
- "bytes": 18023,
559
- "sha256": "602df46f9fade2e2e27feb927e01d3de387ca11810703efe58e7f0942ec42520"
552
+ "bytes": 17861,
553
+ "sha256": "693025847f303f2ec3bf731150746f4a6e9c28a53418d8b5ee4100beb55ece60"
560
554
  },
561
555
  {
562
556
  "path": "wasm/dotnet.js",
@@ -567,14 +561,14 @@ export default {
567
561
  {
568
562
  "path": "wasm/dotnet.native.js",
569
563
  "mediaType": "text/javascript",
570
- "bytes": 248699,
571
- "sha256": "ba22ea6d795060eff2cc5c2ac5439701e6ed30a8c98311a09ebab447d769fb13"
564
+ "bytes": 248697,
565
+ "sha256": "432e3ef8049930253ad4580757a750b831660de5206e350d2c4b98236ecee763"
572
566
  },
573
567
  {
574
568
  "path": "wasm/dotnet.native.wasm",
575
569
  "mediaType": "application/wasm",
576
- "bytes": 6199000,
577
- "sha256": "dc3be76318555466549e78635510239ebfb245ce11530ded1f99ad592b383829"
570
+ "bytes": 6306756,
571
+ "sha256": "4ea6fd68d413a6c88172935ebcd1a39f2c73ae9644d27efb4b1e11727e141617"
578
572
  },
579
573
  {
580
574
  "path": "wasm/dotnet.runtime.js",
@@ -597,8 +591,8 @@ export default {
597
591
  {
598
592
  "path": "wasm/themes.css",
599
593
  "mediaType": "text/css",
600
- "bytes": 3803,
601
- "sha256": "a73a9d0069d07e7db57c63ac509b63ac95a5abe2dfa81df1107161c8258c2d1b"
594
+ "bytes": 3795,
595
+ "sha256": "2a4e25471c5fbb29bd9639bc97311e1dea941ecd7f9cf9d720f18626811ff057"
602
596
  }
603
597
  ]
604
598
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "packageName": "@drawmotive/textgraph",
4
- "packageVersion": "0.2.2-alpha.3",
4
+ "packageVersion": "0.2.2-alpha.5",
5
5
  "abiVersion": "1.0.0",
6
6
  "protocolVersion": 1,
7
7
  "runtimeModule": "wasm/dotnet.js",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "targetFramework": "net10.0",
29
29
  "privateSource": {
30
- "commit": "ee444cb0e43081011079193af05a9707c77739fa",
30
+ "commit": "f7627ef2646772503fb3d8165ee312d4f411566c",
31
31
  "project": "DrawMotive.TextGraph.Bridge"
32
32
  },
33
33
  "entryAssembly": "wasm/DrawMotive.TextGraph.Bridge.wasm",
@@ -52,7 +52,7 @@
52
52
  "path": "wasm/DrawMotive.TextGraph.Bridge.wasm",
53
53
  "mediaType": "application/wasm",
54
54
  "bytes": 87317,
55
- "sha256": "a2364698ab2a1fa49f2765fc2a07729bd2cec4c0d71a866e50d71d8f9f592f67"
55
+ "sha256": "14fa935851250c38939003e847936b5720e95032b8853123e676842f0e58db57"
56
56
  },
57
57
  {
58
58
  "path": "wasm/ExCSS.wasm",
@@ -75,20 +75,20 @@
75
75
  {
76
76
  "path": "wasm/Graphics.Core.wasm",
77
77
  "mediaType": "application/wasm",
78
- "bytes": 3976473,
79
- "sha256": "a5445e3d5772dce757295c41386c68cf0c69814b8da7e8036d14782b4c8e8c82"
78
+ "bytes": 4040985,
79
+ "sha256": "4640576049a4a9a4d2e6ee28683f4b802196f4178ce98c7acbb15a7f46a70340"
80
80
  },
81
81
  {
82
82
  "path": "wasm/Grpc.Core.Api.wasm",
83
83
  "mediaType": "application/wasm",
84
84
  "bytes": 7957,
85
- "sha256": "700b023d385701ac6485a6e580b570cffa445bc6a5d494d5b39e7670e20f0cb6"
85
+ "sha256": "5fa5dee84b136e0cf6c1609f9bf8d61836c79622d64f2678feaf2dab49845ee0"
86
86
  },
87
87
  {
88
88
  "path": "wasm/HarfBuzzSharp.wasm",
89
89
  "mediaType": "application/wasm",
90
90
  "bytes": 30485,
91
- "sha256": "d2b9c8dcc6c55fe4f65ec43e58f4b719a99af3d753cd60f4894195241c134577"
91
+ "sha256": "00d464ae97a97bebdbb33e8f92382359b8245b65baf06ee2606f6053bab1fb3b"
92
92
  },
93
93
  {
94
94
  "path": "wasm/MagicOnion.Abstractions.wasm",
@@ -118,7 +118,7 @@
118
118
  "path": "wasm/Math.Core.wasm",
119
119
  "mediaType": "application/wasm",
120
120
  "bytes": 3241241,
121
- "sha256": "ac0c117fdea47e1bf35fcedb4e7ca99bdba46d150ed3820bb8db12cdbad109d6"
121
+ "sha256": "a5dcb82cad00d2e85b9b8e09cc140f9bfecfd5243571936172fc9c1a8241c6c9"
122
122
  },
123
123
  {
124
124
  "path": "wasm/MemoryPack.Core.wasm",
@@ -166,7 +166,7 @@
166
166
  "path": "wasm/Microsoft.Extensions.Logging.Abstractions.wasm",
167
167
  "mediaType": "application/wasm",
168
168
  "bytes": 19221,
169
- "sha256": "cf2766ee32cf69589ff85a96d5a459450294313a5f5469779eac2ca5c431a03b"
169
+ "sha256": "db4d78d4fa5428496d5fc50050bef9bea40a4f3ecee2deb96d51693719d5b6ad"
170
170
  },
171
171
  {
172
172
  "path": "wasm/Microsoft.Extensions.Logging.wasm",
@@ -214,7 +214,7 @@
214
214
  "path": "wasm/Polly.Core.wasm",
215
215
  "mediaType": "application/wasm",
216
216
  "bytes": 62741,
217
- "sha256": "962f999356db478d3a163ca06d1e2f1c5c54601320e983119b785490e630bb3a"
217
+ "sha256": "757082368c44baa7450a85431f565f29e717b4495d7e29e450a8f08f5f11a8a1"
218
218
  },
219
219
  {
220
220
  "path": "wasm/Polly.wasm",
@@ -238,7 +238,7 @@
238
238
  "path": "wasm/RBush.wasm",
239
239
  "mediaType": "application/wasm",
240
240
  "bytes": 24341,
241
- "sha256": "11a0ea636256c3b15bd02509e8a7331efbaab6f24aa50de1dcd011e9b0e4821c"
241
+ "sha256": "841fbdeb5770d8fc9fb6fdcbca9fa796fb6e123d32501e5dfc5cdbcb6dd1ab69"
242
242
  },
243
243
  {
244
244
  "path": "wasm/SkiaSharp.HarfBuzz.wasm",
@@ -249,8 +249,8 @@
249
249
  {
250
250
  "path": "wasm/SkiaSharp.wasm",
251
251
  "mediaType": "application/wasm",
252
- "bytes": 97557,
253
- "sha256": "329784f35a4aef78ac831e0154488f1258eb63fc14ca86171aae7ec5b1c0318b"
252
+ "bytes": 102677,
253
+ "sha256": "6d16b6aa7d90253782d1d4b1d8b98a1544b5966fd97aaa1a277d5cdb897c36d2"
254
254
  },
255
255
  {
256
256
  "path": "wasm/Stateless.wasm",
@@ -261,8 +261,8 @@
261
261
  {
262
262
  "path": "wasm/System.Collections.Concurrent.wasm",
263
263
  "mediaType": "application/wasm",
264
- "bytes": 39189,
265
- "sha256": "05752be3ea6b72ec64966fabeeccec0e4b4521f3df71bf3fd3648f691cb3f7ad"
264
+ "bytes": 36117,
265
+ "sha256": "25ea32876f0cf30040f7e050d1ee0036fd89aba213356f3cb3f0ec6a51533882"
266
266
  },
267
267
  {
268
268
  "path": "wasm/System.Collections.Immutable.wasm",
@@ -316,7 +316,7 @@
316
316
  "path": "wasm/System.Console.wasm",
317
317
  "mediaType": "application/wasm",
318
318
  "bytes": 15125,
319
- "sha256": "0402a653e9545a0c61b64999f2389e099796a3cd115b1199b36a2867a8aa67fa"
319
+ "sha256": "c16bce901ec0b1db33de58dc79ed18cc26dd4aa5825e0d72fd51252e399ce080"
320
320
  },
321
321
  {
322
322
  "path": "wasm/System.Diagnostics.StackTrace.wasm",
@@ -333,14 +333,14 @@
333
333
  {
334
334
  "path": "wasm/System.IO.Compression.Brotli.wasm",
335
335
  "mediaType": "application/wasm",
336
- "bytes": 5397,
337
- "sha256": "178c6225cedf7c0bdc4e6ebb9353688c013cd933aa26eb4dd862e9f2782ec8b6"
336
+ "bytes": 6933,
337
+ "sha256": "0dc3c240262506a5eb77eb6202b2a12c37aea973524ba967295c0f3a843d3598"
338
338
  },
339
339
  {
340
340
  "path": "wasm/System.IO.Compression.wasm",
341
341
  "mediaType": "application/wasm",
342
342
  "bytes": 13077,
343
- "sha256": "2c4a4ceef580ee5d6901cf6aaabb9b6d775ba04c28a4e2c52b76bb54c74624b8"
343
+ "sha256": "b26be90f4d1742f29d4a2c2e73b867842a11cf6364f55b124e0ecc43d4038a96"
344
344
  },
345
345
  {
346
346
  "path": "wasm/System.IO.Pipelines.wasm",
@@ -357,8 +357,8 @@
357
357
  {
358
358
  "path": "wasm/System.Linq.wasm",
359
359
  "mediaType": "application/wasm",
360
- "bytes": 105749,
361
- "sha256": "e1ae8add3a50b7954cfb2027aab357a2ded89b6d9660c9191a2a1581453cc500"
360
+ "bytes": 104213,
361
+ "sha256": "7ee461cf0fa93b5d7b6cb01fe2981fcbd60136de4a7c043db3ed9585e3d69ac5"
362
362
  },
363
363
  {
364
364
  "path": "wasm/System.Memory.wasm",
@@ -370,7 +370,7 @@
370
370
  "path": "wasm/System.Net.Http.wasm",
371
371
  "mediaType": "application/wasm",
372
372
  "bytes": 142101,
373
- "sha256": "5532cb0efd02b9f2fdd07667ef30cf5afd36aee4e0f06f66f92c1c2720f58b74"
373
+ "sha256": "011ae07a00434aec7d105fb3a91aeca2dec46c6d50f5f54615d24f7479bfaf9e"
374
374
  },
375
375
  {
376
376
  "path": "wasm/System.Net.Primitives.wasm",
@@ -393,8 +393,8 @@
393
393
  {
394
394
  "path": "wasm/System.Private.CoreLib.wasm",
395
395
  "mediaType": "application/wasm",
396
- "bytes": 2177817,
397
- "sha256": "47758f3aed055b59ebd25e11e4852d82c87d2b82ea23c9c6e04d4fdaa686674e"
396
+ "bytes": 2173721,
397
+ "sha256": "21a6b449ff6943fe6a380200821c7cc23cfff63a1d550558be4b1d7d8a32428f"
398
398
  },
399
399
  {
400
400
  "path": "wasm/System.Private.Uri.wasm",
@@ -442,13 +442,13 @@
442
442
  "path": "wasm/System.Runtime.InteropServices.wasm",
443
443
  "mediaType": "application/wasm",
444
444
  "bytes": 8981,
445
- "sha256": "24ea6123c5010df4583cca0f5565fabed6c8bc89765f926b7c2fe61edbe1ee4b"
445
+ "sha256": "43ef7629ff33e07595b82613e61335c2d0dee3041296d70313c72f55b087f1f5"
446
446
  },
447
447
  {
448
448
  "path": "wasm/System.Runtime.Intrinsics.wasm",
449
449
  "mediaType": "application/wasm",
450
- "bytes": 5397,
451
- "sha256": "79a92a3b33a4a74323fd61b48b47ee2b47b817f744373c02f8017949799ab6dd"
450
+ "bytes": 4885,
451
+ "sha256": "026a2bf529c62658c440610709b367bbc48976d6f64cdfb8bd6e5f48183070c7"
452
452
  },
453
453
  {
454
454
  "path": "wasm/System.Runtime.Loader.wasm",
@@ -472,7 +472,7 @@
472
472
  "path": "wasm/System.Runtime.wasm",
473
473
  "mediaType": "application/wasm",
474
474
  "bytes": 16661,
475
- "sha256": "936500e89a8ea4057249ab51af04463554279df097efd77324bd43f5aeef0b63"
475
+ "sha256": "7f46ae98bd0bdedd838fdee5dd3ccdc5b392e0af6af776c31aa78074809f6ff0"
476
476
  },
477
477
  {
478
478
  "path": "wasm/System.Security.Cryptography.wasm",
@@ -496,7 +496,7 @@
496
496
  "path": "wasm/System.Text.Json.wasm",
497
497
  "mediaType": "application/wasm",
498
498
  "bytes": 251669,
499
- "sha256": "b14800d99c5a02aadbcea8e2e0b2696a6d65dcb312699950c9a6d8f5bac95d28"
499
+ "sha256": "543bfbb2f3af04144ba040b78edf1cfcb8954b808227263cbd7bb2dbf9e95868"
500
500
  },
501
501
  {
502
502
  "path": "wasm/System.Text.RegularExpressions.wasm",
@@ -514,7 +514,7 @@
514
514
  "path": "wasm/System.Threading.Thread.wasm",
515
515
  "mediaType": "application/wasm",
516
516
  "bytes": 4885,
517
- "sha256": "cfff6ca7df35dc84d7949522d1098e50f311357efb055cceeb25346c82acb3fc"
517
+ "sha256": "c6636418c82d4aba16a99e82c6aab3a082c7c485a870d092f089c8b5152a2141"
518
518
  },
519
519
  {
520
520
  "path": "wasm/System.Threading.ThreadPool.wasm",
@@ -546,17 +546,11 @@
546
546
  "bytes": 4885,
547
547
  "sha256": "bcf126d8aaa05eaaee1b09f3ef5f224a0983a01f089a0ee21f71733b9edbd118"
548
548
  },
549
- {
550
- "path": "wasm/ZstdSharp.wasm",
551
- "mediaType": "application/wasm",
552
- "bytes": 396565,
553
- "sha256": "291ddc007c01eb1b2fc3459778270ff0e9d31cf7a16d307327955b5f9be53955"
554
- },
555
549
  {
556
550
  "path": "wasm/dotnet.boot.js",
557
551
  "mediaType": "text/javascript",
558
- "bytes": 18023,
559
- "sha256": "602df46f9fade2e2e27feb927e01d3de387ca11810703efe58e7f0942ec42520"
552
+ "bytes": 17861,
553
+ "sha256": "693025847f303f2ec3bf731150746f4a6e9c28a53418d8b5ee4100beb55ece60"
560
554
  },
561
555
  {
562
556
  "path": "wasm/dotnet.js",
@@ -567,14 +561,14 @@
567
561
  {
568
562
  "path": "wasm/dotnet.native.js",
569
563
  "mediaType": "text/javascript",
570
- "bytes": 248699,
571
- "sha256": "ba22ea6d795060eff2cc5c2ac5439701e6ed30a8c98311a09ebab447d769fb13"
564
+ "bytes": 248697,
565
+ "sha256": "432e3ef8049930253ad4580757a750b831660de5206e350d2c4b98236ecee763"
572
566
  },
573
567
  {
574
568
  "path": "wasm/dotnet.native.wasm",
575
569
  "mediaType": "application/wasm",
576
- "bytes": 6199000,
577
- "sha256": "dc3be76318555466549e78635510239ebfb245ce11530ded1f99ad592b383829"
570
+ "bytes": 6306756,
571
+ "sha256": "4ea6fd68d413a6c88172935ebcd1a39f2c73ae9644d27efb4b1e11727e141617"
578
572
  },
579
573
  {
580
574
  "path": "wasm/dotnet.runtime.js",
@@ -597,8 +591,8 @@
597
591
  {
598
592
  "path": "wasm/themes.css",
599
593
  "mediaType": "text/css",
600
- "bytes": 3803,
601
- "sha256": "a73a9d0069d07e7db57c63ac509b63ac95a5abe2dfa81df1107161c8258c2d1b"
594
+ "bytes": 3795,
595
+ "sha256": "2a4e25471c5fbb29bd9639bc97311e1dea941ecd7f9cf9d720f18626811ff057"
602
596
  }
603
597
  ]
604
598
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawmotive/textgraph",
3
- "version": "0.2.2-alpha.3",
3
+ "version": "0.2.2-alpha.5",
4
4
  "description": "Render TextGraph flowcharts and directed graphs as PNG images in Node.js, browsers, React, and Web Workers.",
5
5
  "keywords": [
6
6
  "textgraph",
@@ -81,15 +81,15 @@
81
81
  "node": ">=22"
82
82
  },
83
83
  "devDependencies": {
84
- "@playwright/test": "1.61.0",
84
+ "@playwright/test": "1.63.0",
85
85
  "ajv": "8.20.0",
86
86
  "pngjs": "7.0.0",
87
87
  "typescript": "^7.0.2",
88
- "react": "^19.0.0",
89
- "react-dom": "^19.0.0",
90
- "@types/react": "^19.0.0",
91
- "@types/react-dom": "^19.0.0",
92
- "jsdom": "^26.1.0"
88
+ "react": "^19.3.0",
89
+ "react-dom": "^19.3.0",
90
+ "@types/react": "^19.3.0",
91
+ "@types/react-dom": "^19.3.0",
92
+ "jsdom": "^30.1.2"
93
93
  },
94
94
  "publishConfig": {
95
95
  "access": "public",
package/src/index.d.ts CHANGED
@@ -65,7 +65,7 @@ export interface TextGraphRuntime {
65
65
  readonly abiVersion: string;
66
66
  readonly capabilities?: readonly string[];
67
67
  validate?(source: string): string | Promise<string>;
68
- execute?(requestJson: string, context?: { signal?: AbortSignal }): string | Promise<string>;
68
+ execute?(requestJson: string, context?: { signal?: AbortSignal; onPreview?(responseJson: string): void }): string | Promise<string>;
69
69
  dispose?(): void | Promise<void>;
70
70
  }
71
71
 
@@ -127,6 +127,8 @@ export interface TextGraphLanguagePack {
127
127
  export type TextGraphPngEncoding = 'bytes' | 'base64';
128
128
 
129
129
  export interface TextGraphRenderPngOptions<Encoding extends TextGraphPngEncoding = TextGraphPngEncoding> {
130
+ /** Receives a provisional image before optional fonts load; the promise returns the final image. */
131
+ onPreview?(result: Extract<TextGraphRenderPngResult<Encoding extends 'base64' ? string : Uint8Array>, { success: true }>): void;
130
132
  /** Optional language hint for ambiguous Han text, e.g. ja or zh-CN. */
131
133
  language?: string;
132
134
  /** Defaults to bytes. Base64 contains no data-URL prefix. */
@@ -149,6 +151,8 @@ export type TextGraphRenderPngResult<Png extends Uint8Array | string = Uint8Arra
149
151
 
150
152
  /** Vector export uses logical diagram units and requires textgraph-render-svg-v1. */
151
153
  export interface TextGraphRenderSvgOptions {
154
+ /** Receives a provisional vector before optional fonts load; the promise returns the final vector. */
155
+ onPreview?(result: Extract<TextGraphRenderSvgResult, { success: true }>): void;
152
156
  /** Optional language hint for ambiguous Han text, e.g. ja or zh-CN. */
153
157
  language?: string;
154
158
  /** Non-negative padding in diagram units. Defaults to 10. */
package/src/index.js CHANGED
@@ -38,13 +38,19 @@ export async function initializeTextGraph(options = {}) {
38
38
  // Every export format uses the same operation queue and resource-preparing executor.
39
39
  const render = async (source, callOptions, normalize, decode, capability, format) => {
40
40
  if (instance.state !== 'ready') throw new DrawMotiveError('INSTANCE_DISPOSED', 'The runtime instance is disposing or disposed');
41
- const { request, encoding, signal } = normalize(source, callOptions);
41
+ const { request, encoding, signal, onPreview } = normalize(source, callOptions);
42
42
  throwIfAborted(signal);
43
43
  if (typeof runtime.execute !== 'function' || !capabilities.includes(capability)) throw new DrawMotiveError('UNSUPPORTED_CAPABILITY', `Runtime does not support ${format} rendering`);
44
44
  return instance.mutate(async () => {
45
45
  throwIfAborted(signal);
46
46
  let response;
47
- try { response = await runtime.execute(JSON.stringify(request), { signal }); }
47
+ const context = { signal };
48
+ if (onPreview) context.onPreview = json => {
49
+ throwIfAborted(signal);
50
+ const preview = decode(json, encoding);
51
+ if (preview.success) onPreview(preview);
52
+ };
53
+ try { response = await runtime.execute(JSON.stringify(request), context); }
48
54
  catch (cause) {
49
55
  throwIfAborted(signal);
50
56
  if (cause instanceof DrawMotiveError || cause?.name === 'AbortError') throw cause;
@@ -12,8 +12,10 @@ export interface TextGraphProps extends Omit<ImgHTMLAttributes<HTMLImageElement>
12
12
  source: string;
13
13
  alt?: string;
14
14
  /** Defaults to scale 1; the image displays at logical size. */
15
- renderOptions?: Omit<TextGraphRenderPngOptions, 'encoding' | 'signal'>;
15
+ renderOptions?: Omit<TextGraphRenderPngOptions, 'encoding' | 'signal' | 'onPreview'>;
16
16
  loading?: ReactNode;
17
+ /** Shown beside the provisional image until optional fonts finish loading. */
18
+ fontsLoading?: ReactNode;
17
19
  }
18
20
 
19
21
  export declare function TextGraphProvider(props: TextGraphProviderProps): ReactElement;
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { createContext, createElement, useContext, useEffect, useMemo, useState } from 'react';
3
+ import { Fragment, createContext, createElement, useContext, useEffect, useMemo, useState } from 'react';
4
4
  import { createRuntimeResource } from './runtime.js';
5
5
 
6
6
  const RuntimeContext = createContext(null);
@@ -15,12 +15,12 @@ export function TextGraphProvider({ options = defaultOptions, children }) {
15
15
 
16
16
  /** Renders DSL as an image; only the latest committed input may publish a result. */
17
17
  export function TextGraph({ source, alt = 'TextGraph diagram', renderOptions = defaultOptions,
18
- loading = 'Rendering diagram…', style, ...imageProps }) {
18
+ loading = 'Rendering diagram…', fontsLoading = 'Fonts are still loading…', style, ...imageProps }) {
19
19
  const shared = useContext(RuntimeContext);
20
20
  const resource = useMemo(() => shared ?? createRuntimeResource(defaultOptions), [shared]);
21
- const { scale = 1, padding, maxWidth } = renderOptions;
22
- const request = useMemo(() => ({ source, scale, padding, maxWidth, resource }),
23
- [source, scale, padding, maxWidth, resource]);
21
+ const { scale = 1, padding, maxWidth, language } = renderOptions;
22
+ const request = useMemo(() => ({ source, scale, padding, maxWidth, language, resource }),
23
+ [source, scale, padding, maxWidth, language, resource]);
24
24
  const [state, setState] = useState(null);
25
25
 
26
26
  useEffect(() => {
@@ -30,13 +30,16 @@ export function TextGraph({ source, alt = 'TextGraph diagram', renderOptions = d
30
30
  try {
31
31
  const runtime = await resource.get();
32
32
  if (controller.signal.aborted) return;
33
- const result = await runtime.renderPng(source, { scale, padding, maxWidth, encoding: 'base64', signal: controller.signal });
33
+ const result = await runtime.renderPng(source, { scale, padding, maxWidth, language, encoding: 'base64', signal: controller.signal,
34
+ onPreview: result => { if (!controller.signal.aborted) setState({ request, result, fontsLoading: true }); },
35
+ });
34
36
  if (!controller.signal.aborted) setState({ request, result });
35
37
  } catch (error) {
36
38
  if (!controller.signal.aborted) {
37
39
  // Host loaders may reject arbitrary values, including null. Keep the
38
40
  // failure branch explicit so reporting an error cannot break React render.
39
- setState({ request, error: error instanceof Error ? error : new Error(String(error ?? 'Could not render diagram')) });
41
+ setState(previous => ({ request, result: previous?.request === request ? previous.result : undefined,
42
+ error: error instanceof Error ? error : new Error(String(error ?? 'Could not render diagram')) }));
40
43
  }
41
44
  }
42
45
  })();
@@ -46,14 +49,17 @@ export function TextGraph({ source, alt = 'TextGraph diagram', renderOptions = d
46
49
  // State is keyed by the whole request so old images disappear immediately,
47
50
  // even before passive effects clean up the previous asynchronous render.
48
51
  if (state?.request !== request) return createElement('span', { role: 'status' }, loading);
49
- if (state.error || !state.result.success) {
52
+ if (!state.result?.success) {
50
53
  const message = state.error?.message ?? state.result.diagnostics.map(item => item.message).join('\n');
51
54
  return createElement('span', { role: 'alert' }, message || 'Could not render diagram');
52
55
  }
53
56
  const result = state.result;
54
57
  // Density improves sharpness without enlarging the diagram. Native dimensions
55
58
  // also preserve logical size when maxWidth lowers the actual render density.
56
- return createElement('img', { width: result.displayWidth ?? result.width / scale, height: result.displayHeight ?? result.height / scale, ...imageProps,
59
+ const image = createElement('img', { width: result.displayWidth ?? result.width / scale, height: result.displayHeight ?? result.height / scale, ...imageProps,
57
60
  style: { maxWidth: '100%', ...(imageProps.height == null ? { height: 'auto' } : {}), ...style },
58
61
  src: `data:image/png;base64,${result.png}`, alt });
62
+ if (state.error) return createElement(Fragment, null, image, createElement('span', { role: 'alert' }, state.error.message));
63
+ if (state.fontsLoading) return createElement(Fragment, null, image, createElement('span', { role: 'status' }, fontsLoading));
64
+ return image;
59
65
  }
@@ -0,0 +1,36 @@
1
+ import { readVerifiedAsset } from './integrity.js';
2
+ import { throwIfAborted } from './lifecycle.js';
3
+
4
+ const cacheName = 'textgraph-fonts-v1';
5
+
6
+ /** HTTP fonts are immutable by content hash. Cache verified bytes in browsers
7
+ * and Workers without requiring host headers or a service worker; local files
8
+ * stay authoritative. Storage is optional and cannot turn valid I/O into failure. */
9
+ export async function readVerifiedFontAsset(item, options, readAsset) {
10
+ if (!['http:', 'https:'].includes(item.url?.protocol)) return readVerifiedAsset(item, options, readAsset);
11
+ const url = new URL(item.url);
12
+ url.searchParams.set('v', item.asset.sha256);
13
+ const versioned = { ...item, url };
14
+ let cache;
15
+ try { cache = await globalThis.caches?.open(cacheName); } catch { /* Private contexts may deny storage. */ }
16
+ throwIfAborted(options.signal);
17
+ let cached;
18
+ try { cached = await cache?.match(url.href); } catch { /* Read from the configured source instead. */ }
19
+ throwIfAborted(options.signal);
20
+ if (cached) {
21
+ try {
22
+ const bytes = await readVerifiedAsset(versioned, options, async () => cached);
23
+ throwIfAborted(options.signal);
24
+ return bytes;
25
+ } catch {
26
+ throwIfAborted(options.signal);
27
+ // A damaged entry grants no authority: evict and verify the original source.
28
+ try { await cache.delete(url.href); } catch { /* A new download can still succeed. */ }
29
+ }
30
+ }
31
+ const bytes = await readVerifiedAsset(versioned, options, readAsset);
32
+ throwIfAborted(options.signal);
33
+ try { await cache?.put(url.href, new Response(bytes, { headers: { 'Content-Type': 'font/ttf' } })); } catch { /* Quota and storage failures are non-fatal. */ }
34
+ throwIfAborted(options.signal);
35
+ return bytes;
36
+ }
@@ -2,7 +2,7 @@ import { DrawMotiveError } from './errors.js';
2
2
  import { normalizeFontMetadata } from './language-packs.js';
3
3
  import { decodeDiagnostics } from './validation.js';
4
4
  import { decodeConfiguration } from './rendering.js';
5
- import { readVerifiedAsset } from './integrity.js';
5
+ import { readVerifiedFontAsset } from './font-cache.js';
6
6
  import { throwIfAborted } from './lifecycle.js';
7
7
 
8
8
  export const fontCapability = 'textgraph-fonts-v1';
@@ -102,6 +102,7 @@ export function createFontPreparation({ options, readAsset, execute }) {
102
102
  const loading = { ...options, signal: context.signal };
103
103
  const attempted = new Set();
104
104
  let consumed = false;
105
+ let previewed = false;
105
106
  try {
106
107
  while (true) {
107
108
  throwIfAborted(context.signal);
@@ -110,6 +111,14 @@ export function createFontPreparation({ options, readAsset, execute }) {
110
111
  if (!prepared.success) return JSON.stringify({ protocolVersion: 1, success: false, diagnostics: prepared.diagnostics });
111
112
  const missing = prepared.fontRuns.filter(run => run.missing && run.language);
112
113
  if (!missing.length) break;
114
+ // Native can render with its installed fallback fonts. Publish that image
115
+ // before optional I/O; final measurement still runs after installation.
116
+ if (context.onPreview && !previewed) {
117
+ const preview = await execute(request);
118
+ throwIfAborted(context.signal);
119
+ context.onPreview(preview);
120
+ previewed = true;
121
+ }
113
122
  const selected = new Set();
114
123
  let available = explicit;
115
124
  for (const run of missing) {
@@ -126,7 +135,7 @@ export function createFontPreparation({ options, readAsset, execute }) {
126
135
  for (const font of selected) {
127
136
  attempted.add(font.family);
128
137
  throwIfAborted(context.signal);
129
- const bytes = await readVerifiedAsset({ asset: { path: `font:${font.family}`, bytes: font.bytes, sha256: font.sha256, mediaType: 'font/ttf' }, url: font.source }, loading,
138
+ const bytes = await readVerifiedFontAsset({ asset: { path: `font:${font.family}`, bytes: font.bytes, sha256: font.sha256, mediaType: 'font/ttf' }, url: font.source }, loading,
130
139
  font.source instanceof Uint8Array ? async () => new Response(font.source) : readAsset);
131
140
  throwIfAborted(context.signal);
132
141
  fonts.push({ family: font.family, data: fontBase64(bytes), languages: font.languages });
@@ -1,5 +1,6 @@
1
1
  import { createRuntimeAssetPlan } from './shared.js';
2
2
  import { readVerifiedAsset } from './integrity.js';
3
+ import { readVerifiedFontAsset } from './font-cache.js';
3
4
  import { decodeConfiguration } from './rendering.js';
4
5
  import { throwIfAborted } from './lifecycle.js';
5
6
  import { createFontPreparation, fontBase64 as base64, fontCapability } from './font-resources.js';
@@ -18,9 +19,10 @@ export function createRenderingExecutor({ manifest, options, readAsset, execute
18
19
  const selected = new Set([manifest.rendering.theme, ...manifest.rendering.fonts.map(font => font.asset)]);
19
20
  const plan = createRuntimeAssetPlan({ manifest: { assets: manifest.assets.filter(asset => selected.has(asset.path)) }, moduleUrl: import.meta.url, resolveAsset: options.resolveAsset });
20
21
  const byPath = new Map(plan.map(item => [item.asset.path, item]));
22
+ const fontPaths = new Set(manifest.rendering.fonts.map(font => font.asset));
21
23
  const load = async path => {
22
24
  throwIfAborted(context.signal);
23
- const bytes = await readVerifiedAsset(byPath.get(path), loadingOptions, readAsset);
25
+ const bytes = await (fontPaths.has(path) ? readVerifiedFontAsset : readVerifiedAsset)(byPath.get(path), loadingOptions, readAsset);
24
26
  throwIfAborted(context.signal);
25
27
  return bytes;
26
28
  };
@@ -34,7 +36,7 @@ export function createRenderingExecutor({ manifest, options, readAsset, execute
34
36
  throwIfAborted(context.signal);
35
37
  const item = { asset: { path: `font:${font.family}`, mediaType: 'font/ttf', bytes: font.bytes, sha256: font.sha256 }, url: font.source };
36
38
  const bytes = font.coverage
37
- ? await readVerifiedAsset(item, loadingOptions, font.source instanceof Uint8Array ? async () => new Response(font.source) : readAsset)
39
+ ? await readVerifiedFontAsset(item, loadingOptions, font.source instanceof Uint8Array ? async () => new Response(font.source) : readAsset)
38
40
  : font.source instanceof Uint8Array ? font.source : new Uint8Array(await (await readAsset(item, loadingOptions)).arrayBuffer());
39
41
  throwIfAborted(context.signal);
40
42
  fonts.push({ family: font.family, data: base64(bytes), ...(font.languages ? { languages: font.languages } : {}) });
@@ -7,30 +7,31 @@ const invalid = message => { throw new DrawMotiveError('INVALID_ARGUMENT', messa
7
7
  /** Both export formats share source, logical padding, language and cancellation semantics. */
8
8
  function normalizeCommonOptions(source, options) {
9
9
  if (typeof source !== 'string' || !options || typeof options !== 'object' || Array.isArray(options)) invalid('Rendering requires a source string and options object');
10
- const { padding = 10, signal, language } = options;
10
+ const { padding = 10, signal, language, onPreview } = options;
11
+ if (onPreview !== undefined && typeof onPreview !== 'function') invalid('onPreview must be a function');
11
12
  if (language !== undefined && (typeof language !== 'string' || !/^[a-z]{2,8}(?:-[A-Za-z0-9]+)*$/.test(language))) invalid('language must be a language tag');
12
13
  // The native geometry contract uses float32; reject values lost at that boundary.
13
14
  if (!Number.isFinite(padding) || !Number.isFinite(Math.fround(padding)) || padding < 0) invalid('padding must be non-negative and representable as a finite float32');
14
15
  validateSignal(signal);
15
- return { padding, signal, request: { protocolVersion: 1, operation: 'render', source, ...(language === undefined ? {} : { language }) } };
16
+ return { padding, signal, onPreview, request: { protocolVersion: 1, operation: 'render', source, ...(language === undefined ? {} : { language }) } };
16
17
  }
17
18
 
18
19
  /** Normalize export settings before queuing; native receives only the render contract. */
19
20
  export function normalizeRenderOptions(source, options) {
20
- const { padding, signal, request } = normalizeCommonOptions(source, options);
21
+ const { padding, signal, onPreview, request } = normalizeCommonOptions(source, options);
21
22
  const { encoding = 'bytes', scale = 1, maxWidth } = options;
22
23
  if (!['bytes', 'base64'].includes(encoding)) invalid('encoding must be bytes or base64');
23
24
  // The native geometry contract uses float32; reject values lost at that boundary.
24
25
  if (!Number.isFinite(scale) || !Number.isFinite(Math.fround(scale)) || Math.fround(scale) <= 0) invalid('scale must be positive and representable as a finite float32');
25
26
  if (maxWidth !== undefined && (!Number.isSafeInteger(maxWidth) || maxWidth <= 0)) invalid('maxWidth must be a positive integer');
26
- return { encoding, signal, request: { ...request, export: { format: 'png', scale, padding, ...(maxWidth === undefined ? {} : { maxWidth }) } } };
27
+ return { encoding, signal, onPreview, request: { ...request, export: { format: 'png', scale, padding, ...(maxWidth === undefined ? {} : { maxWidth }) } } };
27
28
  }
28
29
 
29
30
  /** SVG accepts only logical export settings; PNG density and encoding have no vector meaning. */
30
31
  export function normalizeSvgOptions(source, options) {
31
- const { padding, signal, request } = normalizeCommonOptions(source, options);
32
- if (Reflect.ownKeys(options).some(key => !['padding', 'language', 'signal'].includes(key))) invalid('Unknown SVG rendering option');
33
- return { signal, request: { ...request, export: { format: 'svg', padding } } };
32
+ const { padding, signal, onPreview, request } = normalizeCommonOptions(source, options);
33
+ if (Reflect.ownKeys(options).some(key => !['padding', 'language', 'signal', 'onPreview'].includes(key))) invalid('Unknown SVG rendering option');
34
+ return { signal, onPreview, request: { ...request, export: { format: 'svg', padding } } };
34
35
  }
35
36
 
36
37
  /** Accept canonical base64 only; platform atob implementations otherwise tolerate corruption. */
Binary file