@mylasertools/kerfscript 0.2.1 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,13 +1,64 @@
1
1
  # Changelog
2
2
 
3
- ## 0.2.1
3
+ ## 0.3.0 (2026-10-30)
4
4
 
5
+ - `kerfscript.wasm`: a WASI module that runs KerfScript from Python, Rust,
6
+ Go, C# and other languages. It takes a JSON request on standard input and
7
+ writes SVG, DXF, parts and warnings as JSON. `"command": "licenses"`
8
+ returns the licence texts of the module and of the JavaScript engine built
9
+ into it. Build it with `npm run wasm`; see
10
+ [docs/webassembly.md](docs/webassembly.md).
11
+ - A desktop app for Windows, macOS and Linux: the playground with `.kerf`
12
+ files on disk (Open, Save, Save as, double-click to open; Save as adds
13
+ `.kerf` to a name without it and asks before replacing a file of that
14
+ name), exports through a save dialog, `use`/`include` libraries read from
15
+ the design's folder and the folders below it (`.kerf` files only), and an
16
+ About window with the source link and the licences of everything in the
17
+ app. Saving writes the whole file before it replaces the old one, and the
18
+ app asks about unsaved changes before it closes, also on ⌘Q.
19
+ Installers: Windows setup and MSI, a universal macOS dmg, and an AppImage,
20
+ `.deb` and `.rpm` for Linux x64 and arm64; Flathub, winget, Chocolatey and
21
+ AUR manifests. See [docs/desktop.md](docs/desktop.md).
22
+ - Open in LightBurn in the desktop app: sends the cut file (or one sheet) to
23
+ LightBurn 2.2 or later through its REST API. KerfScript asks LightBurn only
24
+ for permission to send files, and talks to it directly, past any proxy
25
+ set on the computer.
26
+ - Chocolatey and MacPorts manifests for the command line.
27
+ - The playground does not load a shared link, draft or file longer than
28
+ 1,000,000 characters, and stops unpacking a compressed link at that size.
29
+ Any code the editor runs fits in a link, whatever its script, and a link
30
+ that cannot be opened says why in the console. Code over the limit is not
31
+ coloured or read for settings, and Copy link refuses it. A link to an
32
+ empty design opens it, instead of the draft.
33
+ - Settings for a list, such as `size = [10, 20]`, keep numbers changed in the
34
+ code since the settings were drawn, instead of putting the old ones back.
35
+ An empty or half-typed number box leaves the code as it is.
36
+ - The playground renders the newest code after fast edits to a slow design,
37
+ instead of stopping with "Stopped after 15 seconds", and keeps working
38
+ when the number of sheets drops to one in the laser file view. Edits after
39
+ opening a shared link or moving a slider on an example are kept on reload.
40
+ - The website's licence page, and `assets/THIRD-PARTY-NOTICES.md` next to
41
+ `kerfscript.js`, have the notices of the libraries bundled in its
42
+ JavaScript.
43
+ - Standalone `kerf` binaries for Windows, macOS and Linux that need no
44
+ Node.js, with the licences of the Deno runtime built into them,
45
+ `kerfscript-cli` `.deb` and `.rpm` packages (which install next to the
46
+ desktop app), a Docker image, a snap, and Homebrew,
47
+ Scoop, winget and AUR manifests, all built by the release workflow with
48
+ SHA-256, SHA-512 and GPG signatures. Nightly builds go to a rolling
49
+ `nightly` prerelease. See RELEASING.md.
50
+ - A `kerf(1)` man page and a `text/x-kerfscript` MIME type for `*.kerf` files
51
+ in the Linux packages.
52
+ - The command line and `kerfscript.wasm` have a font built in, Atkinson
53
+ Hyperlegible as `Atkinson` (the playground's font), so `text()` works
54
+ without `--font`. The JavaScript library still takes fonts from the host.
5
55
  - Memory limits, so a program cannot make a browser tab run out of memory:
6
56
  part copies, circle segments and polygon corners, list and string sizes,
7
57
  all list items together, `text()` length, shape points, exported points
8
58
  and sheets. Each stops the program with an error that says what to change.
9
59
  Defaults are in `DEFAULT_LIMITS` and the `limits` render option changes
10
- them. Every example stays well under them.
60
+ them. Every example stays well under them. Ranges in loops and built-in
61
+ functions stop at `maxListLength` items, not at a fixed million.
11
62
  - A list or string that grows past what JavaScript can hold now gives a
12
63
  KerfScript error instead of a `RangeError`.
13
64
  - Lists are checked while they are built, so `each` in a long loop, list
@@ -15,6 +66,34 @@
15
66
  memory. Strings joined with `+`, strings from functions and `echo()` text
16
67
  count toward `maxListItems` too, and a repeated unknown argument warning
17
68
  is shown once.
69
+ - `echo()`, `str()`, `join()` and `==` on lists that hold the same list many
70
+ times stop at the limits instead of running out of memory: text stops at
71
+ `maxListLength` characters while it is written, and comparing lists does
72
+ not compare the same pair of lists twice. Error messages, part names and
73
+ other settings show only the start of a long list.
74
+ - Comparing lists, dot products and built-in functions that go through a list
75
+ or string, such as `search()`, `contains()`, `max()` and `sum()`, take a
76
+ step for each item, so a loop over a long list stops at `maxSteps` instead
77
+ of running for minutes.
78
+ - `hull()` around named parts makes the hull of each part, the same as
79
+ `hull()` inside the part. It left the parts unchanged before.
80
+ - DXF layers whose names come out the same in DXF, such as `a b` and `a_b`,
81
+ stay separate layers: the later ones get `_2`, `_3` and so on. The
82
+ built-in layers keep `CUT`, `SCORE`, `ENGRAVE` and `GUIDE`.
83
+ - Built-in functions take named arguments, such as `sin(x = 30)` and
84
+ `pow(x = 2, y = 10)`; an unknown name gives a warning, and so does an
85
+ argument given both by position and by name. They were ignored before.
86
+ - Errors and warnings in a library name it, as in
87
+ `(line 3, column 5 in <lib/tabs>)`. The playground marks the line only for
88
+ errors in the program itself.
89
+ - `toString()`, `constructor()` and other names every JavaScript object has
90
+ are unknown functions and libraries, instead of a crash or a wrong value.
91
+ - `text_width()` and `text_metrics()` take a step per character, have the
92
+ same length limit as `text()`, and are much faster for long text.
93
+ - A list times a matrix of the wrong size, and `params` values that are not
94
+ numbers, strings, booleans or lists, are errors instead of a crash.
95
+ - A list times a list whose items multiply to lists is an error instead of
96
+ text.
18
97
  - The commercial licence for closed-source apps and services is free: email
19
98
  hi@mylasertools.com.
20
99
 
package/LANGUAGE.md CHANGED
@@ -230,18 +230,20 @@ export examples.
230
230
 
231
231
  Supply geometry as children of a shape-processing module, for example
232
232
  `translate([10, 0]) circle(d = 5);`. Fonts used by `text()` must be supplied
233
- by the application or through the [command line](docs/cli.md).
233
+ by the application or through the [command line](docs/cli.md). The
234
+ playground, the command line and `kerfscript.wasm` have `Atkinson`
235
+ (Atkinson Hyperlegible) built in.
234
236
 
235
237
  ### Transforms
236
238
 
237
- | Module | Effect |
238
- | --- | --- |
239
- | `translate([x, y])` | Move geometry by x and y |
239
+ | Module | Effect |
240
+ | ---------------------------- | ---------------------------------------------------------------------- |
241
+ | `translate([x, y])` | Move geometry by x and y |
240
242
  | `rotate(a, center = [x, y])` | Rotate by `a` degrees around a point; the default centre is the origin |
241
- | `scale(s)` | Scale both axes by the same factor |
242
- | `scale([sx, sy])` | Scale each axis separately |
243
- | `mirror([nx, ny])` | Reflect across a line through the origin, perpendicular to this vector |
244
- | `multmatrix(m)` | Apply a 2 × 3 transformation matrix |
243
+ | `scale(v)` | Scale both axes by the number `v` |
244
+ | `scale([sx, sy])` | Scale each axis separately |
245
+ | `mirror([nx, ny])` | Reflect across a line through the origin, perpendicular to this vector |
246
+ | `multmatrix(m)` | Apply a 2 × 3 transformation matrix |
245
247
 
246
248
  ### Combining
247
249
 
@@ -250,7 +252,7 @@ by the application or through the [command line](docs/cli.md).
250
252
  | `union()` / `group()` | joins children |
251
253
  | `difference()` | removes every later child's area from every layer of the first child |
252
254
  | `intersection()` | keeps only the area common to all children, layers of the first child kept |
253
- | `hull()` | convex hull |
255
+ | `hull()` | convex hull; each named part inside gets its own hull |
254
256
  | `offset(r)` / `offset(delta, chamfer = false)` | grow (positive) or shrink areas; `r` rounds corners |
255
257
  | `stroke(width)` | turns lines into areas of that width, for cutting single line art |
256
258
  | `outline()` | turns areas into their outline lines |
@@ -266,50 +268,53 @@ by the application or through the [command line](docs/cli.md).
266
268
 
267
269
  ### Other
268
270
 
269
- | Module | Effect |
270
- | --- | --- |
271
- | `echo(...)` | Report values without creating geometry |
272
- | `assert(cond, message)` | Stop with `message` when `cond` is false |
273
- | `children(i)` | Render a child passed to the current module; omit `i` to render all children |
271
+ | Module | Effect |
272
+ | ---------------------------- | -------------------------------------------------------------------------------- |
273
+ | `echo(...)` | Report values without creating geometry |
274
+ | `assert(condition, message)` | Stop with `message` when `condition` is false |
275
+ | `children(index)` | Render a child passed to the current module; omit `index` to render all children |
274
276
 
275
277
  ## Built-in functions
276
278
 
277
- Functions return values. The examples below use positional arguments.
279
+ Functions return values. Arguments can be positional or named with the
280
+ parameter names below, as in `pow(x = 2, y = 10)` or `log(8, base = 2)`. An
281
+ unknown name gives a warning. `concat`, `str` and `chr` take only positional
282
+ arguments.
278
283
 
279
284
  ### Numbers and vectors
280
285
 
281
- | Function | Result |
282
- | --- | --- |
283
- | `abs(x)`, `sign(x)` | Absolute value, or sign as -1, 0 or 1 |
284
- | `floor(x)`, `ceil(x)`, `round(x)` | Round down, up or to the nearest integer |
285
- | `sqrt(x)`, `pow(x, y)` | Square root, or x raised to y |
286
- | `exp(x)`, `ln(x)` | Exponential with base e, or natural logarithm |
287
- | `log(x)`, `log(base, x)` | Base-10 logarithm, or logarithm with a chosen base |
288
- | `sin(a)`, `cos(a)`, `tan(a)` | Trigonometric functions with angles in degrees |
289
- | `asin(x)`, `acos(x)`, `atan(x)`, `atan2(y, x)` | Inverse trigonometric functions, returning degrees |
290
- | `min(values)`, `max(values)` | Minimum or maximum of a list; also accept separate values |
291
- | `clamp(x, low, high)` | Limit x to the given interval |
292
- | `lerp(a, b, t)` | Interpolate between numbers or vectors; t = 0 gives a, t = 1 gives b |
293
- | `norm(v)` | Length of a vector |
294
- | `cross(a, b)` | Cross product; a scalar for 2D vectors or a vector for 3D vectors |
286
+ | Function | Result |
287
+ | ---------------------------------------------- | -------------------------------------------------------------------- |
288
+ | `abs(x)`, `sign(x)` | Absolute value, or sign as -1, 0 or 1 |
289
+ | `floor(x)`, `ceil(x)`, `round(x)` | Round down, up or to the nearest integer |
290
+ | `sqrt(x)`, `pow(x, y)` | Square root, or x raised to y |
291
+ | `exp(x)`, `ln(x)` | Exponential with base e, or natural logarithm |
292
+ | `log(x)`, `log(base, x)` | Base-10 logarithm, or logarithm with a chosen base |
293
+ | `sin(x)`, `cos(x)`, `tan(x)` | Trigonometric functions with angles in degrees |
294
+ | `asin(x)`, `acos(x)`, `atan(x)`, `atan2(y, x)` | Inverse trigonometric functions, returning degrees |
295
+ | `min(values)`, `max(values)` | Minimum or maximum of a list; also accept separate values |
296
+ | `clamp(x, low, high)` | Limit x to the given interval |
297
+ | `lerp(a, b, t)` | Interpolate between numbers or vectors; t = 0 gives a, t = 1 gives b |
298
+ | `norm(v)` | Length of a vector |
299
+ | `cross(a, b)` | Cross product; a scalar for 2D vectors or a vector for 3D vectors |
295
300
 
296
301
  ### Lists and strings
297
302
 
298
- | Function | Result |
299
- | --- | --- |
300
- | `len(value)` | Number of items in a list or characters in a string |
301
- | `concat(...)` | Join lists and individual values into one list |
302
- | `reverse(value)` | Reverse a list or string |
303
- | `sum(values)` | Sum a list of numbers |
304
- | `slice(value, start = 0, end)` | Extract part of a list or string; `end` is exclusive and defaults to its length |
305
- | `str(...)` | Convert values to text and join them |
306
- | `chr(...)`, `ord(text)` | Convert Unicode code points to text, or read the first character's code point |
307
- | `upper(text)`, `lower(text)` | Change letter case |
308
- | `split(text, separator = " ")` | Split text into a list of strings |
309
- | `join(values, separator = "")` | Join values into a string |
310
- | `search(value, list)` | Index of the first equal item, or `undef` if absent; also searches characters in strings |
311
- | `contains(value, item)` | Whether a list contains an item or a string contains a substring |
312
- | `lookup(x, table)` | Interpolate in a table of `[x, y]` pairs; use the nearest endpoint outside the table's range |
303
+ | Function | Result |
304
+ | ------------------------------ | -------------------------------------------------------------------------------------------- |
305
+ | `len(value)` | Number of items in a list or characters in a string |
306
+ | `concat(...)` | Join lists and individual values into one list |
307
+ | `reverse(value)` | Reverse a list or string |
308
+ | `sum(values)` | Sum a list of numbers |
309
+ | `slice(value, start = 0, end)` | Extract part of a list or string; `end` is exclusive and defaults to its length |
310
+ | `str(...)` | Convert values to text and join them |
311
+ | `chr(...)`, `ord(text)` | Convert Unicode code points to text, or read the first character's code point |
312
+ | `upper(text)`, `lower(text)` | Change letter case |
313
+ | `split(text, separator = " ")` | Split text into a list of strings |
314
+ | `join(values, separator = "")` | Join values into a string |
315
+ | `search(value, list)` | Index of the first equal item, or `undef` if absent; also searches characters in strings |
316
+ | `contains(value, item)` | Whether a list contains an item or a string contains a substring |
317
+ | `lookup(x, table)` | Interpolate in a table of `[x, y]` pairs; use the nearest endpoint outside the table's range |
313
318
 
314
319
  ### Types
315
320
 
@@ -342,15 +347,29 @@ list can appear as editable settings. Comments supply captions, ranges,
342
347
  choices and groups:
343
348
 
344
349
  ```kerf
345
- /* [Size] */ // group for the following parameters
346
- // Outer width <- caption: the comment line above
347
- width = 120; // [40:5:400] min:step:max
348
- depth = 80; // [200] max
349
- shape = "round"; // [round:Round, square:Square] choices with labels
350
- count = 3; // [2, 3, 4] choices
351
- /* [Hidden] */ // not listed
350
+ /* [Size] */
351
+ // Outer width
352
+ width = 120; // [40:5:400]
353
+ depth = 80; // [200]
354
+ shape = "round"; // [round:Round, square:Square]
355
+ count = 3; // [2, 3, 4]
356
+ /* [Hidden] */
357
+ seed = 7;
352
358
  ```
353
359
 
360
+ - `/* [Size] */` starts a group; the settings below it are listed under
361
+ _Size_.
362
+ - `// Outer width`, the comment line just above an assignment, is its caption.
363
+ - `[40:5:400]` is a slider from 40 to 400 in steps of 5; `[200]` is a slider
364
+ up to 200.
365
+ - `[round:Round, square:Square]` is a menu of values with labels; `[2, 3, 4]`
366
+ is a menu of values.
367
+ - Settings after `/* [Hidden] */` are not listed.
368
+
369
+ Nothing else may follow the brackets on the same line: with
370
+ `// [40:5:400] min:step:max` the setting is a plain number field, not a
371
+ slider.
372
+
354
373
  These comments follow OpenSCAD's customizer format. Read
355
374
  [Settings](docs/customizer.md) for supported controls, or use `kerf params`
356
375
  to inspect the settings in a file.
@@ -369,30 +388,42 @@ Load the box modules with `use <kerf/box>`:
369
388
  - **SVG**: millimetre units, one Inkscape layer group per KerfScript layer,
370
389
  colours as above, 0.1 mm hairline strokes, `data-part` on every path.
371
390
  - **DXF**: R12 (AC1009) polylines on layers named after KerfScript layers,
372
- with matching AutoCAD colours.
391
+ with matching AutoCAD colours. Names are upper case with characters other
392
+ than letters, digits, `_` and `-` as `_`; when two layers come out the
393
+ same, the later one gets `_2`, `_3` and so on. The built-in layers keep
394
+ `CUT`, `SCORE`, `ENGRAVE` and `GUIDE`, so a layer of your own named `Cut`
395
+ becomes `CUT_2`.
373
396
 
374
397
  ## Limits
375
398
 
376
399
  The interpreter limits call depth to 1,000 for modules and 2,000 for
377
- functions. Expanded ranges are limited to 1,000,000 items. The default
400
+ functions. A range a loop or built-in function goes through is limited to
401
+ `maxListLength` items (below). The default
378
402
  evaluation limit is 5,000,000 steps; applications can change it with
379
- `maxSteps`. Exceeding a limit stops evaluation with an error.
403
+ `maxSteps`. Comparing lists with `==`, multiplying two lists, and built-in
404
+ functions that go through a list or string take a step for each item;
405
+ `len()` of a list, `ord()`, `type_of()` and the `is_` functions take one.
406
+ Exceeding a limit stops evaluation with an error.
380
407
 
381
408
  Programs are also limited in how much memory they can ask for:
382
409
 
383
- | Limit | Default | What it counts |
384
- | --- | --- | --- |
385
- | `maxParts` | 10,000 | copies of all parts, adding up every `qty` |
386
- | `maxSegments` | 10,000 | segments in one circle, ellipse or arc, and corners in one polygon |
387
- | `maxListLength` | 1,000,000 | items in one list, or characters in one string |
388
- | `maxListItems` | 3,000,000 | items in all lists the program builds, plus 4 for each list, one for every 8 characters of the strings it builds or echoes |
389
- | `maxTextLength` | 2,000 | characters in one `text()` |
390
- | `maxPoints` | 2,000,000 | points in all shapes the modules make, plus 100 for each shape |
391
- | `maxOutputPoints` | 1,000,000 | points in the exported files, counting every copy |
392
- | `maxSheets` | 200 | sheets in the layout |
410
+ | Limit | Default | What it counts |
411
+ | ----------------- | --------- | -------------------------------------------------------------------------------------------------------------------------- |
412
+ | `maxParts` | 10,000 | copies of all parts, adding up every `qty` |
413
+ | `maxSegments` | 10,000 | segments in one circle, ellipse or arc, and corners in one polygon |
414
+ | `maxListLength` | 1,000,000 | items in one list or range, or characters in one string, including the text `echo()` writes in one call |
415
+ | `maxListItems` | 3,000,000 | items in all lists the program builds, plus 4 for each list, one for every 8 characters of the strings it builds or echoes |
416
+ | `maxTextLength` | 2,000 | characters in one `text()` |
417
+ | `maxPoints` | 2,000,000 | points in all shapes the modules make, plus 100 for each shape |
418
+ | `maxOutputPoints` | 1,000,000 | points in the exported files, counting every copy |
419
+ | `maxSheets` | 200 | sheets in the layout |
393
420
 
394
421
  Applications can change them with the `limits` render option.
395
422
 
423
+ A list can hold the same list many times, as in `b = [a, a]`. `echo()`,
424
+ `str()` and `join()` write every copy out, so their text can be far longer
425
+ than the list; they stop with an error once it passes `maxListLength`.
426
+
396
427
  Geometry operations also take time. Applications that render user programs
397
428
  should run them in a worker with a timeout; see the
398
429
  [JavaScript API](docs/javascript.md#running-untrusted-code).
package/README.md CHANGED
@@ -101,17 +101,20 @@ it as the start of your own design.
101
101
  cuts come before outer ones, and the whole part is offset by half the kerf.
102
102
  - **Parts, not just shapes.** `part("lid", qty = 2)` names a piece; the
103
103
  renderer packs parts by material and thickness onto sheets.
104
- - **Joinery that fits.** `finger_rect`, `finger_holes`, `tslot` and
105
- `living_hinge` use `$thickness`, so changing the material keeps joints
106
- tight.
107
- - **Familiar syntax.** If you know OpenSCAD, you can read KerfScript. The
108
- customizer comments are the same, so editors that understand OpenSCAD
109
- parameters understand KerfScript ones.
104
+ - **Joinery that follows the material.** `finger_rect`, `finger_holes`,
105
+ `tslot` and `living_hinge` use `$thickness`, so the joints resize when the
106
+ material changes. Measure your kerf and cut a test piece for a snug fit.
107
+ - **Readable code.** Variables, modules, functions, loops and transforms like
108
+ `translate()` and `rotate()`. Settings come from comments such as
109
+ `// [10:1:100]`, which the playground turns into sliders. If you have used
110
+ OpenSCAD, [Coming from OpenSCAD](docs/openscad.md) lists what differs.
110
111
  - **Runs anywhere.** Pure TypeScript with no native code: the same interpreter
111
112
  runs in browsers, Node, workers and the command line.
112
- - **Safe to run.** Programs read libraries only through the resolver you
113
- provide. Step and recursion limits stop runaway programs, and errors name
114
- the line.
113
+ - **Contained.** Programs read libraries only through the resolver you
114
+ provide and have no other access to files or the network. Step, recursion
115
+ and size limits stop runaway programs, and errors name the line. Run
116
+ programs you did not write in a worker or process you can stop; see
117
+ [Running untrusted code](docs/javascript.md#running-untrusted-code).
115
118
 
116
119
  ## Install
117
120
 
@@ -119,6 +122,12 @@ it as the start of your own design.
119
122
  npm install @mylasertools/kerfscript
120
123
  ```
121
124
 
125
+ The `kerf` command line also comes as a standalone binary that needs no
126
+ Node.js, and through Homebrew, winget, Chocolatey, Scoop, Snap, the AUR,
127
+ MacPorts and Docker: see [Command line](docs/cli.md#without-nodejs). The
128
+ desktop app is in [Desktop app](docs/desktop.md), and the WebAssembly module
129
+ in [WebAssembly](docs/webassembly.md).
130
+
122
131
  ## Command line
123
132
 
124
133
  ```sh
@@ -156,7 +165,9 @@ Errors are thrown as `KerfError` with `line` and `col`.
156
165
  - [Getting started](docs/getting-started.md), then the guides on
157
166
  [layers](docs/layers.md), [kerf](docs/kerf.md),
158
167
  [parts and sheets](docs/parts-and-sheets.md), [settings](docs/customizer.md),
159
- the [command line](docs/cli.md) and the [JavaScript API](docs/javascript.md)
168
+ the [desktop app](docs/desktop.md), the [command line](docs/cli.md), the
169
+ [JavaScript API](docs/javascript.md)
170
+ and [WebAssembly](docs/webassembly.md) for other languages
160
171
  - [Coming from OpenSCAD](docs/openscad.md): what is the same and what is not
161
172
  - [LANGUAGE.md](LANGUAGE.md): the full language reference
162
173
  - [Versions](docs/versions.md): language and package versions, and what stays
@@ -117,8 +117,17 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
117
117
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
118
118
  ```
119
119
 
120
- ## Atkinson Hyperlegible (test fixture only, not published to npm)
120
+ ## Desktop app
121
121
 
122
- `test/fixtures/AtkinsonHyperlegible-Regular.ttf`, Copyright 2020 Braille
123
- Institute of America, Inc., licensed under the SIL Open Font License 1.1. See
124
- `test/fixtures/OFL.txt`.
122
+ The desktop app is also built from Rust crates (Tauri and its dependencies)
123
+ under MIT, Apache-2.0, MPL-2.0, BSD, ISC, Zlib, Unicode and similar licences.
124
+ `desktop/THIRD-PARTY-LICENSES.md` lists each one with its licence text, and
125
+ the installers place it next to the app.
126
+
127
+ ## Atkinson Hyperlegible (built into the command line and kerfscript.wasm)
128
+
129
+ `fonts/AtkinsonHyperlegible-Regular.ttf`, Copyright 2020 Braille Institute
130
+ of America, Inc., licensed under the SIL Open Font License 1.1. See
131
+ `fonts/OFL.txt`. It is embedded unmodified in `dist/cli.js` and
132
+ `kerfscript.wasm`, and the playground serves it; `dist/index.js` does not
133
+ include it.