@strategicprojects/rpic 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ import * as rpic from '@strategicprojects/rpic';
9
9
 
10
10
  await rpic.ready(); // browser: wasm fetched automatically
11
11
 
12
- const { svg, animations, diagnostics, warnings } = rpic.compile('box "A"; arrow; box "B"');
12
+ const { svg, animations, diagnostics, warnings, objects } = rpic.compile('box "A"; arrow; box "B"');
13
13
  document.querySelector('#stage').innerHTML = svg;
14
14
 
15
15
  // animate with GSAP:
@@ -25,7 +25,7 @@ rpic.renderSvg('A:(0,0); B:(2,0)\nresistor(A,B)', { circuits: true });
25
25
  ```js
26
26
  import { readFileSync } from 'node:fs';
27
27
  import * as rpic from '@strategicprojects/rpic';
28
- await rpic.ready(readFileSync(new URL('./node_modules/rpic/pkg/rpic_wasm_bg.wasm', import.meta.url)));
28
+ await rpic.ready(readFileSync(new URL('./node_modules/@strategicprojects/rpic/pkg/rpic_wasm_bg.wasm', import.meta.url)));
29
29
  console.log(rpic.renderSvg('box "hi"'));
30
30
  ```
31
31
 
@@ -38,20 +38,21 @@ data) that is only fetched when you ask for it:
38
38
 
39
39
  ```js
40
40
  await rpic.ready(undefined, { math: true }); // browser: fetches pkg/rpic_wasm_math_bg.wasm
41
- rpic.renderSvg('box "$-\\\\frac{T}{2}$" fit', { texlabels: true });
41
+ rpic.renderSvg('box "$-\\frac{T}{2}$" fit', { texlabels: true });
42
42
  ```
43
43
 
44
44
  Apps can keep the fast path untouched and lazy-load math only when the source
45
- contains `$…$`. The build choice is fixed by the first `ready()` call. In
46
- Node, pass the math artifact's bytes:
45
+ contains `$…$`. The build choice is fixed by the first `ready()` call; a later
46
+ call that asks for the other build rejects instead of silently reusing the
47
+ first build. In Node, pass the math artifact's bytes:
47
48
  `ready(readFileSync(new URL('…/pkg/rpic_wasm_math_bg.wasm', import.meta.url)), { math: true })`.
48
49
 
49
50
  ## API
50
51
 
51
52
  | Function | Description |
52
53
  |----------|-------------|
53
- | `ready(wasmInput?, {math?})` | Initialize WASM. Browser: no arg. Node: pass `.wasm` bytes/URL. `math: true` loads the math-enabled build. |
54
- | `compile(src, {circuits?, texlabels?})` | → `{ svg, animations, diagnostics, warnings }` (throws on a pic error with `errorInfo`). |
54
+ | `ready(wasmInput?, {math?})` | Initialize WASM. Browser: no arg. Node: pass `.wasm` bytes/URL. `math: true` loads the math-enabled build; conflicting later calls reject. |
55
+ | `compile(src, {circuits?, texlabels?})` | → `{ svg, animations, diagnostics, warnings, objects }` (throws on a pic error with `errorInfo`). |
55
56
  | `renderSvg(src, {circuits?, texlabels?})` | → SVG string. |
56
57
  | `animate(root, animations, gsap)` | Build/play a GSAP timeline (`draw`/`fade`/`pop`). Browser only. |
57
58
 
package/index.d.ts CHANGED
@@ -26,6 +26,22 @@ export interface Diagnostic {
26
26
  hint: string | null;
27
27
  }
28
28
 
29
+ export interface ObjectGeometry {
30
+ /** matches the shape's `<g id="sN">` group in the SVG */
31
+ id: string;
32
+ /** "box" | "circle" | "ellipse" | "path" | "spline" | "arc" | "brace" | "text" */
33
+ kind: string;
34
+ /** bounds in SVG user units (the viewBox space); `null` for invisible shapes */
35
+ bbox: { x: number; y: number; w: number; h: number } | null;
36
+ /** 1-based source position of the statement that drew it, when known */
37
+ line?: number;
38
+ col?: number;
39
+ /** exclusive end column of the statement's leading token */
40
+ end_col?: number;
41
+ /** absent = your own input; a `copy` include name or `"circuits"` otherwise */
42
+ file?: string;
43
+ }
44
+
29
45
  export interface Bundle {
30
46
  svg: string;
31
47
  animations: Anim[];
@@ -33,6 +49,8 @@ export interface Bundle {
33
49
  diagnostics: string[];
34
50
  /** non-fatal compiler warnings for accepted but suspicious input */
35
51
  warnings: Diagnostic[];
52
+ /** per-object geometry, index-aligned with the `<g id="sN">` groups */
53
+ objects: ObjectGeometry[];
36
54
  }
37
55
 
38
56
  export interface CompileError extends Error {
@@ -59,22 +77,23 @@ export interface ReadyOptions {
59
77
  * Load the math-enabled wasm build (`pkg/rpic_wasm_math_bg.wasm`), which
60
78
  * bundles the RaTeX renderer so `texlabels` sources typeset `$…$` labels.
61
79
  * The heavier artifact is only fetched when requested; the choice is fixed
62
- * by the first `ready()` call. In Node, pass the math wasm bytes as
63
- * `wasmInput`.
80
+ * by the first `ready()` call, and conflicting later calls reject. In Node,
81
+ * pass the math wasm bytes as `wasmInput`.
64
82
  */
65
83
  math?: boolean;
66
84
  }
67
85
 
68
86
  /**
69
87
  * Initialize the WebAssembly module. In the browser call with no argument; in
70
- * Node pass the `.wasm` bytes or a file URL.
88
+ * Node pass the `.wasm` bytes or a file URL. Idempotent while pending or
89
+ * after success; a *failed* init clears the slot so `ready()` can be retried.
71
90
  */
72
91
  export function ready(
73
92
  wasmInput?: BufferSource | URL | string,
74
93
  opts?: ReadyOptions
75
94
  ): Promise<void>;
76
95
 
77
- /** Compile pic source into `{ svg, animations, diagnostics, warnings }`. Throws on a pic error. */
96
+ /** Compile pic source into `{ svg, animations, diagnostics, warnings, objects }`. Throws on a pic error. */
78
97
  export function compile(src: string, opts?: CompileOptions): Bundle;
79
98
 
80
99
  /** Compile and return only the SVG string. */
package/index.js CHANGED
@@ -7,6 +7,7 @@ import * as leanModule from './pkg/rpic_wasm.js';
7
7
 
8
8
  let initPromise = null;
9
9
  let wasm = null; // the initialized glue module (lean or math)
10
+ let initMath = null;
10
11
 
11
12
  /**
12
13
  * Initialize the WebAssembly module (idempotent; concurrent calls share one
@@ -16,18 +17,42 @@ let wasm = null; // the initialized glue module (lean or math)
16
17
  * Pass `{ math: true }` to load the math-enabled build instead: it bundles
17
18
  * the RaTeX renderer so `texlabels` sources typeset `$…$` labels exactly like
18
19
  * the native CLI. The math glue + wasm are only fetched when requested, so
19
- * the lean fast path stays untouched. The choice is fixed by the first call;
20
- * in Node, pass the bytes of `pkg/rpic_wasm_math_bg.wasm` along with it.
20
+ * the lean fast path stays untouched. The choice is fixed by the first
21
+ * successful (or in-flight) call; later calls asking for the other build
22
+ * reject. A *failed* init (bad bytes, fetch error) clears the slot so
23
+ * `ready()` can be retried. In Node, pass the bytes of
24
+ * `pkg/rpic_wasm_math_bg.wasm` along with it.
21
25
  */
22
26
  export function ready(wasmInput, opts = {}) {
23
- if (!initPromise) {
24
- initPromise = (
25
- opts.math ? import('./pkg/rpic_wasm_math.js') : Promise.resolve(leanModule)
26
- ).then(async (mod) => {
27
+ const wantsMath = !!opts.math;
28
+ if (initPromise) {
29
+ if (initMath !== wantsMath) {
30
+ const current = initMath ? 'math-enabled' : 'lean';
31
+ const requested = wantsMath ? 'math-enabled' : 'lean';
32
+ return Promise.reject(
33
+ new Error(`rpic: ready() already initialized the ${current} build; cannot switch to ${requested}`)
34
+ );
35
+ }
36
+ return initPromise;
37
+ }
38
+ initMath = wantsMath;
39
+ const attempt = (
40
+ wantsMath ? import('./pkg/rpic_wasm_math.js') : Promise.resolve(leanModule)
41
+ )
42
+ .then(async (mod) => {
27
43
  await mod.default(wasmInput === undefined ? undefined : { module_or_path: wasmInput });
28
44
  wasm = mod;
45
+ })
46
+ .catch((e) => {
47
+ // a settled rejection must not poison future calls — clear the slot
48
+ // (only if it is still ours) so the next ready() starts fresh
49
+ if (initPromise === attempt) {
50
+ initPromise = null;
51
+ initMath = null;
52
+ }
53
+ throw e;
29
54
  });
30
- }
55
+ initPromise = attempt;
31
56
  return initPromise;
32
57
  }
33
58
 
@@ -38,7 +63,7 @@ function ensure() {
38
63
  }
39
64
 
40
65
  /**
41
- * Compile pic source into `{ svg, animations, diagnostics, warnings }` (throws on a pic error).
66
+ * Compile pic source into `{ svg, animations, diagnostics, warnings, objects }` (throws on a pic error).
42
67
  * @param {string} src
43
68
  * @param {{circuits?: boolean, texlabels?: boolean}} [opts]
44
69
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strategicprojects/rpic",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "The pic graphics language compiled to SVG with animation manifests, via WebAssembly. Browser + Node.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
Binary file
Binary file