@danielsimonjr/mathts-workbook 0.4.12 → 0.5.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
@@ -20,6 +20,7 @@ mtsw run example.mtsw -v # also print the execution event stream
20
20
  mtsw run example.mtsw --json # machine-readable envelope on stdout
21
21
  mtsw run example.mtsw -c gauss # run one cell + its transitive deps (stateless)
22
22
  mtsw run example.mtsw --timeout 5000 # run in a worker; killed (exit 1) if it exceeds 5s
23
+ mtsw run example.mtsw --expect-hash <sha256> # refuse unless file SHA-256 matches
23
24
 
24
25
  # Describe the structured document model (cells, outputs, dependency graph).
25
26
  mtsw describe example.mtsw --json
@@ -30,6 +31,7 @@ mtsw validate example.mtsw [--json]
30
31
  # Print the dependency graph (human only; use `describe --json` for structured data).
31
32
  mtsw graph example.mtsw # text adjacency
32
33
  mtsw graph example.mtsw -f mermaid # Mermaid `graph TD`
34
+ mtsw graph example.mtsw -f dot # Graphviz `digraph deps { ... }`
33
35
 
34
36
  # Engine introspection (for tooling / GUIs).
35
37
  mtsw capabilities --json # version, supported cell types, feature flags
@@ -72,6 +74,7 @@ mtsw import doc.json --json # validates (ids/deps/cycles); stdout
72
74
  # Render to a self-contained HTML document (runs first, then renders).
73
75
  mtsw export example.mtsw -o example.html # one offline file; stdout if no -o
74
76
  mtsw export example.mtsw --no-run # render cached outputs without executing
77
+ mtsw export example.mtsw --format tex --fragment -o body.tex # TeX body only (no preamble)
75
78
  mtsw export example.mtsw --json # envelope: { data: { path, bytes } }
76
79
 
77
80
  # Other export formats: --format tex|pdf|json|ipynb (html is the default).
@@ -79,20 +82,40 @@ mtsw export example.mtsw --format ipynb -o example.ipynb # Jupyter notebook (n
79
82
 
80
83
  # Persistent session for a GUI/tooling: JSON-RPC 2.0 over stdio (NDJSON).
81
84
  mtsw serve
85
+ # then JSON-RPC method "open" with params.path — the CLI ignores a file operand
82
86
  # -> {"jsonrpc":"2.0","id":1,"method":"open","params":{"path":"example.mtsw"}}
83
87
  # <- {"jsonrpc":"2.0","id":1,"result":{...describe doc...}}
84
88
  # -> {"jsonrpc":"2.0","id":2,"method":"run"} # streams cell/event notifications
85
89
  # methods: open/describe/validate/graph/run/cell.*/meta.*/save/capabilities/functions/shutdown
86
90
  ```
87
91
 
92
+ **`export --format tex` (programmatic).** The same `RenderDoc` the CLI builds for TeX
93
+ export is available as `toTeX` from the package entry (with `RenderDoc`, `RenderCell`,
94
+ and `ToTexOptions`). A fragment omits the LaTeX preamble — the same shape as
95
+ `mtsw export … --format tex --fragment`:
96
+
97
+ ```typescript
98
+ import { toTeX } from '@danielsimonjr/mathts-workbook';
99
+
100
+ const body = toTeX(
101
+ { title: 'Notes', cells: [{ type: 'markdown', content: 'Energy $E=mc^2$' }] },
102
+ { fragment: true }
103
+ );
104
+ // → markdown cell text only; no \\documentclass
105
+ ```
106
+
107
+ Use `buildDependencyGraph` + `toDOT` when you need Graphviz DOT instead of Mermaid
108
+ (`mtsw graph -f dot`).
109
+
88
110
  **`export` (self-contained HTML).** `mtsw export <file> --format html` renders a
89
111
  notebook to a single offline `.html` with **no external requests**: markdown prose,
90
112
  **equations typeset as MathML** (rendered natively by modern browsers — Chromium ≥109,
91
113
  Firefox, Safari), code cells with their embedded outputs, ✓/✗ test badges, and (as of
92
- the chart slice) inline SVG plots. All rendering is MathTS-native — the generators
93
- (`toMathML`/`toHTML`/`toCSS`, plus `markdownToHtml`) live in the `expression` package
94
- alongside the node `.toTex()`/`.toHTML()` serializers, with **zero external
95
- dependencies**. Equation cells contain MathTS expression syntax (e.g.
114
+ the chart slice) inline SVG plots. Per-node `.toMathML()` / `.toTex()` / `.toHTML()`
115
+ and `mathMLDocument` come from `@danielsimonjr/mathts-expression`; the workbook
116
+ runtime assembles full HTML with `toHTML` / `toCSS` / `markdownToHtml` / `renderChart`
117
+ (workbook modules used by the CLI — not on the workbook package entry except
118
+ `mathMLToSVG`). Equation cells contain MathTS expression syntax (e.g.
96
119
  `c = 1 / sqrt(eps0 * mu0)`), not raw LaTeX; they are display-only and rendered via
97
120
  `toMathML`. By default `export` runs the workbook first (use `--no-run` for cached
98
121
  outputs); a whole-run failure such as a dependency cycle fails loudly rather than
@@ -121,7 +144,7 @@ matrices, …) don't survive the worker's `postMessage`.
121
144
 
122
145
  **`serve` (persistent session).** One long-lived process holds the workbook in memory with a per-cell result cache and a **stale set**: a `cell.*` edit marks that cell and its transitive dependents stale, and a `run` re-executes **only** the stale cells (reusing cached outputs for the rest) — the incremental latency win a GUI needs. Requests are processed strictly in order; `run` streams `cell/event` notifications (flushed before that run's response in v1, not mid-run). Edits stay in memory until `save`. Single-document per process; concurrent writers are last-write-wins.
123
146
 
124
- **Editing notes.** Cell edits are validity-preserving: an op that would create a duplicate/invalid id, a missing dependency, or a **dependency cycle** is rejected and the file is left byte-for-byte unchanged. Editing a cell clears its (now-stale) persisted output; `--at N` is the cell's final 0-based index; `--force` detaches dependents (clearing their outputs) but does not rewrite cell _content_, so a dependent that still references the removed id by name will error at run. Concurrent editors are last-write-wins (an optimistic-lock guard arrives with the `serve` session).
147
+ **Editing notes.** Cell edits are validity-preserving: an op that would create a duplicate/invalid id, a missing dependency, or a **dependency cycle** is rejected and the file is left byte-for-byte unchanged. Editing a cell clears its (now-stale) persisted output; `--at N` is the cell's final 0-based index; `--force` detaches dependents (clearing their outputs) but does not rewrite cell _content_, so a dependent that still references the removed id by name will error at run. Concurrent editors are last-write-wins in `serve` (last accepted `cell.*` op wins; use `save` to persist).
125
148
 
126
149
  Diagnostics and errors are written to stderr; results (including `--json`) go to stdout, so the exit code can be used in scripts independently of the output.
127
150
 
@@ -133,8 +156,8 @@ should read `ok` and treat a missing/unparseable envelope as the only transport
133
156
  error. Compatibility rule: ignore unknown fields when `major` matches; refuse on
134
157
  a `major` mismatch. `run --cell <id>` is stateless (it recomputes the target's
135
158
  transitive deps each call; `run --cell --write` persists only the executed
136
- cells). This is the contract a GUI binds to; a persistent `serve` mode (streaming
137
- events, incremental re-execution) is planned.
159
+ cells). Stateful editing and incremental re-execution live in `mtsw serve` (see
160
+ above); one-shot commands use this envelope only.
138
161
 
139
162
  **Saving / round-trip.** `serializeWorkbook` (and the write commands above) round-trips a workbook through the parser: structure is preserved exactly, and persisted `output` values round-trip best-effort (plain numbers/strings/arrays/objects exactly; exotic types to their plain shape). All writes are **atomic** (temp file + rename). Note that any write path is parse→serialize and therefore **drops YAML comments and re-orders keys** — a CST-preserving in-place rewrite is a future enhancement.
140
163
 
@@ -224,19 +247,23 @@ A `test` cell's expression must evaluate to a **boolean**: `true` passes, `false
224
247
 
225
248
  ## Cell types (v1)
226
249
 
227
- | Type | Status | Description |
228
- | -------------------------------------------------- | ------ | ------------------------------------------------------ |
229
- | `markdown` | ✅ | Documentation (passed through verbatim) |
230
- | `code` | ✅ | MathTS expression script; last value is the result |
231
- | `data` | ✅ | Structured YAML, parsed (hardened) into a value |
232
- | `test` | ✅ | Boolean assertion (`true` = pass) |
233
- | `tensor` / `equation` / `visualization` / `export` | ⏳ | Reserved; not executed in v1 (reported as unsupported) |
250
+ | Type | Status | Description |
251
+ | --------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
252
+ | `markdown` | ✅ | Documentation (passed through verbatim) |
253
+ | `code` | ✅ | MathTS expression script; last value is the result |
254
+ | `data` | ✅ | Structured YAML, parsed (hardened) into a value |
255
+ | `test` | ✅ | Boolean assertion (`true` = pass) |
256
+ | `equation` | ✅ | MathTS expression syntax; display-only (content echoed, not evaluated) |
257
+ | `visualization` | ✅ | Chart/visual spec; display-only (content echoed, not evaluated) |
258
+ | `tensor` | ⏳ | Deferred — parse fails: `type 'tensor' is reserved but not yet supported (supported: code, markdown, data, test, equation, visualization)` |
259
+ | `export` | ⏳ | Same as `tensor` (`export` is reserved; not in the supported list) |
234
260
 
235
261
  ## Execution modes
236
262
 
237
- - **reactive** — emits stale events for dependents when a cell re-runs
238
- - **sequential** — top-to-bottom (dependency) order
239
- - **manual** — explicit trigger only
263
+ All modes execute cells in dependency order (`graph.executionOrder`). The only
264
+ runtime difference is **reactive**, which emits `cell:stale` for dependents when a
265
+ cell is edited or re-run. **Sequential** and **manual** use the same execution order
266
+ without stale events (both still run the full graph on `run` / `runReport`).
240
267
 
241
268
  ## Security
242
269
 
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAsCH;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAwOD;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAqHvE;AAqDD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAoC7D;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA+C7D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAYjE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAa9D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAU9D;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA2DzD;AAoFD;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CA8H1E;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,KAAK,GAAE,MAAM,CAAC,cAA8B,EAC5C,MAAM,GAAE,MAAM,CAAC,cAA+B,GAC7C,OAAO,CAAC,IAAI,CAAC,CAuEf;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,aAAa,CAAC,CAG3D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAsC1D;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA0B1D;AAuDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAqFxD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAiD3D;AAyED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA4HzD;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAuDrE"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAsCH;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AA8OD;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAqHvE;AAqDD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAoC7D;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA+C7D;AAkBD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CASjE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAO9D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAO9D;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA2DzD;AAoFD;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CA8H1E;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,KAAK,GAAE,MAAM,CAAC,cAA8B,EAC5C,MAAM,GAAE,MAAM,CAAC,cAA+B,GAC7C,OAAO,CAAC,IAAI,CAAC,CAuEf;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,aAAa,CAAC,CAG3D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAsC1D;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA0B1D;AAuDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAqFxD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAiD3D;AAyED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA4HzD;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAuDrE"}
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { c as parseWorkbook, d as buildDependencyGraph, f as detectCycles, g as toMermaid, h as toDOT, l as serializeWorkbook, o as importWorkbook, r as createExecutor, t as formatResult, u as stripOutputs, v as parseYamlSafe } from "./formatter-8ja3r-YP.js";
3
- import { a as capabilitiesInfo, c as writeFileAtomic, d as addCell, f as editCell, g as setMetadata, h as renameCell, i as describeData, l as SCHEMA_VERSION, m as removeCell, n as runWorkbookWithTimeout, o as listFunctions, p as moveCell, r as handleRequest, s as Session, u as VERSION } from "./timeout-runner-CpyusRou.js";
2
+ import { c as parseWorkbook, d as buildDependencyGraph, f as detectCycles, g as toMermaid, h as toDOT, l as serializeWorkbook, o as importWorkbook, r as createExecutor, t as formatResult, u as stripOutputs, v as parseYamlSafe } from "./formatter-Dwrza-un.js";
3
+ import { _ as toTeX, a as capabilitiesInfo, c as writeFileAtomic, d as addCell, f as editCell, g as setMetadata, h as renameCell, i as describeData, l as SCHEMA_VERSION, m as removeCell, n as runWorkbookWithTimeout, o as listFunctions, p as moveCell, r as handleRequest, s as Session, u as VERSION, v as markdownToHtml, y as renderChart } from "./timeout-runner-C249B3OS.js";
4
4
  import * as mathFunctions from "@danielsimonjr/mathts-functions";
5
5
  import { lstatSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
6
6
  import { createHash } from "node:crypto";
@@ -8,253 +8,7 @@ import { pathToFileURL } from "node:url";
8
8
  import { basename } from "node:path";
9
9
  import { createInterface } from "node:readline";
10
10
  import { mathMLDocument, mathMLError } from "@danielsimonjr/mathts-expression";
11
- import { bar, line, scatter } from "@danielsimonjr/mathts-plot";
12
11
  import { latexToPdf } from "@danielsimonjr/mathts-plot/render";
13
- //#region src/markdown.ts
14
- function esc$1(s) {
15
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
16
- }
17
- function sanitizeHrefRaw(href) {
18
- const h = href.trim();
19
- let probe = h.replace(/&amp;/g, "&");
20
- try {
21
- probe = decodeURIComponent(probe);
22
- } catch {}
23
- if (probe.startsWith("//")) return null;
24
- try {
25
- const url = new URL(probe);
26
- if (url.protocol === "http:" || url.protocol === "https:" || url.protocol === "mailto:") return h;
27
- return null;
28
- } catch {
29
- if (probe.startsWith("/") || probe.startsWith("./") || probe.startsWith("../") || probe.startsWith("#")) return h;
30
- return null;
31
- }
32
- }
33
- function sanitizeHref(href) {
34
- const raw = sanitizeHrefRaw(href);
35
- return raw === null ? null : raw.replace(/"/g, "&quot;");
36
- }
37
- function inline(escaped) {
38
- let s = escaped.replace(/`([^`]+)`/g, (_m, code) => `<code>${code}</code>`);
39
- s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
40
- s = s.replace(/\*([^*]+)\*/g, "<em>$1</em>");
41
- s = s.replace(/\[([^\]]*)\]\(([^)\s]+)\)/g, (_m, text, href) => {
42
- const safe = sanitizeHref(href);
43
- return safe ? `<a href="${safe}">${text}</a>` : text;
44
- });
45
- return s;
46
- }
47
- const HR = /^ {0,3}(-{3,}|\*{3,}|_{3,})\s*$/;
48
- const HEADING = /^(#{1,6})\s+(.*)$/;
49
- const UL = /^\s*[-*+]\s+/;
50
- const OL = /^\s*\d+\.\s+/;
51
- function isBlockStart(line) {
52
- return line.trim() === "" || /^```/.test(line.trim()) || HEADING.test(line) || UL.test(line) || OL.test(line) || HR.test(line);
53
- }
54
- function markdownToHtml(src) {
55
- if (!src) return "";
56
- const lines = src.replace(/\r\n?/g, "\n").split("\n");
57
- const out = [];
58
- let i = 0;
59
- while (i < lines.length) {
60
- const line = lines[i];
61
- if (/^```/.test(line.trim())) {
62
- const buf = [];
63
- i++;
64
- while (i < lines.length && !/^```/.test(lines[i].trim())) {
65
- buf.push(lines[i]);
66
- i++;
67
- }
68
- i++;
69
- out.push(`<pre><code>${esc$1(buf.join("\n"))}</code></pre>`);
70
- continue;
71
- }
72
- if (line.trim() === "") {
73
- i++;
74
- continue;
75
- }
76
- if (HR.test(line)) {
77
- out.push("<hr>");
78
- i++;
79
- continue;
80
- }
81
- const h = HEADING.exec(line);
82
- if (h) {
83
- const lvl = h[1].length;
84
- out.push(`<h${lvl}>${inline(esc$1(h[2].trim()))}</h${lvl}>`);
85
- i++;
86
- continue;
87
- }
88
- if (UL.test(line)) {
89
- const items = [];
90
- while (i < lines.length && UL.test(lines[i])) {
91
- items.push(`<li>${inline(esc$1(lines[i].replace(UL, "")))}</li>`);
92
- i++;
93
- }
94
- out.push(`<ul>${items.join("")}</ul>`);
95
- continue;
96
- }
97
- if (OL.test(line)) {
98
- const items = [];
99
- while (i < lines.length && OL.test(lines[i])) {
100
- items.push(`<li>${inline(esc$1(lines[i].replace(OL, "")))}</li>`);
101
- i++;
102
- }
103
- out.push(`<ol>${items.join("")}</ol>`);
104
- continue;
105
- }
106
- const para = [];
107
- while (i < lines.length && !isBlockStart(lines[i])) {
108
- para.push(lines[i]);
109
- i++;
110
- }
111
- out.push(`<p>${inline(esc$1(para.join(" ")))}</p>`);
112
- }
113
- return out.join("\n");
114
- }
115
- function texEscape(s) {
116
- return s.replace(/[\\&%$#_{}~^]/g, (c) => {
117
- switch (c) {
118
- case "\\": return "\\textbackslash{}";
119
- case "~": return "\\textasciitilde{}";
120
- case "^": return "\\textasciicircum{}";
121
- default: return `\\${c}`;
122
- }
123
- });
124
- }
125
- function verbatimBody(body, env) {
126
- return body.split(`\\end{${env}}`).join(`\\end {${env}}`);
127
- }
128
- function escapeInlineNonLink(text) {
129
- let s = texEscape(text);
130
- s = s.replace(/`([^`]+)`/g, (_m, code) => `\\texttt{${code}}`);
131
- s = s.replace(/\*\*([^*]+)\*\*/g, "\\textbf{$1}");
132
- s = s.replace(/\*([^*]+)\*/g, "\\emph{$1}");
133
- return s;
134
- }
135
- function inlineTex(text) {
136
- const linkRe = /\[([^\]]*)\]\(([^)\s]+)\)/g;
137
- let out = "";
138
- let last = 0;
139
- let m;
140
- while ((m = linkRe.exec(text)) !== null) {
141
- out += escapeInlineNonLink(text.slice(last, m.index));
142
- const raw = sanitizeHrefRaw(m[2]);
143
- out += raw ? `\\href{${texEscape(raw)}}{${escapeInlineNonLink(m[1])}}` : escapeInlineNonLink(m[1]);
144
- last = linkRe.lastIndex;
145
- }
146
- out += escapeInlineNonLink(text.slice(last));
147
- return out;
148
- }
149
- function markdownToTex(src) {
150
- if (!src) return "";
151
- const lines = src.replace(/\r\n?/g, "\n").split("\n");
152
- const out = [];
153
- let i = 0;
154
- const SEC = [
155
- "\\section*",
156
- "\\subsection*",
157
- "\\subsubsection*",
158
- "\\paragraph"
159
- ];
160
- while (i < lines.length) {
161
- const line = lines[i];
162
- if (/^```/.test(line.trim())) {
163
- const buf = [];
164
- i++;
165
- while (i < lines.length && !/^```/.test(lines[i].trim())) {
166
- buf.push(lines[i]);
167
- i++;
168
- }
169
- i++;
170
- out.push(`\\begin{lstlisting}\n${verbatimBody(buf.join("\n"), "lstlisting")}\n\\end{lstlisting}`);
171
- continue;
172
- }
173
- if (line.trim() === "") {
174
- i++;
175
- continue;
176
- }
177
- if (HR.test(line)) {
178
- out.push("\\par\\noindent\\rule{\\linewidth}{0.4pt}\\par");
179
- i++;
180
- continue;
181
- }
182
- const h = HEADING.exec(line);
183
- if (h) {
184
- const lvl = Math.min(h[1].length, 4);
185
- out.push(`${SEC[lvl - 1]}{${inlineTex(h[2].trim())}}`);
186
- i++;
187
- continue;
188
- }
189
- if (UL.test(line)) {
190
- const items = [];
191
- while (i < lines.length && UL.test(lines[i])) {
192
- items.push(` \\item ${inlineTex(lines[i].replace(UL, ""))}`);
193
- i++;
194
- }
195
- out.push(`\\begin{itemize}\n${items.join("\n")}\n\\end{itemize}`);
196
- continue;
197
- }
198
- if (OL.test(line)) {
199
- const items = [];
200
- while (i < lines.length && OL.test(lines[i])) {
201
- items.push(` \\item ${inlineTex(lines[i].replace(OL, ""))}`);
202
- i++;
203
- }
204
- out.push(`\\begin{enumerate}\n${items.join("\n")}\n\\end{enumerate}`);
205
- continue;
206
- }
207
- const para = [];
208
- while (i < lines.length && !isBlockStart(lines[i])) {
209
- para.push(lines[i]);
210
- i++;
211
- }
212
- out.push(inlineTex(para.join(" ")));
213
- }
214
- return out.join("\n\n");
215
- }
216
- //#endregion
217
- //#region src/svg.ts
218
- function coerce(v) {
219
- if (typeof v === "number") return v;
220
- if (typeof v === "bigint") return Number(v);
221
- const u = v;
222
- if (u && typeof u.toNumeric === "function") try {
223
- return Number(u.toNumeric());
224
- } catch {
225
- return NaN;
226
- }
227
- return Number(v);
228
- }
229
- function toNums(raw) {
230
- let arr = raw;
231
- const m = raw;
232
- if (m && typeof m.toArray === "function") try {
233
- arr = m.toArray();
234
- } catch {
235
- arr = raw;
236
- }
237
- return Array.isArray(arr) ? arr.flat(Infinity).map(coerce) : [];
238
- }
239
- function renderChart(spec, xRaw, yRaw, format = "svg") {
240
- const xs = toNums(xRaw);
241
- const ys = toNums(yRaw);
242
- const opts = format === "tikz" ? {
243
- title: spec.title,
244
- xLabel: spec.xLabel,
245
- yLabel: spec.yLabel,
246
- format: "tikz",
247
- tikz: { standalone: false }
248
- } : {
249
- title: spec.title,
250
- xLabel: spec.xLabel,
251
- yLabel: spec.yLabel
252
- };
253
- if (spec.type === "scatter") return scatter(xs, ys, opts);
254
- if (spec.type === "bar") return bar(xs, ys, opts);
255
- return line(xs, ys, opts);
256
- }
257
- //#endregion
258
12
  //#region src/html.ts
259
13
  function esc(s) {
260
14
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -366,65 +120,6 @@ function toHTML(doc, options = {}) {
366
120
  ].join("\n");
367
121
  }
368
122
  //#endregion
369
- //#region src/tex.ts
370
- const PREAMBLE = [
371
- "\\documentclass{article}",
372
- "\\usepackage{amsmath}",
373
- "\\usepackage{tikz}",
374
- "\\usepackage{listings}",
375
- "\\usepackage{xcolor}",
376
- "\\usepackage[margin=1in]{geometry}",
377
- "\\usepackage{hyperref}"
378
- ].join("\n");
379
- function renderEquationTex(content, parse) {
380
- if (parse) try {
381
- return `\\[ ${parse(content).toTex()} \\]`;
382
- } catch {}
383
- return `\\texttt{${texEscape(content)}}`;
384
- }
385
- function renderCellTex(cell, parse) {
386
- switch (cell.type) {
387
- case "markdown": return markdownToTex(cell.content);
388
- case "equation": return renderEquationTex(cell.content, parse);
389
- case "code": {
390
- const cap = cell.id ? `\\textit{${texEscape(cell.id)}}\\\\\n` : "";
391
- const src = `\\begin{lstlisting}\n${verbatimBody(cell.content, "lstlisting")}\n\\end{lstlisting}`;
392
- let res = "";
393
- if (cell.error !== void 0) res = `\n\n\\textcolor{red}{\\texttt{${texEscape(cell.error)}}}`;
394
- else if (cell.output !== void 0) res = `\n\n\\begin{verbatim}\n${verbatimBody(cell.output, "verbatim")}\n\\end{verbatim}`;
395
- return `${cap}${src}${res}`;
396
- }
397
- case "test": {
398
- const label = `\\texttt{${texEscape(cell.content)}}`;
399
- if (cell.error !== void 0) return `\\textcolor{orange}{[ERROR]} ${label} --- ${texEscape(cell.error)}`;
400
- if (cell.passed === true) return `\\textcolor{green!60!black}{[PASS]} ${label}`;
401
- if (cell.passed === false) return `\\textcolor{red}{[FAIL]} ${label}`;
402
- return `\\textcolor{gray}{[NOT RUN]} ${label}`;
403
- }
404
- case "data": {
405
- const val = cell.error !== void 0 ? cell.error : cell.output ?? "";
406
- return `${cell.id ? `\\textit{${texEscape(cell.id)}}\\\\\n` : ""}\\begin{verbatim}\n${verbatimBody(val, "verbatim")}\n\\end{verbatim}`;
407
- }
408
- case "chart": return `\\begin{center}\n${cell.chart ? renderChart(cell.chart.spec, cell.chart.x, cell.chart.y, "tikz") : "% no chart"}\n\\end{center}${cell.note ? `\n\n\\textit{${texEscape(cell.note)}}` : ""}`;
409
- default: return `\\begin{verbatim}\n${verbatimBody(cell.content, "verbatim")}\n\\end{verbatim}`;
410
- }
411
- }
412
- function toTeX(doc, options = {}) {
413
- const body = doc.cells.map((c) => renderCellTex(c, options.parse)).join("\n\n");
414
- if (options.fragment) return body;
415
- const meta = [];
416
- if (doc.title) meta.push(`\\title{${texEscape(doc.title)}}`);
417
- if (doc.author) meta.push(`\\author{${texEscape(doc.author)}}`);
418
- const maketitle = doc.title ? "\\maketitle\n" : "";
419
- return [
420
- PREAMBLE,
421
- ...meta,
422
- "\\begin{document}",
423
- `${maketitle}${body}`,
424
- "\\end{document}"
425
- ].join("\n");
426
- }
427
- //#endregion
428
123
  //#region src/pdf.ts
429
124
  function toPDF(doc, outPath, options = {}) {
430
125
  const { parse, ...renderOpts } = options;
@@ -595,7 +290,10 @@ function jsonEnvelope(command, ok, data, problems = []) {
595
290
  return value;
596
291
  };
597
292
  return JSON.stringify({
598
- schemaVersion: SCHEMA_VERSION,
293
+ schemaVersion: {
294
+ major: SCHEMA_VERSION.major,
295
+ minor: SCHEMA_VERSION.minor
296
+ },
599
297
  command,
600
298
  ok,
601
299
  data,
@@ -934,52 +632,38 @@ function describeCommand(args) {
934
632
  exitCode: ok ? 0 : 1
935
633
  };
936
634
  }
937
- function capabilitiesCommand(args) {
938
- const data = capabilitiesInfo();
635
+ function renderInfoCommand(args, command, data, lines) {
939
636
  if (args.includes("--json")) return {
940
- stdout: jsonEnvelope("capabilities", true, data, []),
637
+ stdout: jsonEnvelope(command, true, data, []),
941
638
  stderr: "",
942
639
  exitCode: 0
943
640
  };
944
641
  return {
945
- stdout: [
946
- `mtsw ${data.version} (schema ${SCHEMA_VERSION.major}.${SCHEMA_VERSION.minor})`,
947
- `supported cells: ${data.cellTypes.supported.join(", ")}`,
948
- `deferred cells: ${data.cellTypes.deferred.join(", ")}`,
949
- `commands: ${data.commands.join(", ")}`
950
- ].join("\n"),
642
+ stdout: lines.join("\n"),
951
643
  stderr: "",
952
644
  exitCode: 0
953
645
  };
954
646
  }
647
+ function capabilitiesCommand(args) {
648
+ const data = capabilitiesInfo();
649
+ return renderInfoCommand(args, "capabilities", data, [
650
+ `mtsw ${data.version} (schema ${SCHEMA_VERSION.major}.${SCHEMA_VERSION.minor})`,
651
+ `supported cells: ${data.cellTypes.supported.join(", ")}`,
652
+ `deferred cells: ${data.cellTypes.deferred.join(", ")}`,
653
+ `commands: ${data.commands.join(", ")}`
654
+ ]);
655
+ }
955
656
  function templatesCommand(args) {
956
657
  const templates = Object.entries(TEMPLATES).map(([name, t]) => ({
957
658
  name,
958
659
  description: t.description
959
660
  }));
960
- if (args.includes("--json")) return {
961
- stdout: jsonEnvelope("templates", true, { templates }, []),
962
- stderr: "",
963
- exitCode: 0
964
- };
965
- return {
966
- stdout: templates.map((t) => `${t.name} - ${t.description}`).join("\n"),
967
- stderr: "",
968
- exitCode: 0
969
- };
661
+ const lines = templates.map((t) => `${t.name} - ${t.description}`);
662
+ return renderInfoCommand(args, "templates", { templates }, lines);
970
663
  }
971
664
  function functionsCommand(args) {
972
665
  const data = listFunctions();
973
- if (args.includes("--json")) return {
974
- stdout: jsonEnvelope("functions", true, data, []),
975
- stderr: "",
976
- exitCode: 0
977
- };
978
- return {
979
- stdout: [`functions (${data.functions.length}): ${data.functions.join(", ")}`, `constants (${data.constants.length}): ${data.constants.join(", ")}`].join("\n"),
980
- stderr: "",
981
- exitCode: 0
982
- };
666
+ return renderInfoCommand(args, "functions", data, [`functions (${data.functions.length}): ${data.functions.join(", ")}`, `constants (${data.constants.length}): ${data.constants.join(", ")}`]);
983
667
  }
984
668
  function metaCommand(args) {
985
669
  const json = args.includes("--json");
@@ -102,18 +102,27 @@ function getAncestors(graph, id) {
102
102
  visit(id);
103
103
  return graph.executionOrder.filter((nodeId) => result.has(nodeId));
104
104
  }
105
- function toMermaid(graph) {
106
- const lines = ["graph TD"];
107
- for (const id of graph.nodes.keys()) lines.push(` ${id}["${id}"]`);
108
- for (const [id, node] of graph.nodes) for (const dep of node.dependencies) lines.push(` ${dep} --> ${id}`);
105
+ function renderDependencyGraph(graph, format) {
106
+ const lines = [format.header];
107
+ for (const id of graph.nodes.keys()) lines.push(format.node(id));
108
+ for (const [id, node] of graph.nodes) for (const dep of node.dependencies) lines.push(format.edge(dep, id));
109
+ if (format.footer !== void 0) lines.push(format.footer);
109
110
  return lines.join("\n");
110
111
  }
112
+ function toMermaid(graph) {
113
+ return renderDependencyGraph(graph, {
114
+ header: "graph TD",
115
+ node: (id) => ` ${id}["${id}"]`,
116
+ edge: (dep, id) => ` ${dep} --> ${id}`
117
+ });
118
+ }
111
119
  function toDOT(graph) {
112
- const lines = ["digraph deps {"];
113
- for (const id of graph.nodes.keys()) lines.push(` ${id} [label="${id}"];`);
114
- for (const [id, node] of graph.nodes) for (const dep of node.dependencies) lines.push(` ${dep} -> ${id};`);
115
- lines.push("}");
116
- return lines.join("\n");
120
+ return renderDependencyGraph(graph, {
121
+ header: "digraph deps {",
122
+ node: (id) => ` ${id} [label="${id}"];`,
123
+ edge: (dep, id) => ` ${dep} -> ${id};`,
124
+ footer: "}"
125
+ });
117
126
  }
118
127
  function detectCycles(graph) {
119
128
  const cycles = [];
package/dist/graph.d.ts CHANGED
@@ -32,6 +32,12 @@ export declare function toMermaid(graph: DependencyGraph): string;
32
32
  * Render the dependency graph as Graphviz DOT (the DOT analog of toMermaid).
33
33
  * Cell ids are validated identifiers ([A-Za-z_][A-Za-z0-9_]*), safe verbatim as
34
34
  * both node id and quoted label; no cell content appears in the output.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { buildDependencyGraph, toDOT } from '@danielsimonjr/mathts-workbook';
39
+ * toDOT(buildDependencyGraph(cells)); // digraph deps { … }
40
+ * ```
35
41
  */
36
42
  export declare function toDOT(graph: DependencyGraph): string;
37
43
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAExE;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,eAAe,CA0BnE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,MAAM,EAAE,CAuB5E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB9E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAezE;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAcxD;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAepD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,EAAE,EAAE,CAoC/D"}
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAExE;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,eAAe,CA0BnE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,MAAM,EAAE,CAuB5E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB9E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAezE;AAkCD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAMxD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAOpD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,EAAE,EAAE,CAoC/D"}
package/dist/index.d.ts CHANGED
@@ -2,11 +2,14 @@
2
2
  * @danielsimonjr/mathts-workbook - Scientific workbook runtime
3
3
  * @packageDocumentation
4
4
  */
5
- export type { Workbook, Cell, CellType, ExecutionMode, WorkbookMetadata, RuntimeConfig, ParseResult, WorkbookEvent, CellResult, RunResult, } from './types.js';
5
+ export type { Workbook, Cell, CellType, ExecutionMode, WorkbookMetadata, RuntimeConfig, ParseResult, WorkbookEvent, CellResult, RunResult, DependencyNode, DependencyGraph, } from './types.js';
6
6
  export { parseWorkbook, serializeWorkbook, stripOutputs, detectCellType } from './parser.js';
7
- export { buildDependencyGraph, topologicalSort, getDependents, detectCycles, getAncestors, toMermaid, } from './graph.js';
7
+ export { buildDependencyGraph, topologicalSort, getDependents, detectCycles, getAncestors, toMermaid, toDOT, } from './graph.js';
8
8
  export { WorkbookExecutor, createExecutor } from './executor.js';
9
9
  export { formatResult } from './formatter.js';
10
+ export { toTeX } from './tex.js';
11
+ export type { ToTexOptions } from './tex.js';
12
+ export type { RenderDoc, RenderCell } from './html.js';
10
13
  export { addCell, editCell, removeCell, moveCell, renameCell, setMetadata } from './edit.js';
11
14
  export type { CellPosition, RemoveResult } from './edit.js';
12
15
  export { SCHEMA_VERSION, VERSION } from './contract.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EACV,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,aAAa,EACb,UAAU,EACV,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7F,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGjE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACnF,YAAY,EACV,6BAA6B,EAC7B,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EACV,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,aAAa,EACb,UAAU,EACV,SAAS,EACT,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7F,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,KAAK,GACN,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGjE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACnF,YAAY,EACV,6BAA6B,EAC7B,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { _ as topologicalSort, a as detectCellType, c as parseWorkbook, d as buildDependencyGraph, f as detectCycles, g as toMermaid, l as serializeWorkbook, m as getDependents, n as WorkbookExecutor, p as getAncestors, r as createExecutor, t as formatResult, u as stripOutputs } from "./formatter-8ja3r-YP.js";
2
- import { d as addCell, f as editCell, g as setMetadata, h as renameCell, l as SCHEMA_VERSION, m as removeCell, n as runWorkbookWithTimeout, p as moveCell, r as handleRequest, s as Session, t as WorkbookTimeoutError, u as VERSION } from "./timeout-runner-CpyusRou.js";
1
+ import { _ as topologicalSort, a as detectCellType, c as parseWorkbook, d as buildDependencyGraph, f as detectCycles, g as toMermaid, h as toDOT, l as serializeWorkbook, m as getDependents, n as WorkbookExecutor, p as getAncestors, r as createExecutor, t as formatResult, u as stripOutputs } from "./formatter-Dwrza-un.js";
2
+ import { _ as toTeX, d as addCell, f as editCell, g as setMetadata, h as renameCell, l as SCHEMA_VERSION, m as removeCell, n as runWorkbookWithTimeout, p as moveCell, r as handleRequest, s as Session, t as WorkbookTimeoutError, u as VERSION } from "./timeout-runner-C249B3OS.js";
3
3
  //#region src/svg-math.ts
4
4
  function mathMLToSVG(mathml, options = {}) {
5
5
  const display = options.display ?? "block";
@@ -17,4 +17,4 @@ function mathMLToSVG(mathml, options = {}) {
17
17
  ].join("");
18
18
  }
19
19
  //#endregion
20
- export { SCHEMA_VERSION, Session, VERSION, WorkbookExecutor, WorkbookTimeoutError, addCell, buildDependencyGraph, createExecutor, detectCellType, detectCycles, editCell, formatResult, getAncestors, getDependents, handleRequest, mathMLToSVG, moveCell, parseWorkbook, removeCell, renameCell, runWorkbookWithTimeout, serializeWorkbook, setMetadata, stripOutputs, toMermaid, topologicalSort };
20
+ export { SCHEMA_VERSION, Session, VERSION, WorkbookExecutor, WorkbookTimeoutError, addCell, buildDependencyGraph, createExecutor, detectCellType, detectCycles, editCell, formatResult, getAncestors, getDependents, handleRequest, mathMLToSVG, moveCell, parseWorkbook, removeCell, renameCell, runWorkbookWithTimeout, serializeWorkbook, setMetadata, stripOutputs, toDOT, toMermaid, toTeX, topologicalSort };
@@ -1,4 +1,4 @@
1
- import { c as parseWorkbook, r as createExecutor, t as formatResult } from "./formatter-8ja3r-YP.js";
1
+ import { c as parseWorkbook, r as createExecutor, t as formatResult } from "./formatter-Dwrza-un.js";
2
2
  import { parentPort, workerData } from "node:worker_threads";
3
3
  //#region src/run-worker.ts
4
4
  async function main() {
package/dist/tex.d.ts CHANGED
@@ -15,6 +15,14 @@ export interface ToTexOptions {
15
15
  /** Emit only the cell bodies (for \input) instead of a standalone document. */
16
16
  fragment?: boolean;
17
17
  }
18
- /** Render a document structure to a standalone (or fragment) LaTeX string. Never throws. */
18
+ /**
19
+ * Render a document structure to a standalone (or fragment) LaTeX string. Never throws.
20
+ *
21
+ * @example Fragment body for `\input` (no preamble):
22
+ * ```ts
23
+ * import { toTeX } from '@danielsimonjr/mathts-workbook';
24
+ * toTeX({ cells: [{ type: 'markdown', content: 'hi' }] }, { fragment: true });
25
+ * ```
26
+ */
19
27
  export declare function toTeX(doc: RenderDoc, options?: ToTexOptions): string;
20
28
  //# sourceMappingURL=tex.d.ts.map
package/dist/tex.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tex.d.ts","sourceRoot":"","sources":["../src/tex.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,WAAW,CAAC;AAIvD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAiED,4FAA4F;AAC5F,wBAAgB,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAUxE"}
1
+ {"version":3,"file":"tex.d.ts","sourceRoot":"","sources":["../src/tex.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,WAAW,CAAC;AAIvD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAiED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAUxE"}
@@ -1,7 +1,312 @@
1
- import { c as parseWorkbook, d as buildDependencyGraph, f as detectCycles, i as SUPPORTED_CELL_TYPES, l as serializeWorkbook, m as getDependents, n as WorkbookExecutor, p as getAncestors, s as isValidIdentifier } from "./formatter-8ja3r-YP.js";
1
+ import { c as parseWorkbook, d as buildDependencyGraph, f as detectCycles, i as SUPPORTED_CELL_TYPES, l as serializeWorkbook, m as getDependents, n as WorkbookExecutor, p as getAncestors, s as isValidIdentifier } from "./formatter-Dwrza-un.js";
2
2
  import * as mathFunctions from "@danielsimonjr/mathts-functions";
3
+ import { bar, line, scatter } from "@danielsimonjr/mathts-plot";
3
4
  import { readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
4
5
  import { Worker } from "node:worker_threads";
6
+ //#region src/svg.ts
7
+ function coerce(v) {
8
+ if (typeof v === "number") return v;
9
+ if (typeof v === "bigint") return Number(v);
10
+ const u = v;
11
+ if (u && typeof u.toNumeric === "function") try {
12
+ return Number(u.toNumeric());
13
+ } catch {
14
+ return NaN;
15
+ }
16
+ return Number(v);
17
+ }
18
+ function toNums(raw) {
19
+ let arr = raw;
20
+ const m = raw;
21
+ if (m && typeof m.toArray === "function") try {
22
+ arr = m.toArray();
23
+ } catch {
24
+ arr = raw;
25
+ }
26
+ return Array.isArray(arr) ? arr.flat(Infinity).map(coerce) : [];
27
+ }
28
+ function renderChart(spec, xRaw, yRaw, format = "svg") {
29
+ const xs = toNums(xRaw);
30
+ const ys = toNums(yRaw);
31
+ const opts = format === "tikz" ? {
32
+ title: spec.title,
33
+ xLabel: spec.xLabel,
34
+ yLabel: spec.yLabel,
35
+ format: "tikz",
36
+ tikz: { standalone: false }
37
+ } : {
38
+ title: spec.title,
39
+ xLabel: spec.xLabel,
40
+ yLabel: spec.yLabel
41
+ };
42
+ if (spec.type === "scatter") return scatter(xs, ys, opts);
43
+ if (spec.type === "bar") return bar(xs, ys, opts);
44
+ return line(xs, ys, opts);
45
+ }
46
+ //#endregion
47
+ //#region src/markdown.ts
48
+ function esc(s) {
49
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
50
+ }
51
+ function sanitizeHrefRaw(href) {
52
+ const h = href.trim();
53
+ let probe = h.replace(/&amp;/g, "&");
54
+ try {
55
+ probe = decodeURIComponent(probe);
56
+ } catch {}
57
+ if (probe.startsWith("//")) return null;
58
+ try {
59
+ const url = new URL(probe);
60
+ if (url.protocol === "http:" || url.protocol === "https:" || url.protocol === "mailto:") return h;
61
+ return null;
62
+ } catch {
63
+ if (probe.startsWith("/") || probe.startsWith("./") || probe.startsWith("../") || probe.startsWith("#")) return h;
64
+ return null;
65
+ }
66
+ }
67
+ function sanitizeHref(href) {
68
+ const raw = sanitizeHrefRaw(href);
69
+ return raw === null ? null : raw.replace(/"/g, "&quot;");
70
+ }
71
+ function inline(escaped) {
72
+ let s = escaped.replace(/`([^`]+)`/g, (_m, code) => `<code>${code}</code>`);
73
+ s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
74
+ s = s.replace(/\*([^*]+)\*/g, "<em>$1</em>");
75
+ s = s.replace(/\[([^\]]*)\]\(([^)\s]+)\)/g, (_m, text, href) => {
76
+ const safe = sanitizeHref(href);
77
+ return safe ? `<a href="${safe}">${text}</a>` : text;
78
+ });
79
+ return s;
80
+ }
81
+ const HR = /^ {0,3}(-{3,}|\*{3,}|_{3,})\s*$/;
82
+ const HEADING = /^(#{1,6})\s+(.*)$/;
83
+ const UL = /^\s*[-*+]\s+/;
84
+ const OL = /^\s*\d+\.\s+/;
85
+ function isBlockStart(line) {
86
+ return line.trim() === "" || /^```/.test(line.trim()) || HEADING.test(line) || UL.test(line) || OL.test(line) || HR.test(line);
87
+ }
88
+ function markdownToHtml(src) {
89
+ if (!src) return "";
90
+ const lines = src.replace(/\r\n?/g, "\n").split("\n");
91
+ const out = [];
92
+ let i = 0;
93
+ while (i < lines.length) {
94
+ const line = lines[i];
95
+ if (/^```/.test(line.trim())) {
96
+ const buf = [];
97
+ i++;
98
+ while (i < lines.length && !/^```/.test(lines[i].trim())) {
99
+ buf.push(lines[i]);
100
+ i++;
101
+ }
102
+ i++;
103
+ out.push(`<pre><code>${esc(buf.join("\n"))}</code></pre>`);
104
+ continue;
105
+ }
106
+ if (line.trim() === "") {
107
+ i++;
108
+ continue;
109
+ }
110
+ if (HR.test(line)) {
111
+ out.push("<hr>");
112
+ i++;
113
+ continue;
114
+ }
115
+ const h = HEADING.exec(line);
116
+ if (h) {
117
+ const lvl = h[1].length;
118
+ out.push(`<h${lvl}>${inline(esc(h[2].trim()))}</h${lvl}>`);
119
+ i++;
120
+ continue;
121
+ }
122
+ if (UL.test(line)) {
123
+ const items = [];
124
+ while (i < lines.length && UL.test(lines[i])) {
125
+ items.push(`<li>${inline(esc(lines[i].replace(UL, "")))}</li>`);
126
+ i++;
127
+ }
128
+ out.push(`<ul>${items.join("")}</ul>`);
129
+ continue;
130
+ }
131
+ if (OL.test(line)) {
132
+ const items = [];
133
+ while (i < lines.length && OL.test(lines[i])) {
134
+ items.push(`<li>${inline(esc(lines[i].replace(OL, "")))}</li>`);
135
+ i++;
136
+ }
137
+ out.push(`<ol>${items.join("")}</ol>`);
138
+ continue;
139
+ }
140
+ const para = [];
141
+ while (i < lines.length && !isBlockStart(lines[i])) {
142
+ para.push(lines[i]);
143
+ i++;
144
+ }
145
+ out.push(`<p>${inline(esc(para.join(" ")))}</p>`);
146
+ }
147
+ return out.join("\n");
148
+ }
149
+ function texEscape(s) {
150
+ return s.replace(/[\\&%$#_{}~^]/g, (c) => {
151
+ switch (c) {
152
+ case "\\": return "\\textbackslash{}";
153
+ case "~": return "\\textasciitilde{}";
154
+ case "^": return "\\textasciicircum{}";
155
+ default: return `\\${c}`;
156
+ }
157
+ });
158
+ }
159
+ function verbatimBody(body, env) {
160
+ return body.split(`\\end{${env}}`).join(`\\end {${env}}`);
161
+ }
162
+ function escapeInlineNonLink(text) {
163
+ let s = texEscape(text);
164
+ s = s.replace(/`([^`]+)`/g, (_m, code) => `\\texttt{${code}}`);
165
+ s = s.replace(/\*\*([^*]+)\*\*/g, "\\textbf{$1}");
166
+ s = s.replace(/\*([^*]+)\*/g, "\\emph{$1}");
167
+ return s;
168
+ }
169
+ function inlineTex(text) {
170
+ const linkRe = /\[([^\]]*)\]\(([^)\s]+)\)/g;
171
+ let out = "";
172
+ let last = 0;
173
+ let m;
174
+ while ((m = linkRe.exec(text)) !== null) {
175
+ out += escapeInlineNonLink(text.slice(last, m.index));
176
+ const raw = sanitizeHrefRaw(m[2]);
177
+ out += raw ? `\\href{${texEscape(raw)}}{${escapeInlineNonLink(m[1])}}` : escapeInlineNonLink(m[1]);
178
+ last = linkRe.lastIndex;
179
+ }
180
+ out += escapeInlineNonLink(text.slice(last));
181
+ return out;
182
+ }
183
+ function markdownToTex(src) {
184
+ if (!src) return "";
185
+ const lines = src.replace(/\r\n?/g, "\n").split("\n");
186
+ const out = [];
187
+ let i = 0;
188
+ const SEC = [
189
+ "\\section*",
190
+ "\\subsection*",
191
+ "\\subsubsection*",
192
+ "\\paragraph"
193
+ ];
194
+ while (i < lines.length) {
195
+ const line = lines[i];
196
+ if (/^```/.test(line.trim())) {
197
+ const buf = [];
198
+ i++;
199
+ while (i < lines.length && !/^```/.test(lines[i].trim())) {
200
+ buf.push(lines[i]);
201
+ i++;
202
+ }
203
+ i++;
204
+ out.push(`\\begin{lstlisting}\n${verbatimBody(buf.join("\n"), "lstlisting")}\n\\end{lstlisting}`);
205
+ continue;
206
+ }
207
+ if (line.trim() === "") {
208
+ i++;
209
+ continue;
210
+ }
211
+ if (HR.test(line)) {
212
+ out.push("\\par\\noindent\\rule{\\linewidth}{0.4pt}\\par");
213
+ i++;
214
+ continue;
215
+ }
216
+ const h = HEADING.exec(line);
217
+ if (h) {
218
+ const lvl = Math.min(h[1].length, 4);
219
+ out.push(`${SEC[lvl - 1]}{${inlineTex(h[2].trim())}}`);
220
+ i++;
221
+ continue;
222
+ }
223
+ if (UL.test(line)) {
224
+ const items = [];
225
+ while (i < lines.length && UL.test(lines[i])) {
226
+ items.push(` \\item ${inlineTex(lines[i].replace(UL, ""))}`);
227
+ i++;
228
+ }
229
+ out.push(`\\begin{itemize}\n${items.join("\n")}\n\\end{itemize}`);
230
+ continue;
231
+ }
232
+ if (OL.test(line)) {
233
+ const items = [];
234
+ while (i < lines.length && OL.test(lines[i])) {
235
+ items.push(` \\item ${inlineTex(lines[i].replace(OL, ""))}`);
236
+ i++;
237
+ }
238
+ out.push(`\\begin{enumerate}\n${items.join("\n")}\n\\end{enumerate}`);
239
+ continue;
240
+ }
241
+ const para = [];
242
+ while (i < lines.length && !isBlockStart(lines[i])) {
243
+ para.push(lines[i]);
244
+ i++;
245
+ }
246
+ out.push(inlineTex(para.join(" ")));
247
+ }
248
+ return out.join("\n\n");
249
+ }
250
+ //#endregion
251
+ //#region src/tex.ts
252
+ const PREAMBLE = [
253
+ "\\documentclass{article}",
254
+ "\\usepackage{amsmath}",
255
+ "\\usepackage{tikz}",
256
+ "\\usepackage{listings}",
257
+ "\\usepackage{xcolor}",
258
+ "\\usepackage[margin=1in]{geometry}",
259
+ "\\usepackage{hyperref}"
260
+ ].join("\n");
261
+ function renderEquationTex(content, parse) {
262
+ if (parse) try {
263
+ return `\\[ ${parse(content).toTex()} \\]`;
264
+ } catch {}
265
+ return `\\texttt{${texEscape(content)}}`;
266
+ }
267
+ function renderCellTex(cell, parse) {
268
+ switch (cell.type) {
269
+ case "markdown": return markdownToTex(cell.content);
270
+ case "equation": return renderEquationTex(cell.content, parse);
271
+ case "code": {
272
+ const cap = cell.id ? `\\textit{${texEscape(cell.id)}}\\\\\n` : "";
273
+ const src = `\\begin{lstlisting}\n${verbatimBody(cell.content, "lstlisting")}\n\\end{lstlisting}`;
274
+ let res = "";
275
+ if (cell.error !== void 0) res = `\n\n\\textcolor{red}{\\texttt{${texEscape(cell.error)}}}`;
276
+ else if (cell.output !== void 0) res = `\n\n\\begin{verbatim}\n${verbatimBody(cell.output, "verbatim")}\n\\end{verbatim}`;
277
+ return `${cap}${src}${res}`;
278
+ }
279
+ case "test": {
280
+ const label = `\\texttt{${texEscape(cell.content)}}`;
281
+ if (cell.error !== void 0) return `\\textcolor{orange}{[ERROR]} ${label} --- ${texEscape(cell.error)}`;
282
+ if (cell.passed === true) return `\\textcolor{green!60!black}{[PASS]} ${label}`;
283
+ if (cell.passed === false) return `\\textcolor{red}{[FAIL]} ${label}`;
284
+ return `\\textcolor{gray}{[NOT RUN]} ${label}`;
285
+ }
286
+ case "data": {
287
+ const val = cell.error !== void 0 ? cell.error : cell.output ?? "";
288
+ return `${cell.id ? `\\textit{${texEscape(cell.id)}}\\\\\n` : ""}\\begin{verbatim}\n${verbatimBody(val, "verbatim")}\n\\end{verbatim}`;
289
+ }
290
+ case "chart": return `\\begin{center}\n${cell.chart ? renderChart(cell.chart.spec, cell.chart.x, cell.chart.y, "tikz") : "% no chart"}\n\\end{center}${cell.note ? `\n\n\\textit{${texEscape(cell.note)}}` : ""}`;
291
+ default: return `\\begin{verbatim}\n${verbatimBody(cell.content, "verbatim")}\n\\end{verbatim}`;
292
+ }
293
+ }
294
+ function toTeX(doc, options = {}) {
295
+ const body = doc.cells.map((c) => renderCellTex(c, options.parse)).join("\n\n");
296
+ if (options.fragment) return body;
297
+ const meta = [];
298
+ if (doc.title) meta.push(`\\title{${texEscape(doc.title)}}`);
299
+ if (doc.author) meta.push(`\\author{${texEscape(doc.author)}}`);
300
+ const maketitle = doc.title ? "\\maketitle\n" : "";
301
+ return [
302
+ PREAMBLE,
303
+ ...meta,
304
+ "\\begin{document}",
305
+ `${maketitle}${body}`,
306
+ "\\end{document}"
307
+ ].join("\n");
308
+ }
309
+ //#endregion
5
310
  //#region src/edit.ts
6
311
  function cloneWorkbook(wb) {
7
312
  return {
@@ -151,7 +456,7 @@ const SCHEMA_VERSION = {
151
456
  major: 1,
152
457
  minor: 0
153
458
  };
154
- const VERSION = "0.4.12";
459
+ const VERSION = "0.5.0";
155
460
  const COMMAND_NAMES = [
156
461
  "run",
157
462
  "describe",
@@ -564,4 +869,4 @@ function runWorkbookWithTimeout(source, options) {
564
869
  });
565
870
  }
566
871
  //#endregion
567
- export { capabilitiesInfo as a, writeFileAtomic as c, addCell as d, editCell as f, setMetadata as g, renameCell as h, describeData as i, SCHEMA_VERSION as l, removeCell as m, runWorkbookWithTimeout as n, listFunctions as o, moveCell as p, handleRequest as r, Session as s, WorkbookTimeoutError as t, VERSION as u };
872
+ export { toTeX as _, capabilitiesInfo as a, writeFileAtomic as c, addCell as d, editCell as f, setMetadata as g, renameCell as h, describeData as i, SCHEMA_VERSION as l, removeCell as m, runWorkbookWithTimeout as n, listFunctions as o, moveCell as p, handleRequest as r, Session as s, WorkbookTimeoutError as t, VERSION as u, markdownToHtml as v, renderChart as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danielsimonjr/mathts-workbook",
3
- "version": "0.4.12",
3
+ "version": "0.5.0",
4
4
  "description": "Scientific workbook runtime for MathTS (.mtsw format)",
5
5
  "author": "Daniel Simon Jr.",
6
6
  "license": "MIT",
@@ -34,10 +34,10 @@
34
34
  "build:prod": "tsdown --minify && tsc -p tsconfig.dts.json"
35
35
  },
36
36
  "dependencies": {
37
- "@danielsimonjr/mathts-core": "^0.18.0",
38
- "@danielsimonjr/mathts-expression": "^0.10.2",
39
- "@danielsimonjr/mathts-functions": "^0.69.0",
40
- "@danielsimonjr/mathts-plot": "^0.4.12",
37
+ "@danielsimonjr/mathts-core": "^0.20.0",
38
+ "@danielsimonjr/mathts-expression": "^0.11.0",
39
+ "@danielsimonjr/mathts-functions": "^0.70.0",
40
+ "@danielsimonjr/mathts-plot": "^0.4.14",
41
41
  "yaml": "^2.3.0"
42
42
  },
43
43
  "devDependencies": {