Release assets are now the programs themselves: SolveCraft and solvecraft-cli .exe for Windows,
plain Linux binaries plus the AppImage, a macOS .dmg (SolveCraft.app with an Applications link) and
the universal CLI binary; only the web app stays a zip. The notes template takes {tag}, says to
chmod +x on Linux and macOS, and GitHub adds the change list.
Since no LICENSE or NOTICE file rides along any more, the programs carry them:
THIRD-PARTY-LICENSES.txt (written by `cargo xtask licences` from cargo metadata and each shipped
crate's own licence files, 419 crates; `cargo xtask ci` checks it is current) is embedded with
LICENSE-MIT, LICENSE-APACHE and NOTICE in solvecraft_engine::licences, shown in Help ▸ About ▸
Licences (third-party text laid out by visible rows) and printed by `solvecraft-cli licences`.
Scenario s44_about_licences.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Fixes#34
The surface-analysis curvature map took screen-space derivatives (dpdx/dpdy) inside a branch
of fs_solid. naga accepts that, but browsers' WGSL compiler (Tint, WebGPU) requires derivatives
in uniform control flow and rejects the whole `sc_viewport` shader module, so every viewport
pipeline was invalid and the viewport stayed black as soon as a design was shown. The
curvature is now worked out first thing in fs_solid and passed in, and the canvas image
shader samples its texture before discarding.
Regression test: every fragment entry point makes derivative and implicit-LOD sampling calls
(directly or through helpers) before any branch or return. Checked in headless Chromium with
WebGPU (SwiftShader): before, "dpdx must only be called from uniform control flow" and invalid
pipelines on New Design; after, no shader or pipeline errors.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The title bar's ×, File ▸ Quit, the OS closing the window (Alt+F4, Cmd+Q, the window manager:
close_requested is cancelled until the designs are answered) and app.quit / ui.window close all
go through documents::request_quit, which asks Save / Don't Save / Cancel once per design with
unsaved changes. Save saves (Save As for a never-saved design) and quits only if it saved; Don't
Save discards (no recovery copy is kept); Cancel or Esc stays open. Closing a tab asks the same
question (now titled "Save changes to <name>?"); New and Open never replace an unsaved design
(they open a tab). The web app asks through beforeunload while a design is unsaved. Control:
ui.documents {action: quit, answer: save|dont_save|cancel}, app.quit {force}. Tests: quit with
Save then Don't Save across two tabs, Cancel, a Save that saves nothing, Save As; scenario
s43_quit_asks_to_save.
Fixes#33
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An mdBook under docs/book: a user guide (getting started, UI tour, sketching, features,
parameters, components and joints, sheet metal, plastic, files, shortcuts, MCP) and a developer
guide (CONTRIBUTING, architecture, command engine, kernel boundary, persistent naming, test
harnesses, and the existing docs/*.md pages). `cargo xtask book` generates the command reference
from `solvecraft-cli commands` and renders the screenshots with `solvecraft-cli snapshot`, then
runs mdbook. .github/workflows/pages.yml publishes the book at /solvecraft/ and the web app at
/solvecraft/app/ (skipped while the repository is private). .github/workflows/ci.yml runs
`cargo xtask ci` on pull requests. README and Help → Documentation link to the book.
Fixes#28Fixes#8
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The old ids live only in crates/engine/src/legacy_ids.rs (the legacy migration table): find_command maps them, so old scripts, recipes and control calls keep working, and saved shortcuts, recent and pinned commands are migrated on load (designs store no command ids). Parity maps the observed catalog ids to ours through xtask/data/fusion-ids.tsv. The agent rule now reads: our own dotted id, never another product's. Cargo.toml's repository points at github.com/bherbruck/solvecraft.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- solvecraft_io::vfs: the engine's file access (disk natively; in the browser an in-memory set of
files the host loads and stores). File commands and write_atomic go through it.
- Web host (apps/solvecraft-web/src/files.rs): stored designs are read from OPFS at start
(storage.persist() asked for) and written back when a command writes one; Save/Save As keep
designs in the browser, exports (STEP, STL, 3MF, OBJ, ...) download as files.
- File > Open shows "Files in this browser": open, insert, rename, download, delete, plus open or
insert from the computer (showOpenFilePicker where there is one, an <input type=file>
otherwise). Files dropped on the page are inserted or opened.
- Unsaved work is autosaved to OPFS and offered again after a reload or crash.
- Start page recents come from the stored designs, with thumbnails. In the browser the samples
are built when opened (a picture blocks the page there, and a kernel panic can't be caught).
- Pages without OPFS (http on a network address, old browsers) keep designs in local storage.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
The standard parts library by type (with its ISO standard), size and length, the size's table
values and a picture of the part; picking a hole's wall or rim seats it there with a rigid joint
(the live preview shows it seated), without a pick it goes at the origin. Opens on
FusionFastenersCommand (and parts.insert); OK runs FusionFastenersCommand. Its own icon.
Insert's world picks (`at`, `point`, `direction`) go through the component frames like other
picks. Tests for the library parsing and the insert parameters; checked in the app: an M3 socket
head cap screw seated in a drilled hole with its joint.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
The Split feature takes an optional face tool (FaceAt, the face through a
point on a named body) and splits with kernel split_body, the tool's
surface extended; otherwise the plane as before. Engine test: a box split
by a cylinder's side into the core and the rest.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
- IGES import (kernel iges_in, from the published specification). The file
is restated as STEP and read by the STEP reader, so an IGES file becomes
the same Import base feature.
- Manifold solid B-reps (186/514/510/508/504/502).
- Trimmed (144), bounded (143) and bare B-spline surfaces, sewn into a
solid by shared edges, with face orientation made consistent.
- Curves: lines, arcs, ellipses, composite and rational B-spline curves.
- Surfaces: planes, analytic surfaces 190-198, B-spline surfaces,
surfaces of revolution and tabulated cylinders.
- Transformations, units, colours, names (406 form 15 or labels).
Open, insert and drop .igs/.iges like STEP.
- IGES export (kernel iges_out): each body as a manifold solid B-rep on the
surfaces a STEP export uses, with name properties and colours.
ExportCommand to .igs.
- OBJ import as mesh bodies: objects/groups, polygons fanned, negative
indices, bad lines reported.
- STL: binary files whose header starts with "solid" and those with a zero
triangle count read as binary; non-finite vertices dropped; facet
normals ignored; ASCII with non-UTF-8 names. Mesh bodies get consistent
winding (stray inverted facets turned over).
- Tests: primitives and features round-trip through IGES (volume, area,
name, colour). Six trimmed patches sew into a closed box. Hostile IGES,
OBJ and STL inputs never panic. STL quirks. The engine exports a design
to IGES and opens it again. All 76 oracle parts round-trip through IGES
with matching volume (local check).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
What a component holds moves with its occurrence, and picks in the world land in the
component's frame:
- Sketches and construction planes show where their component is placed (free move, joint
drive, Capture Position); sketch.inspect reports sketch points in the world.
- frames.rs maps a command's world picks into the frame of the body they lie on (through the
inverse of its placement: a moved component or a pasted instance), points that make new
geometry into the active component's frame, and directions with them; instance body names
("Body1 (Pin:2)") become the component's body. Fillet/chamfer edges, shell/offset/draft
faces, face extrude/revolve, sketch on face, holes, sheet metal and plastic picks, primitives,
appearance faces, analyses. Once per command (not again for commands run by commands); the
active-frame fallback stays for commands without a table. Nothing changes while every
placement is the identity.
- component.move_bodies keeps a body where it is in the world (Document.body_offsets: from the
new component's frame to the one it was made in; renames and deleted components follow).
- The UI reads the placed model (Session::world_state) for display, picking, sketch editing and
dialogs; live previews use it once anything is placed.
Tests (engine): sketches and construction planes move (and turn) with the component; world
picks fillet, sketch, hole and make boxes in a moved component; a pick on a pasted instance;
move_bodies keeps the world place there and back; a sketch on a face follows a Move feature.
QA's s34 now runs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
Fold: the bend line (a sketch line), an optional stationary side, Bend Line Position
(centerline, start, end, outside mould line), angle (on the canvas), bend radius (default from
the rule) and flip; live preview and Edit Feature (folds made from two points keep them). Its
own icon; listed after Hem on the SHEET METAL tab and in the timeline.
Configurations: Add Column lists parameters and features to pick, Add Row names the new row
Configuration<N> when no name is typed, bare numbers show with their parameter's unit, the row
name comes before its Activate button, and Enter commits a cell instead of closing the panel.
Motion Study: steps default to the engine's 100, the key's value row is "Value" and Enter there
commits the key (OK makes the study).
Scenarios s22 (fold, flat pattern, DXF; the expected volume is the folded sheet's, the bend
keeping the neutral layer), s23 (configurations) and s24 (motion study) now run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL