gum-jsx 1.1.4 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,12 +10,12 @@
10
10
 
11
11
  A language for creating visualizations using a React-like JSX dialect that evaluates to SVG. Designed for general graphics, plots, graphs, and network diagrams.
12
12
 
13
- Head to **[compendiumlabs.ai/gum](https://compendiumlabs.ai/gum)** for a live demo and documentation. For Python bindings, see **[gum.py](https://github.com/CompendiumLabs/gum.py)**.
13
+ Head to **[compendiumlabs.ai/gum](https://compendiumlabs.ai/gum)** for a live demo and documentation. For Python bindings, see [gum.py](https://github.com/CompendiumLabs/gum.py).
14
14
 
15
15
  # Installation
16
16
 
17
17
  ```bash
18
- npm install gum
18
+ npm install gum-jsx
19
19
  ```
20
20
 
21
21
  # Usage
@@ -73,6 +73,4 @@ CLI options:
73
73
  | Option | Description | Default |
74
74
  |--------|-------------|---------|
75
75
  | `-s, --size <size>` | Image size in pixels | 500 |
76
- | `-f, --format <format>` | Output format: `svg` or `png` | svg |
77
76
  | `-t, --theme <theme>` | Theme: `light` or `dark` | light |
78
- | `-b, --background <color>` | Background color | null |
package/index.d.ts CHANGED
@@ -1219,42 +1219,6 @@ declare module 'gum-jsx/eval' {
1219
1219
  export function evaluateGum(code: string, options?: EvaluateOptions): Svg;
1220
1220
  }
1221
1221
 
1222
- // =============================================================================
1223
- // Rendering (from render.js)
1224
- // =============================================================================
1225
-
1226
- declare module 'gum-jsx/render' {
1227
- /** A point in 2D space [x, y] */
1228
- export type point = [number, number];
1229
-
1230
- export interface RenderOptions {
1231
- size?: number | point;
1232
- background?: string;
1233
- }
1234
-
1235
- /** Render SVG to PNG (returns Buffer in Node, Blob in browser) */
1236
- export function renderPng(svg: string, options?: RenderOptions): Promise<Buffer | Blob>;
1237
-
1238
- /** Node.js renderer class */
1239
- export class NodeRender {
1240
- fonts: string[];
1241
- Resvg: any;
1242
-
1243
- init(): Promise<void>;
1244
- renderPng(svg: string, options?: RenderOptions): Buffer;
1245
- }
1246
-
1247
- /** Browser renderer class */
1248
- export class BrowserRender {
1249
- canvas: HTMLCanvasElement;
1250
- ctx: CanvasRenderingContext2D;
1251
-
1252
- init(): Promise<void>;
1253
- makeCanvas(size?: point, options?: { dpr?: number | null }): { canvas: HTMLCanvasElement; ctx: CanvasRenderingContext2D };
1254
- renderPng(svg: string, options?: RenderOptions): Promise<Blob>;
1255
- }
1256
- }
1257
-
1258
1222
  // =============================================================================
1259
1223
  // Error Types (from error.js)
1260
1224
  // =============================================================================
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "gum-jsx",
3
- "version": "1.1.4",
3
+ "version": "1.2.1",
4
4
  "description": "Language for vector graphics generation.",
5
5
  "type": "module",
6
6
  "author": "Douglas Hanley",
7
- "homepage": "https://github.com/CompendiumLabs/gum.js",
7
+ "homepage": "https://github.com/CompendiumLabs/gum.jsx",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/CompendiumLabs/gum.js"
10
+ "url": "git+https://github.com/CompendiumLabs/gum.jsx"
11
11
  },
12
12
  "license": "MIT",
13
13
  "files": [
@@ -21,7 +21,6 @@
21
21
  },
22
22
  "./error": "./src/error.js",
23
23
  "./eval": "./src/eval.js",
24
- "./render": "./src/render.js",
25
24
  "./docs/*": "./src/docs/*",
26
25
  "./fonts/*": "./src/fonts/*"
27
26
  },
@@ -43,12 +42,8 @@
43
42
  },
44
43
  "optionalDependencies": {
45
44
  "@mathjax/src": "^4.1.0",
46
- "@modelcontextprotocol/sdk": "^1.25.2",
47
- "@resvg/resvg-js": "^2.6.2",
48
45
  "commander": "^14.0.2",
49
46
  "express": "^5.2.1",
50
- "katex": "^0.16.27",
51
- "mathjax": "^4.1.0",
52
- "zod": "^4.3.5"
47
+ "mathjax": "^4.1.0"
53
48
  }
54
49
  }
package/src/cli.js CHANGED
@@ -3,32 +3,20 @@
3
3
  import { program } from 'commander'
4
4
  import { waitForStdin } from './node.js'
5
5
  import { evaluateGum } from './eval.js'
6
- import { renderPng } from './render.js'
7
6
 
8
7
  // get options from commander
9
8
  program
10
9
  .option('-s, --size <size>', 'size of the image', (value) => parseInt(value), 500)
11
- .option('-x, --pixels <pixels>', 'svg pixel coords', (value) => parseInt(value), 500)
12
- .option('-f, --format <format>', 'format of the image', 'svg')
13
10
  .option('-t, --theme <theme>', 'theme to use', 'dark')
14
- .option('-b, --background <color>', 'background color', null)
15
11
  .parse()
16
- const { size, pixels, format, theme, background } = program.opts()
12
+ const { size, theme } = program.opts()
17
13
 
18
14
  // wait for stdin
19
15
  const code = await waitForStdin()
20
16
 
21
17
  // evaluate gum with size
22
- const elem = evaluateGum(code, { size: pixels, theme })
18
+ const elem = evaluateGum(code, { size, theme })
23
19
  const svg = elem.svg()
24
20
 
25
- // output svg or png
26
- if (format == 'png') {
27
- const png = renderPng(svg, { size, background })
28
- process.stdout.write(png)
29
- } else if (format == 'svg') {
30
- process.stdout.write(svg)
31
- } else {
32
- console.error(`Invalid format: ${format}`)
33
- process.exit(1)
34
- }
21
+ // output svg
22
+ process.stdout.write(svg)
package/src/gum.js CHANGED
@@ -1000,7 +1000,7 @@ class Metadata {
1000
1000
 
1001
1001
  class Svg extends Group {
1002
1002
  constructor(args = {}) {
1003
- const { children: children0, size : size0, padding = 1, bare = false, dims = true, filters = null, aspect: aspect0 = 'auto', view: view0, style = null, xmlns = C.svgns, font_family = C.sans, font_weight = C.normal, size = D.size, prec = D.prec, ...attr } = THEME(args, 'Svg')
1003
+ const { children: children0, size : size0 = D.size, padding = 1, bare = false, dims = true, filters = null, aspect: aspect0 = 'auto', view: view0, style = null, xmlns = C.svgns, font_family = C.sans, font_weight = C.normal, prec = D.prec, ...attr } = THEME(args, 'Svg')
1004
1004
  const children = ensure_array(children0)
1005
1005
  const size_base = ensure_vector(size0, 2)
1006
1006
 
package/src/server.js CHANGED
@@ -3,25 +3,8 @@
3
3
  import express from 'express'
4
4
  import { program } from 'commander'
5
5
  import { evaluateGum } from './eval.js'
6
- import { canvas } from './canvas.js'
7
6
  import { ErrorNoCode, ErrorNoReturn, ErrorNoElement } from './eval.js'
8
7
 
9
- class ErrorGenerate extends Error {
10
- constructor(error) {
11
- super(error.message)
12
- this.name = 'ErrorGenerate'
13
- this.error = error
14
- }
15
- }
16
-
17
- class ErrorRender extends Error {
18
- constructor(error) {
19
- super(error.message)
20
- this.name = 'ErrorRender'
21
- this.error = error
22
- }
23
- }
24
-
25
8
  function parseError(error) {
26
9
  if (error instanceof ErrorNoCode) {
27
10
  return 'ERR_NOCODE: No code provided'
@@ -59,21 +42,17 @@ app.get('/', (req, res) => {
59
42
  })
60
43
 
61
44
  // eval gum jsx to svg
62
- app.post('/evaluate', (req, res) => {
45
+ app.post('/', (req, res) => {
63
46
  // get params
64
47
  const code = req.body
65
- const size0 = parseInt(req.query.size ?? 750)
48
+ const size0 = parseInt(req.query.size ?? 500)
66
49
  const theme = req.query.theme ?? 'light'
67
50
 
68
51
  // evaluate code and return svg
69
52
  let svg
70
53
  try {
71
54
  const elem = evaluateGum(code, { size: size0, theme })
72
- try {
73
- svg = elem.svg()
74
- } catch (err) {
75
- throw new ErrorGenerate(err)
76
- }
55
+ svg = elem.svg()
77
56
  } catch (error) {
78
57
  const message = parseError(error)
79
58
  return res.status(500).send(message)
@@ -84,38 +63,6 @@ app.post('/evaluate', (req, res) => {
84
63
  res.send(svg)
85
64
  })
86
65
 
87
- // render gum jsx to png
88
- app.post('/render', async (req, res) => {
89
- // get params
90
- const code = req.body
91
- const size0 = parseInt(req.query.size ?? 750)
92
- const theme = req.query.theme ?? 'light'
93
-
94
- // evaluate code and render to png
95
- let png, svg
96
- try {
97
- const elem = evaluateGum(code, { size: size0, theme })
98
- try {
99
- svg = elem.svg()
100
- } catch (err) {
101
- throw new ErrorGenerate(err)
102
- }
103
- try {
104
- const { size } = elem
105
- png = await canvas.renderPng(svg, { size })
106
- } catch (err) {
107
- throw new ErrorRender(err)
108
- }
109
- } catch (error) {
110
- const message = parseError(error)
111
- return res.status(500).send(message)
112
- }
113
-
114
- // send png
115
- res.setHeader('Content-Type', 'image/png')
116
- res.send(png)
117
- })
118
-
119
66
  // start server
120
67
  app.listen(port, host, () => {
121
68
  // console.log(`Server running on http://${host}:${port}`)
package/src/test.js CHANGED
@@ -3,15 +3,14 @@
3
3
  import { program } from 'commander'
4
4
 
5
5
  import { waitForStdin } from './node.js'
6
- import { CONSTANTS as C, setTheme } from './defaults.js'
7
- import { textSizer, splitWords, is_emoji, FONTS } from './text.js'
6
+ import { setTheme } from './defaults.js'
7
+ import { textSizer, splitWords } from './text.js'
8
8
  import { evaluateGum } from './eval.js'
9
9
 
10
10
  // evaluate command
11
11
  async function cmdEvaluate({ debug }) {
12
12
  const code = await waitForStdin()
13
- setTheme('dark')
14
- const elem = evaluateGum(code, { debug, size: 1000 })
13
+ const elem = evaluateGum(code, { debug, theme: 'dark', size: 1000 })
15
14
  const svg = elem.svg()
16
15
  console.log(svg)
17
16
  }
package/src/dynamic.js DELETED
@@ -1,136 +0,0 @@
1
- /**
2
- ** Interactive
3
- **/
4
-
5
- class Interactive {
6
- constructor(vars, func) {
7
- this.func = func;
8
- this.vars = vars;
9
- }
10
-
11
- element() {
12
- let vals = map_object(this.vars, v => v.value);
13
- return this.func(vals);
14
- }
15
- }
16
-
17
- class Variable {
18
- constructor(init, args) {
19
- args = args ?? {};
20
- this.value = init;
21
- this.attr = filter_object(args, v => v != null);
22
- }
23
-
24
- update(val) {
25
- this.value = val;
26
- }
27
- }
28
-
29
- class Slider extends Variable {
30
- constructor(init, args) {
31
- let {min, max, step, ...attr} = args ?? {};
32
- min = min ?? 0;
33
- max = max ?? 100;
34
- step = step ?? 1;
35
-
36
- let attr1 = {min, max, step, ...attr};
37
- super(init, attr1);
38
- }
39
- }
40
-
41
- class Toggle extends Variable {
42
- constructor(init, args) {
43
- init = init ?? true;
44
- super(init, args);
45
- }
46
- }
47
-
48
- class List extends Variable {
49
- constructor(init, args) {
50
- let {choices, ...attr} = args ?? {};
51
- choices = choices ?? {};
52
-
53
- if (is_array(choices)) {
54
- choices = Object.fromEntries(choices.map(v => [v, v]));
55
- }
56
-
57
- let attr1 = {choices, ...attr};
58
- super(init, attr1);
59
- }
60
- }
61
-
62
- /**
63
- ** Animation
64
- **/
65
-
66
- class Transition {
67
- constructor(args) {
68
- let {tlim} = args ?? {};
69
- this.tlim = tlim ?? [null, null];
70
- }
71
-
72
- frac(t, tlimf) {
73
- let [t0, t1] = this.tlim;
74
- let [t0f, t1f] = tlimf;
75
- t0 = t0 ?? t0f;
76
- t1 = t1 ?? t1f;
77
- let f = (t - t0) / (t1 - t0);
78
- return max(0, min(1, f));
79
- }
80
- }
81
-
82
- class Continuous extends Transition {
83
- constructor(lim, args) {
84
- super(args);
85
- this.lim = lim;
86
- }
87
-
88
- value(t, tlimf) {
89
- let f = this.frac(t, tlimf);
90
- let [lo, hi] = this.lim;
91
- return lo + (hi - lo) * f;
92
- }
93
- }
94
-
95
- class Discrete extends Transition {
96
- constructor(vals, args) {
97
- super(args);
98
- this.vals = vals;
99
- }
100
-
101
- value(t, tlimf) {
102
- let f = this.frac(t, tlimf);
103
- let i0 = floor(f * this.vals.length);
104
- let i = min(i0, this.vals.length - 1);
105
- return this.vals[i];
106
- }
107
- }
108
-
109
- class Animation {
110
- constructor(vars, func, args) {
111
- let {tlim, N} = args ?? {};
112
- tlim = tlim ?? [0, 1];
113
- N = N ?? 10;
114
-
115
- // total frames
116
- let [t0f, t1f] = tlim;
117
- let time = linspace(t0f, t1f, N);
118
-
119
- // animation state
120
- this.frames = time.map(t => {
121
- let vals = map_object(vars, v => v.value(t, tlim));
122
- return func(vals);
123
- });
124
- }
125
-
126
- frame(i) {
127
- let frame = this.frames[i];
128
- return renderElem(frame);
129
- }
130
-
131
- *animate() {
132
- for (let i = 0; i < this.frames.length; i++) {
133
- yield this.frame(i);
134
- }
135
- }
136
- }
package/src/render.js DELETED
@@ -1,146 +0,0 @@
1
- // png rendering
2
-
3
- import { CONSTANTS as C } from './defaults.js'
4
- import { getFontPaths } from './text.js'
5
- import { ensure_vector } from './utils.js'
6
-
7
- //
8
- // constants
9
- //
10
-
11
- const DEFAULT_SIZE = [500, 500]
12
-
13
- //
14
- // node render
15
- //
16
-
17
- class NodeRender {
18
- async init() {
19
- const { sans, mono, moji } = await getFontPaths()
20
- this.fonts = [ sans, mono, moji ]
21
- try {
22
- const { Resvg } = await import(/* @vite-ignore */ '@resvg/resvg-js')
23
- this.Resvg = Resvg
24
- } catch {
25
- this.Resvg = null
26
- }
27
- }
28
-
29
- renderPng(svg, { size = DEFAULT_SIZE, background = 'white' }) {
30
- const [ width, height ] = ensure_vector(size, 2)
31
-
32
- const fontsArgs = {
33
- fontFiles: this.fonts,
34
- loadSystemFonts: false,
35
- defaultFontFamily: C.sans,
36
- }
37
-
38
- const opts = {
39
- background,
40
- fitTo: {
41
- mode: 'width',
42
- value: width,
43
- },
44
- font: fontsArgs,
45
- }
46
-
47
- const resvg = new this.Resvg(svg, opts)
48
- const data = resvg.render()
49
- return data.asPng()
50
- }
51
- }
52
-
53
- //
54
- // browser render
55
- //
56
-
57
- function loadImage(url) {
58
- return new Promise((resolve, reject) => {
59
- const img = new Image()
60
- img.onload = () => resolve(img)
61
- img.onerror = () => reject(new Error('Failed to load image'))
62
- img.src = url
63
- })
64
- }
65
-
66
- function canvasToBlob(canvas) {
67
- return new Promise((resolve, reject) => {
68
- canvas.toBlob((blob) => {
69
- if (blob) {
70
- resolve(blob)
71
- } else {
72
- reject(new Error('Failed to convert canvas to blob'))
73
- }
74
- })
75
- })
76
- }
77
-
78
- class BrowserRender {
79
- async init() {
80
- const { canvas, ctx } = this.makeCanvas()
81
- this.canvas = canvas
82
- this.ctx = ctx
83
- }
84
-
85
- makeCanvas(size = DEFAULT_SIZE, { dpr: dpr0 = null } = {}) {
86
- const dpr = dpr0 ?? window.devicePixelRatio ?? 1
87
- const [ width, height ] = size
88
-
89
- // make canvas and ensize
90
- const canvas = document.createElement('canvas')
91
- canvas.width = Math.round(width * dpr)
92
- canvas.height = Math.round(height * dpr)
93
- canvas.style.width = `${Math.round(width)}px`
94
- canvas.style.height = `${Math.round(height)}px`
95
-
96
- // make context and scale
97
- const ctx = canvas.getContext('2d')
98
- ctx.scale(dpr, dpr)
99
-
100
- // return canvas and context
101
- return { canvas, ctx }
102
- }
103
-
104
- async renderPng(svg, { size = DEFAULT_SIZE, background = 'white' } = {}) {
105
- const [ width, height ] = size
106
-
107
- // make canvas and context
108
- const { canvas, ctx } = this.makeCanvas(size)
109
- const { canvas: exportCanvas, ctx: exportCtx } = this.makeCanvas(size, { dpr: 1 })
110
-
111
- // load svg image
112
- const svgBlob = new Blob([ svg ], { type: 'image/svg+xml' })
113
- const svgUrl = URL.createObjectURL(svgBlob)
114
- const svgImg = await loadImage(svgUrl)
115
-
116
- // fill background
117
- if (background != null) {
118
- ctx.fillStyle = background
119
- ctx.fillRect(0, 0, width, height)
120
- }
121
-
122
- // draw svg and export canvas
123
- ctx.drawImage(svgImg, 0, 0, width, height)
124
- exportCtx.drawImage(canvas, 0, 0, width, height)
125
-
126
- // convert export canvas to blob
127
- const pngBlob = await canvasToBlob(exportCanvas)
128
- URL.revokeObjectURL(svgUrl)
129
-
130
- // return png blob
131
- return pngBlob
132
- }
133
- }
134
-
135
- //
136
- // exports
137
- //
138
-
139
- // set up renderer
140
- const render = (typeof window == 'undefined') ? new NodeRender() : new BrowserRender()
141
- await render.init()
142
- function renderPng(svg, args) {
143
- return render.renderPng(svg, args)
144
- }
145
-
146
- export { renderPng, NodeRender, BrowserRender }