@drawmotive/textgraph 0.2.0 → 0.2.1
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 +11 -4
- package/docs/api-v1.md +9 -3
- package/docs/react.md +6 -3
- package/docs/releases.md +9 -8
- package/generated/wasm/DrawMotive.TextGraph.Bridge.wasm +0 -0
- package/generated/wasm/Graphics.Core.wasm +0 -0
- package/generated/wasm/Grpc.Core.Api.wasm +0 -0
- package/generated/wasm/HarfBuzzSharp.wasm +0 -0
- package/generated/wasm/Math.Core.wasm +0 -0
- package/generated/wasm/Polly.Core.wasm +0 -0
- package/generated/wasm/RBush.wasm +0 -0
- package/generated/wasm/SkiaSharp.wasm +0 -0
- package/generated/wasm/dotnet.boot.js +10 -10
- package/generated/wasm/dotnet.native.js +2 -2
- package/generated/wasm/dotnet.native.wasm +0 -0
- package/generated/wasm-manifest.js +17 -17
- package/generated/wasm-manifest.json +17 -17
- package/package.json +1 -1
- package/schemas/render.schema.json +33 -1
- package/src/index.d.ts +4 -2
- package/src/platform/node.d.ts +2 -0
- package/src/platform/node.js +4 -1
- package/src/react/index.d.ts +1 -0
- package/src/react/index.js +6 -3
- package/src/runtime/dotnet.js +3 -1
- package/src/runtime/node.js +22 -1
- package/src/runtime/rendering.js +11 -3
package/README.md
CHANGED
|
@@ -8,12 +8,12 @@ Render TextGraph diagrams as PNG images and validate diagram source.
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
npm install @drawmotive/textgraph@0.2.
|
|
11
|
+
npm install @drawmotive/textgraph@0.2.1
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
Requires Node.js 22+ or a modern browser.
|
|
15
15
|
|
|
16
|
-
Version `0.2.0`
|
|
16
|
+
Version `0.2.1` defaults React PNG rendering to scale `1`, matching the base SDK. Version `0.2.0` was the first stable release. The exact installation command above becomes available after `0.2.1` is published; `@latest` selects the current published stable release.
|
|
17
17
|
|
|
18
18
|
## Quickstart
|
|
19
19
|
|
|
@@ -36,7 +36,7 @@ try {
|
|
|
36
36
|
}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
PNG output defaults to bytes, scale `
|
|
39
|
+
PNG output defaults to bytes, scale `1`, padding `10`, and a white background. Reuse an instance for multiple diagrams, then call `dispose()`.
|
|
40
40
|
|
|
41
41
|
## Higher resolution
|
|
42
42
|
|
|
@@ -65,6 +65,7 @@ const textgraph = await initializeTextGraph({
|
|
|
65
65
|
});
|
|
66
66
|
const result = await textgraph.renderPng("A -> B", {
|
|
67
67
|
encoding: "base64",
|
|
68
|
+
scale: 2,
|
|
68
69
|
maxWidth: 1200,
|
|
69
70
|
});
|
|
70
71
|
|
|
@@ -72,12 +73,18 @@ if (result.success) {
|
|
|
72
73
|
const image = document.createElement("img");
|
|
73
74
|
image.src = `data:image/png;base64,${result.png}`;
|
|
74
75
|
image.alt = "A connects to B";
|
|
76
|
+
image.width = result.displayWidth ?? result.width / 2;
|
|
77
|
+
image.height = result.displayHeight ?? result.height / 2;
|
|
78
|
+
image.style.maxWidth = "100%";
|
|
79
|
+
image.style.height = "auto";
|
|
75
80
|
document.body.append(image);
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
await textgraph.dispose();
|
|
79
84
|
```
|
|
80
85
|
|
|
86
|
+
Scale `2` supplies more pixels for sharper web images; `displayWidth` and `displayHeight` keep the diagram at its logical size, including when `maxWidth` lowers the raster density. PNG DPI metadata does not control its CSS size. Older runtimes omit display dimensions; dividing pixel dimensions by the requested scale is a fallback that cannot recover logical size after `maxWidth` clamps the output.
|
|
87
|
+
|
|
81
88
|
The npm package includes WASM, .NET runtime modules, fonts, and themes. Bundlers do not automatically deploy these dynamically loaded files. Re-copy assets after SDK updates and use an asset URL matching your deployment base path. Node loads assets from the installed package without this copy step.
|
|
82
89
|
|
|
83
90
|
## React
|
|
@@ -100,7 +107,7 @@ export function App() {
|
|
|
100
107
|
}
|
|
101
108
|
```
|
|
102
109
|
|
|
103
|
-
Changing `source` updates the image. The provider shares one lazy runtime across diagrams and disposes it on unmount. See [React integration](docs/react.md) for loading, errors, rendering options, and server rendering.
|
|
110
|
+
The component defaults to scale `1`, displays at logical size, and shrinks to fit its container. Set `scale: 2` explicitly for higher raster density. Changing `source` updates the image. The provider shares one lazy runtime across diagrams and disposes it on unmount. See [React integration](docs/react.md) for loading, errors, rendering options, and server rendering.
|
|
104
111
|
|
|
105
112
|
## Runnable samples
|
|
106
113
|
|
package/docs/api-v1.md
CHANGED
|
@@ -20,12 +20,12 @@ Initialization resolves after manifest and real bridge agree on ABI, protocol an
|
|
|
20
20
|
|
|
21
21
|
## PNG rendering
|
|
22
22
|
|
|
23
|
-
`renderPng(source, options?)` returns `{ success: true, png, width, height, diagnostics }` or `{ success: false, diagnostics }`. Diagram failures resolve with diagnostics and contain no image data. Successful results and diagnostics are frozen; byte output is a detached, caller-owned `Uint8Array`. Output uses a white background.
|
|
23
|
+
`renderPng(source, options?)` returns `{ success: true, png, width, height, displayWidth?, displayHeight?, diagnostics }` or `{ success: false, diagnostics }`. `width` and `height` are raster pixels. The optional display dimensions are positive numbers representing logical size: raster dimensions divided by the effective scale after any `maxWidth` reduction. Older runtimes omit both fields. Diagram failures resolve with diagnostics and contain no image data. Successful results and diagnostics are frozen; byte output is a detached, caller-owned `Uint8Array`. Output uses a white background.
|
|
24
24
|
|
|
25
25
|
| Option | Default | Meaning |
|
|
26
26
|
| --- | --- | --- |
|
|
27
27
|
| `encoding` | `"bytes"` | `"bytes"` returns `Uint8Array`; `"base64"` returns a string without a data-URL prefix. |
|
|
28
|
-
| `scale` | `
|
|
28
|
+
| `scale` | `1` | Positive finite raster density relative to logical diagram size. |
|
|
29
29
|
| `padding` | `10` | Non-negative finite padding in diagram units. |
|
|
30
30
|
| `maxWidth` | Omitted | Positive safe integer maximum output width in pixels; preserves proportions and only reduces output size. |
|
|
31
31
|
| `signal` | Omitted | Cancels queued work or discards completed work after cancellation. |
|
|
@@ -34,6 +34,8 @@ Literal encodings infer their corresponding PNG type in TypeScript; union encodi
|
|
|
34
34
|
|
|
35
35
|
Scale and padding must remain finite when represented as 32-bit floats; scale must also remain greater than zero. Invalid arguments reject with `INVALID_ARGUMENT`.
|
|
36
36
|
|
|
37
|
+
For web display, use scale `2` and the returned display dimensions as image dimensions, with `max-width: 100%; height: auto`. Increasing density then improves sharpness without enlarging the diagram. DPI metadata does not change CSS sizing. When an older runtime omits display dimensions, dividing raster dimensions by the requested scale works only when `maxWidth` did not reduce the effective density.
|
|
38
|
+
|
|
37
39
|
After applying `maxWidth`, output is limited to 16,384 pixels per side and 16,777,216 pixels in total. Larger images return the `TG_RENDER_SIZE_LIMIT` diagnostic. PNG dimensions and signature are checked against the response metadata; malformed responses reject with `INVALID_RESPONSE`.
|
|
38
40
|
|
|
39
41
|
## Fonts and language packs
|
|
@@ -61,6 +63,10 @@ Existing `loadRuntime`, `readonly` and `mutate` hooks remain for development com
|
|
|
61
63
|
|
|
62
64
|
Root condition precedence is worker/browser/node/default. Explicit entries avoid bundler ambiguity. Browser/Worker source graphs exclude Node imports.
|
|
63
65
|
|
|
66
|
+
The repository development command can set `DRAWMOTIVE_TEXTGRAPH_RUNTIME` to an absolute generated directory for Node and inherited Node Workers. The default Node loader then reads that directory's `wasm-manifest.json` and resolves native modules, fonts, and themes there; existing manifest validation and asset integrity checks still apply. Missing or invalid local artifacts reject initialization or rendering without falling back to packaged assets. A supplied `resolveAsset` receives the selected local URL as its default; an explicit `loadRuntime` remains authoritative. Browser and browser Worker loaders ignore this environment variable.
|
|
67
|
+
|
|
68
|
+
Keep this selection scoped to the development command. With it absent, package loading is unchanged; `info.packageVersion` identifies the JavaScript wrapper and capabilities reflect the intersection of the selected manifest and native bridge. Native manifests marked `privateSource.development: true` are rejected by release verification.
|
|
69
|
+
|
|
64
70
|
## Assets, CSP and offline operation
|
|
65
71
|
|
|
66
72
|
Defaults resolve relative to the installed module. `resolveAsset(asset, defaultUrl)` may return an absolute URL or URL object for each asset. Deploy the complete `generated/wasm` directory: bundlers must preserve/copy these files and configure the resolver for their deployed location. It covers both JS and data. Instance query parameters isolate mutable .NET ESM state; servers should serve identical module bytes regardless of that query.
|
|
@@ -83,6 +89,6 @@ ABI 1.0.0 retains GetAbiVersion/GetPackageKind/CountParseDiagnostics and adds Ge
|
|
|
83
89
|
|
|
84
90
|
Assets have relative path, media type, byte count and SHA-256. Packaging checks reject missing/extra assets, debug files, hash/projection/version drift. Runtime data assets are hash-checked before native startup. Bundled fonts and themes are loaded and hash-checked lazily before the first render; license files require no runtime I/O. These checks establish distribution consistency, not authenticated runtime signatures. `privateSource.commit` identifies committed private C# and bridge inputs. Toolchain changes may change bytes; cross-toolchain byte-for-byte reproducibility is not promised.
|
|
85
91
|
|
|
86
|
-
The rendering wire uses one `Execute(requestJson)` export. Configuration is performed once before the first render with `{ protocolVersion: 1, operation: "configure", theme, fonts: [{ family, data }], fallbackFamilies }`, where `data` is base64 font data. Its response is `{ protocolVersion: 1, success, diagnostics }`. A render request is `{ protocolVersion: 1, operation: "render", source, export: { format: "png", scale, padding, maxWidth? } }`. Native success returns base64 `png`, `width`, `height`, and diagnostics; failures contain only `success: false` and diagnostics beside the protocol version. The JavaScript wrapper converts image data to the requested encoding.
|
|
92
|
+
The rendering wire uses one `Execute(requestJson)` export. Configuration is performed once before the first render with `{ protocolVersion: 1, operation: "configure", theme, fonts: [{ family, data }], fallbackFamilies }`, where `data` is base64 font data. Its response is `{ protocolVersion: 1, success, diagnostics }`. A render request is `{ protocolVersion: 1, operation: "render", source, export: { format: "png", scale, padding, maxWidth? } }`. Native success returns base64 `png`, `width`, `height`, and diagnostics, plus an additive optional `displayWidth`/`displayHeight` pair; failures contain only `success: false` and diagnostics beside the protocol version. The JavaScript wrapper validates the display dimensions when present and converts image data to the requested encoding.
|
|
87
93
|
|
|
88
94
|
For rendering-capable bridges, disposal sends `{ protocolVersion: 1, operation: "dispose" }` and validates the same success/diagnostics envelope as configuration. This request releases native resources without loading fonts or shutting down the host runtime. Legacy validation-only bridges release wrapper references directly.
|
package/docs/react.md
CHANGED
|
@@ -18,8 +18,7 @@ export function Diagram({ source }: { source: string }) {
|
|
|
18
18
|
return (
|
|
19
19
|
<TextGraphProvider options={options}>
|
|
20
20
|
<TextGraph source={source} alt="Process flow"
|
|
21
|
-
renderOptions={{
|
|
22
|
-
style={{ maxWidth: '100%', height: 'auto' }} />
|
|
21
|
+
renderOptions={{ maxWidth: 1200 }} />
|
|
23
22
|
</TextGraphProvider>
|
|
24
23
|
);
|
|
25
24
|
}
|
|
@@ -37,12 +36,16 @@ Use your deployed base path instead of `/` for subdirectory hosting. The [React/
|
|
|
37
36
|
| --- | --- |
|
|
38
37
|
| `source` | Required DSL string; changes trigger rendering. |
|
|
39
38
|
| `alt` | Image description; defaults to `TextGraph diagram`. |
|
|
40
|
-
| `renderOptions` | `scale
|
|
39
|
+
| `renderOptions` | Defaults to `scale: 1`; `padding` and `maxWidth` use SDK defaults. Set `scale: 2` explicitly for higher raster density. |
|
|
41
40
|
| `loading` | React content shown with `role="status"`; defaults to `Rendering diagram…`. |
|
|
42
41
|
| Other image attributes | Passed to the successful `<img>`, including `className`, `style`, `width`, and `height`. |
|
|
43
42
|
|
|
44
43
|
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.
|
|
45
44
|
|
|
45
|
+
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
|
+
|
|
47
|
+
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.
|
|
48
|
+
|
|
46
49
|
An ordinary React render uses the browser main thread. Native layout already running cannot be preempted by cancellation; for heavy interactive workloads use the [Worker sample](https://github.com/drawmotive/textgraph/tree/main/samples/worker) to move SDK execution off the UI thread. The React component does not automatically create a Worker.
|
|
47
50
|
|
|
48
51
|
## Server rendering
|
package/docs/releases.md
CHANGED
|
@@ -7,7 +7,8 @@ Releases are selected deliberately and published by `.github/workflows/release.y
|
|
|
7
7
|
| Version | npm dist-tag | Installation |
|
|
8
8
|
| --- | --- | --- |
|
|
9
9
|
| `0.1.0-alpha.1` | `alpha` | `npm install @drawmotive/textgraph@alpha` |
|
|
10
|
-
| `0.2.0` | `latest` | `npm install @drawmotive/textgraph` |
|
|
10
|
+
| `0.2.0` (published) | `latest` | `npm install @drawmotive/textgraph` |
|
|
11
|
+
| `0.2.1` (next target) | `latest` after publication | `npm install @drawmotive/textgraph@0.2.1` after publication |
|
|
11
12
|
|
|
12
13
|
An exact prerelease version also works: `npm install @drawmotive/textgraph@0.1.0-alpha.1`. npm assigns `latest` on a first publication even when another tag is specified. This default is accepted: `0.1.0-alpha.1` initially owned both `alpha` and `latest`. Subsequent alpha releases update `alpha` and preserve the existing default. A stable release updates `latest`.
|
|
13
14
|
|
|
@@ -35,10 +36,10 @@ Trusted publishing requires npm 11.5.1+ and Node.js 22.14.0+. Publication from a
|
|
|
35
36
|
Native builds belong to the private DrawMotive repository. Commit native source, resource, and bridge changes first, then run there:
|
|
36
37
|
|
|
37
38
|
```bash
|
|
38
|
-
npm run release:textgraph -- 0.2.
|
|
39
|
+
npm run release:textgraph -- 0.2.1
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
This updates the package and lock versions, builds Release assets from the recorded private source commit, generates matching JSON/ESM manifests, updates the root workspace lock, and verifies asset hashes. It does not commit, push, tag, or publish. The `just release-textgraph 0.2.
|
|
42
|
+
This updates the package and lock versions, builds Release assets from the recorded private source commit, generates matching JSON/ESM manifests, updates the root workspace lock, and verifies asset hashes. It does not commit, push, tag, or publish. The `just release-textgraph 0.2.1` alias runs the same command.
|
|
42
43
|
|
|
43
44
|
For a JavaScript-only release reusing already verified native bytes, run in this package:
|
|
44
45
|
|
|
@@ -82,8 +83,8 @@ If npm requires a one-time code, run with `NPM_CONFIG_OTP` set in the local term
|
|
|
82
83
|
After preparation, tests, and review, create the tag on the verified package commit:
|
|
83
84
|
|
|
84
85
|
```bash
|
|
85
|
-
git tag -a textgraph-v0.2.
|
|
86
|
-
git push origin textgraph-v0.2.
|
|
86
|
+
git tag -a textgraph-v0.2.1 -m "Release 0.2.1"
|
|
87
|
+
git push origin textgraph-v0.2.1
|
|
87
88
|
```
|
|
88
89
|
|
|
89
90
|
The release workflow resolves the tag to a commit, runs Node.js and three-browser tests on Ubuntu, packs the tested checkout, and passes the artifact to the `npm` environment for publication. All CI and release jobs use `ubuntu-latest`; the browser suite covers Chromium, Firefox, and WebKit. Stable releases select `latest` automatically. Never pass a custom dist-tag to bypass the release policy.
|
|
@@ -92,14 +93,14 @@ The release workflow resolves the tag to a commit, runs Node.js and three-browse
|
|
|
92
93
|
|
|
93
94
|
```bash
|
|
94
95
|
npm view @drawmotive/textgraph dist-tags --json
|
|
95
|
-
npm view @drawmotive/textgraph@0.2.
|
|
96
|
-
npm install @drawmotive/textgraph@0.2.
|
|
96
|
+
npm view @drawmotive/textgraph@0.2.1 dist.integrity
|
|
97
|
+
npm install @drawmotive/textgraph@0.2.1
|
|
97
98
|
```
|
|
98
99
|
|
|
99
100
|
If a workflow fails before publishing, fix the configuration and rerun the same tag. For a manual dispatch, select the release tag as the workflow ref and supply the same tag as input; dispatching from `main` is rejected so OIDC and provenance identify the correct commit. For example:
|
|
100
101
|
|
|
101
102
|
```bash
|
|
102
|
-
gh workflow run release.yml --ref textgraph-v0.2.
|
|
103
|
+
gh workflow run release.yml --ref textgraph-v0.2.1 -f tag=textgraph-v0.2.1
|
|
103
104
|
```
|
|
104
105
|
|
|
105
106
|
If publication succeeded but a later check failed, the script accepts an identical registry artifact with the correct channel and refuses different bytes at the same version. Keep the original workflow artifact for diagnosing integrity differences.
|
|
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-
|
|
4
|
+
"hash": "sha256-W/6+iMFbhpPEh5Ge4iiu9DhMaqbvbX7w57YbSQ+ZM+s=",
|
|
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-
|
|
18
|
+
"hash": "sha256-R8kDiIGfkO/Q9HySbzLdTbKJgeXehFSNF6B+5/SOPQI="
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"coreAssembly": [
|
|
@@ -34,12 +34,12 @@ export const config = /*json-start*/{
|
|
|
34
34
|
{
|
|
35
35
|
"virtualPath": "Grpc.Core.Api.wasm",
|
|
36
36
|
"name": "Grpc.Core.Api.wasm",
|
|
37
|
-
"hash": "sha256-
|
|
37
|
+
"hash": "sha256-TPziK3Q3YPY1qXINfLKVIGxpqRKKuasyiUBX9v1FNw4="
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"virtualPath": "HarfBuzzSharp.wasm",
|
|
41
41
|
"name": "HarfBuzzSharp.wasm",
|
|
42
|
-
"hash": "sha256-
|
|
42
|
+
"hash": "sha256-drB7TZkNPTKdrFS/IcKY3/XSIN1i+BAVvGv1fa4fozw="
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
"virtualPath": "MagicOnion.Abstractions.wasm",
|
|
@@ -134,7 +134,7 @@ export const config = /*json-start*/{
|
|
|
134
134
|
{
|
|
135
135
|
"virtualPath": "Polly.Core.wasm",
|
|
136
136
|
"name": "Polly.Core.wasm",
|
|
137
|
-
"hash": "sha256-
|
|
137
|
+
"hash": "sha256-mMPGuighials8LGc/l3qseOcJZIivgwgucVnM2MHlLQ="
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
"virtualPath": "R3.wasm",
|
|
@@ -149,7 +149,7 @@ export const config = /*json-start*/{
|
|
|
149
149
|
{
|
|
150
150
|
"virtualPath": "SkiaSharp.wasm",
|
|
151
151
|
"name": "SkiaSharp.wasm",
|
|
152
|
-
"hash": "sha256-
|
|
152
|
+
"hash": "sha256-oSfaMRAPX6qLBtENPhOBzOBN5Ax4EanYCohFCi1qPSA="
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
155
|
"virtualPath": "SkiaSharp.HarfBuzz.wasm",
|
|
@@ -169,22 +169,22 @@ export const config = /*json-start*/{
|
|
|
169
169
|
{
|
|
170
170
|
"virtualPath": "Graphics.Core.wasm",
|
|
171
171
|
"name": "Graphics.Core.wasm",
|
|
172
|
-
"hash": "sha256-
|
|
172
|
+
"hash": "sha256-QqYwwcamvWBKufMUd18Wi5MWzOKtnTnSY/jNnDFyC+g="
|
|
173
173
|
},
|
|
174
174
|
{
|
|
175
175
|
"virtualPath": "Math.Core.wasm",
|
|
176
176
|
"name": "Math.Core.wasm",
|
|
177
|
-
"hash": "sha256
|
|
177
|
+
"hash": "sha256-+48yhPwcX3nmLHVPxdFG8LN0LBMMlO4pOvBNwDiS+4o="
|
|
178
178
|
},
|
|
179
179
|
{
|
|
180
180
|
"virtualPath": "RBush.wasm",
|
|
181
181
|
"name": "RBush.wasm",
|
|
182
|
-
"hash": "sha256-
|
|
182
|
+
"hash": "sha256-0TmcGlWNXgKGDK6A8JeAQfx32iggploPWQKkiUqeDrI="
|
|
183
183
|
},
|
|
184
184
|
{
|
|
185
185
|
"virtualPath": "DrawMotive.TextGraph.Bridge.wasm",
|
|
186
186
|
"name": "DrawMotive.TextGraph.Bridge.wasm",
|
|
187
|
-
"hash": "sha256-
|
|
187
|
+
"hash": "sha256-X7Iw2ugi+nxw0sxpLvFJsIQrunBZAlv4eR2ntEfAixQ="
|
|
188
188
|
},
|
|
189
189
|
{
|
|
190
190
|
"virtualPath": "System.Collections.Concurrent.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/
|
|
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
|
|
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/
|
|
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
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
// Sometimes an existing Module object exists with properties
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "@drawmotive/textgraph",
|
|
4
|
-
"packageVersion": "0.2.
|
|
4
|
+
"packageVersion": "0.2.1",
|
|
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": "
|
|
30
|
+
"commit": "f5a5e5d027072d1faa79ac9b25d5899d1380fdfd",
|
|
31
31
|
"project": "DrawMotive.TextGraph.Bridge"
|
|
32
32
|
},
|
|
33
33
|
"entryAssembly": "wasm/DrawMotive.TextGraph.Bridge.wasm",
|
|
@@ -49,8 +49,8 @@ export default {
|
|
|
49
49
|
{
|
|
50
50
|
"path": "wasm/DrawMotive.TextGraph.Bridge.wasm",
|
|
51
51
|
"mediaType": "application/wasm",
|
|
52
|
-
"bytes":
|
|
53
|
-
"sha256": "
|
|
52
|
+
"bytes": 77077,
|
|
53
|
+
"sha256": "5fb230dae822fa7c70d2cc692ef149b0842bba7059025bf8791da7b447c08b14"
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"path": "wasm/FuzzyBubbles-LICENSE.txt",
|
|
@@ -67,20 +67,20 @@ export default {
|
|
|
67
67
|
{
|
|
68
68
|
"path": "wasm/Graphics.Core.wasm",
|
|
69
69
|
"mediaType": "application/wasm",
|
|
70
|
-
"bytes":
|
|
71
|
-
"sha256": "
|
|
70
|
+
"bytes": 3119385,
|
|
71
|
+
"sha256": "42a630c1c6a6bd604ab9f314775f168b9316cce2ad9d39d263f8cd9c31720be8"
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
"path": "wasm/Grpc.Core.Api.wasm",
|
|
75
75
|
"mediaType": "application/wasm",
|
|
76
76
|
"bytes": 7957,
|
|
77
|
-
"sha256": "
|
|
77
|
+
"sha256": "4cfce22b743760f635a9720d7cb295206c69a9128ab9ab32894057f6fd45370e"
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"path": "wasm/HarfBuzzSharp.wasm",
|
|
81
81
|
"mediaType": "application/wasm",
|
|
82
82
|
"bytes": 29461,
|
|
83
|
-
"sha256": "
|
|
83
|
+
"sha256": "76b07b4d990d3d329dac54bf21c298dff5d220dd62f81015bc6bf57dae1fa33c"
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
"path": "wasm/MagicOnion.Abstractions.wasm",
|
|
@@ -110,7 +110,7 @@ export default {
|
|
|
110
110
|
"path": "wasm/Math.Core.wasm",
|
|
111
111
|
"mediaType": "application/wasm",
|
|
112
112
|
"bytes": 3241241,
|
|
113
|
-
"sha256": "
|
|
113
|
+
"sha256": "fb8f3284fc1c5f79e62c754fc5d146f0b3742c130c94ee293af04dc03892fb8a"
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
"path": "wasm/MemoryPack.Core.wasm",
|
|
@@ -206,7 +206,7 @@ export default {
|
|
|
206
206
|
"path": "wasm/Polly.Core.wasm",
|
|
207
207
|
"mediaType": "application/wasm",
|
|
208
208
|
"bytes": 62741,
|
|
209
|
-
"sha256": "
|
|
209
|
+
"sha256": "98c3c6ba282189a96cf0b19cfe5deab1e39c259222be0c20b9c56733630794b4"
|
|
210
210
|
},
|
|
211
211
|
{
|
|
212
212
|
"path": "wasm/Polly.wasm",
|
|
@@ -230,7 +230,7 @@ export default {
|
|
|
230
230
|
"path": "wasm/RBush.wasm",
|
|
231
231
|
"mediaType": "application/wasm",
|
|
232
232
|
"bytes": 24341,
|
|
233
|
-
"sha256": "
|
|
233
|
+
"sha256": "d1399c1a558d5e02860cae80f0978041fc77da2820a65a0f5902a4894a9e0eb2"
|
|
234
234
|
},
|
|
235
235
|
{
|
|
236
236
|
"path": "wasm/SkiaSharp.HarfBuzz.wasm",
|
|
@@ -242,7 +242,7 @@ export default {
|
|
|
242
242
|
"path": "wasm/SkiaSharp.wasm",
|
|
243
243
|
"mediaType": "application/wasm",
|
|
244
244
|
"bytes": 90389,
|
|
245
|
-
"sha256": "
|
|
245
|
+
"sha256": "a127da31100f5faa8b06d10d3e1381cce04de40c7811a9d80a88450a2d6a3d20"
|
|
246
246
|
},
|
|
247
247
|
{
|
|
248
248
|
"path": "wasm/Stateless.wasm",
|
|
@@ -524,7 +524,7 @@ export default {
|
|
|
524
524
|
"path": "wasm/dotnet.boot.js",
|
|
525
525
|
"mediaType": "text/javascript",
|
|
526
526
|
"bytes": 17141,
|
|
527
|
-
"sha256": "
|
|
527
|
+
"sha256": "38a942cbb9bd0df00af223fa0b1b0f4080a2de476c5367c7b10c7b435e1f1a52"
|
|
528
528
|
},
|
|
529
529
|
{
|
|
530
530
|
"path": "wasm/dotnet.js",
|
|
@@ -535,14 +535,14 @@ export default {
|
|
|
535
535
|
{
|
|
536
536
|
"path": "wasm/dotnet.native.js",
|
|
537
537
|
"mediaType": "text/javascript",
|
|
538
|
-
"bytes":
|
|
539
|
-
"sha256": "
|
|
538
|
+
"bytes": 248270,
|
|
539
|
+
"sha256": "d53e77206394560939d72a9d57269a38b611a3cf9cd32d5c6403a802f3346bd4"
|
|
540
540
|
},
|
|
541
541
|
{
|
|
542
542
|
"path": "wasm/dotnet.native.wasm",
|
|
543
543
|
"mediaType": "application/wasm",
|
|
544
|
-
"bytes":
|
|
545
|
-
"sha256": "
|
|
544
|
+
"bytes": 6020218,
|
|
545
|
+
"sha256": "47c90388819f90efd0f47c926f32dd4db28981e5de84548d17a07ee7f48e3d02"
|
|
546
546
|
},
|
|
547
547
|
{
|
|
548
548
|
"path": "wasm/dotnet.runtime.js",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "@drawmotive/textgraph",
|
|
4
|
-
"packageVersion": "0.2.
|
|
4
|
+
"packageVersion": "0.2.1",
|
|
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": "
|
|
30
|
+
"commit": "f5a5e5d027072d1faa79ac9b25d5899d1380fdfd",
|
|
31
31
|
"project": "DrawMotive.TextGraph.Bridge"
|
|
32
32
|
},
|
|
33
33
|
"entryAssembly": "wasm/DrawMotive.TextGraph.Bridge.wasm",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
{
|
|
50
50
|
"path": "wasm/DrawMotive.TextGraph.Bridge.wasm",
|
|
51
51
|
"mediaType": "application/wasm",
|
|
52
|
-
"bytes":
|
|
53
|
-
"sha256": "
|
|
52
|
+
"bytes": 77077,
|
|
53
|
+
"sha256": "5fb230dae822fa7c70d2cc692ef149b0842bba7059025bf8791da7b447c08b14"
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"path": "wasm/FuzzyBubbles-LICENSE.txt",
|
|
@@ -67,20 +67,20 @@
|
|
|
67
67
|
{
|
|
68
68
|
"path": "wasm/Graphics.Core.wasm",
|
|
69
69
|
"mediaType": "application/wasm",
|
|
70
|
-
"bytes":
|
|
71
|
-
"sha256": "
|
|
70
|
+
"bytes": 3119385,
|
|
71
|
+
"sha256": "42a630c1c6a6bd604ab9f314775f168b9316cce2ad9d39d263f8cd9c31720be8"
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
"path": "wasm/Grpc.Core.Api.wasm",
|
|
75
75
|
"mediaType": "application/wasm",
|
|
76
76
|
"bytes": 7957,
|
|
77
|
-
"sha256": "
|
|
77
|
+
"sha256": "4cfce22b743760f635a9720d7cb295206c69a9128ab9ab32894057f6fd45370e"
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"path": "wasm/HarfBuzzSharp.wasm",
|
|
81
81
|
"mediaType": "application/wasm",
|
|
82
82
|
"bytes": 29461,
|
|
83
|
-
"sha256": "
|
|
83
|
+
"sha256": "76b07b4d990d3d329dac54bf21c298dff5d220dd62f81015bc6bf57dae1fa33c"
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
"path": "wasm/MagicOnion.Abstractions.wasm",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"path": "wasm/Math.Core.wasm",
|
|
111
111
|
"mediaType": "application/wasm",
|
|
112
112
|
"bytes": 3241241,
|
|
113
|
-
"sha256": "
|
|
113
|
+
"sha256": "fb8f3284fc1c5f79e62c754fc5d146f0b3742c130c94ee293af04dc03892fb8a"
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
"path": "wasm/MemoryPack.Core.wasm",
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
"path": "wasm/Polly.Core.wasm",
|
|
207
207
|
"mediaType": "application/wasm",
|
|
208
208
|
"bytes": 62741,
|
|
209
|
-
"sha256": "
|
|
209
|
+
"sha256": "98c3c6ba282189a96cf0b19cfe5deab1e39c259222be0c20b9c56733630794b4"
|
|
210
210
|
},
|
|
211
211
|
{
|
|
212
212
|
"path": "wasm/Polly.wasm",
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
"path": "wasm/RBush.wasm",
|
|
231
231
|
"mediaType": "application/wasm",
|
|
232
232
|
"bytes": 24341,
|
|
233
|
-
"sha256": "
|
|
233
|
+
"sha256": "d1399c1a558d5e02860cae80f0978041fc77da2820a65a0f5902a4894a9e0eb2"
|
|
234
234
|
},
|
|
235
235
|
{
|
|
236
236
|
"path": "wasm/SkiaSharp.HarfBuzz.wasm",
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
"path": "wasm/SkiaSharp.wasm",
|
|
243
243
|
"mediaType": "application/wasm",
|
|
244
244
|
"bytes": 90389,
|
|
245
|
-
"sha256": "
|
|
245
|
+
"sha256": "a127da31100f5faa8b06d10d3e1381cce04de40c7811a9d80a88450a2d6a3d20"
|
|
246
246
|
},
|
|
247
247
|
{
|
|
248
248
|
"path": "wasm/Stateless.wasm",
|
|
@@ -524,7 +524,7 @@
|
|
|
524
524
|
"path": "wasm/dotnet.boot.js",
|
|
525
525
|
"mediaType": "text/javascript",
|
|
526
526
|
"bytes": 17141,
|
|
527
|
-
"sha256": "
|
|
527
|
+
"sha256": "38a942cbb9bd0df00af223fa0b1b0f4080a2de476c5367c7b10c7b435e1f1a52"
|
|
528
528
|
},
|
|
529
529
|
{
|
|
530
530
|
"path": "wasm/dotnet.js",
|
|
@@ -535,14 +535,14 @@
|
|
|
535
535
|
{
|
|
536
536
|
"path": "wasm/dotnet.native.js",
|
|
537
537
|
"mediaType": "text/javascript",
|
|
538
|
-
"bytes":
|
|
539
|
-
"sha256": "
|
|
538
|
+
"bytes": 248270,
|
|
539
|
+
"sha256": "d53e77206394560939d72a9d57269a38b611a3cf9cd32d5c6403a802f3346bd4"
|
|
540
540
|
},
|
|
541
541
|
{
|
|
542
542
|
"path": "wasm/dotnet.native.wasm",
|
|
543
543
|
"mediaType": "application/wasm",
|
|
544
|
-
"bytes":
|
|
545
|
-
"sha256": "
|
|
544
|
+
"bytes": 6020218,
|
|
545
|
+
"sha256": "47c90388819f90efd0f47c926f32dd4db28981e5de84548d17a07ee7f48e3d02"
|
|
546
546
|
},
|
|
547
547
|
{
|
|
548
548
|
"path": "wasm/dotnet.runtime.js",
|
package/package.json
CHANGED
|
@@ -76,6 +76,14 @@
|
|
|
76
76
|
"type": "integer",
|
|
77
77
|
"minimum": 1,
|
|
78
78
|
"maximum": 2147483647
|
|
79
|
+
},
|
|
80
|
+
"displayWidth": {
|
|
81
|
+
"type": "number",
|
|
82
|
+
"exclusiveMinimum": 0
|
|
83
|
+
},
|
|
84
|
+
"displayHeight": {
|
|
85
|
+
"type": "number",
|
|
86
|
+
"exclusiveMinimum": 0
|
|
79
87
|
}
|
|
80
88
|
},
|
|
81
89
|
"required": [
|
|
@@ -145,6 +153,22 @@
|
|
|
145
153
|
"properties": {
|
|
146
154
|
"height": {}
|
|
147
155
|
}
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"required": [
|
|
159
|
+
"displayWidth"
|
|
160
|
+
],
|
|
161
|
+
"properties": {
|
|
162
|
+
"displayWidth": {}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"required": [
|
|
167
|
+
"displayHeight"
|
|
168
|
+
],
|
|
169
|
+
"properties": {
|
|
170
|
+
"displayHeight": {}
|
|
171
|
+
}
|
|
148
172
|
}
|
|
149
173
|
]
|
|
150
174
|
},
|
|
@@ -163,5 +187,13 @@
|
|
|
163
187
|
}
|
|
164
188
|
}
|
|
165
189
|
}
|
|
166
|
-
]
|
|
190
|
+
],
|
|
191
|
+
"dependentRequired": {
|
|
192
|
+
"displayWidth": [
|
|
193
|
+
"displayHeight"
|
|
194
|
+
],
|
|
195
|
+
"displayHeight": [
|
|
196
|
+
"displayWidth"
|
|
197
|
+
]
|
|
198
|
+
}
|
|
167
199
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -114,7 +114,7 @@ export type TextGraphPngEncoding = 'bytes' | 'base64';
|
|
|
114
114
|
export interface TextGraphRenderPngOptions<Encoding extends TextGraphPngEncoding = TextGraphPngEncoding> {
|
|
115
115
|
/** Defaults to bytes. Base64 contains no data-URL prefix. */
|
|
116
116
|
encoding?: Encoding;
|
|
117
|
-
/** Positive
|
|
117
|
+
/** Positive raster density relative to logical diagram size. Defaults to 1. */
|
|
118
118
|
scale?: number;
|
|
119
119
|
/** Non-negative padding in diagram units. Defaults to 10. */
|
|
120
120
|
padding?: number;
|
|
@@ -125,7 +125,9 @@ export interface TextGraphRenderPngOptions<Encoding extends TextGraphPngEncoding
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
export type TextGraphRenderPngResult<Png extends Uint8Array | string = Uint8Array | string> =
|
|
128
|
-
| { readonly success: true; readonly png: Png; readonly width: number; readonly height: number;
|
|
128
|
+
| { readonly success: true; readonly png: Png; readonly width: number; readonly height: number;
|
|
129
|
+
/** Logical display dimensions before density scaling; omitted by older runtimes. */
|
|
130
|
+
readonly displayWidth?: number; readonly displayHeight?: number; readonly diagnostics: readonly TextGraphRenderDiagnostic[] }
|
|
129
131
|
| { readonly success: false; readonly diagnostics: readonly TextGraphRenderDiagnostic[] };
|
|
130
132
|
|
|
131
133
|
export interface TextGraphRuntimeInfo extends TextGraphRuntimeManifest {
|
package/src/platform/node.d.ts
CHANGED
package/src/platform/node.js
CHANGED
|
@@ -6,7 +6,10 @@ import { DrawMotiveError } from '../runtime/errors.js';
|
|
|
6
6
|
|
|
7
7
|
export async function initializeTextGraph(options = {}) {
|
|
8
8
|
if (!options || typeof options !== 'object') throw new DrawMotiveError('INVALID_ARGUMENT', 'Initialization options must be an object');
|
|
9
|
-
|
|
9
|
+
// The workspace development command opts in explicitly; imports and browser
|
|
10
|
+
// loaders never consult a local build directory or replace packaged assets.
|
|
11
|
+
const directory = process.env.DRAWMOTIVE_TEXTGRAPH_RUNTIME;
|
|
12
|
+
return initialize({ ...options, loadRuntime: options.loadRuntime ?? (context => loadNodeRuntime(context, manifest, directory)) });
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
export const platform = 'node';
|
package/src/react/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface TextGraphProviderProps {
|
|
|
11
11
|
export interface TextGraphProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'srcSet' | 'children' | 'loading'> {
|
|
12
12
|
source: string;
|
|
13
13
|
alt?: string;
|
|
14
|
+
/** Defaults to scale 1; the image displays at logical size. */
|
|
14
15
|
renderOptions?: Omit<TextGraphRenderPngOptions, 'encoding' | 'signal'>;
|
|
15
16
|
loading?: ReactNode;
|
|
16
17
|
}
|
package/src/react/index.js
CHANGED
|
@@ -15,10 +15,10 @@ 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…', ...imageProps }) {
|
|
18
|
+
loading = 'Rendering diagram…', style, ...imageProps }) {
|
|
19
19
|
const shared = useContext(RuntimeContext);
|
|
20
20
|
const resource = useMemo(() => shared ?? createRuntimeResource(defaultOptions), [shared]);
|
|
21
|
-
const { scale, padding, maxWidth } = renderOptions;
|
|
21
|
+
const { scale = 1, padding, maxWidth } = renderOptions;
|
|
22
22
|
const request = useMemo(() => ({ source, scale, padding, maxWidth, resource }),
|
|
23
23
|
[source, scale, padding, maxWidth, resource]);
|
|
24
24
|
const [state, setState] = useState(null);
|
|
@@ -51,6 +51,9 @@ export function TextGraph({ source, alt = 'TextGraph diagram', renderOptions = d
|
|
|
51
51
|
return createElement('span', { role: 'alert' }, message || 'Could not render diagram');
|
|
52
52
|
}
|
|
53
53
|
const result = state.result;
|
|
54
|
-
|
|
54
|
+
// Density improves sharpness without enlarging the diagram. Native dimensions
|
|
55
|
+
// 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,
|
|
57
|
+
style: { maxWidth: '100%', ...(imageProps.height == null ? { height: 'auto' } : {}), ...style },
|
|
55
58
|
src: `data:image/png;base64,${result.png}`, alt });
|
|
56
59
|
}
|
package/src/runtime/dotnet.js
CHANGED
|
@@ -83,7 +83,9 @@ export async function startTextGraphRuntime(options, manifest, readAsset) {
|
|
|
83
83
|
export function createBridgeRuntime({ manifest, options, readAsset, bridge, info }) {
|
|
84
84
|
const renders = manifest.capabilities.includes(renderingCapability);
|
|
85
85
|
return {
|
|
86
|
-
|
|
86
|
+
// A bridge may support operations intentionally omitted by the selected
|
|
87
|
+
// manifest; advertise only the intersection that this loader exposes.
|
|
88
|
+
abiVersion: info.abiVersion, capabilities: info.capabilities.filter(capability => manifest.capabilities.includes(capability)),
|
|
87
89
|
validate: source => bridge[manifest.bridge.validate](source),
|
|
88
90
|
...(renders ? { execute: createRenderingExecutor({ manifest, options, readAsset, execute: request => bridge[manifest.bridge.execute](request) }) } : {}),
|
|
89
91
|
async dispose() {
|
package/src/runtime/node.js
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
2
4
|
import { startTextGraphRuntime } from './dotnet.js';
|
|
3
5
|
import { DrawMotiveError } from './errors.js';
|
|
4
6
|
|
|
5
7
|
/** Node file loading is isolated from the browser module graph. */
|
|
6
|
-
export function loadNodeRuntime(options, manifest) {
|
|
8
|
+
export async function loadNodeRuntime(options, manifest, directory) {
|
|
9
|
+
if (directory !== undefined) {
|
|
10
|
+
if (!path.isAbsolute(directory)) throw new DrawMotiveError('INVALID_ARGUMENT', 'DRAWMOTIVE_TEXTGRAPH_RUNTIME must be an absolute generated directory');
|
|
11
|
+
const manifestPath = path.join(directory, 'wasm-manifest.json');
|
|
12
|
+
let json;
|
|
13
|
+
try { json = await readFile(manifestPath, { encoding: 'utf8', signal: options.signal }); }
|
|
14
|
+
catch (cause) {
|
|
15
|
+
if (cause?.name === 'AbortError') throw cause;
|
|
16
|
+
throw new DrawMotiveError('RESOURCE_NOT_FOUND', 'Could not read the selected local runtime manifest', { cause, details: { manifest: manifestPath } });
|
|
17
|
+
}
|
|
18
|
+
try { manifest = JSON.parse(json); }
|
|
19
|
+
catch (cause) { throw new DrawMotiveError('INVALID_MANIFEST', 'The selected local runtime manifest is not valid JSON', { cause }); }
|
|
20
|
+
const resolveAsset = options.resolveAsset;
|
|
21
|
+
// Use the selected manifest and directory together for every native module,
|
|
22
|
+
// theme and font. A missing local artifact must never load packaged bytes.
|
|
23
|
+
options = { ...options, resolveAsset: asset => {
|
|
24
|
+
const defaultUrl = pathToFileURL(path.join(directory, asset.path));
|
|
25
|
+
return resolveAsset ? resolveAsset(asset, defaultUrl) : defaultUrl;
|
|
26
|
+
} };
|
|
27
|
+
}
|
|
7
28
|
return startTextGraphRuntime(options, manifest, async (item, context) => {
|
|
8
29
|
try {
|
|
9
30
|
if (item.url.protocol === 'file:') {
|
package/src/runtime/rendering.js
CHANGED
|
@@ -7,7 +7,7 @@ const stages = ['parse', 'semantic', 'layout', 'render', 'font'];
|
|
|
7
7
|
export function normalizeRenderOptions(source, options) {
|
|
8
8
|
const invalid = message => { throw new DrawMotiveError('INVALID_ARGUMENT', message); };
|
|
9
9
|
if (typeof source !== 'string' || !options || typeof options !== 'object' || Array.isArray(options)) invalid('Rendering requires a source string and options object');
|
|
10
|
-
const { encoding = 'bytes', scale =
|
|
10
|
+
const { encoding = 'bytes', scale = 1, padding = 10, maxWidth, signal } = options;
|
|
11
11
|
if (!['bytes', 'base64'].includes(encoding)) invalid('encoding must be bytes or base64');
|
|
12
12
|
// The native geometry contract uses float32; reject values lost at that boundary.
|
|
13
13
|
if (!Number.isFinite(scale) || !Number.isFinite(Math.fround(scale)) || Math.fround(scale) <= 0) invalid('scale must be positive and representable as a finite float32');
|
|
@@ -34,17 +34,25 @@ export function decodeRender(json, encoding) {
|
|
|
34
34
|
const diagnostics = decodeDiagnostics(value.diagnostics, stages);
|
|
35
35
|
if (value.success === diagnostics.some(item => item.severity === 'error')) throw new Error('Inconsistent success');
|
|
36
36
|
if (!value.success) {
|
|
37
|
-
if (['png', 'width', 'height'].some(key => Object.hasOwn(value, key))) throw new Error('Failure contains image data');
|
|
37
|
+
if (['png', 'width', 'height', 'displayWidth', 'displayHeight'].some(key => Object.hasOwn(value, key))) throw new Error('Failure contains image data');
|
|
38
38
|
return Object.freeze({ success: false, diagnostics });
|
|
39
39
|
}
|
|
40
40
|
if (![value.width, value.height].every(n => Number.isSafeInteger(n) && n > 0 && n <= 0x7fffffff)) throw new Error('Invalid dimensions');
|
|
41
|
+
// Only the native exporter knows the effective density after maxWidth clamps
|
|
42
|
+
// the raster. Older runtimes may omit this additive pair entirely.
|
|
43
|
+
const displayDimensions = {};
|
|
44
|
+
if (Object.hasOwn(value, 'displayWidth') || Object.hasOwn(value, 'displayHeight')) {
|
|
45
|
+
if (![value.displayWidth, value.displayHeight].every(n => Number.isFinite(n) && n > 0)) throw new Error('Invalid display dimensions');
|
|
46
|
+
displayDimensions.displayWidth = value.displayWidth;
|
|
47
|
+
displayDimensions.displayHeight = value.displayHeight;
|
|
48
|
+
}
|
|
41
49
|
const bytes = decodeBase64(value.png);
|
|
42
50
|
const signature = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
43
51
|
if (bytes.length < 33 || !signature.every((byte, index) => bytes[index] === byte)) throw new Error('Invalid PNG signature');
|
|
44
52
|
const header = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
45
53
|
if (header.getUint32(8) !== 13 || header.getUint32(12) !== 0x49484452
|
|
46
54
|
|| header.getUint32(16) !== value.width || header.getUint32(20) !== value.height) throw new Error('PNG dimensions disagree');
|
|
47
|
-
return Object.freeze({ success: true, png: encoding === 'base64' ? value.png : bytes, width: value.width, height: value.height, diagnostics });
|
|
55
|
+
return Object.freeze({ success: true, png: encoding === 'base64' ? value.png : bytes, width: value.width, height: value.height, ...displayDimensions, diagnostics });
|
|
48
56
|
} catch (cause) {
|
|
49
57
|
throw new DrawMotiveError('INVALID_RESPONSE', 'The rendering response violates protocol 1', { cause });
|
|
50
58
|
}
|