Skip to content

KerfScript 0.7.0 - #1

Merged
JulyJ merged 39 commits into
mainfrom
release-0.7
Nov 4, 2026
Merged

JulyJ merged 39 commits into
mainfrom
release-0.7

Conversation

@JulyJ

@JulyJ JulyJ commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

KerfScript 0.7.0, language version 0.3. The full list of changes is in CHANGELOG.md under 0.7.0.

Programs written for 0.2 run as before, except that a variable no longer hides a function of the same name. Numbers with a unit, such as 140cm, are now lengths. New warnings change the text of warnings.

Highlights

  • Assembly:

    • place()
    • part() options: stack, mirror (also one per copy), bend, on, glue and slots. Slots are cut wherever parts pass through each other in the assembly.
    • step() and hardware()
    • a 3D view that builds the model a step at a time
  • Verification: kerf verify and the JavaScript API check:

    • rigid fit
    • shaft capture
    • retention
    • insertion and tool access
    • support and stability
    • joint mobility
    • motion clearance

    --summary lists what didn't pass, grouped by reason.

  • Language:

    • records, {kind = "armchair", x = 10}
    • call() to call a module by name
    • lengths in real units
    • spline(), hatch(), pattern(), paint(), measure() and the path functions
  • New libraries: kerf/gears, kerf/lock, kerf/rings, kerf/joints (halved_pair()), kerf/sizes, kerf/symbols and kerf/geo.

  • CLI: kerf instructions makes printable step pages, kerf diff compares two renders, kerf render -o x.png draws each sheet, and --call runs one module of a library.

  • New warnings:

    • parts narrower than $min_width
    • duplicate part names
    • fills hidden under layers stacked above them
    • paint children that can't be seen
    • settings a program doesn't use
  • $label_parts numbers each part on its back face.

  • Limits: hardware, PNG pictures, records, bend meshes and the children cache are all bounded.

Checks

  • npm test: 717 pass, 0 fail (10 skipped)
  • npm run format:check and tsc --noEmit are clean
  • The site builds with Node 24 (123 pages)

JulyJ added 30 commits November 3, 2026 11:22
…rom the assembly, steps and hardware

Variables no longer hide functions of the same name. New: place(), step(),
hardware(), pattern(), fill_holes(), close(), line(width), text(fit),
path(close), back-face marks, part(stack, mirror, bend, on, slots), path and
rotation functions, a project sheet in kerf.json.
… that builds the model step by step

New libraries kerf/gears, kerf/lock, kerf/rings, kerf/sizes, kerf/symbols and
kerf/geo; flex_strip() and narrow_hinge() in kerf/hinge, a placed and named
box(). spline(), hatch(), paint(), measure(), layer order and bleed,
registration marks, glued parts, lengths in real units with $scale, use
beside the including library, nesting in holes, warnings that say where,
kerf check --stats and --sweep. The playground and model pages show the
assembly in 3D with its steps, bends and hardware.
The package, the alias, the desktop app and the Flatpak metadata move to
0.7.0, and the language to 0.3. The specification covers lengths in units,
libraries finding files beside them and placement in 3D; the guides cover
assembly, the back face, painting and printing, and the new command line
options. A bend places its part.
59 examples gain assembly steps for the 3D view. The balance, the sector
stand, the wheel calendar, the screw press and the slat roll list their
hardware with hardware() and cut their slots from the assembly; the gear
pair uses kerf/gears, the signs fit their text with text(fit). New: a lock
box, a ring vase, a world map sign, a bent pen tray and symbol tags, with
their gallery pictures. The render pipeline's flex box shows assembled.
…s, clear marks and leave edges alone

A front layer named like a back layer is now an error rather than a lost
back job. kerf check --sweep refuses a range that is not one. point_at()
and heading_at() carry on past repeated end points. slots = [...] takes
* patterns and !name exclusions, a slot clears every layer's marks, and a
part that only touches the edge leaves no notch.
0.7 made the first child with area the base of intersection(), so a
child of lines next to an area returned the area as well as the clipped
lines. The area is now what every child covers, so a child of lines alone
leaves none; each child's lines are clipped to the other children's area
and pass whole when no other child has any. Conformance cases check both
orders.
A stack with glue and face = "bottom" put its layers after the first inside the host, one thickness apart upward. They now step along the face's outward normal.
use </abs/path> names that file, and a library loaded that way finds its own uses beside it. A module or function with a built-in's name now warns that it hides it; helix, topo-island and the browser coaster no longer do.
…ettings a program lacks

- Stacks glued under their host bond layer to layer face down.
- Copies of a part compare their slots by the area they differ in, so rounding slivers no longer warn and make models unverifiable.
- kerf verify refuses configuration params the model does not assign; render() warns about them generally (the game-insert fixture used a stale name).
- Revolute ranges are checked in whole turns, so hinges past 180 degrees verify.
- Captured-shaft stops pair with bearings by axial position, not list order.
- Bores are centred on their corners' mean, so odd-sided circles verify.
Unknown fastenings and hold steps, a body moved twice and a cut-invariant claim with fewer than two configurations now give invalid-spec (exit 2) rather than a failed design. scripts/verify-models.mjs finds the CLI from any folder.
…ware kinds

Hardware kinds now live in src/hardware.ts (HARDWARE_KINDS), documented with their look; the 3D viewer reads its colours from there and lights LEDs.
Each row of kerf/symbols now carries its icon grid (the viewBox, 24 x 24 for Material Design Icons, else what it draws); symbol() and symbol_size() take box = "grid".
An opening by $min_width (1.5 mm unless set, 0 for none) finds material too thin to survive, ignoring corner tips, and the warning says where. The hours clock sets 1.3 mm for its spring finger and tapered hands.
…slot

The collision check samples only the depth each part reaches through the other, so a shallow intrusion is seen, and counts 0.1 mm³ rather than 1 mm³. Slots need a part to reach half way through, so a 0.13 mm dip into a floor is reported instead of slotted.
A furniture module's own steps now build it alone and inside a room: "Add the chair: Glue the seat". A caller that wants one step per piece writes substeps = false.
The program becomes the -D settings, use <the file>, and the call, so a prop or module can be tried without a scratch design.
diffRenders() compares renders made with layout none in each part's own coordinates (sheet items now carry their offset), giving the area of shapes that differ, lines added and removed, and where.
… out

The half-way rule dropped slots for 1.5 mm tabs in a 4 mm floor. A part now needs to reach 1 mm in (or half way through a thinner part).
s.kind and s["kind"] read a field (undef when missing); keys(), merge(), is_record() and len() work on records; records compare by fields and echo as {a = 1}. kerf fmt lays them out as lists. The record code sits outside expr()'s switch so recursion depth is unchanged.
A table entry can carry the module that draws it: call(entry.draw, 40, 30) with the call's children, for user and built-in modules.
…vice knows the 0.7 special variables

The halving slot left strips under 1.5 mm beside it for small holes; ring_stack now asserts and names the smallest hole for the thickness ($min_width). $scale, $nest, $registration, $min_width, $mirrored and $bounds get hover text.
A child lying wholly on an area of the same layer under it merges with it: the rose petals on the duvet, cream text on a cream sign.
The number goes where the part has the most room (a point on the ring for a frame), its glyphs turned to read once the part is turned over. kerf check lists the numbers with the parts and after each step; parts get label in JavaScript.
Tiles are tested and clipped against the pieces of the area whose boxes they meet, and skipped when they meet none; 30 hearts on a 400 x 300 facade went from 6.4 s to 0.2 s with the same output.
…p is on shapes kept

A child with the same special variables, frame and step that makes only a shape is made once per call, so tones()-style modules stop multiplying their work. The 2M cap applies to any one shape; all shapes made along the way may add up to ten times that, and children() no longer counts its shapes again.
…ate's slot

The feet narrow (down to 2 mm) and move out from the centre; a hole with no room for them asserts with the smallest hole that has, or feet = false.
instructionsHtml() draws the model after each step from the front right, the step's parts in orange, with the parts (by number) and hardware it adds, after the parts list and the sheets.
A small rasterizer (src/png.ts, Node only): the parts' material in a wood tone, fill layers in their colours, score and cut lines a pixel or two wide however thin they are in the file; --px sets the width.
JulyJ added 9 commits November 3, 2026 16:27
When a stack's layers meet different parts, it becomes a part per layer ("top, layer 1"), so blind joints don't show through; alike layers stay one stack.
The 0.01 mm margin that keeps touching parts from cutting slots no longer trims the slot itself: the cross-section is taken over the whole thickness once a part is known to pass through. Also tests that ring_stack's base stays one piece.
Groups non-passing claims by status and code, failures first, with configurations, bodies and measurements, on stderr; triage() and triageText() in JavaScript. The report and exit codes are unchanged.
- The children cache does not reuse a child when a special variable holds a
  function: two functions with the same parameters wrote alike.
- Writing a record is iterative and bounded like a list, so deeply nested
  records cannot take exponential time or be cut short silently.
- Redefining a layer that is another's back is an error.
- hardware() sides are 3 to 128 and count as work; the viewer clamps too.
- PNG pictures are capped at 16M pixels and 16384 a side, in bytes not floats.
- The playground worker passes each sheet's back-face SVG and DXF, and the
  downloads include them.
- Stability over several loads needs retention links joining them all.
- meshPieces() bands bends only over the part's material and at most
  MAX_BENT_BANDS (2000) of them; a bend beyond its part warns.
- The children cache key writes every value exactly (all digits of a
  number), within 20000 characters, and never reuses one holding a function.
- $label_parts warns and numbers nothing when the program's own front layer
  is named engrave-back.
…dge rounding

A fill wholly under fills of layers stacked above it warns (not hairlines
under 1 mm). part(mirror = [false, true]) mirrors single copies as a part of
their own. kerf/joints adds halved_pair(), checking the strips beside its
slots. A bend ending at the part's edge no longer warns from rounding.
One union of all their rings could cover a fill that no layer above
touched.
@JulyJ
JulyJ merged commit e4d3858 into main Nov 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant