@drawmotive/textgraph 0.2.0 → 0.2.2-alpha.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.
Files changed (44) hide show
  1. package/README.md +176 -74
  2. package/docs/api-v1.md +53 -7
  3. package/docs/images/README.md +28 -0
  4. package/docs/images/service-flow.png +0 -0
  5. package/docs/react.md +7 -4
  6. package/docs/releases.md +34 -15
  7. package/examples/service-flow.textgraph +6 -0
  8. package/generated/wasm/DrawMotive.TextGraph.Bridge.wasm +0 -0
  9. package/generated/wasm/ExCSS.wasm +0 -0
  10. package/generated/wasm/Graphics.Core.wasm +0 -0
  11. package/generated/wasm/Grpc.Core.Api.wasm +0 -0
  12. package/generated/wasm/HarfBuzzSharp.wasm +0 -0
  13. package/generated/wasm/Math.Core.wasm +0 -0
  14. package/generated/wasm/Microsoft.Extensions.Logging.Abstractions.wasm +0 -0
  15. package/generated/wasm/NotoSans-Regular.ttf +0 -0
  16. package/generated/wasm/Polly.Core.wasm +0 -0
  17. package/generated/wasm/RBush.wasm +0 -0
  18. package/generated/wasm/SkiaSharp.wasm +0 -0
  19. package/generated/wasm/System.Linq.wasm +0 -0
  20. package/generated/wasm/System.Memory.wasm +0 -0
  21. package/generated/wasm/System.Private.CoreLib.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.Text.RegularExpressions.wasm +0 -0
  25. package/generated/wasm/dotnet.boot.js +22 -17
  26. package/generated/wasm/dotnet.native.wasm +0 -0
  27. package/generated/wasm/themes.css +25 -12
  28. package/generated/wasm-manifest.js +42 -35
  29. package/generated/wasm-manifest.json +42 -35
  30. package/package.json +15 -3
  31. package/schemas/render.schema.json +33 -1
  32. package/src/index.d.ts +21 -3
  33. package/src/index.js +2 -2
  34. package/src/platform/node.d.ts +2 -0
  35. package/src/platform/node.js +20 -1
  36. package/src/react/index.d.ts +1 -0
  37. package/src/react/index.js +6 -3
  38. package/src/runtime/browser.js +4 -5
  39. package/src/runtime/dotnet.js +10 -6
  40. package/src/runtime/font-resources.js +148 -0
  41. package/src/runtime/language-packs.js +32 -1
  42. package/src/runtime/node.js +22 -1
  43. package/src/runtime/render-resources.js +13 -12
  44. package/src/runtime/rendering.js +13 -4
package/README.md CHANGED
@@ -1,133 +1,235 @@
1
- # TextGraph SDK
1
+ # TextGraph
2
2
 
3
- Render TextGraph diagrams as PNG images and validate diagram source.
3
+ **Turn text into diagrams in your application.**
4
4
 
5
- > **[Report all TextGraph issues on GitHub →](https://github.com/drawmotive/textgraph/issues)**
6
- > Bugs, feature requests, documentation, playground, SDK, fonts, Markdown, and VS Code issues all belong in this shared tracker.
5
+ Render flowcharts and directed graphs as PNGs in Node.js, browsers, React, and Web
6
+ Workers. TextGraph handles automatic layout and provides source validation and
7
+ rendering diagnostics. The renderer runs where your code runs; no hosted rendering
8
+ API or API key is required.
7
9
 
8
- ## Installation
10
+ [Documentation](https://textgraph.dev/diagrams/flowcharts) ·
11
+ [Runnable examples](https://github.com/drawmotive/textgraph/tree/main/samples) ·
12
+ [API reference](https://github.com/drawmotive/textgraph/blob/main/docs/api-v1.md)
13
+
14
+ ## A few lines. A diagram.
15
+
16
+ ```textgraph
17
+ (horizontal)
18
+ browser -> api -> database
19
+
20
+ browser: Browser
21
+ api(fill primary): API Gateway
22
+ database: Database
23
+ ```
24
+
25
+ ![TextGraph output: Browser connects to API Gateway, which connects to Database.](https://raw.githubusercontent.com/drawmotive/textgraph/main/docs/images/service-flow.png)
26
+
27
+ **[Edit this exact diagram in the playground →](https://textgraph.dev/playground#source=%28horizontal%29%0Abrowser%20-%3E%20api%20-%3E%20database%0A%0Abrowser%3A%20Browser%0Aapi%28fill%20primary%29%3A%20API%20Gateway%0Adatabase%3A%20Database%0A)**
28
+
29
+ Actual SDK output from
30
+ [service-flow.textgraph](https://github.com/drawmotive/textgraph/blob/main/examples/service-flow.textgraph).
31
+ Keep source in version control and render again when relationships change.
32
+ TextGraph uses a small, structured language; it does not interpret unrestricted prose.
33
+
34
+ ## Choose your workflow
35
+
36
+ | What you want to do | Use | Working example |
37
+ | --- | --- | --- |
38
+ | Generate diagrams for documentation, reports, or build jobs | Node.js: write PNG bytes to a file | [Node sample](https://github.com/drawmotive/textgraph/tree/main/samples/node) |
39
+ | Embed live diagrams in dashboards or internal tools | React component and shared provider | [React + Vite sample](https://github.com/drawmotive/textgraph/tree/main/samples/react-vite) |
40
+ | Add editable source and previews to a web app | Browser SDK | [Plain JavaScript sample](https://github.com/drawmotive/textgraph/tree/main/samples/browser) |
41
+ | Keep the UI responsive while rendering larger diagrams | SDK in a module Web Worker | [Worker sample](https://github.com/drawmotive/textgraph/tree/main/samples/worker) |
42
+ | Check syntax and references before rendering | The SDK's `validate()` method | [Validation example](https://github.com/drawmotive/textgraph/blob/main/examples/node.mjs) |
43
+
44
+ For existing documentation, use the
45
+ [Markdown/VitePress plugin](https://textgraph.dev/integrations/markdown) or
46
+ [VS Code Markdown extension](https://textgraph.dev/integrations/vscode).
47
+
48
+ ## Install
9
49
 
10
50
  ```bash
11
- npm install @drawmotive/textgraph@0.2.0
51
+ npm install @drawmotive/textgraph@0.2.2-alpha.1
12
52
  ```
13
53
 
14
- Requires Node.js 22+ or a modern browser.
54
+ Requires **Node.js 22+** or a modern browser with WebAssembly and Web Crypto.
55
+ Browser applications need HTTPS or localhost. React support is an optional entry
56
+ in this package, compatible with React 18.2+ and 19.
15
57
 
16
- Version `0.2.0` is the first stable release. Install the exact version above or use `@latest` for the current stable release.
58
+ ## Render a PNG in Node.js
17
59
 
18
- ## Quickstart
60
+ Save as `render.mjs`, then run `node render.mjs`:
19
61
 
20
- ```typescript
62
+ ```javascript
21
63
  import { writeFile } from "node:fs/promises";
22
- import { initializeTextGraph } from "@drawmotive/textgraph";
64
+ import { initializeTextGraph } from "@drawmotive/textgraph/node";
23
65
 
24
66
  const textgraph = await initializeTextGraph();
25
-
26
67
  try {
27
- const result = await textgraph.renderPng("A -> B");
28
-
68
+ const result = await textgraph.renderPng("browser -> api -> database");
29
69
  if (result.success) {
30
70
  await writeFile("diagram.png", result.png);
71
+ console.log("Saved diagram.png");
31
72
  } else {
32
- console.log(result.diagnostics);
73
+ console.error(result.diagnostics);
74
+ process.exitCode = 1;
33
75
  }
34
76
  } finally {
35
77
  await textgraph.dispose();
36
78
  }
37
79
  ```
38
80
 
39
- PNG output defaults to bytes, scale `2`, padding `10`, and a white background. Reuse an instance for multiple diagrams, then call `dispose()`.
40
-
41
- ## Higher resolution
81
+ Node reads the included runtime from the installed package. No browser, .NET
82
+ installation, or asset-copy step is needed. Reuse an initialized instance for
83
+ multiple diagrams, then dispose it when the job is finished.
42
84
 
43
- ```typescript
44
- const result = await textgraph.renderPng("A -> B", {
45
- scale: 4,
46
- padding: 24,
47
- });
48
- ```
85
+ [Complete file-to-PNG sample →](https://github.com/drawmotive/textgraph/tree/main/samples/node)
49
86
 
50
- ## Display in a browser
87
+ ## Add a diagram to React
51
88
 
52
- For a bundled application, copy the runtime into its public directory before development or build:
89
+ For browser, React, and Worker applications, copy the runtime assets before
90
+ starting development or building:
53
91
 
54
92
  ```bash
55
93
  npx textgraph-copy-assets public/textgraph/wasm
56
94
  ```
57
95
 
58
- The copy command and React entry below are available starting with `0.2.0`. The [repository samples](https://github.com/drawmotive/textgraph/tree/main/samples) install and run the SDK as a tarball.
96
+ Then render a component:
97
+
98
+ ```tsx
99
+ import { TextGraph, TextGraphProvider } from "@drawmotive/textgraph/react";
100
+ import type { TextGraphInitializeOptions } from "@drawmotive/textgraph";
101
+
102
+ const options: TextGraphInitializeOptions = {
103
+ resolveAsset: (asset) => new URL(`/textgraph/${asset.path}`, location.origin),
104
+ };
105
+
106
+ export function Diagram({ source }: { source: string }) {
107
+ return (
108
+ <TextGraphProvider options={options}>
109
+ <TextGraph source={source} alt="Service dependencies" />
110
+ </TextGraphProvider>
111
+ );
112
+ }
113
+ ```
114
+
115
+ Changing `source` updates the image. For several diagrams, put one provider
116
+ around their common parent. Loading states and rendering diagnostics are built in.
117
+ The component renders on the main thread; use the Worker sample for heavier
118
+ interactive workloads.
119
+
120
+ [React + Vite sample →](https://github.com/drawmotive/textgraph/tree/main/samples/react-vite) ·
121
+ [React guide →](https://github.com/drawmotive/textgraph/blob/main/docs/react.md)
59
122
 
60
- ```typescript
123
+ ## Render in a browser
124
+
125
+ After copying assets as shown above, use this in a bundled browser application:
126
+
127
+ ```javascript
61
128
  import { initializeTextGraph } from "@drawmotive/textgraph/browser";
62
129
 
63
130
  const textgraph = await initializeTextGraph({
64
131
  resolveAsset: (asset) => new URL(`/textgraph/${asset.path}`, location.origin),
65
132
  });
66
- const result = await textgraph.renderPng("A -> B", {
67
- encoding: "base64",
68
- maxWidth: 1200,
69
- });
70
-
71
- if (result.success) {
72
- const image = document.createElement("img");
73
- image.src = `data:image/png;base64,${result.png}`;
74
- image.alt = "A connects to B";
75
- document.body.append(image);
133
+ try {
134
+ const result = await textgraph.renderPng("browser -> api -> database", {
135
+ encoding: "base64",
136
+ scale: 2,
137
+ });
138
+ if (result.success) {
139
+ const image = document.createElement("img");
140
+ image.src = `data:image/png;base64,${result.png}`;
141
+ image.alt = "Browser connects to API, which connects to database";
142
+ image.width = result.displayWidth ?? result.width / 2;
143
+ image.height = result.displayHeight ?? result.height / 2;
144
+ image.style.maxWidth = "100%";
145
+ image.style.height = "auto";
146
+ document.body.append(image);
147
+ } else {
148
+ console.error(result.diagnostics);
149
+ }
150
+ } finally {
151
+ await textgraph.dispose();
76
152
  }
77
-
78
- await textgraph.dispose();
79
153
  ```
80
154
 
81
- 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.
155
+ For repeated edits, keep the runtime alive and dispose it when the editor closes.
156
+ An async call still computes on its current thread; the Worker sample moves
157
+ rendering off the UI thread.
82
158
 
83
- ## React
159
+ [Browser example →](https://github.com/drawmotive/textgraph/tree/main/samples/browser) ·
160
+ [Worker example →](https://github.com/drawmotive/textgraph/tree/main/samples/worker)
84
161
 
85
- React 18.2+ and 19 use the same SDK through an optional entry; non-React users do not need React. Configure assets once and pass DSL to `TextGraph`:
162
+ ### Deploy the browser runtime
86
163
 
87
- ```tsx
88
- import { TextGraph, TextGraphProvider } from "@drawmotive/textgraph/react";
164
+ - Add the asset-copy command to your application's `predev` and `prebuild` scripts.
165
+ - Deploy the complete copied directory: WASM, JavaScript, fonts, themes, and
166
+ licenses. Re-copy after SDK updates; keep SDK and runtime versions matched.
167
+ - The resolver above assumes hosting at `/`. For a subdirectory, use your deployed
168
+ base path; the Vite samples demonstrate `import.meta.env.BASE_URL`.
89
169
 
90
- const options = {
91
- resolveAsset: (asset) => new URL(`/textgraph/${asset.path}`, location.origin),
92
- };
170
+ [Asset hosting and CSP →](https://github.com/drawmotive/textgraph/blob/main/docs/api-v1.md#assets-csp-and-offline-operation)
93
171
 
94
- export function App() {
95
- return (
96
- <TextGraphProvider options={options}>
97
- <TextGraph source={"A -> B"} alt="Flow diagram" />
98
- </TextGraphProvider>
99
- );
100
- }
172
+ ## Validation and output options
173
+
174
+ On an initialized instance:
175
+
176
+ ```javascript
177
+ const validation = await textgraph.validate("browser -> api -> database");
178
+ console.log(validation.valid, validation.diagnostics);
179
+
180
+ const image = await textgraph.renderPng("browser -> api -> database", {
181
+ scale: 2,
182
+ padding: 24,
183
+ maxWidth: 1200,
184
+ });
101
185
  ```
102
186
 
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.
187
+ Validation checks parsing and semantic references; rendering additionally checks
188
+ layout and output. Diagram failures return diagnostics. Operational failures
189
+ reject with `DrawMotiveError`.
104
190
 
105
- ## Runnable samples
191
+ PNG output defaults to bytes, scale `1`, padding `10`, and a white background.
192
+ Use higher scale for more pixels and returned display dimensions for web sizing.
193
+ TypeScript declarations are included for every public entry point.
106
194
 
107
- The [samples directory](https://github.com/drawmotive/textgraph/tree/main/samples) contains browser JavaScript, React/Vite, Web Worker, and Node applications. Each installs a packed SDK and includes complete run instructions. Samples stay in the repository and are excluded from npm.
195
+ [Full API and options →](https://github.com/drawmotive/textgraph/blob/main/docs/api-v1.md)
108
196
 
109
- ## Additional languages
197
+ ## Run the examples locally
110
198
 
111
- All optional fonts share the `@drawmotive/textgraph-fonts` package, which is not yet published. Select the language descriptors needed by the application; `zhCN` provides Simplified Chinese coverage.
199
+ Samples are complete applications in the public repository and use a local
200
+ package tarball. To try the React sample from a fresh checkout:
112
201
 
113
202
  ```bash
114
- npm install @drawmotive/textgraph-fonts
203
+ git clone https://github.com/drawmotive/textgraph.git
204
+ cd textgraph
205
+ node samples/prepare.mjs react-vite
206
+ cd samples/react-vite
207
+ npm run dev
115
208
  ```
116
209
 
117
- ```typescript
118
- import { initializeTextGraph } from "@drawmotive/textgraph";
119
- import { zhCN } from "@drawmotive/textgraph-fonts";
210
+ Vite samples require Node.js **22.12+**. Replace `react-vite` with `browser` or
211
+ `worker` for those samples. The Node sample uses `npm start` instead.
212
+ Preparation packages the checkout and installs sample dependencies; it does not
213
+ publish anything. Samples are not included in the npm package.
120
214
 
121
- const textgraph = await initializeTextGraph({ languagePacks: [zhCN] });
122
- const result = await textgraph.renderPng("A: 开始\nB: 完成\nA -> B");
123
- await textgraph.dispose();
124
- ```
215
+ [Sample setup and deployment →](https://github.com/drawmotive/textgraph/blob/main/samples/README.md)
125
216
 
126
- ## Validation
217
+ ## Current scope
127
218
 
128
- ```typescript
129
- const result = await textgraph.validate("A -> B");
130
- console.log(result.valid, result.diagnostics);
131
- ```
219
+ - **Available:** flowcharts and directed graphs, automatic layout, PNG rendering,
220
+ and source validation.
221
+ - **Not available yet:** mind maps, sequence diagrams, slides, and SVG export.
222
+ - **Fonts:** Noto Sans and Fuzzy Bubbles are included. Custom font descriptors are
223
+ supported; the optional `@drawmotive/textgraph-fonts` package is not published.
224
+ See [font configuration](https://github.com/drawmotive/textgraph/blob/main/docs/api-v1.md#fonts-and-language-packs).
225
+
226
+ ## Project links
227
+
228
+ [Language guide](https://textgraph.dev/diagrams/flowcharts) ·
229
+ [Changelog](https://github.com/drawmotive/textgraph/blob/main/CHANGELOG.md) ·
230
+ [Report an issue](https://github.com/drawmotive/textgraph/issues) ·
231
+ [MIT license](https://github.com/drawmotive/textgraph/blob/main/LICENSE)
232
+
233
+ ### Try it in a browser
132
234
 
133
- Diagram errors return diagnostics. Operational errors throw `DrawMotiveError`. See the [API reference](docs/api-v1.md) for options, cancellation, and deployment.
235
+ [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
@@ -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` | `2` | Positive finite render 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,15 +34,48 @@ 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
40
42
 
43
+ Runtimes advertising `textgraph-fonts-v1` prepare actual visible labels before measurement.
44
+ Official Chinese, Japanese and emoji fonts load only when needed, then remain decoded
45
+ until the instance is disposed. Node discovers an installed `@drawmotive/textgraph-fonts`
46
+ package automatically; browser hosts copy its assets and supply their deployed catalog:
47
+
48
+ ```javascript
49
+ const runtime = await initializeTextGraph({
50
+ fontAssets: { catalog: new URL("/textgraph/fonts/font-catalog.json", location.origin), fallback: false },
51
+ });
52
+ ```
53
+
54
+ `fontAssets.catalog` is authoritative: missing or corrupt files fail without silently
55
+ switching versions. With no catalog or language packs, fonts are resolved on demand
56
+ from `https://staging.drawmotive.com/static/font-catalog.json`. Set `fallback: false`
57
+ to prohibit that external access, or supply another catalog URL as `fallback`.
58
+ Installing a local package takes precedence over fallback even in offline mode.
59
+ Use `languagePacks: []` with `fallback: false` to explicitly disable automatic font sources.
60
+
61
+ Catalog fonts carry actual Unicode coverage, language hints, byte sizes and SHA-256.
62
+ They are verified before native installation. Catalog URLs are cached per instance;
63
+ HTTP font URLs include the content hash as a stable `v` parameter. HTTP caching remains
64
+ browser/server-owned; there is no new IndexedDB or Service Worker. Offline operation
65
+ requires the complete runtime and font package on a local filesystem or local server;
66
+ an online page cannot fetch an uncached font after losing network access.
67
+
68
+ `renderPng(source, { language: "ja" })` can disambiguate Han-only labels. Without a hint,
69
+ kana selects Japanese for that label and Han-only labels use Chinese. Grapheme-aware
70
+ emoji selection includes flags, ZWJ sequences, modifiers and keycaps. Font selection
71
+ does not depend on which languages were rendered previously. Unsupported glyphs still
72
+ produce font diagnostics; the font catalog defines coverage, not support for every language.
73
+
41
74
  Default rendering includes Noto Sans and Fuzzy Bubbles. Configure additional fonts with `initializeTextGraph({ languagePacks: [...] })`. A `TextGraphLanguagePack` has a non-empty `fonts` array of `{ family, source }` and optional ordered `fallbackFamilies`. Font sources are `URL` objects or non-empty `Uint8Array` values. In Node, use `pathToFileURL()` for filesystem paths; relative asset modules can use `new URL("./font.ttf", import.meta.url)` across platforms.
42
75
 
43
- The initializer copies byte arrays, URLs and descriptor arrays before asynchronous work, so later caller mutations cannot change rendering resources. Families must be unique across all packs and bundled defaults. Fallback families must name configured fonts. Descriptor validation performs no font I/O. Font contents are loaded and configured on first render; validate-only use does not read fonts or themes. Font-loading or configuration failures reject with `DrawMotiveError`; a later render can retry.
76
+ The initializer copies byte arrays, URLs and descriptor arrays before asynchronous work, so later caller mutations cannot change rendering resources. Families must be unique across all packs and bundled defaults. Fallback families must name configured fonts. Descriptor validation performs no font I/O. Legacy descriptors without coverage load on first render; official descriptors carry coverage, languages, bytes and SHA-256 and load on demand. Validate-only use does not read fonts or themes. Font-loading or configuration failures reject with `DrawMotiveError`; a later render can retry.
44
77
 
45
- Bundled resource URLs use `resolveAsset`. Language-pack sources use their supplied URL directly and the platform data loader, including custom `fetch` or the network adapter for network URLs. All optional fonts share `@drawmotive/textgraph-fonts`, installed separately. It currently exports a `zhCN` descriptor for Simplified Chinese; additional language descriptors will use the same package. The package is not yet published.
78
+ Bundled resource URLs use `resolveAsset`. Language-pack sources use their supplied URL directly and the platform data loader, including custom `fetch` or the network adapter for network URLs. All optional fonts share `@drawmotive/textgraph-fonts`, installed separately. It exports `zhCN`, `ja`, `emoji`, `languagePacks` and `fontCatalogUrl`.
46
79
 
47
80
  ## Lifecycle
48
81
 
@@ -57,15 +90,21 @@ Existing `loadRuntime`, `readonly` and `mutate` hooks remain for development com
57
90
  - Node 22 or later: root or `/node`; runtime modules use installed-package `file:` URLs, data uses `node:fs/promises`. HTTP data can use injected fetch, but Node does not import remote HTTP JavaScript. Node `worker_threads` uses `/node`.
58
91
  - Browser: `/browser` or browser conditional export. Modern Chromium, Firefox and WebKit with WebAssembly, ESM, fetch, BigInt and Web Crypto are tested. HTTPS or localhost is required for Web Crypto. Exact engine versions are pinned by the Playwright lockfile; historical minimum browser versions are not claimed.
59
92
  - Browser module Web Worker: `/worker` inside a host-created Worker; no DOM, window, automatic RPC or Worker pool.
60
- - React 18.2+ or 19: `/react` provides `TextGraph` and `TextGraphProvider` over the browser runtime. See [React integration](react.md). This entry is an unreleased addition after `0.1.0-alpha.1`.
93
+ - React 18.2+ or 19: `/react` provides `TextGraph` and `TextGraphProvider` over the browser runtime. See [React integration](react.md).
61
94
 
62
95
  Root condition precedence is worker/browser/node/default. Explicit entries avoid bundler ambiguity. Browser/Worker source graphs exclude Node imports.
63
96
 
97
+ 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.
98
+
99
+ 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.
100
+
64
101
  ## Assets, CSP and offline operation
65
102
 
66
103
  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.
67
104
 
68
- `fetch` or `adapters.network.fetch` receives data URLs and the applicable initialization or render signal. JS uses dynamic import, which injected fetch cannot intercept. Offline installations must cache the module graph as well as WASM/data at importable URLs, using a Service Worker or local server. No GitHub Releases download is required.
105
+ `fetch` or `adapters.network.fetch` receives data URLs and the applicable initialization or render signal. Independent startup data assets download concurrently; all must pass integrity checks before native startup. JS uses dynamic import, which injected fetch cannot intercept. Offline installations must cache the module graph as well as WASM/data at importable URLs, using a Service Worker or local server. No GitHub Releases download is required.
106
+
107
+ Browser, Worker and Node entry points share this concurrent startup loader. Markdown integrations using the Node entry point inherit it when upgraded to the fixed SDK; their default asset reads are local files. Publishing an SDK version does not update pinned dependencies or previously bundled extensions: consumers must update their dependency and lockfile, then rebuild and redeploy or repackage. The separate `@drawmotive/editor` package uses Blazor's own concurrent loader.
69
108
 
70
109
  Serve WASM as `application/wasm`, JS as `text/javascript`, and provide applicable CORS headers for cross-origin assets. Same-origin CSP: `default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'self'; worker-src 'self'`. No eval, new Function or DOM script injection is used. Cross-origin isolation and SharedArrayBuffer are not required by this single-threaded runtime.
71
110
 
@@ -77,12 +116,19 @@ DSL failures resolve with diagnostics. Operational errors reject with `DrawMotiv
77
116
 
78
117
  ## ABI and versioning
79
118
 
119
+ The additive `textgraph-fonts-v1` capability adds commands to `Execute`:
120
+ `prepare-fonts` takes `source` and optional `language` and returns visible grapheme
121
+ `fontRuns` with `text`, `language` and `missing`; `install-fonts` atomically appends
122
+ fonts with language metadata; `cancel-prepare` releases interrupted preparation.
123
+ Render commands accept the same language hint. Existing configuration/disposal
124
+ envelopes remain unchanged. The wrapper checks this capability before using a catalog.
125
+
80
126
  ABI 1.0.0 retains GetAbiVersion/GetPackageKind/CountParseDiagnostics and adds GetRuntimeInfo(), Validate(source) and Execute(requestJson) on DrawMotive.TextGraph.Bridge.Program. Info returns JSON `{ abiVersion, packageKind, protocolVersion: 1, capabilities }`; Validate returns `{ protocolVersion: 1, valid, diagnostics }`. Wrappers require `textgraph-validate-v1`; PNG rendering additionally requires `textgraph-render-v1` and the manifest `bridge.execute` export. Legacy validation exports remain supported. This is a managed export/JSON protocol, not a raw WASM pointer ABI.
81
127
 
82
128
  `generated/wasm-manifest.json` is authoritative; its ESM projection avoids JSON-module browser assumptions. Shipped schemas describe the manifest, validation response and rendering response. Manifest schema, ABI, protocol and npm versions are separate. Additive exports use capabilities; incompatible wire changes require a new supported version. npm versions and bundled assets are released together. DSL/file-format versions are not frozen by this milestone.
83
129
 
84
130
  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
131
 
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.
132
+ 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
133
 
88
134
  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.
@@ -0,0 +1,28 @@
1
+ # README preview
2
+
3
+ `service-flow.png` is actual output from the package's bundled renderer, using
4
+ `examples/service-flow.textgraph`. Keep the root README's source block and
5
+ playground link synchronized with that file.
6
+
7
+ Regenerate from the repository root with Node.js 22+:
8
+
9
+ ```bash
10
+ node --input-type=module <<'JS'
11
+ import { readFile, writeFile } from 'node:fs/promises';
12
+ import { initializeTextGraph } from '@drawmotive/textgraph/node';
13
+
14
+ const textgraph = await initializeTextGraph();
15
+ try {
16
+ const source = await readFile('examples/service-flow.textgraph', 'utf8');
17
+ const result = await textgraph.renderPng(source, { scale: 2, padding: 24 });
18
+ if (!result.success) throw new Error(JSON.stringify(result.diagnostics));
19
+ await writeFile('docs/images/service-flow.png', result.png);
20
+ } finally {
21
+ await textgraph.dispose();
22
+ }
23
+ JS
24
+ ```
25
+
26
+ The root README uses an absolute GitHub raw URL so the image works on npm as well
27
+ as GitHub. Push the image and source to the public repository before publishing
28
+ the next npm version; npm refreshes its displayed README on publication.
Binary file
package/docs/react.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # React integration
2
2
 
3
- The `/react` entry is an unreleased addition after `0.1.0-alpha.1`. React is an optional peer dependency (`^18.2.0 || ^19.0.0`); applications without React use the ordinary SDK entries.
3
+ The `/react` entry provides a diagram component and shared runtime provider. React is an optional peer dependency (`^18.2.0 || ^19.0.0`); applications without React use the ordinary SDK entries.
4
4
 
5
5
  ## Browser assets
6
6
 
@@ -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={{ scale: 2, maxWidth: 1200 }}
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`, `padding`, and `maxWidth`, with SDK defaults. |
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
@@ -4,16 +4,14 @@ Releases are selected deliberately and published by `.github/workflows/release.y
4
4
 
5
5
  ## Channels
6
6
 
7
- | Version | npm dist-tag | Installation |
7
+ | Version | npm dist-tag | Install |
8
8
  | --- | --- | --- |
9
- | `0.1.0-alpha.1` | `alpha` | `npm install @drawmotive/textgraph@alpha` |
10
- | `0.2.0` | `latest` | `npm install @drawmotive/textgraph` |
9
+ | `0.2.1` (stable) | `latest` | `npm install @drawmotive/textgraph@0.2.1` |
10
+ | `0.2.2-alpha.1` (preview) | `alpha` | `npm install @drawmotive/textgraph@0.2.2-alpha.1` |
11
11
 
12
- 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`.
12
+ Alpha releases update `alpha` and preserve an existing `latest`. npm may assign `latest` on a package’s first publication even when `alpha` is specified.
13
13
 
14
- No stable release is required before publishing another alpha. The script verifies the existing default and immutable artifact integrity without attempting to delete tags. To graduate an alpha, prepare and publish a new stable version.
15
-
16
- The scripts accept stable SemVer and `X.Y.Z-alpha.N`. Other channels require an explicit release-policy change. The shared optional `@drawmotive/textgraph-fonts` package has its own release lifecycle and is not published by this workflow.
14
+ The scripts accept stable SemVer and `X.Y.Z-alpha.N`. The optional `@drawmotive/textgraph-fonts` package uses the separate fonts workflow described below.
17
15
 
18
16
  ## Account setup
19
17
 
@@ -35,10 +33,10 @@ Trusted publishing requires npm 11.5.1+ and Node.js 22.14.0+. Publication from a
35
33
  Native builds belong to the private DrawMotive repository. Commit native source, resource, and bridge changes first, then run there:
36
34
 
37
35
  ```bash
38
- npm run release:textgraph -- 0.2.0
36
+ npm run release:textgraph -- 0.2.2-alpha.1
39
37
  ```
40
38
 
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.0` alias runs the same command.
39
+ 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.2-alpha.1` alias runs the same command.
42
40
 
43
41
  For a JavaScript-only release reusing already verified native bytes, run in this package:
44
42
 
@@ -82,26 +80,47 @@ If npm requires a one-time code, run with `NPM_CONFIG_OTP` set in the local term
82
80
  After preparation, tests, and review, create the tag on the verified package commit:
83
81
 
84
82
  ```bash
85
- git tag -a textgraph-v0.2.0 -m "Release 0.2.0"
86
- git push origin textgraph-v0.2.0
83
+ git tag -a textgraph-v0.2.2-alpha.1 -m "Release 0.2.2-alpha.1"
84
+ git push origin textgraph-v0.2.2-alpha.1
87
85
  ```
88
86
 
89
- 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.
87
+ 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. Alpha releases select `alpha`; stable releases select `latest`. Never pass a custom dist-tag to bypass the release policy.
90
88
 
91
89
  ## Verify publication and recover
92
90
 
93
91
  ```bash
94
92
  npm view @drawmotive/textgraph dist-tags --json
95
- npm view @drawmotive/textgraph@0.2.0 dist.integrity
96
- npm install @drawmotive/textgraph@0.2.0
93
+ npm view @drawmotive/textgraph@0.2.2-alpha.1 dist.integrity
94
+ npm install @drawmotive/textgraph@0.2.2-alpha.1
97
95
  ```
98
96
 
99
97
  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
98
 
101
99
  ```bash
102
- gh workflow run release.yml --ref textgraph-v0.2.0 -f tag=textgraph-v0.2.0
100
+ gh workflow run release.yml --ref textgraph-v0.2.2-alpha.1 -f tag=textgraph-v0.2.2-alpha.1
103
101
  ```
104
102
 
105
103
  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.
106
104
 
107
105
  The first alpha owning `latest` is expected and requires no repair. If a later publication unexpectedly changes an existing default, inspect registry state before retrying. Do not unpublish, overwrite versions, or delete tags as a routine recovery step.
106
+
107
+ ## Optional fonts package
108
+
109
+ `.github/workflows/release-fonts.yml` publishes `@drawmotive/textgraph-fonts` from tags named `textgraph-fonts-v<VERSION>`. It verifies the coordinated release target, font hashes, Unicode coverage, pinned provenance, original license/notice hashes, offline installation, and publication policy before packing. The package has no native build or SDK dependency.
110
+
111
+ Run these checks from the TextGraph repository after synchronizing the release target and package/lock versions in the parent repository:
112
+
113
+ ```bash
114
+ npm ci --prefix language-packs --workspaces=false
115
+ npm run build --prefix language-packs --workspaces=false
116
+ npm test --prefix language-packs --workspaces=false
117
+ node --test --test-concurrency=1 test/release.test.mjs test/fonts-release.test.mjs
118
+ node scripts/fonts-release.mjs pack
119
+ node scripts/publish-fonts-release.mjs --dry-run
120
+ ```
121
+
122
+ The exact archive and receipt remain in `language-packs/.release/`; CI retains them as the `textgraph-fonts-npm-release` artifact for 30 days and passes them unchanged to the publish job. Font alpha releases use `alpha`, preserve an existing `latest`, and accept npm assigning `latest` on first publication. A retry verifies the existing version's integrity instead of publishing it again.
123
+
124
+ Allow `textgraph-fonts-v*` tags in the GitHub `npm` environment. For bootstrap, an authorized administrator can set its `NPM_TOKEN` secret to an npm token with permission to create/publish this scoped package. The workflow writes only a token variable reference into a temporary npm configuration; it does not print or retain the credential in the artifact. Once the package exists, configure its trusted publisher with repository `drawmotive/textgraph`, workflow `release-fonts.yml`, and environment `npm`, then remove the bootstrap secret. Without that secret the workflow uses npm trusted publishing with OIDC.
125
+
126
+ After committing and reviewing the release, publish the matching `textgraph-fonts-v<VERSION>` tag. Manual retries must use the same existing tag for both the workflow ref and the `tag` input. Publish the fonts package before refreshing consumer lockfiles; consumers must resolve the actual public registry tarball and integrity.
@@ -0,0 +1,6 @@
1
+ (horizontal)
2
+ browser -> api -> database
3
+
4
+ browser: Browser
5
+ api(fill primary): API Gateway
6
+ database: Database
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file