gum-jsx 1.6.0 → 1.6.2

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 (49) hide show
  1. package/docs/code/Arc.jsx +3 -3
  2. package/docs/code/Axis.jsx +1 -1
  3. package/docs/code/Element.jsx +5 -3
  4. package/docs/code/Line.jsx +8 -13
  5. package/docs/code/Math.jsx +9 -13
  6. package/docs/code/RoundedLine.jsx +7 -11
  7. package/docs/code/Shape.jsx +8 -14
  8. package/docs/code/Spline.jsx +5 -3
  9. package/docs/text/Arrays.md +1 -1
  10. package/docs/text/Graph.md +6 -5
  11. package/docs/text/Group.md +6 -1
  12. package/docs/text/Math.md +1 -1
  13. package/docs/text/Plot.md +12 -0
  14. package/docs/text/Rect.md +1 -1
  15. package/docs/text/Stack.md +1 -1
  16. package/docs/text/Text.md +3 -3
  17. package/gala/code/pendulum_physics.jsx +3 -3
  18. package/gala/code/spline_star.jsx +2 -2
  19. package/package.json +2 -2
  20. package/prompt/intro.md +2 -3
  21. package/scripts/skill.ts +7 -12
  22. package/skills/gum-jsx/SKILL.md +13 -7
  23. package/skills/gum-jsx/references/gala/pendulum_physics.md +3 -3
  24. package/skills/gum-jsx/references/gala/spline_star.md +2 -2
  25. package/skills/gum-jsx/references/geometry.md +31 -44
  26. package/skills/gum-jsx/references/layout.md +1 -1
  27. package/skills/gum-jsx/references/plotting.md +19 -6
  28. package/skills/gum-jsx/references/text.md +3 -3
  29. package/skills/gum-jsx/references/utilities.md +11 -15
  30. package/src/elems/core.ts +9 -8
  31. package/src/elems/geometry.ts +2 -2
  32. package/src/elems/layout.ts +1 -2
  33. package/src/elems/plot.ts +2 -2
  34. package/src/elems/text.ts +5 -9
  35. package/src/fonts/IBMPlexMono-Bold.ttf +0 -0
  36. package/src/fonts/IBMPlexMono-Light.ttf +0 -0
  37. package/src/fonts/IBMPlexMono-Regular.ttf +0 -0
  38. package/src/fonts/IBMPlexSans-Bold.ttf +0 -0
  39. package/src/fonts/IBMPlexSans-Light.ttf +0 -0
  40. package/src/fonts/IBMPlexSans-Regular.ttf +0 -0
  41. package/src/fonts/fonts.ts +74 -16
  42. package/src/gum.ts +3 -3
  43. package/src/lib/const.ts +6 -3
  44. package/src/lib/text.ts +75 -14
  45. package/src/lib/utils.ts +7 -17
  46. package/src/meta.ts +5 -5
  47. package/src/render.ts +26 -7
  48. package/scripts/claude.ts +0 -25
  49. package/src/fonts/IBMPlexSans-Variable.ttf +0 -0
package/docs/code/Arc.jsx CHANGED
@@ -1,5 +1,5 @@
1
1
  // elliptical and circular arcs using start and end angles
2
2
  <Group>
3
- <Arc pos={[0.32, 0.5]} size={[0.44, 0.32]} start={-45} end={210} stroke={blue} stroke-width={2} />
4
- <Arc pos={[0.72, 0.5]} size={0.32} start={90} end={-150} stroke={red} stroke-width={2} />
5
- </Group>
3
+ <Arc pos={[0.3, 0.5]} size={[0.4, 0.3]} start={-45} end={210} stroke={blue} stroke-width={2} />
4
+ <Arc pos={[0.7, 0.5]} size={0.3} start={90} end={-150} stroke={red} stroke-width={2} />
5
+ </Group>
@@ -2,5 +2,5 @@
2
2
  const emoji = ['🗻', '🚀', '🐳', '🍉', '🍩']
3
3
  const ticks = zip(linspace(0, 1, emoji.length), emoji)
4
4
  return <Box padding={[0.5, 1]}>
5
- <HAxis aspect={10} ticks={ticks} tick-side="outer" label-size={1} />
5
+ <HAxis aspect={10} ticks={ticks} tick-side="outer" label-size={1} label-offset={0.25} />
6
6
  </Box>
@@ -1,3 +1,5 @@
1
- // create a custom triangle element called `Tri` and use it to create a triangle with a gray fill
2
- const Tri = ({ pos0, pos1, pos2, ...attr }) => <Shape {...attr} points={[pos0, pos1, pos2]} />
3
- return <Tri pos0={[0.5, 0.1]} pos1={[0.9, 0.9]} pos2={[0.1, 0.9]} fill={gray} />
1
+ // draw a 2x1 rectangle in red with a blue square embedded within it
2
+ <Group aspect={2}>
3
+ <Rect stroke={red} />
4
+ <Square stroke={blue} />
5
+ </Group>
@@ -1,13 +1,8 @@
1
- // draw a diagonal line in blue and a cup shaped line in red
2
- <Group>
3
- <Line stroke={blue} points={[
4
- [0.2, 0.2],
5
- [0.8, 0.8],
6
- ]} />
7
- <Line stroke={red} points={[
8
- [0.3, 0.3],
9
- [0.3, 0.7],
10
- [0.7, 0.7],
11
- [0.7, 0.3],
12
- ]} />
13
- </Group>
1
+ // draw a piecewise line spiraling outwards (with dots at vertices)
2
+ const spiral = linspace(0, 5, 25).map(t => polar(360 * t, t/5))
3
+ return <Box margin>
4
+ <Graph coord={[-1, -1, 1, 1]}>
5
+ <Line points={spiral} />
6
+ <Points points={spiral} point-size={0.03} />
7
+ </Graph>
8
+ </Box>
@@ -1,14 +1,10 @@
1
- // use polar to place points around a circle
2
- const center = [0.5, 0.5]
3
- const ring = range(10).map(i => {
4
- const radius = i % 2 == 0 ? 0.32 : 0.16
5
- return polar([radius, -90 + 36 * i], center)
1
+ // embed a five point star in a circle. place red dots at its vertices.
2
+ const verts = linspace(0, 360, 10, false).map((t, i) => {
3
+ const radius = i % 2 == 0 ? 1 : 0.5
4
+ return polar(90 + t, radius)
6
5
  })
7
- const spokes = range(5).map(i => polar([0.32, -90 + 72 * i], center))
8
-
9
- return <Group aspect={1}>
10
- <Circle pos={center} size={0.64} stroke={darkgray} />
11
- <Shape points={ring} stroke={blue} stroke-width={2} />
12
- {spokes.map(pos => <Line points={[center, pos]} stroke={red} stroke-width={1.5} />)}
13
- {ring.map(pos => <Dot pos={pos} size={0.03} fill={blue} />)}
14
- </Group>
6
+ return <Graph aspect coord={[-1, -1, 1, 1]}>
7
+ <Circle pos={[0, 0]} size={2} stroke={darkgray} />
8
+ <Shape points={verts} stroke={blue} stroke-width={2} />
9
+ {verts.map(pos => <Dot pos={pos} size={0.05} fill={red} />)}
10
+ </Graph>
@@ -1,15 +1,11 @@
1
1
  // a city-block route in blue with rounded corners, with the underlying
2
2
  // vertices marked as black dots to show how the corners are rounded
3
3
  const points = [
4
- [0.10, 0.20],
5
- [0.40, 0.20],
6
- [0.40, 0.80],
7
- [0.70, 0.80],
8
- [0.70, 0.50],
9
- [0.90, 0.50],
4
+ [-0.8, 0.6], [-0.2, 0.6], [-0.2, -0.6],
5
+ [ 0.4, -0.6], [ 0.4, 0.0], [ 0.8, 0.0],
10
6
  ]
11
- return <Frame margin>
12
- <Line opacity={0.3} points={points} />
13
- <RoundedLine stroke={blue} stroke-width={2} radius={0.08} points={points} />
14
- <Points point-size={0.015} points={points} />
15
- </Frame>
7
+ return <Graph aspect padding>
8
+ <Line points={points} opacity={0.3} />
9
+ <RoundedLine points={points} stroke={blue} stroke-width={2} radius={0.1} />
10
+ <Points points={points} point-size={0.03} />
11
+ </Graph>
@@ -1,14 +1,8 @@
1
- // draw a blue triangle with a semi-transparent green square overlaid on top
2
- <Group>
3
- <Shape fill={blue} stroke={none} points={[
4
- [0.5, 0.2],
5
- [0.8, 0.8],
6
- [0.2, 0.8]
7
- ]} />
8
- <Shape fill={green} stroke={none} opacity={0.5} points={[
9
- [0.3, 0.3],
10
- [0.7, 0.3],
11
- [0.7, 0.7],
12
- [0.3, 0.7]
13
- ]} />
14
- </Group>
1
+ // draw a stop sign
2
+ const hexagon = linspace(0, 360, 8, false).map(t => polar(t))
3
+ return <Box fill="#bbb" rounded padding margin>
4
+ <Graph xlim={[-1, 1]} ylim={[-1, 1]} aspect={1}>
5
+ <Shape points={hexagon} fill="#CC0202" stroke={white} stroke_width={20} spin={180/8} />
6
+ <Text pos={[0, 0]} xsize={1.5} color={white} font-weight={bold}>STOP</Text>
7
+ </Graph>
8
+ </Box>
@@ -7,7 +7,9 @@ const points = [
7
7
  [0.50, 0.50],
8
8
  ]
9
9
  return <Frame rounded margin>
10
- <Spline closed stroke={blue} fill={gray} points={points} />
11
- <Shape stroke={red} points={points} />
12
- <Points point-size={0.02} points={points} />
10
+ <Group>
11
+ <Spline closed stroke={blue} fill={gray} points={points} />
12
+ <Shape stroke={red} points={points} />
13
+ <Points point-size={0.02} points={points} />
14
+ </Group>
13
15
  </Frame>
@@ -16,7 +16,7 @@ There are a number of functions designed to make working with arrays easier. The
16
16
  - `norm(arr, degree=1)` — compute the `degree`-norm of array `arr`
17
17
  - `normalize(arr, degree=1)` — normalize array `arr` to have `degree`-norm one
18
18
  - `range(i0, i1, step=1)` — generate an array of evenly spaced values from `i0` to `i1` with spacing `step`
19
- - `linspace(x0, x1, n=50)` — generate an array of `n` evenly spaced values between `x0` and `x1`
19
+ - `linspace(x0, x1, n, end=false)` — generate an array of `n` evenly spaced values between `x0` and `x1` (including `x1` if `end` is true)
20
20
  - `enumerate(arr)` — pair each element of array `arr` with its index
21
21
  - `repeat(x, n)` — repeat array `x` a total of `n` times
22
22
  - `meshgrid(x, y)` — create a mesh grid from arrays `x` and `y`
@@ -2,11 +2,12 @@
2
2
 
3
3
  *Inherits*: [Group](/docs/Group) > [Element](/docs/Element)
4
4
 
5
- This is the core graphing functionality used in [Plot](/docs/Plot) without the axes and labels. By default, the coordinate system is automatically inferred from the limits of child elements. This can be overridden with custom `xlim`/`ylim` specifications. The Elements that are passed to **Graph** can express their position and size information in this new coordinate system.
5
+ This is the core graphing functionality used in [Plot](/docs/Plot) without the axes and labels. By default, the coordinate system is automatically inferred from the limits of child elements. This can be overridden with custom `xlim`/`ylim`/`coord` specifications. The Elements that are passed to **Graph** can express their position and size information in this new coordinate system.
6
6
 
7
- You'll often want to use this (directly or indirectly) to display mathematical curves, as they might otherwise come out looking upside down relative to what you expect (as higher y-values mean "down" in raw SVG).
7
+ Unlike [Group](/docs/Group), **Graph** will automatically pass the given `coord` to all children, so they can express their position and size information in this new coordinate system. This is very useful for elements like [Line](/docs/Line) or [Points](/docs/Points), which evaluate their `points` values based on their own coordinate system, not that of the container.
8
+
9
+ You'll often want to use **Graph** (directly or indirectly) to display mathematical curves, as they might otherwise come out looking upside down relative to what you expect (as higher y-values mean "down" in raw SVG).
8
10
 
9
11
  Parameters:
10
- - `xlim`/`ylim` = `[0, 1]` — the range over which to graph
11
- - `padding` = `0` — limit padding to add when auto-detected from `elems`
12
- - `coord` = `'auto'` — the coordinate system to use for the graph (overrides `xlim`/`ylim`)
12
+ - `xlim`/`ylim`/`coord` = `'auto'` — the coordinate system to use for the graph
13
+ - `padding` = `0` — proportional padding to add when limits are auto-detected from children
@@ -4,10 +4,15 @@
4
4
 
5
5
  This is the main container class that all compound elements are derived from. It accepts a list of child elements and attempts to place them according to their declared properties. Child placement positions are specified in the group's internal coordinates (`coord`), which defaults to the unit square. The coordinate space is specified in `[left, top, right, bottom]` format.
6
6
 
7
- The child's `aspect` is an important determinant of its placement. When it has a `null` aspect, it will fit exactly in the given `rect`. However, when it does have an aspect, it needs to be adjusted in the case that the given `rect` does not have the same aspect. The `expand` and `align` specification arguments govern how this adjustment is made.
7
+ The child's `aspect` is an important determinant of its placement. When the child does not have an aspect, it will fit exactly in the given `rect`. When it does have an aspect, it will be made as large as possible while still fitting in the given `rect`. The `align` argument governs the exact placement in this case, while the `expand` flag makes it as small as possible while still covering the given `rect`.
8
+
9
+ One common pitfall: using `coord` will affect that placement of child elements. But for graphing elements like [Line](/docs/Line) or [Points](/docs/Points), their `points` values are evaluate based on their own coordinate system, not the containing **Group**'s. You must either give them their own `coord` or use [Graph](/docs/Graph), which automatically propagates the coordinate system to all children.
10
+
11
+ To help with debugging, a `debug` flag can be passed to show stencil lines indicating the childrens' placement. The allocated space is shown in dashed blue, while the realized position (accounting for aspect and alignment) is shown in dashed red.
8
12
 
9
13
  Parameters:
10
14
  - `children` = `[]` — a list of child elements
11
15
  - `aspect` = `null` — the aspect ratio of the group's rectangle (can pass `'auto'` to infer from the children)
12
16
  - `coord` = `[0, 0, 1, 1]` — the internal coordinate space to use for child elements (can pass `'auto'` to contain children's rects)
13
17
  - `clip` = `false` — clip children to the group's rectangle if `true` (or a custom shape if specified)
18
+ - `debug` = `false` — show debug boxes for the children
package/docs/text/Math.md CHANGED
@@ -27,6 +27,6 @@ Here we collect a variety of global mathematical functions and constants. You ca
27
27
  - `round(x)` — the rounding function
28
28
  - `clamp(x, lim=[0, 1])` — clamp `x` to the range `lim`
29
29
  - `rescale(x, lim=[0, 1])` — linearly rescale `x` to the range `lim`
30
- - `polar([radius, angle], center=[0, 0])` — convert polar coordinates to a 2D point
30
+ - `polar(angle, radius=1, center=[0, 0])` — convert polar coordinates (`angle` in degrees, `radius` scalar or size vector) to a 2D point around `center`
31
31
 
32
32
  Angles use gum's usual screen-space convention: `0` points right and `90` points down.
package/docs/text/Plot.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  Uses [Graph](/docs/Graph) to plot one or more elements over the desired limits and frame them with axes. If not specified by `xlim` and `ylim`, the limits of the plot will be computed from the bounding box of the constituent elements. By default, the `aspect` will be the ratio of the range of the `xlim` and `ylim`. See [Axis](/docs/Axis) for more details on how to customize the axes, ticks, and labels.
6
6
 
7
+ By default, the extent of **Plot** only includes the graphing area itself, not the axes, labels, or title. To include these, you can set the `margin` parameter to a non-zero value. However, it many cases it makes more sense to enclose **Plot** in a [Frame](/docs/Frame) or [Box](/docs/Box) element and set the `margin` parameter on that instead. This is useful if you want to add border that exactly encloses the graphing area.
8
+
7
9
  Parameters:
8
10
  - `xlim`/`ylim` = `[0, 1]` — the range over which to graph
9
11
  - `xanchor`/`yanchor` — the value at which to place the respective axis. Note that the `xanchor` is a y-value and vice versa. Defaults to `xmin`/`ymin`
@@ -21,3 +23,13 @@ Subunits:
21
23
  - `grid`/`xgrid`/`ygrid` — the grid lines arrayed under the graph
22
24
  - `label`/`xlabel`/`ylabel` — the axis label elements
23
25
  - `title` — the plot title element
26
+
27
+ Title:
28
+ - `title-size` = `0.075` — the size of the title element
29
+ - `title-offset` = `0.05` — the offset of the title element from the top of the plot
30
+
31
+ Labels:
32
+ - `label-size` = `0.05` — the size of the label elements
33
+ - `label-offset` = `0.125` — the offset of the label elements from the axis
34
+ - `xlabel-size`/`ylabel-size` — the size of the x/y label element (overrides `label-size`)
35
+ - `xlabel-offset`/`ylabel-offset` — the offset of the x/y label element from the axis (overrides `label-offset`)
package/docs/text/Rect.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  This makes a rectangle. Without any arguments it will fill its entire allocated space. Unless otherwise specified, it has a `null` aspect. Use **Square** for a square with a unit aspect.
6
6
 
7
- Specifying a `rounded` argument will round the borders by the same amount for each corner. This can be either a scalar or a pair of scalars corresponding to the x and y radii of the corners. To specify different roundings for each corner, use the **RoundedRect** element.
7
+ Specifying a `rounded` argument will round the borders by the same amount for each corner. This can be either a scalar or a pair of scalars corresponding to the x and y radii of the corners. To specify different roundings for each corner, use the **RoundedRect** element. Rounding is expressed as a fractional value between 0 and 1, where 0 means no rounding and 1 will essentially render an ellipse. Values much greater than 0.1 will start to look a little goofy.
8
8
 
9
9
  Parameters:
10
10
  - `rounded` = `null` — proportional border rounding, accepts either scalar or pair of scalars
@@ -6,7 +6,7 @@ Stack one or more **Element** either vertically or horizontally. There are speci
6
6
 
7
7
  The simplest case is when all children have aspect ratios. In the **HStack** case, the overall aspect ratio of the stack is the sum of the child aspect ratios. Children are allocated space in proportion to their aspect ratios. The **VStack** case is similar, but we need to deal in inverse aspect ratio terms.
8
8
 
9
- When some children lack aspect ratios, they will be allocated any remaining space evenly. Regardless of whether a child has an aspect ratio or not, it can be given a fixed size with the `stack-size` parameter. This is specified as a fraction between 0 and 1, independent of spacing.
9
+ When some children lack aspect ratios, they will be allocated any remaining space evenly. Regardless of whether a child has an aspect ratio or not, it can be given a fixed size with the `stack-size` parameter. This is specified as a fraction between 0 and 1, independent of spacing. Be warned though, if you specify `stack-size` for a child with an aspect ratio, it will be ignored. If you specify `stack-size` for every child, the resulting stack will have no aspect ratio.
10
10
 
11
11
  Whenever possible, the aspect ratio of the overall stack is set so that all elements with defined aspect ratios will reach full width (in the **VStack** case) or full height (in the **HStack** case).
12
12
 
package/docs/text/Text.md CHANGED
@@ -6,11 +6,11 @@ Displays text and other elements. Note that you will typically not set the font
6
6
 
7
7
  If `wrap` is specified, the text will be wrapped to the specified width. In either case, single newlines will be respected, though whitespace will be compressed. There are two wrapper elements related to text:
8
8
 
9
- There are two default fonts that are always provided: `sans = 'IBM Plex Sans'` and `mono ='IBM Plex Mono'`. You can use these global variables anywhere.
10
-
11
9
  - [TextBox](/docs/TextBox) / **TextFrame** can handle text with a border and background
12
10
  - **TextStack** can handle multiple lines of text that are passed in as an array
13
11
 
12
+ There are two default fonts that are always provided: `sans = 'IBM Plex Sans'` and `mono ='IBM Plex Mono'`. There are three availabe font weights: `light = 300`, `regular = 400`, and `bold = 700`. The default weight is `light`. You can use these global variables anywhere.
13
+
14
14
  Parameters:
15
15
  - `children` — the text to display
16
16
  - `wrap` = `null` — the width (in ems) to wrap the text at (if `null`, the text will not be wrapped)
@@ -18,4 +18,4 @@ Parameters:
18
18
  - `justify` = `'left'` — the horizontal justification of the text
19
19
  - `color` = `black` — sets the text color using both stroke and fill (this is the usual way)
20
20
  - `font-family` = `sans` — the font family (for display and size calculations)
21
- - `font-weight` = `100` — the font weight (for display and size calculations)
21
+ - `font-weight` = `300` — the font weight (for display and size calculations)
@@ -18,9 +18,9 @@ const arcRad = 0.25
18
18
  // Computed positions
19
19
  const rodRot = 90 - rodAng
20
20
  const eqnY = pivotY + rodLen
21
- const [ bobX, bobY ] = polar([rodLen, rodRot], [pivotX, pivotY])
22
- const [ midX, midY ] = polar([0.50 * rodLen, rodRot], [pivotX, pivotY])
23
- const [ tenX, tenY ] = polar([0.75 * rodLen, rodRot], [pivotX, pivotY])
21
+ const [ bobX, bobY ] = polar(rodRot, rodLen, [pivotX, pivotY])
22
+ const [ midX, midY ] = polar(rodRot, 0.50 * rodLen, [pivotX, pivotY])
23
+ const [ tenX, tenY ] = polar(rodRot, 0.75 * rodLen, [pivotX, pivotY])
24
24
 
25
25
  return <Box margin={0.06}>
26
26
  <VStack spacing={0.05}>
@@ -8,8 +8,8 @@ const theta0 = linspace(0, 2 * pi, n, false).map(t => t - pi / 2)
8
8
  const theta1 = theta0.map(t => t + pi / n)
9
9
 
10
10
  // get inner/outer point positions
11
- const points0 = theta0.map(t => polar([1, t * r2d]))
12
- const points1 = theta1.map(t => polar([R, t * r2d]))
11
+ const points0 = theta0.map(t => polar(t * r2d))
12
+ const points1 = theta1.map(t => polar(t * r2d, R))
13
13
  const points = zip(points0, points1).flat()
14
14
 
15
15
  // return full spline
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gum-jsx",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Language for vector graphics generation.",
5
5
  "type": "module",
6
6
  "author": "Douglas Hanley",
@@ -41,7 +41,7 @@
41
41
  "scripts": {
42
42
  "cli": "bun ./scripts/gum.ts",
43
43
  "test": "bun ./scripts/test.ts",
44
- "skill": "bun ./scripts/skill.ts && cd skills/gum-jsx && zip -r gum-jsx.skill * && mv gum-jsx.skill ..",
44
+ "skill": "bun ./scripts/skill.ts && cd skills && rm -f gum-jsx.skill && zip -r gum-jsx.skill gum-jsx",
45
45
  "claude": "bun run skill && bun ./scripts/claude.ts"
46
46
  },
47
47
  "bin": {
package/prompt/intro.md CHANGED
@@ -115,11 +115,10 @@ Without specifying `stack-size`, the `Text` element would be quite large (it has
115
115
  Here are the handy array functions provided by the library. All of these mimic the behavior of their counterparts in Python and `numpy`. This can be useful for generating `Element` objects from arrays:
116
116
  ```typescript
117
117
  function zip(...arrs: any[]): any[]
118
- function range(start: number, end: number, step: number): number[]
119
- function linspace(start: number, end: number, num: number): number[]
118
+ function range(ia: number, ib?: number, step?: number = 1): number[]
119
+ function linspace(x0: number, x1: number, num: number, end?: boolean = false): number[]
120
120
  function enumerate(x: any[]): any[]
121
121
  function repeat(x: any, n: number): any[]
122
- function lingrid(xlim: range, ylim: range, N: number): number[][]
123
122
  ```
124
123
 
125
124
  Some of the most commonly used mathematical constants are pre-defined in the global scope:
package/scripts/skill.ts CHANGED
@@ -20,13 +20,6 @@ const { tags: docs_tags, cats, text: docs_text, code: docs_code } = getDocs('doc
20
20
  const { tags: gala_tags, text: gala_text, code: gala_code } = getGala('gala')
21
21
 
22
22
  // make reference pages
23
- const docs_pages = Object.fromEntries(docs_tags.map(tag =>
24
- [ tag, prepareDocsPage(docs_text[tag], docs_code[tag]) ]
25
- ))
26
-
27
- const gala_pages = Object.fromEntries(gala_tags.map(tag =>
28
- [ tag, prepareGalaPage(gala_text[tag], gala_code[tag]) ]
29
- ))
30
23
 
31
24
  // load prompt files
32
25
  const head = readFileSync('prompt/head.md', 'utf8').trim()
@@ -43,11 +36,11 @@ ${intro}
43
36
 
44
37
  ${docs}
45
38
 
46
- ${docs_pages['Element']}
39
+ ${prepareDocsPage(docs_text['Element'], docs_code['Element'], true)}
47
40
 
48
- ${docs_pages['Group']}
41
+ ${prepareDocsPage(docs_text['Group'], docs_code['Group'], true)}
49
42
 
50
- ${docs_pages['Box']}
43
+ ${prepareDocsPage(docs_text['Box'], docs_code['Box'], true)}
51
44
 
52
45
  ${refs}
53
46
 
@@ -62,7 +55,9 @@ writeFileSync(`${output}/SKILL.md`, skill + '\n')
62
55
  mkdirSync(`${output}/references`, { recursive: true })
63
56
  Object.entries(cats).forEach(([c, ps]) => {
64
57
  if (c == 'core') return
65
- const content = ps.map(p => docs_pages[p]).join('\n\n')
58
+ const content = ps.map(p =>
59
+ prepareDocsPage(docs_text[p], docs_code[p], true)
60
+ ).join('\n\n')
66
61
  const entry = `# ${capitalize(c)} Elements\n\n${content}\n`
67
62
  writeFileSync(`${output}/references/${c}.md`, entry)
68
63
  })
@@ -70,6 +65,6 @@ Object.entries(cats).forEach(([c, ps]) => {
70
65
  // write gala pages
71
66
  mkdirSync(`${output}/references/gala`, { recursive: true })
72
67
  gala_tags.forEach(t => {
73
- const entry = gala_pages[t]
68
+ const entry = prepareGalaPage(gala_text[t], gala_code[t])
74
69
  writeFileSync(`${output}/references/gala/${t}.md`, entry)
75
70
  })
@@ -120,11 +120,10 @@ Without specifying `stack-size`, the `Text` element would be quite large (it has
120
120
  Here are the handy array functions provided by the library. All of these mimic the behavior of their counterparts in Python and `numpy`. This can be useful for generating `Element` objects from arrays:
121
121
  ```typescript
122
122
  function zip(...arrs: any[]): any[]
123
- function range(start: number, end: number, step: number): number[]
124
- function linspace(start: number, end: number, num: number): number[]
123
+ function range(ia: number, ib?: number, step?: number = 1): number[]
124
+ function linspace(x0: number, x1: number, num: number, end?: boolean = false): number[]
125
125
  function enumerate(x: any[]): any[]
126
126
  function repeat(x: any, n: number): any[]
127
- function lingrid(xlim: range, ylim: range, N: number): number[][]
128
127
  ```
129
128
 
130
129
  Some of the most commonly used mathematical constants are pre-defined in the global scope:
@@ -188,12 +187,14 @@ Methods:
188
187
 
189
188
  **Example**
190
189
 
191
- Prompt: create a custom triangle element called `Tri` and use it to create a triangle with a gray fill
190
+ Prompt: draw a 2x1 rectangle in red with a blue square embedded within it
192
191
 
193
192
  Generated code:
194
193
  ```jsx
195
- const Tri = ({ pos0, pos1, pos2, ...attr }) => <Shape {...attr} points={[pos0, pos1, pos2]} />
196
- return <Tri pos0={[0.5, 0.1]} pos1={[0.9, 0.9]} pos2={[0.1, 0.9]} fill={gray} />
194
+ <Group aspect={2}>
195
+ <Rect stroke={red} />
196
+ <Square stroke={blue} />
197
+ </Group>
197
198
  ```
198
199
 
199
200
  ## Group
@@ -202,13 +203,18 @@ return <Tri pos0={[0.5, 0.1]} pos1={[0.9, 0.9]} pos2={[0.1, 0.9]} fill={gray} />
202
203
 
203
204
  This is the main container class that all compound elements are derived from. It accepts a list of child elements and attempts to place them according to their declared properties. Child placement positions are specified in the group's internal coordinates (`coord`), which defaults to the unit square. The coordinate space is specified in `[left, top, right, bottom]` format.
204
205
 
205
- The child's `aspect` is an important determinant of its placement. When it has a `null` aspect, it will fit exactly in the given `rect`. However, when it does have an aspect, it needs to be adjusted in the case that the given `rect` does not have the same aspect. The `expand` and `align` specification arguments govern how this adjustment is made.
206
+ The child's `aspect` is an important determinant of its placement. When the child does not have an aspect, it will fit exactly in the given `rect`. When it does have an aspect, it will be made as large as possible while still fitting in the given `rect`. The `align` argument governs the exact placement in this case, while the `expand` flag makes it as small as possible while still covering the given `rect`.
207
+
208
+ One common pitfall: using `coord` will affect that placement of child elements. But for graphing elements like **Line** or **Points**, their `points` values are evaluate based on their own coordinate system, not the containing **Group**'s. You must either give them their own `coord` or use **Graph**, which automatically propagates the coordinate system to all children.
209
+
210
+ To help with debugging, a `debug` flag can be passed to show stencil lines indicating the childrens' placement. The allocated space is shown in dashed blue, while the realized position (accounting for aspect and alignment) is shown in dashed red.
206
211
 
207
212
  Parameters:
208
213
  - `children` = `[]` — a list of child elements
209
214
  - `aspect` = `null` — the aspect ratio of the group's rectangle (can pass `'auto'` to infer from the children)
210
215
  - `coord` = `[0, 0, 1, 1]` — the internal coordinate space to use for child elements (can pass `'auto'` to contain children's rects)
211
216
  - `clip` = `false` — clip children to the group's rectangle if `true` (or a custom shape if specified)
217
+ - `debug` = `false` — show debug boxes for the children
212
218
 
213
219
  **Example**
214
220
 
@@ -29,9 +29,9 @@ const arcRad = 0.25
29
29
  // Computed positions
30
30
  const rodRot = 90 - rodAng
31
31
  const eqnY = pivotY + rodLen
32
- const [ bobX, bobY ] = polar([rodLen, rodRot], [pivotX, pivotY])
33
- const [ midX, midY ] = polar([0.50 * rodLen, rodRot], [pivotX, pivotY])
34
- const [ tenX, tenY ] = polar([0.75 * rodLen, rodRot], [pivotX, pivotY])
32
+ const [ bobX, bobY ] = polar(rodRot, rodLen, [pivotX, pivotY])
33
+ const [ midX, midY ] = polar(rodRot, 0.50 * rodLen, [pivotX, pivotY])
34
+ const [ tenX, tenY ] = polar(rodRot, 0.75 * rodLen, [pivotX, pivotY])
35
35
 
36
36
  return <Box margin={0.06}>
37
37
  <VStack spacing={0.05}>
@@ -19,8 +19,8 @@ const theta0 = linspace(0, 2 * pi, n, false).map(t => t - pi / 2)
19
19
  const theta1 = theta0.map(t => t + pi / n)
20
20
 
21
21
  // get inner/outer point positions
22
- const points0 = theta0.map(t => polar([1, t * r2d]))
23
- const points1 = theta1.map(t => polar([R, t * r2d]))
22
+ const points0 = theta0.map(t => polar(t * r2d))
23
+ const points1 = theta1.map(t => polar(t * r2d, R))
24
24
  const points = zip(points0, points1).flat()
25
25
 
26
26
  // return full spline
@@ -6,7 +6,7 @@
6
6
 
7
7
  This makes a rectangle. Without any arguments it will fill its entire allocated space. Unless otherwise specified, it has a `null` aspect. Use **Square** for a square with a unit aspect.
8
8
 
9
- Specifying a `rounded` argument will round the borders by the same amount for each corner. This can be either a scalar or a pair of scalars corresponding to the x and y radii of the corners. To specify different roundings for each corner, use the **RoundedRect** element.
9
+ Specifying a `rounded` argument will round the borders by the same amount for each corner. This can be either a scalar or a pair of scalars corresponding to the x and y radii of the corners. To specify different roundings for each corner, use the **RoundedRect** element. Rounding is expressed as a fractional value between 0 and 1, where 0 means no rounding and 1 will essentially render an ellipse. Values much greater than 0.1 will start to look a little goofy.
10
10
 
11
11
  Parameters:
12
12
  - `rounded` = `null` — proportional border rounding, accepts either scalar or pair of scalars
@@ -57,8 +57,8 @@ Prompt: elliptical and circular arcs using start and end angles
57
57
  Generated code:
58
58
  ```jsx
59
59
  <Group>
60
- <Arc pos={[0.32, 0.5]} size={[0.44, 0.32]} start={-45} end={210} stroke={blue} stroke-width={2} />
61
- <Arc pos={[0.72, 0.5]} size={0.32} start={90} end={-150} stroke={red} stroke-width={2} />
60
+ <Arc pos={[0.3, 0.5]} size={[0.4, 0.3]} start={-45} end={210} stroke={blue} stroke-width={2} />
61
+ <Arc pos={[0.7, 0.5]} size={0.3} start={90} end={-150} stroke={red} stroke-width={2} />
62
62
  </Group>
63
63
  ```
64
64
 
@@ -77,22 +77,17 @@ Parameters:
77
77
 
78
78
  **Example**
79
79
 
80
- Prompt: draw a diagonal line in blue and a cup shaped line in red
80
+ Prompt: draw a piecewise line spiraling outwards (with dots at vertices)
81
81
 
82
82
  Generated code:
83
83
  ```jsx
84
- <Group>
85
- <Line stroke={blue} points={[
86
- [0.2, 0.2],
87
- [0.8, 0.8],
88
- ]} />
89
- <Line stroke={red} points={[
90
- [0.3, 0.3],
91
- [0.3, 0.7],
92
- [0.7, 0.7],
93
- [0.7, 0.3],
94
- ]} />
95
- </Group>
84
+ const spiral = linspace(0, 5, 25).map(t => polar(360 * t, t/5))
85
+ return <Box margin>
86
+ <Graph coord={[-1, -1, 1, 1]}>
87
+ <Line points={spiral} />
88
+ <Points points={spiral} point-size={0.03} />
89
+ </Graph>
90
+ </Box>
96
91
  ```
97
92
 
98
93
  ## Shape
@@ -108,23 +103,17 @@ Parameters:
108
103
 
109
104
  **Example**
110
105
 
111
- Prompt: draw a blue triangle with a semi-transparent green square overlaid on top
106
+ Prompt: draw a stop sign
112
107
 
113
108
  Generated code:
114
109
  ```jsx
115
- <Group>
116
- <Shape fill={blue} stroke={none} points={[
117
- [0.5, 0.2],
118
- [0.8, 0.8],
119
- [0.2, 0.8]
120
- ]} />
121
- <Shape fill={green} stroke={none} opacity={0.5} points={[
122
- [0.3, 0.3],
123
- [0.7, 0.3],
124
- [0.7, 0.7],
125
- [0.3, 0.7]
126
- ]} />
127
- </Group>
110
+ const hexagon = linspace(0, 360, 8, false).map(t => polar(t))
111
+ return <Box fill="#bbb" rounded padding margin>
112
+ <Graph xlim={[-1, 1]} ylim={[-1, 1]} aspect={1}>
113
+ <Shape points={hexagon} fill="#CC0202" stroke={white} stroke_width={20} spin={180/8} />
114
+ <Text pos={[0, 0]} xsize={1.5} color={white} font-weight={bold}>STOP</Text>
115
+ </Graph>
116
+ </Box>
128
117
  ```
129
118
 
130
119
  ## Fill
@@ -187,9 +176,11 @@ const points = [
187
176
  [0.50, 0.50],
188
177
  ]
189
178
  return <Frame rounded margin>
190
- <Spline closed stroke={blue} fill={gray} points={points} />
191
- <Shape stroke={red} points={points} />
192
- <Points point-size={0.02} points={points} />
179
+ <Group>
180
+ <Spline closed stroke={blue} fill={gray} points={points} />
181
+ <Shape stroke={red} points={points} />
182
+ <Points point-size={0.02} points={points} />
183
+ </Group>
193
184
  </Frame>
194
185
  ```
195
186
 
@@ -253,16 +244,12 @@ Generated code:
253
244
  ```jsx
254
245
  // vertices marked as black dots to show how the corners are rounded
255
246
  const points = [
256
- [0.10, 0.20],
257
- [0.40, 0.20],
258
- [0.40, 0.80],
259
- [0.70, 0.80],
260
- [0.70, 0.50],
261
- [0.90, 0.50],
247
+ [-0.8, 0.6], [-0.2, 0.6], [-0.2, -0.6],
248
+ [ 0.4, -0.6], [ 0.4, 0.0], [ 0.8, 0.0],
262
249
  ]
263
- return <Frame margin>
264
- <Line opacity={0.3} points={points} />
265
- <RoundedLine stroke={blue} stroke-width={2} radius={0.08} points={points} />
266
- <Points point-size={0.015} points={points} />
267
- </Frame>
250
+ return <Graph aspect padding>
251
+ <Line points={points} opacity={0.3} />
252
+ <RoundedLine points={points} stroke={blue} stroke-width={2} radius={0.1} />
253
+ <Points points={points} point-size={0.03} />
254
+ </Graph>
268
255
  ```
@@ -44,7 +44,7 @@ Stack one or more **Element** either vertically or horizontally. There are speci
44
44
 
45
45
  The simplest case is when all children have aspect ratios. In the **HStack** case, the overall aspect ratio of the stack is the sum of the child aspect ratios. Children are allocated space in proportion to their aspect ratios. The **VStack** case is similar, but we need to deal in inverse aspect ratio terms.
46
46
 
47
- When some children lack aspect ratios, they will be allocated any remaining space evenly. Regardless of whether a child has an aspect ratio or not, it can be given a fixed size with the `stack-size` parameter. This is specified as a fraction between 0 and 1, independent of spacing.
47
+ When some children lack aspect ratios, they will be allocated any remaining space evenly. Regardless of whether a child has an aspect ratio or not, it can be given a fixed size with the `stack-size` parameter. This is specified as a fraction between 0 and 1, independent of spacing. Be warned though, if you specify `stack-size` for a child with an aspect ratio, it will be ignored. If you specify `stack-size` for every child, the resulting stack will have no aspect ratio.
48
48
 
49
49
  Whenever possible, the aspect ratio of the overall stack is set so that all elements with defined aspect ratios will reach full width (in the **VStack** case) or full height (in the **HStack** case).
50
50